@hachej/boring-workspace 0.1.5 → 0.1.7

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.
@@ -2,7 +2,7 @@ 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
4
  import { FolderOpenIcon as z, FolderIcon as B, ChevronRightIcon as K, Loader2Icon as E } from "lucide-react";
5
- import { K as Y } from "./CommandPalette-aM61U-b0.js";
5
+ import { K as Y } from "./CommandPalette-DzIDQUfk.js";
6
6
  import { Input as G } from "@hachej/boring-ui-kit";
7
7
  import { c as v } from "./utils-B6yFEsav.js";
8
8
  const T = /* @__PURE__ */ new Set(), A = H({
@@ -0,0 +1,474 @@
1
+ import { jsxs as C, jsx as e } from "react/jsx-runtime";
2
+ import { useState as x, useCallback as U, useRef as A, useEffect as N } from "react";
3
+ import { ReactNodeViewRenderer as $, NodeViewWrapper as E, useEditor as P, EditorContent as F, useEditorState as D } from "@tiptap/react";
4
+ import z from "@tiptap/starter-kit";
5
+ import W from "@tiptap/extension-underline";
6
+ import q from "@tiptap/extension-link";
7
+ import S from "@tiptap/extension-placeholder";
8
+ import j from "@tiptap/extension-task-list";
9
+ import _ from "@tiptap/extension-task-item";
10
+ import V from "@tiptap/extension-text-align";
11
+ import X from "@tiptap/extension-highlight";
12
+ import { Table as K } from "@tiptap/extension-table";
13
+ import { TableRow as Q } from "@tiptap/extension-table-row";
14
+ import { TableHeader as O } from "@tiptap/extension-table-header";
15
+ import { TableCell as Z } from "@tiptap/extension-table-cell";
16
+ import G from "@tiptap/extension-image";
17
+ import { c as L } from "./utils-B6yFEsav.js";
18
+ import { uploadFile as J } from "@hachej/boring-agent/front";
19
+ import { an as Y, ao as tt } from "./CommandPalette-DzIDQUfk.js";
20
+ import et from "@tiptap/extension-code-block-lowlight";
21
+ import { createLowlight as it, common as rt } from "lowlight";
22
+ import { Markdown as nt } from "@tiptap/markdown";
23
+ import { BoldIcon as at, ItalicIcon as ot, UnderlineIcon as lt, StrikethroughIcon as st, Heading1Icon as ct, Heading2Icon as ut, Heading3Icon as gt, ListIcon as ft, ListOrderedIcon as dt, ListChecksIcon as mt, QuoteIcon as ht, CodeIcon as pt, LinkIcon as wt, ImageIcon as kt, HighlighterIcon as bt, AlignLeftIcon as vt, AlignCenterIcon as At, AlignRightIcon as It, MinusIcon as Lt } from "lucide-react";
24
+ import { Toolbar as yt, Input as Nt, ToolbarButton as Ct, ToolbarSeparator as xt } from "@hachej/boring-ui-kit";
25
+ function Tt(t) {
26
+ const r = Y(), n = tt(), [a, s] = x(0);
27
+ return { upload: U(
28
+ async (o, h) => {
29
+ s((b) => b + 1);
30
+ try {
31
+ return await J(o, {
32
+ apiBaseUrl: r,
33
+ workspaceRequestId: n,
34
+ directory: (h == null ? void 0 : h.directory) ?? (t == null ? void 0 : t.directory),
35
+ sourcePath: h == null ? void 0 : h.sourcePath
36
+ });
37
+ } finally {
38
+ s((b) => b - 1);
39
+ }
40
+ },
41
+ [r, n, t == null ? void 0 : t.directory]
42
+ ), uploading: a > 0 };
43
+ }
44
+ const T = 64, R = 2e3;
45
+ function Rt(t) {
46
+ return t.replace(/\\/g, "\\\\").replace(/]/g, "\\]");
47
+ }
48
+ function y(t) {
49
+ return t.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
50
+ }
51
+ function H(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 Ht = G.extend({
60
+ name: "image",
61
+ draggable: !0,
62
+ parseMarkdown: (t, r) => r.createNode("image", {
63
+ src: t.href,
64
+ title: t.title,
65
+ alt: t.text
66
+ }),
67
+ renderMarkdown: (t) => {
68
+ var p, k, i, d, m, w, c;
69
+ const r = typeof ((p = t.attrs) == null ? void 0 : p.src) == "string" ? t.attrs.src : "", n = typeof ((k = t.attrs) == null ? void 0 : k.alt) == "string" ? t.attrs.alt : "", a = typeof ((i = t.attrs) == null ? void 0 : i.title) == "string" ? t.attrs.title : "", s = H((d = t.attrs) == null ? void 0 : d.width), g = H((m = t.attrs) == null ? void 0 : m.height), o = ((w = t.attrs) == null ? void 0 : w.align) === "center" || ((c = t.attrs) == null ? void 0 : c.align) === "right" ? t.attrs.align : "left";
70
+ if (!s && !g && o === "left") {
71
+ const l = Rt(n);
72
+ return a ? `![${l}](${r} "${a.replace(/"/g, '\\"')}")` : `![${l}](${r})`;
73
+ }
74
+ const b = `<img ${[
75
+ `src="${y(r)}"`,
76
+ n ? `alt="${y(n)}"` : null,
77
+ a ? `title="${y(a)}"` : null,
78
+ s ? `width="${s}"` : null,
79
+ g ? `height="${g}"` : null
80
+ ].filter(Boolean).join(" ")} />`;
81
+ return o === "center" || o === "right" ? `<p align="${o}">${b}</p>` : b;
82
+ },
83
+ addAttributes() {
84
+ var r;
85
+ return {
86
+ ...((r = this.parent) == null ? void 0 : r.call(this)) ?? {},
87
+ width: {
88
+ default: null,
89
+ parseHTML: (n) => {
90
+ const a = n.getAttribute("width");
91
+ if (a) {
92
+ const g = parseInt(a, 10);
93
+ return Number.isFinite(g) ? g : null;
94
+ }
95
+ const s = n.style.width;
96
+ if (s && s.endsWith("px")) {
97
+ const g = parseInt(s, 10);
98
+ return Number.isFinite(g) ? g : null;
99
+ }
100
+ return null;
101
+ },
102
+ renderHTML: (n) => n.width ? { width: String(n.width) } : {}
103
+ },
104
+ align: {
105
+ default: "left",
106
+ parseHTML: (n) => {
107
+ const a = n.getAttribute("data-align");
108
+ return a === "left" || a === "center" || a === "right" ? a : n.style.marginLeft === "auto" && n.style.marginRight === "auto" ? "center" : n.style.marginLeft === "auto" ? "right" : "left";
109
+ },
110
+ renderHTML: (n) => ({ "data-align": n.align ?? "left" })
111
+ }
112
+ };
113
+ },
114
+ addNodeView() {
115
+ return $(Mt);
116
+ }
117
+ });
118
+ function Mt({ node: t, updateAttributes: r, selected: n }) {
119
+ const a = A(null), s = A(null), [g, o] = x(!1), h = t.attrs.src ?? "", b = t.attrs.alt ?? "", p = t.attrs.title ?? void 0, k = t.attrs.width, i = t.attrs.align ?? "left";
120
+ N(() => {
121
+ if (!g) return;
122
+ const m = (c) => {
123
+ const l = s.current;
124
+ if (!l) return;
125
+ const f = c.clientX - l.x, v = Math.max(T, Math.min(R, l.width + f));
126
+ r({ width: Math.round(v) });
127
+ }, w = () => {
128
+ s.current = null, o(!1);
129
+ };
130
+ return window.addEventListener("pointermove", m), window.addEventListener("pointerup", w), window.addEventListener("pointercancel", w), () => {
131
+ window.removeEventListener("pointermove", m), window.removeEventListener("pointerup", w), window.removeEventListener("pointercancel", w);
132
+ };
133
+ }, [g, r]);
134
+ const d = (m) => {
135
+ var l;
136
+ m.preventDefault(), m.stopPropagation();
137
+ const w = (l = a.current) == null ? void 0 : l.querySelector("img"), c = k ?? (w == null ? void 0 : w.getBoundingClientRect().width) ?? 320;
138
+ s.current = { x: m.clientX, width: c }, o(!0);
139
+ };
140
+ return /* @__PURE__ */ C(
141
+ E,
142
+ {
143
+ ref: a,
144
+ "data-resizable-image": "",
145
+ "data-selected": n ? "true" : void 0,
146
+ className: L(
147
+ "relative block max-w-full align-baseline",
148
+ "[&>img]:block [&>img]:max-w-full [&>img]:h-auto [&>img]:rounded-md",
149
+ i === "left" && "mr-auto",
150
+ i === "center" && "mx-auto",
151
+ i === "right" && "ml-auto",
152
+ n && "ring-2 ring-[color:var(--accent)] ring-offset-1 ring-offset-background"
153
+ ),
154
+ style: { width: k ? `${k}px` : void 0 },
155
+ children: [
156
+ /* @__PURE__ */ e("img", { src: h, alt: b, title: p, draggable: !1 }),
157
+ /* @__PURE__ */ e(
158
+ "span",
159
+ {
160
+ role: "slider",
161
+ "aria-label": "Resize image",
162
+ "aria-valuenow": k ?? 0,
163
+ "aria-valuemin": T,
164
+ "aria-valuemax": R,
165
+ "data-testid": "resize-handle",
166
+ onPointerDown: d,
167
+ className: L(
168
+ "absolute right-0 bottom-0 h-3 w-3 translate-x-1/2 translate-y-1/2 cursor-nwse-resize",
169
+ "rounded-full border border-background bg-[color:var(--accent)] shadow",
170
+ "opacity-0 transition-opacity duration-150 ease-out",
171
+ (n || g) && "opacity-100"
172
+ )
173
+ }
174
+ )
175
+ ]
176
+ }
177
+ );
178
+ }
179
+ const Bt = it(rt), St = [
180
+ /<script[\s>][\s\S]*?<\/script>/gi,
181
+ /<iframe[\s>][\s\S]*?<\/iframe>/gi,
182
+ /\s+on\w+\s*=\s*["'][^"']*["']/gi,
183
+ /\s+on\w+\s*=\s*\S+/gi,
184
+ /href\s*=\s*["']?\s*javascript:[^"'\s>]*/gi,
185
+ /src\s*=\s*["']?\s*javascript:[^"'\s>]*/gi
186
+ ];
187
+ function Ut(t) {
188
+ let r = t;
189
+ for (const n of St)
190
+ r = r.replace(n, "");
191
+ return r;
192
+ }
193
+ const M = [
194
+ z.configure({
195
+ codeBlock: !1,
196
+ link: !1,
197
+ underline: !1
198
+ }),
199
+ W,
200
+ q.configure({
201
+ openOnClick: !0,
202
+ autolink: !0,
203
+ linkOnPaste: !0,
204
+ defaultProtocol: "https",
205
+ HTMLAttributes: { rel: "noopener noreferrer nofollow", target: "_blank" }
206
+ }),
207
+ S.configure({
208
+ placeholder: "Start writing..."
209
+ }),
210
+ j,
211
+ _.configure({ nested: !0 }),
212
+ V.configure({
213
+ types: ["heading", "paragraph"]
214
+ }),
215
+ X,
216
+ K.configure({
217
+ resizable: !0
218
+ }),
219
+ Q,
220
+ O,
221
+ Z,
222
+ Ht.configure({
223
+ inline: !1,
224
+ allowBase64: !0
225
+ }),
226
+ et.configure({ lowlight: Bt }),
227
+ nt.configure({
228
+ markedOptions: {
229
+ gfm: !0,
230
+ breaks: !1,
231
+ pedantic: !1
232
+ }
233
+ })
234
+ ];
235
+ function u({ onClick: t, active: r, disabled: n, title: a, children: s }) {
236
+ return /* @__PURE__ */ e(
237
+ Ct,
238
+ {
239
+ type: "button",
240
+ size: "icon-xs",
241
+ onClick: t,
242
+ disabled: n,
243
+ title: a,
244
+ "aria-pressed": r,
245
+ className: L(
246
+ "text-muted-foreground/70",
247
+ r && "bg-[color:var(--accent-soft)] text-[color:var(--accent)]"
248
+ ),
249
+ children: s
250
+ }
251
+ );
252
+ }
253
+ function I() {
254
+ return /* @__PURE__ */ e(xt, { className: "mx-2 h-3.5" });
255
+ }
256
+ function B(t) {
257
+ const r = t.trim().toLowerCase();
258
+ return !r.startsWith("javascript:") && !r.startsWith("data:text/html");
259
+ }
260
+ function $t(t) {
261
+ return new Promise((r, n) => {
262
+ const a = new FileReader();
263
+ a.onload = () => r(a.result), a.onerror = () => n(a.error ?? new Error("Read failed")), a.readAsDataURL(t);
264
+ });
265
+ }
266
+ function Et(t) {
267
+ if (!t) return null;
268
+ for (const r of Array.from(t.files ?? []))
269
+ if (r.type.startsWith("image/")) return r;
270
+ for (const r of Array.from(t.items ?? [])) {
271
+ if (r.kind !== "file" || !r.type.startsWith("image/")) continue;
272
+ const n = r.getAsFile();
273
+ if (n) return n;
274
+ }
275
+ return null;
276
+ }
277
+ function Pt({
278
+ editor: t,
279
+ onInsertImage: r,
280
+ rawMode: n,
281
+ onToggleRawMode: a
282
+ }) {
283
+ const s = (i) => {
284
+ if (t) {
285
+ if (t.isActive("image")) {
286
+ t.chain().focus().updateAttributes("image", { align: i }).run();
287
+ return;
288
+ }
289
+ t.chain().focus().setTextAlign(i).run();
290
+ }
291
+ }, g = A(null), o = D({
292
+ editor: t,
293
+ selector: ({ editor: i }) => i ? {
294
+ bold: i.isActive("bold"),
295
+ italic: i.isActive("italic"),
296
+ underline: i.isActive("underline"),
297
+ strike: i.isActive("strike"),
298
+ h1: i.isActive("heading", { level: 1 }),
299
+ h2: i.isActive("heading", { level: 2 }),
300
+ h3: i.isActive("heading", { level: 3 }),
301
+ bulletList: i.isActive("bulletList"),
302
+ orderedList: i.isActive("orderedList"),
303
+ taskList: i.isActive("taskList"),
304
+ blockquote: i.isActive("blockquote"),
305
+ codeBlock: i.isActive("codeBlock"),
306
+ link: i.isActive("link"),
307
+ highlight: i.isActive("highlight"),
308
+ alignLeft: i.isActive("image") ? (i.getAttributes("image").align ?? "left") === "left" : i.isActive({ textAlign: "left" }),
309
+ alignCenter: i.isActive("image") ? i.getAttributes("image").align === "center" : i.isActive({ textAlign: "center" }),
310
+ alignRight: i.isActive("image") ? i.getAttributes("image").align === "right" : i.isActive({ textAlign: "right" })
311
+ } : null
312
+ });
313
+ if (!t || !o) return null;
314
+ const h = () => {
315
+ const i = window.prompt("URL:");
316
+ i && B(i) && t.chain().focus().extendMarkRange("link").setLink({ href: i }).run();
317
+ }, b = () => {
318
+ const i = window.prompt("Image URL:");
319
+ i && B(i) && t.chain().focus().setImage({ src: i }).run();
320
+ }, p = (i) => {
321
+ var d;
322
+ if (i != null && i.shiftKey) {
323
+ b();
324
+ return;
325
+ }
326
+ (d = g.current) == null || d.click();
327
+ }, k = async (i) => {
328
+ var m;
329
+ const d = (m = i.target.files) == null ? void 0 : m[0];
330
+ i.target.value = "", !(!d || !d.type.startsWith("image/")) && await r(d);
331
+ };
332
+ return /* @__PURE__ */ C(yt, { className: "border-b border-border/60 bg-background px-3 py-1.5", "aria-label": "Formatting toolbar", children: [
333
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleBold().run(), active: o.bold, title: "Bold", children: /* @__PURE__ */ e(at, { className: "h-4 w-4" }) }),
334
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleItalic().run(), active: o.italic, title: "Italic", children: /* @__PURE__ */ e(ot, { className: "h-4 w-4" }) }),
335
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleUnderline().run(), active: o.underline, title: "Underline", children: /* @__PURE__ */ e(lt, { className: "h-4 w-4" }) }),
336
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleStrike().run(), active: o.strike, title: "Strikethrough", children: /* @__PURE__ */ e(st, { className: "h-4 w-4" }) }),
337
+ /* @__PURE__ */ e(I, {}),
338
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleHeading({ level: 1 }).run(), active: o.h1, title: "Heading 1", children: /* @__PURE__ */ e(ct, { className: "h-4 w-4" }) }),
339
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleHeading({ level: 2 }).run(), active: o.h2, title: "Heading 2", children: /* @__PURE__ */ e(ut, { className: "h-4 w-4" }) }),
340
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleHeading({ level: 3 }).run(), active: o.h3, title: "Heading 3", children: /* @__PURE__ */ e(gt, { className: "h-4 w-4" }) }),
341
+ /* @__PURE__ */ e(I, {}),
342
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleBulletList().run(), active: o.bulletList, title: "Bullet list", children: /* @__PURE__ */ e(ft, { className: "h-4 w-4" }) }),
343
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleOrderedList().run(), active: o.orderedList, title: "Ordered list", children: /* @__PURE__ */ e(dt, { className: "h-4 w-4" }) }),
344
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleTaskList().run(), active: o.taskList, title: "Task list", children: /* @__PURE__ */ e(mt, { className: "h-4 w-4" }) }),
345
+ /* @__PURE__ */ e(I, {}),
346
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleBlockquote().run(), active: o.blockquote, title: "Quote", children: /* @__PURE__ */ e(ht, { className: "h-4 w-4" }) }),
347
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleCodeBlock().run(), active: o.codeBlock, title: "Code block", children: /* @__PURE__ */ e(pt, { className: "h-4 w-4" }) }),
348
+ /* @__PURE__ */ e(u, { onClick: h, active: o.link, title: "Link", children: /* @__PURE__ */ e(wt, { className: "h-4 w-4" }) }),
349
+ /* @__PURE__ */ e(
350
+ u,
351
+ {
352
+ onClick: p,
353
+ title: "Image (click to upload, Shift+click for URL)",
354
+ children: /* @__PURE__ */ e(kt, { className: "h-4 w-4" })
355
+ }
356
+ ),
357
+ /* @__PURE__ */ e(
358
+ Nt,
359
+ {
360
+ ref: g,
361
+ "data-testid": "image-file-input",
362
+ type: "file",
363
+ accept: "image/*",
364
+ className: "hidden",
365
+ onChange: k
366
+ }
367
+ ),
368
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().toggleHighlight().run(), active: o.highlight, title: "Highlight", children: /* @__PURE__ */ e(bt, { className: "h-4 w-4" }) }),
369
+ /* @__PURE__ */ e(I, {}),
370
+ /* @__PURE__ */ e(u, { onClick: () => s("left"), active: o.alignLeft, title: "Align left", children: /* @__PURE__ */ e(vt, { className: "h-4 w-4" }) }),
371
+ /* @__PURE__ */ e(u, { onClick: () => s("center"), active: o.alignCenter, title: "Center align", children: /* @__PURE__ */ e(At, { className: "h-4 w-4" }) }),
372
+ /* @__PURE__ */ e(u, { onClick: () => s("right"), active: o.alignRight, title: "Align right", children: /* @__PURE__ */ e(It, { className: "h-4 w-4" }) }),
373
+ /* @__PURE__ */ e(I, {}),
374
+ /* @__PURE__ */ e(u, { onClick: () => t.chain().focus().setHorizontalRule().run(), title: "Horizontal rule", children: /* @__PURE__ */ e(Lt, { className: "h-4 w-4" }) }),
375
+ /* @__PURE__ */ e(I, {}),
376
+ /* @__PURE__ */ e(
377
+ u,
378
+ {
379
+ onClick: a,
380
+ active: n,
381
+ title: n ? "Rich text" : "Raw markdown",
382
+ children: /* @__PURE__ */ e("span", { className: "font-mono text-[10px] font-semibold leading-none tracking-[-0.02em]", children: "MD" })
383
+ }
384
+ )
385
+ ] });
386
+ }
387
+ function se({
388
+ content: t,
389
+ onChange: r,
390
+ readOnly: n = !1,
391
+ placeholder: a,
392
+ className: s,
393
+ documentPath: g
394
+ }) {
395
+ const { upload: o } = Tt(), [h, b] = x(!1), p = A(r);
396
+ p.current = r;
397
+ const k = A(!1), i = A(null), d = A(async () => {
398
+ });
399
+ d.current = async (l) => {
400
+ const f = i.current;
401
+ if (f)
402
+ try {
403
+ const { url: v } = await o(l, { sourcePath: g });
404
+ f.chain().focus().setImage({ src: v, alt: l.name }).run();
405
+ } catch {
406
+ try {
407
+ const v = await $t(l);
408
+ f.chain().focus().setImage({ src: v, alt: l.name }).run();
409
+ } catch {
410
+ }
411
+ }
412
+ };
413
+ const m = t || { type: "doc", content: [{ type: "paragraph" }] }, w = t ? "markdown" : "json", c = P({
414
+ extensions: a ? [
415
+ ...M.filter((l) => l.name !== "placeholder"),
416
+ S.configure({ placeholder: a })
417
+ ] : M,
418
+ content: m,
419
+ contentType: w,
420
+ editable: !n,
421
+ editorProps: {
422
+ attributes: {
423
+ class: "tiptap-prose max-w-[68ch] px-8 py-6 focus:outline-none min-h-[200px]"
424
+ },
425
+ transformPastedHTML: Ut,
426
+ handlePaste: (l, f) => {
427
+ if (n) return !1;
428
+ const v = Et(f.clipboardData);
429
+ return v ? (f.preventDefault(), d.current(v), !0) : !1;
430
+ }
431
+ },
432
+ onUpdate: ({ editor: l }) => {
433
+ var f, v;
434
+ k.current || (v = p.current) == null || v.call(p, ((f = l.getMarkdown) == null ? void 0 : f.call(l)) ?? l.getHTML());
435
+ }
436
+ });
437
+ return i.current = c, N(() => {
438
+ !c || c.isDestroyed || c.setEditable(!n);
439
+ }, [c, n]), N(() => {
440
+ var f;
441
+ !c || c.isDestroyed || (((f = c.getMarkdown) == null ? void 0 : f.call(c)) ?? c.getHTML()) === t || (k.current = !0, c.commands.setContent(m, { contentType: w }), k.current = !1);
442
+ }, [c, t]), /* @__PURE__ */ C("div", { className: L("flex h-full flex-col overflow-hidden", s), children: [
443
+ !n && /* @__PURE__ */ e(
444
+ Pt,
445
+ {
446
+ editor: c,
447
+ onInsertImage: (l) => d.current(l),
448
+ rawMode: h,
449
+ onToggleRawMode: () => b((l) => !l)
450
+ }
451
+ ),
452
+ /* @__PURE__ */ e("div", { className: "flex-1 overflow-auto", children: h && !n ? /* @__PURE__ */ e(
453
+ "textarea",
454
+ {
455
+ "aria-label": "Raw markdown",
456
+ "data-testid": "markdown-raw-editor",
457
+ 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",
458
+ value: t,
459
+ placeholder: a,
460
+ spellCheck: !1,
461
+ onChange: (l) => {
462
+ var f;
463
+ return (f = p.current) == null ? void 0 : f.call(p, l.target.value);
464
+ }
465
+ }
466
+ ) : /* @__PURE__ */ e(F, { editor: c }) })
467
+ ] });
468
+ }
469
+ export {
470
+ se as MarkdownEditor,
471
+ B as isSafeUrl,
472
+ $t as readFileAsDataUrl,
473
+ Ut as sanitizeHtml
474
+ };
@@ -2,7 +2,7 @@ import { jsxs as d, jsx as o, Fragment as H } from "react/jsx-runtime";
2
2
  import { useCallback as b, useMemo as A, useEffect as S, useState as V, Suspense as X, useRef as q } from "react";
