@inkandswitch/patchwork-pkg-base 0.0.131 → 0.0.132

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.
Files changed (57) hide show
  1. package/module-settings-url +1 -1
  2. package/package.json +1 -1
  3. package/packages/account/dist/index.css +19 -0
  4. package/packages/account/dist/index.css.map +2 -2
  5. package/packages/add-doc-to-sidebar-button/button.js +6 -0
  6. package/packages/add-doc-to-sidebar-button/styles.css +8 -1
  7. package/packages/chat/dist/index.js +1 -1
  8. package/packages/codemirror-base/dist/tool.js +75 -59
  9. package/packages/codemirror-base/dist/tool.js.map +2 -2
  10. package/packages/codemirror-markdown/dist/extensions/markdown.js +25 -0
  11. package/packages/codemirror-markdown/dist/extensions/markdown.js.map +2 -2
  12. package/packages/comments-view/dist/assets/{main-DXYeEDFB.js → main-EQKeEvzx.js} +50 -3
  13. package/packages/comments-view/dist/assets/main-EQKeEvzx.js.map +1 -0
  14. package/packages/comments-view/dist/index.js +3 -3
  15. package/packages/doc-openwith/doc-openwith.js +15 -1
  16. package/packages/doc-presence/doc-presence.js +66 -23
  17. package/packages/drafts/dist/assets/{main-CG9HOCai.js → main-wz7oton3.js} +134 -80
  18. package/packages/drafts/dist/assets/main-wz7oton3.js.map +1 -0
  19. package/packages/drafts/dist/index.js +3 -3
  20. package/packages/file/dist/index.js +1 -1
  21. package/packages/file/dist/{tool-Bucjz6Ay.js → tool-CSiyzu4S.js} +51 -41
  22. package/packages/file/dist/tool-CSiyzu4S.js.map +1 -0
  23. package/packages/file/dist/{typescript-language-server-BId-R8tz.js → typescript-language-server-T-bVgxs5.js} +2 -2
  24. package/packages/file/dist/{typescript-language-server-BId-R8tz.js.map → typescript-language-server-T-bVgxs5.js.map} +1 -1
  25. package/packages/packages/dist/index.js +6 -1
  26. package/packages/patternwitch/tool.js +30 -3
  27. package/packages/providers/dist/assets/FocusProvider-CsJzhQWV.js +2 -0
  28. package/packages/providers/dist/assets/FocusProvider-CsJzhQWV.js.map +1 -0
  29. package/packages/providers/dist/index.js +1 -1
  30. package/packages/theming/dist/{active-theme-WA6XMDTP.js → active-theme-HEMDARRS.js} +3 -3
  31. package/packages/theming/dist/{chunk-4GOF67HA.js → chunk-2LCY4PP2.js} +2 -2
  32. package/packages/theming/dist/{chunk-L7WQSSFT.js → chunk-MJMDS3V5.js} +2 -2
  33. package/packages/theming/dist/{lychee-BMLMUNP6.css → lychee-BADZHTMJ.css} +1 -1
  34. package/packages/theming/dist/main.js +4 -4
  35. package/packages/theming/dist/{theme-titlebar-LG6PFBKR.js → theme-titlebar-JLYY4TZR.js} +3 -3
  36. package/packages/theming/dist/{theme-tray-R3PJDYCL.js → theme-tray-OZPNTCHA.js} +3 -3
  37. package/packages/theming/dist/tool.js +2 -2
  38. package/packages/threepane/dist/IsolatedDocumentArea-PIXYOA25.js +2 -0
  39. package/packages/threepane/dist/PatchworkFrame-O76GWRUL.js +2 -0
  40. package/packages/threepane/dist/{PatchworkFrame-HMA5KZLY.js.map → PatchworkFrame-O76GWRUL.js.map} +3 -3
  41. package/packages/threepane/dist/{chunk-UOTRB26X.js → chunk-Y2SEQB3F.js} +24 -8
  42. package/packages/threepane/dist/{chunk-UOTRB26X.js.map → chunk-Y2SEQB3F.js.map} +3 -3
  43. package/packages/threepane/dist/index.js +1 -1
  44. package/plugin-registry-url +1 -1
  45. package/packages/comments-view/dist/assets/main-DXYeEDFB.js.map +0 -1
  46. package/packages/drafts/dist/assets/main-CG9HOCai.js.map +0 -1
  47. package/packages/file/dist/tool-Bucjz6Ay.js.map +0 -1
  48. package/packages/providers/dist/assets/FocusProvider-AfpGH4B_.js +0 -2
  49. package/packages/providers/dist/assets/FocusProvider-AfpGH4B_.js.map +0 -1
  50. package/packages/threepane/dist/IsolatedDocumentArea-JJMLEL4O.js +0 -2
  51. package/packages/threepane/dist/PatchworkFrame-HMA5KZLY.js +0 -2
  52. /package/packages/theming/dist/{active-theme-WA6XMDTP.js.map → active-theme-HEMDARRS.js.map} +0 -0
  53. /package/packages/theming/dist/{chunk-4GOF67HA.js.map → chunk-2LCY4PP2.js.map} +0 -0
  54. /package/packages/theming/dist/{chunk-L7WQSSFT.js.map → chunk-MJMDS3V5.js.map} +0 -0
  55. /package/packages/theming/dist/{theme-titlebar-LG6PFBKR.js.map → theme-titlebar-JLYY4TZR.js.map} +0 -0
  56. /package/packages/theming/dist/{theme-tray-R3PJDYCL.js.map → theme-tray-OZPNTCHA.js.map} +0 -0
  57. /package/packages/threepane/dist/{IsolatedDocumentArea-JJMLEL4O.js.map → IsolatedDocumentArea-PIXYOA25.js.map} +0 -0
@@ -54,7 +54,7 @@ var closePopover = StateEffect.define();
54
54
  var dismissButton = StateEffect.define();
55
55
  var setPointerDown = StateEffect.define();
56
56
  var POPOVER_CLASS = "cm-comment-popover";
57
- var buildCommentButton = (from, to, head, view, options) => {
57
+ var buildCommentButton = (view, options) => {
58
58
  const button = document.createElement("button");
59
59
  button.className = "cm-comment-button";
60
60
  button.setAttribute("aria-label", "Comment");
@@ -73,9 +73,15 @@ var buildCommentButton = (from, to, head, view, options) => {
73
73
  button.addEventListener("click", (e) => {
74
74
  e.preventDefault();
75
75
  e.stopPropagation();
76
- const threadUrl = options.createThreadForRange(from, to);
76
+ const sel = view.state.selection.main;
77
+ if (sel.empty) return;
78
+ const threadUrl = options.createThreadForRange(sel.from, sel.to);
77
79
  if (!threadUrl) return;
78
- view.dispatch({ effects: openPopover.of({ threadUrl, anchor: head }) });
80
+ if (options.threadsShownElsewhere?.()) {
81
+ view.dispatch({ effects: dismissButton.of(null) });
82
+ return;
83
+ }
84
+ view.dispatch({ effects: openPopover.of({ threadUrl, anchor: sel.head }) });
79
85
  });
80
86
  return button;
81
87
  };
@@ -111,7 +117,7 @@ var buildThreadPopover = (view, state, options, ui) => {
111
117
  }
112
118
  };
113
119
  };
