ntvframes 0.8.21 → 0.8.22

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.
@@ -5,8 +5,8 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover" />
6
6
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
7
7
  <title>HyperFrames Studio</title>
8
- <script type="module" crossorigin src="/assets/index-CoBzmxvz.js"></script>
9
- <link rel="stylesheet" crossorigin href="/assets/index-D82tRaus.css">
8
+ <script type="module" crossorigin src="/assets/index-SzGOj1TJ.js"></script>
9
+ <link rel="stylesheet" crossorigin href="/assets/index-BMJ1qJAS.css">
10
10
  </head>
11
11
  <body>
12
12
  <div data-hf-id="hf-aph5" id="root"></div>
@@ -12613,9 +12613,10 @@ function DomEditProvider({
12613
12613
  copiedAgentPrompt,
12614
12614
  agentPromptSelectionContext,
12615
12615
  revisions,
12616
+ submission,
12616
12617
  remove,
12617
12618
  clear,
12618
- copyBatch,
12619
+ submitBatch,
12619
12620
  domEditSelectionRef,
12620
12621
  handleTimelineElementSelect,
12621
12622
  handlePreviewCanvasMouseDown,
@@ -12727,9 +12728,10 @@ function DomEditProvider({
12727
12728
  handleAskAgent,
12728
12729
  handleAgentModalSubmit,
12729
12730
  revisions,
12731
+ submission,
12730
12732
  remove,
12731
12733
  clear,
12732
- copyBatch,
12734
+ submitBatch,
12733
12735
  handleBlockedDomMove,
12734
12736
  handleDomManualDragStart,
12735
12737
  handleDomEditElementDelete,
@@ -12846,9 +12848,10 @@ function DomEditProvider({
12846
12848
  handleUpdateSegmentEase,
12847
12849
  handleSetAllKeyframeEases,
12848
12850
  revisions,
12851
+ submission,
12849
12852
  remove,
12850
12853
  clear,
12851
- copyBatch
12854
+ submitBatch
12852
12855
  ]
12853
12856
  );
12854
12857
  const selection = useMemo8(
@@ -28435,71 +28438,50 @@ function pauseStudioPreviewPlayback(iframe) {
28435
28438
  }
28436
28439
 
28437
28440
  // src/components/editor/domEditingAgentPrompt.ts
28438
- function formatBoundingBox(bounds) {
28439
- return `x=${Math.round(bounds.x)}, y=${Math.round(bounds.y)}, width=${Math.round(bounds.width)}, height=${Math.round(bounds.height)}`;
28440
- }
28441
- function formatStyleBlock(styles) {
28442
- return Object.entries(styles).filter(([, value]) => value && value !== "initial").map(([key2, value]) => `${key2}: ${value}`).join("\n");
28443
- }
28444
- function formatTextFields(fields) {
28445
- return fields.map(
28446
- (field) => `- key=${field.key}; tag=<${field.tagName}>; source=${field.source}; text=${JSON.stringify(field.value)}`
28447
- ).join("\n");
28441
+ function formatRelevantState(styles) {
28442
+ return [
28443
+ "position",
28444
+ "top",
28445
+ "left",
28446
+ "width",
28447
+ "height",
28448
+ "z-index",
28449
+ "font-family",
28450
+ "color"
28451
+ ].map((property) => [property, styles[property]]).filter(([, value]) => value && value !== "initial").map(([property, value]) => `- ${property}: ${value}`).join("\n");
28448
28452
  }
28449
28453
  function buildElementAgentPrompt({
28450
28454
  selection,
28451
- currentTime,
28452
- tagSnippet,
28453
- selectionContext,
28455
+ currentTime: _currentTime,
28456
+ tagSnippet: _tagSnippet,
28457
+ selectionContext: _selectionContext,
28454
28458
  userInstruction,
28455
- sourceFilePath
28459
+ sourceFilePath: _sourceFilePath
28456
28460
  }) {
28457
- const displayedSourceFile = sourceFilePath?.trim() || selection.sourceFile;
28461
+ const currentText = (selection.textContent ?? "").replace(/\s+/g, " ").trim();
28458
28462
  const lines = [
28459
- "## HyperFrames element edit request v1",
28460
- "Schema version: 1",
28461
- "",
28463
+ "User Request:",
28462
28464
  userInstruction?.trim() || "Edit this selected HyperFrames element.",
28463
28465
  "",
28464
- `Composition: ${selection.compositionPath}`,
28465
- `Playback time: ${formatTime(currentTime)}`,
28466
- `Source file: ${displayedSourceFile}`,
28467
- `DOM id: ${selection.id ?? "(none)"}`,
28468
- `Selector: ${selection.selector ?? "(none)"}`,
28469
- `Selector index: ${selection.selectorIndex ?? 0}`,
28470
- `Tag: <${selection.tagName}>`,
28471
- `Bounds: ${formatBoundingBox(selection.boundingBox)}`
28472
- ];
28473
- if (selection.textContent) {
28474
- lines.push(`Text: ${selection.textContent}`);
28475
- }
28476
- const trimmedSelectionContext = selectionContext?.trim();
28477
- if (trimmedSelectionContext) {
28478
- lines.push("", "Selection context:", trimmedSelectionContext);
28479
- }
28480
- const textFieldsBlock = formatTextFields(selection.textFields);
28481
- if (textFieldsBlock) {
28482
- lines.push("", "Text fields:", textFieldsBlock);
28483
- }
28484
- const inlineStyleBlock = formatStyleBlock(selection.inlineStyles);
28485
- if (inlineStyleBlock) {
28486
- lines.push("", "Inline styles:", inlineStyleBlock);
28487
- }
28488
- const computedStyleBlock = formatStyleBlock(selection.computedStyles);
28489
- if (computedStyleBlock) {
28490
- lines.push("", "Computed styles (browser-resolved):", computedStyleBlock);
28491
- }
28492
- if (tagSnippet) {
28493
- lines.push("", "Target HTML:", tagSnippet);
28494
- }
28495
- lines.push(
28466
+ "Target Element:",
28467
+ `- HyperFrames ID: ${selection.hfId ?? "(none)"}`,
28468
+ `- Selector: ${selection.selector ?? "(none)"}`,
28469
+ `- Selector index: ${selection.selectorIndex ?? 0}`,
28470
+ `- Tag: ${selection.tagName.toLowerCase()}`,
28471
+ `- Current text: ${JSON.stringify(currentText)}`,
28496
28472
  "",
28497
- "Guardrails:",
28498
- "- Make a targeted change to this element only.",
28473
+ "Relevant State:",
28474
+ formatRelevantState(selection.computedStyles),
28475
+ "",
28476
+ "Constraints:",
28477
+ "- Modify only the target element.",
28478
+ "- Preserve all properties and behavior not explicitly requested to change.",
28499
28479
  "- Preserve the rest of the composition and its timing.",
28500
- "- Do not modify other elements' data-* attributes or positioning.",
28501
- "- Prefer existing inline styles or existing CSS rules for this element over adding unrelated selectors."
28502
- );
28480
+ "- Do not modify other elements.",
28481
+ "- Do not modify other elements' data-* attributes.",
28482
+ "- Prefer existing CSS or inline styles for the target element.",
28483
+ "- Do not add unrelated CSS selectors."
28484
+ ];
28503
28485
  return lines.join("\n");
28504
28486
  }
28505
28487
  function buildAgentContextPreview(selection, activeCompPath) {
@@ -50863,7 +50845,7 @@ function PropertyPanelFlatFooter({
50863
50845
  children: /* @__PURE__ */ jsx117("path", { d: "M8 1l1.4 4.6L14 7l-4.6 1.4L8 13l-1.4-4.6L2 7l4.6-1.4z" })
50864
50846
  }
50865
50847
  ),
50866
- "Ask agent about this element"
50848
+ "Agent revisions"
50867
50849
  ]
50868
50850
  }
50869
50851
  ),
@@ -62365,15 +62347,20 @@ function AgentRevisionQueue({
62365
62347
  revisions,
62366
62348
  onRemove,
62367
62349
  onClear,
62368
- onCopyBatch
62350
+ onSubmitBatch,
62351
+ submission
62369
62352
  }) {
62370
62353
  const [open, setOpen] = useState97(false);
62371
- const [copyState, setCopyState] = useState97("idle");
62372
- if (!revisions.length) return null;
62373
- const copyBatch = async () => {
62374
- const copied = await onCopyBatch();
62375
- setCopyState(copied ? "copied" : "failed");
62376
- window.setTimeout(() => setCopyState("idle"), 2200);
62354
+ const [submitting, setSubmitting] = useState97(false);
62355
+ const [submitState, setSubmitState] = useState97("idle");
62356
+ const sessionActive = submission?.status === "queued" || submission?.status === "running";
62357
+ if (!revisions.length && !submission) return null;
62358
+ const submitBatch = async () => {
62359
+ setSubmitting(true);
62360
+ const submitted = await onSubmitBatch();
62361
+ setSubmitting(false);
62362
+ setSubmitState(submitted ? "submitted" : "failed");
62363
+ window.setTimeout(() => setSubmitState("idle"), 2200);
62377
62364
  };
62378
62365
  return /* @__PURE__ */ jsxs133(
62379
62366
  "section",
@@ -62392,23 +62379,27 @@ function AgentRevisionQueue({
62392
62379
  /* @__PURE__ */ jsx161("span", { className: "flex h-9 w-9 shrink-0 items-center justify-center rounded-xl bg-studio-accent/20 text-lg text-studio-accent", children: "\u2726" }),
62393
62380
  /* @__PURE__ */ jsxs133("span", { className: "min-w-0 flex-1", children: [
62394
62381
  /* @__PURE__ */ jsx161("span", { className: "block text-sm font-semibold leading-5 text-neutral-100", children: "Agent revisions" }),
62395
- /* @__PURE__ */ jsxs133("span", { className: "block text-xs leading-5 text-neutral-400", children: [
62396
- revisions.length,
62397
- " queued \xB7 ready for bulk handoff"
62398
- ] })
62382
+ /* @__PURE__ */ jsx161("span", { className: "block text-xs leading-5 text-neutral-400", children: sessionActive ? "Agent is working on your revisions" : `${revisions.length} queued \xB7 ready for submission` })
62399
62383
  ] }),
62400
62384
  /* @__PURE__ */ jsx161(
62401
62385
  "span",
62402
62386
  {
62403
62387
  "aria-hidden": "true",
62404
62388
  className: "flex h-7 w-7 items-center justify-center rounded-md text-base text-neutral-400",
62405
- children: open ? "\u2303" : "\u2304"
62389
+ children: /* @__PURE__ */ jsx161(
62390
+ ChevronRight,
62391
+ {
62392
+ size: 16,
62393
+ weight: "bold",
62394
+ className: open ? "rotate-90" : ""
62395
+ }
62396
+ )
62406
62397
  }
62407
62398
  )
62408
62399
  ]
62409
62400
  }
62410
62401
  ),
62411
- open && /* @__PURE__ */ jsxs133(Fragment46, { children: [
62402
+ open && revisions.length > 0 && /* @__PURE__ */ jsxs133(Fragment46, { children: [
62412
62403
  /* @__PURE__ */ jsx161("div", { className: "max-h-72 space-y-2.5 overflow-y-auto border-t border-neutral-800/80 px-4 py-4", children: revisions.map((revision, index) => /* @__PURE__ */ jsx161(
62413
62404
  RevisionRow,
62414
62405
  {
@@ -62432,13 +62423,20 @@ function AgentRevisionQueue({
62432
62423
  "button",
62433
62424
  {
62434
62425
  type: "button",
62435
- onClick: () => void copyBatch(),
62436
- className: "rounded-lg bg-studio-accent px-4 py-2.5 text-xs font-semibold text-neutral-950 hover:bg-red-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-studio-accent/70",
62437
- children: copyState === "copied" ? "Batch copied" : copyState === "failed" ? "Copy failed" : "Copy batch prompt for Pi"
62426
+ onClick: () => void submitBatch(),
62427
+ disabled: submitting || sessionActive,
62428
+ className: "rounded-lg bg-studio-accent px-4 py-2.5 text-xs font-semibold text-neutral-950 hover:bg-red-400 disabled:cursor-not-allowed disabled:opacity-60 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-studio-accent/70",
62429
+ children: submitting ? "Submitting\u2026" : submitState === "submitted" ? "Revisions submitted" : submitState === "failed" ? "Submission failed" : sessionActive ? "Agent working\u2026" : "Submit revisions"
62438
62430
  }
62439
62431
  )
62432
+ ] })
62433
+ ] }),
62434
+ open && submission && /* @__PURE__ */ jsxs133("div", { className: "border-t border-neutral-800/80 px-4 py-4 text-xs text-neutral-300", children: [
62435
+ /* @__PURE__ */ jsxs133("div", { className: "flex items-center gap-2", children: [
62436
+ sessionActive && /* @__PURE__ */ jsx161("span", { className: "h-3 w-3 animate-spin rounded-full border-2 border-neutral-600 border-t-studio-accent" }),
62437
+ /* @__PURE__ */ jsx161("span", { className: "font-medium text-neutral-100", children: submission.status === "queued" ? "Revision session queued" : submission.status === "running" ? "Agent is working\u2026" : submission.status === "completed" ? "Revisions completed" : "Revision session failed" })
62440
62438
  ] }),
62441
- /* @__PURE__ */ jsx161("p", { className: "px-4 pb-4 text-[11px] leading-5 text-neutral-500", children: "Direct Pi submission will be enabled when the Pi revision endpoint is connected." })
62439
+ (submission.message || submission.error) && /* @__PURE__ */ jsx161("p", { className: "mt-1.5 text-neutral-500", children: submission.message || submission.error })
62442
62440
  ] })
62443
62441
  ]
62444
62442
  }
@@ -62961,15 +62959,16 @@ function StudioOverlays({
62961
62959
  AgentRevisionQueue,
62962
62960
  {
62963
62961
  revisions,
62962
+ submission: domEditSession.submission,
62964
62963
  onRemove: domEditSession.remove,
62965
62964
  onClear: domEditSession.clear,
62966
- onCopyBatch: async () => {
62967
- const copied = await domEditSession.copyBatch();
62965
+ onSubmitBatch: async () => {
62966
+ const submitted = await domEditSession.submitBatch();
62968
62967
  showToast(
62969
- copied ? "Batch prompt copied for Pi." : "Could not copy batch prompt.",
62970
- copied ? "info" : "error"
62968
+ submitted ? "Revisions submitted to Pi." : "Could not submit revisions.",
62969
+ submitted ? "info" : "error"
62971
62970
  );
62972
- return copied;
62971
+ return submitted;
62973
62972
  }
62974
62973
  }
62975
62974
  ),
@@ -67071,9 +67070,13 @@ import { useState as useState107, useCallback as useCallback102, useEffect as us
67071
67070
  import { useCallback as useCallback101, useEffect as useEffect94, useRef as useRef119, useState as useState106 } from "react";
67072
67071
  var MAX_REVISIONS = 20;
67073
67072
  var STORAGE_PREFIX2 = "hyperframes-studio:agent-revisions:";
67073
+ var SUBMISSION_STORAGE_PREFIX = "hyperframes-studio:agent-revision-submission:";
67074
67074
  function storageKey(projectId) {
67075
67075
  return projectId ? `${STORAGE_PREFIX2}${projectId}` : null;
67076
67076
  }
67077
+ function submissionStorageKey(projectId) {
67078
+ return projectId ? `${SUBMISSION_STORAGE_PREFIX}${projectId}` : null;
67079
+ }
67077
67080
  function readQueue(key2) {
67078
67081
  if (!key2) return [];
67079
67082
  try {
@@ -67098,35 +67101,32 @@ function isRevision(value) {
67098
67101
  revision.createdAt
67099
67102
  ].every((field) => typeof field === "string");
67100
67103
  }
67101
- function buildAgentRevisionBatchPrompt(revisions) {
67102
- return [
67103
- "## HyperFrames revision batch v1",
67104
- `Revisions: ${revisions.length}`,
67105
- "",
67106
- "Apply every numbered revision to the current HyperFrames project.",
67107
- "Read all revisions before editing. Preserve unrelated composition content and timing.",
67108
- "If revisions overlap, apply the later revision as the user's latest intent.",
67109
- "Run HyperFrames check after all edits are complete.",
67110
- "",
67111
- ...revisions.flatMap((revision, index) => [
67112
- `### Revision ${index + 1}: ${revision.selectionLabel}`,
67113
- `Instruction: ${revision.instruction}`,
67114
- `Source: ${revision.sourceFile}`,
67115
- `Composition: ${revision.compositionPath}`,
67116
- "",
67117
- revision.prompt,
67118
- ""
67119
- ])
67120
- ].join("\n");
67104
+ function readSubmission(key2) {
67105
+ if (!key2) return null;
67106
+ try {
67107
+ const value = JSON.parse(safeLocalStorage()?.getItem(key2) ?? "null");
67108
+ if (typeof value.submissionId !== "string" || !["queued", "running", "completed", "failed"].includes(value.status))
67109
+ return null;
67110
+ return value;
67111
+ } catch {
67112
+ return null;
67113
+ }
67121
67114
  }
67122
67115
  function useAgentRevisionQueue(projectId) {
67123
67116
  const key2 = storageKey(projectId);
67117
+ const submissionKey = submissionStorageKey(projectId);
67124
67118
  const [revisions, setRevisions] = useState106(() => readQueue(key2));
67119
+ const [submission, setSubmission] = useState106(
67120
+ () => readSubmission(submissionKey)
67121
+ );
67125
67122
  const activeKeyRef = useRef119(key2);
67123
+ const activeSubmissionKeyRef = useRef119(submissionKey);
67126
67124
  useEffect94(() => {
67127
67125
  activeKeyRef.current = key2;
67128
67126
  setRevisions(readQueue(key2));
67129
- }, [key2]);
67127
+ activeSubmissionKeyRef.current = submissionKey;
67128
+ setSubmission(readSubmission(submissionKey));
67129
+ }, [key2, submissionKey]);
67130
67130
  useEffect94(() => {
67131
67131
  if (activeKeyRef.current !== key2) return;
67132
67132
  const storage = safeLocalStorage();
@@ -67137,6 +67137,52 @@ function useAgentRevisionQueue(projectId) {
67137
67137
  } catch {
67138
67138
  }
67139
67139
  }, [key2, revisions]);
67140
+ useEffect94(() => {
67141
+ if (activeSubmissionKeyRef.current !== submissionKey) return;
67142
+ const storage = safeLocalStorage();
67143
+ if (!storage || !submissionKey) return;
67144
+ try {
67145
+ if (submission && (submission.status === "queued" || submission.status === "running")) {
67146
+ storage.setItem(submissionKey, JSON.stringify(submission));
67147
+ } else {
67148
+ storage.removeItem(submissionKey);
67149
+ }
67150
+ } catch {
67151
+ }
67152
+ }, [submissionKey, submission]);
67153
+ useEffect94(() => {
67154
+ const submissionId = submission?.submissionId;
67155
+ if (!submissionId || submission?.status === "completed" || submission?.status === "failed") {
67156
+ return;
67157
+ }
67158
+ let cancelled = false;
67159
+ const poll = async () => {
67160
+ try {
67161
+ const response = await fetch(`/api/agent-revisions/${encodeURIComponent(submissionId)}`);
67162
+ if (response.status === 404) {
67163
+ if (cancelled) return;
67164
+ setSubmission({
67165
+ submissionId,
67166
+ status: "failed",
67167
+ error: "Revision session not found."
67168
+ });
67169
+ return;
67170
+ }
67171
+ if (!response.ok) return;
67172
+ const data = await response.json();
67173
+ if (cancelled || typeof data.submissionId !== "string" || !["queued", "running", "completed", "failed"].includes(data.status))
67174
+ return;
67175
+ setSubmission(data);
67176
+ } catch {
67177
+ }
67178
+ };
67179
+ void poll();
67180
+ const interval = window.setInterval(() => void poll(), 2e3);
67181
+ return () => {
67182
+ cancelled = true;
67183
+ window.clearInterval(interval);
67184
+ };
67185
+ }, [submission?.submissionId, submission?.status]);
67140
67186
  const enqueue = useCallback101((revision) => {
67141
67187
  setRevisions(
67142
67188
  (current) => [
@@ -67149,11 +67195,29 @@ function useAgentRevisionQueue(projectId) {
67149
67195
  setRevisions((current) => current.filter((revision) => revision.id !== id));
67150
67196
  }, []);
67151
67197
  const clear = useCallback101(() => setRevisions([]), []);
67152
- const copyBatch = useCallback101(async () => {
67153
- if (!revisions.length) return false;
67154
- return copyTextToClipboard(buildAgentRevisionBatchPrompt(revisions));
67155
- }, [revisions]);
67156
- return { revisions, enqueue, remove, clear, copyBatch };
67198
+ const submitBatch = useCallback101(async () => {
67199
+ if (!projectId || !revisions.length) return false;
67200
+ try {
67201
+ const response = await fetch("/api/agent-revisions", {
67202
+ method: "POST",
67203
+ headers: { "Content-Type": "application/json" },
67204
+ body: JSON.stringify({
67205
+ schemaVersion: 1,
67206
+ projectName: projectId,
67207
+ prompts: revisions.map((revision) => revision.prompt)
67208
+ })
67209
+ });
67210
+ if (!response.ok) return false;
67211
+ const data = await response.json();
67212
+ if (typeof data.submissionId !== "string") return false;
67213
+ setSubmission({ submissionId: data.submissionId, status: "queued" });
67214
+ setRevisions([]);
67215
+ return true;
67216
+ } catch {
67217
+ return false;
67218
+ }
67219
+ }, [projectId, revisions]);
67220
+ return { revisions, enqueue, remove, clear, submitBatch, submission };
67157
67221
  }
67158
67222
 
67159
67223
  // src/hooks/useAskAgentModal.ts
@@ -73161,9 +73225,10 @@ function useDomEditSession({
73161
73225
  handleAskAgent,
73162
73226
  handleAgentModalSubmit,
73163
73227
  revisions,
73228
+ submission,
73164
73229
  remove: removeAgentRevision,
73165
73230
  clear: clearAgentRevisions,
73166
- copyBatch: copyAgentRevisionBatch
73231
+ submitBatch: submitAgentRevisionBatch
73167
73232
  } = useAskAgentModal({
73168
73233
  projectId,
73169
73234
  activeCompPath,
@@ -73470,9 +73535,10 @@ function useDomEditSession({
73470
73535
  copiedAgentPrompt,
73471
73536
  agentPromptSelectionContext,
73472
73537
  revisions,
73538
+ submission,
73473
73539
  remove: removeAgentRevision,
73474
73540
  clear: clearAgentRevisions,
73475
- copyBatch: copyAgentRevisionBatch,
73541
+ submitBatch: submitAgentRevisionBatch,
73476
73542
  // Refs
73477
73543
  domEditSelectionRef,
73478
73544
  // Callbacks