3
3
  import { LoadingState as Z, ResizeHandle as G, IconButton as $, Button as J, Kbd as Q } from "@hachej/boring-ui-kit";
4
4
  import { c as g } from "./utils-B6yFEsav.js";
5
- import { $ as Y, a6 as ee, E as te, am as ne, ak as re, u as oe } from "./CommandPalette-aM61U-b0.js";
5
+ import { $ as Y, a6 as ee, E as te, am as ne, ak as re, u as oe } from "./CommandPalette-DzIDQUfk.js";
6
6
  import { Search as ae, Plus as ie } from "lucide-react";
7
7
  function be(e, t, r = !0) {
8
8
  if (!r || typeof window > "u") return t;
package/dist/app-front.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { jsx as d, jsxs as Te, Fragment as x } from "react/jsx-runtime";
2
2
  import { useSyncExternalStore as ze, useMemo as v, useRef as K, useState as X, useEffect as y, useCallback as P } from "react";
3
3
  import { ChatPanel as Fe, useSessions as Ge } from "@hachej/boring-agent/front";
4
- import { aj as ke, q as Ye, ak as Qe, u as Xe, al as Ze } from "./CommandPalette-aM61U-b0.js";
5
- import { T as He, C as qe, r as Ee, w as et, W as $e } from "./WorkspaceLoadingState-By0dZoPD.js";
4
+ import { aj as ke, q as Ye, ak as Qe, u as Xe, al as Ze } from "./CommandPalette-DzIDQUfk.js";
5
+ import { T as He, C as qe, r as Ee, w as et, W as $e } from "./WorkspaceLoadingState-Cd8nkNHp.js";
6
6
  function tt() {
7
7
  const e = `s${Date.now()}`;
8
8
  return {
package/dist/testing.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx as ka } from "react/jsx-runtime";
2
2
  import * as Pa from "react";
3
3
  import { createElement as ds, useMemo as Tn, useLayoutEffect as fs, isValidElement as ps, cloneElement as ms, useSyncExternalStore as Ji } from "react";
4
- import { i as vs, q as bs, ai as hs } from "./CommandPalette-aM61U-b0.js";
4
+ import { i as vs, q as bs, ai as hs } from "./CommandPalette-DzIDQUfk.js";
5
5
  import { d as ys } from "./panel-DnvDNQac.js";
6
6
  import * as Rs from "react-dom/test-utils";
7
7
  import Ba from "react-dom";
@@ -1807,6 +1807,9 @@
1807
1807
  .max-h-\[440px\] {
1808
1808
  max-height: 440px;
1809
1809
  }
1810
+ .max-h-full {
1811
+ max-height: 100%;
1812
+ }
1810
1813
  .min-h-0 {
1811
1814
  min-height: calc(var(--spacing) * 0);
1812
1815
  }
@@ -1831,6 +1834,9 @@
1831
1834
  .min-h-\[240px\] {
1832
1835
  min-height: 240px;
1833
1836
  }
1837
+ .min-h-\[480px\] {
1838
+ min-height: 480px;
1839
+ }
1834
1840
  .min-h-screen {
1835
1841
  min-height: 100vh;
1836
1842
  }
@@ -2563,6 +2569,9 @@
2563
2569
  .fill-foreground {
2564
2570
  fill: var(--boring-foreground);
2565
2571
  }
2572
+ .object-contain {
2573
+ object-fit: contain;
2574
+ }
2566
2575
  .p-0 {
2567
2576
  padding: calc(var(--spacing) * 0);
2568
2577
  }
@@ -2827,6 +2836,10 @@
2827
2836
  --tw-tracking: -0.01em;
2828
2837
  letter-spacing: -0.01em;
2829
2838
  }
2839
+ .tracking-\[-0\.02em\] {
2840
+ --tw-tracking: -0.02em;
2841
+ letter-spacing: -0.02em;
2842
+ }
2830
2843
  .tracking-\[0\.05em\] {
2831
2844
  --tw-tracking: 0.05em;
2832
2845
  letter-spacing: 0.05em;
@@ -3340,6 +3353,14 @@
3340
3353
  color: var(--boring-muted-foreground);
3341
3354
  }
3342
3355
  }
