@leavepulse/ui 0.14.10 → 0.15.1

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 (41) hide show
  1. package/dist/chunks/{LpAppShell-BLKZDLh8.js → LpAppShell-CV194os6.js} +2 -2
  2. package/dist/chunks/{LpAutocomplete-B9uoCs4t.js → LpAutocomplete-DIgXmtCA.js} +13 -5
  3. package/dist/chunks/{LpConfirmDialog-BA45MvRL.js → LpConfirmDialog-WHkymac5.js} +1 -1
  4. package/dist/chunks/{LpDrawer-6n5Hd-iF.js → LpDrawer-BHzCy-Aa.js} +1 -1
  5. package/dist/chunks/{LpFileTree-0Ko_uj8q.js → LpFileTree-BQz8Rfgz.js} +55 -34
  6. package/dist/chunks/{LpFileTreeNode-D3lNEP-1.js → LpFileTreeNode-DFg4PhOK.js} +28 -14
  7. package/dist/chunks/{LpLogViewer-BBgaY38i.js → LpLogViewer-BvzRZPjs.js} +2 -1
  8. package/dist/chunks/{LpModal-B9CVVC5U.js → LpModal-DpBHQpbU.js} +1 -1
  9. package/dist/chunks/{LpNotificationBell-B9VHks_D.js → LpNotificationBell-BtfRoL2i.js} +1 -1
  10. package/dist/chunks/{LpScrollArea-BtnZCCBT.js → LpScrollArea-De9DLpa1.js} +2 -2
  11. package/dist/chunks/{LpSidebar-tZ2z-PnM.js → LpSidebar-B11IDRQi.js} +2 -2
  12. package/dist/chunks/{LpSidebarNav-D-YvtwsH.js → LpSidebarNav-TIUNHZX-.js} +1 -1
  13. package/dist/chunks/{LpTable-CzHVcr3v.js → LpTable-CKGaUbEM.js} +1 -1
  14. package/dist/components/LpAppShell.vue.js +1 -1
  15. package/dist/components/LpAutocomplete.vue.js +1 -1
  16. package/dist/components/LpCommandPalette.vue.js +1 -1
  17. package/dist/components/LpConfirmDialog.vue.js +1 -1
  18. package/dist/components/LpDrawer.vue.js +1 -1
  19. package/dist/components/LpFileTree.vue.js +1 -1
  20. package/dist/components/LpFileTreeNode.vue.d.ts +3 -1
  21. package/dist/components/LpFileTreeNode.vue.js +1 -1
  22. package/dist/components/LpLogViewer.vue.js +1 -1
  23. package/dist/components/LpModal.vue.js +1 -1
  24. package/dist/components/LpNotificationBell.vue.js +1 -1
  25. package/dist/components/LpScrollArea.vue.d.ts +4 -2
  26. package/dist/components/LpScrollArea.vue.js +1 -1
  27. package/dist/components/LpSidebar.vue.js +1 -1
  28. package/dist/components/LpSidebarNav.vue.js +1 -1
  29. package/dist/components/LpTable.vue.js +1 -1
  30. package/dist/components/fileTree.d.ts +25 -0
  31. package/dist/components/fileTree.js +66 -0
  32. package/dist/index.d.ts +2 -2
  33. package/dist/index.js +14 -12
  34. package/package.json +3 -2
  35. package/src/components/LpAutocomplete.vue +19 -2
  36. package/src/components/LpFileTree.vue +73 -35
  37. package/src/components/LpFileTreeNode.vue +35 -7
  38. package/src/components/LpLogViewer.vue +4 -0
  39. package/src/components/LpScrollArea.vue +16 -11
  40. package/src/components/fileTree.ts +100 -0
  41. package/src/index.ts +3 -1
@@ -1,8 +1,8 @@
1
1
  import { defineComponent, computed, watch, openBlock, createElementBlock, createVNode, createSlots, withCtx, renderSlot, normalizeProps, guardReactiveProps, createElementVNode, normalizeClass, toDisplayString, createCommentVNode, createBlock } from "vue";
2
2
  import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
3
3
  import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
4
- import { _ as _sfc_main$4 } from "./LpScrollArea-BtnZCCBT.js";
5
- import { _ as _sfc_main$1 } from "./LpSidebar-tZ2z-PnM.js";
4
+ import { _ as _sfc_main$4 } from "./LpScrollArea-De9DLpa1.js";
5
+ import { _ as _sfc_main$1 } from "./LpSidebar-B11IDRQi.js";
6
6
  const _hoisted_1 = { class: "flex h-dvh overflow-hidden bg-surface text-ink" };
7
7
  const _hoisted_2 = { class: "flex min-w-0 flex-1 flex-col" };
