paperclip-plugin-navigator 0.1.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/manifest.d.ts +4 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +41 -0
- package/dist/manifest.js.map +1 -0
- package/dist/ui/index.d.ts +3 -0
- package/dist/ui/index.d.ts.map +1 -0
- package/dist/ui/index.js +258 -0
- package/dist/ui/index.js.map +7 -0
- package/dist/worker.d.ts +19 -0
- package/dist/worker.d.ts.map +1 -0
- package/dist/worker.js +110 -0
- package/dist/worker.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAUzE,QAAA,MAAM,QAAQ,EAAE,yBAoCf,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
package/dist/manifest.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const PLUGIN_ID = "paperclip-plugin-navigator";
|
|
2
|
+
const PLUGIN_VERSION = "0.1.0";
|
|
3
|
+
const NAV_SLOT_ID = "navigator-global-nav";
|
|
4
|
+
const NAV_EXPORT_NAME = "NavigatorPage";
|
|
5
|
+
const manifest = {
|
|
6
|
+
id: PLUGIN_ID,
|
|
7
|
+
apiVersion: 1,
|
|
8
|
+
version: PLUGIN_VERSION,
|
|
9
|
+
displayName: "File Navigator",
|
|
10
|
+
description: "Browse projects with real names and open them directly in your external filebrowser.",
|
|
11
|
+
author: "paperclip-plugin-navigator",
|
|
12
|
+
categories: ["workspace", "ui"],
|
|
13
|
+
capabilities: ["projects.read", "project.workspaces.read", "companies.read"],
|
|
14
|
+
instanceConfigSchema: {
|
|
15
|
+
type: "object",
|
|
16
|
+
properties: {
|
|
17
|
+
fileBrowserBaseUrl: {
|
|
18
|
+
type: "string",
|
|
19
|
+
title: "File Browser Base URL",
|
|
20
|
+
description: "Base URL of your external filebrowser (e.g. https://files.example.com/files)",
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
required: ["fileBrowserBaseUrl"],
|
|
24
|
+
},
|
|
25
|
+
entrypoints: {
|
|
26
|
+
worker: "./dist/worker.js",
|
|
27
|
+
ui: "./dist/ui",
|
|
28
|
+
},
|
|
29
|
+
ui: {
|
|
30
|
+
slots: [
|
|
31
|
+
{
|
|
32
|
+
type: "page",
|
|
33
|
+
id: NAV_SLOT_ID,
|
|
34
|
+
displayName: "Files",
|
|
35
|
+
exportName: NAV_EXPORT_NAME,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
export default manifest;
|
|
41
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../src/manifest.ts"],"names":[],"mappings":"AAEA,MAAM,SAAS,GAAG,4BAA4B,CAAC;AAE/C,MAAM,cAAc,GAAG,OAAO,CAAC;AAE/B,MAAM,WAAW,GAAG,sBAAsB,CAAC;AAE3C,MAAM,eAAe,GAAG,eAAe,CAAC;AAExC,MAAM,QAAQ,GAA8B;IAC1C,EAAE,EAAE,SAAS;IACb,UAAU,EAAE,CAAC;IACb,OAAO,EAAE,cAAc;IACvB,WAAW,EAAE,gBAAgB;IAC7B,WAAW,EACT,sFAAsF;IACxF,MAAM,EAAE,4BAA4B;IACpC,UAAU,EAAE,CAAC,WAAW,EAAE,IAAI,CAAC;IAC/B,YAAY,EAAE,CAAC,eAAe,EAAE,yBAAyB,EAAE,gBAAgB,CAAC;IAC5E,oBAAoB,EAAE;QACpB,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACV,kBAAkB,EAAE;gBAClB,IAAI,EAAE,QAAQ;gBACd,KAAK,EAAE,uBAAuB;gBAC9B,WAAW,EACT,8EAA8E;aACjF;SACF;QACD,QAAQ,EAAE,CAAC,oBAAoB,CAAC;KACjC;IACD,WAAW,EAAE;QACX,MAAM,EAAE,kBAAkB;QAC1B,EAAE,EAAE,WAAW;KAChB;IACD,EAAE,EAAE;QACF,KAAK,EAAE;YACL;gBACE,IAAI,EAAE,MAAM;gBACZ,EAAE,EAAE,WAAW;gBACf,WAAW,EAAE,OAAO;gBACpB,UAAU,EAAE,eAAe;aAC5B;SACF;KACF;CACF,CAAC;AAEF,eAAe,QAAQ,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/ui/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AA0KlE,wBAAgB,aAAa,CAAC,EAAE,OAAO,EAAE,EAAE,eAAe,2CA4HzD"}
|
package/dist/ui/index.js
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
// src/ui/index.tsx
|
|
2
|
+
import { useState, useMemo } from "react";
|
|
3
|
+
import { usePluginData } from "@paperclipai/plugin-sdk/ui";
|
|
4
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
5
|
+
var COLORS = {
|
|
6
|
+
border: "#e5e7eb",
|
|
7
|
+
bg: "#f9fafb",
|
|
8
|
+
bgCard: "#ffffff",
|
|
9
|
+
textPrimary: "#111827",
|
|
10
|
+
textSecondary: "#6b7280",
|
|
11
|
+
textMuted: "#9ca3af",
|
|
12
|
+
primary: "#2563eb",
|
|
13
|
+
primaryHover: "#1d4ed8",
|
|
14
|
+
danger: "#ef4444",
|
|
15
|
+
warningBg: "#fffbeb",
|
|
16
|
+
warningBorder: "#fcd34d",
|
|
17
|
+
warningText: "#92400e"
|
|
18
|
+
};
|
|
19
|
+
var styles = {
|
|
20
|
+
container: {
|
|
21
|
+
fontFamily: "Inter, system-ui, -apple-system, sans-serif",
|
|
22
|
+
padding: "32px",
|
|
23
|
+
maxWidth: "1000px",
|
|
24
|
+
margin: "0 auto",
|
|
25
|
+
color: COLORS.textPrimary
|
|
26
|
+
},
|
|
27
|
+
header: {
|
|
28
|
+
marginBottom: "32px"
|
|
29
|
+
},
|
|
30
|
+
title: {
|
|
31
|
+
fontSize: "24px",
|
|
32
|
+
fontWeight: 600,
|
|
33
|
+
letterSpacing: "-0.025em",
|
|
34
|
+
margin: "0 0 8px 0"
|
|
35
|
+
},
|
|
36
|
+
description: {
|
|
37
|
+
fontSize: "14px",
|
|
38
|
+
color: COLORS.textSecondary,
|
|
39
|
+
margin: 0
|
|
40
|
+
},
|
|
41
|
+
toolbar: {
|
|
42
|
+
display: "flex",
|
|
43
|
+
alignItems: "center",
|
|
44
|
+
justifyContent: "space-between",
|
|
45
|
+
marginBottom: "20px",
|
|
46
|
+
gap: "16px"
|
|
47
|
+
},
|
|
48
|
+
searchWrapper: {
|
|
49
|
+
position: "relative",
|
|
50
|
+
flex: 1
|
|
51
|
+
},
|
|
52
|
+
searchInput: {
|
|
53
|
+
width: "100%",
|
|
54
|
+
padding: "10px 12px 10px 36px",
|
|
55
|
+
fontSize: "14px",
|
|
56
|
+
borderRadius: "8px",
|
|
57
|
+
border: `1px solid ${COLORS.border}`,
|
|
58
|
+
outline: "none",
|
|
59
|
+
transition: "border-color 0.2s"
|
|
60
|
+
},
|
|
61
|
+
searchIcon: {
|
|
62
|
+
position: "absolute",
|
|
63
|
+
left: "12px",
|
|
64
|
+
top: "50%",
|
|
65
|
+
transform: "translateY(-50%)",
|
|
66
|
+
color: COLORS.textMuted,
|
|
67
|
+
pointerEvents: "none"
|
|
68
|
+
},
|
|
69
|
+
configWarning: {
|
|
70
|
+
display: "flex",
|
|
71
|
+
alignItems: "center",
|
|
72
|
+
gap: "12px",
|
|
73
|
+
padding: "12px 16px",
|
|
74
|
+
backgroundColor: COLORS.warningBg,
|
|
75
|
+
border: `1px solid ${COLORS.warningBorder}`,
|
|
76
|
+
borderRadius: "8px",
|
|
77
|
+
marginBottom: "24px",
|
|
78
|
+
fontSize: "14px",
|
|
79
|
+
color: COLORS.warningText
|
|
80
|
+
},
|
|
81
|
+
tableCard: {
|
|
82
|
+
backgroundColor: COLORS.bgCard,
|
|
83
|
+
border: `1px solid ${COLORS.border}`,
|
|
84
|
+
borderRadius: "12px",
|
|
85
|
+
overflow: "hidden",
|
|
86
|
+
boxShadow: "0 1px 3px rgba(0,0,0,0.1)"
|
|
87
|
+
},
|
|
88
|
+
tableHeader: {
|
|
89
|
+
display: "grid",
|
|
90
|
+
gridTemplateColumns: "1fr 140px",
|
|
91
|
+
padding: "12px 20px",
|
|
92
|
+
backgroundColor: COLORS.bg,
|
|
93
|
+
borderBottom: `1px solid ${COLORS.border}`,
|
|
94
|
+
fontSize: "12px",
|
|
95
|
+
fontWeight: 600,
|
|
96
|
+
textTransform: "uppercase",
|
|
97
|
+
letterSpacing: "0.05em",
|
|
98
|
+
color: COLORS.textSecondary
|
|
99
|
+
},
|
|
100
|
+
row: {
|
|
101
|
+
display: "grid",
|
|
102
|
+
gridTemplateColumns: "1fr 140px",
|
|
103
|
+
padding: "16px 20px",
|
|
104
|
+
borderBottom: `1px solid ${COLORS.border}`,
|
|
105
|
+
alignItems: "center",
|
|
106
|
+
transition: "background-color 0.15s",
|
|
107
|
+
":last-child": {
|
|
108
|
+
borderBottom: "none"
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
projectMain: {
|
|
112
|
+
display: "flex",
|
|
113
|
+
flexDirection: "column",
|
|
114
|
+
gap: "4px",
|
|
115
|
+
minWidth: 0
|
|
116
|
+
},
|
|
117
|
+
projectName: {
|
|
118
|
+
fontSize: "15px",
|
|
119
|
+
fontWeight: 500,
|
|
120
|
+
color: COLORS.textPrimary
|
|
121
|
+
},
|
|
122
|
+
projectPath: {
|
|
123
|
+
fontSize: "12px",
|
|
124
|
+
color: COLORS.textMuted,
|
|
125
|
+
fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
|
|
126
|
+
whiteSpace: "nowrap",
|
|
127
|
+
overflow: "hidden",
|
|
128
|
+
textOverflow: "ellipsis"
|
|
129
|
+
},
|
|
130
|
+
actionCell: {
|
|
131
|
+
display: "flex",
|
|
132
|
+
justifyContent: "flex-end"
|
|
133
|
+
},
|
|
134
|
+
button: {
|
|
135
|
+
padding: "8px 16px",
|
|
136
|
+
borderRadius: "6px",
|
|
137
|
+
fontSize: "13px",
|
|
138
|
+
fontWeight: 500,
|
|
139
|
+
cursor: "pointer",
|
|
140
|
+
textDecoration: "none",
|
|
141
|
+
display: "inline-flex",
|
|
142
|
+
alignItems: "center",
|
|
143
|
+
justifyContent: "center",
|
|
144
|
+
transition: "all 0.2s",
|
|
145
|
+
border: "none"
|
|
146
|
+
},
|
|
147
|
+
primaryButton: {
|
|
148
|
+
backgroundColor: COLORS.primary,
|
|
149
|
+
color: "#fff"
|
|
150
|
+
},
|
|
151
|
+
disabledButton: {
|
|
152
|
+
backgroundColor: COLORS.bg,
|
|
153
|
+
color: COLORS.textMuted,
|
|
154
|
+
cursor: "not-allowed",
|
|
155
|
+
border: `1px solid ${COLORS.border}`
|
|
156
|
+
},
|
|
157
|
+
emptyState: {
|
|
158
|
+
padding: "60px 20px",
|
|
159
|
+
textAlign: "center",
|
|
160
|
+
color: COLORS.textSecondary
|
|
161
|
+
}
|
|
162
|
+
};
|
|
163
|
+
function NavigatorPage({ context }) {
|
|
164
|
+
const [searchTerm, setSearchTerm] = useState("");
|
|
165
|
+
const { data, loading, error, refresh } = usePluginData("projects", {
|
|
166
|
+
companyId: context.companyId ?? void 0
|
|
167
|
+
});
|
|
168
|
+
const projects = data ?? [];
|
|
169
|
+
const filteredProjects = useMemo(() => {
|
|
170
|
+
if (!searchTerm) return projects;
|
|
171
|
+
const lower = searchTerm.toLowerCase();
|
|
172
|
+
return projects.filter(
|
|
173
|
+
(p) => p.name.toLowerCase().includes(lower) || p.path && p.path.toLowerCase().includes(lower)
|
|
174
|
+
);
|
|
175
|
+
}, [projects, searchTerm]);
|
|
176
|
+
const isConfigMissing = useMemo(() => {
|
|
177
|
+
return projects.length > 0 && projects.every((p) => p.fileBrowserUrl === null && p.path !== null);
|
|
178
|
+
}, [projects]);
|
|
179
|
+
return /* @__PURE__ */ jsxs("div", { style: styles.container, children: [
|
|
180
|
+
/* @__PURE__ */ jsxs("header", { style: styles.header, children: [
|
|
181
|
+
/* @__PURE__ */ jsx("h1", { style: styles.title, children: "Project Navigator" }),
|
|
182
|
+
/* @__PURE__ */ jsx("p", { style: styles.description, children: "Browse and access your project files via the external file manager." })
|
|
183
|
+
] }),
|
|
184
|
+
isConfigMissing && /* @__PURE__ */ jsxs("div", { style: styles.configWarning, children: [
|
|
185
|
+
/* @__PURE__ */ jsxs("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
186
|
+
/* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "10" }),
|
|
187
|
+
/* @__PURE__ */ jsx("line", { x1: "12", y1: "8", x2: "12", y2: "12" }),
|
|
188
|
+
/* @__PURE__ */ jsx("line", { x1: "12", y1: "16", x2: "12.01", y2: "16" })
|
|
189
|
+
] }),
|
|
190
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
191
|
+
/* @__PURE__ */ jsx("strong", { children: "Configuration needed:" }),
|
|
192
|
+
" The File Browser Base URL is not set. Please contact your administrator."
|
|
193
|
+
] })
|
|
194
|
+
] }),
|
|
195
|
+
/* @__PURE__ */ jsxs("div", { style: styles.toolbar, children: [
|
|
196
|
+
/* @__PURE__ */ jsxs("div", { style: styles.searchWrapper, children: [
|
|
197
|
+
/* @__PURE__ */ jsx("div", { style: styles.searchIcon, children: /* @__PURE__ */ jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
198
|
+
/* @__PURE__ */ jsx("circle", { cx: "11", cy: "11", r: "8" }),
|
|
199
|
+
/* @__PURE__ */ jsx("line", { x1: "21", y1: "21", x2: "16.65", y2: "16.65" })
|
|
200
|
+
] }) }),
|
|
201
|
+
/* @__PURE__ */ jsx(
|
|
202
|
+
"input",
|
|
203
|
+
{
|
|
204
|
+
type: "text",
|
|
205
|
+
placeholder: "Search projects...",
|
|
206
|
+
style: styles.searchInput,
|
|
207
|
+
value: searchTerm,
|
|
208
|
+
onChange: (e) => setSearchTerm(e.target.value)
|
|
209
|
+
}
|
|
210
|
+
)
|
|
211
|
+
] }),
|
|
212
|
+
/* @__PURE__ */ jsxs(
|
|
213
|
+
"button",
|
|
214
|
+
{
|
|
215
|
+
onClick: () => refresh(),
|
|
216
|
+
style: { ...styles.button, ...styles.disabledButton, cursor: "pointer" },
|
|
217
|
+
title: "Refresh list",
|
|
218
|
+
children: [
|
|
219
|
+
/* @__PURE__ */ jsx("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { marginRight: "8px" }, children: /* @__PURE__ */ jsx("path", { d: "M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15" }) }),
|
|
220
|
+
"Refresh"
|
|
221
|
+
]
|
|
222
|
+
}
|
|
223
|
+
)
|
|
224
|
+
] }),
|
|
225
|
+
/* @__PURE__ */ jsxs("div", { style: styles.tableCard, children: [
|
|
226
|
+
/* @__PURE__ */ jsxs("div", { style: styles.tableHeader, children: [
|
|
227
|
+
/* @__PURE__ */ jsx("span", { children: "Project" }),
|
|
228
|
+
/* @__PURE__ */ jsx("span", { style: { textAlign: "right" }, children: "Action" })
|
|
229
|
+
] }),
|
|
230
|
+
loading && /* @__PURE__ */ jsx("div", { style: styles.emptyState, children: /* @__PURE__ */ jsx("p", { children: "Loading projects..." }) }),
|
|
231
|
+
!loading && error && /* @__PURE__ */ jsx("div", { style: { ...styles.emptyState, color: COLORS.danger }, children: /* @__PURE__ */ jsxs("p", { children: [
|
|
232
|
+
"Error loading projects: ",
|
|
233
|
+
error.message
|
|
234
|
+
] }) }),
|
|
235
|
+
!loading && !error && filteredProjects.length === 0 && /* @__PURE__ */ jsx("div", { style: styles.emptyState, children: /* @__PURE__ */ jsx("p", { children: searchTerm ? "No projects match your search." : "No projects found." }) }),
|
|
236
|
+
!loading && !error && filteredProjects.map((project) => /* @__PURE__ */ jsxs("div", { style: styles.row, children: [
|
|
237
|
+
/* @__PURE__ */ jsxs("div", { style: styles.projectMain, children: [
|
|
238
|
+
/* @__PURE__ */ jsx("span", { style: styles.projectName, children: project.name }),
|
|
239
|
+
project.path && /* @__PURE__ */ jsx("span", { style: styles.projectPath, title: project.path, children: project.path })
|
|
240
|
+
] }),
|
|
241
|
+
/* @__PURE__ */ jsx("div", { style: styles.actionCell, children: project.fileBrowserUrl ? /* @__PURE__ */ jsx(
|
|
242
|
+
"a",
|
|
243
|
+
{
|
|
244
|
+
href: project.fileBrowserUrl,
|
|
245
|
+
target: "_blank",
|
|
246
|
+
rel: "noopener noreferrer",
|
|
247
|
+
style: { ...styles.button, ...styles.primaryButton },
|
|
248
|
+
children: "Open Files"
|
|
249
|
+
}
|
|
250
|
+
) : /* @__PURE__ */ jsx("span", { style: { ...styles.button, ...styles.disabledButton }, title: "No workspace available", children: "Unavailable" }) })
|
|
251
|
+
] }, project.id))
|
|
252
|
+
] })
|
|
253
|
+
] });
|
|
254
|
+
}
|
|
255
|
+
export {
|
|
256
|
+
NavigatorPage
|
|
257
|
+
};
|
|
258
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/ui/index.tsx"],
|
|
4
|
+
"sourcesContent": ["import { useState, useMemo } from \"react\";\nimport type { PluginPageProps } from \"@paperclipai/plugin-sdk/ui\";\nimport { usePluginData } from \"@paperclipai/plugin-sdk/ui\";\n\ninterface ProjectEntry {\n id: string;\n name: string;\n path: string | null;\n fileBrowserUrl: string | null;\n}\n\nconst COLORS = {\n border: \"#e5e7eb\",\n bg: \"#f9fafb\",\n bgCard: \"#ffffff\",\n textPrimary: \"#111827\",\n textSecondary: \"#6b7280\",\n textMuted: \"#9ca3af\",\n primary: \"#2563eb\",\n primaryHover: \"#1d4ed8\",\n danger: \"#ef4444\",\n warningBg: \"#fffbeb\",\n warningBorder: \"#fcd34d\",\n warningText: \"#92400e\",\n};\n\nconst styles = {\n container: {\n fontFamily: 'Inter, system-ui, -apple-system, sans-serif',\n padding: \"32px\",\n maxWidth: \"1000px\",\n margin: \"0 auto\",\n color: COLORS.textPrimary,\n },\n header: {\n marginBottom: \"32px\",\n },\n title: {\n fontSize: \"24px\",\n fontWeight: 600,\n letterSpacing: \"-0.025em\",\n margin: \"0 0 8px 0\",\n },\n description: {\n fontSize: \"14px\",\n color: COLORS.textSecondary,\n margin: 0,\n },\n toolbar: {\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"space-between\",\n marginBottom: \"20px\",\n gap: \"16px\",\n },\n searchWrapper: {\n position: \"relative\" as const,\n flex: 1,\n },\n searchInput: {\n width: \"100%\",\n padding: \"10px 12px 10px 36px\",\n fontSize: \"14px\",\n borderRadius: \"8px\",\n border: `1px solid ${COLORS.border}`,\n outline: \"none\",\n transition: \"border-color 0.2s\",\n },\n searchIcon: {\n position: \"absolute\" as const,\n left: \"12px\",\n top: \"50%\",\n transform: \"translateY(-50%)\",\n color: COLORS.textMuted,\n pointerEvents: \"none\" as const,\n },\n configWarning: {\n display: \"flex\",\n alignItems: \"center\",\n gap: \"12px\",\n padding: \"12px 16px\",\n backgroundColor: COLORS.warningBg,\n border: `1px solid ${COLORS.warningBorder}`,\n borderRadius: \"8px\",\n marginBottom: \"24px\",\n fontSize: \"14px\",\n color: COLORS.warningText,\n },\n tableCard: {\n backgroundColor: COLORS.bgCard,\n border: `1px solid ${COLORS.border}`,\n borderRadius: \"12px\",\n overflow: \"hidden\",\n boxShadow: \"0 1px 3px rgba(0,0,0,0.1)\",\n },\n tableHeader: {\n display: \"grid\",\n gridTemplateColumns: \"1fr 140px\",\n padding: \"12px 20px\",\n backgroundColor: COLORS.bg,\n borderBottom: `1px solid ${COLORS.border}`,\n fontSize: \"12px\",\n fontWeight: 600,\n textTransform: \"uppercase\" as const,\n letterSpacing: \"0.05em\",\n color: COLORS.textSecondary,\n },\n row: {\n display: \"grid\",\n gridTemplateColumns: \"1fr 140px\",\n padding: \"16px 20px\",\n borderBottom: `1px solid ${COLORS.border}`,\n alignItems: \"center\",\n transition: \"background-color 0.15s\",\n \":last-child\": {\n borderBottom: \"none\",\n },\n },\n projectMain: {\n display: \"flex\",\n flexDirection: \"column\" as const,\n gap: \"4px\",\n minWidth: 0,\n },\n projectName: {\n fontSize: \"15px\",\n fontWeight: 500,\n color: COLORS.textPrimary,\n },\n projectPath: {\n fontSize: \"12px\",\n color: COLORS.textMuted,\n fontFamily: \"ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace\",\n whiteSpace: \"nowrap\" as const,\n overflow: \"hidden\",\n textOverflow: \"ellipsis\",\n },\n actionCell: {\n display: \"flex\",\n justifyContent: \"flex-end\",\n },\n button: {\n padding: \"8px 16px\",\n borderRadius: \"6px\",\n fontSize: \"13px\",\n fontWeight: 500,\n cursor: \"pointer\",\n textDecoration: \"none\",\n display: \"inline-flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n transition: \"all 0.2s\",\n border: \"none\",\n },\n primaryButton: {\n backgroundColor: COLORS.primary,\n color: \"#fff\",\n },\n disabledButton: {\n backgroundColor: COLORS.bg,\n color: COLORS.textMuted,\n cursor: \"not-allowed\",\n border: `1px solid ${COLORS.border}`,\n },\n emptyState: {\n padding: \"60px 20px\",\n textAlign: \"center\" as const,\n color: COLORS.textSecondary,\n },\n};\n\nexport function NavigatorPage({ context }: PluginPageProps) {\n const [searchTerm, setSearchTerm] = useState(\"\");\n const { data, loading, error, refresh } = usePluginData<ProjectEntry[]>(\"projects\", {\n companyId: context.companyId ?? undefined,\n });\n\n const projects = data ?? [];\n \n const filteredProjects = useMemo(() => {\n if (!searchTerm) return projects;\n const lower = searchTerm.toLowerCase();\n return projects.filter(p => \n p.name.toLowerCase().includes(lower) || \n (p.path && p.path.toLowerCase().includes(lower))\n );\n }, [projects, searchTerm]);\n\n // Infer if config is missing (if we have projects but NO fileBrowserUrls)\n const isConfigMissing = useMemo(() => {\n return projects.length > 0 && projects.every(p => p.fileBrowserUrl === null && p.path !== null);\n }, [projects]);\n\n return (\n <div style={styles.container}>\n <header style={styles.header}>\n <h1 style={styles.title}>Project Navigator</h1>\n <p style={styles.description}>\n Browse and access your project files via the external file manager.\n </p>\n </header>\n\n {isConfigMissing && (\n <div style={styles.configWarning}>\n <svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"12\" cy=\"12\" r=\"10\"/><line x1=\"12\" y1=\"8\" x2=\"12\" y2=\"12\"/><line x1=\"12\" y1=\"16\" x2=\"12.01\" y2=\"16\"/>\n </svg>\n <span>\n <strong>Configuration needed:</strong> The File Browser Base URL is not set. Please contact your administrator.\n </span>\n </div>\n )}\n\n <div style={styles.toolbar}>\n <div style={styles.searchWrapper}>\n <div style={styles.searchIcon}>\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"11\" cy=\"11\" r=\"8\"/><line x1=\"21\" y1=\"21\" x2=\"16.65\" y2=\"16.65\"/>\n </svg>\n </div>\n <input\n type=\"text\"\n placeholder=\"Search projects...\"\n style={styles.searchInput}\n value={searchTerm}\n onChange={(e) => setSearchTerm(e.target.value)}\n />\n </div>\n <button \n onClick={() => refresh()} \n style={{...styles.button, ...styles.disabledButton, cursor: 'pointer'}}\n title=\"Refresh list\"\n >\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" style={{marginRight: '8px'}}>\n <path d=\"M23 4v6h-6M1 20v-6h6M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15\"/>\n </svg>\n Refresh\n </button>\n </div>\n\n <div style={styles.tableCard}>\n <div style={styles.tableHeader}>\n <span>Project</span>\n <span style={{textAlign: 'right'}}>Action</span>\n </div>\n\n {loading && (\n <div style={styles.emptyState}>\n <p>Loading projects...</p>\n </div>\n )}\n\n {!loading && error && (\n <div style={{...styles.emptyState, color: COLORS.danger}}>\n <p>Error loading projects: {error.message}</p>\n </div>\n )}\n\n {!loading && !error && filteredProjects.length === 0 && (\n <div style={styles.emptyState}>\n <p>{searchTerm ? 'No projects match your search.' : 'No projects found.'}</p>\n </div>\n )}\n\n {!loading && !error && filteredProjects.map((project) => (\n <div key={project.id} style={styles.row}>\n <div style={styles.projectMain}>\n <span style={styles.projectName}>{project.name}</span>\n {project.path && (\n <span style={styles.projectPath} title={project.path}>\n {project.path}\n </span>\n )}\n </div>\n <div style={styles.actionCell}>\n {project.fileBrowserUrl ? (\n <a\n href={project.fileBrowserUrl}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n style={{...styles.button, ...styles.primaryButton}}\n >\n Open Files\n </a>\n ) : (\n <span style={{...styles.button, ...styles.disabledButton}} title=\"No workspace available\">\n Unavailable\n </span>\n )}\n </div>\n </div>\n ))}\n </div>\n </div>\n );\n}\n\n"],
|
|
5
|
+
"mappings": ";AAAA,SAAS,UAAU,eAAe;AAElC,SAAS,qBAAqB;AAiMxB,SACE,KADF;AAxLN,IAAM,SAAS;AAAA,EACb,QAAQ;AAAA,EACR,IAAI;AAAA,EACJ,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,eAAe;AAAA,EACf,WAAW;AAAA,EACX,SAAS;AAAA,EACT,cAAc;AAAA,EACd,QAAQ;AAAA,EACR,WAAW;AAAA,EACX,eAAe;AAAA,EACf,aAAa;AACf;AAEA,IAAM,SAAS;AAAA,EACb,WAAW;AAAA,IACT,YAAY;AAAA,IACZ,SAAS;AAAA,IACT,UAAU;AAAA,IACV,QAAQ;AAAA,IACR,OAAO,OAAO;AAAA,EAChB;AAAA,EACA,QAAQ;AAAA,IACN,cAAc;AAAA,EAChB;AAAA,EACA,OAAO;AAAA,IACL,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,QAAQ;AAAA,EACV;AAAA,EACA,aAAa;AAAA,IACX,UAAU;AAAA,IACV,OAAO,OAAO;AAAA,IACd,QAAQ;AAAA,EACV;AAAA,EACA,SAAS;AAAA,IACP,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,cAAc;AAAA,IACd,KAAK;AAAA,EACP;AAAA,EACA,eAAe;AAAA,IACb,UAAU;AAAA,IACV,MAAM;AAAA,EACR;AAAA,EACA,aAAa;AAAA,IACX,OAAO;AAAA,IACP,SAAS;AAAA,IACT,UAAU;AAAA,IACV,cAAc;AAAA,IACd,QAAQ,aAAa,OAAO,MAAM;AAAA,IAClC,SAAS;AAAA,IACT,YAAY;AAAA,EACd;AAAA,EACA,YAAY;AAAA,IACV,UAAU;AAAA,IACV,MAAM;AAAA,IACN,KAAK;AAAA,IACL,WAAW;AAAA,IACX,OAAO,OAAO;AAAA,IACd,eAAe;AAAA,EACjB;AAAA,EACA,eAAe;AAAA,IACb,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,KAAK;AAAA,IACL,SAAS;AAAA,IACT,iBAAiB,OAAO;AAAA,IACxB,QAAQ,aAAa,OAAO,aAAa;AAAA,IACzC,cAAc;AAAA,IACd,cAAc;AAAA,IACd,UAAU;AAAA,IACV,OAAO,OAAO;AAAA,EAChB;AAAA,EACA,WAAW;AAAA,IACT,iBAAiB,OAAO;AAAA,IACxB,QAAQ,aAAa,OAAO,MAAM;AAAA,IAClC,cAAc;AAAA,IACd,UAAU;AAAA,IACV,WAAW;AAAA,EACb;AAAA,EACA,aAAa;AAAA,IACX,SAAS;AAAA,IACT,qBAAqB;AAAA,IACrB,SAAS;AAAA,IACT,iBAAiB,OAAO;AAAA,IACxB,cAAc,aAAa,OAAO,MAAM;AAAA,IACxC,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,eAAe;AAAA,IACf,eAAe;AAAA,IACf,OAAO,OAAO;AAAA,EAChB;AAAA,EACA,KAAK;AAAA,IACH,SAAS;AAAA,IACT,qBAAqB;AAAA,IACrB,SAAS;AAAA,IACT,cAAc,aAAa,OAAO,MAAM;AAAA,IACxC,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,eAAe;AAAA,MACb,cAAc;AAAA,IAChB;AAAA,EACF;AAAA,EACA,aAAa;AAAA,IACX,SAAS;AAAA,IACT,eAAe;AAAA,IACf,KAAK;AAAA,IACL,UAAU;AAAA,EACZ;AAAA,EACA,aAAa;AAAA,IACX,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,OAAO,OAAO;AAAA,EAChB;AAAA,EACA,aAAa;AAAA,IACX,UAAU;AAAA,IACV,OAAO,OAAO;AAAA,IACd,YAAY;AAAA,IACZ,YAAY;AAAA,IACZ,UAAU;AAAA,IACV,cAAc;AAAA,EAChB;AAAA,EACA,YAAY;AAAA,IACV,SAAS;AAAA,IACT,gBAAgB;AAAA,EAClB;AAAA,EACA,QAAQ;AAAA,IACN,SAAS;AAAA,IACT,cAAc;AAAA,IACd,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,QAAQ;AAAA,IACR,gBAAgB;AAAA,IAChB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,gBAAgB;AAAA,IAChB,YAAY;AAAA,IACZ,QAAQ;AAAA,EACV;AAAA,EACA,eAAe;AAAA,IACb,iBAAiB,OAAO;AAAA,IACxB,OAAO;AAAA,EACT;AAAA,EACA,gBAAgB;AAAA,IACd,iBAAiB,OAAO;AAAA,IACxB,OAAO,OAAO;AAAA,IACd,QAAQ;AAAA,IACR,QAAQ,aAAa,OAAO,MAAM;AAAA,EACpC;AAAA,EACA,YAAY;AAAA,IACV,SAAS;AAAA,IACT,WAAW;AAAA,IACX,OAAO,OAAO;AAAA,EAChB;AACF;AAEO,SAAS,cAAc,EAAE,QAAQ,GAAoB;AAC1D,QAAM,CAAC,YAAY,aAAa,IAAI,SAAS,EAAE;AAC/C,QAAM,EAAE,MAAM,SAAS,OAAO,QAAQ,IAAI,cAA8B,YAAY;AAAA,IAClF,WAAW,QAAQ,aAAa;AAAA,EAClC,CAAC;AAED,QAAM,WAAW,QAAQ,CAAC;AAE1B,QAAM,mBAAmB,QAAQ,MAAM;AACrC,QAAI,CAAC,WAAY,QAAO;AACxB,UAAM,QAAQ,WAAW,YAAY;AACrC,WAAO,SAAS;AAAA,MAAO,OACrB,EAAE,KAAK,YAAY,EAAE,SAAS,KAAK,KAClC,EAAE,QAAQ,EAAE,KAAK,YAAY,EAAE,SAAS,KAAK;AAAA,IAChD;AAAA,EACF,GAAG,CAAC,UAAU,UAAU,CAAC;AAGzB,QAAM,kBAAkB,QAAQ,MAAM;AACpC,WAAO,SAAS,SAAS,KAAK,SAAS,MAAM,OAAK,EAAE,mBAAmB,QAAQ,EAAE,SAAS,IAAI;AAAA,EAChG,GAAG,CAAC,QAAQ,CAAC;AAEb,SACE,qBAAC,SAAI,OAAO,OAAO,WACjB;AAAA,yBAAC,YAAO,OAAO,OAAO,QACpB;AAAA,0BAAC,QAAG,OAAO,OAAO,OAAO,+BAAiB;AAAA,MAC1C,oBAAC,OAAE,OAAO,OAAO,aAAa,iFAE9B;AAAA,OACF;AAAA,IAEC,mBACC,qBAAC,SAAI,OAAO,OAAO,eACjB;AAAA,2BAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACrI;AAAA,4BAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,MAAI;AAAA,QAAE,oBAAC,UAAK,IAAG,MAAK,IAAG,KAAI,IAAG,MAAK,IAAG,MAAI;AAAA,QAAE,oBAAC,UAAK,IAAG,MAAK,IAAG,MAAK,IAAG,SAAQ,IAAG,MAAI;AAAA,SAChH;AAAA,MACA,qBAAC,UACC;AAAA,4BAAC,YAAO,mCAAqB;AAAA,QAAS;AAAA,SACxC;AAAA,OACF;AAAA,IAGF,qBAAC,SAAI,OAAO,OAAO,SACjB;AAAA,2BAAC,SAAI,OAAO,OAAO,eACjB;AAAA,4BAAC,SAAI,OAAO,OAAO,YACjB,+BAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SACrI;AAAA,8BAAC,YAAO,IAAG,MAAK,IAAG,MAAK,GAAE,KAAG;AAAA,UAAE,oBAAC,UAAK,IAAG,MAAK,IAAG,MAAK,IAAG,SAAQ,IAAG,SAAO;AAAA,WAC5E,GACF;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,MAAK;AAAA,YACL,aAAY;AAAA,YACZ,OAAO,OAAO;AAAA,YACd,OAAO;AAAA,YACP,UAAU,CAAC,MAAM,cAAc,EAAE,OAAO,KAAK;AAAA;AAAA,QAC/C;AAAA,SACF;AAAA,MACA;AAAA,QAAC;AAAA;AAAA,UACC,SAAS,MAAM,QAAQ;AAAA,UACvB,OAAO,EAAC,GAAG,OAAO,QAAQ,GAAG,OAAO,gBAAgB,QAAQ,UAAS;AAAA,UACrE,OAAM;AAAA,UAEN;AAAA,gCAAC,SAAI,OAAM,MAAK,QAAO,MAAK,SAAQ,aAAY,MAAK,QAAO,QAAO,gBAAe,aAAY,KAAI,eAAc,SAAQ,gBAAe,SAAQ,OAAO,EAAC,aAAa,MAAK,GACvK,8BAAC,UAAK,GAAE,4FAA0F,GACpG;AAAA,YAAM;AAAA;AAAA;AAAA,MAER;AAAA,OACF;AAAA,IAEA,qBAAC,SAAI,OAAO,OAAO,WACjB;AAAA,2BAAC,SAAI,OAAO,OAAO,aACjB;AAAA,4BAAC,UAAK,qBAAO;AAAA,QACb,oBAAC,UAAK,OAAO,EAAC,WAAW,QAAO,GAAG,oBAAM;AAAA,SAC3C;AAAA,MAEC,WACC,oBAAC,SAAI,OAAO,OAAO,YACjB,8BAAC,OAAE,iCAAmB,GACxB;AAAA,MAGD,CAAC,WAAW,SACX,oBAAC,SAAI,OAAO,EAAC,GAAG,OAAO,YAAY,OAAO,OAAO,OAAM,GACrD,+BAAC,OAAE;AAAA;AAAA,QAAyB,MAAM;AAAA,SAAQ,GAC5C;AAAA,MAGD,CAAC,WAAW,CAAC,SAAS,iBAAiB,WAAW,KACjD,oBAAC,SAAI,OAAO,OAAO,YACjB,8BAAC,OAAG,uBAAa,mCAAmC,sBAAqB,GAC3E;AAAA,MAGD,CAAC,WAAW,CAAC,SAAS,iBAAiB,IAAI,CAAC,YAC3C,qBAAC,SAAqB,OAAO,OAAO,KAClC;AAAA,6BAAC,SAAI,OAAO,OAAO,aACjB;AAAA,8BAAC,UAAK,OAAO,OAAO,aAAc,kBAAQ,MAAK;AAAA,UAC9C,QAAQ,QACP,oBAAC,UAAK,OAAO,OAAO,aAAa,OAAO,QAAQ,MAC7C,kBAAQ,MACX;AAAA,WAEJ;AAAA,QACA,oBAAC,SAAI,OAAO,OAAO,YAChB,kBAAQ,iBACP;AAAA,UAAC;AAAA;AAAA,YACC,MAAM,QAAQ;AAAA,YACd,QAAO;AAAA,YACP,KAAI;AAAA,YACJ,OAAO,EAAC,GAAG,OAAO,QAAQ,GAAG,OAAO,cAAa;AAAA,YAClD;AAAA;AAAA,QAED,IAEA,oBAAC,UAAK,OAAO,EAAC,GAAG,OAAO,QAAQ,GAAG,OAAO,eAAc,GAAG,OAAM,0BAAyB,yBAE1F,GAEJ;AAAA,WAxBQ,QAAQ,EAyBlB,CACD;AAAA,OACH;AAAA,KACF;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/worker.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strips the `/paperclip` prefix from an absolute workspace path and appends
|
|
3
|
+
* it to the operator-supplied filebrowser base URL.
|
|
4
|
+
*
|
|
5
|
+
* Example:
|
|
6
|
+
* fileBrowserBaseUrl = "https://files.example.com/files"
|
|
7
|
+
* workspacePath = "/paperclip/instances/default/projects/uuid/wid/name"
|
|
8
|
+
* result = "https://files.example.com/files/instances/default/projects/uuid/wid/name"
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildFileBrowserUrl(fileBrowserBaseUrl: string, workspacePath: string): string;
|
|
11
|
+
export interface ProjectEntry {
|
|
12
|
+
id: string;
|
|
13
|
+
name: string;
|
|
14
|
+
path: string | null;
|
|
15
|
+
fileBrowserUrl: string | null;
|
|
16
|
+
}
|
|
17
|
+
declare const plugin: import("@paperclipai/plugin-sdk").PaperclipPlugin;
|
|
18
|
+
export default plugin;
|
|
19
|
+
//# sourceMappingURL=worker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAIA;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,kBAAkB,EAAE,MAAM,EAC1B,aAAa,EAAE,MAAM,GACpB,MAAM,CAMR;AAED,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAED,QAAA,MAAM,MAAM,mDA8GV,CAAC;AAEH,eAAe,MAAM,CAAC"}
|
package/dist/worker.js
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { definePlugin, runWorker } from "@paperclipai/plugin-sdk";
|
|
2
|
+
const PLUGIN_NAME = "paperclip-plugin-navigator";
|
|
3
|
+
/**
|
|
4
|
+
* Strips the `/paperclip` prefix from an absolute workspace path and appends
|
|
5
|
+
* it to the operator-supplied filebrowser base URL.
|
|
6
|
+
*
|
|
7
|
+
* Example:
|
|
8
|
+
* fileBrowserBaseUrl = "https://files.example.com/files"
|
|
9
|
+
* workspacePath = "/paperclip/instances/default/projects/uuid/wid/name"
|
|
10
|
+
* result = "https://files.example.com/files/instances/default/projects/uuid/wid/name"
|
|
11
|
+
*/
|
|
12
|
+
export function buildFileBrowserUrl(fileBrowserBaseUrl, workspacePath) {
|
|
13
|
+
// Strip the leading /paperclip segment because the filebrowser mounts at that root.
|
|
14
|
+
const relative = workspacePath.replace(/^\/paperclip/, "");
|
|
15
|
+
// Remove any trailing slash from the base URL to avoid double slashes.
|
|
16
|
+
const base = fileBrowserBaseUrl.replace(/\/$/, "");
|
|
17
|
+
return `${base}${relative}`;
|
|
18
|
+
}
|
|
19
|
+
const plugin = definePlugin({
|
|
20
|
+
async setup(ctx) {
|
|
21
|
+
ctx.logger.info(`${PLUGIN_NAME} plugin setup`);
|
|
22
|
+
/**
|
|
23
|
+
* "projects" data handler — called by the UI via usePluginData("projects").
|
|
24
|
+
*
|
|
25
|
+
* Returns an array of ProjectEntry objects enriched with the filebrowser URL
|
|
26
|
+
* for the primary workspace of each project.
|
|
27
|
+
*
|
|
28
|
+
* Robustness features:
|
|
29
|
+
* - Controlled Concurrency: Fetches workspaces in batches to avoid SDK/DB pressure.
|
|
30
|
+
* - Pagination: Supports 'limit' and 'offset' parameters.
|
|
31
|
+
* - Graceful Fallbacks: Handles missing config, missing workspaces, and fetch errors per project.
|
|
32
|
+
*/
|
|
33
|
+
ctx.data.register("projects", async (params) => {
|
|
34
|
+
const limit = typeof params?.limit === "number" ? params.limit : 50;
|
|
35
|
+
const offset = typeof params?.offset === "number" ? params.offset : 0;
|
|
36
|
+
const paramCompanyId = typeof params?.companyId === "string" ? params.companyId : null;
|
|
37
|
+
// 1. Resolve the operator config — we need fileBrowserBaseUrl.
|
|
38
|
+
const config = await ctx.config.get();
|
|
39
|
+
const fileBrowserBaseUrl = typeof config?.fileBrowserBaseUrl === "string" &&
|
|
40
|
+
config.fileBrowserBaseUrl.trim() !== ""
|
|
41
|
+
? config.fileBrowserBaseUrl.trim()
|
|
42
|
+
: null;
|
|
43
|
+
if (!fileBrowserBaseUrl) {
|
|
44
|
+
ctx.logger.warn(`${PLUGIN_NAME}: fileBrowserBaseUrl is not configured — fileBrowserUrl will be null`);
|
|
45
|
+
}
|
|
46
|
+
// 2. Get the companyId.
|
|
47
|
+
let companyId = paramCompanyId;
|
|
48
|
+
if (!companyId) {
|
|
49
|
+
const companies = await ctx.companies.list({ limit: 1 });
|
|
50
|
+
if (companies.length === 0) {
|
|
51
|
+
ctx.logger.warn(`${PLUGIN_NAME}: no companies found, returning []`);
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
companyId = companies[0].id;
|
|
55
|
+
}
|
|
56
|
+
// 3. List projects with pagination.
|
|
57
|
+
const projects = await ctx.projects.list({
|
|
58
|
+
companyId,
|
|
59
|
+
limit,
|
|
60
|
+
offset
|
|
61
|
+
});
|
|
62
|
+
// 4. Process projects in batches to fetch primary workspaces.
|
|
63
|
+
// This prevents thousands of simultaneous requests if projects list is large.
|
|
64
|
+
const BATCH_SIZE = 5;
|
|
65
|
+
const entries = [];
|
|
66
|
+
for (let i = 0; i < projects.length; i += BATCH_SIZE) {
|
|
67
|
+
const batch = projects.slice(i, i + BATCH_SIZE);
|
|
68
|
+
const batchResults = await Promise.all(batch.map(async (project) => {
|
|
69
|
+
try {
|
|
70
|
+
const workspace = await ctx.projects.getPrimaryWorkspace(project.id, companyId);
|
|
71
|
+
if (!workspace) {
|
|
72
|
+
return {
|
|
73
|
+
id: project.id,
|
|
74
|
+
name: project.name,
|
|
75
|
+
path: null,
|
|
76
|
+
fileBrowserUrl: null,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const fileBrowserUrl = fileBrowserBaseUrl
|
|
80
|
+
? buildFileBrowserUrl(fileBrowserBaseUrl, workspace.path)
|
|
81
|
+
: null;
|
|
82
|
+
return {
|
|
83
|
+
id: project.id,
|
|
84
|
+
name: project.name,
|
|
85
|
+
path: workspace.path,
|
|
86
|
+
fileBrowserUrl,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
catch (err) {
|
|
90
|
+
ctx.logger.error(`${PLUGIN_NAME}: failed to get workspace for project ${project.id}: ${String(err)}`);
|
|
91
|
+
return {
|
|
92
|
+
id: project.id,
|
|
93
|
+
name: project.name,
|
|
94
|
+
path: null,
|
|
95
|
+
fileBrowserUrl: null,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}));
|
|
99
|
+
entries.push(...batchResults);
|
|
100
|
+
}
|
|
101
|
+
return entries;
|
|
102
|
+
});
|
|
103
|
+
},
|
|
104
|
+
async onHealth() {
|
|
105
|
+
return { status: "ok", message: `${PLUGIN_NAME} ready` };
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
export default plugin;
|
|
109
|
+
runWorker(plugin, import.meta.url);
|
|
110
|
+
//# sourceMappingURL=worker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "paperclip-plugin-navigator",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Project-aware file navigator for Paperclip. Browse projects with real names and open them in your external filebrowser.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"private": false,
|
|
7
|
+
"license": "MIT",
|
|
8
|
+
"author": "Ricardo Castro",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"paperclip",
|
|
11
|
+
"plugin",
|
|
12
|
+
"navigator",
|
|
13
|
+
"filebrowser"
|
|
14
|
+
],
|
|
15
|
+
"files": [
|
|
16
|
+
"dist"
|
|
17
|
+
],
|
|
18
|
+
"paperclipPlugin": {
|
|
19
|
+
"manifest": "./dist/manifest.js",
|
|
20
|
+
"worker": "./dist/worker.js",
|
|
21
|
+
"ui": "./dist/ui/"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc && node ./scripts/build-ui.mjs",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"test:coverage": "vitest run --coverage",
|
|
28
|
+
"typecheck": "tsc --noEmit",
|
|
29
|
+
"prepublishOnly": "npm run test && npm run build"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"@paperclipai/plugin-sdk": "2026.428.0"
|
|
33
|
+
},
|
|
34
|
+
"devDependencies": {
|
|
35
|
+
"@types/node": "^24.0.0",
|
|
36
|
+
"@types/react": "^19.0.0",
|
|
37
|
+
"@types/react-dom": "^19.0.0",
|
|
38
|
+
"@vitest/coverage-v8": "^2.0.0",
|
|
39
|
+
"@testing-library/react": "^16.0.0",
|
|
40
|
+
"@testing-library/jest-dom": "^6.0.0",
|
|
41
|
+
"esbuild": "^0.27.0",
|
|
42
|
+
"jsdom": "^25.0.0",
|
|
43
|
+
"react": "^19.0.0",
|
|
44
|
+
"react-dom": "^19.0.0",
|
|
45
|
+
"typescript": "^5.7.0",
|
|
46
|
+
"vitest": "^2.0.0"
|
|
47
|
+
},
|
|
48
|
+
"peerDependencies": {
|
|
49
|
+
"react": ">=18"
|
|
50
|
+
},
|
|
51
|
+
"pnpm": {
|
|
52
|
+
"onlyBuiltDependencies": [
|
|
53
|
+
"esbuild"
|
|
54
|
+
]
|
|
55
|
+
}
|
|
56
|
+
}
|