3356
+ .placeholder\:text-muted-foreground\/70 {
3357
+ &::placeholder {
3358
+ color: var(--boring-muted-foreground);
3359
+ @supports (color: color-mix(in lab, red, red)) {
3360
+ color: color-mix(in oklab, var(--boring-muted-foreground) 70%, transparent);
3361
+ }
3362
+ }
3363
+ }
3343
3364
  .after\:absolute {
3344
3365
  &::after {
3345
3366
  content: var(--tw-content);
@@ -3499,6 +3520,13 @@
3499
3520
  }
3500
3521
  }
3501
3522
  }
3523
+ .hover\:bg-muted {
3524
+ &:hover {
3525
+ @media (hover: hover) {
3526
+ background-color: var(--boring-muted);
3527
+ }
3528
+ }
3529
+ }
3502
3530
  .hover\:bg-muted\/40 {
3503
3531
  &:hover {
3504
3532
  @media (hover: hover) {
@@ -1018,7 +1018,7 @@ export declare interface LeftTabParams {
1018
1018
  chromeless?: boolean;
1019
1019
  }
1020
1020
 
1021
- export declare function MarkdownEditor({ content, onChange, readOnly, placeholder, className, }: MarkdownEditorProps): JSX.Element;
1021
+ export declare function MarkdownEditor({ content, onChange, readOnly, placeholder, className, documentPath, }: MarkdownEditorProps): JSX.Element;
1022
1022
 
1023
1023
  export declare function MarkdownEditorPane({ params, api, className }: MarkdownEditorPaneProps): JSX.Element;
1024
1024
 
@@ -1032,6 +1032,8 @@ export declare interface MarkdownEditorProps {
1032
1032
  readOnly?: boolean;
1033
1033
  placeholder?: string;
1034
1034
  className?: string;
1035
+ /** Workspace-relative markdown file path, used to make uploaded image links relative. */
1036
+ documentPath?: string;
1035
1037
  }
1036
1038
 
1037
1039
  export declare const MAX_PANELS = 50;
package/dist/workspace.js CHANGED
@@ -1,8 +1,8 @@
1
1
  var qe = Object.defineProperty;
2
2
  var _e = (e, t, r) => t in e ? qe(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r;
3
3
  var ne = (e, t, r) => _e(e, typeof t != "symbol" ? t + "" : t, r);
4
- import { d as ae, u as Ne, p as Be, P as Ae, a as $e, b as Ke, D as Ve } from "./CommandPalette-aM61U-b0.js";
5
- import { A as xr, C as yr, c as br, e as vr, f as Sr, F as Nr, g as Cr, M as Tr, h as Pr, i as kr, j as Er, k as wr, l as Mr, R as Dr, S as Ir, m as Rr, n as Or, T as Fr, W as Lr, o as zr, q as qr, r as _r, s as Br, t as Ar, v as $r, w as Kr, x as Vr, y as Qr, z as jr, B as Gr, E as Hr, G as Wr, H as Ur, I as Xr, J as Jr, K as Yr, L as Zr, N as ea, O as ta, Q as ra, U as aa, V as na, X as la, Y as oa, Z as sa, _ as ca, $ as ia, a0 as ua, a1 as da, a2 as pa, a3 as fa, a4 as ma, a5 as ga, a6 as ha, a7 as xa, a8 as ya, a9 as ba, aa as va, ab as Sa, ac as Na, ad as Ca, ae as Ta, af as Pa, ag as ka, ah as Ea } from "./CommandPalette-aM61U-b0.js";
4
+ import { d as ae, u as Ne, p as Be, P as Ae, a as $e, b as Ke, D as Ve } from "./CommandPalette-DzIDQUfk.js";
5
+ import { A as xr, C as yr, c as br, e as vr, f as Sr, F as Nr, g as Cr, M as Tr, h as Pr, i as kr, j as Er, k as wr, l as Mr, R as Dr, S as Ir, m as Rr, n as Or, T as Fr, W as Lr, o as zr, q as qr, r as _r, s as Br, t as Ar, v as $r, w as Kr, x as Vr, y as Qr, z as jr, B as Gr, E as Hr, G as Wr, H as Ur, I as Xr, J as Jr, K as Yr, L as Zr, N as ea, O as ta, Q as ra, U as aa, V as na, X as la, Y as oa, Z as sa, _ as ca, $ as ia, a0 as ua, a1 as da, a2 as pa, a3 as fa, a4 as ma, a5 as ga, a6 as ha, a7 as xa, a8 as ya, a9 as ba, aa as va, ab as Sa, ac as Na, ad as Ca, ae as Ta, af as Pa, ag as ka, ah as Ea } from "./CommandPalette-DzIDQUfk.js";
6
6
  import { jsxs as h, jsx as a, Fragment as Qe } from "react/jsx-runtime";
7
7
  import { useSyncExternalStore as je, useState as V, useEffect as U, useRef as w, useCallback as S, useReducer as Ge, useMemo as Z, Suspense as He, Component as We } from "react";
8
8
  import { FilterIcon as Ue, XIcon as Xe, ChevronDownIcon as Ce, ChevronRightIcon as Te, Database as Je, BarChart3 as pe, MenuIcon as Ye, PanelLeftOpenIcon as Ze, PanelLeftCloseIcon as et, PinIcon as tt } from "lucide-react";
@@ -10,10 +10,10 @@ import { EmptyState as Pe, Toolbar as rt, Input as ke, Popover as at, PopoverTri
10
10
  import { Toaster as Ma, dismissToast as Da, toast as Ia } from "@hachej/boring-ui-kit";
11
11
  import { c as H } from "./utils-B6yFEsav.js";
12
12
  import { d as we } from "./panel-DnvDNQac.js";
13
- import { C as Oa, T as Fa, W as La, b as za } from "./WorkspaceLoadingState-By0dZoPD.js";
13
+ import { C as Oa, T as Fa, W as La, b as za } from "./WorkspaceLoadingState-Cd8nkNHp.js";
14
14
  import { C as _a, c as Ba } from "./CodeEditor-DQqOn4xz.js";
15
- import { FileTree as $a } from "./FileTree-DRq_bfue.js";
16
- import { MarkdownEditor as Va } from "./MarkdownEditor-DjiHxnRv.js";
15
+ import { FileTree as $a } from "./FileTree-CspphShP.js";
16
+ import { MarkdownEditor as Va } from "./MarkdownEditor-wDDxxED5.js";
17
17
  function re(e, t) {
18
18
  const { pluginId: r, ...n } = e;
19
19
  return t ? { ...n, pluginId: t } : n;
@@ -134,8 +134,8 @@ slash commands.
134
134
 
135
135
  - `packages/workspace/src/plugins/filesystemPlugin`
136
136
  - `packages/workspace/src/plugins/dataCatalogPlugin`
137
- - `apps/boring-macro-v2/src/plugins/macro`
138
137
  - `apps/workspace-playground/src/plugins/playgroundDataCatalog`
138
+ - Macro plugin example: `hachej/boring-macro` (`src/plugins/macro`)
139
139
 
140
140
  ## Invariants
141
141
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hachej/boring-workspace",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "description": "Workspace UI, plugin, and bridge package for composing chat, files, catalogs, editors, and app-specific panes.",
@@ -108,6 +108,7 @@
108
108
  "@tiptap/extension-task-list": "^3.22.4",
109
109
  "@tiptap/extension-text-align": "^3.22.4",
110
110
  "@tiptap/extension-underline": "^3.22.4",
111
+ "@tiptap/markdown": "^3.22.4",
111
112
  "@tiptap/pm": "^3.22.4",
112
113
  "@tiptap/react": "^3.22.4",
113
114
  "@tiptap/starter-kit": "^3.22.4",
@@ -122,11 +123,10 @@
122
123
  "radix-ui": "^1.4.3",
123
124
  "react-arborist": "^3.4.0",
124
125
  "tailwind-merge": "^2.0.0",
125
- "tiptap-markdown": "^0.9.0",
126
126
  "zod": "^3.23.0",
127
127
  "zustand": "^5.0.0",
128
- "@hachej/boring-agent": "0.1.5",
129
- "@hachej/boring-ui-kit": "0.1.5"
128
+ "@hachej/boring-ui-kit": "0.1.7",
129
+ "@hachej/boring-agent": "0.1.7"
130
130
  },
131
131
  "devDependencies": {
132
132
  "@tailwindcss/postcss": "^4.0.0",