8
8
  const _hoisted_3 = { class: "flex h-16 shrink-0 items-center gap-3 border-b border-line bg-surface px-4 md:px-6" };
@@ -50,9 +50,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
50
50
  (o) => typeof o === "string" ? { value: o } : o
51
51
  )
52
52
  );
53
+ const typing = ref(false);
53
54
  const filtered = computed(() => {
54
55
  const q = text.value.trim().toLowerCase();
55
- if (!props.filter || !q) return items.value;
56
+ if (!props.filter || !typing.value || !q) return items.value;
56
57
  return items.value.filter((o) => {
57
58
  const hay = `${o.label ?? o.value} ${o.description ?? ""}`.toLowerCase();
58
59
  return hay.includes(q);
@@ -72,23 +73,30 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
72
73
  md: "h-(--size-control-md) text-sm",
73
74
  lg: "h-(--size-control-lg) text-sm"
74
75
  };
76
+ function onInput(event) {
77
+ typing.value = true;
78
+ text.value = event.target.value;
79
+ }
75
80
  function onFocus() {
81
+ typing.value = false;
76
82
  focused.value = true;
77
83
  if (canOpen.value) open.value = true;
78
84
  }
79
85
  function choose(opt) {
86
+ typing.value = false;
80
87
  emit("update:modelValue", opt.value);
81
88
  emit("select", opt.value);
82
89
  open.value = false;
83
90
  }
84
91
  function clear() {
92
+ typing.value = false;
85
93
  emit("update:modelValue", "");
86
94
  open.value = false;
87
95
  }
88
96
  return (_ctx, _cache) => {
89
97
  return openBlock(), createBlock(unref(ComboboxRoot), {
90
98
  open: open.value,
91
- "onUpdate:open": _cache[3] || (_cache[3] = ($event) => open.value = $event),
99
+ "onUpdate:open": _cache[2] || (_cache[2] = ($event) => open.value = $event),
92
100
  "ignore-filter": true,
93
101
  "reset-search-term-on-blur": false,
94
102
  "reset-search-term-on-select": false,
@@ -118,9 +126,9 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
118
126
  class: "min-w-0 flex-1 bg-transparent outline-none placeholder:text-muted",
119
127
  onBeforeinput: unref(onBeforeInput),
120
128
  onPaste: unref(onPaste),
121
- onInput: _cache[0] || (_cache[0] = ($event) => text.value = $event.target.value),
129
+ onInput,
122
130
  onFocus,
123
- onBlur: _cache[1] || (_cache[1] = ($event) => focused.value = false)
131
+ onBlur: _cache[0] || (_cache[0] = ($event) => focused.value = false)
124
132
  }, null, 8, ["model-value", "placeholder", "disabled", "aria-invalid", "onBeforeinput", "onPaste"]),
125
133
  __props.clearable && hasValue.value ? (openBlock(), createElementBlock("button", {
126
134
  key: 1,
@@ -144,7 +152,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
144
152
  position: "popper",
145
153
  "side-offset": 6,
146
154
  class: normalizeClass([unref(POPOVER_PANEL), "z-(--z-popover) max-h-72 w-(--reka-combobox-trigger-width) overflow-hidden"]),
147
- onOpenAutoFocus: _cache[2] || (_cache[2] = withModifiers(() => {
155
+ onOpenAutoFocus: _cache[1] || (_cache[1] = withModifiers(() => {
148
156
  }, ["prevent"]))
149
157
  }, {
150
158
  default: withCtx(() => [
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, openBlock, createBlock, withCtx, renderSlot, createVNode, createTextVNode, toDisplayString } from "vue";
2
2
  import { _ as _sfc_main$2 } from "./LpButton-5VXXCjwF.js";
3
- import { _ as _sfc_main$1 } from "./LpModal-B9CVVC5U.js";
3
+ import { _ as _sfc_main$1 } from "./LpModal-DpBHQpbU.js";
4
4
  const _sfc_main = /* @__PURE__ */ defineComponent({
5
5
  __name: "LpConfirmDialog",
6
6
  props: {
@@ -3,7 +3,7 @@ import { usePointerSwipe } from "@vueuse/core";
3
3
  import { DrawerRoot, DrawerPortal, DrawerOverlay, DrawerContent, DrawerHandle, DrawerTitle, DrawerDescription, DrawerClose } from "vaul-vue";
4
4
  import { CLOSE_ICON } from "../components/dropdown.js";
5
5
  import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
6
- import { _ as _sfc_main$2 } from "./LpScrollArea-BtnZCCBT.js";
6
+ import { _ as _sfc_main$2 } from "./LpScrollArea-De9DLpa1.js";
7
7
  const _hoisted_1 = { class: "text-base font-semibold text-ink" };
8
8
  const _hoisted_2 = { class: "flex flex-col gap-1" };
9
9
  const _hoisted_3 = ["data-vaul-no-drag"];
@@ -1,15 +1,15 @@
1
1
  import { defineComponent, ref, watch, computed, openBlock, createElementBlock, Fragment, renderList, normalizeStyle, createBlock, createVNode, withCtx, createElementVNode, renderSlot, mergeProps, normalizeProps, guardReactiveProps, createTextVNode, toDisplayString, createCommentVNode } from "vue";
2
- import { subtreeIds, subtreeSize, formatSize, sortNodes, ancestorIds, checkStateOf } from "../components/fileTree.js";
2
+ import { formatSize, sortNodes, computeStats, subtreeIds, ancestorIds } from "../components/fileTree.js";
3
3
  import { _ as _sfc_main$1 } from "./LpEmptyState-CrfyRJUG.js";
4
- import { _ as _sfc_main$3 } from "./LpFileTreeNode-D3lNEP-1.js";
5
- import { _ as _sfc_main$2 } from "./LpScrollArea-BtnZCCBT.js";
4
+ import { _ as _sfc_main$3 } from "./LpFileTreeNode-DFg4PhOK.js";
5
+ import { _ as _sfc_main$2 } from "./LpScrollArea-De9DLpa1.js";
6
6
  const _hoisted_1 = {
7
7
  key: 0,
8
8
  class: "flex flex-col gap-1"
9
9
  };
10
10
  const _hoisted_2 = {
11
11
  key: 2,
12
- class: "flex min-h-0 flex-col"
12
+ class: "flex h-full min-h-0 flex-col"
13
13
  };
14
14
  const _hoisted_3 = ["aria-multiselectable"];
15
15
  const _hoisted_4 = {
@@ -53,43 +53,56 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
53
53
  if (ids) checkedIds.value = new Set(ids);
54
54
  }
55
55
  );
56
- const byId = computed(() => {
57
- const map = /* @__PURE__ */ new Map();
58
- function walk(list) {
56
+ const index = computed(() => {
57
+ const byId2 = /* @__PURE__ */ new Map();
58
+ const parentOfId = /* @__PURE__ */ new Map();
59
+ function walk(list, parent) {
59
60
  for (const node of list) {
60
- map.set(node.id, node);
61
- if (node.children) walk(node.children);
61
+ byId2.set(node.id, node);
62
+ if (parent !== void 0) parentOfId.set(node.id, parent);
63
+ if (node.children) walk(node.children, node.id);
62
64
  }
63
65
  }
64
66
  walk(props.nodes);
65
- return map;
67
+ return { byId: byId2, parentOfId };
66
68
  });
69
+ const byId = computed(() => index.value.byId);
67
70
  const summary = computed(() => {
68
71
  let files = 0;
69
72
  let dirs = 0;
70
73
  let bytes = 0;
71
74
  let sized = false;
72
- for (const id of checkedIds.value) {
73
- const node = byId.value.get(id);
74
- if (!node) continue;
75
- if (node.kind === "dir") {
76
- dirs++;
77
- const inside = node.children?.length ? subtreeIds(node).some((child) => child !== id && checkedIds.value.has(child)) : false;
78
- if (!inside) {
79
- const size = subtreeSize(node);
80
- if (size !== void 0) {
81
- bytes += size;
75
+ function walk(list) {
76
+ for (const node of list) {
77
+ const stat = stats.value.get(node.id);
78
+ if (!stat || stat.state === "unchecked") continue;
79
+ if (node.kind === "file") {
80
+ files++;
81
+ if (node.size !== void 0) {
82
+ bytes += node.size;
82
83
  sized = true;
83
84
  }
85
+ continue;
84
86
  }
85
- } else {
86
- files++;
87
- if (node.size !== void 0) {
88
- bytes += node.size;
87
+ dirs++;
88
+ if (stat.state === "checked" && stat.size !== void 0) {
89
+ bytes += stat.size;
89
90
  sized = true;
91
+ files += stat.total;
92
+ countDirs(node.children ?? []);
93
+ continue;
90
94
  }
95
+ walk(node.children ?? []);
96
+ }
97
+ }
98
+ function countDirs(list) {
99
+ for (const node of list) {
100
+ if (node.kind !== "dir") continue;
101
+ dirs++;
102
+ countDirs(node.children ?? []);
91
103
  }
92
104
  }
105
+ walk(props.nodes);
93
106
  return {
94
107
  files,
95
108
  dirs,
@@ -111,12 +124,15 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
111
124
  if (value) next.add(id);
112
125
  else next.delete(id);
113
126
  }
114
- for (const ancestorId of ancestorIds(props.nodes, node.id).reverse()) {
127
+ let ancestorId = index.value.parentOfId.get(node.id);
128
+ while (ancestorId !== void 0) {
115
129
  const ancestor = byId.value.get(ancestorId);
116
- if (!ancestor?.children?.length) continue;
117
- const all = ancestor.children.every((c) => c.disabled || next.has(c.id));
118
- if (all) next.add(ancestorId);
119
- else next.delete(ancestorId);
130
+ if (ancestor?.children?.length) {
131
+ const all = ancestor.children.every((c) => c.disabled || next.has(c.id));
132
+ if (all) next.add(ancestorId);
133
+ else next.delete(ancestorId);
134
+ }
135
+ ancestorId = index.value.parentOfId.get(ancestorId);
120
136
  }
121
137
  setChecked(next);
122
138
  }
@@ -130,6 +146,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
130
146
  }
131
147
  const requested = /* @__PURE__ */ new Set();
132
148
  const roots = computed(() => props.sort ? sortNodes(props.nodes) : props.nodes);
149
+ const stats = computed(() => computeStats(props.nodes, checkedIds.value));
133
150
  const visible = computed(() => {
134
151
  const out = [];
135
152
  function walk(list) {
@@ -195,13 +212,16 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
195
212
  }
196
213
  function onSelect(node) {
197
214
  if (node.disabled) return;
215
+ if (props.selected === node.id) {
216
+ emit("update:selected", "");
217
+ return;
218
+ }
198
219
  emit("update:selected", node.id);
199
220
  emit("select", node);
200
221
  if (node.kind === "file") emit("open", node);
201
222
  }
202
223
  function parentOf(id) {
203
- const trail = ancestorIds(props.nodes, id);
204
- const parentId = trail.at(-1);
224
+ const parentId = index.value.parentOfId.get(id);
205
225
  return parentId ? visible.value.find((n) => n.id === parentId) : void 0;
206
226
  }
207
227
  function move(delta) {
@@ -256,7 +276,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
256
276
  if (!node) return;
257
277
  e.preventDefault();
258
278
  if (props.checkable) {
259
- onCheck(node, checkStateOf(node, checkedIds.value) !== "checked");
279
+ onCheck(node, stats.value.get(node.id)?.state !== "checked");
260
280
  break;
261
281
  }
262
282
  onSelect(node);
@@ -318,7 +338,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
318
338
  ref: root,
319
339
  role: "tree",
320
340
  "aria-multiselectable": __props.checkable,
321
- class: "flex flex-col",
341
+ class: "flex min-w-0 flex-col",
322
342
  onKeydown
323
343
  }, [
324
344
  (openBlock(true), createElementBlock(Fragment, null, renderList(roots.value, (node) => {
@@ -334,6 +354,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
334
354
  "icon-size": __props.iconSize,
335
355
  checkable: __props.checkable,
336
356
  checked: checkedIds.value,
357
+ stats: stats.value,
337
358
  "show-size": __props.showSize,
338
359
  "show-modified": __props.showModified,
339
360
  onSelect,
@@ -345,7 +366,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
345
366
  renderSlot(_ctx.$slots, "row", mergeProps({ ref_for: true }, slotProps))
346
367
  ]),
347
368
  _: 3
348
- }, 8, ["node", "expanded", "loading", "selected-id", "focused-id", "sort", "icon-size", "checkable", "checked", "show-size", "show-modified"]);
369
+ }, 8, ["node", "expanded", "loading", "selected-id", "focused-id", "sort", "icon-size", "checkable", "checked", "stats", "show-size", "show-modified"]);
349
370
  }), 128))
350
371
  ], 40, _hoisted_3)
351
372
  ]),
@@ -1,28 +1,32 @@
1
1
  import { defineComponent, computed, resolveComponent, openBlock, createElementBlock, createVNode, withCtx, createElementVNode, withModifiers, normalizeStyle, normalizeClass, createBlock, createCommentVNode, unref, renderSlot, createTextVNode, toDisplayString, Transition, Fragment, renderList, mergeProps } from "vue";
2
- import { sortNodes, checkStateOf, subtreeSize, formatSize, formatModified, fileIcon } from "../components/fileTree.js";
2
+ import { sortNodes, formatSize, formatModified, fileIcon } from "../components/fileTree.js";
3
3
  import { _ as _sfc_main$3 } from "./LpCheckbox-BO0zuuPh.js";
4
4
  import { _ as _sfc_main$1 } from "./LpContextMenu-D0mzXqA7.js";
5
5
  import { _ as _sfc_main$2 } from "./LpIcon-CCnX5_2j.js";
6
6
  const _hoisted_1 = ["aria-level", "aria-selected", "aria-expanded", "aria-disabled"];
7
7
  const _hoisted_2 = ["data-node-id", "tabindex"];
8
8
  const _hoisted_3 = { class: "flex size-4 shrink-0 items-center justify-center" };
9
- const _hoisted_4 = { class: "min-w-0 flex-1 truncate" };
9
+ const _hoisted_4 = { class: "min-w-0 flex-1 shrink-[2] truncate" };
10
10
  const _hoisted_5 = {
11
11
  key: 1,
12
- class: "shrink-0 text-xs text-muted"
12
+ class: "shrink-0 rounded-pill bg-brand-soft px-1.5 py-0.5 text-[11px] font-medium tabular-nums text-brand"
13
13
  };
14
14
  const _hoisted_6 = {
15
15
  key: 2,
16
- class: "shrink-0 text-xs tabular-nums text-muted/80"
16
+ class: "min-w-0 shrink truncate text-xs text-muted"
17
17
  };
18
18
  const _hoisted_7 = {
19
19
  key: 3,
20
- class: "w-16 shrink-0 text-right text-xs tabular-nums text-muted"
20
+ class: "shrink-0 text-xs tabular-nums text-muted/80"
21
21
  };
22
22
  const _hoisted_8 = {
23
+ key: 4,
24
+ class: "w-16 shrink-0 text-right text-xs tabular-nums text-muted"
25
+ };
26
+ const _hoisted_9 = {
23
27
  key: 0,
24
28
  role: "group",
25
- class: "flex flex-col overflow-hidden"
29
+ class: "flex min-w-0 flex-col overflow-hidden"
26
30
  };
27
31
  const _sfc_main = /* @__PURE__ */ defineComponent({
28
32
  __name: "LpFileTreeNode",
@@ -37,6 +41,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
37
41
  iconSize: {},
38
42
  checkable: { type: Boolean },
39
43
  checked: {},
44
+ stats: {},
40
45
  showSize: { type: Boolean },
41
46
  showModified: { type: Boolean }
42
47
  },
@@ -55,14 +60,20 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
55
60
  const list = props.node.children ?? [];
56
61
  return props.sort ? sortNodes(list) : list;
57
62
  });
63
+ const stat = computed(() => props.stats.get(props.node.id));
58
64
  const checkState = computed(
59
- () => props.checkable ? checkStateOf(props.node, props.checked) : "unchecked"
65
+ () => props.checkable ? stat.value?.state ?? "unchecked" : "unchecked"
60
66
  );
61
67
  const sizeLabel = computed(() => {
62
68
  if (!props.showSize) return "";
63
- const bytes = subtreeSize(props.node);
69
+ const bytes = stat.value?.size;
64
70
  return bytes === void 0 ? "" : formatSize(bytes);
65
71
  });
72
+ const countLabel = computed(() => {
73
+ if (!props.checkable || !isDir.value) return "";
74
+ const { checked = 0, total = 0 } = stat.value ?? {};
75
+ return total && checked && checked < total ? `${checked} / ${total}` : "";
76
+ });
66
77
  const modifiedLabel = computed(
67
78
  () => props.showModified && props.node.modified !== void 0 ? formatModified(props.node.modified) : ""
68
79
  );
@@ -104,6 +115,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
104
115
  const _component_LpFileTreeNode = resolveComponent("LpFileTreeNode", true);
105
116
  return openBlock(), createElementBlock("li", {
106
117
  role: "treeitem",
118
+ class: "min-w-0",
107
119
  "aria-level": __props.depth + 1,
108
120
  "aria-selected": isSelected.value,
109
121
  "aria-expanded": canExpand.value ? isOpen.value : void 0,
@@ -116,7 +128,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
116
128
  createElementVNode("div", {
117
129
  "data-node-id": __props.node.id,
118
130
  tabindex: isFocused.value ? 0 : -1,
119
- class: normalizeClass(["group/row relative flex cursor-pointer select-none items-center gap-1.5 rounded-control py-1 pr-2 text-sm outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring", [
131
+ class: normalizeClass(["group/row relative flex min-w-0 cursor-pointer select-none items-center gap-1.5 rounded-control py-1 pr-2 text-sm outline-none transition-colors duration-[var(--duration-fast)] focus-visible:ring-2 focus-visible:ring-ring", [
120
132
  __props.node.disabled ? "cursor-not-allowed text-muted/50" : isSelected.value ? "bg-brand-soft text-ink" : "text-muted hover:bg-surface-soft hover:text-ink"
121
133
  ]]),
122
134
  style: normalizeStyle({ paddingLeft: `${__props.depth * 14 + 6}px` }),
@@ -161,9 +173,10 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
161
173
  createTextVNode(toDisplayString(__props.node.name), 1)
162
174
  ])
163
175
  ]),
164
- __props.node.meta ? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString(__props.node.meta), 1)) : createCommentVNode("", true),
165
- modifiedLabel.value ? (openBlock(), createElementBlock("span", _hoisted_6, toDisplayString(modifiedLabel.value), 1)) : createCommentVNode("", true),
166
- sizeLabel.value ? (openBlock(), createElementBlock("span", _hoisted_7, toDisplayString(sizeLabel.value), 1)) : createCommentVNode("", true)
176
+ countLabel.value ? (openBlock(), createElementBlock("span", _hoisted_5, toDisplayString(countLabel.value), 1)) : createCommentVNode("", true),
177
+ __props.node.meta ? (openBlock(), createElementBlock("span", _hoisted_6, toDisplayString(__props.node.meta), 1)) : createCommentVNode("", true),
178
+ modifiedLabel.value ? (openBlock(), createElementBlock("span", _hoisted_7, toDisplayString(modifiedLabel.value), 1)) : createCommentVNode("", true),
179
+ sizeLabel.value ? (openBlock(), createElementBlock("span", _hoisted_8, toDisplayString(sizeLabel.value), 1)) : createCommentVNode("", true)
167
180
  ], 14, _hoisted_2)
168
181
  ]),
169
182
  _: 3
@@ -174,7 +187,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
174
187
  onLeave: onBranchLeave
175
188
  }, {
176
189
  default: withCtx(() => [
177
- isOpen.value && hasChildren.value ? (openBlock(), createElementBlock("ul", _hoisted_8, [
190
+ isOpen.value && hasChildren.value ? (openBlock(), createElementBlock("ul", _hoisted_9, [
178
191
  (openBlock(true), createElementBlock(Fragment, null, renderList(children.value, (child, i) => {
179
192
  return openBlock(), createBlock(_component_LpFileTreeNode, {
180
193
  key: child.id,
@@ -190,6 +203,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
190
203
  "icon-size": __props.iconSize,
191
204
  checkable: __props.checkable,
192
205
  checked: __props.checked,
206
+ stats: __props.stats,
193
207
  "show-size": __props.showSize,
194
208
  "show-modified": __props.showModified,
195
209
  onSelect: _cache[2] || (_cache[2] = ($event) => emit("select", $event)),
@@ -201,7 +215,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
201
215
  renderSlot(_ctx.$slots, "row", mergeProps({ ref_for: true }, slotProps))
202
216
  ]),
203
217
  _: 3
204
- }, 8, ["style", "node", "depth", "expanded", "loading", "selected-id", "focused-id", "sort", "icon-size", "checkable", "checked", "show-size", "show-modified"]);
218
+ }, 8, ["style", "node", "depth", "expanded", "loading", "selected-id", "focused-id", "sort", "icon-size", "checkable", "checked", "stats", "show-size", "show-modified"]);
205
219
  }), 128))
206
220
  ])) : createCommentVNode("", true)
207
221
  ]),
@@ -1,7 +1,7 @@
1
1
  import { defineComponent, computed, ref, watch, nextTick, onMounted, onBeforeUnmount, openBlock, createElementBlock, createVNode, normalizeStyle, withCtx, createElementVNode, toDisplayString, createCommentVNode, Fragment, createBlock, resolveDynamicComponent, TransitionGroup, mergeProps, renderList, normalizeClass, createTextVNode, Transition } from "vue";
2
2
  import { _ as _sfc_main$3 } from "./LpContextMenu-D0mzXqA7.js";
3
3
  import { _ as _sfc_main$2 } from "./LpIcon-CCnX5_2j.js";
4
- import { _ as _sfc_main$1 } from "./LpScrollArea-BtnZCCBT.js";
4
+ import { _ as _sfc_main$1 } from "./LpScrollArea-De9DLpa1.js";
5
5
  const _hoisted_1 = { class: "relative overflow-hidden rounded-card border border-line bg-surface font-mono text-xs leading-relaxed" };
6
6
  const _hoisted_2 = {
7
7
  key: 0,
@@ -354,6 +354,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
354
354
  ref_key: "scrollRef",
355
355
  ref: scrollRef,
356
356
  fade: "",
357
+ instant: "",
357
358
  "content-class": __props.wrap ? "" : "min-w-max",
358
359
  style: normalizeStyle({ height: __props.height }),
359
360
  onScroll
@@ -2,7 +2,7 @@ import { defineComponent, computed, useSlots, openBlock, createBlock, unref, wit
2
2
  import { DialogRoot, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription, DialogClose } from "reka-ui";
3
3
  import { CLOSE_ICON } from "../components/dropdown.js";
4
4
  import { _ as _sfc_main$1 } from "./LpIcon-CCnX5_2j.js";
5
- import { _ as _sfc_main$2 } from "./LpScrollArea-BtnZCCBT.js";
5
+ import { _ as _sfc_main$2 } from "./LpScrollArea-De9DLpa1.js";
6
6
  const _hoisted_1 = {
7
7
  key: 0,
8
8
  class: "flex shrink-0 items-start justify-between gap-4 p-5 pb-3"
@@ -5,7 +5,7 @@ import { _ as _sfc_main$4 } from "./LpContextMenu-D0mzXqA7.js";
5
5
  import { _ as _sfc_main$2 } from "./LpEmptyState-CrfyRJUG.js";
6
6
  import { _ as _sfc_main$5 } from "./LpIcon-CCnX5_2j.js";
7
7
  import { _ as _sfc_main$1 } from "./LpPopover-8268UmlC.js";
8
- import { _ as _sfc_main$3 } from "./LpScrollArea-BtnZCCBT.js";
8
+ import { _ as _sfc_main$3 } from "./LpScrollArea-De9DLpa1.js";
9
9
  const _hoisted_1 = { class: "flex items-center justify-between gap-2 px-3 py-2.5" };
10
10
  const _hoisted_2 = { class: "text-sm font-semibold" };
11
11
  const _hoisted_3 = {
@@ -5,7 +5,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
5
5
  __name: "LpScrollArea",
6
6
  props: {
7
7
  fade: { type: Boolean },
8
- smooth: { type: Boolean },
8
+ instant: { type: Boolean, default: false },
9
9
  contentClass: {},
10
10
  barInsetTop: {}
11
11
  },
@@ -47,7 +47,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
47
47
  ref_key: "viewportRef",
48
48
  ref: viewportRef,
49
49
  class: normalizeClass(["size-full min-w-0 [&>div]:!block [&>div]:!min-w-0", [
50
- __props.smooth ? "scroll-smooth motion-reduce:scroll-auto" : "",
50
+ __props.instant ? "" : "scroll-smooth motion-reduce:scroll-auto",
51
51
  __props.fade ? "[mask-image:linear-gradient(to_bottom,transparent_0,black_14px,black_calc(100%-14px),transparent_100%)]" : ""
52
52
  ]]),
53
53
  onScrollPassive: _cache[0] || (_cache[0] = ($event) => _ctx.$emit("scroll", $event))
@@ -1,6 +1,6 @@
1
1
  import { defineComponent, computed, useId, openBlock, createElementBlock, Fragment, createElementVNode, mergeProps, normalizeClass, renderSlot, createCommentVNode, createVNode, createSlots, withCtx, normalizeProps, guardReactiveProps, createBlock } from "vue";
2
- import { _ as _sfc_main$2 } from "./LpDrawer-6n5Hd-iF.js";
3
- import { _ as _sfc_main$1 } from "./LpSidebarNav-D-YvtwsH.js";
2
+ import { _ as _sfc_main$2 } from "./LpDrawer-BHzCy-Aa.js";
3
+ import { _ as _sfc_main$1 } from "./LpSidebarNav-TIUNHZX-.js";
4
4
  const _hoisted_1 = {
5
5
  key: 1,
6
6
  class: "mt-2 flex shrink-0 flex-col gap-2"
@@ -4,7 +4,7 @@ import { usePillTransition } from "../composables/usePillTransition.js";
4
4
  import { _ as _sfc_main$4 } from "./LpBadge-CXzBPnwO.js";
5
5
  import { _ as _sfc_main$2 } from "./LpContextMenu-D0mzXqA7.js";
6
6
  import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
7
- import { _ as _sfc_main$1 } from "./LpScrollArea-BtnZCCBT.js";
7
+ import { _ as _sfc_main$1 } from "./LpScrollArea-De9DLpa1.js";
8
8
  const _hoisted_1 = {
9
9
  key: 0,
10
10
  class: "flex min-h-0 flex-1 flex-col gap-4"
@@ -2,7 +2,7 @@ import { defineComponent, computed, ref, watch, onBeforeUnmount, openBlock, crea
2
2
  import { _ as _sfc_main$2 } from "./LpCheckbox-BO0zuuPh.js";
3
3
  import { _ as _sfc_main$4 } from "./LpContextMenu-D0mzXqA7.js";
4
4
  import { _ as _sfc_main$3 } from "./LpIcon-CCnX5_2j.js";
5
- import { _ as _sfc_main$1 } from "./LpScrollArea-BtnZCCBT.js";
5
+ import { _ as _sfc_main$1 } from "./LpScrollArea-De9DLpa1.js";
6
6
  const _hoisted_1 = { class: "w-full border-collapse text-sm" };
7
7
  const _hoisted_2 = { class: "border-b border-line bg-surface-soft" };
8
8
  const _hoisted_3 = {
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpAppShell-BLKZDLh8.js";
1
+ import { _ as _sfc_main } from "../chunks/LpAppShell-CV194os6.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpAutocomplete-B9uoCs4t.js";
1
+ import { _ as _sfc_main } from "../chunks/LpAutocomplete-DIgXmtCA.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,7 +1,7 @@
1
1
  import { defineComponent, ref, computed, watch, nextTick, onMounted, onBeforeUnmount, openBlock, createBlock, unref, withCtx, createVNode, withModifiers, createTextVNode, createElementVNode, withDirectives, withKeys, vModelText, createElementBlock, toDisplayString, createCommentVNode, Fragment, renderList, normalizeClass } from "vue";
2
2
  import { DialogRoot, DialogPortal, DialogOverlay, DialogContent, DialogTitle, DialogDescription } from "reka-ui";
3
3
  import { _ as _sfc_main$1 } from "../chunks/LpIcon-CCnX5_2j.js";
4
- import { _ as _sfc_main$2 } from "../chunks/LpScrollArea-BtnZCCBT.js";
4
+ import { _ as _sfc_main$2 } from "../chunks/LpScrollArea-De9DLpa1.js";
5
5
  import '../assets/LpCommandPalette-D315B47-.css';const _hoisted_1 = { class: "flex shrink-0 items-center gap-2.5 border-b border-line px-4" };
6
6
  const _hoisted_2 = ["placeholder", "onKeydown"];
7
7
  const _hoisted_3 = {
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpConfirmDialog-BA45MvRL.js";
1
+ import { _ as _sfc_main } from "../chunks/LpConfirmDialog-WHkymac5.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpDrawer-6n5Hd-iF.js";
1
+ import { _ as _sfc_main } from "../chunks/LpDrawer-BHzCy-Aa.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpFileTree-0Ko_uj8q.js";
1
+ import { _ as _sfc_main } from "../chunks/LpFileTree-BQz8Rfgz.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { FileNode } from './fileTree';
1
+ import { FileNode, NodeStats } from './fileTree';
2
2
  type __VLS_Props = {
3
3
  node: FileNode;
4
4
  /** Nesting depth, 0 at the root — drives the indent guide and aria-level. */
@@ -13,6 +13,8 @@ type __VLS_Props = {
13
13
  /** Render a tri-state checkbox per row (multi-select mode). */
14
14
  checkable: boolean;
15
15
  checked: Set<string>;
16
+ /** Per-node state, derived once by the root for the whole tree. */
17
+ stats: Map<string, NodeStats>;
16
18
  showSize: boolean;
17
19
  showModified: boolean;
18
20
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpFileTreeNode-D3lNEP-1.js";
1
+ import { _ as _sfc_main } from "../chunks/LpFileTreeNode-DFg4PhOK.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpLogViewer-BBgaY38i.js";
1
+ import { _ as _sfc_main } from "../chunks/LpLogViewer-BvzRZPjs.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpModal-B9CVVC5U.js";
1
+ import { _ as _sfc_main } from "../chunks/LpModal-DpBHQpbU.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpNotificationBell-B9VHks_D.js";
1
+ import { _ as _sfc_main } from "../chunks/LpNotificationBell-BtfRoL2i.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,6 +1,6 @@
1
1
  type __VLS_Props = {
2
2
  fade?: boolean;
3
- smooth?: boolean;
3
+ instant?: boolean;
4
4
  contentClass?: string;
5
5
  barInsetTop?: string;
6
6
  };
@@ -14,7 +14,9 @@ declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {
14
14
  scroll: (event: Event) => any;
15
15
  }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
16
16
  onScroll?: ((event: Event) => any) | undefined;
17
- }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
17
+ }>, {
18
+ instant: boolean;
19
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
18
20
  declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
19
21
  declare const _default: typeof __VLS_export;
20
22
  export default _default;
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpScrollArea-BtnZCCBT.js";
1
+ import { _ as _sfc_main } from "../chunks/LpScrollArea-De9DLpa1.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpSidebar-tZ2z-PnM.js";
1
+ import { _ as _sfc_main } from "../chunks/LpSidebar-B11IDRQi.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpSidebarNav-D-YvtwsH.js";
1
+ import { _ as _sfc_main } from "../chunks/LpSidebarNav-TIUNHZX-.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpTable-CzHVcr3v.js";
1
+ import { _ as _sfc_main } from "../chunks/LpTable-CKGaUbEM.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };