agentic-drive 0.11.0 → 0.12.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/CHANGELOG.md +13 -0
- package/README.md +58 -2
- package/dist/account.d.ts +1 -1
- package/dist/{chunk-NMHARN2A.js → chunk-J2EPNUYS.js} +1 -1
- package/dist/cli.js +1 -1
- package/dist/client.d.ts +2 -2
- package/dist/{drive-CS1CLfqo.d.ts → drive-9cZ_9xU4.d.ts} +1 -1
- package/dist/embed.d.ts +31 -5
- package/dist/embed.js +92 -32
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/mcp.d.ts +1 -1
- package/dist/mcp.js +1 -1
- package/dist/worker.d.ts +2 -2
- package/llms.txt +22 -2
- package/package.json +4 -4
- package/ui-dist/assets/index-C2fZap97.js +49 -0
- package/ui-dist/index.html +1 -1
- package/ui-dist/assets/index-DqkvcDrx.js +0 -49
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.0
|
|
4
|
+
|
|
5
|
+
- Add typed embedded navigation with `DriveLocation`, `location`, and `onNavigate`.
|
|
6
|
+
Mounted views expose `setLocation` and `getLocation` for host routing.
|
|
7
|
+
File links download in place. Explicit download locations return with a
|
|
8
|
+
history replace hint, and navigation cancels pending downloads.
|
|
9
|
+
- Keep the current location on option updates. Route embedded activity actions
|
|
10
|
+
through the host navigation API.
|
|
11
|
+
|
|
12
|
+
## 0.11.1
|
|
13
|
+
|
|
14
|
+
- Allow `agentic-design` and `agentic-identity` releases below 1.0.0 while keeping the existing lower bounds. Test with identity 0.12.0 and design 0.10.1.
|
|
15
|
+
|
|
3
16
|
## 0.11.0
|
|
4
17
|
|
|
5
18
|
- Add `onOpenItem` to the public UI options. Hosts can handle reference opens in list and grid views by pointer or keyboard. Export `OnOpenItem` and `Item` from `agentic-drive/ui`. Keep the download action for unhandled items.
|
package/README.md
CHANGED
|
@@ -248,7 +248,7 @@ view.unmount();
|
|
|
248
248
|
```
|
|
249
249
|
|
|
250
250
|
`mount` returns a `MountedDrive` handle. `DriveBrowser` is the React component;
|
|
251
|
-
`DriveUiOptions` is its configuration. `OnOpenItem` is the reference-open callback type, and `Item` is its argument type. The CSS path supplies its styles.
|
|
251
|
+
`DriveUiOptions` is its configuration. `DriveLocation` is the folder, trash, activity, or file location type. `DriveNavigationOptions` describes host history updates. `OnOpenItem` is the reference-open callback type, and `Item` is its argument type. The CSS path supplies its styles.
|
|
252
252
|
|
|
253
253
|
### CLI, MCP, and adapters
|
|
254
254
|
|
|
@@ -323,7 +323,63 @@ drive.update({ baseUrl: location.origin, getToken: () => session.token, onOpenIt
|
|
|
323
323
|
|
|
324
324
|
`DriveUiOptions` exports this optional hook for both `mount` and `DriveBrowser`. `update()` can replace or remove it. React hosts can change the `onOpenItem` prop without resetting the current folder. The callback is synchronous.
|
|
325
325
|
|
|
326
|
-
|
|
326
|
+
### Embed navigation
|
|
327
|
+
|
|
328
|
+
Use the exported `DriveLocation` type to select a folder, trash, item activity,
|
|
329
|
+
or file download. A folder location has `{ kind: "folder", path: "/" }`.
|
|
330
|
+
Its `path` accepts a folder id, an absolute path such as `"/Work/Plans"`,
|
|
331
|
+
or a path from a folder id. Other locations use `{ kind: "trash" }`,
|
|
332
|
+
`{ kind: "activity", itemId }`, or `{ kind: "file", itemId }`.
|
|
333
|
+
The server checks access for each request. An inaccessible folder shows an error.
|
|
334
|
+
|
|
335
|
+
`mount` starts at `options.location`, or root when it is absent.
|
|
336
|
+
`setLocation` changes the view without remounting the drive.
|
|
337
|
+
`getLocation` returns a copy of the active location. For a folder, pass its
|
|
338
|
+
`path` as `folder` to the client's `registerDocument` method.
|
|
339
|
+
Trash, activity, and downloads have no active folder.
|
|
340
|
+
|
|
341
|
+
```ts
|
|
342
|
+
import { mount, type DriveLocation } from "agentic-drive/ui";
|
|
343
|
+
|
|
344
|
+
const readLocation = (): DriveLocation =>
|
|
345
|
+
JSON.parse(new URL(location.href).searchParams.get("drive") ??
|
|
346
|
+
'{"kind":"folder","path":"/"}'); // Validate URL data in your app.
|
|
347
|
+
const drive = mount(document.getElementById("drive")!, {
|
|
348
|
+
baseUrl: location.origin,
|
|
349
|
+
getToken: () => session.token,
|
|
350
|
+
location: readLocation(),
|
|
351
|
+
onNavigate(next, { replace }) {
|
|
352
|
+
const url = new URL(location.href);
|
|
353
|
+
url.searchParams.set("drive", JSON.stringify(next));
|
|
354
|
+
if (replace) history.replaceState(null, "", url);
|
|
355
|
+
else history.pushState(null, "", url);
|
|
356
|
+
},
|
|
357
|
+
});
|
|
358
|
+
addEventListener("popstate", () => drive.setLocation(readLocation()));
|
|
359
|
+
const active = drive.getLocation();
|
|
360
|
+
if (active.kind === "folder") {
|
|
361
|
+
await client.registerDocument({ name: "Plan", ref: "docs:plan", folder: active.path });
|
|
362
|
+
}
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
`onNavigate` reports folder, breadcrumb, trash, and activity navigation with
|
|
366
|
+
`replace: false`. File links download in place without changing the location or
|
|
367
|
+
notifying the host. Errors leave the folder open. Navigation cancels pending
|
|
368
|
+
downloads, so a late response cannot move the view or start a download.
|
|
369
|
+
An explicit `file` location returns to its parent with `replace: true`. Use
|
|
370
|
+
`replaceState` for that return. Root returns use `"/"`.
|
|
371
|
+
Assigning a location through initial render, `setLocation`, or `update` does not
|
|
372
|
+
call the callback itself. An explicit file location starts a download; its completed return
|
|
373
|
+
does call the callback. An explicit file location that fails shows an error and
|
|
374
|
+
stays selected; the host can set a folder location to leave it.
|
|
375
|
+
`update` keeps the current location unless it receives a new `location`.
|
|
376
|
+
In React, `DriveBrowser location={current} onNavigate={setCurrent}` is controlled:
|
|
377
|
+
the host must apply the next location. Without `location`, React keeps it locally.
|
|
378
|
+
Changes to server or token options keep the location and reload its data.
|
|
379
|
+
The embed never changes the host URL itself.
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
`getToken` returns 1 to 512 letters, digits, `-` or `_`, or `undefined` to send no `authorization` header. A bad `baseUrl` or a `getToken` that is not a function makes `mount` throw a `TypeError`. The server still checks every request against the token. The embed shows files and trash with upload, rename, move, share, and the "Created by agent" label. It has no sign-in form, because your app owns sign-in. It does not change the page address, and it has no theme switch. `agentic-design/styles.css` sets base styles for the whole page, and it follows the dark mode of the system unless your page sets `data-theme`. Add `agentic-design@">=0.8.0 <1.0.0"` to your own dependencies, because your page imports its files. The Worker sends no CORS headers, so the server must be on the origin of your page. Set `baseUrl` to the same address as `DRIVE_PUBLIC_URL`, or the UI refuses its download links. `unmount` and `update` abort the requests that run and stop an upload that has more files to send. The UI keeps the list or grid choice in `localStorage` under `agentic-drive-view`, and it has its own heading for each view. Dialogs, menus, and messages render in `document.body`, outside your element. A click with Ctrl, Cmd, Shift, or the middle button on a folder link does not open the folder. The class names start with `drive-`. The design-system classes start with `agd-`. `DriveBrowser` is the same UI as a React component. It closes its requests when it unmounts. Wrap it in `TooltipProvider` and `ToastProvider` from `agentic-design/react`. `react` and `react-dom` (version 19 or later) are optional peer dependencies. Install them in your project when you use `agentic-drive/ui`. The web UI that the Worker serves uses the same views.
|
|
327
383
|
|
|
328
384
|
### Serve the UI from the package
|
|
329
385
|
|
package/dist/account.d.ts
CHANGED
package/dist/cli.js
CHANGED
package/dist/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as ErrorCode, N as Node, d as ActivityEntry, S as SearchQuery } from './drive-
|
|
2
|
-
export { c as ActivityAction } from './drive-
|
|
1
|
+
import { i as ErrorCode, N as Node, d as ActivityEntry, S as SearchQuery } from './drive-9cZ_9xU4.js';
|
|
2
|
+
export { c as ActivityAction } from './drive-9cZ_9xU4.js';
|
|
3
3
|
import 'agentic-identity';
|
|
4
4
|
|
|
5
5
|
type Role = "viewer" | "commenter" | "editor" | "owner";
|
|
@@ -248,7 +248,7 @@ declare function createDrive(deps: DriveDeps): {
|
|
|
248
248
|
exportPerson(ctx: Ctx): Promise<{
|
|
249
249
|
subject: {
|
|
250
250
|
id: string;
|
|
251
|
-
kind: "human" | "
|
|
251
|
+
kind: "human" | "agent" | "agent_of_human" | "agent_of_agent" | "user_token" | null;
|
|
252
252
|
};
|
|
253
253
|
partial: boolean;
|
|
254
254
|
authored: {
|
package/dist/embed.d.ts
CHANGED
|
@@ -1,12 +1,34 @@
|
|
|
1
1
|
import * as react from 'react';
|
|
2
2
|
import { Item } from './client.js';
|
|
3
|
-
import './drive-
|
|
3
|
+
import './drive-9cZ_9xU4.js';
|
|
4
4
|
import 'agentic-identity';
|
|
5
5
|
|
|
6
6
|
/** Return true to handle a reference open; false or void keeps the download action. */
|
|
7
7
|
type OnOpenItem = (item: Item) => boolean | void;
|
|
8
8
|
|
|
9
|
+
/** Folder locators follow the SDK rules: "/" is root; use an id or an absolute path. */
|
|
10
|
+
type DriveLocation = {
|
|
11
|
+
kind: "folder";
|
|
12
|
+
path: string;
|
|
13
|
+
} | {
|
|
14
|
+
kind: "trash";
|
|
15
|
+
} | {
|
|
16
|
+
kind: "activity";
|
|
17
|
+
itemId: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: "file";
|
|
20
|
+
itemId: string;
|
|
21
|
+
};
|
|
22
|
+
/** Replace the current host history entry for a transient navigation. */
|
|
23
|
+
interface DriveNavigationOptions {
|
|
24
|
+
replace: boolean;
|
|
25
|
+
}
|
|
9
26
|
interface DriveUiOptions {
|
|
27
|
+
/** Controlled in React. In mount/update, selects the location; omission preserves it. */
|
|
28
|
+
location?: DriveLocation;
|
|
29
|
+
/** Internal navigation. Controlled React hosts must apply the next location.
|
|
30
|
+
* Use replaceState when options.replace is true (an explicit download returns). */
|
|
31
|
+
onNavigate?: (location: DriveLocation, options: DriveNavigationOptions) => void;
|
|
10
32
|
/** Runs when a person activates a reference item. Return true to skip the built-in download. */
|
|
11
33
|
onOpenItem?: OnOpenItem;
|
|
12
34
|
/** The address of the drive server, for example `https://drive.example.com`. Only its origin is used. */
|
|
@@ -15,15 +37,19 @@ interface DriveUiOptions {
|
|
|
15
37
|
getToken: () => string | undefined | Promise<string | undefined>;
|
|
16
38
|
}
|
|
17
39
|
/** The folders and trash of the signed-in caller, with upload, share, and the machine-made label. It needs the tooltip and toast providers above it. `mount` adds them.
|
|
18
|
-
* It
|
|
19
|
-
declare function DriveBrowser({ baseUrl, getToken, onOpenItem }: DriveUiOptions): react.JSX.Element | null;
|
|
40
|
+
* It keeps its location when `baseUrl` changes. A bad `baseUrl` or `getToken` throws. Pass a stable `getToken`, because a new function makes a new client. */
|
|
41
|
+
declare function DriveBrowser({ baseUrl, getToken, onOpenItem, location, onNavigate }: DriveUiOptions): react.JSX.Element | null;
|
|
20
42
|
interface MountedDrive {
|
|
21
|
-
/** Show the drive with another address, token callback, or item-open callback. The
|
|
43
|
+
/** Show the drive with another address, token callback, or item-open callback. The current location stays unless options.location is supplied. */
|
|
22
44
|
update(options: DriveUiOptions): void;
|
|
45
|
+
/** Select a location without remounting the drive. Does not call onNavigate. */
|
|
46
|
+
setLocation(location: DriveLocation): void;
|
|
47
|
+
/** Current location. A folder path is suitable for SDK registerDocument({ folder }). */
|
|
48
|
+
getLocation(): DriveLocation;
|
|
23
49
|
/** Remove the UI. A request that runs is aborted, and no later request starts or calls `getToken`. Calling it again does nothing. */
|
|
24
50
|
unmount(): void;
|
|
25
51
|
}
|
|
26
52
|
/** Render the drive into an element, for an app that does not use React itself. A bad `baseUrl` or `getToken` throws a `TypeError`. */
|
|
27
53
|
declare function mount(element: HTMLElement, options: DriveUiOptions): MountedDrive;
|
|
28
54
|
|
|
29
|
-
export { DriveBrowser, type DriveUiOptions, Item, type MountedDrive, type OnOpenItem, mount };
|
|
55
|
+
export { DriveBrowser, type DriveLocation, type DriveNavigationOptions, type DriveUiOptions, Item, type MountedDrive, type OnOpenItem, mount };
|
package/dist/embed.js
CHANGED
|
@@ -4,9 +4,9 @@ import {
|
|
|
4
4
|
} from "./chunk-SEHT5Q5L.js";
|
|
5
5
|
|
|
6
6
|
// ui/embed.tsx
|
|
7
|
-
import { useEffect as useEffect3, useState as useState3 } from "react";
|
|
7
|
+
import { useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
8
8
|
import { createRoot } from "react-dom/client";
|
|
9
|
-
import { FolderIcon as FolderIcon3, SidebarNav as SidebarNav2, ToastProvider, TooltipProvider } from "agentic-design/react";
|
|
9
|
+
import { FolderIcon as FolderIcon3, SidebarNav as SidebarNav2, ToastProvider, TooltipProvider, useToast as useToast2 } from "agentic-design/react";
|
|
10
10
|
|
|
11
11
|
// ui/App.tsx
|
|
12
12
|
import { useEffect as useEffect2, useMemo, useRef as useRef2, useState as useState2 } from "react";
|
|
@@ -183,11 +183,9 @@ function useLoad(fn) {
|
|
|
183
183
|
const [state, set] = useState2({ status: "loading" });
|
|
184
184
|
const [tick, setTick] = useState2(0);
|
|
185
185
|
useEffect2(() => {
|
|
186
|
-
|
|
187
|
-
fn().then((data) =>
|
|
188
|
-
return () =>
|
|
189
|
-
live = false;
|
|
190
|
-
};
|
|
186
|
+
const pending = new AbortController();
|
|
187
|
+
fn(pending.signal).then((data) => !pending.signal.aborted && set({ status: "ok", data }), (e) => !pending.signal.aborted && set({ status: "error", error: errorText(e) }));
|
|
188
|
+
return () => pending.abort();
|
|
191
189
|
}, [tick]);
|
|
192
190
|
return [state, () => setTick((n) => n + 1)];
|
|
193
191
|
}
|
|
@@ -200,18 +198,14 @@ function Status({ state, retry, children }) {
|
|
|
200
198
|
return /* @__PURE__ */ jsx3(Fragment, { children });
|
|
201
199
|
}
|
|
202
200
|
var routeParts = (hash) => hash.replace(/^#\/?/, "").split("/");
|
|
203
|
-
|
|
204
|
-
history.replaceState(null, "", `#/${loc}`);
|
|
205
|
-
dispatchEvent(new HashChangeEvent("hashchange"));
|
|
206
|
-
};
|
|
207
|
-
function Routes({ hash, client, origin, go, onOpenItem }) {
|
|
201
|
+
function Routes({ hash, client, origin, go, onOpenItem, folderLoc }) {
|
|
208
202
|
const parts = routeParts(hash);
|
|
209
203
|
if (parts[0] === "trash") return /* @__PURE__ */ jsx3(TrashView, { client }, "trash");
|
|
210
204
|
if (parts[0] === "a" && parts[1]) return /* @__PURE__ */ jsx3(ActivityView, { client, id: parts[1] }, hash);
|
|
211
205
|
if (parts[0] === "f" && parts[1]) return /* @__PURE__ */ jsx3(Download, { client, id: parts[1], origin, go }, hash);
|
|
212
|
-
return /* @__PURE__ */ jsx3(Browser, { client, loc: parts[0] || "/", origin, onOpenItem }, hash);
|
|
206
|
+
return /* @__PURE__ */ jsx3(Browser, { client, loc: folderLoc ?? (parts[0] || "/"), go, origin, onOpenItem }, hash);
|
|
213
207
|
}
|
|
214
|
-
function Browser({ client, loc, origin, onOpenItem }) {
|
|
208
|
+
function Browser({ client, loc, origin, onOpenItem, go }) {
|
|
215
209
|
const openItem = (e, item) => {
|
|
216
210
|
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || item.kind !== "ref") return;
|
|
217
211
|
if (onOpenItem?.(item) === true) e.preventDefault();
|
|
@@ -299,7 +293,7 @@ function Browser({ client, loc, origin, onOpenItem }) {
|
|
|
299
293
|
/* @__PURE__ */ jsx3(Menu, { align: "end", trigger: /* @__PURE__ */ jsx3(IconButton, { label: `Actions for ${item.name}`, icon: /* @__PURE__ */ jsx3(MoreIcon, {}), variant: "ghost" }), items: [
|
|
300
294
|
{ label: "Rename", onSelect: () => setDlg({ kind: "rename", item }) },
|
|
301
295
|
{ label: "Move", onSelect: () => setDlg({ kind: "move", item }) },
|
|
302
|
-
{ label: "Activity", onSelect: () =>
|
|
296
|
+
{ label: "Activity", onSelect: () => go(`a/${item.id}`) },
|
|
303
297
|
{ label: "Share", onSelect: () => setDlg({ kind: "share", item }) },
|
|
304
298
|
"separator",
|
|
305
299
|
{ label: "Move to trash", danger: true, onSelect: () => void act(() => client.trash(item.id), `Moved ${item.name} to trash`) }
|
|
@@ -508,16 +502,23 @@ function ActivityView({ client, id }) {
|
|
|
508
502
|
/* @__PURE__ */ jsx3(ActivityList, { entries: state.data.entries })
|
|
509
503
|
] }) }) });
|
|
510
504
|
}
|
|
505
|
+
async function downloadItem(client, id, origin, signal) {
|
|
506
|
+
const item = await client.stat(id, { signal });
|
|
507
|
+
signal.throwIfAborted();
|
|
508
|
+
const { url } = await client.url(id, void 0, { signal });
|
|
509
|
+
signal.throwIfAborted();
|
|
510
|
+
if (new URL(url, origin).origin !== origin) throw new Error("The download link points to another site.");
|
|
511
|
+
const a = document.createElement("a");
|
|
512
|
+
a.href = url;
|
|
513
|
+
a.download = item.name;
|
|
514
|
+
a.click();
|
|
515
|
+
return item;
|
|
516
|
+
}
|
|
511
517
|
function Download({ client, id, origin, go }) {
|
|
512
|
-
const [state, retry] = useLoad(async () => {
|
|
513
|
-
const item = await client
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
const a = document.createElement("a");
|
|
517
|
-
a.href = url;
|
|
518
|
-
a.download = item.name;
|
|
519
|
-
a.click();
|
|
520
|
-
go(item.parentId);
|
|
518
|
+
const [state, retry] = useLoad(async (signal) => {
|
|
519
|
+
const item = await downloadItem(client, id, origin, signal);
|
|
520
|
+
signal.throwIfAborted();
|
|
521
|
+
go(item.parentId, { replace: true });
|
|
521
522
|
return item;
|
|
522
523
|
});
|
|
523
524
|
return /* @__PURE__ */ jsx3(Status, { state, retry, children: null });
|
|
@@ -525,6 +526,22 @@ function Download({ client, id, origin, go }) {
|
|
|
525
526
|
|
|
526
527
|
// ui/embed.tsx
|
|
527
528
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
529
|
+
var ROOT = { kind: "folder", path: "/" };
|
|
530
|
+
function locationHash(location2) {
|
|
531
|
+
if (location2.kind === "folder" && typeof location2.path === "string" && location2.path.length)
|
|
532
|
+
return `#/folder/${encodeURIComponent(location2.path)}`;
|
|
533
|
+
if (location2.kind === "trash") return "#/trash";
|
|
534
|
+
if ((location2.kind === "activity" || location2.kind === "file") && typeof location2.itemId === "string" && location2.itemId.length)
|
|
535
|
+
return `#/${location2.kind === "activity" ? "a" : "f"}/${encodeURIComponent(location2.itemId)}`;
|
|
536
|
+
throw new TypeError("Invalid drive location");
|
|
537
|
+
}
|
|
538
|
+
function fromHash(hash) {
|
|
539
|
+
const path = hash.slice(2);
|
|
540
|
+
if (path === "trash") return { kind: "trash" };
|
|
541
|
+
if (path.startsWith("a/")) return { kind: "activity", itemId: path.slice(2) };
|
|
542
|
+
if (path.startsWith("f/")) return { kind: "file", itemId: path.slice(2) };
|
|
543
|
+
return { kind: "folder", path: path || "/" };
|
|
544
|
+
}
|
|
528
545
|
var PLACEHOLDER = "from-get-token";
|
|
529
546
|
function makeClient({ baseUrl, getToken }) {
|
|
530
547
|
if (typeof getToken !== "function") throw new TypeError("getToken must be a function");
|
|
@@ -543,24 +560,48 @@ function makeClient({ baseUrl, getToken }) {
|
|
|
543
560
|
return { client: createClient({ baseUrl, token: PLACEHOLDER, fetch: fetchWithToken }), close: () => closed.abort() };
|
|
544
561
|
}
|
|
545
562
|
var clients = 0;
|
|
546
|
-
function DriveBrowser({ baseUrl, getToken, onOpenItem }) {
|
|
563
|
+
function DriveBrowser({ baseUrl, getToken, onOpenItem, location: location2, onNavigate }) {
|
|
564
|
+
const [local, setLocal] = useState3(ROOT);
|
|
565
|
+
const active = location2 ?? local;
|
|
566
|
+
locationHash(active);
|
|
567
|
+
const navigate = (next, options = { replace: false }) => {
|
|
568
|
+
if (location2 === void 0) setLocal(next);
|
|
569
|
+
onNavigate?.(next, options);
|
|
570
|
+
};
|
|
547
571
|
const [made, setMade] = useState3();
|
|
548
572
|
useEffect3(() => {
|
|
549
573
|
const next = makeClient({ baseUrl, getToken });
|
|
550
574
|
setMade({ id: ++clients, client: next.client, origin: new URL(baseUrl).origin });
|
|
551
575
|
return () => next.close();
|
|
552
576
|
}, [baseUrl, getToken]);
|
|
553
|
-
return made ? /* @__PURE__ */ jsx4(DriveView, { client: made.client, origin: made.origin, onOpenItem }, made.id) : null;
|
|
577
|
+
return made ? /* @__PURE__ */ jsx4(DriveView, { client: made.client, origin: made.origin, onOpenItem, location: active, onNavigate: navigate }, made.id) : null;
|
|
554
578
|
}
|
|
555
|
-
function DriveView({ client, origin, onOpenItem }) {
|
|
556
|
-
const
|
|
557
|
-
const
|
|
579
|
+
function DriveView({ client, origin, onOpenItem, location: location2, onNavigate }) {
|
|
580
|
+
const hash = locationHash(location2);
|
|
581
|
+
const toast = useToast2();
|
|
582
|
+
const pending = useRef3(new AbortController());
|
|
583
|
+
useEffect3(() => {
|
|
584
|
+
const controller = new AbortController();
|
|
585
|
+
pending.current = controller;
|
|
586
|
+
return () => controller.abort();
|
|
587
|
+
}, [hash]);
|
|
588
|
+
const download = async (id) => {
|
|
589
|
+
const signal = pending.current.signal;
|
|
590
|
+
try {
|
|
591
|
+
await downloadItem(client, id, origin, signal);
|
|
592
|
+
} catch (e) {
|
|
593
|
+
if (!signal.aborted) toast({ title: "Could not download", description: e instanceof DriveApiError ? `${e.message} (${e.code})` : e instanceof Error ? e.message : "Download failed", tone: "danger" });
|
|
594
|
+
}
|
|
595
|
+
};
|
|
596
|
+
const go = (loc, options = { replace: false }) => onNavigate(fromHash(`#/${loc === "/" || loc === "root" ? "" : loc}`), options);
|
|
558
597
|
const follow = (e) => {
|
|
559
598
|
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
|
|
560
599
|
const link = e.target.closest?.("a[href^='#/']");
|
|
561
600
|
if (!link || !e.currentTarget.contains(link)) return;
|
|
562
601
|
e.preventDefault();
|
|
563
|
-
|
|
602
|
+
const next = fromHash(link.getAttribute("href"));
|
|
603
|
+
if (next.kind === "file") void download(next.itemId);
|
|
604
|
+
else onNavigate(next, { replace: false });
|
|
564
605
|
};
|
|
565
606
|
const place = hash.startsWith("#/trash") ? "trash" : "files";
|
|
566
607
|
return /* @__PURE__ */ jsxs4("div", { className: "drive-root", onClick: follow, children: [
|
|
@@ -568,26 +609,45 @@ function DriveView({ client, origin, onOpenItem }) {
|
|
|
568
609
|
{ href: "#/", label: "My files", icon: /* @__PURE__ */ jsx4(FolderIcon3, { "aria-hidden": "true" }), current: place === "files" },
|
|
569
610
|
{ href: "#/trash", label: "Trash", current: place === "trash" }
|
|
570
611
|
] }),
|
|
571
|
-
/* @__PURE__ */ jsx4("div", { className: "drive-main", children: /* @__PURE__ */ jsx4(Routes, { hash, client, origin, go, onOpenItem }) })
|
|
612
|
+
/* @__PURE__ */ jsx4("div", { className: "drive-main", children: /* @__PURE__ */ jsx4(Routes, { hash, client, origin, go, onOpenItem, folderLoc: location2.kind === "folder" ? location2.path : void 0 }) })
|
|
572
613
|
] });
|
|
573
614
|
}
|
|
574
615
|
function mount(element, options) {
|
|
616
|
+
let active = { ...options.location ?? ROOT };
|
|
617
|
+
locationHash(active);
|
|
618
|
+
let settings = options;
|
|
575
619
|
let current = makeClient(options);
|
|
576
620
|
const root = createRoot(element);
|
|
577
621
|
let open = true;
|
|
578
622
|
let version = 0;
|
|
623
|
+
const navigate = (next, options2 = { replace: false }) => {
|
|
624
|
+
active = { ...next };
|
|
625
|
+
render(settings, current.client);
|
|
626
|
+
settings.onNavigate?.({ ...active }, options2);
|
|
627
|
+
};
|
|
579
628
|
const render = (next, client) => root.render(
|
|
580
|
-
/* @__PURE__ */ jsx4(TooltipProvider, { children: /* @__PURE__ */ jsx4(ToastProvider, { children: /* @__PURE__ */ jsx4(DriveView, { client, origin: new URL(next.baseUrl).origin, onOpenItem: next.onOpenItem }, version
|
|
629
|
+
/* @__PURE__ */ jsx4(TooltipProvider, { children: /* @__PURE__ */ jsx4(ToastProvider, { children: /* @__PURE__ */ jsx4(DriveView, { client, origin: new URL(next.baseUrl).origin, onOpenItem: next.onOpenItem, location: active, onNavigate: navigate }, version) }) })
|
|
581
630
|
);
|
|
582
631
|
render(options, current.client);
|
|
583
632
|
return {
|
|
584
633
|
update: (next) => {
|
|
585
634
|
if (!open) return;
|
|
635
|
+
if (next.location !== void 0) locationHash(next.location);
|
|
586
636
|
const made = makeClient(next);
|
|
637
|
+
settings = next;
|
|
638
|
+
if (next.location !== void 0) active = { ...next.location };
|
|
639
|
+
version++;
|
|
587
640
|
current.close();
|
|
588
641
|
current = made;
|
|
589
642
|
render(next, made.client);
|
|
590
643
|
},
|
|
644
|
+
setLocation: (next) => {
|
|
645
|
+
if (!open) return;
|
|
646
|
+
locationHash(next);
|
|
647
|
+
active = { ...next };
|
|
648
|
+
render(settings, current.client);
|
|
649
|
+
},
|
|
650
|
+
getLocation: () => ({ ...active }),
|
|
591
651
|
unmount: () => {
|
|
592
652
|
if (!open) return;
|
|
593
653
|
open = false;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { O as OnConflict, D as Drive, C as Ctx, N as Node, A as Actor } from './drive-
|
|
2
|
-
export { a as ACTOR_KINDS, b as Action, c as ActivityAction, d as ActivityEntry, e as ActorKind, f as Can, g as DriveDeps, h as DriveError, E as ERROR_STATUS, i as ErrorCode, R as ROOT_ID, S as SearchQuery, j as createDrive, p as parseName, k as parseSubject, s as subjectKindOf } from './drive-
|
|
1
|
+
import { O as OnConflict, D as Drive, C as Ctx, N as Node, A as Actor } from './drive-9cZ_9xU4.js';
|
|
2
|
+
export { a as ACTOR_KINDS, b as Action, c as ActivityAction, d as ActivityEntry, e as ActorKind, f as Can, g as DriveDeps, h as DriveError, E as ERROR_STATUS, i as ErrorCode, R as ROOT_ID, S as SearchQuery, j as createDrive, p as parseName, k as parseSubject, s as subjectKindOf } from './drive-9cZ_9xU4.js';
|
|
3
3
|
import { Store, Blobs, Doc } from 'agentic-identity';
|
|
4
4
|
export { B as BackfillOptions, a as BackfillResult, b as backfillAuthorIds, c as backfillResources } from './migrate-B79gRPMu.js';
|
|
5
5
|
export { DriveAccount, DriveAccountDeps, driveAccount } from './account.js';
|
package/dist/index.js
CHANGED
package/dist/mcp.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import { D as Drive, C as Ctx } from './drive-
|
|
2
|
+
import { D as Drive, C as Ctx } from './drive-9cZ_9xU4.js';
|
|
3
3
|
import { Store } from 'agentic-identity';
|
|
4
4
|
|
|
5
5
|
declare function createMcpServer(drive: Drive, ctx: Ctx, exportDir: string, store: Store): McpServer;
|
package/dist/mcp.js
CHANGED
package/dist/worker.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Hono } from 'hono';
|
|
2
2
|
import { D1Like, R2Like } from 'agentic-identity/cloudflare';
|
|
3
3
|
import { Store, Blobs } from 'agentic-identity';
|
|
4
|
-
import { C as Ctx, D as Drive } from './drive-
|
|
5
|
-
export { E as ERROR_STATUS, i as ErrorCode, N as Node } from './drive-
|
|
4
|
+
import { C as Ctx, D as Drive } from './drive-9cZ_9xU4.js';
|
|
5
|
+
export { E as ERROR_STATUS, i as ErrorCode, N as Node } from './drive-9cZ_9xU4.js';
|
|
6
6
|
export { B as BackfillOptions, a as BackfillResult, b as backfillAuthorIds, c as backfillResources } from './migrate-B79gRPMu.js';
|
|
7
7
|
|
|
8
8
|
/**
|
package/llms.txt
CHANGED
|
@@ -105,8 +105,8 @@ view.unmount();
|
|
|
105
105
|
```
|
|
106
106
|
|
|
107
107
|
`mount` returns a `MountedDrive` handle. `DriveBrowser` is the React component;
|
|
108
|
-
`DriveUiOptions` is its configuration. The CSS path supplies its styles. The handle
|
|
109
|
-
has `update` and `
|
|
108
|
+
`DriveUiOptions` is its configuration. `DriveLocation` is the folder, trash, activity, or file location type. `DriveNavigationOptions` describes host history updates. The CSS path supplies its styles. The handle
|
|
109
|
+
has `update`, `unmount`, `setLocation`, and `getLocation` methods. The `getToken` callback runs before each request
|
|
110
110
|
and may return undefined. The UI does not change the page address or theme.
|
|
111
111
|
The optional `onOpenItem` hook receives the full `Item` for a reference link, by pointer or Enter in list and grid views. Return true to skip the download; false or void keeps it. `OnOpenItem` and `Item` are exported types from `agentic-drive/ui`. Both `update` and React props can replace the hook.
|
|
112
112
|
React 19 and React DOM 19 are optional peer dependencies.
|
|
@@ -122,3 +122,23 @@ The CLI uses `AGENTIC_DRIVE_TOKEN`. The MCP command serves tools over stdio;
|
|
|
122
122
|
call `drive_ls` with a path. Storage adapters are provided by `agentic-identity`,
|
|
123
123
|
not by a drive subpath. For example: `import { createFileStore, createFileBlobs } from "agentic-identity/file"`;
|
|
124
124
|
pass `createFileStore({ path: "./data/db" })` and `createFileBlobs({ dir: "./data/blobs" })` to `createDrive`. The Worker uses D1 and R2 adapters by default.
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
### Embedded navigation
|
|
128
|
+
|
|
129
|
+
Set the location option to a folder with kind "folder" and path "/" (root),
|
|
130
|
+
a folder id, or an absolute path. Use kind "trash" for trash. Activity and file
|
|
131
|
+
locations use kind "activity" or "file" with itemId.
|
|
132
|
+
|
|
133
|
+
The mounted handle also has setLocation and getLocation methods. getLocation
|
|
134
|
+
returns a copy of the active location. A folder path can be passed as folder to
|
|
135
|
+
registerDocument. update keeps the location unless its options supply one.
|
|
136
|
+
React hosts can control location and apply onNavigate to their own state.
|
|
137
|
+
|
|
138
|
+
onNavigate receives the next location and history options. Use pushState when
|
|
139
|
+
replace is false, and replaceState when replace is true. File links download in place
|
|
140
|
+
without changing location or notifying the host, including on error. Navigation
|
|
141
|
+
cancels pending downloads. Explicit file locations return to the parent with
|
|
142
|
+
replace true on success; an error leaves that explicit location selected.
|
|
143
|
+
Root returns use "/". A direct location assignment does not notify the host,
|
|
144
|
+
but a completed download does. Apply popstate with setLocation or React props.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agentic-drive",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Store, sort, and share files and folders.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"drive",
|
|
@@ -78,8 +78,8 @@
|
|
|
78
78
|
},
|
|
79
79
|
"dependencies": {
|
|
80
80
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
81
|
-
"agentic-design": ">=0.8.0 <0.
|
|
82
|
-
"agentic-identity": ">=0.9.0 <0.
|
|
81
|
+
"agentic-design": ">=0.8.0 <1.0.0",
|
|
82
|
+
"agentic-identity": ">=0.9.0 <1.0.0",
|
|
83
83
|
"hono": "^4.13.14",
|
|
84
84
|
"zod": "^4.6.6"
|
|
85
85
|
},
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
"@types/react-dom": "^19.3.0",
|
|
93
93
|
"@vitejs/plugin-react": "^5.2.0",
|
|
94
94
|
"agentic-design-tested": "npm:agentic-design@0.10.1",
|
|
95
|
-
"agentic-identity-tested": "npm:agentic-identity@0.
|
|
95
|
+
"agentic-identity-tested": "npm:agentic-identity@0.12.0",
|
|
96
96
|
"esbuild": "^0.27.7",
|
|
97
97
|
"jsdom": "^26.1.0",
|
|
98
98
|
"miniflare": "^4.20260730.0",
|