@hachej/boring-workspace 0.1.63 → 0.1.65

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