nudge-ui 0.2.3 → 0.2.5
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/cli/agentCommand.js +4 -28
- package/dist/client.mjs +870 -212
- package/dist/hosts/next/wrapper.js +1 -7
- package/dist/hosts/projectBridge.js +3 -17
- package/dist/inspector/agent/McpConnectionDialog.js +7 -3
- package/dist/inspector/agent/activity.d.ts +1 -1
- package/dist/inspector/agent/activity.js +3 -2
- package/dist/inspector/agent/client.d.ts +2 -2
- package/dist/inspector/agent/client.js +28 -29
- package/dist/inspector/agent/connectionStatus.d.ts +3 -1
- package/dist/inspector/agent/connectionStatus.js +25 -0
- package/dist/inspector/agent/httpTransport.js +31 -147
- package/dist/inspector/agent/protocol.d.ts +1 -1
- package/dist/inspector/agent/protocol.js +1 -1
- package/dist/inspector/agent/verification.d.ts +5 -7
- package/dist/inspector/agent/verification.js +67 -20
- package/dist/inspector/boot.d.ts +3 -0
- package/dist/inspector/boot.js +297 -0
- package/dist/inspector/canvas/AgentActivityOverlay.d.ts +2 -1
- package/dist/inspector/canvas/AgentActivityOverlay.js +6 -3
- package/dist/inspector/canvas/CanvasCard.js +23 -12
- package/dist/inspector/canvas/CanvasElementOverlay.js +97 -128
- package/dist/inspector/canvas/CanvasToolbar.js +2 -1
- package/dist/inspector/canvas/CanvasWorkspace.js +119 -162
- package/dist/inspector/canvas/LockedWorkspaceNotice.d.ts +3 -2
- package/dist/inspector/canvas/LockedWorkspaceNotice.js +10 -6
- package/dist/inspector/canvas/PageCard.d.ts +10 -0
- package/dist/inspector/canvas/PageCard.js +51 -0
- package/dist/inspector/canvas/SketchToolOptions.d.ts +5 -0
- package/dist/inspector/canvas/SketchToolOptions.js +86 -0
- package/dist/inspector/canvas/StaleChangeIndicator.d.ts +1 -1
- package/dist/inspector/canvas/StaleChangeIndicator.js +1 -1
- package/dist/inspector/canvas/ToolOptionsDrawer.d.ts +21 -0
- package/dist/inspector/canvas/ToolOptionsDrawer.js +77 -0
- package/dist/inspector/canvas/activeCanvasDocument.d.ts +3 -2
- package/dist/inspector/canvas/assets/eraser.svg +28 -0
- package/dist/inspector/canvas/assets/pen.svg +25 -0
- package/dist/inspector/canvas/canvasFrame.d.ts +16 -0
- package/dist/inspector/canvas/canvasFrame.js +10 -0
- package/dist/inspector/canvas/canvasGestures.d.ts +0 -1
- package/dist/inspector/canvas/canvasGestures.js +0 -6
- package/dist/inspector/canvas/canvasStore.d.ts +6 -0
- package/dist/inspector/canvas/canvasStore.js +36 -1
- package/dist/inspector/canvas/frameConnection.d.ts +48 -0
- package/dist/inspector/canvas/frameConnection.js +73 -0
- package/dist/inspector/canvas/frameProtocol.d.ts +18 -1
- package/dist/inspector/canvas/frameProtocol.js +36 -0
- package/dist/inspector/canvas/frameScrollbarStyles.d.ts +3 -0
- package/dist/inspector/canvas/frameScrollbarStyles.js +21 -0
- package/dist/inspector/canvas/pageSurface.d.ts +11 -0
- package/dist/inspector/canvas/pageSurface.js +34 -0
- package/dist/inspector/canvas/projection.d.ts +19 -9
- package/dist/inspector/canvas/projection.js +58 -31
- package/dist/inspector/canvas/rendererBootstrap.js +4 -1
- package/dist/inspector/canvas/rendererElementSelector.js +9 -1
- package/dist/inspector/canvas/rendererMessageRouter.d.ts +4 -5
- package/dist/inspector/canvas/rendererMessageRouter.js +12 -16
- package/dist/inspector/canvas/rendererSelectionProxy.d.ts +7 -1
- package/dist/inspector/canvas/rendererSelectionProxy.js +8 -36
- package/dist/inspector/canvas/rendererStylesheet.js +15 -0
- package/dist/inspector/canvas/sessionStore.d.ts +2 -2
- package/dist/inspector/canvas/sessionStore.js +12 -9
- package/dist/inspector/canvas/staleChangeDetector.d.ts +1 -1
- package/dist/inspector/canvas/staleChangeDetector.js +10 -22
- package/dist/inspector/changes/codecs.d.ts +2 -0
- package/dist/inspector/changes/codecs.js +27 -3
- package/dist/inspector/changes/draftPreview.d.ts +12 -0
- package/dist/inspector/changes/draftPreview.js +133 -0
- package/dist/inspector/changes/editModel.d.ts +18 -0
- package/dist/inspector/changes/editModel.js +16 -3
- package/dist/inspector/changes/managedStyleProjection.js +2 -1
- package/dist/inspector/changes/previewDiagnostics.js +1 -1
- package/dist/inspector/changes/types.d.ts +18 -1
- package/dist/inspector/comments/CommentsOverlay.js +18 -16
- package/dist/inspector/comments/element.js +10 -3
- package/dist/inspector/comments/store.js +3 -2
- package/dist/inspector/comments/verification.js +1 -1
- package/dist/inspector/componentSemantics/ComponentPropsSection.js +2 -1
- package/dist/inspector/componentSemantics/adapterRegistry.d.ts +6 -1
- package/dist/inspector/componentSemantics/adapterRegistry.js +23 -1
- package/dist/inspector/componentSemantics/changeAction.js +1 -1
- package/dist/inspector/componentSemantics/reactRuntime.d.ts +2 -1
- package/dist/inspector/componentSemantics/reactRuntime.js +38 -0
- package/dist/inspector/componentSemantics/runtimeBridge.d.ts +4 -2
- package/dist/inspector/componentSemantics/runtimeBridge.js +17 -0
- package/dist/inspector/componentSemantics/textBinding.d.ts +1 -2
- package/dist/inspector/componentSemantics/textBinding.js +4 -45
- package/dist/inspector/componentSemantics/types.d.ts +11 -0
- package/dist/inspector/drafts/draftStore.d.ts +109 -0
- package/dist/inspector/drafts/draftStore.js +451 -0
- package/dist/inspector/drafts/model.d.ts +9 -1
- package/dist/inspector/drafts/model.js +5 -1
- package/dist/inspector/drafts/store.d.ts +53 -15
- package/dist/inspector/drafts/store.js +143 -169
- package/dist/inspector/index.d.ts +2 -4
- package/dist/inspector/index.js +2 -264
- package/dist/inspector/inline-text/inlineTextEditor.d.ts +1 -1
- package/dist/inspector/inline-text/inlineTextEditor.js +1 -2
- package/dist/inspector/inline-text/textEditChange.d.ts +1 -1
- package/dist/inspector/inspection/bridge.d.ts +1 -1
- package/dist/inspector/inspection/bridge.js +2 -1
- package/dist/inspector/inspection/browserCssInspection.d.ts +8 -0
- package/dist/inspector/inspection/browserCssInspection.js +28 -6
- package/dist/inspector/inspection/inspectionSnapshot.d.ts +6 -0
- package/dist/inspector/inspection/inspectionSnapshot.js +10 -0
- package/dist/inspector/inspection/performanceDiagnostics.d.ts +18 -0
- package/dist/inspector/inspection/performanceDiagnostics.js +31 -0
- package/dist/inspector/inspection/useBrowserCssInspection.js +17 -38
- package/dist/inspector/iterations/capture.d.ts +2 -1
- package/dist/inspector/iterations/capture.js +2 -1
- package/dist/inspector/iterations/client.d.ts +2 -1
- package/dist/inspector/iterations/coordinator.d.ts +4 -3
- package/dist/inspector/iterations/coordinator.js +5 -3
- package/dist/inspector/iterations/prompt.js +9 -8
- package/dist/inspector/overlay/interactionStyles.js +17 -0
- package/dist/inspector/pageInspector.d.ts +42 -0
- package/dist/inspector/pageInspector.js +342 -0
- package/dist/inspector/projection/draftProjection.d.ts +10 -2
- package/dist/inspector/projection/draftProjection.js +9 -1
- package/dist/inspector/projection/managedStylesheet.js +24 -4
- package/dist/inspector/projection/renderedInstance.d.ts +15 -2
- package/dist/inspector/projection/renderedInstance.js +60 -7
- package/dist/inspector/projection/structuralProjection.d.ts +0 -9
- package/dist/inspector/projection/structuralProjection.js +16 -46
- package/dist/inspector/projection/textProjection.d.ts +8 -2
- package/dist/inspector/projection/textProjection.js +77 -24
- package/dist/inspector/prompt/clipboardHandoff.d.ts +14 -0
- package/dist/inspector/prompt/clipboardHandoff.js +47 -16
- package/dist/inspector/prompt/generatePrompt.d.ts +1 -1
- package/dist/inspector/prompt/generatePrompt.js +248 -148
- package/dist/inspector/prompt/promptDocument.d.ts +3 -0
- package/dist/inspector/prompt/promptDocument.js +8 -0
- package/dist/inspector/prompt/promptSettings.js +7 -5
- package/dist/inspector/runtime/inspectorStorage.d.ts +17 -0
- package/dist/inspector/runtime/inspectorStorage.js +42 -0
- package/dist/inspector/runtime/ownedStylesheets.d.ts +20 -0
- package/dist/inspector/runtime/ownedStylesheets.js +72 -0
- package/dist/inspector/runtime/runtimeConfig.d.ts +4 -0
- package/dist/inspector/runtime/runtimeConfig.js +10 -1
- package/dist/inspector/runtime/runtimeIdentityClaims.d.ts +29 -0
- package/dist/inspector/runtime/runtimeIdentityClaims.js +188 -0
- package/dist/inspector/runtime/staticHtmlRuntimeIdentity.d.ts +15 -0
- package/dist/inspector/runtime/staticHtmlRuntimeIdentity.js +113 -29
- package/dist/inspector/selection/editScope.d.ts +1 -0
- package/dist/inspector/selection/editScope.js +13 -2
- package/dist/inspector/selection/sourceSite.d.ts +12 -0
- package/dist/inspector/selection/sourceSite.js +28 -0
- package/dist/inspector/shell/AgentStatusButton.js +2 -2
- package/dist/inspector/shell/AppShell.js +6 -2
- package/dist/inspector/shell/ChangesLog.js +27 -9
- package/dist/inspector/shell/CopyPromptButton.js +74 -26
- package/dist/inspector/shell/EmptyState.js +3 -3
- package/dist/inspector/shell/InspectorShell.d.ts +0 -3
- package/dist/inspector/shell/InspectorShell.js +21 -13
- package/dist/inspector/shell/PeekOriginalButton.js +3 -3
- package/dist/inspector/shell/interactionStatePreview.d.ts +9 -0
- package/dist/inspector/shell/interactionStatePreview.js +112 -0
- package/dist/inspector/shell/originalPreview.d.ts +3 -2
- package/dist/inspector/shell/originalPreview.js +10 -37
- package/dist/inspector/shell/stateValue.js +2 -3
- package/dist/inspector/sketch/SketchEditor.js +5 -4
- package/dist/inspector/sketch/SketchFrameOverlay.d.ts +2 -1
- package/dist/inspector/sketch/SketchFrameOverlay.js +5 -4
- package/dist/inspector/sketch/SketchOverlay.d.ts +3 -5
- package/dist/inspector/sketch/SketchOverlay.js +139 -263
- package/dist/inspector/sketch/SketchPromptPanel.d.ts +1 -1
- package/dist/inspector/sketch/SketchPromptPanel.js +8 -1
- package/dist/inspector/sketch/SketchThumbnail.d.ts +5 -0
- package/dist/inspector/sketch/SketchThumbnail.js +18 -0
- package/dist/inspector/sketch/SketchWorkspace.d.ts +1 -2
- package/dist/inspector/sketch/SketchWorkspace.js +69 -34
- package/dist/inspector/sketch/capture.js +19 -3
- package/dist/inspector/sketch/copySketch.d.ts +10 -0
- package/dist/inspector/sketch/copySketch.js +52 -0
- package/dist/inspector/sketch/drawingTools.d.ts +17 -0
- package/dist/inspector/sketch/drawingTools.js +47 -0
- package/dist/inspector/sketch/frameHeading.d.ts +9 -0
- package/dist/inspector/sketch/frameHeading.js +30 -0
- package/dist/inspector/sketch/freehand.js +3 -3
- package/dist/inspector/sketch/geometry.d.ts +6 -0
- package/dist/inspector/sketch/geometry.js +19 -0
- package/dist/inspector/sketch/history.d.ts +30 -0
- package/dist/inspector/sketch/history.js +58 -0
- package/dist/inspector/sketch/interaction.d.ts +1 -3
- package/dist/inspector/sketch/interaction.js +6 -9
- package/dist/inspector/sketch/model.d.ts +35 -1
- package/dist/inspector/sketch/model.js +116 -61
- package/dist/inspector/sketch/persistence.d.ts +14 -0
- package/dist/inspector/sketch/persistence.js +63 -7
- package/dist/inspector/sketch/raster.d.ts +6 -2
- package/dist/inspector/sketch/raster.js +43 -17
- package/dist/inspector/sketch/store.d.ts +1 -1
- package/dist/inspector/sketch/store.js +1 -1
- package/dist/inspector/sketch/videoFrames.d.ts +6 -0
- package/dist/inspector/sketch/videoFrames.js +81 -0
- package/dist/inspector/styleEditors/BackgroundEditor.js +3 -2
- package/dist/inspector/styleEditors/BorderEditor.js +30 -130
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +8 -3
- package/dist/inspector/styleEditors/ImageBackgroundEditor.js +1 -1
- package/dist/inspector/styleEditors/LayoutSection.js +2 -2
- package/dist/inspector/styleEditors/borderModel.d.ts +42 -0
- package/dist/inspector/styleEditors/borderModel.js +114 -0
- package/dist/inspector/styleEditors/layoutValue.js +2 -3
- package/dist/inspector/tokens/TokenField.js +40 -7
- package/dist/inspector/tokens/TokensPanel.js +2 -1
- package/dist/inspector/tokens/editActions.d.ts +3 -3
- package/dist/inspector/tokens/editActions.js +15 -3
- package/dist/inspector/tokens/resolution/containerQueryEvaluator.d.ts +15 -0
- package/dist/inspector/tokens/resolution/containerQueryEvaluator.js +86 -0
- package/dist/inspector/tokens/resolution/cssomCollector.js +44 -9
- package/dist/inspector/tokens/resolution/nestedSelectors.d.ts +6 -0
- package/dist/inspector/tokens/resolution/nestedSelectors.js +67 -0
- package/dist/inspector/tokens/resolution/ruleMatching.js +23 -4
- package/dist/inspector/tokens/resolution.d.ts +6 -3
- package/dist/inspector/tokens/resolution.js +91 -112
- package/dist/inspector/tokens/tokenEdits.d.ts +1 -1
- package/dist/inspector/tokens/tokenEdits.js +1 -1
- package/dist/inspector/ui/InlineStyleWarning.js +2 -2
- package/dist/inspector/ui/InspectorStyle.d.ts +8 -0
- package/dist/inspector/ui/InspectorStyle.js +26 -0
- package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
- package/dist/inspector/ui/PopoverListbox.js +3 -3
- package/dist/inspector/ui/Toast.d.ts +14 -0
- package/dist/inspector/ui/Toast.js +30 -0
- package/dist/inspector/ui/assets/canvasIllustration.d.ts +2 -0
- package/dist/inspector/ui/assets/canvasIllustration.js +21 -0
- package/dist/inspector/ui/assets/gridIllustration.d.ts +2 -0
- package/dist/inspector/ui/assets/{grid.svg → gridIllustration.js} +3 -2
- package/dist/inspector/ui/customCursors.d.ts +5 -0
- package/dist/inspector/ui/customCursors.js +32 -6
- package/dist/inspector/ui/styles.js +12 -10
- package/dist/inspector/workspace/commands.js +1 -2
- package/dist/inspector/workspace/controller.js +1 -2
- package/dist/inspector/workspace/handoff.js +3 -2
- package/dist/inspector/workspace/sentItems.d.ts +16 -0
- package/dist/inspector/workspace/sentItems.js +33 -0
- package/dist/inspector/workspace/timeline.d.ts +24 -10
- package/dist/inspector/workspace/timeline.js +118 -96
- package/dist/internal/agent-protocol.d.ts +50 -13
- package/dist/internal/agent-protocol.js +145 -19
- package/dist/nudge-ui.mjs +81 -65
- package/dist/project/mcpPackage.d.ts +8 -0
- package/dist/project/mcpPackage.js +60 -0
- package/package.json +13 -3
- package/dist/inspector/canvas/measurementProjection.d.ts +0 -8
- package/dist/inspector/canvas/measurementProjection.js +0 -16
- package/dist/inspector/changes/changesLog.d.ts +0 -47
- package/dist/inspector/changes/changesLog.js +0 -247
- package/dist/inspector/changes/draftChanges.d.ts +0 -56
- package/dist/inspector/changes/draftChanges.js +0 -300
- package/dist/inspector/conformance/projection.d.ts +0 -2
- package/dist/inspector/conformance/projection.js +0 -3
- package/dist/inspector/inline-text/textChangeBoundary.d.ts +0 -6
- package/dist/inspector/inline-text/textChangeBoundary.js +0 -1
- package/dist/inspector/overlay/InspectorOverlay.d.ts +0 -5
- package/dist/inspector/overlay/InspectorOverlay.js +0 -332
- package/dist/inspector/overlay/elementSelector.d.ts +0 -1
- package/dist/inspector/overlay/elementSelector.js +0 -111
- package/dist/inspector/sketch/SketchChanges.d.ts +0 -2
- package/dist/inspector/sketch/SketchChanges.js +0 -72
- package/dist/inspector/sketch/SketchEntryButton.d.ts +0 -4
- package/dist/inspector/sketch/SketchEntryButton.js +0 -11
- package/dist/inspector/sketch/SketchLayersPanel.d.ts +0 -2
- package/dist/inspector/sketch/SketchLayersPanel.js +0 -71
|
@@ -1,16 +1,37 @@
|
|
|
1
|
-
import { isComponentChange, isElementChange, isTextContentChange, isTokenChange } from "../changes/
|
|
2
|
-
import { canonicalizeChanges,
|
|
3
|
-
import { getAnyPreviewDiagnostic } from "../changes/previewDiagnostics.js";
|
|
1
|
+
import { isComponentChange, isElementChange, isTextContentChange, isTokenChange } from "../changes/types.js";
|
|
2
|
+
import { canonicalizeChanges, tokenReference } from "../changes/model.js";
|
|
4
3
|
import { formatComponentPropBaseline, formatComponentPropValue, } from "../componentSemantics/changeModel.js";
|
|
5
4
|
import { boundRuntimeEvidence, isRuntimeGeneratedSource, normalizeRuntimeTag, normalizeRuntimeText, } from "../runtime/staticHtmlRuntimeIdentity.js";
|
|
6
5
|
import { getSourceCoordinatePolicy } from "../runtime/runtimeConfig.js";
|
|
7
6
|
import { DEFAULT_CUSTOM_INSTRUCTIONS } from "./promptSettings.js";
|
|
7
|
+
import { PROMPT_TITLE } from "./promptDocument.js";
|
|
8
8
|
import { getCommentsForTarget } from "../comments/store.js";
|
|
9
9
|
import { documentTarget } from "../drafts/model.js";
|
|
10
10
|
const EMPTY_SENTINEL = "<!-- No changes to export -->\n\nThe changes log is empty. Make a change in the Nudge UI inspector first.";
|
|
11
|
+
/** Labels describe the observed tag, without guessing its role in the page. */
|
|
12
|
+
function elementLabel(tag) {
|
|
13
|
+
const normalized = normalizeRuntimeTag(tag);
|
|
14
|
+
const labels = {
|
|
15
|
+
p: "Paragraph", button: "Button", a: "Link", img: "Image", input: "Input",
|
|
16
|
+
textarea: "Text area", select: "Select", form: "Form", nav: "Navigation", section: "Section",
|
|
17
|
+
};
|
|
18
|
+
const label = /^h[1-6]$/.test(normalized) ? "Heading" : labels[normalized] ?? "Element";
|
|
19
|
+
return normalized ? `${label} (${promptText(`<${normalized}>`)})` : label;
|
|
20
|
+
}
|
|
21
|
+
/** Runtime IDs identify one DOM element; authored sites can render several items. */
|
|
22
|
+
function elementPromptKey(cid, src, scope, locator) {
|
|
23
|
+
if (!src || isRuntimeGeneratedSource(src))
|
|
24
|
+
return JSON.stringify(["runtime", cid]);
|
|
25
|
+
return JSON.stringify([cid, src, scope, scope === "rendered-instance" ? [
|
|
26
|
+
locator?.occurrence, locator?.props, locator?.ariaLabel ?? null, locator?.invocation?.callsiteId ?? null,
|
|
27
|
+
] : null]);
|
|
28
|
+
}
|
|
29
|
+
function refPromptKey(ref) {
|
|
30
|
+
return elementPromptKey(ref.sourceSite.cid, ref.sourceSite.src, "rendered-instance", ref.locator);
|
|
31
|
+
}
|
|
11
32
|
function renderPrompt(sections, customInstructions) {
|
|
12
33
|
const lines = [
|
|
13
|
-
|
|
34
|
+
PROMPT_TITLE,
|
|
14
35
|
];
|
|
15
36
|
for (const section of sections) {
|
|
16
37
|
const content = [...section.lines];
|
|
@@ -47,6 +68,7 @@ function groupElementChanges(changes) {
|
|
|
47
68
|
line: change.source.line || change.line,
|
|
48
69
|
column: change.column ?? 0,
|
|
49
70
|
runtimeEvidence: change.runtimeEvidence,
|
|
71
|
+
sourceEvidence: change.sourceEvidence,
|
|
50
72
|
instanceOverride,
|
|
51
73
|
changes: [],
|
|
52
74
|
};
|
|
@@ -56,28 +78,24 @@ function groupElementChanges(changes) {
|
|
|
56
78
|
}
|
|
57
79
|
return [...map.values()];
|
|
58
80
|
}
|
|
59
|
-
function conflictSuffix(rec) {
|
|
60
|
-
const result = getAnyPreviewDiagnostic(changeKey(rec))?.result;
|
|
61
|
-
if (!result || result.status === "applied")
|
|
62
|
-
return "";
|
|
63
|
-
return ` — preview conflict: browser computed \`${result.computedValue || "(no value)"}\` (${result.reason ?? "cascade conflict"}); implement the requested value without assuming \`!important\``;
|
|
64
|
-
}
|
|
65
81
|
function elementChangeLine(rec) {
|
|
82
|
+
const state = rec.state && rec.state !== "base" ? ` (${rec.state})` : "";
|
|
83
|
+
const property = `\`${rec.property}\`${state}`;
|
|
66
84
|
if (rec.newToken && rec.oldToken) {
|
|
67
|
-
return `-
|
|
85
|
+
return `- **Change:** ${property}: \`${rec.oldToken.name}\` → \`${rec.newToken.name}\``;
|
|
68
86
|
}
|
|
69
87
|
if (rec.newToken) {
|
|
70
88
|
const before = rec.oldRawValue !== undefined ? `\`${rec.oldRawValue}\` → ` : "";
|
|
71
89
|
// Describe exactly the value the managed stylesheet wrote for this swap
|
|
72
90
|
// (adapter literal or var(cssName)); a bare var(name) would be invalid
|
|
73
91
|
// CSS for adapter tokens whose names are not custom-property names.
|
|
74
|
-
return `-
|
|
92
|
+
return `- **Change:** ${property}: ${before}\`${tokenReference(rec.newToken)}\``;
|
|
75
93
|
}
|
|
76
94
|
if (rec.rawValue !== undefined) {
|
|
77
95
|
const before = rec.oldRawValue !== undefined ? `\`${rec.oldRawValue}\` → ` : "";
|
|
78
|
-
return `-
|
|
96
|
+
return `- **Change:** ${property}: ${before}\`${rec.rawValue}\``;
|
|
79
97
|
}
|
|
80
|
-
return `-
|
|
98
|
+
return `- **Change:** ${property}: (no value)`;
|
|
81
99
|
}
|
|
82
100
|
function sourceIntentLine(rec) {
|
|
83
101
|
if (!rec.sourceProperty || rec.sourceProperty === rec.property)
|
|
@@ -86,27 +104,22 @@ function sourceIntentLine(rec) {
|
|
|
86
104
|
return ` - Source declaration (CSSOM): \`${rec.sourceProperty}${authored}\`; preview edit uses physical \`${rec.property}\``;
|
|
87
105
|
}
|
|
88
106
|
function tokenChangeLine(rec) {
|
|
89
|
-
return `- \`${rec.tokenName}\` (${rec.contextLabel}, ${rec.file}:${rec.line}): \`${rec.oldRawValue}\` → \`${rec.rawValue}
|
|
107
|
+
return `- **Change:** \`${rec.tokenName}\` (${rec.contextLabel}, ${rec.file}:${rec.line}): \`${rec.oldRawValue}\` → \`${rec.rawValue}\``;
|
|
90
108
|
}
|
|
91
109
|
function componentChangeLine(rec) {
|
|
92
110
|
const sourceGuidance = rec.authoredAs === "literal"
|
|
93
111
|
? rec.property === "children"
|
|
94
112
|
? "replace the literal child text"
|
|
95
|
-
: "
|
|
113
|
+
: ""
|
|
96
114
|
: rec.authoredAs === "default"
|
|
97
115
|
? "add the prop at this invocation"
|
|
98
116
|
: `preserve the authored ${rec.authoredAs} and update its source logic`;
|
|
99
|
-
|
|
100
|
-
? "this rendered item only"
|
|
101
|
-
: "all outputs at this source site";
|
|
102
|
-
return `- \`${rec.property}\`: \`${formatComponentPropBaseline(rec.before)}\` → \`${formatComponentPropValue(rec.after)}\` — ${sourceGuidance}; scope: ${scope}`;
|
|
117
|
+
return `- **Change:** \`${rec.property}\`: \`${formatComponentPropBaseline(rec.before)}\` → \`${formatComponentPropValue(rec.after)}\`${sourceGuidance ? ` — ${sourceGuidance}` : ""}`;
|
|
103
118
|
}
|
|
104
|
-
function
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
return "preserve the expression and update its source logic";
|
|
109
|
-
return "update the source that produces this rendered copy";
|
|
119
|
+
function editScopeLine(scope) {
|
|
120
|
+
return scope === "rendered-instance"
|
|
121
|
+
? " - Apply to this selected item only; keep other instances unchanged."
|
|
122
|
+
: " - Apply to all items rendered at this source location.";
|
|
110
123
|
}
|
|
111
124
|
/** Keep arbitrary rendered copy valid inside the Markdown prompt. */
|
|
112
125
|
function promptText(value) {
|
|
@@ -116,35 +129,62 @@ function promptText(value) {
|
|
|
116
129
|
const fence = "~".repeat(Math.max(3, longestTildeRun + 1));
|
|
117
130
|
return `${fence}text\n${value}\n${fence}`;
|
|
118
131
|
}
|
|
119
|
-
function
|
|
120
|
-
const
|
|
121
|
-
? `${target.sourceSite.cid} (source unknown; runtime-created DOM)`
|
|
122
|
-
: `${target.sourceSite.cid} (\`${target.sourceSite.src}\`)`;
|
|
123
|
-
const lines = [
|
|
124
|
-
` - Source site: ${sourceLabel}`,
|
|
125
|
-
];
|
|
126
|
-
const props = boundRuntimeEvidence(target.props);
|
|
127
|
-
const ariaLabel = boundRuntimeEvidence(target.ariaLabel);
|
|
128
|
-
if (props)
|
|
129
|
-
lines.push(` - Props evidence: ${promptText(props)}`);
|
|
130
|
-
if (ariaLabel)
|
|
131
|
-
lines.push(` - Accessible name evidence: ${promptText(ariaLabel)}`);
|
|
132
|
-
return lines;
|
|
133
|
-
}
|
|
134
|
-
function textScopeLines(change) {
|
|
135
|
-
const scope = change.scope === "source-site" ? "source site / all outputs" : "this rendered item only";
|
|
136
|
-
const lines = [` - Scope: ${scope}`];
|
|
132
|
+
function textBindingLines(change, sourceLocation) {
|
|
133
|
+
const lines = [];
|
|
137
134
|
if (change.evidence) {
|
|
138
|
-
|
|
135
|
+
const evidence = change.evidence;
|
|
136
|
+
const location = evidence.callsiteId === sourceLocation ? "" : ` at ${promptText(evidence.callsiteId)}`;
|
|
137
|
+
lines.push(` - Text source: ${promptText(`${evidence.componentName}.${evidence.property}`)}${location} (${evidence.mountedCount} rendered instances).`);
|
|
139
138
|
}
|
|
140
139
|
return lines;
|
|
141
140
|
}
|
|
141
|
+
/** Shared presentation for captured facts; these do not change target matching. */
|
|
142
|
+
function targetEvidenceLines(evidence, example = false) {
|
|
143
|
+
const text = normalizeRuntimeText(evidence.text ?? null);
|
|
144
|
+
const ariaLabel = boundRuntimeEvidence(evidence.ariaLabel ?? null);
|
|
145
|
+
const elementId = boundRuntimeEvidence(evidence.elementId ?? null);
|
|
146
|
+
const props = boundRuntimeEvidence(evidence.props ?? null);
|
|
147
|
+
const lines = [];
|
|
148
|
+
if (elementId && !example)
|
|
149
|
+
lines.push(`- Element ID: ${promptText(elementId)}`);
|
|
150
|
+
if (text)
|
|
151
|
+
lines.push(`- ${example ? "Example text" : "Text"}: ${promptText(text)}`);
|
|
152
|
+
if (ariaLabel)
|
|
153
|
+
lines.push(`- ${example ? "Example accessible name" : "Accessible name"}: ${promptText(ariaLabel)}`);
|
|
154
|
+
if (props && props !== `id:${elementId}`)
|
|
155
|
+
lines.push(`- ${example ? "Example props" : "Props"}: ${promptText(props)}`);
|
|
156
|
+
if (evidence.domPath && !example)
|
|
157
|
+
lines.push(`- DOM path: ${promptText(evidence.domPath)}`);
|
|
158
|
+
return lines;
|
|
159
|
+
}
|
|
160
|
+
function readableTargetLabel(text, tag) {
|
|
161
|
+
if (tag)
|
|
162
|
+
return elementLabel(tag);
|
|
163
|
+
const bounded = normalizeRuntimeText(text);
|
|
164
|
+
return bounded ? `Text: ${inlineHeadingText(bounded)}` : "Element";
|
|
165
|
+
}
|
|
166
|
+
/** Use an inline code span even when copy contains Markdown delimiters. */
|
|
167
|
+
function inlineHeadingText(text) {
|
|
168
|
+
const runs = text.match(/`+/g) ?? [];
|
|
169
|
+
const delimiter = "`".repeat(Math.max(0, ...runs.map((run) => run.length)) + 1);
|
|
170
|
+
const padding = text.startsWith("`") || text.endsWith("`") ? " " : "";
|
|
171
|
+
return `${delimiter}${padding}${text}${padding}${delimiter}`;
|
|
172
|
+
}
|
|
142
173
|
function boundedEvidence(value) {
|
|
143
174
|
return boundRuntimeEvidence(value);
|
|
144
175
|
}
|
|
145
176
|
function sourceSiteLabel(ref) {
|
|
146
|
-
if (isRuntimeGeneratedSource(ref.sourceSite.src)) {
|
|
147
|
-
|
|
177
|
+
if (!ref.sourceSite.src || isRuntimeGeneratedSource(ref.sourceSite.src)) {
|
|
178
|
+
const text = boundedEvidence(ref.locator.text);
|
|
179
|
+
const ariaLabel = boundedEvidence(ref.locator.ariaLabel ?? null);
|
|
180
|
+
const props = boundedEvidence(ref.locator.props);
|
|
181
|
+
if (ariaLabel)
|
|
182
|
+
return `the element named ${promptText(ariaLabel)}`;
|
|
183
|
+
if (text)
|
|
184
|
+
return `the element with text ${promptText(text)}`;
|
|
185
|
+
if (props)
|
|
186
|
+
return `the element with props ${promptText(props)}`;
|
|
187
|
+
return "the element";
|
|
148
188
|
}
|
|
149
189
|
return `${ref.sourceSite.cid} (${ref.sourceSite.src})`;
|
|
150
190
|
}
|
|
@@ -159,25 +199,27 @@ function structuralParentIdentityKey(ref) {
|
|
|
159
199
|
locator.ariaLabel ?? null,
|
|
160
200
|
]);
|
|
161
201
|
}
|
|
162
|
-
function
|
|
163
|
-
const evidence = group.runtimeEvidence;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
202
|
+
function elementEvidenceLines(group, example = false) {
|
|
203
|
+
const evidence = group.runtimeEvidence ?? group.sourceEvidence;
|
|
204
|
+
return evidence ? targetEvidenceLines(evidence, example)
|
|
205
|
+
: group.instanceOverride ? targetEvidenceLines(group.instanceOverride.target.locator, example) : [];
|
|
206
|
+
}
|
|
207
|
+
/** Combine identical final requests for individually selected elements. */
|
|
208
|
+
function selectedStyleGroupKey(group) {
|
|
209
|
+
const requests = group.changes.map((change) => JSON.stringify([elementChangeLine(change), sourceIntentLine(change)])).sort();
|
|
210
|
+
return JSON.stringify(["selected-styles", requests]);
|
|
211
|
+
}
|
|
212
|
+
function selectedItemLine(group, sourceLocation) {
|
|
213
|
+
const facts = elementEvidenceLines(group).map((line) => line.slice(2));
|
|
214
|
+
if (sourceLocation)
|
|
215
|
+
facts.unshift(`Source: ${promptText(sourceLocation)}`);
|
|
216
|
+
const invocation = group.instanceOverride?.target.locator.invocation;
|
|
217
|
+
if (invocation)
|
|
218
|
+
facts.push(`Component usage: ${promptText(invocation.callsiteId)}`);
|
|
219
|
+
return ` - ${facts.join("; ") || "Selected element"}`;
|
|
220
|
+
}
|
|
221
|
+
function setHeading(label, tag) {
|
|
222
|
+
return tag ? `${label} (${promptText(`<${normalizeRuntimeTag(tag)}>`)})` : label;
|
|
181
223
|
}
|
|
182
224
|
/**
|
|
183
225
|
* Coordinate precision follows each element's identity origin, not the
|
|
@@ -194,7 +236,7 @@ function hasExactAuthoredCoordinates(policy, cid, file) {
|
|
|
194
236
|
return policy.exactFileExtensions.some((extension) => file.endsWith(extension));
|
|
195
237
|
}
|
|
196
238
|
/**
|
|
197
|
-
* Formats one source location for a prompt
|
|
239
|
+
* Formats one source location for a prompt. Exact origins keep the
|
|
198
240
|
* authored column; line-only origins — and sites whose column is unknown —
|
|
199
241
|
* degrade to `file:line` rather than fabricating precision.
|
|
200
242
|
*/
|
|
@@ -204,27 +246,19 @@ function formatSourceLocation(policy, source) {
|
|
|
204
246
|
}
|
|
205
247
|
return `${source.file}:${source.line}`;
|
|
206
248
|
}
|
|
207
|
-
function elementGroupSource(group, policy) {
|
|
208
|
-
if (group.runtimeEvidence) {
|
|
209
|
-
return group.runtimeEvidence.reason === "unannotated"
|
|
210
|
-
? "source unknown; no authored location available"
|
|
211
|
-
: "source unknown; runtime-created DOM";
|
|
212
|
-
}
|
|
213
|
-
return formatSourceLocation(policy, group);
|
|
214
|
-
}
|
|
215
249
|
function renderedInstanceDescription(ref) {
|
|
216
250
|
const text = boundedEvidence(ref.locator.text);
|
|
217
251
|
const ariaLabel = boundedEvidence(ref.locator.ariaLabel ?? null);
|
|
218
252
|
const props = boundedEvidence(ref.locator.props);
|
|
219
|
-
const source = isRuntimeGeneratedSource(ref.sourceSite.src)
|
|
220
|
-
?
|
|
221
|
-
:
|
|
253
|
+
const source = ref.sourceSite.src && !isRuntimeGeneratedSource(ref.sourceSite.src)
|
|
254
|
+
? ` (${ref.sourceSite.src})`
|
|
255
|
+
: "";
|
|
222
256
|
if (text)
|
|
223
|
-
return `text ${promptText(text)}
|
|
257
|
+
return `text ${promptText(text)}${source}`;
|
|
224
258
|
if (ariaLabel)
|
|
225
|
-
return `the element named ${promptText(ariaLabel)}
|
|
259
|
+
return `the element named ${promptText(ariaLabel)}${source}`;
|
|
226
260
|
if (props)
|
|
227
|
-
return `the element with props ${promptText(props)}
|
|
261
|
+
return `the element with props ${promptText(props)}${source}`;
|
|
228
262
|
// With no semantic evidence, the ordinal is the only way to distinguish
|
|
229
263
|
// repeated outputs from the same source site.
|
|
230
264
|
return `rendered instance ${ref.locator.occurrence + 1} of ${sourceSiteLabel(ref)}`;
|
|
@@ -232,7 +266,10 @@ function renderedInstanceDescription(ref) {
|
|
|
232
266
|
function structuralChangeLine(change) {
|
|
233
267
|
const target = renderedInstanceDescription(change.target);
|
|
234
268
|
if (change.kind === "delete") {
|
|
235
|
-
|
|
269
|
+
const invocation = change.target.locator.invocation;
|
|
270
|
+
return invocation
|
|
271
|
+
? `- Remove the ${promptText(`<${invocation.componentName}>`)} invocation at ${invocation.callsiteId} from the source; keep its other invocations. It renders ${target}.`
|
|
272
|
+
: `- Remove ${target} from the source.`;
|
|
236
273
|
}
|
|
237
274
|
const before = change.destination.before;
|
|
238
275
|
const sourceParent = sourceSiteLabel(change.source.parent);
|
|
@@ -254,6 +291,7 @@ function renderedInstanceKey(ref) {
|
|
|
254
291
|
locator.props,
|
|
255
292
|
locator.text,
|
|
256
293
|
locator.ariaLabel ?? null,
|
|
294
|
+
locator.invocation?.callsiteId ?? null,
|
|
257
295
|
]);
|
|
258
296
|
}
|
|
259
297
|
/** Export final intent, not the gesture history required to replay the preview. */
|
|
@@ -328,82 +366,144 @@ export function generatePrompt(changes, frameworkHints, structuralChanges = [],
|
|
|
328
366
|
lines: tokenChanges.map(tokenChangeLine),
|
|
329
367
|
});
|
|
330
368
|
}
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
lines.push(componentChangeLine(change));
|
|
338
|
-
lines.push(` - Component contract: \`${target.componentId}\``);
|
|
339
|
-
if (change.evidence) {
|
|
340
|
-
lines.push(` - Rendered evidence: ${change.evidence.mountedCount} mounted outputs`);
|
|
341
|
-
if (change.evidence.props)
|
|
342
|
-
lines.push(` - Props evidence: ${promptText(boundedEvidence(change.evidence.props) ?? "")}`);
|
|
343
|
-
if (change.evidence.ariaLabel)
|
|
344
|
-
lines.push(` - Accessible name evidence: ${promptText(boundedEvidence(change.evidence.ariaLabel) ?? "")}`);
|
|
345
|
-
lines.push(` - Before text: ${promptText(change.evidence.beforeText)}`);
|
|
346
|
-
}
|
|
347
|
-
lines.push("");
|
|
369
|
+
const groups = new Map();
|
|
370
|
+
function groupFor(key, heading, tag) {
|
|
371
|
+
let group = groups.get(key);
|
|
372
|
+
if (!group) {
|
|
373
|
+
group = { heading, details: new Set(), lines: [] };
|
|
374
|
+
groups.set(key, group);
|
|
348
375
|
}
|
|
349
|
-
|
|
376
|
+
if (tag)
|
|
377
|
+
group.heading = elementLabel(tag);
|
|
378
|
+
return group;
|
|
350
379
|
}
|
|
351
|
-
|
|
352
|
-
const
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
380
|
+
for (const change of componentChanges) {
|
|
381
|
+
const target = change.target;
|
|
382
|
+
const evidence = change.evidence;
|
|
383
|
+
const scope = change.scope ?? "source-site";
|
|
384
|
+
const key = JSON.stringify(["component", target.callsiteId, scope, scope === "rendered-instance"
|
|
385
|
+
? [evidence?.occurrence, evidence?.props, evidence?.ariaLabel] : null]);
|
|
386
|
+
const group = groupFor(key, `${target.componentName} component`);
|
|
387
|
+
group.lines.push(componentChangeLine(change));
|
|
388
|
+
group.lines.push(editScopeLine(scope));
|
|
389
|
+
group.details.add(`- Source: ${promptText(formatSourceLocation(coordinatePolicy, target))}`);
|
|
390
|
+
if (evidence) {
|
|
391
|
+
group.details.add(`- Rendered instances: ${evidence.mountedCount}`);
|
|
392
|
+
if (evidence.props)
|
|
393
|
+
group.details.add(`- Props: ${promptText(boundedEvidence(evidence.props) ?? "")}`);
|
|
394
|
+
if (evidence.ariaLabel)
|
|
395
|
+
group.details.add(`- Accessible name: ${promptText(boundedEvidence(evidence.ariaLabel) ?? "")}`);
|
|
396
|
+
group.details.add(`- Before text: ${promptText(evidence.beforeText)}`);
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
const selectedSets = new Map();
|
|
400
|
+
for (const element of elementGroups) {
|
|
401
|
+
if (!element.instanceOverride && element.changes[0]?.sourceInstanceCount !== 1)
|
|
402
|
+
continue;
|
|
403
|
+
const key = selectedStyleGroupKey(element);
|
|
404
|
+
const selected = selectedSets.get(key) ?? [];
|
|
405
|
+
selected.push(element);
|
|
406
|
+
selectedSets.set(key, selected);
|
|
407
|
+
}
|
|
408
|
+
const exportedSets = new Set();
|
|
409
|
+
for (const element of elementGroups) {
|
|
410
|
+
const first = element.changes[0];
|
|
411
|
+
const runtimeCreated = !!element.runtimeEvidence || !element.file || isRuntimeGeneratedSource(element.file);
|
|
412
|
+
const src = runtimeCreated ? "" : `${element.file}:${element.line}:${element.column}`;
|
|
413
|
+
const key = element.instanceOverride ? refPromptKey(element.instanceOverride.target)
|
|
414
|
+
: first.scope === "rendered-instance" && !runtimeCreated ? `style:${element.key}`
|
|
415
|
+
: elementPromptKey(element.cid, src, "source-site");
|
|
416
|
+
const tag = element.runtimeEvidence?.tagName ?? element.sourceEvidence?.tagName;
|
|
417
|
+
const label = readableTargetLabel(element.runtimeEvidence?.text ?? element.sourceEvidence?.text ?? null, tag);
|
|
418
|
+
const selectedKey = selectedStyleGroupKey(element);
|
|
419
|
+
const selected = element.instanceOverride || first.sourceInstanceCount === 1 ? selectedSets.get(selectedKey) ?? [] : [];
|
|
420
|
+
if (selected.length > 1) {
|
|
421
|
+
if (exportedSets.has(selectedKey))
|
|
422
|
+
continue;
|
|
423
|
+
exportedSets.add(selectedKey);
|
|
424
|
+
const tags = new Set(selected.map((item) => item.runtimeEvidence?.tagName ?? item.sourceEvidence?.tagName));
|
|
425
|
+
const group = groupFor(selectedKey, setHeading("Selected elements", tags.size === 1 ? tag : undefined));
|
|
426
|
+
const locations = selected.map((item) => !item.runtimeEvidence && item.file && !isRuntimeGeneratedSource(item.file)
|
|
427
|
+
? formatSourceLocation(coordinatePolicy, item) : null);
|
|
428
|
+
const sharedLocation = new Set(locations).size === 1 ? locations[0] : null;
|
|
429
|
+
if (sharedLocation)
|
|
430
|
+
group.details.add(`- Source: ${promptText(sharedLocation)}`);
|
|
431
|
+
group.details.add(["- Selected items:", ...selected.map((item, index) => selectedItemLine(item, sharedLocation ? null : locations[index] ?? null))].join("\n"));
|
|
432
|
+
for (const change of element.changes) {
|
|
433
|
+
group.lines.push(`${elementChangeLine(change)} for these selected items.`);
|
|
362
434
|
const intent = sourceIntentLine(change);
|
|
363
435
|
if (intent)
|
|
364
|
-
lines.push(intent);
|
|
436
|
+
group.lines.push(intent);
|
|
365
437
|
}
|
|
366
|
-
|
|
438
|
+
continue;
|
|
367
439
|
}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
const
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
lines.push(
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
lines.push("");
|
|
440
|
+
const linked = !element.instanceOverride && !runtimeCreated && first.scope !== "rendered-instance"
|
|
441
|
+
&& (element.changes.some((change) => (change.sourceInstanceCount ?? 1) > 1)
|
|
442
|
+
|| first.scope === "source-site" && first.sourceInstanceCount === undefined);
|
|
443
|
+
const group = groupFor(key, label, tag);
|
|
444
|
+
if (linked)
|
|
445
|
+
group.heading = setHeading("Linked elements", tag);
|
|
446
|
+
if (!runtimeCreated) {
|
|
447
|
+
group.details.add(`- Source: \`${formatSourceLocation(coordinatePolicy, element)}\``);
|
|
448
|
+
}
|
|
449
|
+
for (const detail of elementEvidenceLines(element, linked))
|
|
450
|
+
group.details.add(detail);
|
|
451
|
+
for (const change of element.changes) {
|
|
452
|
+
const targetSet = linked ? " for all items rendered at this source location."
|
|
453
|
+
: element.instanceOverride ? " for this selected item." : "";
|
|
454
|
+
group.lines.push(`${elementChangeLine(change)}${targetSet}`);
|
|
455
|
+
const intent = sourceIntentLine(change);
|
|
456
|
+
if (intent)
|
|
457
|
+
group.lines.push(intent);
|
|
387
458
|
}
|
|
388
|
-
sections.push({ heading: "Rendered text changes", lines });
|
|
389
459
|
}
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
460
|
+
for (const change of textChanges) {
|
|
461
|
+
const target = change.target;
|
|
462
|
+
const runtimeCreated = !target.sourceSite.src || isRuntimeGeneratedSource(target.sourceSite.src);
|
|
463
|
+
const scope = change.scope ?? "rendered-instance";
|
|
464
|
+
const key = elementPromptKey(target.sourceSite.cid, target.sourceSite.src, scope, {
|
|
465
|
+
kind: "evidence", occurrence: target.occurrence, props: target.props, text: target.beforeText, ariaLabel: target.ariaLabel,
|
|
394
466
|
});
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
heading: "Element comments",
|
|
399
|
-
lines: comments.flatMap((comment) => [
|
|
400
|
-
`### <${comment.tag}> (${comment.target.sourceSite.src || "source unknown"})`,
|
|
401
|
-
`- Page: ${promptText(comment.route)}`,
|
|
402
|
-
`- Target: ${renderedInstanceDescription(comment.target)}`,
|
|
403
|
-
`- Note: ${promptText(comment.note)}`,
|
|
404
|
-
"",
|
|
405
|
-
]),
|
|
467
|
+
const group = groupFor(key, readableTargetLabel(change.before, target.tagName), target.tagName);
|
|
468
|
+
const sourceLocation = runtimeCreated ? null : formatSourceLocation(coordinatePolicy, {
|
|
469
|
+
cid: target.sourceSite.cid, ...change.source,
|
|
406
470
|
});
|
|
471
|
+
if (sourceLocation)
|
|
472
|
+
group.details.add(`- Source: ${promptText(sourceLocation)}`);
|
|
473
|
+
group.lines.push(`- **Change:** Replace text ${promptText(change.before)} with ${promptText(change.after)}.`);
|
|
474
|
+
if (change.scope)
|
|
475
|
+
group.lines.push(editScopeLine(change.scope));
|
|
476
|
+
if (change.authoredAs === "expression")
|
|
477
|
+
group.lines.push(" - Preserve the expression and update its source logic.");
|
|
478
|
+
group.lines.push(...textBindingLines(change, sourceLocation));
|
|
479
|
+
for (const detail of targetEvidenceLines({
|
|
480
|
+
...target, text: group.heading.startsWith("Text:") ? null : target.beforeText,
|
|
481
|
+
}))
|
|
482
|
+
group.details.add(detail);
|
|
483
|
+
}
|
|
484
|
+
for (const change of structuralIntent) {
|
|
485
|
+
const ref = change.target;
|
|
486
|
+
const group = groupFor(refPromptKey(ref), readableTargetLabel(ref.locator.text));
|
|
487
|
+
group.lines.push(structuralChangeLine(change));
|
|
488
|
+
}
|
|
489
|
+
for (const comment of comments) {
|
|
490
|
+
const ref = comment.target;
|
|
491
|
+
const runtimeCreated = !ref.sourceSite.src || isRuntimeGeneratedSource(ref.sourceSite.src);
|
|
492
|
+
const group = groupFor(refPromptKey(ref), elementLabel(comment.tag), comment.tag);
|
|
493
|
+
if (!runtimeCreated) {
|
|
494
|
+
const parsed = /^(.*):(\d+):(\d+)$/.exec(ref.sourceSite.src);
|
|
495
|
+
const location = parsed ? formatSourceLocation(coordinatePolicy, {
|
|
496
|
+
cid: ref.sourceSite.cid, file: parsed[1], line: Number(parsed[2]), column: Number(parsed[3]),
|
|
497
|
+
}) : ref.sourceSite.src;
|
|
498
|
+
group.details.add(`- Source: ${promptText(location)}`);
|
|
499
|
+
}
|
|
500
|
+
group.details.add(`- Page: ${promptText(comment.route)}`);
|
|
501
|
+
for (const detail of targetEvidenceLines(ref.locator))
|
|
502
|
+
group.details.add(detail);
|
|
503
|
+
group.lines.push(`- **Note:** ${promptText(comment.note)}`);
|
|
504
|
+
}
|
|
505
|
+
for (const group of groups.values()) {
|
|
506
|
+
sections.push({ heading: group.heading, lines: group.details.size > 0 ? [...group.details, "", ...group.lines] : group.lines });
|
|
407
507
|
}
|
|
408
508
|
return renderPrompt(sections, customInstructions);
|
|
409
509
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export const PROMPT_TITLE = "# Requested design changes";
|
|
2
|
+
/** Keeps the title first when a host adds page or iteration instructions. */
|
|
3
|
+
export function withPromptContext(context, prompt) {
|
|
4
|
+
const body = prompt === PROMPT_TITLE || prompt.startsWith(`${PROMPT_TITLE}\n`)
|
|
5
|
+
? prompt.slice(PROMPT_TITLE.length).trimStart()
|
|
6
|
+
: prompt;
|
|
7
|
+
return [PROMPT_TITLE, "", ...context, ...(body.trim() ? ["", body] : [])].join("\n");
|
|
8
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { inspectorStorage } from "../runtime/inspectorStorage.js";
|
|
2
|
+
import { isDemoRuntime } from "../runtime/runtimeConfig.js";
|
|
1
3
|
/** The default guidance included in every generated prompt. */
|
|
2
4
|
export const DEFAULT_CUSTOM_INSTRUCTIONS = "Preserve existing tokens, logical properties, and CSS intent while applying these rendered changes.";
|
|
3
5
|
function storageKey(projectId) {
|
|
@@ -5,10 +7,10 @@ function storageKey(projectId) {
|
|
|
5
7
|
}
|
|
6
8
|
/** Reads the project-scoped custom prompt instructions from browser storage. */
|
|
7
9
|
export function loadCustomInstructions(projectId) {
|
|
8
|
-
if (
|
|
10
|
+
if (isDemoRuntime())
|
|
9
11
|
return DEFAULT_CUSTOM_INSTRUCTIONS;
|
|
10
12
|
try {
|
|
11
|
-
return
|
|
13
|
+
return inspectorStorage()?.getItem(storageKey(projectId)) ?? DEFAULT_CUSTOM_INSTRUCTIONS;
|
|
12
14
|
}
|
|
13
15
|
catch {
|
|
14
16
|
return DEFAULT_CUSTOM_INSTRUCTIONS;
|
|
@@ -16,10 +18,10 @@ export function loadCustomInstructions(projectId) {
|
|
|
16
18
|
}
|
|
17
19
|
/** Persists custom prompt instructions without making storage availability a prerequisite for editing. */
|
|
18
20
|
export function saveCustomInstructions(projectId, instructions) {
|
|
19
|
-
if (
|
|
20
|
-
return;
|
|
21
|
+
if (isDemoRuntime())
|
|
22
|
+
return; // demo runtimes never persist
|
|
21
23
|
try {
|
|
22
|
-
|
|
24
|
+
inspectorStorage()?.setItem(storageKey(projectId), instructions);
|
|
23
25
|
}
|
|
24
26
|
catch {
|
|
25
27
|
// Storage can be disabled or full; the in-memory UI state still applies.
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/** The synchronous subset of `Storage` that inspector persistence uses. */
|
|
2
|
+
export interface InspectorStorage {
|
|
3
|
+
getItem(key: string): string | null;
|
|
4
|
+
setItem(key: string, value: string): void;
|
|
5
|
+
removeItem(key: string): void;
|
|
6
|
+
/** Reloads an asynchronous backend under the lease, before controller hydration. */
|
|
7
|
+
refresh?(): Promise<void>;
|
|
8
|
+
}
|
|
9
|
+
/** Replaces `localStorage` for inspector-owned state, for example with extension storage. */
|
|
10
|
+
export declare function configureInspectorStorage(storage: InspectorStorage | null): void;
|
|
11
|
+
/** The configured storage, else `localStorage`; null where neither exists. */
|
|
12
|
+
export declare function inspectorStorage(): InspectorStorage | null;
|
|
13
|
+
/**
|
|
14
|
+
* Serves reads from a snapshot loaded before boot and forwards each write.
|
|
15
|
+
* Asynchronous backends stay behind the synchronous interface callers expect.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createMirroredStorage(initial: Readonly<Record<string, string>>, persist: (key: string, value: string | null) => void, read?: () => Promise<Readonly<Record<string, string>>>): InspectorStorage;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
let configured = null;
|
|
2
|
+
/** Replaces `localStorage` for inspector-owned state, for example with extension storage. */
|
|
3
|
+
export function configureInspectorStorage(storage) {
|
|
4
|
+
configured = storage;
|
|
5
|
+
}
|
|
6
|
+
/** The configured storage, else `localStorage`; null where neither exists. */
|
|
7
|
+
export function inspectorStorage() {
|
|
8
|
+
if (configured)
|
|
9
|
+
return configured;
|
|
10
|
+
return typeof localStorage === "undefined" ? null : localStorage;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Serves reads from a snapshot loaded before boot and forwards each write.
|
|
14
|
+
* Asynchronous backends stay behind the synchronous interface callers expect.
|
|
15
|
+
*/
|
|
16
|
+
export function createMirroredStorage(initial, persist, read) {
|
|
17
|
+
const values = new Map(Object.entries(initial));
|
|
18
|
+
let refreshId = 0;
|
|
19
|
+
return {
|
|
20
|
+
getItem: (key) => values.get(key) ?? null,
|
|
21
|
+
setItem(key, value) {
|
|
22
|
+
values.set(key, value);
|
|
23
|
+
persist(key, value);
|
|
24
|
+
},
|
|
25
|
+
removeItem(key) {
|
|
26
|
+
if (!values.delete(key))
|
|
27
|
+
return;
|
|
28
|
+
persist(key, null);
|
|
29
|
+
},
|
|
30
|
+
...(read ? {
|
|
31
|
+
async refresh() {
|
|
32
|
+
const id = ++refreshId;
|
|
33
|
+
const snapshot = await read();
|
|
34
|
+
if (id !== refreshId)
|
|
35
|
+
return;
|
|
36
|
+
values.clear();
|
|
37
|
+
for (const [key, value] of Object.entries(snapshot))
|
|
38
|
+
values.set(key, value);
|
|
39
|
+
},
|
|
40
|
+
} : {}),
|
|
41
|
+
};
|
|
42
|
+
}
|