@hachej/boring-workspace 0.1.47 → 0.1.48

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.
package/dist/testing.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { jsx as Ba } from "react/jsx-runtime";
2
2
  import * as Pa from "react";
3
3
  import { createElement as is, useMemo as wn, useLayoutEffect as us, isValidElement as ss, cloneElement as ds, useSyncExternalStore as Gi } from "react";
4
- import { h as cs, q as fs, o as ps } from "./WorkspaceProvider-CSwoKjTp.js";
4
+ import { h as cs, q as fs, o as ps } from "./WorkspaceProvider-CRd7lb76.js";
5
5
  import { d as ms } from "./panel-DnvDNQac.js";
6
6
  import * as bs from "react-dom/test-utils";
7
7
  import ka from "react-dom";
@@ -567,6 +567,13 @@ export declare interface DispatchContext {
567
567
  openWorkbenchSources?: () => void;
568
568
  /** Close the workbench pane when a command opened it only for an ephemeral task. */
569
569
  closeWorkbench?: () => void;
570
+ /**
571
+ * Park an op that couldn't run because the surface never mounted within the
572
+ * retry budget (collapsed surface / warmup overlay). The host flushes parked
573
+ * ops when the SurfaceShell next becomes ready. Without this the op is
574
+ * silently dropped.
575
+ */
576
+ enqueue?: (run: (surface: SurfaceShellApi) => void) => void;
570
577
  }
571
578
 
572
579
  export declare function DockviewShell({ layout, persistedLayout, onReady, onLayoutChange, allowedPanels, className, prefixHeaderActions, rightHeaderActions, watermarkComponent, }: DockviewShellProps): JSX.Element;
