agentic-drive 0.8.0 → 0.9.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.
package/dist/embed.js CHANGED
@@ -4,28 +4,28 @@ import {
4
4
  } from "./chunk-LELTSSZF.js";
5
5
 
6
6
  // ui/embed.tsx
7
- import { useEffect as useEffect2, useState as useState2 } from "react";
7
+ import { useEffect as useEffect3, useState as useState3 } from "react";
8
8
  import { createRoot } from "react-dom/client";
9
- import { FolderIcon as FolderIcon2, SidebarNav as SidebarNav2, ToastProvider, TooltipProvider } from "agentic-design/react";
9
+ import { FolderIcon as FolderIcon3, SidebarNav as SidebarNav2, ToastProvider, TooltipProvider } from "agentic-design/react";
10
10
 
11
11
  // ui/App.tsx
12
- import { useEffect, useMemo, useRef, useState } from "react";
12
+ import { useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
13
13
  import {
14
14
  AppShell,
15
- Button,
15
+ Button as Button2,
16
16
  Dialog,
17
17
  DialogClose,
18
18
  EmptyState,
19
- FileIcon,
19
+ FileIcon as FileIcon2,
20
20
  FileList,
21
21
  FileRow,
22
- FolderIcon,
22
+ FolderIcon as FolderIcon2,
23
23
  IconButton,
24
24
  Input,
25
25
  Menu,
26
26
  MoreIcon,
27
27
  PlusIcon,
28
- PresenceChip,
28
+ PresenceChip as PresenceChip2,
29
29
  Select,
30
30
  ShareDialog,
31
31
  SidebarNav,
@@ -34,15 +34,121 @@ import {
34
34
  useTheme,
35
35
  useToast
36
36
  } from "agentic-design/react";
37
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
37
+
38
+ // ui/browse.tsx
39
+ import { useEffect, useRef, useState } from "react";
40
+ import { Button, FileIcon, FolderIcon, PresenceChip } from "agentic-design/react";
41
+ import { jsx, jsxs } from "react/jsx-runtime";
42
+ function sortItems(items, { key, dir }, dateOf = (i) => i.updatedAt) {
43
+ const sign = dir === "asc" ? 1 : -1;
44
+ const by = key === "name" ? (a, b) => a.name.localeCompare(b.name, void 0, { numeric: true }) : (a, b) => dateOf(a) - dateOf(b);
45
+ return [...items].sort((a, b) => (a.kind === "folder" ? 0 : 1) - (b.kind === "folder" ? 0 : 1) || sign * by(a, b) || a.name.localeCompare(b.name));
46
+ }
47
+ function Stamp({ at }) {
48
+ const d = new Date(at);
49
+ return Number.isFinite(d.getTime()) ? /* @__PURE__ */ jsx("time", { dateTime: d.toISOString(), children: dateFormat.format(d) }) : /* @__PURE__ */ jsx("span", { children: "\u2014" });
50
+ }
51
+ var dateFormat = new Intl.DateTimeFormat(void 0, { dateStyle: "medium", timeStyle: "short" });
52
+ function ExpandableSearch({ value, onChange }) {
53
+ const [open, setOpen] = useState(false);
54
+ const input = useRef(null);
55
+ const trigger = useRef(null);
56
+ const restore = useRef(false);
57
+ const shown = open || !!value;
58
+ useEffect(() => {
59
+ if (shown) input.current?.focus();
60
+ else if (restore.current) {
61
+ restore.current = false;
62
+ trigger.current?.focus();
63
+ }
64
+ }, [shown]);
65
+ const close = () => {
66
+ restore.current = true;
67
+ onChange("");
68
+ setOpen(false);
69
+ };
70
+ if (!shown) {
71
+ return /* @__PURE__ */ jsx(Button, { ref: trigger, variant: "ghost", size: "sm", onClick: () => setOpen(true), children: "Search" });
72
+ }
73
+ return /* @__PURE__ */ jsxs("div", { className: "drive-search", role: "search", children: [
74
+ /* @__PURE__ */ jsx(
75
+ "input",
76
+ {
77
+ ref: input,
78
+ type: "search",
79
+ className: "drive-search__input",
80
+ "aria-label": "Search this folder",
81
+ placeholder: "Search this folder",
82
+ value,
83
+ onChange: (e) => onChange(e.target.value),
84
+ onKeyDown: (e) => {
85
+ if (e.key === "Escape") close();
86
+ }
87
+ }
88
+ ),
89
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: close, children: "Close search" })
90
+ ] });
91
+ }
92
+ function ItemTable({ label, items, sort, onSort, selected = /* @__PURE__ */ new Set(), onSelect, href, who: who2, actions, dateOf = (i) => i.updatedAt }) {
93
+ const all = items.length > 0 && items.every((i) => selected.has(i.id));
94
+ const some = !all && items.some((i) => selected.has(i.id));
95
+ const head = useRef(null);
96
+ useEffect(() => {
97
+ if (head.current) head.current.indeterminate = some;
98
+ }, [some]);
99
+ const toggle = (id, on) => {
100
+ if (!onSelect) return;
101
+ const next = new Set(selected);
102
+ if (on) next.add(id);
103
+ else next.delete(id);
104
+ onSelect(next);
105
+ };
106
+ const th = (key, text, cls) => /* @__PURE__ */ jsx("th", { scope: "col", className: cls, "aria-sort": sort.key === key ? sort.dir === "asc" ? "ascending" : "descending" : "none", children: /* @__PURE__ */ jsxs("button", { type: "button", className: "drive-sort", onClick: () => onSort(key), children: [
107
+ text,
108
+ /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: sort.key === key ? sort.dir === "asc" ? " \u2191" : " \u2193" : "" })
109
+ ] }) });
110
+ return /* @__PURE__ */ jsxs("table", { className: "drive-table", "aria-label": label, children: [
111
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { children: [
112
+ onSelect && /* @__PURE__ */ jsx("th", { scope: "col", className: "drive-table__check", children: /* @__PURE__ */ jsx("input", { ref: head, type: "checkbox", className: "agd-checkbox", "aria-label": "Select all items", checked: all, onChange: (e) => onSelect(e.target.checked ? new Set(items.map((i) => i.id)) : /* @__PURE__ */ new Set()) }) }),
113
+ th("name", "Name"),
114
+ /* @__PURE__ */ jsx("th", { scope: "col", className: "drive-table__who", children: "Edited by" }),
115
+ th("modified", "Modified", "drive-table__date"),
116
+ /* @__PURE__ */ jsx("th", { scope: "col", children: /* @__PURE__ */ jsx("span", { className: "drive-sr", children: "Actions" }) })
117
+ ] }) }),
118
+ /* @__PURE__ */ jsx("tbody", { children: items.map((i) => /* @__PURE__ */ jsxs("tr", { className: "drive-row", "data-selected": selected.has(i.id) || void 0, children: [
119
+ onSelect && /* @__PURE__ */ jsx("td", { className: "drive-table__check", children: /* @__PURE__ */ jsx("input", { type: "checkbox", className: "agd-checkbox", "aria-label": `Select ${i.name}`, checked: selected.has(i.id), onChange: (e) => toggle(i.id, e.target.checked) }) }),
120
+ /* @__PURE__ */ jsx("td", { children: /* @__PURE__ */ jsxs("div", { className: "drive-table__name", children: [
121
+ /* @__PURE__ */ jsx("span", { className: "drive-table__icon", "aria-hidden": "true", children: i.kind === "folder" ? /* @__PURE__ */ jsx(FolderIcon, {}) : /* @__PURE__ */ jsx(FileIcon, {}) }),
122
+ /* @__PURE__ */ jsx("a", { href: href(i), children: i.name })
123
+ ] }) }),
124
+ /* @__PURE__ */ jsx("td", { className: "drive-table__who", children: /* @__PURE__ */ jsx(PresenceChip, { entry: who2(i.updatedBy, i.owner) }) }),
125
+ /* @__PURE__ */ jsx("td", { className: "drive-table__date", children: /* @__PURE__ */ jsx(Stamp, { at: dateOf(i) }) }),
126
+ /* @__PURE__ */ jsx("td", { className: "drive-table__actions", children: actions(i) })
127
+ ] }, i.id)) })
128
+ ] });
129
+ }
130
+ function BulkBar({ count, busy, onTrash, onClear }) {
131
+ if (count === 0) return null;
132
+ return /* @__PURE__ */ jsxs("div", { role: "region", "aria-label": "Selected items", className: "drive-bulk", children: [
133
+ /* @__PURE__ */ jsxs("span", { role: "status", children: [
134
+ count,
135
+ " selected"
136
+ ] }),
137
+ /* @__PURE__ */ jsx(Button, { variant: "danger", size: "sm", onClick: onTrash, disabled: busy, children: "Move to trash" }),
138
+ /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "sm", onClick: onClear, children: "Clear selection" })
139
+ ] });
140
+ }
141
+
142
+ // ui/App.tsx
143
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
38
144
  var errorText = (e) => e instanceof DriveApiError ? `${e.message} (${e.code})` : e instanceof Error ? e.message : "Something went wrong";
