From 9825614d91de5ce9a8433aa09bc761e0d2cf369d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Miguel=20=C3=81ngel?= Date: Sat, 10 Oct 2026 19:08:30 +0000 Subject: [PATCH] fix(studio): Studio's own text reads without em or en dashes (#5414) * fix(studio): text in Studio reads without em or en dashes * fix(studio): the dash check also catches HTML entities, and peak reads Unavailable * test(studio): the dash check test covers every entity form * fix(studio): messages Studio shows from lint, sdk, core and the renderer drop dashes too * fix(producer): the gate check stops early on the engine's new fallback wording --- docs/studio/animation.mdx | 2 +- package.json | 5 +- packages/cli/src/utils/checkBrowser.test.ts | 2 +- packages/core/src/audio/audioFxWorklets.ts | 14 +-- packages/core/src/audioFx.ts | 10 +- packages/core/src/audioFxCopy.ts | 10 +- packages/core/src/audioFxEq.ts | 2 +- packages/core/src/audioFxJobs.ts | 4 +- packages/core/src/audioFxPresets.ts | 38 +++---- packages/core/src/audioLeveller.ts | 2 +- packages/core/src/figma/client.ts | 18 +-- packages/core/src/figma/emitTimelineScript.ts | 2 +- packages/core/src/fonts/deterministicFonts.ts | 6 +- packages/core/src/runtime/getVariables.ts | 2 +- packages/core/src/runtime/init.ts | 2 +- packages/core/src/runtime/vfx.ts | 4 +- packages/core/src/runtime/webAudioRoute.ts | 2 +- packages/core/src/vfx/lumaMatte.frag.ts | 2 +- packages/core/src/vfx/noise.frag.ts | 2 +- packages/engine/src/config.ts | 2 +- .../engine/src/services/browserManager.ts | 4 +- .../engine/src/services/drawElementService.ts | 2 +- packages/engine/src/services/frameCapture.ts | 48 ++++---- .../src/services/parallelCoordinator.ts | 4 +- .../engine/src/services/screenshotService.ts | 2 +- packages/engine/src/services/systemMemory.ts | 4 +- .../engine/src/services/threeDProjection.ts | 4 +- packages/engine/src/utils/gpuParityDiff.ts | 8 +- packages/engine/src/utils/hdr.test.ts | 2 +- packages/engine/src/utils/hdr.ts | 2 +- packages/lint/src/project.ts | 6 +- packages/lint/src/rules/composition.ts | 28 ++--- packages/lint/src/rules/core.ts | 4 +- packages/lint/src/rules/fonts.ts | 4 +- packages/lint/src/rules/gsap.ts | 44 ++++---- packages/lint/src/rules/media.ts | 22 ++-- .../src/outputResolutionCompatibility.ts | 2 +- .../parsers/src/slideshow/parseSlideshow.ts | 2 +- packages/player/src/hyperframes-player.ts | 4 +- .../src/slideshow/hyperframes-slideshow.ts | 4 +- packages/player/src/styles.ts | 2 +- packages/producer/de-gatecheck.sh | 8 +- packages/producer/src/server.ts | 2 +- .../producer/src/services/distributed/plan.ts | 10 +- .../distributed/renderConfigValidation.ts | 6 +- packages/producer/src/services/fileServer.ts | 6 +- .../producer/src/services/hdrCompositor.ts | 4 +- .../producer/src/services/htmlCompiler.ts | 12 +- .../src/services/render/captureCost.ts | 4 +- .../src/services/render/hdrMode.test.ts | 8 +- .../producer/src/services/render/hdrMode.ts | 10 +- .../src/services/render/planValidation.ts | 2 +- .../services/render/stages/compileStage.ts | 8 +- .../src/services/render/stages/freezePlan.ts | 2 +- .../src/services/render/stages/probeStage.ts | 10 +- .../src/services/render/videoFrameCoverage.ts | 2 +- .../src/services/renderOrchestrator.ts | 14 +-- packages/producer/src/transparency-test.ts | 10 +- .../producer/src/utils/audioRegression.ts | 2 +- packages/sdk/src/engine/mutate.ts | 12 +- .../shader-transitions/src/hyper-shader.ts | 4 +- .../studio-server/src/helpers/loudness.ts | 2 +- .../studio-server/src/routes/thumbnail.ts | 2 +- .../captions/components/CaptionOverlay.tsx | 2 +- .../src/captions/hooks/useCaptionSync.ts | 2 +- .../src/components/LinkedSelectionToggle.tsx | 4 +- packages/studio/src/components/LintModal.tsx | 6 +- .../studio/src/components/MediaPreview.tsx | 4 +- .../components/ProjectUnreachableBanner.tsx | 2 +- .../src/components/StudioRightPanels.tsx | 2 +- .../src/components/TimelineToolbar.test.tsx | 4 +- .../studio/src/components/TimelineToolbar.tsx | 12 +- .../src/components/editor/AnimationCard.tsx | 6 +- .../components/editor/AnimationCardParts.tsx | 2 +- .../components/editor/BlockParamsPanel.tsx | 6 +- .../components/editor/ComputedTweenNotice.tsx | 4 +- .../studio/src/components/editor/FileTree.tsx | 2 +- .../components/editor/GsapAnimationList.tsx | 2 +- .../editor/GsapAnimationSection.tsx | 4 +- .../components/editor/OffCanvasIndicators.tsx | 2 +- .../components/editor/PromotableControl.tsx | 2 +- .../src/components/editor/PropertyPanel.tsx | 4 +- .../src/components/editor/SnapToolbar.tsx | 4 +- .../src/components/editor/Transform3DCube.tsx | 2 +- .../editor/gsapAnimationConstants.ts | 4 +- .../editor/gsapAnimationHelpers.test.ts | 2 +- .../components/editor/gsapAnimationHelpers.ts | 2 +- .../propertyPanelColorGradingControls.tsx | 2 +- .../components/editor/propertyPanelFill.tsx | 2 +- .../editor/propertyPanelFlatMotionSection.tsx | 6 +- .../editor/propertyPanelFlatPrimitives.tsx | 4 +- .../editor/propertyPanelFlatSelectRow.tsx | 2 +- .../editor/propertyPanelFlatTextSection.tsx | 2 +- .../components/editor/propertyPanelFont.tsx | 6 +- .../editor/propertyPanelFxBandRuler.tsx | 4 +- .../editor/propertyPanelFxNodeOpenBody.tsx | 4 +- .../editor/propertyPanelTimingSection.tsx | 2 +- .../editor/propertyPanelTransformCommit.ts | 6 +- .../editor/useColorGradingController.ts | 2 +- .../components/panels/SlideshowSubPanels.tsx | 4 +- .../panels/VariablesBindElement.tsx | 2 +- .../src/components/panels/VariablesPanel.tsx | 6 +- .../src/components/renders/RenderQueue.tsx | 4 +- .../src/components/renders/useRenderQueue.ts | 6 +- .../src/components/sidebar/AssetCard.tsx | 4 +- .../src/components/sidebar/AudioRow.tsx | 2 +- .../src/components/sidebar/BlocksTab.tsx | 4 +- .../components/sidebar/CompositionsTab.tsx | 4 +- .../components/sidebar/PromptPreviewModal.tsx | 2 +- .../src/hooks/timelineAudioGroupCreate.ts | 2 +- .../src/hooks/timelineEditingHelpers.ts | 2 +- packages/studio/src/hooks/useBlockHandlers.ts | 2 +- .../studio/src/hooks/useDomEditSession.ts | 2 +- packages/studio/src/hooks/useEditorSave.ts | 2 +- packages/studio/src/hooks/useFrameCapture.ts | 2 +- packages/studio/src/hooks/useGestureCommit.ts | 8 +- .../studio/src/hooks/useGsapScriptCommits.ts | 2 +- .../components/AudioGainDialog.test.tsx | 2 +- .../src/player/components/AudioGainDialog.tsx | 3 +- .../src/player/components/EditModal.tsx | 4 +- .../components/KeyframeDiamondContextMenu.tsx | 2 +- .../src/player/components/OutOfSyncBadge.tsx | 2 +- .../studio/src/player/components/Player.tsx | 2 +- .../src/player/components/ShortcutsPanel.tsx | 4 +- .../Timeline.virtualization.test.tsx | 2 +- .../components/TimelineAutomationLaneSlot.tsx | 2 +- .../src/player/components/TimelineClip.tsx | 2 +- .../player/components/TimelineFxButton.tsx | 4 +- .../components/TimelineTrackHeader.test.tsx | 6 +- .../components/timelineClipDragCommit.ts | 4 +- .../components/trackHeaderLabelRows.tsx | 2 +- .../studio/src/player/lib/playbackAdapter.ts | 2 +- packages/studio/src/utils/hmrStableContext.ts | 2 +- .../src/utils/sdkCutover.linkedTiming.test.ts | 2 +- scripts/check-studio-copy-dashes.mjs | 94 ++++++++++++++++ scripts/check-studio-copy-dashes.test.mjs | 105 ++++++++++++++++++ 136 files changed, 549 insertions(+), 358 deletions(-) create mode 100644 scripts/check-studio-copy-dashes.mjs create mode 100644 scripts/check-studio-copy-dashes.test.mjs diff --git a/docs/studio/animation.mdx b/docs/studio/animation.mdx index 95eb3c7b88..df3af55d30 100644 --- a/docs/studio/animation.mdx +++ b/docs/studio/animation.mdx @@ -122,7 +122,7 @@ runtime expression, but it cannot always rewrite that source safely. - **Unroll to edit** rewrites supported helper- or loop-generated motion as explicit tweens that Studio can edit. -- **Computed value — edit it in the Code tab** means the value must be changed +- **Computed value. Edit it in the Code tab.**: the value must be changed in source. - When the intent is easier to state than the implementation, copy the element context to your agent and describe the result you want preserved. diff --git a/package.json b/package.json index 80650a6db3..83c843361c 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,7 @@ "sync-schemas:check": "tsx scripts/sync-schemas.ts --check", "sync:package-subpaths": "node scripts/package-subpaths.mjs --write", "check:package-subpaths": "node scripts/package-subpaths.mjs", - "lint": "bun run check:docs-snippet-motion && bun run check:tracked-artifacts && bun run check:workspace-contracts && bun run check:gcp-cloud-run-dockerfile && bun run check:package-cycles && bun run check:package-subpaths && bun run check:cli-process-ownership && oxlint . && tsx scripts/lint-skills.ts && node scripts/check-skill-mirror.mjs", + "lint": "bun run check:docs-snippet-motion && bun run check:studio-copy-dashes && bun run check:tracked-artifacts && bun run check:workspace-contracts && bun run check:gcp-cloud-run-dockerfile && bun run check:package-cycles && bun run check:package-subpaths && bun run check:cli-process-ownership && oxlint . && tsx scripts/lint-skills.ts && node scripts/check-skill-mirror.mjs", "check:gcp-cloud-run-dockerfile": "bun run --cwd packages/gcp-cloud-run test:dockerfile-workspaces", "lint:skills": "tsx scripts/lint-skills.ts", "check:skill-mirror": "node scripts/check-skill-mirror.mjs", @@ -40,6 +40,7 @@ "check:workspace-contracts": "node scripts/check-workspace-contracts.mjs", "check:package-cycles": "node scripts/check-package-cycles.mjs", "check:cli-process-ownership": "node scripts/check-cli-process-ownership.mjs", + "check:studio-copy-dashes": "node scripts/check-studio-copy-dashes.mjs", "format": "oxfmt .", "test": "bun run test:unit", "test:unit": "bun run --filter '*' test", @@ -52,7 +53,7 @@ "player:perf": "bun run --filter @hyperframes/player perf", "format:check": "oxfmt --check .", "knip": "knip", - "test:scripts": "node --import tsx --test scripts/animejs-v4-guidance.test.mjs scripts/check-tracked-artifacts.test.mjs scripts/check-registry-set-delta.test.mjs scripts/check-no-main-deletions.test.mjs scripts/check-pr-captures.test.mjs scripts/check-comment-citations.test.mjs scripts/comments-workflow.test.mjs scripts/comment-ratchet.test.mjs scripts/check-docs-snippet-motion.test.mjs scripts/registry-target-paths.test.mjs scripts/check-workspace-contracts.test.mjs scripts/check-media-use-copy-parity.test.mjs scripts/check-svg-sanitize-parity.test.mjs scripts/check-media-use-svg-sanitize-generated.test.mjs scripts/check-package-cycles.test.mjs scripts/check-cli-process-ownership.test.mjs scripts/check-large-files.test.mjs scripts/prune-fallow-audit-caches.test.mjs scripts/package-subpaths.test.mjs scripts/validate-release-channel.test.mjs scripts/clawhub-sync.test.mjs scripts/publish-workflow.test.mjs scripts/pr-edit-concurrency.test.mjs scripts/merge-queue-workflows.test.mjs scripts/install-workspace-dependencies.test.mjs scripts/draft-changelog.test.ts scripts/set-version.test.ts scripts/release-prepare.test.ts scripts/cli-options.test.ts scripts/changelog-weekly.test.ts scripts/claude-plugin-compression.test.ts scripts/catalog-payload-assets.test.ts scripts/host-registry-assets.test.ts scripts/catalog-preview-temp.test.ts scripts/backfill-block-previews.test.ts scripts/catalog-hosted-files.test.ts scripts/player-cdn-pin.test.ts scripts/studio-runtime-smoke.test.mjs scripts/verify-packed-manifests.test.mjs scripts/lint-skills.test.mjs scripts/creator-editing-recipes.test.mjs packages/gcp-cloud-run/check-dockerfile-workspaces.test.mjs packages/core/scripts/writeGeneratedFile.test.ts scripts/catalog-publication.test.mjs scripts/ci/resolve-workflow-pr.test.mjs scripts/ci/base-file.test.mjs scripts/check-catalog-source-pr.test.mjs scripts/generate-registry-items.test.ts scripts/catalog-drift.test.ts scripts/catalog-fetch-mirror.test.ts scripts/catalog-script-inlining.test.ts scripts/catalog-detail.test.ts scripts/generate-catalog-pages.test.ts scripts/verify-catalog-payloads.test.ts scripts/registry-skill-files.test.ts scripts/creator-editing-capabilities.test.mjs scripts/generate-catalog-previews.test.ts scripts/registry-primitive-payloads.test.ts registry/components/pan-stations/pan-stations.test.mjs && vitest run scripts/catalog/ scripts/contrastRatchet.test.ts scripts/generate-catalog-payloads.test.ts", + "test:scripts": "node --import tsx --test scripts/animejs-v4-guidance.test.mjs scripts/check-tracked-artifacts.test.mjs scripts/check-registry-set-delta.test.mjs scripts/check-no-main-deletions.test.mjs scripts/check-pr-captures.test.mjs scripts/check-comment-citations.test.mjs scripts/comments-workflow.test.mjs scripts/comment-ratchet.test.mjs scripts/check-docs-snippet-motion.test.mjs scripts/registry-target-paths.test.mjs scripts/check-workspace-contracts.test.mjs scripts/check-media-use-copy-parity.test.mjs scripts/check-svg-sanitize-parity.test.mjs scripts/check-media-use-svg-sanitize-generated.test.mjs scripts/check-package-cycles.test.mjs scripts/check-cli-process-ownership.test.mjs scripts/check-studio-copy-dashes.test.mjs scripts/check-large-files.test.mjs scripts/prune-fallow-audit-caches.test.mjs scripts/package-subpaths.test.mjs scripts/validate-release-channel.test.mjs scripts/clawhub-sync.test.mjs scripts/publish-workflow.test.mjs scripts/pr-edit-concurrency.test.mjs scripts/merge-queue-workflows.test.mjs scripts/install-workspace-dependencies.test.mjs scripts/draft-changelog.test.ts scripts/set-version.test.ts scripts/release-prepare.test.ts scripts/cli-options.test.ts scripts/changelog-weekly.test.ts scripts/claude-plugin-compression.test.ts scripts/catalog-payload-assets.test.ts scripts/host-registry-assets.test.ts scripts/catalog-preview-temp.test.ts scripts/backfill-block-previews.test.ts scripts/catalog-hosted-files.test.ts scripts/player-cdn-pin.test.ts scripts/studio-runtime-smoke.test.mjs scripts/verify-packed-manifests.test.mjs scripts/lint-skills.test.mjs scripts/creator-editing-recipes.test.mjs packages/gcp-cloud-run/check-dockerfile-workspaces.test.mjs packages/core/scripts/writeGeneratedFile.test.ts scripts/catalog-publication.test.mjs scripts/ci/resolve-workflow-pr.test.mjs scripts/ci/base-file.test.mjs scripts/check-catalog-source-pr.test.mjs scripts/generate-registry-items.test.ts scripts/catalog-drift.test.ts scripts/catalog-fetch-mirror.test.ts scripts/catalog-script-inlining.test.ts scripts/catalog-detail.test.ts scripts/generate-catalog-pages.test.ts scripts/verify-catalog-payloads.test.ts scripts/registry-skill-files.test.ts scripts/creator-editing-capabilities.test.mjs scripts/generate-catalog-previews.test.ts scripts/registry-primitive-payloads.test.ts registry/components/pan-stations/pan-stations.test.mjs && vitest run scripts/catalog/ scripts/contrastRatchet.test.ts scripts/generate-catalog-payloads.test.ts", "typecheck:scripts": "tsc --noEmit -p scripts/tsconfig.json", "test:skills": "HYPERFRAMES_MEDIA_HOME_REQUIRED=1 node --import ./packages/cli/scripts/test-home.mjs --test 'skills/**/*.test.mjs' 'packages/cli/src/media-use/**/*.test.mjs'", "generate:previews": "tsx scripts/generate-template-previews.ts", diff --git a/packages/cli/src/utils/checkBrowser.test.ts b/packages/cli/src/utils/checkBrowser.test.ts index c67a35cfa0..60b5df99c7 100644 --- a/packages/cli/src/utils/checkBrowser.test.ts +++ b/packages/cli/src/utils/checkBrowser.test.ts @@ -450,7 +450,7 @@ it("surfaces the runtime's web-audio-bypass console.info line as its own info fi "info", '[hyperframes] runtime_web_audio_bypass: "https://cdn.example.com/track.mp3" ' + "(cross_origin_no_cors): Web Audio capture withheld; the track plays through native " + - "HTMLMediaElement output. Native playback cannot reproduce: fx-chain — proxy or download " + + "HTMLMediaElement output. Native playback cannot reproduce: fx-chain. Fix: proxy or download " + "the asset to a same-origin URL to keep it.", ); const authorInfo = fakeConsoleMessage("info", "debug runtime_web_audio_bypass lookalike"); diff --git a/packages/core/src/audio/audioFxWorklets.ts b/packages/core/src/audio/audioFxWorklets.ts index 1e4e3c7b07..9f9354401c 100644 --- a/packages/core/src/audio/audioFxWorklets.ts +++ b/packages/core/src/audio/audioFxWorklets.ts @@ -21,7 +21,7 @@ const dbToLin = (db) => Math.pow(10, db / 20); * Per channel matters: the followers advance once per sample, so a single shared * follower stepped once per channel per sample. On stereo that ran a 20 ms attack * as 10 ms, and gave the right channel a gain computed from an envelope that had - * already traversed the left — so the two channels ducked by different amounts + * already traversed the left, so the two channels ducked by different amounts * from the same input and the image pumped. */ class EnvBank { @@ -233,7 +233,7 @@ function readTap(ring, write, delaySamples) { return ring[i0] * (1 - frac) + ring[i1] * frac; } -/** Equal-power-ish crossfade, zero at a tap's reset point — hides the splice. */ +/** Equal-power-ish crossfade, zero at a tap's reset point, which hides the splice. */ function xfade(phase) { return Math.sin(Math.PI * phase); } @@ -245,7 +245,7 @@ function xfade(phase) { * out, which hides the splice each tap makes when it wraps. * * write/phase are block-level state, advanced once per SAMPLE across all - * channels together (not once per channel) — advancing them inside the + * channels together (not once per channel). Advancing them inside the * per-channel loop would move the tap 2x/4x too fast on a stereo/quad input. */ class HfPitchshift extends AudioWorkletProcessor { @@ -258,13 +258,13 @@ class HfPitchshift extends AudioWorkletProcessor { this.phase = 0; // Samples written so far, capped at one grain. The taps read up to a grain // behind the write head, so until this fills they would read the ring's - // zeros — the head of every clip came out attenuated or silent. + // zeros, so the head of every clip came out attenuated or silent. this.filled = 0; // How much of the wet (pitch-shifted) path is currently in the output, and // where it is heading. Crossing between dry and wet is a ~50 ms jump in the // signal, so it is RAMPED rather than switched: a hard swap either way is a // click. Ramping in both directions is also what lets a node return to true - // bypass at semitones 0 — a one-way latch left preview stuck with the delay + // bypass at semitones 0. A one-way latch left preview stuck with the delay // that the render, building a fresh node from the attribute, does not have. this.wet = 0; this.wetTarget = 0; @@ -292,7 +292,7 @@ class HfPitchshift extends AudioWorkletProcessor { // Nothing to shift, or mixed fully out. The grain delay is ~grain/2 // whatever the ratio, so at semitones=0 this degenerated into a pure 50 ms - // delay of the signal — while the copy for that exact setting reads + // delay of the signal, while the copy for that exact setting reads // "Unchanged pitch". this.wetTarget = semitones === 0 ? 0 : mix; @@ -371,7 +371,7 @@ export function ensureAudioFxWorklets(ctx: BaseAudioContext): Promise { modulePromise = (async () => { if (!ctx.audioWorklet) { throw new Error( - "AudioWorklet is unavailable — the page needs a secure context (https, localhost or file://)", + "AudioWorklet is unavailable: the page needs a secure context (https, localhost or file://)", ); } // A data: URL rather than a blob:, because a blob inherits the page origin diff --git a/packages/core/src/audioFx.ts b/packages/core/src/audioFx.ts index 60a57e52a6..7a75c2d947 100644 --- a/packages/core/src/audioFx.ts +++ b/packages/core/src/audioFx.ts @@ -103,7 +103,7 @@ const freq = ( automatable: true, }); -const qParam = (def = 0.707, hint = "Bandwidth — higher is narrower."): HfAudioFxNumberParam => ({ +const qParam = (def = 0.707, hint = "Bandwidth: higher is narrower."): HfAudioFxNumberParam => ({ kind: "number", key: "q", label: "Q", @@ -187,7 +187,7 @@ export const HF_AUDIO_FX: readonly HfAudioFxDef[] = [ id: "highpass", label: "High-pass", group: "filter", - description: "Remove low frequencies — the usual fix for rumble on a voice.", + description: "Remove low frequencies: the usual fix for rumble on a voice.", params: [freq("frequency", "Cutoff", 300, 20, 20000), qParam(0.707), poles], web: "biquad-highpass", }, @@ -195,7 +195,7 @@ export const HF_AUDIO_FX: readonly HfAudioFxDef[] = [ id: "lowpass", label: "Low-pass", group: "filter", - description: "Remove high frequencies — darkens or muffles a track.", + description: "Remove high frequencies, which darkens or muffles a track.", params: [freq("frequency", "Cutoff", 8000, 100, 20000), qParam(0.707), poles], web: "biquad-lowpass", }, @@ -288,7 +288,7 @@ export const HF_AUDIO_FX: readonly HfAudioFxDef[] = [ id: "limiter", label: "Limiter", group: "dynamics", - description: "Hard ceiling — nothing gets past the limit.", + description: "Hard ceiling: nothing gets past the limit.", params: [ { kind: "number", @@ -533,7 +533,7 @@ export const HF_AUDIO_FX: readonly HfAudioFxDef[] = [ max: 250, step: 1, default: 1, - hint: "Repeats each sample N times — a crude downsample.", + hint: "Repeats each sample N times, a crude downsample.", }, { kind: "number", diff --git a/packages/core/src/audioFxCopy.ts b/packages/core/src/audioFxCopy.ts index 3490a840fa..c6e2ac7aa0 100644 --- a/packages/core/src/audioFxCopy.ts +++ b/packages/core/src/audioFxCopy.ts @@ -63,7 +63,7 @@ export const EFFECT_COPY: Record = { }, highpass: { title: "Remove Rumble", - does: "Cuts the very bottom — traffic, footsteps, air conditioning, hands on the mic.", + does: "Cuts the very bottom: traffic, footsteps, air conditioning, hands on the mic.", reachFor: "There's a low hum or thump under everything.", primary: "frequency", primaryEnds: { low: "Only the deepest", high: "Thins it out" }, @@ -94,7 +94,7 @@ export const EFFECT_COPY: Record = { peaking: { title: "Shape One Range", does: "Lifts or lowers one part of the sound and leaves the rest alone.", - reachFor: "One quality is wrong — boomy, boxy, harsh — but the rest is fine.", + reachFor: "One quality is wrong (boomy, boxy, harsh) but the rest is fine.", primary: "gain", primaryEnds: { low: "Take it out", high: "Bring it forward" }, band: [20, 20000], @@ -184,7 +184,7 @@ export const EFFECT_COPY: Record = { }, gate: { title: "Silence the Gaps", - does: "Mutes the pauses. Whatever sits underneath stays — this closes the gaps, it does not remove noise.", + does: "Mutes the pauses. Whatever sits underneath stays: this closes the gaps, it does not remove noise.", reachFor: "You can hear the room in the gaps.", primary: "strength", primaryEnds: { low: "Only true silence", high: "Cuts quiet parts too" }, @@ -192,7 +192,7 @@ export const EFFECT_COPY: Record = { threshold: { label: "Quieter than this is a gap" }, range: { label: "How far to duck the gaps", - hint: "Not all the way down, usually — total silence sounds broken.", + hint: "Not all the way down, usually. Total silence sounds broken.", }, ratio: { label: "How hard" }, attack: { label: "How fast it opens" }, @@ -410,7 +410,7 @@ export const SUMMARY: Record string> = { ? "Doing nothing yet" : `${n(p.gain) > 0 ? "More" : "Less"} sparkle above ${hz(p.frequency)}`, compressor: (p) => - `Evening out — ${strength(Math.min(1, (n(p.ratio, 3) - 1) / 7), ["gentle", "moderate", "firm"])}`, + `Evening out, ${strength(Math.min(1, (n(p.ratio, 3) - 1) / 7), ["gentle", "moderate", "firm"])}`, limiter: (p) => `Nothing louder than ${n(p.limit, -1)} dB`, truepeak: (p) => `Peaks pulled down toward ${n(p.ceiling, -1)} dBTP`, gate: (p) => `Closing gaps quieter than ${n(p.threshold, -45)} dB`, diff --git a/packages/core/src/audioFxEq.ts b/packages/core/src/audioFxEq.ts index c1fbcea6aa..ce728d6402 100644 --- a/packages/core/src/audioFxEq.ts +++ b/packages/core/src/audioFxEq.ts @@ -183,7 +183,7 @@ export function removeAudioEq(chain: HfAudioFxChain, eqId: string): HfAudioFxCha */ export function audioEqSummary(bands: readonly HfAudioEqBand[]): string { const moved = bands.filter((b) => Math.abs(b.gain) >= 0.1); - if (moved.length === 0) return "Flat — nothing changed yet"; + if (moved.length === 0) return "Flat: nothing changed yet"; return moved .map((b) => `${b.name} ${b.gain > 0 ? "+" : "−"}${Math.abs(Number(b.gain.toFixed(1)))}`) .join(", "); diff --git a/packages/core/src/audioFxJobs.ts b/packages/core/src/audioFxJobs.ts index bbdc5f4eec..73d517f035 100644 --- a/packages/core/src/audioFxJobs.ts +++ b/packages/core/src/audioFxJobs.ts @@ -51,7 +51,7 @@ export const HF_AUDIO_FX_JOBS: readonly HfAudioFxJob[] = [ { id: "tame-boominess", label: "Tame Boominess", - does: "Too much low-end body — it booms.", + does: "Too much low-end body, so it booms.", type: "peaking", params: { frequency: 200, gain: -4, q: 1.4 }, }, @@ -72,7 +72,7 @@ export const HF_AUDIO_FX_JOBS: readonly HfAudioFxJob[] = [ { id: "add-clarity", label: "Add Clarity", - does: "It is hard to make out — it sits back.", + does: "It is hard to make out because it sits back.", type: "peaking", params: { frequency: 3000, gain: 2.5, q: 1 }, }, diff --git a/packages/core/src/audioFxPresets.ts b/packages/core/src/audioFxPresets.ts index 2eb36f1f9e..de72f51ffc 100644 --- a/packages/core/src/audioFxPresets.ts +++ b/packages/core/src/audioFxPresets.ts @@ -111,7 +111,7 @@ export const HF_AUDIO_FX_PRESETS: readonly HfAudioFxPreset[] = [ "voice-broadcast", "voice", "Broadcast", - "Denser and more forward — a radio-presenter sound.", + "Denser and more forward: a radio-presenter sound.", [ { type: "highpass", label: "Remove Rumble", params: { frequency: 90, q: 0.707, poles: "2" } }, { type: "peaking", label: "Reduce Boxiness", params: { frequency: 400, gain: -3, q: 1.4 } }, @@ -159,7 +159,7 @@ export const HF_AUDIO_FX_PRESETS: readonly HfAudioFxPreset[] = [ "room-gate", "repair", "Quiet Between Phrases", - "Silences the gaps between words. Room tone under speech stays — this closes the pauses, it does not remove noise.", + "Silences the gaps between words. Room tone under speech stays: this closes the pauses, it does not remove noise.", [ { type: "gate", @@ -184,23 +184,17 @@ export const HF_AUDIO_FX_PRESETS: readonly HfAudioFxPreset[] = [ ), // ------------------------------------------------------------ character -- - preset( - "telephone", - "character", - "Telephone", - "Down the line — the narrow band of a phone call.", - [ - ...steep("highpass", 300, "Strip the Bass"), - ...steep("lowpass", 3400, "Strip the Treble"), - { type: "peaking", label: "Phone Honk", params: { frequency: 1200, gain: 6, q: 1.2 } }, - { type: "peaking", label: "De-mud", params: { frequency: 550, gain: -4, q: 1 } }, - { - type: "saturate", - label: "Circuit Grit", - params: { type: "tanh", threshold: -9, output: -2 }, - }, - ], - ), + preset("telephone", "character", "Telephone", "Down the line: the narrow band of a phone call.", [ + ...steep("highpass", 300, "Strip the Bass"), + ...steep("lowpass", 3400, "Strip the Treble"), + { type: "peaking", label: "Phone Honk", params: { frequency: 1200, gain: 6, q: 1.2 } }, + { type: "peaking", label: "De-mud", params: { frequency: 550, gain: -4, q: 1 } }, + { + type: "saturate", + label: "Circuit Grit", + params: { type: "tanh", threshold: -9, output: -2 }, + }, + ]), preset("radio-am", "character", "AM Radio", "Narrow, gritty and a little crushed.", [ // Narrower than the megaphone at BOTH ends, which is most of the difference // between them: an AM channel is a few kHz wide and the receiver rolls off @@ -324,7 +318,7 @@ export const HF_AUDIO_FX_PRESETS: readonly HfAudioFxPreset[] = [ "doofus-worble", "character", "Doofus Worble", - "Seasick and wobbling — no straight signal left.", + "Seasick and wobbling, no straight signal left.", [ { type: "chorus", @@ -357,7 +351,7 @@ export const HF_AUDIO_FX_PRESETS: readonly HfAudioFxPreset[] = [ ]), // ---------------------------------------------------------------- space -- - preset("room-tight", "space", "Tight Room", "A small hard room — presence without wash.", [ + preset("room-tight", "space", "Tight Room", "A small hard room: presence without wash.", [ { type: "reverb", label: "Tight Room", @@ -380,7 +374,7 @@ export const HF_AUDIO_FX_PRESETS: readonly HfAudioFxPreset[] = [ preset("hall", "space", "Hall", "Long and open, for something that should sit far back.", [ { type: "reverb", label: "Hall", params: { size: 0.9, damping: 0.3, wet: 0.4, dry: 0.75 } }, ]), - preset("slap-echo", "space", "Slap Echo", "One quick repeat — rockabilly vocal, not a wash.", [ + preset("slap-echo", "space", "Slap Echo", "One quick repeat: rockabilly vocal, not a wash.", [ { type: "delay", label: "Slap Echo", params: { time: 110, feedback: 0.12, mix: 0.22 } }, ]), preset("dub-throw", "space", "Dub Throw", "Repeats that trail off well behind the beat.", [ diff --git a/packages/core/src/audioLeveller.ts b/packages/core/src/audioLeveller.ts index 012cb983e8..1c1f77ee80 100644 --- a/packages/core/src/audioLeveller.ts +++ b/packages/core/src/audioLeveller.ts @@ -242,7 +242,7 @@ export function removeLevelling(chain: HfAudioFxChain): { /** What the module says when it is closed. */ export function levellingSummary(points: readonly HfAutomationPoint[]): string { const moves = points.filter((p) => p.v !== 0); - if (moves.length === 0) return "Already even — nothing to do"; + if (moves.length === 0) return "Already even: nothing to do"; const lift = Math.max(...moves.map((p) => p.v)); const cut = Math.min(...moves.map((p) => p.v)); const parts: string[] = []; diff --git a/packages/core/src/figma/client.ts b/packages/core/src/figma/client.ts index 8d8b2eed1f..c62d1d0312 100644 --- a/packages/core/src/figma/client.ts +++ b/packages/core/src/figma/client.ts @@ -200,12 +200,12 @@ export function createFigmaClient(options: FigmaClientOptions): FigmaClient { [ "FIGMA_TOKEN is missing. One-time setup:", " 1. figma.com/settings → Security → Personal access tokens → Generate new token", - " 2. Scopes (read-only is all this integration ever needs — it never writes to figma):", + " 2. Scopes (read-only is all this integration ever needs: it never writes to figma):", " File content: Read-only · File metadata: Read-only", " Library content: Read-only (needed for the `tokens` published-styles fallback)", - " Variables: Read-only (optional — brand variables, requires figma Enterprise;", + " Variables: Read-only (optional: brand variables, requires figma Enterprise;", " without it `tokens` falls back to published styles)", - ' 3. export FIGMA_TOKEN="figd_…" — add it to your shell profile or the project .env', + ' 3. export FIGMA_TOKEN="figd_…", then add it to your shell profile or the project .env', " so future sessions skip this step", "Then re-run this command.", ].join("\n"), @@ -237,26 +237,26 @@ export function createFigmaClient(options: FigmaClientOptions): FigmaClient { if (body && /invalid token/i.test(body)) return new FigmaClientError( "BAD_TOKEN", - "figma rejected the token (403 Invalid token) — it is invalid, expired, or revoked. Re-mint at figma.com/settings → Security, then update FIGMA_TOKEN.", + "figma rejected the token (403 Invalid token): it is invalid, expired, or revoked. Re-mint at figma.com/settings → Security, then update FIGMA_TOKEN.", 403, opts.endpoint, ); if (opts.enterpriseGated) return new FigmaClientError( "REQUIRES_ENTERPRISE", - "figma variables require an Enterprise plan (403) — fall back to styles", + "figma variables require an Enterprise plan (403), so fall back to styles", 403, opts.endpoint, ); if (body && /scope/i.test(body)) return new FigmaClientError( "FORBIDDEN", - `figma denied access (403): ${body} — add the named scope at figma.com/settings → Security → Personal access tokens.`, + `figma denied access (403): ${body}. Add the named scope at figma.com/settings → Security → Personal access tokens.`, 403, opts.endpoint, ); const scopeLine = opts.scopeHint - ? `This endpoint needs the "${opts.scopeHint}" scope — add it at figma.com/settings → Security → Personal access tokens.` + ? `This endpoint needs the "${opts.scopeHint}" scope. Add it at figma.com/settings → Security → Personal access tokens.` : "The token is missing a read scope, or your account can't view this file. Check File content: Read-only + File metadata: Read-only at figma.com/settings → Security."; return new FigmaClientError( "FORBIDDEN", @@ -272,7 +272,7 @@ export function createFigmaClient(options: FigmaClientOptions): FigmaClient { if (res.status === 401) throw new FigmaClientError( "BAD_TOKEN", - "figma rejected the token (401) — it is expired or revoked. Re-mint at figma.com/settings → Security, then update FIGMA_TOKEN.", + "figma rejected the token (401): it is expired or revoked. Re-mint at figma.com/settings → Security, then update FIGMA_TOKEN.", 401, opts.endpoint, ); @@ -280,7 +280,7 @@ export function createFigmaClient(options: FigmaClientOptions): FigmaClient { if (res.status === 429) throw new FigmaClientError( "RATE_LIMITED", - `figma rate limit hit (429) and still limited after ${maxRetries} retries — wait a minute and re-run, or import fewer nodes per call.`, + `figma rate limit hit (429) and still limited after ${maxRetries} retries. Wait a minute and re-run, or import fewer nodes per call.`, 429, opts.endpoint, ); diff --git a/packages/core/src/figma/emitTimelineScript.ts b/packages/core/src/figma/emitTimelineScript.ts index 9ec93d4ec1..564838caf8 100644 --- a/packages/core/src/figma/emitTimelineScript.ts +++ b/packages/core/src/figma/emitTimelineScript.ts @@ -36,7 +36,7 @@ export function emitTimelineScript(spec: TimelineSpec): string { : 'typeof gsap === "undefined"'; const libs = needsCustomEase ? "gsap + CustomEase" : "gsap"; lines.push( - `if (${missing}) { console.warn(${lit(`figma timeline ${spec.timelineId}: ${libs} not loaded — add the CDN