nudge-ui 0.2.4 → 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 +863 -213
- 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 +1 -1
- package/dist/inspector/agent/verification.js +7 -4
- 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 +21 -12
- package/dist/inspector/canvas/CanvasElementOverlay.js +82 -127
- package/dist/inspector/canvas/CanvasToolbar.js +2 -1
- package/dist/inspector/canvas/CanvasWorkspace.js +74 -97
- 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/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 +1 -0
- package/dist/inspector/changes/codecs.js +20 -5
- package/dist/inspector/changes/draftPreview.d.ts +12 -0
- package/dist/inspector/changes/draftPreview.js +133 -0
- package/dist/inspector/changes/editModel.d.ts +8 -0
- package/dist/inspector/changes/editModel.js +8 -2
- package/dist/inspector/changes/managedStyleProjection.js +2 -1
- package/dist/inspector/changes/previewDiagnostics.js +1 -1
- package/dist/inspector/changes/types.d.ts +11 -1
- package/dist/inspector/comments/CommentsOverlay.js +18 -16
- 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/changeAction.js +1 -1
- package/dist/inspector/componentSemantics/textBinding.d.ts +1 -2
- package/dist/inspector/componentSemantics/textBinding.js +4 -45
- 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/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.js +7 -1
- package/dist/inspector/projection/structuralProjection.d.ts +0 -9
- package/dist/inspector/projection/structuralProjection.js +14 -44
- package/dist/inspector/projection/textProjection.d.ts +8 -2
- package/dist/inspector/projection/textProjection.js +21 -13
- package/dist/inspector/prompt/clipboardHandoff.d.ts +9 -0
- package/dist/inspector/prompt/clipboardHandoff.js +42 -16
- package/dist/inspector/prompt/generatePrompt.d.ts +1 -1
- package/dist/inspector/prompt/generatePrompt.js +243 -147
- 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/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 +60 -54
- 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 +2 -1
- 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 +34 -3
- package/dist/inspector/tokens/TokensPanel.js +2 -1
- package/dist/inspector/tokens/editActions.d.ts +3 -3
- package/dist/inspector/tokens/editActions.js +9 -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 +3 -6
- package/dist/inspector/workspace/sentItems.js +6 -24
- 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 +12 -2
- 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 -49
- package/dist/inspector/changes/changesLog.js +0 -255
- package/dist/inspector/changes/draftChanges.d.ts +0 -58
- package/dist/inspector/changes/draftChanges.js +0 -304
- 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
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { type ChangeRecord } from "../changes/types.js";
|
|
2
|
+
import type { StructuralChange } from "../changes/structuralTypes.js";
|
|
3
|
+
import type { SketchDocument } from "../sketch/model.js";
|
|
4
|
+
import type { Timeline } from "../workspace/timeline.js";
|
|
5
|
+
import { type Draft, type DraftContents, type DraftSnapshot } from "./model.js";
|
|
6
|
+
export interface DraftChangesSnapshot extends DraftContents {
|
|
7
|
+
readonly draftId: string;
|
|
8
|
+
readonly revision: number;
|
|
9
|
+
readonly canUndo: boolean;
|
|
10
|
+
readonly canRedo: boolean;
|
|
11
|
+
}
|
|
12
|
+
/** A canonical write result. `blocked` means the lease or a save lock refused the write. */
|
|
13
|
+
export type CommitResult = "applied" | "unchanged" | "blocked";
|
|
14
|
+
/** Why the active draft's preview must be rebuilt. */
|
|
15
|
+
export type DraftProjectionEvent =
|
|
16
|
+
/** The active draft's contents changed through an edit, revert, undo, or redo. */
|
|
17
|
+
{
|
|
18
|
+
readonly kind: "edit";
|
|
19
|
+
readonly verificationTargets?: ReadonlyMap<string, HTMLElement | null>;
|
|
20
|
+
}
|
|
21
|
+
/** Source verification removed these records from the active draft. */
|
|
22
|
+
| {
|
|
23
|
+
readonly kind: "reconcile";
|
|
24
|
+
readonly structuralIds: ReadonlySet<string>;
|
|
25
|
+
}
|
|
26
|
+
/** Another draft became active, or drafts were loaded. */
|
|
27
|
+
| {
|
|
28
|
+
readonly kind: "switch";
|
|
29
|
+
}
|
|
30
|
+
/** The active draft was cleared; every earlier preview diagnostic is void. */
|
|
31
|
+
| {
|
|
32
|
+
readonly kind: "reset";
|
|
33
|
+
};
|
|
34
|
+
export type DraftDiscardTarget = {
|
|
35
|
+
readonly kind: "selector";
|
|
36
|
+
readonly selector: string;
|
|
37
|
+
} | {
|
|
38
|
+
readonly kind: "instance-override";
|
|
39
|
+
readonly id: string;
|
|
40
|
+
};
|
|
41
|
+
export interface DraftStorage {
|
|
42
|
+
read(key: string): string | null;
|
|
43
|
+
write(key: string, value: string): void;
|
|
44
|
+
remove(key: string): void;
|
|
45
|
+
}
|
|
46
|
+
export interface DraftSketchSource {
|
|
47
|
+
getSnapshot(): {
|
|
48
|
+
readonly ready: boolean;
|
|
49
|
+
readonly documents: readonly SketchDocument[];
|
|
50
|
+
};
|
|
51
|
+
subscribe(listener: () => void): () => void;
|
|
52
|
+
}
|
|
53
|
+
export interface DraftStoreSeams {
|
|
54
|
+
/** Whether this tab holds the workspace write lease. */
|
|
55
|
+
canWrite(): boolean;
|
|
56
|
+
readonly timeline: Timeline;
|
|
57
|
+
readonly storage: DraftStorage;
|
|
58
|
+
readonly sketches: DraftSketchSource;
|
|
59
|
+
/** Rebuilds the active draft's preview. Called after subscribers see the new snapshot. */
|
|
60
|
+
project(snapshot: DraftChangesSnapshot, event: DraftProjectionEvent): void;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* The Workspace's drafts. A draft's identity is its target key (ADR-0030):
|
|
64
|
+
* every frame that shows one page or iteration edits the same draft. Every
|
|
65
|
+
* write is lease-gated, every change to the active draft is projected, and
|
|
66
|
+
* reconciliation removes only verified records, including from history.
|
|
67
|
+
*/
|
|
68
|
+
export interface DraftStore {
|
|
69
|
+
getActive(): DraftChangesSnapshot;
|
|
70
|
+
/** Fires for any draft content change, active-draft switch, or history change. */
|
|
71
|
+
subscribeActive(listener: () => void): () => void;
|
|
72
|
+
get(id: string): DraftChangesSnapshot;
|
|
73
|
+
ids(): readonly string[];
|
|
74
|
+
getDraft(id: string): Draft | null;
|
|
75
|
+
getSnapshot(): DraftSnapshot;
|
|
76
|
+
subscribe(listener: () => void): () => void;
|
|
77
|
+
canEdit(id?: string): boolean;
|
|
78
|
+
isLocked(id?: string): boolean;
|
|
79
|
+
/** Blocks writes to one draft until the returned release runs. */
|
|
80
|
+
lock(id: string): () => void;
|
|
81
|
+
/** Makes a draft active; `reveal` brings its frame back when history replays an edit. */
|
|
82
|
+
activate(id: string, reveal?: () => void): void;
|
|
83
|
+
commitChanges(incoming: readonly ChangeRecord[], verificationTargets?: ReadonlyMap<string, HTMLElement | null>): CommitResult;
|
|
84
|
+
revertChange(change: ChangeRecord): boolean;
|
|
85
|
+
discardChanges(target: DraftDiscardTarget): boolean;
|
|
86
|
+
commitStructuralChange(change: StructuralChange): boolean;
|
|
87
|
+
revertStructuralChange(changeId: string): boolean;
|
|
88
|
+
/** Replaces the active draft as one undoable edit. */
|
|
89
|
+
commitContents(next: DraftContents): boolean;
|
|
90
|
+
undo(): boolean;
|
|
91
|
+
redo(): boolean;
|
|
92
|
+
/** Removes source-verified records so no undo step can resurrect them. */
|
|
93
|
+
reconcile(id: string, verifiedKeys: ReadonlySet<string>, verifiedStructuralIds: ReadonlySet<string>): number;
|
|
94
|
+
clear(id: string, expectedRevision?: number): boolean;
|
|
95
|
+
clearActive(): boolean;
|
|
96
|
+
/** Drops a draft whose content no longer exists, such as a deleted iteration. */
|
|
97
|
+
forget(id: string): void;
|
|
98
|
+
sketchOwner(document: SketchDocument): string | null;
|
|
99
|
+
assignSketch(draftId: string, sketchId: string): void;
|
|
100
|
+
/** Replaces every in-memory draft with the stored drafts, so a tab that takes over never writes stale edits. */
|
|
101
|
+
load(projectId: string, options?: {
|
|
102
|
+
persistent?: boolean;
|
|
103
|
+
}): void;
|
|
104
|
+
persist(): void;
|
|
105
|
+
/** Discards stored and in-memory drafts for the current project. */
|
|
106
|
+
discardAll(): void;
|
|
107
|
+
reset(): void;
|
|
108
|
+
}
|
|
109
|
+
export declare function createDraftStore(seams: DraftStoreSeams): DraftStore;
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
import { deserializeChange, isSerializableChange, serializeChange, } from "../changes/codecs.js";
|
|
2
|
+
import { changeKey, mergeChange, sameEffectiveChanges, selectorForChange } from "../changes/model.js";
|
|
3
|
+
import { isElementChange } from "../changes/types.js";
|
|
4
|
+
import { isStructuralChange } from "../projection/structuralProjectionBoundary.js";
|
|
5
|
+
import { applicationTarget, documentTarget, isDraftTarget, targetFromKey, targetKey, } from "./model.js";
|
|
6
|
+
const EMPTY = { changes: [], structuralChanges: [] };
|
|
7
|
+
const PROJECTION_RANK = { edit: 1, reconcile: 2, switch: 3, reset: 4 };
|
|
8
|
+
function cloneValue(value, key) {
|
|
9
|
+
if (key === "oldToken" || key === "newToken")
|
|
10
|
+
return value;
|
|
11
|
+
if (Array.isArray(value))
|
|
12
|
+
return value.map((item) => cloneValue(item));
|
|
13
|
+
if (value !== null && typeof value === "object") {
|
|
14
|
+
const clone = {};
|
|
15
|
+
for (const [childKey, child] of Object.entries(value))
|
|
16
|
+
clone[childKey] = cloneValue(child, childKey);
|
|
17
|
+
return clone;
|
|
18
|
+
}
|
|
19
|
+
return value;
|
|
20
|
+
}
|
|
21
|
+
function freezeValue(value, key) {
|
|
22
|
+
if (key === "oldToken" || key === "newToken")
|
|
23
|
+
return value;
|
|
24
|
+
if (value !== null && typeof value === "object" && !Object.isFrozen(value)) {
|
|
25
|
+
for (const [childKey, child] of Object.entries(value))
|
|
26
|
+
freezeValue(child, childKey);
|
|
27
|
+
Object.freeze(value);
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function frozenContents(value) {
|
|
32
|
+
return freezeValue({
|
|
33
|
+
changes: value.changes.map((change) => cloneValue(change)),
|
|
34
|
+
structuralChanges: value.structuralChanges.map((change) => cloneValue(change)),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function sameDraftContents(left, right) {
|
|
38
|
+
return sameEffectiveChanges([...left.changes], [...right.changes])
|
|
39
|
+
&& JSON.stringify(left.structuralChanges) === JSON.stringify(right.structuralChanges);
|
|
40
|
+
}
|
|
41
|
+
function mergeProjection(current, next) {
|
|
42
|
+
return current && PROJECTION_RANK[current.kind] > PROJECTION_RANK[next.kind] ? current : next;
|
|
43
|
+
}
|
|
44
|
+
function isPersistedDrafts(value, projectId) {
|
|
45
|
+
if (!value || typeof value !== "object")
|
|
46
|
+
return false;
|
|
47
|
+
const candidate = value;
|
|
48
|
+
if (candidate.schemaVersion !== 2 || candidate.projectId !== projectId || !Array.isArray(candidate.drafts))
|
|
49
|
+
return false;
|
|
50
|
+
const targets = new Set();
|
|
51
|
+
for (const draft of candidate.drafts) {
|
|
52
|
+
if (!draft || !isDraftTarget(draft.target) || targets.has(targetKey(draft.target))
|
|
53
|
+
|| !Number.isSafeInteger(draft.revision) || draft.revision < 1
|
|
54
|
+
|| !draft.contents || !Array.isArray(draft.contents.changes) || !draft.contents.changes.every(isSerializableChange)
|
|
55
|
+
|| !Array.isArray(draft.contents.structuralChanges) || !draft.contents.structuralChanges.every(isStructuralChange))
|
|
56
|
+
return false;
|
|
57
|
+
const textIds = draft.contents.changes
|
|
58
|
+
.filter((change) => change.kind === "text-content")
|
|
59
|
+
.map((change) => "id" in change ? change.id : "");
|
|
60
|
+
if (new Set(textIds).size !== textIds.length)
|
|
61
|
+
return false;
|
|
62
|
+
targets.add(targetKey(draft.target));
|
|
63
|
+
}
|
|
64
|
+
return !!candidate.sketchOwners && typeof candidate.sketchOwners === "object" && !Array.isArray(candidate.sketchOwners)
|
|
65
|
+
&& Object.values(candidate.sketchOwners).every((id) => typeof id === "string" && targetFromKey(id) !== null);
|
|
66
|
+
}
|
|
67
|
+
export function createDraftStore(seams) {
|
|
68
|
+
const drafts = new Map();
|
|
69
|
+
const locked = new Set();
|
|
70
|
+
/** Overrides the capture-URL owner for sketches drawn while another draft was active. */
|
|
71
|
+
const sketchOwners = new Map();
|
|
72
|
+
const activeListeners = new Set();
|
|
73
|
+
const draftListeners = new Set();
|
|
74
|
+
/** Until a frame is activated, edits belong to the current page's draft. */
|
|
75
|
+
let activeDraftId = null;
|
|
76
|
+
let projectId = "";
|
|
77
|
+
let persistent = true;
|
|
78
|
+
let activeSnapshot = null;
|
|
79
|
+
let draftsSnapshot = null;
|
|
80
|
+
let depth = 0;
|
|
81
|
+
let activePending = false;
|
|
82
|
+
let draftsPending = false;
|
|
83
|
+
let pendingProjection = null;
|
|
84
|
+
const activeId = () => activeDraftId ??= targetKey(applicationTarget());
|
|
85
|
+
const storageKey = () => `nudge-ui-drafts:${projectId}:v2`;
|
|
86
|
+
function draftState(id = activeId()) {
|
|
87
|
+
let draft = drafts.get(id);
|
|
88
|
+
if (!draft) {
|
|
89
|
+
draft = { id, revision: 0, contents: EMPTY };
|
|
90
|
+
drafts.set(id, draft);
|
|
91
|
+
}
|
|
92
|
+
return draft;
|
|
93
|
+
}
|
|
94
|
+
function flush() {
|
|
95
|
+
if (activePending) {
|
|
96
|
+
activePending = false;
|
|
97
|
+
for (const listener of activeListeners)
|
|
98
|
+
listener();
|
|
99
|
+
}
|
|
100
|
+
if (draftsPending) {
|
|
101
|
+
draftsPending = false;
|
|
102
|
+
for (const listener of draftListeners)
|
|
103
|
+
listener();
|
|
104
|
+
}
|
|
105
|
+
if (pendingProjection) {
|
|
106
|
+
const event = pendingProjection;
|
|
107
|
+
pendingProjection = null;
|
|
108
|
+
seams.project(getActive(), event);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/** Subscribers and the projector observe only complete, settled state. */
|
|
112
|
+
function mutate(operation) {
|
|
113
|
+
depth += 1;
|
|
114
|
+
try {
|
|
115
|
+
return operation();
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
depth -= 1;
|
|
119
|
+
if (depth === 0)
|
|
120
|
+
flush();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function activeChanged() {
|
|
124
|
+
activeSnapshot = null;
|
|
125
|
+
activePending = true;
|
|
126
|
+
if (depth === 0)
|
|
127
|
+
flush();
|
|
128
|
+
}
|
|
129
|
+
function draftsChanged() {
|
|
130
|
+
draftsSnapshot = null;
|
|
131
|
+
draftsPending = true;
|
|
132
|
+
if (depth === 0)
|
|
133
|
+
flush();
|
|
134
|
+
}
|
|
135
|
+
function contentsChanged() {
|
|
136
|
+
draftsSnapshot = null;
|
|
137
|
+
draftsPending = true;
|
|
138
|
+
activeChanged();
|
|
139
|
+
}
|
|
140
|
+
function project(event) {
|
|
141
|
+
pendingProjection = mergeProjection(pendingProjection, event);
|
|
142
|
+
}
|
|
143
|
+
function replaceContents(draft, next) {
|
|
144
|
+
draft.contents = frozenContents(next);
|
|
145
|
+
draft.revision += 1;
|
|
146
|
+
contentsChanged();
|
|
147
|
+
}
|
|
148
|
+
function resetContents() {
|
|
149
|
+
drafts.clear();
|
|
150
|
+
locked.clear();
|
|
151
|
+
activeDraftId = null;
|
|
152
|
+
seams.timeline.clear();
|
|
153
|
+
}
|
|
154
|
+
function canEdit(id = activeId()) {
|
|
155
|
+
return seams.canWrite() && !locked.has(id);
|
|
156
|
+
}
|
|
157
|
+
function commitActive(next, event = { kind: "edit" }) {
|
|
158
|
+
if (!canEdit())
|
|
159
|
+
return false;
|
|
160
|
+
return mutate(() => {
|
|
161
|
+
const draft = draftState();
|
|
162
|
+
const before = draft.contents;
|
|
163
|
+
replaceContents(draft, next);
|
|
164
|
+
seams.timeline.recordEdit({ draftId: draft.id, activate: draft.activate, before, after: draft.contents }, sameDraftContents);
|
|
165
|
+
project(event);
|
|
166
|
+
return true;
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
function activate(id, reveal) {
|
|
170
|
+
mutate(() => {
|
|
171
|
+
draftState(id).activate = reveal;
|
|
172
|
+
if (activeId() === id)
|
|
173
|
+
return;
|
|
174
|
+
activeDraftId = id;
|
|
175
|
+
activeChanged();
|
|
176
|
+
project({ kind: "switch" });
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
function replay(direction) {
|
|
180
|
+
if (locked.size > 0 || !seams.canWrite())
|
|
181
|
+
return false;
|
|
182
|
+
return mutate(() => {
|
|
183
|
+
const replayed = seams.timeline.replay(direction, (entry, next) => {
|
|
184
|
+
replaceContents(draftState(entry.draftId), next);
|
|
185
|
+
entry.activate?.();
|
|
186
|
+
if (!entry.activate)
|
|
187
|
+
activate(entry.draftId);
|
|
188
|
+
});
|
|
189
|
+
if (replayed)
|
|
190
|
+
project({ kind: "edit" });
|
|
191
|
+
return replayed;
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
function clear(id, event, expectedRevision) {
|
|
195
|
+
const draft = draftState(id);
|
|
196
|
+
if (!canEdit(id) || (expectedRevision !== undefined && draft.revision !== expectedRevision))
|
|
197
|
+
return false;
|
|
198
|
+
mutate(() => {
|
|
199
|
+
replaceContents(draft, EMPTY);
|
|
200
|
+
seams.timeline.discardDraftHistory(id);
|
|
201
|
+
if (id === activeId())
|
|
202
|
+
project(event);
|
|
203
|
+
});
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
function get(id) {
|
|
207
|
+
const draft = drafts.get(id);
|
|
208
|
+
return {
|
|
209
|
+
draftId: id,
|
|
210
|
+
revision: draft?.revision ?? 0,
|
|
211
|
+
changes: draft?.contents.changes ?? [],
|
|
212
|
+
structuralChanges: draft?.contents.structuralChanges ?? [],
|
|
213
|
+
...seams.timeline.status(),
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
function getActive() {
|
|
217
|
+
if (!activeSnapshot) {
|
|
218
|
+
const draft = draftState();
|
|
219
|
+
activeSnapshot = Object.freeze({ draftId: draft.id, revision: draft.revision, ...draft.contents, ...seams.timeline.status() });
|
|
220
|
+
}
|
|
221
|
+
return activeSnapshot;
|
|
222
|
+
}
|
|
223
|
+
function sketchOwner(document) {
|
|
224
|
+
const owner = sketchOwners.get(document.id);
|
|
225
|
+
if (owner)
|
|
226
|
+
return owner;
|
|
227
|
+
try {
|
|
228
|
+
return targetKey(documentTarget(document.capture.url));
|
|
229
|
+
}
|
|
230
|
+
catch {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
function getDraft(id) {
|
|
235
|
+
const target = targetFromKey(id);
|
|
236
|
+
if (!target)
|
|
237
|
+
return null;
|
|
238
|
+
const { revision, changes, structuralChanges } = get(id);
|
|
239
|
+
return {
|
|
240
|
+
id,
|
|
241
|
+
target,
|
|
242
|
+
revision,
|
|
243
|
+
contents: { changes, structuralChanges },
|
|
244
|
+
sketchIds: seams.sketches.getSnapshot().documents.filter((document) => sketchOwner(document) === id).map((document) => document.id),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function persisted() {
|
|
248
|
+
const sketches = seams.sketches.getSnapshot();
|
|
249
|
+
const liveSketches = new Set(sketches.documents.map((document) => document.id));
|
|
250
|
+
return {
|
|
251
|
+
schemaVersion: 2,
|
|
252
|
+
projectId,
|
|
253
|
+
drafts: [...drafts.values()].flatMap(({ id, revision, contents }) => {
|
|
254
|
+
const target = targetFromKey(id);
|
|
255
|
+
if (!target || contents.changes.length + contents.structuralChanges.length === 0)
|
|
256
|
+
return [];
|
|
257
|
+
return [{
|
|
258
|
+
target,
|
|
259
|
+
revision,
|
|
260
|
+
contents: {
|
|
261
|
+
changes: contents.changes.flatMap((change) => {
|
|
262
|
+
const serialized = serializeChange(change);
|
|
263
|
+
return serialized ? [serialized] : [];
|
|
264
|
+
}),
|
|
265
|
+
structuralChanges: contents.structuralChanges,
|
|
266
|
+
},
|
|
267
|
+
}];
|
|
268
|
+
}),
|
|
269
|
+
sketchOwners: Object.fromEntries([...sketchOwners].filter(([id]) => !sketches.ready || liveSketches.has(id))),
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function restore() {
|
|
273
|
+
const raw = persistent ? seams.storage.read(storageKey()) : null;
|
|
274
|
+
const stored = raw ? JSON.parse(raw) : null;
|
|
275
|
+
if (!isPersistedDrafts(stored, projectId))
|
|
276
|
+
return;
|
|
277
|
+
for (const draft of stored.drafts) {
|
|
278
|
+
const state = draftState(targetKey(draft.target));
|
|
279
|
+
state.contents = frozenContents({
|
|
280
|
+
changes: draft.contents.changes.map(deserializeChange),
|
|
281
|
+
structuralChanges: draft.contents.structuralChanges,
|
|
282
|
+
});
|
|
283
|
+
state.revision = draft.revision;
|
|
284
|
+
}
|
|
285
|
+
for (const [sketchId, draftId] of Object.entries(stored.sketchOwners))
|
|
286
|
+
sketchOwners.set(sketchId, draftId);
|
|
287
|
+
}
|
|
288
|
+
seams.timeline.subscribe(activeChanged);
|
|
289
|
+
seams.sketches.subscribe(draftsChanged);
|
|
290
|
+
return {
|
|
291
|
+
getActive,
|
|
292
|
+
subscribeActive(listener) {
|
|
293
|
+
activeListeners.add(listener);
|
|
294
|
+
return () => activeListeners.delete(listener);
|
|
295
|
+
},
|
|
296
|
+
get,
|
|
297
|
+
ids: () => [...drafts.keys()],
|
|
298
|
+
getDraft,
|
|
299
|
+
getSnapshot() {
|
|
300
|
+
if (!draftsSnapshot) {
|
|
301
|
+
const ids = new Set([
|
|
302
|
+
...drafts.keys(),
|
|
303
|
+
...seams.sketches.getSnapshot().documents.flatMap((document) => sketchOwner(document) ?? []),
|
|
304
|
+
]);
|
|
305
|
+
draftsSnapshot = { projectId, drafts: [...ids].flatMap((id) => getDraft(id) ?? []) };
|
|
306
|
+
}
|
|
307
|
+
return draftsSnapshot;
|
|
308
|
+
},
|
|
309
|
+
subscribe(listener) {
|
|
310
|
+
draftListeners.add(listener);
|
|
311
|
+
return () => draftListeners.delete(listener);
|
|
312
|
+
},
|
|
313
|
+
canEdit,
|
|
314
|
+
isLocked: (id) => id === undefined ? locked.size > 0 : locked.has(id),
|
|
315
|
+
lock(id) {
|
|
316
|
+
if (locked.has(id))
|
|
317
|
+
throw new Error("This iteration is already saving.");
|
|
318
|
+
locked.add(id);
|
|
319
|
+
return () => { locked.delete(id); };
|
|
320
|
+
},
|
|
321
|
+
activate,
|
|
322
|
+
commitChanges(incoming, verificationTargets) {
|
|
323
|
+
if (incoming.length === 0)
|
|
324
|
+
return "unchanged";
|
|
325
|
+
const contents = draftState().contents;
|
|
326
|
+
const changes = incoming.reduce((current, change) => mergeChange(current, change), [...contents.changes]);
|
|
327
|
+
if (sameEffectiveChanges([...contents.changes], changes))
|
|
328
|
+
return "unchanged";
|
|
329
|
+
return commitActive({ ...contents, changes }, { kind: "edit", verificationTargets }) ? "applied" : "blocked";
|
|
330
|
+
},
|
|
331
|
+
revertChange(change) {
|
|
332
|
+
const contents = draftState().contents;
|
|
333
|
+
const changes = contents.changes.filter((candidate) => changeKey(candidate) !== changeKey(change));
|
|
334
|
+
return changes.length !== contents.changes.length && commitActive({ ...contents, changes });
|
|
335
|
+
},
|
|
336
|
+
discardChanges(target) {
|
|
337
|
+
const contents = draftState().contents;
|
|
338
|
+
const changes = contents.changes.filter((change) => target.kind === "selector"
|
|
339
|
+
? selectorForChange(change) !== target.selector
|
|
340
|
+
: !isElementChange(change) || change.instanceOverride?.id !== target.id);
|
|
341
|
+
return changes.length !== contents.changes.length && commitActive({ ...contents, changes });
|
|
342
|
+
},
|
|
343
|
+
commitStructuralChange(change) {
|
|
344
|
+
const contents = draftState().contents;
|
|
345
|
+
if (contents.structuralChanges.some((candidate) => candidate.id === change.id))
|
|
346
|
+
return false;
|
|
347
|
+
return commitActive({ ...contents, structuralChanges: [...contents.structuralChanges, change] });
|
|
348
|
+
},
|
|
349
|
+
revertStructuralChange(changeId) {
|
|
350
|
+
const contents = draftState().contents;
|
|
351
|
+
const structuralChanges = contents.structuralChanges.filter((change) => change.id !== changeId);
|
|
352
|
+
return structuralChanges.length !== contents.structuralChanges.length && commitActive({ ...contents, structuralChanges });
|
|
353
|
+
},
|
|
354
|
+
commitContents: (next) => !sameDraftContents(draftState().contents, next) && commitActive(next),
|
|
355
|
+
undo: () => replay("undo"),
|
|
356
|
+
redo: () => replay("redo"),
|
|
357
|
+
reconcile(id, verifiedKeys, verifiedStructuralIds) {
|
|
358
|
+
if (!canEdit(id))
|
|
359
|
+
return 0;
|
|
360
|
+
const draft = draftState(id);
|
|
361
|
+
const retain = (value) => ({
|
|
362
|
+
changes: value.changes.filter((change) => !verifiedKeys.has(changeKey(change))),
|
|
363
|
+
structuralChanges: value.structuralChanges.filter((change) => !verifiedStructuralIds.has(change.id)),
|
|
364
|
+
});
|
|
365
|
+
const next = retain(draft.contents);
|
|
366
|
+
const removed = draft.contents.changes.length - next.changes.length
|
|
367
|
+
+ draft.contents.structuralChanges.length - next.structuralChanges.length;
|
|
368
|
+
if (removed === 0)
|
|
369
|
+
return 0;
|
|
370
|
+
mutate(() => {
|
|
371
|
+
replaceContents(draft, next);
|
|
372
|
+
seams.timeline.pruneDraftHistory(id, retain, sameDraftContents);
|
|
373
|
+
if (id === activeId())
|
|
374
|
+
project({ kind: "reconcile", structuralIds: verifiedStructuralIds });
|
|
375
|
+
});
|
|
376
|
+
return removed;
|
|
377
|
+
},
|
|
378
|
+
clear: (id, expectedRevision) => clear(id, { kind: "edit" }, expectedRevision),
|
|
379
|
+
clearActive: () => clear(activeId(), { kind: "reset" }),
|
|
380
|
+
forget(id) {
|
|
381
|
+
if (!drafts.has(id) || locked.has(id))
|
|
382
|
+
return;
|
|
383
|
+
mutate(() => {
|
|
384
|
+
seams.timeline.discardDraftHistory(id);
|
|
385
|
+
drafts.delete(id);
|
|
386
|
+
contentsChanged();
|
|
387
|
+
if (id === activeId())
|
|
388
|
+
project({ kind: "edit" });
|
|
389
|
+
});
|
|
390
|
+
},
|
|
391
|
+
sketchOwner,
|
|
392
|
+
assignSketch(draftId, sketchId) {
|
|
393
|
+
if (sketchOwners.get(sketchId) === draftId || !targetFromKey(draftId))
|
|
394
|
+
return;
|
|
395
|
+
sketchOwners.set(sketchId, draftId);
|
|
396
|
+
draftsChanged();
|
|
397
|
+
},
|
|
398
|
+
load(nextProjectId, options = {}) {
|
|
399
|
+
mutate(() => {
|
|
400
|
+
projectId = nextProjectId;
|
|
401
|
+
persistent = options.persistent ?? true;
|
|
402
|
+
sketchOwners.clear();
|
|
403
|
+
resetContents();
|
|
404
|
+
try {
|
|
405
|
+
restore();
|
|
406
|
+
}
|
|
407
|
+
catch {
|
|
408
|
+
resetContents();
|
|
409
|
+
sketchOwners.clear();
|
|
410
|
+
}
|
|
411
|
+
contentsChanged();
|
|
412
|
+
project({ kind: "switch" });
|
|
413
|
+
});
|
|
414
|
+
},
|
|
415
|
+
persist() {
|
|
416
|
+
if (!projectId || !persistent || !seams.canWrite())
|
|
417
|
+
return;
|
|
418
|
+
try {
|
|
419
|
+
seams.storage.write(storageKey(), JSON.stringify(persisted()));
|
|
420
|
+
}
|
|
421
|
+
catch {
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
},
|
|
425
|
+
discardAll() {
|
|
426
|
+
if (projectId && persistent && seams.canWrite()) {
|
|
427
|
+
try {
|
|
428
|
+
seams.storage.remove(storageKey());
|
|
429
|
+
}
|
|
430
|
+
catch {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
mutate(() => {
|
|
435
|
+
sketchOwners.clear();
|
|
436
|
+
resetContents();
|
|
437
|
+
contentsChanged();
|
|
438
|
+
project({ kind: "reset" });
|
|
439
|
+
});
|
|
440
|
+
},
|
|
441
|
+
reset() {
|
|
442
|
+
mutate(() => {
|
|
443
|
+
projectId = "";
|
|
444
|
+
persistent = true;
|
|
445
|
+
sketchOwners.clear();
|
|
446
|
+
resetContents();
|
|
447
|
+
contentsChanged();
|
|
448
|
+
});
|
|
449
|
+
},
|
|
450
|
+
};
|
|
451
|
+
}
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ChangeRecord } from "../changes/types.js";
|
|
2
|
+
import type { StructuralChange } from "../changes/structuralTypes.js";
|
|
3
|
+
/** The canonical intent of one draft: ordinary records and structural records. */
|
|
4
|
+
export interface DraftContents {
|
|
5
|
+
readonly changes: readonly ChangeRecord[];
|
|
6
|
+
readonly structuralChanges: readonly StructuralChange[];
|
|
7
|
+
}
|
|
2
8
|
export type DraftTarget = {
|
|
3
9
|
readonly kind: "application";
|
|
4
10
|
readonly route: string;
|
|
@@ -28,6 +34,8 @@ export declare function htmlTarget(artifactId: string): {
|
|
|
28
34
|
artifactId: string;
|
|
29
35
|
path: string;
|
|
30
36
|
};
|
|
37
|
+
/** The HTML iteration artifact ID when the URL addresses an iteration document. */
|
|
38
|
+
export declare function artifactIdFromUrl(url: string): string | null;
|
|
31
39
|
/** Maps a captured document URL to the draft that owns it: an iteration preview or an application page. */
|
|
32
40
|
export declare function documentTarget(url: string): DraftTarget;
|
|
33
41
|
export declare function targetKey(target: DraftTarget): string;
|
|
@@ -10,9 +10,13 @@ export function applicationTarget(url = window.location.href) {
|
|
|
10
10
|
export function htmlTarget(artifactId) {
|
|
11
11
|
return { kind: "html", artifactId, path: `.nudge/artifacts/${artifactId}/document.html` };
|
|
12
12
|
}
|
|
13
|
+
/** The HTML iteration artifact ID when the URL addresses an iteration document. */
|
|
14
|
+
export function artifactIdFromUrl(url) {
|
|
15
|
+
return ARTIFACT_PATH.exec(new URL(url, window.location.href).pathname)?.[1] ?? null;
|
|
16
|
+
}
|
|
13
17
|
/** Maps a captured document URL to the draft that owns it: an iteration preview or an application page. */
|
|
14
18
|
export function documentTarget(url) {
|
|
15
|
-
const artifactId =
|
|
19
|
+
const artifactId = artifactIdFromUrl(url);
|
|
16
20
|
return artifactId ? htmlTarget(artifactId) : applicationTarget(url);
|
|
17
21
|
}
|
|
18
22
|
export function targetKey(target) {
|
|
@@ -1,28 +1,66 @@
|
|
|
1
1
|
import { type FrameContent } from "../canvas/frameContent.js";
|
|
2
|
-
import {
|
|
2
|
+
import type { ChangeRecord } from "../changes/types.js";
|
|
3
|
+
import type { StructuralChange } from "../changes/structuralTypes.js";
|
|
4
|
+
import type { StyleRule } from "../projection/managedStylesheet.js";
|
|
3
5
|
import type { SketchDocument } from "../sketch/model.js";
|
|
4
|
-
import { type
|
|
6
|
+
import { type CommitResult, type DraftChangesSnapshot } from "./draftStore.js";
|
|
7
|
+
import { type Draft, type DraftContents, type DraftSnapshot } from "./model.js";
|
|
8
|
+
export declare function getActiveDraftChanges(): DraftChangesSnapshot;
|
|
9
|
+
export declare function subscribeActiveDraftChanges(listener: () => void): () => void;
|
|
10
|
+
export declare function getDraftChanges(id: string): DraftChangesSnapshot;
|
|
11
|
+
export declare function listDraftIds(): readonly string[];
|
|
12
|
+
export declare function getDraft(id: string): Draft | null;
|
|
13
|
+
export declare function getDraftsSnapshot(): DraftSnapshot;
|
|
14
|
+
export declare function subscribeDrafts(listener: () => void): () => void;
|
|
15
|
+
export declare function useDrafts(): DraftSnapshot;
|
|
16
|
+
export declare function canEditDraft(id?: string): boolean;
|
|
17
|
+
export declare function isDraftLocked(id?: string): boolean;
|
|
18
|
+
export declare function lockDraft(id: string): () => void;
|
|
19
|
+
export declare function useChanges(): ChangeRecord[];
|
|
20
|
+
export declare function getChangesList(): ChangeRecord[];
|
|
21
|
+
export declare function getPendingRules(): StyleRule[];
|
|
22
|
+
export interface AppendChangesOptions {
|
|
23
|
+
/** The rendered target captured for each change key at commit time. */
|
|
24
|
+
verificationTargets?: ReadonlyMap<string, HTMLElement | null>;
|
|
25
|
+
}
|
|
26
|
+
/** Appends records and reports whether canonical workspace state changed. */
|
|
27
|
+
export declare function appendChanges(incoming: readonly ChangeRecord[], options?: AppendChangesOptions): CommitResult;
|
|
28
|
+
export declare function appendChange(change: ChangeRecord): CommitResult;
|
|
29
|
+
export declare function revertChange(change: ChangeRecord): void;
|
|
30
|
+
export declare function discardChangesForSelector(selector: string): void;
|
|
31
|
+
/** Relink removes every CSS declaration owned by one durable rendered target. */
|
|
32
|
+
export declare function discardChangesForInstanceOverride(overrideId: string): void;
|
|
33
|
+
export declare function commitStructuralChange(change: StructuralChange): boolean;
|
|
34
|
+
export declare function revertStructuralChange(changeId: string): boolean;
|
|
35
|
+
/** Replaces ordinary and structural intent as one undoable edit. */
|
|
36
|
+
export declare function commitDraftContents(changes: readonly ChangeRecord[], structuralChanges: readonly StructuralChange[]): boolean;
|
|
37
|
+
export declare function undo(): boolean;
|
|
38
|
+
export declare function redo(): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Removes source-verified records without letting older undo snapshots
|
|
41
|
+
* resurrect their previews. Newer, unsent edits and their history survive.
|
|
42
|
+
*/
|
|
43
|
+
export declare function reconcileDraftChanges(id: string, verifiedKeys: ReadonlySet<string>, verifiedStructuralIds: ReadonlySet<string>): number;
|
|
44
|
+
/** Clears the active draft and its edit steps, preserving other session history. */
|
|
45
|
+
export declare function clearActiveDraft(): void;
|
|
46
|
+
/** Drops a draft whose content no longer exists, such as a deleted iteration. */
|
|
47
|
+
export declare function forgetDraft(id: string): void;
|
|
48
|
+
export declare function loadDrafts(projectId: string, options?: {
|
|
49
|
+
persistent?: boolean;
|
|
50
|
+
}): void;
|
|
51
|
+
export declare function persistDrafts(): void;
|
|
52
|
+
/** Discards stored and in-memory drafts for the current project. */
|
|
53
|
+
export declare function clearDrafts(): void;
|
|
54
|
+
export declare function resetDrafts(): void;
|
|
5
55
|
export declare function draftIdForContent(content: FrameContent): string;
|
|
6
56
|
export declare function draftIdForCard(cardId: string): string | null;
|
|
7
|
-
export declare function sketchOwner(document: SketchDocument): string | null;
|
|
8
|
-
export declare function getDraft(id: string): Draft | null;
|
|
9
57
|
export declare function getDraftForCard(cardId: string): Draft | null;
|
|
10
58
|
export declare function getDraftContentsForCard(cardId: string): DraftContents | null;
|
|
11
|
-
export declare function persistDrafts(): void;
|
|
12
|
-
/** Replaces every in-memory draft with the stored drafts, so a tab that takes over never writes stale edits. */
|
|
13
|
-
export declare function loadDrafts(nextProjectId: string, options?: {
|
|
14
|
-
persistent?: boolean;
|
|
15
|
-
}): void;
|
|
16
59
|
export declare function activateDraftForCard(cardId: string): void;
|
|
17
60
|
export declare function activateDraft(draftId: string, preferredCardId?: string): void;
|
|
18
61
|
export declare function clearSavedIterationDraft(cardId: string, expectedRevision?: number): boolean;
|
|
19
62
|
/** Returns the first preferred frame that exists, then the first frame. */
|
|
20
63
|
export declare function getEditableCardId(...preferredCardIds: readonly (string | null | undefined)[]): string | null;
|
|
64
|
+
export declare function sketchOwner(document: SketchDocument): string | null;
|
|
21
65
|
export declare function assignSketchToDraft(draftId: string, sketchId: string): void;
|
|
22
66
|
export declare function sketchBelongsToCard(document: SketchDocument, cardId: string | null): boolean;
|
|
23
|
-
export declare function subscribeDrafts(listener: () => void): () => void;
|
|
24
|
-
export declare function getDraftsSnapshot(): DraftSnapshot;
|
|
25
|
-
export declare function useDrafts(): DraftSnapshot;
|
|
26
|
-
/** Discards stored and in-memory drafts for the current project. */
|
|
27
|
-
export declare function clearDrafts(): void;
|
|
28
|
-
export declare function resetDrafts(): void;
|