39
145
  var byName = (a, b) => (a.kind === "folder" ? 0 : 1) - (b.kind === "folder" ? 0 : 1) || a.name.localeCompare(b.name);
40
146
  var who = (a, owner) => a.kind === "human" ? { id: a.id, name: a.id, kind: "person" } : { id: a.id, name: a.id, kind: "agent", actingFor: owner };
41
- var MachineLabel = ({ item }) => item.machineMade ? /* @__PURE__ */ jsx("span", { className: "agd-pill", children: "Created by agent" }) : null;
147
+ var MachineLabel = ({ item }) => item.machineMade ? /* @__PURE__ */ jsx2("span", { className: "agd-pill", children: "Created by agent" }) : null;
42
148
  function useLoad(fn) {
43
- const [state, set] = useState({ status: "loading" });
44
- const [tick, setTick] = useState(0);
45
- useEffect(() => {
149
+ const [state, set] = useState2({ status: "loading" });
150
+ const [tick, setTick] = useState2(0);
151
+ useEffect2(() => {
46
152
  let live = true;
47
153
  fn().then((data) => live && set({ status: "ok", data }), (e) => live && set({ status: "error", error: errorText(e) }));
48
154
  return () => {
@@ -52,27 +158,30 @@ function useLoad(fn) {
52
158
  return [state, () => setTick((n) => n + 1)];
53
159
  }
54
160
  function Status({ state, retry, children }) {
55
- if (state.status === "loading") return /* @__PURE__ */ jsxs("p", { role: "status", className: "drive-status", children: [
56
- /* @__PURE__ */ jsx("span", { className: "agd-spinner", "aria-hidden": "true" }),
161
+ if (state.status === "loading") return /* @__PURE__ */ jsxs2("p", { role: "status", className: "drive-status", children: [
162
+ /* @__PURE__ */ jsx2("span", { className: "agd-spinner", "aria-hidden": "true" }),
57
163
  " Loading\u2026"
58
164
  ] });
59
- if (state.status === "error") return /* @__PURE__ */ jsx("div", { role: "alert", children: /* @__PURE__ */ jsx(EmptyState, { title: "Could not load this page", description: state.error, action: /* @__PURE__ */ jsx(Button, { onClick: retry, children: "Try again" }) }) });
60
- return /* @__PURE__ */ jsx(Fragment, { children });
165
+ if (state.status === "error") return /* @__PURE__ */ jsx2("div", { role: "alert", children: /* @__PURE__ */ jsx2(EmptyState, { title: "Could not load this page", description: state.error, action: /* @__PURE__ */ jsx2(Button2, { onClick: retry, children: "Try again" }) }) });
166
+ return /* @__PURE__ */ jsx2(Fragment, { children });
61
167
  }
62
168
  var routeParts = (hash) => hash.replace(/^#\/?/, "").split("/");
63
169
  function Routes({ hash, client, origin, go }) {
64
170
  const parts = routeParts(hash);
65
- if (parts[0] === "trash") return /* @__PURE__ */ jsx(TrashView, { client }, "trash");
66
- if (parts[0] === "f" && parts[1]) return /* @__PURE__ */ jsx(Download, { client, id: parts[1], origin, go }, hash);
67
- return /* @__PURE__ */ jsx(Browser, { client, loc: parts[0] || "/", origin }, hash);
171
+ if (parts[0] === "trash") return /* @__PURE__ */ jsx2(TrashView, { client }, "trash");
172
+ if (parts[0] === "f" && parts[1]) return /* @__PURE__ */ jsx2(Download, { client, id: parts[1], origin, go }, hash);
173
+ return /* @__PURE__ */ jsx2(Browser, { client, loc: parts[0] || "/", origin }, hash);
68
174
  }
69
175
  function Browser({ client, loc, origin }) {
70
176
  const toast = useToast();
71
- const [view, setView] = useState(() => localStorage.getItem("agentic-drive-view") === "grid" ? "grid" : "list");
72
- const [dlg, setDlg] = useState(null);
73
- const [drag, setDrag] = useState(false);
74
- const [busy, setBusy] = useState(false);
75
- const input = useRef(null);
177
+ const [view, setView] = useState2(() => localStorage.getItem("agentic-drive-view") === "grid" ? "grid" : "list");
178
+ const [dlg, setDlg] = useState2(null);
179
+ const [drag, setDrag] = useState2(false);
180
+ const [busy, setBusy] = useState2(false);
181
+ const [query, setQuery] = useState2("");
182
+ const [sort, setSort] = useState2({ key: "name", dir: "asc" });
183
+ const [selected, setSelected] = useState2(/* @__PURE__ */ new Set());
184
+ const input = useRef2(null);
76
185
  const [state, reload] = useLoad(async () => {
77
186
  const root = await client.stat("/");
78
187
  const folder = loc === "/" ? root : await client.stat(loc);
@@ -116,20 +225,35 @@ function Browser({ client, loc, origin }) {
116
225
  setDrag(false);
117
226
  if (e.dataTransfer.files.length) void upload(e.dataTransfer.files);
118
227
  };
228
+ const onSort = (key) => setSort((s) => ({ key, dir: s.key === key && s.dir === "asc" ? "desc" : "asc" }));
229
+ const trashSelected = async (ids) => {
230
+ setBusy(true);
231
+ const results = await Promise.allSettled(ids.map((id) => client.trash(id)));
232
+ const failed = results.filter((r) => r.status === "rejected");
233
+ if (failed.length) toast({ title: `${failed.length} of ${ids.length} items did not move`, description: errorText(failed[0].reason), tone: "danger" });
234
+ else toast({ title: `Moved ${ids.length} ${ids.length === 1 ? "item" : "items"} to trash`, tone: "success" });
235
+ setSelected(/* @__PURE__ */ new Set());
236
+ setBusy(false);
237
+ reload();
238
+ };
119
239
  const setViewMode = (v) => {
240
+ setSelected(/* @__PURE__ */ new Set());
120
241
  localStorage.setItem("agentic-drive-view", v);
121
242
  setView(v);
122
243
  };
123
- return /* @__PURE__ */ jsx("section", { onDragOver: (e) => {
244
+ return /* @__PURE__ */ jsx2("section", { onDragOver: (e) => {
124
245
  e.preventDefault();
125
246
  setDrag(true);
126
247
  }, onDragLeave: (e) => {
127
248
  if (!e.currentTarget.contains(e.relatedTarget)) setDrag(false);
128
- }, onDrop, className: drag ? "drive-drop drive-drop--over" : "drive-drop", "aria-label": "Files", children: /* @__PURE__ */ jsx(Status, { state, retry: reload, children: state.status === "ok" && (() => {
129
- const { folder, root, crumbs, items, tree } = state.data;
130
- const actions = (item) => /* @__PURE__ */ jsxs(Fragment, { children: [
131
- /* @__PURE__ */ jsx(MachineLabel, { item }),
132
- /* @__PURE__ */ jsx(Menu, { align: "end", trigger: /* @__PURE__ */ jsx(IconButton, { label: `Actions for ${item.name}`, icon: /* @__PURE__ */ jsx(MoreIcon, {}), variant: "ghost" }), items: [
249
+ }, onDrop, className: `drive-drop${drag ? " drive-drop--over" : ""}${view === "list" && selected.size ? " drive-drop--bulk" : ""}`, "aria-label": "Files", children: /* @__PURE__ */ jsx2(Status, { state, retry: reload, children: state.status === "ok" && (() => {
250
+ const { folder, root, crumbs, tree } = state.data;
251
+ const needle = query.trim().toLowerCase();
252
+ const items = sortItems(state.data.items.filter((i) => i.name.toLowerCase().includes(needle)), sort);
253
+ const picked = items.filter((i) => selected.has(i.id)).map((i) => i.id);
254
+ const actions = (item) => /* @__PURE__ */ jsxs2(Fragment, { children: [
255
+ /* @__PURE__ */ jsx2(MachineLabel, { item }),
256
+ /* @__PURE__ */ jsx2(Menu, { align: "end", trigger: /* @__PURE__ */ jsx2(IconButton, { label: `Actions for ${item.name}`, icon: /* @__PURE__ */ jsx2(MoreIcon, {}), variant: "ghost" }), items: [
133
257
  { label: "Rename", onSelect: () => setDlg({ kind: "rename", item }) },
134
258
  { label: "Move", onSelect: () => setDlg({ kind: "move", item }) },
135
259
  { label: "Share", onSelect: () => setDlg({ kind: "share", item }) },
@@ -138,55 +262,63 @@ function Browser({ client, loc, origin }) {
138
262
  ] })
139
263
  ] });
140
264
  const href = (i) => i.kind === "folder" ? `#/${i.id}` : `#/f/${i.id}`;
141
- const level = (id) => /* @__PURE__ */ jsx("ul", { className: "drive-tree", children: (tree.get(id) ?? []).map((n) => /* @__PURE__ */ jsxs("li", { children: [
142
- /* @__PURE__ */ jsxs("a", { href: `#/${n.id}`, "aria-current": n.id === folder.id ? "page" : void 0, children: [
143
- /* @__PURE__ */ jsx(FolderIcon, { "aria-hidden": "true" }),
265
+ const level = (id) => /* @__PURE__ */ jsx2("ul", { className: "drive-tree", children: (tree.get(id) ?? []).map((n) => /* @__PURE__ */ jsxs2("li", { children: [
266
+ /* @__PURE__ */ jsxs2("a", { href: `#/${n.id}`, "aria-current": n.id === folder.id ? "page" : void 0, children: [
267
+ /* @__PURE__ */ jsx2(FolderIcon2, { "aria-hidden": "true" }),
144
268
  " ",
145
269
  n.name
146
270
  ] }),
147
271
  tree.has(n.id) && level(n.id)
148
272
  ] }, n.id)) });
149
- return /* @__PURE__ */ jsxs(Fragment, { children: [
150
- /* @__PURE__ */ jsx("nav", { "aria-label": "Breadcrumb", className: "drive-crumbs", children: /* @__PURE__ */ jsxs("ol", { children: [
151
- /* @__PURE__ */ jsx("li", { children: crumbs.length ? /* @__PURE__ */ jsx("a", { href: "#/", children: "My files" }) : /* @__PURE__ */ jsx("span", { "aria-current": "page", children: "My files" }) }),
152
- crumbs.map((c, i) => /* @__PURE__ */ jsx("li", { children: i === crumbs.length - 1 ? /* @__PURE__ */ jsx("span", { "aria-current": "page", children: c.name }) : /* @__PURE__ */ jsx("a", { href: `#/${c.id}`, children: c.name }) }, c.id))
273
+ return /* @__PURE__ */ jsxs2(Fragment, { children: [
274
+ /* @__PURE__ */ jsx2("nav", { "aria-label": "Breadcrumb", className: "drive-crumbs", children: /* @__PURE__ */ jsxs2("ol", { children: [
275
+ /* @__PURE__ */ jsx2("li", { children: crumbs.length ? /* @__PURE__ */ jsx2("a", { href: "#/", children: "My files" }) : /* @__PURE__ */ jsx2("span", { "aria-current": "page", children: "My files" }) }),
276
+ crumbs.map((c, i) => /* @__PURE__ */ jsx2("li", { children: i === crumbs.length - 1 ? /* @__PURE__ */ jsx2("span", { "aria-current": "page", children: c.name }) : /* @__PURE__ */ jsx2("a", { href: `#/${c.id}`, children: c.name }) }, c.id))
153
277
  ] }) }),
154
- /* @__PURE__ */ jsxs("div", { className: "drive-bar", children: [
155
- /* @__PURE__ */ jsx("h1", { children: folder.id === root.id ? "My files" : folder.name }),
156
- /* @__PURE__ */ jsxs(Toolbar, { label: "Folder actions", children: [
157
- /* @__PURE__ */ jsx(ToolbarButton, { label: "New folder", icon: /* @__PURE__ */ jsx(PlusIcon, {}), onClick: () => setDlg({ kind: "mkdir" }) }),
158
- /* @__PURE__ */ jsx(ToolbarButton, { label: "Upload files", icon: /* @__PURE__ */ jsx(FileIcon, {}), onClick: () => input.current?.click(), disabled: busy }),
159
- /* @__PURE__ */ jsx(ToolbarButton, { label: "List view", icon: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u2630" }), pressed: view === "list", onClick: () => setViewMode("list") }),
160
- /* @__PURE__ */ jsx(ToolbarButton, { label: "Grid view", icon: /* @__PURE__ */ jsx("span", { "aria-hidden": "true", children: "\u25A6" }), pressed: view === "grid", onClick: () => setViewMode("grid") })
278
+ /* @__PURE__ */ jsxs2("div", { className: "drive-bar", children: [
279
+ /* @__PURE__ */ jsxs2("div", { className: "drive-title", children: [
280
+ /* @__PURE__ */ jsx2("h1", { children: folder.id === root.id ? "My files" : folder.name }),
281
+ folder.id !== root.id && /* @__PURE__ */ jsx2(Button2, { variant: "secondary", size: "sm", onClick: () => setDlg({ kind: "share", item: folder }), children: "Share" })
282
+ ] }),
283
+ /* @__PURE__ */ jsxs2(Toolbar, { label: "Folder actions", children: [
284
+ /* @__PURE__ */ jsx2(ToolbarButton, { label: "New folder", icon: /* @__PURE__ */ jsx2(PlusIcon, {}), onClick: () => setDlg({ kind: "mkdir" }) }),
285
+ /* @__PURE__ */ jsx2(ToolbarButton, { label: "Upload files", icon: /* @__PURE__ */ jsx2(FileIcon2, {}), onClick: () => input.current?.click(), disabled: busy }),
286
+ /* @__PURE__ */ jsx2(ToolbarButton, { label: "List view", icon: /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", children: "\u2630" }), pressed: view === "list", onClick: () => setViewMode("list") }),
287
+ /* @__PURE__ */ jsx2(ToolbarButton, { label: "Grid view", icon: /* @__PURE__ */ jsx2("span", { "aria-hidden": "true", children: "\u25A6" }), pressed: view === "grid", onClick: () => setViewMode("grid") })
161
288
  ] }),
162
- /* @__PURE__ */ jsx("input", { ref: input, type: "file", multiple: true, hidden: true, "aria-label": "Choose files to upload", onChange: (e) => {
289
+ /* @__PURE__ */ jsx2("input", { ref: input, type: "file", multiple: true, hidden: true, "aria-label": "Choose files to upload", onChange: (e) => {
163
290
  if (e.target.files) void upload(e.target.files);
164
291
  e.target.value = "";
165
292
  } })
166
293
  ] }),
167
- /* @__PURE__ */ jsxs("div", { className: "drive-split", children: [
168
- /* @__PURE__ */ jsx("nav", { "aria-label": "Folders", className: "drive-folders", children: level(root.id) }),
169
- /* @__PURE__ */ jsx("div", { children: items.length === 0 ? /* @__PURE__ */ jsx(EmptyState, { title: "This folder is empty", description: "Drop files here, or use Upload files.", action: /* @__PURE__ */ jsx(Button, { onClick: () => input.current?.click(), disabled: busy, children: "Upload files" }) }) : view === "list" ? /* @__PURE__ */ jsx(FileList, { label: `Items in ${folder.name || "My files"}`, children: items.map((i) => /* @__PURE__ */ jsx(FileRow, { name: i.name, href: href(i), kind: i.kind === "folder" ? "folder" : "file", modified: new Date(i.updatedAt), editedBy: who(i.updatedBy, i.owner), actions: actions(i) }, i.id)) }) : /* @__PURE__ */ jsx("ul", { className: "drive-grid", "aria-label": `Items in ${folder.name || "My files"}`, children: items.map((i) => /* @__PURE__ */ jsxs("li", { className: "drive-card", children: [
170
- /* @__PURE__ */ jsx("span", { className: "drive-card__icon", "aria-hidden": "true", children: i.kind === "folder" ? /* @__PURE__ */ jsx(FolderIcon, {}) : /* @__PURE__ */ jsx(FileIcon, {}) }),
171
- /* @__PURE__ */ jsx("a", { href: href(i), className: "drive-card__name", children: i.name }),
172
- /* @__PURE__ */ jsxs("span", { className: "drive-card__meta", children: [
173
- "Edited by ",
174
- /* @__PURE__ */ jsx(PresenceChip, { entry: who(i.updatedBy, i.owner) })
175
- ] }),
176
- /* @__PURE__ */ jsx("span", { className: "drive-card__actions", children: actions(i) })
177
- ] }, i.id)) }) })
294
+ /* @__PURE__ */ jsxs2("div", { className: "drive-split", children: [
295
+ /* @__PURE__ */ jsx2("nav", { "aria-label": "Folders", className: "drive-folders", children: level(root.id) }),
296
+ /* @__PURE__ */ jsxs2("div", { children: [
297
+ /* @__PURE__ */ jsx2("div", { className: "drive-tools", children: /* @__PURE__ */ jsx2(ExpandableSearch, { value: query, onChange: setQuery }) }),
298
+ /* @__PURE__ */ jsx2("p", { role: "status", className: "drive-sr", children: needle ? `${items.length} of ${state.data.items.length} items match` : "" }),
299
+ state.data.items.length === 0 ? /* @__PURE__ */ jsx2(EmptyState, { title: "This folder is empty", description: "Drop files here, or use Upload files.", action: /* @__PURE__ */ jsx2(Button2, { onClick: () => input.current?.click(), disabled: busy, children: "Upload files" }) }) : items.length === 0 ? /* @__PURE__ */ jsx2(EmptyState, { title: "Nothing matches", description: `No item in this folder has \u201C${query.trim()}\u201D in its name.`, action: /* @__PURE__ */ jsx2(Button2, { onClick: () => setQuery(""), children: "Clear search" }) }) : view === "list" ? /* @__PURE__ */ jsx2(ItemTable, { label: `Items in ${folder.name || "My files"}`, items, sort, onSort, selected, onSelect: setSelected, href, who, actions }) : /* @__PURE__ */ jsx2("ul", { className: "drive-grid", "aria-label": `Items in ${folder.name || "My files"}`, children: items.map((i) => /* @__PURE__ */ jsxs2("li", { className: "drive-card", children: [
300
+ /* @__PURE__ */ jsx2("span", { className: "drive-card__icon", "aria-hidden": "true", children: i.kind === "folder" ? /* @__PURE__ */ jsx2(FolderIcon2, {}) : /* @__PURE__ */ jsx2(FileIcon2, {}) }),
301
+ /* @__PURE__ */ jsx2("a", { href: href(i), className: "drive-card__name", children: i.name }),
302
+ /* @__PURE__ */ jsxs2("span", { className: "drive-card__meta", children: [
303
+ "Edited by ",
304
+ /* @__PURE__ */ jsx2(PresenceChip2, { entry: who(i.updatedBy, i.owner) })
305
+ ] }),
306
+ /* @__PURE__ */ jsx2("span", { className: "drive-card__actions", children: actions(i) })
307
+ ] }, i.id)) })
308
+ ] })
178
309
  ] }),
179
- dlg?.kind === "mkdir" && /* @__PURE__ */ jsx(Prompt, { open: true, title: "New folder", label: "Folder name", initial: "", submit: "Create", onClose: () => setDlg(null), onSubmit: (name) => act(() => client.mkdir(folder.id === root.id ? `/${name}` : `${folder.id}/${name}`), `Created ${name}`) }),
180
- dlg?.kind === "rename" && /* @__PURE__ */ jsx(Prompt, { open: true, title: "Rename", label: "New name", initial: dlg.item.name, submit: "Rename", onClose: () => setDlg(null), onSubmit: (name) => act(() => client.move(dlg.item.id, dlg.item.parentId, { name }), `Renamed to ${name}`) }),
181
- dlg?.kind === "move" && /* @__PURE__ */ jsx(MoveDialog, { client, item: dlg.item, onClose: () => setDlg(null), onMove: (to) => act(() => client.move(dlg.item.id, to), `Moved ${dlg.item.name}`) }),
182
- dlg?.kind === "share" && /* @__PURE__ */ jsx(Sharing, { client, item: dlg.item, origin, onClose: () => setDlg(null) })
310
+ view === "list" && /* @__PURE__ */ jsx2(BulkBar, { count: picked.length, busy, onTrash: () => void trashSelected(picked), onClear: () => setSelected(/* @__PURE__ */ new Set()) }),
311
+ dlg?.kind === "mkdir" && /* @__PURE__ */ jsx2(Prompt, { open: true, title: "New folder", label: "Folder name", initial: "", submit: "Create", onClose: () => setDlg(null), onSubmit: (name) => act(() => client.mkdir(folder.id === root.id ? `/${name}` : `${folder.id}/${name}`), `Created ${name}`) }),
312
+ dlg?.kind === "rename" && /* @__PURE__ */ jsx2(Prompt, { open: true, title: "Rename", label: "New name", initial: dlg.item.name, submit: "Rename", onClose: () => setDlg(null), onSubmit: (name) => act(() => client.move(dlg.item.id, dlg.item.parentId, { name }), `Renamed to ${name}`) }),
313
+ dlg?.kind === "move" && /* @__PURE__ */ jsx2(MoveDialog, { client, item: dlg.item, onClose: () => setDlg(null), onMove: (to) => act(() => client.move(dlg.item.id, to), `Moved ${dlg.item.name}`) }),
314
+ dlg?.kind === "share" && /* @__PURE__ */ jsx2(Sharing, { client, item: dlg.item, origin, onClose: () => setDlg(null) })
183
315
  ] });
184
316
  })() }) });
185
317
  }
186
318
  function Prompt({ open, title, label, initial, submit, onClose, onSubmit }) {
187
- const [value, setValue] = useState(initial);
188
- const sent = useRef(false);
189
- const [problem, setProblem] = useState();
319
+ const [value, setValue] = useState2(initial);
320
+ const sent = useRef2(false);
321
+ const [problem, setProblem] = useState2();
190
322
  const send = (name) => {
191
323
  if (!name) return;
192
324
  if (name.includes("/")) return setProblem("A name cannot have a slash.");
@@ -196,16 +328,16 @@ function Prompt({ open, title, label, initial, submit, onClose, onSubmit }) {
196
328
  sent.current = false;
197
329
  });
198
330
  };
199
- return /* @__PURE__ */ jsx(Dialog, { open: !!open, onOpenChange: (o) => {
331
+ return /* @__PURE__ */ jsx2(Dialog, { open: !!open, onOpenChange: (o) => {
200
332
  if (!o) onClose();
201
- }, title, children: /* @__PURE__ */ jsxs("form", { onSubmit: (e) => {
333
+ }, title, children: /* @__PURE__ */ jsxs2("form", { onSubmit: (e) => {
202
334
  e.preventDefault();
203
335
  if (!sent.current) send(value.trim());
204
336
  }, className: "drive-form", children: [
205
- /* @__PURE__ */ jsx(Input, { label, value, onChange: (e) => setValue(e.target.value), error: problem, required: true }),
206
- /* @__PURE__ */ jsxs("div", { className: "drive-form__actions", children: [
207
- /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", type: "button", children: "Cancel" }) }),
208
- /* @__PURE__ */ jsx(Button, { type: "submit", children: submit })
337
+ /* @__PURE__ */ jsx2(Input, { label, value, onChange: (e) => setValue(e.target.value), error: problem, required: true }),
338
+ /* @__PURE__ */ jsxs2("div", { className: "drive-form__actions", children: [
339
+ /* @__PURE__ */ jsx2(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx2(Button2, { variant: "ghost", type: "button", children: "Cancel" }) }),
340
+ /* @__PURE__ */ jsx2(Button2, { type: "submit", children: submit })
209
341
  ] })
210
342
  ] }) });
211
343
  }
@@ -216,11 +348,11 @@ function MoveDialog({ client, item, onClose, onMove }) {
216
348
  const own = all.find((x) => x.id === item.id)?.path;
217
349
  return [{ id: "/", path: "/", name: "My files" }, ...all.filter((x) => x.id !== root.id && !(own && (x.path === own || x.path.startsWith(`${own}/`))))];
218
350
  });
219
- const [to, setTo] = useState("/");
220
- const sent = useRef(false);
221
- return /* @__PURE__ */ jsx(Dialog, { open: true, onOpenChange: (o) => {
351
+ const [to, setTo] = useState2("/");
352
+ const sent = useRef2(false);
353
+ return /* @__PURE__ */ jsx2(Dialog, { open: true, onOpenChange: (o) => {
222
354
  if (!o) onClose();
223
- }, title: `Move ${item.name}`, children: /* @__PURE__ */ jsx(Status, { state: folders, retry, children: folders.status === "ok" && /* @__PURE__ */ jsxs("form", { onSubmit: (e) => {
355
+ }, title: `Move ${item.name}`, children: /* @__PURE__ */ jsx2(Status, { state: folders, retry, children: folders.status === "ok" && /* @__PURE__ */ jsxs2("form", { onSubmit: (e) => {
224
356
  e.preventDefault();
225
357
  if (sent.current) return;
226
358
  sent.current = true;
@@ -228,10 +360,10 @@ function MoveDialog({ client, item, onClose, onMove }) {
228
360
  sent.current = false;
229
361
  });
230
362
  }, className: "drive-form", children: [
231
- /* @__PURE__ */ jsx(Select, { label: "Move to", value: to, onChange: (e) => setTo(e.target.value), children: folders.data.map((x) => /* @__PURE__ */ jsx("option", { value: x.id, children: x.path === "/" ? "My files" : x.path }, x.id)) }),
232
- /* @__PURE__ */ jsxs("div", { className: "drive-form__actions", children: [
233
- /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "ghost", type: "button", children: "Cancel" }) }),
234
- /* @__PURE__ */ jsx(Button, { type: "submit", children: "Move" })
363
+ /* @__PURE__ */ jsx2(Select, { label: "Move to", value: to, onChange: (e) => setTo(e.target.value), children: folders.data.map((x) => /* @__PURE__ */ jsx2("option", { value: x.id, children: x.path === "/" ? "My files" : x.path }, x.id)) }),
364
+ /* @__PURE__ */ jsxs2("div", { className: "drive-form__actions", children: [
365
+ /* @__PURE__ */ jsx2(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx2(Button2, { variant: "ghost", type: "button", children: "Cancel" }) }),
366
+ /* @__PURE__ */ jsx2(Button2, { type: "submit", children: "Move" })
235
367
  ] })
236
368
  ] }) }) });
237
369
  }
@@ -239,7 +371,7 @@ var EXPIRY_MS = { never: void 0, "1d": 864e5, "7d": 7 * 864e5, "30d": 30 * 864e5
239
371
  function Sharing({ client, item, origin, onClose }) {
240
372
  const toast = useToast();
241
373
  const [state, reload] = useLoad(() => client.shares(item.id));
242
- const [link, setLink] = useState({ role: "viewer", expiry: "never", signedInOnly: false });
374
+ const [link, setLink] = useState2({ role: "viewer", expiry: "never", signedInOnly: false });
243
375
  const liveLinks = state.status === "ok" ? state.data.links.filter((l) => !l.revoked && (l.expiresAt === null || l.expiresAt > Date.now())) : [];
244
376
  const live = liveLinks[0];
245
377
  const current = link.id ? link : live ? { ...link, id: live.id, role: live.role === "owner" ? "editor" : live.role, signedInOnly: live.signedInOnly } : link;
@@ -270,7 +402,7 @@ function Sharing({ client, item, origin, onClose }) {
270
402
  url: current.token ? `${origin}/#/s/${item.id}/${current.token}` : "Turn the link off and on to make a new one. The address shows once.",
271
403
  expiresAt: live?.expiresAt ? new Date(live.expiresAt) : null
272
404
  };
273
- return /* @__PURE__ */ jsx(
405
+ return /* @__PURE__ */ jsx2(
274
406
  ShareDialog,
275
407
  {
276
408
  open: true,
@@ -298,9 +430,12 @@ function Sharing({ client, item, origin, onClose }) {
298
430
  }
299
431
  );
300
432
  }
433
+ var trashedAt = (i) => i.trashedAt ?? i.updatedAt;
301
434
  function TrashView({ client }) {
302
435
  const toast = useToast();
303
436
  const [state, reload] = useLoad(() => client.listTrash());
437
+ const [sort, setSort] = useState2({ key: "name", dir: "asc" });
438
+ const onSort = (key) => setSort((s) => ({ key, dir: s.key === key && s.dir === "asc" ? "desc" : "asc" }));
304
439
  const restore = async (i) => {
305
440
  try {
306
441
  await client.restore(i.id, { onConflict: "rename" });
@@ -310,12 +445,12 @@ function TrashView({ client }) {
310
445
  }
311
446
  reload();
312
447
  };
313
- return /* @__PURE__ */ jsxs("section", { "aria-label": "Trash", children: [
314
- /* @__PURE__ */ jsx("h1", { children: "Trash" }),
315
- /* @__PURE__ */ jsx(Status, { state, retry: reload, children: state.status === "ok" && (state.data.length === 0 ? /* @__PURE__ */ jsx(EmptyState, { title: "The trash is empty", description: "Items you move to the trash show up here." }) : /* @__PURE__ */ jsx(FileList, { label: "Items in the trash", children: [...state.data].sort(byName).map((i) => /* @__PURE__ */ jsx(FileRow, { name: i.name, href: "#/trash", kind: i.kind === "folder" ? "folder" : "file", modified: new Date(i.trashedAt ?? i.updatedAt), editedBy: who(i.updatedBy, i.owner), actions: /* @__PURE__ */ jsxs(Fragment, { children: [
316
- /* @__PURE__ */ jsx(MachineLabel, { item: i }),
317
- /* @__PURE__ */ jsx(Button, { size: "sm", onClick: () => void restore(i), children: `Restore ${i.name}` })
318
- ] }) }, i.id)) })) })
448
+ return /* @__PURE__ */ jsxs2("section", { "aria-label": "Trash", children: [
449
+ /* @__PURE__ */ jsx2("h1", { children: "Trash" }),
450
+ /* @__PURE__ */ jsx2(Status, { state, retry: reload, children: state.status === "ok" && (state.data.length === 0 ? /* @__PURE__ */ jsx2(EmptyState, { title: "The trash is empty", description: "Items you move to the trash show up here." }) : /* @__PURE__ */ jsx2(ItemTable, { label: "Items in the trash", items: sortItems(state.data, sort, trashedAt), dateOf: trashedAt, sort, onSort, href: () => "#/trash", who, actions: (i) => /* @__PURE__ */ jsxs2(Fragment, { children: [
451
+ /* @__PURE__ */ jsx2(MachineLabel, { item: i }),
452
+ /* @__PURE__ */ jsx2(Button2, { size: "sm", onClick: () => void restore(i), children: `Restore ${i.name}` })
453
+ ] }) })) })
319
454
  ] });
320
455
  }
321
456
  function Download({ client, id, origin, go }) {
@@ -330,11 +465,11 @@ function Download({ client, id, origin, go }) {
330
465
  go(item.parentId);
331
466
  return item;
332
467
  });
333
- return /* @__PURE__ */ jsx(Status, { state, retry, children: null });
468
+ return /* @__PURE__ */ jsx2(Status, { state, retry, children: null });
334
469
  }
335
470
 
336
471
  // ui/embed.tsx
337
- import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
472
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
338
473
  var PLACEHOLDER = "from-get-token";
339
474
  function makeClient({ baseUrl, getToken }) {
340
475
  if (typeof getToken !== "function") throw new TypeError("getToken must be a function");
@@ -354,16 +489,16 @@ function makeClient({ baseUrl, getToken }) {
354
489
  }
355
490
  var clients = 0;
356
491
  function DriveBrowser({ baseUrl, getToken }) {
357
- const [made, setMade] = useState2();
358
- useEffect2(() => {
492
+ const [made, setMade] = useState3();
493
+ useEffect3(() => {
359
494
  const next = makeClient({ baseUrl, getToken });
360
495
  setMade({ id: ++clients, client: next.client, origin: new URL(baseUrl).origin });
361
496
  return () => next.close();
362
497
  }, [baseUrl, getToken]);
363
- return made ? /* @__PURE__ */ jsx2(DriveView, { client: made.client, origin: made.origin }, made.id) : null;
498
+ return made ? /* @__PURE__ */ jsx3(DriveView, { client: made.client, origin: made.origin }, made.id) : null;
364
499
  }
365
500
  function DriveView({ client, origin }) {
366
- const [hash, setHash] = useState2("#/");
501
+ const [hash, setHash] = useState3("#/");
367
502
  const go = (loc) => setHash(`#/${loc === "/" ? "" : loc}`);
368
503
  const follow = (e) => {
369
504
  if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
@@ -373,12 +508,12 @@ function DriveView({ client, origin }) {
373
508
  setHash(link.getAttribute("href"));
374
509
  };
375
510
  const place = hash.startsWith("#/trash") ? "trash" : "files";
376
- return /* @__PURE__ */ jsxs2("div", { className: "drive-root", onClick: follow, children: [
377
- /* @__PURE__ */ jsx2(SidebarNav2, { label: "Places", items: [
378
- { href: "#/", label: "My files", icon: /* @__PURE__ */ jsx2(FolderIcon2, { "aria-hidden": "true" }), current: place === "files" },
511
+ return /* @__PURE__ */ jsxs3("div", { className: "drive-root", onClick: follow, children: [
512
+ /* @__PURE__ */ jsx3(SidebarNav2, { label: "Places", items: [
513
+ { href: "#/", label: "My files", icon: /* @__PURE__ */ jsx3(FolderIcon3, { "aria-hidden": "true" }), current: place === "files" },
379
514
  { href: "#/trash", label: "Trash", current: place === "trash" }
380
515
  ] }),
381
- /* @__PURE__ */ jsx2("div", { className: "drive-main", children: /* @__PURE__ */ jsx2(Routes, { hash, client, origin, go }) })
516
+ /* @__PURE__ */ jsx3("div", { className: "drive-main", children: /* @__PURE__ */ jsx3(Routes, { hash, client, origin, go }) })
382
517
  ] });
383
518
  }
384
519
  function mount(element, options) {
@@ -387,7 +522,7 @@ function mount(element, options) {
387
522
  let open = true;
388
523
  let version = 0;
389
524
  const render = (next, client) => root.render(
390
- /* @__PURE__ */ jsx2(TooltipProvider, { children: /* @__PURE__ */ jsx2(ToastProvider, { children: /* @__PURE__ */ jsx2(DriveView, { client, origin: new URL(next.baseUrl).origin }, version++) }) })
525
+ /* @__PURE__ */ jsx3(TooltipProvider, { children: /* @__PURE__ */ jsx3(ToastProvider, { children: /* @__PURE__ */ jsx3(DriveView, { client, origin: new URL(next.baseUrl).origin }, version++) }) })
391
526
  );
392
527
  render(options, current.client);
393
528
  return {
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  } from "./chunk-AB2RTTHJ.js";
4
4
  import {
5
5
  VERSION
6
- } from "./chunk-6UQ6CDLJ.js";
6
+ } from "./chunk-IMWV5QEH.js";
7
7
  import {
8
8
  ACTOR_KINDS,
9
9
  AuthError,
package/dist/mcp.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  VERSION
3
- } from "./chunk-6UQ6CDLJ.js";
3
+ } from "./chunk-IMWV5QEH.js";
4
4
  import {
5
5
  ACTOR_KINDS,
6
6
  DriveError,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentic-drive",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Store, sort, and share files and folders.",
5
5
  "keywords": [
6
6
  "drive",