@hachej/boring-workspace 0.1.16 → 0.1.18

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 (38) hide show
  1. package/README.md +36 -34
  2. package/dist/{FileTree-Dl-qUAB0.js → FileTree-DHVB9rpk.js} +15 -15
  3. package/dist/MarkdownEditor-L1KDH0bM.js +534 -0
  4. package/dist/{WorkspaceLoadingState-CSZfENWe.js → WorkspaceLoadingState-DYDxUYnx.js} +114 -110
  5. package/dist/WorkspaceProvider-CDPaAO5u.js +5971 -0
  6. package/dist/app-front.d.ts +94 -107
  7. package/dist/app-front.js +243 -233
  8. package/dist/app-server.d.ts +130 -15
  9. package/dist/app-server.js +1569 -304
  10. package/dist/{bootstrapServer-BreQ9QBc.d.ts → createInMemoryBridge-BDxDzihm.d.ts} +11 -26
  11. package/dist/manifest-CyNNdfYz.d.ts +58 -0
  12. package/dist/plugin.d.ts +199 -0
  13. package/dist/plugin.js +300 -0
  14. package/dist/server.d.ts +239 -4
  15. package/dist/server.js +901 -78
  16. package/dist/shared.d.ts +4 -112
  17. package/dist/surface-COYagY2m.d.ts +111 -0
  18. package/dist/testing.d.ts +19 -1
  19. package/dist/testing.js +2 -2
  20. package/dist/{agent-tool-DEtfQPVB.d.ts → ui-bridge-Gfh1MMgl.d.ts} +30 -30
  21. package/dist/workspace.css +36 -0
  22. package/dist/workspace.d.ts +165 -120
  23. package/dist/workspace.js +330 -377
  24. package/docs/INTERFACES.md +9 -9
  25. package/docs/PLUGIN_STRUCTURE.md +39 -145
  26. package/docs/PLUGIN_SYSTEM.md +355 -0
  27. package/docs/README.md +6 -1
  28. package/docs/plans/README.md +1 -0
  29. package/docs/plans/archive/HOT_RELOADABLE_AGENT_PLUGINS_PLAN.md +218 -0
  30. package/docs/plans/archive/RELOAD_PLUGGABILITY_PLAN.md +174 -0
  31. package/docs/plans/archive/UNIFIED_PLUGIN_SYSTEM_PLAN.md +769 -0
  32. package/package.json +11 -5
  33. package/dist/CommandPalette-NOEOVkN2.js +0 -5714
  34. package/dist/MarkdownEditor-yc6mFsnI.js +0 -533
  35. package/docs/bridge.md +0 -135
  36. package/docs/panels.md +0 -102
  37. package/docs/plugins.md +0 -158
  38. /package/docs/plans/{MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md → archive/MACRO_PLUGIN_GENERIC_HELPERS_AUDIT.md} +0 -0
@@ -0,0 +1,534 @@
1
+ import { jsxs as H, jsx as e } from "react/jsx-runtime";
2
+ import { useState as E, useCallback as K, useRef as L, useEffect as D, useMemo as Q } from "react";
3
+ import { ReactNodeViewRenderer as Z, NodeViewWrapper as G, useEditor as J, EditorContent as Y, useEditorState as tt } from "@tiptap/react";
4
+ import et from "@tiptap/starter-kit";
5
+ import rt from "@tiptap/extension-underline";
6
+ import it from "@tiptap/extension-link";
7
+ import j from "@tiptap/extension-placeholder";
8
+ import nt from "@tiptap/extension-task-list";
9
+ import at from "@tiptap/extension-task-item";
10
+ import ot from "@tiptap/extension-text-align";
11
+ import lt from "@tiptap/extension-highlight";
12
+ import { Table as st } from "@tiptap/extension-table";
13
+ import { TableRow as ct } from "@tiptap/extension-table-row";
14
+ import { TableHeader as ut } from "@tiptap/extension-table-header";
15
+ import { TableCell as gt } from "@tiptap/extension-table-cell";
16
+ import dt from "@tiptap/extension-image";
17
+ import { c as U } from "./utils-B6yFEsav.js";
18
+ import { ao as q, ap as _ } from "./WorkspaceProvider-CDPaAO5u.js";
19
+ import { uploadFile as ft } from "@hachej/boring-agent/front";
20
+ import mt from "@tiptap/extension-code-block-lowlight";
21
+ import { createLowlight as pt, common as ht } from "lowlight";
22
+ import { Markdown as wt } from "@tiptap/markdown";
23
+ import { BoldIcon as kt, ItalicIcon as vt, UnderlineIcon as bt, StrikethroughIcon as It, Heading1Icon as At, Heading2Icon as yt, Heading3Icon as Lt, ListIcon as xt, ListOrderedIcon as Nt, ListChecksIcon as Ct, QuoteIcon as Tt, CodeIcon as Mt, LinkIcon as Rt, ImageIcon as Ht, HighlighterIcon as Ut, AlignLeftIcon as St, AlignCenterIcon as $t, AlignRightIcon as Bt, MinusIcon as Dt, Loader2Icon as Et } from "lucide-react";
24
+ import { Toolbar as Ft, Input as zt, ToolbarButton as Wt, ToolbarSeparator as Pt } from "@hachej/boring-ui-kit";
25
+ function jt(t) {
26
+ const r = q(), i = _(), [a, s] = E(0);
27
+ return { upload: K(
28
+ async (g, o) => {
29
+ s((p) => p + 1);
30
+ try {
31
+ return await ft(g, {
32
+ apiBaseUrl: r,
33
+ workspaceRequestId: i,
34
+ directory: (o == null ? void 0 : o.directory) ?? (t == null ? void 0 : t.directory),
35
+ sourcePath: o == null ? void 0 : o.sourcePath
36
+ });
37
+ } finally {
38
+ s((p) => p - 1);
39
+ }
40
+ },
41
+ [r, i, t == null ? void 0 : t.directory]
42
+ ), uploading: a > 0 };
43
+ }
44
+ const F = 64, z = 2e3;
45
+ function qt(t) {
46
+ return t.replace(/\\/g, "\\\\").replace(/]/g, "\\]");
47
+ }
48
+ function B(t) {
49
+ return t.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
50
+ }
51
+ function W(t) {
52
+ if (typeof t == "number" && Number.isFinite(t) && t > 0) return t;
53
+ if (typeof t == "string" && t.trim()) {
54
+ const r = Number(t);
55
+ if (Number.isFinite(r) && r > 0) return r;
56
+ }
57
+ return null;
58
+ }
59
+ const O = dt.extend({
60
+ name: "image",
61
+ draggable: !0,
62
+ addOptions() {
63
+ var t;
64
+ return {
65
+ ...(t = this.parent) == null ? void 0 : t.call(this),
66
+ resolveSrc: (r) => r
67
+ };
68
+ },
69
+ parseMarkdown: (t, r) => r.createNode("image", {
70
+ src: t.href,
71
+ title: t.title,
72
+ alt: t.text
73
+ }),
74
+ renderMarkdown: (t) => {
75
+ var y, b, A, n, c, k, I;
76
+ const r = typeof ((y = t.attrs) == null ? void 0 : y.src) == "string" ? t.attrs.src : "", i = typeof ((b = t.attrs) == null ? void 0 : b.alt) == "string" ? t.attrs.alt : "", a = typeof ((A = t.attrs) == null ? void 0 : A.title) == "string" ? t.attrs.title : "", s = W((n = t.attrs) == null ? void 0 : n.width), l = W((c = t.attrs) == null ? void 0 : c.height), g = ((k = t.attrs) == null ? void 0 : k.align) === "center" || ((I = t.attrs) == null ? void 0 : I.align) === "right" ? t.attrs.align : "left";
77
+ if (!s && !l && g === "left") {
78
+ const h = qt(i);
79
+ return a ? `![${h}](${r} "${a.replace(/"/g, '\\"')}")` : `![${h}](${r})`;
80
+ }
81
+ const p = `<img ${[
82
+ `src="${B(r)}"`,
83
+ i ? `alt="${B(i)}"` : null,
84
+ a ? `title="${B(a)}"` : null,
85
+ s ? `width="${s}"` : null,
86
+ l ? `height="${l}"` : null
87
+ ].filter(Boolean).join(" ")} />`;
88
+ return g === "center" || g === "right" ? `<p align="${g}">${p}</p>` : p;
89
+ },
90
+ addAttributes() {
91
+ var r;
92
+ return {
93
+ ...((r = this.parent) == null ? void 0 : r.call(this)) ?? {},
94
+ width: {
95
+ default: null,
96
+ parseHTML: (i) => {
97
+ const a = i.getAttribute("width");
98
+ if (a) {
99
+ const l = parseInt(a, 10);
100
+ return Number.isFinite(l) ? l : null;
101
+ }
102
+ const s = i.style.width;
103
+ if (s && s.endsWith("px")) {
104
+ const l = parseInt(s, 10);
105
+ return Number.isFinite(l) ? l : null;
106
+ }
107
+ return null;
108
+ },
109
+ renderHTML: (i) => i.width ? { width: String(i.width) } : {}
110
+ },
111
+ align: {
112
+ default: "left",
113
+ parseHTML: (i) => {
114
+ const a = i.getAttribute("data-align");
115
+ return a === "left" || a === "center" || a === "right" ? a : i.style.marginLeft === "auto" && i.style.marginRight === "auto" ? "center" : i.style.marginLeft === "auto" ? "right" : "left";
116
+ },
117
+ renderHTML: (i) => ({ "data-align": i.align ?? "left" })
118
+ },
119
+ // Transient marker used by MarkdownEditor.insertImageRef to find THIS
120
+ // specific inserted image when its background upload completes, even
121
+ // if the user pasted the same file twice (which produces two image
122
+ // nodes with identical `src` data URLs). Never serialized to HTML or
123
+ // markdown — purely in-memory state for the swap.
124
+ pendingUploadId: {
125
+ default: null,
126
+ parseHTML: () => null,
127
+ renderHTML: () => ({})
128
+ }
129
+ };
130
+ },
131
+ addNodeView() {
132
+ return Z(_t);
133
+ }
134
+ });
135
+ function _t({ node: t, updateAttributes: r, selected: i, extension: a }) {
136
+ const s = L(null), l = L(null), [g, o] = E(!1), p = t.attrs.src ?? "", b = (a.options.resolveSrc ?? ((h) => h))(p), A = t.attrs.alt ?? "", n = t.attrs.title ?? void 0, c = t.attrs.width, k = t.attrs.align ?? "left";
137
+ D(() => {
138
+ if (!g) return;
139
+ const h = (x) => {
140
+ const u = l.current;
141
+ if (!u) return;
142
+ const d = x.clientX - u.x, m = Math.max(F, Math.min(z, u.width + d));
143
+ r({ width: Math.round(m) });
144
+ }, v = () => {
145
+ l.current = null, o(!1);
146
+ };
147
+ return window.addEventListener("pointermove", h), window.addEventListener("pointerup", v), window.addEventListener("pointercancel", v), () => {
148
+ window.removeEventListener("pointermove", h), window.removeEventListener("pointerup", v), window.removeEventListener("pointercancel", v);
149
+ };
150
+ }, [g, r]);
151
+ const I = (h) => {
152
+ var u;
153
+ h.preventDefault(), h.stopPropagation();
154
+ const v = (u = s.current) == null ? void 0 : u.querySelector("img"), x = c ?? (v == null ? void 0 : v.getBoundingClientRect().width) ?? 320;
155
+ l.current = { x: h.clientX, width: x }, o(!0);
156
+ };
157
+ return /* @__PURE__ */ H(
158
+ G,
159
+ {
160
+ ref: s,
161
+ "data-resizable-image": "",
162
+ "data-selected": i ? "true" : void 0,
163
+ className: U(
164
+ "relative block max-w-full align-baseline",
165
+ "[&>img]:block [&>img]:max-w-full [&>img]:h-auto [&>img]:rounded-md",
166
+ k === "left" && "mr-auto",
167
+ k === "center" && "mx-auto",
168
+ k === "right" && "ml-auto",
169
+ i && "ring-2 ring-[color:var(--accent)] ring-offset-1 ring-offset-background"
170
+ ),
171
+ style: { width: c ? `${c}px` : void 0 },
172
+ children: [
173
+ /* @__PURE__ */ e("img", { src: b, alt: A, title: n, draggable: !1 }),
174
+ /* @__PURE__ */ e(
175
+ "span",
176
+ {
177
+ role: "slider",
178
+ "aria-label": "Resize image",
179
+ "aria-valuenow": c ?? 0,
180
+ "aria-valuemin": F,
181
+ "aria-valuemax": z,
182
+ "data-testid": "resize-handle",
183
+ onPointerDown: I,
184
+ className: U(
185
+ "absolute right-0 bottom-0 h-3 w-3 translate-x-1/2 translate-y-1/2 cursor-nwse-resize",
186
+ "rounded-full border border-background bg-[color:var(--accent)] shadow",
187
+ "opacity-0 transition-opacity duration-150 ease-out",
188
+ (i || g) && "opacity-100"
189
+ )
190
+ }
191
+ )
192
+ ]
193
+ }
194
+ );
195
+ }
196
+ const Ot = pt(ht), Vt = [
197
+ /<script[\s>][\s\S]*?<\/script>/gi,
198
+ /<iframe[\s>][\s\S]*?<\/iframe>/gi,
199
+ /\s+on\w+\s*=\s*["'][^"']*["']/gi,
200
+ /\s+on\w+\s*=\s*\S+/gi,
201
+ /href\s*=\s*["']?\s*javascript:[^"'\s>]*/gi,
202
+ /src\s*=\s*["']?\s*javascript:[^"'\s>]*/gi
203
+ ];
204
+ function Xt(t) {
205
+ let r = t;
206
+ for (const i of Vt)
207
+ r = r.replace(i, "");
208
+ return r;
209
+ }
210
+ function Kt(t) {
211
+ return /^(?:[a-z][a-z0-9+.-]*:|\/|#)/i.test(t);
212
+ }
213
+ function Qt(t, r) {
214
+ const i = t.match(/^([^?#]*)([?#].*)?$/), a = (i == null ? void 0 : i[1]) ?? t, s = (i == null ? void 0 : i[2]) ?? "", l = r != null && r.includes("/") ? r.slice(0, r.lastIndexOf("/")) : "", g = `${l ? `${l}/` : ""}${a}`.split("/"), o = [];
215
+ for (const p of g)
216
+ !p || p === "." || (p === ".." ? o.pop() : o.push(p));
217
+ return `${o.join("/")}${s}`;
218
+ }
219
+ function Zt(t, r, i, a) {
220
+ if (!t || Kt(t)) return t;
221
+ const s = Qt(t, r), l = i.replace(/\/$/, ""), g = new URLSearchParams({ path: s });
222
+ return a && g.set("workspaceId", a), `${l}/api/v1/files/raw?${g.toString()}`;
223
+ }
224
+ const Gt = [
225
+ et.configure({
226
+ codeBlock: !1,
227
+ link: !1,
228
+ underline: !1
229
+ }),
230
+ rt,
231
+ it.configure({
232
+ openOnClick: !0,
233
+ autolink: !0,
234
+ linkOnPaste: !0,
235
+ defaultProtocol: "https",
236
+ HTMLAttributes: { rel: "noopener noreferrer nofollow", target: "_blank" }
237
+ }),
238
+ j.configure({
239
+ placeholder: "Start writing..."
240
+ }),
241
+ nt,
242
+ at.configure({ nested: !0 }),
243
+ ot.configure({
244
+ types: ["heading", "paragraph"]
245
+ }),
246
+ lt,
247
+ st.configure({
248
+ resizable: !0
249
+ }),
250
+ ct,
251
+ ut,
252
+ gt,
253
+ O.configure({
254
+ inline: !1,
255
+ allowBase64: !0
256
+ }),
257
+ mt.configure({ lowlight: Ot }),
258
+ wt.configure({
259
+ markedOptions: {
260
+ gfm: !0,
261
+ breaks: !1,
262
+ pedantic: !1
263
+ }
264
+ })
265
+ ];
266
+ function f({ onClick: t, active: r, disabled: i, title: a, children: s }) {
267
+ return /* @__PURE__ */ e(
268
+ Wt,
269
+ {
270
+ type: "button",
271
+ size: "icon-xs",
272
+ onClick: t,
273
+ disabled: i,
274
+ title: a,
275
+ "aria-pressed": r,
276
+ className: U(
277
+ "text-muted-foreground/70",
278
+ r && "bg-[color:var(--accent-soft)] text-[color:var(--accent)]"
279
+ ),
280
+ children: s
281
+ }
282
+ );
283
+ }
284
+ function N() {
285
+ return /* @__PURE__ */ e(Pt, { className: "mx-2 h-3.5" });
286
+ }
287
+ function P(t) {
288
+ const r = t.trim().toLowerCase();
289
+ return !r.startsWith("javascript:") && !r.startsWith("data:text/html");
290
+ }
291
+ function Jt(t) {
292
+ return new Promise((r, i) => {
293
+ const a = new FileReader();
294
+ a.onload = () => r(a.result), a.onerror = () => i(a.error ?? new Error("Read failed")), a.readAsDataURL(t);
295
+ });
296
+ }
297
+ function Yt(t) {
298
+ if (!t) return null;
299
+ for (const r of Array.from(t.files ?? []))
300
+ if (r.type.startsWith("image/")) return r;
301
+ for (const r of Array.from(t.items ?? [])) {
302
+ if (r.kind !== "file" || !r.type.startsWith("image/")) continue;
303
+ const i = r.getAsFile();
304
+ if (i) return i;
305
+ }
306
+ return null;
307
+ }
308
+ function te({
309
+ editor: t,
310
+ onInsertImage: r,
311
+ rawMode: i,
312
+ onToggleRawMode: a,
313
+ uploading: s
314
+ }) {
315
+ const l = (n) => {
316
+ if (t) {
317
+ if (t.isActive("image")) {
318
+ t.chain().focus().updateAttributes("image", { align: n }).run();
319
+ return;
320
+ }
321
+ t.chain().focus().setTextAlign(n).run();
322
+ }
323
+ }, g = L(null), o = tt({
324
+ editor: t,
325
+ selector: ({ editor: n }) => n ? {
326
+ bold: n.isActive("bold"),
327
+ italic: n.isActive("italic"),
328
+ underline: n.isActive("underline"),
329
+ strike: n.isActive("strike"),
330
+ h1: n.isActive("heading", { level: 1 }),
331
+ h2: n.isActive("heading", { level: 2 }),
332
+ h3: n.isActive("heading", { level: 3 }),
333
+ bulletList: n.isActive("bulletList"),
334
+ orderedList: n.isActive("orderedList"),
335
+ taskList: n.isActive("taskList"),
336
+ blockquote: n.isActive("blockquote"),
337
+ codeBlock: n.isActive("codeBlock"),
338
+ link: n.isActive("link"),
339
+ highlight: n.isActive("highlight"),
340
+ alignLeft: n.isActive("image") ? (n.getAttributes("image").align ?? "left") === "left" : n.isActive({ textAlign: "left" }),
341
+ alignCenter: n.isActive("image") ? n.getAttributes("image").align === "center" : n.isActive({ textAlign: "center" }),
342
+ alignRight: n.isActive("image") ? n.getAttributes("image").align === "right" : n.isActive({ textAlign: "right" })
343
+ } : null
344
+ });
345
+ if (!t || !o) return null;
346
+ const p = () => {
347
+ const n = window.prompt("URL:");
348
+ n && P(n) && t.chain().focus().extendMarkRange("link").setLink({ href: n }).run();
349
+ }, y = () => {
350
+ const n = window.prompt("Image URL:");
351
+ n && P(n) && t.chain().focus().setImage({ src: n }).run();
352
+ }, b = (n) => {
353
+ var c;
354
+ if (n != null && n.shiftKey) {
355
+ y();
356
+ return;
357
+ }
358
+ (c = g.current) == null || c.click();
359
+ }, A = async (n) => {
360
+ var k;
361
+ const c = (k = n.target.files) == null ? void 0 : k[0];
362
+ n.target.value = "", !(!c || !c.type.startsWith("image/")) && await r(c);
363
+ };
364
+ return /* @__PURE__ */ H(Ft, { className: "border-b border-border/60 bg-background px-3 py-1.5", "aria-label": "Formatting toolbar", children: [
365
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().toggleBold().run(), active: o.bold, title: "Bold", children: /* @__PURE__ */ e(kt, { className: "h-4 w-4" }) }),
366
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().toggleItalic().run(), active: o.italic, title: "Italic", children: /* @__PURE__ */ e(vt, { className: "h-4 w-4" }) }),
367
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().toggleUnderline().run(), active: o.underline, title: "Underline", children: /* @__PURE__ */ e(bt, { className: "h-4 w-4" }) }),
368
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().toggleStrike().run(), active: o.strike, title: "Strikethrough", children: /* @__PURE__ */ e(It, { className: "h-4 w-4" }) }),
369
+ /* @__PURE__ */ e(N, {}),
370
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().toggleHeading({ level: 1 }).run(), active: o.h1, title: "Heading 1", children: /* @__PURE__ */ e(At, { className: "h-4 w-4" }) }),
371
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().toggleHeading({ level: 2 }).run(), active: o.h2, title: "Heading 2", children: /* @__PURE__ */ e(yt, { className: "h-4 w-4" }) }),
372
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().toggleHeading({ level: 3 }).run(), active: o.h3, title: "Heading 3", children: /* @__PURE__ */ e(Lt, { className: "h-4 w-4" }) }),
373
+ /* @__PURE__ */ e(N, {}),
374
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().toggleBulletList().run(), active: o.bulletList, title: "Bullet list", children: /* @__PURE__ */ e(xt, { className: "h-4 w-4" }) }),
375
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().toggleOrderedList().run(), active: o.orderedList, title: "Ordered list", children: /* @__PURE__ */ e(Nt, { className: "h-4 w-4" }) }),
376
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().toggleTaskList().run(), active: o.taskList, title: "Task list", children: /* @__PURE__ */ e(Ct, { className: "h-4 w-4" }) }),
377
+ /* @__PURE__ */ e(N, {}),
378
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().toggleBlockquote().run(), active: o.blockquote, title: "Quote", children: /* @__PURE__ */ e(Tt, { className: "h-4 w-4" }) }),
379
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().toggleCodeBlock().run(), active: o.codeBlock, title: "Code block", children: /* @__PURE__ */ e(Mt, { className: "h-4 w-4" }) }),
380
+ /* @__PURE__ */ e(f, { onClick: p, active: o.link, title: "Link", children: /* @__PURE__ */ e(Rt, { className: "h-4 w-4" }) }),
381
+ /* @__PURE__ */ e(
382
+ f,
383
+ {
384
+ onClick: b,
385
+ title: "Image (click to upload, Shift+click for URL)",
386
+ children: /* @__PURE__ */ e(Ht, { className: "h-4 w-4" })
387
+ }
388
+ ),
389
+ /* @__PURE__ */ e(
390
+ zt,
391
+ {
392
+ ref: g,
393
+ "data-testid": "image-file-input",
394
+ type: "file",
395
+ accept: "image/*",
396
+ className: "hidden",
397
+ onChange: A
398
+ }
399
+ ),
400
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().toggleHighlight().run(), active: o.highlight, title: "Highlight", children: /* @__PURE__ */ e(Ut, { className: "h-4 w-4" }) }),
401
+ /* @__PURE__ */ e(N, {}),
402
+ /* @__PURE__ */ e(f, { onClick: () => l("left"), active: o.alignLeft, title: "Align left", children: /* @__PURE__ */ e(St, { className: "h-4 w-4" }) }),
403
+ /* @__PURE__ */ e(f, { onClick: () => l("center"), active: o.alignCenter, title: "Center align", children: /* @__PURE__ */ e($t, { className: "h-4 w-4" }) }),
404
+ /* @__PURE__ */ e(f, { onClick: () => l("right"), active: o.alignRight, title: "Align right", children: /* @__PURE__ */ e(Bt, { className: "h-4 w-4" }) }),
405
+ /* @__PURE__ */ e(N, {}),
406
+ /* @__PURE__ */ e(f, { onClick: () => t.chain().focus().setHorizontalRule().run(), title: "Horizontal rule", children: /* @__PURE__ */ e(Dt, { className: "h-4 w-4" }) }),
407
+ /* @__PURE__ */ e(N, {}),
408
+ s && /* @__PURE__ */ H(
409
+ "span",
410
+ {
411
+ role: "status",
412
+ "aria-live": "polite",
413
+ className: "ml-auto flex items-center gap-1.5 text-[11px] text-muted-foreground/80",
414
+ children: [
415
+ /* @__PURE__ */ e(Et, { className: "h-3 w-3 animate-spin", "aria-hidden": !0 }),
416
+ "Uploading…"
417
+ ]
418
+ }
419
+ ),
420
+ /* @__PURE__ */ e(
421
+ f,
422
+ {
423
+ onClick: a,
424
+ active: i,
425
+ title: i ? "Rich text" : "Raw markdown",
426
+ children: /* @__PURE__ */ e("span", { className: "font-mono text-[10px] font-semibold leading-none tracking-[-0.02em]", children: "MD" })
427
+ }
428
+ )
429
+ ] });
430
+ }
431
+ function xe({
432
+ content: t,
433
+ onChange: r,
434
+ readOnly: i = !1,
435
+ placeholder: a,
436
+ className: s,
437
+ documentPath: l
438
+ }) {
439
+ const g = q(), o = _(), { upload: p, uploading: y } = jt(), [b, A] = E(!1), n = Q(() => {
440
+ const d = O.configure({
441
+ inline: !1,
442
+ allowBase64: !0,
443
+ resolveSrc: (w) => Zt(w, l, g, o)
444
+ }), m = Gt.map((w) => w.name === "image" ? d : w);
445
+ return a ? [
446
+ ...m.filter((w) => w.name !== "placeholder"),
447
+ j.configure({ placeholder: a })
448
+ ] : m;
449
+ }, [g, l, a, o]), c = L(r);
450
+ c.current = r;
451
+ const k = L(!1), I = L(null), h = L(async () => {
452
+ });
453
+ h.current = async (d) => {
454
+ const m = I.current;
455
+ if (!m) return;
456
+ let w;
457
+ try {
458
+ w = await Jt(d);
459
+ } catch {
460
+ return;
461
+ }
462
+ const S = typeof crypto < "u" && typeof crypto.randomUUID == "function" ? crypto.randomUUID() : `pending-${Date.now()}-${Math.random().toString(36).slice(2)}`, V = { src: w, alt: d.name, pendingUploadId: S };
463
+ m.chain().focus().setImage(V).run();
464
+ try {
465
+ const { url: C } = await p(d, { sourcePath: l }), T = I.current;
466
+ if (!T || T.isDestroyed) return;
467
+ T.commands.command(({ tr: $, state: M }) => (M.doc.descendants((R, X) => (R.type.name === "image" && R.attrs.pendingUploadId === S && $.setNodeMarkup(X, void 0, { ...R.attrs, src: C, pendingUploadId: null }), !0)), !0));
468
+ } catch {
469
+ const C = I.current;
470
+ if (!C || C.isDestroyed) return;
471
+ C.commands.command(({ tr: T, state: $ }) => ($.doc.descendants((M, R) => (M.type.name === "image" && M.attrs.pendingUploadId === S && T.setNodeMarkup(R, void 0, { ...M.attrs, pendingUploadId: null }), !0)), !0));
472
+ }
473
+ };
474
+ const v = t || { type: "doc", content: [{ type: "paragraph" }] }, x = t ? "markdown" : "json", u = J({
475
+ extensions: n,
476
+ content: v,
477
+ contentType: x,
478
+ editable: !i,
479
+ editorProps: {
480
+ attributes: {
481
+ class: "tiptap-prose max-w-[68ch] px-8 py-6 focus:outline-none min-h-[200px]"
482
+ },
483
+ transformPastedHTML: Xt,
484
+ handlePaste: (d, m) => {
485
+ if (i) return !1;
486
+ const w = Yt(m.clipboardData);
487
+ return w ? (m.preventDefault(), h.current(w), !0) : !1;
488
+ }
489
+ },
490
+ onUpdate: ({ editor: d }) => {
491
+ var m, w;
492
+ k.current || (w = c.current) == null || w.call(c, ((m = d.getMarkdown) == null ? void 0 : m.call(d)) ?? d.getHTML());
493
+ }
494
+ });
495
+ return I.current = u, D(() => {
496
+ !u || u.isDestroyed || u.setEditable(!i);
497
+ }, [u, i]), D(() => {
498
+ var m;
499
+ !u || u.isDestroyed || (((m = u.getMarkdown) == null ? void 0 : m.call(u)) ?? u.getHTML()) === t || (k.current = !0, u.commands.setContent(v, { contentType: x }), k.current = !1);
500
+ }, [u, t]), /* @__PURE__ */ H("div", { className: U("flex h-full flex-col overflow-hidden", s), children: [
501
+ !i && /* @__PURE__ */ e(
502
+ te,
503
+ {
504
+ editor: u,
505
+ onInsertImage: (d) => h.current(d),
506
+ rawMode: b,
507
+ onToggleRawMode: () => A((d) => !d),
508
+ uploading: y
509
+ }
510
+ ),
511
+ /* @__PURE__ */ e("div", { className: "flex-1 overflow-auto", children: b && !i ? /* @__PURE__ */ e(
512
+ "textarea",
513
+ {
514
+ "aria-label": "Raw markdown",
515
+ "data-testid": "markdown-raw-editor",
516
+ className: "h-full min-h-[200px] w-full resize-none bg-background px-8 py-6 font-mono text-[13px] leading-6 text-foreground outline-none placeholder:text-muted-foreground/70",
517
+ value: t,
518
+ placeholder: a,
519
+ spellCheck: !1,
520
+ onChange: (d) => {
521
+ var m;
522
+ return (m = c.current) == null ? void 0 : m.call(c, d.target.value);
523
+ }
524
+ }
525
+ ) : /* @__PURE__ */ e(Y, { editor: u }) })
526
+ ] });
527
+ }
528
+ export {
529
+ xe as MarkdownEditor,
530
+ P as isSafeUrl,
531
+ Zt as rawFileUrlForMarkdownImage,
532
+ Jt as readFileAsDataUrl,
533
+ Xt as sanitizeHtml
534
+ };