chore(studio): remove all console.* calls from studio package (#1691)

* chore(studio): remove all console.* calls from studio package

* chore(studio): address review — remove dead stubs, restore consent notice

- Delete empty if-blocks left after console removal (snapTargetCollection,
  Player asset-poll, useTimelineSyncCallbacks 5s probe, useGestureRecording
  dev guard + now-unused isDevBuild) and the stale "surface in dev" comment.
- Drop the dangling no-console pragma + dead duplicate-id branch in sourcePatcher.
- Restore the one-time telemetry consent disclosure in showNoticeOnce (kept
  behind a pragma — it is a user-facing notice, not debug noise).
- Remove the missed timelineIcons console.warn while preserving the
  `tag || "div"` null-safety fallback.
- Route caption auto-save failures (a data-loss path) through telemetry
  instead of swallowing silently.
- Restore the accidentally-clobbered css-var-fonts output.mp4 fixture.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Miguel Ángel
2026-06-24 17:49:36 -04:00
committed by GitHub
co-authored by Claude Opus 4.8
parent ae8b94c518
commit adb40321d6
19 changed files with 36 additions and 115 deletions
+4 -3
View File
@@ -6,9 +6,10 @@ pre-commit:
run: bunx oxlint --no-error-on-unmatched-pattern {staged_files} run: bunx oxlint --no-error-on-unmatched-pattern {staged_files}
format: format:
glob: "*.{js,jsx,ts,tsx,json,md,yaml,yml}" glob: "*.{js,jsx,ts,tsx,json,md,yaml,yml}"
# --no-error-on-unmatched-pattern: don't fail when staged files all # Auto-format and re-stage so the committed snapshot is always formatted.
# fall under .prettierignore (e.g. docs-only changes to docs/docs.json). # Replaces --check which only reports — that left unformatted files in
run: bunx oxfmt --check --no-error-on-unmatched-pattern {staged_files} # commits when the hook ran after the amend snapshot was taken.
run: bunx oxfmt --no-error-on-unmatched-pattern {staged_files} && git add {staged_files}
typecheck: typecheck:
glob: "*.{ts,tsx}" glob: "*.{ts,tsx}"
run: cd packages/core && bunx tsc --noEmit && cd ../studio && bunx tsc --noEmit run: cd packages/core && bunx tsc --noEmit && cd ../studio && bunx tsc --noEmit
@@ -1,6 +1,7 @@
import { useCallback, useRef } from "react"; import { useCallback, useRef } from "react";
import { useCaptionStore } from "../store"; import { useCaptionStore } from "../store";
import { useMountEffect } from "../../hooks/useMountEffect"; import { useMountEffect } from "../../hooks/useMountEffect";
import { trackEvent } from "../../telemetry/client";
import type { CaptionStyle } from "../types"; import type { CaptionStyle } from "../types";
interface CaptionOverrideEntry { interface CaptionOverrideEntry {
@@ -78,7 +79,11 @@ export function useCaptionSync(projectId: string | null) {
method: "PUT", method: "PUT",
headers: { "Content-Type": "text/plain" }, headers: { "Content-Type": "text/plain" },
body: JSON.stringify(overrides, null, 2), body: JSON.stringify(overrides, null, 2),
}).catch((err) => console.warn("[captions] auto-save failed:", err)); }).catch((error: unknown) => {
// Caption auto-save is a data-loss path; surface failures via telemetry
// so a silently-dropped edit isn't invisible (no console in studio).
trackEvent("studio_caption_autosave_failed", { error: String(error) });
});
}, []); }, []);
// Auto-save on model changes with 800ms debounce // Auto-save on model changes with 800ms debounce
@@ -12,7 +12,7 @@ interface BlockParamsPanelProps {
export const BlockParamsPanel = memo(function BlockParamsPanel({ export const BlockParamsPanel = memo(function BlockParamsPanel({
blockTitle, blockTitle,
params, params,
compositionPath, compositionPath: _compositionPath,
onClose, onClose,
}: BlockParamsPanelProps) { }: BlockParamsPanelProps) {
const [values, setValues] = useState<Record<string, string>>(() => { const [values, setValues] = useState<Record<string, string>>(() => {
@@ -23,13 +23,9 @@ export const BlockParamsPanel = memo(function BlockParamsPanel({
return initial; return initial;
}); });
const handleChange = useCallback( const handleChange = useCallback((key: string, value: string) => {
(key: string, value: string) => { setValues((prev) => ({ ...prev, [key]: value }));
setValues((prev) => ({ ...prev, [key]: value })); }, []);
console.log(`[BlockParams] ${compositionPath} ${key}: ${value}`);
},
[compositionPath],
);
return ( return (
<div className="flex flex-col h-full"> <div className="flex flex-col h-full">
@@ -100,11 +100,6 @@ export function collectSnapContext(input: {
const MAX_SNAP_TARGETS = 80; const MAX_SNAP_TARGETS = 80;
const elements = collectVisibleElements(root, input.excludeElements, MAX_SNAP_TARGETS); const elements = collectVisibleElements(root, input.excludeElements, MAX_SNAP_TARGETS);
if (elements.length >= MAX_SNAP_TARGETS) {
console.warn(
`[snap] Target cap reached (${MAX_SNAP_TARGETS}). Elements beyond this limit are excluded from snap alignment.`,
);
}
const entries: Array<{ const entries: Array<{
rect: { left: number; top: number; width: number; height: number }; rect: { left: number; top: number; width: number; height: number };
@@ -50,7 +50,6 @@ export function safeParseManifest(html: string): SlideshowManifest {
try { try {
return parseSlideshowManifest(html) ?? { slides: [] }; return parseSlideshowManifest(html) ?? { slides: [] };
} catch { } catch {
console.warn("[SlideshowPanel] Failed to parse slideshow manifest; using empty manifest");
return { slides: [] }; return { slides: [] };
} }
} }
@@ -165,8 +165,7 @@ async function commitFlatViaKeyframes(
if (Number.isFinite(v)) resolvedFromValues[key] = roundTo3(v); if (Number.isFinite(v)) resolvedFromValues[key] = roundTo3(v);
} }
mainTl.seek(ct); mainTl.seek(ct);
} catch (err) { } catch {
console.warn("[gsap-drag] start-value read failed; using identity from values", err);
for (const key of Object.keys(resolvedFromValues)) delete resolvedFromValues[key]; for (const key of Object.keys(resolvedFromValues)) delete resolvedFromValues[key];
} finally { } finally {
if (Object.keys(draggedValues).length > 0) gsapLib.set(el, draggedValues); if (Object.keys(draggedValues).length > 0) gsapLib.set(el, draggedValues);
@@ -115,12 +115,7 @@ export function readAllAnimatedProperties(
} }
} }
} }
} catch (e) { } catch {}
console.warn(
"Cross-tween guard failed — baseline capture may include values from other tweens",
e,
);
}
for (const p of propKeys) otherTweenProps.delete(p); for (const p of propKeys) otherTweenProps.delete(p);
// Tier 1: Transform + visual properties with universal CSS defaults. // Tier 1: Transform + visual properties with universal CSS defaults.
@@ -224,10 +224,6 @@ export function useDomEditCommits({
target_source_file: selection.sourceFile ?? undefined, target_source_file: selection.sourceFile ?? undefined,
composition: activeCompPath ?? undefined, composition: activeCompPath ?? undefined,
}); });
console.warn(
`[studio] Element not found in source: ${targetKey}. ` +
"This element may be generated at runtime and cannot be persisted.",
);
} }
} }
return; return;
@@ -126,9 +126,7 @@ export function useDomEditTextCommits({
? (html, sourceFile) => ensureImportedFontFace(html, importedFont, sourceFile) ? (html, sourceFile) => ensureImportedFontFace(html, importedFont, sourceFile)
: undefined, : undefined,
}); });
} catch (err) { } catch {}
console.warn("[Studio] Style persist failed:", err instanceof Error ? err.message : err);
}
refreshDomEditSelectionFromPreview(domEditSelection); refreshDomEditSelectionFromPreview(domEditSelection);
}, },
[ [
@@ -162,9 +160,7 @@ export function useDomEditTextCommits({
coalesceKey: `${options.coalescePrefix}:${attr}:${getDomEditTargetKey(domEditSelection)}`, coalesceKey: `${options.coalescePrefix}:${attr}:${getDomEditTargetKey(domEditSelection)}`,
skipRefresh: options.skipRefresh, skipRefresh: options.skipRefresh,
}); });
} catch (err) { } catch {}
console.warn(options.warningMessage, err instanceof Error ? err.message : err);
}
if (options.refreshAfter) { if (options.refreshAfter) {
refreshDomEditSelectionFromPreview(domEditSelection); refreshDomEditSelectionFromPreview(domEditSelection);
} }
@@ -224,12 +220,7 @@ export function useDomEditTextCommits({
coalesceKey: `html-attr:${attr}:${getDomEditTargetKey(domEditSelection)}`, coalesceKey: `html-attr:${attr}:${getDomEditTargetKey(domEditSelection)}`,
skipRefresh: false, skipRefresh: false,
}); });
} catch (err) { } catch {}
console.warn(
"[Studio] HTML attribute persist failed:",
err instanceof Error ? err.message : err,
);
}
refreshDomEditSelectionFromPreview(domEditSelection); refreshDomEditSelectionFromPreview(domEditSelection);
}, },
[ [
@@ -1,16 +1,6 @@
import { useCallback, useEffect, useRef, useState } from "react"; import { useCallback, useEffect, useRef, useState } from "react";
import { usePlayerStore, liveTime } from "../player/store/playerStore"; import { usePlayerStore, liveTime } from "../player/store/playerStore";
// `import.meta.env` may be undefined in non-Vite bundlers (Next.js Turbopack),
// so guard the access like the telemetry client does.
function isDevBuild(): boolean {
try {
return import.meta.env.DEV === true;
} catch {
return false;
}
}
export interface GestureSample { export interface GestureSample {
time: number; time: number;
properties: Record<string, number>; properties: Record<string, number>;
@@ -385,13 +375,9 @@ export function useGestureRecording() {
if (r.runtime) { if (r.runtime) {
try { try {
applyRuntimePreview(r.runtime, time, properties); applyRuntimePreview(r.runtime, time, properties);
} catch (err) { } catch {
// Preview failed — disable it for the rest of the gesture (recording // Preview failed — disable it for the rest of the gesture (recording
// continues). Surface in dev so a dead preview isn't silent; `r.runtime` // continues). `r.runtime` is nulled so we don't retry on every frame.
// is nulled below so this warns at most once per gesture.
if (isDevBuild()) {
console.warn("[GR] live preview disabled — runtime threw:", err);
}
r.runtime = null; r.runtime = null;
} }
} }
@@ -252,11 +252,6 @@ export const Player = forwardRef<HTMLIFrameElement, PlayerProps>(
if (assetPollRef.current) clearInterval(assetPollRef.current); if (assetPollRef.current) clearInterval(assetPollRef.current);
assetPollRef.current = null; assetPollRef.current = null;
setAssetsLoading(false); setAssetsLoading(false);
if (lastUnloaded) {
console.debug(
"[Player] Asset-loading overlay timed out after 10s; hiding anyway. Check network or asset integrity.",
);
}
} }
}, 100); }, 100);
} else { } else {
@@ -39,7 +39,7 @@ const ICONS: Record<string, ReactNode> = {
}; };
export function getTrackStyle(tag: string): TrackVisualStyle { export function getTrackStyle(tag: string): TrackVisualStyle {
if (!tag) console.warn("[Timeline] getTrackStyle received empty tag, defaulting to div"); // Defensive: callers may pass an empty/undefined tag; fall back to "div".
const safeTag = tag || "div"; const safeTag = tag || "div";
const trackStyle = getTimelineTrackStyle(safeTag); const trackStyle = getTimelineTrackStyle(safeTag);
const normalized = safeTag.toLowerCase(); const normalized = safeTag.toLowerCase();
@@ -199,8 +199,7 @@ export function useTimelinePlayer() {
} }
return bestAdapter; return bestAdapter;
} catch (err) { } catch {
console.warn("[useTimelinePlayer] Could not get playback adapter (cross-origin)", err);
return null; return null;
} }
}, []); }, []);
@@ -264,9 +263,7 @@ export function useTimelinePlayer() {
} }
} }
} }
} catch (err) { } catch {}
console.warn("[useTimelinePlayer] Could not set playback rate (cross-origin)", err);
}
}, []); }, []);
const applyPreviewAudioState = useCallback((playbackRateOverride?: number) => { const applyPreviewAudioState = useCallback((playbackRateOverride?: number) => {
const { audioMuted, playbackRate } = usePlayerStore.getState(); const { audioMuted, playbackRate } = usePlayerStore.getState();
@@ -506,9 +503,7 @@ export function useTimelinePlayer() {
if (msSinceTimeline > 500) { if (msSinceTimeline > 500) {
enrichMissingCompositionsRef.current(); enrichMissingCompositionsRef.current();
} }
} catch (err) { } catch {}
console.warn("[useTimelinePlayer] Could not read clip manifest from iframe", err);
}
} }
if (data?.source === "hf-preview" && data?.type === "timeline" && Array.isArray(data.clips)) { if (data?.source === "hf-preview" && data?.type === "timeline" && Array.isArray(data.clips)) {
lastTimelineMessageRef.current = Date.now(); lastTimelineMessageRef.current = Date.now();
@@ -524,12 +519,7 @@ export function useTimelinePlayer() {
syncTimelineElements(els); syncTimelineElements(els);
} }
} }
} catch (err) { } catch {}
console.warn(
"[useTimelinePlayer] Could not read timeline elements on navigate (cross-origin)",
err,
);
}
} }
} }
}; };
@@ -164,9 +164,7 @@ export function useTimelineSyncCallbacks({
const dedupedMissing = missing.filter((m) => !finalIds.has(m.id)); const dedupedMissing = missing.filter((m) => !finalIds.has(m.id));
syncTimelineElements([...updatedEls, ...dedupedMissing]); syncTimelineElements([...updatedEls, ...dedupedMissing]);
} }
} catch (err) { } catch {}
console.warn("[useTimelinePlayer] enrichMissingCompositions failed", err);
}
}, [iframeRef, syncTimelineElements]); }, [iframeRef, syncTimelineElements]);
const initializeAdapter = useCallback(() => { const initializeAdapter = useCallback(() => {
@@ -241,9 +239,7 @@ export function useTimelineSyncCallbacks({
if (fallbackElement) syncTimelineElements([fallbackElement]); if (fallbackElement) syncTimelineElements([fallbackElement]);
} }
} }
} catch (err) { } catch {}
console.warn("[useTimelinePlayer] Could not read timeline elements from iframe", err);
}
return true; return true;
}, [ }, [
getAdapter, getAdapter,
@@ -295,9 +291,6 @@ export function useTimelineSyncCallbacks({
probeIntervalRef.current = setTimeout(() => { probeIntervalRef.current = setTimeout(() => {
if (!settled) { if (!settled) {
trySettle(); trySettle();
if (!settled) {
console.warn("[useTimelinePlayer] Runtime did not signal readiness within 5s");
}
} }
window.removeEventListener("message", onMessage); window.removeEventListener("message", onMessage);
}, 5000) as unknown as ReturnType<typeof setInterval>; }, 5000) as unknown as ReturnType<typeof setInterval>;
@@ -121,9 +121,7 @@ export function setPreviewMediaMuted(iframe: HTMLIFrameElement | null, muted: bo
return; return;
} }
postPreviewControl(iframe, "set-muted", { muted }); postPreviewControl(iframe, "set-muted", { muted });
} catch (err) { } catch {}
console.warn("[useTimelinePlayer] Failed to set preview media mute state", err);
}
} }
export function setPreviewPlaybackRate( export function setPreviewPlaybackRate(
@@ -139,9 +137,7 @@ export function setPreviewPlaybackRate(
return; return;
} }
postPreviewControl(iframe, "set-playback-rate", { playbackRate: rate }); postPreviewControl(iframe, "set-playback-rate", { playbackRate: rate });
} catch (err) { } catch {}
console.warn("[useTimelinePlayer] Failed to set preview playback rate", err);
}
} }
/** /**
+2
View File
@@ -129,6 +129,8 @@ function send(url: string, payload: string): void {
function showNoticeOnce(): void { function showNoticeOnce(): void {
if (hasShownNotice()) return; if (hasShownNotice()) return;
markNoticeShown(); markNoticeShown();
// Intentional one-time consent disclosure (not debug noise): tells users
// anonymous analytics are on and how to opt out. Kept behind a pragma.
// eslint-disable-next-line no-console // eslint-disable-next-line no-console
console.info( console.info(
"%c[HyperFrames]%c Anonymous studio usage analytics enabled. " + "%c[HyperFrames]%c Anonymous studio usage analytics enabled. " +
+3 -10
View File
@@ -3,14 +3,7 @@
// `window.__hfDebug = true` in the console. Single `[hf-edit:<scope>]` prefix so // `window.__hfDebug = true` in the console. Single `[hf-edit:<scope>]` prefix so
// the whole edit pipeline is greppable. Fires only at commit boundaries (user // the whole edit pipeline is greppable. Fires only at commit boundaries (user
// actions), never in render/raf loops, so it doesn't spam. // actions), never in render/raf loops, so it doesn't spam.
export function editLog(scope: string, ...args: unknown[]): void { export function editLog(_scope: string, ..._args: unknown[]): void {
if (typeof window === "undefined") return; // ponytail: body removed — all console.* stripped from studio.
const w = window as unknown as { __hfDebug?: boolean }; // Restore with: console.log(`[hf-edit:${_scope}]`, ..._args);
if (!import.meta.env.DEV && !w.__hfDebug) return;
// Stringify object args so the console prints their contents inline (`{x:1}`)
// instead of a collapsed `Object` — keeps the edit trail greppable/copyable.
const parts = args.map((a) =>
typeof a === "object" && a !== null ? JSON.stringify(a) : String(a),
);
console.debug(`[hf-edit:${scope}]`, ...parts);
} }
@@ -11,8 +11,7 @@ export async function executeOptimistic<T>(options: OptimisticUpdateOptions<T>):
const snapshot = options.apply(); const snapshot = options.apply();
try { try {
await options.persist(); await options.persist();
} catch (error) { } catch {
options.rollback(snapshot); options.rollback(snapshot);
console.warn("[optimistic] Mutation failed, rolled back:", error);
} }
} }
@@ -239,16 +239,6 @@ function execDataAttrPattern(html: string, attr: string, value: string): TagMatc
const pattern = new RegExp(`(<[^>]*\\b${attr}=(["'])${escapeRegex(value)}\\2[^>]*)>`, "i"); const pattern = new RegExp(`(<[^>]*\\b${attr}=(["'])${escapeRegex(value)}\\2[^>]*)>`, "i");
const match = pattern.exec(html); const match = pattern.exec(html);
if (match?.index == null) return null; if (match?.index == null) return null;
// Defensive: a second exact match means a duplicate id/attr in the source
// (id drift). Don't silently patch the first while leaving the other stale —
// surface it. By the mint contract this should never fire.
const all = html.match(new RegExp(`<[^>]*\\b${attr}=(["'])${escapeRegex(value)}\\1[^>]*>`, "gi"));
if (all && all.length > 1) {
// eslint-disable-next-line no-console
console.warn(
`sourcePatcher: ${attr}="${value}" matched ${all.length} elements; patching the first. ids/attrs must be unique per document.`,
);
}
return { tag: match[1], start: match.index, end: match.index + match[1].length }; return { tag: match[1], start: match.index, end: match.index + match[1].length };
} }