@floegence/floe-webapp-core 0.31.0 → 0.32.0

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.
@@ -4,6 +4,11 @@ import type { FileItem, ViewMode, ContextMenuCallbacks, ContextMenuItem, FileLis
4
4
  export interface FileBrowserProps {
5
5
  /** File tree data */
6
6
  files: FileItem[];
7
+ /**
8
+ * Controlled current path.
9
+ * When provided, the browser path follows this value and user navigation is emitted via onPathChange.
10
+ */
11
+ path?: string;
7
12
  /** Initial path to display */
8
13
  initialPath?: string;
9
14
  /** Initial view mode */
@@ -12,6 +17,11 @@ export interface FileBrowserProps {
12
17
  initialListColumnRatios?: FileListColumnRatios;
13
18
  /** Callback when navigation occurs */
14
19
  onNavigate?: (path: string) => void;
20
+ /**
21
+ * Callback when the user changes path from inside FileBrowser.
22
+ * Use with `path` for controlled mode.
23
+ */
24
+ onPathChange?: (path: string, source: 'user' | 'programmatic') => void;
15
25
  /** Callback when selection changes */
16
26
  onSelect?: (items: FileItem[]) => void;
17
27
  /** Callback when a file is opened */
@@ -1,23 +1,26 @@
1
- import { createComponent as n, insert as l, addEventListener as P, use as L, memo as w, effect as T, className as W, setStyleProperty as B, template as m, delegateEvents as p } from "solid-js/web";
2
- import { onMount as G, onCleanup as H, createEffect as Q, Show as o } from "solid-js";
1
+ import { createComponent as n, insert as l, addEventListener as P, use as L, memo as w, effect as T, className as W, setStyleProperty as B, template as m, delegateEvents as G } from "solid-js/web";
2
+ import { onMount as H, onCleanup as Q, createEffect as q, Show as a } from "solid-js";
3
3
  import { cn as K } from "../../utils/cn.js";
4
- import { useLayout as q } from "../../context/LayoutContext.js";
5
- import { useFileBrowserDrag as J } from "../../context/FileBrowserDragContext.js";
6
- import { deferAfterPaint as U } from "../../utils/defer.js";
7
- import { FileBrowserProvider as X, useFileBrowser as Y } from "./FileBrowserContext.js";
8
- import { ResizeHandle as Z } from "../layout/ResizeHandle.js";
4
+ import { useLayout as J } from "../../context/LayoutContext.js";
5
+ import { useFileBrowserDrag as U } from "../../context/FileBrowserDragContext.js";
6
+ import { deferAfterPaint as X } from "../../utils/defer.js";
7
+ import { FileBrowserProvider as Y, useFileBrowser as Z } from "./FileBrowserContext.js";
8
+ import { ResizeHandle as p } from "../layout/ResizeHandle.js";
9
9
  import { DirectoryTree as ee } from "./DirectoryTree.js";
10
10
  import { FileListView as te } from "./FileListView.js";
11
11
  import { FileGridView as re } from "./FileGridView.js";
12
12
  import { FileBrowserToolbar as ie } from "./FileBrowserToolbar.js";
13
13
  import { FileContextMenu as ne } from "./FileContextMenu.js";
14
14
  import { DragPreview as le } from "./DragPreview.js";
15
- var oe = /* @__PURE__ */ m('<div class="border-b border-border">'), ae = /* @__PURE__ */ m('<button type=button class="flex items-center justify-center w-5 h-5 rounded cursor-pointer hover:bg-sidebar-accent/80 transition-colors"aria-label="Close sidebar"><svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round class="w-3.5 h-3.5"><path d="M18 6 6 18"></path><path d="m6 6 12 12">'), se = /* @__PURE__ */ m('<div class="absolute inset-0 bg-background/60 backdrop-blur-sm z-[9]">'), de = /* @__PURE__ */ m('<div><div class="flex flex-1 min-h-0 relative"><aside><div class="h-full flex flex-col"><div class="flex items-center justify-between px-3 py-2 border-b border-sidebar-border"><span class="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60">Explorer</span></div><div class="flex-1 min-h-0 overflow-auto py-1"></div></div></aside><div class="flex-1 min-w-0 flex flex-col"><div class="flex-1 min-h-0"></div><div class="flex items-center justify-between px-3 py-1 border-t border-border text-[10px] text-muted-foreground"><span> items</span><span class="truncate max-w-[200px]">');
15
+ var ae = /* @__PURE__ */ m('<div class="border-b border-border">'), oe = /* @__PURE__ */ m('<button type=button class="flex items-center justify-center w-5 h-5 rounded cursor-pointer hover:bg-sidebar-accent/80 transition-colors"aria-label="Close sidebar"><svg xmlns=http://www.w3.org/2000/svg viewBox="0 0 24 24"fill=none stroke=currentColor stroke-width=2 stroke-linecap=round stroke-linejoin=round class="w-3.5 h-3.5"><path d="M18 6 6 18"></path><path d="m6 6 12 12">'), se = /* @__PURE__ */ m('<div class="absolute inset-0 bg-background/60 backdrop-blur-sm z-[9]">'), de = /* @__PURE__ */ m('<div><div class="flex flex-1 min-h-0 relative"><aside><div class="h-full flex flex-col"><div class="flex items-center justify-between px-3 py-2 border-b border-sidebar-border"><span class="text-[11px] font-semibold uppercase tracking-wider text-muted-foreground/60">Explorer</span></div><div class="flex-1 min-h-0 overflow-auto py-1"></div></div></aside><div class="flex-1 min-w-0 flex flex-col"><div class="flex-1 min-h-0"></div><div class="flex items-center justify-between px-3 py-1 border-t border-border text-[10px] text-muted-foreground"><span> items</span><span class="truncate max-w-[200px]">');
16
16
  function De(e) {
17
- return n(X, {
17
+ return n(Y, {
18
18
  get files() {
19
19
  return e.files;
20
20
  },
21
+ get path() {
22
+ return e.path;
23
+ },
21
24
  get initialPath() {
22
25
  return e.initialPath;
23
26
  },
@@ -42,6 +45,9 @@ function De(e) {
42
45
  get onNavigate() {
43
46
  return e.onNavigate;
44
47
  },
48
+ get onPathChange() {
49
+ return e.onPathChange;
50
+ },
45
51
  get onSelect() {
46
52
  return e.onSelect;
47
53
  },
@@ -88,9 +94,9 @@ function De(e) {
88
94
  });
89
95
  }
90
96
  function ue(e) {
91
- const r = Y(), O = q(), d = J(), a = () => O.isMobile(), C = () => r.sidebarWidth(), E = () => e.sidebarResizable ?? !0, s = () => (e.enableDragDrop ?? !0) && !!d, u = () => e.instanceId ?? `filebrowser-${Math.random().toString(36).slice(2, 9)}`;
97
+ const r = Z(), O = J(), d = U(), o = () => O.isMobile(), C = () => r.sidebarWidth(), E = () => e.sidebarResizable ?? !0, s = () => (e.enableDragDrop ?? !0) && !!d, u = () => e.instanceId ?? `filebrowser-${Math.random().toString(36).slice(2, 9)}`;
92
98
  let M, I = null, S = null;
93
- G(() => {
99
+ H(() => {
94
100
  if (!d || !s()) return;
95
101
  const i = {
96
102
  instanceId: u(),
@@ -103,12 +109,12 @@ function ue(e) {
103
109
  optimisticInsert: r.optimisticInsert
104
110
  };
105
111
  d.registerInstance(i);
106
- }), H(() => {
112
+ }), Q(() => {
107
113
  d && s() && d.unregisterInstance(u());
108
114
  });
109
115
  let y = !1, f = !1;
110
- Q(() => {
111
- const i = a();
116
+ q(() => {
117
+ const i = o();
112
118
  if (!y) {
113
119
  y = !0, f = i, i && e.hideSidebarOnMobile !== !1 && !r.sidebarCollapsed() && r.toggleSidebar();
114
120
  return;
@@ -116,26 +122,26 @@ function ue(e) {
116
122
  !f && i && e.hideSidebarOnMobile !== !1 && !r.sidebarCollapsed() && r.toggleSidebar(), f = i;
117
123
  });
118
124
  const j = (i) => {
119
- (i.metaKey || i.ctrlKey) && i.key.toLowerCase() === "f" && (i.preventDefault(), r.setFilterActive(!0), U(() => M?.focus()));
120
- }, D = () => !r.sidebarCollapsed() || !a();
125
+ (i.metaKey || i.ctrlKey) && i.key.toLowerCase() === "f" && (i.preventDefault(), r.setFilterActive(!0), X(() => M?.focus()));
126
+ }, D = () => !r.sidebarCollapsed() || !o();
121
127
  return (() => {
122
128
  var i = de(), h = i.firstChild, c = h.firstChild, k = c.firstChild, v = k.firstChild;
123
129
  v.firstChild;
124
130
  var $ = v.nextSibling, x = c.nextSibling, b = x.firstChild, V = b.nextSibling, g = V.firstChild, A = g.firstChild, N = g.nextSibling;
125
- return i.$$keydown = j, l(i, n(o, {
131
+ return i.$$keydown = j, l(i, n(a, {
126
132
  get when() {
127
133
  return e.header;
128
134
  },
129
135
  get children() {
130
- var t = oe();
136
+ var t = ae();
131
137
  return l(t, () => e.header), t;
132
138
  }
133
- }), h), l(v, n(o, {
139
+ }), h), l(v, n(a, {
134
140
  get when() {
135
- return a();
141
+ return o();
136
142
  },
137
143
  get children() {
138
- var t = ae();
144
+ var t = oe();
139
145
  return P(t, "click", r.toggleSidebar, !0), t;
140
146
  }
141
147
  }), null), L((t) => {
@@ -147,21 +153,21 @@ function ue(e) {
147
153
  get enableDragDrop() {
148
154
  return s();
149
155
  }
150
- })), l(c, n(o, {
156
+ })), l(c, n(a, {
151
157
  get when() {
152
- return w(() => !!(E() && D()))() && !a();
158
+ return w(() => !!(E() && D()))() && !o();
153
159
  },
154
160
  get children() {
155
- return n(Z, {
161
+ return n(p, {
156
162
  direction: "horizontal",
157
163
  onResize: (t) => {
158
164
  r.setSidebarWidth(r.sidebarWidth() + t);
159
165
  }
160
166
  });
161
167
  }
162
- }), null), l(h, n(o, {
168
+ }), null), l(h, n(a, {
163
169
  get when() {
164
- return w(() => !!a())() && !r.sidebarCollapsed();
170
+ return w(() => !!o())() && !r.sidebarCollapsed();
165
171
  },
166
172
  get children() {
167
173
  var t = se();
@@ -171,7 +177,7 @@ function ue(e) {
171
177
  filterInputRef: (t) => M = t
172
178
  }), b), L((t) => {
173
179
  I = t;
174
- }, b), l(b, n(o, {
180
+ }, b), l(b, n(a, {
175
181
  get when() {
176
182
  return r.viewMode() === "list";
177
183
  },
@@ -195,14 +201,14 @@ function ue(e) {
195
201
  }
196
202
  });
197
203
  }
198
- })), l(g, () => r.currentFiles().length, A), l(g, n(o, {
204
+ })), l(g, () => r.currentFiles().length, A), l(g, n(a, {
199
205
  get when() {
200
206
  return r.filterQueryApplied().trim();
201
207
  },
202
208
  get children() {
203
209
  return [" ", "(filtered)"];
204
210
  }
205
- }), null), l(g, n(o, {
211
+ }), null), l(g, n(a, {
206
212
  get when() {
207
213
  return r.selectedItems().size > 0;
208
214
  },
@@ -222,7 +228,7 @@ function ue(e) {
222
228
  get hideItems() {
223
229
  return e.hideContextMenuItems;
224
230
  }
225
- }), null), l(i, n(o, {
231
+ }), null), l(i, n(a, {
226
232
  get when() {
227
233
  return s();
228
234
  },
@@ -235,7 +241,7 @@ function ue(e) {
235
241
  "transition-all duration-200 ease-out",
236
242
  "overflow-hidden",
237
243
  // Mobile overlay
238
- a() && !r.sidebarCollapsed() && "absolute inset-y-0 left-0 z-10 shadow-lg"
244
+ o() && !r.sidebarCollapsed() && "absolute inset-y-0 left-0 z-10 shadow-lg"
239
245
  ), z = D() ? `${C()}px` : "0px", F = `${C()}px`;
240
246
  return _ !== t.e && W(i, t.e = _), R !== t.t && W(c, t.t = R), z !== t.a && B(c, "width", t.a = z), F !== t.o && B(k, "width", t.o = F), t;
241
247
  }, {
@@ -246,7 +252,7 @@ function ue(e) {
246
252
  }), i;
247
253
  })();
248
254
  }
249
- p(["keydown", "click"]);
255
+ G(["keydown", "click"]);
250
256
  export {
251
257
  De as FileBrowser
252
258
  };
@@ -3,6 +3,10 @@ import type { FileItem, ViewMode, FileListColumnRatios, FileBrowserContextValue
3
3
  export interface FileBrowserProviderProps {
4
4
  children: JSX.Element;
5
5
  files: FileItem[];
6
+ /**
7
+ * Controlled current path. When provided, FileBrowser follows this value.
8
+ */
9
+ path?: string;
6
10
  initialPath?: string;
7
11
  initialViewMode?: ViewMode;
8
12
  /** Initial list view column ratios (for resizable columns) */
@@ -23,6 +27,7 @@ export interface FileBrowserProviderProps {
23
27
  /** Label for the root/home directory in breadcrumb (default: 'Root') */
24
28
  homeLabel?: string;
25
29
  onNavigate?: (path: string) => void;
30
+ onPathChange?: (path: string, source: 'user' | 'programmatic') => void;
26
31
  onSelect?: (items: FileItem[]) => void;
27
32
  onOpen?: (item: FileItem) => void;
28
33
  }
@@ -1,164 +1,164 @@
1
- import { createComponent as tt } from "solid-js/web";
2
- import { createSignal as h, createEffect as g, createMemo as z, useContext as nt, createContext as ot, untrack as it } from "solid-js";
3
- import { useResolvedFloeConfig as st } from "../../context/FloeConfigContext.js";
4
- import { deferAfterPaint as rt, deferNonBlocking as A } from "../../utils/defer.js";
5
- const de = ot(), b = {
1
+ import { createComponent as ot } from "solid-js/web";
2
+ import { createSignal as p, createEffect as I, createMemo as z, useContext as it, createContext as st, untrack as rt } from "solid-js";
3
+ import { useResolvedFloeConfig as ct } from "../../context/FloeConfigContext.js";
4
+ import { deferAfterPaint as at, deferNonBlocking as y } from "../../utils/defer.js";
5
+ const ue = st(), b = {
6
6
  name: 0.65,
7
7
  modifiedAt: 0.2,
8
8
  size: 0.15
9
- }, te = "fileBrowser:listColumnRatios", ne = 220, ct = "fileBrowser:sidebarWidth", at = 160, lt = 520, oe = "fileBrowser:viewMode", ie = "fileBrowser:sortConfig", se = "fileBrowser:expandedFolders", re = "fileBrowser:sidebarCollapsed";
10
- function ce(n, s) {
11
- const c = typeof n == "number" && Number.isFinite(n) ? n : s;
12
- return Math.max(at, Math.min(lt, Math.round(c)));
9
+ }, oe = "fileBrowser:listColumnRatios", ie = 220, lt = "fileBrowser:sidebarWidth", dt = 160, ft = 520, se = "fileBrowser:viewMode", re = "fileBrowser:sortConfig", ce = "fileBrowser:expandedFolders", ae = "fileBrowser:sidebarCollapsed";
10
+ function le(n, r) {
11
+ const s = typeof n == "number" && Number.isFinite(n) ? n : r;
12
+ return Math.max(dt, Math.min(ft, Math.round(s)));
13
13
  }
14
- function dt(n, s) {
15
- return n === "list" || n === "grid" ? n : s;
14
+ function ut(n, r) {
15
+ return n === "list" || n === "grid" ? n : r;
16
16
  }
17
- function ft(n, s) {
18
- if (!n || typeof n != "object") return s;
19
- const c = n, f = c.field, l = c.direction;
20
- return !(f === "name" || f === "size" || f === "modifiedAt" || f === "type") || !(l === "asc" || l === "desc") ? s : {
17
+ function mt(n, r) {
18
+ if (!n || typeof n != "object") return r;
19
+ const s = n, f = s.field, l = s.direction;
20
+ return !(f === "name" || f === "size" || f === "modifiedAt" || f === "type") || !(l === "asc" || l === "desc") ? r : {
21
21
  field: f,
22
22
  direction: l
23
23
  };
24
24
  }
25
- function ut(n) {
25
+ function ht(n) {
26
26
  if (!Array.isArray(n)) return ["/"];
27
- const s = [], c = /* @__PURE__ */ new Set();
27
+ const r = [], s = /* @__PURE__ */ new Set();
28
28
  for (const f of n) {
29
29
  if (typeof f != "string") continue;
30
30
  const l = f.trim();
31
- !l || c.has(l) || (c.add(l), s.push(l));
31
+ !l || s.has(l) || (s.add(l), r.push(l));
32
32
  }
33
- return c.has("/") || s.unshift("/"), s;
33
+ return s.has("/") || r.unshift("/"), r;
34
34
  }
35
- function ae(n) {
36
- const s = Number.isFinite(n.name) ? n.name : b.name, c = Number.isFinite(n.modifiedAt) ? n.modifiedAt : b.modifiedAt, f = Number.isFinite(n.size) ? n.size : b.size, l = Math.max(0, s), m = Math.max(0, c), y = Math.max(0, f), w = l + m + y;
35
+ function de(n) {
36
+ const r = Number.isFinite(n.name) ? n.name : b.name, s = Number.isFinite(n.modifiedAt) ? n.modifiedAt : b.modifiedAt, f = Number.isFinite(n.size) ? n.size : b.size, l = Math.max(0, r), m = Math.max(0, s), C = Math.max(0, f), w = l + m + C;
37
37
  return w <= 0 ? b : {
38
38
  name: l / w,
39
39
  modifiedAt: m / w,
40
- size: y / w
40
+ size: C / w
41
41
  };
42
42
  }
43
- function le(n, s) {
44
- if (!s) return [];
45
- const c = [];
43
+ function fe(n, r) {
44
+ if (!r) return [];
45
+ const s = [];
46
46
  let f = 0;
47
- for (const l of s) {
47
+ for (const l of r) {
48
48
  const m = n.indexOf(l, f);
49
49
  if (m === -1) return null;
50
- c.push(m), f = m + 1;
50
+ s.push(m), f = m + 1;
51
51
  }
52
- return c;
52
+ return s;
53
53
  }
54
- function gt(n) {
55
- const s = st(), c = (e) => {
54
+ function Ct(n) {
55
+ const r = ct(), s = (e) => {
56
56
  const t = (e ?? "").trim();
57
57
  return t === "" ? "/" : t;
58
- }, f = (n.persistenceKey ?? "").trim(), l = !!f, m = (e) => f ? `${f}:${e}` : e, y = l ? dt(s.persist.load(m(oe), n.initialViewMode ?? "list"), n.initialViewMode ?? "list") : n.initialViewMode ?? "list", w = {
58
+ }, f = (n.persistenceKey ?? "").trim(), l = !!f, m = (e) => f ? `${f}:${e}` : e, C = l ? ut(r.persist.load(m(se), n.initialViewMode ?? "list"), n.initialViewMode ?? "list") : n.initialViewMode ?? "list", w = {
59
59
  field: "name",
60
60
  direction: "asc"
61
- }, fe = l ? ft(s.persist.load(m(ie), w), w) : w, ue = l ? ut(s.persist.load(m(se), ["/"])) : ["/"], me = l ? s.persist.load(m(re), !1) === !0 : !1, [E, pe] = h(c(n.initialPath ?? "/")), [M, F] = h(/* @__PURE__ */ new Set()), [D, he] = h(y), [P, Se] = h(fe), we = ae(s.persist.load(te, n.initialListColumnRatios ?? b)), [N, Ie] = h(we), W = (n.sidebarWidthStorageKey ?? "").trim() || ct, ge = ce(s.persist.load(W, n.initialSidebarWidth ?? ne), n.initialSidebarWidth ?? ne), [K, ye] = h(ge), [_, k] = h(new Set(ue)), [U, Ce] = h(me), [xe, V] = h(null), [G, X] = h(""), [v, R] = h(""), [Ae, Y] = h(!1);
61
+ }, me = l ? mt(r.persist.load(m(re), w), w) : w, he = l ? ht(r.persist.load(m(ce), ["/"])) : ["/"], pe = l ? r.persist.load(m(ae), !1) === !0 : !1, Se = () => typeof n.path == "string" ? s(n.path) : s(n.initialPath ?? "/"), [x, D] = p(Se()), [E, M] = p(/* @__PURE__ */ new Set()), [N, we] = p(C), [F, Ie] = p(me), ge = de(r.persist.load(oe, n.initialListColumnRatios ?? b)), [W, ye] = p(ge), K = (n.sidebarWidthStorageKey ?? "").trim() || lt, Ce = le(r.persist.load(K, n.initialSidebarWidth ?? ie), n.initialSidebarWidth ?? ie), [U, xe] = p(Ce), [v, k] = p(new Set(he)), [V, Ae] = p(pe), [Pe, G] = p(null), [X, Y] = p(""), [_, R] = p(""), [be, q] = p(!1);
62
62
  let L = 0;
63
- g(() => {
64
- const e = G().trim();
63
+ I(() => {
64
+ const e = X().trim();
65
65
  L += 1;
66
66
  const t = L;
67
67
  if (!e) {
68
68
  R("");
69
69
  return;
70
70
  }
71
- rt(() => {
71
+ at(() => {
72
72
  t === L && R(e);
73
73
  });
74
- }), g(() => {
75
- s.persist.debouncedSave(te, N());
76
- }), g(() => {
77
- s.persist.debouncedSave(W, K());
78
- }), g(() => {
79
- l && s.persist.debouncedSave(m(oe), D());
80
- }), g(() => {
81
- l && s.persist.debouncedSave(m(ie), P());
82
- }), g(() => {
74
+ }), I(() => {
75
+ r.persist.debouncedSave(oe, W());
76
+ }), I(() => {
77
+ r.persist.debouncedSave(K, U());
78
+ }), I(() => {
79
+ l && r.persist.debouncedSave(m(se), N());
80
+ }), I(() => {
81
+ l && r.persist.debouncedSave(m(re), F());
82
+ }), I(() => {
83
83
  if (!l) return;
84
- const e = [..._()].sort((t, o) => t.localeCompare(o));
85
- s.persist.debouncedSave(m(se), e);
86
- }), g(() => {
87
- l && s.persist.debouncedSave(m(re), U());
84
+ const e = [...v()].sort((t, o) => t.localeCompare(o));
85
+ r.persist.debouncedSave(m(ce), e);
86
+ }), I(() => {
87
+ l && r.persist.debouncedSave(m(ae), V());
88
88
  });
89
- const be = (e) => he(e), Ee = (e) => Se(e), Me = () => n.homeLabel ?? "Root", [q, C] = h([]);
90
- let I = null, B = {
89
+ const Ee = (e) => we(e), Me = (e) => Ie(e), Fe = () => n.homeLabel ?? "Root", [Q, A] = p([]);
90
+ let g = null, B = {
91
91
  top: 0,
92
92
  left: 0
93
93
  };
94
- const Q = () => n.files, j = (e) => {
95
- const t = c(e);
94
+ const j = () => n.files, H = (e) => {
95
+ const t = s(e);
96
96
  if (t === "/") return "/";
97
97
  const o = t.split("/").filter(Boolean);
98
98
  return o.pop(), o.length ? "/" + o.join("/") : "/";
99
- }, Fe = (e, t) => {
100
- const o = q();
99
+ }, ve = (e, t) => {
100
+ const o = Q();
101
101
  if (o.length === 0) return e;
102
102
  let i = [...e];
103
- const a = c(t);
104
- for (const r of o)
105
- switch (r.type) {
103
+ const a = s(t);
104
+ for (const c of o)
105
+ switch (c.type) {
106
106
  case "remove": {
107
- const u = new Set(r.paths.map(c));
108
- i = i.filter((d) => !u.has(c(d.path)));
107
+ const u = new Set(c.paths.map(s));
108
+ i = i.filter((d) => !u.has(s(d.path)));
109
109
  break;
110
110
  }
111
111
  case "update": {
112
- const u = c(r.oldPath), d = i.findIndex((S) => c(S.path) === u);
112
+ const u = s(c.oldPath), d = i.findIndex((S) => s(S.path) === u);
113
113
  if (d !== -1) {
114
- const S = r.updates.path ?? i[d].path;
115
- j(S) === a ? i[d] = {
114
+ const S = c.updates.path ?? i[d].path;
115
+ H(S) === a ? i[d] = {
116
116
  ...i[d],
117
- ...r.updates
117
+ ...c.updates
118
118
  } : i.splice(d, 1);
119
119
  } else {
120
- const S = r.updates.path;
121
- S && j(S);
120
+ const S = c.updates.path;
121
+ S && H(S);
122
122
  }
123
123
  break;
124
124
  }
125
125
  case "insert": {
126
- c(r.parentPath) === a && (i.some((d) => c(d.path) === c(r.item.path)) || i.push(r.item));
126
+ s(c.parentPath) === a && (i.some((d) => s(d.path) === s(c.item.path)) || i.push(c.item));
127
127
  break;
128
128
  }
129
129
  }
130
130
  return i;
131
- }, Pe = z(() => {
131
+ }, _e = z(() => {
132
132
  const e = /* @__PURE__ */ new Map(), t = (i) => {
133
133
  for (const a of i)
134
- a.type === "folder" && (e.set(c(a.path), a.children ?? []), a.children?.length && t(a.children));
135
- }, o = Q();
134
+ a.type === "folder" && (e.set(s(a.path), a.children ?? []), a.children?.length && t(a.children));
135
+ }, o = j();
136
136
  return e.set("/", o), t(o), e;
137
- }), _e = z(() => {
138
- const e = c(E());
139
- let t = Pe().get(e) ?? [];
140
- t = Fe(t, e);
141
- const o = P(), i = [...t].sort((r, u) => {
142
- if (r.type !== u.type) return r.type === "folder" ? -1 : 1;
137
+ }), Re = z(() => {
138
+ const e = s(x());
139
+ let t = _e().get(e) ?? [];
140
+ t = ve(t, e);
141
+ const o = F(), i = [...t].sort((c, u) => {
142
+ if (c.type !== u.type) return c.type === "folder" ? -1 : 1;
143
143
  let d = 0;
144
144
  switch (o.field) {
145
145
  case "name":
146
- d = r.name.localeCompare(u.name);
146
+ d = c.name.localeCompare(u.name);
147
147
  break;
148
148
  case "size":
149
- d = (r.size ?? 0) - (u.size ?? 0);
149
+ d = (c.size ?? 0) - (u.size ?? 0);
150
150
  break;
151
151
  case "modifiedAt":
152
- d = (r.modifiedAt?.getTime() ?? 0) - (u.modifiedAt?.getTime() ?? 0);
152
+ d = (c.modifiedAt?.getTime() ?? 0) - (u.modifiedAt?.getTime() ?? 0);
153
153
  break;
154
154
  case "type":
155
- d = (r.extension ?? "").localeCompare(u.extension ?? "");
155
+ d = (c.extension ?? "").localeCompare(u.extension ?? "");
156
156
  break;
157
157
  }
158
158
  return o.direction === "asc" ? d : -d;
159
159
  }), a = /* @__PURE__ */ new Map();
160
- for (const r of i)
161
- a.set(r.id, r.name.toLowerCase());
160
+ for (const c of i)
161
+ a.set(c.id, c.name.toLowerCase());
162
162
  return {
163
163
  items: i,
164
164
  nameLowerById: a
@@ -167,203 +167,213 @@ function gt(n) {
167
167
  const {
168
168
  items: e,
169
169
  nameLowerById: t
170
- } = _e(), i = v().trim().toLowerCase(), a = /* @__PURE__ */ new Map(), r = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
170
+ } = Re(), i = _().trim().toLowerCase(), a = /* @__PURE__ */ new Map(), c = /* @__PURE__ */ new Map(), u = /* @__PURE__ */ new Map();
171
171
  if (!i) {
172
- for (let p = 0; p < e.length; p++) {
173
- const x = e[p];
174
- r.set(x.id, x), u.set(x.id, p);
172
+ for (let h = 0; h < e.length; h++) {
173
+ const P = e[h];
174
+ c.set(P.id, P), u.set(P.id, h);
175
175
  }
176
176
  return {
177
177
  items: e,
178
178
  matchById: a,
179
- fileById: r,
179
+ fileById: c,
180
180
  indexById: u
181
181
  };
182
182
  }
183
183
  const d = [];
184
184
  let S = 0;
185
- for (const p of e) {
186
- const x = t.get(p.id) ?? p.name.toLowerCase(), ee = le(x, i);
187
- ee && (d.push(p), a.set(p.id, {
188
- matchedIndices: ee
189
- }), r.set(p.id, p), u.set(p.id, S), S += 1);
185
+ for (const h of e) {
186
+ const P = t.get(h.id) ?? h.name.toLowerCase(), ne = fe(P, i);
187
+ ne && (d.push(h), a.set(h.id, {
188
+ matchedIndices: ne
189
+ }), c.set(h.id, h), u.set(h.id, S), S += 1);
190
190
  }
191
191
  return {
192
192
  items: d,
193
193
  matchById: a,
194
- fileById: r,
194
+ fileById: c,
195
195
  indexById: u
196
196
  };
197
- }), ve = () => O().items, H = (e) => {
197
+ }), Le = () => O().items, $ = (e) => {
198
198
  const t = O(), o = [];
199
199
  for (const i of e) {
200
200
  const a = t.fileById.get(i);
201
201
  if (!a) continue;
202
- const r = t.indexById.get(i);
202
+ const c = t.indexById.get(i);
203
203
  o.push({
204
- index: r ?? Number.MAX_SAFE_INTEGER,
204
+ index: c ?? Number.MAX_SAFE_INTEGER,
205
205
  item: a
206
206
  });
207
207
  }
208
208
  return o.sort((i, a) => i.index - a.index), o.map((i) => i.item);
209
- }, T = (e) => {
210
- const t = c(e);
211
- if (t === E()) return;
212
- pe(t), F(/* @__PURE__ */ new Set()), X(""), R(""), Y(!1);
213
- const o = n.onSelect;
214
- A(() => o?.([]));
215
- const i = n.onNavigate;
216
- A(() => i?.(t));
217
- }, Re = (e) => {
218
- Ie(ae(e));
219
- }, Le = () => {
220
- const e = E();
209
+ }, J = () => {
210
+ M(/* @__PURE__ */ new Set()), Y(""), R(""), q(!1);
211
+ const e = n.onSelect;
212
+ y(() => e?.([]));
213
+ };
214
+ I(() => {
215
+ if (typeof n.path != "string") return;
216
+ const e = s(n.path);
217
+ e !== x() && (D(e), J());
218
+ });
219
+ const T = (e) => {
220
+ const t = s(e);
221
+ if (t === x()) return;
222
+ D(t), J();
223
+ const o = n.onNavigate;
224
+ y(() => o?.(t));
225
+ const i = n.onPathChange;
226
+ y(() => i?.(t, "user"));
227
+ }, Be = (e) => {
228
+ ye(de(e));
229
+ }, Oe = () => {
230
+ const e = x();
221
231
  if (e === "/" || e === "") return;
222
232
  const t = e.split("/").filter(Boolean);
223
233
  t.pop(), T(t.length ? "/" + t.join("/") : "/");
224
- }, $ = (e) => {
234
+ }, Z = (e) => {
225
235
  e.type === "folder" && (T(e.path), k((t) => {
226
236
  const o = new Set(t);
227
237
  return o.add(e.path), o;
228
238
  }));
229
- }, Be = (e, t = !1) => {
230
- const o = M(), i = t ? new Set(o) : /* @__PURE__ */ new Set();
231
- t ? i.has(e) ? i.delete(e) : i.add(e) : (i.clear(), i.add(e)), F(i);
239
+ }, Te = (e, t = !1) => {
240
+ const o = E(), i = t ? new Set(o) : /* @__PURE__ */ new Set();
241
+ t ? i.has(e) ? i.delete(e) : i.add(e) : (i.clear(), i.add(e)), M(i);
232
242
  const a = n.onSelect;
233
243
  if (a) {
234
- const r = new Set(i);
235
- A(() => {
236
- const u = it(() => H(r));
244
+ const c = new Set(i);
245
+ y(() => {
246
+ const u = rt(() => $(c));
237
247
  a(u);
238
248
  });
239
249
  }
240
- }, Oe = () => {
241
- F(/* @__PURE__ */ new Set());
250
+ }, ze = () => {
251
+ M(/* @__PURE__ */ new Set());
242
252
  const e = n.onSelect;
243
- A(() => e?.([]));
244
- }, Te = (e) => M().has(e), ze = (e) => {
253
+ y(() => e?.([]));
254
+ }, De = (e) => E().has(e), Ne = (e) => {
245
255
  k((t) => {
246
256
  const o = new Set(t);
247
257
  return o.has(e) ? o.delete(e) : o.add(e), o;
248
258
  });
249
- }, De = (e) => _().has(e), Ne = () => Ce((e) => !e), We = (e) => {
250
- ye((t) => ce(e, t));
251
- }, Ke = (e) => V(e), ke = () => V(null), Ue = (e) => {
252
- X(e);
253
- }, Ve = (e) => {
254
- const t = v().trim();
259
+ }, We = (e) => v().has(e), Ke = () => Ae((e) => !e), Ue = (e) => {
260
+ xe((t) => le(e, t));
261
+ }, ke = (e) => G(e), Ve = () => G(null), Ge = (e) => {
262
+ Y(e);
263
+ }, Xe = (e) => {
264
+ const t = _().trim();
255
265
  if (!t) return null;
256
- const o = le(e.toLowerCase(), t.toLowerCase());
266
+ const o = fe(e.toLowerCase(), t.toLowerCase());
257
267
  return o ? {
258
268
  matchedIndices: o
259
269
  } : null;
260
- }, Ge = (e) => O().matchById.get(e) ?? null, Xe = () => H(M()), Ye = (e) => {
270
+ }, Ye = (e) => O().matchById.get(e) ?? null, qe = () => $(E()), Qe = (e) => {
261
271
  if (e.type === "folder")
262
- $(e);
272
+ Z(e);
263
273
  else {
264
274
  const t = n.onOpen;
265
- A(() => t?.(e));
275
+ y(() => t?.(e));
266
276
  }
267
- }, qe = (e) => {
268
- e.length !== 0 && C((t) => [...t, {
277
+ }, je = (e) => {
278
+ e.length !== 0 && A((t) => [...t, {
269
279
  type: "remove",
270
280
  paths: e
271
281
  }]);
272
- }, Qe = (e, t) => {
273
- C((o) => [...o, {
282
+ }, He = (e, t) => {
283
+ A((o) => [...o, {
274
284
  type: "update",
275
285
  oldPath: e,
276
286
  updates: t
277
287
  }]);
278
- }, je = (e, t) => {
279
- C((o) => [...o, {
288
+ }, $e = (e, t) => {
289
+ A((o) => [...o, {
280
290
  type: "insert",
281
291
  parentPath: e,
282
292
  item: t
283
293
  }]);
284
- }, He = () => {
285
- C([]);
286
- }, $e = () => {
287
- C([]);
288
- }, Je = () => q().length > 0, Ze = (e) => {
289
- I = e;
290
- }, J = () => I ? {
291
- top: I.scrollTop,
292
- left: I.scrollLeft
294
+ }, Je = () => {
295
+ A([]);
296
+ }, Ze = () => {
297
+ A([]);
298
+ }, et = () => Q().length > 0, tt = (e) => {
299
+ g = e;
300
+ }, ee = () => g ? {
301
+ top: g.scrollTop,
302
+ left: g.scrollLeft
293
303
  } : {
294
304
  top: 0,
295
305
  left: 0
296
- }, Z = (e) => {
297
- I && (I.scrollTop = e.top, I.scrollLeft = e.left);
298
- }, et = {
299
- currentPath: E,
306
+ }, te = (e) => {
307
+ g && (g.scrollTop = e.top, g.scrollLeft = e.left);
308
+ }, nt = {
309
+ currentPath: x,
300
310
  setCurrentPath: T,
301
- navigateUp: Le,
302
- navigateTo: $,
303
- homeLabel: Me,
304
- selectedItems: () => M(),
305
- selectItem: Be,
306
- clearSelection: Oe,
307
- isSelected: Te,
308
- getSelectedItemsList: Xe,
309
- viewMode: D,
310
- setViewMode: be,
311
- sortConfig: P,
312
- setSortConfig: Ee,
313
- listColumnRatios: N,
314
- setListColumnRatios: Re,
315
- expandedFolders: _,
316
- toggleFolder: ze,
317
- isExpanded: De,
318
- files: Q,
319
- currentFiles: ve,
320
- filterQuery: G,
321
- setFilterQuery: Ue,
322
- filterQueryApplied: v,
323
- isFilterActive: Ae,
324
- setFilterActive: Y,
325
- getFilterMatch: Ve,
326
- getFilterMatchForId: Ge,
327
- sidebarCollapsed: U,
328
- toggleSidebar: Ne,
329
- sidebarWidth: K,
330
- setSidebarWidth: We,
331
- contextMenu: xe,
332
- showContextMenu: Ke,
333
- hideContextMenu: ke,
334
- openItem: Ye,
311
+ navigateUp: Oe,
312
+ navigateTo: Z,
313
+ homeLabel: Fe,
314
+ selectedItems: () => E(),
315
+ selectItem: Te,
316
+ clearSelection: ze,
317
+ isSelected: De,
318
+ getSelectedItemsList: qe,
319
+ viewMode: N,
320
+ setViewMode: Ee,
321
+ sortConfig: F,
322
+ setSortConfig: Me,
323
+ listColumnRatios: W,
324
+ setListColumnRatios: Be,
325
+ expandedFolders: v,
326
+ toggleFolder: Ne,
327
+ isExpanded: We,
328
+ files: j,
329
+ currentFiles: Le,
330
+ filterQuery: X,
331
+ setFilterQuery: Ge,
332
+ filterQueryApplied: _,
333
+ isFilterActive: be,
334
+ setFilterActive: q,
335
+ getFilterMatch: Xe,
336
+ getFilterMatchForId: Ye,
337
+ sidebarCollapsed: V,
338
+ toggleSidebar: Ke,
339
+ sidebarWidth: U,
340
+ setSidebarWidth: Ue,
341
+ contextMenu: Pe,
342
+ showContextMenu: ke,
343
+ hideContextMenu: Ve,
344
+ openItem: Qe,
335
345
  // Optimistic updates
336
- optimisticRemove: qe,
337
- optimisticUpdate: Qe,
338
- optimisticInsert: je,
339
- clearOptimisticUpdates: He,
340
- rollbackOptimisticUpdates: $e,
341
- hasOptimisticUpdates: Je,
346
+ optimisticRemove: je,
347
+ optimisticUpdate: He,
348
+ optimisticInsert: $e,
349
+ clearOptimisticUpdates: Je,
350
+ rollbackOptimisticUpdates: Ze,
351
+ hasOptimisticUpdates: et,
342
352
  // Scroll position management
343
- setScrollContainer: Ze,
344
- getScrollPosition: J,
345
- setScrollPosition: Z,
346
- saveScrollPosition: () => (B = J(), B),
353
+ setScrollContainer: tt,
354
+ getScrollPosition: ee,
355
+ setScrollPosition: te,
356
+ saveScrollPosition: () => (B = ee(), B),
347
357
  restoreScrollPosition: () => {
348
358
  requestAnimationFrame(() => {
349
- Z(B);
359
+ te(B);
350
360
  });
351
361
  }
352
362
  };
353
- return tt(de.Provider, {
354
- value: et,
363
+ return ot(ue.Provider, {
364
+ value: nt,
355
365
  get children() {
356
366
  return n.children;
357
367
  }
358
368
  });
359
369
  }
360
- function yt() {
361
- const n = nt(de);
370
+ function xt() {
371
+ const n = it(ue);
362
372
  if (!n)
363
373
  throw new Error("useFileBrowser must be used within a FileBrowserProvider");
364
374
  return n;
365
375
  }
366
376
  export {
367
- gt as FileBrowserProvider,
368
- yt as useFileBrowser
377
+ Ct as FileBrowserProvider,
378
+ xt as useFileBrowser
369
379
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@floegence/floe-webapp-core",
3
- "version": "0.31.0",
3
+ "version": "0.32.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -81,7 +81,7 @@
81
81
  "clsx": "^2.1.1",
82
82
  "diff": "^8.0.3",
83
83
  "marked": "^17.0.1",
84
- "mermaid": "^11.12.2",
84
+ "mermaid": "^11.12.3",
85
85
  "shiki": "^3.21.0",
86
86
  "solid-motionone": "^1.0.4",
87
87
  "tailwind-merge": "^3.4.0"