nudge-ui 0.2.3 → 0.2.4

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.
Files changed (43) hide show
  1. package/dist/client.mjs +17 -9
  2. package/dist/inspector/agent/verification.d.ts +4 -6
  3. package/dist/inspector/agent/verification.js +60 -16
  4. package/dist/inspector/canvas/CanvasCard.js +2 -0
  5. package/dist/inspector/canvas/CanvasElementOverlay.js +17 -3
  6. package/dist/inspector/canvas/CanvasWorkspace.js +46 -66
  7. package/dist/inspector/canvas/frameScrollbarStyles.d.ts +3 -0
  8. package/dist/inspector/canvas/frameScrollbarStyles.js +21 -0
  9. package/dist/inspector/changes/changesLog.d.ts +3 -1
  10. package/dist/inspector/changes/changesLog.js +8 -0
  11. package/dist/inspector/changes/codecs.d.ts +1 -0
  12. package/dist/inspector/changes/codecs.js +10 -1
  13. package/dist/inspector/changes/draftChanges.d.ts +2 -0
  14. package/dist/inspector/changes/draftChanges.js +4 -0
  15. package/dist/inspector/changes/editModel.d.ts +10 -0
  16. package/dist/inspector/changes/editModel.js +9 -2
  17. package/dist/inspector/changes/types.d.ts +7 -0
  18. package/dist/inspector/comments/element.js +10 -3
  19. package/dist/inspector/componentSemantics/adapterRegistry.d.ts +6 -1
  20. package/dist/inspector/componentSemantics/adapterRegistry.js +23 -1
  21. package/dist/inspector/componentSemantics/reactRuntime.d.ts +2 -1
  22. package/dist/inspector/componentSemantics/reactRuntime.js +38 -0
  23. package/dist/inspector/componentSemantics/runtimeBridge.d.ts +4 -2
  24. package/dist/inspector/componentSemantics/runtimeBridge.js +17 -0
  25. package/dist/inspector/componentSemantics/types.d.ts +11 -0
  26. package/dist/inspector/iterations/capture.js +2 -1
  27. package/dist/inspector/projection/renderedInstance.d.ts +15 -2
  28. package/dist/inspector/projection/renderedInstance.js +53 -6
  29. package/dist/inspector/projection/structuralProjection.js +2 -2
  30. package/dist/inspector/projection/textProjection.js +56 -11
  31. package/dist/inspector/prompt/clipboardHandoff.d.ts +5 -0
  32. package/dist/inspector/prompt/clipboardHandoff.js +5 -0
  33. package/dist/inspector/prompt/generatePrompt.js +5 -1
  34. package/dist/inspector/selection/sourceSite.d.ts +12 -0
  35. package/dist/inspector/selection/sourceSite.js +28 -0
  36. package/dist/inspector/shell/CopyPromptButton.js +49 -7
  37. package/dist/inspector/styleEditors/BackgroundEditor.js +1 -1
  38. package/dist/inspector/tokens/TokenField.js +6 -4
  39. package/dist/inspector/tokens/editActions.js +6 -0
  40. package/dist/inspector/ui/styles.js +1 -1
  41. package/dist/inspector/workspace/sentItems.d.ts +19 -0
  42. package/dist/inspector/workspace/sentItems.js +51 -0
  43. package/package.json +2 -2
@@ -1,4 +1,4 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
2
  import { Menu } from "@base-ui/react/menu";
3
3
  import { iterationId } from "../canvas/frameContent.js";
4
4
  import { useEffect, useMemo, useRef, useState, useSyncExternalStore } from "react";
@@ -15,7 +15,8 @@ import { getStructuralChanges, subscribeStructuralChanges } from "../projection/
15
15
  import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
16
16
  import { getAgentClient, useAgentClient } from "../agent/client.js";
17
17
  import { createAgentPresentationAdapter } from "../canvas/agentPresentation.js";
18
- import { verifyAndReconcileAgentDispatch } from "../agent/verification.js";
18
+ import { captureHandoffOwner, getAgentDispatch, verifyAndReconcileAgentDispatch, } from "../agent/verification.js";
19
+ import { clearSentItems, getSentItems, sentItemCount } from "../workspace/sentItems.js";
19
20
  import { getClipboardHandoffRevision, getLastClipboardReconciledCount, subscribeClipboardHandoff, } from "../prompt/clipboardHandoff.js";
20
21
  import { useSketches, settleSketchDispatch } from "../sketch/store.js";
21
22
  import { SketchLayersPanel } from "../sketch/SketchLayersPanel.js";
@@ -47,7 +48,9 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
47
48
  const [preparing, setPreparing] = useState(false);
48
49
  const handoffBusy = useRef(false);
49
50
  const [saveMessage, setSaveMessage] = useState(null);
50
- const [agentCompletionStatus, setAgentCompletionStatus] = useState(null);
51
+ const [agentOutcome, setAgentOutcome] = useState(null);
52
+ const [clipboardPhase, setClipboardPhase] = useState("idle");
53
+ const [undoClear, setUndoClear] = useState(null);
51
54
  const [sketchFallback, setSketchFallback] = useState(null);
52
55
  const [localSettingsOpen, setLocalSettingsOpen] = useState(false);
53
56
  const [localSettingsSection, setLocalSettingsSection] = useState("instructions");
@@ -85,6 +88,7 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
85
88
  if (agent.state !== "completed" || revision === undefined)
86
89
  return;
87
90
  let active = true;
91
+ const dispatch = getAgentDispatch(revision);
88
92
  void verifyAndReconcileAgentDispatch(revision)
