@inkandswitch/patchwork-pkg-base 0.0.7 → 0.0.8

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 (48) hide show
  1. package/modules.json +4 -0
  2. package/package.json +1 -1
  3. package/packages/account-history/dist/assets/HistoryRecorder-HO_sxeuE.js +248 -0
  4. package/packages/account-history/dist/assets/HistoryRecorder-HO_sxeuE.js.map +1 -0
  5. package/packages/account-history/dist/assets/HistoryViewer-Ct5g7zek.js +308 -0
  6. package/packages/account-history/dist/assets/HistoryViewer-Ct5g7zek.js.map +1 -0
  7. package/packages/account-history/dist/assets/NotebookViewer-BkYJL6Jc.js +504 -0
  8. package/packages/account-history/dist/assets/NotebookViewer-BkYJL6Jc.js.map +1 -0
  9. package/packages/account-history/dist/assets/index-CFzuSuJg.js +148 -0
  10. package/packages/account-history/dist/assets/index-CFzuSuJg.js.map +1 -0
  11. package/packages/account-history/dist/assets/makeDocumentProjection-DXgEZYyv.js +706 -0
  12. package/packages/account-history/dist/assets/makeDocumentProjection-DXgEZYyv.js.map +1 -0
  13. package/packages/account-history/dist/assets/store-CRmRL4Cv.js +1079 -0
  14. package/packages/account-history/dist/assets/store-CRmRL4Cv.js.map +1 -0
  15. package/packages/account-history/dist/index.js +5 -0
  16. package/packages/account-history/dist/index.js.map +1 -0
  17. package/packages/account-history/package.json +33 -0
  18. package/packages/chat/dist/ChatRoot-BXsklgz7.js +6113 -0
  19. package/packages/chat/dist/context-tool-CenQrztX.js +107 -0
  20. package/packages/chat/dist/datatype-1niKKK-c.js +15 -0
  21. package/packages/chat/dist/index-DYFHDHZ9.js +368 -0
  22. package/packages/chat/dist/index.js +5 -0
  23. package/packages/chat/dist/tool-D9BTS5wc.js +38 -0
  24. package/packages/chat/package.json +42 -0
  25. package/packages/comments-view/dist/assets/comments-D9lwpHOh.js +50 -0
  26. package/packages/comments-view/dist/assets/comments-D9lwpHOh.js.map +1 -0
  27. package/packages/comments-view/dist/assets/datatype-C4SMwWiO.js +22 -0
  28. package/packages/comments-view/dist/assets/datatype-C4SMwWiO.js.map +1 -0
  29. package/packages/comments-view/dist/assets/main-C8GVksuY.js +2973 -0
  30. package/packages/comments-view/dist/assets/main-C8GVksuY.js.map +1 -0
  31. package/packages/comments-view/dist/index.js +115 -0
  32. package/packages/comments-view/dist/index.js.map +1 -0
  33. package/packages/comments-view/package.json +36 -0
  34. package/packages/drafts/dist/assets/DraftDatatype-Dn8VF6oC.js +14 -0
  35. package/packages/drafts/dist/assets/DraftDatatype-Dn8VF6oC.js.map +1 -0
  36. package/packages/drafts/dist/assets/DraftListProvider-DVhUGU1x.js +362 -0
  37. package/packages/drafts/dist/assets/DraftListProvider-DVhUGU1x.js.map +1 -0
  38. package/packages/drafts/dist/assets/DraftOverlayProvider-NMDqnpBV.js +123 -0
  39. package/packages/drafts/dist/assets/DraftOverlayProvider-NMDqnpBV.js.map +1 -0
  40. package/packages/drafts/dist/assets/clone-policy-D4XOfZxs.js +15 -0
  41. package/packages/drafts/dist/assets/clone-policy-D4XOfZxs.js.map +1 -0
  42. package/packages/drafts/dist/assets/index-DAqs20jZ.js +65 -0
  43. package/packages/drafts/dist/assets/index-DAqs20jZ.js.map +1 -0
  44. package/packages/drafts/dist/assets/main-Cdev7z-t.js +2366 -0
  45. package/packages/drafts/dist/assets/main-Cdev7z-t.js.map +1 -0
  46. package/packages/drafts/dist/index.js +140 -0
  47. package/packages/drafts/dist/index.js.map +1 -0
  48. package/packages/drafts/package.json +34 -0
