mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-10-30 09:16:55 +00:00
feat(studio): upgrade Studio to Tailwind CSS v4 (#3618)
* chore(studio): upgrade Tailwind CSS to v4 through the Vite plugin Studio moves from Tailwind 3.4 to 4.3.3, loaded by @tailwindcss/vite instead of PostCSS. The JS config becomes an @theme block in studio.css carrying the same studio.* and panel.* colors, with explicit @source globs so the gitignored data/projects tree is never scanned. The official upgrade tool renamed the utilities, but it rewrites bare words inside any string, so its edits to event names, CSS property names, filter function names, status enum values and prose were reverted by hand. Nothing in Studio relied on the v3 default border color, ring width or ring color, so the tool's compatibility shim was dropped rather than kept. * chore(studio): note the duplicated palette between theme css and preset * fix(studio): keep the pointer cursor on buttons after the Tailwind v4 upgrade v4 preflight dropped the pointer cursor on enabled buttons, so add it back in one base rule. Also migrate the last outline-none to outline-hidden. * fix(studio): keep the Renders Export button at its shipped 14px size * chore(studio): allow the tailwind v4 config deletions in the deletion guard
This commit is contained in:
@@ -24,7 +24,7 @@
|
||||
},
|
||||
"packages/aws-lambda": {
|
||||
"name": "@hyperframes/aws-lambda",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.48",
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "^3.700.0",
|
||||
"@aws-sdk/client-sfn": "^3.700.0",
|
||||
@@ -57,7 +57,7 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@hyperframes/cli",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.48",
|
||||
"bin": {
|
||||
"hyperframes": "./bin/hyperframes.mjs",
|
||||
"hyperframes-localize-fonts": "./bin/hyperframes-localize-fonts.mjs",
|
||||
@@ -111,7 +111,7 @@
|
||||
},
|
||||
"packages/core": {
|
||||
"name": "@hyperframes/core",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.48",
|
||||
"dependencies": {
|
||||
"@chenglou/pretext": "^0.0.5",
|
||||
"@hyperframes/lint": "workspace:*",
|
||||
@@ -136,7 +136,7 @@
|
||||
},
|
||||
"packages/engine": {
|
||||
"name": "@hyperframes/engine",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.48",
|
||||
"dependencies": {
|
||||
"@hono/node-server": "^2.0.5",
|
||||
"@hyperframes/core": "workspace:^",
|
||||
@@ -155,7 +155,7 @@
|
||||
},
|
||||
"packages/gcp-cloud-run": {
|
||||
"name": "@hyperframes/gcp-cloud-run",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.48",
|
||||
"dependencies": {
|
||||
"@google-cloud/storage": "^7.14.0",
|
||||
"@google-cloud/workflows": "^4.2.0",
|
||||
@@ -176,7 +176,7 @@
|
||||
},
|
||||
"packages/lint": {
|
||||
"name": "@hyperframes/lint",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.48",
|
||||
"dependencies": {
|
||||
"@hyperframes/parsers": "workspace:*",
|
||||
"htmlparser2": "^10.1.0",
|
||||
@@ -194,7 +194,7 @@
|
||||
},
|
||||
"packages/parsers": {
|
||||
"name": "@hyperframes/parsers",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.48",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.27.0",
|
||||
"acorn": "^8.17.0",
|
||||
@@ -214,7 +214,7 @@
|
||||
},
|
||||
"packages/player": {
|
||||
"name": "@hyperframes/player",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.48",
|
||||
"dependencies": {
|
||||
"@hyperframes/core": "workspace:*",
|
||||
},
|
||||
@@ -229,7 +229,7 @@
|
||||
},
|
||||
"packages/producer": {
|
||||
"name": "@hyperframes/producer",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.48",
|
||||
"dependencies": {
|
||||
"@fontsource/archivo-black": "^5.2.8",
|
||||
"@fontsource/eb-garamond": "^5.2.7",
|
||||
@@ -274,7 +274,7 @@
|
||||
},
|
||||
"packages/sdk": {
|
||||
"name": "@hyperframes/sdk",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.48",
|
||||
"dependencies": {
|
||||
"@hyperframes/core": "workspace:*",
|
||||
"@hyperframes/parsers": "workspace:*",
|
||||
@@ -304,7 +304,7 @@
|
||||
},
|
||||
"packages/shader-transitions": {
|
||||
"name": "@hyperframes/shader-transitions",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.48",
|
||||
"dependencies": {
|
||||
"html2canvas": "^1.4.1",
|
||||
},
|
||||
@@ -316,7 +316,7 @@
|
||||
},
|
||||
"packages/studio": {
|
||||
"name": "@hyperframes/studio",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.48",
|
||||
"dependencies": {
|
||||
"@codemirror/autocomplete": "^6.20.1",
|
||||
"@codemirror/commands": "^6.10.3",
|
||||
@@ -343,15 +343,14 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hyperframes/producer": "workspace:*",
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@types/react": "19",
|
||||
"@types/react-dom": "19",
|
||||
"@vitejs/plugin-react": "^4.0.0",
|
||||
"autoprefixer": "^10.4.0",
|
||||
"chokidar": "^4.0.3",
|
||||
"fake-indexeddb": "^6.2.5",
|
||||
"postcss": "^8.4.0",
|
||||
"puppeteer-core": "^25.2.1",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"tsup": "^8.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^6.4.2",
|
||||
@@ -366,7 +365,7 @@
|
||||
},
|
||||
"packages/studio-server": {
|
||||
"name": "@hyperframes/studio-server",
|
||||
"version": "0.8.33",
|
||||
"version": "0.8.48",
|
||||
"dependencies": {
|
||||
"@hyperframes/core": "workspace:*",
|
||||
"@hyperframes/parsers": "workspace:*",
|
||||
@@ -391,8 +390,6 @@
|
||||
"react-dom": "^19.0.0",
|
||||
},
|
||||
"packages": {
|
||||
"@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="],
|
||||
|
||||
"@asamuzakjp/css-color": ["@asamuzakjp/css-color@5.1.11", "", { "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", "@csstools/css-calc": "^3.2.0", "@csstools/css-color-parser": "^4.1.0", "@csstools/css-parser-algorithms": "^4.0.0", "@csstools/css-tokenizer": "^4.0.0" } }, "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg=="],
|
||||
|
||||
"@asamuzakjp/dom-selector": ["@asamuzakjp/dom-selector@7.1.1", "", { "dependencies": { "@asamuzakjp/generational-cache": "^1.0.1", "@asamuzakjp/nwsapi": "^2.3.9", "bidi-js": "^1.0.3", "css-tree": "^3.2.1", "is-potential-custom-element-name": "^1.0.1" } }, "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ=="],
|
||||
@@ -861,12 +858,6 @@
|
||||
|
||||
"@nodable/entities": ["@nodable/entities@2.1.0", "", {}, "sha512-nyT7T3nbMyBI/lvr6L5TyWbFJAI9FTgVRakNoBqCD+PmID8DzFrrNdLLtHMwMszOtqZa8PAOV24ZqDnQrhQINA=="],
|
||||
|
||||
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
|
||||
|
||||
"@nodelib/fs.stat": ["@nodelib/fs.stat@2.0.5", "", {}, "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A=="],
|
||||
|
||||
"@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="],
|
||||
|
||||
"@oxc-parser/binding-android-arm-eabi": ["@oxc-parser/binding-android-arm-eabi@0.128.0", "", { "os": "android", "cpu": "arm" }, "sha512-aca6ZvzmCBUGOANQRiRQRZuRKYI3ENhcit6GisnknOOmcezfQc7xJ4dxlPU7MV7mOvrC7RNR1u3LAD7xyaiCxA=="],
|
||||
|
||||
"@oxc-parser/binding-android-arm64": ["@oxc-parser/binding-android-arm64@0.128.0", "", { "os": "android", "cpu": "arm64" }, "sha512-BbeDmuohoJ7Rz/it5wnkj69i/OsCPS3Z51nLEzwO/Y6YshtC4JU+15oNwhY8v4LRKRYclRc7ggOikwrsJ/eOEQ=="],
|
||||
@@ -1131,6 +1122,36 @@
|
||||
|
||||
"@swc/helpers": ["@swc/helpers@0.5.21", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg=="],
|
||||
|
||||
"@tailwindcss/node": ["@tailwindcss/node@4.3.3", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.24.1", "jiti": "^2.7.0", "lightningcss": "1.32.0", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.3.3" } }, "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg=="],
|
||||
|
||||
"@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.3", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.3", "@tailwindcss/oxide-darwin-arm64": "4.3.3", "@tailwindcss/oxide-darwin-x64": "4.3.3", "@tailwindcss/oxide-freebsd-x64": "4.3.3", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", "@tailwindcss/oxide-linux-x64-musl": "4.3.3", "@tailwindcss/oxide-wasm32-wasi": "4.3.3", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" } }, "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA=="],
|
||||
|
||||
"@tailwindcss/oxide-android-arm64": ["@tailwindcss/oxide-android-arm64@4.3.3", "", { "os": "android", "cpu": "arm64" }, "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw=="],
|
||||
|
||||
"@tailwindcss/oxide-darwin-arm64": ["@tailwindcss/oxide-darwin-arm64@4.3.3", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw=="],
|
||||
|
||||
"@tailwindcss/oxide-darwin-x64": ["@tailwindcss/oxide-darwin-x64@4.3.3", "", { "os": "darwin", "cpu": "x64" }, "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw=="],
|
||||
|
||||
"@tailwindcss/oxide-freebsd-x64": ["@tailwindcss/oxide-freebsd-x64@4.3.3", "", { "os": "freebsd", "cpu": "x64" }, "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm-gnueabihf": ["@tailwindcss/oxide-linux-arm-gnueabihf@4.3.3", "", { "os": "linux", "cpu": "arm" }, "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-gnu": ["@tailwindcss/oxide-linux-arm64-gnu@4.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-arm64-musl": ["@tailwindcss/oxide-linux-arm64-musl@4.3.3", "", { "os": "linux", "cpu": "arm64" }, "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-gnu": ["@tailwindcss/oxide-linux-x64-gnu@4.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w=="],
|
||||
|
||||
"@tailwindcss/oxide-linux-x64-musl": ["@tailwindcss/oxide-linux-x64-musl@4.3.3", "", { "os": "linux", "cpu": "x64" }, "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi": ["@tailwindcss/oxide-wasm32-wasi@4.3.3", "", { "dependencies": { "@emnapi/core": "^1.11.1", "@emnapi/runtime": "^1.11.1", "@emnapi/wasi-threads": "^1.2.2", "@napi-rs/wasm-runtime": "^1.1.4", "@tybys/wasm-util": "^0.10.2", "tslib": "^2.8.1" }, "cpu": "none" }, "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ=="],
|
||||
|
||||
"@tailwindcss/oxide-win32-arm64-msvc": ["@tailwindcss/oxide-win32-arm64-msvc@4.3.3", "", { "os": "win32", "cpu": "arm64" }, "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ=="],
|
||||
|
||||
"@tailwindcss/oxide-win32-x64-msvc": ["@tailwindcss/oxide-win32-x64-msvc@4.3.3", "", { "os": "win32", "cpu": "x64" }, "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw=="],
|
||||
|
||||
"@tailwindcss/vite": ["@tailwindcss/vite@4.3.3", "", { "dependencies": { "@tailwindcss/node": "4.3.3", "@tailwindcss/oxide": "4.3.3", "tailwindcss": "4.3.3" }, "peerDependencies": { "vite": "^5.2.0 || ^6 || ^7 || ^8" } }, "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw=="],
|
||||
|
||||
"@tanstack/react-virtual": ["@tanstack/react-virtual@3.14.6", "", { "dependencies": { "@tanstack/virtual-core": "3.17.4" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-4+Uq8m0/gzO4kMCHUEpTtGX1RnONK0C+g88b2ltwPMWUBiaVarBuWKoPJaz7gj1cKCVRAdyu+U8GcKhwCc2beA=="],
|
||||
|
||||
"@tanstack/virtual-core": ["@tanstack/virtual-core@3.17.4", "", {}, "sha512-nGm5KteqxasUdThLc2izl6dHUqLv0LQj7Nuyo5gYalTPf/U8a9ermvsl7reT+6ioBW1l8WfpP/mcU338nLXpqw=="],
|
||||
@@ -1235,10 +1256,6 @@
|
||||
|
||||
"any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="],
|
||||
|
||||
"anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="],
|
||||
|
||||
"arg": ["arg@5.0.2", "", {}, "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg=="],
|
||||
|
||||
"argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="],
|
||||
|
||||
"array-ify": ["array-ify@1.0.0", "", {}, "sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng=="],
|
||||
@@ -1257,8 +1274,6 @@
|
||||
|
||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
|
||||
"autoprefixer": ["autoprefixer@10.5.0", "", { "dependencies": { "browserslist": "^4.28.2", "caniuse-lite": "^1.0.30001787", "fraction.js": "^5.3.4", "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "peerDependencies": { "postcss": "^8.1.0" }, "bin": { "autoprefixer": "bin/autoprefixer" } }, "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong=="],
|
||||
|
||||
"aws-cdk-lib": ["aws-cdk-lib@2.254.0", "", { "dependencies": { "@aws-cdk/asset-awscli-v1": "2.2.273", "@aws-cdk/asset-node-proxy-agent-v6": "^2.1.1", "@aws-cdk/cloud-assembly-api": "^2.2.3", "@aws-cdk/cloud-assembly-schema": "^53.21.0", "@balena/dockerignore": "^1.0.2", "case": "1.6.3", "fs-extra": "^11.3.3", "ignore": "^5.3.2", "jsonschema": "^1.5.0", "mime-types": "^2.1.35", "minimatch": "^10.2.3", "punycode": "^2.3.1", "semver": "^7.7.4", "table": "^6.9.0", "yaml": "1.10.3" }, "peerDependencies": { "constructs": "^10.5.0" } }, "sha512-O7fn6fu1FXb0BgoO/+WeiBXZ16H/q6z4fOndjdG62c9bPU7Z/UeW5gz7qBiwLm4ERvTObobLCqv7wjd8bp+v3A=="],
|
||||
|
||||
"b4a": ["b4a@1.8.1", "", { "peerDependencies": { "react-native-b4a": "*" }, "optionalPeers": ["react-native-b4a"] }, "sha512-aiqre1Nr0B/6DgE2N5vwTc+2/oQZ4Wh1t4NznYY4E00y8LCt6NqdRv81so00oo27D8MVKTpUa/MwUUtBLXCoDw=="],
|
||||
@@ -1287,8 +1302,6 @@
|
||||
|
||||
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
|
||||
|
||||
"binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="],
|
||||
|
||||
"boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="],
|
||||
|
||||
"boolean": ["boolean@3.2.0", "", {}, "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw=="],
|
||||
@@ -1299,8 +1312,6 @@
|
||||
|
||||
"brace-expansion": ["brace-expansion@5.0.6", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g=="],
|
||||
|
||||
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||
|
||||
"brotli": ["brotli@1.3.3", "", { "dependencies": { "base64-js": "^1.1.2" } }, "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg=="],
|
||||
|
||||
"browserslist": ["browserslist@4.28.2", "", { "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", "electron-to-chromium": "^1.5.328", "node-releases": "^2.0.36", "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" } }, "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg=="],
|
||||
@@ -1321,8 +1332,6 @@
|
||||
|
||||
"callsites": ["callsites@3.1.0", "", {}, "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="],
|
||||
|
||||
"camelcase-css": ["camelcase-css@2.0.1", "", {}, "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA=="],
|
||||
|
||||
"caniuse-lite": ["caniuse-lite@1.0.30001792", "", {}, "sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw=="],
|
||||
|
||||
"case": ["case@1.6.3", "", {}, "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ=="],
|
||||
@@ -1427,10 +1436,6 @@
|
||||
|
||||
"dfa": ["dfa@1.2.0", "", {}, "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q=="],
|
||||
|
||||
"didyoumean": ["didyoumean@1.2.2", "", {}, "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw=="],
|
||||
|
||||
"dlv": ["dlv@1.1.3", "", {}, "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA=="],
|
||||
|
||||
"dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="],
|
||||
|
||||
"domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="],
|
||||
@@ -1455,6 +1460,8 @@
|
||||
|
||||
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
|
||||
|
||||
"enhanced-resolve": ["enhanced-resolve@5.24.5", "", { "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.3.3" } }, "sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A=="],
|
||||
|
||||
"entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="],
|
||||
|
||||
"env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="],
|
||||
@@ -1501,8 +1508,6 @@
|
||||
|
||||
"fast-fifo": ["fast-fifo@1.3.2", "", {}, "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ=="],
|
||||
|
||||
"fast-glob": ["fast-glob@3.3.3", "", { "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" } }, "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg=="],
|
||||
|
||||
"fast-string-truncated-width": ["fast-string-truncated-width@3.0.3", "", {}, "sha512-0jjjIEL6+0jag3l2XWWizO64/aZVtpiGE3t0Zgqxv0DPuxiMjvB3M24fCyhZUO4KomJQPj3LTSUnDP3GpdwC0g=="],
|
||||
|
||||
"fast-string-width": ["fast-string-width@3.0.2", "", { "dependencies": { "fast-string-truncated-width": "^3.0.2" } }, "sha512-gX8LrtNEI5hq8DVUfRQMbr5lpaS4nMIWV+7XEbXk2b8kiQIizgnlr12B4dA3ZEx3308ze0O4Q1R+cHts8kyUJg=="],
|
||||
@@ -1515,8 +1520,6 @@
|
||||
|
||||
"fast-xml-parser": ["fast-xml-parser@5.7.3", "", { "dependencies": { "@nodable/entities": "^2.1.0", "fast-xml-builder": "^1.1.7", "path-expression-matcher": "^1.5.0", "strnum": "^2.2.3" }, "bin": { "fxparser": "src/cli/cli.js" } }, "sha512-C0AaNuC+mscy6vrAQKAc/rMq+zAPHodfHGZu4sGVehvAQt/JLG1O5zEcYcXSY5zSqr4YVgxsB+pHXTq0i7eDlg=="],
|
||||
|
||||
"fastq": ["fastq@1.20.1", "", { "dependencies": { "reusify": "^1.0.4" } }, "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw=="],
|
||||
|
||||
"fd-package-json": ["fd-package-json@2.0.0", "", { "dependencies": { "walk-up-path": "^4.0.0" } }, "sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ=="],
|
||||
|
||||
"fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="],
|
||||
@@ -1527,8 +1530,6 @@
|
||||
|
||||
"ffprobe-static": ["ffprobe-static@3.1.0", "", {}, "sha512-Dvpa9uhVMOYivhHKWLGDoa512J751qN1WZAIO+Xw4L/mrUSPxS4DApzSUDbCFE/LUq2+xYnznEahTd63AqBSpA=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
|
||||
"fix-dts-default-cjs-exports": ["fix-dts-default-cjs-exports@1.0.1", "", { "dependencies": { "magic-string": "^0.30.17", "mlly": "^1.7.4", "rollup": "^4.34.8" } }, "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg=="],
|
||||
|
||||
"fontkit": ["fontkit@2.0.4", "", { "dependencies": { "@swc/helpers": "^0.5.12", "brotli": "^1.3.2", "clone": "^2.1.2", "dfa": "^1.2.0", "fast-deep-equal": "^3.1.3", "restructure": "^3.0.0", "tiny-inflate": "^1.0.3", "unicode-properties": "^1.4.0", "unicode-trie": "^2.0.0" } }, "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g=="],
|
||||
@@ -1541,8 +1542,6 @@
|
||||
|
||||
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
||||
|
||||
"fraction.js": ["fraction.js@5.3.4", "", {}, "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ=="],
|
||||
|
||||
"fs-extra": ["fs-extra@11.3.5", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg=="],
|
||||
|
||||
"fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="],
|
||||
@@ -1571,8 +1570,6 @@
|
||||
|
||||
"glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="],
|
||||
|
||||
"glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="],
|
||||
|
||||
"global-agent": ["global-agent@3.0.0", "", { "dependencies": { "boolean": "^3.0.1", "es6-error": "^4.1.1", "matcher": "^3.0.0", "roarr": "^2.15.3", "semver": "^7.3.2", "serialize-error": "^7.0.1" } }, "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q=="],
|
||||
|
||||
"global-directory": ["global-directory@5.0.0", "", { "dependencies": { "ini": "6.0.0" } }, "sha512-1pgFdhK3J2LeM+dVf2Pd424yHx2ou338lC0ErNP2hPx4j8eW1Sp0XqSjNxtk6Tc4Kr5wlWtSvz8cn2yb7/SG/w=="],
|
||||
@@ -1635,22 +1632,12 @@
|
||||
|
||||
"is-arrayish": ["is-arrayish@0.2.1", "", {}, "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg=="],
|
||||
|
||||
"is-binary-path": ["is-binary-path@2.1.0", "", { "dependencies": { "binary-extensions": "^2.0.0" } }, "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw=="],
|
||||
|
||||
"is-core-module": ["is-core-module@2.16.2", "", { "dependencies": { "hasown": "^2.0.3" } }, "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA=="],
|
||||
|
||||
"is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
|
||||
|
||||
"is-extglob": ["is-extglob@2.1.1", "", {}, "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ=="],
|
||||
|
||||
"is-fullwidth-code-point": ["is-fullwidth-code-point@3.0.0", "", {}, "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg=="],
|
||||
|
||||
"is-glob": ["is-glob@4.0.3", "", { "dependencies": { "is-extglob": "^2.1.1" } }, "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="],
|
||||
|
||||
"is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
|
||||
|
||||
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
||||
|
||||
"is-obj": ["is-obj@2.0.0", "", {}, "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="],
|
||||
|
||||
"is-plain-obj": ["is-plain-obj@4.1.0", "", {}, "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg=="],
|
||||
@@ -1725,6 +1712,30 @@
|
||||
|
||||
"lefthook-windows-x64": ["lefthook-windows-x64@2.1.6", "", { "os": "win32", "cpu": "x64" }, "sha512-q4z2n3xucLscoWiyMwFViEj3N8MDSkPulMwcJYuCYFHoPhP1h+icqNu7QRLGYj6AnVrCQweiUJY3Tb2X+GbD/A=="],
|
||||
|
||||
"lightningcss": ["lightningcss@1.32.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ=="],
|
||||
|
||||
"lightningcss-android-arm64": ["lightningcss-android-arm64@1.32.0", "", { "os": "android", "cpu": "arm64" }, "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg=="],
|
||||
|
||||
"lightningcss-darwin-arm64": ["lightningcss-darwin-arm64@1.32.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ=="],
|
||||
|
||||
"lightningcss-darwin-x64": ["lightningcss-darwin-x64@1.32.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w=="],
|
||||
|
||||
"lightningcss-freebsd-x64": ["lightningcss-freebsd-x64@1.32.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig=="],
|
||||
|
||||
"lightningcss-linux-arm-gnueabihf": ["lightningcss-linux-arm-gnueabihf@1.32.0", "", { "os": "linux", "cpu": "arm" }, "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw=="],
|
||||
|
||||
"lightningcss-linux-arm64-gnu": ["lightningcss-linux-arm64-gnu@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ=="],
|
||||
|
||||
"lightningcss-linux-arm64-musl": ["lightningcss-linux-arm64-musl@1.32.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg=="],
|
||||
|
||||
"lightningcss-linux-x64-gnu": ["lightningcss-linux-x64-gnu@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA=="],
|
||||
|
||||
"lightningcss-linux-x64-musl": ["lightningcss-linux-x64-musl@1.32.0", "", { "os": "linux", "cpu": "x64" }, "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg=="],
|
||||
|
||||
"lightningcss-win32-arm64-msvc": ["lightningcss-win32-arm64-msvc@1.32.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw=="],
|
||||
|
||||
"lightningcss-win32-x64-msvc": ["lightningcss-win32-x64-msvc@1.32.0", "", { "os": "win32", "cpu": "x64" }, "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q=="],
|
||||
|
||||
"lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="],
|
||||
|
||||
"lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="],
|
||||
@@ -1745,7 +1756,7 @@
|
||||
|
||||
"magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="],
|
||||
|
||||
"magicast": ["magicast@0.5.4", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "source-map-js": "^1.2.1" } }, "sha512-llBEhWm1SacoRwgHUoQJYtwp4PBLF4faQi5TCpIGyGs9n4y5+juI0tDgyKIfpqxckRHaHzouUEph3THklWh03w=="],
|
||||
"magicast": ["magicast@0.5.5", "", { "dependencies": { "@babel/parser": "^7.29.7", "@babel/types": "^7.29.7", "source-map-js": "^1.2.1" } }, "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA=="],
|
||||
|
||||
"make-dir": ["make-dir@4.0.0", "", { "dependencies": { "semver": "^7.5.3" } }, "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw=="],
|
||||
|
||||
@@ -1759,10 +1770,6 @@
|
||||
|
||||
"meow": ["meow@13.2.0", "", {}, "sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA=="],
|
||||
|
||||
"merge2": ["merge2@1.4.1", "", {}, "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg=="],
|
||||
|
||||
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||
|
||||
"mime": ["mime@3.0.0", "", { "bin": { "mime": "cli.js" } }, "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A=="],
|
||||
|
||||
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
@@ -1787,7 +1794,7 @@
|
||||
|
||||
"mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="],
|
||||
|
||||
"nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
|
||||
"nanoid": ["nanoid@3.3.18", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w=="],
|
||||
|
||||
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
||||
|
||||
@@ -1795,8 +1802,6 @@
|
||||
|
||||
"node-releases": ["node-releases@2.0.44", "", {}, "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ=="],
|
||||
|
||||
"normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="],
|
||||
|
||||
"nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="],
|
||||
|
||||
"object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="],
|
||||
@@ -1843,8 +1848,6 @@
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="],
|
||||
|
||||
"path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="],
|
||||
|
||||
"pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="],
|
||||
@@ -1853,26 +1856,16 @@
|
||||
|
||||
"picomatch": ["picomatch@4.0.4", "", {}, "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A=="],
|
||||
|
||||
"pify": ["pify@2.3.0", "", {}, "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog=="],
|
||||
|
||||
"pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="],
|
||||
|
||||
"pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="],
|
||||
|
||||
"postcss": ["postcss@8.5.14", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="],
|
||||
|
||||
"postcss-import": ["postcss-import@15.1.0", "", { "dependencies": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", "resolve": "^1.1.7" }, "peerDependencies": { "postcss": "^8.0.0" } }, "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew=="],
|
||||
|
||||
"postcss-js": ["postcss-js@4.1.0", "", { "dependencies": { "camelcase-css": "^2.0.1" }, "peerDependencies": { "postcss": "^8.4.21" } }, "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw=="],
|
||||
"postcss": ["postcss@8.5.28", "", { "dependencies": { "nanoid": "^3.3.18", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-RRuzqDtt5Y9h3quz5hWhK+TPnsmVs6WwSU6LkJMeY4HstUEDuYTG8UJSdawMRzmzAtV+KEoG8N3Qg2qLy5vM/A=="],
|
||||
|
||||
"postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "postcss", "tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="],
|
||||
|
||||
"postcss-nested": ["postcss-nested@6.2.0", "", { "dependencies": { "postcss-selector-parser": "^6.1.1" }, "peerDependencies": { "postcss": "^8.2.14" } }, "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ=="],
|
||||
|
||||
"postcss-selector-parser": ["postcss-selector-parser@7.1.4", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-HeP7D2wyhkR+XaK6v4W8oRF62Dsz4flyuczALJp61GckGm42u1saSSJ/0auvcBqxs3jMRFEcPK34At/0JBKdOg=="],
|
||||
|
||||
"postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="],
|
||||
|
||||
"prettier": ["prettier@3.8.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw=="],
|
||||
|
||||
"progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="],
|
||||
@@ -1889,16 +1882,12 @@
|
||||
|
||||
"puppeteer-core": ["puppeteer-core@25.8.0", "", { "dependencies": { "@puppeteer/browsers": "3.2.1", "chromium-bidi": "17.0.2", "devtools-protocol": "0.0.1666840", "typed-query-selector": "^2.12.2", "webdriver-bidi-protocol": "0.4.2", "ws": "^8.21.1" } }, "sha512-LDOrawV8vfCVk+yLj2ozvajNP4Sv3OV9y3Tpiyy2g2Z+aQlbcozP6KJfI4iSBq7YQER+86ihEtPa5ioiZyWxMQ=="],
|
||||
|
||||
"queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="],
|
||||
|
||||
"react": ["react@19.2.6", "", {}, "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q=="],
|
||||
|
||||
"react-dom": ["react-dom@19.2.6", "", { "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { "react": "^19.2.6" } }, "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g=="],
|
||||
|
||||
"react-refresh": ["react-refresh@0.17.0", "", {}, "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ=="],
|
||||
|
||||
"read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="],
|
||||
|
||||
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||
|
||||
"readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="],
|
||||
@@ -1909,8 +1898,6 @@
|
||||
|
||||
"require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="],
|
||||
|
||||
"resolve": ["resolve@1.22.12", "", { "dependencies": { "es-errors": "^1.3.0", "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA=="],
|
||||
|
||||
"resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="],
|
||||
|
||||
"resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="],
|
||||
@@ -1921,8 +1908,6 @@
|
||||
|
||||
"retry-request": ["retry-request@7.0.2", "", { "dependencies": { "@types/request": "^2.48.8", "extend": "^3.0.2", "teeny-request": "^9.0.0" } }, "sha512-dUOvLMJ0/JJYEn8NrpOaGNE7X3vpI5XlZS/u0ANjqtcZVKnIxP7IgCFwrKTxENw29emmwug53awKtaMm4i9g5w=="],
|
||||
|
||||
"reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="],
|
||||
|
||||
"rimraf": ["rimraf@5.0.10", "", { "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" } }, "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ=="],
|
||||
|
||||
"roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="],
|
||||
@@ -1931,8 +1916,6 @@
|
||||
|
||||
"run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="],
|
||||
|
||||
"run-parallel": ["run-parallel@1.2.0", "", { "dependencies": { "queue-microtask": "^1.2.2" } }, "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA=="],
|
||||
|
||||
"rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
@@ -2005,13 +1988,13 @@
|
||||
|
||||
"supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="],
|
||||
|
||||
"supports-preserve-symlinks-flag": ["supports-preserve-symlinks-flag@1.0.0", "", {}, "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w=="],
|
||||
|
||||
"symbol-tree": ["symbol-tree@3.2.4", "", {}, "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw=="],
|
||||
|
||||
"table": ["table@6.9.0", "", { "dependencies": { "ajv": "^8.0.1", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", "string-width": "^4.2.3", "strip-ansi": "^6.0.1" } }, "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A=="],
|
||||
|
||||
"tailwindcss": ["tailwindcss@3.4.19", "", { "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", "chokidar": "^3.6.0", "didyoumean": "^1.2.2", "dlv": "^1.1.3", "fast-glob": "^3.3.2", "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.7", "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", "picocolors": "^1.1.1", "postcss": "^8.4.47", "postcss-import": "^15.1.0", "postcss-js": "^4.0.1", "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", "postcss-nested": "^6.2.0", "postcss-selector-parser": "^6.1.2", "resolve": "^1.22.8", "sucrase": "^3.35.0" }, "bin": { "tailwind": "lib/cli.js", "tailwindcss": "lib/cli.js" } }, "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ=="],
|
||||
"tailwindcss": ["tailwindcss@4.3.3", "", {}, "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ=="],
|
||||
|
||||
"tapable": ["tapable@2.3.3", "", {}, "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A=="],
|
||||
|
||||
"tar": ["tar@7.5.15", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ=="],
|
||||
|
||||
@@ -2049,8 +2032,6 @@
|
||||
|
||||
"tldts-core": ["tldts-core@7.0.30", "", {}, "sha512-uiHN8PIB1VmWyS98eZYja4xzlYqeFZVjb4OuYlJQnZAuJhMw4PbKQOKgHKhBdJR3FE/t5mUQ1Kd80++B+qhD1Q=="],
|
||||
|
||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||
|
||||
"tough-cookie": ["tough-cookie@6.0.1", "", { "dependencies": { "tldts": "^7.0.5" } }, "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw=="],
|
||||
|
||||
"tr46": ["tr46@6.0.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw=="],
|
||||
@@ -2187,7 +2168,17 @@
|
||||
|
||||
"@puppeteer/browsers/yargs": ["yargs@18.0.0", "", { "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "string-width": "^7.2.0", "y18n": "^5.0.5", "yargs-parser": "^22.0.0" } }, "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg=="],
|
||||
|
||||
"anymatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.11.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.3", "tslib": "^2.4.0" }, "bundled": true }, "sha512-zLpS5asjEb7lq8jYLq37N6XKaE41DIexlY1rF/z4/tIl3wo13Sqm28fRyfIsKZD+NZ8mM5RoKkpW/rBcuoSZSg=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/runtime": ["@emnapi/runtime@1.11.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.3", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-ELEBe8PsLvvJ6QMr0zLt8ffvOHW/dc1m3CEzNMg7aJUv3bMaoDtw2TXyDAwkYBuroxxuHEwhRTLJSe5sya547g=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" }, "bundled": true }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" }, "bundled": true }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="],
|
||||
|
||||
"@tailwindcss/oxide-wasm32-wasi/tslib": ["tslib@2.8.1", "", { "bundled": true }, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"aws-cdk-lib/yaml": ["yaml@1.10.3", "", {}, "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA=="],
|
||||
|
||||
@@ -2201,8 +2192,6 @@
|
||||
|
||||
"dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="],
|
||||
|
||||
"fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"gaxios/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"gaxios/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
||||
@@ -2231,12 +2220,10 @@
|
||||
|
||||
"jsdom/whatwg-mimetype": ["whatwg-mimetype@5.0.0", "", {}, "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw=="],
|
||||
|
||||
"magicast/@babel/parser": ["@babel/parser@7.29.8", "", { "dependencies": { "@babel/types": "^7.29.8" }, "bin": "./bin/babel-parser.js" }, "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA=="],
|
||||
"magicast/@babel/parser": ["@babel/parser@7.29.9", "", { "dependencies": { "@babel/types": "^7.29.8" }, "bin": "./bin/babel-parser.js" }, "sha512-CjXrNHTnvqBVqHgdBysY3vk2T8tpJHb5/RMeHJBTyVa9xgugCB0CJTx/3oO8RV2QRQP391RWpB7D6hLjm8V9uA=="],
|
||||
|
||||
"magicast/@babel/types": ["@babel/types@7.29.8", "", { "dependencies": { "@babel/helper-string-parser": "^7.29.7", "@babel/helper-validator-identifier": "^7.29.7" } }, "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg=="],
|
||||
|
||||
"micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||
|
||||
"minizlib/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
|
||||
|
||||
"mlly/acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="],
|
||||
@@ -2249,20 +2236,12 @@
|
||||
|
||||
"path-scurry/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
|
||||
|
||||
"postcss-nested/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
|
||||
|
||||
"puppeteer-core/ws": ["ws@8.21.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw=="],
|
||||
|
||||
"recast/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"sharp/semver": ["semver@7.8.5", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA=="],
|
||||
|
||||
"tailwindcss/chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="],
|
||||
|
||||
"tailwindcss/jiti": ["jiti@1.21.7", "", { "bin": { "jiti": "bin/jiti.js" } }, "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A=="],
|
||||
|
||||
"tailwindcss/postcss-selector-parser": ["postcss-selector-parser@6.1.2", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg=="],
|
||||
|
||||
"tar/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="],
|
||||
|
||||
"teeny-request/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
||||
@@ -2273,6 +2252,8 @@
|
||||
|
||||
"tsx/esbuild": ["esbuild@0.27.7", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.7", "@esbuild/android-arm": "0.27.7", "@esbuild/android-arm64": "0.27.7", "@esbuild/android-x64": "0.27.7", "@esbuild/darwin-arm64": "0.27.7", "@esbuild/darwin-x64": "0.27.7", "@esbuild/freebsd-arm64": "0.27.7", "@esbuild/freebsd-x64": "0.27.7", "@esbuild/linux-arm": "0.27.7", "@esbuild/linux-arm64": "0.27.7", "@esbuild/linux-ia32": "0.27.7", "@esbuild/linux-loong64": "0.27.7", "@esbuild/linux-mips64el": "0.27.7", "@esbuild/linux-ppc64": "0.27.7", "@esbuild/linux-riscv64": "0.27.7", "@esbuild/linux-s390x": "0.27.7", "@esbuild/linux-x64": "0.27.7", "@esbuild/netbsd-arm64": "0.27.7", "@esbuild/netbsd-x64": "0.27.7", "@esbuild/openbsd-arm64": "0.27.7", "@esbuild/openbsd-x64": "0.27.7", "@esbuild/openharmony-arm64": "0.27.7", "@esbuild/sunos-x64": "0.27.7", "@esbuild/win32-arm64": "0.27.7", "@esbuild/win32-ia32": "0.27.7", "@esbuild/win32-x64": "0.27.7" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w=="],
|
||||
|
||||
"vite/postcss": ["postcss@8.5.14", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg=="],
|
||||
|
||||
"@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="],
|
||||
|
||||
"@google-cloud/storage/google-auth-library/gcp-metadata": ["gcp-metadata@6.1.1", "", { "dependencies": { "gaxios": "^6.1.1", "google-logging-utils": "^0.0.2", "json-bigint": "^1.0.0" } }, "sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A=="],
|
||||
@@ -2315,10 +2296,6 @@
|
||||
|
||||
"node-fetch/whatwg-url/webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||
|
||||
"tailwindcss/chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="],
|
||||
|
||||
"tailwindcss/chokidar/readdirp": ["readdirp@3.6.0", "", { "dependencies": { "picomatch": "^2.2.1" } }, "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA=="],
|
||||
|
||||
"tsup/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.7", "", { "os": "aix", "cpu": "ppc64" }, "sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg=="],
|
||||
|
||||
"tsup/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.7", "", { "os": "android", "cpu": "arm" }, "sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ=="],
|
||||
@@ -2423,6 +2400,8 @@
|
||||
|
||||
"tsx/esbuild/@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.7", "", { "os": "win32", "cpu": "x64" }, "sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg=="],
|
||||
|
||||
"vite/postcss/nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="],
|
||||
|
||||
"@google-cloud/storage/google-auth-library/gcp-metadata/google-logging-utils": ["google-logging-utils@0.0.2", "", {}, "sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ=="],
|
||||
|
||||
"@puppeteer/browsers/yargs/cliui/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="],
|
||||
@@ -2445,8 +2424,6 @@
|
||||
|
||||
"google-gax/retry-request/teeny-request/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"tailwindcss/chokidar/readdirp/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||
|
||||
"@puppeteer/browsers/yargs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="],
|
||||
|
||||
"@puppeteer/browsers/yargs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="],
|
||||
|
||||
@@ -81,15 +81,14 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@hyperframes/producer": "workspace:*",
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@types/react": "19",
|
||||
"@types/react-dom": "19",
|
||||
"@vitejs/plugin-react": "^4.0.0",
|
||||
"autoprefixer": "^10.4.0",
|
||||
"chokidar": "^4.0.3",
|
||||
"fake-indexeddb": "^6.2.5",
|
||||
"postcss": "^8.4.0",
|
||||
"puppeteer-core": "^25.2.1",
|
||||
"tailwindcss": "^3.4.0",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"tsup": "^8.0.0",
|
||||
"typescript": "^5.0.0",
|
||||
"vite": "^6.4.2",
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
export default {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
};
|
||||
@@ -171,7 +171,7 @@ function AnimationPhase({
|
||||
}}
|
||||
className="flex-1 accent-studio-accent disabled:opacity-40"
|
||||
/>
|
||||
<span className="text-2xs text-neutral-400 font-mono w-8 text-right flex-shrink-0">
|
||||
<span className="text-2xs text-neutral-400 font-mono w-8 text-right shrink-0">
|
||||
{intensity.toFixed(2)}
|
||||
</span>
|
||||
</div>
|
||||
@@ -249,7 +249,7 @@ export const CaptionAnimationPanel = memo(function CaptionAnimationPanel() {
|
||||
return (
|
||||
<div className="flex flex-col h-full min-h-0">
|
||||
{gated && (
|
||||
<div className="flex-shrink-0 mx-3 mt-2 px-2 py-1.5 rounded border border-amber-500/30 bg-amber-500/10">
|
||||
<div className="shrink-0 mx-3 mt-2 px-2 py-1.5 rounded-sm border border-amber-500/30 bg-amber-500/10">
|
||||
<p className="text-2xs text-amber-300/90 leading-snug">
|
||||
Animation editing isn't applied to playback or saved yet, so these controls are
|
||||
disabled.
|
||||
@@ -286,13 +286,13 @@ export const CaptionAnimationPanel = memo(function CaptionAnimationPanel() {
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="flex-shrink-0 px-3 py-2 border-t border-neutral-800">
|
||||
<div className="shrink-0 px-3 py-2 border-t border-neutral-800">
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleApplyToAll}
|
||||
disabled={gated}
|
||||
title={gated ? "Disabled until animation editing is applied to playback" : undefined}
|
||||
className="w-full py-1.5 rounded border border-neutral-700 text-2xs text-neutral-300 hover:border-studio-accent/50 hover:text-studio-accent transition-colors disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:border-neutral-700 disabled:hover:text-neutral-300"
|
||||
className="w-full py-1.5 rounded-sm border border-neutral-700 text-2xs text-neutral-300 hover:border-studio-accent/50 hover:text-studio-accent transition-colors disabled:opacity-40 disabled:cursor-not-allowed disabled:hover:border-neutral-700 disabled:hover:text-neutral-300"
|
||||
>
|
||||
Apply to all groups
|
||||
</button>
|
||||
|
||||
@@ -213,7 +213,7 @@ export const CaptionPropertyPanel = memo(function CaptionPropertyPanel({
|
||||
return (
|
||||
<div className="flex flex-col h-full min-h-0">
|
||||
{/* Header */}
|
||||
<div className="px-3 py-2 border-b border-neutral-800 flex-shrink-0">
|
||||
<div className="px-3 py-2 border-b border-neutral-800 shrink-0">
|
||||
<div className="flex items-center justify-between mb-1.5">
|
||||
<span className="text-2xs text-neutral-500">{countLabel}</span>
|
||||
</div>
|
||||
@@ -225,7 +225,7 @@ export const CaptionPropertyPanel = memo(function CaptionPropertyPanel({
|
||||
aria-selected={activeTab === "style"}
|
||||
onClick={() => setActiveTab("style")}
|
||||
className={[
|
||||
"flex-1 py-0.5 rounded text-2xs font-medium transition-colors",
|
||||
"flex-1 py-0.5 rounded-sm text-2xs font-medium transition-colors",
|
||||
activeTab === "style"
|
||||
? "bg-studio-accent/20 text-studio-accent border border-studio-accent/50"
|
||||
: "text-neutral-500 border border-neutral-800 hover:text-neutral-300 hover:border-neutral-600",
|
||||
@@ -239,7 +239,7 @@ export const CaptionPropertyPanel = memo(function CaptionPropertyPanel({
|
||||
aria-selected={activeTab === "animation"}
|
||||
onClick={() => setActiveTab("animation")}
|
||||
className={[
|
||||
"flex-1 py-0.5 rounded text-2xs font-medium transition-colors",
|
||||
"flex-1 py-0.5 rounded-sm text-2xs font-medium transition-colors",
|
||||
activeTab === "animation"
|
||||
? "bg-studio-accent/20 text-studio-accent border border-studio-accent/50"
|
||||
: "text-neutral-500 border border-neutral-800 hover:text-neutral-300 hover:border-neutral-600",
|
||||
|
||||
@@ -109,7 +109,7 @@ export const CaptionTimeline = memo(function CaptionTimeline({
|
||||
tabIndex={0}
|
||||
aria-label={`Caption word "${seg.text}"`}
|
||||
aria-pressed={isSelected}
|
||||
className={`absolute top-1 bottom-1 rounded flex items-center overflow-hidden cursor-pointer focus-visible:ring-1 focus-visible:ring-white outline-none${
|
||||
className={`absolute top-1 bottom-1 rounded flex items-center overflow-hidden cursor-pointer focus-visible:ring-1 focus-visible:ring-white outline-hidden${
|
||||
isSelected ? " ring-1 ring-white/50 z-10" : ""
|
||||
}`}
|
||||
style={{
|
||||
|
||||
@@ -2,7 +2,7 @@ import type React from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
|
||||
export const inputCls =
|
||||
"w-full bg-neutral-900 border border-neutral-800 rounded px-1.5 py-0.5 text-2xs text-neutral-200 font-mono outline-none focus:border-studio-accent disabled:opacity-40 disabled:cursor-not-allowed";
|
||||
"w-full bg-neutral-900 border border-neutral-800 rounded-sm px-1.5 py-0.5 text-2xs text-neutral-200 font-mono outline-hidden focus:border-studio-accent disabled:opacity-40 disabled:cursor-not-allowed";
|
||||
|
||||
export function Section({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
return (
|
||||
@@ -20,7 +20,7 @@ export function Section({ label, children }: { label: string; children: React.Re
|
||||
export function Row({ label, children }: { label: string; children: React.ReactNode }) {
|
||||
return (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-2xs text-neutral-600 w-14 text-right flex-shrink-0">{label}</span>
|
||||
<span className="text-2xs text-neutral-600 w-14 text-right shrink-0">{label}</span>
|
||||
<div className="flex-1 min-w-0">{children}</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -66,8 +66,8 @@ export function AskAgentModal({
|
||||
<div
|
||||
className={
|
||||
anchorPoint
|
||||
? "hf-backdrop-in fixed inset-0 z-[100] bg-black/60 backdrop-blur-sm"
|
||||
: "hf-backdrop-in fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm"
|
||||
? "hf-backdrop-in fixed inset-0 z-100 bg-black/60 backdrop-blur-xs"
|
||||
: "hf-backdrop-in fixed inset-0 z-100 flex items-center justify-center bg-black/60 backdrop-blur-xs"
|
||||
}
|
||||
onClick={requestClose}
|
||||
>
|
||||
@@ -77,7 +77,7 @@ export function AskAgentModal({
|
||||
aria-modal="true"
|
||||
aria-label="Copy prompt to AI agent"
|
||||
tabIndex={-1}
|
||||
className={`w-[480px] rounded-2xl border border-neutral-800 bg-neutral-950 shadow-2xl outline-none ${
|
||||
className={`w-[480px] rounded-2xl border border-neutral-800 bg-neutral-950 shadow-2xl outline-hidden ${
|
||||
anchorPoint ? "fixed" : ""
|
||||
}`}
|
||||
style={modalPositionStyle}
|
||||
@@ -112,7 +112,7 @@ export function AskAgentModal({
|
||||
<div className="px-5 py-4 space-y-3">
|
||||
<textarea
|
||||
ref={inputRef}
|
||||
className="w-full h-24 px-3 py-2 rounded-lg border border-neutral-800 bg-neutral-900/60 text-sm text-neutral-200 placeholder-neutral-600 resize-none focus:outline-none focus:border-studio-accent/60 focus:ring-1 focus:ring-studio-accent/30"
|
||||
className="w-full h-24 px-3 py-2 rounded-lg border border-neutral-800 bg-neutral-900/60 text-sm text-neutral-200 placeholder-neutral-600 resize-none focus:outline-hidden focus:border-studio-accent/60 focus:ring-1 focus:ring-studio-accent/30"
|
||||
placeholder="Describe what you want to change…"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
@@ -127,7 +127,7 @@ export function AskAgentModal({
|
||||
<summary className="text-[11px] text-neutral-500 cursor-pointer select-none hover:text-neutral-400">
|
||||
Context included in prompt
|
||||
</summary>
|
||||
<pre className="mt-2 max-h-40 overflow-auto rounded-lg bg-neutral-900/80 px-3 py-2 text-[11px] leading-relaxed text-neutral-500 whitespace-pre-wrap break-words border border-neutral-800/50">
|
||||
<pre className="mt-2 max-h-40 overflow-auto rounded-lg bg-neutral-900/80 px-3 py-2 text-[11px] leading-relaxed text-neutral-500 whitespace-pre-wrap wrap-break-word border border-neutral-800/50">
|
||||
{contextPreview}
|
||||
</pre>
|
||||
</details>
|
||||
|
||||
@@ -276,7 +276,7 @@ function EditorShellBody({
|
||||
ref={containerRef}
|
||||
// Shell canvas is a step LIGHTER than the near-black panel cards so the
|
||||
// gaps between panels read as visible seams (CapCut-style).
|
||||
className="flex flex-col flex-1 min-h-0 bg-[#18181B]"
|
||||
className="flex flex-col flex-1 min-h-0 bg-panel-surface"
|
||||
onKeyDown={handleKeyDown}
|
||||
tabIndex={-1}
|
||||
>
|
||||
@@ -313,7 +313,7 @@ function EditorShellBody({
|
||||
onSelectTimelineElement={onSelectTimelineElement}
|
||||
timelineFooter={
|
||||
captionEditMode ? (
|
||||
<div className="border-t border-neutral-800/30 flex-shrink-0" style={{ height: 60 }}>
|
||||
<div className="border-t border-neutral-800/30 shrink-0" style={{ height: 60 }}>
|
||||
<div className="flex items-center gap-1.5 px-2 py-0.5">
|
||||
<span className="text-[9px] font-medium text-neutral-500 uppercase tracking-wider">
|
||||
Captions
|
||||
|
||||
@@ -32,7 +32,7 @@ function ConflictReview({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-[110] flex items-center justify-center bg-black/70 px-5 backdrop-blur-sm"
|
||||
className="fixed inset-0 z-110 flex items-center justify-center bg-black/70 px-5 backdrop-blur-xs"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
@@ -41,7 +41,7 @@ function ConflictReview({
|
||||
aria-modal="true"
|
||||
aria-labelledby="external-conflict-title"
|
||||
tabIndex={-1}
|
||||
className="max-h-[88vh] w-full max-w-5xl overflow-auto rounded-xl border border-amber-400/30 bg-neutral-950 p-5 text-neutral-100 shadow-2xl outline-none"
|
||||
className="max-h-[88vh] w-full max-w-5xl overflow-auto rounded-xl border border-amber-400/30 bg-neutral-950 p-5 text-neutral-100 shadow-2xl outline-hidden"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
@@ -53,7 +53,7 @@ function ConflictReview({
|
||||
Reviewing or exporting does not change either version.
|
||||
</p>
|
||||
</div>
|
||||
<button type="button" onClick={onClose} className="rounded px-2 py-1 text-neutral-400">
|
||||
<button type="button" onClick={onClose} className="rounded-sm px-2 py-1 text-neutral-400">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
@@ -68,14 +68,14 @@ function ConflictReview({
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded border border-neutral-700 px-2 py-1 text-[11px]"
|
||||
className="rounded-sm border border-neutral-700 px-2 py-1 text-[11px]"
|
||||
onClick={() => void navigator.clipboard.writeText(side.content)}
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded border border-neutral-700 px-2 py-1 text-[11px]"
|
||||
className="rounded-sm border border-neutral-700 px-2 py-1 text-[11px]"
|
||||
onClick={() =>
|
||||
downloadText(`${conflict.error.filePath}.${side.suffix}.html`, side.content)
|
||||
}
|
||||
@@ -87,7 +87,7 @@ function ConflictReview({
|
||||
<textarea
|
||||
readOnly
|
||||
value={side.content}
|
||||
className="h-80 w-full resize-y rounded border border-neutral-800 bg-neutral-900 p-3 font-mono text-[11px] leading-relaxed text-neutral-300"
|
||||
className="h-80 w-full resize-y rounded-sm border border-neutral-800 bg-neutral-900 p-3 font-mono text-[11px] leading-relaxed text-neutral-300"
|
||||
/>
|
||||
</section>
|
||||
))}
|
||||
@@ -110,7 +110,7 @@ function FailedDraftReview({
|
||||
useDialogBehavior({ open: true, onClose, containerRef });
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-[110] flex items-center justify-center bg-black/70 px-5 backdrop-blur-sm"
|
||||
className="fixed inset-0 z-110 flex items-center justify-center bg-black/70 px-5 backdrop-blur-xs"
|
||||
onClick={onClose}
|
||||
>
|
||||
<div
|
||||
@@ -119,7 +119,7 @@ function FailedDraftReview({
|
||||
aria-modal="true"
|
||||
aria-labelledby="failed-draft-title"
|
||||
tabIndex={-1}
|
||||
className="max-h-[88vh] w-full max-w-3xl overflow-auto rounded-xl border border-amber-400/30 bg-neutral-950 p-5 text-neutral-100 shadow-2xl outline-none"
|
||||
className="max-h-[88vh] w-full max-w-3xl overflow-auto rounded-xl border border-amber-400/30 bg-neutral-950 p-5 text-neutral-100 shadow-2xl outline-hidden"
|
||||
onClick={(event) => event.stopPropagation()}
|
||||
>
|
||||
<div className="flex items-start justify-between gap-4">
|
||||
@@ -131,21 +131,21 @@ function FailedDraftReview({
|
||||
Copy or download this draft before choosing to discard it.
|
||||
</p>
|
||||
</div>
|
||||
<button type="button" onClick={onClose} className="rounded px-2 py-1 text-neutral-400">
|
||||
<button type="button" onClick={onClose} className="rounded-sm px-2 py-1 text-neutral-400">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<div className="mt-4 flex justify-end gap-2">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded border border-neutral-700 px-2 py-1 text-[11px]"
|
||||
className="rounded-sm border border-neutral-700 px-2 py-1 text-[11px]"
|
||||
onClick={() => void navigator.clipboard.writeText(content)}
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="rounded border border-neutral-700 px-2 py-1 text-[11px]"
|
||||
className="rounded-sm border border-neutral-700 px-2 py-1 text-[11px]"
|
||||
onClick={() => downloadText(`${path}.studio.html`, content)}
|
||||
>
|
||||
Download
|
||||
@@ -154,7 +154,7 @@ function FailedDraftReview({
|
||||
<textarea
|
||||
readOnly
|
||||
value={content}
|
||||
className="mt-2 h-80 w-full resize-y rounded border border-neutral-800 bg-neutral-900 p-3 font-mono text-[11px] leading-relaxed text-neutral-300"
|
||||
className="mt-2 h-80 w-full resize-y rounded-sm border border-neutral-800 bg-neutral-900 p-3 font-mono text-[11px] leading-relaxed text-neutral-300"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
@@ -176,7 +176,7 @@ export function ExternalFileConflictBanner({
|
||||
<>
|
||||
<div
|
||||
role="alert"
|
||||
className="absolute left-1/2 top-14 z-[94] flex max-w-[calc(100vw-32px)] -translate-x-1/2 flex-wrap items-center gap-3 rounded-md border border-amber-400/30 bg-amber-950/95 px-4 py-2 text-[12px] font-medium text-amber-50 shadow-lg"
|
||||
className="absolute left-1/2 top-14 z-94 flex max-w-[calc(100vw-32px)] -translate-x-1/2 flex-wrap items-center gap-3 rounded-md border border-amber-400/30 bg-amber-950/95 px-4 py-2 text-[12px] font-medium text-amber-50 shadow-lg"
|
||||
>
|
||||
<span>
|
||||
{conflict
|
||||
@@ -201,7 +201,7 @@ export function ExternalFileConflictBanner({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void coordinator.useExternalFile()}
|
||||
className="rounded border border-amber-200/30 px-2 py-1"
|
||||
className="rounded-sm border border-amber-200/30 px-2 py-1"
|
||||
>
|
||||
Discard Studio edits and reload file
|
||||
</button>
|
||||
@@ -217,7 +217,7 @@ export function ExternalFileConflictBanner({
|
||||
void coordinator.keepStudioFile();
|
||||
}
|
||||
}}
|
||||
className="rounded border border-red-300/40 px-2 py-1 text-red-100"
|
||||
className="rounded-sm border border-red-300/40 px-2 py-1 text-red-100"
|
||||
>
|
||||
Overwrite file with Studio version
|
||||
</button>
|
||||
@@ -234,7 +234,7 @@ export function ExternalFileConflictBanner({
|
||||
void coordinator.keepStudioFile();
|
||||
}
|
||||
}}
|
||||
className="rounded border border-red-300/40 px-2 py-1 text-red-100"
|
||||
className="rounded-sm border border-red-300/40 px-2 py-1 text-red-100"
|
||||
>
|
||||
Overwrite file with recovered Studio draft
|
||||
</button>
|
||||
|
||||
@@ -58,7 +58,7 @@ export function LintModal({
|
||||
|
||||
return (
|
||||
<div
|
||||
className="hf-backdrop-in fixed inset-0 z-[100] flex items-center justify-center bg-black/60 backdrop-blur-sm"
|
||||
className="hf-backdrop-in fixed inset-0 z-100 flex items-center justify-center bg-black/60 backdrop-blur-xs"
|
||||
onClick={requestClose}
|
||||
>
|
||||
<div
|
||||
@@ -67,7 +67,7 @@ export function LintModal({
|
||||
aria-modal="true"
|
||||
aria-label={title}
|
||||
tabIndex={-1}
|
||||
className="bg-neutral-950 border border-neutral-800 rounded-xl shadow-2xl w-full max-w-xl max-h-[80vh] flex flex-col overflow-hidden outline-none"
|
||||
className="bg-neutral-950 border border-neutral-800 rounded-xl shadow-2xl w-full max-w-xl max-h-[80vh] flex flex-col overflow-hidden outline-hidden"
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
>
|
||||
{/* Header */}
|
||||
@@ -130,20 +130,13 @@ export function LintModal({
|
||||
{errors.map((f, i) => (
|
||||
<div key={`e-${i}`} className="py-3 border-b border-neutral-800/50 last:border-0">
|
||||
<div className="flex items-start gap-2">
|
||||
<WarningIcon
|
||||
size={14}
|
||||
className="text-red-400 flex-shrink-0 mt-0.5"
|
||||
weight="fill"
|
||||
/>
|
||||
<WarningIcon size={14} className="text-red-400 shrink-0 mt-0.5" weight="fill" />
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm text-neutral-200">{f.message}</p>
|
||||
{f.file && <p className="text-xs text-neutral-600 font-mono mt-0.5">{f.file}</p>}
|
||||
{f.fixHint && (
|
||||
<div className="flex items-start gap-1 mt-1.5">
|
||||
<CaretRightIcon
|
||||
size={10}
|
||||
className="text-studio-accent flex-shrink-0 mt-0.5"
|
||||
/>
|
||||
<CaretRightIcon size={10} className="text-studio-accent shrink-0 mt-0.5" />
|
||||
<p className="text-xs text-studio-accent">{f.fixHint}</p>
|
||||
</div>
|
||||
)}
|
||||
@@ -154,16 +147,13 @@ export function LintModal({
|
||||
{warnings.map((f, i) => (
|
||||
<div key={`w-${i}`} className="py-3 border-b border-neutral-800/50 last:border-0">
|
||||
<div className="flex items-start gap-2">
|
||||
<WarningIcon size={14} className="text-amber-400 flex-shrink-0 mt-0.5" />
|
||||
<WarningIcon size={14} className="text-amber-400 shrink-0 mt-0.5" />
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm text-neutral-300">{f.message}</p>
|
||||
{f.file && <p className="text-xs text-neutral-600 font-mono mt-0.5">{f.file}</p>}
|
||||
{f.fixHint && (
|
||||
<div className="flex items-start gap-1 mt-1.5">
|
||||
<CaretRightIcon
|
||||
size={10}
|
||||
className="text-studio-accent flex-shrink-0 mt-0.5"
|
||||
/>
|
||||
<CaretRightIcon size={10} className="text-studio-accent shrink-0 mt-0.5" />
|
||||
<p className="text-xs text-studio-accent">{f.fixHint}</p>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -45,7 +45,7 @@ export function MediaPreview({ projectId, filePath }: { projectId: string; fileP
|
||||
src={serveUrl}
|
||||
alt={name}
|
||||
onError={setFailed}
|
||||
className="max-w-full max-h-[70%] object-contain rounded border border-neutral-800"
|
||||
className="max-w-full max-h-[70%] object-contain rounded-sm border border-neutral-800"
|
||||
/>
|
||||
<span className="mt-3 text-[11px] text-neutral-500 font-mono">{filePath}</span>
|
||||
</div>
|
||||
@@ -59,7 +59,7 @@ export function MediaPreview({ projectId, filePath }: { projectId: string; fileP
|
||||
src={serveUrl}
|
||||
controls
|
||||
onError={setFailed}
|
||||
className="max-w-full max-h-[70%] rounded border border-neutral-800"
|
||||
className="max-w-full max-h-[70%] rounded-sm border border-neutral-800"
|
||||
/>
|
||||
<span className="mt-3 text-[11px] text-neutral-500 font-mono">{filePath}</span>
|
||||
</div>
|
||||
|
||||
@@ -8,14 +8,14 @@ interface SaveQueuePausedBannerProps {
|
||||
export function SaveQueuePausedBanner({ message, onRetry }: SaveQueuePausedBannerProps) {
|
||||
return (
|
||||
<div
|
||||
className="hf-backdrop-in absolute left-1/2 top-14 z-[92] flex max-w-[calc(100vw-32px)] -translate-x-1/2 items-center gap-3 rounded-md border border-red-500/30 bg-red-950/85 px-4 py-2 text-[12px] font-medium text-red-100 shadow-lg shadow-black/30"
|
||||
className="hf-backdrop-in absolute left-1/2 top-14 z-92 flex max-w-[calc(100vw-32px)] -translate-x-1/2 items-center gap-3 rounded-md border border-red-500/30 bg-red-950/85 px-4 py-2 text-[12px] font-medium text-red-100 shadow-lg shadow-black/30"
|
||||
role="alert"
|
||||
>
|
||||
<span>{message}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={onRetry}
|
||||
className="rounded border border-red-300/20 px-2 py-1 text-[11px] text-red-100 transition-colors hover:bg-red-400/10 active:scale-[0.98]"
|
||||
className="rounded-sm border border-red-300/20 px-2 py-1 text-[11px] text-red-100 transition-colors hover:bg-red-400/10 active:scale-[0.98]"
|
||||
>
|
||||
Retry saving
|
||||
</button>
|
||||
|
||||
@@ -174,7 +174,7 @@ export function StudioHeader({
|
||||
const ffmpegMissing = renderQueue.ffmpegMissing;
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between h-10 px-3 bg-neutral-900 border-b border-neutral-800 flex-shrink-0">
|
||||
<div className="flex items-center justify-between h-10 px-3 bg-neutral-900 border-b border-neutral-800 shrink-0">
|
||||
{/* Left: logo + project name */}
|
||||
<div className="flex items-center gap-3">
|
||||
<HyperframesLogo />
|
||||
|
||||
@@ -86,7 +86,7 @@ export function StudioLeftSidebar({
|
||||
|
||||
if (effectiveLeftCollapsed) {
|
||||
return (
|
||||
<div className="mr-0.5 flex w-10 flex-shrink-0 flex-col items-center rounded-lg border border-neutral-800/50 bg-neutral-950 pt-1">
|
||||
<div className="mr-0.5 flex w-10 shrink-0 flex-col items-center rounded-lg border border-neutral-800/50 bg-neutral-950 pt-1">
|
||||
<button
|
||||
type="button"
|
||||
onClick={toggleLeftSidebar}
|
||||
@@ -169,14 +169,14 @@ export function StudioLeftSidebar({
|
||||
/>
|
||||
{/* Vertical resize divider: 3px visible seam, 13px pointer-capture zone via
|
||||
the absolutely-positioned inner hit area. The outer element is w-[3px] so
|
||||
it contributes only 3px of gap in the flex row; the inner -left-[2px]
|
||||
it contributes only 3px of gap in the flex row; the inner left-[-2px]
|
||||
element widens the hit area without affecting layout. */}
|
||||
<div
|
||||
role="separator"
|
||||
aria-label="Resize sidebar"
|
||||
aria-orientation="vertical"
|
||||
tabIndex={0}
|
||||
className="group relative w-[3px] flex-shrink-0 cursor-col-resize outline-none focus-visible:bg-studio-accent/20"
|
||||
className="group relative w-[3px] shrink-0 cursor-col-resize outline-hidden focus-visible:bg-studio-accent/20"
|
||||
style={{ touchAction: "none" }}
|
||||
onPointerDown={(e) => handlePanelResizeStart("left", e)}
|
||||
onPointerMove={handlePanelResizeMove}
|
||||
@@ -195,7 +195,7 @@ export function StudioLeftSidebar({
|
||||
24px WCAG 2.2 (2.5.8) target because the next pixel on either side is
|
||||
live: the sidebar's scrolling tab content on the left, the preview
|
||||
stage on the right. Silently stealing their clicks is the worse bug. */}
|
||||
<div className="absolute inset-y-0 -left-[2px] w-[13px]" />
|
||||
<div className="absolute inset-y-0 left-[-2px] w-[13px]" />
|
||||
{/* Visible hairline */}
|
||||
<div className="absolute top-1/2 left-0 h-[52px] w-[3px] -translate-y-1/2 bg-white/12 transition-colors group-hover:bg-white/18 group-active:bg-white/24" />
|
||||
</div>
|
||||
|
||||
@@ -77,7 +77,7 @@ export function StudioOverlays({
|
||||
)}
|
||||
{/* One bottom-right stack so the feedback card and toasts queue instead
|
||||
of covering each other. Empty when nothing is showing. */}
|
||||
<div className="absolute bottom-6 right-6 z-[91] flex flex-col items-end gap-2">
|
||||
<div className="absolute bottom-6 right-6 z-91 flex flex-col items-end gap-2">
|
||||
{toasts.map((toast) => (
|
||||
<StudioToast
|
||||
key={toast.id}
|
||||
|
||||
@@ -347,7 +347,7 @@ export function StudioRightPanel({
|
||||
aria-label="Resize inspector panel"
|
||||
aria-orientation="vertical"
|
||||
tabIndex={0}
|
||||
className="group relative w-[3px] flex-shrink-0 cursor-col-resize outline-none focus-visible:bg-studio-accent/20"
|
||||
className="group relative w-[3px] shrink-0 cursor-col-resize outline-hidden focus-visible:bg-studio-accent/20"
|
||||
style={{ touchAction: "none" }}
|
||||
onPointerDown={(e) => handlePanelResizeStart("right", e)}
|
||||
onPointerMove={handlePanelResizeMove}
|
||||
@@ -364,12 +364,12 @@ export function StudioRightPanel({
|
||||
{/* Asymmetric hit zone: 8px into the preview's p-2 gutter (the only dead
|
||||
space), the 3px seam, 2px into the card. Stops short of the 24px WCAG
|
||||
2.5.8 target because the next pixel each way is live. */}
|
||||
<div className="absolute inset-y-0 -left-[8px] w-[13px]" />
|
||||
<div className="absolute inset-y-0 left-[-8px] w-[13px]" />
|
||||
{/* Visible hairline */}
|
||||
<div className="absolute top-1/2 left-0 h-[52px] w-[3px] -translate-y-1/2 bg-white/12 transition-colors group-hover:bg-white/18 group-active:bg-white/24" />
|
||||
</div>
|
||||
<div
|
||||
className="flex min-w-0 flex-shrink-0 flex-col overflow-hidden rounded-lg border border-neutral-800 bg-neutral-950"
|
||||
className="flex min-w-0 shrink-0 flex-col overflow-hidden rounded-lg border border-neutral-800 bg-neutral-950"
|
||||
style={{ width: rightWidth }}
|
||||
>
|
||||
{captionEditMode ? (
|
||||
@@ -444,7 +444,7 @@ export function StudioRightPanel({
|
||||
role="separator"
|
||||
aria-label="Resize Layers and Design panes"
|
||||
aria-orientation="horizontal"
|
||||
className="group flex h-2 flex-shrink-0 cursor-row-resize items-center justify-center border-y border-neutral-800 bg-neutral-900"
|
||||
className="group flex h-2 shrink-0 cursor-row-resize items-center justify-center border-y border-neutral-800 bg-neutral-900"
|
||||
style={{ touchAction: "none" }}
|
||||
onPointerDown={handleInspectorSplitResizeStart}
|
||||
onPointerMove={handleInspectorSplitResizeMove}
|
||||
|
||||
@@ -30,7 +30,7 @@ export function StudioToast({ message, tone, leaving, onDismiss }: StudioToastPr
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className={`min-w-0 break-words leading-5 ${isError ? "text-red-200" : "text-neutral-200"}`}
|
||||
className={`min-w-0 wrap-break-word leading-5 ${isError ? "text-red-200" : "text-neutral-200"}`}
|
||||
>
|
||||
{message}
|
||||
</span>
|
||||
@@ -38,7 +38,7 @@ export function StudioToast({ message, tone, leaving, onDismiss }: StudioToastPr
|
||||
<button
|
||||
type="button"
|
||||
onClick={onDismiss}
|
||||
className="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-md text-neutral-500 transition-colors hover:bg-white/10 hover:text-neutral-300"
|
||||
className="flex h-5 w-5 shrink-0 items-center justify-center rounded-md text-neutral-500 transition-colors hover:bg-white/10 hover:text-neutral-300"
|
||||
aria-label="Dismiss"
|
||||
>
|
||||
<svg
|
||||
|
||||
@@ -189,8 +189,8 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
|
||||
// CapCut-flat icon buttons: no per-button border/box chrome — a transparent
|
||||
// 28px hit area with a subtle rounded hover wash, consistent 16px glyphs.
|
||||
const flatBtn = "flex h-7 w-7 items-center justify-center rounded-md transition-colors";
|
||||
const flatIdle = `${flatBtn} text-neutral-400 hover:bg-white/[0.06] hover:text-neutral-200 active:scale-[0.98]`;
|
||||
const flatActive = `${flatBtn} bg-white/[0.08] text-neutral-100 active:scale-[0.98]`;
|
||||
const flatIdle = `${flatBtn} text-neutral-400 hover:bg-white/6 hover:text-neutral-200 active:scale-[0.98]`;
|
||||
const flatActive = `${flatBtn} bg-white/8 text-neutral-100 active:scale-[0.98]`;
|
||||
const flatDisabled = `${flatBtn} text-neutral-700 cursor-not-allowed`;
|
||||
|
||||
return (
|
||||
@@ -297,7 +297,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
|
||||
className={
|
||||
!onToggleKeyframe
|
||||
? flatDisabled
|
||||
: `${flatBtn} active:scale-[0.98] hover:bg-white/[0.06] ${
|
||||
: `${flatBtn} active:scale-[0.98] hover:bg-white/6 ${
|
||||
keyframeState === "active"
|
||||
? "text-studio-accent"
|
||||
: keyframeState === "inactive"
|
||||
@@ -332,7 +332,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
|
||||
onClick={() => setAutoKeyframeEnabled(!autoKeyframeEnabled)}
|
||||
aria-label="Auto-record manual edits as keyframes"
|
||||
aria-pressed={autoKeyframeEnabled}
|
||||
className={`${flatBtn} active:scale-[0.98] hover:bg-white/[0.06] ${
|
||||
className={`${flatBtn} active:scale-[0.98] hover:bg-white/6 ${
|
||||
autoKeyframeEnabled
|
||||
? "text-red-400 hover:text-red-300"
|
||||
: "text-neutral-600 hover:text-neutral-400"
|
||||
@@ -429,7 +429,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
|
||||
}}
|
||||
className={
|
||||
canAdd
|
||||
? `${flatBtn} text-neutral-400 hover:bg-white/[0.06] hover:text-[#22c55e] active:scale-[0.98]`
|
||||
? `${flatBtn} text-neutral-400 hover:bg-white/6 hover:text-[#22c55e] active:scale-[0.98]`
|
||||
: flatDisabled
|
||||
}
|
||||
>
|
||||
@@ -467,7 +467,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
|
||||
className={`h-7 px-2 rounded-md text-[11px] font-medium transition-colors ${
|
||||
thumbnailsVisible
|
||||
? "bg-studio-accent/10 text-studio-accent"
|
||||
: "text-neutral-400 hover:bg-white/[0.06] hover:text-neutral-200"
|
||||
: "text-neutral-400 hover:bg-white/6 hover:text-neutral-200"
|
||||
}`}
|
||||
>
|
||||
<Image size={16} aria-hidden="true" />
|
||||
@@ -480,7 +480,7 @@ export function TimelineToolbar({ domEditSession, onSplitElement }: TimelineTool
|
||||
className={`h-7 px-2 rounded-md text-[11px] font-medium transition-colors ${
|
||||
zoomMode === "fit"
|
||||
? "bg-studio-accent/10 text-studio-accent"
|
||||
: "text-neutral-400 hover:bg-white/[0.06] hover:text-neutral-200"
|
||||
: "text-neutral-400 hover:bg-white/6 hover:text-neutral-200"
|
||||
}`}
|
||||
>
|
||||
Fit
|
||||
|
||||
@@ -172,7 +172,7 @@ export const AnimationCard = memo(function AnimationCard({
|
||||
return (
|
||||
<div className="border-b border-neutral-800 pb-2">
|
||||
<div className="flex items-center gap-2 py-1.5">
|
||||
<span className="rounded bg-neutral-800 px-1.5 py-0.5 text-[10px] font-medium text-neutral-400">
|
||||
<span className="rounded-sm bg-neutral-800 px-1.5 py-0.5 text-[10px] font-medium text-neutral-400">
|
||||
Position
|
||||
</span>
|
||||
<span className="text-[11px] text-neutral-500">
|
||||
@@ -201,7 +201,7 @@ export const AnimationCard = memo(function AnimationCard({
|
||||
className="flex w-full items-center gap-2 py-1.5 active:scale-[0.99]"
|
||||
>
|
||||
<span
|
||||
className="rounded bg-panel-accent/10 px-1.5 py-0.5 text-[10px] font-semibold text-panel-accent"
|
||||
className="rounded-sm bg-panel-accent/10 px-1.5 py-0.5 text-[10px] font-semibold text-panel-accent"
|
||||
title={METHOD_TOOLTIPS[animation.method]}
|
||||
>
|
||||
{methodLabel}
|
||||
@@ -226,7 +226,7 @@ export const AnimationCard = memo(function AnimationCard({
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
fill="currentColor"
|
||||
className={`flex-shrink-0 transition-transform ${flat ? "text-panel-text-5" : "text-neutral-500"} ${expanded ? "" : "-rotate-90"}`}
|
||||
className={`shrink-0 transition-transform ${flat ? "text-panel-text-5" : "text-neutral-500"} ${expanded ? "" : "-rotate-90"}`}
|
||||
>
|
||||
<path d="M2 3l3 4 3-4z" />
|
||||
</svg>
|
||||
@@ -262,7 +262,7 @@ export const AnimationCard = memo(function AnimationCard({
|
||||
setCopied(true);
|
||||
setTimeout(() => setCopied(false), 1500);
|
||||
}}
|
||||
className="flex-shrink-0 rounded px-1.5 py-0.5 text-[9px] font-medium text-neutral-500 transition-colors hover:bg-neutral-800 hover:text-neutral-300"
|
||||
className="shrink-0 rounded-sm px-1.5 py-0.5 text-[9px] font-medium text-neutral-500 transition-colors hover:bg-neutral-800 hover:text-neutral-300"
|
||||
title="Copy description to clipboard — paste into agent prompts"
|
||||
>
|
||||
{copied ? "Copied" : "Copy"}
|
||||
|
||||
@@ -46,7 +46,7 @@ function RemoveButton({ onClick, title }: { onClick: () => void; title: string }
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
className="relative flex-shrink-0 rounded p-1.5 text-neutral-600 transition-colors hover:bg-neutral-800 hover:text-red-400 active:scale-[0.95]"
|
||||
className="relative shrink-0 rounded-sm p-1.5 text-neutral-600 transition-colors hover:bg-neutral-800 hover:text-red-400 active:scale-[0.95]"
|
||||
title={title}
|
||||
aria-label={title}
|
||||
>
|
||||
@@ -91,7 +91,7 @@ export function PropertyRow({
|
||||
role="switch"
|
||||
aria-checked={isVisible}
|
||||
onClick={() => onCommit(isVisible ? "hidden" : "visible")}
|
||||
className="flex-shrink-0 rounded-full transition-colors duration-200 relative"
|
||||
className="shrink-0 rounded-full transition-colors duration-200 relative"
|
||||
style={{ width: 28, height: 16, background: isVisible ? P.accent : P.borderInput }}
|
||||
title={isVisible ? "Visible — click to hide" : "Hidden — click to show"}
|
||||
>
|
||||
@@ -117,13 +117,13 @@ export function PropertyRow({
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="flex items-center gap-1">
|
||||
<div className="min-w-0 flex-1 flex items-center gap-2 px-2 py-1 rounded-lg bg-neutral-900 border border-neutral-800">
|
||||
<span className="flex-shrink-0 text-[11px] font-medium text-neutral-500">
|
||||
<span className="shrink-0 text-[11px] font-medium text-neutral-500">
|
||||
{PROP_LABELS[prop] ?? prop}
|
||||
</span>
|
||||
<input
|
||||
type="text"
|
||||
defaultValue={String(val)}
|
||||
className="flex-1 bg-transparent text-[11px] text-neutral-200 outline-none"
|
||||
className="flex-1 bg-transparent text-[11px] text-neutral-200 outline-hidden"
|
||||
onBlur={(e) => onCommit(e.currentTarget.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") e.currentTarget.blur();
|
||||
@@ -139,7 +139,7 @@ export function PropertyRow({
|
||||
key={p.value}
|
||||
type="button"
|
||||
onClick={() => onCommit(p.value)}
|
||||
className="px-1.5 py-0.5 rounded text-[9px] font-medium text-neutral-500 bg-neutral-800/50 hover:bg-neutral-800 hover:text-neutral-300 transition-colors"
|
||||
className="px-1.5 py-0.5 rounded-sm text-[9px] font-medium text-neutral-500 bg-neutral-800/50 hover:bg-neutral-800 hover:text-neutral-300 transition-colors"
|
||||
>
|
||||
{p.label}
|
||||
</button>
|
||||
@@ -190,7 +190,7 @@ export function AddPropertyTrigger({
|
||||
return (
|
||||
<select
|
||||
autoFocus
|
||||
className="min-w-0 rounded-lg border border-neutral-700 bg-neutral-900 px-2 py-1 text-[11px] text-neutral-100 outline-none"
|
||||
className="min-w-0 rounded-lg border border-neutral-700 bg-neutral-900 px-2 py-1 text-[11px] text-neutral-100 outline-hidden"
|
||||
defaultValue=""
|
||||
onChange={(e) => {
|
||||
if (e.target.value) onAdd(e.target.value);
|
||||
|
||||
@@ -222,7 +222,7 @@ function ParamControl({
|
||||
disabled={disabled}
|
||||
aria-label={`${param.label} color`}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="w-7 h-7 rounded border border-neutral-700 bg-transparent cursor-pointer disabled:cursor-not-allowed disabled:opacity-50"
|
||||
className="w-7 h-7 rounded-sm border border-neutral-700 bg-transparent cursor-pointer disabled:cursor-not-allowed disabled:opacity-50"
|
||||
/>
|
||||
<input
|
||||
type="text"
|
||||
@@ -230,7 +230,7 @@ function ParamControl({
|
||||
disabled={disabled}
|
||||
aria-label={`${param.label} value`}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="flex-1 bg-neutral-900 border border-neutral-800 rounded px-2 py-1 text-[10px] text-neutral-200 font-mono focus:outline-none focus:border-neutral-700 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
className="flex-1 bg-neutral-900 border border-neutral-800 rounded-sm px-2 py-1 text-[10px] text-neutral-200 font-mono focus:outline-hidden focus:border-neutral-700 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
@@ -259,7 +259,7 @@ function ParamControl({
|
||||
disabled={disabled}
|
||||
aria-label={param.label}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="w-full bg-neutral-900 border border-neutral-800 rounded px-2 py-1 text-[10px] text-neutral-200 focus:outline-none focus:border-neutral-700 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
className="w-full bg-neutral-900 border border-neutral-800 rounded-sm px-2 py-1 text-[10px] text-neutral-200 focus:outline-hidden focus:border-neutral-700 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
/>
|
||||
)}
|
||||
|
||||
@@ -269,7 +269,7 @@ function ParamControl({
|
||||
disabled={disabled}
|
||||
aria-label={param.label}
|
||||
onChange={(e) => onChange(e.target.value)}
|
||||
className="w-full bg-neutral-900 border border-neutral-800 rounded px-2 py-1 text-[10px] text-neutral-200 focus:outline-none focus:border-neutral-700 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
className="w-full bg-neutral-900 border border-neutral-800 rounded-sm px-2 py-1 text-[10px] text-neutral-200 focus:outline-hidden focus:border-neutral-700 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
>
|
||||
{param.options.map((opt) => (
|
||||
<option key={opt.value} value={opt.value}>
|
||||
|
||||
@@ -71,7 +71,7 @@ export function BorderRadiusEditor({
|
||||
width={PREVIEW_W}
|
||||
height={PREVIEW_H}
|
||||
viewBox={`0 0 ${PREVIEW_W} ${PREVIEW_H}`}
|
||||
className="flex-shrink-0"
|
||||
className="shrink-0"
|
||||
>
|
||||
<path
|
||||
d={path}
|
||||
|
||||
@@ -212,7 +212,7 @@ export const CanvasContextMenu = memo(function CanvasContextMenu({
|
||||
return createPortal(
|
||||
<div
|
||||
ref={menuRef}
|
||||
className="fixed z-[200] bg-neutral-900 border border-neutral-700 rounded-md shadow-lg py-1 min-w-[180px]"
|
||||
className="fixed z-200 bg-neutral-900 border border-neutral-700 rounded-md shadow-lg py-1 min-w-[180px]"
|
||||
style={{ left: adjustedX, top: adjustedY }}
|
||||
onPointerDown={stopBubble}
|
||||
onMouseDown={stopBubble}
|
||||
|
||||
@@ -29,7 +29,7 @@ export function ComputedTweenNotice({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onUnroll}
|
||||
className="flex-shrink-0 rounded px-1.5 py-0.5 text-[9px] font-medium text-panel-accent hover:bg-neutral-800"
|
||||
className="shrink-0 rounded-sm px-1.5 py-0.5 text-[9px] font-medium text-panel-accent hover:bg-neutral-800"
|
||||
title="Rewrite the helper/loop into explicit tweens so this keyframe edits directly"
|
||||
>
|
||||
Unroll to edit
|
||||
|
||||
@@ -465,7 +465,7 @@ export const DomEditOverlay = memo(function DomEditOverlay({
|
||||
ref={overlayRef}
|
||||
// Standing aside is the only way the caret below can be reached, and is
|
||||
// what keeps selection, drag and marquee from firing mid-edit.
|
||||
className={`absolute inset-0 z-10 outline-none ${
|
||||
className={`absolute inset-0 z-10 outline-hidden ${
|
||||
inlineText.editing ? "pointer-events-none" : "pointer-events-auto"
|
||||
}`}
|
||||
data-editing-text={inlineText.editing ? "true" : undefined}
|
||||
|
||||
@@ -518,7 +518,7 @@ export function EaseCurveSection({
|
||||
aria-valuemax={1}
|
||||
aria-valuenow={key === "p1" ? x1 : x2}
|
||||
aria-valuetext={`x ${key === "p1" ? x1 : x2}, y ${key === "p1" ? y1 : y2}`}
|
||||
className="cursor-grab stroke-transparent outline-none active:cursor-grabbing focus-visible:stroke-white focus-visible:stroke-[2px]"
|
||||
className="cursor-grab stroke-transparent outline-hidden active:cursor-grabbing focus-visible:stroke-white focus-visible:stroke-[2px]"
|
||||
onPointerDown={(e) => handlePointerDown(key, e)}
|
||||
onKeyDown={(event) => handleKeyDown(key, event)}
|
||||
onPointerEnter={() => setHover(key)}
|
||||
|
||||
@@ -93,7 +93,7 @@ export function EaseBezierField({
|
||||
}
|
||||
}}
|
||||
onBlur={(event) => commit(event.currentTarget.value)}
|
||||
className={`w-full rounded border bg-black/20 px-1.5 py-1 font-mono text-[10px] text-neutral-300 outline-none ${
|
||||
className={`w-full rounded border bg-black/20 px-1.5 py-1 font-mono text-[10px] text-neutral-300 outline-hidden ${
|
||||
error
|
||||
? "border-red-500/70 focus:border-red-400"
|
||||
: "border-white/10 focus:border-panel-accent/50"
|
||||
@@ -183,7 +183,7 @@ export function SpringBounceField({
|
||||
const bounce = parseSpringBounce(`spring(${value})`);
|
||||
if (bounce !== null) onCommit(`spring(${round2(bounce)})`);
|
||||
}}
|
||||
className="w-16 rounded border border-white/10 bg-black/20 px-1.5 py-1 font-mono text-[10px] text-neutral-300 outline-none focus:border-panel-accent/50"
|
||||
className="w-16 rounded-sm border border-white/10 bg-black/20 px-1.5 py-1 font-mono text-[10px] text-neutral-300 outline-hidden focus:border-panel-accent/50"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -207,7 +207,7 @@ export function WiggleField({
|
||||
min={1}
|
||||
step={1}
|
||||
onCommit={(value) => commitWiggle(onCommit, value, config.type, amplitude)}
|
||||
className="w-14 rounded border border-white/10 bg-black/20 px-1.5 py-1 font-mono text-[10px] text-neutral-300 outline-none focus:border-panel-accent/50"
|
||||
className="w-14 rounded-sm border border-white/10 bg-black/20 px-1.5 py-1 font-mono text-[10px] text-neutral-300 outline-hidden focus:border-panel-accent/50"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-1">
|
||||
@@ -220,7 +220,7 @@ export function WiggleField({
|
||||
commitWiggle(onCommit, config.wiggles, event.currentTarget.value, amplitude);
|
||||
}
|
||||
}}
|
||||
className="rounded border border-white/10 bg-black/20 px-1.5 py-1 text-[10px] text-neutral-300 outline-none focus:border-panel-accent/50"
|
||||
className="rounded-sm border border-white/10 bg-black/20 px-1.5 py-1 text-[10px] text-neutral-300 outline-hidden focus:border-panel-accent/50"
|
||||
>
|
||||
{WIGGLE_TYPES.map((type) => (
|
||||
<option key={type} value={type}>
|
||||
@@ -238,7 +238,7 @@ export function WiggleField({
|
||||
max={1}
|
||||
step={0.01}
|
||||
onCommit={(value) => commitWiggle(onCommit, config.wiggles, config.type, value)}
|
||||
className="w-16 rounded border border-white/10 bg-black/20 px-1.5 py-1 font-mono text-[10px] text-neutral-300 outline-none focus:border-panel-accent/50"
|
||||
className="w-16 rounded-sm border border-white/10 bg-black/20 px-1.5 py-1 font-mono text-[10px] text-neutral-300 outline-hidden focus:border-panel-accent/50"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -235,14 +235,14 @@ export const FileTree = memo(function FileTree({
|
||||
<div className="flex flex-col h-full min-h-0">
|
||||
{/* FILES header with action buttons */}
|
||||
{hasFileOps && (
|
||||
<div className="flex items-center justify-between px-2.5 py-1.5 border-b border-neutral-800/50 flex-shrink-0">
|
||||
<div className="flex items-center justify-between px-2.5 py-1.5 border-b border-neutral-800/50 shrink-0">
|
||||
<span className="text-[10px] font-semibold tracking-wider text-neutral-600 uppercase">
|
||||
Files
|
||||
</span>
|
||||
<div className="flex items-center gap-0.5">
|
||||
<button
|
||||
onClick={() => handleNewFile("")}
|
||||
className="p-1.5 rounded hover:bg-neutral-800 text-neutral-600 hover:text-neutral-400 active:scale-[0.97] transition-colors"
|
||||
className="p-1.5 rounded-sm hover:bg-neutral-800 text-neutral-600 hover:text-neutral-400 active:scale-[0.97] transition-colors"
|
||||
title="New File"
|
||||
aria-label="New File"
|
||||
>
|
||||
@@ -250,7 +250,7 @@ export const FileTree = memo(function FileTree({
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleNewFolder("")}
|
||||
className="p-1.5 rounded hover:bg-neutral-800 text-neutral-600 hover:text-neutral-400 active:scale-[0.97] transition-colors"
|
||||
className="p-1.5 rounded-sm hover:bg-neutral-800 text-neutral-600 hover:text-neutral-400 active:scale-[0.97] transition-colors"
|
||||
title="New Folder"
|
||||
aria-label="New Folder"
|
||||
>
|
||||
@@ -263,7 +263,7 @@ export const FileTree = memo(function FileTree({
|
||||
<div
|
||||
className={`flex-1 overflow-y-auto py-1 transition-colors ${
|
||||
dragOverFolder === ""
|
||||
? "bg-[#3CE6AC]/5 outline outline-1 outline-[#3CE6AC]/30 -outline-offset-1"
|
||||
? "bg-[#3CE6AC]/5 outline-solid outline-1 outline-[#3CE6AC]/30 -outline-offset-1"
|
||||
: ""
|
||||
}`}
|
||||
onContextMenu={handleRootContextMenu}
|
||||
|
||||
@@ -23,7 +23,7 @@ const W = "duotone" as const;
|
||||
|
||||
export function FileIcon({ path }: { path: string }) {
|
||||
const ext = path.split(".").pop()?.toLowerCase() ?? "";
|
||||
const c = "flex-shrink-0";
|
||||
const c = "shrink-0";
|
||||
if (ext === "html") return <FileHtml size={SZ} weight={W} color="#E44D26" className={c} />;
|
||||
if (ext === "css") return <FileCss size={SZ} weight={W} color="#264DE4" className={c} />;
|
||||
if (ext === "js" || ext === "mjs" || ext === "cjs")
|
||||
|
||||
@@ -107,7 +107,7 @@ export function ContextMenu({
|
||||
<>
|
||||
<button
|
||||
role="menuitem"
|
||||
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-neutral-300 hover:bg-neutral-800 focus-visible:bg-neutral-800 active:bg-neutral-700 outline-none cursor-pointer text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-neutral-300 hover:bg-neutral-800 focus-visible:bg-neutral-800 active:bg-neutral-700 outline-hidden cursor-pointer text-left"
|
||||
onClick={() => {
|
||||
onNewFile(state.targetPath);
|
||||
onClose();
|
||||
@@ -118,7 +118,7 @@ export function ContextMenu({
|
||||
</button>
|
||||
<button
|
||||
role="menuitem"
|
||||
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-neutral-300 hover:bg-neutral-800 focus-visible:bg-neutral-800 active:bg-neutral-700 outline-none cursor-pointer text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-neutral-300 hover:bg-neutral-800 focus-visible:bg-neutral-800 active:bg-neutral-700 outline-hidden cursor-pointer text-left"
|
||||
onClick={() => {
|
||||
onNewFolder(state.targetPath);
|
||||
onClose();
|
||||
@@ -134,7 +134,7 @@ export function ContextMenu({
|
||||
<>
|
||||
<button
|
||||
role="menuitem"
|
||||
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-neutral-300 hover:bg-neutral-800 focus-visible:bg-neutral-800 active:bg-neutral-700 outline-none cursor-pointer text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-neutral-300 hover:bg-neutral-800 focus-visible:bg-neutral-800 active:bg-neutral-700 outline-hidden cursor-pointer text-left"
|
||||
onClick={() => {
|
||||
onNewFile(parentPath);
|
||||
onClose();
|
||||
@@ -148,7 +148,7 @@ export function ContextMenu({
|
||||
)}
|
||||
<button
|
||||
role="menuitem"
|
||||
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-neutral-300 hover:bg-neutral-800 focus-visible:bg-neutral-800 active:bg-neutral-700 outline-none cursor-pointer text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-neutral-300 hover:bg-neutral-800 focus-visible:bg-neutral-800 active:bg-neutral-700 outline-hidden cursor-pointer text-left"
|
||||
onClick={() => {
|
||||
onRename(state.targetPath);
|
||||
onClose();
|
||||
@@ -160,7 +160,7 @@ export function ContextMenu({
|
||||
{!state.targetIsFolder && (
|
||||
<button
|
||||
role="menuitem"
|
||||
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-neutral-300 hover:bg-neutral-800 focus-visible:bg-neutral-800 active:bg-neutral-700 outline-none cursor-pointer text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-neutral-300 hover:bg-neutral-800 focus-visible:bg-neutral-800 active:bg-neutral-700 outline-hidden cursor-pointer text-left"
|
||||
onClick={() => {
|
||||
onDuplicate(state.targetPath);
|
||||
onClose();
|
||||
@@ -173,7 +173,7 @@ export function ContextMenu({
|
||||
<div className="border-t border-neutral-700 my-1" />
|
||||
<button
|
||||
role="menuitem"
|
||||
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-red-400 hover:bg-red-900/30 focus-visible:bg-red-900/30 active:bg-red-900/50 outline-none cursor-pointer text-left"
|
||||
className="w-full flex items-center gap-2 px-3 py-1.5 text-xs text-red-400 hover:bg-red-900/30 focus-visible:bg-red-900/30 active:bg-red-900/50 outline-hidden cursor-pointer text-left"
|
||||
onClick={() => {
|
||||
onDelete(state.targetPath);
|
||||
onClose();
|
||||
@@ -263,7 +263,7 @@ export function InlineInput({
|
||||
style={{ paddingLeft: `${8 + depth * 12 + (isFolder ? 0 : 14)}px` }}
|
||||
>
|
||||
{isFolder ? (
|
||||
<FolderSimple size={SZ_ICON} weight="duotone" color="#6B7280" className="flex-shrink-0" />
|
||||
<FolderSimple size={SZ_ICON} weight="duotone" color="#6B7280" className="shrink-0" />
|
||||
) : (
|
||||
<FileIcon path={value} />
|
||||
)}
|
||||
@@ -278,7 +278,7 @@ export function InlineInput({
|
||||
onKeyDown={handleKeyDown}
|
||||
onBlur={handleBlur}
|
||||
aria-invalid={error ? true : undefined}
|
||||
className={`w-full min-w-0 bg-neutral-800 text-neutral-200 text-xs px-1.5 py-0.5 rounded border outline-none ${
|
||||
className={`w-full min-w-0 bg-neutral-800 text-neutral-200 text-xs px-1.5 py-0.5 rounded border outline-hidden ${
|
||||
error ? "border-red-500/70" : "border-neutral-600 focus:border-[#3CE6AC]"
|
||||
}`}
|
||||
spellCheck={false}
|
||||
@@ -344,13 +344,13 @@ export function DeleteConfirm({
|
||||
<div className="flex gap-1.5">
|
||||
<button
|
||||
onClick={onCancel}
|
||||
className="flex-1 px-2 py-1 rounded bg-neutral-700 text-neutral-300 hover:bg-neutral-600 transition-colors"
|
||||
className="flex-1 px-2 py-1 rounded-sm bg-neutral-700 text-neutral-300 hover:bg-neutral-600 transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={onConfirm}
|
||||
className="flex-1 px-2 py-1 rounded bg-red-900/60 text-red-300 hover:bg-red-800/60 transition-colors"
|
||||
className="flex-1 px-2 py-1 rounded-sm bg-red-900/60 text-red-300 hover:bg-red-800/60 transition-colors"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
@@ -419,7 +419,7 @@ export const TreeFile = memo(function TreeFile({
|
||||
<span className="truncate flex-1">{node.name}</span>
|
||||
{lintInfo && lintInfo.count > 0 && (
|
||||
<span
|
||||
className="flex-shrink-0 min-w-[16px] rounded-full bg-amber-500/20 px-1 text-[8px] font-bold text-amber-400 text-center mr-1"
|
||||
className="shrink-0 min-w-[16px] rounded-full bg-amber-500/20 px-1 text-[8px] font-bold text-amber-400 text-center mr-1"
|
||||
title={lintInfo.messages.join("\n")}
|
||||
>
|
||||
{lintInfo.count}
|
||||
@@ -505,11 +505,11 @@ export const TreeFolder = memo(function TreeFolder({
|
||||
}}
|
||||
onDragLeave={onDragLeave}
|
||||
className={`w-full flex items-center gap-1.5 px-2.5 py-1 min-h-7 text-left text-xs text-neutral-400 hover:bg-neutral-800/30 hover:text-neutral-300 transition-colors ${
|
||||
isDragOver ? "bg-[#3CE6AC]/10 outline outline-1 outline-[#3CE6AC]/40" : ""
|
||||
isDragOver ? "bg-[#3CE6AC]/10 outline-solid outline-1 outline-[#3CE6AC]/40" : ""
|
||||
}`}
|
||||
style={{ paddingLeft: `${8 + depth * 12}px` }}
|
||||
>
|
||||
<Chevron size={10} className="flex-shrink-0 text-neutral-600" />
|
||||
<Chevron size={10} className="shrink-0 text-neutral-600" />
|
||||
<span className="truncate font-medium">{node.name}</span>
|
||||
</button>
|
||||
{isOpen && (
|
||||
|
||||
@@ -77,7 +77,7 @@ export function InlineTextToolbar({
|
||||
data-inline-text-toolbar="true"
|
||||
role="toolbar"
|
||||
aria-label="Text formatting"
|
||||
className="pointer-events-auto fixed z-[200] flex items-center gap-1 rounded-lg border border-white/10 bg-[#15171c] p-1 shadow-[0_8px_24px_rgba(0,0,0,0.55)]"
|
||||
className="pointer-events-auto fixed z-200 flex items-center gap-1 rounded-lg border border-white/10 bg-[#15171c] p-1 shadow-[0_8px_24px_rgba(0,0,0,0.55)]"
|
||||
style={{
|
||||
left: placement.left,
|
||||
top: placement.top,
|
||||
|
||||
@@ -37,7 +37,7 @@ export function InspectorHeaderActions({
|
||||
track("toggle", "Element visibility");
|
||||
void onToggleHidden(selectedElementId, !selectedElementHidden);
|
||||
}}
|
||||
className="flex h-6 w-6 items-center justify-center rounded text-neutral-500 transition-colors hover:bg-neutral-800 hover:text-neutral-300"
|
||||
className="flex h-6 w-6 items-center justify-center rounded-sm text-neutral-500 transition-colors hover:bg-neutral-800 hover:text-neutral-300"
|
||||
>
|
||||
{selectedElementHidden ? (
|
||||
<EyeSlash size={13} weight="bold" aria-hidden="true" />
|
||||
@@ -54,7 +54,7 @@ export function InspectorHeaderActions({
|
||||
onUngroup();
|
||||
}}
|
||||
title="Ungroup (⌘⇧G)"
|
||||
className="flex h-6 items-center rounded px-2 text-[11px] font-medium text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-neutral-200"
|
||||
className="flex h-6 items-center rounded-sm px-2 text-[11px] font-medium text-neutral-400 transition-colors hover:bg-neutral-800 hover:text-neutral-200"
|
||||
>
|
||||
Ungroup
|
||||
</button>
|
||||
@@ -91,7 +91,7 @@ export function InspectorHeaderActions({
|
||||
track("button", "Clear selection");
|
||||
onClear();
|
||||
}}
|
||||
className="flex h-6 w-6 items-center justify-center rounded text-neutral-500 transition-colors hover:bg-neutral-800 hover:text-neutral-300"
|
||||
className="flex h-6 w-6 items-center justify-center rounded-sm text-neutral-500 transition-colors hover:bg-neutral-800 hover:text-neutral-300"
|
||||
>
|
||||
<X size={13} />
|
||||
</button>
|
||||
|
||||
@@ -29,7 +29,7 @@ export const KeyframeDiamond = memo(function KeyframeDiamond({
|
||||
e.stopPropagation();
|
||||
onClick();
|
||||
}}
|
||||
className="relative flex-shrink-0 p-0.5 transition-opacity hover:opacity-100 before:absolute before:-inset-1.5 before:content-['']"
|
||||
className="relative shrink-0 p-0.5 transition-opacity hover:opacity-100 before:absolute before:-inset-1.5 before:content-['']"
|
||||
style={{ color, opacity }}
|
||||
title={title}
|
||||
aria-label={title}
|
||||
|
||||
@@ -74,7 +74,7 @@ export function KeyframeEaseList({
|
||||
const next = e.target.value;
|
||||
if (next) onApplyAll(next);
|
||||
}}
|
||||
className="ml-auto cursor-pointer rounded bg-neutral-800 px-1.5 py-0.5 text-[9px] text-neutral-300 outline-none hover:bg-neutral-700 focus:ring-1 focus:ring-panel-accent/40"
|
||||
className="ml-auto cursor-pointer rounded-sm bg-neutral-800 px-1.5 py-0.5 text-[9px] text-neutral-300 outline-hidden hover:bg-neutral-700 focus:ring-1 focus:ring-panel-accent/40"
|
||||
>
|
||||
<option value="" disabled>
|
||||
Set all…
|
||||
|
||||
@@ -485,7 +485,7 @@ export const LayersPanel = memo(function LayersPanel() {
|
||||
onClick={(e) => toggleCollapse(layer.key, e)}
|
||||
aria-expanded={!isCollapsed}
|
||||
aria-label={isCollapsed ? "Expand children" : "Collapse children"}
|
||||
className="relative flex h-4 w-4 flex-shrink-0 items-center justify-center rounded text-neutral-500 hover:text-neutral-300 before:absolute before:-inset-1.5 before:content-['']"
|
||||
className="relative flex h-4 w-4 shrink-0 items-center justify-center rounded-sm text-neutral-500 hover:text-neutral-300 before:absolute before:-inset-1.5 before:content-['']"
|
||||
>
|
||||
<svg
|
||||
width="8"
|
||||
@@ -498,10 +498,10 @@ export const LayersPanel = memo(function LayersPanel() {
|
||||
</svg>
|
||||
</button>
|
||||
) : (
|
||||
<span className="w-4 flex-shrink-0" />
|
||||
<span className="w-4 shrink-0" />
|
||||
)}
|
||||
<span
|
||||
className={`flex h-5 w-5 flex-shrink-0 items-center justify-center rounded text-[8px] font-bold uppercase ${
|
||||
className={`flex h-5 w-5 shrink-0 items-center justify-center rounded text-[8px] font-bold uppercase ${
|
||||
selected
|
||||
? "bg-panel-accent/18 text-panel-accent"
|
||||
: isCompHost
|
||||
|
||||
@@ -19,7 +19,7 @@ export function MarqueeOverlay({ candidateRects, marqueeRect }: MarqueeOverlayPr
|
||||
<div
|
||||
key={i}
|
||||
aria-hidden="true"
|
||||
className="pointer-events-none absolute rounded-sm border border-studio-accent bg-studio-accent/5"
|
||||
className="pointer-events-none absolute rounded-xs border border-studio-accent bg-studio-accent/5"
|
||||
style={{ left: r.left, top: r.top, width: r.width, height: r.height }}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -167,7 +167,7 @@ export function ChildRectOutlines({
|
||||
{rects.map((rect, index) => (
|
||||
<div
|
||||
key={index}
|
||||
className="pointer-events-none absolute border border-dashed border-white/20 rounded-sm"
|
||||
className="pointer-events-none absolute border border-dashed border-white/20 rounded-xs"
|
||||
style={{
|
||||
left: rect.left,
|
||||
top: rect.top,
|
||||
|
||||
@@ -83,7 +83,7 @@ export function PromotableControl({
|
||||
<span
|
||||
// Sits above the row (not on top of top-0) so it clears a value that
|
||||
// renders flush to the row's right edge, e.g. flat Font/Color rows.
|
||||
className="pointer-events-none absolute -top-2 right-1.5 z-10 inline-flex max-w-[60%] items-center gap-1 truncate rounded bg-studio-accent/20 px-1 py-px font-mono text-[8px] font-medium text-studio-accent"
|
||||
className="pointer-events-none absolute -top-2 right-1.5 z-10 inline-flex max-w-[60%] items-center gap-1 truncate rounded-sm bg-studio-accent/20 px-1 py-px font-mono text-[8px] font-medium text-studio-accent"
|
||||
title={`Bound to variable "${promote.boundId}"`}
|
||||
>
|
||||
◆ {promote.boundId}
|
||||
@@ -97,7 +97,7 @@ export function PromotableControl({
|
||||
e.stopPropagation();
|
||||
promote.promote();
|
||||
}}
|
||||
className="absolute -top-2 right-1.5 z-10 inline-flex items-center gap-1 rounded bg-neutral-800/80 px-1 py-px font-mono text-[8px] font-medium text-neutral-400 opacity-70 transition-colors hover:bg-studio-accent/20 hover:text-studio-accent hover:opacity-100"
|
||||
className="absolute -top-2 right-1.5 z-10 inline-flex items-center gap-1 rounded-sm bg-neutral-800/80 px-1 py-px font-mono text-[8px] font-medium text-neutral-400 opacity-70 transition-colors hover:bg-studio-accent/20 hover:text-studio-accent hover:opacity-100"
|
||||
>
|
||||
◇ var
|
||||
</button>
|
||||
|
||||
@@ -18,7 +18,7 @@ function FlatEmptyState() {
|
||||
</svg>
|
||||
</span>
|
||||
<div className="text-[13px] font-semibold text-panel-text-0">Nothing selected</div>
|
||||
<div className="max-w-[250px] text-[11px] leading-[1.5] text-panel-text-3">
|
||||
<div className="max-w-[250px] text-[11px] leading-normal text-panel-text-3">
|
||||
Click any element on the canvas to edit it, or drag to select several.
|
||||
</div>
|
||||
<div className="mt-2 flex w-full flex-col gap-1.5">
|
||||
@@ -27,7 +27,7 @@ function FlatEmptyState() {
|
||||
<span className="text-panel-danger">●</span>
|
||||
Record a gesture
|
||||
</span>
|
||||
<span className="rounded border border-panel-border-input px-[5px] py-px font-mono text-[9px] text-panel-text-5">
|
||||
<span className="rounded-sm border border-panel-border-input px-[5px] py-px font-mono text-[9px] text-panel-text-5">
|
||||
R
|
||||
</span>
|
||||
</span>
|
||||
@@ -36,7 +36,7 @@ function FlatEmptyState() {
|
||||
<span className="text-panel-accent">✦</span>
|
||||
Describe a change to the agent
|
||||
</span>
|
||||
<span className="rounded border border-panel-border-input px-[5px] py-px font-mono text-[9px] text-panel-text-5">
|
||||
<span className="rounded-sm border border-panel-border-input px-[5px] py-px font-mono text-[9px] text-panel-text-5">
|
||||
⌘K
|
||||
</span>
|
||||
</span>
|
||||
@@ -74,7 +74,7 @@ function FlatMultiSelectState({
|
||||
return (
|
||||
<div className="flex flex-col gap-3 px-4 py-3">
|
||||
<div className="flex items-center gap-3 rounded-xl border border-panel-border bg-panel-surface p-3">
|
||||
<span className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-lg bg-panel-accent/10 text-panel-accent">
|
||||
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-panel-accent/10 text-panel-accent">
|
||||
<Layers size={16} />
|
||||
</span>
|
||||
<div className="min-w-0 flex-1">
|
||||
@@ -90,7 +90,7 @@ function FlatMultiSelectState({
|
||||
data-flat-multiselect-clear="true"
|
||||
aria-label="Clear selection"
|
||||
onClick={onClearSelection}
|
||||
className="flex h-[26px] w-[26px] flex-shrink-0 items-center justify-center text-panel-text-3"
|
||||
className="flex h-[26px] w-[26px] shrink-0 items-center justify-center text-panel-text-3"
|
||||
>
|
||||
<svg
|
||||
width="13"
|
||||
@@ -113,14 +113,14 @@ function FlatMultiSelectState({
|
||||
className="flex items-center gap-2 rounded-lg border border-panel-border bg-panel-bg px-2.5 py-[7px]"
|
||||
>
|
||||
<span
|
||||
className={`flex h-[18px] w-[18px] flex-shrink-0 items-center justify-center rounded text-[9px] font-bold ${className}`}
|
||||
className={`flex h-[18px] w-[18px] shrink-0 items-center justify-center rounded-sm text-[9px] font-bold ${className}`}
|
||||
>
|
||||
{glyph}
|
||||
</span>
|
||||
<span className="min-w-0 flex-1 truncate text-[11px] text-panel-text-1">
|
||||
{element.label}
|
||||
</span>
|
||||
<span className="flex-shrink-0 font-mono text-[9px] text-panel-text-4">
|
||||
<span className="shrink-0 font-mono text-[9px] text-panel-text-4">
|
||||
{element.id ? `#${element.id}` : element.selector}
|
||||
</span>
|
||||
</span>
|
||||
|
||||
@@ -41,13 +41,13 @@ export function PropertyPanelFlatHeader({
|
||||
<Icon
|
||||
size={15}
|
||||
data-flat-header-icon="true"
|
||||
className={`flex-shrink-0 ${ICON_COLOR_BY_KIND[elementKind]}`}
|
||||
className={`shrink-0 ${ICON_COLOR_BY_KIND[elementKind]}`}
|
||||
/>
|
||||
<div className="flex min-w-0 flex-1 items-baseline gap-2">
|
||||
<span className="truncate text-[13px] font-semibold text-panel-text-0">{name}</span>
|
||||
<span className="truncate font-mono text-[10px] text-panel-text-4">{meta}</span>
|
||||
</div>
|
||||
<div className="flex flex-shrink-0 items-center gap-2.5 text-panel-text-3">
|
||||
<div className="flex shrink-0 items-center gap-2.5 text-panel-text-3">
|
||||
{showUngroup && (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -179,7 +179,7 @@ export const SnapToolbar = memo(function SnapToolbar({ onSnapChange }: SnapToolb
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="absolute -right-0.5 -bottom-0.5 rounded p-0.5 text-white/50 hover:text-white/90 bg-black/50"
|
||||
className="absolute -right-0.5 -bottom-0.5 rounded-sm p-0.5 text-white/50 hover:text-white/90 bg-black/50"
|
||||
onClick={() => setGridPopoverOpen((v) => !v)}
|
||||
title="Grid options"
|
||||
aria-label="Grid options"
|
||||
@@ -209,7 +209,7 @@ export const SnapToolbar = memo(function SnapToolbar({ onSnapChange }: SnapToolb
|
||||
updatePrefs({ gridSpacing: val });
|
||||
}
|
||||
}}
|
||||
className="w-16 rounded bg-neutral-900 border border-neutral-600 px-1.5 py-0.5 text-xs text-white text-right tabular-nums outline-none focus:border-studio-accent"
|
||||
className="w-16 rounded-sm bg-neutral-900 border border-neutral-600 px-1.5 py-0.5 text-xs text-white text-right tabular-nums outline-hidden focus:border-studio-accent"
|
||||
/>
|
||||
</label>
|
||||
<label className="flex items-center gap-2 text-xs text-white/80 cursor-pointer">
|
||||
|
||||
@@ -139,7 +139,7 @@ export function TimelineFxPopover({
|
||||
ref={rootRef}
|
||||
role="dialog"
|
||||
aria-label="Effects"
|
||||
className="z-[200] flex flex-col overflow-hidden rounded-md border border-white/10 bg-[#1b1b1f] p-2 shadow-xl"
|
||||
className="z-200 flex flex-col overflow-hidden rounded-md border border-white/10 bg-[#1b1b1f] p-2 shadow-xl"
|
||||
style={clampedStyle(anchorRect)}
|
||||
onKeyDown={onKeyDown}
|
||||
onPointerDown={(event) => event.stopPropagation()}
|
||||
|
||||
@@ -143,7 +143,7 @@ export function TopologyLens({ iframeRef, activeCompositionPath }: TopologyLensP
|
||||
ref={overlayRef}
|
||||
aria-hidden="true"
|
||||
data-topology-lens={visible ? state.phase : "hidden"}
|
||||
className="pointer-events-none absolute inset-0 z-[58] overflow-hidden"
|
||||
className="pointer-events-none absolute inset-0 z-58 overflow-hidden"
|
||||
>
|
||||
{visible && (
|
||||
<>
|
||||
@@ -171,7 +171,7 @@ export function TopologyLens({ iframeRef, activeCompositionPath }: TopologyLensP
|
||||
key={index}
|
||||
data-topology-contour="true"
|
||||
data-topology-node={label}
|
||||
className="hf-topology-contour pointer-events-none absolute z-[1] rounded"
|
||||
className="hf-topology-contour pointer-events-none absolute z-1 rounded-sm"
|
||||
style={{
|
||||
...rectStyle(rect),
|
||||
animationDelay: `${Math.min(index, 5) * 28}ms`,
|
||||
@@ -181,7 +181,7 @@ export function TopologyLens({ iframeRef, activeCompositionPath }: TopologyLensP
|
||||
{!reducedMotion && (
|
||||
<div
|
||||
data-topology-scan="true"
|
||||
className="pointer-events-none absolute z-[2] overflow-hidden rounded"
|
||||
className="pointer-events-none absolute z-2 overflow-hidden rounded-sm"
|
||||
style={rectStyle(geometry.field.rect)}
|
||||
>
|
||||
<div className="hf-topology-scan absolute inset-y-0 left-0 w-1/4" />
|
||||
@@ -196,7 +196,7 @@ export function TopologyLens({ iframeRef, activeCompositionPath }: TopologyLensP
|
||||
data-topology-terminal={
|
||||
state.phase === "localizing" ? (state.terminal ?? undefined) : undefined
|
||||
}
|
||||
className="hf-topology-target pointer-events-none absolute z-[3] rounded-md"
|
||||
className="hf-topology-target pointer-events-none absolute z-3 rounded-md"
|
||||
style={rectStyle(geometry.target.rect)}
|
||||
/>
|
||||
</>
|
||||
|
||||
@@ -183,7 +183,7 @@ export function Transform3DCube({
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative overflow-hidden rounded-lg border border-neutral-800 bg-gradient-to-b from-neutral-900 to-neutral-950">
|
||||
<div className="relative overflow-hidden rounded-lg border border-neutral-800 bg-linear-to-b from-neutral-900 to-neutral-950">
|
||||
<svg
|
||||
ref={svgRef}
|
||||
viewBox={`0 0 ${VIEW_W} ${VIEW_H}`}
|
||||
@@ -294,7 +294,7 @@ export function Transform3DCube({
|
||||
onClick={onRecenter}
|
||||
title="Reset 3D orientation"
|
||||
aria-label="Reset 3D orientation"
|
||||
className="absolute right-1.5 top-1.5 rounded p-0.5 text-neutral-500 hover:bg-neutral-800 hover:text-neutral-200"
|
||||
className="absolute right-1.5 top-1.5 rounded-sm p-0.5 text-neutral-500 hover:bg-neutral-800 hover:text-neutral-200"
|
||||
>
|
||||
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor">
|
||||
<circle cx="12" cy="12" r="9" strokeWidth="2" />
|
||||
|
||||
@@ -79,7 +79,7 @@ function ColorSlider({
|
||||
aria-valuemax={max}
|
||||
aria-valuenow={value}
|
||||
aria-disabled={disabled}
|
||||
className={`relative h-4 rounded-full border border-neutral-700 shadow-[inset_0_1px_2px_rgba(0,0,0,0.55)] outline-none focus:border-panel-accent focus:ring-2 focus:ring-panel-accent/40 ${
|
||||
className={`relative h-4 rounded-full border border-neutral-700 shadow-[inset_0_1px_2px_rgba(0,0,0,0.55)] outline-hidden focus:border-panel-accent focus:ring-2 focus:ring-panel-accent/40 ${
|
||||
disabled ? "cursor-not-allowed opacity-50" : "cursor-ew-resize"
|
||||
}`}
|
||||
style={{ background }}
|
||||
@@ -328,7 +328,7 @@ export function ColorField({
|
||||
role="dialog"
|
||||
aria-label={`${label} color picker`}
|
||||
tabIndex={-1}
|
||||
className="fixed z-[9999] w-[292px] overflow-hidden rounded-2xl border border-neutral-700 bg-neutral-950 shadow-2xl shadow-black/50 outline-none"
|
||||
className="fixed z-9999 w-[292px] overflow-hidden rounded-2xl border border-neutral-700 bg-neutral-950 shadow-2xl shadow-black/50 outline-hidden"
|
||||
style={{
|
||||
left: panelPosition?.left ?? -9999,
|
||||
top: panelPosition?.top ?? -9999,
|
||||
@@ -367,8 +367,8 @@ export function ColorField({
|
||||
onPointerUp={settleColorGesture}
|
||||
onPointerCancel={cancelColorGesture}
|
||||
>
|
||||
<div className="absolute inset-0 bg-gradient-to-r from-white to-transparent" />
|
||||
<div className="absolute inset-0 bg-gradient-to-t from-black to-transparent" />
|
||||
<div className="absolute inset-0 bg-linear-to-r from-white to-transparent" />
|
||||
<div className="absolute inset-0 bg-linear-to-t from-black to-transparent" />
|
||||
<div
|
||||
className="pointer-events-none absolute top-0 h-full w-px -translate-x-1/2 bg-white/70 shadow-[0_0_0_1px_rgba(0,0,0,0.45)] mix-blend-difference"
|
||||
style={{ left: `${hsv.saturation * 100}%` }}
|
||||
@@ -389,7 +389,7 @@ export function ColorField({
|
||||
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<div
|
||||
className="h-9 w-9 flex-shrink-0 rounded-xl border border-neutral-600 shadow-[inset_0_1px_0_rgba(255,255,255,0.08)]"
|
||||
className="h-9 w-9 shrink-0 rounded-xl border border-neutral-600 shadow-[inset_0_1px_0_rgba(255,255,255,0.08)]"
|
||||
style={{ backgroundColor: currentColor }}
|
||||
/>
|
||||
<div className="min-w-0 flex-1">
|
||||
@@ -442,7 +442,7 @@ export function ColorField({
|
||||
value={hexDraft}
|
||||
onChange={(event) => handleHexChange(event.target.value)}
|
||||
onBlur={settleColorGesture}
|
||||
className={`${FIELD} h-10 w-full text-[11px] font-medium outline-none`}
|
||||
className={`${FIELD} h-10 w-full text-[11px] font-medium outline-hidden`}
|
||||
spellCheck={false}
|
||||
/>
|
||||
</label>
|
||||
@@ -475,7 +475,7 @@ export function ColorField({
|
||||
className="flex items-center gap-2 disabled:cursor-not-allowed"
|
||||
>
|
||||
<span
|
||||
className="h-4 w-4 flex-shrink-0 rounded-[4px]"
|
||||
className="h-4 w-4 shrink-0 rounded-[4px]"
|
||||
style={{ backgroundColor: open ? currentColor : value || "transparent" }}
|
||||
/>
|
||||
<span className="font-mono text-[11px] text-panel-text-0">
|
||||
@@ -484,7 +484,7 @@ export function ColorField({
|
||||
{mixed && (
|
||||
<span
|
||||
data-color-mixed-indicator="true"
|
||||
className="rounded bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4"
|
||||
className="rounded-sm bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4"
|
||||
>
|
||||
Mixed
|
||||
</span>
|
||||
@@ -504,7 +504,7 @@ export function ColorField({
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
onClick={onReset}
|
||||
className="rounded bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4 transition-colors hover:text-panel-text-0 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
className="rounded-sm bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4 transition-colors hover:text-panel-text-0 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
@@ -519,7 +519,7 @@ export function ColorField({
|
||||
className={`${FIELD} flex items-center gap-3 text-left hover:border-neutral-700 disabled:cursor-not-allowed ${open ? "border-neutral-600" : ""}`}
|
||||
>
|
||||
<div
|
||||
className="relative h-7 w-7 flex-shrink-0 overflow-hidden rounded-lg border border-neutral-700 bg-neutral-950 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]"
|
||||
className="relative h-7 w-7 shrink-0 overflow-hidden rounded-lg border border-neutral-700 bg-neutral-950 shadow-[inset_0_1px_0_rgba(255,255,255,0.04)]"
|
||||
style={{ backgroundColor: value || "transparent" }}
|
||||
/>
|
||||
<span className="min-w-0 flex-1 truncate text-[11px] font-medium text-neutral-100">
|
||||
@@ -528,7 +528,7 @@ export function ColorField({
|
||||
{mixed && (
|
||||
<span
|
||||
data-color-mixed-indicator="true"
|
||||
className="rounded bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4"
|
||||
className="rounded-sm bg-panel-hover px-1.5 py-0.5 text-[9px] font-medium text-panel-text-4"
|
||||
>
|
||||
Mixed
|
||||
</span>
|
||||
|
||||
@@ -477,7 +477,7 @@ export function CurveGraph({
|
||||
onSettle();
|
||||
}
|
||||
}}
|
||||
className="mx-auto aspect-square w-full max-w-[200px] touch-none rounded border border-panel-border-input bg-black/20 outline-none focus:ring-1 focus:ring-panel-accent"
|
||||
className="mx-auto aspect-square w-full max-w-[200px] touch-none rounded-sm border border-panel-border-input bg-black/20 outline-hidden focus:ring-1 focus:ring-panel-accent"
|
||||
>
|
||||
<defs>
|
||||
<linearGradient id={`hf-hue-axis-${tab.key}`}>
|
||||
|
||||
@@ -328,7 +328,7 @@ export function ColorGradingControls({
|
||||
<select
|
||||
value={String(grading.preset ?? "neutral")}
|
||||
onChange={(event) => applyPreset(event.target.value)}
|
||||
className="w-full min-w-0 rounded-md bg-panel-input px-3 py-2 text-[11px] font-medium text-panel-text-1 outline-none"
|
||||
className="w-full min-w-0 rounded-md bg-panel-input px-3 py-2 text-[11px] font-medium text-panel-text-1 outline-hidden"
|
||||
>
|
||||
{HF_COLOR_GRADING_PRESETS.map((preset) => (
|
||||
<option key={preset.id} value={preset.id}>
|
||||
@@ -358,14 +358,12 @@ export function ColorGradingControls({
|
||||
aria-expanded={lutOpen}
|
||||
>
|
||||
{lutOpen ? (
|
||||
<ChevronDown size={11} className="flex-shrink-0 text-panel-text-5" />
|
||||
<ChevronDown size={11} className="shrink-0 text-panel-text-5" />
|
||||
) : (
|
||||
<ChevronRight size={11} className="flex-shrink-0 text-panel-text-5" />
|
||||
<ChevronRight size={11} className="shrink-0 text-panel-text-5" />
|
||||
)}
|
||||
<span className="min-w-0 flex-1 truncate">Custom LUT</span>
|
||||
{grading.lut && (
|
||||
<span className="h-1.5 w-1.5 flex-shrink-0 rounded-full bg-studio-accent" />
|
||||
)}
|
||||
{grading.lut && <span className="h-1.5 w-1.5 shrink-0 rounded-full bg-studio-accent" />}
|
||||
</button>
|
||||
{lutOpen && (
|
||||
<div className="grid gap-1.5 border-t border-panel-border/60 p-1.5">
|
||||
@@ -380,7 +378,7 @@ export function ColorGradingControls({
|
||||
nextSrc && grading.lut?.src === nextSrc ? grading.lut.intensity : 1,
|
||||
);
|
||||
}}
|
||||
className="w-full min-w-0 rounded-md bg-panel-input px-3 py-2 text-[11px] font-medium text-panel-text-1 outline-none"
|
||||
className="w-full min-w-0 rounded-md bg-panel-input px-3 py-2 text-[11px] font-medium text-panel-text-1 outline-hidden"
|
||||
title="Uploaded .cube LUT"
|
||||
>
|
||||
<option value="">None</option>
|
||||
@@ -441,7 +439,7 @@ export function ColorGradingControls({
|
||||
<div className="grid gap-2">
|
||||
{selectedProjectLut && (
|
||||
<div className="flex min-w-0 items-start gap-2 text-[10px] leading-4 text-panel-text-3">
|
||||
<span className="mt-[5px] h-1.5 w-1.5 flex-shrink-0 rounded-full bg-studio-accent" />
|
||||
<span className="mt-[5px] h-1.5 w-1.5 shrink-0 rounded-full bg-studio-accent" />
|
||||
<span className="min-w-0 flex-1 truncate" title={selectedProjectLut}>
|
||||
<span className="font-medium text-panel-text-2">Uploaded LUT</span>
|
||||
{` · ${selectedProjectLut}`}
|
||||
@@ -527,7 +525,7 @@ export function ColorGradingControls({
|
||||
aria-label="Close settings"
|
||||
title="Close settings"
|
||||
onClick={() => setDetailSettings(null)}
|
||||
className="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded text-panel-text-5 transition-colors hover:bg-panel-hover hover:text-panel-text-1"
|
||||
className="flex h-5 w-5 shrink-0 items-center justify-center rounded-sm text-panel-text-5 transition-colors hover:bg-panel-hover hover:text-panel-text-1"
|
||||
>
|
||||
<X size={11} />
|
||||
</button>
|
||||
|
||||
@@ -22,10 +22,10 @@ function StatusPill({ status }: { status: RuntimeColorGradingStatus }) {
|
||||
: "bg-panel-text-5";
|
||||
return (
|
||||
<div
|
||||
className="flex min-w-0 items-center gap-1.5 rounded bg-panel-input px-2 py-1 text-[10px] font-medium text-panel-text-3"
|
||||
className="flex min-w-0 items-center gap-1.5 rounded-sm bg-panel-input px-2 py-1 text-[10px] font-medium text-panel-text-3"
|
||||
title={status.message}
|
||||
>
|
||||
<span className={`h-1.5 w-1.5 flex-shrink-0 rounded-full ${dotClass}`} />
|
||||
<span className={`h-1.5 w-1.5 shrink-0 rounded-full ${dotClass}`} />
|
||||
<span className="truncate">{status.message}</span>
|
||||
</div>
|
||||
);
|
||||
@@ -47,7 +47,7 @@ function HdrMediaWarning({ metadata }: { metadata: MediaMetadata | null }) {
|
||||
<div className="mb-3 rounded-md border border-amber-500/30 bg-amber-500/10 px-3 py-2 text-[11px] leading-4 text-amber-100">
|
||||
<div className="mb-1 flex min-w-0 items-center justify-between gap-2">
|
||||
<span className="font-semibold">{metadata.color.label} source</span>
|
||||
<span className="rounded bg-amber-400/20 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-amber-100">
|
||||
<span className="rounded-sm bg-amber-400/20 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-amber-100">
|
||||
SDR preview
|
||||
</span>
|
||||
</div>
|
||||
@@ -118,7 +118,7 @@ function HoldBeforeButton({
|
||||
event.preventDefault();
|
||||
onHoldChange(false);
|
||||
}}
|
||||
className={`flex h-6 w-6 flex-shrink-0 items-center justify-center rounded transition-colors ${
|
||||
className={`flex h-6 w-6 shrink-0 items-center justify-center rounded transition-colors ${
|
||||
active
|
||||
? "bg-studio-accent text-black"
|
||||
: "text-panel-text-4 hover:bg-panel-hover hover:text-panel-text-1"
|
||||
@@ -194,7 +194,7 @@ export function ColorGradingSection({
|
||||
track("button", "Reset color grading");
|
||||
resetGrading();
|
||||
}}
|
||||
className="flex h-6 w-6 flex-shrink-0 items-center justify-center rounded text-panel-text-4 transition-colors hover:bg-panel-hover hover:text-panel-text-1"
|
||||
className="flex h-6 w-6 shrink-0 items-center justify-center rounded-sm text-panel-text-4 transition-colors hover:bg-panel-hover hover:text-panel-text-1"
|
||||
title="Reset color grading"
|
||||
>
|
||||
<RotateCcw size={12} />
|
||||
@@ -218,7 +218,7 @@ export function ColorGradingSection({
|
||||
setApplyScope(event.currentTarget.value as typeof applyScope);
|
||||
}}
|
||||
disabled={applyBusy}
|
||||
className="w-full min-w-0 rounded-md bg-panel-input px-3 py-2 text-[11px] font-medium text-panel-text-1 outline-none disabled:cursor-not-allowed disabled:opacity-50"
|
||||
className="w-full min-w-0 rounded-md bg-panel-input px-3 py-2 text-[11px] font-medium text-panel-text-1 outline-hidden disabled:cursor-not-allowed disabled:opacity-50"
|
||||
title="Choose where to copy these color grading settings"
|
||||
>
|
||||
<option value="source-file">Current file media</option>
|
||||
|
||||
@@ -157,7 +157,7 @@ export function ColorGradingSliderControl({
|
||||
event.stopPropagation();
|
||||
settings.onClick();
|
||||
}}
|
||||
className={`relative flex h-5 w-5 flex-shrink-0 items-center justify-center rounded transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40 ${
|
||||
className={`relative flex h-5 w-5 shrink-0 items-center justify-center rounded transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40 ${
|
||||
settings.active ? "text-studio-accent" : "text-panel-text-5"
|
||||
}`}
|
||||
title={settings.label}
|
||||
@@ -178,7 +178,7 @@ export function ColorGradingSliderControl({
|
||||
track("button", `Reset ${label}`);
|
||||
onReset();
|
||||
}}
|
||||
className="flex h-5 w-5 flex-shrink-0 items-center justify-center rounded text-panel-text-5 transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
className="flex h-5 w-5 shrink-0 items-center justify-center rounded-sm text-panel-text-5 transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
title={`Reset ${label}`}
|
||||
>
|
||||
<RotateCcw size={11} />
|
||||
@@ -228,7 +228,7 @@ export function ColorGradingSliderControl({
|
||||
</div>
|
||||
|
||||
<div className="flex min-w-0 items-center justify-end gap-1">
|
||||
<div className="flex flex-shrink-0 items-center rounded-md bg-panel-input px-1.5 py-px">
|
||||
<div className="flex shrink-0 items-center rounded-md bg-panel-input px-1.5 py-px">
|
||||
<input
|
||||
type="number"
|
||||
value={inputValue}
|
||||
@@ -256,12 +256,12 @@ export function ColorGradingSliderControl({
|
||||
nudge(-1);
|
||||
}
|
||||
}}
|
||||
className="hf-color-grading-number h-4 w-[36px] bg-transparent text-right text-[10px] font-medium tabular-nums text-panel-text-1 outline-none disabled:cursor-not-allowed"
|
||||
className="hf-color-grading-number h-4 w-[36px] bg-transparent text-right text-[10px] font-medium tabular-nums text-panel-text-1 outline-hidden disabled:cursor-not-allowed"
|
||||
title={displayValue}
|
||||
/>
|
||||
{suffix && <span className="ml-0.5 text-[10px] text-panel-text-5">{suffix}</span>}
|
||||
</div>
|
||||
<div className="flex flex-shrink-0 overflow-hidden rounded-md bg-panel-input">
|
||||
<div className="flex shrink-0 overflow-hidden rounded-md bg-panel-input">
|
||||
<button
|
||||
type="button"
|
||||
disabled={disabled}
|
||||
|
||||
@@ -204,7 +204,7 @@ function TonalWheel({
|
||||
}
|
||||
}}
|
||||
onBlur={onSettle}
|
||||
className="relative mx-auto aspect-square w-full max-w-[112px] touch-none rounded-full border border-panel-border-input outline-none focus:ring-1 focus:ring-panel-accent disabled:opacity-40"
|
||||
className="relative mx-auto aspect-square w-full max-w-[112px] touch-none rounded-full border border-panel-border-input outline-hidden focus:ring-1 focus:ring-panel-accent disabled:opacity-40"
|
||||
style={{
|
||||
background:
|
||||
"radial-gradient(circle, rgb(128 128 128) 0%, transparent 72%), conic-gradient(from 90deg, #f33, #f3f, #33f, #3ff, #3f3, #ff3, #f33)",
|
||||
|
||||
@@ -213,7 +213,7 @@ export function CommitField({
|
||||
}}
|
||||
onKeyDown={handleKeyDown}
|
||||
title={parseNumericToken(value) ? "Scroll or use Arrow keys to adjust" : undefined}
|
||||
className={`min-w-0 w-full bg-transparent text-[11px] font-medium text-neutral-100 outline-none disabled:cursor-not-allowed disabled:text-neutral-600 ${
|
||||
className={`min-w-0 w-full bg-transparent text-[11px] font-medium text-neutral-100 outline-hidden disabled:cursor-not-allowed disabled:text-neutral-600 ${
|
||||
align === "right" ? "text-right" : "text-left"
|
||||
}`}
|
||||
/>
|
||||
|
||||
@@ -133,7 +133,7 @@ export function ImageFillField({
|
||||
: "cursor-pointer hover:border-neutral-600 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
<Plus size={12} className="flex-shrink-0" />
|
||||
<Plus size={12} className="shrink-0" />
|
||||
<span className="truncate">{uploading ? "Uploading…" : "Upload image"}</span>
|
||||
</button>
|
||||
<input
|
||||
@@ -178,7 +178,7 @@ export function ImageFillField({
|
||||
}
|
||||
onCommit(`url("${toProjectRootAssetPath(next)}")`);
|
||||
}}
|
||||
className="min-w-0 w-full appearance-none bg-transparent text-[11px] font-medium text-neutral-100 outline-none disabled:cursor-not-allowed disabled:text-neutral-600"
|
||||
className="min-w-0 w-full appearance-none bg-transparent text-[11px] font-medium text-neutral-100 outline-hidden disabled:cursor-not-allowed disabled:text-neutral-600"
|
||||
>
|
||||
<option value="">None</option>
|
||||
{imageAssets.map((asset) => (
|
||||
@@ -288,7 +288,7 @@ export function GradientField({
|
||||
position: Math.max(0, Math.min(100, Math.round(stop.position + delta))),
|
||||
});
|
||||
}}
|
||||
className="absolute top-1/2 h-4 w-4 -translate-y-1/2 cursor-ew-resize rounded-full border-2 border-white/90 shadow-[0_0_0_1px_rgba(0,0,0,0.35)] outline-none focus-visible:ring-2 focus-visible:ring-studio-accent"
|
||||
className="absolute top-1/2 h-4 w-4 -translate-y-1/2 cursor-ew-resize rounded-full border-2 border-white/90 shadow-[0_0_0_1px_rgba(0,0,0,0.35)] outline-hidden focus-visible:ring-2 focus-visible:ring-studio-accent"
|
||||
style={{
|
||||
left: `calc(${stop.position}% - 8px)`,
|
||||
backgroundColor: stop.color,
|
||||
@@ -339,7 +339,7 @@ export function GradientField({
|
||||
track("toggle", "Repeat gradient");
|
||||
patch({ repeating: e.target.checked });
|
||||
}}
|
||||
className="h-4 w-4 rounded border-neutral-700 bg-neutral-950 text-panel-accent focus:ring-panel-accent"
|
||||
className="h-4 w-4 rounded-sm border-neutral-700 bg-neutral-950 text-panel-accent focus:ring-panel-accent"
|
||||
/>
|
||||
Repeat
|
||||
</label>
|
||||
|
||||
@@ -74,7 +74,7 @@ export function FlatColorGradingAccessory({
|
||||
commitCompare(false);
|
||||
}}
|
||||
title="Hold to show original"
|
||||
className="flex-shrink-0 text-panel-text-3 hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
className="shrink-0 text-panel-text-3 hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
<Compare size={12} />
|
||||
</button>
|
||||
@@ -82,7 +82,7 @@ export function FlatColorGradingAccessory({
|
||||
<span
|
||||
data-flat-grade-status-dot="true"
|
||||
title={runtimeStatus.message}
|
||||
className={`h-[5px] w-[5px] flex-shrink-0 rounded-full ${STATUS_DOT_CLASS[runtimeStatus.state]}`}
|
||||
className={`h-[5px] w-[5px] shrink-0 rounded-full ${STATUS_DOT_CLASS[runtimeStatus.state]}`}
|
||||
/>
|
||||
<span
|
||||
data-flat-grade-status-message="true"
|
||||
@@ -100,7 +100,7 @@ export function FlatColorGradingAccessory({
|
||||
track("button", "Reset color grading");
|
||||
resetGrading();
|
||||
}}
|
||||
className="flex-shrink-0 text-panel-text-3 hover:text-panel-text-1"
|
||||
className="shrink-0 text-panel-text-3 hover:text-panel-text-1"
|
||||
>
|
||||
<RotateCcw size={12} />
|
||||
</button>
|
||||
|
||||
@@ -74,7 +74,7 @@ function HdrBanner({ metadata }: { metadata: MediaMetadata | null }) {
|
||||
>
|
||||
<div className="mb-0.5 flex items-center justify-between gap-2">
|
||||
<span className="font-semibold">{metadata.color.label} source</span>
|
||||
<span className="rounded bg-amber-400/20 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-amber-100">
|
||||
<span className="rounded-sm bg-amber-400/20 px-1.5 py-0.5 text-[9px] font-semibold uppercase tracking-wide text-amber-100">
|
||||
SDR preview
|
||||
</span>
|
||||
</div>
|
||||
@@ -389,7 +389,7 @@ export function FlatColorGradingSection({
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
fill="currentColor"
|
||||
className={`flex-shrink-0 text-panel-text-5 transition-transform ${lutOpen ? "rotate-90" : ""}`}
|
||||
className={`shrink-0 text-panel-text-5 transition-transform ${lutOpen ? "rotate-90" : ""}`}
|
||||
>
|
||||
<path d="M2 3l3 4 3-4z" />
|
||||
</svg>
|
||||
@@ -409,7 +409,7 @@ export function FlatColorGradingSection({
|
||||
track("select", "Custom LUT");
|
||||
actions.applyLut(src || null, src && lut?.src === src ? lut.intensity : 1);
|
||||
}}
|
||||
className="border-b border-panel-border-input/50 bg-transparent font-mono text-[10px] text-panel-text-3 outline-none hover:border-panel-border-input"
|
||||
className="border-b border-panel-border-input/50 bg-transparent font-mono text-[10px] text-panel-text-3 outline-hidden hover:border-panel-border-input"
|
||||
>
|
||||
<option value="">None</option>
|
||||
{lutAssets.map((asset) => (
|
||||
@@ -423,7 +423,7 @@ export function FlatColorGradingSection({
|
||||
disabled={!onImportAssets}
|
||||
onClick={() => lutInputRef.current?.click()}
|
||||
title="Import .cube LUT"
|
||||
className="flex-shrink-0 text-panel-text-4 hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
className="shrink-0 text-panel-text-4 hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
<Plus size={12} />
|
||||
</button>
|
||||
@@ -467,7 +467,7 @@ export function FlatColorGradingSection({
|
||||
data-flat-grade-settings="vignette"
|
||||
title="Vignette settings"
|
||||
onClick={() => setDetailSettingsOpen((c) => (c === "vignette" ? null : "vignette"))}
|
||||
className="flex-shrink-0 text-panel-text-4 hover:text-panel-text-1"
|
||||
className="shrink-0 text-panel-text-4 hover:text-panel-text-1"
|
||||
>
|
||||
<Settings size={12} />
|
||||
</button>
|
||||
@@ -479,7 +479,7 @@ export function FlatColorGradingSection({
|
||||
data-flat-grade-settings="grain"
|
||||
title="Grain settings"
|
||||
onClick={() => setDetailSettingsOpen((c) => (c === "grain" ? null : "grain"))}
|
||||
className="flex-shrink-0 text-panel-text-4 hover:text-panel-text-1"
|
||||
className="shrink-0 text-panel-text-4 hover:text-panel-text-1"
|
||||
>
|
||||
<Settings size={12} />
|
||||
</button>
|
||||
@@ -505,7 +505,7 @@ export function FlatColorGradingSection({
|
||||
onSetApplyScope(e.target.value as "source-file" | "project");
|
||||
}}
|
||||
disabled={applyBusy}
|
||||
className="border-b border-panel-border-input/50 bg-transparent font-mono text-[11px] text-panel-text-0 outline-none hover:border-panel-border-input disabled:opacity-50"
|
||||
className="border-b border-panel-border-input/50 bg-transparent font-mono text-[11px] text-panel-text-0 outline-hidden hover:border-panel-border-input disabled:opacity-50"
|
||||
>
|
||||
<option value="source-file">Current file media</option>
|
||||
<option value="project">All project media</option>
|
||||
|
||||
@@ -48,7 +48,7 @@ export function FlatEffectsAccessory({
|
||||
track("button", "Reset effects");
|
||||
onCommitColorGrading({ ...grading, effects: { ...DEFAULT_EFFECTS }, palette: null });
|
||||
}}
|
||||
className="flex-shrink-0 text-panel-text-3 hover:text-panel-text-1"
|
||||
className="shrink-0 text-panel-text-3 hover:text-panel-text-1"
|
||||
>
|
||||
<RotateCcw size={12} />
|
||||
</button>
|
||||
@@ -215,7 +215,7 @@ export function FlatEffectsSection({
|
||||
nextPalette[index] = event.target.value;
|
||||
onCommitColorGrading({ ...grading, palette: nextPalette });
|
||||
}}
|
||||
className="h-6 w-6 cursor-pointer rounded-sm border border-panel-border-input bg-transparent p-0"
|
||||
className="h-6 w-6 cursor-pointer rounded-xs border border-panel-border-input bg-transparent p-0"
|
||||
/>
|
||||
{palette.length > 2 && (
|
||||
<button
|
||||
@@ -227,7 +227,7 @@ export function FlatEffectsSection({
|
||||
palette: palette.filter((_, colorIndex) => colorIndex !== index),
|
||||
})
|
||||
}
|
||||
className="absolute -right-1 -top-1 hidden h-3.5 w-3.5 items-center justify-center rounded-full bg-panel-bg text-panel-text-2 shadow group-hover/swatch:flex"
|
||||
className="absolute -right-1 -top-1 hidden h-3.5 w-3.5 items-center justify-center rounded-full bg-panel-bg text-panel-text-2 shadow-sm group-hover/swatch:flex"
|
||||
>
|
||||
<X size={8} />
|
||||
</button>
|
||||
@@ -244,7 +244,7 @@ export function FlatEffectsSection({
|
||||
palette: [...palette, palette.at(-1) ?? "#ffffff"],
|
||||
})
|
||||
}
|
||||
className="flex h-6 w-6 items-center justify-center rounded-sm border border-panel-border-input text-panel-text-4 hover:text-panel-text-1"
|
||||
className="flex h-6 w-6 items-center justify-center rounded-xs border border-panel-border-input text-panel-text-4 hover:text-panel-text-1"
|
||||
>
|
||||
<Plus size={11} />
|
||||
</button>
|
||||
|
||||
@@ -146,7 +146,7 @@ export function FlatMediaSection({
|
||||
<div className="space-y-1.5">
|
||||
<div className="flex min-h-8 items-center justify-between gap-2">
|
||||
<span className="flex min-w-0 items-center gap-2">
|
||||
<span className="h-5 w-8 flex-shrink-0 rounded-[3px] bg-panel-surface" />
|
||||
<span className="h-5 w-8 shrink-0 rounded-[3px] bg-panel-surface" />
|
||||
<span className="min-w-0 truncate font-mono text-[11px] text-panel-text-0">
|
||||
{srcAttr}
|
||||
</span>
|
||||
@@ -161,14 +161,14 @@ export function FlatMediaSection({
|
||||
setTimeout(() => setCopied(false), 1500);
|
||||
});
|
||||
}}
|
||||
className="flex flex-shrink-0 items-center gap-1 text-[10px] text-panel-text-3 hover:text-panel-text-1"
|
||||
className="flex shrink-0 items-center gap-1 text-[10px] text-panel-text-3 hover:text-panel-text-1"
|
||||
>
|
||||
{copied ? <Check size={11} /> : <ClipboardList size={11} />}
|
||||
{copied ? "Copied" : "Copy"}
|
||||
</button>
|
||||
</div>
|
||||
{isVisualMedia && (
|
||||
<div className="ml-[1px] border-l-2 border-panel-border-input py-1 pl-[10px]">
|
||||
<div className="ml-px border-l-2 border-panel-border-input py-1 pl-[10px]">
|
||||
<div className="flex min-h-6 items-center justify-between">
|
||||
<span className="flex items-baseline gap-[7px]">
|
||||
<span className="text-[11px] font-semibold text-panel-text-1">Cutout</span>
|
||||
|
||||
@@ -95,7 +95,7 @@ export function FlatOverlaysSection({
|
||||
)}
|
||||
<Plus
|
||||
size={12}
|
||||
className="absolute right-1.5 top-1.5 text-white opacity-70 drop-shadow group-hover:text-panel-accent group-hover:opacity-100"
|
||||
className="absolute right-1.5 top-1.5 text-white opacity-70 drop-shadow-sm group-hover:text-panel-accent group-hover:opacity-100"
|
||||
/>
|
||||
</span>
|
||||
<span className="block truncate px-2 py-1.5 text-[10px] text-panel-text-2">
|
||||
|
||||
@@ -42,7 +42,7 @@ export function FlatRow({
|
||||
return (
|
||||
<div className="group flex min-h-[30px] items-center justify-between gap-3">
|
||||
<span className={`text-[11px] ${VALUE_TIER_LABEL_CLASS[tier]}`}>{label}</span>
|
||||
<span className="flex min-w-0 flex-shrink-0 items-center gap-1.5">
|
||||
<span className="flex min-w-0 shrink-0 items-center gap-1.5">
|
||||
<span
|
||||
data-flat-row-value="true"
|
||||
className={`min-w-0 border-b pb-px font-mono text-[11px] ${VALUE_TIER_VALUE_CLASS[tier]} ${
|
||||
@@ -73,7 +73,7 @@ export function FlatRow({
|
||||
track("button", `Reset ${label}`);
|
||||
onReset();
|
||||
}}
|
||||
className="flex-shrink-0 text-panel-text-3 opacity-0 transition-opacity hover:text-panel-text-1 group-hover:opacity-100"
|
||||
className="shrink-0 text-panel-text-3 opacity-0 transition-opacity hover:text-panel-text-1 group-hover:opacity-100"
|
||||
>
|
||||
<RotateCcw size={11} />
|
||||
</button>
|
||||
@@ -84,7 +84,7 @@ export function FlatRow({
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
fill="currentColor"
|
||||
className="flex-shrink-0 text-panel-text-5"
|
||||
className="shrink-0 text-panel-text-5"
|
||||
>
|
||||
<path d="M2 3l3 4 3-4z" />
|
||||
</svg>
|
||||
@@ -196,7 +196,7 @@ export function FlatGroupHeader({
|
||||
type="button"
|
||||
data-flat-group-collapsed="true"
|
||||
onClick={onToggleOpen}
|
||||
className={`${animateEntrance ? "hf-flat-group-enter " : ""}flex min-h-10 w-full flex-shrink-0 items-center justify-between gap-2 border-b border-panel-hairline bg-panel-bg px-4 text-left`}
|
||||
className={`${animateEntrance ? "hf-flat-group-enter " : ""}flex min-h-10 w-full shrink-0 items-center justify-between gap-2 border-b border-panel-hairline bg-panel-bg px-4 text-left`}
|
||||
>
|
||||
<span className="flex min-w-0 items-center gap-2">
|
||||
<span className="text-[12px] font-medium text-panel-text-2">{title}</span>
|
||||
@@ -211,7 +211,7 @@ export function FlatGroupHeader({
|
||||
height="12"
|
||||
viewBox="0 0 12 12"
|
||||
fill="currentColor"
|
||||
className="flex-shrink-0 text-panel-text-5"
|
||||
className="shrink-0 text-panel-text-5"
|
||||
>
|
||||
<path d="M4 2l4 4-4 4z" />
|
||||
</svg>
|
||||
@@ -221,7 +221,7 @@ export function FlatGroupHeader({
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${animateEntrance ? "hf-flat-group-enter " : ""}flex min-h-10 flex-shrink-0 items-center justify-between bg-panel-bg px-4`}
|
||||
className={`${animateEntrance ? "hf-flat-group-enter " : ""}flex min-h-10 shrink-0 items-center justify-between bg-panel-bg px-4`}
|
||||
>
|
||||
<span className="text-[12px] font-semibold text-panel-text-0">{title}</span>
|
||||
<span className="flex items-center gap-2.5 text-panel-text-5">
|
||||
@@ -415,7 +415,7 @@ export function FlatSlider({
|
||||
|
||||
return (
|
||||
<div className="flex min-h-[28px] items-center gap-2.5">
|
||||
<span className="w-[86px] flex-shrink-0 text-[11px] text-panel-text-3">{label}</span>
|
||||
<span className="w-[86px] shrink-0 text-[11px] text-panel-text-3">{label}</span>
|
||||
<div
|
||||
data-flat-slider-track="true"
|
||||
role="slider"
|
||||
@@ -516,7 +516,7 @@ export function FlatSlider({
|
||||
{centerTick && (
|
||||
<div
|
||||
data-flat-slider-center-tick="true"
|
||||
className="absolute left-1/2 top-[-1px] h-1 w-px -translate-x-1/2 bg-panel-text-5"
|
||||
className="absolute left-1/2 -top-px h-1 w-px -translate-x-1/2 bg-panel-text-5"
|
||||
/>
|
||||
)}
|
||||
{tier === "explicitCustom" && (
|
||||
@@ -537,14 +537,14 @@ export function FlatSlider({
|
||||
</div>
|
||||
<span
|
||||
data-flat-slider-value="true"
|
||||
className={`w-11 flex-shrink-0 text-right font-mono text-[10px] ${
|
||||
className={`w-11 shrink-0 text-right font-mono text-[10px] ${
|
||||
tier === "explicitCustom" ? "text-panel-text-0" : "text-panel-text-3"
|
||||
}`}
|
||||
>
|
||||
{displayValue}
|
||||
</span>
|
||||
{(centerTick || onReset) && (
|
||||
<span data-flat-slider-reset-slot="true" className="w-3.5 flex-shrink-0">
|
||||
<span data-flat-slider-reset-slot="true" className="w-3.5 shrink-0">
|
||||
{tier === "explicitCustom" && onReset && (
|
||||
<button
|
||||
type="button"
|
||||
|
||||
@@ -68,7 +68,7 @@ export function FlatSelectRow({
|
||||
track("select", trackName);
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
className={`appearance-none bg-transparent text-right font-mono text-[11px] outline-none disabled:cursor-not-allowed ${VALUE_TIER_VALUE_CLASS[tier]}`}
|
||||
className={`appearance-none bg-transparent text-right font-mono text-[11px] outline-hidden disabled:cursor-not-allowed ${VALUE_TIER_VALUE_CLASS[tier]}`}
|
||||
>
|
||||
{renderedOptions.map((option) => (
|
||||
<option key={option.value} value={option.value}>
|
||||
@@ -81,7 +81,7 @@ export function FlatSelectRow({
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
fill="currentColor"
|
||||
className="flex-shrink-0 text-panel-text-5"
|
||||
className="shrink-0 text-panel-text-5"
|
||||
>
|
||||
<path d="M2 3l3 4 3-4z" />
|
||||
</svg>
|
||||
@@ -96,7 +96,7 @@ export function FlatSelectRow({
|
||||
track("button", `Reset ${trackName}`);
|
||||
onReset();
|
||||
}}
|
||||
className="flex-shrink-0 text-panel-text-3 opacity-0 transition-opacity hover:text-panel-text-1 group-hover:opacity-100 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
className="shrink-0 text-panel-text-3 opacity-0 transition-opacity hover:text-panel-text-1 group-hover:opacity-100 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
<RotateCcw size={11} />
|
||||
</button>
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
|
||||
import React, { act, useState } from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import postcss from "postcss";
|
||||
import tailwindcss from "tailwindcss";
|
||||
import { compile } from "tailwindcss";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
import { FlatTextLayerList, FlatTextSection } from "./propertyPanelFlatTextSection";
|
||||
import type { DomEditSelection, DomEditTextField } from "./domEditingTypes";
|
||||
@@ -511,13 +510,13 @@ describe("FlatTextSection — multi-field", () => {
|
||||
expect(contentTextarea?.classList).toContain("resize-y");
|
||||
expect(contentTextarea?.classList).toContain("overflow-y-auto");
|
||||
|
||||
const compiled = await postcss([
|
||||
tailwindcss({
|
||||
content: [{ raw: host.innerHTML, extension: "html" }],
|
||||
corePlugins: { preflight: false },
|
||||
}),
|
||||
]).process("@tailwind utilities;", { from: undefined });
|
||||
expect(compiled.css).toContain("field-sizing: content");
|
||||
// Tailwind v4 has no PostCSS-plugin JS API; `compile` takes the stylesheet
|
||||
// and is handed the candidates directly instead of scanning raw content.
|
||||
const candidates = Array.from(host.querySelectorAll<HTMLElement>("*")).flatMap((el) =>
|
||||
Array.from(el.classList),
|
||||
);
|
||||
const compiled = await compile("@tailwind utilities;");
|
||||
expect(compiled.build(candidates)).toContain("field-sizing: content");
|
||||
|
||||
act(() => root.unmount());
|
||||
});
|
||||
|
||||
@@ -121,7 +121,7 @@ function FlatTextFieldEditor({
|
||||
track("select", "Weight");
|
||||
onSetTextFieldStyle(field.key, "font-weight", e.target.value);
|
||||
}}
|
||||
className={`appearance-none bg-transparent text-right font-mono text-[11px] outline-none ${
|
||||
className={`appearance-none bg-transparent text-right font-mono text-[11px] outline-hidden ${
|
||||
VALUE_TIER_VALUE_CLASS[resolveValueTier(field.inlineStyles["font-weight"], "400")]
|
||||
}`}
|
||||
>
|
||||
@@ -138,7 +138,7 @@ function FlatTextFieldEditor({
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
fill="currentColor"
|
||||
className="flex-shrink-0 text-panel-text-5"
|
||||
className="shrink-0 text-panel-text-5"
|
||||
>
|
||||
<path d="M2 3l3 4 3-4z" />
|
||||
</svg>
|
||||
@@ -387,13 +387,13 @@ export function FlatTextLayerList({
|
||||
}`}
|
||||
>
|
||||
<span
|
||||
className="h-3 w-3 flex-shrink-0 rounded-sm"
|
||||
className="h-3 w-3 shrink-0 rounded-xs"
|
||||
style={{ backgroundColor: getTextFieldColor(field, styles) }}
|
||||
/>
|
||||
<span className="min-w-0 flex-1 truncate text-[11px] text-panel-text-1">
|
||||
{formatTextFieldPreview(field.value) || `Text ${index + 1}`}
|
||||
</span>
|
||||
<span className="flex-shrink-0 font-mono text-[9px] text-panel-text-4">
|
||||
<span className="shrink-0 font-mono text-[9px] text-panel-text-4">
|
||||
{field.tagName}
|
||||
</span>
|
||||
{fields.length > 1 && (
|
||||
@@ -406,7 +406,7 @@ export function FlatTextLayerList({
|
||||
track("button", "Remove text field");
|
||||
onRemove(field.key);
|
||||
}}
|
||||
className="flex-shrink-0 text-panel-text-4 hover:text-panel-text-1"
|
||||
className="shrink-0 text-panel-text-4 hover:text-panel-text-1"
|
||||
>
|
||||
<X size={10} />
|
||||
</button>
|
||||
|
||||
@@ -37,7 +37,7 @@ export function FlatToggle({
|
||||
track("toggle", label);
|
||||
onChange(!checked);
|
||||
}}
|
||||
className={`relative h-[14px] w-6 flex-shrink-0 rounded-full transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${
|
||||
className={`relative h-[14px] w-6 shrink-0 rounded-full transition-colors disabled:cursor-not-allowed disabled:opacity-50 ${
|
||||
checked ? "bg-panel-accent/35" : "bg-panel-hover"
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -425,7 +425,7 @@ export function FontFamilyField({
|
||||
role="combobox"
|
||||
aria-expanded={open}
|
||||
aria-autocomplete="list"
|
||||
className="min-w-0 rounded-lg border border-neutral-800 bg-neutral-900 px-2.5 py-2 text-[11px] font-medium text-neutral-100 outline-none placeholder:text-neutral-600 focus:border-neutral-600"
|
||||
className="min-w-0 rounded-lg border border-neutral-800 bg-neutral-900 px-2.5 py-2 text-[11px] font-medium text-neutral-100 outline-hidden placeholder:text-neutral-600 focus:border-neutral-600"
|
||||
/>
|
||||
{canQueryLocalFonts && (
|
||||
<button
|
||||
@@ -487,12 +487,12 @@ export function FontFamilyField({
|
||||
{option.family}
|
||||
</span>
|
||||
{renderAliasFor(option.family) && (
|
||||
<span className="flex-shrink-0 text-[9px] text-neutral-500">
|
||||
<span className="shrink-0 text-[9px] text-neutral-500">
|
||||
→ {renderAliasFor(option.family)}
|
||||
</span>
|
||||
)}
|
||||
</span>
|
||||
<span className="flex-shrink-0 text-[9px] uppercase tracking-[0.14em] text-neutral-600">
|
||||
<span className="shrink-0 text-[9px] uppercase tracking-[0.14em] text-neutral-600">
|
||||
{option.source}
|
||||
</span>
|
||||
</button>
|
||||
@@ -524,7 +524,7 @@ export function FontFamilyField({
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
fill="currentColor"
|
||||
className="flex-shrink-0 text-panel-text-5"
|
||||
className="shrink-0 text-panel-text-5"
|
||||
>
|
||||
<path d="M2 3l3 4 3-4z" />
|
||||
</svg>
|
||||
@@ -549,7 +549,7 @@ export function FontFamilyField({
|
||||
>
|
||||
{currentFamily}
|
||||
</span>
|
||||
<span className="flex-shrink-0 text-[10px] uppercase tracking-[0.14em] text-neutral-600">
|
||||
<span className="shrink-0 text-[10px] uppercase tracking-[0.14em] text-neutral-600">
|
||||
Font
|
||||
</span>
|
||||
</button>
|
||||
|
||||
@@ -338,7 +338,7 @@ function CarveSourceRow({
|
||||
<div className="hf-fx-row flex min-h-6 items-center gap-2">
|
||||
{/* Wraps like every other name in this column (see FxParamRow) — one
|
||||
truncating row beside wrapping ones reads as a rendering bug. */}
|
||||
<span className="hf-fx-label w-[86px] flex-shrink-0 break-words text-[10px] leading-tight text-panel-text-2">
|
||||
<span className="hf-fx-label w-[86px] shrink-0 wrap-break-word text-[10px] leading-tight text-panel-text-2">
|
||||
Listen to
|
||||
</span>
|
||||
{soleVoice ? (
|
||||
|
||||
@@ -141,7 +141,7 @@ describe("FxParamRow label tooltip", () => {
|
||||
// The whole name is present and allowed to wrap. No `title`: a tooltip
|
||||
// answers one row on hover, wrapping answers the column at rest.
|
||||
expect(label?.textContent).toBe("Gap between repeats");
|
||||
expect(label?.className).toContain("break-words");
|
||||
expect(label?.className).toContain("wrap-break-word");
|
||||
expect(label?.className).not.toContain("truncate");
|
||||
expect(label?.getAttribute("title")).toBeNull();
|
||||
// The hint stays where it was: the two are different questions, and the
|
||||
|
||||
@@ -88,7 +88,7 @@ export function AutomationToggle({
|
||||
<Tooltip label={automated ? "Automated" : "Automate"}>
|
||||
<button
|
||||
type="button"
|
||||
className={`hf-fx-automate w-[16px] flex-shrink-0 rounded-[3px] border font-mono text-[9px] leading-none ${
|
||||
className={`hf-fx-automate w-[16px] shrink-0 rounded-[3px] border font-mono text-[9px] leading-none ${
|
||||
automated
|
||||
? "border-panel-accent text-panel-accent"
|
||||
: "border-panel-border-input text-panel-text-2 hover:text-panel-text-0"
|
||||
@@ -192,10 +192,10 @@ export function FxParamRow({
|
||||
big the space is" — so 86px of truncation left "How big the sp…", and
|
||||
three rows of that read as the same word four times. A title only
|
||||
answers it on hover, one row at a time; wrapping answers it for the
|
||||
whole column at rest. `break-words` so a long single token breaks
|
||||
whole column at rest. `wrap-break-word` so a long single token breaks
|
||||
instead of widening the column. The row keeps `title={param.hint}`:
|
||||
the name and the explanation are different questions. */}
|
||||
<span className="hf-fx-label w-[86px] flex-shrink-0 break-words text-[10px] leading-tight text-panel-text-2">
|
||||
<span className="hf-fx-label w-[86px] shrink-0 wrap-break-word text-[10px] leading-tight text-panel-text-2">
|
||||
{param.label}
|
||||
</span>
|
||||
<select
|
||||
@@ -236,7 +236,7 @@ export function FxParamRow({
|
||||
>
|
||||
{/* See the enum row above for why the name wraps instead of truncating. */}
|
||||
<span
|
||||
className={`hf-fx-label w-[86px] flex-shrink-0 break-words text-[10px] leading-tight ${
|
||||
className={`hf-fx-label w-[86px] shrink-0 wrap-break-word text-[10px] leading-tight ${
|
||||
automated ? "text-panel-accent" : "text-panel-text-2"
|
||||
}`}
|
||||
>
|
||||
@@ -258,7 +258,7 @@ export function FxParamRow({
|
||||
onBlur={commit}
|
||||
/>
|
||||
<input
|
||||
className="hf-fx-number w-[54px] flex-shrink-0 rounded-[3px] bg-panel-surface px-1 py-0.5 text-right font-mono text-[10px] text-panel-text-0"
|
||||
className="hf-fx-number w-[54px] shrink-0 rounded-[3px] bg-panel-surface px-1 py-0.5 text-right font-mono text-[10px] text-panel-text-0"
|
||||
type="number"
|
||||
min={param.min}
|
||||
max={param.max}
|
||||
@@ -287,7 +287,7 @@ export function FxParamRow({
|
||||
}}
|
||||
/>
|
||||
{param.unit ? (
|
||||
<span className="hf-fx-unit w-[22px] flex-shrink-0 font-mono text-[9px] text-panel-text-2">
|
||||
<span className="hf-fx-unit w-[22px] shrink-0 font-mono text-[9px] text-panel-text-2">
|
||||
{param.unit}
|
||||
</span>
|
||||
) : null}
|
||||
|
||||
@@ -87,7 +87,7 @@ function Fader({
|
||||
<div className="relative h-[74px] w-full">
|
||||
<span className="pointer-events-none absolute inset-x-0 top-1/2 h-px bg-panel-border-input" />
|
||||
<span
|
||||
className="pointer-events-none absolute left-1/2 w-[3px] -translate-x-1/2 rounded-sm bg-panel-accent"
|
||||
className="pointer-events-none absolute left-1/2 w-[3px] -translate-x-1/2 rounded-xs bg-panel-accent"
|
||||
style={
|
||||
// `value`, not `band.gain`: mid-drag across zero the fill would
|
||||
// otherwise keep pointing the way it started.
|
||||
@@ -95,7 +95,7 @@ function Fader({
|
||||
}
|
||||
/>
|
||||
<input
|
||||
className="hf-fx-eq-fader absolute left-1/2 h-[19px] w-[74px] -translate-x-1/2 -translate-y-1/2 rotate-[-90deg] cursor-ns-resize appearance-none bg-transparent"
|
||||
className="hf-fx-eq-fader absolute left-1/2 h-[19px] w-[74px] -translate-x-1/2 -translate-y-1/2 -rotate-90 cursor-ns-resize appearance-none bg-transparent"
|
||||
style={{ top: "50%" }}
|
||||
type="range"
|
||||
min={-HF_AUDIO_EQ_RANGE_DB}
|
||||
|
||||
@@ -146,7 +146,7 @@ export const FX_PRESET_STYLE: Record<string, FxPresetStyle> = {
|
||||
},
|
||||
// The name is a joke and the type is in on it.
|
||||
"doofus-worble": {
|
||||
type: "text-[16px] tracking-[0.1em]",
|
||||
type: "text-[16px] tracking-widest",
|
||||
color: "hsl(288, 85%, 72%)",
|
||||
family: FACE.theatrical,
|
||||
},
|
||||
@@ -164,7 +164,7 @@ export const FX_PRESET_STYLE: Record<string, FxPresetStyle> = {
|
||||
},
|
||||
// Rough and too close — heavy industrial caps, growled rather than shouted.
|
||||
monster: {
|
||||
type: "text-[15px] font-black uppercase tracking-[0.05em]",
|
||||
type: "text-[15px] font-black uppercase tracking-wider",
|
||||
color: "hsl(350, 78%, 64%)",
|
||||
family: FACE.condensed,
|
||||
},
|
||||
|
||||
@@ -109,7 +109,7 @@ export function GradingNumberField({
|
||||
event.currentTarget.blur();
|
||||
}
|
||||
}}
|
||||
className={`border-b border-panel-border-input/50 bg-transparent py-0.5 text-right font-mono text-[9px] text-panel-text-2 outline-none focus:border-panel-accent disabled:opacity-40 ${inputClassName}`}
|
||||
className={`border-b border-panel-border-input/50 bg-transparent py-0.5 text-right font-mono text-[9px] text-panel-text-2 outline-hidden focus:border-panel-accent disabled:opacity-40 ${inputClassName}`}
|
||||
/>
|
||||
</label>
|
||||
);
|
||||
|
||||
@@ -182,7 +182,7 @@ export function MediaSection({
|
||||
event.stopPropagation();
|
||||
void runBackgroundRemoval();
|
||||
}}
|
||||
className="flex h-8 flex-shrink-0 items-center gap-1.5 rounded-md bg-panel-input px-2.5 text-[11px] font-medium text-panel-text-2 transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
className="flex h-8 shrink-0 items-center gap-1.5 rounded-md bg-panel-input px-2.5 text-[11px] font-medium text-panel-text-2 transition-colors hover:bg-panel-hover hover:text-panel-text-1 disabled:cursor-not-allowed disabled:opacity-50"
|
||||
title={
|
||||
canRemoveBackground
|
||||
? "Remove background and save a transparent asset"
|
||||
|
||||
@@ -70,21 +70,21 @@ export function MetricField({
|
||||
scrub && !disabled
|
||||
? ({
|
||||
className:
|
||||
"flex-shrink-0 text-[11px] font-medium text-neutral-500 cursor-ew-resize select-none",
|
||||
"shrink-0 text-[11px] font-medium text-neutral-500 cursor-ew-resize select-none",
|
||||
onPointerDown: handleScrubPointerDown,
|
||||
onPointerMove: handleScrubPointerMove,
|
||||
onPointerUp: handleScrubPointerUp,
|
||||
onPointerCancel: handleScrubPointerUp,
|
||||
onLostPointerCapture: handleScrubPointerUp,
|
||||
} as const)
|
||||
: ({ className: "flex-shrink-0 text-[11px] font-medium text-neutral-500" } as const);
|
||||
: ({ className: "shrink-0 text-[11px] font-medium text-neutral-500" } as const);
|
||||
|
||||
return (
|
||||
<div className={FIELD} title={tooltip}>
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<span {...scrubProps}>{label}</span>
|
||||
<CommitField value={value} disabled={disabled} liveCommit={liveCommit} onCommit={commit} />
|
||||
{suffix && <span className="flex-shrink-0 text-[10px] text-neutral-600">{suffix}</span>}
|
||||
{suffix && <span className="shrink-0 text-[10px] text-neutral-600">{suffix}</span>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
@@ -262,7 +262,7 @@ export function SelectField({
|
||||
const renderedOptions = value && !options.includes(value) ? [value, ...options] : options;
|
||||
return (
|
||||
<label className={`${FIELD} flex items-center gap-3`}>
|
||||
<span className="flex-shrink-0 text-[11px] font-medium text-neutral-500">{label}</span>
|
||||
<span className="shrink-0 text-[11px] font-medium text-neutral-500">{label}</span>
|
||||
<select
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
@@ -270,7 +270,7 @@ export function SelectField({
|
||||
track("select", label);
|
||||
onChange(e.target.value);
|
||||
}}
|
||||
className="min-w-0 w-full appearance-none bg-transparent text-[11px] font-medium text-neutral-100 outline-none disabled:cursor-not-allowed disabled:text-neutral-600"
|
||||
className="min-w-0 w-full appearance-none bg-transparent text-[11px] font-medium text-neutral-100 outline-hidden disabled:cursor-not-allowed disabled:text-neutral-600"
|
||||
>
|
||||
{renderedOptions.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
@@ -302,7 +302,7 @@ export function Section({
|
||||
height="10"
|
||||
viewBox="0 0 10 10"
|
||||
fill="currentColor"
|
||||
className={`flex-shrink-0 text-panel-text-5 transition-transform duration-150 ${
|
||||
className={`shrink-0 text-panel-text-5 transition-transform duration-150 ${
|
||||
collapsed ? "-rotate-90" : ""
|
||||
}`}
|
||||
>
|
||||
@@ -324,7 +324,7 @@ export function Section({
|
||||
<h3 className="text-[12px] font-semibold text-panel-text-1">{title}</h3>
|
||||
{collapseIcon}
|
||||
</button>
|
||||
{accessory && <div className="flex flex-shrink-0 items-center">{accessory}</div>}
|
||||
{accessory && <div className="flex shrink-0 items-center">{accessory}</div>}
|
||||
</div>
|
||||
{!collapsed && <div className="px-4 pb-3">{children}</div>}
|
||||
</section>
|
||||
|
||||
@@ -147,7 +147,7 @@ export function TextAreaField({
|
||||
onFocus={handleFocus}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
className="[field-sizing:content] max-h-[40vh] min-h-12 w-full resize-y overflow-x-hidden overflow-y-auto bg-transparent font-mono text-[11px] leading-normal text-panel-text-0 outline-none disabled:cursor-not-allowed disabled:text-panel-text-4"
|
||||
className="field-sizing-content max-h-[40vh] min-h-12 w-full resize-y overflow-x-hidden overflow-y-auto bg-transparent font-mono text-[11px] leading-normal text-panel-text-0 outline-hidden disabled:cursor-not-allowed disabled:text-panel-text-4"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
@@ -165,7 +165,7 @@ export function TextAreaField({
|
||||
onFocus={handleFocus}
|
||||
onChange={handleChange}
|
||||
onBlur={handleBlur}
|
||||
className="[field-sizing:content] max-h-[40vh] min-h-20 w-full resize-y overflow-x-hidden overflow-y-auto bg-transparent text-[11px] font-medium text-neutral-100 outline-none disabled:cursor-not-allowed disabled:text-neutral-600"
|
||||
className="field-sizing-content max-h-[40vh] min-h-20 w-full resize-y overflow-x-hidden overflow-y-auto bg-transparent text-[11px] font-medium text-neutral-100 outline-hidden disabled:cursor-not-allowed disabled:text-neutral-600"
|
||||
/>
|
||||
</div>
|
||||
</label>
|
||||
@@ -189,7 +189,7 @@ function FontWeightField({
|
||||
return (
|
||||
<div className={FIELD}>
|
||||
<div className="flex min-w-0 items-center gap-3">
|
||||
<span className="flex-shrink-0 text-[11px] font-medium text-neutral-500">Weight</span>
|
||||
<span className="shrink-0 text-[11px] font-medium text-neutral-500">Weight</span>
|
||||
<select
|
||||
value={value}
|
||||
disabled={disabled}
|
||||
@@ -197,7 +197,7 @@ function FontWeightField({
|
||||
track("select", "Weight");
|
||||
onCommit(e.target.value);
|
||||
}}
|
||||
className="min-w-0 w-full appearance-none bg-transparent text-[11px] font-medium text-neutral-100 outline-none disabled:cursor-not-allowed disabled:text-neutral-600"
|
||||
className="min-w-0 w-full appearance-none bg-transparent text-[11px] font-medium text-neutral-100 outline-hidden disabled:cursor-not-allowed disabled:text-neutral-600"
|
||||
>
|
||||
{displayOptions.map((o) => (
|
||||
<option key={o} value={o}>
|
||||
@@ -323,7 +323,7 @@ function TextFieldEditor({
|
||||
track("button", "Remove text field");
|
||||
onRemoveTextField(field.key);
|
||||
}}
|
||||
className="inline-flex h-7 flex-shrink-0 items-center rounded-lg border border-neutral-700 bg-neutral-950 px-2.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-white"
|
||||
className="inline-flex h-7 shrink-0 items-center rounded-lg border border-neutral-700 bg-neutral-950 px-2.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-white"
|
||||
>
|
||||
Remove
|
||||
</button>
|
||||
@@ -476,7 +476,7 @@ export function TextSection({
|
||||
}}
|
||||
className="inline-flex h-7 max-w-full items-center gap-1.5 rounded-lg border border-neutral-700 bg-neutral-950 px-2.5 text-[11px] font-medium text-neutral-300 transition-colors hover:border-neutral-600 hover:text-white"
|
||||
>
|
||||
<Plus size={12} className="flex-shrink-0" />
|
||||
<Plus size={12} className="shrink-0" />
|
||||
<span className="truncate">Add text</span>
|
||||
</button>
|
||||
</div>
|
||||
@@ -497,14 +497,14 @@ export function TextSection({
|
||||
<div className="flex min-w-0 items-center justify-between gap-2">
|
||||
<div className="flex min-w-0 items-center gap-2">
|
||||
<span
|
||||
className="h-4 w-4 flex-shrink-0 rounded border border-neutral-700 bg-neutral-950"
|
||||
className="h-4 w-4 shrink-0 rounded-sm border border-neutral-700 bg-neutral-950"
|
||||
style={{ backgroundColor: getTextFieldColor(field, styles) }}
|
||||
/>
|
||||
<span className="min-w-0 truncate text-[11px] font-medium text-neutral-100">
|
||||
{formatTextFieldPreview(field.value) || `Text ${index + 1}`}
|
||||
</span>
|
||||
</div>
|
||||
<span className="flex-shrink-0 rounded-md border border-neutral-700 bg-neutral-950 px-1.5 py-0.5 text-[10px] text-neutral-500">
|
||||
<span className="shrink-0 rounded-md border border-neutral-700 bg-neutral-950 px-1.5 py-0.5 text-[10px] text-neutral-500">
|
||||
{field.tagName}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -243,7 +243,7 @@ export const StudioFeedbackCard = memo(function StudioFeedbackCard() {
|
||||
if (req) trackStudioFeedbackInterviewClick({ reason: req.reason });
|
||||
close("sent");
|
||||
}}
|
||||
className="h-6 flex-shrink-0 whitespace-nowrap rounded-full border border-white/20 bg-white/[0.08] px-2.5 text-[11px] leading-6 text-neutral-100 transition-[background-color,border-color,transform] duration-150 ease-out hover:border-white/35 hover:bg-white/[0.16] active:scale-[0.97] motion-reduce:transition-none"
|
||||
className="h-6 shrink-0 whitespace-nowrap rounded-full border border-white/20 bg-white/8 px-2.5 text-[11px] leading-6 text-neutral-100 transition-[background-color,border-color,transform] duration-150 ease-out hover:border-white/35 hover:bg-white/16 active:scale-[0.97] motion-reduce:transition-none"
|
||||
>
|
||||
Talk to us, 30 min
|
||||
</a>
|
||||
@@ -251,7 +251,7 @@ export const StudioFeedbackCard = memo(function StudioFeedbackCard() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => close("close")}
|
||||
className="-mr-1 -mt-0.5 flex h-5 w-5 flex-shrink-0 items-center justify-center rounded-md text-neutral-500 transition-colors duration-150 hover:bg-white/10 hover:text-neutral-200"
|
||||
className="-mr-1 -mt-0.5 flex h-5 w-5 shrink-0 items-center justify-center rounded-md text-neutral-500 transition-colors duration-150 hover:bg-white/10 hover:text-neutral-200"
|
||||
aria-label="Dismiss"
|
||||
>
|
||||
<svg
|
||||
@@ -283,7 +283,7 @@ export const StudioFeedbackCard = memo(function StudioFeedbackCard() {
|
||||
key={n}
|
||||
// A resting fill, so the row reads as eleven controls rather
|
||||
// than as a line of text that happens to be clickable.
|
||||
className="flex h-7 cursor-pointer items-center justify-center rounded-md bg-white/[0.04] text-[11px] tabular-nums text-neutral-400 transition-[background-color,color,transform] duration-150 ease-out hover:bg-white/[0.14] hover:text-neutral-100 active:scale-[0.97] has-[:checked]:bg-white/90 has-[:checked]:text-neutral-900 has-[:focus-visible]:ring-1 has-[:focus-visible]:ring-white/50 motion-reduce:transition-none"
|
||||
className="flex h-7 cursor-pointer items-center justify-center rounded-md bg-white/4 text-[11px] tabular-nums text-neutral-400 transition-[background-color,color,transform] duration-150 ease-out hover:bg-white/[0.14] hover:text-neutral-100 active:scale-[0.97] has-checked:bg-white/90 has-checked:text-neutral-900 has-focus-visible:ring-1 has-focus-visible:ring-white/50 motion-reduce:transition-none"
|
||||
>
|
||||
<input
|
||||
type="radio"
|
||||
@@ -328,8 +328,8 @@ export const StudioFeedbackCard = memo(function StudioFeedbackCard() {
|
||||
// attached to nothing.
|
||||
className={`h-6 rounded-full border px-2.5 text-[11px] transition-[background-color,border-color,color,transform] duration-150 ease-out active:scale-[0.97] motion-reduce:transition-none ${
|
||||
failed
|
||||
? "border-red-300/20 bg-red-100/[0.06] text-red-100/80 hover:border-red-200/70 hover:bg-red-100/25 hover:text-white"
|
||||
: "border-white/10 bg-white/[0.04] text-neutral-300 hover:border-white/60 hover:bg-white/25 hover:text-white"
|
||||
? "border-red-300/20 bg-red-100/6 text-red-100/80 hover:border-red-200/70 hover:bg-red-100/25 hover:text-white"
|
||||
: "border-white/10 bg-white/4 text-neutral-300 hover:border-white/60 hover:bg-white/25 hover:text-white"
|
||||
}`}
|
||||
>
|
||||
{preset.label}
|
||||
@@ -355,12 +355,12 @@ export const StudioFeedbackCard = memo(function StudioFeedbackCard() {
|
||||
placeholder={followUp.placeholder}
|
||||
aria-label={followUp.prompt}
|
||||
maxLength={500}
|
||||
className="h-7 min-w-0 flex-1 rounded-md border border-white/10 bg-black/20 px-2 text-[11px] text-neutral-100 outline-none transition-colors duration-150 placeholder:text-neutral-500 focus:border-white/25"
|
||||
className="h-7 min-w-0 flex-1 rounded-md border border-white/10 bg-black/20 px-2 text-[11px] text-neutral-100 outline-hidden transition-colors duration-150 placeholder:text-neutral-500 focus:border-white/25"
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => submit()}
|
||||
className="h-7 flex-shrink-0 rounded-md bg-white/10 px-2.5 text-[11px] text-neutral-100 transition-[background-color,transform] duration-150 ease-out hover:bg-white/[0.16] active:scale-[0.97] motion-reduce:transition-none"
|
||||
className="h-7 shrink-0 rounded-md bg-white/10 px-2.5 text-[11px] text-neutral-100 transition-[background-color,transform] duration-150 ease-out hover:bg-white/16 active:scale-[0.97] motion-reduce:transition-none"
|
||||
>
|
||||
Send
|
||||
</button>
|
||||
|
||||
@@ -42,7 +42,11 @@ function AssetPreviewMedia({
|
||||
}) {
|
||||
if (kind === "image") {
|
||||
return (
|
||||
<img src={serveUrl} alt={name} className="max-w-full max-h-[40vh] rounded object-contain" />
|
||||
<img
|
||||
src={serveUrl}
|
||||
alt={name}
|
||||
className="max-w-full max-h-[40vh] rounded-sm object-contain"
|
||||
/>
|
||||
);
|
||||
}
|
||||
if (kind === "video") {
|
||||
@@ -53,7 +57,7 @@ function AssetPreviewMedia({
|
||||
autoPlay
|
||||
muted
|
||||
playsInline
|
||||
className="max-w-full max-h-[40vh] rounded"
|
||||
className="max-w-full max-h-[40vh] rounded-sm"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export function CompositionBreadcrumb({ stack, onNavigate }: CompositionBreadcru
|
||||
return (
|
||||
<nav
|
||||
aria-label="Composition navigation"
|
||||
className="flex items-center gap-1 px-2 h-8 border-b border-neutral-800/50 bg-neutral-900/50 flex-shrink-0"
|
||||
className="flex items-center gap-1 px-2 h-8 border-b border-neutral-800/50 bg-neutral-900/50 shrink-0"
|
||||
>
|
||||
{/* Back button — always goes to parent */}
|
||||
<button
|
||||
@@ -33,7 +33,7 @@ export function CompositionBreadcrumb({ stack, onNavigate }: CompositionBreadcru
|
||||
});
|
||||
onNavigate(stack.length - 2);
|
||||
}}
|
||||
className="flex items-center gap-1 px-1.5 py-0.5 rounded text-xs text-neutral-400 hover:text-white hover:bg-neutral-800 active:scale-[0.98] transition-colors"
|
||||
className="flex items-center gap-1 px-1.5 py-0.5 rounded-sm text-xs text-neutral-400 hover:text-white hover:bg-neutral-800 active:scale-[0.98] transition-colors"
|
||||
title="Back (Esc, or double-click empty timeline)"
|
||||
aria-label="Back to parent composition"
|
||||
>
|
||||
@@ -45,7 +45,7 @@ export function CompositionBreadcrumb({ stack, onNavigate }: CompositionBreadcru
|
||||
const isLast = i === stack.length - 1;
|
||||
return (
|
||||
<span key={level.id} className="flex items-center gap-1">
|
||||
{i > 0 && <CaretRight size={10} className="text-neutral-600 flex-shrink-0" />}
|
||||
{i > 0 && <CaretRight size={10} className="text-neutral-600 shrink-0" />}
|
||||
{isLast ? (
|
||||
<span className="text-xs text-neutral-200 font-medium">{level.label}</span>
|
||||
) : (
|
||||
|
||||
@@ -442,7 +442,7 @@ export const NLEPreview = memo(function NLEPreview({
|
||||
<div className="flex flex-col h-full min-h-0">
|
||||
<div
|
||||
ref={viewportRef}
|
||||
className="relative flex-1 flex items-center justify-center p-2 overflow-hidden min-h-0 outline-none focus:ring-1 focus:ring-studio-accent/40 bg-neutral-950"
|
||||
className="relative flex-1 flex items-center justify-center p-2 overflow-hidden min-h-0 outline-hidden focus:ring-1 focus:ring-studio-accent/40 bg-neutral-950"
|
||||
tabIndex={0}
|
||||
aria-label="Composition preview"
|
||||
>
|
||||
@@ -492,14 +492,14 @@ export const NLEPreview = memo(function NLEPreview({
|
||||
</div>
|
||||
<div
|
||||
ref={hudRef}
|
||||
className="pointer-events-none absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 rounded-lg px-4 py-2 text-sm font-mono tabular-nums text-white/90 bg-black/60 backdrop-blur-sm shadow-lg"
|
||||
className="pointer-events-none absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 z-50 rounded-lg px-4 py-2 text-sm font-mono tabular-nums text-white/90 bg-black/60 backdrop-blur-xs shadow-lg"
|
||||
style={{ opacity: 0, transition: "opacity 200ms ease-in" }}
|
||||
aria-live="polite"
|
||||
/>
|
||||
{!isPreviewAtFit(settledZoom) && (
|
||||
<button
|
||||
type="button"
|
||||
className="absolute bottom-3 right-3 z-50 rounded-md px-2.5 py-1 text-xs font-medium text-white/80 bg-black/50 backdrop-blur-sm hover:bg-black/70 hover:text-white transition-colors"
|
||||
className="absolute bottom-3 right-3 z-50 rounded-md px-2.5 py-1 text-xs font-medium text-white/80 bg-black/50 backdrop-blur-xs hover:bg-black/70 hover:text-white transition-colors"
|
||||
onClick={() => applyZoom(DEFAULT_PREVIEW_ZOOM)}
|
||||
aria-label="Reset zoom to fit"
|
||||
data-testid="preview-reset-zoom"
|
||||
|
||||
@@ -222,13 +222,13 @@ export function PreviewOverlays({
|
||||
<TopologyLens iframeRef={previewIframeRef} activeCompositionPath={activeCompPath} />
|
||||
<CaptionOverlay iframeRef={previewIframeRef} />
|
||||
{/* Mode indicator + explicit exit */}
|
||||
<div className="absolute top-2 left-1/2 -translate-x-1/2 z-[60] flex items-center gap-2 rounded-full border border-studio-accent/40 bg-black/70 px-2.5 py-1">
|
||||
<div className="absolute top-2 left-1/2 -translate-x-1/2 z-60 flex items-center gap-2 rounded-full border border-studio-accent/40 bg-black/70 px-2.5 py-1">
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-studio-accent" aria-hidden="true" />
|
||||
<span className="text-2xs text-neutral-200">Editing captions</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={exitCaptionMode}
|
||||
className="rounded text-2xs text-neutral-400 underline underline-offset-2 hover:text-neutral-100 focus-visible:outline focus-visible:outline-1 focus-visible:outline-studio-accent"
|
||||
className="rounded-sm text-2xs text-neutral-400 underline underline-offset-2 hover:text-neutral-100 focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-studio-accent"
|
||||
>
|
||||
Exit
|
||||
</button>
|
||||
@@ -236,13 +236,13 @@ export function PreviewOverlays({
|
||||
{captionSyncError && (
|
||||
<div
|
||||
role="alert"
|
||||
className="absolute top-10 left-1/2 -translate-x-1/2 z-[60] flex items-center gap-2 rounded-full border border-red-500/50 bg-red-950/90 px-2.5 py-1"
|
||||
className="absolute top-10 left-1/2 -translate-x-1/2 z-60 flex items-center gap-2 rounded-full border border-red-500/50 bg-red-950/90 px-2.5 py-1"
|
||||
>
|
||||
<span className="text-2xs text-red-200">{captionSyncError}</span>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => useCaptionStore.getState().retrySave?.()}
|
||||
className="rounded text-2xs text-red-100 underline underline-offset-2 hover:text-white"
|
||||
className="rounded-sm text-2xs text-red-100 underline underline-offset-2 hover:text-white"
|
||||
>
|
||||
Retry
|
||||
</button>
|
||||
@@ -250,7 +250,7 @@ export function PreviewOverlays({
|
||||
type="button"
|
||||
onClick={() => useCaptionStore.getState().setSyncError(null)}
|
||||
aria-label="Dismiss"
|
||||
className="rounded px-0.5 text-2xs text-red-300/70 hover:text-red-100"
|
||||
className="rounded-sm px-0.5 text-2xs text-red-300/70 hover:text-red-100"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
@@ -338,7 +338,7 @@ export function PreviewOverlays({
|
||||
<button
|
||||
type="button"
|
||||
onClick={enterCaptionMode}
|
||||
className="absolute top-2 left-1/2 -translate-x-1/2 z-[60] rounded-full border border-neutral-700 bg-black/60 px-2.5 py-1 text-2xs text-neutral-300 transition-colors hover:border-studio-accent/50 hover:text-studio-accent focus-visible:outline focus-visible:outline-1 focus-visible:outline-studio-accent"
|
||||
className="absolute top-2 left-1/2 -translate-x-1/2 z-60 rounded-full border border-neutral-700 bg-black/60 px-2.5 py-1 text-2xs text-neutral-300 transition-colors hover:border-studio-accent/50 hover:text-studio-accent focus-visible:outline-solid focus-visible:outline-1 focus-visible:outline-studio-accent"
|
||||
>
|
||||
Edit captions
|
||||
</button>
|
||||
|
||||
@@ -139,7 +139,7 @@ export function PreviewPane({
|
||||
onCompositionSizeChange={setPreviewCompositionSize}
|
||||
/>
|
||||
{previewDragOver && (
|
||||
<div className="absolute inset-2 z-40 rounded-lg border-2 border-dashed border-studio-accent/50 bg-studio-accent/[0.04] pointer-events-none" />
|
||||
<div className="absolute inset-2 z-40 rounded-lg border-2 border-dashed border-studio-accent/50 bg-studio-accent/4 pointer-events-none" />
|
||||
)}
|
||||
<AssetPreviewOverlay />
|
||||
</div>
|
||||
@@ -148,7 +148,7 @@ export function PreviewPane({
|
||||
</div>
|
||||
{/* Transport row: no own background or border — the controls sit flat on
|
||||
the preview panel's surface (CapCut-style). */}
|
||||
<div className="flex-shrink-0">
|
||||
<div className="shrink-0">
|
||||
{!isFullscreen && compositionStack.length > 1 && (
|
||||
<CompositionBreadcrumb stack={compositionStack} onNavigate={handleNavigateComposition} />
|
||||
)}
|
||||
|
||||
@@ -266,7 +266,7 @@ export function TimelinePane({
|
||||
{/* Timeline section — inner padding (not margin) keeps the divider's
|
||||
height math exact while giving the panel a gap from the shell edges. */}
|
||||
<div
|
||||
className="relative flex flex-col flex-shrink-0 px-px pb-px"
|
||||
className="relative flex flex-col shrink-0 px-px pb-px"
|
||||
style={{ height: timelineH }}
|
||||
aria-disabled={timelineDisabled || undefined}
|
||||
>
|
||||
@@ -280,7 +280,7 @@ export function TimelinePane({
|
||||
}
|
||||
}}
|
||||
>
|
||||
<div className="flex-shrink-0">{timelineToolbar}</div>
|
||||
<div className="shrink-0">{timelineToolbar}</div>
|
||||
<Timeline
|
||||
sessionEpoch={timelineSessionEpoch}
|
||||
onSeek={seek}
|
||||
@@ -304,7 +304,7 @@ export function TimelinePane({
|
||||
canPasteClip={canPasteClip}
|
||||
/>
|
||||
</div>
|
||||
{timelineFooter && <div className="flex-shrink-0">{timelineFooter}</div>}
|
||||
{timelineFooter && <div className="shrink-0">{timelineFooter}</div>}
|
||||
{timelineDisabled && (
|
||||
<div
|
||||
className="absolute inset-0 z-30 cursor-not-allowed bg-black/18 flex items-center justify-center"
|
||||
|
||||
@@ -79,7 +79,7 @@ export function TimelineResizeDivider({
|
||||
(containerRef.current?.getBoundingClientRect().height ?? 600) - MIN_PREVIEW_H,
|
||||
)}
|
||||
tabIndex={0}
|
||||
className="group relative h-[3px] flex-shrink-0 cursor-row-resize z-10 outline-none focus-visible:bg-studio-accent/20"
|
||||
className="group relative h-[3px] shrink-0 cursor-row-resize z-10 outline-hidden focus-visible:bg-studio-accent/20"
|
||||
style={{ touchAction: "none" }}
|
||||
onPointerDown={handlePointerDown}
|
||||
onPointerMove={handlePointerMove}
|
||||
@@ -93,7 +93,7 @@ export function TimelineResizeDivider({
|
||||
the 24px WCAG 2.2 (2.5.8) target because that is all the dead space
|
||||
there is: 28px transport buttons sit above and 28px toolbar buttons
|
||||
below, and silently stealing their clicks is the worse bug. */}
|
||||
<div className="absolute inset-x-0 -top-[4px] h-[10px]" />
|
||||
<div className="absolute inset-x-0 top-[-4px] h-[10px]" />
|
||||
{/* Visible hairline — invisible at rest, subtle wash on hover/drag/focus */}
|
||||
<div className="h-[3px] w-full bg-transparent transition-colors group-hover:bg-white/12 group-active:bg-white/18 group-focus-visible:bg-studio-accent/60" />
|
||||
</div>
|
||||
|
||||
@@ -435,7 +435,7 @@ export function SlideshowPanel({ scenes, onPersist, onPersistNotes }: SlideshowP
|
||||
type="button"
|
||||
disabled={retrying}
|
||||
onClick={handleRetryPersist}
|
||||
className="px-2 py-0.5 text-[10px] rounded bg-red-600 text-white enabled:hover:bg-red-500 enabled:active:scale-[0.97] disabled:opacity-50 transition-colors"
|
||||
className="px-2 py-0.5 text-[10px] rounded-sm bg-red-600 text-white enabled:hover:bg-red-500 enabled:active:scale-[0.97] disabled:opacity-50 transition-colors"
|
||||
>
|
||||
{retrying ? "Retrying…" : "Retry"}
|
||||
</button>
|
||||
@@ -447,7 +447,7 @@ export function SlideshowPanel({ scenes, onPersist, onPersistNotes }: SlideshowP
|
||||
type="button"
|
||||
onClick={handleUndo}
|
||||
title="Undo last slideshow edit (⌘Z)"
|
||||
className="px-2 py-0.5 text-[10px] rounded text-neutral-400 hover:text-neutral-200 hover:bg-neutral-800 active:scale-[0.97] transition-colors"
|
||||
className="px-2 py-0.5 text-[10px] rounded-sm text-neutral-400 hover:text-neutral-200 hover:bg-neutral-800 active:scale-[0.97] transition-colors"
|
||||
>
|
||||
Undo ({undoDepth})
|
||||
</button>
|
||||
|
||||
@@ -90,11 +90,11 @@ export function SlideList({
|
||||
checked={isSlide}
|
||||
onChange={() => onToggle(scene.id)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className="accent-studio-accent flex-shrink-0"
|
||||
className="accent-studio-accent shrink-0"
|
||||
/>
|
||||
<span className="flex-1 truncate">{scene.label || scene.id}</span>
|
||||
{isSlide && (
|
||||
<span className="flex gap-0.5 flex-shrink-0">
|
||||
<span className="flex gap-0.5 shrink-0">
|
||||
<button
|
||||
type="button"
|
||||
aria-label="Move slide up"
|
||||
@@ -162,7 +162,7 @@ export function SlideInspector({
|
||||
<div className="flex flex-col gap-1">
|
||||
<label className="text-[11px] text-neutral-400">Notes</label>
|
||||
<textarea
|
||||
className="bg-neutral-800 border border-neutral-700 rounded px-2 py-1.5 text-[11px] text-white resize-none placeholder-neutral-600 focus:border-studio-accent/60 focus:outline-none"
|
||||
className="bg-neutral-800 border border-neutral-700 rounded-sm px-2 py-1.5 text-[11px] text-white resize-none placeholder-neutral-600 focus:border-studio-accent/60 focus:outline-hidden"
|
||||
rows={3}
|
||||
placeholder="Speaker notes or script..."
|
||||
value={slide?.notes ?? ""}
|
||||
@@ -174,7 +174,7 @@ export function SlideInspector({
|
||||
<span className="text-[11px] text-neutral-400">Fragment hold-points</span>
|
||||
<button
|
||||
type="button"
|
||||
className="text-[10px] px-2 py-0.5 rounded bg-neutral-700 hover:bg-neutral-600 text-neutral-200 transition-colors"
|
||||
className="text-[10px] px-2 py-0.5 rounded-sm bg-neutral-700 hover:bg-neutral-600 text-neutral-200 transition-colors"
|
||||
onClick={onMarkFragment}
|
||||
title={`Mark ${currentTime.toFixed(2)}s as hold-point`}
|
||||
>
|
||||
@@ -186,7 +186,7 @@ export function SlideInspector({
|
||||
{fragments.map((t, i) => (
|
||||
<span
|
||||
key={`frag-${i}`}
|
||||
className="inline-flex items-center gap-1 bg-neutral-700 rounded px-1.5 py-0.5 text-[10px] text-neutral-200"
|
||||
className="inline-flex items-center gap-1 bg-neutral-700 rounded-sm px-1.5 py-0.5 text-[10px] text-neutral-200"
|
||||
>
|
||||
{t.toFixed(2)}s
|
||||
<button
|
||||
@@ -249,7 +249,7 @@ export function BranchTree({
|
||||
<input
|
||||
id={inputId}
|
||||
type="text"
|
||||
className="flex-1 bg-neutral-800 border border-neutral-700 rounded px-2 py-1 text-[11px] text-white placeholder-neutral-600 focus:border-studio-accent/60 focus:outline-none"
|
||||
className="flex-1 bg-neutral-800 border border-neutral-700 rounded-sm px-2 py-1 text-[11px] text-white placeholder-neutral-600 focus:border-studio-accent/60 focus:outline-hidden"
|
||||
placeholder="New branch name..."
|
||||
value={newLabel}
|
||||
onChange={(e) => setNewLabel(e.target.value)}
|
||||
@@ -260,7 +260,7 @@ export function BranchTree({
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="px-2 py-1 rounded bg-neutral-700 hover:bg-neutral-600 text-[11px] text-neutral-200 transition-colors flex-shrink-0"
|
||||
className="px-2 py-1 rounded-sm bg-neutral-700 hover:bg-neutral-600 text-[11px] text-neutral-200 transition-colors shrink-0"
|
||||
onClick={handleCreate}
|
||||
>
|
||||
Add
|
||||
@@ -322,11 +322,11 @@ function BranchItem({
|
||||
}, [draft, onRename, seq.id, seq.label]);
|
||||
|
||||
return (
|
||||
<div className="border border-neutral-700/60 rounded p-2 flex flex-col gap-2">
|
||||
<div className="border border-neutral-700/60 rounded-sm p-2 flex flex-col gap-2">
|
||||
<div className="flex items-center gap-1">
|
||||
{editing ? (
|
||||
<input
|
||||
className="flex-1 bg-neutral-800 border border-neutral-600 rounded px-1.5 py-0.5 text-[11px] text-white focus:border-studio-accent/60 focus:outline-none"
|
||||
className="flex-1 bg-neutral-800 border border-neutral-600 rounded-sm px-1.5 py-0.5 text-[11px] text-white focus:border-studio-accent/60 focus:outline-hidden"
|
||||
value={draft}
|
||||
autoFocus
|
||||
onChange={(e) => setDraft(e.target.value)}
|
||||
@@ -364,7 +364,7 @@ function BranchItem({
|
||||
</button>
|
||||
</div>
|
||||
{confirmingDelete && (
|
||||
<div className="px-2 py-1.5 bg-red-950/30 border-l-2 border-red-500 flex flex-col gap-1 rounded-sm">
|
||||
<div className="px-2 py-1.5 bg-red-950/30 border-l-2 border-red-500 flex flex-col gap-1 rounded-xs">
|
||||
<span className="text-[10px] text-red-400">
|
||||
Delete branch “{seq.label}”
|
||||
{seq.slides.length > 0
|
||||
@@ -379,14 +379,14 @@ function BranchItem({
|
||||
setConfirmingDelete(false);
|
||||
onDelete(seq.id);
|
||||
}}
|
||||
className="px-2 py-0.5 text-[10px] rounded bg-red-600 text-white hover:bg-red-500 active:bg-red-700 transition-colors"
|
||||
className="px-2 py-0.5 text-[10px] rounded-sm bg-red-600 text-white hover:bg-red-500 active:bg-red-700 transition-colors"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setConfirmingDelete(false)}
|
||||
className="px-2 py-0.5 text-[10px] rounded text-neutral-400 hover:text-neutral-200 transition-colors"
|
||||
className="px-2 py-0.5 text-[10px] rounded-sm text-neutral-400 hover:text-neutral-200 transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
@@ -407,7 +407,7 @@ function BranchItem({
|
||||
aria-label={`Assign ${scene.label || scene.id} to branch ${seq.label}`}
|
||||
checked={assigned}
|
||||
onChange={(e) => onAssign(seq.id, scene.id, e.target.checked)}
|
||||
className="accent-studio-accent flex-shrink-0"
|
||||
className="accent-studio-accent shrink-0"
|
||||
/>
|
||||
{assigned ? (
|
||||
<button
|
||||
@@ -497,7 +497,7 @@ export function HotspotTool({
|
||||
<label className="text-[11px] text-neutral-400">Hotspot label</label>
|
||||
<input
|
||||
type="text"
|
||||
className="bg-neutral-800 border border-neutral-700 rounded px-2 py-1 text-[11px] text-white placeholder-neutral-600 focus:border-studio-accent/60 focus:outline-none"
|
||||
className="bg-neutral-800 border border-neutral-700 rounded-sm px-2 py-1 text-[11px] text-white placeholder-neutral-600 focus:border-studio-accent/60 focus:outline-hidden"
|
||||
placeholder="Button label..."
|
||||
value={hotspotLabel}
|
||||
onChange={(e) => setHotspotLabel(e.target.value)}
|
||||
@@ -505,7 +505,7 @@ export function HotspotTool({
|
||||
/>
|
||||
<label className="text-[11px] text-neutral-400">Target branch</label>
|
||||
<select
|
||||
className="bg-neutral-800 border border-neutral-700 rounded px-2 py-1 text-[11px] text-white focus:border-studio-accent/60 focus:outline-none"
|
||||
className="bg-neutral-800 border border-neutral-700 rounded-sm px-2 py-1 text-[11px] text-white focus:border-studio-accent/60 focus:outline-hidden"
|
||||
value={targetSequenceId}
|
||||
onChange={(e) => setTargetSequenceId(e.target.value)}
|
||||
aria-label="Target branch sequence"
|
||||
@@ -527,7 +527,7 @@ export function HotspotTool({
|
||||
? "Choose a target branch first"
|
||||
: undefined
|
||||
}
|
||||
className="px-3 py-1.5 rounded bg-studio-accent/80 enabled:hover:bg-studio-accent enabled:active:scale-[0.98] text-white text-[11px] font-medium transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
className="px-3 py-1.5 rounded-sm bg-studio-accent/80 enabled:hover:bg-studio-accent enabled:active:scale-[0.98] text-white text-[11px] font-medium transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
onClick={handleMakeHotspot}
|
||||
>
|
||||
Make hotspot
|
||||
@@ -540,7 +540,10 @@ export function HotspotTool({
|
||||
{hotspots.map((h) => {
|
||||
const seqLabel = sequences.find((s) => s.id === h.target)?.label ?? h.target;
|
||||
return (
|
||||
<div key={h.id} className="flex items-center gap-2 bg-neutral-800 rounded px-2 py-1">
|
||||
<div
|
||||
key={h.id}
|
||||
className="flex items-center gap-2 bg-neutral-800 rounded-sm px-2 py-1"
|
||||
>
|
||||
<span className="flex-1 text-[11px] text-neutral-200 truncate">
|
||||
{h.label} → <span className="text-neutral-400">{seqLabel}</span>
|
||||
</span>
|
||||
|
||||
@@ -227,7 +227,7 @@ export function VariablesBindElement({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveKey(null)}
|
||||
className="h-6 rounded px-2 text-[10px] text-neutral-500 hover:text-neutral-300"
|
||||
className="h-6 rounded-sm px-2 text-[10px] text-neutral-500 hover:text-neutral-300"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
@@ -238,7 +238,7 @@ export function VariablesBindElement({
|
||||
setActiveKey(null);
|
||||
onBind(active, trimmedId);
|
||||
}}
|
||||
className="h-6 rounded bg-neutral-800 px-2 text-[10px] font-medium text-neutral-200 hover:bg-neutral-700 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
className="h-6 rounded-sm bg-neutral-800 px-2 text-[10px] font-medium text-neutral-200 hover:bg-neutral-700 disabled:cursor-not-allowed disabled:opacity-40"
|
||||
>
|
||||
{existingDecl ? "Bind anyway" : "Bind"}
|
||||
</button>
|
||||
|
||||
@@ -304,14 +304,14 @@ export function DeclarationForm({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
className="h-6 rounded px-2 text-[10px] text-neutral-500 hover:text-neutral-300"
|
||||
className="h-6 rounded-sm px-2 text-[10px] text-neutral-500 hover:text-neutral-300"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
onClick={submit}
|
||||
className="h-6 rounded bg-neutral-800 px-2 text-[10px] font-medium text-neutral-200 hover:bg-neutral-700"
|
||||
className="h-6 rounded-sm bg-neutral-800 px-2 text-[10px] font-medium text-neutral-200 hover:bg-neutral-700"
|
||||
>
|
||||
{submitLabel}
|
||||
</button>
|
||||
|
||||
@@ -51,7 +51,7 @@ function CompositionSection({
|
||||
<span className="truncate text-[10px] font-medium text-neutral-300">
|
||||
{decl.label}
|
||||
</span>
|
||||
<span className="rounded bg-neutral-800 px-1 py-px font-mono text-[8px] text-neutral-500">
|
||||
<span className="rounded-sm bg-neutral-800 px-1 py-px font-mono text-[8px] text-neutral-500">
|
||||
{decl.type}
|
||||
</span>
|
||||
<span className="ml-auto flex items-center gap-1">
|
||||
|
||||
@@ -96,12 +96,12 @@ function VariableRow({
|
||||
<div className="space-y-1.5 rounded-lg border border-neutral-800/70 p-2">
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="truncate text-[10px] font-medium text-neutral-300">{decl.label}</span>
|
||||
<span className="rounded bg-neutral-800 px-1 py-px font-mono text-[8px] text-neutral-500">
|
||||
<span className="rounded-sm bg-neutral-800 px-1 py-px font-mono text-[8px] text-neutral-500">
|
||||
{decl.type}
|
||||
</span>
|
||||
{unused && (
|
||||
<span
|
||||
className="rounded bg-amber-900/40 px-1 py-px text-[8px] text-amber-400"
|
||||
className="rounded-sm bg-amber-900/40 px-1 py-px text-[8px] text-amber-400"
|
||||
title="No script reads this variable"
|
||||
>
|
||||
unused
|
||||
@@ -189,7 +189,7 @@ function PreviewModeHeader({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onReset}
|
||||
className="h-6 rounded px-2 text-[10px] text-neutral-400 hover:text-neutral-200"
|
||||
className="h-6 rounded-sm px-2 text-[10px] text-neutral-400 hover:text-neutral-200"
|
||||
>
|
||||
Reset
|
||||
</button>
|
||||
|
||||
@@ -14,7 +14,7 @@ import type {
|
||||
} from "@hyperframes/sdk";
|
||||
|
||||
export const VARIABLES_INPUT_CLASS =
|
||||
"w-full bg-neutral-900 border border-neutral-800 rounded px-2 py-1 text-[10px] text-neutral-200 focus:outline-none focus:border-neutral-700";
|
||||
"w-full bg-neutral-900 border border-neutral-800 rounded-sm px-2 py-1 text-[10px] text-neutral-200 focus:outline-hidden focus:border-neutral-700";
|
||||
|
||||
/** Text input that drafts locally and commits on blur/Enter. */
|
||||
function DraftTextInput({
|
||||
@@ -106,7 +106,7 @@ function ColorControl({
|
||||
if (draft !== null && draft !== colorValue) onCommit(draft);
|
||||
setDraft(null);
|
||||
}}
|
||||
className="h-6 w-6 cursor-pointer rounded border border-neutral-700 bg-transparent"
|
||||
className="h-6 w-6 cursor-pointer rounded-sm border border-neutral-700 bg-transparent"
|
||||
/>
|
||||
<DraftTextInput
|
||||
value={colorValue}
|
||||
|
||||
@@ -8,7 +8,7 @@ const CUE_MS = 1600;
|
||||
// Matches the focus treatment every other button in this panel uses. A control
|
||||
// the keyboard can reach but not see focus on is unusable without a mouse.
|
||||
const FOCUS_RING =
|
||||
"outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-studio-accent";
|
||||
"outline-hidden focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-studio-accent";
|
||||
|
||||
/**
|
||||
* Shown above Export when the dev server reports no usable FFmpeg.
|
||||
@@ -81,7 +81,7 @@ export const FfmpegRequiredNotice = memo(function FfmpegRequiredNotice({
|
||||
|
||||
{status.command ? (
|
||||
<div className="flex items-center gap-1.5">
|
||||
<code className="flex-1 overflow-x-auto whitespace-nowrap rounded-sm bg-black/40 px-2 py-1 text-[10px] text-panel-text-2">
|
||||
<code className="flex-1 overflow-x-auto whitespace-nowrap rounded-xs bg-black/40 px-2 py-1 text-[10px] text-panel-text-2">
|
||||
{status.command}
|
||||
</code>
|
||||
{/* Fixed width so swapping the label to "Copied" cannot shift the
|
||||
@@ -89,7 +89,7 @@ export const FfmpegRequiredNotice = memo(function FfmpegRequiredNotice({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => void copy(status.command ?? "")}
|
||||
className={`h-6 w-14 flex-shrink-0 rounded-sm border border-amber-500/30 text-[10px] font-medium text-amber-200 transition-colors hover:bg-amber-500/20 active:scale-[0.98] ${FOCUS_RING}`}
|
||||
className={`h-6 w-14 shrink-0 rounded-xs border border-amber-500/30 text-[10px] font-medium text-amber-200 transition-colors hover:bg-amber-500/20 active:scale-[0.98] ${FOCUS_RING}`}
|
||||
>
|
||||
{copied ? "Copied" : "Copy"}
|
||||
</button>
|
||||
@@ -107,7 +107,7 @@ export const FfmpegRequiredNotice = memo(function FfmpegRequiredNotice({
|
||||
type="button"
|
||||
onClick={onRecheck}
|
||||
disabled={checking}
|
||||
className={`rounded-sm py-0.5 text-[10px] font-medium text-amber-200 underline-offset-2 transition-colors hover:underline disabled:opacity-50 ${FOCUS_RING}`}
|
||||
className={`rounded-xs py-0.5 text-[10px] font-medium text-amber-200 underline-offset-2 transition-colors hover:underline disabled:opacity-50 ${FOCUS_RING}`}
|
||||
>
|
||||
{checking ? "Checking…" : "Recheck"}
|
||||
</button>
|
||||
@@ -115,7 +115,7 @@ export const FfmpegRequiredNotice = memo(function FfmpegRequiredNotice({
|
||||
href={DOWNLOAD_URL}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className={`rounded-sm py-0.5 text-[10px] text-panel-text-2 underline-offset-2 transition-colors hover:text-panel-text-0 hover:underline ${FOCUS_RING}`}
|
||||
className={`rounded-xs py-0.5 text-[10px] text-panel-text-2 underline-offset-2 transition-colors hover:text-panel-text-0 hover:underline ${FOCUS_RING}`}
|
||||
>
|
||||
Other install options
|
||||
</a>
|
||||
|
||||
@@ -81,6 +81,13 @@ function exportButtonIn(host: HTMLElement): HTMLButtonElement {
|
||||
return button;
|
||||
}
|
||||
|
||||
describe("RenderQueue Export button", () => {
|
||||
it("leaves the font size to the shared Button", () => {
|
||||
const host = mountRenderQueue(vi.fn());
|
||||
expect(exportButtonIn(host).className).not.toMatch(/text-\[/);
|
||||
});
|
||||
});
|
||||
|
||||
describe("RenderQueue FFmpeg gate", () => {
|
||||
it("refuses Export and shows the install command when the server reports no FFmpeg", () => {
|
||||
ffmpegStatus = {
|
||||
|
||||
@@ -207,7 +207,7 @@ function FormatInfoTooltip({ format }: { format: "mp4" | "webm" | "mov" }) {
|
||||
onFocus={show}
|
||||
onBlur={hide}
|
||||
onClick={() => setOpen((prev) => !prev)}
|
||||
className="flex items-center justify-center p-0.5 -m-0.5 rounded text-panel-text-5 hover:text-panel-text-3 transition-colors cursor-help outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-studio-accent"
|
||||
className="flex items-center justify-center p-0.5 -m-0.5 rounded-sm text-panel-text-5 hover:text-panel-text-3 transition-colors cursor-help outline-hidden focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-studio-accent"
|
||||
>
|
||||
<svg
|
||||
width="12"
|
||||
@@ -232,7 +232,7 @@ function FormatInfoTooltip({ format }: { format: "mp4" | "webm" | "mov" }) {
|
||||
role="tooltip"
|
||||
onPointerEnter={show}
|
||||
onPointerLeave={hide}
|
||||
className="fixed w-52 p-2 rounded bg-panel-input border border-neutral-700 shadow-lg z-[200]"
|
||||
className="fixed w-52 p-2 rounded-sm bg-panel-input border border-neutral-700 shadow-lg z-200"
|
||||
style={{ left: position?.left ?? -9999, top: position?.top ?? -9999 }}
|
||||
>
|
||||
<p className="text-[10px] font-semibold text-panel-text-1 mb-0.5">{info.label}</p>
|
||||
@@ -303,7 +303,7 @@ function FormatExportButton({
|
||||
const showQuality = format !== "mov";
|
||||
|
||||
const selectCls =
|
||||
"h-7 w-full px-2 text-[11px] bg-panel-input rounded-md text-panel-text-1 outline-none cursor-pointer disabled:opacity-50 hover:bg-panel-hover transition-colors";
|
||||
"h-7 w-full px-2 text-[11px] bg-panel-input rounded-md text-panel-text-1 outline-hidden cursor-pointer disabled:opacity-50 hover:bg-panel-hover transition-colors";
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-3">
|
||||
@@ -407,7 +407,7 @@ function FormatExportButton({
|
||||
trackStudioEvent("render_start", { format, quality, resolution: outputResolution, fps });
|
||||
void onStartRender(format, quality, outputResolution, fps);
|
||||
}}
|
||||
className="w-full text-[11px] font-semibold"
|
||||
className="w-full font-semibold"
|
||||
>
|
||||
{isRendering ? "Rendering…" : "Export"}
|
||||
</Button>
|
||||
@@ -454,7 +454,7 @@ export const RenderQueue = memo(function RenderQueue({
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="px-3 py-3 border-b border-panel-border flex-shrink-0">
|
||||
<div className="px-3 py-3 border-b border-panel-border shrink-0">
|
||||
<FormatExportButton
|
||||
onStartRender={onStartRender}
|
||||
isRendering={isRendering}
|
||||
@@ -476,7 +476,7 @@ export const RenderQueue = memo(function RenderQueue({
|
||||
<button
|
||||
onClick={onDismissActionError}
|
||||
aria-label="Dismiss error"
|
||||
className="text-[10px] text-panel-text-4 hover:text-panel-text-2 flex-shrink-0"
|
||||
className="text-[10px] text-panel-text-4 hover:text-panel-text-2 shrink-0"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
|
||||
@@ -77,8 +77,8 @@ export const RenderQueueItem = memo(function RenderQueueItem({
|
||||
disabled={!isComplete}
|
||||
aria-label={isComplete ? `Open ${job.filename} in a new tab` : undefined}
|
||||
className={[
|
||||
"w-20 h-[45px] rounded-md overflow-hidden bg-panel-input flex-shrink-0 relative",
|
||||
"outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-studio-accent",
|
||||
"w-20 h-[45px] rounded-md overflow-hidden bg-panel-input shrink-0 relative",
|
||||
"outline-hidden focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-studio-accent",
|
||||
isComplete ? "cursor-pointer" : "cursor-default",
|
||||
].join(" ")}
|
||||
>
|
||||
@@ -130,7 +130,7 @@ export const RenderQueueItem = memo(function RenderQueueItem({
|
||||
{job.filename}
|
||||
</span>
|
||||
{job.durationMs && (
|
||||
<span className="text-[9px] text-panel-text-5 flex-shrink-0">
|
||||
<span className="text-[9px] text-panel-text-5 shrink-0">
|
||||
{formatDuration(job.durationMs)}
|
||||
</span>
|
||||
)}
|
||||
@@ -171,7 +171,7 @@ export const RenderQueueItem = memo(function RenderQueueItem({
|
||||
</div>
|
||||
|
||||
{/* Actions — always visible to prevent layout shifts */}
|
||||
<div className="flex items-center gap-1.5 flex-shrink-0">
|
||||
<div className="flex items-center gap-1.5 shrink-0">
|
||||
{isRendering ? (
|
||||
<Button
|
||||
size="sm"
|
||||
@@ -211,7 +211,7 @@ export const RenderQueueItem = memo(function RenderQueueItem({
|
||||
<>
|
||||
<button
|
||||
onClick={isComplete ? handleDownload : undefined}
|
||||
className={`p-1.5 min-w-6 min-h-6 rounded transition-colors outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-studio-accent ${
|
||||
className={`p-1.5 min-w-6 min-h-6 rounded transition-colors outline-hidden focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-studio-accent ${
|
||||
isComplete
|
||||
? "text-panel-text-5 hover:text-panel-accent"
|
||||
: "text-panel-text-5/30 cursor-default"
|
||||
@@ -240,7 +240,7 @@ export const RenderQueueItem = memo(function RenderQueueItem({
|
||||
e.stopPropagation();
|
||||
setConfirmingDelete(true);
|
||||
}}
|
||||
className="p-1.5 min-w-6 min-h-6 rounded text-panel-text-5 hover:text-red-400 transition-colors outline-none focus-visible:outline focus-visible:outline-2 focus-visible:outline-studio-accent"
|
||||
className="p-1.5 min-w-6 min-h-6 rounded-sm text-panel-text-5 hover:text-red-400 transition-colors outline-hidden focus-visible:outline-solid focus-visible:outline-2 focus-visible:outline-studio-accent"
|
||||
title="Delete render file"
|
||||
aria-label={`Delete ${job.filename}`}
|
||||
>
|
||||
|
||||
@@ -30,7 +30,7 @@ function CopyChip({ feedback, asset }: { feedback: CopyFeedback; asset: string }
|
||||
return (
|
||||
<span
|
||||
role="status"
|
||||
className={`flex-shrink-0 text-[9px] font-medium px-1.5 py-px rounded ${
|
||||
className={`shrink-0 text-[9px] font-medium px-1.5 py-px rounded ${
|
||||
feedback.ok ? "text-panel-accent bg-panel-accent/10" : "text-red-400 bg-red-500/10"
|
||||
}`}
|
||||
>
|
||||
@@ -223,12 +223,12 @@ export function AssetCard({
|
||||
onContextMenu={(e) => openAssetContextMenu(e, setContextMenu)}
|
||||
onPointerEnter={() => setHovered(true)}
|
||||
onPointerLeave={() => setHovered(false)}
|
||||
className={`flex flex-col gap-1 cursor-pointer rounded-md p-1 transition-colors outline-none focus-visible:bg-neutral-800/60 ${
|
||||
className={`flex flex-col gap-1 cursor-pointer rounded-md p-1 transition-colors outline-hidden focus-visible:bg-neutral-800/60 ${
|
||||
isCopied ? "bg-studio-accent/10" : "hover:bg-neutral-800/40"
|
||||
}`}
|
||||
>
|
||||
{/* Thumbnail */}
|
||||
<div className="w-full aspect-video rounded overflow-hidden bg-neutral-900 relative">
|
||||
<div className="w-full aspect-video rounded-sm overflow-hidden bg-neutral-900 relative">
|
||||
{isImage && !imgError && (
|
||||
<img
|
||||
src={serveUrl}
|
||||
@@ -261,14 +261,14 @@ export function AssetCard({
|
||||
|
||||
{/* "Added" badge — top-left */}
|
||||
{used && (
|
||||
<span className="absolute top-1 left-1 text-[9px] font-semibold leading-none px-1.5 py-[3px] rounded bg-neutral-950/80 text-panel-text-1">
|
||||
<span className="absolute top-1 left-1 text-[9px] font-semibold leading-none px-1.5 py-[3px] rounded-sm bg-neutral-950/80 text-panel-text-1">
|
||||
Added
|
||||
</span>
|
||||
)}
|
||||
|
||||
{/* Duration badge — top-right, media only */}
|
||||
{durationLabel && (
|
||||
<span className="absolute top-1 right-1 text-[9px] font-medium leading-none px-1.5 py-[3px] rounded bg-neutral-950/80 text-panel-text-2 tabular-nums">
|
||||
<span className="absolute top-1 right-1 text-[9px] font-medium leading-none px-1.5 py-[3px] rounded-sm bg-neutral-950/80 text-panel-text-2 tabular-nums">
|
||||
{durationLabel}
|
||||
</span>
|
||||
)}
|
||||
@@ -348,13 +348,13 @@ export function FontRow({
|
||||
}}
|
||||
onDragStart={(e) => writeAssetDragData(e, asset)}
|
||||
onContextMenu={(e) => openAssetContextMenu(e, setContextMenu)}
|
||||
className={`px-2.5 py-1.5 flex items-center gap-2.5 cursor-pointer transition-colors outline-none focus-visible:bg-neutral-800/60 ${
|
||||
className={`px-2.5 py-1.5 flex items-center gap-2.5 cursor-pointer transition-colors outline-hidden focus-visible:bg-neutral-800/60 ${
|
||||
isCopied
|
||||
? "bg-studio-accent/10 border-l-2 border-studio-accent"
|
||||
: "border-l-2 border-transparent hover:bg-neutral-800/50"
|
||||
}`}
|
||||
>
|
||||
<div className="w-[50px] h-[32px] rounded overflow-hidden bg-neutral-900 flex-shrink-0 flex items-center justify-center">
|
||||
<div className="w-[50px] h-[32px] rounded-sm overflow-hidden bg-neutral-900 shrink-0 flex items-center justify-center">
|
||||
<span className="text-[9px] font-medium text-neutral-700">{extension}</span>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
@@ -366,7 +366,7 @@ export function FontRow({
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-[10px] text-neutral-600 truncate">{extension}</span>
|
||||
{used && (
|
||||
<span className="text-[9px] font-medium text-panel-accent bg-panel-accent/10 px-1.5 py-px rounded">
|
||||
<span className="text-[9px] font-medium text-panel-accent bg-panel-accent/10 px-1.5 py-px rounded-sm">
|
||||
in use
|
||||
</span>
|
||||
)}
|
||||
|
||||
@@ -95,11 +95,11 @@ export function ContextMenu({
|
||||
}, [renameDraft, asset, onRename, onClose]);
|
||||
|
||||
const itemCls =
|
||||
"w-full text-left px-3 py-1.5 text-neutral-300 hover:bg-neutral-800 focus-visible:bg-neutral-800 outline-none active:bg-neutral-700/70 transition-colors";
|
||||
"w-full text-left px-3 py-1.5 text-neutral-300 hover:bg-neutral-800 focus-visible:bg-neutral-800 outline-hidden active:bg-neutral-700/70 transition-colors";
|
||||
|
||||
return (
|
||||
<div
|
||||
className="fixed inset-0 z-[200]"
|
||||
className="fixed inset-0 z-200"
|
||||
onClick={onClose}
|
||||
onContextMenu={(e) => {
|
||||
e.preventDefault();
|
||||
@@ -181,19 +181,19 @@ export function ContextMenu({
|
||||
}
|
||||
}}
|
||||
aria-label={`Rename ${filename(asset)}`}
|
||||
className="w-full bg-neutral-800 border border-neutral-600 rounded px-1.5 py-1 text-[11px] text-white focus:border-studio-accent/60 focus:outline-none"
|
||||
className="w-full bg-neutral-800 border border-neutral-600 rounded-sm px-1.5 py-1 text-[11px] text-white focus:border-studio-accent/60 focus:outline-hidden"
|
||||
/>
|
||||
{renameError && <span className="text-[10px] text-red-400">{renameError}</span>}
|
||||
<div className="flex items-center justify-end gap-1">
|
||||
<button
|
||||
onClick={() => setMode("menu")}
|
||||
className="px-2 py-0.5 text-[10px] rounded text-neutral-400 hover:text-neutral-200 transition-colors"
|
||||
className="px-2 py-0.5 text-[10px] rounded-sm text-neutral-400 hover:text-neutral-200 transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
onClick={commitRename}
|
||||
className="px-2 py-0.5 text-[10px] rounded bg-studio-accent/80 hover:bg-studio-accent text-white transition-colors"
|
||||
className="px-2 py-0.5 text-[10px] rounded-sm bg-studio-accent/80 hover:bg-studio-accent text-white transition-colors"
|
||||
>
|
||||
Rename
|
||||
</button>
|
||||
@@ -217,16 +217,16 @@ function DeleteConfirm({
|
||||
return (
|
||||
<div className="px-2 py-1.5 bg-red-950/30 border-l-2 border-red-500 flex items-center justify-between gap-2">
|
||||
<span className="text-[10px] text-red-400 truncate">Delete {name}?</span>
|
||||
<div className="flex items-center gap-1 flex-shrink-0">
|
||||
<div className="flex items-center gap-1 shrink-0">
|
||||
<button
|
||||
onClick={onConfirm}
|
||||
className="px-2 py-0.5 text-[10px] rounded bg-red-600 text-white hover:bg-red-500 active:bg-red-700 transition-colors"
|
||||
className="px-2 py-0.5 text-[10px] rounded-sm bg-red-600 text-white hover:bg-red-500 active:bg-red-700 transition-colors"
|
||||
>
|
||||
Delete
|
||||
</button>
|
||||
<button
|
||||
onClick={onCancel}
|
||||
className="px-2 py-0.5 text-[10px] rounded text-neutral-400 hover:text-neutral-200 transition-colors"
|
||||
className="px-2 py-0.5 text-[10px] rounded-sm text-neutral-400 hover:text-neutral-200 transition-colors"
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
|
||||
@@ -331,7 +331,7 @@ export const AssetsTab = memo(function AssetsTab({
|
||||
: [activeFilter as MediaCategory].filter((c) => categorized[c].length > 0);
|
||||
return (
|
||||
<div
|
||||
className={`flex-1 flex flex-col min-h-0 transition-colors ${dragOver ? "bg-studio-accent/[0.05]" : ""}`}
|
||||
className={`flex-1 flex flex-col min-h-0 transition-colors ${dragOver ? "bg-studio-accent/5" : ""}`}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
setDragOver(true);
|
||||
@@ -340,7 +340,7 @@ export const AssetsTab = memo(function AssetsTab({
|
||||
onDrop={handleDrop}
|
||||
>
|
||||
{/* Header — matches design panel Section pattern */}
|
||||
<div className="px-4 pt-2.5 pb-1.5 flex-shrink-0">
|
||||
<div className="px-4 pt-2.5 pb-1.5 shrink-0">
|
||||
{/* Scope toggle */}
|
||||
<div className="flex gap-1 mb-2.5 p-0.5 rounded-md bg-panel-input">
|
||||
{(["local", "global"] as const).map((m) => (
|
||||
|
||||
@@ -227,9 +227,9 @@ export function AudioRow({
|
||||
e.preventDefault();
|
||||
setContextMenu({ x: e.clientX, y: e.clientY });
|
||||
}}
|
||||
className={`group w-full text-left px-4 py-1.5 flex items-center gap-2.5 transition-colors cursor-pointer outline-none focus-visible:bg-neutral-800/60 ${
|
||||
className={`group w-full text-left px-4 py-1.5 flex items-center gap-2.5 transition-colors cursor-pointer outline-hidden focus-visible:bg-neutral-800/60 ${
|
||||
playing
|
||||
? "bg-panel-accent/[0.06]"
|
||||
? "bg-panel-accent/6"
|
||||
: isCopied
|
||||
? "bg-panel-accent/10"
|
||||
: "hover:bg-panel-surface-hover"
|
||||
@@ -238,7 +238,7 @@ export function AudioRow({
|
||||
<button
|
||||
aria-label={playing ? `Pause preview of ${name}` : `Play preview of ${name}`}
|
||||
aria-pressed={playing}
|
||||
className={`w-7 h-7 rounded-md flex-shrink-0 flex items-center justify-center transition-colors active:scale-[0.95] ${
|
||||
className={`w-7 h-7 rounded-md shrink-0 flex items-center justify-center transition-colors active:scale-[0.95] ${
|
||||
playing
|
||||
? "bg-panel-accent/15 text-panel-accent"
|
||||
: "text-panel-text-5 group-hover:text-panel-text-3"
|
||||
@@ -267,20 +267,20 @@ export function AudioRow({
|
||||
{name}
|
||||
</span>
|
||||
{!playing && (
|
||||
<span className="text-[11px] text-panel-text-5 flex-shrink-0">
|
||||
<span className="text-[11px] text-panel-text-5 shrink-0">
|
||||
{meta?.duration ? `${meta.duration}s · ` : ""}
|
||||
{subtype}
|
||||
</span>
|
||||
)}
|
||||
{used && (
|
||||
<span className="text-[9px] font-medium text-panel-accent bg-panel-accent/10 px-1.5 py-px rounded flex-shrink-0">
|
||||
<span className="text-[9px] font-medium text-panel-accent bg-panel-accent/10 px-1.5 py-px rounded-sm shrink-0">
|
||||
in use
|
||||
</span>
|
||||
)}
|
||||
{(isCopied || copyFailed) && (
|
||||
<span
|
||||
role="status"
|
||||
className={`flex-shrink-0 text-[9px] font-medium px-1.5 py-px rounded ${
|
||||
className={`shrink-0 text-[9px] font-medium px-1.5 py-px rounded ${
|
||||
copyFailed ? "text-red-400 bg-red-500/10" : "text-panel-accent bg-panel-accent/10"
|
||||
}`}
|
||||
>
|
||||
|
||||
@@ -49,7 +49,7 @@ export const BlocksTab = memo(function BlocksTab({ onAddBlock, onPreviewBlock }:
|
||||
return (
|
||||
<div className="flex flex-col flex-1 min-h-0">
|
||||
{/* Search */}
|
||||
<div className="px-3 pt-2 pb-1 flex-shrink-0">
|
||||
<div className="px-3 pt-2 pb-1 shrink-0">
|
||||
<SearchInput
|
||||
value={search}
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
@@ -59,7 +59,7 @@ export const BlocksTab = memo(function BlocksTab({ onAddBlock, onPreviewBlock }:
|
||||
</div>
|
||||
|
||||
{/* Category pills */}
|
||||
<div className="px-3 pt-1 pb-2 flex-shrink-0 overflow-x-auto">
|
||||
<div className="px-3 pt-1 pb-2 shrink-0 overflow-x-auto">
|
||||
<div className="flex gap-1">
|
||||
<CategoryPill label="All" active={category === null} onClick={() => setCategory(null)} />
|
||||
{BLOCK_CATEGORIES.map((cat) => (
|
||||
@@ -151,7 +151,7 @@ function CategoryPill({
|
||||
type="button"
|
||||
onClick={onClick}
|
||||
aria-pressed={active}
|
||||
className={`flex-shrink-0 px-2 py-1 rounded-full text-[10px] font-medium transition-colors active:scale-[0.98] ${
|
||||
className={`shrink-0 px-2 py-1 rounded-full text-[10px] font-medium transition-colors active:scale-[0.98] ${
|
||||
active
|
||||
? colors
|
||||
? `${colors.bg} ${colors.text}`
|
||||
@@ -474,12 +474,12 @@ function BlockCard({
|
||||
{/* Badges */}
|
||||
<div className="absolute top-1 right-1 flex items-center gap-0.5 pointer-events-none">
|
||||
{needsWebGL && (
|
||||
<span className="px-1 py-px rounded text-[7px] font-semibold text-purple-300 bg-purple-900/70">
|
||||
<span className="px-1 py-px rounded-sm text-[7px] font-semibold text-purple-300 bg-purple-900/70">
|
||||
WebGL
|
||||
</span>
|
||||
)}
|
||||
{duration != null && (
|
||||
<span className="px-1 py-px rounded text-[8px] font-medium text-white/80 bg-black/50">
|
||||
<span className="px-1 py-px rounded-sm text-[8px] font-medium text-white/80 bg-black/50">
|
||||
{duration}s
|
||||
</span>
|
||||
)}
|
||||
@@ -492,7 +492,7 @@ function BlockCard({
|
||||
{title}
|
||||
</div>
|
||||
<div className="flex items-center gap-1 mt-0.5">
|
||||
<span className={`w-1.5 h-1.5 rounded-full flex-shrink-0 ${colors.dot}`} />
|
||||
<span className={`w-1.5 h-1.5 rounded-full shrink-0 ${colors.dot}`} />
|
||||
<span className={`text-[8px] ${colors.text}`}>
|
||||
{BLOCK_CATEGORIES.find((c) => c.id === category)?.label}
|
||||
</span>
|
||||
|
||||
@@ -238,13 +238,13 @@ function CompCard({
|
||||
}}
|
||||
onPointerEnter={handleEnter}
|
||||
onPointerLeave={handleLeave}
|
||||
className={`group/card w-full select-none text-left px-2 py-1.5 flex items-center gap-2.5 transition-colors cursor-grab active:cursor-grabbing outline-none focus-visible:bg-neutral-800/60 ${
|
||||
className={`group/card w-full select-none text-left px-2 py-1.5 flex items-center gap-2.5 transition-colors cursor-grab active:cursor-grabbing outline-hidden focus-visible:bg-neutral-800/60 ${
|
||||
isActive
|
||||
? "bg-studio-accent/10 border-l-2 border-studio-accent"
|
||||
: "border-l-2 border-transparent hover:bg-neutral-800/50"
|
||||
}`}
|
||||
>
|
||||
<div className="w-20 h-[45px] rounded overflow-hidden bg-neutral-900 flex-shrink-0 relative">
|
||||
<div className="w-20 h-[45px] rounded-sm overflow-hidden bg-neutral-900 shrink-0 relative">
|
||||
{thumbnailFailed ? (
|
||||
<div className="absolute inset-0 flex items-center justify-center px-1 text-center text-[8px] leading-tight text-neutral-600">
|
||||
Preview unavailable
|
||||
@@ -314,7 +314,7 @@ function CompCard({
|
||||
{lintInfo && lintInfo.count > 0 && (
|
||||
<span
|
||||
aria-label={`${lintInfo.count} lint finding${lintInfo.count === 1 ? "" : "s"}`}
|
||||
className="flex-shrink-0 min-w-[16px] text-center rounded-full bg-amber-500/20 px-1 text-[8px] font-bold text-amber-400"
|
||||
className="shrink-0 min-w-[16px] text-center rounded-full bg-amber-500/20 px-1 text-[8px] font-bold text-amber-400"
|
||||
>
|
||||
{lintInfo.count}
|
||||
</span>
|
||||
@@ -331,7 +331,7 @@ function CompCard({
|
||||
event.stopPropagation();
|
||||
onAddToTimeline();
|
||||
}}
|
||||
className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded text-neutral-600 opacity-0 transition-[color,background-color,opacity] hover:bg-neutral-800 hover:text-studio-accent group-hover/card:opacity-100 group-focus-within/card:opacity-100 focus:opacity-100"
|
||||
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-sm text-neutral-600 opacity-0 transition-[color,background-color,opacity] hover:bg-neutral-800 hover:text-studio-accent group-hover/card:opacity-100 group-focus-within/card:opacity-100 focus:opacity-100"
|
||||
>
|
||||
<span aria-hidden="true">+</span>
|
||||
</button>
|
||||
@@ -349,7 +349,7 @@ function CompCard({
|
||||
// h-6 w-6 = the 24x24 WCAG 2.2 (2.5.8) minimum target; the 14px glyph
|
||||
// is unchanged, only the box grows. The sibling "+" button is h-8 w-8,
|
||||
// so the card row already has the room.
|
||||
className={`flex h-6 w-6 flex-shrink-0 items-center justify-center rounded transition-colors ${
|
||||
className={`flex h-6 w-6 shrink-0 items-center justify-center rounded transition-colors ${
|
||||
isRendering
|
||||
? "text-neutral-600 cursor-not-allowed"
|
||||
: "text-neutral-600 hover:text-studio-accent hover:bg-neutral-800"
|
||||
|
||||
@@ -80,7 +80,7 @@ export function GlobalAssetsView({ searchQuery }: { searchQuery: string }) {
|
||||
className="px-4 py-1.5 flex items-center gap-2.5 border-l-2 border-transparent hover:bg-neutral-800/50"
|
||||
title={`${row.id} · ${row.type}`}
|
||||
>
|
||||
<span className="text-[9px] font-medium text-neutral-600 uppercase w-10 flex-shrink-0">
|
||||
<span className="text-[9px] font-medium text-neutral-600 uppercase w-10 shrink-0">
|
||||
{row.type}
|
||||
</span>
|
||||
<span className="text-xs text-panel-text-1 truncate">{row.label}</span>
|
||||
|
||||
@@ -165,7 +165,7 @@ export const LeftSidebar = memo(
|
||||
) : (
|
||||
<>
|
||||
{/* Tabs — Code first */}
|
||||
<div className="border-b border-neutral-800/50 px-3 py-3 flex-shrink-0">
|
||||
<div className="border-b border-neutral-800/50 px-3 py-3 shrink-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<div
|
||||
ref={tablistRef}
|
||||
@@ -200,7 +200,7 @@ export const LeftSidebar = memo(
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggleCollapse}
|
||||
className="flex h-8 w-8 flex-shrink-0 items-center justify-center rounded-md border border-transparent text-neutral-500 transition-colors hover:border-neutral-800 hover:bg-neutral-900 hover:text-neutral-300"
|
||||
className="flex h-8 w-8 shrink-0 items-center justify-center rounded-md border border-transparent text-neutral-500 transition-colors hover:border-neutral-800 hover:bg-neutral-900 hover:text-neutral-300"
|
||||
title="Hide sidebar"
|
||||
aria-label="Hide sidebar"
|
||||
>
|
||||
@@ -262,7 +262,7 @@ export const LeftSidebar = memo(
|
||||
{tab === "code" && (
|
||||
<div id="sidebar-panel-code" role="tabpanel" className="flex flex-1 min-h-0">
|
||||
{(fileProp?.length ?? 0) > 0 && (
|
||||
<div className="w-[160px] flex-shrink-0 border-r border-neutral-800 overflow-y-auto">
|
||||
<div className="w-[160px] shrink-0 border-r border-neutral-800 overflow-y-auto">
|
||||
<FileTree
|
||||
files={fileProp ?? []}
|
||||
activeFile={editingFile?.path ?? null}
|
||||
@@ -300,7 +300,7 @@ export const LeftSidebar = memo(
|
||||
|
||||
{/* Lint button pinned at the bottom */}
|
||||
{onLint && (
|
||||
<div className="border-t border-neutral-800 p-2 flex-shrink-0">
|
||||
<div className="border-t border-neutral-800 p-2 shrink-0">
|
||||
<button
|
||||
onClick={onLint}
|
||||
disabled={linting}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user