@jk3labs/paperclip-plugin-file-browser 0.1.4
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/README.md +30 -0
- package/dist/manifest.js +81 -0
- package/dist/manifest.js.map +7 -0
- package/dist/ui/index.js +139 -0
- package/dist/ui/index.js.map +7 -0
- package/dist/worker.js +9268 -0
- package/dist/worker.js.map +7 -0
- package/package.json +47 -0
package/README.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# JKL File Browser
|
|
2
|
+
|
|
3
|
+
A file browser plugin for Paperclip workspaces
|
|
4
|
+
|
|
5
|
+
## Development
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm install
|
|
9
|
+
pnpm dev # watch builds
|
|
10
|
+
pnpm dev:ui # local dev server with hot-reload events
|
|
11
|
+
pnpm test
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
`pnpm dev` rebuilds the worker, manifest, and UI bundles into `dist/`.
|
|
15
|
+
When this package is installed from a local path, Paperclip watches that rebuilt
|
|
16
|
+
output and reloads the plugin worker. Local installs run trusted code from this
|
|
17
|
+
folder on your machine.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
## Install Into Paperclip
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
paperclipai plugin install C:/Users/Jad_K/.paperclip/instances/default/projects/d0b61e38-82d9-4bcf-b9e8-8ea970388ba9/2ba5752a-ea82-4afc-8d60-2ede6be97f44/_default/file-browser
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Build Options
|
|
28
|
+
|
|
29
|
+
- `pnpm build` uses esbuild presets from `@jkawwa/paperclipai-plugin-sdk/bundlers`.
|
|
30
|
+
- `pnpm build:rollup` uses rollup presets from the same SDK.
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// src/manifest.ts
|
|
2
|
+
var manifest = {
|
|
3
|
+
id: "jkl.file-browser",
|
|
4
|
+
apiVersion: 1,
|
|
5
|
+
version: "0.1.4",
|
|
6
|
+
displayName: "File Browser",
|
|
7
|
+
description: "A file browser plugin for Paperclip workspaces",
|
|
8
|
+
author: "Plugin Author",
|
|
9
|
+
categories: ["workspace"],
|
|
10
|
+
capabilities: [
|
|
11
|
+
"events.subscribe",
|
|
12
|
+
"plugin.state.read",
|
|
13
|
+
"plugin.state.write",
|
|
14
|
+
"local.folders",
|
|
15
|
+
"ui.dashboardWidget.register",
|
|
16
|
+
"ui.detailTab.register",
|
|
17
|
+
"api.routes.register",
|
|
18
|
+
"project.workspaces.read"
|
|
19
|
+
],
|
|
20
|
+
localFolders: [
|
|
21
|
+
{
|
|
22
|
+
folderKey: "workspace",
|
|
23
|
+
displayName: "Workspace Root",
|
|
24
|
+
description: "Root directory of the project workspace to browse files",
|
|
25
|
+
access: "read"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
apiRoutes: [
|
|
29
|
+
{
|
|
30
|
+
routeKey: "list-files",
|
|
31
|
+
method: "GET",
|
|
32
|
+
path: "/files/list",
|
|
33
|
+
auth: "board-or-agent",
|
|
34
|
+
capability: "api.routes.register",
|
|
35
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
routeKey: "download-file",
|
|
39
|
+
method: "GET",
|
|
40
|
+
path: "/files/download",
|
|
41
|
+
auth: "board-or-agent",
|
|
42
|
+
capability: "api.routes.register",
|
|
43
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
routeKey: "download-zip",
|
|
47
|
+
method: "GET",
|
|
48
|
+
path: "/files/zip",
|
|
49
|
+
auth: "board-or-agent",
|
|
50
|
+
capability: "api.routes.register",
|
|
51
|
+
companyResolution: { from: "query", key: "companyId" }
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
entrypoints: {
|
|
55
|
+
worker: "./dist/worker.js",
|
|
56
|
+
ui: "./dist/ui"
|
|
57
|
+
},
|
|
58
|
+
ui: {
|
|
59
|
+
slots: [
|
|
60
|
+
{
|
|
61
|
+
type: "dashboardWidget",
|
|
62
|
+
id: "file-browser-widget",
|
|
63
|
+
displayName: "File Browser",
|
|
64
|
+
exportName: "DashboardWidget"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: "detailTab",
|
|
68
|
+
id: "file-browser-tab",
|
|
69
|
+
displayName: "Files",
|
|
70
|
+
exportName: "FileBrowserTab",
|
|
71
|
+
entityTypes: ["project", "project_workspace", "issue"],
|
|
72
|
+
order: 50
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
var manifest_default = manifest;
|
|
78
|
+
export {
|
|
79
|
+
manifest_default as default
|
|
80
|
+
};
|
|
81
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/manifest.ts"],
|
|
4
|
+
"sourcesContent": ["import type { PaperclipPluginManifestV1 } from \"@paperclipai/plugin-sdk\";\n\nconst manifest: PaperclipPluginManifestV1 = {\n id: \"jkl.file-browser\",\n apiVersion: 1,\n version: \"0.1.4\",\n displayName: \"File Browser\",\n description: \"A file browser plugin for Paperclip workspaces\",\n author: \"Plugin Author\",\n categories: [\"workspace\"],\n capabilities: [\n \"events.subscribe\",\n \"plugin.state.read\",\n \"plugin.state.write\",\n \"local.folders\",\n \"ui.dashboardWidget.register\",\n \"ui.detailTab.register\",\n \"api.routes.register\",\n \"project.workspaces.read\"\n ],\n localFolders: [\n {\n folderKey: \"workspace\",\n displayName: \"Workspace Root\",\n description: \"Root directory of the project workspace to browse files\",\n access: \"read\"\n }\n ],\n apiRoutes: [\n {\n routeKey: \"list-files\",\n method: \"GET\",\n path: \"/files/list\",\n auth: \"board-or-agent\",\n capability: \"api.routes.register\",\n companyResolution: { from: \"query\", key: \"companyId\" }\n },\n {\n routeKey: \"download-file\",\n method: \"GET\",\n path: \"/files/download\",\n auth: \"board-or-agent\",\n capability: \"api.routes.register\",\n companyResolution: { from: \"query\", key: \"companyId\" }\n },\n {\n routeKey: \"download-zip\",\n method: \"GET\",\n path: \"/files/zip\",\n auth: \"board-or-agent\",\n capability: \"api.routes.register\",\n companyResolution: { from: \"query\", key: \"companyId\" }\n }\n ],\n entrypoints: {\n worker: \"./dist/worker.js\",\n ui: \"./dist/ui\"\n },\n ui: {\n slots: [\n {\n type: \"dashboardWidget\",\n id: \"file-browser-widget\",\n displayName: \"File Browser\",\n exportName: \"DashboardWidget\"\n },\n {\n type: \"detailTab\",\n id: \"file-browser-tab\",\n displayName: \"Files\",\n exportName: \"FileBrowserTab\",\n entityTypes: [\"project\", \"project_workspace\", \"issue\"],\n order: 50\n }\n ]\n }\n};\n\nexport default manifest;\n"],
|
|
5
|
+
"mappings": ";AAEA,IAAM,WAAsC;AAAA,EAC1C,IAAI;AAAA,EACJ,YAAY;AAAA,EACZ,SAAS;AAAA,EACT,aAAa;AAAA,EACb,aAAa;AAAA,EACb,QAAQ;AAAA,EACR,YAAY,CAAC,WAAW;AAAA,EACxB,cAAc;AAAA,IACZ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAAA,EACA,cAAc;AAAA,IACZ;AAAA,MACE,WAAW;AAAA,MACX,aAAa;AAAA,MACb,aAAa;AAAA,MACb,QAAQ;AAAA,IACV;AAAA,EACF;AAAA,EACA,WAAW;AAAA,IACT;AAAA,MACE,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,mBAAmB,EAAE,MAAM,SAAS,KAAK,YAAY;AAAA,IACvD;AAAA,IACA;AAAA,MACE,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,mBAAmB,EAAE,MAAM,SAAS,KAAK,YAAY;AAAA,IACvD;AAAA,IACA;AAAA,MACE,UAAU;AAAA,MACV,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,MAAM;AAAA,MACN,YAAY;AAAA,MACZ,mBAAmB,EAAE,MAAM,SAAS,KAAK,YAAY;AAAA,IACvD;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX,QAAQ;AAAA,IACR,IAAI;AAAA,EACN;AAAA,EACA,IAAI;AAAA,IACF,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,aAAa;AAAA,QACb,YAAY;AAAA,MACd;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,IAAI;AAAA,QACJ,aAAa;AAAA,QACb,YAAY;AAAA,QACZ,aAAa,CAAC,WAAW,qBAAqB,OAAO;AAAA,QACrD,OAAO;AAAA,MACT;AAAA,IACF;AAAA,EACF;AACF;AAEA,IAAO,mBAAQ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/ui/index.js
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// src/ui/index.tsx
|
|
2
|
+
import { useCallback, useState } from "react";
|
|
3
|
+
import {
|
|
4
|
+
usePluginData,
|
|
5
|
+
usePluginAction,
|
|
6
|
+
useHostContext
|
|
7
|
+
} from "@paperclipai/plugin-sdk/ui";
|
|
8
|
+
import {
|
|
9
|
+
FileTree,
|
|
10
|
+
MetricCard
|
|
11
|
+
} from "@paperclipai/plugin-sdk/ui";
|
|
12
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
13
|
+
function buildNodes(data) {
|
|
14
|
+
if (!data) return [];
|
|
15
|
+
const dirNodes = data.directories.map((d) => ({
|
|
16
|
+
name: d.name,
|
|
17
|
+
path: d.path,
|
|
18
|
+
kind: "dir",
|
|
19
|
+
children: []
|
|
20
|
+
}));
|
|
21
|
+
const fileNodes = data.files.map((f) => ({
|
|
22
|
+
name: f.name,
|
|
23
|
+
path: f.path,
|
|
24
|
+
kind: "file",
|
|
25
|
+
children: []
|
|
26
|
+
}));
|
|
27
|
+
return [...dirNodes, ...fileNodes];
|
|
28
|
+
}
|
|
29
|
+
function formatSize(bytes) {
|
|
30
|
+
if (bytes == null) return "-";
|
|
31
|
+
if (bytes < 1024) return `${bytes} B`;
|
|
32
|
+
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
33
|
+
return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
|
|
34
|
+
}
|
|
35
|
+
function FileBrowser({ companyId }) {
|
|
36
|
+
const [currentPath, setCurrentPath] = useState("");
|
|
37
|
+
const [selectedFile, setSelectedFile] = useState(null);
|
|
38
|
+
const { data, loading, error, refresh } = usePluginData(
|
|
39
|
+
"file-tree",
|
|
40
|
+
{ companyId: companyId ?? "", relativePath: currentPath }
|
|
41
|
+
);
|
|
42
|
+
const downloadFile = usePluginAction("download-file");
|
|
43
|
+
const downloadZip = usePluginAction("download-zip");
|
|
44
|
+
const nodes = buildNodes(data);
|
|
45
|
+
const handleSelectFile = useCallback((path) => {
|
|
46
|
+
setSelectedFile(path);
|
|
47
|
+
}, []);
|
|
48
|
+
const handleToggleDir = useCallback((path) => {
|
|
49
|
+
setCurrentPath(path);
|
|
50
|
+
setSelectedFile(null);
|
|
51
|
+
}, []);
|
|
52
|
+
const handleDownloadFile = useCallback(async () => {
|
|
53
|
+
if (!selectedFile || !companyId) return;
|
|
54
|
+
try {
|
|
55
|
+
await downloadFile({ companyId, relativePath: selectedFile });
|
|
56
|
+
} catch {
|
|
57
|
+
}
|
|
58
|
+
}, [selectedFile, companyId, downloadFile]);
|
|
59
|
+
const handleDownloadZip = useCallback(async () => {
|
|
60
|
+
if (!currentPath || !companyId) return;
|
|
61
|
+
try {
|
|
62
|
+
await downloadZip({ companyId, relativePath: currentPath });
|
|
63
|
+
} catch {
|
|
64
|
+
}
|
|
65
|
+
}, [currentPath, companyId, downloadZip]);
|
|
66
|
+
const handleNavigateUp = useCallback(() => {
|
|
67
|
+
if (!currentPath) return;
|
|
68
|
+
const parts = currentPath.split("/");
|
|
69
|
+
parts.pop();
|
|
70
|
+
setCurrentPath(parts.join("/"));
|
|
71
|
+
setSelectedFile(null);
|
|
72
|
+
}, [currentPath]);
|
|
73
|
+
const handleRefresh = useCallback(() => {
|
|
74
|
+
refresh();
|
|
75
|
+
}, [refresh]);
|
|
76
|
+
const fileCount = data?.files.length ?? 0;
|
|
77
|
+
const dirCount = data?.directories.length ?? 0;
|
|
78
|
+
const totalSize = data?.files.reduce((sum, f) => sum + (f.size ?? 0), 0) ?? 0;
|
|
79
|
+
if (!companyId) {
|
|
80
|
+
return /* @__PURE__ */ jsx("div", { style: { padding: "1rem" }, children: "Select a company to browse files." });
|
|
81
|
+
}
|
|
82
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "0.75rem", padding: "0.5rem 0" }, children: [
|
|
83
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.5rem", flexWrap: "wrap" }, children: [
|
|
84
|
+
/* @__PURE__ */ jsx(MetricCard, { label: "Files", value: fileCount }),
|
|
85
|
+
/* @__PURE__ */ jsx(MetricCard, { label: "Directories", value: dirCount }),
|
|
86
|
+
/* @__PURE__ */ jsx(MetricCard, { label: "Total Size", value: formatSize(totalSize) })
|
|
87
|
+
] }),
|
|
88
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: "0.5rem", alignItems: "center", flexWrap: "wrap" }, children: [
|
|
89
|
+
currentPath && /* @__PURE__ */ jsx("button", { onClick: handleNavigateUp, style: { fontSize: "0.85rem" }, children: ".. /" }),
|
|
90
|
+
/* @__PURE__ */ jsxs("span", { style: { fontSize: "0.85rem", color: "var(--text-secondary, #888)" }, children: [
|
|
91
|
+
"/",
|
|
92
|
+
currentPath || ""
|
|
93
|
+
] }),
|
|
94
|
+
/* @__PURE__ */ jsxs("div", { style: { marginLeft: "auto", display: "flex", gap: "0.5rem" }, children: [
|
|
95
|
+
/* @__PURE__ */ jsx("button", { onClick: handleRefresh, style: { fontSize: "0.85rem" }, children: "Refresh" }),
|
|
96
|
+
selectedFile && /* @__PURE__ */ jsx("button", { onClick: handleDownloadFile, style: { fontSize: "0.85rem" }, children: "Download File" }),
|
|
97
|
+
currentPath && /* @__PURE__ */ jsx("button", { onClick: handleDownloadZip, style: { fontSize: "0.85rem" }, children: "Download Folder (ZIP)" })
|
|
98
|
+
] })
|
|
99
|
+
] }),
|
|
100
|
+
data?.error && /* @__PURE__ */ jsxs("div", { style: { color: "var(--text-error, red)", fontSize: "0.85rem" }, children: [
|
|
101
|
+
"Error: ",
|
|
102
|
+
data.error
|
|
103
|
+
] }),
|
|
104
|
+
/* @__PURE__ */ jsx(
|
|
105
|
+
FileTree,
|
|
106
|
+
{
|
|
107
|
+
nodes,
|
|
108
|
+
selectedFile,
|
|
109
|
+
expandedPaths: [],
|
|
110
|
+
onSelectFile: handleSelectFile,
|
|
111
|
+
onToggleDir: handleToggleDir,
|
|
112
|
+
loading,
|
|
113
|
+
error: error ? { message: error.message } : null,
|
|
114
|
+
empty: { title: "Empty directory", description: "No files or directories found." },
|
|
115
|
+
ariaLabel: "File browser"
|
|
116
|
+
}
|
|
117
|
+
),
|
|
118
|
+
selectedFile && /* @__PURE__ */ jsxs("div", { style: { fontSize: "0.8rem", color: "var(--text-secondary, #888)", paddingTop: "0.25rem" }, children: [
|
|
119
|
+
"Selected: ",
|
|
120
|
+
selectedFile
|
|
121
|
+
] })
|
|
122
|
+
] });
|
|
123
|
+
}
|
|
124
|
+
function DashboardWidget(_props) {
|
|
125
|
+
const { companyId } = useHostContext();
|
|
126
|
+
return /* @__PURE__ */ jsxs("div", { style: { display: "grid", gap: "0.5rem" }, children: [
|
|
127
|
+
/* @__PURE__ */ jsx("strong", { children: "File Browser" }),
|
|
128
|
+
/* @__PURE__ */ jsx(FileBrowser, { companyId })
|
|
129
|
+
] });
|
|
130
|
+
}
|
|
131
|
+
function FileBrowserTab(props) {
|
|
132
|
+
const { companyId } = props.context;
|
|
133
|
+
return /* @__PURE__ */ jsx(FileBrowser, { companyId });
|
|
134
|
+
}
|
|
135
|
+
export {
|
|
136
|
+
DashboardWidget,
|
|
137
|
+
FileBrowserTab
|
|
138
|
+
};
|
|
139
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ui/index.tsx"],
|
|
4
|
+
"sourcesContent": ["import { useCallback, useState } from \"react\";\nimport {\n usePluginData,\n usePluginAction,\n useHostContext,\n type PluginWidgetProps,\n type PluginDetailTabProps,\n} from \"@paperclipai/plugin-sdk/ui\";\nimport {\n FileTree,\n MetricCard,\n Spinner,\n type FileTreeNode,\n} from \"@paperclipai/plugin-sdk/ui\";\n\ninterface FileEntry {\n name: string;\n path: string;\n kind: \"file\" | \"directory\";\n size?: number | null;\n modifiedAt?: string | null;\n}\n\ninterface FileTreeData {\n currentPath: string;\n directories: FileEntry[];\n files: FileEntry[];\n error?: string;\n}\n\nfunction buildNodes(data: FileTreeData | null): FileTreeNode[] {\n if (!data) return [];\n const dirNodes: FileTreeNode[] = data.directories.map((d) => ({\n name: d.name,\n path: d.path,\n kind: \"dir\" as const,\n children: [],\n }));\n const fileNodes: FileTreeNode[] = data.files.map((f) => ({\n name: f.name,\n path: f.path,\n kind: \"file\" as const,\n children: [],\n }));\n return [...dirNodes, ...fileNodes];\n}\n\nfunction formatSize(bytes: number | null | undefined): string {\n if (bytes == null) return \"-\";\n if (bytes < 1024) return `${bytes} B`;\n if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;\n return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;\n}\n\nfunction FileBrowser({ companyId }: { companyId: string | null }) {\n const [currentPath, setCurrentPath] = useState(\"\");\n const [selectedFile, setSelectedFile] = useState<string | null>(null);\n\n const { data, loading, error, refresh } = usePluginData<FileTreeData>(\n \"file-tree\",\n { companyId: companyId ?? \"\", relativePath: currentPath }\n );\n\n const downloadFile = usePluginAction(\"download-file\");\n const downloadZip = usePluginAction(\"download-zip\");\n\n const nodes = buildNodes(data);\n\n const handleSelectFile = useCallback((path: string) => {\n setSelectedFile(path);\n }, []);\n\n const handleToggleDir = useCallback((path: string) => {\n setCurrentPath(path);\n setSelectedFile(null);\n }, []);\n\n const handleDownloadFile = useCallback(async () => {\n if (!selectedFile || !companyId) return;\n try {\n await downloadFile({ companyId, relativePath: selectedFile });\n } catch {\n // Bridge error propagated\n }\n }, [selectedFile, companyId, downloadFile]);\n\n const handleDownloadZip = useCallback(async () => {\n if (!currentPath || !companyId) return;\n try {\n await downloadZip({ companyId, relativePath: currentPath });\n } catch {\n // Bridge error propagated\n }\n }, [currentPath, companyId, downloadZip]);\n\n const handleNavigateUp = useCallback(() => {\n if (!currentPath) return;\n const parts = currentPath.split(\"/\");\n parts.pop();\n setCurrentPath(parts.join(\"/\"));\n setSelectedFile(null);\n }, [currentPath]);\n\n const handleRefresh = useCallback(() => {\n refresh();\n }, [refresh]);\n\n const fileCount = data?.files.length ?? 0;\n const dirCount = data?.directories.length ?? 0;\n const totalSize = data?.files.reduce((sum, f) => sum + (f.size ?? 0), 0) ?? 0;\n\n if (!companyId) {\n return <div style={{ padding: \"1rem\" }}>Select a company to browse files.</div>;\n }\n\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: \"0.75rem\", padding: \"0.5rem 0\" }}>\n <div style={{ display: \"flex\", gap: \"0.5rem\", flexWrap: \"wrap\" }}>\n <MetricCard label=\"Files\" value={fileCount} />\n <MetricCard label=\"Directories\" value={dirCount} />\n <MetricCard label=\"Total Size\" value={formatSize(totalSize)} />\n </div>\n\n <div style={{ display: \"flex\", gap: \"0.5rem\", alignItems: \"center\", flexWrap: \"wrap\" }}>\n {currentPath && (\n <button onClick={handleNavigateUp} style={{ fontSize: \"0.85rem\" }}>\n .. / \n </button>\n )}\n <span style={{ fontSize: \"0.85rem\", color: \"var(--text-secondary, #888)\" }}>\n /{currentPath || \"\"}\n </span>\n <div style={{ marginLeft: \"auto\", display: \"flex\", gap: \"0.5rem\" }}>\n <button onClick={handleRefresh} style={{ fontSize: \"0.85rem\" }}>\n Refresh\n </button>\n {selectedFile && (\n <button onClick={handleDownloadFile} style={{ fontSize: \"0.85rem\" }}>\n Download File\n </button>\n )}\n {currentPath && (\n <button onClick={handleDownloadZip} style={{ fontSize: \"0.85rem\" }}>\n Download Folder (ZIP)\n </button>\n )}\n </div>\n </div>\n\n {data?.error && (\n <div style={{ color: \"var(--text-error, red)\", fontSize: \"0.85rem\" }}>\n Error: {data.error}\n </div>\n )}\n\n <FileTree\n nodes={nodes}\n selectedFile={selectedFile}\n expandedPaths={[]}\n onSelectFile={handleSelectFile}\n onToggleDir={handleToggleDir}\n loading={loading}\n error={error ? { message: error.message } : null}\n empty={{ title: \"Empty directory\", description: \"No files or directories found.\" }}\n ariaLabel=\"File browser\"\n />\n\n {selectedFile && (\n <div style={{ fontSize: \"0.8rem\", color: \"var(--text-secondary, #888)\", paddingTop: \"0.25rem\" }}>\n Selected: {selectedFile}\n </div>\n )}\n </div>\n );\n}\n\nexport function DashboardWidget(_props: PluginWidgetProps) {\n const { companyId } = useHostContext();\n return (\n <div style={{ display: \"grid\", gap: \"0.5rem\" }}>\n <strong>File Browser</strong>\n <FileBrowser companyId={companyId} />\n </div>\n );\n}\n\nexport function FileBrowserTab(props: PluginDetailTabProps) {\n const { companyId } = props.context;\n return <FileBrowser companyId={companyId} />;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|