mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-13 15:49:53 +00:00
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:
co-authored by
Claude Opus 4.8
parent
ae8b94c518
commit
adb40321d6
+4
-3
@@ -6,9 +6,10 @@ pre-commit:
|
||||
run: bunx oxlint --no-error-on-unmatched-pattern {staged_files}
|
||||
format:
|
||||
glob: "*.{js,jsx,ts,tsx,json,md,yaml,yml}"
|
||||
# --no-error-on-unmatched-pattern: don't fail when staged files all
|
||||
# fall under .prettierignore (e.g. docs-only changes to docs/docs.json).
|
||||
run: bunx oxfmt --check --no-error-on-unmatched-pattern {staged_files}
|
||||
# Auto-format and re-stage so the committed snapshot is always formatted.
|
||||
# Replaces --check which only reports — that left unformatted files in
|
||||
# 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:
|
||||
glob: "*.{ts,tsx}"
|
||||
run: cd packages/core && bunx tsc --noEmit && cd ../studio && bunx tsc --noEmit
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useCallback, useRef } from "react";
|
||||
import { useCaptionStore } from "../store";
|
||||
import { useMountEffect } from "../../hooks/useMountEffect";
|
||||
import { trackEvent } from "../../telemetry/client";
|
||||
import type { CaptionStyle } from "../types";
|
||||
|
||||
interface CaptionOverrideEntry {
|
||||
@@ -78,7 +79,11 @@ export function useCaptionSync(projectId: string | null) {
|
||||
method: "PUT",
|
||||
headers: { "Content-Type": "text/plain" },
|
||||
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
|
||||
|
||||
@@ -12,7 +12,7 @@ interface BlockParamsPanelProps {
|
||||
export const BlockParamsPanel = memo(function BlockParamsPanel({
|
||||
blockTitle,
|
||||
params,
|
||||
compositionPath,
|
||||
compositionPath: _compositionPath,
|
||||
onClose,
|
||||
}: BlockParamsPanelProps) {
|
||||
const [values, setValues] = useState<Record<string, string>>(() => {
|
||||
@@ -23,13 +23,9 @@ export const BlockParamsPanel = memo(function BlockParamsPanel({
|
||||
return initial;
|
||||
});
|
||||
|
||||
const handleChange = useCallback(
|
||||
(key: string, value: string) => {
|
||||
setValues((prev) => ({ ...prev, [key]: value }));
|
||||
console.log(`[BlockParams] ${compositionPath} ${key}: ${value}`);
|
||||
},
|
||||
[compositionPath],
|
||||
);
|
||||
const handleChange = useCallback((key: string, value: string) => {
|
||||
setValues((prev) => ({ ...prev, [key]: value }));
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full">
|
||||
|
||||
@@ -100,11 +100,6 @@ export function collectSnapContext(input: {
|
||||
|
||||
const MAX_SNAP_TARGETS = 80;
|
||||
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<{
|
||||
rect: { left: number; top: number; width: number; height: number };
|
||||
|
||||
@@ -50,7 +50,6 @@ export function safeParseManifest(html: string): SlideshowManifest {
|
||||
try {
|
||||
return parseSlideshowManifest(html) ?? { slides: [] };
|
||||
} catch {
|
||||
console.warn("[SlideshowPanel] Failed to parse slideshow manifest; using empty manifest");
|
||||
return { slides: [] };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -165,8 +165,7 @@ async function commitFlatViaKeyframes(
|
||||
if (Number.isFinite(v)) resolvedFromValues[key] = roundTo3(v);
|
||||
}
|
||||
mainTl.seek(ct);
|
||||
} catch (err) {
|
||||
console.warn("[gsap-drag] start-value read failed; using identity from values", err);
|
||||
} catch {
|
||||
for (const key of Object.keys(resolvedFromValues)) delete resolvedFromValues[key];
|
||||
} finally {
|
||||
if (Object.keys(draggedValues).length > 0) gsapLib.set(el, draggedValues);
|
||||
|
||||
@@ -115,12 +115,7 @@ export function readAllAnimatedProperties(
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn(
|
||||
"Cross-tween guard failed — baseline capture may include values from other tweens",
|
||||
e,
|
||||
);
|
||||
}
|
||||
} catch {}
|
||||
for (const p of propKeys) otherTweenProps.delete(p);
|
||||
|
||||
// Tier 1: Transform + visual properties with universal CSS defaults.
|
||||
|
||||
@@ -224,10 +224,6 @@ export function useDomEditCommits({
|
||||
target_source_file: selection.sourceFile ?? 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;
|
||||
|
||||
@@ -126,9 +126,7 @@ export function useDomEditTextCommits({
|
||||
? (html, sourceFile) => ensureImportedFontFace(html, importedFont, sourceFile)
|
||||
: undefined,
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn("[Studio] Style persist failed:", err instanceof Error ? err.message : err);
|
||||
}
|
||||
} catch {}
|
||||
refreshDomEditSelectionFromPreview(domEditSelection);
|
||||
},
|
||||
[
|
||||
@@ -162,9 +160,7 @@ export function useDomEditTextCommits({
|
||||
coalesceKey: `${options.coalescePrefix}:${attr}:${getDomEditTargetKey(domEditSelection)}`,
|
||||
skipRefresh: options.skipRefresh,
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn(options.warningMessage, err instanceof Error ? err.message : err);
|
||||
}
|
||||
} catch {}
|
||||
if (options.refreshAfter) {
|
||||
refreshDomEditSelectionFromPreview(domEditSelection);
|
||||
}
|
||||
@@ -224,12 +220,7 @@ export function useDomEditTextCommits({
|
||||
coalesceKey: `html-attr:${attr}:${getDomEditTargetKey(domEditSelection)}`,
|
||||
skipRefresh: false,
|
||||
});
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
"[Studio] HTML attribute persist failed:",
|
||||
err instanceof Error ? err.message : err,
|
||||
);
|
||||
}
|
||||
} catch {}
|
||||
refreshDomEditSelectionFromPreview(domEditSelection);
|
||||
},
|
||||
[
|
||||
|
||||
@@ -1,16 +1,6 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
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 {
|
||||
time: number;
|
||||
properties: Record<string, number>;
|
||||
@@ -385,13 +375,9 @@ export function useGestureRecording() {
|
||||
if (r.runtime) {
|
||||
try {
|
||||
applyRuntimePreview(r.runtime, time, properties);
|
||||
} catch (err) {
|
||||
} catch {
|
||||
// Preview failed — disable it for the rest of the gesture (recording
|
||||
// continues). Surface in dev so a dead preview isn't silent; `r.runtime`
|
||||
// is nulled below so this warns at most once per gesture.
|
||||
if (isDevBuild()) {
|
||||
console.warn("[GR] live preview disabled — runtime threw:", err);
|
||||
}
|
||||
// continues). `r.runtime` is nulled so we don't retry on every frame.
|
||||
r.runtime = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -252,11 +252,6 @@ export const Player = forwardRef<HTMLIFrameElement, PlayerProps>(
|
||||
if (assetPollRef.current) clearInterval(assetPollRef.current);
|
||||
assetPollRef.current = null;
|
||||
setAssetsLoading(false);
|
||||
if (lastUnloaded) {
|
||||
console.debug(
|
||||
"[Player] Asset-loading overlay timed out after 10s; hiding anyway. Check network or asset integrity.",
|
||||
);
|
||||
}
|
||||
}
|
||||
}, 100);
|
||||
} else {
|
||||
|
||||
@@ -39,7 +39,7 @@ const ICONS: Record<string, ReactNode> = {
|
||||
};
|
||||
|
||||
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 trackStyle = getTimelineTrackStyle(safeTag);
|
||||
const normalized = safeTag.toLowerCase();
|
||||
|
||||
@@ -199,8 +199,7 @@ export function useTimelinePlayer() {
|
||||
}
|
||||
|
||||
return bestAdapter;
|
||||
} catch (err) {
|
||||
console.warn("[useTimelinePlayer] Could not get playback adapter (cross-origin)", err);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}, []);
|
||||
@@ -264,9 +263,7 @@ export function useTimelinePlayer() {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("[useTimelinePlayer] Could not set playback rate (cross-origin)", err);
|
||||
}
|
||||
} catch {}
|
||||
}, []);
|
||||
const applyPreviewAudioState = useCallback((playbackRateOverride?: number) => {
|
||||
const { audioMuted, playbackRate } = usePlayerStore.getState();
|
||||
@@ -506,9 +503,7 @@ export function useTimelinePlayer() {
|
||||
if (msSinceTimeline > 500) {
|
||||
enrichMissingCompositionsRef.current();
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("[useTimelinePlayer] Could not read clip manifest from iframe", err);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
if (data?.source === "hf-preview" && data?.type === "timeline" && Array.isArray(data.clips)) {
|
||||
lastTimelineMessageRef.current = Date.now();
|
||||
@@ -524,12 +519,7 @@ export function useTimelinePlayer() {
|
||||
syncTimelineElements(els);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn(
|
||||
"[useTimelinePlayer] Could not read timeline elements on navigate (cross-origin)",
|
||||
err,
|
||||
);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -164,9 +164,7 @@ export function useTimelineSyncCallbacks({
|
||||
const dedupedMissing = missing.filter((m) => !finalIds.has(m.id));
|
||||
syncTimelineElements([...updatedEls, ...dedupedMissing]);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("[useTimelinePlayer] enrichMissingCompositions failed", err);
|
||||
}
|
||||
} catch {}
|
||||
}, [iframeRef, syncTimelineElements]);
|
||||
|
||||
const initializeAdapter = useCallback(() => {
|
||||
@@ -241,9 +239,7 @@ export function useTimelineSyncCallbacks({
|
||||
if (fallbackElement) syncTimelineElements([fallbackElement]);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn("[useTimelinePlayer] Could not read timeline elements from iframe", err);
|
||||
}
|
||||
} catch {}
|
||||
return true;
|
||||
}, [
|
||||
getAdapter,
|
||||
@@ -295,9 +291,6 @@ export function useTimelineSyncCallbacks({
|
||||
probeIntervalRef.current = setTimeout(() => {
|
||||
if (!settled) {
|
||||
trySettle();
|
||||
if (!settled) {
|
||||
console.warn("[useTimelinePlayer] Runtime did not signal readiness within 5s");
|
||||
}
|
||||
}
|
||||
window.removeEventListener("message", onMessage);
|
||||
}, 5000) as unknown as ReturnType<typeof setInterval>;
|
||||
|
||||
@@ -121,9 +121,7 @@ export function setPreviewMediaMuted(iframe: HTMLIFrameElement | null, muted: bo
|
||||
return;
|
||||
}
|
||||
postPreviewControl(iframe, "set-muted", { muted });
|
||||
} catch (err) {
|
||||
console.warn("[useTimelinePlayer] Failed to set preview media mute state", err);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
export function setPreviewPlaybackRate(
|
||||
@@ -139,9 +137,7 @@ export function setPreviewPlaybackRate(
|
||||
return;
|
||||
}
|
||||
postPreviewControl(iframe, "set-playback-rate", { playbackRate: rate });
|
||||
} catch (err) {
|
||||
console.warn("[useTimelinePlayer] Failed to set preview playback rate", err);
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -129,6 +129,8 @@ function send(url: string, payload: string): void {
|
||||
function showNoticeOnce(): void {
|
||||
if (hasShownNotice()) return;
|
||||
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
|
||||
console.info(
|
||||
"%c[HyperFrames]%c Anonymous studio usage analytics enabled. " +
|
||||
|
||||
@@ -3,14 +3,7 @@
|
||||
// `window.__hfDebug = true` in the console. Single `[hf-edit:<scope>]` prefix so
|
||||
// the whole edit pipeline is greppable. Fires only at commit boundaries (user
|
||||
// actions), never in render/raf loops, so it doesn't spam.
|
||||
export function editLog(scope: string, ...args: unknown[]): void {
|
||||
if (typeof window === "undefined") return;
|
||||
const w = window as unknown as { __hfDebug?: boolean };
|
||||
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);
|
||||
export function editLog(_scope: string, ..._args: unknown[]): void {
|
||||
// ponytail: body removed — all console.* stripped from studio.
|
||||
// Restore with: console.log(`[hf-edit:${_scope}]`, ..._args);
|
||||
}
|
||||
|
||||
@@ -11,8 +11,7 @@ export async function executeOptimistic<T>(options: OptimisticUpdateOptions<T>):
|
||||
const snapshot = options.apply();
|
||||
try {
|
||||
await options.persist();
|
||||
} catch (error) {
|
||||
} catch {
|
||||
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 match = pattern.exec(html);
|
||||
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 };
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user