@hachej/boring-workspace 0.1.13 → 0.1.14

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