@@ -688,10 +695,14 @@ declare class FetchClient {
688
695
  * `FileConflictError` so the editor can ask the user to reload or
689
696
  * force-overwrite. The returned `mtimeMs` is the server's stat
690
697
  * after the write; callers use it as the OCC baseline for the
691
- * next save.
698
+ * next save. Set `returnMtimeMs: false` only for writes that do
699
+ * not need a fresh OCC baseline (for example, creating an empty
700
+ * file before immediately opening/refetching it). That lets remote
701
+ * sandboxes skip an expensive post-write stat.
692
702
  */
693
703
  writeFile(path: string, content: string, opts?: {
694
704
  expectedMtimeMs?: number;
705
+ returnMtimeMs?: boolean;
695
706
  }): Promise<{
696
707
  mtimeMs?: number;
697
708
  }>;
@@ -830,6 +841,15 @@ export declare const filesystemPlugin: BoringFrontFactoryWithId;
830
841
 
831
842
  export declare function FileTree({ files, selectedPath, searchQuery, height, editing, revealPath, pendingPaths, onSelect, onExpand, onCollapse, onContextMenu, onSubmitEdit, onCancelEdit, onRevealHandled, onDragDrop, className, }: FileTreeProps): JSX.Element;
832
843
 
844
+ /**
845
+ * The minimal slice of {@link WorkspaceBridge} a file tree needs: click-to-open
846
+ * (`openFile`), initial selection (`getActiveFile`), reactive reveal (`select`,
847
+ * plus optional `subscribe`). Lives here (core bridge layer) — not in the
848
+ * filesystem plugin — so core chrome (WorkbenchLeftPane) can forward a
849
+ * surface-backed adapter without importing a plugin module.
850
+ */
851
+ declare type FileTreeBridge = Pick<WorkspaceBridge, "openFile" | "getActiveFile" | "select"> & Partial<Pick<WorkspaceBridge, "subscribe">>;
852
+
833
853
  declare interface FileTreeEditState {
834
854
  /** Path of the row currently being edited (rename target or draft path). */
835
855
  path: string;
@@ -875,7 +895,7 @@ export declare interface FileTreePaneProps extends Partial<PaneProps<FileTreePan
875
895
  rootDir?: string;
876
896
  searchQuery?: string;
877
897
  panelApi?: DockviewPanelApi;
878
- bridge?: WorkspaceBridge;
898
+ bridge?: FileTreeBridge;
879
899
  chromeless?: boolean;
880
900
  className?: string;
881
901
  }
@@ -922,7 +942,7 @@ export declare interface FileTreeViewProps {
922
942
  rootDir?: string;
923
943
  /** Already-debounced query. Empty/undefined means no filter. */
924
944
  searchQuery?: string;
925
- bridge?: Pick<WorkspaceBridge, "openFile" | "getActiveFile" | "select"> & Partial<Pick<WorkspaceBridge, "subscribe">>;
945
+ bridge?: FileTreeBridge;
926
946
  revealFileTreeRequest?: {
927
947
  path: string;
928
948
  seq: number;
@@ -1865,7 +1885,7 @@ declare interface WorkbenchLeftPaneOpenPanelConfig {
1865
1885
 
1866
1886
  export declare interface WorkbenchLeftPaneProps {
1867
1887
  rootDir?: string;
1868
- bridge?: WorkspaceBridge;
1888
+ bridge?: FileTreeBridge;
1869
1889
  defaultTab?: WorkbenchLeftTabId;
1870
1890
  revealFileTreeRequest?: {
1871
1891
  path: string;
package/dist/workspace.js CHANGED
@@ -1,17 +1,17 @@
1
1
  var V = Object.defineProperty;
2
2
  var X = (e, t, r) => t in e ? V(e, t, { enumerable: !0, configurable: !0, writable: !0, value: r }) : e[t] = r;
3
3
  var T = (e, t, r) => X(e, typeof t != "symbol" ? t + "" : t, r);
4
- import { u as K, p as Q, a as Y, b as Z, D as ee } from "./WorkspaceProvider-CSwoKjTp.js";
5
- import { A as Je, C as Ge, c as Ve, d as Xe, e as Qe, F as Ye, f as Ze, M as et, g as tt, P as rt, h as at, i as nt, j as st, k as ot, R as it, S as lt, l as ct, m as dt, T as ut, U as pt, W as ft, n as mt, o as ht, q as gt, r as bt, s as yt, t as vt, v as xt, w as kt, x as wt, y as Pt, z as St, B as Ct, E as Nt, G as Et, H as Rt, I as Tt, J as It, K as Ot, L as Ft, N as Lt, O as Mt, Q as Bt, V as Wt, X as Dt, Y as Kt, Z as $t, _ as jt, $ as zt, a0 as Ht, a1 as Ut, a2 as _t, a3 as qt, a4 as At, a5 as Jt, a6 as Gt, a7 as Vt, a8 as Xt, a9 as Qt, aa as Yt, ab as Zt, ac as er, ad as tr, ae as rr, af as ar, ag as nr, ah as sr, ai as or, aj as ir, ak as lr, al as cr, am as dr, an as ur, ao as pr, ap as fr, aq as mr, ar as hr, as as gr } from "./WorkspaceProvider-CSwoKjTp.js";
4
+ import { u as K, p as Q, a as Y, b as Z, D as ee } from "./WorkspaceProvider-CRd7lb76.js";
5
+ import { A as Je, C as Ge, c as Ve, d as Xe, e as Qe, F as Ye, f as Ze, M as et, g as tt, P as rt, h as at, i as nt, j as st, k as ot, R as it, S as lt, l as ct, m as dt, T as ut, U as pt, W as ft, n as mt, o as ht, q as gt, r as bt, s as yt, t as vt, v as xt, w as kt, x as wt, y as Pt, z as St, B as Ct, E as Nt, G as Et, H as Rt, I as Tt, J as It, K as Ot, L as Ft, N as Lt, O as Mt, Q as Bt, V as Wt, X as Dt, Y as Kt, Z as $t, _ as jt, $ as zt, a0 as Ht, a1 as Ut, a2 as _t, a3 as qt, a4 as At, a5 as Jt, a6 as Gt, a7 as Vt, a8 as Xt, a9 as Qt, aa as Yt, ab as Zt, ac as er, ad as tr, ae as rr, af as ar, ag as nr, ah as sr, ai as or, aj as ir, ak as lr, al as cr, am as dr, an as ur, ao as pr, ap as fr, aq as mr, ar as hr, as as gr } from "./WorkspaceProvider-CRd7lb76.js";
6
6
  import { c as C } from "./utils-B6yFEsav.js";
7
- import { C as yr, T as vr, W as xr, b as kr } from "./WorkspaceLoadingState-BoFzsDji.js";
7
+ import { C as yr, T as vr, W as xr, b as kr } from "./WorkspaceLoadingState-CjXix_Fa.js";
8
8
  import { jsx as a, jsxs as g, Fragment as te } from "react/jsx-runtime";
9
9
  import { Button as P, Sheet as re, SheetContent as ae, SheetHeader as ne, SheetTitle as se, SheetDescription as oe, EmptyState as ie, Kbd as I, ErrorState as le, IconButton as O } from "@hachej/boring-ui-kit";
10
10
  import { Toaster as Pr, dismissToast as Sr, toast as Cr } from "@hachej/boring-ui-kit";
11
11
  import { useSyncExternalStore as $, useState as N, useEffect as k, useRef as S, useCallback as b, useMemo as w, Suspense as ce, Component as de } from "react";
12
12
  import { C as Er, c as Rr } from "./CodeEditor-DQqOn4xz.js";
13
- import { FileTree as Ir } from "./FileTree-DKzSeYJX.js";
14
- import { MarkdownEditor as Fr } from "./MarkdownEditor-FJtYn6Xx.js";
13
+ import { FileTree as Ir } from "./FileTree-CoZWkHqn.js";
14
+ import { MarkdownEditor as Fr } from "./MarkdownEditor-DOZy49fg.js";
15
15
  import { MenuIcon as ue, PanelLeftOpenIcon as pe, PanelLeftCloseIcon as fe, PinIcon as me, CheckIcon as he, CopyIcon as ge } from "lucide-react";
16
16
  import { d as Mr } from "./panel-DnvDNQac.js";
17
17
  function We() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hachej/boring-workspace",
3
- "version": "0.1.47",
3
+ "version": "0.1.48",
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.",
@@ -135,9 +135,9 @@
135
135
  "tailwind-merge": "^2.0.0",
136
136
  "zod": "^3.23.0",
137
137
  "zustand": "^5.0.0",
138
- "@hachej/boring-agent": "0.1.47",
139
- "@hachej/boring-ui-kit": "0.1.47",
140
- "@hachej/boring-ui-plugin-cli": "0.1.47"
138
+ "@hachej/boring-agent": "0.1.48",
139
+ "@hachej/boring-ui-kit": "0.1.48",
140
+ "@hachej/boring-ui-plugin-cli": "0.1.48"
141
141
  },
142
142
  "devDependencies": {
143
143
  "@tailwindcss/postcss": "^4.0.0",
@@ -1,289 +0,0 @@
1
- import { jsx as c, jsxs as z } from "react/jsx-runtime";
2
- import { useRef as D, useEffect as A, useMemo as M, useCallback as w, createContext as S, useContext as $ } from "react";
3
- import { Tree as E } from "react-arborist";
4
- import { FolderOpenIcon as J, FolderIcon as K, ChevronRightIcon as U, Loader2Icon as V } from "lucide-react";
5
- import { L as X } from "./WorkspaceProvider-CSwoKjTp.js";
6
- import { EmptyState as Y, Input as Z } from "@hachej/boring-ui-kit";
7
- import { c as N } from "./utils-B6yFEsav.js";
8
- import { createDragDropManager as y } from "dnd-core";
9
- import { HTML5Backend as G } from "react-dnd-html5-backend";
10
- const F = Symbol.for("@hachej/boring-workspace/file-tree-dnd-manager");
11
- function H() {
12
- const e = globalThis;
13
- return e[F] ?? (e[F] = y(
14
- G,
15
- typeof window > "u" ? void 0 : window
16
- )), e[F];
17
- }
18
- const P = /* @__PURE__ */ new Set(), C = S({
19
- onContextMenu: void 0,
20
- editing: null,
21
- pendingPaths: P,
22
- onSubmitEdit: void 0,
23
- onCancelEdit: void 0
24
- });
25
- function Q({
26
- initialValue: e,
27
- onSubmit: u,
28
- onCancel: s,
29
- isDraft: f
30
- }) {
31
- const r = D(null), a = D(!1);
32
- A(() => {
33
- const t = r.current;
34
- if (t)
35
- if (t.focus(), !f && e.includes(".")) {
36
- const o = e.lastIndexOf(".");
37
- t.setSelectionRange(0, o);
38
- } else
39
- t.select();
40
- }, [e, f]);
41
- const l = () => {
42
- var o;
43
- if (a.current) return;
44
- a.current = !0;
45
- const t = ((o = r.current) == null ? void 0 : o.value.trim()) ?? "";
46
- !t || t === e ? s() : u(t);
47
- };
48
- return /* @__PURE__ */ c(
49
- Z,
50
- {
51
- ref: r,
52
- type: "text",
53
- defaultValue: e,
54
- "data-testid": "file-tree-edit-input",
55
- "aria-label": f ? "Name" : "Rename",
56
- onPointerDown: (t) => t.stopPropagation(),
57
- onClick: (t) => t.stopPropagation(),
58
- onKeyDown: (t) => {
59
- t.stopPropagation(), t.key === "Enter" ? (t.preventDefault(), l()) : t.key === "Escape" && (t.preventDefault(), a.current = !0, s());
60
- },
61
- onBlur: l,
62
- className: "h-5 min-w-0 flex-1 rounded-sm border-[color:var(--accent)]/60 px-1 text-[13px] leading-[1.2] focus-visible:ring-[color:var(--accent)]"
63
- }
64
- );
65
- }
66
- function tt(e, u) {
67
- if (!(u != null && u.trim())) return e.length;
68
- const s = u.trim().toLowerCase(), f = (r) => {
69
- var l;
70
- let a = 0;
71
- for (const t of r) {
72
- const o = t.name.toLowerCase().includes(s), p = (l = t.children) != null && l.length ? f(t.children) : 0;
73
- (o || p > 0) && (a += 1);
74
- }
75
- return a;
76
- };
77
- return f(e);
78
- }
79
- function et({ node: e, style: u, dragHandle: s }) {
80
- const { onContextMenu: f, editing: r, pendingPaths: a, onSubmitEdit: l, onCancelEdit: t } = $(C), o = e.data, p = o.kind === "dir", h = (r == null ? void 0 : r.path) === o.path, b = a.has(o.path), k = p ? e.isOpen ? J : K : X(o.name || "untitled");
81
- return /* @__PURE__ */ z(
82
- "div",
83
- {
84
- ref: s,
85
- style: u,
86
- className: N(
87
- "group relative mx-1 flex items-center gap-1.5 rounded-md px-2 py-0.5 text-[13px] leading-[1.4] cursor-pointer select-none text-foreground",
88
- "transition-colors duration-150 ease-[cubic-bezier(0.22,1,0.36,1)]",
89
- !h && "hover:bg-foreground/[0.04]",
90
- e.isSelected && !h && "bg-[oklch(from_var(--accent)_l_c_h/0.10)] text-foreground font-medium",
91
- e.willReceiveDrop && "bg-foreground/5 outline outline-1 outline-border"
92
- ),
93
- onClick: (d) => {
94
- h || (d.stopPropagation(), p ? e.toggle() : (e.select(), e.activate()));
95
- },
96
- onContextMenu: (d) => {
97
- h || o.isDraft || (d.preventDefault(), d.stopPropagation(), f == null || f(d, o));
98
- },
99
- children: [
100
- p ? /* @__PURE__ */ c(
101
- U,
102
- {
103
- className: N(
104
- "h-3 w-3 shrink-0 text-muted-foreground/70 transition-transform duration-150 ease-[cubic-bezier(0.22,1,0.36,1)]",
105
- e.isOpen && "rotate-90"
106
- ),
107
- strokeWidth: 2
108
- }
109
- ) : /* @__PURE__ */ c("span", { className: "w-3 shrink-0" }),
110
- /* @__PURE__ */ c(
111
- k,
112
- {
113
- className: N(
114
- "h-4 w-4 shrink-0",
115
- e.isSelected ? "text-[color:var(--accent)]" : "text-muted-foreground/80"
116
- ),
117
- strokeWidth: 1.5
118
- }
119
- ),
120
- h ? /* @__PURE__ */ c(
121
- Q,
122
- {
123
- initialValue: (r == null ? void 0 : r.initialValue) ?? o.name ?? "",
124
- isDraft: !!(r != null && r.isDraft),
125
- onSubmit: (d) => l == null ? void 0 : l(o.path, d),
126
- onCancel: () => t == null ? void 0 : t()
127
- }
128
- ) : /* @__PURE__ */ c(
129
- "span",
130
- {
131
- className: N(
132
- "truncate",
133
- b && "text-muted-foreground italic"
134
- ),
135
- children: o.name
136
- }
137
- ),
138
- b && !h && /* @__PURE__ */ c(
139
- V,
140
- {
141
- "data-testid": "file-tree-pending-spinner",
142
- "aria-label": "Pending",
143
- className: "ml-auto h-3 w-3 shrink-0 animate-spin text-muted-foreground/70",
144
- strokeWidth: 2
145
- }
146
- )
147
- ]
148
- }
149
- );
150
- }
151
- function lt({
152
- files: e,
153
- selectedPath: u,
154
- searchQuery: s,
155
- height: f = 400,
156
- editing: r,
157
- revealPath: a,
158
- pendingPaths: l,
159
- onSelect: t,
160
- onExpand: o,
161
- onCollapse: p,
162
- onContextMenu: h,
163
- onSubmitEdit: b,
164
- onCancelEdit: k,
165
- onRevealHandled: d,
166
- onDragDrop: T,
167
- className: I
168
- }) {
169
- const v = D(null);
170
- A(() => {
171
- if (!(r != null && r.isDraft)) return;
172
- const n = requestAnimationFrame(() => {
173
- var i;
174
- (i = v.current) == null || i.scrollTo(r.path);
175
- });
176
- return () => cancelAnimationFrame(n);
177
- }, [r == null ? void 0 : r.isDraft, r == null ? void 0 : r.path]), A(() => {
178
- if (!a) return;
179
- let n = 0;
180
- const i = requestAnimationFrame(() => {
181
- const m = v.current;
182
- if (!m) return;
183
- m.openParents(a);
184
- const x = m.get(a);
185
- x && (x.isInternal && x.open(), n = requestAnimationFrame(() => {
186
- var g;
187
- (g = v.current) == null || g.scrollTo(a), d == null || d(a);
188
- }));
189
- });
190
- return () => {
191
- cancelAnimationFrame(i), cancelAnimationFrame(n);
192
- };
193
- }, [e, d, a]);
194
- const L = M(
195
- () => u || void 0,
196
- [u]
197
- ), j = w(
198
- (n) => {
199
- n.data.kind === "file" && (t == null || t(n.data.path));
200
- },
201
- [t]
202
- ), O = w(
203
- (n) => {
204
- var m;
205
- const i = (m = v.current) == null ? void 0 : m.get(n);
206
- i && (i.isOpen ? o == null || o(i.data.path) : p == null || p(i.data.path));
207
- },
208
- [o, p]
209
- ), R = w(
210
- (n) => {
211
- if (!T) return;
212
- const i = !n.parentNode || n.parentNode.isRoot, m = i ? "." : n.parentNode.data.path;
213
- if (!(!i && n.parentNode.data.kind !== "dir"))
214
- for (const x of n.dragNodes) {
215
- const g = x.data.path;
216
- if (m === g || m !== "." && m.startsWith(g + "/")) return;
217
- T(g, m);
218
- }
219
- },
220
- [T]
221
- ), W = w(
222
- (n) => {
223
- if (!n.parentNode || n.parentNode.isRoot) return !1;
224
- if (n.parentNode.data.kind !== "dir") return !0;
225
- for (const i of n.dragNodes)
226
- if (n.parentNode.data.path === i.data.path || n.parentNode.data.path.startsWith(i.data.path + "/")) return !0;
227
- return !1;
228
- },
229
- []
230
- ), q = w(
231
- (n, i) => n.data.name.toLowerCase().includes(i.toLowerCase()),
232
- []
233
- ), B = M(
234
- () => ({
235
- onContextMenu: h,
236
- editing: r ?? null,
237
- pendingPaths: l ?? P,
238
- onSubmitEdit: b,
239
- onCancelEdit: k
240
- }),
241
- [h, r, l, b, k]
242
- ), _ = M(
243
- () => tt(e, s),
244
- [e, s]
245
- );
246
- return e.length === 0 ? /* @__PURE__ */ c(
247
- "div",
248
- {
249
- className: N(
250
- "flex h-full items-center justify-center text-sm text-muted-foreground",
251
- I
252
- ),
253
- children: "No files"
254
- }
255
- ) : _ === 0 ? /* @__PURE__ */ c("div", { className: N("flex h-full items-center justify-center p-6", I), children: /* @__PURE__ */ c(
256
- Y,
257
- {
258
- className: "min-h-0 border-0",
259
- title: "No matching files",
260
- description: s != null && s.trim() ? `No files match “${s.trim()}”.` : "No files match the current filter."
261
- }
262
- ) }) : /* @__PURE__ */ c(C.Provider, { value: B, children: /* @__PURE__ */ c("div", { "data-boring-workspace-part": "file-tree", className: N("file-tree", I), children: /* @__PURE__ */ c(
263
- E,
264
- {
265
- ref: v,
266
- data: e,
267
- idAccessor: "path",
268
- childrenAccessor: "children",
269
- openByDefault: !1,
270
- width: "100%",
271
- height: f,
272
- rowHeight: 26,
273
- indent: 14,
274
- selection: L,
275
- searchTerm: s ?? "",
276
- searchMatch: q,
277
- onActivate: j,
278
- onToggle: O,
279
- onMove: R,
280
- disableDrop: W,
281
- disableEdit: !0,
282
- dndManager: H(),
283
- children: et
284
- }
285
- ) }) });
286
- }
287
- export {
288
- lt as FileTree
289
- };