@hyperframes/studio 0.7.107 → 0.7.108
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{hyperframes-player-BLrfwq5o.js → hyperframes-player-QqavTWRK.js} +1 -1
- package/dist/assets/{index-BA9yhzfR.js → index-BQjXA1pz.js} +1 -1
- package/dist/assets/{index-BSBk5srs.js → index-BhHHMprP.js} +1 -1
- package/dist/assets/index-DNkh9mbV.css +1 -0
- package/dist/assets/index-GqONLcOl.js +428 -0
- package/dist/index.d.ts +85 -2
- package/dist/index.html +2 -2
- package/dist/index.js +17696 -12512
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/components/StudioRightPanel.tsx +18 -1
- package/src/components/TimelineToolbar.test.tsx +46 -0
- package/src/components/TimelineToolbar.tsx +17 -0
- package/src/components/editor/PropertyPanel.test.tsx +5 -1
- package/src/components/editor/PropertyPanel.tsx +7 -23
- package/src/components/editor/PropertyPanelFlat.tsx +25 -0
- package/src/components/editor/audioFxSummary.test.ts +75 -0
- package/src/components/editor/audioFxSummary.ts +36 -0
- package/src/components/editor/audioFxTelemetry.test.ts +129 -0
- package/src/components/editor/audioFxTelemetry.ts +230 -0
- package/src/components/editor/propertyPanelAudioFxGroup.test.tsx +1830 -0
- package/src/components/editor/propertyPanelAudioFxGroup.tsx +299 -0
- package/src/components/editor/propertyPanelAudioFxGroupUtils.ts +21 -0
- package/src/components/editor/propertyPanelAutomation.test.ts +60 -0
- package/src/components/editor/propertyPanelAutomation.ts +97 -0
- package/src/components/editor/propertyPanelFlatMediaSection.tsx +37 -9
- package/src/components/editor/propertyPanelFlatProps.ts +1 -0
- package/src/components/editor/propertyPanelFxAddMenu.tsx +209 -0
- package/src/components/editor/propertyPanelFxBandRuler.tsx +72 -0
- package/src/components/editor/propertyPanelFxCarveModule.tsx +382 -0
- package/src/components/editor/propertyPanelFxControls.test.tsx +115 -0
- package/src/components/editor/propertyPanelFxControls.tsx +356 -0
- package/src/components/editor/propertyPanelFxEqModule.tsx +196 -0
- package/src/components/editor/propertyPanelFxFamily.ts +83 -0
- package/src/components/editor/propertyPanelFxNodeOpenBody.tsx +269 -0
- package/src/components/editor/propertyPanelFxNodeParams.tsx +112 -0
- package/src/components/editor/propertyPanelFxNodeRow.tsx +339 -0
- package/src/components/editor/propertyPanelFxPresetMenu.tsx +159 -0
- package/src/components/editor/propertyPanelFxPresetRun.tsx +215 -0
- package/src/components/editor/propertyPanelFxPresetStyle.test.ts +142 -0
- package/src/components/editor/propertyPanelFxPresetStyle.ts +216 -0
- package/src/components/editor/propertyPanelFxRackChain.tsx +202 -0
- package/src/components/editor/propertyPanelFxSection.test.tsx +1747 -0
- package/src/components/editor/propertyPanelFxSection.tsx +550 -0
- package/src/components/editor/propertyPanelFxSectionTypes.ts +91 -0
- package/src/components/editor/propertyPanelTypes.ts +5 -0
- package/src/components/editor/useFxAudition.ts +95 -0
- package/src/components/editor/useFxCarve.ts +0 -0
- package/src/components/editor/useFxChainObserved.ts +83 -0
- package/src/components/editor/useFxLevelling.ts +235 -0
- package/src/components/editor/useVolumeAutomation.test.tsx +122 -0
- package/src/components/editor/useVolumeAutomation.ts +54 -0
- package/src/contexts/DomEditContext.tsx +11 -0
- package/src/hooks/useAppHotkeys.test.ts +198 -0
- package/src/hooks/useAppHotkeys.ts +30 -3
- package/src/hooks/useAutomationSelectionKeyboard.test.tsx +429 -0
- package/src/hooks/useAutomationSelectionKeyboard.ts +371 -0
- package/src/hooks/useDomEditAttributeCommits.ts +90 -10
- package/src/hooks/useDomEditCommits.test.tsx +38 -0
- package/src/hooks/useDomEditCommits.ts +2 -0
- package/src/hooks/useDomEditSession.ts +2 -0
- package/src/hooks/useDomEditTextCommits.ts +2 -0
- package/src/hooks/useElementLifecycleOps.ts +3 -0
- package/src/hooks/useLivePlayheadTime.ts +49 -0
- package/src/hooks/usePreviewPersistence.ts +7 -0
- package/src/hooks/useTimelineEditing.ts +3 -0
- package/src/player/components/AutomationSelectionMenu.tsx +76 -0
- package/src/player/components/AutomationValueInput.tsx +51 -0
- package/src/player/components/LayerDisclosureRow.tsx +5 -4
- package/src/player/components/Timeline.test.ts +84 -0
- package/src/player/components/TimelineAutomationLane.test.tsx +1720 -0
- package/src/player/components/TimelineAutomationLane.tsx +674 -0
- package/src/player/components/TimelineAutomationLaneSlot.test.tsx +193 -0
- package/src/player/components/TimelineLanes.test.tsx +24 -5
- package/src/player/components/TimelineLanes.tsx +142 -154
- package/src/player/components/TimelineTrackHeader.test.tsx +212 -1
- package/src/player/components/TimelineTrackHeader.tsx +166 -6
- package/src/player/components/TimelineTrackRow.tsx +8 -1
- package/src/player/components/automationClipboard.test.ts +94 -0
- package/src/player/components/automationClipboard.ts +136 -0
- package/src/player/components/automationGestureKeys.test.ts +49 -0
- package/src/player/components/automationGestureKeys.ts +49 -0
- package/src/player/components/automationLaneData.test.ts +270 -0
- package/src/player/components/automationLaneData.ts +258 -0
- package/src/player/components/automationLaneDragMath.ts +194 -0
- package/src/player/components/automationLaneGeometry.test.ts +239 -0
- package/src/player/components/automationLaneGeometry.ts +287 -0
- package/src/player/components/automationLaneHeight.ts +15 -0
- package/src/player/components/automationLanePointer.ts +20 -0
- package/src/player/components/automationLaneSelection.test.ts +183 -0
- package/src/player/components/automationLaneSelection.ts +135 -0
- package/src/player/components/automationShapes.test.ts +82 -0
- package/src/player/components/automationShapes.ts +70 -0
- package/src/player/components/automationSimplify.test.ts +48 -0
- package/src/player/components/automationSimplify.ts +51 -0
- package/src/player/components/timelineClipGestureHandlers.ts +223 -0
- package/src/player/components/timelineLaneProps.ts +19 -0
- package/src/player/components/timelineLayout.ts +11 -5
- package/src/player/components/useAutomationEdgeStretch.test.ts +27 -0
- package/src/player/components/useAutomationEdgeStretch.ts +269 -0
- package/src/player/components/useAutomationLaneGestures.ts +537 -0
- package/src/player/components/useAutomationLanes.test.tsx +112 -0
- package/src/player/components/useAutomationLanes.ts +170 -0
- package/src/player/components/useAutomationRangeDrag.ts +114 -0
- package/src/player/components/useTimelineTrackLayout.test.ts +107 -1
- package/src/player/components/useTimelineTrackLayout.ts +83 -7
- package/src/player/hooks/useExpandedTimelineElements.ts +7 -0
- package/src/player/hooks/useTimelinePlayer.ts +13 -1
- package/src/player/lib/automationStoreSync.test.ts +83 -0
- package/src/player/lib/automationStoreSync.ts +56 -0
- package/src/player/lib/timelineDOM.test.ts +33 -0
- package/src/player/lib/timelineDOM.ts +12 -0
- package/src/player/store/automationSelectionSlice.test.ts +30 -0
- package/src/player/store/automationSelectionSlice.ts +54 -0
- package/src/player/store/playerStore.test.ts +15 -0
- package/src/player/store/playerStore.ts +54 -65
- package/src/player/store/timelineElement.ts +78 -0
- package/src/styles/studio.css +89 -0
- package/src/telemetry/agentRuntime.test.ts +45 -0
- package/src/telemetry/agentRuntime.ts +62 -0
- package/src/telemetry/system.ts +8 -0
- package/src/utils/studioFileHistory.test.ts +49 -0
- package/src/utils/studioFileHistory.ts +17 -1
- package/src/utils/studioTelemetry.ts +16 -10
- package/dist/assets/index-dF-CmLZu.js +0 -428
- package/dist/assets/index-zQ4JFwwB.css +0 -1
package/src/telemetry/system.ts
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
// No PII — only environment characteristics useful for product analytics.
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
6
6
|
|
|
7
|
+
import { agentRuntimeProperty } from "./agentRuntime";
|
|
8
|
+
|
|
7
9
|
export interface BrowserSystemMeta {
|
|
8
10
|
user_agent: string;
|
|
9
11
|
language: string;
|
|
@@ -13,6 +15,8 @@ export interface BrowserSystemMeta {
|
|
|
13
15
|
timezone_offset_minutes: number;
|
|
14
16
|
is_mobile: boolean;
|
|
15
17
|
studio_version: string;
|
|
18
|
+
/** Which coding agent is driving this Studio, or null when a person is. */
|
|
19
|
+
agent_runtime: string;
|
|
16
20
|
}
|
|
17
21
|
|
|
18
22
|
const EMPTY_META: BrowserSystemMeta = {
|
|
@@ -24,6 +28,7 @@ const EMPTY_META: BrowserSystemMeta = {
|
|
|
24
28
|
timezone_offset_minutes: 0,
|
|
25
29
|
is_mobile: false,
|
|
26
30
|
studio_version: "dev",
|
|
31
|
+
agent_runtime: "none",
|
|
27
32
|
};
|
|
28
33
|
|
|
29
34
|
let cached: BrowserSystemMeta | null = null;
|
|
@@ -46,6 +51,9 @@ export function getBrowserSystemMeta(): BrowserSystemMeta {
|
|
|
46
51
|
timezone_offset_minutes: new Date().getTimezoneOffset(),
|
|
47
52
|
is_mobile: /Android|iPhone|iPad/i.test(ua),
|
|
48
53
|
studio_version: typeof __STUDIO_VERSION__ !== "undefined" ? __STUDIO_VERSION__ : "dev",
|
|
54
|
+
// Same value the `studio:*` transport attaches, from the same accessor —
|
|
55
|
+
// two families that disagreed here would split every agent breakdown.
|
|
56
|
+
agent_runtime: agentRuntimeProperty(),
|
|
49
57
|
};
|
|
50
58
|
return cached;
|
|
51
59
|
}
|
|
@@ -29,6 +29,55 @@ describe("saveProjectFilesWithHistory", () => {
|
|
|
29
29
|
});
|
|
30
30
|
});
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Deleting a clip POSTs `remove-element`, which rewrites the file server-side,
|
|
34
|
+
* and only then saves the duration shrink. Expecting the content read before
|
|
35
|
+
* the mutation made the server refuse that write as a conflict: the save queue
|
|
36
|
+
* paused on the 409 and the clip stayed on the timeline until a reload.
|
|
37
|
+
*/
|
|
38
|
+
it("expects what is on disk, not the undo baseline, when they differ", async () => {
|
|
39
|
+
const expectations: Record<string, string | undefined> = {};
|
|
40
|
+
const recordEdit = vi.fn();
|
|
41
|
+
|
|
42
|
+
await saveProjectFilesWithHistory({
|
|
43
|
+
projectId: "project-1",
|
|
44
|
+
label: "Delete timeline clip",
|
|
45
|
+
kind: "timeline",
|
|
46
|
+
files: { "index.html": "removed+shrunk" },
|
|
47
|
+
readFile: async () => "original",
|
|
48
|
+
diskContent: { "index.html": "removed" },
|
|
49
|
+
writeFile: async (path, _content, expectedContent) => {
|
|
50
|
+
expectations[path] = expectedContent;
|
|
51
|
+
},
|
|
52
|
+
recordEdit,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
expect(expectations["index.html"]).toBe("removed");
|
|
56
|
+
// Undo still goes all the way back, which is the whole reason the two are
|
|
57
|
+
// allowed to differ.
|
|
58
|
+
expect(recordEdit).toHaveBeenCalledWith(
|
|
59
|
+
expect.objectContaining({
|
|
60
|
+
files: { "index.html": { before: "original", after: "removed+shrunk" } },
|
|
61
|
+
}),
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("still expects the undo baseline when nothing says otherwise", async () => {
|
|
66
|
+
const expectations: Record<string, string | undefined> = {};
|
|
67
|
+
await saveProjectFilesWithHistory({
|
|
68
|
+
projectId: "project-1",
|
|
69
|
+
label: "Move layer",
|
|
70
|
+
kind: "manual",
|
|
71
|
+
files: { "index.html": "after" },
|
|
72
|
+
readFile: async () => "before",
|
|
73
|
+
writeFile: async (path, _content, expectedContent) => {
|
|
74
|
+
expectations[path] = expectedContent;
|
|
75
|
+
},
|
|
76
|
+
recordEdit: vi.fn(),
|
|
77
|
+
});
|
|
78
|
+
expect(expectations["index.html"]).toBe("before");
|
|
79
|
+
});
|
|
80
|
+
|
|
32
81
|
it("skips writes and history for unchanged content", async () => {
|
|
33
82
|
const writeFile = vi.fn();
|
|
34
83
|
const recordEdit = vi.fn();
|
|
@@ -34,6 +34,21 @@ interface SaveProjectFilesWithHistoryInput {
|
|
|
34
34
|
readFile: (path: string) => Promise<string>;
|
|
35
35
|
writeFile: ProjectFileWriter;
|
|
36
36
|
recordEdit: (entry: RecordEditInput) => Promise<void>;
|
|
37
|
+
/**
|
|
38
|
+
* What a path holds ON DISK right now, when that is not the same as the
|
|
39
|
+
* history's "before".
|
|
40
|
+
*
|
|
41
|
+
* The two are normally one value, so the write's optimistic-concurrency
|
|
42
|
+
* expectation was taken straight from the undo baseline. They come apart when
|
|
43
|
+
* a server-side mutation has already written part of the edit: deleting a clip
|
|
44
|
+
* POSTs `remove-element`, which rewrites the file, and only then saves the
|
|
45
|
+
* duration shrink — expecting the pre-delete content it read at the start. The
|
|
46
|
+
* server had moved the file on, so the write was refused as a conflict, the
|
|
47
|
+
* save queue paused, and the clip stayed on the timeline until a reload.
|
|
48
|
+
*
|
|
49
|
+
* Undo still restores `before`; this only says what to expect on disk.
|
|
50
|
+
*/
|
|
51
|
+
diskContent?: Record<string, string>;
|
|
37
52
|
}
|
|
38
53
|
|
|
39
54
|
export async function readProjectFileContent(pid: string, path: string): Promise<string> {
|
|
@@ -57,6 +72,7 @@ export async function saveProjectFilesWithHistory({
|
|
|
57
72
|
readFile,
|
|
58
73
|
writeFile,
|
|
59
74
|
recordEdit,
|
|
75
|
+
diskContent,
|
|
60
76
|
}: SaveProjectFilesWithHistoryInput): Promise<string[]> {
|
|
61
77
|
return serializeStudioFileMutations(writeFile, Object.keys(files), async () => {
|
|
62
78
|
const snapshots: Record<string, { before: string; after: string }> = {};
|
|
@@ -73,7 +89,7 @@ export async function saveProjectFilesWithHistory({
|
|
|
73
89
|
const writtenPaths: string[] = [];
|
|
74
90
|
try {
|
|
75
91
|
for (const path of changedPaths) {
|
|
76
|
-
await writeFile(path, snapshots[path].after, snapshots[path].before);
|
|
92
|
+
await writeFile(path, snapshots[path].after, diskContent?.[path] ?? snapshots[path].before);
|
|
77
93
|
writtenPaths.push(path);
|
|
78
94
|
}
|
|
79
95
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { resolveStudioDistinctId } from "../telemetry/distinctId";
|
|
2
2
|
import { browserTelemetryAllowed } from "../telemetry/policy";
|
|
3
3
|
import { canaryEventProperties } from "../telemetry/canary";
|
|
4
|
+
import { agentRuntimeProperty } from "../telemetry/agentRuntime";
|
|
4
5
|
|
|
5
6
|
// PostHog public ingest key — write-only, safe to ship in the client bundle
|
|
6
7
|
const POSTHOG_API_KEY = "phc_zjjbX0PnWxERXrMHhkEJWj9A9BhGVLRReICgsfTMmpx";
|
|
@@ -42,6 +43,11 @@ function isEnabled(): boolean {
|
|
|
42
43
|
function getSessionProperties(): EventProperties {
|
|
43
44
|
return {
|
|
44
45
|
studio_version: typeof __STUDIO_VERSION__ !== "undefined" ? __STUDIO_VERSION__ : "dev",
|
|
46
|
+
// On EVERY event, not just the audio ones. "Which of these sessions was a
|
|
47
|
+
// person and which was an agent" is a question worth asking of any feature,
|
|
48
|
+
// and a property that only some events carry cannot answer it — the
|
|
49
|
+
// breakdown silently reads as though the agent never used the rest.
|
|
50
|
+
agent_runtime: agentRuntimeProperty(),
|
|
45
51
|
screen_width: window.screen?.width,
|
|
46
52
|
screen_height: window.screen?.height,
|
|
47
53
|
viewport_width: window.innerWidth,
|
|
@@ -73,9 +79,8 @@ export function trackStudioEvent(event: string, properties: EventProperties = {}
|
|
|
73
79
|
}
|
|
74
80
|
}
|
|
75
81
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
82
|
+
/** The queue, shaped for PostHog's batch endpoint — shared by both drain paths. */
|
|
83
|
+
function drainBatch() {
|
|
79
84
|
const batch = queue.map((e) => ({
|
|
80
85
|
event: e.event,
|
|
81
86
|
properties: { ...e.properties, $ip: null },
|
|
@@ -83,6 +88,13 @@ async function flushEvents(): Promise<void> {
|
|
|
83
88
|
timestamp: e.timestamp,
|
|
84
89
|
}));
|
|
85
90
|
queue = [];
|
|
91
|
+
return batch;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function flushEvents(): Promise<void> {
|
|
95
|
+
if (queue.length === 0) return;
|
|
96
|
+
|
|
97
|
+
const batch = drainBatch();
|
|
86
98
|
|
|
87
99
|
const controller = new AbortController();
|
|
88
100
|
const timeout = setTimeout(() => controller.abort(), FLUSH_TIMEOUT_MS);
|
|
@@ -112,13 +124,7 @@ export function flushViaBeacon(): void {
|
|
|
112
124
|
flushTimer = null;
|
|
113
125
|
}
|
|
114
126
|
if (queue.length === 0) return;
|
|
115
|
-
const batch =
|
|
116
|
-
event: e.event,
|
|
117
|
-
properties: { ...e.properties, $ip: null },
|
|
118
|
-
distinct_id: getDistinctId(),
|
|
119
|
-
timestamp: e.timestamp,
|
|
120
|
-
}));
|
|
121
|
-
queue = [];
|
|
127
|
+
const batch = drainBatch();
|
|
122
128
|
const body = JSON.stringify({ api_key: POSTHOG_API_KEY, batch });
|
|
123
129
|
try {
|
|
124
130
|
navigator.sendBeacon(`${POSTHOG_HOST}/batch/`, body);
|