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
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import type { StyleRule } from "../projection/managedStylesheet.js";
|
|
2
2
|
import type { ChangeRecord } from "./types.js";
|
|
3
|
-
import { type CommitResult } from "./
|
|
3
|
+
import { type CommitResult } from "./draftChanges.js";
|
|
4
4
|
import type { StructuralChange } from "./structuralTypes.js";
|
|
5
5
|
export { isComponentChange, isElementChange, isTextContentChange, isTextContentChangeValue, isPreviewableChange, isTokenChange, } from "./types.js";
|
|
6
|
-
export type { ChangeRecord, ComponentChangeRecord, ElementChangeRecord, PreviewableChangeRecord, TextContentChangeRecord, TokenChangeRecord, RuntimeElementEvidence, } from "./types.js";
|
|
6
|
+
export type { ChangeRecord, ComponentChangeRecord, ElementChangeRecord, PreviewableChangeRecord, TextContentChangeRecord, TokenChangeRecord, RuntimeElementEvidence, SourceElementEvidence, } from "./types.js";
|
|
7
7
|
export interface AppendChangesOptions {
|
|
8
8
|
/** The rendered target captured for each change key at commit time. */
|
|
9
9
|
verificationTargets?: ReadonlyMap<string, HTMLElement | null>;
|
|
@@ -25,7 +25,9 @@ export declare function revertChange(change: ChangeRecord): void;
|
|
|
25
25
|
*/
|
|
26
26
|
export declare function reconcileVerifiedChanges(verifiedKeys: ReadonlySet<string>): number;
|
|
27
27
|
/** Atomically reconciles ordinary and structural intent from one agent handoff. */
|
|
28
|
-
export declare function
|
|
28
|
+
export declare function reconcileVerifiedDraftChanges(verifiedKeys: ReadonlySet<string>, verifiedStructuralIds: ReadonlySet<string>): number;
|
|
29
|
+
/** Replaces ordinary and structural intent as one undoable edit. */
|
|
30
|
+
export declare function commitDraftContents(changes: readonly ChangeRecord[], structuralChanges: readonly StructuralChange[]): boolean;
|
|
29
31
|
export declare function discardChangesForSelector(selector: string): void;
|
|
30
32
|
/** Relink removes every CSS declaration owned by one durable rendered target. */
|
|
31
33
|
export declare function discardChangesForInstanceOverride(overrideId: string): void;
|
|
@@ -38,9 +40,10 @@ export declare function redo(): boolean;
|
|
|
38
40
|
*/
|
|
39
41
|
export declare function restoreChangeRecords(incoming: ChangeRecord[]): void;
|
|
40
42
|
/** Restores every canonical workspace intent as one subscriber-visible snapshot. */
|
|
41
|
-
export declare function
|
|
42
|
-
/** Clears
|
|
43
|
-
export declare function
|
|
43
|
+
export declare function loadActiveDraftChanges(incoming: readonly ChangeRecord[], structuralChanges: readonly StructuralChange[], preserveHistory?: boolean): void;
|
|
44
|
+
/** Clears the active draft and its edit steps, preserving other session history. */
|
|
45
|
+
export declare function clearActiveDraft(): void;
|
|
44
46
|
export declare function getChangesList(): ChangeRecord[];
|
|
45
47
|
export { subscribe as subscribeChanges, getChangesSnapshot as getChanges };
|
|
46
48
|
export declare function useChanges(): ChangeRecord[];
|
|
49
|
+
export declare function refreshDraftPreview(): void;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import { canEditDraft } from "./draftChanges.js";
|
|
1
2
|
import { useSyncExternalStore } from "react";
|
|
2
3
|
import { getSelectedElement } from "../selection/selectionStore.js";
|
|
3
4
|
import { changeKey, } from "./model.js";
|
|
4
5
|
import { buildManagedStyleRules, verifyManagedStyleProjection, } from "./managedStyleProjection.js";
|
|
5
6
|
import { isPreviewableChange } from "./types.js";
|
|
6
7
|
import { cancelInlineTextForClear } from "../inline-text/inlineTextLifecycle.js";
|
|
7
|
-
import {
|
|
8
|
+
import { draftChangeStore, } from "./draftChanges.js";
|
|
8
9
|
import { beginPreviewAttempt, clearPreviewDiagnostics, getHostPreviewDocument, publishPreviewDiagnostic, } from "./previewDiagnostics.js";
|
|
9
10
|
import { clearStructuralProjectionReports, pruneStructuralProjectionReports } from "../projection/structuralProjection.js";
|
|
10
|
-
import {
|
|
11
|
+
import { applyHostDraftProjection, compileDraftProjection, } from "../projection/draftProjection.js";
|
|
11
12
|
import { isEditorShellDocument } from "../runtime/editorShell.js";
|
|
12
13
|
import { isOriginalPreviewActive, subscribeOriginalPreview, } from "../shell/originalPreview.js";
|
|
13
14
|
export { isComponentChange, isElementChange, isTextContentChange, isTextContentChangeValue, isPreviewableChange, isTokenChange, } from "./types.js";
|
|
@@ -19,12 +20,12 @@ export { isComponentChange, isElementChange, isTextContentChange, isTextContentC
|
|
|
19
20
|
let pendingVerificationTargets = new Map();
|
|
20
21
|
let verificationHandle = null;
|
|
21
22
|
function subscribe(cb) {
|
|
22
|
-
return
|
|
23
|
+
return draftChangeStore.subscribe(cb);
|
|
23
24
|
}
|
|
24
25
|
function getChangesSnapshot() {
|
|
25
26
|
// SAFETY: The public change model is the concrete record union stored by
|
|
26
27
|
// the workspace snapshot; this module preserves the mutable array facade.
|
|
27
|
-
return
|
|
28
|
+
return draftChangeStore.getSnapshot().changes;
|
|
28
29
|
}
|
|
29
30
|
export function getPendingRules() {
|
|
30
31
|
return buildManagedStyleRules(getChangesSnapshot());
|
|
@@ -36,7 +37,7 @@ function reapply(workspace) {
|
|
|
36
37
|
if (isOriginalPreviewActive())
|
|
37
38
|
return;
|
|
38
39
|
if (!isEditorShellDocument())
|
|
39
|
-
|
|
40
|
+
applyHostDraftProjection(compileDraftProjection(workspace));
|
|
40
41
|
}
|
|
41
42
|
function flushVerification() {
|
|
42
43
|
verificationHandle = null;
|
|
@@ -53,7 +54,7 @@ function flushVerification() {
|
|
|
53
54
|
if (isEditorShellDocument())
|
|
54
55
|
return;
|
|
55
56
|
const current = getChangesSnapshot();
|
|
56
|
-
const attempt = beginPreviewAttempt(getHostPreviewDocument(),
|
|
57
|
+
const attempt = beginPreviewAttempt(getHostPreviewDocument(), draftChangeStore.getSnapshot().revision);
|
|
57
58
|
if (!attempt)
|
|
58
59
|
return;
|
|
59
60
|
for (let i = 0; i < current.length; i++) {
|
|
@@ -125,10 +126,10 @@ subscribeOriginalPreview(() => {
|
|
|
125
126
|
});
|
|
126
127
|
/** Append records and report whether canonical workspace state changed. */
|
|
127
128
|
export function appendChanges(incoming, options = {}) {
|
|
128
|
-
const result =
|
|
129
|
+
const result = draftChangeStore.commitChangeRecords(incoming);
|
|
129
130
|
if (result !== "applied")
|
|
130
131
|
return result;
|
|
131
|
-
reapply(
|
|
132
|
+
reapply(draftChangeStore.getSnapshot());
|
|
132
133
|
// Re-verify the full surviving set so earlier diagnostics are refreshed at
|
|
133
134
|
// the new revision instead of being orphaned at the old one.
|
|
134
135
|
markForVerification(getChangesSnapshot().map(changeKey), options.verificationTargets);
|
|
@@ -138,9 +139,9 @@ export function appendChange(change) {
|
|
|
138
139
|
return appendChanges([change]);
|
|
139
140
|
}
|
|
140
141
|
export function revertChange(change) {
|
|
141
|
-
if (!
|
|
142
|
+
if (!draftChangeStore.revertChangeRecord(change))
|
|
142
143
|
return;
|
|
143
|
-
reapply(
|
|
144
|
+
reapply(draftChangeStore.getSnapshot());
|
|
144
145
|
markForVerification(getChangesSnapshot().map(changeKey));
|
|
145
146
|
}
|
|
146
147
|
/**
|
|
@@ -152,46 +153,54 @@ export function revertChange(change) {
|
|
|
152
153
|
* preserving newer, unsent edits and their remaining undo history.
|
|
153
154
|
*/
|
|
154
155
|
export function reconcileVerifiedChanges(verifiedKeys) {
|
|
155
|
-
const removed =
|
|
156
|
+
const removed = reconcileVerifiedDraftChanges(verifiedKeys, new Set());
|
|
156
157
|
if (removed === 0)
|
|
157
158
|
return 0;
|
|
158
159
|
return removed;
|
|
159
160
|
}
|
|
160
161
|
/** Atomically reconciles ordinary and structural intent from one agent handoff. */
|
|
161
|
-
export function
|
|
162
|
-
const removed =
|
|
162
|
+
export function reconcileVerifiedDraftChanges(verifiedKeys, verifiedStructuralIds) {
|
|
163
|
+
const removed = draftChangeStore.reconcileActiveDraftChanges(verifiedKeys, verifiedStructuralIds);
|
|
163
164
|
if (removed === 0)
|
|
164
165
|
return 0;
|
|
165
|
-
reapply(
|
|
166
|
+
reapply(draftChangeStore.getSnapshot());
|
|
166
167
|
pruneStructuralProjectionReports(verifiedStructuralIds);
|
|
167
168
|
markForVerification(getChangesSnapshot().map(changeKey));
|
|
168
169
|
return removed;
|
|
169
170
|
}
|
|
171
|
+
/** Replaces ordinary and structural intent as one undoable edit. */
|
|
172
|
+
export function commitDraftContents(changes, structuralChanges) {
|
|
173
|
+
if (!draftChangeStore.commitContents({ changes: [...changes], structuralChanges: [...structuralChanges] }))
|
|
174
|
+
return false;
|
|
175
|
+
reapply(draftChangeStore.getSnapshot());
|
|
176
|
+
markForVerification(getChangesSnapshot().map(changeKey));
|
|
177
|
+
return true;
|
|
178
|
+
}
|
|
170
179
|
export function discardChangesForSelector(selector) {
|
|
171
|
-
if (!
|
|
180
|
+
if (!draftChangeStore.discardChangeRecords({ kind: "selector", selector }))
|
|
172
181
|
return;
|
|
173
|
-
reapply(
|
|
182
|
+
reapply(draftChangeStore.getSnapshot());
|
|
174
183
|
markForVerification(getChangesSnapshot().map(changeKey));
|
|
175
184
|
}
|
|
176
185
|
/** Relink removes every CSS declaration owned by one durable rendered target. */
|
|
177
186
|
export function discardChangesForInstanceOverride(overrideId) {
|
|
178
|
-
if (!
|
|
187
|
+
if (!draftChangeStore.discardChangeRecords({ kind: "instance-override", id: overrideId }))
|
|
179
188
|
return;
|
|
180
|
-
reapply(
|
|
189
|
+
reapply(draftChangeStore.getSnapshot());
|
|
181
190
|
markForVerification(getChangesSnapshot().map(changeKey));
|
|
182
191
|
}
|
|
183
192
|
export function undo() {
|
|
184
|
-
const undone =
|
|
193
|
+
const undone = draftChangeStore.undoChange();
|
|
185
194
|
if (undone)
|
|
186
|
-
reapply(
|
|
195
|
+
reapply(draftChangeStore.getSnapshot());
|
|
187
196
|
if (undone)
|
|
188
197
|
markForVerification(getChangesSnapshot().map(changeKey));
|
|
189
198
|
return undone;
|
|
190
199
|
}
|
|
191
200
|
export function redo() {
|
|
192
|
-
const redone =
|
|
201
|
+
const redone = draftChangeStore.redoChange();
|
|
193
202
|
if (redone)
|
|
194
|
-
reapply(
|
|
203
|
+
reapply(draftChangeStore.getSnapshot());
|
|
195
204
|
if (redone)
|
|
196
205
|
markForVerification(getChangesSnapshot().map(changeKey));
|
|
197
206
|
return redone;
|
|
@@ -207,28 +216,30 @@ export function restoreChangeRecords(incoming) {
|
|
|
207
216
|
// inspect a newly loaded record with its old selection context.
|
|
208
217
|
pendingVerificationTargets.clear();
|
|
209
218
|
clearPreviewDiagnostics();
|
|
210
|
-
const workspace =
|
|
211
|
-
|
|
212
|
-
reapply(
|
|
219
|
+
const workspace = draftChangeStore.getSnapshot();
|
|
220
|
+
draftChangeStore.restoreActiveDraftChanges({ changes: incoming, structuralChanges: workspace.structuralChanges });
|
|
221
|
+
reapply(draftChangeStore.getSnapshot());
|
|
213
222
|
}
|
|
214
223
|
/** Restores every canonical workspace intent as one subscriber-visible snapshot. */
|
|
215
|
-
export function
|
|
224
|
+
export function loadActiveDraftChanges(incoming, structuralChanges, preserveHistory = false) {
|
|
216
225
|
pendingVerificationTargets.clear();
|
|
217
226
|
clearPreviewDiagnostics();
|
|
218
227
|
clearStructuralProjectionReports();
|
|
219
|
-
|
|
220
|
-
reapply(
|
|
228
|
+
draftChangeStore.restoreActiveDraftChanges({ changes: incoming, structuralChanges }, preserveHistory);
|
|
229
|
+
reapply(draftChangeStore.getSnapshot());
|
|
221
230
|
}
|
|
222
|
-
/** Clears
|
|
223
|
-
export function
|
|
231
|
+
/** Clears the active draft and its edit steps, preserving other session history. */
|
|
232
|
+
export function clearActiveDraft() {
|
|
233
|
+
if (!canEditDraft())
|
|
234
|
+
return;
|
|
224
235
|
// A pending blur/composition timer must not be able to append a draft after
|
|
225
236
|
// the canonical set has been cleared.
|
|
226
237
|
cancelInlineTextForClear();
|
|
227
238
|
pendingVerificationTargets.clear();
|
|
228
239
|
clearPreviewDiagnostics();
|
|
229
240
|
clearStructuralProjectionReports();
|
|
230
|
-
|
|
231
|
-
reapply(
|
|
241
|
+
draftChangeStore.clearActiveDraftChanges();
|
|
242
|
+
reapply(draftChangeStore.getSnapshot());
|
|
232
243
|
}
|
|
233
244
|
export function getChangesList() {
|
|
234
245
|
return [...getChangesSnapshot()];
|
|
@@ -237,3 +248,8 @@ export { subscribe as subscribeChanges, getChangesSnapshot as getChanges };
|
|
|
237
248
|
export function useChanges() {
|
|
238
249
|
return useSyncExternalStore(subscribe, getChangesSnapshot, getChangesSnapshot);
|
|
239
250
|
}
|
|
251
|
+
export function refreshDraftPreview() {
|
|
252
|
+
pendingVerificationTargets.clear();
|
|
253
|
+
reapply(draftChangeStore.getSnapshot());
|
|
254
|
+
markForVerification(getChangesSnapshot().map(changeKey));
|
|
255
|
+
}
|
|
@@ -32,6 +32,7 @@ export interface SerializableElementChange {
|
|
|
32
32
|
component: string;
|
|
33
33
|
};
|
|
34
34
|
runtimeEvidence?: NonNullable<ElementChangeRecord["runtimeEvidence"]>;
|
|
35
|
+
sourceEvidence?: NonNullable<ElementChangeRecord["sourceEvidence"]>;
|
|
35
36
|
scope?: EditScope;
|
|
36
37
|
instanceOverride?: RenderedInstanceOverride;
|
|
37
38
|
state?: NonNullable<ElementChangeRecord["state"]>;
|
|
@@ -16,7 +16,7 @@ export function isSerializableElementChangeValue(value) {
|
|
|
16
16
|
|| !hasOnlyKeys(value, [
|
|
17
17
|
"kind", "cid", "file", "line", "column", "selector", "property",
|
|
18
18
|
"sourceProperty", "sourceAuthoredValue", "oldToken", "newToken", "rawValue",
|
|
19
|
-
"oldRawValue", "source", "runtimeEvidence", "scope", "instanceOverride", "state",
|
|
19
|
+
"oldRawValue", "source", "runtimeEvidence", "sourceEvidence", "scope", "instanceOverride", "state",
|
|
20
20
|
])
|
|
21
21
|
|| (value.kind !== undefined && value.kind !== "element")
|
|
22
22
|
|| typeof value.cid !== "string"
|
|
@@ -33,6 +33,7 @@ export function isSerializableElementChangeValue(value) {
|
|
|
33
33
|
|| (value.rawValue !== undefined && typeof value.rawValue !== "string")
|
|
34
34
|
|| (value.oldRawValue !== undefined && typeof value.oldRawValue !== "string")
|
|
35
35
|
|| (value.runtimeEvidence !== undefined && !isRuntimeElementEvidenceValue(value.runtimeEvidence))
|
|
36
|
+
|| (value.sourceEvidence !== undefined && !isSourceElementEvidenceValue(value.sourceEvidence))
|
|
36
37
|
|| !isEditScopeValue(value.scope)
|
|
37
38
|
|| (value.instanceOverride !== undefined && value.scope !== "rendered-instance")
|
|
38
39
|
|| (value.scope === "rendered-instance" && !isRenderedInstanceOverride(value.instanceOverride))
|
|
@@ -115,6 +116,7 @@ export function serializeElementChange(change) {
|
|
|
115
116
|
oldRawValue: change.oldRawValue,
|
|
116
117
|
source: change.source,
|
|
117
118
|
runtimeEvidence: change.runtimeEvidence ? { ...change.runtimeEvidence } : undefined,
|
|
119
|
+
sourceEvidence: change.sourceEvidence ? { ...change.sourceEvidence } : undefined,
|
|
118
120
|
scope: change.scope === "rendered-instance" ? "rendered-instance" : "source-site",
|
|
119
121
|
instanceOverride: change.scope === "rendered-instance" ? change.instanceOverride : undefined,
|
|
120
122
|
state: change.state,
|
|
@@ -200,6 +202,7 @@ export function deserializeElementChange(serialized) {
|
|
|
200
202
|
oldRawValue: serialized.oldRawValue,
|
|
201
203
|
source: serialized.source,
|
|
202
204
|
runtimeEvidence: serialized.runtimeEvidence ? { ...serialized.runtimeEvidence } : undefined,
|
|
205
|
+
sourceEvidence: serialized.sourceEvidence ? { ...serialized.sourceEvidence } : undefined,
|
|
203
206
|
scope: serialized.scope ?? "source-site",
|
|
204
207
|
instanceOverride: serialized.scope === "rendered-instance" ? serialized.instanceOverride : undefined,
|
|
205
208
|
state: serialized.state,
|
|
@@ -331,6 +334,12 @@ function isRuntimeElementEvidenceValue(value) {
|
|
|
331
334
|
&& (typeof value.props === "string" || value.props === null)
|
|
332
335
|
&& (typeof value.ariaLabel === "string" || value.ariaLabel === null);
|
|
333
336
|
}
|
|
337
|
+
function isSourceElementEvidenceValue(value) {
|
|
338
|
+
return isRecord(value)
|
|
339
|
+
&& hasOnlyKeys(value, ["tagName", "props"])
|
|
340
|
+
&& typeof value.tagName === "string"
|
|
341
|
+
&& (typeof value.props === "string" || value.props === null);
|
|
342
|
+
}
|
|
334
343
|
function isTokenRefValue(value) {
|
|
335
344
|
if (value === null)
|
|
336
345
|
return true;
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type ChangeRecord } from "./types.js";
|
|
2
|
+
import type { StructuralChange } from "./structuralTypes.js";
|
|
3
|
+
export { recordCanvasChange, discardCanvasHistory, discardDraftHistory, clearSessionUndoHistory, createChangeHistoryGroup, } from "../workspace/timeline.js";
|
|
4
|
+
export interface DraftContents {
|
|
5
|
+
readonly changes: readonly ChangeRecord[];
|
|
6
|
+
readonly structuralChanges: readonly StructuralChange[];
|
|
7
|
+
}
|
|
8
|
+
export interface DraftChangesSnapshot extends DraftContents {
|
|
9
|
+
readonly draftId: string;
|
|
10
|
+
readonly revision: number;
|
|
11
|
+
readonly canUndo: boolean;
|
|
12
|
+
readonly canRedo: boolean;
|
|
13
|
+
}
|
|
14
|
+
export declare function lockDraft(id: string): () => void;
|
|
15
|
+
export declare function isDraftLocked(id?: string): boolean;
|
|
16
|
+
export declare function canEditDraft(id?: string): boolean;
|
|
17
|
+
export declare function setActiveDraft(id: string, activate?: () => void): void;
|
|
18
|
+
export type DraftProjector = (snapshot: DraftChangesSnapshot) => void;
|
|
19
|
+
export type CommitResult = "applied" | "unchanged" | "blocked";
|
|
20
|
+
export interface DraftChangeStore {
|
|
21
|
+
getSnapshot(): DraftChangesSnapshot;
|
|
22
|
+
subscribe(listener: () => void): () => void;
|
|
23
|
+
commitChangeRecords(incoming: readonly ChangeRecord[]): CommitResult;
|
|
24
|
+
revertChangeRecord(change: ChangeRecord): boolean;
|
|
25
|
+
discardChangeRecords(target: {
|
|
26
|
+
kind: "selector";
|
|
27
|
+
selector: string;
|
|
28
|
+
} | {
|
|
29
|
+
kind: "instance-override";
|
|
30
|
+
id: string;
|
|
31
|
+
}): boolean;
|
|
32
|
+
commitStructuralChange(change: StructuralChange): boolean;
|
|
33
|
+
revertStructuralChangeRecord(changeId: string): boolean;
|
|
34
|
+
/** Replaces the active draft as one undoable edit. */
|
|
35
|
+
commitContents(next: DraftContents): boolean;
|
|
36
|
+
reconcileActiveDraftChanges(verifiedKeys: ReadonlySet<string>, verifiedStructuralIds: ReadonlySet<string>): number;
|
|
37
|
+
undoChange(): boolean;
|
|
38
|
+
redoChange(): boolean;
|
|
39
|
+
restoreActiveDraftChanges(next: DraftContents, preserveHistory?: boolean): void;
|
|
40
|
+
clearActiveDraftChanges(): void;
|
|
41
|
+
}
|
|
42
|
+
export declare function subscribeDraftChanges(listener: (draftId: string) => void): () => void;
|
|
43
|
+
export declare function getDraftChanges(id: string): DraftChangesSnapshot;
|
|
44
|
+
export declare function restoreDraftChanges(id: string, next: DraftContents, revision?: number): void;
|
|
45
|
+
export declare function reconcileDraftChanges(id: string, verifiedKeys: ReadonlySet<string>, verifiedStructuralIds: ReadonlySet<string>): number;
|
|
46
|
+
export declare function clearDraftChanges(id: string, expectedRevision?: number): boolean;
|
|
47
|
+
export declare function listDraftIds(): readonly string[];
|
|
48
|
+
/** Drops a draft whose content no longer exists, such as a deleted iteration. */
|
|
49
|
+
export declare function forgetDraft(id: string): void;
|
|
50
|
+
export declare const draftChangeStore: DraftChangeStore;
|
|
51
|
+
export declare function getActiveDraftChanges(): DraftChangesSnapshot;
|
|
52
|
+
export declare function subscribeActiveDraftChanges(listener: () => void): () => void;
|
|
53
|
+
export declare function commitChangeRecords(incoming: readonly ChangeRecord[], project: DraftProjector): CommitResult;
|
|
54
|
+
export declare function commitStructuralChange(change: StructuralChange, project: DraftProjector): boolean;
|
|
55
|
+
export declare function undoChange(project: DraftProjector): boolean;
|
|
56
|
+
export declare function redoChange(project: DraftProjector): boolean;
|
|
57
|
+
export declare function restoreActiveDraftChanges(next: DraftContents, project: DraftProjector): void;
|
|
58
|
+
export declare function resetDraftChanges(): void;
|
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { canWriteWorkspace } from "../canvas/workspaceLease.js";
|
|
2
|
+
import { applicationTarget, targetKey } from "../drafts/model.js";
|
|
3
|
+
import { changeKey, mergeChange, sameEffectiveChanges, selectorForChange } from "./model.js";
|
|
4
|
+
import { isElementChange } from "./types.js";
|
|
5
|
+
import { recordEdit, replayTimeline, pruneDraftHistory, timelineStatus, subscribeTimeline, clearSessionUndoHistory, discardDraftHistory, } from "../workspace/timeline.js";
|
|
6
|
+
export { recordCanvasChange, discardCanvasHistory, discardDraftHistory, clearSessionUndoHistory, createChangeHistoryGroup, } from "../workspace/timeline.js";
|
|
7
|
+
const drafts = new Map();
|
|
8
|
+
const lockedDrafts = new Set();
|
|
9
|
+
const listeners = new Set();
|
|
10
|
+
const draftListeners = new Set();
|
|
11
|
+
/** Until a frame is activated, edits belong to the current page's draft. */
|
|
12
|
+
let activeDraftId = null;
|
|
13
|
+
function activeId() {
|
|
14
|
+
return activeDraftId ??= targetKey(applicationTarget());
|
|
15
|
+
}
|
|
16
|
+
function draftState(id = activeId()) {
|
|
17
|
+
let draft = drafts.get(id);
|
|
18
|
+
if (!draft) {
|
|
19
|
+
draft = { id, revision: 0, contents: { changes: [], structuralChanges: [] } };
|
|
20
|
+
drafts.set(id, draft);
|
|
21
|
+
}
|
|
22
|
+
return draft;
|
|
23
|
+
}
|
|
24
|
+
export function lockDraft(id) {
|
|
25
|
+
if (lockedDrafts.has(id))
|
|
26
|
+
throw new Error("This iteration is already saving.");
|
|
27
|
+
lockedDrafts.add(id);
|
|
28
|
+
return () => { lockedDrafts.delete(id); };
|
|
29
|
+
}
|
|
30
|
+
export function isDraftLocked(id) {
|
|
31
|
+
return id === undefined ? lockedDrafts.size > 0 : lockedDrafts.has(id);
|
|
32
|
+
}
|
|
33
|
+
export function canEditDraft(id = activeId()) {
|
|
34
|
+
return canWriteWorkspace() && !lockedDrafts.has(id);
|
|
35
|
+
}
|
|
36
|
+
export function setActiveDraft(id, activate) {
|
|
37
|
+
draftState(id).activate = activate;
|
|
38
|
+
if (activeId() === id)
|
|
39
|
+
return;
|
|
40
|
+
activeDraftId = id;
|
|
41
|
+
publishSelection();
|
|
42
|
+
}
|
|
43
|
+
let snapshot = null;
|
|
44
|
+
function cloneContents(value) {
|
|
45
|
+
return {
|
|
46
|
+
changes: value.changes.map((change) => cloneValue(change)),
|
|
47
|
+
structuralChanges: value.structuralChanges.map((change) => cloneValue(change)),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function cloneValue(value, key) {
|
|
51
|
+
if (key === "oldToken" || key === "newToken")
|
|
52
|
+
return value;
|
|
53
|
+
if (Array.isArray(value))
|
|
54
|
+
return value.map((item) => cloneValue(item));
|
|
55
|
+
if (value !== null && typeof value === "object") {
|
|
56
|
+
const clone = {};
|
|
57
|
+
for (const [key, child] of Object.entries(value)) {
|
|
58
|
+
clone[key] = cloneValue(child, key);
|
|
59
|
+
}
|
|
60
|
+
return clone;
|
|
61
|
+
}
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
function freezeValue(value, key) {
|
|
65
|
+
if (key === "oldToken" || key === "newToken")
|
|
66
|
+
return value;
|
|
67
|
+
if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
|
|
68
|
+
for (const [childKey, child] of Object.entries(value))
|
|
69
|
+
freezeValue(child, childKey);
|
|
70
|
+
Object.freeze(value);
|
|
71
|
+
}
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
function createSnapshot() {
|
|
75
|
+
const draft = draftState();
|
|
76
|
+
return Object.freeze({ draftId: draft.id, revision: draft.revision, ...draft.contents, ...timelineStatus() });
|
|
77
|
+
}
|
|
78
|
+
let notificationDepth = 0;
|
|
79
|
+
let notificationPending = false;
|
|
80
|
+
function mutate(operation) {
|
|
81
|
+
notificationDepth += 1;
|
|
82
|
+
try {
|
|
83
|
+
return operation();
|
|
84
|
+
}
|
|
85
|
+
finally {
|
|
86
|
+
notificationDepth -= 1;
|
|
87
|
+
if (notificationDepth === 0 && notificationPending) {
|
|
88
|
+
notificationPending = false;
|
|
89
|
+
publishSelection();
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
function publishSelection() {
|
|
94
|
+
if (notificationDepth > 0) {
|
|
95
|
+
notificationPending = true;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
snapshot = null;
|
|
99
|
+
for (const listener of listeners)
|
|
100
|
+
listener();
|
|
101
|
+
}
|
|
102
|
+
subscribeTimeline(publishSelection);
|
|
103
|
+
function replaceContents(draft, next) {
|
|
104
|
+
draft.contents = freezeValue(cloneContents(next));
|
|
105
|
+
draft.revision += 1;
|
|
106
|
+
for (const listener of draftListeners)
|
|
107
|
+
listener(draft.id);
|
|
108
|
+
publishSelection();
|
|
109
|
+
}
|
|
110
|
+
export function subscribeDraftChanges(listener) {
|
|
111
|
+
draftListeners.add(listener);
|
|
112
|
+
return () => draftListeners.delete(listener);
|
|
113
|
+
}
|
|
114
|
+
export function getDraftChanges(id) {
|
|
115
|
+
const draft = drafts.get(id);
|
|
116
|
+
return {
|
|
117
|
+
draftId: id,
|
|
118
|
+
revision: draft?.revision ?? 0,
|
|
119
|
+
changes: draft?.contents.changes ?? [],
|
|
120
|
+
structuralChanges: draft?.contents.structuralChanges ?? [],
|
|
121
|
+
...timelineStatus(),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
export function restoreDraftChanges(id, next, revision = 1) {
|
|
125
|
+
const draft = draftState(id);
|
|
126
|
+
draft.contents = freezeValue(cloneContents(next));
|
|
127
|
+
draft.revision = revision;
|
|
128
|
+
publishSelection();
|
|
129
|
+
}
|
|
130
|
+
function commit(next) {
|
|
131
|
+
if (!canEditDraft())
|
|
132
|
+
return false;
|
|
133
|
+
const draft = draftState();
|
|
134
|
+
const before = draft.contents;
|
|
135
|
+
mutate(() => {
|
|
136
|
+
replaceContents(draft, next);
|
|
137
|
+
recordEdit({ draftId: draft.id, activate: draft.activate, before, after: draft.contents }, sameDraftContents);
|
|
138
|
+
});
|
|
139
|
+
return true;
|
|
140
|
+
}
|
|
141
|
+
function getSnapshot() { return snapshot ??= createSnapshot(); }
|
|
142
|
+
function subscribe(listener) {
|
|
143
|
+
listeners.add(listener);
|
|
144
|
+
return () => listeners.delete(listener);
|
|
145
|
+
}
|
|
146
|
+
function commitChangeRecordsImpl(incoming) {
|
|
147
|
+
if (incoming.length === 0)
|
|
148
|
+
return "unchanged";
|
|
149
|
+
const contents = draftState().contents;
|
|
150
|
+
const changes = incoming.reduce((current, change) => mergeChange(current, change), [...contents.changes]);
|
|
151
|
+
if (sameEffectiveChanges([...contents.changes], changes))
|
|
152
|
+
return "unchanged";
|
|
153
|
+
return commit({ ...contents, changes }) ? "applied" : "blocked";
|
|
154
|
+
}
|
|
155
|
+
function revertChangeRecordImpl(change) {
|
|
156
|
+
const contents = draftState().contents;
|
|
157
|
+
const changes = contents.changes.filter((candidate) => changeKey(candidate) !== changeKey(change));
|
|
158
|
+
return changes.length !== contents.changes.length && commit({ ...contents, changes });
|
|
159
|
+
}
|
|
160
|
+
function discardChangeRecordsImpl(target) {
|
|
161
|
+
const contents = draftState().contents;
|
|
162
|
+
const changes = contents.changes.filter((change) => target.kind === "selector"
|
|
163
|
+
? selectorForChange(change) !== target.selector
|
|
164
|
+
: !isElementChange(change) || change.instanceOverride?.id !== target.id);
|
|
165
|
+
return changes.length !== contents.changes.length && commit({ ...contents, changes });
|
|
166
|
+
}
|
|
167
|
+
function commitStructuralChangeImpl(change) {
|
|
168
|
+
const contents = draftState().contents;
|
|
169
|
+
if (contents.structuralChanges.some((candidate) => candidate.id === change.id))
|
|
170
|
+
return false;
|
|
171
|
+
return commit({ ...contents, structuralChanges: [...contents.structuralChanges, change] });
|
|
172
|
+
}
|
|
173
|
+
function revertStructuralChangeRecordImpl(changeId) {
|
|
174
|
+
const contents = draftState().contents;
|
|
175
|
+
const structuralChanges = contents.structuralChanges.filter((change) => change.id !== changeId);
|
|
176
|
+
return structuralChanges.length !== contents.structuralChanges.length && commit({ ...contents, structuralChanges });
|
|
177
|
+
}
|
|
178
|
+
function commitContentsImpl(next) {
|
|
179
|
+
return !sameDraftContents(draftState().contents, next) && commit(next);
|
|
180
|
+
}
|
|
181
|
+
export function reconcileDraftChanges(id, verifiedKeys, verifiedStructuralIds) {
|
|
182
|
+
if (!canEditDraft(id))
|
|
183
|
+
return 0;
|
|
184
|
+
const draft = draftState(id);
|
|
185
|
+
const retain = (value) => ({
|
|
186
|
+
changes: value.changes.filter((change) => !verifiedKeys.has(changeKey(change))),
|
|
187
|
+
structuralChanges: value.structuralChanges.filter((change) => !verifiedStructuralIds.has(change.id)),
|
|
188
|
+
});
|
|
189
|
+
const next = retain(draft.contents);
|
|
190
|
+
const removed = draft.contents.changes.length - next.changes.length + draft.contents.structuralChanges.length - next.structuralChanges.length;
|
|
191
|
+
if (removed === 0)
|
|
192
|
+
return 0;
|
|
193
|
+
mutate(() => {
|
|
194
|
+
replaceContents(draft, next);
|
|
195
|
+
pruneDraftHistory(id, retain, sameDraftContents);
|
|
196
|
+
});
|
|
197
|
+
return removed;
|
|
198
|
+
}
|
|
199
|
+
function reconcileActiveDraftChangesImpl(keys, ids) {
|
|
200
|
+
return reconcileDraftChanges(activeId(), keys, ids);
|
|
201
|
+
}
|
|
202
|
+
function sameDraftContents(left, right) {
|
|
203
|
+
return sameEffectiveChanges([...left.changes], [...right.changes])
|
|
204
|
+
&& JSON.stringify(left.structuralChanges) === JSON.stringify(right.structuralChanges);
|
|
205
|
+
}
|
|
206
|
+
function replay(direction) {
|
|
207
|
+
if (isDraftLocked() || !canWriteWorkspace())
|
|
208
|
+
return false;
|
|
209
|
+
return mutate(() => replayTimeline(direction, (entry, next) => {
|
|
210
|
+
replaceContents(draftState(entry.draftId), next);
|
|
211
|
+
entry.activate?.();
|
|
212
|
+
if (!entry.activate)
|
|
213
|
+
setActiveDraft(entry.draftId);
|
|
214
|
+
}));
|
|
215
|
+
}
|
|
216
|
+
function restoreActiveDraftChangesImpl(next, preserveHistory = false) {
|
|
217
|
+
mutate(() => {
|
|
218
|
+
replaceContents(draftState(), next);
|
|
219
|
+
if (!preserveHistory)
|
|
220
|
+
clearSessionUndoHistory();
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
export function clearDraftChanges(id, expectedRevision) {
|
|
224
|
+
const draft = draftState(id);
|
|
225
|
+
if (!canEditDraft(id) || (expectedRevision !== undefined && draft.revision !== expectedRevision))
|
|
226
|
+
return false;
|
|
227
|
+
mutate(() => {
|
|
228
|
+
replaceContents(draft, { changes: [], structuralChanges: [] });
|
|
229
|
+
discardDraftHistory(id);
|
|
230
|
+
});
|
|
231
|
+
return true;
|
|
232
|
+
}
|
|
233
|
+
export function listDraftIds() {
|
|
234
|
+
return [...drafts.keys()];
|
|
235
|
+
}
|
|
236
|
+
/** Drops a draft whose content no longer exists, such as a deleted iteration. */
|
|
237
|
+
export function forgetDraft(id) {
|
|
238
|
+
if (!drafts.has(id) || lockedDrafts.has(id))
|
|
239
|
+
return;
|
|
240
|
+
discardDraftHistory(id);
|
|
241
|
+
drafts.delete(id);
|
|
242
|
+
for (const listener of draftListeners)
|
|
243
|
+
listener(id);
|
|
244
|
+
publishSelection();
|
|
245
|
+
}
|
|
246
|
+
function clearActiveDraftChangesImpl() { clearDraftChanges(activeId()); }
|
|
247
|
+
function resetDraftChangesImpl() {
|
|
248
|
+
drafts.clear();
|
|
249
|
+
lockedDrafts.clear();
|
|
250
|
+
activeDraftId = null;
|
|
251
|
+
clearSessionUndoHistory();
|
|
252
|
+
publishSelection();
|
|
253
|
+
}
|
|
254
|
+
export const draftChangeStore = {
|
|
255
|
+
getSnapshot, subscribe,
|
|
256
|
+
commitChangeRecords: commitChangeRecordsImpl,
|
|
257
|
+
revertChangeRecord: revertChangeRecordImpl,
|
|
258
|
+
discardChangeRecords: discardChangeRecordsImpl,
|
|
259
|
+
commitStructuralChange: commitStructuralChangeImpl,
|
|
260
|
+
revertStructuralChangeRecord: revertStructuralChangeRecordImpl,
|
|
261
|
+
commitContents: commitContentsImpl,
|
|
262
|
+
reconcileActiveDraftChanges: reconcileActiveDraftChangesImpl,
|
|
263
|
+
undoChange: () => replay("undo"),
|
|
264
|
+
redoChange: () => replay("redo"),
|
|
265
|
+
restoreActiveDraftChanges: restoreActiveDraftChangesImpl,
|
|
266
|
+
clearActiveDraftChanges: clearActiveDraftChangesImpl,
|
|
267
|
+
};
|
|
268
|
+
export function getActiveDraftChanges() {
|
|
269
|
+
return draftChangeStore.getSnapshot();
|
|
270
|
+
}
|
|
271
|
+
export function subscribeActiveDraftChanges(listener) {
|
|
272
|
+
return draftChangeStore.subscribe(listener);
|
|
273
|
+
}
|
|
274
|
+
export function commitChangeRecords(incoming, project) {
|
|
275
|
+
const result = draftChangeStore.commitChangeRecords(incoming);
|
|
276
|
+
if (result === "applied")
|
|
277
|
+
project(draftChangeStore.getSnapshot());
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
export function commitStructuralChange(change, project) {
|
|
281
|
+
const changed = draftChangeStore.commitStructuralChange(change);
|
|
282
|
+
if (changed)
|
|
283
|
+
project(draftChangeStore.getSnapshot());
|
|
284
|
+
return changed;
|
|
285
|
+
}
|
|
286
|
+
export function undoChange(project) {
|
|
287
|
+
const changed = draftChangeStore.undoChange();
|
|
288
|
+
if (changed)
|
|
289
|
+
project(draftChangeStore.getSnapshot());
|
|
290
|
+
return changed;
|
|
291
|
+
}
|
|
292
|
+
export function redoChange(project) {
|
|
293
|
+
const changed = draftChangeStore.redoChange();
|
|
294
|
+
if (changed)
|
|
295
|
+
project(draftChangeStore.getSnapshot());
|
|
296
|
+
return changed;
|
|
297
|
+
}
|
|
298
|
+
export function restoreActiveDraftChanges(next, project) {
|
|
299
|
+
draftChangeStore.restoreActiveDraftChanges(next);
|
|
300
|
+
project(draftChangeStore.getSnapshot());
|
|
301
|
+
}
|
|
302
|
+
export function resetDraftChanges() {
|
|
303
|
+
resetDraftChangesImpl();
|
|
304
|
+
}
|
|
@@ -18,8 +18,18 @@ export interface RenderedInstanceRef {
|
|
|
18
18
|
text: string | null;
|
|
19
19
|
/** An accessible name can distinguish icon-only repeated controls. */
|
|
20
20
|
ariaLabel?: string | null;
|
|
21
|
+
/**
|
|
22
|
+
* The component invocation whose entire output is this element. Present
|
|
23
|
+
* only when evidence alone cannot tell identical outputs apart.
|
|
24
|
+
*/
|
|
25
|
+
invocation?: InvocationRef;
|
|
21
26
|
};
|
|
22
27
|
}
|
|
28
|
+
/** A component callsite, as stamped by the compiler, and its component name. */
|
|
29
|
+
export interface InvocationRef {
|
|
30
|
+
callsiteId: string;
|
|
31
|
+
componentName: string;
|
|
32
|
+
}
|
|
23
33
|
export interface RenderedInstanceOverride {
|
|
24
34
|
id: string;
|
|
25
35
|
target: RenderedInstanceRef;
|