anywork-library-plugin 0.0.0-stage → 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/lib/client.js ADDED
@@ -0,0 +1,632 @@
1
+ /* Generated by scripts/assemble.mjs. Edit lib/api.js and lib/panel.js, then npm run assemble. */
2
+ window.__ModuleLoader__.load({
3
+ id: "anywork-library-plugin",
4
+ factory: (require) => {
5
+ var module = { exports: {} };
6
+ var exports = module.exports;
7
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
8
+ var React = require("react");
9
+
10
+ // Portal client for 资料库. Drive and notes stay on the Anywork host.
11
+
12
+ const MAX_UPLOAD = 50 * 1024 * 1024;
13
+
14
+ function canDeleteNote(role) {
15
+ return role === "admin";
16
+ }
17
+
18
+ function portalErrorText(err) {
19
+ var msg = err && err.message ? String(err.message) : String(err || "");
20
+ var status = err && err.status;
21
+ if (status === 401 || /login required/i.test(msg)) {
22
+ return "读不到资料库(需要门户登录)。请从门户地址打开工作台后再试。";
23
+ }
24
+ return msg || "读取失败";
25
+ }
26
+
27
+ function readBody(response) {
28
+ return response.text().then(function (text) {
29
+ var data = null;
30
+ if (text) {
31
+ try { data = JSON.parse(text); } catch (e) { data = null; }
32
+ }
33
+ if (!response.ok) {
34
+ var err = new Error((data && data.error) || text || ("HTTP " + response.status));
35
+ err.status = response.status;
36
+ throw err;
37
+ }
38
+ return data == null ? {} : data;
39
+ });
40
+ }
41
+
42
+ function createPortalApi(getFetch) {
43
+ function doFetch(url, options) {
44
+ var fetchImpl = typeof getFetch === "function" ? getFetch() : getFetch;
45
+ if (typeof fetchImpl !== "function") {
46
+ var err = new Error("fetch 不可用");
47
+ err.status = 0;
48
+ return Promise.reject(err);
49
+ }
50
+ var opts = options || {};
51
+ opts.credentials = opts.credentials || "same-origin";
52
+ return Promise.resolve(fetchImpl(url, opts)).then(readBody);
53
+ }
54
+
55
+ return {
56
+ listDrive: function (path) {
57
+ return doFetch("/portal/api/drive/list?path=" + encodeURIComponent(path || ""), {
58
+ headers: { accept: "application/json" },
59
+ });
60
+ },
61
+ upload: function (path, file) {
62
+ return doFetch(
63
+ "/portal/api/drive/upload?path=" + encodeURIComponent(path || "") + "&name=" + encodeURIComponent(file.name),
64
+ { method: "POST", body: file, headers: { accept: "application/json" } }
65
+ );
66
+ },
67
+ mkdir: function (path, name) {
68
+ return doFetch("/portal/api/drive/mkdir", {
69
+ method: "POST",
70
+ headers: { "content-type": "application/json", accept: "application/json" },
71
+ body: JSON.stringify({ path: path || "", name: name }),
72
+ });
73
+ },
74
+ rm: function (path) {
75
+ return doFetch("/portal/api/drive/rm", {
76
+ method: "POST",
77
+ headers: { "content-type": "application/json", accept: "application/json" },
78
+ body: JSON.stringify({ path: path }),
79
+ });
80
+ },
81
+ mv: function (from, to) {
82
+ return doFetch("/portal/api/drive/mv", {
83
+ method: "POST",
84
+ headers: { "content-type": "application/json", accept: "application/json" },
85
+ body: JSON.stringify({ from: from, to: to || "" }),
86
+ });
87
+ },
88
+ downloadUrl: function (path) {
89
+ return "/portal/api/drive/download?path=" + encodeURIComponent(path);
90
+ },
91
+ listNotes: function () {
92
+ return doFetch("/portal/api/kb/list", { headers: { accept: "application/json" } });
93
+ },
94
+ searchNotes: function (q) {
95
+ return doFetch("/portal/api/kb/search?q=" + encodeURIComponent(q), { headers: { accept: "application/json" } });
96
+ },
97
+ readNote: function (name) {
98
+ return doFetch("/portal/api/kb/note?name=" + encodeURIComponent(name), { headers: { accept: "application/json" } });
99
+ },
100
+ saveNote: function (input, lang) {
101
+ return doFetch("/portal/api/kb/save?lang=" + encodeURIComponent(lang || "zh"), {
102
+ method: "POST",
103
+ headers: { "content-type": "application/json", accept: "application/json" },
104
+ body: JSON.stringify({
105
+ title: input.title || "",
106
+ tags: input.tags || "",
107
+ content: input.content || "",
108
+ }),
109
+ });
110
+ },
111
+ removeNote: function (name, lang) {
112
+ return doFetch("/portal/api/kb/rm?lang=" + encodeURIComponent(lang || "zh"), {
113
+ method: "POST",
114
+ headers: { "content-type": "application/json", accept: "application/json" },
115
+ body: JSON.stringify({ name: name }),
116
+ });
117
+ },
118
+ };
119
+ }
120
+
121
+ var inject = ["slots"];
122
+ var ZLK_SEQ = 0;
123
+
124
+ var ZLK_CSS = `
125
+ .zlk-nav { display:flex; align-items:center; gap:10px; width:100%; padding:7px 10px; border:0; border-radius:10px; background:transparent; color:inherit; cursor:pointer; font:400 14px/22px -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Noto Sans SC", "Microsoft YaHei", sans-serif; text-align:left; }
126
+ .zlk-nav:hover { background:rgba(127,127,127,.12); }
127
+ .zlk-nav.on { background:rgba(79,124,247,.13); }
128
+ .zlk-nav .ic { width:16px; height:16px; display:flex; align-items:center; justify-content:center; flex:0 0 auto; }
129
+ .zlk-nav .lbl { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
130
+ .zlk-fv { position:fixed; left:var(--zlk-fv-left, 0px); top:0; right:0; bottom:0; z-index:9999; display:flex; flex-direction:column; background:#e6e6e6; color:#1c1e21; overflow:auto; font-family:-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Noto Sans SC", "Microsoft YaHei", sans-serif; }
131
+ .zlk-hd { position:sticky; top:0; z-index:2; display:flex; align-items:center; gap:12px; padding:16px 16px 14px; background:#fff; }
132
+ .zlk-mark { width:44px; height:44px; border-radius:12px; display:flex; align-items:center; justify-content:center; background:#fff; border:1px solid #e4e4e4; color:#4b5563; flex:0 0 auto; }
133
+ .zlk-hd-text { flex:1; min-width:0; }
134
+ .zlk-t1 { font-size:18px; font-weight:700; line-height:1.3; }
135
+ .zlk-t2 { margin-top:3px; font-size:12px; line-height:1.45; color:#8b919a; }
136
+ .zlk-back { margin-left:auto; flex:0 0 auto; height:34px; padding:0 14px; border:1px solid #d5d5d5; border-radius:999px; background:#fff; color:#3a3a3a; font:500 12.5px/1 inherit; cursor:pointer; white-space:nowrap; }
137
+ .zlk-back:hover { background:#f6f6f6; }
138
+ .zlk-bd { padding:14px 16px 72px; }
139
+ .zlk-tabs { display:flex; gap:4px; padding:4px; margin:0 0 14px; background:#fff; border-radius:12px; box-shadow:0 1px 2px rgba(16,24,40,.04); }
140
+ .zlk-tab { flex:1; display:flex; align-items:center; justify-content:center; gap:6px; height:36px; border:0; border-radius:9px; background:transparent; color:#6b7280; font:500 14px/1 inherit; cursor:pointer; }
141
+ .zlk-tab.on { background:#fff; color:#1c1e21; font-weight:650; box-shadow:0 1px 3px rgba(16,24,40,.14); }
142
+ .zlk-hint { margin:0 2px 12px; font-size:13px; line-height:1.65; color:#8b9098; }
143
+ .zlk-row { display:flex; gap:8px; align-items:center; margin-top:12px; flex-wrap:wrap; }
144
+ .zlk-inp { width:100%; min-width:0; height:40px; box-sizing:border-box; border:1px solid #e1e1e1; border-radius:10px; padding:0 12px; background:#fff; color:inherit; font:400 14px/1.4 inherit; outline:none; }
145
+ .zlk-inp::placeholder { color:#c5c8ce; }
146
+ .zlk-inp:focus { border-color:#4f7cf7; box-shadow:0 0 0 3px rgba(79,124,247,.15); }
147
+ .zlk-ta { height:auto; min-height:88px; padding:10px 12px; resize:vertical; }
148
+ .zlk-search { flex:1; }
149
+ .zlk-btn { height:36px; padding:0 16px; border:0; border-radius:10px; background:#3478f6; color:#fff; font:600 14px/1 inherit; cursor:pointer; }
150
+ .zlk-btn:hover { background:#2d6ae0; }
151
+ .zlk-btn.gh { background:#fff; border:1px solid #e1e1e1; color:#3a3a3a; font-weight:500; }
152
+ .zlk-btn.gh:hover { background:#f7f7f7; }
153
+ .zlk-form { display:flex; flex-direction:column; gap:8px; margin-top:12px; }
154
+ .zlk-msg { margin-top:8px; font-size:12.5px; color:#8b9098; }
155
+ .zlk-msg.ok { color:#248a3d; }
156
+ .zlk-msg.err { color:#d64545; }
157
+ .zlk-it { display:flex; gap:10px; align-items:flex-start; padding:8px 2px 14px; }
158
+ .zlk-ico { width:36px; height:36px; border-radius:10px; display:flex; align-items:center; justify-content:center; background:#fff; border:1px solid #ececec; color:#6b7280; flex:0 0 auto; font-size:16px; }
159
+ .zlk-gr { flex:1; min-width:0; }
160
+ .zlk-top { display:flex; align-items:flex-start; gap:10px; }
161
+ .zlk-title { flex:1; min-width:0; font-size:15px; font-weight:650; line-height:1.45; color:#222; word-break:break-word; }
162
+ .zlk-title.link { cursor:pointer; }
163
+ .zlk-meta { margin-top:6px; font-size:13px; line-height:1.4; color:#9aa0a6; }
164
+ .zlk-acts { display:flex; flex-direction:column; gap:8px; flex:0 0 auto; }
165
+ .zlk-acts.row { flex-direction:row; flex-wrap:wrap; justify-content:flex-end; }
166
+ .zlk-mini { min-width:64px; height:32px; padding:0 12px; border:1px solid #d8d8d8; border-radius:8px; background:#fff; color:#3a3a3a; font:400 13px/1 inherit; cursor:pointer; text-decoration:none; display:inline-flex; align-items:center; justify-content:center; box-sizing:border-box; }
167
+ .zlk-mini:hover { background:#f7f7f7; }
168
+ .zlk-body { margin-top:10px; padding:10px 12px; border-radius:10px; background:#fff; border:1px solid #ececec; font-size:13.5px; line-height:1.65; white-space:pre-wrap; word-break:break-word; color:#333; }
169
+ .zlk-empty { margin-top:12px; padding:18px 14px; border:1.5px dashed #cfd3d8; border-radius:12px; background:#f7f8fa; color:#4f7cf7; cursor:pointer; }
170
+ .zlk-empty b { display:block; font-size:14px; }
171
+ .zlk-empty span { display:block; margin-top:4px; font-size:12px; color:#8b9098; font-weight:400; }
172
+ .zlk-file { position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); }
173
+ .zlk-bc { font-size:13px; margin:2px 0 4px; color:#4b5563; }
174
+ .zlk-bc .lk { color:#3478f6; cursor:pointer; }
175
+ .zlk-drop { margin-top:12px; border:1.5px dashed #cfd3d8; border-radius:12px; background:#f7f8fa; text-align:center; padding:14px 10px; }
176
+ .zlk-drop.on { border-color:#3478f6; background:#f3f7ff; }
177
+ .zlk-drop b { display:block; color:#3478f6; font-size:14px; }
178
+ .zlk-drop span { display:block; margin-top:4px; font-size:12px; color:#8b9098; }
179
+ .zlk-pick { margin-top:12px; padding:12px; border-radius:12px; background:#fff; border:1px solid #e6e6e6; }
180
+ `;
181
+
182
+ function ensureCss() {
183
+ if (typeof document === "undefined") return;
184
+ if (document.getElementById("library-plugin-css")) return;
185
+ var st = document.createElement("style");
186
+ st.id = "library-plugin-css";
187
+ st.textContent = ZLK_CSS;
188
+ (document.head || document.documentElement).appendChild(st);
189
+ }
190
+
191
+ function h(type, props) {
192
+ var args = [type, props];
193
+ for (var i = 2; i < arguments.length; i++) args.push(arguments[i]);
194
+ return React.createElement.apply(React, args);
195
+ }
196
+
197
+ function svg(size) {
198
+ var props = {
199
+ viewBox: "0 0 24 24", width: size, height: size, fill: "none", stroke: "currentColor",
200
+ strokeWidth: 1.7, strokeLinecap: "round", strokeLinejoin: "round", "aria-hidden": "true",
201
+ };
202
+ var args = ["svg", props];
203
+ for (var i = 1; i < arguments.length; i++) args.push(arguments[i]);
204
+ return h.apply(null, args);
205
+ }
206
+
207
+ function iconFolder(size) {
208
+ return svg(size, h("path", { d: "M3.5 7.5A1.5 1.5 0 0 1 5 6h4l2 2h8a1.5 1.5 0 0 1 1.5 1.5v8A1.5 1.5 0 0 1 19 19H5a1.5 1.5 0 0 1-1.5-1.5v-10z" }));
209
+ }
210
+
211
+ function measureFv() {
212
+ var left = 0;
213
+ try {
214
+ if (!(window.matchMedia && window.matchMedia("(max-width: 820px)").matches)) {
215
+ var sc = document.querySelector('[class*="sidebarCol"]');
216
+ if (sc) {
217
+ var r = sc.getBoundingClientRect();
218
+ if (r && r.right > 40 && r.right < window.innerWidth * 0.6) left = Math.round(r.right);
219
+ }
220
+ }
221
+ document.documentElement.style.setProperty("--zlk-fv-left", left + "px");
222
+ } catch (e) { /* ignore */ }
223
+ }
224
+
225
+ function notifyFv(id) {
226
+ try { window.dispatchEvent(new CustomEvent("desk-fv", { detail: { id: id } })); } catch (e) { /* ignore */ }
227
+ }
228
+
229
+ function deskLang() {
230
+ try {
231
+ var lang = (document.documentElement && document.documentElement.lang) || (navigator.language || "zh");
232
+ return String(lang).toLowerCase().indexOf("en") === 0 ? "en" : "zh";
233
+ } catch (e) { return "zh"; }
234
+ }
235
+
236
+ function formatSize(n) {
237
+ n = Number(n) || 0;
238
+ if (n < 1024) return n + " B";
239
+ if (n < 1048576) return (n / 1024).toFixed(n < 10240 ? 1 : 0) + " KB";
240
+ return (n / 1048576).toFixed(1) + " MB";
241
+ }
242
+
243
+ function formatWhen(value) {
244
+ var m = /^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2})/.exec(String(value || ""));
245
+ if (m) return m[1] + " " + m[2];
246
+ return String(value || "");
247
+ }
248
+
249
+ function joinPath(dir, name) {
250
+ return dir ? dir + "/" + name : name;
251
+ }
252
+
253
+ function LibraryPanel(props) {
254
+ var wide = !!(props && props.wide);
255
+ var openPair = React.useState(false);
256
+ var open = openPair[0];
257
+ var setOpen = openPair[1];
258
+ var tabPair = React.useState("notes");
259
+ var tab = tabPair[0];
260
+ var setTab = tabPair[1];
261
+ var fsPair = React.useState({ phase: "idle", path: "", entries: [], message: "", newFolder: false, pick: null, drag: false });
262
+ var fs = fsPair[0];
263
+ var setFs = fsPair[1];
264
+ var nsPair = React.useState({ phase: "idle", role: "member", notes: [], q: "", hits: [], message: "", expanded: "", content: "", composing: false });
265
+ var ns = nsPair[0];
266
+ var setNs = nsPair[1];
267
+ var apiRef = React.useRef(null);
268
+ var idRef = React.useRef("");
269
+ var openRef = React.useRef(false);
270
+ var fileRef = React.useRef(null);
271
+ var qRef = React.useRef(null);
272
+ var titleRef = React.useRef(null);
273
+ var bodyRef = React.useRef(null);
274
+ var folderRef = React.useRef(null);
275
+ var pathRef = React.useRef("");
276
+ if (!idRef.current) idRef.current = "ziliaoku-" + (++ZLK_SEQ);
277
+ if (!apiRef.current) apiRef.current = createPortalApi(function () { return window.fetch.bind(window); });
278
+ openRef.current = open;
279
+ pathRef.current = fs.path;
280
+
281
+ function patchFs(p) { setFs(function (prev) { return Object.assign({}, prev, p); }); }
282
+ function patchNs(p) { setNs(function (prev) { return Object.assign({}, prev, p); }); }
283
+
284
+ function loadFiles(path) {
285
+ var p = String(path == null ? "" : path);
286
+ patchFs({ phase: "loading", path: p, entries: [], message: "", pick: null, newFolder: false });
287
+ apiRef.current.listDrive(p).then(function (d) {
288
+ patchFs({ phase: "ready", path: d.path != null ? d.path : p, entries: d.entries || [], message: "" });
289
+ }).catch(function (e) {
290
+ patchFs({ phase: "error", message: portalErrorText(e) });
291
+ });
292
+ }
293
+
294
+ function loadNotes() {
295
+ patchNs({ phase: "loading", message: "" });
296
+ apiRef.current.listNotes().then(function (d) {
297
+ patchNs({ phase: "ready", role: d.role || "member", notes: d.notes || [], message: "", tone: "" });
298
+ }).catch(function (e) {
299
+ patchNs({ phase: "error", tone: "err", message: portalErrorText(e) });
300
+ });
301
+ }
302
+
303
+ React.useEffect(function () {
304
+ function onFv(e) {
305
+ var who = e && e.detail ? e.detail.id : null;
306
+ if (who !== idRef.current) setOpen(false);
307
+ }
308
+ function onKey(e) { if (e.key === "Escape" && openRef.current) setOpen(false); }
309
+ function onDown(e) {
310
+ if (!openRef.current) return;
311
+ var t = e.target;
312
+ if (t && t.closest && (t.closest(".zlk-fv") || t.closest(".zlk-nav"))) return;
313
+ setOpen(false);
314
+ }
315
+ window.addEventListener("desk-fv", onFv);
316
+ document.addEventListener("keydown", onKey);
317
+ document.addEventListener("pointerdown", onDown, true);
318
+ return function () {
319
+ window.removeEventListener("desk-fv", onFv);
320
+ document.removeEventListener("keydown", onKey);
321
+ document.removeEventListener("pointerdown", onDown, true);
322
+ };
323
+ }, []);
324
+
325
+ React.useEffect(function () {
326
+ if (!open) return;
327
+ measureFv();
328
+ notifyFv(idRef.current);
329
+ loadFiles("");
330
+ loadNotes();
331
+ }, [open]);
332
+
333
+ function toggle() { setOpen(function (v) { return !v; }); }
334
+ function close() { setOpen(false); }
335
+
336
+ function uploadFiles(list) {
337
+ var files = [];
338
+ if (list) for (var i = 0; i < list.length; i++) files.push(list[i]);
339
+ var cursor = 0;
340
+ function next() {
341
+ if (cursor >= files.length) { loadFiles(pathRef.current); return; }
342
+ var f = files[cursor++];
343
+ if (f.size > MAX_UPLOAD) {
344
+ patchFs({ message: "上传失败:" + f.name + " 超过 50MB" });
345
+ next();
346
+ return;
347
+ }
348
+ patchFs({ message: "上传中:" + f.name + " …" });
349
+ apiRef.current.upload(pathRef.current, f).then(next).catch(function (e) {
350
+ patchFs({ message: "上传失败:" + portalErrorText(e) });
351
+ });
352
+ }
353
+ if (files.length) next();
354
+ }
355
+
356
+ function createFolder() {
357
+ var name = folderRef.current ? String(folderRef.current.value || "").trim() : "";
358
+ if (!name) { if (folderRef.current) folderRef.current.focus(); return; }
359
+ apiRef.current.mkdir(fs.path, name).then(function () { loadFiles(fs.path); }).catch(function (e) {
360
+ patchFs({ message: portalErrorText(e) });
361
+ });
362
+ }
363
+
364
+ function removeEntry(full, isDir) {
365
+ var label = (isDir ? "删除文件夹「" : "删除文件「") + full.split("/").pop() + "」?此操作不可恢复。";
366
+ if (window.confirm && !window.confirm(label)) return;
367
+ apiRef.current.rm(full).then(function () { loadFiles(fs.path); }).catch(function (e) {
368
+ patchFs({ message: portalErrorText(e) });
369
+ });
370
+ }
371
+
372
+ function openPicker(name) {
373
+ patchFs({ pick: { name: name, path: "", entries: [] } });
374
+ apiRef.current.listDrive("").then(function (d) {
375
+ patchFs({ pick: { name: name, path: d.path != null ? d.path : "", entries: d.entries || [] } });
376
+ }).catch(function (e) { patchFs({ message: portalErrorText(e), pick: null }); });
377
+ }
378
+
379
+ function pickerGo(path) {
380
+ apiRef.current.listDrive(path).then(function (d) {
381
+ setFs(function (prev) {
382
+ var pick = prev.pick || { name: "" };
383
+ return Object.assign({}, prev, { pick: { name: pick.name, path: d.path != null ? d.path : path, entries: d.entries || [] } });
384
+ });
385
+ }).catch(function (e) { patchFs({ message: portalErrorText(e) }); });
386
+ }
387
+
388
+ function moveHere() {
389
+ if (!fs.pick) return;
390
+ var from = joinPath(fs.path, fs.pick.name);
391
+ apiRef.current.mv(from, fs.pick.path).then(function () { loadFiles(fs.path); }).catch(function (e) {
392
+ patchFs({ message: portalErrorText(e) });
393
+ });
394
+ }
395
+
396
+ function runSearch() {
397
+ var q = qRef.current ? String(qRef.current.value || "").trim() : "";
398
+ if (!q) { patchNs({ q: "", hits: [] }); return; }
399
+ apiRef.current.searchNotes(q).then(function (d) {
400
+ patchNs({ q: q, hits: (d && d.hits) || [], message: "" });
401
+ }).catch(function (e) { patchNs({ message: portalErrorText(e) }); });
402
+ }
403
+
404
+ function saveNote() {
405
+ var title = titleRef.current ? String(titleRef.current.value || "").trim() : "";
406
+ var body = bodyRef.current ? String(bodyRef.current.value || "").trim() : "";
407
+ if (!body) { if (bodyRef.current) bodyRef.current.focus(); return; }
408
+ if (!title) title = body.split("\n")[0].slice(0, 36);
409
+ apiRef.current.saveNote({ title: title, content: body }, deskLang()).then(function () {
410
+ if (qRef.current) qRef.current.value = "";
411
+ return apiRef.current.listNotes();
412
+ }).then(function (d) {
413
+ patchNs({ phase: "ready", role: (d && d.role) || "member", notes: (d && d.notes) || [], composing: false, q: "", hits: [], message: "已记下 ✓", tone: "ok" });
414
+ }).catch(function (e) { patchNs({ message: portalErrorText(e), tone: "err" }); });
415
+ }
416
+
417
+ function toggleNote(name) {
418
+ if (ns.expanded === name) { patchNs({ expanded: "", content: "" }); return; }
419
+ patchNs({ expanded: name, content: "读取中…" });
420
+ apiRef.current.readNote(name).then(function (d) {
421
+ patchNs({ expanded: name, content: d && d.ok ? d.content : ((d && d.error) || "读取失败") });
422
+ }).catch(function (e) { patchNs({ expanded: name, content: portalErrorText(e) }); });
423
+ }
424
+
425
+ function deleteNote(name) {
426
+ if (window.confirm && !window.confirm("删除笔记:" + name + "?")) return;
427
+ apiRef.current.removeNote(name, deskLang()).then(function () {
428
+ patchNs({ expanded: "", content: "" });
429
+ loadNotes();
430
+ }).catch(function (e) { patchNs({ message: portalErrorText(e) }); });
431
+ }
432
+
433
+ var trigger = h("button", {
434
+ type: "button", className: "zlk-nav" + (open ? " on" : ""), onClick: toggle, title: "资料库", "data-zlk-open": "1",
435
+ }, h("span", { className: "ic" }, iconFolder(16)), wide ? h("span", { className: "lbl" }, "资料库") : null);
436
+ if (!open) return h("div", { "data-zlk-root": "1" }, trigger);
437
+
438
+ var bodyKids = [
439
+ h("div", { key: "tabs", className: "zlk-tabs" },
440
+ h("button", { type: "button", className: "zlk-tab" + (tab === "files" ? " on" : ""), "data-zlk-tab": "files", onClick: function () { setTab("files"); } }, "📁 文件"),
441
+ h("button", { type: "button", className: "zlk-tab" + (tab === "notes" ? " on" : ""), "data-zlk-tab": "notes", onClick: function () { setTab("notes"); } }, "📝 笔记")
442
+ ),
443
+ ];
444
+
445
+ if (tab === "files") {
446
+ bodyKids.push(h("div", { key: "hint", className: "zlk-hint" }, "放原始文件;按项目或客户建文件夹,找起来最快"));
447
+ var segs = String(fs.path || "").split("/").filter(Boolean);
448
+ var bc = [h("span", { key: "root", className: "lk", onClick: function () { loadFiles(""); } }, "🏠 根目录")];
449
+ var acc = "";
450
+ segs.forEach(function (seg, i) {
451
+ acc = acc ? acc + "/" + seg : seg;
452
+ var here = acc;
453
+ bc.push(h("span", { key: "s" + i }, " / "));
454
+ bc.push(h("span", { key: "c" + i, className: "lk", onClick: function () { loadFiles(here); } }, seg));
455
+ });
456
+ bodyKids.push(h("div", { key: "bc", className: "zlk-bc" }, bc));
457
+ bodyKids.push(h("div", { key: "bar", className: "zlk-row" },
458
+ h("button", { type: "button", className: "zlk-btn gh", "data-zlk-mkdir": "1", onClick: function () { patchFs({ newFolder: !fs.newFolder, pick: null }); } }, "+ 新建文件夹"),
459
+ h("button", { type: "button", className: "zlk-btn", "data-zlk-upload": "1", onClick: function () { if (fileRef.current) fileRef.current.click(); } }, "⬆ 上传"),
460
+ h("input", { ref: fileRef, className: "zlk-file", type: "file", multiple: true, "data-zlk-file-input": "1", onChange: function (e) { uploadFiles(e.target.files); e.target.value = ""; } })
461
+ ));
462
+ if (fs.newFolder) {
463
+ bodyKids.push(h("div", { key: "nf", className: "zlk-row" },
464
+ h("input", { ref: folderRef, className: "zlk-inp zlk-search", "data-zlk-folder-name": "1", placeholder: "文件夹名字,比如「客户A」「2026报价」", onKeyDown: function (e) { if (e.key === "Enter") createFolder(); } }),
465
+ h("button", { type: "button", className: "zlk-btn", "data-zlk-mkdir-save": "1", onClick: createFolder }, "创建"),
466
+ h("button", { type: "button", className: "zlk-btn gh", onClick: function () { patchFs({ newFolder: false }); } }, "取消")
467
+ ));
468
+ }
469
+ bodyKids.push(h("div", {
470
+ key: "drop",
471
+ className: "zlk-drop" + (fs.drag ? " on" : ""),
472
+ "data-zlk-drop": "1",
473
+ onDragOver: function (e) { e.preventDefault(); if (!fs.drag) patchFs({ drag: true }); },
474
+ onDragLeave: function () { patchFs({ drag: false }); },
475
+ onDrop: function (e) { e.preventDefault(); patchFs({ drag: false }); uploadFiles(e.dataTransfer && e.dataTransfer.files); },
476
+ }, h("b", null, "⬆ 拖文件到这里上传"), h("span", null, "上传到当前文件夹 · 单个 ≤50MB")));
477
+ if (fs.message) bodyKids.push(h("div", { key: "msg", className: "zlk-msg" + (fs.phase === "error" || fs.message.indexOf("失败") >= 0 ? " err" : "") }, fs.message));
478
+ if (fs.phase === "loading") bodyKids.push(h("div", { key: "ld", className: "zlk-hint" }, "读取中…"));
479
+ if (fs.pick) {
480
+ var pk = fs.pick;
481
+ var psegs = String(pk.path || "").split("/").filter(Boolean);
482
+ var pbc = [h("span", { key: "pr", className: "lk", onClick: function () { pickerGo(""); } }, "🏠 根目录")];
483
+ var pacc = "";
484
+ psegs.forEach(function (seg, i) {
485
+ pacc = pacc ? pacc + "/" + seg : seg;
486
+ var there = pacc;
487
+ pbc.push(h("span", { key: "ps" + i }, " / "));
488
+ pbc.push(h("span", { key: "pc" + i, className: "lk", onClick: function () { pickerGo(there); } }, seg));
489
+ });
490
+ var folders = (pk.entries || []).filter(function (en) { return en.dir; }).map(function (en) {
491
+ var next = joinPath(pk.path, en.name);
492
+ return h("div", { key: en.name, className: "zlk-it", onClick: function () { pickerGo(next); } },
493
+ h("span", { className: "zlk-ico" }, "📁"),
494
+ h("div", { className: "zlk-title link" }, en.name)
495
+ );
496
+ });
497
+ bodyKids.push(h("div", { key: "pick", className: "zlk-pick" },
498
+ h("div", { className: "zlk-hint" }, "把「" + pk.name + "」移动到:"),
499
+ h("div", { className: "zlk-bc" }, pbc),
500
+ folders.length ? h("div", null, folders) : h("div", { className: "zlk-hint" }, "(没有子文件夹,可直接「移到这里」)"),
501
+ h("div", { className: "zlk-row" },
502
+ h("button", { type: "button", className: "zlk-btn", "data-zlk-move-here": "1", onClick: moveHere }, "移到这里"),
503
+ h("button", { type: "button", className: "zlk-btn gh", onClick: function () { patchFs({ pick: null }); } }, "取消")
504
+ )
505
+ ));
506
+ }
507
+ var rootEmpty = !fs.path && !(fs.entries && fs.entries.length) && fs.phase !== "error";
508
+ (fs.entries || []).forEach(function (en) {
509
+ var full = joinPath(fs.path, en.name);
510
+ var acts = [];
511
+ if (en.dir) acts.push(h("button", { key: "o", type: "button", className: "zlk-mini", onClick: function () { loadFiles(full); } }, "打开"));
512
+ else acts.push(h("a", { key: "d", className: "zlk-mini", href: apiRef.current.downloadUrl(full) }, "下载"));
513
+ acts.push(h("button", { key: "m", type: "button", className: "zlk-mini", "data-zlk-move": en.name, onClick: function () { openPicker(en.name); } }, "移动到…"));
514
+ acts.push(h("button", { key: "x", type: "button", className: "zlk-mini", "data-zlk-del-file": en.name, onClick: function () { removeEntry(full, en.dir); } }, "删除"));
515
+ bodyKids.push(h("div", { key: "f-" + en.name, className: "zlk-it", "data-zlk-file": en.name },
516
+ h("span", { className: "zlk-ico" }, en.dir ? "📁" : "📄"),
517
+ h("div", { className: "zlk-gr" },
518
+ h("div", { className: "zlk-top" },
519
+ h("div", { className: "zlk-title" + (en.dir ? " link" : ""), onClick: en.dir ? function () { loadFiles(full); } : null }, en.name),
520
+ h("div", { className: "zlk-acts row" }, acts)
521
+ ),
522
+ h("div", { className: "zlk-meta" }, (en.dir ? "文件夹" : formatSize(en.size)) + (en.mtime ? " · " + formatWhen(en.mtime) : ""))
523
+ )
524
+ ));
525
+ });
526
+ if (rootEmpty) {
527
+ bodyKids.push(h("div", { key: "ea", className: "zlk-empty", onClick: function () { if (fileRef.current) fileRef.current.click(); } },
528
+ h("b", null, "① 建个文件夹,把文件传进来"), h("span", null, "比如「客户A」「2026报价」—— 拖进去就行")));
529
+ bodyKids.push(h("div", { key: "eb", className: "zlk-empty", onClick: function () { setTab("notes"); patchNs({ composing: true }); } },
530
+ h("b", null, "② 记第一条笔记"), h("span", null, "今天有什么结论?写一句话也算")));
531
+ }
532
+ } else {
533
+ bodyKids.push(h("div", { key: "hint", className: "zlk-hint" }, "记结论和经验,搜一下就能找到 —— AI 助理也能直接引用"));
534
+ bodyKids.push(h("div", { key: "search", className: "zlk-row" },
535
+ h("input", { ref: qRef, className: "zlk-inp zlk-search", "data-zlk-search": "1", placeholder: "搜笔记,比如「印尼」「定价」…", onKeyDown: function (e) { if (e.key === "Enter") runSearch(); } }),
536
+ h("button", { type: "button", className: "zlk-btn gh", "data-zlk-search-btn": "1", onClick: runSearch }, "搜索")
537
+ ));
538
+ bodyKids.push(h("div", { key: "compose", className: "zlk-row" },
539
+ h("button", { type: "button", className: "zlk-btn", "data-zlk-compose": "1", onClick: function () { patchNs({ composing: !ns.composing }); } }, "+ 记一条")
540
+ ));
541
+ if (ns.composing) {
542
+ bodyKids.push(h("div", { key: "form", className: "zlk-form" },
543
+ h("input", { ref: titleRef, className: "zlk-inp", "data-zlk-title": "1", placeholder: "标题(不填就用第一行)" }),
544
+ h("textarea", { ref: bodyRef, className: "zlk-inp zlk-ta", "data-zlk-body": "1", placeholder: "想到什么写什么…… 一句话也算" }),
545
+ h("div", { className: "zlk-row" },
546
+ h("button", { type: "button", className: "zlk-btn", "data-zlk-save": "1", onClick: saveNote }, "保存 ✓"),
547
+ h("button", { type: "button", className: "zlk-btn gh", onClick: function () { patchNs({ composing: false }); } }, "取消")
548
+ )
549
+ ));
550
+ }
551
+ if (ns.message) bodyKids.push(h("div", { key: "nm", className: "zlk-msg" + (ns.tone === "err" || ns.phase === "error" ? " err" : " ok") }, ns.message));
552
+ if (ns.phase === "loading") bodyKids.push(h("div", { key: "ld", className: "zlk-hint" }, "读取中…"));
553
+ if (ns.q) {
554
+ bodyKids.push(h("div", { key: "hc", className: "zlk-hint" }, "命中 " + ns.hits.length));
555
+ if (!ns.hits.length) bodyKids.push(h("div", { key: "miss", className: "zlk-empty" }, h("b", null, "没有命中,换个短点的词试试")));
556
+ ns.hits.forEach(function (hit, i) {
557
+ bodyKids.push(h("div", { key: "h" + i, className: "zlk-it" },
558
+ h("span", { className: "zlk-ico" }, "🔎"),
559
+ h("div", { className: "zlk-gr" },
560
+ h("div", { className: "zlk-meta" }, hit.file + " : " + hit.line),
561
+ h("div", { className: "zlk-body" }, hit.text)
562
+ )
563
+ ));
564
+ });
565
+ bodyKids.push(h("div", { key: "back", className: "zlk-row" },
566
+ h("button", { type: "button", className: "zlk-btn gh", "data-zlk-back-list": "1", onClick: function () { if (qRef.current) qRef.current.value = ""; patchNs({ q: "", hits: [] }); } }, "返回笔记列表")
567
+ ));
568
+ } else if (ns.phase === "ready") {
569
+ if (!ns.notes.length) {
570
+ bodyKids.push(h("div", { key: "empty", className: "zlk-empty", onClick: function () { patchNs({ composing: true }); } },
571
+ h("b", null, "还没有笔记 —— 点「+ 记一条」写第一句"),
572
+ h("span", null, "结论、口径、经验,一句话也算")
573
+ ));
574
+ }
575
+ ns.notes.forEach(function (note) {
576
+ var shown = ns.expanded === note.name;
577
+ var acts = [h("button", { key: "v", type: "button", className: "zlk-mini", "data-zlk-view": note.name, onClick: function () { toggleNote(note.name); } }, shown ? "收起" : "查看")];
578
+ if (canDeleteNote(ns.role)) {
579
+ acts.push(h("button", { key: "x", type: "button", className: "zlk-mini", "data-zlk-del": note.name, onClick: function () { deleteNote(note.name); } }, "删除"));
580
+ }
581
+ bodyKids.push(h("div", { key: note.name, className: "zlk-it", "data-zlk-note": note.name },
582
+ h("span", { className: "zlk-ico" }, "📝"),
583
+ h("div", { className: "zlk-gr" },
584
+ h("div", { className: "zlk-top" },
585
+ h("div", { className: "zlk-title" }, note.title || note.name),
586
+ h("div", { className: "zlk-acts" }, acts)
587
+ ),
588
+ h("div", { className: "zlk-meta" }, formatWhen(note.mtime) + (note.author ? " · " + note.author : "")),
589
+ shown ? h("div", { className: "zlk-body" }, ns.content) : null
590
+ )
591
+ ));
592
+ });
593
+ }
594
+ }
595
+
596
+ return h("div", { "data-zlk-root": "1" },
597
+ trigger,
598
+ h("section", { className: "zlk-fv", "data-zlk-panel": "1" },
599
+ h("header", { className: "zlk-hd" },
600
+ h("span", { className: "zlk-mark" }, iconFolder(22)),
601
+ h("div", { className: "zlk-hd-text" },
602
+ h("div", { className: "zlk-t1" }, "资料库"),
603
+ h("div", { className: "zlk-t2" }, "文件 + 笔记 · 放进来 = 全员可见")
604
+ ),
605
+ h("button", { type: "button", className: "zlk-back", "data-zlk-back": "1", onClick: close }, "← 返回对话")
606
+ ),
607
+ h("div", { className: "zlk-bd" }, bodyKids)
608
+ )
609
+ );
610
+ }
611
+
612
+ function apply(ctx) {
613
+ ctx.slots.inject("sidebar.footer.action", function () {
614
+ return ctx.slots.register(
615
+ {
616
+ name: "sidebar.footer.action",
617
+ id: "ziliaoku",
618
+ order: 91,
619
+ label: function () { return "资料库"; },
620
+ },
621
+ LibraryPanel
622
+ );
623
+ });
624
+ }
625
+
626
+ ensureCss();
627
+
628
+ exports.apply = apply;
629
+ exports.inject = inject;
630
+ return module.exports;
631
+ },
632
+ });
package/lib/index.js ADDED
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Host entry for library-plugin.
3
+ * The browser surface (sidebar 「资料库」, panel id ziliaoku) lives in ./client.js.
4
+ * This process does not read or write the library.
5
+ */
6
+ export const name = "library-plugin";
7
+
8
+ export function apply() {}