diff --git a/packages/studio/package-subpaths.json b/packages/studio/package-subpaths.json index ac6ec74faf..06ad51e6ed 100644 --- a/packages/studio/package-subpaths.json +++ b/packages/studio/package-subpaths.json @@ -19,6 +19,12 @@ "types": null, "environments": ["browser"] }, + "./components.css": { + "source": "./src/styles/components.css", + "runtime": "./src/styles/components.css", + "types": null, + "environments": ["browser"] + }, "./styles.css": { "source": "./src/styles/studio.css", "runtime": "./dist/styles.css", diff --git a/packages/studio/package.json b/packages/studio/package.json index 4931203bca..4646431b3b 100644 --- a/packages/studio/package.json +++ b/packages/studio/package.json @@ -27,6 +27,7 @@ "types": "./src/styles/tailwind-preset.ts" }, "./theme.css": "./src/styles/theme.css", + "./components.css": "./src/styles/components.css", "./styles.css": "./src/styles/studio.css", "./package.json": "./package.json" }, @@ -42,6 +43,7 @@ "types": "./dist/styles/tailwind-preset.d.ts" }, "./theme.css": "./src/styles/theme.css", + "./components.css": "./src/styles/components.css", "./styles.css": "./dist/styles.css", "./package.json": "./package.json" }, diff --git a/packages/studio/src/player/components/VolumeControl.styles.test.ts b/packages/studio/src/player/components/VolumeControl.styles.test.ts index d6f72db27d..1b28177119 100644 --- a/packages/studio/src/player/components/VolumeControl.styles.test.ts +++ b/packages/studio/src/player/components/VolumeControl.styles.test.ts @@ -1,7 +1,7 @@ import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; -const studioCss = readFileSync(new URL("../../styles/studio.css", import.meta.url), "utf8"); +const studioCss = readFileSync(new URL("../../styles/components.css", import.meta.url), "utf8"); describe("preview volume range styles", () => { it("uses the same compact thumb in Chromium and Firefox", () => { diff --git a/packages/studio/src/player/components/timelineMotionStyles.test.ts b/packages/studio/src/player/components/timelineMotionStyles.test.ts index 78d52d56c0..07a3aaaead 100644 --- a/packages/studio/src/player/components/timelineMotionStyles.test.ts +++ b/packages/studio/src/player/components/timelineMotionStyles.test.ts @@ -1,7 +1,7 @@ import { readFileSync } from "node:fs"; import { describe, expect, it } from "vitest"; -const studioCss = readFileSync(new URL("../../styles/studio.css", import.meta.url), "utf8"); +const studioCss = readFileSync(new URL("../../styles/components.css", import.meta.url), "utf8"); const themeCss = readFileSync(new URL("../../styles/theme.css", import.meta.url), "utf8"); const timelineOverlaySources = [ "TimelineShortcutHint.tsx", diff --git a/packages/studio/src/styles/componentStyles.test.ts b/packages/studio/src/styles/componentStyles.test.ts new file mode 100644 index 0000000000..1dde2ab593 --- /dev/null +++ b/packages/studio/src/styles/componentStyles.test.ts @@ -0,0 +1,35 @@ +/** Guards the split between Studio's app sheet and the component rules an embedder imports. */ + +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { compile } from "tailwindcss"; +import { describe, expect, it } from "vitest"; +import { loadStylesheet, STYLES_DIR } from "./styleSources"; + +/** Class names in a stylesheet's selectors, at any nesting depth (inside @layer or @media too). */ +function selectorClasses(css: string): string[] { + const source = css.replace(/\/\*[\s\S]*?\*\//g, "").replace(/@(?:import|source)[^;]*;/g, ""); + return [...source.matchAll(/([^{};]+)\{/g)] + .map((match) => match[1].trim()) + .filter((prelude) => !prelude.startsWith("@")) + .flatMap((selector) => [...selector.matchAll(/\.(-?[_a-zA-Z][\w-]*)/g)].map((m) => m[1])); +} + +describe("studio component styles", () => { + it("keeps class rules out of the app sheet, where an embedder never sees them", () => { + const appSheet = readFileSync(path.join(STYLES_DIR, "studio.css"), "utf8"); + + expect(selectorClasses(appSheet), "move these rules to components.css").toEqual([]); + expect( + selectorClasses(readFileSync(path.join(STYLES_DIR, "components.css"), "utf8")), + ).toContain("timeline-clip__label"); + }); + + it("gives an embedder the timeline rules from theme.css and components.css alone", async () => { + const entry = '@import "tailwindcss";\n@import "./theme.css";\n@import "./components.css";'; + const css = (await compile(entry, { base: STYLES_DIR, loadStylesheet })).build([]); + + expect(css).toMatch(/^\s*\.timeline-clip__label \{/m); + expect(css).not.toContain("#root"); + }); +}); diff --git a/packages/studio/src/styles/components.css b/packages/studio/src/styles/components.css new file mode 100644 index 0000000000..d99bfec6ac --- /dev/null +++ b/packages/studio/src/styles/components.css @@ -0,0 +1,843 @@ +/* Rules for Studio's components, for any page that renders them. Reads the tokens in + * theme.css. The app shell (body, #root, preflight) stays in studio.css. */ + +.hf-color-grading-number::-webkit-outer-spin-button, +.hf-color-grading-number::-webkit-inner-spin-button { + margin: 0; + appearance: none; +} + +.hf-color-grading-number { + -moz-appearance: textfield; + appearance: textfield; +} + +.hf-color-grading-range { + height: 1.5rem; + cursor: default; + -webkit-appearance: none; + appearance: none; + background: transparent; +} + +.hf-color-grading-range:disabled { + cursor: not-allowed; + opacity: 0.5; +} + +.hf-color-grading-range::-webkit-slider-runnable-track { + height: 1.25rem; + border: 0; + background: transparent; +} + +.hf-color-grading-range::-webkit-slider-thumb { + width: 0.625rem; + height: 1rem; + margin-top: 0.125rem; + border: 0; + border-radius: 999px; + -webkit-appearance: none; + appearance: none; + background: #ffffff; + box-shadow: + 0 0 0 2px #0c0c0e, + 0 1px 4px rgba(0, 0, 0, 0.55); + cursor: default; +} + +.hf-color-grading-range::-moz-range-track { + height: 1.25rem; + border: 0; + background: transparent; +} + +.hf-color-grading-range::-moz-range-thumb { + width: 0.625rem; + height: 1rem; + border: 0; + border-radius: 999px; + background: #ffffff; + box-shadow: + 0 0 0 2px #0c0c0e, + 0 1px 4px rgba(0, 0, 0, 0.55); + cursor: default; +} + +.hf-color-grading-range:focus-visible { + outline: none; +} + +.hf-color-grading-range:focus-visible::-webkit-slider-thumb { + box-shadow: + 0 0 0 2px #0c0c0e, + 0 0 0 4px rgba(60, 230, 172, 0.22), + 0 1px 4px rgba(0, 0, 0, 0.55); +} + +.hf-color-grading-range:focus-visible::-moz-range-thumb { + box-shadow: + 0 0 0 2px #0c0c0e, + 0 0 0 4px rgba(60, 230, 172, 0.22), + 0 1px 4px rgba(0, 0, 0, 0.55); +} + +.hf-preview-volume-range { + height: 1.5rem; + cursor: pointer; + appearance: none; + background: transparent; +} + +.hf-preview-volume-range:disabled { + opacity: 0.3; +} + +.hf-preview-volume-range::-webkit-slider-runnable-track { + height: 2px; + background: transparent; +} + +.hf-preview-volume-range::-webkit-slider-thumb { + width: 0.5rem; + height: 0.5rem; + margin-top: -3px; + border: 0; + border-radius: 999px; + appearance: none; + background: #ffffff; + box-shadow: + 0 0 0 2px #0a0a0a, + 0 1px 3px rgba(0, 0, 0, 0.5); + cursor: grab; +} + +.hf-preview-volume-range::-webkit-slider-thumb:active { + cursor: grabbing; +} + +.hf-preview-volume-range::-moz-range-track, +.hf-preview-volume-range::-moz-range-progress { + height: 2px; + border: 0; + background: transparent; +} + +.hf-preview-volume-range::-moz-range-thumb { + width: 0.5rem; + height: 0.5rem; + border: 0; + border-radius: 999px; + background: #ffffff; + box-shadow: + 0 0 0 2px #0a0a0a, + 0 1px 3px rgba(0, 0, 0, 0.5); + cursor: grab; +} + +.hf-preview-volume-range::-moz-range-thumb:active { + cursor: grabbing; +} + +.hf-preview-volume-range:focus-visible { + outline: none; +} + +.hf-preview-volume-range:focus-visible::-webkit-slider-thumb, +.hf-preview-volume-range:focus-visible::-moz-range-thumb { + box-shadow: + 0 0 0 2px #0a0a0a, + 0 0 0 4px rgba(60, 230, 172, 0.4), + 0 1px 3px rgba(0, 0, 0, 0.5); +} + +.timeline-clip { + background-color: var(--clip-bg); + border: 1px solid var(--clip-border); + border-radius: var(--timeline-clip-radius); + box-shadow: + inset 0 1px 0 var(--timeline-clip-highlight), + var(--timeline-clip-shadow); +} + +.timeline-transition { + position: absolute; + width: 0; + height: 0; + pointer-events: none; + z-index: 30; +} + +.timeline-transition__zone { + position: absolute; + left: calc(var(--timeline-transition-zone-width) / -2); + top: -21px; + width: var(--timeline-transition-zone-width); + height: 42px; + border-radius: 8px; + overflow: hidden; + background: var(--timeline-transition-zone); + filter: blur(2.5px); + opacity: 0.95; +} + +.timeline-transition__image { + position: absolute; + inset: -4px; + background-position: center; + background-size: cover; +} + +.timeline-transition__image--out { + mask-image: linear-gradient(90deg, black 0%, transparent 100%); +} + +.timeline-transition__image--in { + mask-image: linear-gradient(90deg, transparent 0%, black 100%); +} + +.timeline-transition__badge { + position: absolute; + left: -14px; + top: -14px; + display: grid; + width: 28px; + height: 28px; + place-items: center; + border: 1px solid color-mix(in srgb, var(--timeline-transition-badge-ink) 22%, transparent); + border-radius: 999px; + background: var(--timeline-transition-badge); + box-shadow: 0 2px 5px var(--timeline-playhead-shadow); +} + +.timeline-transition__badge svg { + width: 17px; + height: 17px; +} +.timeline-transition__frame, +.timeline-transition__frame--out, +.timeline-transition__frame--in { + fill: none; + stroke: var(--timeline-transition-badge-ink); + stroke-width: 1.5; +} +.timeline-transition__intersection { + fill: var(--timeline-transition-badge-ink); + opacity: 0.7; +} + +.timeline-clip::before { + content: ""; + position: absolute; + inset: 0; + border-radius: inherit; + background: var(--clip-bg-active); + opacity: 0; + pointer-events: none; +} + +.timeline-clip[data-active] { + border-color: var(--clip-border-active); +} + +.timeline-clip[data-active]::before { + opacity: 1; +} + +/* Audio clips read as a distinct kind — a persistent violet tint vs the teal- + accented visual clips — so the audio zone at the bottom is visually separate + at a glance (the timeline's kinds: visual on top, audio below). */ +.timeline-clip.is-audio { + background-color: var(--timeline-clip-audio-bg); + border-color: var(--timeline-clip-audio-border); + border-radius: var(--timeline-clip-audio-radius); +} + +.timeline-clip.is-audio.is-hovered { + background-color: var(--timeline-clip-audio-bg-hover); +} + +.timeline-clip.is-audio.is-dragging { + box-shadow: + inset 0 0 0 999px var(--timeline-clip-audio-bg-dragging), + 0 8px 24px var(--timeline-clip-shadow-dragging); +} + +/* Mute greys the pill in place. Hot path: no transition. */ +.timeline-clip.is-audio[data-clip-hidden="true"], +.timeline-clip.is-audio[data-audio-muted="true"] { + background-color: var(--timeline-clip-audio-muted-bg); + transition: none; +} + +.timeline-audio-link { + position: absolute; + z-index: 4; + top: -9px; + left: 50%; + width: 1px; + height: 10px; + background: var(--timeline-audio-link); + pointer-events: none; +} + +.timeline-clip.is-hovered { + background-color: var(--clip-bg-hover); +} + +.timeline-clip[data-active].is-hovered { + border-color: var(--clip-border-hover); +} + +.timeline-clip.is-selected { + outline: 1.5px solid var(--timeline-clip-selection); + outline-offset: -1.5px; +} + +.timeline-clip.is-selected::after { + content: ""; + position: absolute; + inset: 0; + z-index: 4; + border-radius: inherit; + box-shadow: inset 0 0 0 1px var(--timeline-clip-selection-highlight); + pointer-events: none; +} + +.timeline-clip[data-active].is-selected { + outline: 1.5px solid var(--timeline-clip-selection); + outline-offset: -1.5px; +} + +.timeline-clip.is-dragging { + /* Veil is a shadow, not background-color, so drop does not fade the hover fill. */ + box-shadow: + inset 0 0 0 999px var(--clip-bg-dragging), + 0 8px 24px var(--timeline-clip-shadow-dragging); +} + +.timeline-clip__label { + position: absolute; + top: 5px; + left: 6px; + z-index: 6; + width: fit-content; + max-width: calc(100% - 22px); + overflow: hidden; + padding: 1px 5px; + border-radius: 4px; + background: var(--timeline-clip-chip-bg); + color: var(--timeline-clip-chip-text); + font-size: 10px; + font-weight: 500; + line-height: 16px; + pointer-events: none; + text-overflow: ellipsis; + white-space: nowrap; +} + +.timeline-clip.is-audio .timeline-clip__label { + top: 11px; + left: 9px; + background: var(--timeline-clip-chip-audio-bg); +} + +.timeline-clip__timecode { + position: absolute; + bottom: 6px; + left: 12px; + overflow: hidden; + color: var(--timeline-tick-text); + font-family: var(--font-mono); + font-size: 9px; + font-variant-numeric: tabular-nums; + line-height: 1; + text-overflow: ellipsis; + white-space: nowrap; +} + +@media (prefers-reduced-motion: no-preference) { + .timeline-clip { + transition: background-color 120ms ease-out; + } + + .timeline-clip__handle-bar { + transition: opacity 120ms ease-out; + } +} + +/* CodeMirror overrides */ +.cm-editor { + height: 100%; + font-size: 13px; +} + +.cm-editor .cm-scroller { + font-family: var(--font-mono); +} + +.cm-editor.cm-focused { + outline: none; +} + +/* + * HyperFrames brand loader. Shared by preview overlays that need a calm, + * branded loading state instead of a generic spinner. + */ +.hf-loader { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.75rem; + width: min(34rem, 100%); + padding: 1.5rem; + box-sizing: border-box; + text-align: center; + cursor: default; + user-select: none; + -webkit-user-select: none; + -webkit-user-drag: none; +} + +.hf-frame { + display: grid; + place-items: center; + width: 100%; + height: 100%; + min-height: 12rem; + border: 1px solid rgba(255, 255, 255, 0.08); + background: rgba(0, 0, 0, 0.52); +} + +.hf-loader-mark-frame { + display: grid; + place-items: center; + overflow: visible; + transform-origin: 50% 50%; + user-select: none; + -webkit-user-select: none; + -webkit-user-drag: none; +} + +.hf-loader-mark { + display: block; + overflow: visible; + filter: drop-shadow(0 0 7px rgba(79, 219, 94, 0.2)); + user-select: none; + -webkit-user-select: none; + -webkit-user-drag: none; +} + +.hf-loader-title { + font-family: var(--font-sans); + font-size: 1rem; + font-weight: 600; + letter-spacing: 0; + color: var(--hf-heading, #f4f4f5); + max-width: 100%; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.hf-loader-detail { + max-width: 32rem; + min-height: 2.5rem; + overflow: hidden; + color: var(--hf-text-secondary, rgba(244, 244, 245, 0.68)); + font-family: var(--font-sans); + font-size: 0.82rem; + line-height: 1.6; +} + +.hf-loader-mono { + width: min(36rem, 100%); + min-height: 1.5rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--hf-text-tertiary, rgba(244, 244, 245, 0.46)); + font-family: var(--font-mono); + font-size: 0.75rem; + letter-spacing: 0; + font-variant-numeric: tabular-nums; +} + +.hf-loader-progress { + width: min(18rem, 72vw); + height: 0.375rem; + overflow: hidden; + border-radius: 999px; + background: rgba(255, 255, 255, 0.1); +} + +@keyframes hf-thumb-fade { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.hf-loader-progress__fill { + width: 100%; + height: 100%; + transform: scaleX(0); + transform-origin: left center; + border-radius: inherit; + background: linear-gradient(90deg, #06e3fa, #4fdb5e); + transition: transform 160ms ease; +} + +/* Toast enter/exit (StudioToast) */ +@keyframes hf-toast-in { + from { + opacity: 0; + transform: translateY(8px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes hf-toast-out { + from { + opacity: 1; + transform: translateY(0); + } + to { + opacity: 0; + transform: translateY(8px); + } +} + +.hf-toast-enter { + animation: hf-toast-in 160ms ease-out; +} + +.hf-toast-exit { + animation: hf-toast-out 160ms ease-in forwards; +} + +@media (prefers-reduced-motion: reduce) { + .hf-toast-enter, + .hf-toast-exit { + animation: none; + } + .hf-toast-exit { + opacity: 0; + } +} + +/* Overlay/backdrop entrance — the heaviest visual changes shouldn't pop in */ +@keyframes hf-backdrop-in { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +.hf-backdrop-in { + animation: hf-backdrop-in 150ms ease-out; +} + +@media (prefers-reduced-motion: reduce) { + .hf-backdrop-in { + animation: none; + } +} + +/* Flat inspector group header/body entrance (FlatGroupHeader) */ +@keyframes hf-flat-group-in { + from { + opacity: 0; + transform: translateY(-4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +.hf-flat-group-enter { + animation: hf-flat-group-in 120ms ease-out; +} + +@media (prefers-reduced-motion: reduce) { + .hf-flat-group-enter { + animation: none; + } +} + +/* Preset shelf: the hovered row, and the wave that says it is being heard. + * + * Hovering a preset auditions it on the running audio, which is invisible — the + * panel looks identical whether the audition is playing or the pointer just + * happens to be resting there. The bars are that feedback: they run only while a + * preset is actually sounding, so they are a readout of the audio rather than a + * hover decoration. */ +.hf-fx-preset-item { + position: relative; +} + +@media (prefers-reduced-motion: no-preference) { + .hf-fx-preset-item { + transition: + background-color 120ms ease, + color 120ms ease; + } +} + +.hf-fx-preset-item:hover, +.hf-fx-preset-item:focus-visible { + /* The accent at low alpha rather than a grey: the row is lit by the thing that + is playing, and it should read as the same event as the bars. */ + background-color: rgba(60, 230, 172, 0.12); +} + +.hf-fx-preset-wave { + position: absolute; + top: 50%; + right: 0.5rem; + display: flex; + align-items: center; + gap: 2px; + height: 0.75rem; + transform: translateY(-50%); + opacity: 0; + transition: opacity 120ms ease; + pointer-events: none; +} + +.hf-fx-preset-item:hover .hf-fx-preset-wave, +.hf-fx-preset-item:focus-visible .hf-fx-preset-wave { + opacity: 1; +} + +.hf-fx-preset-wave span { + width: 2px; + height: 100%; + border-radius: 1px; + background: #3ce6ac; + transform: scaleY(0.25); + transform-origin: center; +} + +@media (prefers-reduced-motion: no-preference) { + .hf-fx-preset-wave span { + animation: hf-fx-preset-wave 900ms ease-in-out infinite; + } + /* Staggered so it reads as a waveform travelling rather than four bars + pumping in unison, which reads as a loading spinner. */ + .hf-fx-preset-wave span:nth-child(2) { + animation-delay: 150ms; + } + .hf-fx-preset-wave span:nth-child(3) { + animation-delay: 300ms; + } + .hf-fx-preset-wave span:nth-child(4) { + animation-delay: 450ms; + } +} + +/* Still visible without motion — it is telling the author something is + playing, and that fact does not go away because the bars hold still. */ +@media (prefers-reduced-motion: reduce) { + .hf-fx-preset-wave span { + transform: scaleY(0.7); + } +} + +/* Receipt-driven WebMCP Topology Lens. Every animation is one-shot Studio + chrome; none of these nodes enter the composition iframe or rendered output. */ +.hf-topology-contour { + border: 1px solid rgba(60, 230, 172, 0.62); + box-shadow: + 0 0 0 1px rgba(6, 227, 250, 0.08), + inset 0 0 18px rgba(60, 230, 172, 0.035); +} + +.hf-topology-field::before, +.hf-topology-contour::before, +.hf-topology-target::before { + content: attr(data-topology-node); + position: absolute; + top: 2px; + left: 3px; + z-index: 2; + padding: 1px 3px; + border-radius: 2px; + background: rgba(5, 15, 12, 0.86); + color: rgb(90, 247, 193); + font: + 600 7px/1.3 ui-monospace, + SFMono-Regular, + Menlo, + Monaco, + Consolas, + monospace; + letter-spacing: 0.04em; + text-transform: lowercase; +} + +.hf-topology-field { + z-index: 0; + border: 1px solid rgba(60, 230, 172, 0.28); + background: linear-gradient( + 90deg, + rgba(60, 230, 172, 0.025), + rgba(60, 230, 172, 0.1) 68%, + rgba(6, 227, 250, 0.06) + ); + box-shadow: inset 0 0 28px rgba(60, 230, 172, 0.05); +} + +.hf-topology-target { + border: 1px solid rgba(60, 230, 172, 0.82); + box-shadow: + 0 0 0 1px rgba(60, 230, 172, 0.14), + 0 0 18px rgba(6, 227, 250, 0.09); +} + +.hf-topology-scan { + background: linear-gradient( + 90deg, + transparent, + rgba(60, 230, 172, 0.16) 55%, + rgba(230, 255, 247, 0.9) + ); + clip-path: polygon(0 0, 100% 0, 72% 100%, 0 100%); +} + +.hf-topology-seal { + filter: drop-shadow(0 0 6px rgba(60, 230, 172, 0.36)); +} + +@media (prefers-reduced-motion: no-preference) { + .hf-topology-contour { + animation: hf-topology-reveal 176ms cubic-bezier(0.77, 0, 0.175, 1) both; + } + + .hf-topology-field[data-topology-phase="acquiring"] { + animation: hf-topology-reveal 240ms cubic-bezier(0.77, 0, 0.175, 1) both; + } + + .hf-topology-scan { + animation: hf-topology-scan 224ms cubic-bezier(0.77, 0, 0.175, 1) both; + } + + .hf-topology-target[data-topology-phase="acquiring"] { + animation: hf-topology-localize 224ms cubic-bezier(0.23, 1, 0.32, 1) 72ms both; + } + + .hf-topology-target[data-topology-phase="localizing"] { + animation: hf-topology-localize 176ms cubic-bezier(0.23, 1, 0.32, 1) both; + } + + .hf-topology-target[data-topology-phase="sealing"] { + animation: hf-topology-localize 144ms cubic-bezier(0.23, 1, 0.32, 1) both; + } + + .hf-topology-seal { + animation: hf-topology-seal 224ms cubic-bezier(0.23, 1, 0.32, 1) both; + } + + .hf-topology-seal path:first-of-type { + animation: hf-topology-seal-left 224ms cubic-bezier(0.23, 1, 0.32, 1) both; + } + + .hf-topology-seal path:nth-of-type(2) { + animation: hf-topology-seal-right 224ms cubic-bezier(0.23, 1, 0.32, 1) both; + } +} + +@keyframes hf-topology-reveal { + from { + opacity: 0; + clip-path: inset(0 100% 0 0); + } + to { + opacity: 1; + clip-path: inset(0); + } +} + +@keyframes hf-topology-scan { + from { + opacity: 0; + transform: translate3d(-100%, 0, 0); + } + 16% { + opacity: 1; + } + to { + opacity: 0; + transform: translate3d(400%, 0, 0); + } +} + +@keyframes hf-topology-localize { + from { + opacity: 0; + transform: scaleX(0.76); + } + to { + opacity: 1; + transform: scaleX(1); + } +} + +@keyframes hf-topology-seal { + from { + opacity: 0; + transform: scale(0.88); + } + to { + opacity: 1; + transform: scale(1); + } +} + +@keyframes hf-topology-seal-left { + from { + transform: translate3d(-9px, 0, 0); + } + to { + transform: translate3d(0, 0, 0); + } +} + +@keyframes hf-topology-seal-right { + from { + transform: translate3d(9px, 0, 0); + } + to { + transform: translate3d(0, 0, 0); + } +} + +@media (prefers-reduced-motion: reduce) { + .hf-topology-contour, + .hf-topology-field, + .hf-topology-target, + .hf-topology-seal, + .hf-topology-seal path { + animation: none; + transform: none; + } +} + +@keyframes hf-fx-preset-wave { + 0%, + 100% { + transform: scaleY(0.25); + } + 50% { + transform: scaleY(1); + } +} diff --git a/packages/studio/src/styles/hex-baseline.json b/packages/studio/src/styles/hex-baseline.json index 8b62977fa6..a15bc029ec 100644 --- a/packages/studio/src/styles/hex-baseline.json +++ b/packages/studio/src/styles/hex-baseline.json @@ -57,8 +57,8 @@ "packages/studio/src/player/components/ShortcutsPanel.tsx": 7, "packages/studio/src/player/components/SpeedMenu.tsx": 2, "packages/studio/src/player/components/trackHeaderLabelRows.tsx": 6, + "packages/studio/src/styles/components.css": 48, "packages/studio/src/telemetry/client.ts": 1, - "packages/studio/src/styles/studio.css": 48, "packages/studio/src/webmcp/webmcpTestUtils.ts": 1 } } diff --git a/packages/studio/src/styles/studio.css b/packages/studio/src/styles/studio.css index 09fbb29ed7..26e899e0c4 100644 --- a/packages/studio/src/styles/studio.css +++ b/packages/studio/src/styles/studio.css @@ -1,6 +1,7 @@ @import "tailwindcss" source(none); @import "./palette-reset.css"; @import "./theme.css"; +@import "./components.css"; /* Explicit sources: Studio's own TypeScript and its shell HTML. Automatic * detection would walk up to the repo root and scan `data/projects`, gitignored user content. */ @@ -33,156 +34,6 @@ body { overflow: hidden; } -.hf-color-grading-number::-webkit-outer-spin-button, -.hf-color-grading-number::-webkit-inner-spin-button { - margin: 0; - appearance: none; -} - -.hf-color-grading-number { - -moz-appearance: textfield; - appearance: textfield; -} - -.hf-color-grading-range { - height: 1.5rem; - cursor: default; - -webkit-appearance: none; - appearance: none; - background: transparent; -} - -.hf-color-grading-range:disabled { - cursor: not-allowed; - opacity: 0.5; -} - -.hf-color-grading-range::-webkit-slider-runnable-track { - height: 1.25rem; - border: 0; - background: transparent; -} - -.hf-color-grading-range::-webkit-slider-thumb { - width: 0.625rem; - height: 1rem; - margin-top: 0.125rem; - border: 0; - border-radius: 999px; - -webkit-appearance: none; - appearance: none; - background: #ffffff; - box-shadow: - 0 0 0 2px #0c0c0e, - 0 1px 4px rgba(0, 0, 0, 0.55); - cursor: default; -} - -.hf-color-grading-range::-moz-range-track { - height: 1.25rem; - border: 0; - background: transparent; -} - -.hf-color-grading-range::-moz-range-thumb { - width: 0.625rem; - height: 1rem; - border: 0; - border-radius: 999px; - background: #ffffff; - box-shadow: - 0 0 0 2px #0c0c0e, - 0 1px 4px rgba(0, 0, 0, 0.55); - cursor: default; -} - -.hf-color-grading-range:focus-visible { - outline: none; -} - -.hf-color-grading-range:focus-visible::-webkit-slider-thumb { - box-shadow: - 0 0 0 2px #0c0c0e, - 0 0 0 4px rgba(60, 230, 172, 0.22), - 0 1px 4px rgba(0, 0, 0, 0.55); -} - -.hf-color-grading-range:focus-visible::-moz-range-thumb { - box-shadow: - 0 0 0 2px #0c0c0e, - 0 0 0 4px rgba(60, 230, 172, 0.22), - 0 1px 4px rgba(0, 0, 0, 0.55); -} - -.hf-preview-volume-range { - height: 1.5rem; - cursor: pointer; - appearance: none; - background: transparent; -} - -.hf-preview-volume-range:disabled { - opacity: 0.3; -} - -.hf-preview-volume-range::-webkit-slider-runnable-track { - height: 2px; - background: transparent; -} - -.hf-preview-volume-range::-webkit-slider-thumb { - width: 0.5rem; - height: 0.5rem; - margin-top: -3px; - border: 0; - border-radius: 999px; - appearance: none; - background: #ffffff; - box-shadow: - 0 0 0 2px #0a0a0a, - 0 1px 3px rgba(0, 0, 0, 0.5); - cursor: grab; -} - -.hf-preview-volume-range::-webkit-slider-thumb:active { - cursor: grabbing; -} - -.hf-preview-volume-range::-moz-range-track, -.hf-preview-volume-range::-moz-range-progress { - height: 2px; - border: 0; - background: transparent; -} - -.hf-preview-volume-range::-moz-range-thumb { - width: 0.5rem; - height: 0.5rem; - border: 0; - border-radius: 999px; - background: #ffffff; - box-shadow: - 0 0 0 2px #0a0a0a, - 0 1px 3px rgba(0, 0, 0, 0.5); - cursor: grab; -} - -.hf-preview-volume-range::-moz-range-thumb:active { - cursor: grabbing; -} - -.hf-preview-volume-range:focus-visible { - outline: none; -} - -.hf-preview-volume-range:focus-visible::-webkit-slider-thumb, -.hf-preview-volume-range:focus-visible::-moz-range-thumb { - box-shadow: - 0 0 0 2px #0a0a0a, - 0 0 0 4px rgba(60, 230, 172, 0.4), - 0 1px 3px rgba(0, 0, 0, 0.5); -} - #root { width: 100vw; /* @@ -198,694 +49,3 @@ body { height: 100vh; height: 100dvh; } - -.timeline-clip { - background-color: var(--clip-bg); - border: 1px solid var(--clip-border); - border-radius: var(--timeline-clip-radius); - box-shadow: - inset 0 1px 0 var(--timeline-clip-highlight), - var(--timeline-clip-shadow); -} - -.timeline-transition { - position: absolute; - width: 0; - height: 0; - pointer-events: none; - z-index: 30; -} - -.timeline-transition__zone { - position: absolute; - left: calc(var(--timeline-transition-zone-width) / -2); - top: -21px; - width: var(--timeline-transition-zone-width); - height: 42px; - border-radius: 8px; - overflow: hidden; - background: var(--timeline-transition-zone); - filter: blur(2.5px); - opacity: 0.95; -} - -.timeline-transition__image { - position: absolute; - inset: -4px; - background-position: center; - background-size: cover; -} - -.timeline-transition__image--out { - mask-image: linear-gradient(90deg, black 0%, transparent 100%); -} - -.timeline-transition__image--in { - mask-image: linear-gradient(90deg, transparent 0%, black 100%); -} - -.timeline-transition__badge { - position: absolute; - left: -14px; - top: -14px; - display: grid; - width: 28px; - height: 28px; - place-items: center; - border: 1px solid color-mix(in srgb, var(--timeline-transition-badge-ink) 22%, transparent); - border-radius: 999px; - background: var(--timeline-transition-badge); - box-shadow: 0 2px 5px var(--timeline-playhead-shadow); -} - -.timeline-transition__badge svg { - width: 17px; - height: 17px; -} -.timeline-transition__frame, -.timeline-transition__frame--out, -.timeline-transition__frame--in { - fill: none; - stroke: var(--timeline-transition-badge-ink); - stroke-width: 1.5; -} -.timeline-transition__intersection { - fill: var(--timeline-transition-badge-ink); - opacity: 0.7; -} - -.timeline-clip::before { - content: ""; - position: absolute; - inset: 0; - border-radius: inherit; - background: var(--clip-bg-active); - opacity: 0; - pointer-events: none; -} - -.timeline-clip[data-active] { - border-color: var(--clip-border-active); -} - -.timeline-clip[data-active]::before { - opacity: 1; -} - -/* Audio clips read as a distinct kind — a persistent violet tint vs the teal- - accented visual clips — so the audio zone at the bottom is visually separate - at a glance (the timeline's kinds: visual on top, audio below). */ -.timeline-clip.is-audio { - background-color: var(--timeline-clip-audio-bg); - border-color: var(--timeline-clip-audio-border); - border-radius: var(--timeline-clip-audio-radius); -} - -.timeline-clip.is-audio.is-hovered { - background-color: var(--timeline-clip-audio-bg-hover); -} - -.timeline-clip.is-audio.is-dragging { - box-shadow: - inset 0 0 0 999px var(--timeline-clip-audio-bg-dragging), - 0 8px 24px var(--timeline-clip-shadow-dragging); -} - -/* Mute greys the pill in place. Hot path: no transition. */ -.timeline-clip.is-audio[data-clip-hidden="true"], -.timeline-clip.is-audio[data-audio-muted="true"] { - background-color: var(--timeline-clip-audio-muted-bg); - transition: none; -} - -.timeline-audio-link { - position: absolute; - z-index: 4; - top: -9px; - left: 50%; - width: 1px; - height: 10px; - background: var(--timeline-audio-link); - pointer-events: none; -} - -.timeline-clip.is-hovered { - background-color: var(--clip-bg-hover); -} - -.timeline-clip[data-active].is-hovered { - border-color: var(--clip-border-hover); -} - -.timeline-clip.is-selected { - outline: 1.5px solid var(--timeline-clip-selection); - outline-offset: -1.5px; -} - -.timeline-clip.is-selected::after { - content: ""; - position: absolute; - inset: 0; - z-index: 4; - border-radius: inherit; - box-shadow: inset 0 0 0 1px var(--timeline-clip-selection-highlight); - pointer-events: none; -} - -.timeline-clip[data-active].is-selected { - outline: 1.5px solid var(--timeline-clip-selection); - outline-offset: -1.5px; -} - -.timeline-clip.is-dragging { - /* Veil is a shadow, not background-color, so drop does not fade the hover fill. */ - box-shadow: - inset 0 0 0 999px var(--clip-bg-dragging), - 0 8px 24px var(--timeline-clip-shadow-dragging); -} - -.timeline-clip__label { - position: absolute; - top: 5px; - left: 6px; - z-index: 6; - width: fit-content; - max-width: calc(100% - 22px); - overflow: hidden; - padding: 1px 5px; - border-radius: 4px; - background: var(--timeline-clip-chip-bg); - color: var(--timeline-clip-chip-text); - font-size: 10px; - font-weight: 500; - line-height: 16px; - pointer-events: none; - text-overflow: ellipsis; - white-space: nowrap; -} - -.timeline-clip.is-audio .timeline-clip__label { - top: 11px; - left: 9px; - background: var(--timeline-clip-chip-audio-bg); -} - -.timeline-clip__timecode { - position: absolute; - bottom: 6px; - left: 12px; - overflow: hidden; - color: var(--timeline-tick-text); - font-family: var(--font-mono); - font-size: 9px; - font-variant-numeric: tabular-nums; - line-height: 1; - text-overflow: ellipsis; - white-space: nowrap; -} - -@media (prefers-reduced-motion: no-preference) { - .timeline-clip { - transition: background-color 120ms ease-out; - } - - .timeline-clip__handle-bar { - transition: opacity 120ms ease-out; - } -} - -/* CodeMirror overrides */ -.cm-editor { - height: 100%; - font-size: 13px; -} - -.cm-editor .cm-scroller { - font-family: var(--font-mono); -} - -.cm-editor.cm-focused { - outline: none; -} - -/* - * HyperFrames brand loader. Shared by preview overlays that need a calm, - * branded loading state instead of a generic spinner. - */ -.hf-loader { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 0.75rem; - width: min(34rem, 100%); - padding: 1.5rem; - box-sizing: border-box; - text-align: center; - cursor: default; - user-select: none; - -webkit-user-select: none; - -webkit-user-drag: none; -} - -.hf-frame { - display: grid; - place-items: center; - width: 100%; - height: 100%; - min-height: 12rem; - border: 1px solid rgba(255, 255, 255, 0.08); - background: rgba(0, 0, 0, 0.52); -} - -.hf-loader-mark-frame { - display: grid; - place-items: center; - overflow: visible; - transform-origin: 50% 50%; - user-select: none; - -webkit-user-select: none; - -webkit-user-drag: none; -} - -.hf-loader-mark { - display: block; - overflow: visible; - filter: drop-shadow(0 0 7px rgba(79, 219, 94, 0.2)); - user-select: none; - -webkit-user-select: none; - -webkit-user-drag: none; -} - -.hf-loader-title { - font-family: var(--font-sans); - font-size: 1rem; - font-weight: 600; - letter-spacing: 0; - color: var(--hf-heading, #f4f4f5); - max-width: 100%; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.hf-loader-detail { - max-width: 32rem; - min-height: 2.5rem; - overflow: hidden; - color: var(--hf-text-secondary, rgba(244, 244, 245, 0.68)); - font-family: var(--font-sans); - font-size: 0.82rem; - line-height: 1.6; -} - -.hf-loader-mono { - width: min(36rem, 100%); - min-height: 1.5rem; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - color: var(--hf-text-tertiary, rgba(244, 244, 245, 0.46)); - font-family: var(--font-mono); - font-size: 0.75rem; - letter-spacing: 0; - font-variant-numeric: tabular-nums; -} - -.hf-loader-progress { - width: min(18rem, 72vw); - height: 0.375rem; - overflow: hidden; - border-radius: 999px; - background: rgba(255, 255, 255, 0.1); -} - -@keyframes hf-thumb-fade { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -.hf-loader-progress__fill { - width: 100%; - height: 100%; - transform: scaleX(0); - transform-origin: left center; - border-radius: inherit; - background: linear-gradient(90deg, #06e3fa, #4fdb5e); - transition: transform 160ms ease; -} - -/* Toast enter/exit (StudioToast) */ -@keyframes hf-toast-in { - from { - opacity: 0; - transform: translateY(8px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes hf-toast-out { - from { - opacity: 1; - transform: translateY(0); - } - to { - opacity: 0; - transform: translateY(8px); - } -} - -.hf-toast-enter { - animation: hf-toast-in 160ms ease-out; -} - -.hf-toast-exit { - animation: hf-toast-out 160ms ease-in forwards; -} - -@media (prefers-reduced-motion: reduce) { - .hf-toast-enter, - .hf-toast-exit { - animation: none; - } - .hf-toast-exit { - opacity: 0; - } -} - -/* Overlay/backdrop entrance — the heaviest visual changes shouldn't pop in */ -@keyframes hf-backdrop-in { - from { - opacity: 0; - } - to { - opacity: 1; - } -} - -.hf-backdrop-in { - animation: hf-backdrop-in 150ms ease-out; -} - -@media (prefers-reduced-motion: reduce) { - .hf-backdrop-in { - animation: none; - } -} - -/* Flat inspector group header/body entrance (FlatGroupHeader) */ -@keyframes hf-flat-group-in { - from { - opacity: 0; - transform: translateY(-4px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.hf-flat-group-enter { - animation: hf-flat-group-in 120ms ease-out; -} - -@media (prefers-reduced-motion: reduce) { - .hf-flat-group-enter { - animation: none; - } -} - -/* Preset shelf: the hovered row, and the wave that says it is being heard. - * - * Hovering a preset auditions it on the running audio, which is invisible — the - * panel looks identical whether the audition is playing or the pointer just - * happens to be resting there. The bars are that feedback: they run only while a - * preset is actually sounding, so they are a readout of the audio rather than a - * hover decoration. */ -.hf-fx-preset-item { - position: relative; -} - -@media (prefers-reduced-motion: no-preference) { - .hf-fx-preset-item { - transition: - background-color 120ms ease, - color 120ms ease; - } -} - -.hf-fx-preset-item:hover, -.hf-fx-preset-item:focus-visible { - /* The accent at low alpha rather than a grey: the row is lit by the thing that - is playing, and it should read as the same event as the bars. */ - background-color: rgba(60, 230, 172, 0.12); -} - -.hf-fx-preset-wave { - position: absolute; - top: 50%; - right: 0.5rem; - display: flex; - align-items: center; - gap: 2px; - height: 0.75rem; - transform: translateY(-50%); - opacity: 0; - transition: opacity 120ms ease; - pointer-events: none; -} - -.hf-fx-preset-item:hover .hf-fx-preset-wave, -.hf-fx-preset-item:focus-visible .hf-fx-preset-wave { - opacity: 1; -} - -.hf-fx-preset-wave span { - width: 2px; - height: 100%; - border-radius: 1px; - background: #3ce6ac; - transform: scaleY(0.25); - transform-origin: center; -} - -@media (prefers-reduced-motion: no-preference) { - .hf-fx-preset-wave span { - animation: hf-fx-preset-wave 900ms ease-in-out infinite; - } - /* Staggered so it reads as a waveform travelling rather than four bars - pumping in unison, which reads as a loading spinner. */ - .hf-fx-preset-wave span:nth-child(2) { - animation-delay: 150ms; - } - .hf-fx-preset-wave span:nth-child(3) { - animation-delay: 300ms; - } - .hf-fx-preset-wave span:nth-child(4) { - animation-delay: 450ms; - } -} - -/* Still visible without motion — it is telling the author something is - playing, and that fact does not go away because the bars hold still. */ -@media (prefers-reduced-motion: reduce) { - .hf-fx-preset-wave span { - transform: scaleY(0.7); - } -} - -/* Receipt-driven WebMCP Topology Lens. Every animation is one-shot Studio - chrome; none of these nodes enter the composition iframe or rendered output. */ -.hf-topology-contour { - border: 1px solid rgba(60, 230, 172, 0.62); - box-shadow: - 0 0 0 1px rgba(6, 227, 250, 0.08), - inset 0 0 18px rgba(60, 230, 172, 0.035); -} - -.hf-topology-field::before, -.hf-topology-contour::before, -.hf-topology-target::before { - content: attr(data-topology-node); - position: absolute; - top: 2px; - left: 3px; - z-index: 2; - padding: 1px 3px; - border-radius: 2px; - background: rgba(5, 15, 12, 0.86); - color: rgb(90, 247, 193); - font: - 600 7px/1.3 ui-monospace, - SFMono-Regular, - Menlo, - Monaco, - Consolas, - monospace; - letter-spacing: 0.04em; - text-transform: lowercase; -} - -.hf-topology-field { - z-index: 0; - border: 1px solid rgba(60, 230, 172, 0.28); - background: linear-gradient( - 90deg, - rgba(60, 230, 172, 0.025), - rgba(60, 230, 172, 0.1) 68%, - rgba(6, 227, 250, 0.06) - ); - box-shadow: inset 0 0 28px rgba(60, 230, 172, 0.05); -} - -.hf-topology-target { - border: 1px solid rgba(60, 230, 172, 0.82); - box-shadow: - 0 0 0 1px rgba(60, 230, 172, 0.14), - 0 0 18px rgba(6, 227, 250, 0.09); -} - -.hf-topology-scan { - background: linear-gradient( - 90deg, - transparent, - rgba(60, 230, 172, 0.16) 55%, - rgba(230, 255, 247, 0.9) - ); - clip-path: polygon(0 0, 100% 0, 72% 100%, 0 100%); -} - -.hf-topology-seal { - filter: drop-shadow(0 0 6px rgba(60, 230, 172, 0.36)); -} - -@media (prefers-reduced-motion: no-preference) { - .hf-topology-contour { - animation: hf-topology-reveal 176ms cubic-bezier(0.77, 0, 0.175, 1) both; - } - - .hf-topology-field[data-topology-phase="acquiring"] { - animation: hf-topology-reveal 240ms cubic-bezier(0.77, 0, 0.175, 1) both; - } - - .hf-topology-scan { - animation: hf-topology-scan 224ms cubic-bezier(0.77, 0, 0.175, 1) both; - } - - .hf-topology-target[data-topology-phase="acquiring"] { - animation: hf-topology-localize 224ms cubic-bezier(0.23, 1, 0.32, 1) 72ms both; - } - - .hf-topology-target[data-topology-phase="localizing"] { - animation: hf-topology-localize 176ms cubic-bezier(0.23, 1, 0.32, 1) both; - } - - .hf-topology-target[data-topology-phase="sealing"] { - animation: hf-topology-localize 144ms cubic-bezier(0.23, 1, 0.32, 1) both; - } - - .hf-topology-seal { - animation: hf-topology-seal 224ms cubic-bezier(0.23, 1, 0.32, 1) both; - } - - .hf-topology-seal path:first-of-type { - animation: hf-topology-seal-left 224ms cubic-bezier(0.23, 1, 0.32, 1) both; - } - - .hf-topology-seal path:nth-of-type(2) { - animation: hf-topology-seal-right 224ms cubic-bezier(0.23, 1, 0.32, 1) both; - } -} - -@keyframes hf-topology-reveal { - from { - opacity: 0; - clip-path: inset(0 100% 0 0); - } - to { - opacity: 1; - clip-path: inset(0); - } -} - -@keyframes hf-topology-scan { - from { - opacity: 0; - transform: translate3d(-100%, 0, 0); - } - 16% { - opacity: 1; - } - to { - opacity: 0; - transform: translate3d(400%, 0, 0); - } -} - -@keyframes hf-topology-localize { - from { - opacity: 0; - transform: scaleX(0.76); - } - to { - opacity: 1; - transform: scaleX(1); - } -} - -@keyframes hf-topology-seal { - from { - opacity: 0; - transform: scale(0.88); - } - to { - opacity: 1; - transform: scale(1); - } -} - -@keyframes hf-topology-seal-left { - from { - transform: translate3d(-9px, 0, 0); - } - to { - transform: translate3d(0, 0, 0); - } -} - -@keyframes hf-topology-seal-right { - from { - transform: translate3d(9px, 0, 0); - } - to { - transform: translate3d(0, 0, 0); - } -} - -@media (prefers-reduced-motion: reduce) { - .hf-topology-contour, - .hf-topology-field, - .hf-topology-target, - .hf-topology-seal, - .hf-topology-seal path { - animation: none; - transform: none; - } -} - -@keyframes hf-fx-preset-wave { - 0%, - 100% { - transform: scaleY(0.25); - } - 50% { - transform: scaleY(1); - } -} diff --git a/packages/studio/src/styles/tokenGate.test.ts b/packages/studio/src/styles/tokenGate.test.ts index 40dec4151b..589833b3d0 100644 --- a/packages/studio/src/styles/tokenGate.test.ts +++ b/packages/studio/src/styles/tokenGate.test.ts @@ -87,7 +87,7 @@ describe("token gate", () => { }); it("accepts variants, static utilities and Studio's own CSS classes", async () => { - // `timeline-clip` is a plain rule in `studio.css`, not a utility. + // `timeline-clip` is a plain rule in `components.css`, not a utility. const source = `
`; expect(await unresolved(new Map([["a.tsx", source]]))).toEqual([]); diff --git a/scripts/contrast-pairs.json b/scripts/contrast-pairs.json index 49d13dccc5..743b14ec3a 100644 --- a/scripts/contrast-pairs.json +++ b/scripts/contrast-pairs.json @@ -47,8 +47,8 @@ "role": "text", "minimum": 4.5, "sources": [ - "packages/studio/src/styles/studio.css:203", - "packages/studio/src/styles/studio.css:286" + "packages/studio/src/styles/components.css:155", + "packages/studio/src/styles/components.css:238" ] }, { @@ -62,8 +62,8 @@ "role": "text", "minimum": 4.5, "sources": [ - "packages/studio/src/styles/studio.css:230", - "packages/studio/src/styles/studio.css:300" + "packages/studio/src/styles/components.css:182", + "packages/studio/src/styles/components.css:252" ] }, { @@ -73,8 +73,8 @@ "role": "text", "minimum": 4.5, "sources": [ - "packages/studio/src/styles/studio.css:203", - "packages/studio/src/styles/studio.css:305" + "packages/studio/src/styles/components.css:155", + "packages/studio/src/styles/components.css:257" ] }, { @@ -84,9 +84,9 @@ "role": "text", "minimum": 4.5, "sources": [ - "packages/studio/src/styles/studio.css:203", - "packages/studio/src/styles/studio.css:213", - "packages/studio/src/styles/studio.css:305" + "packages/studio/src/styles/components.css:155", + "packages/studio/src/styles/components.css:165", + "packages/studio/src/styles/components.css:257" ] }, { @@ -96,8 +96,8 @@ "role": "graphic", "minimum": 3, "sources": [ - "packages/studio/src/styles/studio.css:203", - "packages/studio/src/styles/studio.css:204", + "packages/studio/src/styles/components.css:155", + "packages/studio/src/styles/components.css:156", "packages/studio/src/player/components/TimelineLanes.tsx:270" ], "paintBacking": ["--timeline-row-bg", "--timeline-clip-bg"] @@ -109,8 +109,8 @@ "role": "graphic", "minimum": 3, "sources": [ - "packages/studio/src/styles/studio.css:203", - "packages/studio/src/styles/studio.css:219", + "packages/studio/src/styles/components.css:155", + "packages/studio/src/styles/components.css:171", "packages/studio/src/player/components/TimelineLanes.tsx:270" ], "paintBacking": ["--timeline-row-bg", "--timeline-clip-bg"] @@ -122,7 +122,7 @@ "role": "graphic", "minimum": 3, "sources": [ - "packages/studio/src/styles/studio.css:251", + "packages/studio/src/styles/components.css:203", "packages/studio/src/player/components/TimelineLanes.tsx:270" ] }, @@ -136,7 +136,7 @@ "packages/studio/src/player/components/TimelineClip.tsx:160", "packages/studio/src/player/components/TimelineClip.tsx:161", "packages/studio/src/player/components/timelineTheme.ts:86", - "packages/studio/src/styles/studio.css:203" + "packages/studio/src/styles/components.css:155" ], "opacity": 0.492 }, @@ -150,9 +150,9 @@ "packages/studio/src/player/components/TimelineClip.tsx:160", "packages/studio/src/player/components/TimelineClip.tsx:161", "packages/studio/src/player/components/timelineTheme.ts:86", - "packages/studio/src/styles/studio.css:203", - "packages/studio/src/styles/studio.css:213", - "packages/studio/src/styles/studio.css:223" + "packages/studio/src/styles/components.css:155", + "packages/studio/src/styles/components.css:165", + "packages/studio/src/styles/components.css:175" ], "opacity": 0.492 }, @@ -166,7 +166,7 @@ "packages/studio/src/player/components/TimelineClip.tsx:160", "packages/studio/src/player/components/TimelineClip.tsx:161", "packages/studio/src/player/components/timelineTheme.ts:87", - "packages/studio/src/styles/studio.css:243" + "packages/studio/src/styles/components.css:195" ], "opacity": 0.456 }, @@ -184,9 +184,9 @@ "packages/studio/src/player/components/TimelineClip.tsx:160", "packages/studio/src/player/components/TimelineClip.tsx:161", "packages/studio/src/player/components/timelineTheme.ts:87", - "packages/studio/src/styles/studio.css:243", - "packages/studio/src/styles/studio.css:213", - "packages/studio/src/styles/studio.css:223" + "packages/studio/src/styles/components.css:195", + "packages/studio/src/styles/components.css:165", + "packages/studio/src/styles/components.css:175" ], "opacity": 0.456 }, @@ -200,7 +200,7 @@ "packages/studio/src/player/components/TimelineClip.tsx:160", "packages/studio/src/player/components/TimelineClip.tsx:161", "packages/studio/src/player/components/timelineTheme.ts:85", - "packages/studio/src/styles/studio.css:261" + "packages/studio/src/styles/components.css:213" ], "opacity": 0.57 }, @@ -218,9 +218,9 @@ "packages/studio/src/player/components/TimelineClip.tsx:160", "packages/studio/src/player/components/TimelineClip.tsx:161", "packages/studio/src/player/components/timelineTheme.ts:85", - "packages/studio/src/styles/studio.css:261", - "packages/studio/src/styles/studio.css:213", - "packages/studio/src/styles/studio.css:223" + "packages/studio/src/styles/components.css:213", + "packages/studio/src/styles/components.css:165", + "packages/studio/src/styles/components.css:175" ], "opacity": 0.57 }, @@ -231,8 +231,8 @@ "role": "graphic", "minimum": 3, "sources": [ - "packages/studio/src/styles/studio.css:230", - "packages/studio/src/styles/studio.css:213", + "packages/studio/src/styles/components.css:182", + "packages/studio/src/styles/components.css:165", "packages/studio/src/player/components/AudioWaveform.tsx:139", "packages/studio/src/player/components/AudioWaveform.tsx:145" ], @@ -250,8 +250,8 @@ "role": "graphic", "minimum": 3, "sources": [ - "packages/studio/src/styles/studio.css:230", - "packages/studio/src/styles/studio.css:213", + "packages/studio/src/styles/components.css:182", + "packages/studio/src/styles/components.css:165", "packages/studio/src/player/components/AudioWaveform.tsx:139", "packages/studio/src/player/components/AudioWaveform.tsx:145" ], @@ -265,7 +265,7 @@ "role": "graphic", "minimum": 3, "sources": [ - "packages/studio/src/styles/studio.css:246", + "packages/studio/src/styles/components.css:198", "packages/studio/src/player/components/AudioWaveform.tsx:144" ], "opacity": 0.64, @@ -352,7 +352,7 @@ "minimum": 4.5, "sources": [ "packages/studio/src/styles/theme.css", - "packages/studio/src/styles/studio.css" + "packages/studio/src/styles/components.css" ] }, { @@ -363,7 +363,7 @@ "minimum": 3, "sources": [ "packages/studio/src/styles/theme.css", - "packages/studio/src/styles/studio.css" + "packages/studio/src/styles/components.css" ] } ]