114
- var buttonTooltip = (state, options) => {
120
+ var buttonTooltip = (state, options, create) => {
115
121
  const sel = state.selection.main;
116
122
  if (sel.empty || state.readOnly) return null;
117
123
  return {
@@ -119,9 +125,7 @@ var buttonTooltip = (state, options) => {
119
125
  above: true,
120
126
  strictSide: false,
121
127
  arrow: false,
122
- create: (view) => ({
123
- dom: buildCommentButton(sel.from, sel.to, sel.head, view, options)
124
- })
128
+ create
125
129
  };
126
130
  };
127
131
  var popoverTooltip = (state, options, ui) => ({
@@ -132,65 +136,74 @@ var popoverTooltip = (state, options, ui) => ({
132
136
  arrow: false,
133
137
  create: (view) => buildThreadPopover(view, state, options, ui)
134
138
  });
135
- var commentUIField = (options, ui) => StateField.define({
136
- create: (state) => ({
137
- popover: null,
138
- buttonDismissed: false,
139
- pointerDown: false,
140
- tooltip: buttonTooltip(state, options)
141
- }),
142
- update(value, tr) {
143
- let popover = value.popover;
144
- let buttonDismissed = value.buttonDismissed;
145
- let pointerDown = value.pointerDown;
146
- let popoverChanged = false;
147
- const readOnlyChanged = tr.startState.readOnly !== tr.state.readOnly;
148
- if (readOnlyChanged && tr.state.readOnly && popover) {
149
- popover = null;
150
- popoverChanged = true;
151
- }
152
- if (popover && tr.docChanged) {
153
- popover = { ...popover, anchor: tr.changes.mapPos(popover.anchor) };
154
- popoverChanged = true;
155
- }
156
- for (const effect of tr.effects) {
157
- if (effect.is(openPopover)) {
158
- popover = effect.value;
159
- popoverChanged = true;
160
- } else if (effect.is(closePopover)) {
139
+ var commentUIField = (options, ui) => {
140
+ const createButton = (view) => ({
141
+ dom: buildCommentButton(view, options)
142
+ });
143
+ return StateField.define({
144
+ create: (state) => ({
145
+ popover: null,
146
+ buttonDismissed: false,
147
+ pointerDown: false,
148
+ tooltip: buttonTooltip(state, options, createButton)
149
+ }),
150
+ update(value, tr) {
151
+ let popover = value.popover;
152
+ let buttonDismissed = value.buttonDismissed;
153
+ let pointerDown = value.pointerDown;
154
+ let popoverChanged = false;
155
+ const readOnlyChanged = tr.startState.readOnly !== tr.state.readOnly;
156
+ if (readOnlyChanged && tr.state.readOnly && popover) {
161
157
  popover = null;
162
158
  popoverChanged = true;
163
- buttonDismissed = true;
164
- } else if (effect.is(dismissButton)) {
165
- buttonDismissed = true;
166
- } else if (effect.is(setPointerDown)) {
167
- pointerDown = effect.value;
168
159
  }
169
- }
170
- if (tr.selection) buttonDismissed = false;
171
- if (popover) {
172
- if (!popoverChanged) {
173
- return buttonDismissed === value.buttonDismissed && pointerDown === value.pointerDown ? value : { ...value, buttonDismissed, pointerDown };
160
+ if (popover && tr.docChanged) {
161
+ popover = { ...popover, anchor: tr.changes.mapPos(popover.anchor) };
162
+ popoverChanged = true;
163
+ }
164
+ for (const effect of tr.effects) {
165
+ if (effect.is(openPopover)) {
166
+ popover = effect.value;
167
+ popoverChanged = true;
168
+ } else if (effect.is(closePopover)) {
169
+ popover = null;
170
+ popoverChanged = true;
171
+ buttonDismissed = true;
172
+ } else if (effect.is(dismissButton)) {
173
+ buttonDismissed = true;
174
+ } else if (effect.is(setPointerDown)) {
175
+ pointerDown = effect.value;
176
+ }
177
+ }
178
+ if (tr.selection) buttonDismissed = false;
179
+ if (popover) {
180
+ if (!popoverChanged) {
181
+ return buttonDismissed === value.buttonDismissed && pointerDown === value.pointerDown ? value : { ...value, buttonDismissed, pointerDown };
182
+ }
183
+ return {
184
+ popover,
185
+ buttonDismissed,
186
+ pointerDown,
187
+ // Following its text as the document shifts is a move, not a new
188
+ // popover: keep the tooltip's `create`, so CodeMirror repositions the
189
+ // thread view it has rather than closing it (which discards an
190
+ // unwritten draft, see `onClose`) and mounting another.
191
+ tooltip: value.tooltip && value.popover?.threadUrl === popover.threadUrl ? { ...value.tooltip, pos: popover.anchor } : popoverTooltip(popover, options, ui)
192
+ };
193
+ }
194
+ if (!popoverChanged && !tr.docChanged && !tr.selection && !readOnlyChanged && buttonDismissed === value.buttonDismissed && pointerDown === value.pointerDown) {
195
+ return value;
174
196
  }
175
197
  return {
176
- popover,
198
+ popover: null,
177
199
  buttonDismissed,
178
200
  pointerDown,
179
- tooltip: popoverTooltip(popover, options, ui)
201
+ tooltip: buttonDismissed || pointerDown ? null : buttonTooltip(tr.state, options, createButton)
180
202
  };
181
- }
182
- if (!popoverChanged && !tr.docChanged && !tr.selection && !readOnlyChanged && buttonDismissed === value.buttonDismissed && pointerDown === value.pointerDown) {
183
- return value;
184
- }
185
- return {
186
- popover: null,
187
- buttonDismissed,
188
- pointerDown,
189
- tooltip: buttonDismissed || pointerDown ? null : buttonTooltip(tr.state, options)
190
- };
191
- },
192
- provide: (field) => showTooltip.from(field, (value) => value.tooltip)
193
- });
203
+ },
204
+ provide: (field) => showTooltip.from(field, (value) => value.tooltip)
205
+ });
206
+ };
194
207
  var trackPointer = (view) => {
195
208
  view.dispatch({ effects: setPointerDown.of(true) });
196
209
  const up = () => {
@@ -219,6 +232,7 @@ var commentInteractions = (options, ui, field) => EditorView.domEventHandlers({
219
232
  ui.suppressed = null;
220
233
  return false;
221
234
  }
235
+ if (options.threadsShownElsewhere?.()) return false;
222
236
  if (hit.threadUrl === ui.suppressed) return false;
223
237
  ui.suppressed = null;
224
238
  view.dispatch({
@@ -540,7 +554,9 @@ function CodeMirrorEditor(props) {
540
554
  createThreadForRange,
541
555
  threadAtPos,
542
556
  watchThreadForClose,
543
- onClose: onClosePopover
557
+ onClose: onClosePopover,
558
+ // read when clicked, so no need to rebuild the extension on change
559
+ threadsShownElsewhere: () => Object.keys(focusDoc()?.commentViews ?? {}).length > 0
544
560
  }), ...datatypeExtensions() ?? []];
545
561
  return (() => {
546
562
  var _el$ = _tmpl$(), _el$2 = _el$.firstChild, _el$3 = _el$2.firstChild;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/tool.tsx", "../src/lib/extensions/commentUI.ts", "../src/lib/extensions/comments.ts", "../node_modules/.pnpm/@inkandswitch+patchwork-providers-solid@0.2.8_@automerge+automerge-repo@2.6.0-subductio_64b21ee2f70813c59f45ca0bcbd2a52c/node_modules/@inkandswitch/patchwork-providers-solid/dist/index.js"],
4
- "sourcesContent": ["import { template as _$template } from \"solid-js/web\";\nimport { insert as _$insert } from \"solid-js/web\";\nimport { createComponent as _$createComponent } from \"solid-js/web\";\nvar _tmpl$ = /*#__PURE__*/_$template(`<div style=\"width:100%;height:100%;overflow:hidden;position:relative;background:var(--studio-fill, white)\"><div style=display:flex;height:100%><div style=position:relative;flex:1;height:100%>`);\nimport { CodeMirror } from \"./lib/codemirror.tsx\";\nimport { render } from \"solid-js/web\";\nimport { RangeSet } from \"@codemirror/state\";\nimport { Decoration } from \"@codemirror/view\";\nimport { commentUI } from \"./lib/extensions/commentUI.ts\";\nimport { cursor } from \"@automerge/automerge-repo/slim\";\nimport { getRegistry } from \"@inkandswitch/patchwork-plugins\";\nimport { subscribeDoc, subscribe } from \"@inkandswitch/patchwork-providers-solid\";\nimport { createMemo, createResource, Show } from \"solid-js\";\nimport { createCommentForRange } from \"./lib/extensions/comments.ts\";\nconst PATH = [\"content\"];\nexport function CodeMirrorEditor(props) {\n const baseline = subscribe(props.element, {\n type: \"draft:baseline\",\n url: props.handle.url\n }, {\n heads: null\n });\n const commentEntries = subscribe(props.element, {\n type: \"patchwork:comments\"\n }, []);\n const [focusDoc, focusHandle] = subscribeDoc(props.element, {\n type: \"patchwork:focus\"\n });\n const contactUrl = subscribe(props.element, {\n type: \"patchwork:contact\"\n });\n const [commentTargets] = createResource(commentEntries, entries => getDedupedCommentTargets(entries, props.handle.url, props.repo), {\n initialValue: []\n });\n const focusRefUrls = createMemo(() => {\n const doc = focusDoc();\n return [...Object.keys(doc?.selection ?? {}), ...Object.keys(doc?.highlight ?? {})];\n });\n const [emphasisTargets] = createResource(focusRefUrls, async urls => resolveSubDocUrlsOfDoc(urls, props.handle.url, props.repo), {\n initialValue: []\n });\n const scrollTarget = createMemo(() => {\n let from = Infinity;\n let to = -Infinity;\n for (const ref of emphasisTargets()) {\n const positions = ref.rangePositions();\n if (!positions) continue;\n const [start, end] = positions;\n from = Math.min(from, start);\n to = Math.max(to, end);\n }\n return from <= to ? [from, to] : null;\n });\n let lastEmittedUrl;\n const onChangeSelection = (from, to) => {\n const handle = focusHandle();\n if (!handle) return;\n const nextUrl = props.handle.sub(...PATH, cursor(from, to)).url;\n if (nextUrl === lastEmittedUrl) return;\n handle.change(doc => {\n doc.selection = {\n [nextUrl]: true\n };\n });\n lastEmittedUrl = nextUrl;\n };\n const decorations = () => {\n const targetRefs = commentTargets();\n const emphasisRefs = emphasisTargets();\n return RangeSet.of(buildCommentDecorations(targetRefs, emphasisRefs), true\n // sort ranges\n );\n };\n const createThreadForRange = (from, to) => {\n const url = contactUrl();\n if (!url) {\n console.warn(\"Cannot create comment: no contactUrl available\");\n return null;\n }\n return createCommentForRange(props.handle, PATH, from, to, url);\n };\n const threadAtPos = pos => {\n const threadByTarget = /* @__PURE__ */new Map();\n for (const entry of commentEntries()) {\n threadByTarget.set(entry.targetUrl, entry.threadUrl);\n }\n for (const handle of commentTargets()) {\n const positions = handle.rangePositions();\n if (!positions) continue;\n const [start, end] = positions;\n if (start === end || pos < start || pos > end) continue;\n const threadUrl = threadByTarget.get(handle.url);\n if (threadUrl) return {\n threadUrl,\n from: start\n };\n }\n return null;\n };\n const findThreadByUrl = threadUrl => {\n const threads = props.handle.doc()?.[\"@comments\"]?.threads ?? [];\n return threads.find(thread => props.handle.sub(\"@comments\", \"threads\", {\n id: thread.id\n }).url === threadUrl);\n };\n const watchThreadForClose = (threadUrl, close) => {\n const contact = contactUrl();\n const hasOwnDraft = thread => thread.comments.some(c => c.contactUrl === contact && (c.draftContent !== void 0 || c.content === void 0));\n const seed = findThreadByUrl(threadUrl);\n let sawDraft = seed ? hasOwnDraft(seed) : false;\n const onChange = () => {\n const thread = findThreadByUrl(threadUrl);\n if (!thread) return close();\n if (hasOwnDraft(thread)) sawDraft = true;else if (sawDraft) close();\n };\n props.handle.on(\"change\", onChange);\n return () => props.handle.off(\"change\", onChange);\n };\n const onClosePopover = threadUrl => {\n const contact = contactUrl();\n const isEmptyOwnDraft = c => c.contactUrl === contact && c.content === void 0 && (c.draftContent === void 0 || c.draftContent.trim() === \"\");\n const thread = findThreadByUrl(threadUrl);\n if (!thread || !thread.comments.some(isEmptyOwnDraft)) return;\n const threadId = thread.id;\n queueMicrotask(() => {\n props.handle.change(doc => {\n const list = doc[\"@comments\"]?.threads;\n if (!list) return;\n const index = list.findIndex(t => t.id === threadId);\n if (index === -1) return;\n const target = list[index];\n for (let i = target.comments.length - 1; i >= 0; i--) {\n if (isEmptyOwnDraft(target.comments[i])) target.comments.splice(i, 1);\n }\n if (target.comments.length === 0) list.splice(index, 1);\n });\n });\n };\n const [datatypeExtensions] = createResource(() => loadCodeMirrorExtensionsForDoc(props.handle));\n const extensions = () => [commentUI({\n createThreadForRange,\n threadAtPos,\n watchThreadForClose,\n onClose: onClosePopover\n }), ...(datatypeExtensions() ?? [])];\n return (() => {\n var _el$ = _tmpl$(),\n _el$2 = _el$.firstChild,\n _el$3 = _el$2.firstChild;\n _$insert(_el$3, _$createComponent(Show, {\n get when() {\n return datatypeExtensions.state === \"ready\";\n },\n get children() {\n return _$createComponent(CodeMirror, {\n get handle() {\n return props.handle;\n },\n get repo() {\n return props.repo;\n },\n path: PATH,\n decorations: decorations,\n baseline: () => baseline()?.heads ?? null,\n get extensions() {\n return extensions();\n },\n onChangeSelection: onChangeSelection,\n scrollTarget: scrollTarget,\n contactUrl: () => contactUrl() ?? null\n });\n }\n }));\n return _el$;\n })();\n}\nexport const mount = (handle, element) => render(() => _$createComponent(CodeMirrorEditor, {\n handle: handle,\n get repo() {\n return element.repo;\n },\n element: element\n}), element);\nasync function getDedupedCommentTargets(comments, docUrl, repo) {\n if (!comments) return [];\n const overlappingRefs = /* @__PURE__ */new Set();\n for (const comment of comments) {\n if (comment.targetUrl.startsWith(docUrl)) {\n const target = await repo.find(comment.targetUrl);\n overlappingRefs.add(target);\n }\n }\n return Array.from(overlappingRefs);\n}\nasync function resolveSubDocUrlsOfDoc(urls, docUrl, repo) {\n const refs = /* @__PURE__ */new Set();\n for (const url of urls) {\n if (url.startsWith(docUrl)) {\n refs.add(await repo.find(url));\n }\n }\n return Array.from(refs);\n}\nfunction buildCommentDecorations(targetRefs, emphasisRefs) {\n const out = [];\n for (const ref of targetRefs) {\n const positions = ref.rangePositions();\n if (!positions) continue;\n const [start, end] = positions;\n if (start === end) continue;\n const isEmphasised = emphasisRefs.some(s => s.overlaps(ref));\n out.push(Decoration.mark({\n attributes: {\n style: commentTargetStyle(isEmphasised)\n }\n }).range(start, end));\n }\n return out;\n}\nfunction commentTargetStyle(isEmphasised) {\n const paper = isEmphasised ? \"56%\" : \"82%\";\n return `\n border-bottom: 2px solid var(--text-editor-secondary-text);\n background-color: color-mix(in oklch, var(--studio-secondary), var(--text-editor-fill) ${paper});\n `;\n}\nasync function loadCodeMirrorExtensionsForDoc(handle) {\n const docType = handle.doc()?.[\"@patchwork\"]?.type;\n const registry = getRegistry(\"codemirror:extension\");\n const loaded = await registry.loadAll(registry.filter(ext => {\n return ext.supportedDatatypes === \"*\" || Array.isArray(ext.supportedDatatypes) && ext.supportedDatatypes.includes(docType);\n }));\n return loaded.flatMap(ext => {\n const impl = ext.module;\n return Array.isArray(impl) ? impl : [impl];\n });\n}", "import {\n Extension,\n StateEffect,\n StateField,\n type EditorState,\n} from \"@codemirror/state\";\nimport {\n EditorView,\n showTooltip,\n type Tooltip,\n type TooltipView,\n} from \"@codemirror/view\";\nimport type { AutomergeUrl } from \"@automerge/automerge-repo/slim\";\nimport { COMMENT_THREAD_TYPE } from \"./comments.ts\";\n\n// The data hooks the editor UI needs from its host (the CodeMirror tool). The\n// extension owns all the DOM/positioning/lifecycle; the host owns everything\n// that needs the repo, the current contact, or the comment index.\nexport type CommentUIOptions = {\n // Create a thread targeting [from, to] and return its url, or null if it\n // couldn't be created (e.g. no current contact). Called when the floating\n // \"Comment\" button is clicked.\n createThreadForRange: (from: number, to: number) => AutomergeUrl | null;\n // Map a document position to the thread whose commented range covers it (and\n // that range's start), or null. Called when the user clicks commented text so\n // we can pop that thread open under the range's start.\n threadAtPos: (\n pos: number\n ) => { threadUrl: AutomergeUrl; from: number } | null;\n // Watch a thread and invoke `close` once its pending draft is submitted (or\n // cancelled, or the thread is removed). Returns an unsubscribe. Lets the\n // popover dismiss itself the moment the comment is saved.\n watchThreadForClose: (threadUrl: AutomergeUrl, close: () => void) => () => void;\n // Called whenever a popover closes, for any reason. Lets the host discard an\n // abandoned, never-filled-in draft so dismissing the popover doesn't litter\n // the document with empty comments.\n onClose: (threadUrl: AutomergeUrl) => void;\n};\n\n// Which thread the popover is showing, and the document position it hangs off.\ntype PopoverState = { threadUrl: AutomergeUrl; anchor: number };\n\nconst openPopover = StateEffect.define<PopoverState>();\nconst closePopover = StateEffect.define<null>();\n// Hide the floating \"Comment\" button until the next selection change (Escape).\nconst dismissButton = StateEffect.define<null>();\n// The main mouse button went down (true) or up (false) in the editor. The\n// button waits for the mouse to come up: it doesn't chase a selection that's\n// still being dragged out.\nconst setPointerDown = StateEffect.define<boolean>();\n\n// Mutable UI state shared across the extension's pieces (not editor state\n// because it isn't versioned/undoable): the thread we most recently dismissed,\n// so a click that stays inside it doesn't immediately pop it back open.\ntype CommentUIState = { suppressed: AutomergeUrl | null };\n\nconst POPOVER_CLASS = \"cm-comment-popover\";\n\n// A speech-bubble icon + \"Comment\" label, shown floating above the selection.\nconst buildCommentButton = (\n from: number,\n to: number,\n head: number,\n view: EditorView,\n options: CommentUIOptions\n): HTMLElement => {\n const button = document.createElement(\"button\");\n button.className = \"cm-comment-button\";\n button.setAttribute(\"aria-label\", \"Comment\");\n\n button.innerHTML = `\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\"\n fill=\"currentColor\" stroke=\"none\"/>\n </svg>\n <span>Comment</span>\n `;\n\n // Without this, the browser focuses the button on mousedown, the editor\n // blurs, the selection collapses, our tooltip source returns null, and\n // CodeMirror tears down the button before `click` fires \u2014 so the first\n // press silently does nothing and the user has to click twice. Swallowing\n // mousedown keeps the editor focused and the selection (and button) alive\n // long enough for the click to land.\n button.addEventListener(\"mousedown\", (e) => {\n e.preventDefault();\n e.stopPropagation();\n });\n\n button.addEventListener(\"click\", (e) => {\n e.preventDefault();\n e.stopPropagation();\n const threadUrl = options.createThreadForRange(from, to);\n if (!threadUrl) return;\n // Open the popover right where the button was (the selection head).\n view.dispatch({ effects: openPopover.of({ threadUrl, anchor: head }) });\n });\n\n return button;\n};\n\n// The popover body: the shared comment-thread tool, rendered inline via the\n// `patchwork-view` custom element. It's mounted inside `view.dom`, so the\n// element's provider events (repo, contact, \u2026) still bubble up to the host.\nconst buildThreadPopover = (\n view: EditorView,\n state: PopoverState,\n options: CommentUIOptions,\n ui: CommentUIState\n): TooltipView => {\n const dom = document.createElement(\"div\");\n dom.className = POPOVER_CLASS;\n\n const threadView = document.createElement(\"patchwork-view\");\n threadView.setAttribute(\"doc-url\", state.threadUrl);\n threadView.setAttribute(\"tool-id\", COMMENT_THREAD_TYPE);\n dom.append(threadView);\n\n const close = () => view.dispatch({ effects: closePopover.of(null) });\n\n // Dismiss on a click anywhere outside the popover, or on Escape. Both are\n // armed on a deferred tick so the very interaction that opened the popover\n // (the button click / the click on commented text) doesn't also close it.\n const onOutsideMouseDown = (event: MouseEvent) => {\n if (!dom.contains(event.target as Node)) close();\n };\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") close();\n };\n const doc = view.win.document;\n const armTimer = view.win.setTimeout(() => {\n doc.addEventListener(\"mousedown\", onOutsideMouseDown, true);\n doc.addEventListener(\"keydown\", onKeyDown, true);\n }, 0);\n\n // Close as soon as the draft is submitted (or the thread goes away).\n const stopWatch = options.watchThreadForClose(state.threadUrl, close);\n\n return {\n dom,\n destroy() {\n view.win.clearTimeout(armTimer);\n doc.removeEventListener(\"mousedown\", onOutsideMouseDown, true);\n doc.removeEventListener(\"keydown\", onKeyDown, true);\n stopWatch();\n // Remember this thread as dismissed so clicking within its range again\n // doesn't immediately reopen it (cleared once a click lands elsewhere).\n ui.suppressed = state.threadUrl;\n options.onClose(state.threadUrl);\n },\n };\n};\n\nconst buttonTooltip = (\n state: EditorState,\n options: CommentUIOptions\n): Tooltip | null => {\n const sel = state.selection.main;\n // A read-only editor can't take the comment write, so don't offer the button.\n if (sel.empty || state.readOnly) return null;\n\n // Anchor the button at the drag tail (the selection head) rather than the\n // start, so dragging left-to-right lands it on the right and right-to-left\n // on the left.\n return {\n pos: sel.head,\n above: true,\n strictSide: false,\n arrow: false,\n create: (view) => ({\n dom: buildCommentButton(sel.from, sel.to, sel.head, view, options),\n }),\n };\n};\n\nconst popoverTooltip = (\n state: PopoverState,\n options: CommentUIOptions,\n ui: CommentUIState\n): Tooltip => ({\n pos: state.anchor,\n above: false, // comes out underneath the anchor\n strictSide: false,\n arrow: false,\n create: (view) => buildThreadPopover(view, state, options, ui),\n});\n\ntype FieldValue = {\n popover: PopoverState | null;\n // The floating button is hidden while this is true \u2014 after Escape, or after a\n // comment is added \u2014 until a fresh selection re-arms it.\n buttonDismissed: boolean;\n // A selection is being dragged out with the mouse.\n pointerDown: boolean;\n tooltip: Tooltip | null;\n};\n\n// A single field driving the one tooltip slot: the thread popover when one is\n// open, otherwise the selection's \"Comment\" button.\nconst commentUIField = (options: CommentUIOptions, ui: CommentUIState) =>\n StateField.define<FieldValue>({\n create: (state) => ({\n popover: null,\n buttonDismissed: false,\n pointerDown: false,\n tooltip: buttonTooltip(state, options),\n }),\n update(value, tr) {\n let popover = value.popover;\n let buttonDismissed = value.buttonDismissed;\n let pointerDown = value.pointerDown;\n let popoverChanged = false;\n\n // Read-only can flip mid-session: entering it closes any open popover \u2014\n // its reply box writes to a doc that rejects writes \u2014 and the flip\n // itself must recompute the tooltip so the button hides/re-arms.\n const readOnlyChanged = tr.startState.readOnly !== tr.state.readOnly;\n if (readOnlyChanged && tr.state.readOnly && popover) {\n popover = null;\n popoverChanged = true;\n }\n\n // Keep the popover pinned to its text as the document shifts under it.\n if (popover && tr.docChanged) {\n popover = { ...popover, anchor: tr.changes.mapPos(popover.anchor) };\n popoverChanged = true;\n }\n for (const effect of tr.effects) {\n if (effect.is(openPopover)) {\n popover = effect.value;\n popoverChanged = true;\n } else if (effect.is(closePopover)) {\n popover = null;\n popoverChanged = true;\n // A comment was just added (or dismissed): don't flash the button\n // back up over the still-selected text.\n buttonDismissed = true;\n } else if (effect.is(dismissButton)) {\n buttonDismissed = true;\n } else if (effect.is(setPointerDown)) {\n pointerDown = effect.value;\n }\n }\n // Any fresh selection re-arms the button.\n if (tr.selection) buttonDismissed = false;\n\n if (popover) {\n // Reuse the exact same tooltip object unless the popover state itself\n // changed. Otherwise an unrelated transaction (a stray selection\n // change, an extension reconfigure, \u2026) would hand CodeMirror a fresh\n // Tooltip and it would tear down and remount the thread view mid-edit.\n if (!popoverChanged) {\n return buttonDismissed === value.buttonDismissed &&\n pointerDown === value.pointerDown\n ? value\n : { ...value, buttonDismissed, pointerDown };\n }\n return {\n popover,\n buttonDismissed,\n pointerDown,\n tooltip: popoverTooltip(popover, options, ui),\n };\n }\n\n // No popover: show the selection button, unless it's been dismissed.\n // Recompute only when something relevant changed.\n if (\n !popoverChanged &&\n !tr.docChanged &&\n !tr.selection &&\n !readOnlyChanged &&\n buttonDismissed === value.buttonDismissed &&\n pointerDown === value.pointerDown\n ) {\n return value;\n }\n return {\n popover: null,\n buttonDismissed,\n pointerDown,\n tooltip:\n buttonDismissed || pointerDown\n ? null\n : buttonTooltip(tr.state, options),\n };\n },\n provide: (field) => showTooltip.from(field, (value) => value.tooltip),\n });\n\n// Note the main button going down, and (wherever it's let go \u2014 often outside\n// the editor, at the end of a drag) coming back up.\nexport const trackPointer = (view: EditorView) => {\n view.dispatch({ effects: setPointerDown.of(true) });\n const up = () => {\n view.win.removeEventListener(\"mouseup\", up, true);\n view.win.removeEventListener(\"blur\", up);\n // the editor may have gone while the button was down\n if (view.dom.isConnected)\n view.dispatch({ effects: setPointerDown.of(false) });\n };\n view.win.addEventListener(\"mouseup\", up, true);\n view.win.addEventListener(\"blur\", up);\n};\n\n// Editor-level interactions: click commented text to (re)open its thread, and\n// Escape to dismiss the floating button.\nconst commentInteractions = (\n options: CommentUIOptions,\n ui: CommentUIState,\n field: StateField<FieldValue>\n): Extension =>\n EditorView.domEventHandlers({\n mousedown(event, view) {\n if (event.button === 0) trackPointer(view);\n if (\n event.button !== 0 ||\n event.metaKey ||\n event.ctrlKey ||\n event.altKey ||\n event.shiftKey\n ) {\n return false;\n }\n // Clicks inside an open popover are the thread tool's own business.\n if ((event.target as HTMLElement | null)?.closest(\".\" + POPOVER_CLASS)) {\n return false;\n }\n // Read-only: don't open threads either \u2014 the popover's reply box would\n // write into a doc that rejects writes.\n if (view.state.readOnly) return false;\n const pos = view.posAtCoords({ x: event.clientX, y: event.clientY });\n if (pos == null) return false;\n const hit = options.threadAtPos(pos);\n if (!hit) {\n ui.suppressed = null; // clicked away from any comment; re-arm reopening\n return false;\n }\n // Don't reopen the thread we just dismissed while the click stays inside\n // it \u2014 the user has to click elsewhere (or another comment) first.\n if (hit.threadUrl === ui.suppressed) return false;\n ui.suppressed = null;\n view.dispatch({\n effects: openPopover.of({ threadUrl: hit.threadUrl, anchor: hit.from }),\n });\n return false; // let the click also move the cursor as usual\n },\n keydown(event, view) {\n if (event.key !== \"Escape\") return false;\n const state = view.state.field(field, false);\n // While a popover is open its own Escape handler wins (it also works when\n // the popover's textarea holds focus); here we only hide the button.\n if (!state || state.popover || !state.tooltip) return false;\n view.dispatch({ effects: dismissButton.of(null) });\n return true;\n },\n });\n\n// Global colours are lifted into local --cm-comment-* tokens in a\n// :root/:host/[theme] block so they re-evaluate when the theme swaps. This\n// derivation can't live in the baseTheme below \u2014 CodeMirror prefixes every\n// non-\"&\" selector with the editor's scope class, so a `:root` key would\n// become `.cm... :root` and never match. It's injected as a plain stylesheet\n// instead, and the button reads only the derived tokens. The button is editor\n// UI, so it derives from --editor-* (not --studio-*, which is studio chrome).\nconst COMMENT_BUTTON_VARS_ID = \"cm-comment-button-vars\";\nconst ensureCommentButtonVars = (): void => {\n if (\n typeof document === \"undefined\" ||\n document.getElementById(COMMENT_BUTTON_VARS_ID)\n ) {\n return;\n }\n const style = document.createElement(\"style\");\n style.id = COMMENT_BUTTON_VARS_ID;\n style.textContent = `\n :root, :host, [theme] {\n /* the button sits on the editor surface: bg is the plain editor fill,\n text is the editor ink, and hover lifts a gentle one step up the fill\n ramp. see --text-editor-* in the theme. */\n --cm-comment-button-fg: var(--text-editor-line, #0a2b23);\n --cm-comment-button-bg: var(--text-editor-fill, #35f7ca);\n --cm-comment-button-bg-hover: var(--text-editor-fill-offset-10, #2ce0b6);\n }\n `;\n document.head.append(style);\n};\n\nconst commentUITheme = EditorView.baseTheme({\n \".cm-tooltip:has(.cm-comment-button)\": {\n border: \"none\",\n background: \"transparent\",\n boxShadow: \"none\",\n },\n \".cm-comment-button\": {\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: \"5px\",\n padding: \"4px 9px\",\n font: \"inherit\",\n fontSize: \"12px\",\n fontWeight: \"500\",\n lineHeight: \"1\",\n color: \"var(--cm-comment-button-fg)\",\n background: \"var(--cm-comment-button-bg)\",\n border: \"none\",\n borderRadius: \"6px\",\n boxShadow: \"0 2px 8px rgba(0, 0, 0, 0.25)\",\n cursor: \"var(--studio-cursor-pointer, pointer)\",\n userSelect: \"none\",\n whiteSpace: \"nowrap\",\n transition: \"background-color max(1ms, calc(0.15s * var(--studio-motion-scale, 1) * var(--studio-motion-preference, 1)))\",\n // fades in once the selection's made (it waits for the mouse to come up)\n animation:\n \"cm-comment-button-in max(1ms, calc(0.16s * var(--studio-motion-scale, 1) * var(--studio-motion-preference, 1))) ease-out both\",\n },\n \"@keyframes cm-comment-button-in\": {\n from: { opacity: \"0\", transform: \"translateY(2px)\" },\n to: { opacity: \"1\", transform: \"none\" },\n },\n \".cm-comment-button:hover\": {\n background: \"var(--cm-comment-button-bg-hover)\",\n },\n \".cm-comment-button svg\": {\n display: \"block\",\n },\n // The thread tool carries its own card (background, border, padding), so the\n // tooltip wrapper is just a transparent, softly-lifted host for it.\n \".cm-tooltip:has(.cm-comment-popover)\": {\n border: \"none\",\n background: \"transparent\",\n boxShadow: \"none\",\n },\n \".cm-comment-popover\": {\n width: \"min(360px, 92vw)\",\n marginTop: \"6px\",\n fontFamily: \"var(--editor-family)\",\n filter: \"drop-shadow(0 4px 14px rgba(0, 0, 0, 0.25))\",\n },\n // The popover supplies its own frame (the tooltip wrapper's shadow), so the\n // thread card inside drops its own border and shadow to avoid doubling up.\n \".cm-comment-popover .comments-thread-card\": {\n border: \"0\",\n boxShadow: \"none\",\n },\n // The draft textarea is width:100% with its own padding + border; without\n // border-box it spills out of the card's right edge (the card's own reset is\n // scoped to the sidebar panel, which the popover isn't inside).\n \".cm-comment-popover .comment-draft-editor\": {\n boxSizing: \"border-box\",\n maxWidth: \"100%\",\n minWidth: \"0\",\n width: \"100%\",\n },\n \".cm-comment-popover .comment-draft-editor .cm-content\": {\n minHeight: \"0\",\n minWidth: \"0\",\n margin: \"0\",\n padding: \"8px\",\n },\n \".cm-comment-popover .comment-draft-editor .cm-line\": {\n margin: \"0\",\n padding: \"0\",\n },\n \".cm-comment-popover .comment-draft-editor .cm-scroller\": {\n minWidth: \"0\",\n },\n});\n\n// The comment UI: the floating \"Comment\" button on a selection, and the thread\n// popover it (and clicking commented text) opens.\nexport const commentUI = (options: CommentUIOptions): Extension => {\n ensureCommentButtonVars();\n const ui: CommentUIState = { suppressed: null };\n const field = commentUIField(options, ui);\n return [field, commentInteractions(options, ui, field), commentUITheme];\n};\n", "import {\n cursor,\n type AutomergeUrl,\n type DocHandle,\n} from \"@automerge/automerge-repo/slim\";\n\nexport type DocWithComments = {\n \"@comments\"?: {\n threads: CommentThread[];\n };\n};\n\n/**\n * The `@patchwork.type` stamped on a comment thread so a datatype (and thus a\n * relevant tool) can be resolved when the thread's subdocument is opened on its\n * own. Mirrors `COMMENT_THREAD_TYPE` in the comments-view module.\n */\nexport const COMMENT_THREAD_TYPE = \"comment-thread\";\n\nexport type CommentThread = {\n id: string;\n refs: AutomergeUrl[];\n isResolved: boolean;\n comments: Comment[];\n \"@patchwork\"?: { type: string; title?: string };\n};\n\nexport type Comment = {\n id: string;\n content?: string;\n draftContent?: string;\n contactUrl: AutomergeUrl;\n timestamp: number;\n};\n\n// Creates a thread (with one empty, unfilled comment) targeting [from, to] and\n// returns the thread's url so the caller can open it \u2014 e.g. in a popover \u2014 for\n// the author to fill in. An abandoned, never-filled comment should be cleaned\n// up by the caller (see the comment UI's `onClose`).\nexport function createCommentForRange(\n handle: DocHandle<unknown>,\n path: readonly string[],\n from: number,\n to: number,\n contactUrl: AutomergeUrl\n): AutomergeUrl {\n const targetUrl = handle.sub(...path, cursor(from, to)).url;\n const threadId = crypto.randomUUID();\n const commentId = crypto.randomUUID();\n\n handle.change((doc: DocWithComments) => {\n doc[\"@comments\"] ??= { threads: [] };\n doc[\"@comments\"].threads.push({\n id: threadId,\n refs: [targetUrl],\n isResolved: false,\n comments: [\n {\n id: commentId,\n contactUrl,\n timestamp: Date.now(),\n },\n ],\n \"@patchwork\": { type: COMMENT_THREAD_TYPE },\n });\n });\n\n return handle.sub(\"@comments\", \"threads\", { id: threadId }).url;\n}\n", "import { createEffect, createSignal, onCleanup, onMount, } from \"solid-js\";\nimport { createStore, reconcile } from \"solid-js/store\";\nimport * as Providers from \"@inkandswitch/patchwork-providers\";\nfunction resolveElement(source) {\n return typeof source === \"function\" ? source() : source;\n}\n/**\n * Generic reactive request. Resolves the first value a provider emits for\n * `selector` (via the one-shot `request` helper) and returns an accessor that\n * reads `undefined` until then. `T` is the response type.\n */\nexport function request(element, selector) {\n const [value, setValue] = createSignal(undefined);\n onMount(() => {\n const target = resolveElement(element);\n if (!target)\n return;\n Providers.request(target, selector).then((v) => {\n if (v == null)\n return;\n setValue(() => v);\n });\n });\n return value;\n}\nexport function subscribe(element, selector, initialValue) {\n const [value, setValue] = createSignal(initialValue);\n onMount(() => {\n const target = resolveElement(element);\n if (!target)\n return;\n const unsubscribe = Providers.subscribe(target, selector, setValue);\n onCleanup(unsubscribe);\n });\n return value;\n}\n/**\n * Store-backed subscription. Use this when a consumer wants Solid's\n * fine-grained nested reactivity for incoming JSON object or array snapshots.\n * Requires an initial object/array so the store has a stable root.\n *\n * `reconcile` preserves stable object/array identity where possible, so this\n * helper is not a good fit when the top-level value is used as an identity key\n * for resources or memos.\n */\nexport function subscribeReconciled(element, selector, initialValue) {\n const [store, setStore] = createStore(initialValue);\n onMount(() => {\n const target = resolveElement(element);\n if (!target)\n return;\n const unsubscribe = Providers.subscribe(target, selector, (v) => {\n setStore(reconcile(v));\n });\n onCleanup(unsubscribe);\n });\n return store;\n}\n/**\n * Handle-specialized subscription. Use when the answering provider emits an\n * `AutomergeUrl`. The handle is recovered locally from the global repo\n * (`window.repo`), so it stays fully live \u2014 reads project reactively and writes go\n * straight back to the same repo. Returns `[doc, handle]` matching the shape\n * of `solid-automerge`'s `useDocument`; both read `undefined` until the first\n * url arrives. `T` is the doc shape inside the handle.\n */\nexport function subscribeDoc(element, selector) {\n const [handle, setHandle] = createSignal(undefined);\n onMount(() => {\n const target = resolveElement(element);\n if (!target)\n return;\n let canceled = false;\n const unsubscribe = Providers.subscribe(target, selector, (url) => {\n if (!url)\n return;\n const repo = \"repo\" in window ? window.repo : undefined;\n if (!repo)\n return;\n void Promise.resolve(repo.find(url)).then((h) => {\n if (canceled)\n return;\n setHandle(() => h);\n });\n });\n onCleanup(() => {\n canceled = true;\n unsubscribe();\n });\n });\n // Mirror the doc into a store by `reconcile`-ing against the *materialized*\n // snapshot (`handle.doc()`) on every change, rather than replaying the\n // change's incremental patches (as `solid-automerge`'s `createDocumentProjection`\n // / `autoproduce` does).\n //\n // A whole-value write such as `doc.list = [...]` is lowered by Automerge to\n // `putObjectFromHydrate`, whose patch stream is a `put` that materializes the\n // new container *plus* per-element `insert` patches. Replaying that delta with\n // `applyPatches` double-applies the contents \u2014 the `put` seeds `[a, b]`, then\n // the inserts splice `a, b` in again, yielding `[a, b, a, b]` \u2014 even though the\n // document itself is correct. The materialized snapshot is always\n // authoritative, so reconciling against it is robust to any valid change,\n // whole-array reassignment included.\n const [store, setStore] = createStore({});\n const [ready, setReady] = createSignal(false);\n createEffect(() => {\n const h = handle();\n if (!h) {\n setReady(false);\n return;\n }\n const sync = () => {\n setStore(reconcile((h.doc() ?? {})));\n setReady(true);\n };\n sync();\n h.on(\"change\", sync);\n onCleanup(() => h.off(\"change\", sync));\n });\n const doc = () => (ready() ? store : undefined);\n return [doc, handle];\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { template as _$template } from \"solid-js/web\";\nimport { insert as _$insert } from \"solid-js/web\";\nimport { createComponent as _$createComponent } from \"solid-js/web\";\nvar _tmpl$ = /*#__PURE__*/_$template(`<div style=\"width:100%;height:100%;overflow:hidden;position:relative;background:var(--studio-fill, white)\"><div style=display:flex;height:100%><div style=position:relative;flex:1;height:100%>`);\nimport { CodeMirror } from \"./lib/codemirror.tsx\";\nimport { render } from \"solid-js/web\";\nimport { RangeSet } from \"@codemirror/state\";\nimport { Decoration } from \"@codemirror/view\";\nimport { commentUI } from \"./lib/extensions/commentUI.ts\";\nimport { cursor } from \"@automerge/automerge-repo/slim\";\nimport { getRegistry } from \"@inkandswitch/patchwork-plugins\";\nimport { subscribeDoc, subscribe } from \"@inkandswitch/patchwork-providers-solid\";\nimport { createMemo, createResource, Show } from \"solid-js\";\nimport { createCommentForRange } from \"./lib/extensions/comments.ts\";\nconst PATH = [\"content\"];\nexport function CodeMirrorEditor(props) {\n const baseline = subscribe(props.element, {\n type: \"draft:baseline\",\n url: props.handle.url\n }, {\n heads: null\n });\n const commentEntries = subscribe(props.element, {\n type: \"patchwork:comments\"\n }, []);\n const [focusDoc, focusHandle] = subscribeDoc(props.element, {\n type: \"patchwork:focus\"\n });\n const contactUrl = subscribe(props.element, {\n type: \"patchwork:contact\"\n });\n const [commentTargets] = createResource(commentEntries, entries => getDedupedCommentTargets(entries, props.handle.url, props.repo), {\n initialValue: []\n });\n const focusRefUrls = createMemo(() => {\n const doc = focusDoc();\n return [...Object.keys(doc?.selection ?? {}), ...Object.keys(doc?.highlight ?? {})];\n });\n const [emphasisTargets] = createResource(focusRefUrls, async urls => resolveSubDocUrlsOfDoc(urls, props.handle.url, props.repo), {\n initialValue: []\n });\n const scrollTarget = createMemo(() => {\n let from = Infinity;\n let to = -Infinity;\n for (const ref of emphasisTargets()) {\n const positions = ref.rangePositions();\n if (!positions) continue;\n const [start, end] = positions;\n from = Math.min(from, start);\n to = Math.max(to, end);\n }\n return from <= to ? [from, to] : null;\n });\n let lastEmittedUrl;\n const onChangeSelection = (from, to) => {\n const handle = focusHandle();\n if (!handle) return;\n const nextUrl = props.handle.sub(...PATH, cursor(from, to)).url;\n if (nextUrl === lastEmittedUrl) return;\n handle.change(doc => {\n doc.selection = {\n [nextUrl]: true\n };\n });\n lastEmittedUrl = nextUrl;\n };\n const decorations = () => {\n const targetRefs = commentTargets();\n const emphasisRefs = emphasisTargets();\n return RangeSet.of(buildCommentDecorations(targetRefs, emphasisRefs), true\n // sort ranges\n );\n };\n const createThreadForRange = (from, to) => {\n const url = contactUrl();\n if (!url) {\n console.warn(\"Cannot create comment: no contactUrl available\");\n return null;\n }\n return createCommentForRange(props.handle, PATH, from, to, url);\n };\n const threadAtPos = pos => {\n const threadByTarget = /* @__PURE__ */new Map();\n for (const entry of commentEntries()) {\n threadByTarget.set(entry.targetUrl, entry.threadUrl);\n }\n for (const handle of commentTargets()) {\n const positions = handle.rangePositions();\n if (!positions) continue;\n const [start, end] = positions;\n if (start === end || pos < start || pos > end) continue;\n const threadUrl = threadByTarget.get(handle.url);\n if (threadUrl) return {\n threadUrl,\n from: start\n };\n }\n return null;\n };\n const findThreadByUrl = threadUrl => {\n const threads = props.handle.doc()?.[\"@comments\"]?.threads ?? [];\n return threads.find(thread => props.handle.sub(\"@comments\", \"threads\", {\n id: thread.id\n }).url === threadUrl);\n };\n const watchThreadForClose = (threadUrl, close) => {\n const contact = contactUrl();\n const hasOwnDraft = thread => thread.comments.some(c => c.contactUrl === contact && (c.draftContent !== void 0 || c.content === void 0));\n const seed = findThreadByUrl(threadUrl);\n let sawDraft = seed ? hasOwnDraft(seed) : false;\n const onChange = () => {\n const thread = findThreadByUrl(threadUrl);\n if (!thread) return close();\n if (hasOwnDraft(thread)) sawDraft = true;else if (sawDraft) close();\n };\n props.handle.on(\"change\", onChange);\n return () => props.handle.off(\"change\", onChange);\n };\n const onClosePopover = threadUrl => {\n const contact = contactUrl();\n const isEmptyOwnDraft = c => c.contactUrl === contact && c.content === void 0 && (c.draftContent === void 0 || c.draftContent.trim() === \"\");\n const thread = findThreadByUrl(threadUrl);\n if (!thread || !thread.comments.some(isEmptyOwnDraft)) return;\n const threadId = thread.id;\n queueMicrotask(() => {\n props.handle.change(doc => {\n const list = doc[\"@comments\"]?.threads;\n if (!list) return;\n const index = list.findIndex(t => t.id === threadId);\n if (index === -1) return;\n const target = list[index];\n for (let i = target.comments.length - 1; i >= 0; i--) {\n if (isEmptyOwnDraft(target.comments[i])) target.comments.splice(i, 1);\n }\n if (target.comments.length === 0) list.splice(index, 1);\n });\n });\n };\n const [datatypeExtensions] = createResource(() => loadCodeMirrorExtensionsForDoc(props.handle));\n const extensions = () => [commentUI({\n createThreadForRange,\n threadAtPos,\n watchThreadForClose,\n onClose: onClosePopover,\n // read when clicked, so no need to rebuild the extension on change\n threadsShownElsewhere: () => Object.keys(focusDoc()?.commentViews ?? {}).length > 0\n }), ...(datatypeExtensions() ?? [])];\n return (() => {\n var _el$ = _tmpl$(),\n _el$2 = _el$.firstChild,\n _el$3 = _el$2.firstChild;\n _$insert(_el$3, _$createComponent(Show, {\n get when() {\n return datatypeExtensions.state === \"ready\";\n },\n get children() {\n return _$createComponent(CodeMirror, {\n get handle() {\n return props.handle;\n },\n get repo() {\n return props.repo;\n },\n path: PATH,\n decorations: decorations,\n baseline: () => baseline()?.heads ?? null,\n get extensions() {\n return extensions();\n },\n onChangeSelection: onChangeSelection,\n scrollTarget: scrollTarget,\n contactUrl: () => contactUrl() ?? null\n });\n }\n }));\n return _el$;\n })();\n}\nexport const mount = (handle, element) => render(() => _$createComponent(CodeMirrorEditor, {\n handle: handle,\n get repo() {\n return element.repo;\n },\n element: element\n}), element);\nasync function getDedupedCommentTargets(comments, docUrl, repo) {\n if (!comments) return [];\n const overlappingRefs = /* @__PURE__ */new Set();\n for (const comment of comments) {\n if (comment.targetUrl.startsWith(docUrl)) {\n const target = await repo.find(comment.targetUrl);\n overlappingRefs.add(target);\n }\n }\n return Array.from(overlappingRefs);\n}\nasync function resolveSubDocUrlsOfDoc(urls, docUrl, repo) {\n const refs = /* @__PURE__ */new Set();\n for (const url of urls) {\n if (url.startsWith(docUrl)) {\n refs.add(await repo.find(url));\n }\n }\n return Array.from(refs);\n}\nfunction buildCommentDecorations(targetRefs, emphasisRefs) {\n const out = [];\n for (const ref of targetRefs) {\n const positions = ref.rangePositions();\n if (!positions) continue;\n const [start, end] = positions;\n if (start === end) continue;\n const isEmphasised = emphasisRefs.some(s => s.overlaps(ref));\n out.push(Decoration.mark({\n attributes: {\n style: commentTargetStyle(isEmphasised)\n }\n }).range(start, end));\n }\n return out;\n}\nfunction commentTargetStyle(isEmphasised) {\n const paper = isEmphasised ? \"56%\" : \"82%\";\n return `\n border-bottom: 2px solid var(--text-editor-secondary-text);\n background-color: color-mix(in oklch, var(--studio-secondary), var(--text-editor-fill) ${paper});\n `;\n}\nasync function loadCodeMirrorExtensionsForDoc(handle) {\n const docType = handle.doc()?.[\"@patchwork\"]?.type;\n const registry = getRegistry(\"codemirror:extension\");\n const loaded = await registry.loadAll(registry.filter(ext => {\n return ext.supportedDatatypes === \"*\" || Array.isArray(ext.supportedDatatypes) && ext.supportedDatatypes.includes(docType);\n }));\n return loaded.flatMap(ext => {\n const impl = ext.module;\n return Array.isArray(impl) ? impl : [impl];\n });\n}", "import {\n Extension,\n StateEffect,\n StateField,\n type EditorState,\n} from \"@codemirror/state\";\nimport {\n EditorView,\n showTooltip,\n type Tooltip,\n type TooltipView,\n} from \"@codemirror/view\";\nimport type { AutomergeUrl } from \"@automerge/automerge-repo/slim\";\nimport { COMMENT_THREAD_TYPE } from \"./comments.ts\";\n\n// The data hooks the editor UI needs from its host (the CodeMirror tool). The\n// extension owns all the DOM/positioning/lifecycle; the host owns everything\n// that needs the repo, the current contact, or the comment index.\nexport type CommentUIOptions = {\n // Create a thread targeting [from, to] and return its url, or null if it\n // couldn't be created (e.g. no current contact). Called when the floating\n // \"Comment\" button is clicked.\n createThreadForRange: (from: number, to: number) => AutomergeUrl | null;\n // Map a document position to the thread whose commented range covers it (and\n // that range's start), or null. Called when the user clicks commented text so\n // we can pop that thread open under the range's start.\n threadAtPos: (\n pos: number\n ) => { threadUrl: AutomergeUrl; from: number } | null;\n // Watch a thread and invoke `close` once its pending draft is submitted (or\n // cancelled, or the thread is removed). Returns an unsubscribe. Lets the\n // popover dismiss itself the moment the comment is saved.\n watchThreadForClose: (threadUrl: AutomergeUrl, close: () => void) => () => void;\n // Called whenever a popover closes, for any reason. Lets the host discard an\n // abandoned, never-filled-in draft so dismissing the popover doesn't litter\n // the document with empty comments.\n onClose: (threadUrl: AutomergeUrl) => void;\n // True while some other view (a comments panel) is showing threads on\n // screen. Then clicking commented text only moves the caret \u2014 that view\n // follows the selection to the thread \u2014 and a new comment is drafted\n // there, not in an inline popover. Without it, or while it's false, the\n // editor shows threads inline.\n threadsShownElsewhere?: () => boolean;\n};\n\n// Which thread the popover is showing, and the document position it hangs off.\ntype PopoverState = { threadUrl: AutomergeUrl; anchor: number };\n\nconst openPopover = StateEffect.define<PopoverState>();\nconst closePopover = StateEffect.define<null>();\n// Hide the floating \"Comment\" button until the next selection change (Escape).\nconst dismissButton = StateEffect.define<null>();\n// The main mouse button went down (true) or up (false) in the editor. The\n// button waits for the mouse to come up: it doesn't chase a selection that's\n// still being dragged out.\nconst setPointerDown = StateEffect.define<boolean>();\n\n// Mutable UI state shared across the extension's pieces (not editor state\n// because it isn't versioned/undoable): the thread we most recently dismissed,\n// so a click that stays inside it doesn't immediately pop it back open.\ntype CommentUIState = { suppressed: AutomergeUrl | null };\n\nconst POPOVER_CLASS = \"cm-comment-popover\";\n\n// A speech-bubble icon + \"Comment\" label, shown floating above the selection.\n// It reads the selection when it is pressed rather than being built for one:\n// the same button stays up as the selection under it is extended.\nconst buildCommentButton = (\n view: EditorView,\n options: CommentUIOptions\n): HTMLElement => {\n const button = document.createElement(\"button\");\n button.className = \"cm-comment-button\";\n button.setAttribute(\"aria-label\", \"Comment\");\n\n button.innerHTML = `\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\">\n <path d=\"M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z\"\n fill=\"currentColor\" stroke=\"none\"/>\n </svg>\n <span>Comment</span>\n `;\n\n // Without this, the browser focuses the button on mousedown, the editor\n // blurs, the selection collapses, our tooltip source returns null, and\n // CodeMirror tears down the button before `click` fires \u2014 so the first\n // press silently does nothing and the user has to click twice. Swallowing\n // mousedown keeps the editor focused and the selection (and button) alive\n // long enough for the click to land.\n button.addEventListener(\"mousedown\", (e) => {\n e.preventDefault();\n e.stopPropagation();\n });\n\n button.addEventListener(\"click\", (e) => {\n e.preventDefault();\n e.stopPropagation();\n const sel = view.state.selection.main;\n if (sel.empty) return;\n const threadUrl = options.createThreadForRange(sel.from, sel.to);\n if (!threadUrl) return;\n // A visible comments panel picks the new thread up (it's on the\n // selection) and opens its draft; just put the button away.\n if (options.threadsShownElsewhere?.()) {\n view.dispatch({ effects: dismissButton.of(null) });\n return;\n }\n // Open the popover right where the button was (the selection head).\n view.dispatch({ effects: openPopover.of({ threadUrl, anchor: sel.head }) });\n });\n\n return button;\n};\n\n// The popover body: the shared comment-thread tool, rendered inline via the\n// `patchwork-view` custom element. It's mounted inside `view.dom`, so the\n// element's provider events (repo, contact, \u2026) still bubble up to the host.\nconst buildThreadPopover = (\n view: EditorView,\n state: PopoverState,\n options: CommentUIOptions,\n ui: CommentUIState\n): TooltipView => {\n const dom = document.createElement(\"div\");\n dom.className = POPOVER_CLASS;\n\n const threadView = document.createElement(\"patchwork-view\");\n threadView.setAttribute(\"doc-url\", state.threadUrl);\n threadView.setAttribute(\"tool-id\", COMMENT_THREAD_TYPE);\n dom.append(threadView);\n\n const close = () => view.dispatch({ effects: closePopover.of(null) });\n\n // Dismiss on a click anywhere outside the popover, or on Escape. Both are\n // armed on a deferred tick so the very interaction that opened the popover\n // (the button click / the click on commented text) doesn't also close it.\n const onOutsideMouseDown = (event: MouseEvent) => {\n if (!dom.contains(event.target as Node)) close();\n };\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key === \"Escape\") close();\n };\n const doc = view.win.document;\n const armTimer = view.win.setTimeout(() => {\n doc.addEventListener(\"mousedown\", onOutsideMouseDown, true);\n doc.addEventListener(\"keydown\", onKeyDown, true);\n }, 0);\n\n // Close as soon as the draft is submitted (or the thread goes away).\n const stopWatch = options.watchThreadForClose(state.threadUrl, close);\n\n return {\n dom,\n destroy() {\n view.win.clearTimeout(armTimer);\n doc.removeEventListener(\"mousedown\", onOutsideMouseDown, true);\n doc.removeEventListener(\"keydown\", onKeyDown, true);\n stopWatch();\n // Remember this thread as dismissed so clicking within its range again\n // doesn't immediately reopen it (cleared once a click lands elsewhere).\n ui.suppressed = state.threadUrl;\n options.onClose(state.threadUrl);\n },\n };\n};\n\n// `create` has to be the same function from one selection to the next (see\n// `commentUIField`): CodeMirror keeps a tooltip's DOM only while its `create`\n// is unchanged, and otherwise rebuilds the button, replaying its entrance, on\n// every keystroke that moves the selection.\nconst buttonTooltip = (\n state: EditorState,\n options: CommentUIOptions,\n create: Tooltip[\"create\"]\n): Tooltip | null => {\n const sel = state.selection.main;\n // A read-only editor can't take the comment write, so don't offer the button.\n if (sel.empty || state.readOnly) return null;\n\n // Anchor the button at the drag tail (the selection head) rather than the\n // start, so dragging left-to-right lands it on the right and right-to-left\n // on the left.\n return {\n pos: sel.head,\n above: true,\n strictSide: false,\n arrow: false,\n create,\n };\n};\n\nconst popoverTooltip = (\n state: PopoverState,\n options: CommentUIOptions,\n ui: CommentUIState\n): Tooltip => ({\n pos: state.anchor,\n above: false, // comes out underneath the anchor\n strictSide: false,\n arrow: false,\n create: (view) => buildThreadPopover(view, state, options, ui),\n});\n\ntype FieldValue = {\n popover: PopoverState | null;\n // The floating button is hidden while this is true \u2014 after Escape, or after a\n // comment is added \u2014 until a fresh selection re-arms it.\n buttonDismissed: boolean;\n // A selection is being dragged out with the mouse.\n pointerDown: boolean;\n tooltip: Tooltip | null;\n};\n\n// A single field driving the one tooltip slot: the thread popover when one is\n// open, otherwise the selection's \"Comment\" button.\nconst commentUIField = (options: CommentUIOptions, ui: CommentUIState) => {\n const createButton: Tooltip[\"create\"] = (view) => ({\n dom: buildCommentButton(view, options),\n });\n return StateField.define<FieldValue>({\n create: (state) => ({\n popover: null,\n buttonDismissed: false,\n pointerDown: false,\n tooltip: buttonTooltip(state, options, createButton),\n }),\n update(value, tr) {\n let popover = value.popover;\n let buttonDismissed = value.buttonDismissed;\n let pointerDown = value.pointerDown;\n let popoverChanged = false;\n\n // Read-only can flip mid-session: entering it closes any open popover \u2014\n // its reply box writes to a doc that rejects writes \u2014 and the flip\n // itself must recompute the tooltip so the button hides/re-arms.\n const readOnlyChanged = tr.startState.readOnly !== tr.state.readOnly;\n if (readOnlyChanged && tr.state.readOnly && popover) {\n popover = null;\n popoverChanged = true;\n }\n\n // Keep the popover pinned to its text as the document shifts under it.\n if (popover && tr.docChanged) {\n popover = { ...popover, anchor: tr.changes.mapPos(popover.anchor) };\n popoverChanged = true;\n }\n for (const effect of tr.effects) {\n if (effect.is(openPopover)) {\n popover = effect.value;\n popoverChanged = true;\n } else if (effect.is(closePopover)) {\n popover = null;\n popoverChanged = true;\n // A comment was just added (or dismissed): don't flash the button\n // back up over the still-selected text.\n buttonDismissed = true;\n } else if (effect.is(dismissButton)) {\n buttonDismissed = true;\n } else if (effect.is(setPointerDown)) {\n pointerDown = effect.value;\n }\n }\n // Any fresh selection re-arms the button.\n if (tr.selection) buttonDismissed = false;\n\n if (popover) {\n // Reuse the exact same tooltip object unless the popover state itself\n // changed. Otherwise an unrelated transaction (a stray selection\n // change, an extension reconfigure, \u2026) would hand CodeMirror a fresh\n // Tooltip and it would tear down and remount the thread view mid-edit.\n if (!popoverChanged) {\n return buttonDismissed === value.buttonDismissed &&\n pointerDown === value.pointerDown\n ? value\n : { ...value, buttonDismissed, pointerDown };\n }\n return {\n popover,\n buttonDismissed,\n pointerDown,\n // Following its text as the document shifts is a move, not a new\n // popover: keep the tooltip's `create`, so CodeMirror repositions the\n // thread view it has rather than closing it (which discards an\n // unwritten draft, see `onClose`) and mounting another.\n tooltip:\n value.tooltip && value.popover?.threadUrl === popover.threadUrl\n ? { ...value.tooltip, pos: popover.anchor }\n : popoverTooltip(popover, options, ui),\n };\n }\n\n // No popover: show the selection button, unless it's been dismissed.\n // Recompute only when something relevant changed.\n if (\n !popoverChanged &&\n !tr.docChanged &&\n !tr.selection &&\n !readOnlyChanged &&\n buttonDismissed === value.buttonDismissed &&\n pointerDown === value.pointerDown\n ) {\n return value;\n }\n return {\n popover: null,\n buttonDismissed,\n pointerDown,\n tooltip:\n buttonDismissed || pointerDown\n ? null\n : buttonTooltip(tr.state, options, createButton),\n };\n },\n provide: (field) => showTooltip.from(field, (value) => value.tooltip),\n });\n};\n\n// Note the main button going down, and (wherever it's let go \u2014 often outside\n// the editor, at the end of a drag) coming back up.\nexport const trackPointer = (view: EditorView) => {\n view.dispatch({ effects: setPointerDown.of(true) });\n const up = () => {\n view.win.removeEventListener(\"mouseup\", up, true);\n view.win.removeEventListener(\"blur\", up);\n // the editor may have gone while the button was down\n if (view.dom.isConnected)\n view.dispatch({ effects: setPointerDown.of(false) });\n };\n view.win.addEventListener(\"mouseup\", up, true);\n view.win.addEventListener(\"blur\", up);\n};\n\n// Editor-level interactions: click commented text to (re)open its thread, and\n// Escape to dismiss the floating button.\nconst commentInteractions = (\n options: CommentUIOptions,\n ui: CommentUIState,\n field: StateField<FieldValue>\n): Extension =>\n EditorView.domEventHandlers({\n mousedown(event, view) {\n if (event.button === 0) trackPointer(view);\n if (\n event.button !== 0 ||\n event.metaKey ||\n event.ctrlKey ||\n event.altKey ||\n event.shiftKey\n ) {\n return false;\n }\n // Clicks inside an open popover are the thread tool's own business.\n if ((event.target as HTMLElement | null)?.closest(\".\" + POPOVER_CLASS)) {\n return false;\n }\n // Read-only: don't open threads either \u2014 the popover's reply box would\n // write into a doc that rejects writes.\n if (view.state.readOnly) return false;\n const pos = view.posAtCoords({ x: event.clientX, y: event.clientY });\n if (pos == null) return false;\n const hit = options.threadAtPos(pos);\n if (!hit) {\n ui.suppressed = null; // clicked away from any comment; re-arm reopening\n return false;\n }\n // A visible comments panel follows the caret to this thread itself.\n if (options.threadsShownElsewhere?.()) return false;\n // Don't reopen the thread we just dismissed while the click stays inside\n // it \u2014 the user has to click elsewhere (or another comment) first.\n if (hit.threadUrl === ui.suppressed) return false;\n ui.suppressed = null;\n view.dispatch({\n effects: openPopover.of({ threadUrl: hit.threadUrl, anchor: hit.from }),\n });\n return false; // let the click also move the cursor as usual\n },\n keydown(event, view) {\n if (event.key !== \"Escape\") return false;\n const state = view.state.field(field, false);\n // While a popover is open its own Escape handler wins (it also works when\n // the popover's textarea holds focus); here we only hide the button.\n if (!state || state.popover || !state.tooltip) return false;\n view.dispatch({ effects: dismissButton.of(null) });\n return true;\n },\n });\n\n// Global colours are lifted into local --cm-comment-* tokens in a\n// :root/:host/[theme] block so they re-evaluate when the theme swaps. This\n// derivation can't live in the baseTheme below \u2014 CodeMirror prefixes every\n// non-\"&\" selector with the editor's scope class, so a `:root` key would\n// become `.cm... :root` and never match. It's injected as a plain stylesheet\n// instead, and the button reads only the derived tokens. The button is editor\n// UI, so it derives from --editor-* (not --studio-*, which is studio chrome).\nconst COMMENT_BUTTON_VARS_ID = \"cm-comment-button-vars\";\nconst ensureCommentButtonVars = (): void => {\n if (\n typeof document === \"undefined\" ||\n document.getElementById(COMMENT_BUTTON_VARS_ID)\n ) {\n return;\n }\n const style = document.createElement(\"style\");\n style.id = COMMENT_BUTTON_VARS_ID;\n style.textContent = `\n :root, :host, [theme] {\n /* the button sits on the editor surface: bg is the plain editor fill,\n text is the editor ink, and hover lifts a gentle one step up the fill\n ramp. see --text-editor-* in the theme. */\n --cm-comment-button-fg: var(--text-editor-line, #0a2b23);\n --cm-comment-button-bg: var(--text-editor-fill, #35f7ca);\n --cm-comment-button-bg-hover: var(--text-editor-fill-offset-10, #2ce0b6);\n }\n `;\n document.head.append(style);\n};\n\nconst commentUITheme = EditorView.baseTheme({\n \".cm-tooltip:has(.cm-comment-button)\": {\n border: \"none\",\n background: \"transparent\",\n boxShadow: \"none\",\n },\n \".cm-comment-button\": {\n display: \"inline-flex\",\n alignItems: \"center\",\n gap: \"5px\",\n padding: \"4px 9px\",\n font: \"inherit\",\n fontSize: \"12px\",\n fontWeight: \"500\",\n lineHeight: \"1\",\n color: \"var(--cm-comment-button-fg)\",\n background: \"var(--cm-comment-button-bg)\",\n border: \"none\",\n borderRadius: \"6px\",\n boxShadow: \"0 2px 8px rgba(0, 0, 0, 0.25)\",\n cursor: \"var(--studio-cursor-pointer, pointer)\",\n userSelect: \"none\",\n whiteSpace: \"nowrap\",\n transition: \"background-color max(1ms, calc(0.15s * var(--studio-motion-scale, 1) * var(--studio-motion-preference, 1)))\",\n // fades in once the selection's made (it waits for the mouse to come up)\n animation:\n \"cm-comment-button-in max(1ms, calc(0.16s * var(--studio-motion-scale, 1) * var(--studio-motion-preference, 1))) ease-out both\",\n },\n \"@keyframes cm-comment-button-in\": {\n from: { opacity: \"0\", transform: \"translateY(2px)\" },\n to: { opacity: \"1\", transform: \"none\" },\n },\n \".cm-comment-button:hover\": {\n background: \"var(--cm-comment-button-bg-hover)\",\n },\n \".cm-comment-button svg\": {\n display: \"block\",\n },\n // The thread tool carries its own card (background, border, padding), so the\n // tooltip wrapper is just a transparent, softly-lifted host for it.\n \".cm-tooltip:has(.cm-comment-popover)\": {\n border: \"none\",\n background: \"transparent\",\n boxShadow: \"none\",\n },\n \".cm-comment-popover\": {\n width: \"min(360px, 92vw)\",\n marginTop: \"6px\",\n fontFamily: \"var(--editor-family)\",\n filter: \"drop-shadow(0 4px 14px rgba(0, 0, 0, 0.25))\",\n },\n // The popover supplies its own frame (the tooltip wrapper's shadow), so the\n // thread card inside drops its own border and shadow to avoid doubling up.\n \".cm-comment-popover .comments-thread-card\": {\n border: \"0\",\n boxShadow: \"none\",\n },\n // The draft textarea is width:100% with its own padding + border; without\n // border-box it spills out of the card's right edge (the card's own reset is\n // scoped to the sidebar panel, which the popover isn't inside).\n \".cm-comment-popover .comment-draft-editor\": {\n boxSizing: \"border-box\",\n maxWidth: \"100%\",\n minWidth: \"0\",\n width: \"100%\",\n },\n \".cm-comment-popover .comment-draft-editor .cm-content\": {\n minHeight: \"0\",\n minWidth: \"0\",\n margin: \"0\",\n padding: \"8px\",\n },\n \".cm-comment-popover .comment-draft-editor .cm-line\": {\n margin: \"0\",\n padding: \"0\",\n },\n \".cm-comment-popover .comment-draft-editor .cm-scroller\": {\n minWidth: \"0\",\n },\n});\n\n// The comment UI: the floating \"Comment\" button on a selection, and the thread\n// popover it (and clicking commented text) opens.\nexport const commentUI = (options: CommentUIOptions): Extension => {\n ensureCommentButtonVars();\n const ui: CommentUIState = { suppressed: null };\n const field = commentUIField(options, ui);\n return [field, commentInteractions(options, ui, field), commentUITheme];\n};\n", "import {\n cursor,\n type AutomergeUrl,\n type DocHandle,\n} from \"@automerge/automerge-repo/slim\";\n\nexport type DocWithComments = {\n \"@comments\"?: {\n threads: CommentThread[];\n };\n};\n\n/**\n * The `@patchwork.type` stamped on a comment thread so a datatype (and thus a\n * relevant tool) can be resolved when the thread's subdocument is opened on its\n * own. Mirrors `COMMENT_THREAD_TYPE` in the comments-view module.\n */\nexport const COMMENT_THREAD_TYPE = \"comment-thread\";\n\nexport type CommentThread = {\n id: string;\n refs: AutomergeUrl[];\n isResolved: boolean;\n comments: Comment[];\n \"@patchwork\"?: { type: string; title?: string };\n};\n\nexport type Comment = {\n id: string;\n content?: string;\n draftContent?: string;\n contactUrl: AutomergeUrl;\n timestamp: number;\n};\n\n// Creates a thread (with one empty, unfilled comment) targeting [from, to] and\n// returns the thread's url so the caller can open it \u2014 e.g. in a popover \u2014 for\n// the author to fill in. An abandoned, never-filled comment should be cleaned\n// up by the caller (see the comment UI's `onClose`).\nexport function createCommentForRange(\n handle: DocHandle<unknown>,\n path: readonly string[],\n from: number,\n to: number,\n contactUrl: AutomergeUrl\n): AutomergeUrl {\n const targetUrl = handle.sub(...path, cursor(from, to)).url;\n const threadId = crypto.randomUUID();\n const commentId = crypto.randomUUID();\n\n handle.change((doc: DocWithComments) => {\n doc[\"@comments\"] ??= { threads: [] };\n doc[\"@comments\"].threads.push({\n id: threadId,\n refs: [targetUrl],\n isResolved: false,\n comments: [\n {\n id: commentId,\n contactUrl,\n timestamp: Date.now(),\n },\n ],\n \"@patchwork\": { type: COMMENT_THREAD_TYPE },\n });\n });\n\n return handle.sub(\"@comments\", \"threads\", { id: threadId }).url;\n}\n", "import { createEffect, createSignal, onCleanup, onMount, } from \"solid-js\";\nimport { createStore, reconcile } from \"solid-js/store\";\nimport * as Providers from \"@inkandswitch/patchwork-providers\";\nfunction resolveElement(source) {\n return typeof source === \"function\" ? source() : source;\n}\n/**\n * Generic reactive request. Resolves the first value a provider emits for\n * `selector` (via the one-shot `request` helper) and returns an accessor that\n * reads `undefined` until then. `T` is the response type.\n */\nexport function request(element, selector) {\n const [value, setValue] = createSignal(undefined);\n onMount(() => {\n const target = resolveElement(element);\n if (!target)\n return;\n Providers.request(target, selector).then((v) => {\n if (v == null)\n return;\n setValue(() => v);\n });\n });\n return value;\n}\nexport function subscribe(element, selector, initialValue) {\n const [value, setValue] = createSignal(initialValue);\n onMount(() => {\n const target = resolveElement(element);\n if (!target)\n return;\n const unsubscribe = Providers.subscribe(target, selector, setValue);\n onCleanup(unsubscribe);\n });\n return value;\n}\n/**\n * Store-backed subscription. Use this when a consumer wants Solid's\n * fine-grained nested reactivity for incoming JSON object or array snapshots.\n * Requires an initial object/array so the store has a stable root.\n *\n * `reconcile` preserves stable object/array identity where possible, so this\n * helper is not a good fit when the top-level value is used as an identity key\n * for resources or memos.\n */\nexport function subscribeReconciled(element, selector, initialValue) {\n const [store, setStore] = createStore(initialValue);\n onMount(() => {\n const target = resolveElement(element);\n if (!target)\n return;\n const unsubscribe = Providers.subscribe(target, selector, (v) => {\n setStore(reconcile(v));\n });\n onCleanup(unsubscribe);\n });\n return store;\n}\n/**\n * Handle-specialized subscription. Use when the answering provider emits an\n * `AutomergeUrl`. The handle is recovered locally from the global repo\n * (`window.repo`), so it stays fully live \u2014 reads project reactively and writes go\n * straight back to the same repo. Returns `[doc, handle]` matching the shape\n * of `solid-automerge`'s `useDocument`; both read `undefined` until the first\n * url arrives. `T` is the doc shape inside the handle.\n */\nexport function subscribeDoc(element, selector) {\n const [handle, setHandle] = createSignal(undefined);\n onMount(() => {\n const target = resolveElement(element);\n if (!target)\n return;\n let canceled = false;\n const unsubscribe = Providers.subscribe(target, selector, (url) => {\n if (!url)\n return;\n const repo = \"repo\" in window ? window.repo : undefined;\n if (!repo)\n return;\n void Promise.resolve(repo.find(url)).then((h) => {\n if (canceled)\n return;\n setHandle(() => h);\n });\n });\n onCleanup(() => {\n canceled = true;\n unsubscribe();\n });\n });\n // Mirror the doc into a store by `reconcile`-ing against the *materialized*\n // snapshot (`handle.doc()`) on every change, rather than replaying the\n // change's incremental patches (as `solid-automerge`'s `createDocumentProjection`\n // / `autoproduce` does).\n //\n // A whole-value write such as `doc.list = [...]` is lowered by Automerge to\n // `putObjectFromHydrate`, whose patch stream is a `put` that materializes the\n // new container *plus* per-element `insert` patches. Replaying that delta with\n // `applyPatches` double-applies the contents \u2014 the `put` seeds `[a, b]`, then\n // the inserts splice `a, b` in again, yielding `[a, b, a, b]` \u2014 even though the\n // document itself is correct. The materialized snapshot is always\n // authoritative, so reconciling against it is robust to any valid change,\n // whole-array reassignment included.\n const [store, setStore] = createStore({});\n const [ready, setReady] = createSignal(false);\n createEffect(() => {\n const h = handle();\n if (!h) {\n setReady(false);\n return;\n }\n const sync = () => {\n setStore(reconcile((h.doc() ?? {})));\n setReady(true);\n };\n sync();\n h.on(\"change\", sync);\n onCleanup(() => h.off(\"change\", sync));\n });\n const doc = () => (ready() ? store : undefined);\n return [doc, handle];\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["cursor", "subscribe", "subscribe", "cursor"]
7
7
  }
@@ -2361,6 +2361,17 @@ var Bullet = class extends WidgetType2 {
2361
2361
  return span;
2362
2362
  }
2363
2363
  };
2364
+ var Rule = class extends WidgetType2 {
2365
+ eq() {
2366
+ return true;
2367
+ }
2368
+ toDOM() {
2369
+ const hr = document.createElement("span");
2370
+ hr.className = "cm-md-hr";
2371
+ hr.setAttribute("role", "separator");
2372
+ return hr;
2373
+ }
2374
+ };
2364
2375
  var NumberLabel = class extends WidgetType2 {
2365
2376
  constructor(text) {
2366
2377
  super();
@@ -2589,6 +2600,14 @@ function build2(view) {
2589
2600
  }
2590
2601
  return false;
2591
2602
  }
2603
+ if (name === "HorizontalRule") {
2604
+ const line = doc2.lineAt(node.from);
2605
+ if (!editing.has(line.number))
2606
+ decos.push(
2607
+ Decoration3.replace({ widget: new Rule() }).range(node.from, node.to)
2608
+ );
2609
+ return false;
2610
+ }
2592
2611
  if (name === "InlineCode") {
2593
2612
  decos.push(inlineCode.range(node.from, node.to));
2594
2613
  return false;
@@ -2745,6 +2764,12 @@ var marginTheme = EditorView3.baseTheme({
2745
2764
  borderBottomLeftRadius: "6px",
2746
2765
  borderBottomRightRadius: "6px"
2747
2766
  },
2767
+ ".cm-md-hr": {
2768
+ display: "inline-block",
2769
+ width: "100%",
2770
+ verticalAlign: "middle",
2771
+ borderTop: "var(--studio-border-width, 1px) var(--studio-border-style, solid) var(--text-editor-fill-offset-40, rgb(0 0 0 / 0.15))"
2772
+ },
2748
2773
  ".cm-md-inline-code": {
2749
2774
  background: "var(--text-editor-fill-offset-10, rgb(0 0 0 / 0.04))",
2750
2775
  borderRadius: "4px",