89
93
  .then(async (removed) => {
90
94
  for (const [, iframe] of getRegisteredFrames()) {
@@ -99,7 +103,7 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
99
103
  const latest = agentRef.current;
100
104
  if (latest.request?.changeRevision !== revision || latest.state !== "completed")
101
105
  return;
102
- setAgentCompletionStatus(removed > 0 ? "verified" : null);
106
+ setAgentOutcome({ removed, dispatch });
103
107
  })
104
108
  .catch(() => {
105
109
  if (!active)
@@ -107,7 +111,7 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
107
111
  const latest = agentRef.current;
108
112
  if (latest.request?.changeRevision !== revision || latest.state !== "completed")
109
113
  return;
110
- setAgentCompletionStatus(null);
114
+ setAgentOutcome({ removed: 0, dispatch });
111
115
  });
112
116
  return () => {
113
117
  active = false;
@@ -227,7 +231,9 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
227
231
  setSaveMessage(error instanceof Error ? error.message : "The HTML iteration could not be saved.");
228
232
  return;
229
233
  }
230
- setAgentCompletionStatus(null);
234
+ setAgentOutcome(null);
235
+ setUndoClear(null);
236
+ setClipboardPhase("idle");
231
237
  setSketchFallback(null);
232
238
  const outcome = await deliverHandoff(prepared, runtimeConfig.projectId, canSend ? agentClient : null);
233
239
  if (outcome.kind === "sketch-failed") {
@@ -237,6 +243,39 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
237
243
  if (outcome.kind === "sent")
238
244
  return;
239
245
  setCopied(true);
246
+ setClipboardPhase("copied");
247
+ }
248
+ useEffect(() => {
249
+ if (clipboardPhase !== "copied" && clipboardPhase !== "away")
250
+ return;
251
+ // Focus moving into a canvas frame is not a return from the agent.
252
+ const onBlur = () => {
253
+ if (!(document.activeElement instanceof HTMLIFrameElement))
254
+ setClipboardPhase("away");
255
+ };
256
+ const onFocus = () => setClipboardPhase((phase) => phase === "away" ? "returned" : phase);
257
+ window.addEventListener("blur", onBlur);
258
+ window.addEventListener("focus", onFocus);
259
+ return () => {
260
+ window.removeEventListener("blur", onBlur);
261
+ window.removeEventListener("focus", onFocus);
262
+ };
263
+ }, [clipboardPhase]);
264
+ useEffect(() => {
265
+ if (!undoClear)
266
+ return;
267
+ const timer = window.setTimeout(() => setUndoClear(null), 10_000);
268
+ return () => window.clearTimeout(timer);
269
+ }, [undoClear]);
270
+ const handoffOwner = captureHandoffOwner(activeDraftCardId);
271
+ const sentItems = getSentItems(handoffOwner.draftId, handoffOwner.target, agentOutcome?.dispatch ?? null);
272
+ const remainingCount = sentItemCount(sentItems);
273
+ const showImplemented = (agentOutcome?.removed ?? 0) > 0 || reconciledCount > 0
274
+ || (remainingCount > 0 && (agentOutcome !== null || clipboardPhase === "returned"));
275
+ function clearRemaining() {
276
+ const restore = clearSentItems(sentItems);
277
+ if (restore)
278
+ setUndoClear(() => restore);
240
279
  }
241
280
  useEffect(() => {
242
281
  function onKeydown(event) {
@@ -285,7 +324,10 @@ export function CopyPromptButton({ settingsOpen: controlledSettingsOpen, setting
285
324
  openMcpConnection();
286
325
  }
287
326
  const promptButton = (_jsxs(Button, { variant: "primary", className: "copy-prompt__main", "data-test": "copy-prompt", type: "button", disabled: disabled, "data-copied": copied ? "true" : "false", "data-agent-state": agent.state, "aria-busy": preparing || working || connecting ? "true" : undefined, onClick: activeArtifactId ? undefined : onClick, children: [icon, label, changeCount > 0 ? (_jsx("span", { className: "copy-prompt__change-count", "data-test": "copy-prompt-change-count", children: changeCount })) : null, activeArtifactId ? _jsx(IconChevronDown, { size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }) : null] }));
288
- return (_jsxs("div", { className: "copy-prompt__stack", "data-test": "copy-prompt-control", children: [activeArtifactId ? (_jsxs(Menu.Root, { modal: false, children: [_jsx(Menu.Trigger, { render: promptButton }), _jsx(Menu.Portal, { container: portalContainer(), children: _jsx(Menu.Positioner, { className: "inspector-popover__positioner", side: "bottom", align: "start", sideOffset: 6, children: _jsxs(Menu.Popup, { className: "inspector-popover__popup copy-prompt__menu", "data-test": "copy-prompt-menu", children: [_jsxs(Menu.Item, { className: "copy-prompt__menu-item", "data-test": "copy-iteration-prompt", label: "Copy iteration prompt", onClick: () => void copyIterationPrompt("iteration"), children: [_jsx(IconArtboard, { className: "copy-prompt__menu-icon", size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), _jsxs("span", { className: "copy-prompt__menu-content", children: [_jsx("span", { className: "copy-prompt__menu-title", children: "Copy iteration prompt" }), _jsx("span", { className: "copy-prompt__menu-description", children: "Continue editing this HTML iteration." })] })] }), _jsxs(Menu.Item, { className: "copy-prompt__menu-item", "data-test": "copy-iteration-implementation", label: "Copy prompt for live app", onClick: () => void copyIterationPrompt("application"), children: [_jsx(IconBoltFilled, { className: "copy-prompt__menu-icon copy-prompt__menu-icon--live", size: "var(--icon-size-small)", "aria-hidden": "true" }), _jsxs("span", { className: "copy-prompt__menu-content", children: [_jsx("span", { className: "copy-prompt__menu-title", children: "Copy prompt for live app" }), _jsx("span", { className: "copy-prompt__menu-description", children: "Implement design in real app" })] })] })] }) }) })] }, activeDraftCardId)) : promptButton, _jsx(SketchLayersPanel, {}), statusAction || agentStatus.kind === "connected-not-listening" ? (_jsx(StatusCallout, { className: "copy-prompt__agent-status", tone: agentStatus.tone, "data-test": "agent-connection-status", children: _jsxs("div", { className: "copy-prompt__agent-status-content", children: [_jsx("span", { role: "status", children: agentStatus.label }), statusAction ? (_jsx(Button, { size: "compact", variant: "quiet", "data-test": "agent-status-action", "data-action": statusAction.kind, type: "button", onClick: () => void handleAgentStatusAction(), children: statusAction.label })) : null] }) })) : null, reconciledCount > 0 ? (_jsxs("p", { className: "copy-prompt__hint copy-prompt__hint--centered", "data-test": "clipboard-reconciled-hint", role: "status", children: [reconciledCount, " Implemented changes"] })) : null, saveMessage ? (_jsx("p", { className: "copy-prompt__hint copy-prompt__hint--centered", "data-test": "iteration-save-hint", role: "status", children: saveMessage })) : null, agentCompletionStatus === "verified" ? (_jsx("p", { className: "copy-prompt__hint copy-prompt__hint--centered copy-prompt__hint--implemented", "data-test": "agent-verified-hint", role: "status", children: "Changes implemented" })) : null, sketchFallback ? (_jsxs(StatusCallout, { className: "copy-prompt__sketch-fallback", tone: "warning", "data-test": "sketch-dispatch-error", children: [_jsx("span", { children: sketchFallback.error }), _jsx(Button, { size: "compact", variant: "secondary", type: "button", "data-test": "sketch-copy-fallback", onClick: () => {
327
+ return (_jsxs("div", { className: "copy-prompt__stack", "data-test": "copy-prompt-control", children: [activeArtifactId ? (_jsxs(Menu.Root, { modal: false, children: [_jsx(Menu.Trigger, { render: promptButton }), _jsx(Menu.Portal, { container: portalContainer(), children: _jsx(Menu.Positioner, { className: "inspector-popover__positioner", side: "bottom", align: "start", sideOffset: 6, children: _jsxs(Menu.Popup, { className: "inspector-popover__popup copy-prompt__menu", "data-test": "copy-prompt-menu", children: [_jsxs(Menu.Item, { className: "copy-prompt__menu-item", "data-test": "copy-iteration-prompt", label: "Copy iteration prompt", onClick: () => void copyIterationPrompt("iteration"), children: [_jsx(IconArtboard, { className: "copy-prompt__menu-icon", size: "var(--icon-size-small)", stroke: "var(--icon-stroke-width)", "aria-hidden": "true" }), _jsxs("span", { className: "copy-prompt__menu-content", children: [_jsx("span", { className: "copy-prompt__menu-title", children: "Copy iteration prompt" }), _jsx("span", { className: "copy-prompt__menu-description", children: "Continue editing this HTML iteration." })] })] }), _jsxs(Menu.Item, { className: "copy-prompt__menu-item", "data-test": "copy-iteration-implementation", label: "Copy prompt for live app", onClick: () => void copyIterationPrompt("application"), children: [_jsx(IconBoltFilled, { className: "copy-prompt__menu-icon copy-prompt__menu-icon--live", size: "var(--icon-size-small)", "aria-hidden": "true" }), _jsxs("span", { className: "copy-prompt__menu-content", children: [_jsx("span", { className: "copy-prompt__menu-title", children: "Copy prompt for live app" }), _jsx("span", { className: "copy-prompt__menu-description", children: "Implement design in real app" })] })] })] }) }) })] }, activeDraftCardId)) : promptButton, _jsx(SketchLayersPanel, {}), statusAction || agentStatus.kind === "connected-not-listening" ? (_jsx(StatusCallout, { className: "copy-prompt__agent-status", tone: agentStatus.tone, "data-test": "agent-connection-status", children: _jsxs("div", { className: "copy-prompt__agent-status-content", children: [_jsx("span", { role: "status", children: agentStatus.label }), statusAction ? (_jsx(Button, { size: "compact", variant: "quiet", "data-test": "agent-status-action", "data-action": statusAction.kind, type: "button", onClick: () => void handleAgentStatusAction(), children: statusAction.label })) : null] }) })) : null, undoClear ? (_jsxs("p", { className: "copy-prompt__hint copy-prompt__hint--centered copy-prompt__hint--implemented", "data-test": "handoff-cleared-hint", role: "status", children: ["Cleared stale edits.", " ", _jsx("button", { type: "button", className: "copy-prompt__hint-action", "data-test": "handoff-undo-clear", onClick: () => {
328
+ undoClear();
329
+ setUndoClear(null);
330
+ }, children: "Undo" })] })) : showImplemented ? (_jsxs("p", { className: "copy-prompt__hint copy-prompt__hint--centered copy-prompt__hint--implemented", "data-test": "handoff-implemented-hint", role: "status", children: ["Changes implemented.", remainingCount > 0 ? (_jsxs(_Fragment, { children: [" ", _jsx("button", { type: "button", className: "copy-prompt__hint-action", "data-test": "handoff-clear-remaining", onClick: clearRemaining, children: "Clear stale edits" })] })) : null] })) : null, saveMessage ? (_jsx("p", { className: "copy-prompt__hint copy-prompt__hint--centered", "data-test": "iteration-save-hint", role: "status", children: saveMessage })) : null, sketchFallback ? (_jsxs(StatusCallout, { className: "copy-prompt__sketch-fallback", tone: "warning", "data-test": "sketch-dispatch-error", children: [_jsx("span", { children: sketchFallback.error }), _jsx(Button, { size: "compact", variant: "secondary", type: "button", "data-test": "sketch-copy-fallback", onClick: () => {
289
331
  void copyHandoff(sketchFallback.handoff).then(() => setSketchFallback(null)).catch(() => undefined);
290
332
  }, children: "Copy prompt" })] })) : null, _jsx(SettingsDialog, { open: settingsOpen, initialSection: settingsSection, value: customInstructions, onValueChange: handleCustomInstructionsChange, onOpenChange: setSettingsOpen, projectId: runtimeConfig.projectId, origin: window.location.origin, snapshot: agent, onConnect: () => agentClient.connect(), onTakeOver: () => agentClient.takeOver(), onDisconnect: () => agentClient.disconnect(), onCheckAgain: () => agentClient.checkConnection() })] }));
291
333
  }
@@ -26,7 +26,7 @@ export function BackgroundEditor(props) {
26
26
  const elements = selection?.domElements ?? [el];
27
27
  const source = readBackgroundLayers((property, fallback) => getStateStyleValue(el, property, fallback));
28
28
  const base = source.at(-1);
29
- const authoredColor = props.tokenRow?.property === "background-color"
29
+ const authoredColor = props.tokenRow?.property === "background-color" || props.tokenRow?.property === "background"
30
30
  ? props.tokenRow.authored ?? props.tokenRow.declaredValue : undefined;
31
31
  if (base?.baseColor && authoredColor) {
32
32
  const table = Object.fromEntries((props.entries ?? getNudgeUiTokenEntries()).map((entry) => [entry.name, entry]));
@@ -104,11 +104,13 @@ function primaryFontFamily(value) {
104
104
  return value.trim();
105
105
  }
106
106
  function rgbToHex(value) {
107
- const match = value.match(/^rgba?\(\s*(\d+(?:\.\d+)?)\D+(\d+(?:\.\d+)?)\D+(\d+(?:\.\d+)?)/i);
108
- if (!match)
107
+ const rgb = value.match(/^rgba?\(\s*(\d+(?:\.\d+)?)\D+(\d+(?:\.\d+)?)\D+(\d+(?:\.\d+)?)/i);
108
+ const srgb = rgb ? null : value.match(/^color\(\s*srgb\s+([\d.e+-]+)\s+([\d.e+-]+)\s+([\d.e+-]+)/i);
109
+ const channels = rgb ? [rgb[1], rgb[2], rgb[3]].map(Number) : srgb ? [srgb[1], srgb[2], srgb[3]].map((part) => Number(part) * 255) : null;
110
+ if (!channels || channels.some((channel) => !Number.isFinite(channel)))
109
111
  return null;
110
- return `#${[match[1], match[2], match[3]]
111
- .map((part) => Math.max(0, Math.min(255, Math.round(Number(part)))).toString(16).padStart(2, "0"))
112
+ return `#${channels
113
+ .map((channel) => Math.max(0, Math.min(255, Math.round(channel))).toString(16).padStart(2, "0"))
112
114
  .join("")}`;
113
115
  }
114
116
  export function colorValueToHex(value) {
@@ -42,6 +42,10 @@ function sourceFields(el) {
42
42
  file: parsed.file,
43
43
  line: parsed.line,
44
44
  column: parsed.column,
45
+ sourceEvidence: {
46
+ tagName: normalizeRuntimeTag(el.tagName),
47
+ props: boundRuntimeEvidence(el.getAttribute("data-cprops")),
48
+ },
45
49
  };
46
50
  }
47
51
  export { getPendingRules, getChangesList as getChangeRecords, clearActiveDraft as resetPendingRules } from "../changes/changesLog.js";
@@ -87,6 +91,7 @@ export function swapTokens(target, changes) {
87
91
  newToken: change.newToken,
88
92
  source: { file: source.file, line: source.line, component: cid },
89
93
  runtimeEvidence: source.runtimeEvidence,
94
+ sourceEvidence: source.sourceEvidence,
90
95
  state,
91
96
  ...scopePlan.get(el),
92
97
  };
@@ -155,6 +160,7 @@ function buildStyleRecord(el, scopePlan, { property, value, oldRawValue, metadat
155
160
  oldRawValue: (oldRawValue ?? getStateStyleValue(el, property)) || undefined,
156
161
  source: { file: source.file, line: source.line, component: cid },
157
162
  runtimeEvidence: source.runtimeEvidence,
163
+ sourceEvidence: source.sourceEvidence,
158
164
  state,
159
165
  ...scopePlan.get(el),
160
166
  };
@@ -18,7 +18,7 @@ const inspectorPopoverStyles = ".inspector-popover__positioner {\n z-index: 30;
18
18
  const sideValuesFieldStyles = ".side-values {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--space-8);\n}\n\n.side-values__header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.side-values__label {\n min-width: 0;\n color: var(--text-secondary);\n font-size: var(--font-size-base);\n line-height: 1.25;\n}\n\n.side-values__linked {\n min-width: 0;\n}\n\n.side-values__value-row {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: center;\n gap: var(--space-8);\n}\n\n.side-values[data-expanded=\"true\"] .side-values__value-row {\n align-items: start;\n}\n\n.side-values__linked-row {\n grid-template-columns: auto minmax(0, 1fr) auto;\n}\n\n.side-values__linked-row--no-label {\n grid-template-columns: minmax(0, 1fr) auto;\n}\n\n.side-values__linked-row .side-values__label {\n white-space: nowrap;\n}\n\n.side-values__pairs {\n display: grid;\n min-width: 0;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n gap: var(--space-8);\n}\n\n.side-values__grid {\n display: grid;\n min-width: 0;\n grid-template-columns: repeat(2, minmax(0, 1fr));\n grid-template-areas:\n \"left top\"\n \"right bottom\";\n gap: var(--space-8);\n}\n\n.side-values__grid [data-side=\"left\"] {\n grid-area: left;\n}\n\n.side-values__grid [data-side=\"right\"] {\n grid-area: right;\n}\n\n.side-values__grid [data-side=\"top\"] {\n grid-area: top;\n}\n\n.side-values__grid [data-side=\"bottom\"] {\n grid-area: bottom;\n}\n\n.side-values__grid[data-layout=\"corners\"] {\n grid-template-areas:\n \"top-left top-right\"\n \"bottom-left bottom-right\";\n}\n\n.side-values__grid[data-layout=\"corners\"] [data-side=\"top\"] {\n grid-area: top-left;\n}\n\n.side-values__grid[data-layout=\"corners\"] [data-side=\"right\"] {\n grid-area: top-right;\n}\n\n.side-values__grid[data-layout=\"corners\"] [data-side=\"bottom\"] {\n grid-area: bottom-right;\n}\n\n.side-values__grid[data-layout=\"corners\"] [data-side=\"left\"] {\n grid-area: bottom-left;\n}\n\n.side-values__side {\n color: var(--text-muted);\n}\n\n.side-values__icon {\n width: var(--icon-size-small);\n height: var(--icon-size-small);\n flex: 0 0 var(--icon-size-small);\n fill: none;\n stroke: currentColor;\n stroke-linecap: round;\n stroke-linejoin: round;\n stroke-width: var(--icon-stroke-width);\n}\n\n.side-values__axis-icon {\n color: var(--text-secondary);\n}\n\n.side-values__side-icon {\n color: var(--text-secondary);\n}\n\n.side-values__icon-emphasis,\n.side-values__icon-emphasis path {\n stroke-width: var(--icon-stroke-width);\n}\n\n.side-values__control {\n display: flex;\n align-self: stretch;\n min-width: 0;\n flex: 1 1 auto;\n}\n";
19
19
  const tooltipStyles = ".tooltip__trigger {\n display: inline-flex;\n}\n\n.tooltip__trigger[data-tooltip-trigger-disabled] > * {\n pointer-events: none;\n}\n\n.tooltip__positioner {\n z-index: 35;\n pointer-events: auto;\n}\n\n.tooltip__popup {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: var(--space-8);\n width: max-content;\n max-width: min(320px, calc(100vw - var(--space-24)));\n padding: var(--space-4) var(--space-8);\n border: 0;\n border-radius: var(--radius-8);\n background: var(--surface-raised-2x);\n box-shadow: var(--shadow-popup);\n color: var(--text-primary);\n font-family: var(--font-family);\n font-size: var(--font-size-base);\n font-weight: var(--font-weight-base);\n line-height: 1.3;\n}\n\n.tooltip__popup[data-open] {\n animation: tooltip-enter 150ms ease-out;\n}\n\n@keyframes tooltip-enter {\n from {\n transform: scale(0.9);\n }\n to {\n transform: scale(1);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .tooltip__popup[data-open] {\n animation: none;\n }\n}\n\n.tooltip__content {\n min-width: 0;\n position: relative;\n z-index: 1;\n}\n\n.tooltip__shortcut {\n flex: 0 0 auto;\n position: relative;\n z-index: 1;\n color: var(--text-secondary);\n font: inherit;\n line-height: inherit;\n white-space: nowrap;\n}\n";
20
20
  const inlineStyleWarningStyles = ".inline-style-warning {\n display: inline-flex;\n flex: 0 0 auto;\n align-items: center;\n justify-content: center;\n width: var(--icon-size-small);\n height: var(--icon-size-small);\n padding: 0;\n border: 0;\n border-radius: 50%;\n background: var(--warning, #b45309);\n color: var(--surface-raised-2x);\n font-size: 11px;\n font-weight: 700;\n line-height: 1;\n cursor: help;\n}\n\n.inline-style-warning:focus-visible {\n outline: 0;\n box-shadow: 0 0 0 2px var(--accent);\n}\n\n.inline-style-warning__symbol {\n display: block;\n line-height: 1;\n}\n";
21
- const inspectorShellStyles = ":host { --panel-width: min(320px, 100vw); }\n\n.panel {\n position: fixed;\n inset: 0 0 0 auto;\n z-index: 2;\n width: var(--panel-width);\n height: 100dvh;\n overflow: auto;\n background: var(--surface-panel);\n color: var(--text-primary);\n border: 0;\n border-left: 1px solid var(--border-subtle);\n border-radius: var(--radius-0);\n font-family: var(--font-family);\n font-size: var(--font-size-base);\n line-height: 1.35;\n display: flex;\n flex-direction: column;\n pointer-events: auto;\n cursor: default;\n scrollbar-color: var(--border-strong) transparent;\n scrollbar-width: thin;\n}\n\n.panel[data-open=\"false\"] {\n display: none;\n}\n\n.panel__restore {\n position: fixed;\n top: max(12px, env(safe-area-inset-top));\n right: max(12px, env(safe-area-inset-right));\n z-index: calc(var(--z-index) + 1);\n display: flex;\n align-items: center;\n justify-content: center;\n width: calc(var(--control-height-large) + (var(--space-4) * 2));\n height: calc(var(--control-height-large) + (var(--space-4) * 2));\n padding: var(--space-4);\n border-radius: var(--radius-12);\n background: var(--surface-raised-2x);\n box-shadow: var(--shadow-popup);\n pointer-events: auto;\n animation: panel-restore-in 160ms ease-out;\n}\n\n.panel__restore-button.icon-button {\n width: 100%;\n height: 100%;\n}\n\n@keyframes panel-restore-in {\n from { opacity: 0; transform: translateX(8px); }\n to { opacity: 1; transform: translateX(0); }\n}\n\n.scope__warning {\n color: var(--warning-text);\n font-weight: 500;\n}\n\n.panel__tabs {\n display: flex;\n flex-direction: column;\n gap: var(--space-8);\n padding: var(--space-12) var(--space-24);\n border-bottom: 1px solid var(--border-subtle);\n}\n\n.panel__header-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-8);\n width: 100%;\n}\n\n.panel__header-actions,\n.panel__copy-row {\n display: flex;\n align-items: center;\n}\n\n.panel__header-actions {\n min-width: 0;\n gap: var(--space-4);\n}\n\n.panel__copy-row {\n gap: var(--space-8);\n}\n\n.panel__header-divider {\n width: 1px;\n height: var(--control-height-large);\n margin: 0 var(--space-4);\n background: var(--border-subtle);\n}\n\n.panel__copy-row {\n width: 100%;\n}\n\n.copy-prompt__stack {\n width: 100%;\n min-width: 0;\n}\n\n.copy-prompt__main.button {\n width: 100%;\n position: relative;\n}\n\n.copy-prompt__menu {\n width: max(var(--anchor-width), 280px);\n max-width: calc(100vw - 24px);\n}\n\n.copy-prompt__menu-item {\n display: flex;\n align-items: flex-start;\n gap: var(--space-8);\n padding: var(--space-8);\n border-radius: var(--radius-4);\n color: var(--text-primary);\n font-size: var(--font-size-base);\n line-height: 1.4;\n cursor: pointer;\n outline: 0;\n}\n\n.copy-prompt__menu-item[data-highlighted] {\n background: var(--surface-control);\n}\n\n.copy-prompt__menu-icon {\n flex: 0 0 auto;\n color: var(--text-secondary);\n}\n\n.copy-prompt__menu-icon--live {\n color: var(--accent);\n}\n\n.copy-prompt__menu-content {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--space-4);\n min-width: 0;\n}\n\n.copy-prompt__menu-title {\n font-weight: var(--font-weight-base);\n}\n\n.copy-prompt__menu-description {\n color: var(--text-secondary);\n overflow-wrap: anywhere;\n}\n\n.copy-prompt__menu-description code {\n font-family: var(--font-mono);\n}\n\n.agent-status-button {\n position: relative;\n}\n\n.agent-status-button__avatars {\n display: inline-flex;\n align-items: center;\n}\n\n.agent-status-button__avatar {\n width: 20px;\n height: 20px;\n display: block;\n background: var(--surface-raised-2x);\n border-radius: 50%;\n box-shadow: 0 0 0 1px var(--border-subtle);\n padding: 2px;\n}\n\n.agent-status-button__avatar svg {\n width: 100%;\n height: 100%;\n display: block;\n}\n\n.agent-status-button__avatar + .agent-status-button__avatar {\n margin-left: calc(var(--space-4) * -1);\n}\n\n.agent-status-button__dot {\n position: absolute;\n top: 50%;\n right: 3px;\n width: 6px;\n height: 6px;\n transform: translateY(-50%);\n border-radius: 50%;\n background: var(--text-muted);\n}\n\n.agent-status-button[data-mcp-listening=\"true\"] .agent-status-button__dot {\n background: var(--success);\n}\n\n.copy-prompt__hint {\n margin: var(--space-8) 0 0;\n color: var(--text-secondary);\n font-size: var(--font-size-small);\n line-height: 1.4;\n}\n\n.copy-prompt__hint--centered {\n text-align: center;\n}\n\n.copy-prompt__hint--implemented {\n font-size: var(--font-size-base);\n}\n\n.copy-prompt__hint code {\n font-family: var(--font-mono);\n}\n\n.copy-prompt__agent-status {\n margin-top: var(--space-8);\n}\n\n.copy-prompt__agent-status-content {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-8);\n}\n\n.copy-prompt__agent-status-content > span {\n min-width: 0;\n}\n\n.copy-prompt__agent-status-content .button--quiet {\n flex: 0 0 auto;\n color: inherit;\n}\n\n.copy-prompt__sketch-fallback {\n display: grid;\n gap: var(--space-8);\n margin-top: var(--space-8);\n}\n\n.panel__body {\n min-height: auto;\n padding: 0 20px 28px;\n background: var(--surface-panel);\n color: var(--text-secondary);\n overflow: visible;\n flex: 0 0 auto;\n}\n\n.panel__body:has(.empty-state) {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n min-height: 0;\n}\n\n.panel::-webkit-scrollbar {\n width: 6px;\n}\n\n.panel::-webkit-scrollbar-thumb {\n background: var(--border-strong);\n border-radius: var(--radius-pill);\n}\n\n.selection {\n display: flex;\n flex-direction: column;\n gap: var(--space-8);\n padding: 14px 0 16px;\n border-bottom: 1px solid var(--border-subtle);\n}\n\n.selection--without-scope-callout {\n border-bottom: 0;\n}\n\n.selection__summary {\n display: grid;\n gap: 2px;\n}\n\n.selection__count {\n color: var(--text-primary);\n font-weight: 600;\n}\n\n.style-state {\n grid-template-columns: var(--label-column) minmax(0, 1fr);\n column-gap: var(--space-8);\n}\n\n.scope {\n margin-top: var(--space-8);\n}\n\n.scope__action {\n margin-top: 0;\n}\n\n.scope__unlinked,\n.scope__linked {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-8);\n}\n\n.style-editors {\n margin-top: 0;\n background: var(--surface-panel);\n border-top: 0;\n padding-top: 0;\n}\n\n.component-props__source {\n color: var(--text-muted-subtle);\n font-size: 10px;\n font-weight: 650;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n\n.component-props .field-row__label {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.inline-text-diagnostic {\n margin-top: 12px;\n font-weight: 400;\n}\n\n.style-editors__title {\n color: var(--text-muted);\n margin-bottom: 8px;\n font-weight: 600;\n}\n\n.empty-state + .changes {\n margin-top: auto;\n}\n\n.changes__empty {\n color: var(--text-muted-subtle);\n}\n\n.editor {\n margin: 0;\n padding: var(--space-8) 0;\n background: var(--surface-panel);\n border: 0;\n border-bottom: 1px solid var(--border-subtle);\n border-radius: 0;\n display: flex;\n flex-direction: column;\n gap: var(--space-8);\n}\n\n.editor__title {\n color: var(--text-primary);\n font-size: var(--font-size-base);\n font-weight: var(--font-weight-label);\n letter-spacing: -0.02em;\n min-height: var(--control-height-large);\n display: flex;\n align-items: center;\n justify-content: space-between;\n text-transform: none;\n}\n\n.editor__title-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.editor__title-row .editor__title {\n margin-bottom: 0;\n}\n";
21
+ const inspectorShellStyles = ":host { --panel-width: min(320px, 100vw); }\n\n.panel {\n position: fixed;\n inset: 0 0 0 auto;\n z-index: 2;\n width: var(--panel-width);\n height: 100dvh;\n overflow: auto;\n background: var(--surface-panel);\n color: var(--text-primary);\n border: 0;\n border-left: 1px solid var(--border-subtle);\n border-radius: var(--radius-0);\n font-family: var(--font-family);\n font-size: var(--font-size-base);\n line-height: 1.35;\n display: flex;\n flex-direction: column;\n pointer-events: auto;\n cursor: default;\n scrollbar-color: var(--border-strong) transparent;\n scrollbar-width: thin;\n}\n\n.panel[data-open=\"false\"] {\n display: none;\n}\n\n.panel__restore {\n position: fixed;\n top: max(12px, env(safe-area-inset-top));\n right: max(12px, env(safe-area-inset-right));\n z-index: calc(var(--z-index) + 1);\n display: flex;\n align-items: center;\n justify-content: center;\n width: calc(var(--control-height-large) + (var(--space-4) * 2));\n height: calc(var(--control-height-large) + (var(--space-4) * 2));\n padding: var(--space-4);\n border-radius: var(--radius-12);\n background: var(--surface-raised-2x);\n box-shadow: var(--shadow-popup);\n pointer-events: auto;\n animation: panel-restore-in 160ms ease-out;\n}\n\n.panel__restore-button.icon-button {\n width: 100%;\n height: 100%;\n}\n\n@keyframes panel-restore-in {\n from { opacity: 0; transform: translateX(8px); }\n to { opacity: 1; transform: translateX(0); }\n}\n\n.scope__warning {\n color: var(--warning-text);\n font-weight: 500;\n}\n\n.panel__tabs {\n display: flex;\n flex-direction: column;\n gap: var(--space-8);\n padding: var(--space-12) var(--space-24);\n border-bottom: 1px solid var(--border-subtle);\n}\n\n.panel__header-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-8);\n width: 100%;\n}\n\n.panel__header-actions,\n.panel__copy-row {\n display: flex;\n align-items: center;\n}\n\n.panel__header-actions {\n min-width: 0;\n gap: var(--space-4);\n}\n\n.panel__copy-row {\n gap: var(--space-8);\n}\n\n.panel__header-divider {\n width: 1px;\n height: var(--control-height-large);\n margin: 0 var(--space-4);\n background: var(--border-subtle);\n}\n\n.panel__copy-row {\n width: 100%;\n}\n\n.copy-prompt__stack {\n width: 100%;\n min-width: 0;\n}\n\n.copy-prompt__main.button {\n width: 100%;\n position: relative;\n}\n\n.copy-prompt__menu {\n width: max(var(--anchor-width), 280px);\n max-width: calc(100vw - 24px);\n}\n\n.copy-prompt__menu-item {\n display: flex;\n align-items: flex-start;\n gap: var(--space-8);\n padding: var(--space-8);\n border-radius: var(--radius-4);\n color: var(--text-primary);\n font-size: var(--font-size-base);\n line-height: 1.4;\n cursor: pointer;\n outline: 0;\n}\n\n.copy-prompt__menu-item[data-highlighted] {\n background: var(--surface-control);\n}\n\n.copy-prompt__menu-icon {\n flex: 0 0 auto;\n color: var(--text-secondary);\n}\n\n.copy-prompt__menu-icon--live {\n color: var(--accent);\n}\n\n.copy-prompt__menu-content {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--space-4);\n min-width: 0;\n}\n\n.copy-prompt__menu-title {\n font-weight: var(--font-weight-base);\n}\n\n.copy-prompt__menu-description {\n color: var(--text-secondary);\n overflow-wrap: anywhere;\n}\n\n.copy-prompt__menu-description code {\n font-family: var(--font-mono);\n}\n\n.agent-status-button {\n position: relative;\n}\n\n.agent-status-button__avatars {\n display: inline-flex;\n align-items: center;\n}\n\n.agent-status-button__avatar {\n width: 20px;\n height: 20px;\n display: block;\n background: var(--surface-raised-2x);\n border-radius: 50%;\n box-shadow: 0 0 0 1px var(--border-subtle);\n padding: 2px;\n}\n\n.agent-status-button__avatar svg {\n width: 100%;\n height: 100%;\n display: block;\n}\n\n.agent-status-button__avatar + .agent-status-button__avatar {\n margin-left: calc(var(--space-4) * -1);\n}\n\n.agent-status-button__dot {\n position: absolute;\n top: 50%;\n right: 3px;\n width: 6px;\n height: 6px;\n transform: translateY(-50%);\n border-radius: 50%;\n background: var(--text-muted);\n}\n\n.agent-status-button[data-mcp-listening=\"true\"] .agent-status-button__dot {\n background: var(--success);\n}\n\n.copy-prompt__hint {\n margin: var(--space-8) 0 0;\n color: var(--text-secondary);\n font-size: var(--font-size-small);\n line-height: 1.4;\n}\n\n.copy-prompt__hint--centered {\n text-align: center;\n}\n\n.copy-prompt__hint--implemented {\n font-size: var(--font-size-base);\n}\n\n.copy-prompt__hint-action {\n padding: 0;\n border: 0;\n background: none;\n color: inherit;\n font: inherit;\n text-decoration: underline;\n text-underline-offset: 2px;\n cursor: pointer;\n}\n\n.copy-prompt__hint-action:hover {\n color: var(--text-primary);\n}\n\n.copy-prompt__hint-action:focus-visible {\n border-radius: var(--radius-4);\n outline: 2px solid var(--accent);\n outline-offset: 2px;\n}\n\n.copy-prompt__hint code {\n font-family: var(--font-mono);\n}\n\n.copy-prompt__agent-status {\n margin-top: var(--space-8);\n}\n\n.copy-prompt__agent-status-content {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-8);\n}\n\n.copy-prompt__agent-status-content > span {\n min-width: 0;\n}\n\n.copy-prompt__agent-status-content .button--quiet {\n flex: 0 0 auto;\n color: inherit;\n}\n\n.copy-prompt__sketch-fallback {\n display: grid;\n gap: var(--space-8);\n margin-top: var(--space-8);\n}\n\n.panel__body {\n min-height: auto;\n padding: 0 20px 28px;\n background: var(--surface-panel);\n color: var(--text-secondary);\n overflow: visible;\n flex: 0 0 auto;\n}\n\n.panel__body:has(.empty-state) {\n display: flex;\n flex: 1 1 auto;\n flex-direction: column;\n min-height: 0;\n}\n\n.panel::-webkit-scrollbar {\n width: 6px;\n}\n\n.panel::-webkit-scrollbar-thumb {\n background: var(--border-strong);\n border-radius: var(--radius-pill);\n}\n\n.selection {\n display: flex;\n flex-direction: column;\n gap: var(--space-8);\n padding: 14px 0 16px;\n border-bottom: 1px solid var(--border-subtle);\n}\n\n.selection--without-scope-callout {\n border-bottom: 0;\n}\n\n.selection__summary {\n display: grid;\n gap: 2px;\n}\n\n.selection__count {\n color: var(--text-primary);\n font-weight: 600;\n}\n\n.style-state {\n grid-template-columns: var(--label-column) minmax(0, 1fr);\n column-gap: var(--space-8);\n}\n\n.scope {\n margin-top: var(--space-8);\n}\n\n.scope__action {\n margin-top: 0;\n}\n\n.scope__unlinked,\n.scope__linked {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-8);\n}\n\n.style-editors {\n margin-top: 0;\n background: var(--surface-panel);\n border-top: 0;\n padding-top: 0;\n}\n\n.component-props__source {\n color: var(--text-muted-subtle);\n font-size: 10px;\n font-weight: 650;\n letter-spacing: 0.06em;\n text-transform: uppercase;\n}\n\n.component-props .field-row__label {\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.inline-text-diagnostic {\n margin-top: 12px;\n font-weight: 400;\n}\n\n.style-editors__title {\n color: var(--text-muted);\n margin-bottom: 8px;\n font-weight: 600;\n}\n\n.empty-state + .changes {\n margin-top: auto;\n}\n\n.changes__empty {\n color: var(--text-muted-subtle);\n}\n\n.editor {\n margin: 0;\n padding: var(--space-8) 0;\n background: var(--surface-panel);\n border: 0;\n border-bottom: 1px solid var(--border-subtle);\n border-radius: 0;\n display: flex;\n flex-direction: column;\n gap: var(--space-8);\n}\n\n.editor__title {\n color: var(--text-primary);\n font-size: var(--font-size-base);\n font-weight: var(--font-weight-label);\n letter-spacing: -0.02em;\n min-height: var(--control-height-large);\n display: flex;\n align-items: center;\n justify-content: space-between;\n text-transform: none;\n}\n\n.editor__title-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.editor__title-row .editor__title {\n margin-bottom: 0;\n}\n";
22
22
  const inspectorOverlayStyles = ".hover-outline,\n.selected-outline {\n position: fixed;\n pointer-events: none;\n outline-offset: -2px;\n}\n\n.measurement-overlay {\n position: fixed;\n inset: 0;\n z-index: 0;\n overflow: visible;\n pointer-events: none;\n}\n\n.alignment-guide {\n stroke: rgb(59 130 246 / 46%);\n stroke-dasharray: 3 4;\n stroke-width: 1;\n vector-effect: non-scaling-stroke;\n}\n\n.measurement-ruler {\n stroke: #ef4444;\n stroke-width: 1;\n vector-effect: non-scaling-stroke;\n}\n\n.measurement-projection {\n stroke: #ef4444;\n stroke-dasharray: 4 4;\n stroke-width: 1;\n vector-effect: non-scaling-stroke;\n}\n\n.measurement-label {\n fill: #fff;\n font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;\n font-size: 12px;\n font-weight: 600;\n}\n\n.measurement-label-chip {\n fill: #ef4444;\n}\n\n.dom-drop-line {\n z-index: 4;\n height: 3px;\n margin-top: -1px;\n border-radius: 999px;\n background: #3b82f6;\n box-shadow: 0 0 0 1px rgb(255 255 255 / 80%);\n}\n\n.dom-drop-target {\n z-index: 3;\n box-sizing: border-box;\n border: 1px solid rgb(59 130 246 / 72%);\n border-radius: 4px;\n background: rgb(59 130 246 / 5%);\n}\n\n.hover-outline {\n outline: 2px solid var(--accent);\n}\n\n.selected-outline {\n outline: 2px solid var(--accent);\n}\n\n.hover-margin-fill {\n background: rgb(59 130 246 / 14%);\n}\n\n.hover-margin {\n border-color: rgb(59 130 246 / 52%);\n border-style: dotted;\n border-width: 0;\n opacity: 0.72;\n}\n\n.hover-margin[data-axis=\"horizontal\"] {\n border-top-width: 1px;\n}\n\n.hover-margin[data-axis=\"vertical\"] {\n border-left-width: 1px;\n}\n\n.dom-navigator,\n.dom-navigator-preview {\n position: fixed;\n pointer-events: none;\n}\n\n.dom-navigator {\n display: flex;\n align-items: center;\n min-height: var(--control-height-small);\n border-radius: var(--radius-4);\n background: var(--accent);\n color: var(--surface-raised-2x);\n z-index: 2;\n pointer-events: auto;\n font-family: var(--font-family);\n font-size: var(--font-size-base);\n}\n\n.dom-navigator__trigger,\n.dom-navigator__item {\n appearance: none;\n display: flex;\n align-items: center;\n gap: var(--space-4);\n border: 0;\n color: var(--text-primary);\n cursor: pointer;\n font: inherit;\n white-space: nowrap;\n}\n\n.dom-navigator__trigger {\n min-height: var(--control-height-small);\n justify-content: center;\n width: var(--control-height-small);\n padding: 0;\n border-radius: var(--radius-4);\n background: transparent;\n color: var(--surface-raised-2x);\n box-shadow: none;\n}\n\n.dom-navigator__label {\n padding-right: var(--space-8);\n white-space: nowrap;\n}\n\n.dom-navigator__positioner {\n z-index: 2;\n pointer-events: auto;\n}\n\n.dom-navigator__menu {\n display: grid;\n min-width: 140px;\n padding: var(--space-4);\n gap: var(--space-4);\n border: 0;\n border-radius: var(--radius-8);\n background: var(--surface-raised-2x);\n box-shadow: var(--shadow-popup);\n}\n\n.dom-navigator__item {\n width: 100%;\n min-height: var(--control-height-small);\n padding: 0 var(--space-8);\n border-color: transparent;\n border-radius: var(--radius-4);\n background: transparent;\n text-align: left;\n font-family: var(--font-family);\n font-size: var(--font-size-base);\n}\n\n.dom-navigator__trigger:hover {\n background: var(--surface-well);\n color: var(--surface-raised-2x);\n}\n\n.dom-navigator__trigger:focus-visible {\n background: var(--surface-well);\n color: var(--surface-raised-2x);\n outline: 1px solid currentColor;\n outline-offset: -3px;\n}\n\n.dom-navigator__item:hover,\n.dom-navigator__item:focus-visible {\n background: var(--surface-well);\n outline: 0;\n}\n\n.dom-navigator__item--selected {\n background: var(--accent-surface);\n}\n\n.dom-navigator-preview {\n z-index: 1;\n outline: 2px solid var(--accent);\n outline-offset: -2px;\n}\n";
23
23
  const emptyStateStyles = ".empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: var(--space-40) var(--space-0) var(--space-24);\n min-height: 70dvh;\n text-align: center;\n color: var(--text-secondary);\n}\n\n.empty-state__icon { display: block; width: min(100%, 182px); height: auto; margin-bottom: var(--space-16); }\n.empty-state__title { margin: 0; color: var(--text-primary); font-size: var(--font-size-base); font-weight: var(--font-weight-base); }\n.empty-state--canvas {\n align-items: stretch;\n justify-content: flex-start;\n min-height: 0;\n padding-top: var(--space-24);\n text-align: left;\n}\n\n.empty-state__canvas .empty-state__title {\n margin-bottom: var(--space-24);\n font-weight: var(--font-weight-label);\n}\n\n.empty-state__canvas p {\n display: flex;\n align-items: flex-start;\n gap: var(--space-12);\n margin: 0 0 var(--space-20);\n line-height: 1.5;\n}\n\n.empty-state__canvas p:last-child { margin-bottom: 0; }\n.empty-state__canvas svg { width: 20px; height: 20px; margin-top: 2px; flex-shrink: 0; }\n.empty-state__live-badge {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n flex: 0 0 20px;\n height: 20px;\n margin-top: 2px;\n border-radius: var(--radius-4);\n background: var(--accent-surface);\n color: var(--accent);\n}\n.empty-state__live-badge svg { width: 12px; height: 12px; margin-top: 0; }\n.empty-state__canvas strong { display: block; color: var(--text-primary); font-weight: var(--font-weight-label); }\n.empty-state__shortcuts { width: 100%; margin-top: var(--space-32); text-align: left; }\n.empty-state__shortcuts .disclosure__panel { padding-top: var(--space-16); }\n.empty-state__shortcut-list { display: grid; gap: var(--space-8); margin: var(--space-0); padding: var(--space-0); list-style: none; }\n.empty-state__shortcut { display: flex; align-items: center; justify-content: space-between; gap: var(--space-8); min-height: var(--control-height-small); color: var(--text-secondary); }\n.empty-state__shortcut-keys { display: inline-flex; align-items: center; gap: var(--space-4); color: var(--text-muted); }\n.empty-state__shortcut-keys kbd { display: inline-flex; align-items: center; justify-content: center; min-width: var(--control-height-small); height: var(--control-height-small); padding: var(--space-0) var(--space-8); border-radius: var(--radius-4); background: var(--surface-raised-2x); color: var(--text-primary); font-family: var(--font-family); font-size: var(--font-size-small); box-shadow: var(--shadow-control); }\n.empty-state__shortcut-keys > span { color: var(--text-muted-subtle); font-size: var(--font-size-small); }\n";
24
24
  const styleEditorStyles = "/* Shared chrome for style-editor sections. */\n.layout,\n.spacing,\n.typography,\n.border,\n.property,\n.color {\n display: flex;\n flex-direction: column;\n gap: var(--space-8);\n background: var(--surface-panel);\n}\n\n.appearance {\n gap: var(--space-12);\n}\n\n.appearance__fields {\n display: grid;\n grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) auto;\n gap: var(--space-8);\n}\n\n.appearance__field {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--space-8);\n}\n\n.appearance__field-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: var(--space-8);\n}\n\n.appearance__field-label {\n min-width: 0;\n color: var(--text-secondary);\n font-size: var(--font-size-base);\n line-height: 1.25;\n}\n\n.border-radius-editor__main {\n display: flex;\n min-width: 0;\n flex-direction: column;\n gap: var(--space-8);\n}\n\n.border-radius-editor--embedded {\n display: contents;\n}\n\n.border-radius-editor--embedded > .border-radius-editor__main {\n grid-column: 2;\n}\n\n.border-radius-editor--embedded > .border-radius-editor__toggle {\n grid-column: 3;\n align-self: end;\n}\n\n.border-radius__grouped-row {\n display: grid;\n min-width: 0;\n grid-template-columns: minmax(0, 1fr) auto;\n align-items: stretch;\n gap: var(--space-8);\n}\n\n.border-radius__grouped-row > .control-surface {\n min-width: 0;\n}\n\n.border-radius-editor__individuals {\n grid-column: 1 / span 2;\n min-width: 0;\n}\n\n.opacity-editor__effective {\n flex: 0 0 auto;\n color: var(--text-secondary);\n font-size: var(--font-size-base);\n}\n\n.box-shadow-editor__layers,\n.box-shadow-editor__layer {\n display: grid;\n gap: var(--space-8);\n}\n\n.box-shadow-editor__dimensions {\n display: grid;\n grid-template-columns: repeat(4, minmax(0, 1fr));\n gap: var(--space-4);\n}\n\n.box-shadow-editor__layer {\n grid-template-columns: minmax(0, 1fr) var(--control-height-large);\n align-items: center;\n}\n\n.box-shadow-editor__layer + .box-shadow-editor__layer {\n padding-top: var(--space-12);\n border-top: 1px solid var(--border-subtle);\n}\n\n.box-shadow-editor__remove {\n grid-column: 2;\n grid-row: 1;\n}\n\n.box-shadow-editor__color {\n grid-column: 1;\n}\n\n.box-shadow-editor__dimensions > .control-surface {\n --control-surface-padding: 2px 4px;\n}\n\n.box-shadow-editor__dimensions .token-field {\n gap: var(--space-4);\n}\n";
@@ -0,0 +1,19 @@
1
+ import { type ChangeRecord } from "../changes/changesLog.js";
2
+ import type { StructuralChange } from "../changes/structuralTypes.js";
3
+ import { type HandoffSnapshot } from "../agent/verification.js";
4
+ import { type ElementComment } from "../comments/store.js";
5
+ import type { DraftTarget } from "../drafts/model.js";
6
+ /** Handed-off work that verification has not confirmed and the user has not changed since. */
7
+ export interface SentItems {
8
+ readonly draftId: string;
9
+ readonly changes: readonly ChangeRecord[];
10
+ readonly structuralChanges: readonly StructuralChange[];
11
+ readonly comments: readonly ElementComment[];
12
+ }
13
+ export declare function getSentItems(draftId: string, target: DraftTarget, agentDispatch: HandoffSnapshot | null): SentItems;
14
+ export declare function sentItemCount(items: SentItems): number;
15
+ /**
16
+ * Removes sent items from the active draft as one undoable edit. Returns a
17
+ * restore callback, or null when the draft is not active or nothing changed.
18
+ */
19
+ export declare function clearSentItems(items: SentItems): (() => void) | null;
@@ -0,0 +1,51 @@
1
+ import { changeKey } from "../changes/model.js";
2
+ import { commitDraftContents } from "../changes/changesLog.js";
3
+ import { getActiveDraftChanges, getDraftChanges } from "../changes/draftChanges.js";
4
+ import { unchangedHandoffRecords } from "../agent/verification.js";
5
+ import { getClipboardHandoffRecords } from "../prompt/clipboardHandoff.js";
6
+ import { getComments, getCommentsForTarget, removeComment, saveComment } from "../comments/store.js";
7
+ export function getSentItems(draftId, target, agentDispatch) {
8
+ const clipboard = getClipboardHandoffRecords(draftId);
9
+ const agent = agentDispatch?.owner.draftId === draftId
10
+ ? unchangedHandoffRecords(agentDispatch)
11
+ : { changes: [], structuralChanges: [] };
12
+ const changes = new Map([...clipboard.changes, ...agent.changes].map((change) => [changeKey(change), change]));
13
+ const structuralChanges = new Map([...clipboard.structuralChanges, ...agent.structuralChanges].map((change) => [change.id, change]));
14
+ return {
15
+ draftId,
16
+ changes: [...changes.values()],
17
+ structuralChanges: [...structuralChanges.values()],
18
+ comments: getCommentsForTarget(target).filter((comment) => comment.handedOff),
19
+ };
20
+ }
21
+ export function sentItemCount(items) {
22
+ return items.changes.length + items.structuralChanges.length + items.comments.length;
23
+ }
24
+ /**
25
+ * Removes sent items from the active draft as one undoable edit. Returns a
26
+ * restore callback, or null when the draft is not active or nothing changed.
27
+ */
28
+ export function clearSentItems(items) {
29
+ const current = getActiveDraftChanges();
30
+ if (current.draftId !== items.draftId || sentItemCount(items) === 0)
31
+ return null;
32
+ const keys = new Set(items.changes.map(changeKey));
33
+ const ids = new Set(items.structuralChanges.map((change) => change.id));
34
+ const draftChanged = items.changes.length + items.structuralChanges.length > 0;
35
+ if (draftChanged && !commitDraftContents(current.changes.filter((change) => !keys.has(changeKey(change))), current.structuralChanges.filter((change) => !ids.has(change.id))))
36
+ return null;
37
+ for (const comment of items.comments)
38
+ removeComment(comment.id);
39
+ return () => {
40
+ const draft = getDraftChanges(items.draftId);
41
+ if (draftChanged && getActiveDraftChanges().draftId === items.draftId) {
42
+ const present = new Set(draft.changes.map(changeKey));
43
+ const presentIds = new Set(draft.structuralChanges.map((change) => change.id));
44
+ commitDraftContents([...draft.changes, ...items.changes.filter((change) => !present.has(changeKey(change)))], [...draft.structuralChanges, ...items.structuralChanges.filter((change) => !presentIds.has(change.id))]);
45
+ }
46
+ const existing = new Set(getComments().map((comment) => comment.id));
47
+ for (const comment of items.comments)
48
+ if (!existing.has(comment.id))
49
+ saveComment(comment);
50
+ };
51
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "nudge-ui",
3
- "version": "0.2.3",
4
- "description": "Development-only visual UI inspector. One package, one subpath per host.",
3
+ "version": "0.2.4",
4
+ "description": "A design panel for your codebase. Development only.",
5
5
  "license": "MIT",
6
6
  "repository": {
7
7
  "type": "git",