nudge-ui 0.2.2 → 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.
- package/README.md +164 -46
- package/dist/client.mjs +260 -61
- package/dist/css/value-semantics/colorSemantics.d.ts +4 -0
- package/dist/css/value-semantics/colorSemantics.js +17 -0
- package/dist/hosts/astro/astroRuntimeConfig.js +1 -0
- package/dist/hosts/astro/clientTransport.js +38 -32
- package/dist/hosts/astro/integration.js +5 -1
- package/dist/hosts/astro/markdownIdentity.d.ts +28 -0
- package/dist/hosts/astro/markdownIdentity.js +81 -0
- package/dist/hosts/next/manifest.js +1 -1
- package/dist/hosts/next/sidecar.js +5 -1
- package/dist/hosts/static/manifest.js +1 -1
- package/dist/hosts/static/server.js +3 -0
- package/dist/hosts/vite/vite.d.ts +3 -0
- package/dist/hosts/vite/vite.js +24 -4
- package/dist/inspector/agent/McpConnectionDialog.js +13 -19
- package/dist/inspector/agent/activity.d.ts +18 -0
- package/dist/inspector/agent/activity.js +70 -0
- package/dist/inspector/agent/client.d.ts +2 -0
- package/dist/inspector/agent/client.js +29 -1
- package/dist/inspector/agent/connectionStatus.js +1 -1
- package/dist/inspector/agent/httpTransport.js +9 -2
- package/dist/inspector/agent/protocol.d.ts +6 -3
- package/dist/inspector/agent/protocol.js +1 -1
- package/dist/inspector/agent/verification.d.ts +15 -7
- package/dist/inspector/agent/verification.js +113 -41
- package/dist/inspector/canvas/AgentActivityOverlay.d.ts +13 -0
- package/dist/inspector/canvas/AgentActivityOverlay.js +111 -0
- package/dist/inspector/canvas/CanvasCard.d.ts +9 -2
- package/dist/inspector/canvas/CanvasCard.js +135 -38
- package/dist/inspector/canvas/CanvasElementOverlay.js +166 -16
- package/dist/inspector/canvas/CanvasToolIcons.d.ts +10 -0
- package/dist/inspector/canvas/CanvasToolIcons.js +20 -0
- package/dist/inspector/canvas/CanvasToolbar.js +8 -4
- package/dist/inspector/canvas/CanvasWorkspace.js +270 -122
- package/dist/inspector/canvas/StaleChangeIndicator.js +3 -13
- package/dist/inspector/canvas/agentPresentation.d.ts +8 -8
- package/dist/inspector/canvas/agentPresentation.js +64 -52
- package/dist/inspector/canvas/canvasGestures.d.ts +1 -1
- package/dist/inspector/canvas/canvasGestures.js +1 -1
- package/dist/inspector/canvas/canvasStore.d.ts +46 -82
- package/dist/inspector/canvas/canvasStore.js +251 -284
- package/dist/inspector/canvas/frameContent.d.ts +21 -0
- package/dist/inspector/canvas/frameContent.js +44 -0
- package/dist/inspector/canvas/frameProtocol.d.ts +21 -4
- package/dist/inspector/canvas/frameProtocol.js +3 -3
- package/dist/inspector/canvas/frameScrollbarStyles.d.ts +3 -0
- package/dist/inspector/canvas/frameScrollbarStyles.js +21 -0
- package/dist/inspector/canvas/keyboardShortcuts.d.ts +3 -1
- package/dist/inspector/canvas/keyboardShortcuts.js +4 -4
- package/dist/inspector/canvas/layout.d.ts +52 -0
- package/dist/inspector/canvas/layout.js +106 -0
- package/dist/inspector/canvas/projection.d.ts +15 -5
- package/dist/inspector/canvas/projection.js +69 -13
- package/dist/inspector/canvas/rendererBootstrap.js +32 -4
- package/dist/inspector/canvas/rendererElementSelector.js +47 -6
- package/dist/inspector/canvas/rendererStylesheet.js +2 -2
- package/dist/inspector/canvas/resizeHandleScale.js +1 -1
- package/dist/inspector/canvas/sessionStore.d.ts +11 -26
- package/dist/inspector/canvas/sessionStore.js +43 -183
- package/dist/inspector/canvas/staleChangeDetector.js +9 -3
- package/dist/inspector/canvas/useCanvasLayoutAnimation.d.ts +5 -0
- package/dist/inspector/canvas/useCanvasLayoutAnimation.js +107 -0
- package/dist/inspector/canvas/viewStore.d.ts +38 -0
- package/dist/inspector/canvas/viewStore.js +104 -0
- package/dist/inspector/canvas/wheelPan.d.ts +8 -0
- package/dist/inspector/canvas/wheelPan.js +8 -0
- package/dist/inspector/changes/changesLog.d.ts +9 -6
- package/dist/inspector/changes/changesLog.js +48 -32
- package/dist/inspector/changes/codecs.d.ts +1 -0
- package/dist/inspector/changes/codecs.js +10 -1
- package/dist/inspector/changes/draftChanges.d.ts +58 -0
- package/dist/inspector/changes/draftChanges.js +304 -0
- package/dist/inspector/changes/editModel.d.ts +10 -0
- package/dist/inspector/changes/editModel.js +9 -2
- package/dist/inspector/changes/previewDiagnostics.d.ts +4 -2
- package/dist/inspector/changes/previewDiagnostics.js +11 -25
- package/dist/inspector/changes/types.d.ts +7 -0
- package/dist/inspector/clientManifest.d.ts +1 -1
- package/dist/inspector/comments/CommentsOverlay.d.ts +2 -0
- package/dist/inspector/comments/CommentsOverlay.js +250 -0
- package/dist/inspector/comments/element.d.ts +6 -0
- package/dist/inspector/comments/element.js +54 -0
- package/dist/inspector/comments/store.d.ts +29 -0
- package/dist/inspector/comments/store.js +121 -0
- package/dist/inspector/comments/verification.d.ts +5 -0
- package/dist/inspector/comments/verification.js +52 -0
- package/dist/inspector/componentSemantics/adapterRegistry.d.ts +6 -1
- package/dist/inspector/componentSemantics/adapterRegistry.js +23 -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/types.d.ts +11 -0
- package/dist/inspector/drafts/model.d.ts +35 -0
- package/dist/inspector/drafts/model.js +45 -0
- package/dist/inspector/drafts/store.d.ts +28 -0
- package/dist/inspector/drafts/store.js +231 -0
- package/dist/inspector/index.js +20 -27
- package/dist/inspector/inline-text/inlineTextEditor.js +7 -0
- package/dist/inspector/inspection/useBrowserCssInspection.js +15 -1
- package/dist/inspector/iterations/capture.d.ts +2 -0
- package/dist/inspector/iterations/capture.js +174 -0
- package/dist/inspector/iterations/client.d.ts +7 -0
- package/dist/inspector/iterations/client.js +52 -0
- package/dist/inspector/iterations/coordinator.d.ts +15 -0
- package/dist/inspector/iterations/coordinator.js +130 -0
- package/dist/inspector/iterations/prompt.d.ts +3 -0
- package/dist/inspector/iterations/prompt.js +26 -0
- package/dist/inspector/overlay/DomNavigator.js +21 -15
- package/dist/inspector/overlay/interactionStyles.js +3 -1
- package/dist/inspector/overlay/spacingGestures.d.ts +13 -1
- package/dist/inspector/overlay/spacingGestures.js +82 -5
- package/dist/inspector/projection/{workspaceProjection.d.ts → draftProjection.d.ts} +6 -6
- package/dist/inspector/projection/{workspaceProjection.js → draftProjection.js} +4 -4
- package/dist/inspector/projection/renderedInstance.d.ts +17 -3
- package/dist/inspector/projection/renderedInstance.js +62 -11
- package/dist/inspector/projection/structuralProjection.d.ts +2 -1
- package/dist/inspector/projection/structuralProjection.js +21 -17
- package/dist/inspector/projection/textProjection.d.ts +2 -1
- package/dist/inspector/projection/textProjection.js +64 -15
- package/dist/inspector/prompt/PromptSettingsDialog.js +1 -5
- package/dist/inspector/prompt/clipboardHandoff.d.ts +15 -18
- package/dist/inspector/prompt/clipboardHandoff.js +97 -143
- package/dist/inspector/prompt/generatePrompt.d.ts +4 -1
- package/dist/inspector/prompt/generatePrompt.js +22 -17
- package/dist/inspector/runtime/runtimeConfig.d.ts +9 -0
- package/dist/inspector/runtime/runtimeConfig.js +24 -0
- package/dist/inspector/selection/sourceSite.d.ts +12 -0
- package/dist/inspector/selection/sourceSite.js +28 -0
- package/dist/inspector/settings/SettingsDialog.js +21 -8
- package/dist/inspector/shell/AgentStatusButton.d.ts +11 -0
- package/dist/inspector/shell/AgentStatusButton.js +36 -0
- package/dist/inspector/shell/AppShell.js +4 -2
- package/dist/inspector/shell/ChangesLog.js +46 -26
- package/dist/inspector/shell/CopyPromptButton.js +184 -93
- package/dist/inspector/shell/EmptyState.js +9 -8
- package/dist/inspector/shell/InspectorShell.js +13 -21
- package/dist/inspector/shell/PeekOriginalButton.js +24 -23
- package/dist/inspector/shell/originalPreview.js +5 -5
- package/dist/inspector/shell/shortcuts.d.ts +3 -0
- package/dist/inspector/shell/shortcuts.js +11 -0
- package/dist/inspector/sketch/SketchEditor.js +14 -14
- package/dist/inspector/sketch/SketchEntryButton.js +1 -1
- package/dist/inspector/sketch/SketchFrameOverlay.d.ts +3 -1
- package/dist/inspector/sketch/SketchFrameOverlay.js +11 -8
- package/dist/inspector/sketch/SketchLayersPanel.js +13 -5
- package/dist/inspector/sketch/SketchOverlay.js +7 -7
- package/dist/inspector/sketch/SketchWorkspace.js +18 -12
- package/dist/inspector/sketch/interaction.d.ts +1 -0
- package/dist/inspector/sketch/interaction.js +8 -4
- package/dist/inspector/sketch/model.d.ts +5 -0
- package/dist/inspector/sketch/model.js +6 -2
- package/dist/inspector/sketch/raster.d.ts +1 -2
- package/dist/inspector/sketch/raster.js +68 -4
- package/dist/inspector/styleEditors/BackgroundEditor.d.ts +4 -0
- package/dist/inspector/styleEditors/BackgroundEditor.js +195 -0
- package/dist/inspector/styleEditors/BorderEditor.js +49 -78
- package/dist/inspector/styleEditors/BorderRadiusEditor.js +6 -1
- package/dist/inspector/styleEditors/BoxShadowEditor.d.ts +1 -2
- package/dist/inspector/styleEditors/BoxShadowEditor.js +132 -4
- package/dist/inspector/styleEditors/ColorPicker.d.ts +5 -1
- package/dist/inspector/styleEditors/ColorPicker.js +10 -4
- package/dist/inspector/styleEditors/GapField.js +3 -5
- package/dist/inspector/styleEditors/GridChildSection.d.ts +1 -1
- package/dist/inspector/styleEditors/GridChildSection.js +18 -9
- package/dist/inspector/styleEditors/GridPicker.d.ts +1 -1
- package/dist/inspector/styleEditors/GridPicker.js +8 -2
- package/dist/inspector/styleEditors/GridSection.js +1 -1
- package/dist/inspector/styleEditors/GridValueField.js +19 -14
- package/dist/inspector/styleEditors/ImageBackgroundEditor.d.ts +18 -0
- package/dist/inspector/styleEditors/ImageBackgroundEditor.js +118 -0
- package/dist/inspector/styleEditors/LayoutBlockedIndicator.d.ts +2 -2
- package/dist/inspector/styleEditors/LayoutBlockedIndicator.js +3 -3
- package/dist/inspector/styleEditors/LayoutComboField.js +6 -4
- package/dist/inspector/styleEditors/LayoutDropdown.js +6 -3
- package/dist/inspector/styleEditors/LayoutSection.js +32 -11
- package/dist/inspector/styleEditors/SpacingBox.js +7 -5
- package/dist/inspector/styleEditors/Typography.js +11 -3
- package/dist/inspector/styleEditors/backgroundLayers.d.ts +24 -0
- package/dist/inspector/styleEditors/backgroundLayers.js +51 -0
- package/dist/inspector/styleEditors/gradientValue.d.ts +17 -0
- package/dist/inspector/styleEditors/gradientValue.js +114 -0
- package/dist/inspector/styleEditors/inlineAuthored.d.ts +10 -0
- package/dist/inspector/styleEditors/inlineAuthored.js +114 -0
- package/dist/inspector/styleEditors/layoutValue.d.ts +0 -8
- package/dist/inspector/styleEditors/layoutValue.js +0 -42
- package/dist/inspector/styleEditors/nudgeValue.d.ts +2 -2
- package/dist/inspector/styleEditors/nudgeValue.js +36 -8
- package/dist/inspector/tokens/TokenField.d.ts +7 -0
- package/dist/inspector/tokens/TokenField.js +35 -25
- package/dist/inspector/tokens/TokenLabel.js +5 -14
- package/dist/inspector/tokens/editActions.d.ts +1 -1
- package/dist/inspector/tokens/editActions.js +7 -1
- package/dist/inspector/ui/AtRuleContext.js +4 -6
- package/dist/inspector/ui/Button.d.ts +6 -2
- package/dist/inspector/ui/Button.js +4 -3
- package/dist/inspector/ui/ControlSurface.d.ts +2 -2
- package/dist/inspector/ui/ControlSurface.js +4 -3
- package/dist/inspector/ui/InlineStyleWarning.d.ts +11 -0
- package/dist/inspector/ui/InlineStyleWarning.js +13 -0
- package/dist/inspector/ui/InspectorPopover.d.ts +2 -1
- package/dist/inspector/ui/InspectorPopover.js +4 -5
- package/dist/inspector/ui/PopoverListbox.d.ts +2 -1
- package/dist/inspector/ui/PopoverListbox.js +8 -7
- package/dist/inspector/ui/SegmentedControl.d.ts +2 -1
- package/dist/inspector/ui/SegmentedControl.js +2 -2
- package/dist/inspector/ui/Select.d.ts +6 -1
- package/dist/inspector/ui/Select.js +7 -11
- package/dist/inspector/ui/SideValuesField.js +1 -1
- package/dist/inspector/ui/Tooltip.d.ts +26 -0
- package/dist/inspector/ui/Tooltip.js +32 -0
- package/dist/inspector/ui/classNames.d.ts +2 -0
- package/dist/inspector/ui/classNames.js +4 -0
- package/dist/inspector/ui/customCursors.d.ts +23 -0
- package/dist/inspector/ui/customCursors.js +122 -0
- package/dist/inspector/ui/dragCursor.d.ts +0 -2
- package/dist/inspector/ui/dragCursor.js +2 -10
- package/dist/inspector/ui/portalContainer.d.ts +2 -0
- package/dist/inspector/ui/portalContainer.js +6 -0
- package/dist/inspector/ui/styles.js +29 -21
- package/dist/inspector/workspace/commands.d.ts +9 -0
- package/dist/inspector/workspace/commands.js +82 -0
- package/dist/inspector/workspace/controller.d.ts +4 -0
- package/dist/inspector/workspace/controller.js +66 -0
- package/dist/inspector/workspace/handoff.d.ts +42 -0
- package/dist/inspector/workspace/handoff.js +115 -0
- package/dist/inspector/workspace/sentItems.d.ts +19 -0
- package/dist/inspector/workspace/sentItems.js +51 -0
- package/dist/inspector/workspace/timeline.d.ts +30 -0
- package/dist/inspector/workspace/timeline.js +108 -0
- package/dist/internal/agent-protocol.d.ts +19 -2
- package/dist/internal/agent-protocol.js +13 -0
- package/dist/nudge-ui.mjs +558 -311
- package/dist/project/artifacts.d.ts +13 -0
- package/dist/project/artifacts.js +253 -0
- package/dist/transport/artifacts.d.ts +8 -0
- package/dist/transport/artifacts.js +9 -0
- package/dist/transport/editor.js +3 -1
- package/dist/transport/routes.d.ts +2 -0
- package/dist/transport/routes.js +2 -0
- package/package.json +7 -3
- package/dist/inspector/changes/workspaceChanges.d.ts +0 -47
- package/dist/inspector/changes/workspaceChanges.js +0 -265
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { getCanvasCards, getFocusedCardId, getSelectedCardId, subscribe as subscribeCanvas, setCanvasHistoryActivator, } from "../canvas/canvasStore.js";
|
|
2
|
+
import { subscribeCanvasView } from "../canvas/viewStore.js";
|
|
3
|
+
import { loadComments } from "../comments/store.js";
|
|
4
|
+
import { loadDrafts, activateDraft, activateDraftForCard, draftIdForCard, subscribeDrafts } from "../drafts/store.js";
|
|
5
|
+
import { applicationTarget, targetKey } from "../drafts/model.js";
|
|
6
|
+
import { getActiveDraftChanges } from "../changes/draftChanges.js";
|
|
7
|
+
import { hydrateSession, enableAutoSave, scheduleAutoSave, flushAutoSave, resetAutoSave, } from "../canvas/sessionStore.js";
|
|
8
|
+
import { releaseLease } from "../canvas/workspaceLease.js";
|
|
9
|
+
import { subscribeClipboardHandoff } from "../prompt/clipboardHandoff.js";
|
|
10
|
+
import { getNudgeUiRuntimeConfig } from "../runtime/runtimeConfig.js";
|
|
11
|
+
import { releaseDetachedIterations } from "./timeline.js";
|
|
12
|
+
export function startWorkspaceController() {
|
|
13
|
+
const config = getNudgeUiRuntimeConfig();
|
|
14
|
+
const persistent = config.demo !== true;
|
|
15
|
+
loadComments();
|
|
16
|
+
loadDrafts(config.projectId, { persistent });
|
|
17
|
+
const result = persistent ? hydrateSession() : { restored: false };
|
|
18
|
+
const save = persistent ? scheduleAutoSave : () => undefined;
|
|
19
|
+
let active = "";
|
|
20
|
+
const synchronize = () => {
|
|
21
|
+
const cardId = getSelectedCardId() ?? getFocusedCardId() ?? getCanvasCards()[0]?.id;
|
|
22
|
+
const draftId = (cardId && draftIdForCard(cardId)) || targetKey(applicationTarget());
|
|
23
|
+
const next = `${cardId ?? ""}\n${draftId}`;
|
|
24
|
+
if (next === active && getActiveDraftChanges().draftId === draftId)
|
|
25
|
+
return;
|
|
26
|
+
active = next;
|
|
27
|
+
activateDraft(draftId, cardId);
|
|
28
|
+
};
|
|
29
|
+
synchronize();
|
|
30
|
+
setCanvasHistoryActivator(activateDraftForCard);
|
|
31
|
+
if (persistent)
|
|
32
|
+
enableAutoSave();
|
|
33
|
+
const stopCanvas = subscribeCanvas(() => {
|
|
34
|
+
synchronize();
|
|
35
|
+
save();
|
|
36
|
+
});
|
|
37
|
+
const stopView = subscribeCanvasView(save);
|
|
38
|
+
const stopDrafts = subscribeDrafts(save);
|
|
39
|
+
const stopClipboard = subscribeClipboardHandoff(save);
|
|
40
|
+
const beforeunload = () => {
|
|
41
|
+
flushAutoSave();
|
|
42
|
+
releaseLease();
|
|
43
|
+
};
|
|
44
|
+
const pagehide = (event) => {
|
|
45
|
+
flushAutoSave();
|
|
46
|
+
if (!event.persisted)
|
|
47
|
+
releaseDetachedIterations();
|
|
48
|
+
};
|
|
49
|
+
if (persistent)
|
|
50
|
+
window.addEventListener("beforeunload", beforeunload);
|
|
51
|
+
window.addEventListener("pagehide", pagehide);
|
|
52
|
+
return {
|
|
53
|
+
restored: result.restored,
|
|
54
|
+
dispose() {
|
|
55
|
+
flushAutoSave();
|
|
56
|
+
stopCanvas();
|
|
57
|
+
stopView();
|
|
58
|
+
stopDrafts();
|
|
59
|
+
stopClipboard();
|
|
60
|
+
window.removeEventListener("beforeunload", beforeunload);
|
|
61
|
+
window.removeEventListener("pagehide", pagehide);
|
|
62
|
+
setCanvasHistoryActivator(() => undefined);
|
|
63
|
+
resetAutoSave();
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { type AgentClient } from "../agent/client.js";
|
|
2
|
+
import type { AgentSketchMetadata } from "../agent/protocol.js";
|
|
3
|
+
import { type HandoffSnapshot } from "../agent/verification.js";
|
|
4
|
+
import { type ElementComment } from "../comments/store.js";
|
|
5
|
+
import { type FrameworkHints } from "../prompt/generatePrompt.js";
|
|
6
|
+
import { type SketchClipboardHandoffSnapshot } from "../sketch/handoff.js";
|
|
7
|
+
import type { SketchQueueItem } from "../sketch/model.js";
|
|
8
|
+
export interface HandoffRequest {
|
|
9
|
+
readonly cardId: string | null;
|
|
10
|
+
readonly hints: FrameworkHints;
|
|
11
|
+
readonly customInstructions: string;
|
|
12
|
+
readonly pendingSketches: readonly SketchQueueItem[];
|
|
13
|
+
}
|
|
14
|
+
export interface PreparedHandoff extends HandoffSnapshot {
|
|
15
|
+
readonly prompt: string;
|
|
16
|
+
readonly comments: readonly ElementComment[];
|
|
17
|
+
readonly sketches: SketchClipboardHandoffSnapshot | null;
|
|
18
|
+
readonly sketchMetadata: readonly AgentSketchMetadata[];
|
|
19
|
+
}
|
|
20
|
+
export type HandoffOutcome = {
|
|
21
|
+
readonly kind: "sent";
|
|
22
|
+
} | {
|
|
23
|
+
readonly kind: "copied";
|
|
24
|
+
}
|
|
25
|
+
/** The agent rejected sketch attachments; the prompt can still be copied. */
|
|
26
|
+
| {
|
|
27
|
+
readonly kind: "sketch-failed";
|
|
28
|
+
readonly handoff: PreparedHandoff;
|
|
29
|
+
readonly error: string;
|
|
30
|
+
};
|
|
31
|
+
type PromptDispatcher = Pick<AgentClient, "dispatchPrompt" | "getSnapshot">;
|
|
32
|
+
export declare function prepareHandoff(request: HandoffRequest): Promise<PreparedHandoff>;
|
|
33
|
+
export declare function prepareIterationImplementationPrompt(request: HandoffRequest): Promise<string>;
|
|
34
|
+
/** Copies a prompt and records the checkpoint that later clipboard verification reconciles. */
|
|
35
|
+
export declare function copyHandoff(handoff: PreparedHandoff): Promise<void>;
|
|
36
|
+
/**
|
|
37
|
+
* Sends the prompt to a connected agent, or copies it when no agent is given.
|
|
38
|
+
* A failed send without sketches falls back to the clipboard; a failed sketch
|
|
39
|
+
* send is returned so the user can choose to copy without the images.
|
|
40
|
+
*/
|
|
41
|
+
export declare function deliverHandoff(handoff: PreparedHandoff, projectId: string, agent: PromptDispatcher | null): Promise<HandoffOutcome>;
|
|
42
|
+
export {};
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { beginActivityDispatch } from "../agent/activity.js";
|
|
2
|
+
import { createPromptRevision } from "../agent/client.js";
|
|
3
|
+
import { captureHandoffOwner, discardAgentDispatch, recordAgentDispatch } from "../agent/verification.js";
|
|
4
|
+
import { htmlIterationImplementationPrompt, htmlIterationPrompt } from "../iterations/prompt.js";
|
|
5
|
+
import { getCanvasCardLabel, getCanvasCards } from "../canvas/canvasStore.js";
|
|
6
|
+
import { getCommentsForTarget, markCommentsHandedOff } from "../comments/store.js";
|
|
7
|
+
import { applicationTarget } from "../drafts/model.js";
|
|
8
|
+
import { prepareHtmlIterationHandoff } from "../iterations/coordinator.js";
|
|
9
|
+
import { getRegisteredFrames } from "../canvas/projection.js";
|
|
10
|
+
import { getDraftChanges } from "../changes/draftChanges.js";
|
|
11
|
+
import { recordClipboardHandoff } from "../prompt/clipboardHandoff.js";
|
|
12
|
+
import { copyToClipboard } from "../prompt/copyToClipboard.js";
|
|
13
|
+
import { generatePrompt } from "../prompt/generatePrompt.js";
|
|
14
|
+
import { createSketchAttachments, createSketchHandoffSnapshot, sketchMetadataForHandoff, } from "../sketch/handoff.js";
|
|
15
|
+
import { markSketchesDispatching, markSketchesHandingOff, settleSketchDispatch } from "../sketch/store.js";
|
|
16
|
+
async function prepareDraftHandoff(request) {
|
|
17
|
+
const owner = captureHandoffOwner(request.cardId);
|
|
18
|
+
const target = owner.target;
|
|
19
|
+
const comments = [...getCommentsForTarget(target)];
|
|
20
|
+
let { changes, structuralChanges } = getDraftChanges(owner.draftId);
|
|
21
|
+
if (target.kind === "html") {
|
|
22
|
+
const frame = request.cardId ? getRegisteredFrames().get(request.cardId) : undefined;
|
|
23
|
+
if (!request.cardId || !frame)
|
|
24
|
+
throw new Error("The HTML iteration is still loading. Try again when the frame is ready.");
|
|
25
|
+
({ changes, structuralChanges } = await prepareHtmlIterationHandoff(request.cardId, target.artifactId, frame));
|
|
26
|
+
}
|
|
27
|
+
const sketches = createSketchHandoffSnapshot(request.pendingSketches);
|
|
28
|
+
const sketchMetadata = sketchMetadataForHandoff(sketches);
|
|
29
|
+
return { owner, changes: [...changes], structuralChanges: [...structuralChanges], comments, sketches, sketchMetadata };
|
|
30
|
+
}
|
|
31
|
+
export async function prepareHandoff(request) {
|
|
32
|
+
const handoff = await prepareDraftHandoff(request);
|
|
33
|
+
const { owner: { target }, changes, structuralChanges, sketchMetadata, comments } = handoff;
|
|
34
|
+
const hints = target.kind === "html" ? { ...request.hints, framework: "HTML" } : request.hints;
|
|
35
|
+
const generated = generatePrompt([...changes], hints, structuralChanges, request.customInstructions, sketchMetadata, comments);
|
|
36
|
+
const prompt = target.kind === "html"
|
|
37
|
+
? htmlIterationPrompt(target.artifactId, generated, changes.length + structuralChanges.length + comments.length === 0, sketchMetadata.length > 0, request.customInstructions)
|
|
38
|
+
: `Page: ${target.route}\nApply the requested changes to this page. Shared source may affect other pages.\n\n${generated}`;
|
|
39
|
+
return { ...handoff, prompt };
|
|
40
|
+
}
|
|
41
|
+
export async function prepareIterationImplementationPrompt(request) {
|
|
42
|
+
const cards = getCanvasCards();
|
|
43
|
+
const card = cards.find((candidate) => candidate.id === request.cardId);
|
|
44
|
+
if (card?.content.kind !== "iteration")
|
|
45
|
+
throw new Error("Select an HTML iteration to implement in the app.");
|
|
46
|
+
const label = getCanvasCardLabel(card, cards);
|
|
47
|
+
const page = applicationTarget(card.content.sourceUrl).route;
|
|
48
|
+
const { changes, structuralChanges, sketchMetadata, comments } = await prepareDraftHandoff(request);
|
|
49
|
+
const instructions = request.customInstructions.trim();
|
|
50
|
+
const generated = changes.length + structuralChanges.length + sketchMetadata.length + comments.length > 0
|
|
51
|
+
? generatePrompt([...changes], request.hints, structuralChanges, instructions, sketchMetadata, comments)
|
|
52
|
+
: instructions ? `## Custom instructions\n\n${instructions}` : "";
|
|
53
|
+
return htmlIterationImplementationPrompt(card.content.artifactId, label, page, generated);
|
|
54
|
+
}
|
|
55
|
+
/** Copies a prompt and records the checkpoint that later clipboard verification reconciles. */
|
|
56
|
+
export async function copyHandoff(handoff) {
|
|
57
|
+
await copyToClipboard(handoff.prompt);
|
|
58
|
+
markCommentsHandedOff(handoff.comments);
|
|
59
|
+
const { owner, changes, structuralChanges } = handoff;
|
|
60
|
+
if (owner.target.kind === "application" && changes.length + structuralChanges.length > 0) {
|
|
61
|
+
recordClipboardHandoff(changes, structuralChanges, owner);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Sends the prompt to a connected agent, or copies it when no agent is given.
|
|
66
|
+
* A failed send without sketches falls back to the clipboard; a failed sketch
|
|
67
|
+
* send is returned so the user can choose to copy without the images.
|
|
68
|
+
*/
|
|
69
|
+
export async function deliverHandoff(handoff, projectId, agent) {
|
|
70
|
+
if (agent) {
|
|
71
|
+
const outcome = await dispatchHandoff(handoff, projectId, agent);
|
|
72
|
+
if (outcome)
|
|
73
|
+
return outcome;
|
|
74
|
+
}
|
|
75
|
+
await copyHandoff(handoff);
|
|
76
|
+
return { kind: "copied" };
|
|
77
|
+
}
|
|
78
|
+
async function dispatchHandoff(handoff, projectId, agent) {
|
|
79
|
+
const { owner, changes, structuralChanges, sketches } = handoff;
|
|
80
|
+
const revision = createPromptRevision(changes, structuralChanges, [...handoff.sketchMetadata, ...handoff.comments]);
|
|
81
|
+
const entries = sketches?.entries.map((entry) => ({ id: entry.id, revision: entry.revision })) ?? [];
|
|
82
|
+
recordAgentDispatch(revision, changes, structuralChanges, owner);
|
|
83
|
+
const clientDispatchId = sketches?.localBatchId ?? `dispatch-${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
84
|
+
try {
|
|
85
|
+
let attachments = undefined;
|
|
86
|
+
if (sketches) {
|
|
87
|
+
await markSketchesDispatching(entries, revision, sketches.localBatchId);
|
|
88
|
+
attachments = await createSketchAttachments(sketches);
|
|
89
|
+
}
|
|
90
|
+
beginActivityDispatch(projectId, clientDispatchId, owner.frameIds, owner.target);
|
|
91
|
+
const response = await agent.dispatchPrompt(handoff.prompt, revision, {
|
|
92
|
+
clientDispatchId,
|
|
93
|
+
...(attachments === undefined ? {} : { attachments }),
|
|
94
|
+
});
|
|
95
|
+
if (response) {
|
|
96
|
+
markCommentsHandedOff(handoff.comments);
|
|
97
|
+
if (sketches)
|
|
98
|
+
await markSketchesHandingOff(entries, revision, sketches.localBatchId, response.request.requestId);
|
|
99
|
+
return { kind: "sent" };
|
|
100
|
+
}
|
|
101
|
+
discardAgentDispatch(revision);
|
|
102
|
+
if (!sketches)
|
|
103
|
+
return null;
|
|
104
|
+
await settleSketchDispatch(revision, "failed", undefined, sketches.localBatchId);
|
|
105
|
+
const latest = agent.getSnapshot();
|
|
106
|
+
return { kind: "sketch-failed", handoff, error: latest.request?.error ?? latest.error ?? "The agent did not accept the sketch attachments." };
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
discardAgentDispatch(revision);
|
|
110
|
+
if (!sketches)
|
|
111
|
+
return null;
|
|
112
|
+
await settleSketchDispatch(revision, "failed", undefined, sketches.localBatchId).catch(() => undefined);
|
|
113
|
+
return { kind: "sketch-failed", handoff, error: error instanceof Error ? error.message : "The sketch could not be sent to the agent." };
|
|
114
|
+
}
|
|
115
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { DraftContents } from "../changes/draftChanges.js";
|
|
2
|
+
export interface EditHistoryEntry {
|
|
3
|
+
readonly kind: "edit";
|
|
4
|
+
readonly draftId: string;
|
|
5
|
+
readonly group?: symbol;
|
|
6
|
+
readonly before: DraftContents;
|
|
7
|
+
readonly after: DraftContents;
|
|
8
|
+
readonly activate?: () => void;
|
|
9
|
+
}
|
|
10
|
+
export interface CanvasHistoryEntry {
|
|
11
|
+
readonly kind: "canvas";
|
|
12
|
+
readonly cardIds: readonly string[];
|
|
13
|
+
readonly undo: () => void;
|
|
14
|
+
readonly redo: () => void;
|
|
15
|
+
readonly dispose: () => void;
|
|
16
|
+
}
|
|
17
|
+
export declare function subscribeTimeline(listener: () => void): () => void;
|
|
18
|
+
export declare function timelineStatus(): {
|
|
19
|
+
canUndo: boolean;
|
|
20
|
+
canRedo: boolean;
|
|
21
|
+
};
|
|
22
|
+
export declare function recordEdit(entry: Omit<EditHistoryEntry, "kind" | "group">, equal: (a: DraftContents, b: DraftContents) => boolean): void;
|
|
23
|
+
export declare function recordCanvasChange(entry: Omit<CanvasHistoryEntry, "kind">): void;
|
|
24
|
+
export declare function replayTimeline(direction: "undo" | "redo", applyEdit: (entry: EditHistoryEntry, contents: DraftContents) => void): boolean;
|
|
25
|
+
export declare function pruneDraftHistory(draftId: string, retain: (contents: DraftContents) => DraftContents, equal: (a: DraftContents, b: DraftContents) => boolean): void;
|
|
26
|
+
export declare function discardDraftHistory(draftId: string): void;
|
|
27
|
+
export declare function discardCanvasHistory(cardId: string): void;
|
|
28
|
+
export declare function clearSessionUndoHistory(): void;
|
|
29
|
+
export declare function releaseDetachedIterations(): void;
|
|
30
|
+
export declare function createChangeHistoryGroup(): (update: () => void) => void;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
let undoStack = [];
|
|
2
|
+
let redoStack = [];
|
|
3
|
+
let activeGroup;
|
|
4
|
+
const listeners = new Set();
|
|
5
|
+
function notify() {
|
|
6
|
+
for (const listener of listeners)
|
|
7
|
+
listener();
|
|
8
|
+
}
|
|
9
|
+
function discardRedo() {
|
|
10
|
+
const discarded = redoStack;
|
|
11
|
+
redoStack = [];
|
|
12
|
+
for (const entry of discarded)
|
|
13
|
+
if (entry.kind === "canvas")
|
|
14
|
+
entry.dispose();
|
|
15
|
+
}
|
|
16
|
+
export function subscribeTimeline(listener) {
|
|
17
|
+
listeners.add(listener);
|
|
18
|
+
return () => listeners.delete(listener);
|
|
19
|
+
}
|
|
20
|
+
export function timelineStatus() {
|
|
21
|
+
return { canUndo: undoStack.length > 0, canRedo: redoStack.length > 0 };
|
|
22
|
+
}
|
|
23
|
+
export function recordEdit(entry, equal) {
|
|
24
|
+
const previous = undoStack.at(-1);
|
|
25
|
+
const grouped = activeGroup && previous?.kind === "edit" && previous.draftId === entry.draftId && previous.group === activeGroup;
|
|
26
|
+
const next = { kind: "edit", ...entry, before: grouped ? previous.before : entry.before, group: activeGroup };
|
|
27
|
+
if (grouped)
|
|
28
|
+
undoStack.pop();
|
|
29
|
+
if (!equal(next.before, next.after))
|
|
30
|
+
undoStack.push(next);
|
|
31
|
+
discardRedo();
|
|
32
|
+
notify();
|
|
33
|
+
}
|
|
34
|
+
export function recordCanvasChange(entry) {
|
|
35
|
+
discardRedo();
|
|
36
|
+
undoStack.push({ kind: "canvas", ...entry });
|
|
37
|
+
notify();
|
|
38
|
+
}
|
|
39
|
+
export function replayTimeline(direction, applyEdit) {
|
|
40
|
+
const source = direction === "undo" ? undoStack : redoStack;
|
|
41
|
+
const destination = direction === "undo" ? redoStack : undoStack;
|
|
42
|
+
const entry = source.pop();
|
|
43
|
+
if (!entry)
|
|
44
|
+
return false;
|
|
45
|
+
if (entry.kind === "edit")
|
|
46
|
+
applyEdit(entry, direction === "undo" ? entry.before : entry.after);
|
|
47
|
+
else
|
|
48
|
+
entry[direction]();
|
|
49
|
+
destination.push(entry);
|
|
50
|
+
notify();
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
export function pruneDraftHistory(draftId, retain, equal) {
|
|
54
|
+
const prune = (entries) => entries.flatMap((entry) => {
|
|
55
|
+
if (entry.kind !== "edit" || entry.draftId !== draftId)
|
|
56
|
+
return [entry];
|
|
57
|
+
const next = { ...entry, before: retain(entry.before), after: retain(entry.after) };
|
|
58
|
+
return equal(next.before, next.after) ? [] : [next];
|
|
59
|
+
});
|
|
60
|
+
undoStack = prune(undoStack);
|
|
61
|
+
redoStack = prune(redoStack);
|
|
62
|
+
notify();
|
|
63
|
+
}
|
|
64
|
+
export function discardDraftHistory(draftId) {
|
|
65
|
+
const retain = (entry) => entry.kind !== "edit" || entry.draftId !== draftId;
|
|
66
|
+
undoStack = undoStack.filter(retain);
|
|
67
|
+
redoStack = redoStack.filter(retain);
|
|
68
|
+
notify();
|
|
69
|
+
}
|
|
70
|
+
export function discardCanvasHistory(cardId) {
|
|
71
|
+
const retain = (entry) => entry.kind !== "canvas" || !entry.cardIds.includes(cardId);
|
|
72
|
+
undoStack = undoStack.filter(retain);
|
|
73
|
+
const discarded = redoStack.filter((entry) => !retain(entry));
|
|
74
|
+
redoStack = redoStack.filter(retain);
|
|
75
|
+
for (const entry of discarded)
|
|
76
|
+
if (entry.kind === "canvas")
|
|
77
|
+
entry.dispose();
|
|
78
|
+
notify();
|
|
79
|
+
}
|
|
80
|
+
export function clearSessionUndoHistory() {
|
|
81
|
+
const discarded = [...undoStack, ...redoStack];
|
|
82
|
+
undoStack = [];
|
|
83
|
+
redoStack = [];
|
|
84
|
+
for (const entry of discarded)
|
|
85
|
+
if (entry.kind === "canvas")
|
|
86
|
+
entry.dispose();
|
|
87
|
+
notify();
|
|
88
|
+
}
|
|
89
|
+
export function releaseDetachedIterations() {
|
|
90
|
+
discardRedo();
|
|
91
|
+
for (const entry of undoStack)
|
|
92
|
+
if (entry.kind === "canvas")
|
|
93
|
+
entry.dispose();
|
|
94
|
+
notify();
|
|
95
|
+
}
|
|
96
|
+
export function createChangeHistoryGroup() {
|
|
97
|
+
const group = Symbol("change-history-group");
|
|
98
|
+
return (update) => {
|
|
99
|
+
const previous = activeGroup;
|
|
100
|
+
activeGroup = group;
|
|
101
|
+
try {
|
|
102
|
+
update();
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
activeGroup = previous;
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
@@ -177,6 +177,8 @@ export interface AgentStatusSnapshot {
|
|
|
177
177
|
readonly projectId: string;
|
|
178
178
|
readonly connection: AgentConnectionState;
|
|
179
179
|
readonly listenerActive: boolean;
|
|
180
|
+
/** MCP client name supplied during initialization; absent for older adapters. */
|
|
181
|
+
readonly agentClientName?: string;
|
|
180
182
|
readonly paired: boolean;
|
|
181
183
|
/** The last paired page, retained only for this process lifetime. */
|
|
182
184
|
readonly pageUrl?: string | null;
|
|
@@ -186,7 +188,7 @@ export interface AgentStatusSnapshot {
|
|
|
186
188
|
readonly error?: string;
|
|
187
189
|
}) | null;
|
|
188
190
|
}
|
|
189
|
-
export type BridgeEventType = "status" | "canvas-command" | "connected" | "disconnected";
|
|
191
|
+
export type BridgeEventType = "activity" | "status" | "canvas-command" | "connected" | "disconnected";
|
|
190
192
|
export interface BridgeStatusEvent {
|
|
191
193
|
readonly type: "status";
|
|
192
194
|
readonly status: AgentStatusSnapshot;
|
|
@@ -203,8 +205,23 @@ export interface BridgeCanvasCommandEvent {
|
|
|
203
205
|
readonly type: "canvas-command";
|
|
204
206
|
readonly command: CanvasCommand;
|
|
205
207
|
}
|
|
208
|
+
/** Temporary attribution for one file operation in an authoritative request. */
|
|
209
|
+
export interface AgentActivity {
|
|
210
|
+
readonly requestId: string;
|
|
211
|
+
/** Workspace-relative source path. */
|
|
212
|
+
readonly file: string;
|
|
213
|
+
readonly operation: "read" | "edit";
|
|
214
|
+
readonly line?: number;
|
|
215
|
+
readonly endLine?: number;
|
|
216
|
+
readonly componentId?: string;
|
|
217
|
+
}
|
|
218
|
+
export declare function isAgentActivity(value: unknown): value is AgentActivity;
|
|
219
|
+
export interface BridgeActivityEvent {
|
|
220
|
+
readonly type: "activity";
|
|
221
|
+
readonly activity: AgentActivity;
|
|
222
|
+
}
|
|
206
223
|
/** Payload delivered to a browser SSE connection. */
|
|
207
|
-
export type BridgeEvent = BridgeStatusEvent | BridgeConnectedEvent | BridgeDisconnectedEvent | BridgeCanvasCommandEvent;
|
|
224
|
+
export type BridgeEvent = BridgeActivityEvent | BridgeStatusEvent | BridgeConnectedEvent | BridgeDisconnectedEvent | BridgeCanvasCommandEvent;
|
|
208
225
|
export interface BridgeEnvelope {
|
|
209
226
|
readonly protocolVersion: AgentProtocolVersion;
|
|
210
227
|
readonly projectId: string;
|
|
@@ -61,6 +61,19 @@ export const CANVAS_COMMAND_TYPES = [
|
|
|
61
61
|
"fit-all",
|
|
62
62
|
"remove-group",
|
|
63
63
|
];
|
|
64
|
+
export function isAgentActivity(value) {
|
|
65
|
+
if (!isRecord(value) || !hasOnlyKeys(value, ["requestId", "file", "operation", "line", "endLine", "componentId"]))
|
|
66
|
+
return false;
|
|
67
|
+
const line = (value) => Number.isSafeInteger(value) && Number(value) > 0 && Number(value) <= 10_000_000;
|
|
68
|
+
return nonEmptyString(value.requestId, AGENT_PROTOCOL_LIMITS.requestId)
|
|
69
|
+
&& nonEmptyString(value.file, AGENT_PROTOCOL_LIMITS.workspaceRoot)
|
|
70
|
+
&& !/^(?:[\\/]|[a-z]:)/i.test(value.file) && !/[\\\u0000-\u001f]/.test(value.file)
|
|
71
|
+
&& !value.file.split("/").some((part, index, parts) => !part || part === "." || (part === ".." && parts.slice(0, index).some((previous) => previous !== "..")))
|
|
72
|
+
&& (value.operation === "read" || value.operation === "edit")
|
|
73
|
+
&& (value.line === undefined || line(value.line))
|
|
74
|
+
&& (value.endLine === undefined || (line(value.endLine) && line(value.line) && Number(value.endLine) >= Number(value.line)))
|
|
75
|
+
&& (value.componentId === undefined || nonEmptyString(value.componentId, 512));
|
|
76
|
+
}
|
|
64
77
|
const ORIGIN_PROTOCOLS = new Set(["http:", "https:"]);
|
|
65
78
|
function isRecord(value) {
|
|
66
79
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|