@@ -0,0 +1,2973 @@
1
+ (function() {
2
+ "use strict";
3
+ try {
4
+ if (typeof document != "undefined") {
5
+ var elementStyle = document.createElement("style");
6
+ elementStyle.appendChild(document.createTextNode("@layer package {\n:root,\n:host,\n[theme] {\n --comments-fg: var(--text-editor-line, var(--studio-line, black));\n --comments-muted-fg: var(--studio-line-offset-50, #999);\n --comments-bg: var(--text-editor-fill, var(--studio-fill, white));\n --comments-border: var(--studio-fill-offset-20, #ccc);\n --comments-hover-bg: var(--studio-fill-offset-10, #f5f5f5);\n --comments-font: var(--studio-family-sans, system-ui, sans-serif);\n --comments-accent: var(--studio-secondary, #33ccf8);\n}\n}\n\n/* The width:100% boxes below (panel, textarea) also carry padding and a\n border, so without border-box they extend past their container and overflow\n it. Scoped to the panel and the thread card so the injected stylesheet never\n touches the host page (it may be injected into document.head, not a shadow\n root). The card is covered on its own because a single thread renders\n standalone too — e.g. in the editor's comment popover, with no panel around\n it — and its draft textarea would otherwise spill out of the card. */\n.comments-panel,\n.comments-panel *,\n.comments-panel *::before,\n.comments-panel *::after,\n.comments-thread-card,\n.comments-thread-card *,\n.comments-thread-card *::before,\n.comments-thread-card *::after {\n box-sizing: border-box;\n}\n\n.comments-panel {\n height: 100%;\n width: 100%;\n overflow: auto;\n display: flex;\n flex-direction: column;\n padding: 0.5rem;\n gap: 0.5rem;\n font-family: var(--comments-font);\n color: var(--comments-fg);\n}\n\n.comments-panel-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n font-size: 0.75rem;\n color: var(--comments-muted-fg);\n}\n\n.comments-panel-header-title {\n font-weight: 500;\n}\n\n.comments-empty {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 1.5rem 0.5rem;\n text-align: center;\n font-size: 0.875rem;\n color: var(--comments-muted-fg);\n}\n\n.comments-thread {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n/* The thread's card + actions are rendered by the `comment-thread` tool into\n its <patchwork-view> host, so that host is the flex column that lays them\n out (a custom element is `display: inline` by default). */\n.comments-thread > patchwork-view {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.comments-thread-card {\n background: var(--comments-bg);\n border: 1px solid var(--comments-border);\n padding: 0.5rem;\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n cursor: pointer;\n transition: box-shadow 0.15s ease;\n}\n\n.comments-thread-card--primary {\n box-shadow:\n 0 10px 15px -3px rgb(0 0 0 / 0.1),\n 0 4px 6px -4px rgb(0 0 0 / 0.1);\n}\n\n.comments-thread-card--secondary {\n box-shadow:\n 0 4px 6px -1px rgb(0 0 0 / 0.1),\n 0 2px 4px -2px rgb(0 0 0 / 0.1);\n}\n\n.comments-thread-card--inactive {\n box-shadow: 0 1px 2px 0 rgb(0 0 0 / 0.05);\n}\n\n/* Resolved threads are hidden in the sidebar but shown when embedded. Keep\n them clearly readable, just visibly \"settled\": muted fill, dashed border,\n and slightly dimmed so they read as archived rather than active. */\n.comments-thread-card--resolved {\n background: var(--comments-hover-bg);\n border-style: dashed;\n opacity: 0.75;\n}\n\n.comments-thread-resolved-label {\n align-self: flex-start;\n font-size: 0.6875rem;\n font-weight: 500;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n padding: 0.0625rem 0.375rem;\n border-radius: 0.625rem;\n color: var(--comments-muted-fg);\n border: 1px solid var(--comments-border);\n}\n\n.comments-thread-card-body {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.comments-thread-doc-level {\n font-size: 0.6875rem;\n text-transform: uppercase;\n letter-spacing: 0.03em;\n color: var(--comments-muted-fg);\n}\n\n.comments-thread-header {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n}\n\n.comments-thread-actions {\n display: flex;\n flex-direction: row;\n gap: 0.5rem;\n justify-content: flex-end;\n}\n\n.comment-card {\n display: flex;\n flex-direction: column;\n gap: 0.5rem;\n}\n\n.comment-header {\n display: flex;\n flex-direction: row;\n justify-content: space-between;\n align-items: center;\n}\n\n.comment-author {\n display: flex;\n flex-direction: row;\n gap: 0.5rem;\n align-items: center;\n}\n\n.comment-author-name {\n font-size: 0.875rem;\n font-weight: 500;\n white-space: nowrap;\n color: var(--comments-fg);\n}\n\n.comment-timestamp {\n font-size: 0.75rem;\n color: var(--comments-muted-fg);\n}\n\n.comment-content {\n font-size: 1rem;\n color: var(--comments-fg);\n white-space: pre-wrap;\n overflow-wrap: break-word;\n}\n\n/* The draft editor is a cute.txt (CodeMirror) instance; box it like the old\n textarea. Its inner padding/font come from the injected cute.txt styles. */\n.comment-draft-editor {\n height: 6rem;\n min-height: 6rem;\n border: 1px solid var(--comments-border);\n border-radius: 0.5rem;\n color: var(--comments-fg);\n background: var(--comments-bg);\n overflow: hidden;\n}\n\n.comment-draft-editor .cm-content {\n box-sizing: border-box;\n min-height: 100%;\n}\n\n.comment-draft-editor .cm-scroller {\n height: 100%;\n}\n\n.comment-draft-editor:focus-within {\n outline: 2px solid var(--comments-accent);\n outline-offset: 1px;\n}\n\n.comment-btn {\n padding: 0.25rem 0.5rem;\n border-radius: 0.25rem;\n font-family: inherit;\n font-size: 0.875rem;\n cursor: pointer;\n border: none;\n background: transparent;\n color: var(--comments-fg);\n}\n\n.comment-btn:hover {\n background: var(--comments-hover-bg);\n}"));
7
+ document.head.appendChild(elementStyle);
8
+ }
9
+ } catch (e) {
10
+ console.error("vite-plugin-css-injected-by-js", e);
11
+ }
12
+ })();
13
+ import { insert, createComponent, getOwner, effect, setAttribute, template, delegateEvents, className, memo, use, render } from "solid-js/web";
14
+ import { onCleanup, createMemo, createContext, useContext, createResource, createEffect, createSignal, onMount, Show, For } from "solid-js";
15
+ import { createStore, reconcile, produce } from "solid-js/store";
16
+ import { applyPatches } from "@automerge/automerge/slim";
17
+ import "@automerge/automerge-repo";
18
+ import { c as createDocumentThread, a as createReply } from "./comments-D9lwpHOh.js";
19
+ import { StateField, Prec, Annotation } from "@codemirror/state";
20
+ import { EditorView, keymap, Decoration, WidgetType, ViewPlugin } from "@codemirror/view";
21
+ import * as am from "@automerge/automerge";
22
+ function getDefaultExportFromCjs(x) {
23
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, "default") ? x["default"] : x;
24
+ }
25
+ var browser = { exports: {} };
26
+ var ms;
27
+ var hasRequiredMs;
28
+ function requireMs() {
29
+ if (hasRequiredMs) return ms;
30
+ hasRequiredMs = 1;
31
+ var s = 1e3;
32
+ var m = s * 60;
33
+ var h = m * 60;
34
+ var d = h * 24;
35
+ var w = d * 7;
36
+ var y = d * 365.25;
37
+ ms = function(val, options) {
38
+ options = options || {};
39
+ var type = typeof val;
40
+ if (type === "string" && val.length > 0) {
41
+ return parse(val);
42
+ } else if (type === "number" && isFinite(val)) {
43
+ return options.long ? fmtLong(val) : fmtShort(val);
44
+ }
45
+ throw new Error(
46
+ "val is not a non-empty string or a valid number. val=" + JSON.stringify(val)
47
+ );
48
+ };
49
+ function parse(str) {
50
+ str = String(str);
51
+ if (str.length > 100) {
52
+ return;
53
+ }
54
+ var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(
55
+ str
56
+ );
57
+ if (!match) {
58
+ return;
59
+ }
60
+ var n = parseFloat(match[1]);
61
+ var type = (match[2] || "ms").toLowerCase();
62
+ switch (type) {
63
+ case "years":
64
+ case "year":
65
+ case "yrs":
66
+ case "yr":
67
+ case "y":
68
+ return n * y;
69
+ case "weeks":
70
+ case "week":
71
+ case "w":
72
+ return n * w;
73
+ case "days":
74
+ case "day":
75
+ case "d":
76
+ return n * d;
77
+ case "hours":
78
+ case "hour":
79
+ case "hrs":
80
+ case "hr":
81
+ case "h":
82
+ return n * h;
83
+ case "minutes":
84
+ case "minute":
85
+ case "mins":
86
+ case "min":
87
+ case "m":
88
+ return n * m;
89
+ case "seconds":
90
+ case "second":
91
+ case "secs":
92
+ case "sec":
93
+ case "s":
94
+ return n * s;
95
+ case "milliseconds":
96
+ case "millisecond":
97
+ case "msecs":
98
+ case "msec":
99
+ case "ms":
100
+ return n;
101
+ default:
102
+ return void 0;
103
+ }
104
+ }
105
+ function fmtShort(ms2) {
106
+ var msAbs = Math.abs(ms2);
107
+ if (msAbs >= d) {
108
+ return Math.round(ms2 / d) + "d";
109
+ }
110
+ if (msAbs >= h) {
111
+ return Math.round(ms2 / h) + "h";
112
+ }
113
+ if (msAbs >= m) {
114
+ return Math.round(ms2 / m) + "m";
115
+ }
116
+ if (msAbs >= s) {
117
+ return Math.round(ms2 / s) + "s";
118
+ }
119
+ return ms2 + "ms";
120
+ }
121
+ function fmtLong(ms2) {
122
+ var msAbs = Math.abs(ms2);
123
+ if (msAbs >= d) {
124
+ return plural(ms2, msAbs, d, "day");
125
+ }
126
+ if (msAbs >= h) {
127
+ return plural(ms2, msAbs, h, "hour");
128
+ }
129
+ if (msAbs >= m) {
130
+ return plural(ms2, msAbs, m, "minute");
131
+ }
132
+ if (msAbs >= s) {
133
+ return plural(ms2, msAbs, s, "second");
134
+ }
135
+ return ms2 + " ms";
136
+ }
137
+ function plural(ms2, msAbs, n, name) {
138
+ var isPlural = msAbs >= n * 1.5;
139
+ return Math.round(ms2 / n) + " " + name + (isPlural ? "s" : "");
140
+ }
141
+ return ms;
142
+ }
143
+ var common;
144
+ var hasRequiredCommon;
145
+ function requireCommon() {
146
+ if (hasRequiredCommon) return common;
147
+ hasRequiredCommon = 1;
148
+ function setup(env) {
149
+ createDebug.debug = createDebug;
150
+ createDebug.default = createDebug;
151
+ createDebug.coerce = coerce;
152
+ createDebug.disable = disable;
153
+ createDebug.enable = enable;
154
+ createDebug.enabled = enabled;
155
+ createDebug.humanize = requireMs();
156
+ createDebug.destroy = destroy;
157
+ Object.keys(env).forEach((key) => {
158
+ createDebug[key] = env[key];
159
+ });
160
+ createDebug.names = [];
161
+ createDebug.skips = [];
162
+ createDebug.formatters = {};
163
+ function selectColor(namespace) {
164
+ let hash = 0;
165
+ for (let i = 0; i < namespace.length; i++) {
166
+ hash = (hash << 5) - hash + namespace.charCodeAt(i);
167
+ hash |= 0;
168
+ }
169
+ return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
170
+ }
171
+ createDebug.selectColor = selectColor;
172
+ function createDebug(namespace) {
173
+ let prevTime;
174
+ let enableOverride = null;
175
+ let namespacesCache;
176
+ let enabledCache;
177
+ function debug2(...args) {
178
+ if (!debug2.enabled) {
179
+ return;
180
+ }
181
+ const self = debug2;
182
+ const curr = Number(/* @__PURE__ */ new Date());
183
+ const ms2 = curr - (prevTime || curr);
184
+ self.diff = ms2;
185
+ self.prev = prevTime;
186
+ self.curr = curr;
187
+ prevTime = curr;
188
+ args[0] = createDebug.coerce(args[0]);
189
+ if (typeof args[0] !== "string") {
190
+ args.unshift("%O");
191
+ }
192
+ let index = 0;
193
+ args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
194
+ if (match === "%%") {
195
+ return "%";
196
+ }
197
+ index++;
198
+ const formatter = createDebug.formatters[format];
199
+ if (typeof formatter === "function") {
200
+ const val = args[index];
201
+ match = formatter.call(self, val);
202
+ args.splice(index, 1);
203
+ index--;
204
+ }
205
+ return match;
206
+ });
207
+ createDebug.formatArgs.call(self, args);
208
+ const logFn = self.log || createDebug.log;
209
+ logFn.apply(self, args);
210
+ }
211
+ debug2.namespace = namespace;
212
+ debug2.useColors = createDebug.useColors();
213
+ debug2.color = createDebug.selectColor(namespace);
214
+ debug2.extend = extend;
215
+ debug2.destroy = createDebug.destroy;
216
+ Object.defineProperty(debug2, "enabled", {
217
+ enumerable: true,
218
+ configurable: false,
219
+ get: () => {
220
+ if (enableOverride !== null) {
221
+ return enableOverride;
222
+ }
223
+ if (namespacesCache !== createDebug.namespaces) {
224
+ namespacesCache = createDebug.namespaces;
225
+ enabledCache = createDebug.enabled(namespace);
226
+ }
227
+ return enabledCache;
228
+ },
229
+ set: (v) => {
230
+ enableOverride = v;
231
+ }
232
+ });
233
+ if (typeof createDebug.init === "function") {
234
+ createDebug.init(debug2);
235
+ }
236
+ return debug2;
237
+ }
238
+ function extend(namespace, delimiter) {
239
+ const newDebug = createDebug(this.namespace + (typeof delimiter === "undefined" ? ":" : delimiter) + namespace);
240
+ newDebug.log = this.log;
241
+ return newDebug;
242
+ }
243
+ function enable(namespaces) {
244
+ createDebug.save(namespaces);
245
+ createDebug.namespaces = namespaces;
246
+ createDebug.names = [];
247
+ createDebug.skips = [];
248
+ const split = (typeof namespaces === "string" ? namespaces : "").trim().replace(/\s+/g, ",").split(",").filter(Boolean);
249
+ for (const ns of split) {
250
+ if (ns[0] === "-") {
251
+ createDebug.skips.push(ns.slice(1));
252
+ } else {
253
+ createDebug.names.push(ns);
254
+ }
255
+ }
256
+ }
257
+ function matchesTemplate(search, template2) {
258
+ let searchIndex = 0;
259
+ let templateIndex = 0;
260
+ let starIndex = -1;
261
+ let matchIndex = 0;
262
+ while (searchIndex < search.length) {
263
+ if (templateIndex < template2.length && (template2[templateIndex] === search[searchIndex] || template2[templateIndex] === "*")) {
264
+ if (template2[templateIndex] === "*") {
265
+ starIndex = templateIndex;
266
+ matchIndex = searchIndex;
267
+ templateIndex++;
268
+ } else {
269
+ searchIndex++;
270
+ templateIndex++;
271
+ }
272
+ } else if (starIndex !== -1) {
273
+ templateIndex = starIndex + 1;
274
+ matchIndex++;
275
+ searchIndex = matchIndex;
276
+ } else {
277
+ return false;
278
+ }
279
+ }
280
+ while (templateIndex < template2.length && template2[templateIndex] === "*") {
281
+ templateIndex++;
282
+ }
283
+ return templateIndex === template2.length;
284
+ }
285
+ function disable() {
286
+ const namespaces = [
287
+ ...createDebug.names,
288
+ ...createDebug.skips.map((namespace) => "-" + namespace)
289
+ ].join(",");
290
+ createDebug.enable("");
291
+ return namespaces;
292
+ }
293
+ function enabled(name) {
294
+ for (const skip of createDebug.skips) {
295
+ if (matchesTemplate(name, skip)) {
296
+ return false;
297
+ }
298
+ }
299
+ for (const ns of createDebug.names) {
300
+ if (matchesTemplate(name, ns)) {
301
+ return true;
302
+ }
303
+ }
304
+ return false;
305
+ }
306
+ function coerce(val) {
307
+ if (val instanceof Error) {
308
+ return val.stack || val.message;
309
+ }
310
+ return val;
311
+ }
312
+ function destroy() {
313
+ console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
314
+ }
315
+ createDebug.enable(createDebug.load());
316
+ return createDebug;
317
+ }
318
+ common = setup;
319
+ return common;
320
+ }
321
+ var hasRequiredBrowser;
322
+ function requireBrowser() {
323
+ if (hasRequiredBrowser) return browser.exports;
324
+ hasRequiredBrowser = 1;
325
+ (function(module, exports) {
326
+ var define_process_env_default = {};
327
+ exports.formatArgs = formatArgs;
328
+ exports.save = save;
329
+ exports.load = load;
330
+ exports.useColors = useColors;
331
+ exports.storage = localstorage();
332
+ exports.destroy = /* @__PURE__ */ (() => {
333
+ let warned = false;
334
+ return () => {
335
+ if (!warned) {
336
+ warned = true;
337
+ console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.");
338
+ }
339
+ };
340
+ })();
341
+ exports.colors = [
342
+ "#0000CC",
343
+ "#0000FF",
344
+ "#0033CC",
345
+ "#0033FF",
346
+ "#0066CC",
347
+ "#0066FF",
348
+ "#0099CC",
349
+ "#0099FF",
350
+ "#00CC00",
351
+ "#00CC33",
352
+ "#00CC66",
353
+ "#00CC99",
354
+ "#00CCCC",
355
+ "#00CCFF",
356
+ "#3300CC",
357
+ "#3300FF",
358
+ "#3333CC",
359
+ "#3333FF",
360
+ "#3366CC",
361
+ "#3366FF",
362
+ "#3399CC",
363
+ "#3399FF",
364
+ "#33CC00",
365
+ "#33CC33",
366
+ "#33CC66",
367
+ "#33CC99",
368
+ "#33CCCC",
369
+ "#33CCFF",
370
+ "#6600CC",
371
+ "#6600FF",
372
+ "#6633CC",
373
+ "#6633FF",
374
+ "#66CC00",
375
+ "#66CC33",
376
+ "#9900CC",
377
+ "#9900FF",
378
+ "#9933CC",
379
+ "#9933FF",
380
+ "#99CC00",
381
+ "#99CC33",
382
+ "#CC0000",
383
+ "#CC0033",
384
+ "#CC0066",
385
+ "#CC0099",
386
+ "#CC00CC",
387
+ "#CC00FF",
388
+ "#CC3300",
389
+ "#CC3333",
390
+ "#CC3366",
391
+ "#CC3399",
392
+ "#CC33CC",
393
+ "#CC33FF",
394
+ "#CC6600",
395
+ "#CC6633",
396
+ "#CC9900",
397
+ "#CC9933",
398
+ "#CCCC00",
399
+ "#CCCC33",
400
+ "#FF0000",
401
+ "#FF0033",
402
+ "#FF0066",
403
+ "#FF0099",
404
+ "#FF00CC",
405
+ "#FF00FF",
406
+ "#FF3300",
407
+ "#FF3333",
408
+ "#FF3366",
409
+ "#FF3399",
410
+ "#FF33CC",
411
+ "#FF33FF",
412
+ "#FF6600",
413
+ "#FF6633",
414
+ "#FF9900",
415
+ "#FF9933",
416
+ "#FFCC00",
417
+ "#FFCC33"
418
+ ];
419
+ function useColors() {
420
+ if (typeof window !== "undefined" && window.process && (window.process.type === "renderer" || window.process.__nwjs)) {
421
+ return true;
422
+ }
423
+ if (typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
424
+ return false;
425
+ }
426
+ let m;
427
+ return typeof document !== "undefined" && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773
428
+ typeof window !== "undefined" && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31?
429
+ // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
430
+ typeof navigator !== "undefined" && navigator.userAgent && (m = navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)) && parseInt(m[1], 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker
431
+ typeof navigator !== "undefined" && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/);
432
+ }
433
+ function formatArgs(args) {
434
+ args[0] = (this.useColors ? "%c" : "") + this.namespace + (this.useColors ? " %c" : " ") + args[0] + (this.useColors ? "%c " : " ") + "+" + module.exports.humanize(this.diff);
435
+ if (!this.useColors) {
436
+ return;
437
+ }
438
+ const c = "color: " + this.color;
439
+ args.splice(1, 0, c, "color: inherit");
440
+ let index = 0;
441
+ let lastC = 0;
442
+ args[0].replace(/%[a-zA-Z%]/g, (match) => {
443
+ if (match === "%%") {
444
+ return;
445
+ }
446
+ index++;
447
+ if (match === "%c") {
448
+ lastC = index;
449
+ }
450
+ });
451
+ args.splice(lastC, 0, c);
452
+ }
453
+ exports.log = console.debug || console.log || (() => {
454
+ });
455
+ function save(namespaces) {
456
+ try {
457
+ if (namespaces) {
458
+ exports.storage.setItem("debug", namespaces);
459
+ } else {
460
+ exports.storage.removeItem("debug");
461
+ }
462
+ } catch (error) {
463
+ }
464
+ }
465
+ function load() {
466
+ let r;
467
+ try {
468
+ r = exports.storage.getItem("debug") || exports.storage.getItem("DEBUG");
469
+ } catch (error) {
470
+ }
471
+ if (!r && typeof process !== "undefined" && "env" in process) {
472
+ r = define_process_env_default.DEBUG;
473
+ }
474
+ return r;
475
+ }
476
+ function localstorage() {
477
+ try {
478
+ return localStorage;
479
+ } catch (error) {
480
+ }
481
+ }
482
+ module.exports = requireCommon()(exports);
483
+ const { formatters } = module.exports;
484
+ formatters.j = function(v) {
485
+ try {
486
+ return JSON.stringify(v);
487
+ } catch (error) {
488
+ return "[UnexpectedJSONParseError]: " + error.message;
489
+ }
490
+ };
491
+ })(browser, browser.exports);
492
+ return browser.exports;
493
+ }
494
+ var browserExports = requireBrowser();
495
+ const debug = /* @__PURE__ */ getDefaultExportFromCjs(browserExports);
496
+ var log = debug("cabbages");
497
+ function pojo(target) {
498
+ return typeof target == "object" && typeof target != null && Object.getPrototypeOf(target) == Object.prototype;
499
+ }
500
+ var BLOCK_MARKER = "";
501
+ function apply(target, ...rest) {
502
+ if (rest.length == 0) {
503
+ log("empty patch");
504
+ return;
505
+ }
506
+ const [path, range, val] = rest;
507
+ let p = [...path];
508
+ while (true) {
509
+ let key = p.shift();
510
+ if (!p.length) {
511
+ const RANGE_ARRAY = Array.isArray(range);
512
+ if (pojo(target) && RANGE_ARRAY && typeof key == "undefined" && typeof range[0] == "string") {
513
+ delete target[range[0]];
514
+ return;
515
+ }
516
+ if (RANGE_ARRAY || typeof range == "number") {
517
+ if (typeof key == "undefined") {
518
+ throw new Error("cant treat top level as a seq");
519
+ }
520
+ key = key;
521
+ let [start, end] = Array.isArray(range) ? range : [range, range + 1];
522
+ const ZERO_LENGTH = Array.isArray(range) && range.length == 0;
523
+ if (!ZERO_LENGTH && (start == null || end == null)) {
524
+ throw new RangeError("it's all or nothing, no half measures");
525
+ }
526
+ const DELETE = typeof val == "undefined";
527
+ const INSERT = start === end && !DELETE;
528
+ const APPEND = ZERO_LENGTH && !DELETE;
529
+ const REPLACE = !INSERT && !DELETE && !APPEND;
530
+ if (typeof target[key] == "undefined") {
531
+ if (typeof target == "string") {
532
+ return;
533
+ } else if (typeof val == "string") {
534
+ target[key] = "";
535
+ } else {
536
+ target[key] = [];
537
+ }
538
+ }
539
+ let seq = target[key];
540
+ if (Array.isArray(seq)) {
541
+ if (APPEND) {
542
+ Array.isArray(val) ? seq.push(...val) : seq.push(val);
543
+ return;
544
+ }
545
+ if (INSERT || REPLACE) {
546
+ Array.isArray(val) ? seq.splice(start, end - start, ...val) : seq.splice(start, end - start, val);
547
+ return;
548
+ }
549
+ if (DELETE) {
550
+ seq.splice(start, end - start);
551
+ return;
552
+ }
553
+ throw new Error("i don't know what happened");
554
+ }
555
+ if (typeof seq == "string") {
556
+ if (APPEND) {
557
+ target[key] = seq + val;
558
+ return;
559
+ }
560
+ if (REPLACE || INSERT) {
561
+ target[key] = seq.slice(0, start) + (typeof val == "string" ? val : val.map(
562
+ (n) => typeof n == "string" ? n : BLOCK_MARKER
563
+ ).join("")) + seq.slice(end);
564
+ return;
565
+ }
566
+ if (DELETE) {
567
+ target[key] = seq.slice(0, start) + seq.slice(end);
568
+ return;
569
+ }
570
+ throw new Error("i don't know what happened");
571
+ }
572
+ if (pojo(seq) && RANGE_ARRAY && typeof range[0] == "string") {
573
+ delete seq[range[0]];
574
+ return;
575
+ }
576
+ throw new Error("not implemented");
577
+ }
578
+ if (typeof key == "undefined") {
579
+ if (typeof range != "string") {
580
+ throw new Error(`can't index top-level map with ${range}`);
581
+ }
582
+ if (typeof val == "undefined") {
583
+ delete target[range];
584
+ } else {
585
+ target[range] = val;
586
+ }
587
+ return;
588
+ }
589
+ if (typeof target[key] == "undefined") {
590
+ target[key] = {};
591
+ }
592
+ if (RANGE_ARRAY) {
593
+ let [a, b] = range;
594
+ if (a != null && b != null) {
595
+ if (typeof val == "undefined" && a != null && b != null) {
596
+ delete target[key][a || b];
597
+ } else {
598
+ target[key][a || b] = val;
599
+ }
600
+ }
601
+ } else {
602
+ if (typeof val == "undefined") {
603
+ delete target[key][range];
604
+ } else if (typeof target[key] == "string") {
605
+ return;
606
+ } else {
607
+ target[key][range] = val;
608
+ }
609
+ }
610
+ return;
611
+ }
612
+ if (typeof key == "undefined") {
613
+ throw new Error("cant treat top level as a seq");
614
+ }
615
+ key = key;
616
+ let nextkey = p[0];
617
+ if (typeof target[key] == "undefined") {
618
+ if (typeof nextkey == "string") {
619
+ target[key] = {};
620
+ } else if (typeof nextkey == "number") {
621
+ target[key] = [];
622
+ } else {
623
+ throw new Error(`can't go down this road ${target}.${key}.${nextkey}`);
624
+ }
625
+ }
626
+ target = target[key];
627
+ }
628
+ }
629
+ function get(obj, key) {
630
+ for (let p = 0; p < key.length; p++) {
631
+ obj = obj ? obj[key[p]] : void 0;
632
+ }
633
+ return obj;
634
+ }
635
+ function fromAutomerge(autopatch, payload) {
636
+ const path = autopatch.path.slice(0, -1);
637
+ const key = autopatch.path[autopatch.path.length - 1];
638
+ switch (autopatch.action) {
639
+ case "mark":
640
+ case "unmark":
641
+ log(
642
+ `skipping ${autopatch.action} because it doesn't affect material reality`
643
+ );
644
+ return [];
645
+ case "inc":
646
+ if (payload) {
647
+ return [path, key, get(payload.patchInfo.after, autopatch.path)];
648
+ } else {
649
+ throw new Error(
650
+ `can't apply ${autopatch.action} without payload argument`
651
+ );
652
+ }
653
+ case "conflict":
654
+ if (payload) {
655
+ return [path, key, get(payload.patchInfo.after, autopatch.path)];
656
+ } else {
657
+ throw new Error(
658
+ `can't apply ${autopatch.action} without payload argument`
659
+ );
660
+ }
661
+ case "del": {
662
+ return typeof key == "string" ? [path, key] : [path, [key, key + (autopatch.length || 1)]];
663
+ }
664
+ case "insert": {
665
+ return [path, [key, key], autopatch.values];
666
+ }
667
+ case "splice": {
668
+ return [path, [key, key], [autopatch.value]];
669
+ }
670
+ case "put": {
671
+ return [path, key, autopatch.value];
672
+ }
673
+ }
674
+ }
675
+ function autoproduce$1(payload) {
676
+ return (doc) => {
677
+ for (let patch of payload.patches) {
678
+ apply(doc, ...fromAutomerge(patch, payload));
679
+ }
680
+ };
681
+ }
682
+ const cache$1 = /* @__PURE__ */ new WeakMap();
683
+ function makeDocumentProjection$1(handle) {
684
+ onCleanup(() => {
685
+ const item = cache$1.get(handle);
686
+ if (!item) return;
687
+ if (!item.refs--) {
688
+ item.cleanup();
689
+ }
690
+ });
691
+ if (cache$1.has(handle)) {
692
+ const item = cache$1.get(handle);
693
+ item.refs++;
694
+ return item.store;
695
+ }
696
+ const [doc, set] = createStore(handle.doc());
697
+ cache$1.set(handle, {
698
+ refs: 0,
699
+ store: doc,
700
+ cleanup() {
701
+ handle.off("change", patch);
702
+ handle.off("delete", ondelete);
703
+ cache$1.delete(handle);
704
+ }
705
+ });
706
+ function patch(payload) {
707
+ if (payload.scopeReplaced) {
708
+ set(reconcile(payload.doc ?? {}));
709
+ return;
710
+ }
711
+ set(produce(autoproduce$1(payload)));
712
+ }
713
+ function ondelete() {
714
+ set(reconcile({}));
715
+ }
716
+ handle.on("change", patch);
717
+ handle.on("delete", ondelete);
718
+ handle.whenReady().then(() => {
719
+ set(handle.doc());
720
+ });
721
+ return doc;
722
+ }
723
+ const access = (v) => typeof v === "function" && !v.length ? v() : v;
724
+ function createDocumentProjection$1(handle) {
725
+ const projection = createMemo(
726
+ () => access(handle) && makeDocumentProjection$1(access(handle))
727
+ );
728
+ return projection;
729
+ }
730
+ const RepoContext = createContext(
731
+ null
732
+ );
733
+ function useDocHandle(url, options) {
734
+ const contextRepo = useContext(RepoContext);
735
+ if (!options?.repo && !contextRepo) {
736
+ throw new Error("use outside <RepoContext> requires options.repo");
737
+ }
738
+ const repo = options?.repo || contextRepo;
739
+ function getExistingHandle() {
740
+ if (options?.["~skipInitialValue"]) return void 0;
741
+ const unwrappedURL = access(url);
742
+ if (!unwrappedURL) return void 0;
743
+ const state = repo.findWithProgress(unwrappedURL).peek();
744
+ return state.state === "ready" ? state.handle : void 0;
745
+ }
746
+ const [handle, { mutate }] = createResource(
747
+ url,
748
+ (url2) => repo.findWithProgress(url2).whenReady(),
749
+ {
750
+ initialValue: getExistingHandle()
751
+ }
752
+ );
753
+ createEffect(() => {
754
+ if (!access(url)) {
755
+ mutate();
756
+ }
757
+ });
758
+ return handle;
759
+ }
760
+ function useDocument(url, options) {
761
+ const handle = useDocHandle(url, options);
762
+ return [createDocumentProjection$1(handle), handle];
763
+ }
764
+ function useRepo() {
765
+ const repo = useContext(RepoContext);
766
+ if (!repo) throw new Error("Please wrap me in a <RepoContext value={repo}>");
767
+ return repo;
768
+ }
769
+ const STYLE = `
770
+ @layer cutetxt {
771
+ .cutetxt-tool,
772
+ .cutetxt-editor,
773
+ .cutetxt-rendered,
774
+ .cutetxt-autocomplete {
775
+ --cutetxt-bg: Canvas;
776
+ --cutetxt-fg: currentColor;
777
+ --cutetxt-transparent: transparent;
778
+ --cutetxt-link: var(--link, currentColor);
779
+ --cutetxt-code-bg: color-mix(in srgb, var(--cutetxt-fg) 10%, var(--cutetxt-transparent));
780
+ --cutetxt-spoiler-bg: currentColor;
781
+ --cutetxt-spoiler-revealed-bg: color-mix(in srgb, var(--cutetxt-fg) 12%, var(--cutetxt-transparent));
782
+ --cutetxt-embed-bg: var(--cutetxt-bg);
783
+ --cutetxt-embed-border: color-mix(in srgb, var(--cutetxt-fg) 18%, var(--cutetxt-transparent));
784
+ --cutetxt-embed-bar-bg: color-mix(in srgb, var(--cutetxt-fg) 5%, var(--cutetxt-bg));
785
+ --cutetxt-embed-bar-border: color-mix(in srgb, var(--cutetxt-fg) 12%, var(--cutetxt-transparent));
786
+ --cutetxt-embed-bar-fg: color-mix(in srgb, var(--cutetxt-fg) 72%, var(--cutetxt-transparent));
787
+ --cutetxt-embed-handle: color-mix(in srgb, var(--cutetxt-fg) 38%, var(--cutetxt-transparent));
788
+ --cutetxt-embed-handle-active: color-mix(in srgb, var(--cutetxt-fg) 62%, var(--cutetxt-transparent));
789
+ --cutetxt-control-bg: color-mix(in srgb, var(--cutetxt-bg) 85%, var(--cutetxt-fg) 4%);
790
+ --cutetxt-control-border: color-mix(in srgb, var(--cutetxt-fg) 20%, var(--cutetxt-transparent));
791
+ --cutetxt-control-fg: color-mix(in srgb, var(--cutetxt-fg) 70%, var(--cutetxt-transparent));
792
+ --cutetxt-menu-bg: var(--cutetxt-bg);
793
+ --cutetxt-menu-border: color-mix(in srgb, var(--cutetxt-fg) 15%, var(--cutetxt-transparent));
794
+ --cutetxt-menu-shadow: color-mix(in srgb, var(--cutetxt-fg) 20%, var(--cutetxt-transparent));
795
+ --cutetxt-hover-bg: color-mix(in srgb, var(--cutetxt-fg) 10%, var(--cutetxt-transparent));
796
+ --cutetxt-codeblock-bg: color-mix(in srgb, var(--cutetxt-fg) 8%, var(--cutetxt-transparent));
797
+ --cutetxt-think-border: color-mix(in srgb, var(--cutetxt-fg) 25%, var(--cutetxt-transparent));
798
+ --cutetxt-think-fg: color-mix(in srgb, var(--cutetxt-fg) 65%, var(--cutetxt-transparent));
799
+ --cutetxt-popup-shadow: color-mix(in srgb, var(--cutetxt-fg) 25%, var(--cutetxt-transparent));
800
+ --cutetxt-active-bg: color-mix(in srgb, var(--cutetxt-fg) 12%, var(--cutetxt-transparent));
801
+ }
802
+ .cutetxt-tool { display: block; height: 100%; }
803
+ .cutetxt-editor { height: 100%; }
804
+
805
+ .cutetxt-editor .cm-editor { height: 100%; }
806
+ .cutetxt-editor .cm-content {
807
+ font: 15px/1.55 system-ui, sans-serif;
808
+ padding: 12px 14px;
809
+ caret-color: currentColor;
810
+ }
811
+ .cutetxt-editor .cm-editor.cm-focused { outline: none; }
812
+ .cutetxt-editor .cm-scroller { font-family: inherit; }
813
+
814
+ /* Marks rendered by tagName (strong/em/u/s) inherit browser defaults; these two
815
+ need classes. */
816
+ .cutetxt-code {
817
+ font-family: ui-monospace, monospace;
818
+ font-size: 0.9em;
819
+ background: var(--cutetxt-code-bg);
820
+ padding: 0.1em 0.3em;
821
+ border-radius: 4px;
822
+ }
823
+ .cutetxt-spoiler {
824
+ background: var(--cutetxt-spoiler-bg);
825
+ border-radius: 3px;
826
+ transition: color 0.1s;
827
+ cursor: pointer;
828
+ }
829
+ .cutetxt-spoiler:not(.cutetxt-spoiler--revealed) {
830
+ color: transparent;
831
+ }
832
+ .cutetxt-spoiler--revealed { background: var(--cutetxt-spoiler-revealed-bg); }
833
+
834
+ .cutetxt-underline-em { text-decoration: underline; font-style: italic; }
835
+ .cutetxt-inverted { filter: invert(1); }
836
+ .cutetxt-editor a, .cutetxt-rendered a { color: var(--cutetxt-link); }
837
+
838
+ /* Block atoms: embeds, code fences, think blocks. */
839
+ .cutetxt-block {
840
+ display: block;
841
+ margin: 6px 0;
842
+ }
843
+ .cutetxt-block { position: relative; }
844
+ .cutetxt-block > patchwork-view,
845
+ .cutetxt-block > img { display: block; max-width: 100%; }
846
+
847
+ .cutetxt-embed {
848
+ box-sizing: border-box;
849
+ display: flex;
850
+ flex-direction: column;
851
+ width: min(100%, 640px);
852
+ min-width: 160px;
853
+ min-height: 120px;
854
+ max-width: 100%;
855
+ margin-right: 2px;
856
+ margin-bottom: 2px;
857
+ overflow: visible;
858
+ border: 1px solid var(--cutetxt-embed-border);
859
+ border-radius: 7px;
860
+ background: var(--cutetxt-embed-bg);
861
+ }
862
+ .cutetxt-embed-bar {
863
+ box-sizing: border-box;
864
+ display: flex;
865
+ align-items: center;
866
+ gap: 8px;
867
+ min-height: 27px;
868
+ padding: 3px 5px 3px 9px;
869
+ border-bottom: 1px solid var(--cutetxt-embed-bar-border);
870
+ background: var(--cutetxt-embed-bar-bg);
871
+ color: var(--cutetxt-embed-bar-fg);
872
+ font: 12px/1.3 system-ui, sans-serif;
873
+ user-select: none;
874
+ cursor: text;
875
+ }
876
+ .cutetxt-embed-title {
877
+ min-width: 0;
878
+ flex: 1 1 auto;
879
+ overflow: hidden;
880
+ text-overflow: ellipsis;
881
+ white-space: nowrap;
882
+ }
883
+ .cutetxt-embed-actions {
884
+ display: flex;
885
+ gap: 6px;
886
+ align-items: center;
887
+ margin-left: auto;
888
+ padding-right: 7px;
889
+ flex: 0 0 auto;
890
+ min-width: max-content;
891
+ }
892
+ .cutetxt-embed-icon {
893
+ box-sizing: border-box;
894
+ min-width: 22px;
895
+ height: 22px;
896
+ padding: 0 6px;
897
+ border: 1px solid var(--cutetxt-control-border);
898
+ border-radius: 999px;
899
+ background: var(--cutetxt-control-bg);
900
+ color: var(--cutetxt-control-fg);
901
+ cursor: pointer;
902
+ font: 11px/1 system-ui, sans-serif;
903
+ }
904
+ .cutetxt-embed-icon:hover,
905
+ .cutetxt-embed-icon:focus {
906
+ color: var(--cutetxt-fg);
907
+ }
908
+ .cutetxt-embed-delete {
909
+ position: relative;
910
+ right: -7px;
911
+ width: 22px;
912
+ min-width: 22px;
913
+ padding: 0;
914
+ border-radius: 999px;
915
+ font-weight: 600;
916
+ }
917
+ .cutetxt-embed-body {
918
+ min-height: 88px;
919
+ flex: 1 1 auto;
920
+ overflow: auto;
921
+ border-radius: 0 0 7px 7px;
922
+ }
923
+ .cutetxt-embed-body > patchwork-view,
924
+ .cutetxt-embed-body > img {
925
+ display: block;
926
+ max-width: 100%;
927
+ }
928
+ .cutetxt-embed-resize {
929
+ position: absolute;
930
+ right: -5px;
931
+ bottom: -5px;
932
+ width: 16px;
933
+ height: 16px;
934
+ cursor: nwse-resize;
935
+ touch-action: none;
936
+ }
937
+ .cutetxt-embed-resize::before {
938
+ content: "";
939
+ position: absolute;
940
+ right: 3px;
941
+ bottom: 3px;
942
+ width: 9px;
943
+ height: 9px;
944
+ border-right: 3px solid var(--cutetxt-embed-handle);
945
+ border-bottom: 3px solid var(--cutetxt-embed-handle);
946
+ border-radius: 0 0 2px 0;
947
+ }
948
+ .cutetxt-embed:hover .cutetxt-embed-resize::before,
949
+ .cutetxt-embed.is-resizing .cutetxt-embed-resize::before {
950
+ border-color: var(--cutetxt-embed-handle-active);
951
+ }
952
+ .cutetxt-embed.is-resizing {
953
+ user-select: none;
954
+ }
955
+
956
+ /* Tool-picker on embed widgets: a small "▾ tool" control + dropdown menu. */
957
+ .cutetxt-embed-picker { position: relative; z-index: 2; flex: none; }
958
+ .cutetxt-embed-tool {
959
+ font: 11px/1.4 system-ui, sans-serif;
960
+ padding: 2px 6px;
961
+ max-width: 88px;
962
+ overflow: hidden;
963
+ text-overflow: ellipsis;
964
+ white-space: nowrap;
965
+ border-radius: 5px;
966
+ border: 1px solid var(--cutetxt-control-border);
967
+ background: var(--cutetxt-control-bg);
968
+ color: var(--cutetxt-control-fg);
969
+ cursor: pointer;
970
+ opacity: 0.75;
971
+ transition: opacity 0.1s;
972
+ }
973
+ .cutetxt-embed:hover .cutetxt-embed-tool,
974
+ .cutetxt-embed-tool:focus { opacity: 1; }
975
+ .cutetxt-embed-menu {
976
+ position: absolute;
977
+ top: calc(100% + 3px);
978
+ right: 0;
979
+ min-width: 120px;
980
+ padding: 4px;
981
+ border-radius: 7px;
982
+ border: 1px solid var(--cutetxt-menu-border);
983
+ background: var(--cutetxt-menu-bg);
984
+ box-shadow: 0 4px 16px var(--cutetxt-menu-shadow);
985
+ display: flex;
986
+ flex-direction: column;
987
+ gap: 1px;
988
+ font: 12px/1.4 system-ui, sans-serif;
989
+ }
990
+ .cutetxt-embed-menu-item {
991
+ text-align: left;
992
+ padding: 4px 8px;
993
+ border: none;
994
+ border-radius: 4px;
995
+ background: none;
996
+ color: inherit;
997
+ cursor: pointer;
998
+ }
999
+ .cutetxt-embed-menu-item:hover { background: var(--cutetxt-hover-bg); }
1000
+ .cutetxt-embed-menu-item.is-current { font-weight: 600; }
1001
+
1002
+ .cutetxt-codeblock {
1003
+ margin: 0;
1004
+ padding: 10px 12px;
1005
+ border-radius: 8px;
1006
+ background: var(--cutetxt-codeblock-bg);
1007
+ overflow-x: auto;
1008
+ font: 13px/1.5 ui-monospace, monospace;
1009
+ }
1010
+
1011
+ .cutetxt-think {
1012
+ padding: 8px 12px;
1013
+ border-left: 3px solid var(--cutetxt-think-border);
1014
+ color: var(--cutetxt-think-fg);
1015
+ font-style: italic;
1016
+ white-space: pre-wrap;
1017
+ }
1018
+
1019
+ /* Inline replaces: emoji, etc. */
1020
+ .cutetxt-replace { display: inline; }
1021
+ .cutetxt-emoji { font-style: normal; }
1022
+
1023
+ /* Autocomplete popup (appended to <body>, positioned fixed at the caret). */
1024
+ .cutetxt-autocomplete {
1025
+ position: fixed;
1026
+ z-index: 30;
1027
+ min-width: 150px;
1028
+ max-height: 220px;
1029
+ overflow-y: auto;
1030
+ padding: 4px;
1031
+ border-radius: 8px;
1032
+ border: 1px solid var(--cutetxt-menu-border);
1033
+ background: var(--cutetxt-menu-bg);
1034
+ box-shadow: 0 6px 20px var(--cutetxt-popup-shadow);
1035
+ font: 13px/1.4 system-ui, sans-serif;
1036
+ }
1037
+ .cutetxt-ac-item {
1038
+ display: flex;
1039
+ gap: 8px;
1040
+ align-items: center;
1041
+ padding: 4px 8px;
1042
+ border-radius: 5px;
1043
+ cursor: pointer;
1044
+ }
1045
+ .cutetxt-ac-item.is-active { background: var(--cutetxt-active-bg); }
1046
+ .cutetxt-ac-icon { width: 1.2em; height: 1.2em; object-fit: contain; text-align: center; flex: none; }
1047
+ .cutetxt-ac-desc { opacity: 0.7; }
1048
+ .cutetxt-ac-detail { margin-left: auto; opacity: 0.7; }
1049
+ }
1050
+ `;
1051
+ function autoproduce(payload) {
1052
+ return (doc) => applyPatches(doc, payload.patches);
1053
+ }
1054
+ var cache = /* @__PURE__ */ new WeakMap();
1055
+ function initial(handle) {
1056
+ const template2 = {};
1057
+ applyPatches(template2, handle.diff([], handle.heads()));
1058
+ return template2;
1059
+ }
1060
+ function makeDocumentProjection(handle) {
1061
+ onCleanup(() => {
1062
+ const item = cache.get(handle);
1063
+ if (!item) return;
1064
+ if (!item.refs--) item.cleanup();
1065
+ });
1066
+ if (cache.has(handle)) {
1067
+ const item = cache.get(handle);
1068
+ item.refs++;
1069
+ return item.store;
1070
+ }
1071
+ const [doc, set] = createStore(initial(handle));
1072
+ cache.set(handle, {
1073
+ refs: 0,
1074
+ store: doc,
1075
+ cleanup() {
1076
+ handle.off("change", patch);
1077
+ handle.off("delete", ondelete);
1078
+ cache.delete(handle);
1079
+ }
1080
+ });
1081
+ function patch(payload) {
1082
+ if (payload.scopeReplaced) {
1083
+ set(reconcile(payload.doc ?? {}));
1084
+ return;
1085
+ }
1086
+ set(produce(autoproduce(payload)));
1087
+ }
1088
+ function ondelete() {
1089
+ set(reconcile({}));
1090
+ }
1091
+ handle.on("change", patch);
1092
+ handle.on("delete", ondelete);
1093
+ return doc;
1094
+ }
1095
+ function createDocumentProjection(handle) {
1096
+ return createMemo(() => {
1097
+ const unwrappedHandle = typeof handle == "function" ? handle() : handle;
1098
+ return unwrappedHandle && makeDocumentProjection(unwrappedHandle);
1099
+ });
1100
+ }
1101
+ createContext(null);
1102
+ function subscribe$2(element, selector, listener) {
1103
+ const view = element.closest("patchwork-view");
1104
+ const dispatchEl = view ?? element;
1105
+ const channel = new MessageChannel();
1106
+ const port = channel.port2;
1107
+ const controller = new AbortController();
1108
+ const { signal } = controller;
1109
+ port.addEventListener("message", (event) => {
1110
+ if (event.data?.type === "change")
1111
+ listener(event.data.value);
1112
+ }, { signal });
1113
+ port.start();
1114
+ const detail = {
1115
+ selector,
1116
+ port: channel.port1
1117
+ };
1118
+ dispatchEl.dispatchEvent(new CustomEvent("patchwork:subscribe", {
1119
+ detail,
1120
+ bubbles: true,
1121
+ composed: true
1122
+ }));
1123
+ return () => {
1124
+ if (signal.aborted)
1125
+ return;
1126
+ controller.abort();
1127
+ port.postMessage({ type: "unsubscribe" });
1128
+ port.close();
1129
+ };
1130
+ }
1131
+ function resolveElement(source) {
1132
+ return typeof source === "function" ? source() : source;
1133
+ }
1134
+ function subscribe$1(element, selector, initialValue) {
1135
+ const [value, setValue] = createSignal(initialValue);
1136
+ onMount(() => {
1137
+ const target = resolveElement(element);
1138
+ if (!target)
1139
+ return;
1140
+ const unsubscribe = subscribe$2(target, selector, setValue);
1141
+ onCleanup(unsubscribe);
1142
+ });
1143
+ return value;
1144
+ }
1145
+ function subscribeDoc(element, selector) {
1146
+ const [handle, setHandle] = createSignal(void 0);
1147
+ onMount(() => {
1148
+ const target = resolveElement(element);
1149
+ if (!target)
1150
+ return;
1151
+ let canceled = false;
1152
+ const unsubscribe = subscribe$2(target, selector, (url) => {
1153
+ if (!url)
1154
+ return;
1155
+ const repo = "repo" in window ? window.repo : void 0;
1156
+ if (!repo)
1157
+ return;
1158
+ void Promise.resolve(repo.find(url)).then((h) => {
1159
+ if (canceled)
1160
+ return;
1161
+ setHandle(() => h);
1162
+ });
1163
+ });
1164
+ onCleanup(() => {
1165
+ canceled = true;
1166
+ unsubscribe();
1167
+ });
1168
+ });
1169
+ const doc = createDocumentProjection(handle);
1170
+ return [doc, handle];
1171
+ }
1172
+ function subscribe(element, selector, listener) {
1173
+ const view = element.closest("patchwork-view");
1174
+ const dispatchEl = view ?? element;
1175
+ const channel = new MessageChannel();
1176
+ const port = channel.port2;
1177
+ const controller = new AbortController();
1178
+ const { signal } = controller;
1179
+ port.addEventListener("message", (event) => {
1180
+ if (event.data?.type === "change")
1181
+ listener(event.data.value);
1182
+ }, { signal });
1183
+ port.start();
1184
+ const detail = {
1185
+ selector,
1186
+ port: channel.port1
1187
+ };
1188
+ dispatchEl.dispatchEvent(new CustomEvent("patchwork:subscribe", {
1189
+ detail,
1190
+ bubbles: true,
1191
+ composed: true
1192
+ }));
1193
+ return () => {
1194
+ if (signal.aborted)
1195
+ return;
1196
+ controller.abort();
1197
+ port.postMessage({ type: "unsubscribe" });
1198
+ port.close();
1199
+ };
1200
+ }
1201
+ var _tmpl$$1 = /* @__PURE__ */ template(`<button class=comment-btn title="Comment on this document">Add comment`), _tmpl$2$1 = /* @__PURE__ */ template(`<div class=comments-panel><div class=comments-panel-header><span class=comments-panel-header-title>Comments`), _tmpl$3$1 = /* @__PURE__ */ template(`<div class=comments-empty>No comments yet`), _tmpl$4$1 = /* @__PURE__ */ template(`<div class=comments-thread><patchwork-view tool-id=comment-thread>`, true, false, false);
1202
+ function CommentsView(props) {
1203
+ const repo = useRepo();
1204
+ const selectedDocUrls = subscribe$1(props.element, {
1205
+ type: "patchwork:selected-doc"
1206
+ }, []);
1207
+ const targetDocUrl = () => selectedDocUrls()[0];
1208
+ const commentEntries = useScopedCommentEntries(props.element, targetDocUrl);
1209
+ const [focusDoc, focusHandle] = subscribeDoc(props.element, {
1210
+ type: "patchwork:focus"
1211
+ });
1212
+ const [, contactHandle] = subscribeDoc(props.element, {
1213
+ type: "patchwork:contact"
1214
+ });
1215
+ const currentContactUrl = () => contactHandle()?.url;
1216
+ const threadUrls = createMemo(() => {
1217
+ const entries = commentEntries();
1218
+ const threadUrls2 = /* @__PURE__ */ new Set();
1219
+ for (const {
1220
+ threadUrl
1221
+ } of entries) {
1222
+ threadUrls2.add(threadUrl);
1223
+ }
1224
+ return Array.from(threadUrls2);
1225
+ });
1226
+ const threadTargetUrlMap = createMemo(() => {
1227
+ const entries = commentEntries();
1228
+ const map = /* @__PURE__ */ new Map();
1229
+ for (const {
1230
+ targetUrl,
1231
+ threadUrl
1232
+ } of entries) {
1233
+ const existing = map.get(threadUrl);
1234
+ if (existing) {
1235
+ if (!existing.includes(targetUrl)) existing.push(targetUrl);
1236
+ } else {
1237
+ map.set(threadUrl, [targetUrl]);
1238
+ }
1239
+ }
1240
+ return map;
1241
+ });
1242
+ const selectedHandles = useResolvedHandles(() => Object.keys(focusDoc()?.selection ?? {}), repo);
1243
+ const threadTargetHandleMap = useResolvedHandleMap(threadTargetUrlMap, repo);
1244
+ const threadsWithVisibleTarget = useThreadsWithVisibleTarget(threadTargetUrlMap, repo);
1245
+ const renderableThreadUrls = createMemo(() => threadUrls().filter((url) => threadsWithVisibleTarget().has(url)));
1246
+ const docLevelThreadUrls = createMemo(() => {
1247
+ const map = threadTargetUrlMap();
1248
+ const set = /* @__PURE__ */ new Set();
1249
+ for (const [threadUrl, targets] of map) {
1250
+ if (targets.length > 0 && targets.every((t) => !t.includes("/") && !t.includes("#"))) {
1251
+ set.add(threadUrl);
1252
+ }
1253
+ }
1254
+ return set;
1255
+ });
1256
+ const displayedThreadUrls = createMemo(() => threadUrls().filter((url) => threadsWithVisibleTarget().has(url) || docLevelThreadUrls().has(url)));
1257
+ const overlappingThreads = createMemo(() => renderableThreadUrls().filter((url) => threadOverlapsSelection(threadTargetHandleMap().get(url) ?? [], selectedHandles())));
1258
+ const [pinnedThread, setPinnedThread] = createSignal();
1259
+ const primaryThreadUrl = createMemo(() => {
1260
+ const pinned = pinnedThread();
1261
+ if (pinned && docLevelThreadUrls().has(pinned)) return pinned;
1262
+ const overlaps = overlappingThreads();
1263
+ if (overlaps.length === 0) return void 0;
1264
+ if (pinned && overlaps.includes(pinned)) return pinned;
1265
+ return overlaps[0];
1266
+ });
1267
+ const secondaryThreadUrls = createMemo(() => {
1268
+ const p = primaryThreadUrl();
1269
+ return new Set(overlappingThreads().filter((u) => u !== p));
1270
+ });
1271
+ createEffect(() => {
1272
+ const handle = focusHandle();
1273
+ if (!handle) return;
1274
+ const p = primaryThreadUrl();
1275
+ const urls = p ? threadTargetUrlMap().get(p) ?? [] : [];
1276
+ const desired = {};
1277
+ for (const u of urls) desired[u] = true;
1278
+ handle.change((doc) => {
1279
+ doc.highlight = desired;
1280
+ });
1281
+ });
1282
+ const onSelectThread = (threadUrl, targetUrls) => {
1283
+ const handle = focusHandle();
1284
+ if (!handle) return;
1285
+ const wasPrimary = primaryThreadUrl() === threadUrl;
1286
+ setPinnedThread(wasPrimary ? void 0 : threadUrl);
1287
+ const next = {};
1288
+ if (!wasPrimary) for (const u of targetUrls) next[u] = true;
1289
+ handle.change((doc) => {
1290
+ doc.selection = next;
1291
+ });
1292
+ };
1293
+ const onClickThreadCard = (e, threadUrl) => {
1294
+ const target = e.target;
1295
+ if (target?.closest("textarea, button, input, a, select, .cm-editor")) return;
1296
+ onSelectThread(threadUrl, threadTargetUrlMap().get(threadUrl) ?? []);
1297
+ };
1298
+ const threadState = (threadUrl) => primaryThreadUrl() === threadUrl ? "primary" : secondaryThreadUrls().has(threadUrl) ? "secondary" : "inactive";
1299
+ onCleanup(() => {
1300
+ const handle = focusHandle();
1301
+ if (!handle) return;
1302
+ handle.change((doc) => {
1303
+ doc.highlight = {};
1304
+ });
1305
+ });
1306
+ const canAddComment = () => Boolean(targetDocUrl()) && Boolean(currentContactUrl());
1307
+ const onAddComment = async () => {
1308
+ const url = targetDocUrl();
1309
+ const contactUrl = currentContactUrl();
1310
+ if (!url || !contactUrl) return;
1311
+ const handle = await repo.find(url);
1312
+ createDocumentThread({
1313
+ docHandle: handle,
1314
+ contactUrl
1315
+ });
1316
+ };
1317
+ return (() => {
1318
+ var _el$ = _tmpl$2$1(), _el$2 = _el$.firstChild;
1319
+ _el$2.firstChild;
1320
+ insert(_el$2, createComponent(Show, {
1321
+ get when() {
1322
+ return canAddComment();
1323
+ },
1324
+ get children() {
1325
+ var _el$4 = _tmpl$$1();
1326
+ _el$4.$$click = onAddComment;
1327
+ return _el$4;
1328
+ }
1329
+ }), null);
1330
+ insert(_el$, createComponent(Show, {
1331
+ get when() {
1332
+ return displayedThreadUrls().length > 0;
1333
+ },
1334
+ get fallback() {
1335
+ return _tmpl$3$1();
1336
+ },
1337
+ get children() {
1338
+ return createComponent(For, {
1339
+ get each() {
1340
+ return displayedThreadUrls();
1341
+ },
1342
+ children: (threadUrl) => (() => {
1343
+ var _el$6 = _tmpl$4$1(), _el$7 = _el$6.firstChild;
1344
+ _el$6.$$click = (e) => onClickThreadCard(e, threadUrl);
1345
+ _el$7.docUrl = threadUrl;
1346
+ _el$7._$owner = getOwner();
1347
+ effect(() => setAttribute(_el$7, "data-thread-state", threadState(threadUrl)));
1348
+ return _el$6;
1349
+ })()
1350
+ });
1351
+ }
1352
+ }), null);
1353
+ return _el$;
1354
+ })();
1355
+ }
1356
+ function useScopedCommentEntries(element, docUrl) {
1357
+ const [entries, setEntries] = createSignal([]);
1358
+ createEffect(() => {
1359
+ const url = docUrl();
1360
+ setEntries([]);
1361
+ if (!url) return;
1362
+ const unsubscribe = subscribe(element, {
1363
+ type: "patchwork:comments",
1364
+ url
1365
+ }, (value) => setEntries(value ?? []));
1366
+ onCleanup(unsubscribe);
1367
+ });
1368
+ return entries;
1369
+ }
1370
+ function useResolvedHandles(urls, repo) {
1371
+ const [handles, setHandles] = createSignal([]);
1372
+ createEffect(() => {
1373
+ const list = urls();
1374
+ let cancelled = false;
1375
+ Promise.all(list.map((u) => repo.find(u).catch(() => void 0))).then((resolved) => {
1376
+ if (cancelled) return;
1377
+ setHandles(resolved.filter((h) => Boolean(h)));
1378
+ });
1379
+ onCleanup(() => {
1380
+ cancelled = true;
1381
+ });
1382
+ });
1383
+ return handles;
1384
+ }
1385
+ function useResolvedHandleMap(map, repo) {
1386
+ const [resolved, setResolved] = createSignal(/* @__PURE__ */ new Map());
1387
+ createEffect(() => {
1388
+ const m = map();
1389
+ let cancelled = false;
1390
+ void (async () => {
1391
+ const out = /* @__PURE__ */ new Map();
1392
+ for (const [key, urls] of m) {
1393
+ const handles = await Promise.all(urls.map((u) => repo.find(u).catch(() => void 0)));
1394
+ out.set(key, handles.filter((h) => Boolean(h)));
1395
+ }
1396
+ if (!cancelled) setResolved(out);
1397
+ })();
1398
+ onCleanup(() => {
1399
+ cancelled = true;
1400
+ });
1401
+ });
1402
+ return resolved;
1403
+ }
1404
+ function useThreadsWithVisibleTarget(map, repo) {
1405
+ const [visible, setVisible] = createSignal(/* @__PURE__ */ new Set());
1406
+ createEffect(() => {
1407
+ const m = map();
1408
+ let cancelled = false;
1409
+ let resolved = /* @__PURE__ */ new Map();
1410
+ const recompute = () => {
1411
+ if (cancelled) return;
1412
+ const next = /* @__PURE__ */ new Set();
1413
+ for (const [threadUrl, handles] of resolved) {
1414
+ if (handles.some(targetIsVisible)) next.add(threadUrl);
1415
+ }
1416
+ setVisible(next);
1417
+ };
1418
+ void (async () => {
1419
+ const out = /* @__PURE__ */ new Map();
1420
+ for (const [threadUrl, urls] of m) {
1421
+ const handles = await Promise.all(urls.map((u) => repo.find(u).catch(() => void 0)));
1422
+ out.set(threadUrl, handles.filter((h) => Boolean(h)));
1423
+ }
1424
+ if (cancelled) return;
1425
+ resolved = out;
1426
+ for (const h of flatHandles(resolved)) h.on("change", recompute);
1427
+ recompute();
1428
+ })();
1429
+ onCleanup(() => {
1430
+ cancelled = true;
1431
+ for (const h of flatHandles(resolved)) h.off("change", recompute);
1432
+ });
1433
+ });
1434
+ return visible;
1435
+ }
1436
+ function flatHandles(map) {
1437
+ const out = [];
1438
+ for (const handles of map.values()) out.push(...handles);
1439
+ return out;
1440
+ }
1441
+ function targetIsVisible(handle) {
1442
+ const positions = handle.rangePositions();
1443
+ if (!positions) return false;
1444
+ const [start, end] = positions;
1445
+ return start !== end;
1446
+ }
1447
+ function threadOverlapsSelection(targets, selection) {
1448
+ return targets.some((t) => selection.some((s) => s.overlaps(t)));
1449
+ }
1450
+ delegateEvents(["click"]);
1451
+ const relativeTime = (timestamp) => {
1452
+ const now = Date.now();
1453
+ const diff = now - timestamp;
1454
+ const seconds = Math.floor(diff / 1e3);
1455
+ const minutes = Math.floor(seconds / 60);
1456
+ const hours = Math.floor(minutes / 60);
1457
+ const days = Math.floor(hours / 24);
1458
+ const weeks = Math.floor(days / 7);
1459
+ const months = Math.floor(days / 30);
1460
+ const years = Math.floor(days / 365);
1461
+ if (seconds < 60) {
1462
+ return "just now";
1463
+ } else if (minutes < 60) {
1464
+ return `${minutes} minute${minutes === 1 ? "" : "s"} ago`;
1465
+ } else if (hours < 24) {
1466
+ return `${hours} hour${hours === 1 ? "" : "s"} ago`;
1467
+ } else if (days < 7) {
1468
+ return `${days} day${days === 1 ? "" : "s"} ago`;
1469
+ } else if (weeks < 4) {
1470
+ return `${weeks} week${weeks === 1 ? "" : "s"} ago`;
1471
+ } else if (months < 12) {
1472
+ return `${months} month${months === 1 ? "" : "s"} ago`;
1473
+ } else {
1474
+ return `${years} year${years === 1 ? "" : "s"} ago`;
1475
+ }
1476
+ };
1477
+ function scanFlags(re) {
1478
+ return "g" + (re.flags.includes("i") ? "i" : "");
1479
+ }
1480
+ function scanAtoms(text, specs, block, out) {
1481
+ for (const [name, spec] of Object.entries(specs || {})) {
1482
+ if (!spec.pattern)
1483
+ continue;
1484
+ const re = new RegExp(spec.pattern.source, scanFlags(spec.pattern));
1485
+ let m;
1486
+ while (m = re.exec(text)) {
1487
+ if (!m[0]) {
1488
+ re.lastIndex = m.index + 1;
1489
+ continue;
1490
+ }
1491
+ out.push({
1492
+ kind: "atom",
1493
+ name,
1494
+ spec,
1495
+ block,
1496
+ start: m.index,
1497
+ end: m.index + m[0].length,
1498
+ attrs: spec.parse ? spec.parse(m[0]) : {}
1499
+ });
1500
+ }
1501
+ }
1502
+ }
1503
+ function findMarkupRanges(text, schema) {
1504
+ const candidates = [];
1505
+ for (const [name, spec] of Object.entries(schema.marks || {})) {
1506
+ if (!spec.pattern)
1507
+ continue;
1508
+ const re = new RegExp(spec.pattern.source, scanFlags(spec.pattern));
1509
+ let m;
1510
+ while (m = re.exec(text)) {
1511
+ const inner = m[1];
1512
+ if (!inner) {
1513
+ re.lastIndex = m.index + 1;
1514
+ continue;
1515
+ }
1516
+ const start = m.index;
1517
+ const openLen = m[0].indexOf(inner);
1518
+ candidates.push({
1519
+ kind: "mark",
1520
+ name,
1521
+ spec,
1522
+ start,
1523
+ innerStart: start + openLen,
1524
+ innerEnd: start + openLen + inner.length,
1525
+ end: start + m[0].length,
1526
+ attrs: spec.parse ? spec.parse(m[0]) : void 0
1527
+ });
1528
+ }
1529
+ }
1530
+ scanAtoms(text, schema.blocks, true, candidates);
1531
+ scanAtoms(text, schema.replaces, false, candidates);
1532
+ candidates.sort((a, b) => a.start - b.start || b.end - b.start - (a.end - a.start));
1533
+ const accepted = [];
1534
+ let lastEnd = 0;
1535
+ for (const c of candidates) {
1536
+ if (c.start < lastEnd)
1537
+ continue;
1538
+ accepted.push(c);
1539
+ lastEnd = c.end;
1540
+ }
1541
+ return accepted;
1542
+ }
1543
+ function parseMarkup(text, schema) {
1544
+ const nodes = [];
1545
+ let best = null;
1546
+ const consider = (specs, kind2, block2) => {
1547
+ for (const [, spec2] of Object.entries(specs || {})) {
1548
+ if (!spec2.pattern)
1549
+ continue;
1550
+ const re = new RegExp(spec2.pattern.source, scanFlags(spec2.pattern).replace("g", ""));
1551
+ const m2 = re.exec(text);
1552
+ const ok = kind2 === "mark" ? m2 && m2[1] : m2 && m2[0];
1553
+ if (ok && (best === null || m2.index < best.m.index))
1554
+ best = { kind: kind2, spec: spec2, m: m2, block: block2 };
1555
+ }
1556
+ };
1557
+ consider(schema.marks, "mark");
1558
+ consider(schema.blocks, "atom", true);
1559
+ consider(schema.replaces, "atom", false);
1560
+ if (!best) {
1561
+ if (text)
1562
+ nodes.push({ text });
1563
+ return nodes;
1564
+ }
1565
+ const { kind, spec, m, block } = best;
1566
+ if (m.index > 0)
1567
+ nodes.push({ text: text.slice(0, m.index) });
1568
+ if (kind === "mark") {
1569
+ const inner = m[1];
1570
+ const leaf = spec.raw || inner.length >= m[0].length;
1571
+ nodes.push({
1572
+ mark: spec,
1573
+ attrs: spec.parse ? spec.parse(m[0]) : void 0,
1574
+ children: leaf ? [{ text: inner }] : parseMarkup(inner, schema)
1575
+ });
1576
+ } else {
1577
+ nodes.push({ atom: spec, attrs: spec.parse ? spec.parse(m[0]) : {}, block });
1578
+ }
1579
+ for (const n of parseMarkup(text.slice(m.index + m[0].length), schema))
1580
+ nodes.push(n);
1581
+ return nodes;
1582
+ }
1583
+ const HTML_ESCAPE = { "&": "&amp;", "<": "&lt;", ">": "&gt;", '"': "&quot;", "'": "&#39;" };
1584
+ const escapeHTML = (s) => String(s ?? "").replace(/[&<>]/g, (c) => HTML_ESCAPE[c]);
1585
+ const escapeAttr = (s) => String(s ?? "").replace(/[&<>"']/g, (c) => HTML_ESCAPE[c]);
1586
+ function embedRangeForDOM(view, dom, schema) {
1587
+ const embeds = Array.from(view.dom.querySelectorAll(".cutetxt-embed"));
1588
+ const index = embeds.indexOf(dom);
1589
+ if (index < 0)
1590
+ return null;
1591
+ return findMarkupRanges(view.state.doc.toString(), schema).filter((x) => x.kind === "atom" && x.name === "embed")[index];
1592
+ }
1593
+ function setEmbedAttrs(view, dom, schema, patch) {
1594
+ const embed = schema.blocks && schema.blocks.embed;
1595
+ if (!embed)
1596
+ return;
1597
+ const r = embedRangeForDOM(view, dom, schema);
1598
+ if (!r)
1599
+ return;
1600
+ const nextAttrs = { ...r.attrs, ...patch };
1601
+ if (Object.keys(patch).every((key) => String(r.attrs[key] || "") === String(nextAttrs[key] || "")))
1602
+ return;
1603
+ const token = embed.token(nextAttrs);
1604
+ view.dispatch({ changes: { from: r.start, to: r.end, insert: token }, userEvent: "input" });
1605
+ }
1606
+ function editEmbedToken(view, dom, schema) {
1607
+ const r = embedRangeForDOM(view, dom, schema);
1608
+ if (!r)
1609
+ return;
1610
+ view.dispatch({ selection: { anchor: r.start }, scrollIntoView: true, userEvent: "select" });
1611
+ view.focus();
1612
+ }
1613
+ function deleteEmbedToken(view, dom, schema) {
1614
+ const r = embedRangeForDOM(view, dom, schema);
1615
+ if (!r)
1616
+ return;
1617
+ const doc = view.state.doc;
1618
+ let from = r.start;
1619
+ let to = r.end;
1620
+ if (to < doc.length && doc.sliceString(to, to + 1) === "\n")
1621
+ to++;
1622
+ else if (from > 0 && doc.sliceString(from - 1, from) === "\n")
1623
+ from--;
1624
+ view.dispatch({
1625
+ changes: { from, to, insert: "" },
1626
+ selection: { anchor: from },
1627
+ userEvent: "delete"
1628
+ });
1629
+ view.focus();
1630
+ }
1631
+ function embedIconButton(label, title, onClick) {
1632
+ const button = document.createElement("button");
1633
+ button.type = "button";
1634
+ button.className = "cutetxt-embed-icon";
1635
+ button.classList.add("cutetxt-embed-delete");
1636
+ button.textContent = label;
1637
+ button.title = title;
1638
+ button.setAttribute("aria-label", title);
1639
+ button.addEventListener("mousedown", (event) => event.preventDefault());
1640
+ button.addEventListener("click", (event) => {
1641
+ event.preventDefault();
1642
+ event.stopPropagation();
1643
+ onClick();
1644
+ });
1645
+ return button;
1646
+ }
1647
+ function buildToolPicker(view, wrap, schema, picker, attrs) {
1648
+ const holder = document.createElement("span");
1649
+ holder.className = "cutetxt-embed-picker";
1650
+ const button = document.createElement("button");
1651
+ button.type = "button";
1652
+ button.className = "cutetxt-embed-tool";
1653
+ button.textContent = (attrs.toolId || "tool") + " ▾";
1654
+ holder.append(button);
1655
+ let menu = null;
1656
+ const closeMenu = () => {
1657
+ if (!menu)
1658
+ return;
1659
+ menu.remove();
1660
+ menu = null;
1661
+ document.removeEventListener("mousedown", onDocDown, true);
1662
+ };
1663
+ const onDocDown = (event) => {
1664
+ if (!holder.contains(event.target))
1665
+ closeMenu();
1666
+ };
1667
+ button.addEventListener("mousedown", (event) => event.preventDefault());
1668
+ button.addEventListener("click", async (event) => {
1669
+ event.preventDefault();
1670
+ event.stopPropagation();
1671
+ if (menu)
1672
+ return closeMenu();
1673
+ menu = document.createElement("div");
1674
+ menu.className = "cutetxt-embed-menu";
1675
+ menu.textContent = "…";
1676
+ holder.append(menu);
1677
+ document.addEventListener("mousedown", onDocDown, true);
1678
+ let tools = [];
1679
+ try {
1680
+ tools = await picker(attrs.url) || [];
1681
+ } catch {
1682
+ tools = [];
1683
+ }
1684
+ if (!menu)
1685
+ return;
1686
+ menu.textContent = "";
1687
+ if (!tools.length) {
1688
+ menu.textContent = "no tools";
1689
+ return;
1690
+ }
1691
+ for (const tool of tools) {
1692
+ const item = document.createElement("button");
1693
+ item.type = "button";
1694
+ item.className = "cutetxt-embed-menu-item";
1695
+ if (tool.id === attrs.toolId)
1696
+ item.classList.add("is-current");
1697
+ item.textContent = tool.name || tool.id;
1698
+ item.addEventListener("mousedown", (ev) => ev.preventDefault());
1699
+ item.addEventListener("click", (ev) => {
1700
+ ev.preventDefault();
1701
+ ev.stopPropagation();
1702
+ closeMenu();
1703
+ setEmbedAttrs(view, wrap, schema, { toolId: tool.id });
1704
+ });
1705
+ menu.append(item);
1706
+ }
1707
+ });
1708
+ return holder;
1709
+ }
1710
+ class AtomWidget extends WidgetType {
1711
+ spec;
1712
+ attrs;
1713
+ block;
1714
+ name;
1715
+ schema;
1716
+ picker;
1717
+ repo;
1718
+ key;
1719
+ cleanup;
1720
+ constructor(spec, attrs, block, name, schema, picker, repo) {
1721
+ super();
1722
+ this.spec = spec;
1723
+ this.attrs = attrs;
1724
+ this.block = block;
1725
+ this.name = name;
1726
+ this.schema = schema;
1727
+ this.picker = picker;
1728
+ this.repo = repo;
1729
+ const keyAttrs = name === "embed" ? { url: attrs.url, toolId: attrs.toolId } : attrs;
1730
+ this.key = (block ? "b:" : "r:") + name + ":" + (spec.token ? spec.token(keyAttrs) : JSON.stringify(keyAttrs));
1731
+ }
1732
+ eq(other) {
1733
+ return other.key === this.key;
1734
+ }
1735
+ updateDOM(dom, view, from) {
1736
+ if (this.name !== "embed" || from.name !== "embed")
1737
+ return false;
1738
+ if (this.attrs.url !== from.attrs.url || this.attrs.toolId !== from.attrs.toolId)
1739
+ return false;
1740
+ this.cleanup = from.cleanup;
1741
+ const body = dom.querySelector(".cutetxt-embed-body");
1742
+ if (this.attrs.width)
1743
+ dom.style.width = this.attrs.width + "px";
1744
+ else
1745
+ dom.style.removeProperty("width");
1746
+ if (body instanceof HTMLElement) {
1747
+ if (this.attrs.height)
1748
+ body.style.height = this.attrs.height + "px";
1749
+ else
1750
+ body.style.removeProperty("height");
1751
+ }
1752
+ return true;
1753
+ }
1754
+ toDOM(view) {
1755
+ const wrap = document.createElement(this.block ? "div" : "span");
1756
+ wrap.className = this.block ? "cutetxt-block" : "cutetxt-replace";
1757
+ wrap.contentEditable = "false";
1758
+ const node = this.spec.toDOM(this.attrs);
1759
+ if (this.name === "embed") {
1760
+ const bar = document.createElement("div");
1761
+ bar.className = "cutetxt-embed-bar";
1762
+ bar.title = "Edit embed token";
1763
+ bar.addEventListener("mousedown", (event) => {
1764
+ if (event.target.closest("button, .cutetxt-embed-menu"))
1765
+ return;
1766
+ event.preventDefault();
1767
+ });
1768
+ bar.addEventListener("click", (event) => {
1769
+ if (event.target.closest("button, .cutetxt-embed-menu"))
1770
+ return;
1771
+ event.preventDefault();
1772
+ event.stopPropagation();
1773
+ editEmbedToken(view, wrap, this.schema);
1774
+ });
1775
+ const title = document.createElement("span");
1776
+ title.className = "cutetxt-embed-title";
1777
+ title.textContent = this.attrs.toolId || "embed";
1778
+ bar.append(title);
1779
+ const actions = document.createElement("span");
1780
+ actions.className = "cutetxt-embed-actions";
1781
+ actions.append(embedIconButton("x", "Delete embed", () => deleteEmbedToken(view, wrap, this.schema)));
1782
+ if (this.picker)
1783
+ actions.prepend(buildToolPicker(view, wrap, this.schema, this.picker, this.attrs));
1784
+ bar.append(actions);
1785
+ const body = document.createElement("div");
1786
+ body.className = "cutetxt-embed-body";
1787
+ if (node instanceof Node)
1788
+ body.append(node);
1789
+ else if (typeof node === "string")
1790
+ body.append(document.createTextNode(node));
1791
+ const handle = document.createElement("div");
1792
+ handle.className = "cutetxt-embed-resize";
1793
+ handle.title = "Resize";
1794
+ handle.setAttribute("role", "separator");
1795
+ handle.setAttribute("aria-label", "Resize embed");
1796
+ wrap.append(bar, body, handle);
1797
+ this.setupTitle(title);
1798
+ this.setupResize(view, wrap, body, handle, node);
1799
+ } else {
1800
+ if (node instanceof Node)
1801
+ wrap.append(node);
1802
+ else if (typeof node === "string")
1803
+ wrap.append(document.createTextNode(node));
1804
+ }
1805
+ return wrap;
1806
+ }
1807
+ coordsAt(dom, _pos, side) {
1808
+ const rect = dom.getBoundingClientRect();
1809
+ const left = side > 0 ? rect.right : rect.left;
1810
+ return { left, right: left, top: rect.top, bottom: rect.bottom };
1811
+ }
1812
+ setCleanup(fn) {
1813
+ const prev = this.cleanup;
1814
+ this.cleanup = prev ? () => {
1815
+ prev();
1816
+ fn();
1817
+ } : fn;
1818
+ }
1819
+ setupTitle(title) {
1820
+ const fallback = this.attrs.toolId || "embed";
1821
+ const apply2 = (doc) => {
1822
+ const patchwork = doc && doc["@patchwork"];
1823
+ title.textContent = patchwork && patchwork.title || doc && (doc.title || doc.name) || fallback;
1824
+ };
1825
+ title.textContent = fallback;
1826
+ if (!this.repo || !this.attrs.url)
1827
+ return;
1828
+ let handle = null;
1829
+ let disposed = false;
1830
+ const refresh = () => {
1831
+ if (!disposed && handle)
1832
+ apply2(handle.doc());
1833
+ };
1834
+ this.repo.find(this.attrs.url).then((h) => {
1835
+ if (disposed)
1836
+ return;
1837
+ handle = h;
1838
+ refresh();
1839
+ handle.on?.("change", refresh);
1840
+ }).catch(() => {
1841
+ });
1842
+ this.setCleanup(() => {
1843
+ disposed = true;
1844
+ handle?.off?.("change", refresh);
1845
+ });
1846
+ }
1847
+ // Make the embed frame resizable. Width/height stored in the token describe the
1848
+ // embedded tool viewport, not the frame chrome around it.
1849
+ setupResize(view, wrap, body, handle, node) {
1850
+ wrap.classList.add("cutetxt-embed");
1851
+ if (this.attrs.width)
1852
+ wrap.style.width = this.attrs.width + "px";
1853
+ if (this.attrs.height)
1854
+ body.style.height = this.attrs.height + "px";
1855
+ if (node instanceof HTMLElement) {
1856
+ node.style.width = "100%";
1857
+ node.style.height = "100%";
1858
+ node.style.display = "block";
1859
+ }
1860
+ let drag = null;
1861
+ const onDown = (event) => {
1862
+ event.preventDefault();
1863
+ event.stopPropagation();
1864
+ handle.setPointerCapture?.(event.pointerId);
1865
+ drag = {
1866
+ id: event.pointerId,
1867
+ x: event.clientX,
1868
+ y: event.clientY,
1869
+ width: wrap.getBoundingClientRect().width,
1870
+ height: body.getBoundingClientRect().height,
1871
+ moved: false
1872
+ };
1873
+ wrap.classList.add("is-resizing");
1874
+ };
1875
+ const onMove = (event) => {
1876
+ if (!drag || event.pointerId !== drag.id)
1877
+ return;
1878
+ event.preventDefault();
1879
+ const w = Math.max(160, Math.round(drag.width + event.clientX - drag.x));
1880
+ const h = Math.max(88, Math.round(drag.height + event.clientY - drag.y));
1881
+ drag.moved = drag.moved || w !== Math.round(drag.width) || h !== Math.round(drag.height);
1882
+ wrap.style.width = w + "px";
1883
+ body.style.height = h + "px";
1884
+ };
1885
+ const onUp = (event) => {
1886
+ if (!drag || event.pointerId !== drag.id)
1887
+ return;
1888
+ handle.releasePointerCapture?.(event.pointerId);
1889
+ wrap.classList.remove("is-resizing");
1890
+ const moved = drag.moved;
1891
+ drag = null;
1892
+ if (!moved)
1893
+ return;
1894
+ const w = Math.round(body.clientWidth);
1895
+ const h = Math.round(body.clientHeight);
1896
+ if (w && h)
1897
+ setEmbedAttrs(view, wrap, this.schema, { width: w, height: h });
1898
+ };
1899
+ const onCancel = (event) => {
1900
+ if (!drag || event.pointerId !== drag.id)
1901
+ return;
1902
+ handle.releasePointerCapture?.(event.pointerId);
1903
+ drag = null;
1904
+ wrap.classList.remove("is-resizing");
1905
+ };
1906
+ handle.addEventListener("pointerdown", onDown);
1907
+ handle.addEventListener("pointermove", onMove);
1908
+ handle.addEventListener("pointerup", onUp);
1909
+ handle.addEventListener("pointercancel", onCancel);
1910
+ this.setCleanup(() => {
1911
+ handle.removeEventListener("pointerdown", onDown);
1912
+ handle.removeEventListener("pointermove", onMove);
1913
+ handle.removeEventListener("pointerup", onUp);
1914
+ handle.removeEventListener("pointercancel", onCancel);
1915
+ });
1916
+ }
1917
+ destroy() {
1918
+ if (this.cleanup)
1919
+ this.cleanup();
1920
+ }
1921
+ ignoreEvent() {
1922
+ return true;
1923
+ }
1924
+ }
1925
+ function buildDecorations(state, schema, picker, repo) {
1926
+ const text = state.doc.toString();
1927
+ const sel = state.selection.main;
1928
+ const decos = [];
1929
+ for (const r of findMarkupRanges(text, schema)) {
1930
+ const overlaps = sel.from < r.end && sel.to > r.start;
1931
+ const atStart = r.kind === "atom" && sel.empty && sel.from === r.start;
1932
+ const atEnd = r.kind === "atom" && sel.empty && sel.from === r.end;
1933
+ if (overlaps || atStart || atEnd)
1934
+ continue;
1935
+ if (r.kind === "atom") {
1936
+ decos.push(Decoration.replace({
1937
+ widget: new AtomWidget(r.spec, r.attrs, r.block, r.name, schema, picker, repo),
1938
+ block: r.block
1939
+ }).range(r.start, r.end));
1940
+ continue;
1941
+ }
1942
+ const [tagName, dom] = r.spec.toDOM(r.attrs);
1943
+ let cls;
1944
+ let attributes;
1945
+ if (dom) {
1946
+ const { class: c, ...rest } = dom;
1947
+ cls = c;
1948
+ if (Object.keys(rest).length)
1949
+ attributes = rest;
1950
+ }
1951
+ if (r.innerStart > r.start)
1952
+ decos.push(Decoration.replace({}).range(r.start, r.innerStart));
1953
+ decos.push(Decoration.mark({ tagName, class: cls, attributes }).range(r.innerStart, r.innerEnd));
1954
+ if (r.end > r.innerEnd)
1955
+ decos.push(Decoration.replace({}).range(r.innerEnd, r.end));
1956
+ }
1957
+ return Decoration.set(decos, true);
1958
+ }
1959
+ function previewField(getSchema, getToolsForDoc, repo) {
1960
+ return StateField.define({
1961
+ create: (state) => buildDecorations(state, getSchema(), getToolsForDoc, repo),
1962
+ update(value, tr) {
1963
+ if (tr.docChanged || tr.selection)
1964
+ return buildDecorations(tr.state, getSchema(), getToolsForDoc, repo);
1965
+ return value.map(tr.changes);
1966
+ },
1967
+ provide: (f) => EditorView.decorations.from(f)
1968
+ });
1969
+ }
1970
+ function wrapSelection(view, delim) {
1971
+ const { from, to } = view.state.selection.main;
1972
+ view.dispatch({
1973
+ changes: [
1974
+ { from, insert: delim },
1975
+ { to, insert: delim }
1976
+ ],
1977
+ selection: { anchor: from + delim.length, head: to + delim.length },
1978
+ userEvent: "input"
1979
+ });
1980
+ }
1981
+ const MARK_KEYS = ["Mod-b", "Mod-i", "Mod-u"];
1982
+ function markKeymap(getSchema) {
1983
+ return keymap.of(MARK_KEYS.map((k) => ({
1984
+ key: k,
1985
+ preventDefault: true,
1986
+ run(view) {
1987
+ const marks = (getSchema() || {}).marks || {};
1988
+ for (const spec of Object.values(marks)) {
1989
+ if (spec.key === k && spec.wrap) {
1990
+ wrapSelection(view, spec.wrap);
1991
+ return true;
1992
+ }
1993
+ }
1994
+ return false;
1995
+ }
1996
+ })));
1997
+ }
1998
+ function embedToken(schema, attrs) {
1999
+ const spec = schema.blocks && schema.blocks.embed;
2000
+ return spec && spec.token ? spec.token(attrs) : "";
2001
+ }
2002
+ function insertText(view, text) {
2003
+ const pos = view.state.selection.main.from;
2004
+ view.dispatch({
2005
+ changes: { from: pos, insert: text },
2006
+ selection: { anchor: pos + text.length },
2007
+ userEvent: "input"
2008
+ });
2009
+ }
2010
+ function extensionForFile(file, mimeType) {
2011
+ const fromName = file.name && file.name.includes(".") ? file.name.split(".").pop() : "";
2012
+ return (fromName || (mimeType.split("/")[1] || "bin").split("+")[0] || "bin").toLowerCase();
2013
+ }
2014
+ async function createFileToken(schema, repo, file) {
2015
+ if (!repo || !file)
2016
+ return "";
2017
+ const buffer = new Uint8Array(await file.arrayBuffer());
2018
+ const mimeType = file.type || "application/octet-stream";
2019
+ const extension = extensionForFile(file, mimeType);
2020
+ const name = file.name || `file.${extension}`;
2021
+ const fileHandle = await repo.create2({
2022
+ content: buffer,
2023
+ extension,
2024
+ mimeType,
2025
+ name,
2026
+ "@patchwork": { type: "file" }
2027
+ });
2028
+ return embedToken(schema, { url: fileHandle.url, toolId: "file" });
2029
+ }
2030
+ async function insertFileBlob(view, schema, repo, file) {
2031
+ const token = await createFileToken(schema, repo, file);
2032
+ if (token)
2033
+ insertText(view, token);
2034
+ }
2035
+ async function insertDroppedFiles(view, schema, repo, files, at) {
2036
+ if (!repo || !files.length)
2037
+ return false;
2038
+ const tokens = [];
2039
+ for (const file of files) {
2040
+ const token = await createFileToken(schema, repo, file);
2041
+ if (token)
2042
+ tokens.push(token);
2043
+ }
2044
+ if (!tokens.length)
2045
+ return false;
2046
+ const insert2 = tokens.map((token) => token + "\n").join("");
2047
+ view.dispatch({
2048
+ changes: { from: at, insert: insert2 },
2049
+ selection: { anchor: at + insert2.length },
2050
+ userEvent: "input"
2051
+ });
2052
+ return true;
2053
+ }
2054
+ function docUrlFromDrop(dataTransfer) {
2055
+ if (!dataTransfer)
2056
+ return null;
2057
+ const dnd = dataTransfer.getData("text/x-patchwork-dnd");
2058
+ if (dnd) {
2059
+ try {
2060
+ const url = JSON.parse(dnd)?.items?.[0]?.url;
2061
+ if (url)
2062
+ return url;
2063
+ } catch {
2064
+ }
2065
+ }
2066
+ const urls = dataTransfer.getData("text/x-patchwork-urls");
2067
+ if (urls) {
2068
+ try {
2069
+ const parsed = JSON.parse(urls);
2070
+ if (Array.isArray(parsed) && parsed[0])
2071
+ return parsed[0];
2072
+ } catch {
2073
+ }
2074
+ }
2075
+ const text = dataTransfer.getData("text/uri-list") || dataTransfer.getData("text/plain");
2076
+ const match = (text || "").match(/automerge:[A-Za-z0-9]+/);
2077
+ return match ? match[0] : null;
2078
+ }
2079
+ function dndHandlers(getSchema, repo) {
2080
+ return EditorView.domEventHandlers({
2081
+ paste(event, view) {
2082
+ const items = event.clipboardData && event.clipboardData.items;
2083
+ if (!items)
2084
+ return false;
2085
+ for (const item of items) {
2086
+ if (item.kind === "file" && item.type.startsWith("image/")) {
2087
+ event.preventDefault();
2088
+ insertFileBlob(view, getSchema(), repo, item.getAsFile());
2089
+ return true;
2090
+ }
2091
+ }
2092
+ return false;
2093
+ },
2094
+ dragover(event) {
2095
+ if (event.dataTransfer?.files && event.dataTransfer.files.length || docUrlFromDrop(event.dataTransfer)) {
2096
+ event.preventDefault();
2097
+ if (event.dataTransfer)
2098
+ event.dataTransfer.dropEffect = "copy";
2099
+ return true;
2100
+ }
2101
+ return false;
2102
+ },
2103
+ drop(event, view) {
2104
+ const files = Array.from(event.dataTransfer?.files || []);
2105
+ if (files.length) {
2106
+ event.preventDefault();
2107
+ const pos2 = view.posAtCoords({ x: event.clientX, y: event.clientY });
2108
+ const at2 = pos2 == null ? view.state.selection.main.from : pos2;
2109
+ insertDroppedFiles(view, getSchema(), repo, files, at2);
2110
+ return true;
2111
+ }
2112
+ const url = docUrlFromDrop(event.dataTransfer);
2113
+ if (!url)
2114
+ return false;
2115
+ event.preventDefault();
2116
+ const pos = view.posAtCoords({ x: event.clientX, y: event.clientY });
2117
+ const token = embedToken(getSchema(), { url });
2118
+ if (!token)
2119
+ return false;
2120
+ const at = pos == null ? view.state.selection.main.from : pos;
2121
+ const insert2 = token + "\n";
2122
+ view.dispatch({
2123
+ changes: { from: at, insert: insert2 },
2124
+ selection: { anchor: at + insert2.length },
2125
+ userEvent: "input"
2126
+ });
2127
+ return true;
2128
+ }
2129
+ });
2130
+ }
2131
+ function cutePreview(getSchema, opts = {}) {
2132
+ const { repo, getToolsForDoc } = opts;
2133
+ const exts = [previewField(getSchema, getToolsForDoc, repo), markKeymap(getSchema)];
2134
+ if (repo)
2135
+ exts.push(dndHandlers(getSchema, repo));
2136
+ return exts;
2137
+ }
2138
+ const DEFAULTS = { className: "cutetxt-autocomplete", itemClass: "cutetxt-ac-item", max: 10 };
2139
+ function defaultRow(opt, active, cfg) {
2140
+ const row = document.createElement("div");
2141
+ row.className = cfg.itemClass + (active ? " is-active" : "");
2142
+ if (opt.icon && opt.icon.kind === "image") {
2143
+ const img = document.createElement("img");
2144
+ img.className = "cutetxt-ac-icon";
2145
+ img.src = opt.icon.src;
2146
+ row.append(img);
2147
+ } else if (opt.icon && opt.icon.kind === "emoji") {
2148
+ const span = document.createElement("span");
2149
+ span.className = "cutetxt-ac-icon";
2150
+ span.textContent = opt.icon.char;
2151
+ row.append(span);
2152
+ }
2153
+ const label = document.createElement("span");
2154
+ label.className = "cutetxt-ac-label";
2155
+ label.textContent = opt.label;
2156
+ row.append(label);
2157
+ if (opt.description != null) {
2158
+ const d = document.createElement("span");
2159
+ d.className = "cutetxt-ac-desc";
2160
+ d.textContent = String(opt.description);
2161
+ row.append(d);
2162
+ }
2163
+ if (opt.detail != null) {
2164
+ const d = document.createElement("span");
2165
+ d.className = "cutetxt-ac-detail";
2166
+ d.textContent = String(opt.detail);
2167
+ row.append(d);
2168
+ }
2169
+ return row;
2170
+ }
2171
+ function cuteAutocomplete(getSpecs, opts = {}) {
2172
+ const cfg = { ...DEFAULTS, ...opts };
2173
+ const normCache = /* @__PURE__ */ new WeakMap();
2174
+ const norm = (spec) => {
2175
+ let n = normCache.get(spec);
2176
+ if (!n) {
2177
+ n = { ...spec, trigger: new RegExp(spec.trigger.source, spec.trigger.flags.replace("g", "")) };
2178
+ normCache.set(spec, n);
2179
+ }
2180
+ return n;
2181
+ };
2182
+ let popup = null;
2183
+ let state = null;
2184
+ let reqId = 0;
2185
+ const close = () => {
2186
+ state = null;
2187
+ if (popup) {
2188
+ popup.remove();
2189
+ popup = null;
2190
+ }
2191
+ };
2192
+ function render2(view) {
2193
+ if (!state)
2194
+ return close();
2195
+ if (!popup) {
2196
+ popup = document.createElement("div");
2197
+ popup.className = cfg.className;
2198
+ popup.contentEditable = "false";
2199
+ document.body.appendChild(popup);
2200
+ }
2201
+ popup.replaceChildren();
2202
+ state.options.forEach((opt, i) => {
2203
+ const active = i === state.index;
2204
+ const row = cfg.renderRow ? cfg.renderRow(opt, { index: i, active }) : defaultRow(opt, active, cfg);
2205
+ row.addEventListener("mousedown", (event) => {
2206
+ event.preventDefault();
2207
+ state.index = i;
2208
+ accept(view);
2209
+ });
2210
+ popup.append(row);
2211
+ });
2212
+ positionPopup(view);
2213
+ }
2214
+ function positionPopup(view) {
2215
+ if (!popup || !state)
2216
+ return;
2217
+ const coords = view.coordsAtPos(state.from);
2218
+ if (!coords)
2219
+ return;
2220
+ const margin = 6;
2221
+ const vw = window.innerWidth;
2222
+ const vh = window.innerHeight;
2223
+ const rect = popup.getBoundingClientRect();
2224
+ let left = coords.left;
2225
+ if (left + rect.width > vw - margin)
2226
+ left = vw - rect.width - margin;
2227
+ if (left < margin)
2228
+ left = margin;
2229
+ const below = coords.bottom + margin;
2230
+ const above = coords.top - rect.height - margin;
2231
+ const roomBelow = vh - coords.bottom;
2232
+ const roomAbove = coords.top;
2233
+ const top = below + rect.height > vh - margin && roomAbove > roomBelow ? Math.max(margin, above) : below;
2234
+ popup.style.left = left + "px";
2235
+ popup.style.top = top + "px";
2236
+ const after = popup.getBoundingClientRect();
2237
+ const dx = left - after.left;
2238
+ const dy = top - after.top;
2239
+ if (dx || dy) {
2240
+ popup.style.left = left + dx + "px";
2241
+ popup.style.top = top + dy + "px";
2242
+ }
2243
+ }
2244
+ function accept(view) {
2245
+ if (!state)
2246
+ return false;
2247
+ const opt = state.options[state.index];
2248
+ if (!opt)
2249
+ return false;
2250
+ const head = view.state.selection.main.head;
2251
+ let from = state.from;
2252
+ let to = head;
2253
+ if (opt.replace === "all") {
2254
+ from = 0;
2255
+ to = view.state.doc.length;
2256
+ } else if (opt.replace === "line") {
2257
+ const line = view.state.doc.lineAt(head);
2258
+ from = line.from;
2259
+ to = line.to;
2260
+ }
2261
+ view.dispatch({
2262
+ changes: { from, to, insert: opt.insert },
2263
+ selection: { anchor: from + opt.insert.length },
2264
+ userEvent: "input.complete"
2265
+ });
2266
+ close();
2267
+ return true;
2268
+ }
2269
+ function move(view, delta) {
2270
+ if (!state)
2271
+ return false;
2272
+ state.index = (state.index + delta + state.options.length) % state.options.length;
2273
+ render2(view);
2274
+ return true;
2275
+ }
2276
+ async function refresh(view) {
2277
+ const sel = view.state.selection.main;
2278
+ if (!sel.empty || !view.hasFocus)
2279
+ return close();
2280
+ const line = view.state.doc.lineAt(sel.head);
2281
+ const before = view.state.doc.sliceString(line.from, sel.head);
2282
+ for (const raw of getSpecs()) {
2283
+ const spec = norm(raw);
2284
+ const m = spec.trigger.exec(before);
2285
+ if (!m)
2286
+ continue;
2287
+ const from = sel.head - m[0].length;
2288
+ const id = ++reqId;
2289
+ let opts2;
2290
+ try {
2291
+ opts2 = await spec.options(m[1] ?? "", { view });
2292
+ } catch {
2293
+ opts2 = [];
2294
+ }
2295
+ if (id !== reqId)
2296
+ return;
2297
+ opts2 = (opts2 || []).slice(0, cfg.max);
2298
+ if (!opts2.length)
2299
+ return close();
2300
+ const prev = state && state.options[state.index] && state.options[state.index].label;
2301
+ const keep = opts2.findIndex((o) => o.label === prev);
2302
+ state = { from, options: opts2, index: keep < 0 ? 0 : keep };
2303
+ render2(view);
2304
+ return;
2305
+ }
2306
+ close();
2307
+ }
2308
+ const listener = EditorView.updateListener.of((update) => {
2309
+ if (update.docChanged || update.selectionSet || update.focusChanged)
2310
+ refresh(update.view);
2311
+ });
2312
+ const cleanup = ViewPlugin.define(() => ({ destroy: close }));
2313
+ const keys = Prec.highest(keymap.of([
2314
+ { key: "ArrowDown", run: (view) => move(view, 1) },
2315
+ { key: "ArrowUp", run: (view) => move(view, -1) },
2316
+ { key: "Enter", run: (view) => accept(view) },
2317
+ { key: "Tab", run: (view) => accept(view) },
2318
+ {
2319
+ key: "Escape",
2320
+ run: () => {
2321
+ if (!state)
2322
+ return false;
2323
+ close();
2324
+ return true;
2325
+ }
2326
+ }
2327
+ ]));
2328
+ return [cleanup, listener, keys];
2329
+ }
2330
+ function getText(doc, path) {
2331
+ let node = doc;
2332
+ for (const key of path)
2333
+ node = node == null ? node : node[key];
2334
+ return typeof node === "string" ? node : "";
2335
+ }
2336
+ const remote = Annotation.define();
2337
+ function pushLocalEdits(handle, path, update) {
2338
+ if (!update.docChanged)
2339
+ return;
2340
+ if (update.transactions.some((tr) => tr.annotation(remote)))
2341
+ return;
2342
+ const edits = [];
2343
+ update.changes.iterChanges((fromA, toA, _fromB, _toB, inserted) => {
2344
+ edits.push([fromA, toA - fromA, inserted.toString()]);
2345
+ });
2346
+ if (edits.length === 0)
2347
+ return;
2348
+ handle.change((doc) => {
2349
+ for (let i = edits.length - 1; i >= 0; i--) {
2350
+ const [from, del, ins] = edits[i];
2351
+ am.splice(doc, path, from, del, ins);
2352
+ }
2353
+ });
2354
+ }
2355
+ function reconcileText(view, handle, path) {
2356
+ const text = getText(handle.doc(), path);
2357
+ const current = view.state.doc.toString();
2358
+ if (text === current)
2359
+ return;
2360
+ let start = 0;
2361
+ const min = Math.min(text.length, current.length);
2362
+ while (start < min && text[start] === current[start])
2363
+ start++;
2364
+ let endText = text.length;
2365
+ let endCur = current.length;
2366
+ while (endText > start && endCur > start && text[endText - 1] === current[endCur - 1]) {
2367
+ endText--;
2368
+ endCur--;
2369
+ }
2370
+ view.dispatch({
2371
+ changes: { from: start, to: endCur, insert: text.slice(start, endText) },
2372
+ annotations: remote.of(true)
2373
+ });
2374
+ }
2375
+ function createCuteEditor({ handle, path, schema, parent, repo, getToolsForDoc, autocompletes = [], editable = true }) {
2376
+ let view;
2377
+ const onChange = () => {
2378
+ queueMicrotask(() => {
2379
+ if (view)
2380
+ reconcileText(view, handle, path);
2381
+ });
2382
+ };
2383
+ handle.on("change", onChange);
2384
+ const updateListener = EditorView.updateListener.of((update) => {
2385
+ pushLocalEdits(handle, path, update);
2386
+ });
2387
+ const acExtensions = autocompletes.length ? cuteAutocomplete(() => autocompletes) : [];
2388
+ view = new EditorView({
2389
+ doc: getText(handle.doc(), path),
2390
+ parent,
2391
+ extensions: [
2392
+ ...cutePreview(() => schema, { repo, getToolsForDoc }),
2393
+ updateListener,
2394
+ EditorView.lineWrapping,
2395
+ EditorView.editable.of(editable),
2396
+ ...acExtensions
2397
+ ]
2398
+ });
2399
+ return {
2400
+ view,
2401
+ destroy() {
2402
+ handle.off("change", onChange);
2403
+ view.destroy();
2404
+ }
2405
+ };
2406
+ }
2407
+ function build(parent, nodes) {
2408
+ for (const node of nodes) {
2409
+ if (node.text != null) {
2410
+ const parts = node.text.split("\n");
2411
+ parts.forEach((part, i) => {
2412
+ if (i > 0)
2413
+ parent.append(document.createElement("br"));
2414
+ if (part)
2415
+ parent.append(document.createTextNode(part));
2416
+ });
2417
+ } else if (node.atom) {
2418
+ const el = node.atom.toDOM(node.attrs);
2419
+ const wrap = document.createElement(node.block ? "div" : "span");
2420
+ wrap.className = node.block ? "cutetxt-block" : "cutetxt-replace";
2421
+ if (el instanceof Node)
2422
+ wrap.append(el);
2423
+ else if (typeof el === "string")
2424
+ wrap.append(document.createTextNode(el));
2425
+ parent.append(wrap);
2426
+ } else {
2427
+ const [tagName, attrs] = node.mark.toDOM(node.attrs);
2428
+ const el = document.createElement(tagName);
2429
+ if (attrs)
2430
+ for (const [key, value] of Object.entries(attrs))
2431
+ el.setAttribute(key, value);
2432
+ build(el, node.children);
2433
+ parent.append(el);
2434
+ }
2435
+ }
2436
+ }
2437
+ function renderMarkup(text, schema) {
2438
+ const container = document.createElement("div");
2439
+ container.className = "cutetxt-rendered";
2440
+ build(container, parseMarkup(typeof text === "string" ? text : "", schema));
2441
+ return container;
2442
+ }
2443
+ function defineRichSchema({ marks = {}, blocks = {}, replaces = {} } = {}) {
2444
+ return { marks, blocks, replaces };
2445
+ }
2446
+ const defaultMarks = {
2447
+ strong: { pattern: /\*([^*\n]+?)\*/, toDOM: () => ["strong"], key: "Mod-b", wrap: "*" },
2448
+ "underline-em": { pattern: /___([^_\n]+?)___/, toDOM: () => ["span", { class: "cutetxt-underline-em" }], wrap: "___" },
2449
+ underline: { pattern: /__([^_\n]+?)__/, toDOM: () => ["u"], key: "Mod-u", wrap: "__" },
2450
+ em: { pattern: new RegExp("(?<![_\\w])_([^_\\n]+?)_(?![_.\\w])"), toDOM: () => ["em"], key: "Mod-i", wrap: "_" },
2451
+ strike: { pattern: /~~([^~\n]+?)~~/, toDOM: () => ["s"], wrap: "~~" },
2452
+ spoiler: { pattern: /\|\|([^|\n]+?)\|\|/, toDOM: () => ["span", { class: "cutetxt-spoiler" }], wrap: "||" },
2453
+ code: { pattern: /`([^`\n]+?)`/, toDOM: () => ["code", { class: "cutetxt-code" }], wrap: "`", raw: true },
2454
+ sub: { pattern: /\._([^_\n]+?)_\./, toDOM: () => ["sub"] },
2455
+ sup: { pattern: /\.\^([^^\n]+?)\^\./, toDOM: () => ["sup"] },
2456
+ inverted: { pattern: /%%([^%\n]+?)%%/, toDOM: () => ["span", { class: "cutetxt-inverted" }], wrap: "%%" },
2457
+ marquee: { pattern: /<>([^\n]+?)<>/, toDOM: () => ["marquee"], wrap: "<>" },
2458
+ link: {
2459
+ pattern: /(https?:\/\/[^\s<]+)/,
2460
+ parse: (s) => ({ href: s }),
2461
+ toDOM: ({ href }) => ["a", { href, target: "_blank", rel: "noopener noreferrer" }]
2462
+ }
2463
+ };
2464
+ const CODE_FENCE = /```([a-zA-Z0-9]*)\n([\s\S]*?)```/;
2465
+ const THINK = /<think>([\s\S]*?)<\/think>/;
2466
+ const defaultBlocks = {
2467
+ // An embed: a self-closing <patchwork-view .../> tag, rendered as the live element.
2468
+ embed: {
2469
+ pattern: /<patchwork-view\b[^>]*?\/>/,
2470
+ parse(tag) {
2471
+ const attr = (n) => tag.match(new RegExp(`\\b${n}="([^"]*)"`))?.[1] ?? tag.match(new RegExp(`\\b${n}='([^']*)'`))?.[1];
2472
+ return { url: attr("doc-url"), toolId: attr("tool-id"), width: attr("width"), height: attr("height") };
2473
+ },
2474
+ token({ url, toolId, width, height }) {
2475
+ return `<patchwork-view doc-url="${url}"` + (toolId ? ` tool-id="${toolId}"` : "") + (width ? ` width="${width}"` : "") + (height ? ` height="${height}"` : "") + `/>`;
2476
+ },
2477
+ toDOM({ url, toolId, width, height }) {
2478
+ const el = document.createElement("patchwork-view");
2479
+ if (url)
2480
+ el.setAttribute("doc-url", url);
2481
+ if (toolId)
2482
+ el.setAttribute("tool-id", toolId);
2483
+ if (width)
2484
+ el.style.width = width + "px";
2485
+ if (height)
2486
+ el.style.height = height + "px";
2487
+ return el;
2488
+ },
2489
+ toHTML({ url, toolId, width, height }) {
2490
+ const style = (width ? `width:${escapeAttr(width)}px;` : "") + (height ? `height:${escapeAttr(height)}px;` : "");
2491
+ const attrs = (url ? ` doc-url="${escapeAttr(url)}"` : "") + (toolId ? ` tool-id="${escapeAttr(toolId)}"` : "") + (style ? ` style="${escapeAttr(style)}"` : "");
2492
+ return `<patchwork-view${attrs}></patchwork-view>`;
2493
+ }
2494
+ },
2495
+ // A fenced code block: ```lang\n…\n```
2496
+ codeblock: {
2497
+ pattern: CODE_FENCE,
2498
+ parse(s) {
2499
+ const m = s.match(CODE_FENCE);
2500
+ return { lang: m?.[1] || "", code: m?.[2] ?? "" };
2501
+ },
2502
+ toDOM({ lang, code }) {
2503
+ const pre = document.createElement("pre");
2504
+ pre.className = "cutetxt-codeblock";
2505
+ const codeEl = document.createElement("code");
2506
+ if (lang)
2507
+ codeEl.className = "language-" + lang;
2508
+ codeEl.textContent = code;
2509
+ pre.append(codeEl);
2510
+ return pre;
2511
+ },
2512
+ toHTML({ lang, code }) {
2513
+ const cls = lang ? ` class="language-${escapeAttr(lang)}"` : "";
2514
+ return `<pre class="cutetxt-codeblock"><code${cls}>${escapeHTML(code)}</code></pre>`;
2515
+ }
2516
+ },
2517
+ // A <think>…</think> block.
2518
+ think: {
2519
+ pattern: THINK,
2520
+ parse(s) {
2521
+ return { text: s.match(THINK)?.[1] ?? "" };
2522
+ },
2523
+ toDOM({ text }) {
2524
+ const div = document.createElement("div");
2525
+ div.className = "cutetxt-think";
2526
+ div.textContent = text;
2527
+ return div;
2528
+ },
2529
+ toHTML({ text }) {
2530
+ return `<div class="cutetxt-think">${escapeHTML(text)}</div>`;
2531
+ }
2532
+ }
2533
+ };
2534
+ const EMOJI = {
2535
+ smile: "🙂",
2536
+ grin: "😀",
2537
+ joy: "😂",
2538
+ heart: "❤️",
2539
+ fire: "🔥",
2540
+ tada: "🎉",
2541
+ eyes: "👀",
2542
+ "+1": "👍",
2543
+ "-1": "👎",
2544
+ rocket: "🚀",
2545
+ sparkles: "✨",
2546
+ thinking: "🤔"
2547
+ };
2548
+ const escapeRe = (s) => s.replace(/[.*+?^${}()|[\]\\-]/g, "\\$&");
2549
+ const EMOJI_RE = new RegExp(":(" + Object.keys(EMOJI).map(escapeRe).join("|") + "):");
2550
+ const defaultReplaces = {
2551
+ emoji: {
2552
+ pattern: EMOJI_RE,
2553
+ parse(s) {
2554
+ return { name: s.slice(1, -1) };
2555
+ },
2556
+ toDOM({ name }) {
2557
+ const span = document.createElement("span");
2558
+ span.className = "cutetxt-emoji";
2559
+ span.textContent = EMOJI[name] ?? ":" + name + ":";
2560
+ return span;
2561
+ },
2562
+ toHTML({ name }) {
2563
+ return `<span class="cutetxt-emoji">${escapeHTML(EMOJI[name] ?? ":" + name + ":")}</span>`;
2564
+ }
2565
+ }
2566
+ };
2567
+ const defaultAutocompletes = {
2568
+ emoji: {
2569
+ // require ≥1 char after `:` so a bare `:` (or the trailing `:` of a just-
2570
+ // inserted `:fire:`) doesn't reopen the popup.
2571
+ trigger: /:([\w+-]+)$/,
2572
+ options(query) {
2573
+ const q = (query || "").toLowerCase();
2574
+ return Object.keys(EMOJI).filter((name) => name.includes(q)).slice(0, 8).map((name) => ({ label: name, detail: EMOJI[name], insert: ":" + name + ":" }));
2575
+ }
2576
+ }
2577
+ };
2578
+ const defaultSchema = defineRichSchema({
2579
+ marks: defaultMarks,
2580
+ blocks: defaultBlocks,
2581
+ replaces: defaultReplaces
2582
+ });
2583
+ const read = (v, fallback) => (typeof v === "function" ? v() : v) ?? fallback;
2584
+ function CuteText(props) {
2585
+ const el = document.createElement("div");
2586
+ el.className = "cutetxt-rendered";
2587
+ createEffect(() => {
2588
+ const rendered = renderMarkup(read(props.text, ""), read(props.schema, defaultSchema));
2589
+ el.replaceChildren(...rendered.childNodes);
2590
+ });
2591
+ return el;
2592
+ }
2593
+ var _tmpl$ = /* @__PURE__ */ template(`<span class=comments-thread-resolved-label>Resolved`), _tmpl$2 = /* @__PURE__ */ template(`<span class=comments-thread-doc-level>On this document`), _tmpl$3 = /* @__PURE__ */ template(`<div><div class=comments-thread-card-body>`), _tmpl$4 = /* @__PURE__ */ template(`<button class=comment-btn>Cancel`), _tmpl$5 = /* @__PURE__ */ template(`<button class=comment-btn>Save`), _tmpl$6 = /* @__PURE__ */ template(`<div class=comments-thread-actions>`), _tmpl$7 = /* @__PURE__ */ template(`<button class=comment-btn title="Reopen comment">Reopen`), _tmpl$8 = /* @__PURE__ */ template(`<button class=comment-btn title="Resolve comment">Resolve`), _tmpl$9 = /* @__PURE__ */ template(`<button class=comment-btn title="Reply to comment">Reply`), _tmpl$0 = /* @__PURE__ */ template(`<span class=comment-timestamp>`), _tmpl$1 = /* @__PURE__ */ template(`<div class=comment-card><div class=comment-header><div class=comment-author><patchwork-view tool-id=contact-avatar></patchwork-view><span class=comment-author-name>`, true, false, false), _tmpl$10 = /* @__PURE__ */ template(`<div class=comment-content>`), _tmpl$11 = /* @__PURE__ */ template(`<div class="cutetxt-editor comment-draft-editor">`);
2594
+ function CommentThreadView(props) {
2595
+ const [, contactHandle] = subscribeDoc(props.element, {
2596
+ type: "patchwork:contact"
2597
+ });
2598
+ const currentContactUrl = () => contactHandle()?.url;
2599
+ const [thread, setThread] = createSignal(props.handle.doc());
2600
+ createEffect(() => {
2601
+ const h = props.handle;
2602
+ setThread(() => h.doc());
2603
+ const onChange = () => setThread(() => h.doc());
2604
+ h.on("change", onChange);
2605
+ onCleanup(() => h.off("change", onChange));
2606
+ });
2607
+ const state = createElementAttribute(props.element, "data-thread-state", "primary");
2608
+ const isPrimary = () => state() === "primary";
2609
+ const stateClass = () => state() === "primary" ? "comments-thread-card--primary" : state() === "secondary" ? "comments-thread-card--secondary" : "comments-thread-card--inactive";
2610
+ const isDocLevel = createMemo(() => {
2611
+ const refs = thread()?.refs ?? [];
2612
+ return refs.length > 0 && refs.every((ref) => !ref.includes("/") && !ref.includes("#"));
2613
+ });
2614
+ const isResolved = () => thread()?.isResolved ?? false;
2615
+ const cardClass = () => `comments-thread-card ${stateClass()}${isResolved() ? " comments-thread-card--resolved" : ""}`;
2616
+ const commentIds = createMemo((prev) => {
2617
+ const t = thread();
2618
+ if (!t) return [];
2619
+ const next = [];
2620
+ for (const c of t.comments) next.push(c.id);
2621
+ if (prev && prev.length === next.length && next.every((id, i) => id === prev[i])) {
2622
+ return prev;
2623
+ }
2624
+ return next;
2625
+ }, []);
2626
+ const draftComment = createMemo(() => {
2627
+ const t = thread();
2628
+ if (!t) return void 0;
2629
+ const isDraft = (c) => c.draftContent !== void 0 || c.content === void 0;
2630
+ const contactUrl = currentContactUrl();
2631
+ return t.comments.find((c) => isDraft(c) && c.contactUrl === contactUrl) ?? t.comments.find(isDraft);
2632
+ });
2633
+ const draftCommentHandle = createMemo(() => {
2634
+ const t = thread();
2635
+ const d = draftComment();
2636
+ if (!t || !d) return void 0;
2637
+ return props.handle.sub("comments", {
2638
+ id: d.id
2639
+ });
2640
+ });
2641
+ const onResolveThread = () => {
2642
+ props.handle.change((t) => {
2643
+ t.isResolved = true;
2644
+ });
2645
+ };
2646
+ const onReopenThread = () => {
2647
+ props.handle.change((t) => {
2648
+ t.isResolved = false;
2649
+ });
2650
+ };
2651
+ const onReplyToComment = () => {
2652
+ const contactUrl = currentContactUrl();
2653
+ if (!contactUrl) return;
2654
+ createReply({
2655
+ threadHandle: props.handle,
2656
+ content: "",
2657
+ contactUrl
2658
+ });
2659
+ };
2660
+ const onDeleteComment = (commentHandle) => {
2661
+ commentHandle.remove();
2662
+ if (props.handle.doc()?.comments.length === 0) {
2663
+ props.handle.remove();
2664
+ }
2665
+ };
2666
+ const onSaveDraft = () => {
2667
+ const h = draftCommentHandle();
2668
+ if (!h) return;
2669
+ h.change((comment) => {
2670
+ comment.content = comment.draftContent;
2671
+ comment.timestamp = Date.now();
2672
+ delete comment.draftContent;
2673
+ });
2674
+ };
2675
+ const onCancelDraft = () => {
2676
+ const h = draftCommentHandle();
2677
+ if (!h) return;
2678
+ const commentValue = h.doc();
2679
+ if (commentValue?.content === void 0) {
2680
+ onDeleteComment(h);
2681
+ return;
2682
+ }
2683
+ h.change((comment) => {
2684
+ delete comment.draftContent;
2685
+ });
2686
+ };
2687
+ return createComponent(Show, {
2688
+ get when() {
2689
+ return thread();
2690
+ },
2691
+ get children() {
2692
+ return [(() => {
2693
+ var _el$ = _tmpl$3(), _el$2 = _el$.firstChild;
2694
+ insert(_el$2, createComponent(Show, {
2695
+ get when() {
2696
+ return isResolved();
2697
+ },
2698
+ get children() {
2699
+ return _tmpl$();
2700
+ }
2701
+ }), null);
2702
+ insert(_el$2, createComponent(Show, {
2703
+ get when() {
2704
+ return isDocLevel();
2705
+ },
2706
+ get children() {
2707
+ return _tmpl$2();
2708
+ }
2709
+ }), null);
2710
+ insert(_el$2, createComponent(For, {
2711
+ get each() {
2712
+ return commentIds();
2713
+ },
2714
+ children: (commentId) => {
2715
+ const commentHandle = createMemo(() => props.handle.sub("comments", {
2716
+ id: commentId
2717
+ }));
2718
+ return createComponent(Show, {
2719
+ get when() {
2720
+ return commentHandle();
2721
+ },
2722
+ children: (handle) => createComponent(CommentView, {
2723
+ get commentHandle() {
2724
+ return handle();
2725
+ },
2726
+ get currentContactUrl() {
2727
+ return currentContactUrl();
2728
+ },
2729
+ get repo() {
2730
+ return props.repo;
2731
+ }
2732
+ })
2733
+ });
2734
+ }
2735
+ }), null);
2736
+ effect(() => className(_el$, cardClass()));
2737
+ return _el$;
2738
+ })(), createComponent(Show, {
2739
+ get when() {
2740
+ return draftComment() || isResolved() || isPrimary();
2741
+ },
2742
+ get children() {
2743
+ var _el$5 = _tmpl$6();
2744
+ insert(_el$5, createComponent(Show, {
2745
+ get when() {
2746
+ return draftComment();
2747
+ },
2748
+ get fallback() {
2749
+ return createComponent(Show, {
2750
+ get when() {
2751
+ return isResolved();
2752
+ },
2753
+ get fallback() {
2754
+ return [(() => {
2755
+ var _el$9 = _tmpl$8();
2756
+ _el$9.$$click = onResolveThread;
2757
+ return _el$9;
2758
+ })(), (() => {
2759
+ var _el$0 = _tmpl$9();
2760
+ _el$0.$$click = onReplyToComment;
2761
+ return _el$0;
2762
+ })()];
2763
+ },
2764
+ get children() {
2765
+ var _el$8 = _tmpl$7();
2766
+ _el$8.$$click = onReopenThread;
2767
+ return _el$8;
2768
+ }
2769
+ });
2770
+ },
2771
+ get children() {
2772
+ return [(() => {
2773
+ var _el$6 = _tmpl$4();
2774
+ _el$6.$$click = onCancelDraft;
2775
+ return _el$6;
2776
+ })(), (() => {
2777
+ var _el$7 = _tmpl$5();
2778
+ _el$7.$$click = onSaveDraft;
2779
+ return _el$7;
2780
+ })()];
2781
+ }
2782
+ }));
2783
+ return _el$5;
2784
+ }
2785
+ })];
2786
+ }
2787
+ });
2788
+ }
2789
+ function CommentView(props) {
2790
+ const [comment, setComment] = createSignal(void 0);
2791
+ createEffect(() => {
2792
+ const h = props.commentHandle;
2793
+ setComment(() => h.doc());
2794
+ const onChange = () => setComment(() => h.doc());
2795
+ h.on("change", onChange);
2796
+ onCleanup(() => h.off("change", onChange));
2797
+ });
2798
+ const contactUrl = () => comment()?.contactUrl;
2799
+ const [contact] = useDocument(contactUrl, {
2800
+ repo: props.repo
2801
+ });
2802
+ const isDraft = () => {
2803
+ const c = comment();
2804
+ if (!c) return false;
2805
+ return c.draftContent !== void 0 || c.content === void 0;
2806
+ };
2807
+ const shouldRender = () => {
2808
+ const c = comment();
2809
+ if (!c) return false;
2810
+ if (isDraft() && c.contactUrl !== props.currentContactUrl) return false;
2811
+ return true;
2812
+ };
2813
+ const contactName = () => {
2814
+ const ct = contact();
2815
+ return ct?.type === "registered" ? ct.name : "Anonymous";
2816
+ };
2817
+ return createComponent(Show, {
2818
+ get when() {
2819
+ return memo(() => !!shouldRender())() && comment();
2820
+ },
2821
+ get children() {
2822
+ var _el$1 = _tmpl$1(), _el$10 = _el$1.firstChild, _el$11 = _el$10.firstChild, _el$12 = _el$11.firstChild, _el$13 = _el$12.nextSibling;
2823
+ _el$12._$owner = getOwner();
2824
+ insert(_el$13, contactName);
2825
+ insert(_el$10, createComponent(Show, {
2826
+ get when() {
2827
+ return memo(() => !!!isDraft())() && comment().timestamp;
2828
+ },
2829
+ get children() {
2830
+ var _el$14 = _tmpl$0();
2831
+ insert(_el$14, () => relativeTime(comment().timestamp));
2832
+ return _el$14;
2833
+ }
2834
+ }), null);
2835
+ insert(_el$1, createComponent(Show, {
2836
+ get when() {
2837
+ return isDraft();
2838
+ },
2839
+ get fallback() {
2840
+ return (() => {
2841
+ var _el$15 = _tmpl$10();
2842
+ insert(_el$15, createComponent(CuteText, {
2843
+ text: () => comment()?.content ?? ""
2844
+ }));
2845
+ return _el$15;
2846
+ })();
2847
+ },
2848
+ get children() {
2849
+ return createComponent(CuteDraftEditor, {
2850
+ get commentHandle() {
2851
+ return props.commentHandle;
2852
+ },
2853
+ get repo() {
2854
+ return props.repo;
2855
+ }
2856
+ });
2857
+ }
2858
+ }), null);
2859
+ effect((_p$) => {
2860
+ var _v$ = props.commentHandle.url, _v$2 = comment().contactUrl;
2861
+ _v$ !== _p$.e && setAttribute(_el$1, "data-id", _p$.e = _v$);
2862
+ _v$2 !== _p$.t && (_el$12.docUrl = _p$.t = _v$2);
2863
+ return _p$;
2864
+ }, {
2865
+ e: void 0,
2866
+ t: void 0
2867
+ });
2868
+ return _el$1;
2869
+ }
2870
+ });
2871
+ }
2872
+ function CuteDraftEditor(props) {
2873
+ let parent;
2874
+ onMount(() => {
2875
+ const handle = props.commentHandle;
2876
+ if (typeof handle.doc()?.draftContent !== "string") {
2877
+ handle.change((c) => {
2878
+ if (typeof c.draftContent !== "string") c.draftContent = "";
2879
+ });
2880
+ }
2881
+ const editor = createCuteEditor({
2882
+ handle,
2883
+ path: ["draftContent"],
2884
+ schema: defaultSchema,
2885
+ // `defaultAutocompletes` is a map keyed by id; the editor wants the specs
2886
+ // as a flat array (e.g. the built-in emoji `:` completer).
2887
+ autocompletes: Object.values(defaultAutocompletes),
2888
+ parent,
2889
+ repo: props.repo
2890
+ });
2891
+ editor.view.focus();
2892
+ const onKeyDown = (event) => {
2893
+ if (!(event.metaKey || event.ctrlKey) || event.code !== "Space") return;
2894
+ event.preventDefault();
2895
+ event.stopPropagation();
2896
+ const draft = handle.doc()?.draftContent ?? "";
2897
+ if (!draft.trim()) return;
2898
+ handle.change((c) => {
2899
+ c.content = c.draftContent;
2900
+ c.timestamp = Date.now();
2901
+ delete c.draftContent;
2902
+ });
2903
+ };
2904
+ parent.addEventListener("keydown", onKeyDown, true);
2905
+ onCleanup(() => {
2906
+ parent.removeEventListener("keydown", onKeyDown, true);
2907
+ editor.destroy();
2908
+ });
2909
+ });
2910
+ return (() => {
2911
+ var _el$16 = _tmpl$11();
2912
+ var _ref$ = parent;
2913
+ typeof _ref$ === "function" ? use(_ref$, _el$16) : parent = _el$16;
2914
+ return _el$16;
2915
+ })();
2916
+ }
2917
+ function createElementAttribute(element, name, fallback) {
2918
+ const read2 = () => element.getAttribute(name) ?? fallback;
2919
+ const [value, setValue] = createSignal(read2());
2920
+ onMount(() => {
2921
+ const observer = new MutationObserver(() => setValue(read2()));
2922
+ observer.observe(element, {
2923
+ attributes: true,
2924
+ attributeFilter: [name]
2925
+ });
2926
+ setValue(read2());
2927
+ onCleanup(() => observer.disconnect());
2928
+ });
2929
+ return value;
2930
+ }
2931
+ delegateEvents(["click"]);
2932
+ function ensureCuteStyle() {
2933
+ const id = "cute-txt-style";
2934
+ if (typeof document === "undefined" || document.getElementById(id)) return;
2935
+ const style = document.createElement("style");
2936
+ style.id = id;
2937
+ style.textContent = STYLE;
2938
+ document.head.append(style);
2939
+ }
2940
+ ensureCuteStyle();
2941
+ const renderCommentsView = (_handle, element) => {
2942
+ return render(() => createComponent(RepoContext.Provider, {
2943
+ get value() {
2944
+ return element.repo;
2945
+ },
2946
+ get children() {
2947
+ return createComponent(CommentsView, {
2948
+ element
2949
+ });
2950
+ }
2951
+ }), element);
2952
+ };
2953
+ const renderCommentThread = (handle, element) => {
2954
+ return render(() => createComponent(RepoContext.Provider, {
2955
+ get value() {
2956
+ return element.repo;
2957
+ },
2958
+ get children() {
2959
+ return createComponent(CommentThreadView, {
2960
+ handle,
2961
+ element,
2962
+ get repo() {
2963
+ return element.repo;
2964
+ }
2965
+ });
2966
+ }
2967
+ }), element);
2968
+ };
2969
+ export {
2970
+ renderCommentThread,
2971
+ renderCommentsView
2972
+ };
2973
+ //# sourceMappingURL=main-C8GVksuY.js.map