@inkandswitch/patchwork-pkg-base 0.0.41 → 0.0.42
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/package.json +1 -1
- package/packages/account/dist/{InlineContactAvatar-TVDLE2AV.js → InlineContactAvatar-6UAAJ73R.js} +2 -1
- package/packages/account/dist/InlineContactAvatar-6UAAJ73R.js.map +7 -0
- package/packages/account/dist/index.js +1 -1
- package/packages/chat/dist/{ChatRoot-DQkQQvPO.js → ChatRoot-WdCS4aLg.js} +3001 -2807
- package/packages/chat/dist/{context-tool-BuY29sJF.js → context-tool-KkwLFr2k.js} +2 -2
- package/packages/chat/dist/{index-NiYOPQzY.js → index-P8UuZ1jj.js} +3 -3
- package/packages/chat/dist/index.js +1 -1
- package/packages/chat/dist/{tool-He3mRTzT.js → tool-DWqQOW59.js} +2 -2
- package/packages/chat/package.json +1 -1
- package/packages/codemirror-base/dist/chunk-H5POLO74.js +2658 -0
- package/packages/codemirror-base/dist/chunk-H5POLO74.js.map +7 -0
- package/packages/codemirror-base/dist/lib/codemirror.js +1 -1
- package/packages/codemirror-base/dist/tool.js +9 -5
- package/packages/codemirror-base/dist/tool.js.map +2 -2
- package/packages/codemirror-base/package.json +1 -1
- package/packages/codemirror-markdown/dist/extensions/markdown.js +0 -94
- package/packages/codemirror-markdown/dist/extensions/markdown.js.map +3 -3
- package/packages/drafts/dist/assets/{DraftOverlayProvider-B0Mtq1UF.js → DraftOverlayProvider-VYKlzOy1.js} +85 -25
- package/packages/drafts/dist/assets/DraftOverlayProvider-VYKlzOy1.js.map +1 -0
- package/packages/drafts/dist/assets/{DraftListProvider-D41q9O5B.js → DraftStateProvider-DX8b_RgQ.js} +270 -39
- package/packages/drafts/dist/assets/DraftStateProvider-DX8b_RgQ.js.map +1 -0
- package/packages/drafts/dist/assets/{clone-policy-C4pVv5sr.js → clone-policy-CIdEhg7W.js} +5 -2
- package/packages/drafts/dist/assets/{clone-policy-C4pVv5sr.js.map → clone-policy-CIdEhg7W.js.map} +1 -1
- package/packages/drafts/dist/assets/draft-docs-Bi8K1y3a.js +554 -0
- package/packages/drafts/dist/assets/draft-docs-Bi8K1y3a.js.map +1 -0
- package/packages/drafts/dist/assets/main-BR_tW0sp.js +3105 -0
- package/packages/drafts/dist/assets/main-BR_tW0sp.js.map +1 -0
- package/packages/drafts/dist/index.js +14 -14
- package/packages/drafts/dist/index.js.map +1 -1
- package/packages/drafts/package.json +1 -1
- package/packages/threepane/dist/IsolatedDocumentArea-2JIBFDOX.js +2 -0
- package/packages/threepane/dist/{PatchworkFrame-4AEBOBRE.js → PatchworkFrame-P5P6JTJE.js} +2 -2
- package/packages/threepane/dist/chunk-CGHYPNSB.js +972 -0
- package/packages/threepane/dist/chunk-CGHYPNSB.js.map +7 -0
- package/packages/threepane/dist/index.js +1 -1
- package/packages/tldraw4/dist/tool.js +61 -10
- package/packages/tldraw4/dist/tool.js.map +2 -2
- package/packages/tldraw4/package.json +2 -2
- package/packages/account/dist/InlineContactAvatar-TVDLE2AV.js.map +0 -7
- package/packages/codemirror-base/dist/chunk-5IG5TKXV.js +0 -795
- package/packages/codemirror-base/dist/chunk-5IG5TKXV.js.map +0 -7
- package/packages/drafts/dist/assets/DraftListProvider-D41q9O5B.js.map +0 -1
- package/packages/drafts/dist/assets/DraftOverlayProvider-B0Mtq1UF.js.map +0 -1
- package/packages/drafts/dist/assets/main-Cp8VsNHh.js +0 -2331
- package/packages/drafts/dist/assets/main-Cp8VsNHh.js.map +0 -1
- package/packages/threepane/dist/IsolatedDocumentArea-MZBBRFMY.js +0 -2
- package/packages/threepane/dist/chunk-HNLVJAZZ.js +0 -972
- package/packages/threepane/dist/chunk-HNLVJAZZ.js.map +0 -7
- /package/packages/threepane/dist/{IsolatedDocumentArea-MZBBRFMY.js.map → IsolatedDocumentArea-2JIBFDOX.js.map} +0 -0
- /package/packages/threepane/dist/{PatchworkFrame-4AEBOBRE.js.map → PatchworkFrame-P5P6JTJE.js.map} +0 -0
|
@@ -1,22 +1,10 @@
|
|
|
1
1
|
import { isValidAutomergeUrl, stringifyAutomergeUrl, parseAutomergeUrl } from "@automerge/automerge-repo/slim";
|
|
2
2
|
import { s as subscribe, a as accept } from "./index-FKZtof6Z.js";
|
|
3
|
-
import {
|
|
3
|
+
import { S as SKIPPED_DATATYPES, c as canonicalUrl } from "./clone-policy-CIdEhg7W.js";
|
|
4
4
|
import "@automerge/automerge-repo";
|
|
5
5
|
const HANDLE_DESCRIPTOR_SELECTOR = "repo:handle-descriptor";
|
|
6
6
|
const CHECKED_OUT_SELECTOR = "draft:checked-out";
|
|
7
7
|
const DraftOverlayProvider = (element) => {
|
|
8
|
-
const rawUrl = element.getAttribute("url");
|
|
9
|
-
let draftUrl = null;
|
|
10
|
-
if (rawUrl) {
|
|
11
|
-
if (!isValidAutomergeUrl(rawUrl)) {
|
|
12
|
-
console.warn(
|
|
13
|
-
`[drafts] <patchwork-view component="patchwork-draft-overlay-provider"> has an invalid url attribute (got ${JSON.stringify(rawUrl)})`
|
|
14
|
-
);
|
|
15
|
-
return () => {
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
draftUrl = rawUrl;
|
|
19
|
-
}
|
|
20
8
|
const repo = "repo" in window ? window.repo : void 0;
|
|
21
9
|
if (!repo) {
|
|
22
10
|
console.warn(
|
|
@@ -27,23 +15,46 @@ const DraftOverlayProvider = (element) => {
|
|
|
27
15
|
}
|
|
28
16
|
const liveRepo = repo;
|
|
29
17
|
let disposed = false;
|
|
18
|
+
let draftUrl = null;
|
|
19
|
+
let draftReady = null;
|
|
20
|
+
let refresh = new AbortController();
|
|
30
21
|
const cloneResolutions = /* @__PURE__ */ new Map();
|
|
31
|
-
const
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
22
|
+
const descriptorSubscribers = /* @__PURE__ */ new Set();
|
|
23
|
+
const rawSeed = element.getAttribute("url");
|
|
24
|
+
if (rawSeed) {
|
|
25
|
+
if (isValidAutomergeUrl(rawSeed)) {
|
|
26
|
+
void applyDraft(rawSeed);
|
|
27
|
+
} else {
|
|
28
|
+
console.warn(
|
|
29
|
+
`[drafts] <patchwork-view component="patchwork-draft-overlay-provider"> has an invalid url attribute (got ${JSON.stringify(rawSeed)})`
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
39
33
|
let checkedOutHandle = null;
|
|
34
|
+
let checkpointSignature = JSON.stringify(null);
|
|
35
|
+
const onCheckedOutChange = () => {
|
|
36
|
+
const doc = checkedOutHandle?.doc();
|
|
37
|
+
const nextDraft = doc?.checkedOut ?? null;
|
|
38
|
+
const nextSignature = JSON.stringify(doc?.at ?? null);
|
|
39
|
+
const checkpointMoved = nextSignature !== checkpointSignature;
|
|
40
|
+
checkpointSignature = nextSignature;
|
|
41
|
+
if (nextDraft !== draftUrl) {
|
|
42
|
+
void applyDraft(nextDraft);
|
|
43
|
+
} else if (checkpointMoved) {
|
|
44
|
+
refreshDescriptors();
|
|
45
|
+
}
|
|
46
|
+
};
|
|
40
47
|
const unsubscribeCheckedOut = subscribe(
|
|
41
48
|
element,
|
|
42
49
|
{ type: CHECKED_OUT_SELECTOR },
|
|
43
50
|
(url) => {
|
|
44
51
|
if (disposed || !isValidAutomergeUrl(url)) return;
|
|
45
52
|
void liveRepo.find(url).then((handle) => {
|
|
46
|
-
if (
|
|
53
|
+
if (disposed || checkedOutHandle === handle) return;
|
|
54
|
+
checkedOutHandle?.off("change", onCheckedOutChange);
|
|
55
|
+
checkedOutHandle = handle;
|
|
56
|
+
handle.on("change", onCheckedOutChange);
|
|
57
|
+
onCheckedOutChange();
|
|
47
58
|
});
|
|
48
59
|
}
|
|
49
60
|
);
|
|
@@ -56,10 +67,18 @@ const DraftOverlayProvider = (element) => {
|
|
|
56
67
|
}
|
|
57
68
|
const original = canonicalUrl(rawTarget);
|
|
58
69
|
accept(event, (respond) => {
|
|
70
|
+
const subscriber = { original, respond };
|
|
71
|
+
descriptorSubscribers.add(subscriber);
|
|
72
|
+
const { signal } = refresh;
|
|
59
73
|
void resolveDescriptor(original).then((descriptor) => {
|
|
60
|
-
if (
|
|
74
|
+
if (signal.aborted) return;
|
|
61
75
|
respond(descriptor);
|
|
76
|
+
}).catch((err) => {
|
|
77
|
+
console.error(`[drafts] failed to resolve ${original}:`, err);
|
|
62
78
|
});
|
|
79
|
+
return () => {
|
|
80
|
+
descriptorSubscribers.delete(subscriber);
|
|
81
|
+
};
|
|
63
82
|
});
|
|
64
83
|
return;
|
|
65
84
|
}
|
|
@@ -67,10 +86,48 @@ const DraftOverlayProvider = (element) => {
|
|
|
67
86
|
element.addEventListener("patchwork:subscribe", onSubscribe);
|
|
68
87
|
return () => {
|
|
69
88
|
disposed = true;
|
|
89
|
+
refresh.abort();
|
|
70
90
|
element.removeEventListener("patchwork:subscribe", onSubscribe);
|
|
71
91
|
unsubscribeCheckedOut();
|
|
92
|
+
checkedOutHandle?.off("change", onCheckedOutChange);
|
|
93
|
+
checkedOutHandle = null;
|
|
94
|
+
descriptorSubscribers.clear();
|
|
72
95
|
cloneResolutions.clear();
|
|
73
96
|
};
|
|
97
|
+
async function applyDraft(next) {
|
|
98
|
+
if (disposed) return;
|
|
99
|
+
if (next === draftUrl) return;
|
|
100
|
+
draftUrl = next;
|
|
101
|
+
cloneResolutions.clear();
|
|
102
|
+
element.setAttribute("draft-url", next ?? "");
|
|
103
|
+
draftReady = next ? (async () => {
|
|
104
|
+
const handle = await liveRepo.find(next);
|
|
105
|
+
if (disposed) {
|
|
106
|
+
throw new Error("[drafts] provider disposed mid-load");
|
|
107
|
+
}
|
|
108
|
+
return handle;
|
|
109
|
+
})() : null;
|
|
110
|
+
draftReady?.catch((err) => {
|
|
111
|
+
console.error(`[drafts] failed to load draft overlay for ${next}:`, err);
|
|
112
|
+
});
|
|
113
|
+
refreshDescriptors();
|
|
114
|
+
}
|
|
115
|
+
function refreshDescriptors() {
|
|
116
|
+
refresh.abort();
|
|
117
|
+
refresh = new AbortController();
|
|
118
|
+
const { signal } = refresh;
|
|
119
|
+
for (const subscriber of [...descriptorSubscribers]) {
|
|
120
|
+
void resolveDescriptor(subscriber.original).then((descriptor) => {
|
|
121
|
+
if (signal.aborted) return;
|
|
122
|
+
subscriber.respond(descriptor);
|
|
123
|
+
}).catch((err) => {
|
|
124
|
+
console.error(
|
|
125
|
+
`[drafts] failed to re-map ${subscriber.original}:`,
|
|
126
|
+
err
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
74
131
|
async function resolveDescriptor(original) {
|
|
75
132
|
const to = checkedOutHandle?.doc()?.at?.[original]?.to ?? void 0;
|
|
76
133
|
if (!draftUrl || await isSkippedDoc(original)) {
|
|
@@ -100,8 +157,11 @@ const DraftOverlayProvider = (element) => {
|
|
|
100
157
|
function resolveClone(original) {
|
|
101
158
|
const cached = cloneResolutions.get(original);
|
|
102
159
|
if (cached) return cached;
|
|
160
|
+
const ready = draftReady;
|
|
103
161
|
const promise = (async () => {
|
|
104
|
-
if (!ready)
|
|
162
|
+
if (!ready) {
|
|
163
|
+
throw new Error("[drafts] resolveClone called without a draft");
|
|
164
|
+
}
|
|
105
165
|
const handle = await ready;
|
|
106
166
|
const existing = handle.doc()?.clones?.[original];
|
|
107
167
|
if (existing) return canonicalUrl(existing.cloneUrl);
|
|
@@ -121,4 +181,4 @@ const DraftOverlayProvider = (element) => {
|
|
|
121
181
|
export {
|
|
122
182
|
DraftOverlayProvider
|
|
123
183
|
};
|
|
124
|
-
//# sourceMappingURL=DraftOverlayProvider-
|
|
184
|
+
//# sourceMappingURL=DraftOverlayProvider-VYKlzOy1.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DraftOverlayProvider-VYKlzOy1.js","sources":["../../src/providers/DraftOverlayProvider.ts"],"sourcesContent":["import {\n isValidAutomergeUrl,\n parseAutomergeUrl,\n stringifyAutomergeUrl,\n type AutomergeUrl,\n type DocHandle,\n type UrlHeads,\n} from \"@automerge/automerge-repo/slim\";\nimport {\n accept,\n subscribe,\n type DocHandleDescriptor,\n type SubscribeEvent,\n} from \"@inkandswitch/patchwork-providers\";\n\nimport type { CheckedOutDraft, DraftDoc } from \"../draft-types.js\";\nimport { SKIPPED_DATATYPES, canonicalUrl } from \"../clone-policy.js\";\n\nconst HANDLE_DESCRIPTOR_SELECTOR = \"repo:handle-descriptor\";\nconst CHECKED_OUT_SELECTOR = \"draft:checked-out\";\n\n// Remaps documents resolved beneath it onto per-draft clones, so edits stay\n// inside the checked-out draft — and re-points *live* in place when the\n// selection changes, without the host remounting anything.\n//\n// The host `<patchwork-view>` wraps tool document resolution in an\n// `OverlayRepo` that opens a *streaming* `repo:handle-descriptor` subscription\n// per document. This provider always claims those subscriptions (even while\n// \"main\" is selected, where it answers a pass-through `{ url }`) and keeps the\n// `respond` callbacks registered. It follows the selection itself via the\n// ancestor draft-state provider's `draft:checked-out` doc: when\n// `CheckedOutDraft.checkedOut` changes, every live subscription is re-answered\n// with the new mapping — `{ url, cloneUrl }` on a draft (the clone is forked\n// eagerly on first resolution and recorded in `DraftDoc.clones`), `{ url }` on\n// main — and the `OverlayRepo` swaps handle backings in place.\n//\n// Descriptors also honor the active checkpoint: `CheckedOutDraft.at` maps each\n// member doc to per-doc `to`/`from` heads, and the `to` heads are baked onto\n// the backing url (the clone on a draft, the original on main) so nested views\n// freeze with the doc they live in; `OverlayRepo` honors heads on the backing\n// url. Checkpoint moves (history scrubbing rewrites `at` without changing\n// `checkedOut`) also re-answer every live subscription, so pins update by\n// swapping backings in place — no remount. The fork point lives in\n// `DraftDoc.clones[url].clonedAt`; the draft-state provider reads it to serve\n// `draft:baseline` (this provider no longer answers that).\n//\n// A `url` attribute, when present, seeds the initial selection. That is how\n// the chat preview iframe (see chat's `preview-frame.ts`) pins a\n// self-bootstrapped overlay to a specific draft in a realm that has no\n// draft-state provider to follow. The *current* selection is reflected onto the\n// (un-observed, so remount-free) `draft-url` attribute for outside readers.\nexport const DraftOverlayProvider = (element: HTMLElement) => {\n const repo = \"repo\" in window ? window.repo : undefined;\n if (!repo) {\n console.warn(\n \"[drafts] window.repo is not set; draft overlay provider disabled\"\n );\n return () => {};\n }\n const liveRepo = repo;\n\n let disposed = false;\n\n // The checked-out draft this overlay currently maps onto. Null = \"main\"\n // (pass-through descriptors, save for checkpoint pinning).\n let draftUrl: AutomergeUrl | null = null;\n let draftReady: Promise<DocHandle<DraftDoc>> | null = null;\n // Aborted and replaced on every descriptor refresh (draft re-point or\n // checkpoint move), and aborted on dispose, so in-flight resolutions from a\n // superseded state can detect they lost the race and stay silent. The\n // signal only gates `respond` — it is never passed into the resolution\n // work, which is shared across batches (see `cloneResolutions`).\n let refresh = new AbortController();\n\n // One eager-clone resolution per original url; de-dupes concurrent requests.\n // Cleared on re-point (it is per-draft state).\n const cloneResolutions = new Map<AutomergeUrl, Promise<AutomergeUrl>>();\n\n // Live `repo:handle-descriptor` subscriptions, kept so a re-point can push\n // fresh descriptors to every consumer.\n type DescriptorSubscriber = {\n original: AutomergeUrl;\n respond: (descriptor: DocHandleDescriptor) => void;\n };\n const descriptorSubscribers = new Set<DescriptorSubscriber>();\n\n // Seed the selection from the `url` attribute when present (the chat\n // preview iframe mounts us with a pinned draft and no draft-state provider).\n const rawSeed = element.getAttribute(\"url\");\n if (rawSeed) {\n if (isValidAutomergeUrl(rawSeed)) {\n void applyDraft(rawSeed);\n } else {\n console.warn(\n `[drafts] <patchwork-view component=\"patchwork-draft-overlay-provider\"> ` +\n `has an invalid url attribute (got ${JSON.stringify(rawSeed)})`\n );\n }\n }\n\n // Follow the selection: the ancestor draft-state provider serves the\n // ephemeral CheckedOutDraft doc url; we watch its `checkedOut` live. The\n // same doc carries the checkpoint (`at`), read at resolve time so\n // descriptors can pin a nested doc to its per-doc `to` heads — absent means\n // render live.\n //\n // A draft switch re-points via `applyDraft` (resets per-draft state); a\n // checkpoint-only move (scrubbing rewrites `at` while `checkedOut` stays\n // put) just re-answers live subscriptions with freshly pinned descriptors.\n let checkedOutHandle: DocHandle<CheckedOutDraft> | null = null;\n let checkpointSignature = JSON.stringify(null);\n const onCheckedOutChange = () => {\n const doc = checkedOutHandle?.doc();\n const nextDraft = doc?.checkedOut ?? null;\n const nextSignature = JSON.stringify(doc?.at ?? null);\n const checkpointMoved = nextSignature !== checkpointSignature;\n checkpointSignature = nextSignature;\n if (nextDraft !== draftUrl) {\n void applyDraft(nextDraft);\n } else if (checkpointMoved) {\n refreshDescriptors();\n }\n };\n const unsubscribeCheckedOut = subscribe<AutomergeUrl>(\n element,\n { type: CHECKED_OUT_SELECTOR },\n (url) => {\n if (disposed || !isValidAutomergeUrl(url)) return;\n void liveRepo.find<CheckedOutDraft>(url).then((handle) => {\n if (disposed || checkedOutHandle === handle) return;\n checkedOutHandle?.off(\"change\", onCheckedOutChange);\n checkedOutHandle = handle;\n handle.on(\"change\", onCheckedOutChange);\n onCheckedOutChange();\n });\n }\n );\n\n const onSubscribe = (event: SubscribeEvent) => {\n const selector = event.detail.selector;\n\n if (selector.type === HANDLE_DESCRIPTOR_SELECTOR) {\n const rawTarget = selector.url;\n if (typeof rawTarget !== \"string\" || !isValidAutomergeUrl(rawTarget)) {\n return;\n }\n const original = canonicalUrl(rawTarget);\n accept<DocHandleDescriptor>(event, (respond) => {\n const subscriber: DescriptorSubscriber = { original, respond };\n descriptorSubscribers.add(subscriber);\n const { signal } = refresh;\n void resolveDescriptor(original)\n .then((descriptor) => {\n // A re-point raced this resolution; `applyDraft`'s refresh pass\n // answers this subscriber with the new mapping instead.\n if (signal.aborted) return;\n respond(descriptor);\n })\n .catch((err) => {\n console.error(`[drafts] failed to resolve ${original}:`, err);\n });\n return () => {\n descriptorSubscribers.delete(subscriber);\n };\n });\n return;\n }\n };\n\n element.addEventListener(\"patchwork:subscribe\", onSubscribe);\n return () => {\n disposed = true;\n refresh.abort();\n element.removeEventListener(\"patchwork:subscribe\", onSubscribe);\n unsubscribeCheckedOut();\n checkedOutHandle?.off(\"change\", onCheckedOutChange);\n checkedOutHandle = null;\n descriptorSubscribers.clear();\n cloneResolutions.clear();\n };\n\n // Re-point the overlay at a new selection in place: reset the per-draft\n // state, then push fresh descriptors to every live subscriber.\n async function applyDraft(next: AutomergeUrl | null): Promise<void> {\n if (disposed) return;\n if (next === draftUrl) return;\n\n draftUrl = next;\n cloneResolutions.clear();\n\n // Reflect the selection for outside readers (e.g. the chat preview frame).\n // `draft-url` is not observed by <patchwork-view>, so this never remounts.\n element.setAttribute(\"draft-url\", next ?? \"\");\n\n draftReady = next\n ? (async () => {\n const handle = await liveRepo.find<DraftDoc>(next);\n if (disposed) {\n throw new Error(\"[drafts] provider disposed mid-load\");\n }\n return handle;\n })()\n : null;\n draftReady?.catch((err) => {\n console.error(`[drafts] failed to load draft overlay for ${next}:`, err);\n });\n\n refreshDescriptors();\n }\n\n // Re-answer every live descriptor subscription against the current\n // selection and checkpoint. Aborts the previous batch so slower, superseded\n // resolutions (including initial answers racing this refresh) stay silent.\n function refreshDescriptors(): void {\n refresh.abort();\n refresh = new AbortController();\n const { signal } = refresh;\n for (const subscriber of [...descriptorSubscribers]) {\n void resolveDescriptor(subscriber.original)\n .then((descriptor) => {\n if (signal.aborted) return;\n subscriber.respond(descriptor);\n })\n .catch((err) => {\n console.error(\n `[drafts] failed to re-map ${subscriber.original}:`,\n err\n );\n });\n }\n }\n\n // Resolve a `repo:handle-descriptor` request against the current selection.\n // The backing url is pinned to the active checkpoint's `to` heads for this\n // doc (if any) so nested views freeze with the doc they live in;\n // `OverlayRepo` honors heads on the backing url.\n // - On \"main\" (no draft): no clone, just the (maybe pinned) original.\n // - Skipped docs (account, contacts): the real doc, never forked.\n // - Everything else on a draft: the per-draft clone (pinned when checked out).\n async function resolveDescriptor(\n original: AutomergeUrl\n ): Promise<DocHandleDescriptor> {\n const to = checkedOutHandle?.doc()?.at?.[original]?.to ?? undefined;\n if (!draftUrl || (await isSkippedDoc(original))) {\n return to\n ? { url: original, cloneUrl: withHeads(original, to) }\n : { url: original };\n }\n const cloneUrl = await resolveClone(original);\n return { url: original, cloneUrl: withHeads(cloneUrl, to) };\n }\n\n // Stamp `heads` onto `url` (same documentId), or return it unchanged when\n // there is no pin. Heads ride on the url so `OverlayRepo` resolves the doc at\n // that point in time.\n function withHeads(\n url: AutomergeUrl,\n heads: UrlHeads | undefined\n ): AutomergeUrl {\n if (!heads) return url;\n return stringifyAutomergeUrl({\n documentId: parseAutomergeUrl(url).documentId,\n heads,\n });\n }\n\n // A doc is skipped when its `@patchwork.type` is in `SKIPPED_DATATYPES`. On\n // any failure we fall back to cloning (the existing behaviour), which is the\n // safe default — a doc that should be skipped merely keeps forking, it isn't\n // lost.\n async function isSkippedDoc(original: AutomergeUrl): Promise<boolean> {\n try {\n const handle = await liveRepo.find<{ \"@patchwork\"?: { type?: string } }>(\n original\n );\n const type = handle.doc()?.[\"@patchwork\"]?.type;\n return type != null && SKIPPED_DATATYPES.has(type);\n } catch {\n return false;\n }\n }\n\n // Ensure a clone of `original` exists for the current draft and return its\n // url. Reuses an existing clone recorded in `DraftDoc.clones`; otherwise\n // forks `original` at its current heads and records the fork point so the\n // baseline and merge-back can find it.\n function resolveClone(original: AutomergeUrl): Promise<AutomergeUrl> {\n const cached = cloneResolutions.get(original);\n if (cached) return cached;\n const ready = draftReady;\n const promise = (async () => {\n if (!ready) {\n throw new Error(\"[drafts] resolveClone called without a draft\");\n }\n const handle = await ready;\n const existing = handle.doc()?.clones?.[original];\n if (existing) return canonicalUrl(existing.cloneUrl);\n\n const originalHandle = await liveRepo.find<unknown>(original);\n const clonedAt = originalHandle.heads();\n const clone = liveRepo.clone(originalHandle);\n const cloneUrl = canonicalUrl(clone.url);\n\n handle.change((d) => {\n d.clones[original] = { cloneUrl, clonedAt };\n });\n\n return cloneUrl;\n })();\n cloneResolutions.set(original, promise);\n return promise;\n }\n};\n"],"names":[],"mappings":";;;;AAkBA,MAAM,6BAA6B;AACnC,MAAM,uBAAuB;AAgCtB,MAAM,uBAAuB,CAAC,YAAyB;AAC5D,QAAM,OAAO,UAAU,SAAS,OAAO,OAAO;AAC9C,MAAI,CAAC,MAAM;AACT,YAAQ;AAAA,MACN;AAAA,IAAA;AAEF,WAAO,MAAM;AAAA,IAAC;AAAA,EAChB;AACA,QAAM,WAAW;AAEjB,MAAI,WAAW;AAIf,MAAI,WAAgC;AACpC,MAAI,aAAkD;AAMtD,MAAI,UAAU,IAAI,gBAAA;AAIlB,QAAM,uCAAuB,IAAA;AAQ7B,QAAM,4CAA4B,IAAA;AAIlC,QAAM,UAAU,QAAQ,aAAa,KAAK;AAC1C,MAAI,SAAS;AACX,QAAI,oBAAoB,OAAO,GAAG;AAChC,WAAK,WAAW,OAAO;AAAA,IACzB,OAAO;AACL,cAAQ;AAAA,QACN,4GACuC,KAAK,UAAU,OAAO,CAAC;AAAA,MAAA;AAAA,IAElE;AAAA,EACF;AAWA,MAAI,mBAAsD;AAC1D,MAAI,sBAAsB,KAAK,UAAU,IAAI;AAC7C,QAAM,qBAAqB,MAAM;AAC/B,UAAM,MAAM,kBAAkB,IAAA;AAC9B,UAAM,YAAY,KAAK,cAAc;AACrC,UAAM,gBAAgB,KAAK,UAAU,KAAK,MAAM,IAAI;AACpD,UAAM,kBAAkB,kBAAkB;AAC1C,0BAAsB;AACtB,QAAI,cAAc,UAAU;AAC1B,WAAK,WAAW,SAAS;AAAA,IAC3B,WAAW,iBAAiB;AAC1B,yBAAA;AAAA,IACF;AAAA,EACF;AACA,QAAM,wBAAwB;AAAA,IAC5B;AAAA,IACA,EAAE,MAAM,qBAAA;AAAA,IACR,CAAC,QAAQ;AACP,UAAI,YAAY,CAAC,oBAAoB,GAAG,EAAG;AAC3C,WAAK,SAAS,KAAsB,GAAG,EAAE,KAAK,CAAC,WAAW;AACxD,YAAI,YAAY,qBAAqB,OAAQ;AAC7C,0BAAkB,IAAI,UAAU,kBAAkB;AAClD,2BAAmB;AACnB,eAAO,GAAG,UAAU,kBAAkB;AACtC,2BAAA;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EAAA;AAGF,QAAM,cAAc,CAAC,UAA0B;AAC7C,UAAM,WAAW,MAAM,OAAO;AAE9B,QAAI,SAAS,SAAS,4BAA4B;AAChD,YAAM,YAAY,SAAS;AAC3B,UAAI,OAAO,cAAc,YAAY,CAAC,oBAAoB,SAAS,GAAG;AACpE;AAAA,MACF;AACA,YAAM,WAAW,aAAa,SAAS;AACvC,aAA4B,OAAO,CAAC,YAAY;AAC9C,cAAM,aAAmC,EAAE,UAAU,QAAA;AACrD,8BAAsB,IAAI,UAAU;AACpC,cAAM,EAAE,WAAW;AACnB,aAAK,kBAAkB,QAAQ,EAC5B,KAAK,CAAC,eAAe;AAGpB,cAAI,OAAO,QAAS;AACpB,kBAAQ,UAAU;AAAA,QACpB,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,kBAAQ,MAAM,8BAA8B,QAAQ,KAAK,GAAG;AAAA,QAC9D,CAAC;AACH,eAAO,MAAM;AACX,gCAAsB,OAAO,UAAU;AAAA,QACzC;AAAA,MACF,CAAC;AACD;AAAA,IACF;AAAA,EACF;AAEA,UAAQ,iBAAiB,uBAAuB,WAAW;AAC3D,SAAO,MAAM;AACX,eAAW;AACX,YAAQ,MAAA;AACR,YAAQ,oBAAoB,uBAAuB,WAAW;AAC9D,0BAAA;AACA,sBAAkB,IAAI,UAAU,kBAAkB;AAClD,uBAAmB;AACnB,0BAAsB,MAAA;AACtB,qBAAiB,MAAA;AAAA,EACnB;AAIA,iBAAe,WAAW,MAA0C;AAClE,QAAI,SAAU;AACd,QAAI,SAAS,SAAU;AAEvB,eAAW;AACX,qBAAiB,MAAA;AAIjB,YAAQ,aAAa,aAAa,QAAQ,EAAE;AAE5C,iBAAa,QACR,YAAY;AACX,YAAM,SAAS,MAAM,SAAS,KAAe,IAAI;AACjD,UAAI,UAAU;AACZ,cAAM,IAAI,MAAM,qCAAqC;AAAA,MACvD;AACA,aAAO;AAAA,IACT,OACA;AACJ,gBAAY,MAAM,CAAC,QAAQ;AACzB,cAAQ,MAAM,6CAA6C,IAAI,KAAK,GAAG;AAAA,IACzE,CAAC;AAED,uBAAA;AAAA,EACF;AAKA,WAAS,qBAA2B;AAClC,YAAQ,MAAA;AACR,cAAU,IAAI,gBAAA;AACd,UAAM,EAAE,WAAW;AACnB,eAAW,cAAc,CAAC,GAAG,qBAAqB,GAAG;AACnD,WAAK,kBAAkB,WAAW,QAAQ,EACvC,KAAK,CAAC,eAAe;AACpB,YAAI,OAAO,QAAS;AACpB,mBAAW,QAAQ,UAAU;AAAA,MAC/B,CAAC,EACA,MAAM,CAAC,QAAQ;AACd,gBAAQ;AAAA,UACN,6BAA6B,WAAW,QAAQ;AAAA,UAChD;AAAA,QAAA;AAAA,MAEJ,CAAC;AAAA,IACL;AAAA,EACF;AASA,iBAAe,kBACb,UAC8B;AAC9B,UAAM,KAAK,kBAAkB,IAAA,GAAO,KAAK,QAAQ,GAAG,MAAM;AAC1D,QAAI,CAAC,YAAa,MAAM,aAAa,QAAQ,GAAI;AAC/C,aAAO,KACH,EAAE,KAAK,UAAU,UAAU,UAAU,UAAU,EAAE,EAAA,IACjD,EAAE,KAAK,SAAA;AAAA,IACb;AACA,UAAM,WAAW,MAAM,aAAa,QAAQ;AAC5C,WAAO,EAAE,KAAK,UAAU,UAAU,UAAU,UAAU,EAAE,EAAA;AAAA,EAC1D;AAKA,WAAS,UACP,KACA,OACc;AACd,QAAI,CAAC,MAAO,QAAO;AACnB,WAAO,sBAAsB;AAAA,MAC3B,YAAY,kBAAkB,GAAG,EAAE;AAAA,MACnC;AAAA,IAAA,CACD;AAAA,EACH;AAMA,iBAAe,aAAa,UAA0C;AACpE,QAAI;AACF,YAAM,SAAS,MAAM,SAAS;AAAA,QAC5B;AAAA,MAAA;AAEF,YAAM,OAAO,OAAO,IAAA,IAAQ,YAAY,GAAG;AAC3C,aAAO,QAAQ,QAAQ,kBAAkB,IAAI,IAAI;AAAA,IACnD,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAMA,WAAS,aAAa,UAA+C;AACnE,UAAM,SAAS,iBAAiB,IAAI,QAAQ;AAC5C,QAAI,OAAQ,QAAO;AACnB,UAAM,QAAQ;AACd,UAAM,WAAW,YAAY;AAC3B,UAAI,CAAC,OAAO;AACV,cAAM,IAAI,MAAM,8CAA8C;AAAA,MAChE;AACA,YAAM,SAAS,MAAM;AACrB,YAAM,WAAW,OAAO,IAAA,GAAO,SAAS,QAAQ;AAChD,UAAI,SAAU,QAAO,aAAa,SAAS,QAAQ;AAEnD,YAAM,iBAAiB,MAAM,SAAS,KAAc,QAAQ;AAC5D,YAAM,WAAW,eAAe,MAAA;AAChC,YAAM,QAAQ,SAAS,MAAM,cAAc;AAC3C,YAAM,WAAW,aAAa,MAAM,GAAG;AAEvC,aAAO,OAAO,CAAC,MAAM;AACnB,UAAE,OAAO,QAAQ,IAAI,EAAE,UAAU,SAAA;AAAA,MACnC,CAAC;AAED,aAAO;AAAA,IACT,GAAA;AACA,qBAAiB,IAAI,UAAU,OAAO;AACtC,WAAO;AAAA,EACT;AACF;"}
|
package/packages/drafts/dist/assets/{DraftListProvider-D41q9O5B.js → DraftStateProvider-DX8b_RgQ.js}
RENAMED
|
@@ -1,18 +1,186 @@
|
|
|
1
1
|
import { isValidAutomergeUrl, encodeHeads } from "@automerge/automerge-repo/slim";
|
|
2
|
-
import { a as accept } from "./index-FKZtof6Z.js";
|
|
3
|
-
import {
|
|
2
|
+
import { s as subscribe, a as accept } from "./index-FKZtof6Z.js";
|
|
3
|
+
import { S as SKIPPED_DATATYPES, c as canonicalUrl } from "./clone-policy-CIdEhg7W.js";
|
|
4
|
+
import { c as createChangeGrouper, e as ensureMainDraft } from "./draft-docs-Bi8K1y3a.js";
|
|
5
|
+
import * as Automerge from "@automerge/automerge/slim";
|
|
4
6
|
import "@automerge/automerge-repo";
|
|
7
|
+
function createActorRecorder(element) {
|
|
8
|
+
let contactUrl = null;
|
|
9
|
+
let attributionHandle = null;
|
|
10
|
+
const recordedActorIds = /* @__PURE__ */ new Set();
|
|
11
|
+
const pendingActorIds = /* @__PURE__ */ new Set();
|
|
12
|
+
let disposed = false;
|
|
13
|
+
const unsubscribe = subscribe(
|
|
14
|
+
element,
|
|
15
|
+
{ type: CONTACT_SELECTOR },
|
|
16
|
+
(value) => {
|
|
17
|
+
if (disposed || contactUrl) return;
|
|
18
|
+
if (typeof value === "string" && isValidAutomergeUrl(value)) {
|
|
19
|
+
contactUrl = value;
|
|
20
|
+
flushPendingActors();
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
);
|
|
24
|
+
return {
|
|
25
|
+
recordLocalChange(doc) {
|
|
26
|
+
if (disposed) return;
|
|
27
|
+
let actorId;
|
|
28
|
+
try {
|
|
29
|
+
actorId = Automerge.getActorId(doc);
|
|
30
|
+
} catch {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (recordedActorIds.has(actorId)) return;
|
|
34
|
+
pendingActorIds.add(actorId);
|
|
35
|
+
flushPendingActors();
|
|
36
|
+
},
|
|
37
|
+
setAttributionHandle(handle) {
|
|
38
|
+
if (disposed) return;
|
|
39
|
+
attributionHandle = handle;
|
|
40
|
+
flushPendingActors();
|
|
41
|
+
},
|
|
42
|
+
dispose() {
|
|
43
|
+
disposed = true;
|
|
44
|
+
pendingActorIds.clear();
|
|
45
|
+
unsubscribe();
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
function flushPendingActors() {
|
|
49
|
+
if (!attributionHandle || !contactUrl || pendingActorIds.size === 0) return;
|
|
50
|
+
const url = contactUrl;
|
|
51
|
+
const actorIds = [...pendingActorIds];
|
|
52
|
+
pendingActorIds.clear();
|
|
53
|
+
for (const id of actorIds) recordedActorIds.add(id);
|
|
54
|
+
const existing = attributionHandle.doc()?.actors ?? {};
|
|
55
|
+
const missing = actorIds.filter((id) => existing[id] !== url);
|
|
56
|
+
if (missing.length === 0) return;
|
|
57
|
+
attributionHandle.change((d) => {
|
|
58
|
+
for (const id of missing) d.actors[id] = url;
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function ensureActorAttribution(repo, mainDraftHandle) {
|
|
63
|
+
const existingUrl = mainDraftHandle.doc()?.actorAttributionUrl;
|
|
64
|
+
if (existingUrl && isValidAutomergeUrl(existingUrl)) {
|
|
65
|
+
return repo.find(existingUrl);
|
|
66
|
+
}
|
|
67
|
+
const attribution = repo.create({
|
|
68
|
+
"@patchwork": { type: "actor-attribution" },
|
|
69
|
+
actors: {}
|
|
70
|
+
});
|
|
71
|
+
mainDraftHandle.change((d) => {
|
|
72
|
+
if (!d.actorAttributionUrl) d.actorAttributionUrl = attribution.url;
|
|
73
|
+
});
|
|
74
|
+
const settled = mainDraftHandle.doc()?.actorAttributionUrl;
|
|
75
|
+
if (settled && settled !== attribution.url && isValidAutomergeUrl(settled)) {
|
|
76
|
+
return repo.find(settled);
|
|
77
|
+
}
|
|
78
|
+
return attribution;
|
|
79
|
+
}
|
|
80
|
+
const CONTACT_SELECTOR = "patchwork:contact";
|
|
81
|
+
function createDraftRouter(checkedOutHandle) {
|
|
82
|
+
let availableDrafts = /* @__PURE__ */ new Set();
|
|
83
|
+
let pendingUrlDraft = readDraftParam();
|
|
84
|
+
let lastCheckedOut = null;
|
|
85
|
+
let disposed = false;
|
|
86
|
+
checkedOutHandle.on("change", syncSelectionToUrl);
|
|
87
|
+
window.addEventListener("hashchange", onHashChange);
|
|
88
|
+
return {
|
|
89
|
+
updateAvailableDrafts(draftUrls) {
|
|
90
|
+
if (disposed) return;
|
|
91
|
+
availableDrafts = new Set(draftUrls);
|
|
92
|
+
reconcileSelection();
|
|
93
|
+
},
|
|
94
|
+
dispose() {
|
|
95
|
+
disposed = true;
|
|
96
|
+
checkedOutHandle.off("change", syncSelectionToUrl);
|
|
97
|
+
window.removeEventListener("hashchange", onHashChange);
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
function onHashChange() {
|
|
101
|
+
if (disposed) return;
|
|
102
|
+
const urlDraft = readDraftParam();
|
|
103
|
+
const selected = checkedOutHandle.doc()?.checkedOut ?? null;
|
|
104
|
+
if (urlDraft === selected) {
|
|
105
|
+
pendingUrlDraft = null;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
if (urlDraft === null) {
|
|
109
|
+
pendingUrlDraft = null;
|
|
110
|
+
selectDraft(null);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (availableDrafts.has(urlDraft)) {
|
|
114
|
+
pendingUrlDraft = null;
|
|
115
|
+
selectDraft(urlDraft);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
pendingUrlDraft = urlDraft;
|
|
119
|
+
}
|
|
120
|
+
function reconcileSelection() {
|
|
121
|
+
if (pendingUrlDraft && availableDrafts.has(pendingUrlDraft)) {
|
|
122
|
+
const target = pendingUrlDraft;
|
|
123
|
+
pendingUrlDraft = null;
|
|
124
|
+
selectDraft(target);
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const selected = checkedOutHandle.doc()?.checkedOut ?? null;
|
|
128
|
+
if (selected && !availableDrafts.has(selected)) selectDraft(null);
|
|
129
|
+
}
|
|
130
|
+
function selectDraft(selected) {
|
|
131
|
+
if (checkedOutHandle.doc()?.checkedOut === selected) return;
|
|
132
|
+
checkedOutHandle.change((d) => {
|
|
133
|
+
d.checkedOut = selected;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
function syncSelectionToUrl() {
|
|
137
|
+
if (disposed) return;
|
|
138
|
+
const selected = checkedOutHandle.doc()?.checkedOut ?? null;
|
|
139
|
+
if (selected === lastCheckedOut) return;
|
|
140
|
+
lastCheckedOut = selected;
|
|
141
|
+
if (pendingUrlDraft && selected !== pendingUrlDraft) pendingUrlDraft = null;
|
|
142
|
+
writeDraftParam(selected);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
const DRAFT_PARAM = "draft";
|
|
146
|
+
const RAW_HASH_KEYS = /* @__PURE__ */ new Set(["doc", DRAFT_PARAM]);
|
|
147
|
+
function readDraftParam() {
|
|
148
|
+
const raw = new URLSearchParams(window.location.hash.slice(1)).get(
|
|
149
|
+
DRAFT_PARAM
|
|
150
|
+
);
|
|
151
|
+
return raw && isValidAutomergeUrl(raw) ? raw : null;
|
|
152
|
+
}
|
|
153
|
+
function writeDraftParam(selected) {
|
|
154
|
+
const params = new URLSearchParams(window.location.hash.slice(1));
|
|
155
|
+
if ((params.get(DRAFT_PARAM) ?? null) === selected) return;
|
|
156
|
+
if (selected) params.set(DRAFT_PARAM, selected);
|
|
157
|
+
else params.delete(DRAFT_PARAM);
|
|
158
|
+
try {
|
|
159
|
+
history.replaceState(null, "", "#" + serializeHash(params));
|
|
160
|
+
} catch {
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function serializeHash(params) {
|
|
164
|
+
const parts = [];
|
|
165
|
+
params.forEach((value, key) => {
|
|
166
|
+
if (!value) return;
|
|
167
|
+
parts.push(
|
|
168
|
+
`${key}=${RAW_HASH_KEYS.has(key) ? value : encodeURIComponent(value)}`
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
return parts.join("&");
|
|
172
|
+
}
|
|
5
173
|
const ROOT_DOC_SELECTOR = "draft:root-doc";
|
|
6
174
|
const CHECKED_OUT_SELECTOR = "draft:checked-out";
|
|
7
175
|
const DRAFT_LIST_SELECTOR = "draft:list";
|
|
8
176
|
const BASELINE_SELECTOR = "draft:baseline";
|
|
9
177
|
const ATTR_DOC_URL = "doc-url";
|
|
10
178
|
const EMPTY_HEADS = encodeHeads([]);
|
|
11
|
-
const
|
|
179
|
+
const DraftStateProvider = (element) => {
|
|
12
180
|
const rawUrl = element.getAttribute(ATTR_DOC_URL);
|
|
13
181
|
if (!rawUrl || !isValidAutomergeUrl(rawUrl)) {
|
|
14
182
|
console.warn(
|
|
15
|
-
`[drafts] <patchwork-view component="patchwork-draft-
|
|
183
|
+
`[drafts] <patchwork-view component="patchwork-draft-state-provider"> is missing a valid ${ATTR_DOC_URL} attribute (got ${JSON.stringify(rawUrl)})`
|
|
16
184
|
);
|
|
17
185
|
return () => {
|
|
18
186
|
};
|
|
@@ -21,7 +189,7 @@ const DraftListProvider = (element) => {
|
|
|
21
189
|
const maybeRepo = "repo" in window ? window.repo : void 0;
|
|
22
190
|
if (!maybeRepo) {
|
|
23
191
|
console.warn(
|
|
24
|
-
"[drafts] window.repo is not set; draft-
|
|
192
|
+
"[drafts] window.repo is not set; draft-state provider disabled"
|
|
25
193
|
);
|
|
26
194
|
return () => {
|
|
27
195
|
};
|
|
@@ -29,6 +197,7 @@ const DraftListProvider = (element) => {
|
|
|
29
197
|
const repo = maybeRepo;
|
|
30
198
|
let hostDocHandle = null;
|
|
31
199
|
let checkedOutHandle = null;
|
|
200
|
+
let draftRouter = null;
|
|
32
201
|
const trackedDrafts = /* @__PURE__ */ new Map();
|
|
33
202
|
let mainDraftHandle = null;
|
|
34
203
|
const pendingCheckedOutSubscribers = /* @__PURE__ */ new Set();
|
|
@@ -36,9 +205,21 @@ const DraftListProvider = (element) => {
|
|
|
36
205
|
const listSubscribers = /* @__PURE__ */ new Set();
|
|
37
206
|
let orderedDraftUrls = [];
|
|
38
207
|
let draftList = {
|
|
39
|
-
main: {
|
|
40
|
-
|
|
208
|
+
main: {
|
|
209
|
+
url: docUrl,
|
|
210
|
+
parent: null,
|
|
211
|
+
members: [],
|
|
212
|
+
childCount: 0,
|
|
213
|
+
name: null,
|
|
214
|
+
changeGroupDocUrl: null
|
|
215
|
+
},
|
|
216
|
+
drafts: [],
|
|
217
|
+
actorAttributionUrl: null
|
|
41
218
|
};
|
|
219
|
+
const actorRecorder = createActorRecorder(element);
|
|
220
|
+
const changeGrouper = createChangeGrouper(repo, {
|
|
221
|
+
onLocalChange: actorRecorder.recordLocalChange
|
|
222
|
+
});
|
|
42
223
|
const mountCounts = /* @__PURE__ */ new Map();
|
|
43
224
|
const skipVerdicts = /* @__PURE__ */ new Map();
|
|
44
225
|
let disposed = false;
|
|
@@ -47,12 +228,9 @@ const DraftListProvider = (element) => {
|
|
|
47
228
|
const onTrackedChange = () => {
|
|
48
229
|
scheduleRewalk();
|
|
49
230
|
recomputeList();
|
|
50
|
-
notifyBaselines();
|
|
51
231
|
};
|
|
52
232
|
const onHostDocChange = () => scheduleRewalk();
|
|
53
|
-
const onCheckedOutChange = () =>
|
|
54
|
-
notifyBaselines();
|
|
55
|
-
};
|
|
233
|
+
const onCheckedOutChange = () => notifyBaselines();
|
|
56
234
|
const onMounted = (event) => {
|
|
57
235
|
const detail = event.detail;
|
|
58
236
|
if (!("url" in detail)) return;
|
|
@@ -75,8 +253,21 @@ const DraftListProvider = (element) => {
|
|
|
75
253
|
const handle = await repo.find(docUrl);
|
|
76
254
|
if (disposed) return;
|
|
77
255
|
hostDocHandle = handle;
|
|
256
|
+
const hostType = handle.doc()?.["@patchwork"]?.type;
|
|
257
|
+
if (hostType == null || !SKIPPED_DATATYPES.has(hostType)) {
|
|
258
|
+
try {
|
|
259
|
+
const mainDraft = await ensureMainDraft(repo, handle);
|
|
260
|
+
if (disposed) return;
|
|
261
|
+
const attribution = await ensureActorAttribution(repo, mainDraft);
|
|
262
|
+
if (disposed) return;
|
|
263
|
+
actorRecorder.setAttributionHandle(attribution);
|
|
264
|
+
} catch (err) {
|
|
265
|
+
console.warn("[drafts] failed to eagerly create main draft:", err);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
78
268
|
checkedOutHandle = repo.create({ checkedOut: null });
|
|
79
269
|
checkedOutHandle.on("change", onCheckedOutChange);
|
|
270
|
+
draftRouter = createDraftRouter(checkedOutHandle);
|
|
80
271
|
const checkedOutUrl = checkedOutHandle.url;
|
|
81
272
|
for (const respond of pendingCheckedOutSubscribers) {
|
|
82
273
|
respond(checkedOutUrl);
|
|
@@ -88,7 +279,7 @@ const DraftListProvider = (element) => {
|
|
|
88
279
|
recomputeList();
|
|
89
280
|
})();
|
|
90
281
|
ready.catch((err) => {
|
|
91
|
-
console.error(`[drafts] failed to initialize draft-
|
|
282
|
+
console.error(`[drafts] failed to initialize draft-state provider:`, err);
|
|
92
283
|
});
|
|
93
284
|
const onSubscribe = (event) => {
|
|
94
285
|
const { type } = event.detail.selector;
|
|
@@ -141,6 +332,10 @@ const DraftListProvider = (element) => {
|
|
|
141
332
|
element.addEventListener("patchwork:unmounted", onUnmounted);
|
|
142
333
|
return () => {
|
|
143
334
|
disposed = true;
|
|
335
|
+
changeGrouper.dispose();
|
|
336
|
+
actorRecorder.dispose();
|
|
337
|
+
draftRouter?.dispose();
|
|
338
|
+
draftRouter = null;
|
|
144
339
|
element.removeEventListener("patchwork:subscribe", onSubscribe);
|
|
145
340
|
element.removeEventListener("patchwork:mounted", onMounted);
|
|
146
341
|
element.removeEventListener("patchwork:unmounted", onUnmounted);
|
|
@@ -169,7 +364,6 @@ const DraftListProvider = (element) => {
|
|
|
169
364
|
return;
|
|
170
365
|
}
|
|
171
366
|
rewalkInFlight = true;
|
|
172
|
-
const liveCheckedOut = checkedOutHandle;
|
|
173
367
|
void (async () => {
|
|
174
368
|
try {
|
|
175
369
|
const mainDraft = await ensureMainDraftTracked();
|
|
@@ -185,12 +379,7 @@ const DraftListProvider = (element) => {
|
|
|
185
379
|
);
|
|
186
380
|
if (disposed) return;
|
|
187
381
|
orderedDraftUrls = allDrafts;
|
|
188
|
-
|
|
189
|
-
if (selected && !allDrafts.includes(selected)) {
|
|
190
|
-
liveCheckedOut.change((d) => {
|
|
191
|
-
d.checkedOut = null;
|
|
192
|
-
});
|
|
193
|
-
}
|
|
382
|
+
draftRouter?.updateAvailableDrafts(allDrafts);
|
|
194
383
|
} catch (err) {
|
|
195
384
|
console.error("[drafts] rewalk failed:", err);
|
|
196
385
|
} finally {
|
|
@@ -205,11 +394,36 @@ const DraftListProvider = (element) => {
|
|
|
205
394
|
}
|
|
206
395
|
function recomputeList() {
|
|
207
396
|
if (disposed) return;
|
|
397
|
+
updateChangeGrouping();
|
|
208
398
|
const next = computeList();
|
|
209
399
|
if (draftListsEqual(draftList, next)) return;
|
|
210
400
|
draftList = next;
|
|
211
401
|
for (const respond of listSubscribers) respond(next);
|
|
212
402
|
}
|
|
403
|
+
function updateChangeGrouping() {
|
|
404
|
+
if (disposed) return;
|
|
405
|
+
const specs = [];
|
|
406
|
+
if (mainDraftHandle) {
|
|
407
|
+
specs.push({
|
|
408
|
+
draftHandle: mainDraftHandle,
|
|
409
|
+
members: clonesToMembers(mainDraftHandle.doc()?.clones ?? {}),
|
|
410
|
+
rootDocUrl: docUrl
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
const selected = checkedOutHandle?.doc()?.checkedOut ?? null;
|
|
414
|
+
const ordered = selected && orderedDraftUrls.includes(selected) ? [selected, ...orderedDraftUrls.filter((u) => u !== selected)] : orderedDraftUrls;
|
|
415
|
+
for (const url of ordered) {
|
|
416
|
+
const handle = trackedDrafts.get(url);
|
|
417
|
+
const doc = handle?.doc();
|
|
418
|
+
if (!handle || !doc || doc.mergedAt !== void 0) continue;
|
|
419
|
+
specs.push({
|
|
420
|
+
draftHandle: handle,
|
|
421
|
+
members: clonesToMembers(doc.clones),
|
|
422
|
+
rootDocUrl: docUrl
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
changeGrouper.setTimelines(specs);
|
|
426
|
+
}
|
|
213
427
|
function computeList() {
|
|
214
428
|
const drafts = [];
|
|
215
429
|
for (const url of orderedDraftUrls) {
|
|
@@ -217,34 +431,52 @@ const DraftListProvider = (element) => {
|
|
|
217
431
|
if (!doc || doc.mergedAt !== void 0) continue;
|
|
218
432
|
drafts.push({
|
|
219
433
|
url,
|
|
434
|
+
parent: effectiveParent(doc.parent),
|
|
220
435
|
members: clonesToMembers(doc.clones),
|
|
221
436
|
childCount: doc.drafts.length,
|
|
222
|
-
name: doc.name ?? null
|
|
437
|
+
name: doc.name ?? null,
|
|
438
|
+
changeGroupDocUrl: doc.changeGroupDocUrl ?? null
|
|
223
439
|
});
|
|
224
440
|
}
|
|
225
|
-
return {
|
|
441
|
+
return {
|
|
442
|
+
main: computeMainSummary(),
|
|
443
|
+
drafts,
|
|
444
|
+
actorAttributionUrl: mainDraftHandle?.doc()?.actorAttributionUrl ?? null
|
|
445
|
+
};
|
|
446
|
+
}
|
|
447
|
+
function effectiveParent(parentUrl) {
|
|
448
|
+
const seen = /* @__PURE__ */ new Set();
|
|
449
|
+
let cursor = parentUrl ?? null;
|
|
450
|
+
while (cursor && !seen.has(cursor)) {
|
|
451
|
+
seen.add(cursor);
|
|
452
|
+
const doc = trackedDrafts.get(cursor)?.doc();
|
|
453
|
+
if (!doc || doc.mergedAt === void 0) return cursor;
|
|
454
|
+
cursor = doc.parent ?? null;
|
|
455
|
+
}
|
|
456
|
+
return cursor;
|
|
226
457
|
}
|
|
227
458
|
function computeMainSummary() {
|
|
228
459
|
const url = mainDraftHandle?.url ?? docUrl;
|
|
229
460
|
const childCount = mainDraftHandle?.doc()?.drafts.length ?? 0;
|
|
230
461
|
const name = mainDraftHandle?.doc()?.name ?? null;
|
|
462
|
+
const changeGroupDocUrl = mainDraftHandle?.doc()?.changeGroupDocUrl ?? null;
|
|
231
463
|
const mainClones = mainDraftHandle?.doc()?.clones;
|
|
232
464
|
if (mainClones && Object.keys(mainClones).length > 0) {
|
|
233
|
-
return {
|
|
465
|
+
return {
|
|
466
|
+
url,
|
|
467
|
+
parent: null,
|
|
468
|
+
members: clonesToMembers(mainClones),
|
|
469
|
+
childCount,
|
|
470
|
+
name,
|
|
471
|
+
changeGroupDocUrl
|
|
472
|
+
};
|
|
234
473
|
}
|
|
235
474
|
const members = [...mountCounts.keys()].filter((u) => skipVerdicts.get(u) !== true).map((u) => ({ url: u, cloneUrl: null, clonedAt: null })).sort(byMemberUrl);
|
|
236
|
-
return { url, members, childCount, name };
|
|
475
|
+
return { url, parent: null, members, childCount, name, changeGroupDocUrl };
|
|
237
476
|
}
|
|
238
477
|
function currentBaseline(target) {
|
|
239
|
-
const
|
|
240
|
-
|
|
241
|
-
if (entry) return { heads: entry.from ?? null };
|
|
242
|
-
const checkedOut = doc?.checkedOut;
|
|
243
|
-
if (checkedOut) {
|
|
244
|
-
const clonedAt = trackedDrafts.get(checkedOut)?.doc()?.clones?.[target]?.clonedAt;
|
|
245
|
-
return { heads: clonedAt ?? null };
|
|
246
|
-
}
|
|
247
|
-
return { heads: null };
|
|
478
|
+
const entry = checkedOutHandle?.doc()?.at?.[target];
|
|
479
|
+
return { heads: entry?.from ?? null };
|
|
248
480
|
}
|
|
249
481
|
function notifyBaselines() {
|
|
250
482
|
for (const [target, set] of baselineSubscribers) {
|
|
@@ -314,10 +546,8 @@ async function collectAllDrafts(repo, roots, tracked, onNewChange) {
|
|
|
314
546
|
tracked.set(url, h);
|
|
315
547
|
h.on("change", onNewChange);
|
|
316
548
|
}
|
|
317
|
-
const drafts = h.doc()?.drafts ?? [];
|
|
318
|
-
|
|
319
|
-
if (isValidAutomergeUrl(child)) queue.push(child);
|
|
320
|
-
}
|
|
549
|
+
const drafts = (h.doc()?.drafts ?? []).filter(isValidAutomergeUrl);
|
|
550
|
+
queue.unshift(...drafts);
|
|
321
551
|
}
|
|
322
552
|
return order;
|
|
323
553
|
}
|
|
@@ -329,6 +559,7 @@ function clonesToMembers(clones) {
|
|
|
329
559
|
})).sort(byMemberUrl);
|
|
330
560
|
}
|
|
331
561
|
function draftListsEqual(a, b) {
|
|
562
|
+
if (a.actorAttributionUrl !== b.actorAttributionUrl) return false;
|
|
332
563
|
if (!summariesEqual(a.main, b.main)) return false;
|
|
333
564
|
if (a.drafts.length !== b.drafts.length) return false;
|
|
334
565
|
for (let i = 0; i < a.drafts.length; i++) {
|
|
@@ -337,7 +568,7 @@ function draftListsEqual(a, b) {
|
|
|
337
568
|
return true;
|
|
338
569
|
}
|
|
339
570
|
function summariesEqual(a, b) {
|
|
340
|
-
return a.url === b.url && a.childCount === b.childCount && a.name === b.name && memberListsEqual(a.members, b.members);
|
|
571
|
+
return a.url === b.url && a.parent === b.parent && a.childCount === b.childCount && a.name === b.name && a.changeGroupDocUrl === b.changeGroupDocUrl && memberListsEqual(a.members, b.members);
|
|
341
572
|
}
|
|
342
573
|
function byMemberUrl(a, b) {
|
|
343
574
|
return a.url < b.url ? -1 : a.url > b.url ? 1 : 0;
|
|
@@ -358,6 +589,6 @@ function sameHeads(a, b) {
|
|
|
358
589
|
return a.every((h) => set.has(h));
|
|
359
590
|
}
|
|
360
591
|
export {
|
|
361
|
-
|
|
592
|
+
DraftStateProvider
|
|
362
593
|
};
|
|
363
|
-
//# sourceMappingURL=
|
|
594
|
+
//# sourceMappingURL=DraftStateProvider-DX8b_RgQ.js.map
|