@hachej/boring-workspace 0.1.64 → 0.1.66

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