feat: 集成 Tailwind CSS v4,替换手写样式为 utility classes
This commit is contained in:
@@ -113,7 +113,8 @@ function wsUrl(): string {
|
|||||||
return `${proto}//${location.host}/ws${q}`;
|
return `${proto}//${location.host}/ws${q}`;
|
||||||
}
|
}
|
||||||
function setStatus(cls: string, text: string): void {
|
function setStatus(cls: string, text: string): void {
|
||||||
statusEl.className = `status ${cls}`;
|
statusEl.classList.remove("connected", "disconnected", "connecting");
|
||||||
|
statusEl.classList.add(cls);
|
||||||
statusText.textContent = text;
|
statusText.textContent = text;
|
||||||
}
|
}
|
||||||
function connectWS(): void {
|
function connectWS(): void {
|
||||||
|
|||||||
@@ -15,7 +15,21 @@
|
|||||||
"linter": {
|
"linter": {
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"rules": {
|
"rules": {
|
||||||
"recommended": true
|
"recommended": true,
|
||||||
|
"nursery": {
|
||||||
|
"useSortedClasses": "warn"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"css": {
|
||||||
|
"parser": {
|
||||||
|
"tailwindDirectives": true
|
||||||
|
},
|
||||||
|
"formatter": {
|
||||||
|
"enabled": true
|
||||||
|
},
|
||||||
|
"linter": {
|
||||||
|
"enabled": true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"javascript": {
|
"javascript": {
|
||||||
|
|||||||
92
web/bun.lock
92
web/bun.lock
@@ -6,6 +6,8 @@
|
|||||||
"name": "web",
|
"name": "web",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^2.4.4",
|
"@biomejs/biome": "^2.4.4",
|
||||||
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
|
"tailwindcss": "^4.2.1",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
},
|
},
|
||||||
@@ -82,6 +84,16 @@
|
|||||||
|
|
||||||
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="],
|
"@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.3", "", { "os": "win32", "cpu": "x64" }, "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA=="],
|
||||||
|
|
||||||
|
"@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="],
|
||||||
|
|
||||||
|
"@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="],
|
||||||
|
|
||||||
|
"@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="],
|
||||||
|
|
||||||
|
"@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="],
|
||||||
|
|
||||||
|
"@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.59.0", "", { "os": "android", "cpu": "arm" }, "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg=="],
|
"@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.59.0", "", { "os": "android", "cpu": "arm" }, "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg=="],
|
||||||
|
|
||||||
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.59.0", "", { "os": "android", "cpu": "arm64" }, "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q=="],
|
"@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.59.0", "", { "os": "android", "cpu": "arm64" }, "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q=="],
|
||||||
@@ -132,16 +144,80 @@
|
|||||||
|
|
||||||
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA=="],
|
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA=="],
|
||||||
|
|
||||||
|
"@tailwindcss/node": ["@tailwindcss/node@4.2.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.1" } }, "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.1", "@tailwindcss/oxide-darwin-arm64": "4.2.1", "@tailwindcss/oxide-darwin-x64": "4.2.1", "@tailwindcss/oxide-freebsd-x64": "4.2.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", "@tailwindcss/oxide-linux-x64-musl": "4.2.1", "@tailwindcss/oxide-wasm32-wasi": "4.2.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.2.1", "", { "os": "android", "cpu": "arm64" }, "sha512-eZ7G1Zm5EC8OOKaesIKuw77jw++QJ2lL9N+dDpdQiAB/c/B2wDh0QPFHbkBVrXnwNugvrbJFk1gK2SsVjwWReg=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-q/LHkOstoJ7pI1J0q6djesLzRvQSIfEto148ppAd+BVQK0JYjQIFSK3JgYZJa+Yzi0DDa52ZsQx2rqytBnf8Hw=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.2.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-/f/ozlaXGY6QLbpvd/kFTro2l18f7dHKpB+ieXz+Cijl4Mt9AI2rTrpq7V+t04nK+j9XBQHnSMdeQRhbGyt6fw=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.2.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-5e/AkgYJT/cpbkys/OU2Ei2jdETCLlifwm7ogMC7/hksI2fC3iiq6OcXwjibcIjPung0kRtR3TxEITkqgn0TcA=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.2.1", "", { "os": "linux", "cpu": "arm" }, "sha512-Uny1EcVTTmerCKt/1ZuKTkb0x8ZaiuYucg2/kImO5A5Y/kBz41/+j0gxUZl+hTF3xkWpDmHX+TaWhOtba2Fyuw=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-CTrwomI+c7n6aSSQlsPL0roRiNMDQ/YzMD9EjcR+H4f0I1SQ8QqIuPnsVp7QgMkC1Qi8rtkekLkOFjo7OlEFRQ=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WZA0CHRL/SP1TRbA5mp9htsppSEkWuQ4KsSUumYQnyl8ZdT39ntwqmz4IUHGN6p4XdSlYfJwM4rRzZLShHsGAQ=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-qMFzxI2YlBOLW5PhblzuSWlWfwLHaneBE0xHzLrBgNtqN6mWfs+qYbhryGSXQjFYB1Dzf5w+LN5qbUTPhW7Y5g=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-5r1X2FKnCMUPlXTWRYpHdPYUY6a1Ar/t7P24OuiEdEOmms5lyqjDRvVY1yy9Rmioh+AunQ0rWiOTPE8F9A3v5g=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.2.1", "", { "dependencies": { "@emnapi/core": "^1.8.1", "@emnapi/runtime": "^1.8.1", "@emnapi/wasi-threads": "^1.1.0", "@napi-rs/wasm-runtime": "^1.1.1", "@tybys/wasm-util": "^0.10.1", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-MGFB5cVPvshR85MTJkEvqDUnuNoysrsRxd6vnk1Lf2tbiqNlXpHYZqkqOQalydienEWOHHFyyuTSYRsLfxFJ2Q=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.2.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-YlUEHRHBGnCMh4Nj4GnqQyBtsshUPdiNroZj8VPkvTZSoHsilRCwXcVKnG9kyi0ZFAS/3u+qKHBdDc81SADTRA=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.2.1", "", { "os": "win32", "cpu": "x64" }, "sha512-rbO34G5sMWWyrN/idLeVxAZgAKWrn5LiR3/I90Q9MkA67s6T1oB0xtTe+0heoBvHSpbU9Mk7i6uwJnpo4u21XQ=="],
|
||||||
|
|
||||||
|
"@tailwindcss/vite": ["@tailwindcss/vite@4.2.1", "", { "dependencies": { "@tailwindcss/node": "4.2.1", "@tailwindcss/oxide": "4.2.1", "tailwindcss": "4.2.1" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7" } }, "sha512-TBf2sJjYeb28jD2U/OhwdW0bbOsxkWPwQ7SrqGf9sVcoYwZj7rkXljroBO9wKBut9XnmQLXanuDUeqQK0lGg/w=="],
|
||||||
|
|
||||||
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@25.3.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ=="],
|
"@types/node": ["@types/node@25.3.3", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-DpzbrH7wIcBaJibpKo9nnSQL0MTRdnWttGyE5haGwK86xgMOkFLp7vEyfQPGLOJh5wNYiJ3V9PmUMDhV9u8kkQ=="],
|
||||||
|
|
||||||
|
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||||
|
|
||||||
|
"enhanced-resolve": ["enhanced-resolve@5.20.0", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.0" } }, "sha512-/ce7+jQ1PQ6rVXwe+jKEg5hW5ciicHwIQUagZkp6IufBoY3YDgdTTY1azVs0qoRgVmvsNB+rbjLJxDAeHHtwsQ=="],
|
||||||
|
|
||||||
"esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
|
"esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="],
|
||||||
|
|
||||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||||
|
|
||||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||||
|
|
||||||
|
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||||
|
|
||||||
|
"jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="],
|
||||||
|
|
||||||
|
"lightningcss": ["lightningcss@1.31.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.31.1", "lightningcss-darwin-arm64": "1.31.1", "lightningcss-darwin-x64": "1.31.1", "lightningcss-freebsd-x64": "1.31.1", "lightningcss-linux-arm-gnueabihf": "1.31.1", "lightningcss-linux-arm64-gnu": "1.31.1", "lightningcss-linux-arm64-musl": "1.31.1", "lightningcss-linux-x64-gnu": "1.31.1", "lightningcss-linux-x64-musl": "1.31.1", "lightningcss-win32-arm64-msvc": "1.31.1", "lightningcss-win32-x64-msvc": "1.31.1" } }, "sha512-l51N2r93WmGUye3WuFoN5k10zyvrVs0qfKBhyC5ogUQ6Ew6JUSswh78mbSO+IU3nTWsyOArqPCcShdQSadghBQ=="],
|
||||||
|
|
||||||
|
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.31.1", "", { "os": "android", "cpu": "arm64" }, "sha512-HXJF3x8w9nQ4jbXRiNppBCqeZPIAfUo8zE/kOEGbW5NZvGc/K7nMxbhIr+YlFlHW5mpbg/YFPdbnCh1wAXCKFg=="],
|
||||||
|
|
||||||
|
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.31.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-02uTEqf3vIfNMq3h/z2cJfcOXnQ0GRwQrkmPafhueLb2h7mqEidiCzkE4gBMEH65abHRiQvhdcQ+aP0D0g67sg=="],
|
||||||
|
|
||||||
|
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.31.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-1ObhyoCY+tGxtsz1lSx5NXCj3nirk0Y0kB/g8B8DT+sSx4G9djitg9ejFnjb3gJNWo7qXH4DIy2SUHvpoFwfTA=="],
|
||||||
|
|
||||||
|
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.31.1", "", { "os": "freebsd", "cpu": "x64" }, "sha512-1RINmQKAItO6ISxYgPwszQE1BrsVU5aB45ho6O42mu96UiZBxEXsuQ7cJW4zs4CEodPUioj/QrXW1r9pLUM74A=="],
|
||||||
|
|
||||||
|
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.31.1", "", { "os": "linux", "cpu": "arm" }, "sha512-OOCm2//MZJ87CdDK62rZIu+aw9gBv4azMJuA8/KB74wmfS3lnC4yoPHm0uXZ/dvNNHmnZnB8XLAZzObeG0nS1g=="],
|
||||||
|
|
||||||
|
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-WKyLWztD71rTnou4xAD5kQT+982wvca7E6QoLpoawZ1gP9JM0GJj4Tp5jMUh9B3AitHbRZ2/H3W5xQmdEOUlLg=="],
|
||||||
|
|
||||||
|
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.31.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-mVZ7Pg2zIbe3XlNbZJdjs86YViQFoJSpc41CbVmKBPiGmC4YrfeOyz65ms2qpAobVd7WQsbW4PdsSJEMymyIMg=="],
|
||||||
|
|
||||||
|
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-xGlFWRMl+0KvUhgySdIaReQdB4FNudfUTARn7q0hh/V67PVGCs3ADFjw+6++kG1RNd0zdGRlEKa+T13/tQjPMA=="],
|
||||||
|
|
||||||
|
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.31.1", "", { "os": "linux", "cpu": "x64" }, "sha512-eowF8PrKHw9LpoZii5tdZwnBcYDxRw2rRCyvAXLi34iyeYfqCQNA9rmUM0ce62NlPhCvof1+9ivRaTY6pSKDaA=="],
|
||||||
|
|
||||||
|
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.31.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-aJReEbSEQzx1uBlQizAOBSjcmr9dCdL3XuC/6HLXAxmtErsj2ICo5yYggg1qOODQMtnjNQv2UHb9NpOuFtYe4w=="],
|
||||||
|
|
||||||
|
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.31.1", "", { "os": "win32", "cpu": "x64" }, "sha512-I9aiFrbd7oYHwlnQDqr1Roz+fTz61oDDJX7n9tYF9FJymH1cIN1DtKw3iYt6b8WZgEjoNwVSncwF4wx/ZedMhw=="],
|
||||||
|
|
||||||
|
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||||
|
|
||||||
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
"nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="],
|
||||||
|
|
||||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||||
@@ -154,6 +230,10 @@
|
|||||||
|
|
||||||
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
"source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="],
|
||||||
|
|
||||||
|
"tailwindcss": ["tailwindcss@4.2.1", "", {}, "sha512-/tBrSQ36vCleJkAOsy9kbNTgaxvGbyOamC30PRePTQe/o1MFwEKHQk4Cn7BNGaPtjp+PuUrByJehM1hgxfq4sw=="],
|
||||||
|
|
||||||
|
"tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="],
|
||||||
|
|
||||||
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
"tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="],
|
||||||
|
|
||||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
@@ -161,5 +241,17 @@
|
|||||||
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
"undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="],
|
||||||
|
|
||||||
"vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
|
"vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.8.1", "", { "dependencies": { "@emnapi/wasi-threads": "1.1.0", "tslib": "^2.4.0" }, "bundled": true }, "sha512-AvT9QFpxK0Zd8J0jopedNm+w/2fIzvtPKPjqyw9jwvBaReTTqPBk9Hixaz7KbjimP+QNz605/XnjFcDAL2pqBg=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.8.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.1.0", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.1", "", { "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "bundled": true }, "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.1", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg=="],
|
||||||
|
|
||||||
|
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,50 +8,50 @@
|
|||||||
<meta name="theme-color" content="#08080d">
|
<meta name="theme-color" content="#08080d">
|
||||||
<title>VoicePaste</title>
|
<title>VoicePaste</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body class="h-full bg-bg text-fg overflow-hidden select-none">
|
||||||
<div id="app">
|
<div id="app" class="relative z-1 flex flex-col h-full max-w-[480px] mx-auto">
|
||||||
<header>
|
<header class="flex items-center justify-between pt-2 pb-5 shrink-0">
|
||||||
<h1>VoicePaste</h1>
|
<h1 class="text-[22px] font-bold tracking-[-0.03em]">VoicePaste</h1>
|
||||||
<div id="status" class="status disconnected">
|
<div id="status" class="status disconnected flex items-center gap-[7px] text-xs font-medium text-fg-dim px-3 py-[5px] rounded-full bg-surface border border-edge transition-all">
|
||||||
<span class="dot"></span>
|
<span class="dot size-[7px] rounded-full bg-fg-dim shrink-0 transition-all duration-300"></span>
|
||||||
<span id="status-text">连接中…</span>
|
<span id="status-text">连接中…</span>
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<section id="preview-section">
|
<section id="preview-section" class="shrink-0 pb-3">
|
||||||
<div id="preview" class="preview-box">
|
<div id="preview" class="preview-box bg-surface border border-edge rounded-card px-[18px] py-4 min-h-20 max-h-40 overflow-y-auto transition-all duration-300">
|
||||||
<p id="preview-text" class="placeholder">按住说话…</p>
|
<p id="preview-text" class="placeholder text-base leading-relaxed break-words">按住说话…</p>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="mic-section">
|
<section id="mic-section" class="flex flex-col items-center pt-5 pb-4 shrink-0 gap-3.5">
|
||||||
<div class="mic-wrapper">
|
<div class="mic-wrapper relative flex items-center justify-center touch-none">
|
||||||
<button id="mic-btn" type="button" disabled>
|
<button id="mic-btn" class="relative z-1 size-24 rounded-full border-2 border-edge text-fg-secondary flex items-center justify-center cursor-pointer select-none touch-none" type="button" disabled>
|
||||||
<svg viewBox="0 0 24 24" width="48" height="48" fill="currentColor" aria-label="麦克风" role="img">
|
<svg viewBox="0 0 24 24" width="48" height="48" fill="currentColor" aria-label="麦克风" role="img">
|
||||||
<title>麦克风</title>
|
<title>麦克风</title>
|
||||||
<path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z"/>
|
<path d="M12 14c1.66 0 3-1.34 3-3V5c0-1.66-1.34-3-3-3S9 3.34 9 5v6c0 1.66 1.34 3 3 3z"/>
|
||||||
<path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z"/>
|
<path d="M17 11c0 2.76-2.24 5-5 5s-5-2.24-5-5H5c0 3.53 2.61 6.43 6 6.92V21h2v-3.08c3.39-.49 6-3.39 6-6.92h-2z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<div class="mic-rings" aria-hidden="true">
|
<div class="mic-rings absolute inset-0 pointer-events-none" aria-hidden="true">
|
||||||
<span class="ring"></span>
|
<span class="ring absolute inset-0 rounded-full border-[1.5px] border-accent opacity-0"></span>
|
||||||
<span class="ring"></span>
|
<span class="ring absolute inset-0 rounded-full border-[1.5px] border-accent opacity-0"></span>
|
||||||
<span class="ring"></span>
|
<span class="ring absolute inset-0 rounded-full border-[1.5px] border-accent opacity-0"></span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p class="mic-hint">按住说话</p>
|
<p class="text-fg-dim text-sm font-medium">按住说话</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section id="history-section">
|
<section id="history-section" class="flex-1 min-h-0 flex flex-col overflow-hidden">
|
||||||
<div class="history-header">
|
<div class="flex items-center justify-between pb-2.5 shrink-0">
|
||||||
<h2>历史记录</h2>
|
<h2 class="text-[13px] font-semibold text-fg-dim uppercase tracking-[0.06em]">历史记录</h2>
|
||||||
<button id="clear-history" type="button" class="text-btn">清空</button>
|
<button id="clear-history" type="button" class="text-btn bg-transparent border-none text-fg-dim text-xs font-medium cursor-pointer px-2.5 py-1 rounded-lg transition-all duration-150">清空</button>
|
||||||
</div>
|
</div>
|
||||||
<ul id="history-list"></ul>
|
<ul id="history-list" class="list-none flex-1 overflow-y-auto"></ul>
|
||||||
<p id="history-empty" class="placeholder">暂无记录</p>
|
<p id="history-empty" class="text-fg-dim text-sm text-center py-10">暂无记录</p>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
<div id="toast" class="toast"></div>
|
<div id="toast" class="toast fixed left-1/2 z-[999] rounded-3xl text-[13px] font-medium text-fg border border-edge pointer-events-none opacity-0 px-[22px] py-2.5"></div>
|
||||||
|
|
||||||
<script type="module" src="app.ts"></script>
|
<script type="module" src="app.ts"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@biomejs/biome": "^2.4.4",
|
"@biomejs/biome": "^2.4.4",
|
||||||
|
"@tailwindcss/vite": "^4.2.1",
|
||||||
|
"tailwindcss": "^4.2.1",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.3.1"
|
"vite": "^7.3.1"
|
||||||
}
|
}
|
||||||
|
|||||||
528
web/style.css
528
web/style.css
@@ -1,54 +1,38 @@
|
|||||||
*,
|
@import "tailwindcss";
|
||||||
*::before,
|
|
||||||
*::after {
|
/* ─── Design Tokens ─── */
|
||||||
margin: 0;
|
@theme {
|
||||||
padding: 0;
|
--color-bg: #08080d;
|
||||||
box-sizing: border-box;
|
--color-surface: #111117;
|
||||||
}
|
--color-surface-hover: #17171e;
|
||||||
|
--color-surface-active: #1c1c25;
|
||||||
:root {
|
--color-edge: #1e1e2a;
|
||||||
/* Warm dark palette — subtle indigo undertone */
|
--color-edge-active: #2c2c3e;
|
||||||
--bg: #08080d;
|
--color-fg: #eaeaef;
|
||||||
--surface: #111117;
|
--color-fg-secondary: #9e9eb5;
|
||||||
--surface-hover: #17171e;
|
--color-fg-dim: #5a5a6e;
|
||||||
--surface-active: #1c1c25;
|
--color-accent: #6366f1;
|
||||||
--border: #1e1e2a;
|
--color-accent-hover: #818cf8;
|
||||||
--border-active: #2c2c3e;
|
--color-danger: #f43f5e;
|
||||||
--text: #eaeaef;
|
--color-success: #34d399;
|
||||||
--text-secondary: #9e9eb5;
|
--radius-card: 14px;
|
||||||
--text-dim: #5a5a6e;
|
|
||||||
--accent: #6366f1;
|
|
||||||
--accent-hover: #818cf8;
|
|
||||||
--accent-glow: rgba(99, 102, 241, 0.2);
|
|
||||||
--accent-glow-md: rgba(99, 102, 241, 0.35);
|
|
||||||
--accent-glow-lg: rgba(99, 102, 241, 0.08);
|
|
||||||
--danger: #f43f5e;
|
|
||||||
--success: #34d399;
|
|
||||||
--radius: 14px;
|
|
||||||
--radius-sm: 8px;
|
|
||||||
--safe-top: env(safe-area-inset-top, 0px);
|
|
||||||
--safe-bottom: env(safe-area-inset-bottom, 0px);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ─── Base ─── */
|
||||||
|
@layer base {
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
height: 100%;
|
|
||||||
font-family:
|
font-family:
|
||||||
"SF Pro Display", -apple-system, BlinkMacSystemFont, "PingFang SC",
|
"SF Pro Display", -apple-system, BlinkMacSystemFont, "PingFang SC",
|
||||||
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
"Hiragino Sans GB", "Microsoft YaHei", sans-serif;
|
||||||
background: var(--bg);
|
|
||||||
color: var(--text);
|
|
||||||
-webkit-font-smoothing: antialiased;
|
-webkit-font-smoothing: antialiased;
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
-webkit-touch-callout: none;
|
-webkit-touch-callout: none;
|
||||||
user-select: none;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Subtle ambient glow at top */
|
|
||||||
body::after {
|
body::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: fixed;
|
@apply fixed pointer-events-none z-0;
|
||||||
top: -30%;
|
top: -30%;
|
||||||
left: 50%;
|
left: 50%;
|
||||||
transform: translateX(-50%);
|
transform: translateX(-50%);
|
||||||
@@ -59,78 +43,149 @@ body::after {
|
|||||||
rgba(99, 102, 241, 0.04) 0%,
|
rgba(99, 102, 241, 0.04) 0%,
|
||||||
transparent 70%
|
transparent 70%
|
||||||
);
|
);
|
||||||
pointer-events: none;
|
}
|
||||||
z-index: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ─── App Container (safe-area padding) ─── */
|
||||||
#app {
|
#app {
|
||||||
position: relative;
|
padding: calc(16px + env(safe-area-inset-top, 0px)) 20px
|
||||||
z-index: 1;
|
calc(16px + env(safe-area-inset-bottom, 0px));
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
height: 100%;
|
|
||||||
max-width: 480px;
|
|
||||||
margin: 0 auto;
|
|
||||||
padding: calc(16px + var(--safe-top)) 20px calc(16px + var(--safe-bottom));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ─── Header ─── */
|
|
||||||
header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 8px 0 20px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
header h1 {
|
|
||||||
font-size: 22px;
|
|
||||||
font-weight: 700;
|
|
||||||
letter-spacing: -0.03em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 7px;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
color: var(--text-dim);
|
|
||||||
padding: 5px 12px;
|
|
||||||
border-radius: 20px;
|
|
||||||
background: var(--surface);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
transition: all 0.25s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.status .dot {
|
|
||||||
width: 7px;
|
|
||||||
height: 7px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background: var(--text-dim);
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ─── Connection Status States ─── */
|
||||||
.status.connected {
|
.status.connected {
|
||||||
border-color: rgba(52, 211, 153, 0.15);
|
@apply border-success/15;
|
||||||
}
|
}
|
||||||
|
|
||||||
.status.connected .dot {
|
.status.connected .dot {
|
||||||
background: var(--success);
|
@apply bg-success;
|
||||||
box-shadow: 0 0 6px rgba(52, 211, 153, 0.5);
|
box-shadow: 0 0 6px rgba(52, 211, 153, 0.5);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status.disconnected .dot {
|
.status.disconnected .dot {
|
||||||
background: var(--danger);
|
@apply bg-danger;
|
||||||
box-shadow: 0 0 6px rgba(244, 63, 94, 0.4);
|
box-shadow: 0 0 6px rgba(244, 63, 94, 0.4);
|
||||||
}
|
}
|
||||||
|
|
||||||
.status.connecting .dot {
|
.status.connecting .dot {
|
||||||
background: var(--accent);
|
@apply bg-accent;
|
||||||
animation: pulse 1.4s ease-in-out infinite;
|
animation: pulse 1.4s ease-in-out infinite;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ─── Preview Active State ─── */
|
||||||
|
.preview-box.active {
|
||||||
|
border-color: rgba(99, 102, 241, 0.4);
|
||||||
|
box-shadow:
|
||||||
|
0 0 0 1px rgba(99, 102, 241, 0.2),
|
||||||
|
0 4px 24px -4px rgba(99, 102, 241, 0.15);
|
||||||
|
background: linear-gradient(
|
||||||
|
180deg,
|
||||||
|
rgba(99, 102, 241, 0.03) 0%,
|
||||||
|
var(--color-surface) 100%
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Placeholder Text ─── */
|
||||||
|
#preview-text.placeholder {
|
||||||
|
@apply text-fg-dim;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Mic Button ─── */
|
||||||
|
#mic-btn {
|
||||||
|
background: linear-gradient(
|
||||||
|
145deg,
|
||||||
|
var(--color-surface-hover),
|
||||||
|
var(--color-surface)
|
||||||
|
);
|
||||||
|
box-shadow:
|
||||||
|
0 2px 12px rgba(0, 0, 0, 0.3),
|
||||||
|
inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
||||||
|
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
}
|
||||||
|
#mic-btn:disabled {
|
||||||
|
@apply opacity-30 cursor-not-allowed;
|
||||||
|
}
|
||||||
|
#mic-btn:not(:disabled):active,
|
||||||
|
#mic-btn.recording {
|
||||||
|
@apply bg-accent border-accent-hover text-white;
|
||||||
|
transform: scale(1.06);
|
||||||
|
box-shadow:
|
||||||
|
0 0 32px rgba(99, 102, 241, 0.35),
|
||||||
|
0 0 80px rgba(99, 102, 241, 0.2);
|
||||||
|
}
|
||||||
|
#mic-btn.recording {
|
||||||
|
animation: mic-breathe 1.8s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Wave Rings ─── */
|
||||||
|
#mic-btn.recording + .mic-rings .ring {
|
||||||
|
animation: ring-expand 2.4s cubic-bezier(0.2, 0, 0.2, 1) infinite;
|
||||||
|
}
|
||||||
|
#mic-btn.recording + .mic-rings .ring:nth-child(2) {
|
||||||
|
animation-delay: 0.8s;
|
||||||
|
}
|
||||||
|
#mic-btn.recording + .mic-rings .ring:nth-child(3) {
|
||||||
|
animation-delay: 1.6s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── History Items (dynamically created) ─── */
|
||||||
|
#history-list li {
|
||||||
|
@apply bg-surface border border-edge rounded-card cursor-pointer flex items-start gap-3 transition-all duration-150;
|
||||||
|
padding: 14px 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.5;
|
||||||
|
animation: slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1) both;
|
||||||
|
animation-delay: calc(var(--i, 0) * 40ms);
|
||||||
|
}
|
||||||
|
#history-list li:active {
|
||||||
|
@apply bg-surface-active border-edge-active;
|
||||||
|
transform: scale(0.985);
|
||||||
|
}
|
||||||
|
#history-list li .hist-text {
|
||||||
|
@apply flex-1 break-words;
|
||||||
|
}
|
||||||
|
#history-list li .hist-time {
|
||||||
|
@apply text-fg-dim whitespace-nowrap shrink-0;
|
||||||
|
font-size: 11px;
|
||||||
|
padding-top: 2px;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Text Button Active State ─── */
|
||||||
|
.text-btn:active {
|
||||||
|
@apply text-danger;
|
||||||
|
background: rgba(244, 63, 94, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Scrollbar ─── */
|
||||||
|
.preview-box::-webkit-scrollbar,
|
||||||
|
#history-list::-webkit-scrollbar {
|
||||||
|
width: 3px;
|
||||||
|
}
|
||||||
|
.preview-box::-webkit-scrollbar-track,
|
||||||
|
#history-list::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
.preview-box::-webkit-scrollbar-thumb,
|
||||||
|
#history-list::-webkit-scrollbar-thumb {
|
||||||
|
@apply bg-edge;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Toast ─── */
|
||||||
|
.toast {
|
||||||
|
bottom: calc(80px + env(safe-area-inset-bottom, 0px));
|
||||||
|
transform: translateX(-50%) translateY(8px);
|
||||||
|
background: rgba(28, 28, 37, 0.85);
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
-webkit-backdrop-filter: blur(16px);
|
||||||
|
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
||||||
|
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||||
|
}
|
||||||
|
.toast.show {
|
||||||
|
@apply opacity-100;
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Keyframes ─── */
|
||||||
@keyframes pulse {
|
@keyframes pulse {
|
||||||
0%,
|
0%,
|
||||||
100% {
|
100% {
|
||||||
@@ -141,153 +196,21 @@ header h1 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ─── Preview ─── */
|
|
||||||
#preview-section {
|
|
||||||
flex-shrink: 0;
|
|
||||||
padding-bottom: 12px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-box {
|
|
||||||
background: var(--surface);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
padding: 16px 18px;
|
|
||||||
min-height: 80px;
|
|
||||||
max-height: 160px;
|
|
||||||
overflow-y: auto;
|
|
||||||
transition:
|
|
||||||
border-color 0.3s ease,
|
|
||||||
box-shadow 0.3s ease,
|
|
||||||
background 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-box.active {
|
|
||||||
border-color: rgba(99, 102, 241, 0.4);
|
|
||||||
box-shadow:
|
|
||||||
0 0 0 1px var(--accent-glow),
|
|
||||||
0 4px 24px -4px rgba(99, 102, 241, 0.15);
|
|
||||||
background: linear-gradient(
|
|
||||||
180deg,
|
|
||||||
rgba(99, 102, 241, 0.03) 0%,
|
|
||||||
var(--surface) 100%
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#preview-text {
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.6;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
#preview-text.placeholder {
|
|
||||||
color: var(--text-dim);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ─── Mic Button ─── */
|
|
||||||
#mic-section {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
padding: 20px 0 16px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
gap: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mic-wrapper {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
touch-action: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mic-btn {
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
width: 96px;
|
|
||||||
height: 96px;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 2px solid var(--border);
|
|
||||||
background: linear-gradient(145deg, var(--surface-hover), var(--surface));
|
|
||||||
color: var(--text-secondary);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
|
|
||||||
-webkit-user-select: none;
|
|
||||||
touch-action: none;
|
|
||||||
box-shadow:
|
|
||||||
0 2px 12px rgba(0, 0, 0, 0.3),
|
|
||||||
inset 0 1px 0 rgba(255, 255, 255, 0.04);
|
|
||||||
}
|
|
||||||
|
|
||||||
#mic-btn svg {
|
|
||||||
transition: transform 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mic-btn:disabled {
|
|
||||||
opacity: 0.3;
|
|
||||||
cursor: not-allowed;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mic-btn:not(:disabled):active,
|
|
||||||
#mic-btn.recording {
|
|
||||||
background: var(--accent);
|
|
||||||
border-color: var(--accent-hover);
|
|
||||||
color: #fff;
|
|
||||||
transform: scale(1.06);
|
|
||||||
box-shadow:
|
|
||||||
0 0 32px var(--accent-glow-md),
|
|
||||||
0 0 80px var(--accent-glow);
|
|
||||||
}
|
|
||||||
|
|
||||||
#mic-btn.recording {
|
|
||||||
animation: mic-breathe 1.8s ease-in-out infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes mic-breathe {
|
@keyframes mic-breathe {
|
||||||
0%,
|
0%,
|
||||||
100% {
|
100% {
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 0 32px var(--accent-glow-md),
|
0 0 32px rgba(99, 102, 241, 0.35),
|
||||||
0 0 80px var(--accent-glow);
|
0 0 80px rgba(99, 102, 241, 0.2);
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 0 48px var(--accent-glow-md),
|
0 0 48px rgba(99, 102, 241, 0.35),
|
||||||
0 0 120px var(--accent-glow),
|
0 0 120px rgba(99, 102, 241, 0.2),
|
||||||
0 0 200px var(--accent-glow-lg);
|
0 0 200px rgba(99, 102, 241, 0.08);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Wave rings — radiate outward when recording */
|
|
||||||
.mic-rings {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.mic-rings .ring {
|
|
||||||
position: absolute;
|
|
||||||
inset: 0;
|
|
||||||
border-radius: 50%;
|
|
||||||
border: 1.5px solid var(--accent);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mic-btn.recording + .mic-rings .ring {
|
|
||||||
animation: ring-expand 2.4s cubic-bezier(0.2, 0, 0.2, 1) infinite;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mic-btn.recording + .mic-rings .ring:nth-child(2) {
|
|
||||||
animation-delay: 0.8s;
|
|
||||||
}
|
|
||||||
|
|
||||||
#mic-btn.recording + .mic-rings .ring:nth-child(3) {
|
|
||||||
animation-delay: 1.6s;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes ring-expand {
|
@keyframes ring-expand {
|
||||||
0% {
|
0% {
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
@@ -299,99 +222,6 @@ header h1 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.mic-hint {
|
|
||||||
font-size: 13px;
|
|
||||||
color: var(--text-dim);
|
|
||||||
letter-spacing: 0.01em;
|
|
||||||
transition: color 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ─── History ─── */
|
|
||||||
#history-section {
|
|
||||||
flex: 1;
|
|
||||||
min-height: 0;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.history-header h2 {
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--text-dim);
|
|
||||||
text-transform: uppercase;
|
|
||||||
letter-spacing: 0.06em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-btn {
|
|
||||||
background: none;
|
|
||||||
border: none;
|
|
||||||
color: var(--text-dim);
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 500;
|
|
||||||
cursor: pointer;
|
|
||||||
padding: 4px 10px;
|
|
||||||
border-radius: var(--radius-sm);
|
|
||||||
transition: all 0.15s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.text-btn:active {
|
|
||||||
color: var(--danger);
|
|
||||||
background: rgba(244, 63, 94, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
#history-list {
|
|
||||||
list-style: none;
|
|
||||||
flex: 1;
|
|
||||||
overflow-y: auto;
|
|
||||||
-webkit-overflow-scrolling: touch;
|
|
||||||
}
|
|
||||||
|
|
||||||
#history-list li {
|
|
||||||
background: var(--surface);
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
border-radius: var(--radius);
|
|
||||||
padding: 14px 16px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
font-size: 14px;
|
|
||||||
line-height: 1.5;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.15s ease;
|
|
||||||
display: flex;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 12px;
|
|
||||||
animation: slide-up 0.35s cubic-bezier(0.16, 1, 0.3, 1) both;
|
|
||||||
animation-delay: calc(var(--i, 0) * 40ms);
|
|
||||||
}
|
|
||||||
|
|
||||||
#history-list li:active {
|
|
||||||
background: var(--surface-active);
|
|
||||||
border-color: var(--border-active);
|
|
||||||
transform: scale(0.985);
|
|
||||||
}
|
|
||||||
|
|
||||||
#history-list li .hist-text {
|
|
||||||
flex: 1;
|
|
||||||
word-break: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
#history-list li .hist-time {
|
|
||||||
font-size: 11px;
|
|
||||||
color: var(--text-dim);
|
|
||||||
white-space: nowrap;
|
|
||||||
flex-shrink: 0;
|
|
||||||
padding-top: 2px;
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slide-up {
|
@keyframes slide-up {
|
||||||
from {
|
from {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
@@ -402,57 +232,3 @@ header h1 {
|
|||||||
transform: translateY(0);
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#history-empty {
|
|
||||||
text-align: center;
|
|
||||||
padding: 40px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.placeholder {
|
|
||||||
color: var(--text-dim);
|
|
||||||
font-size: 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ─── Scrollbar ─── */
|
|
||||||
.preview-box::-webkit-scrollbar,
|
|
||||||
#history-list::-webkit-scrollbar {
|
|
||||||
width: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-box::-webkit-scrollbar-track,
|
|
||||||
#history-list::-webkit-scrollbar-track {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.preview-box::-webkit-scrollbar-thumb,
|
|
||||||
#history-list::-webkit-scrollbar-thumb {
|
|
||||||
background: var(--border);
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ─── Toast ─── */
|
|
||||||
.toast {
|
|
||||||
position: fixed;
|
|
||||||
bottom: calc(80px + var(--safe-bottom, 0px));
|
|
||||||
left: 50%;
|
|
||||||
transform: translateX(-50%) translateY(8px);
|
|
||||||
background: rgba(28, 28, 37, 0.85);
|
|
||||||
color: var(--text);
|
|
||||||
padding: 10px 22px;
|
|
||||||
border-radius: 24px;
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 500;
|
|
||||||
z-index: 999;
|
|
||||||
opacity: 0;
|
|
||||||
transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
||||||
pointer-events: none;
|
|
||||||
border: 1px solid var(--border);
|
|
||||||
backdrop-filter: blur(16px);
|
|
||||||
-webkit-backdrop-filter: blur(16px);
|
|
||||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.toast.show {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateX(-50%) translateY(0);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
import tailwindcss from "@tailwindcss/vite";
|
||||||
import { defineConfig } from "vite";
|
import { defineConfig } from "vite";
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
plugins: [tailwindcss()],
|
||||||
root: ".",
|
root: ".",
|
||||||
build: {
|
build: {
|
||||||
outDir: "dist",
|
outDir: "dist",
|
||||||
|
|||||||
Reference in New Issue
Block a user