anywork-library-plugin 0.1.0 → 0.2.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/README.md +4 -10
- package/lib/api.js +14 -13
- package/lib/client.js +160 -175
- package/lib/index.js +190 -8
- package/lib/panel.js +146 -162
- package/lib/store.js +209 -0
- package/package.json +2 -1
package/lib/index.js
CHANGED
|
@@ -1,8 +1,190 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
import { createReadStream } from "node:fs";
|
|
2
|
+
import {
|
|
3
|
+
MAX_UPLOAD,
|
|
4
|
+
fileForDownload,
|
|
5
|
+
listDrive,
|
|
6
|
+
listNotes,
|
|
7
|
+
makeDir,
|
|
8
|
+
movePath,
|
|
9
|
+
readNote,
|
|
10
|
+
removeNote,
|
|
11
|
+
removePath,
|
|
12
|
+
saveFile,
|
|
13
|
+
saveNote,
|
|
14
|
+
searchNotes,
|
|
15
|
+
} from "./store.js";
|
|
16
|
+
|
|
17
|
+
export const name = "anywork-library-plugin";
|
|
18
|
+
|
|
19
|
+
const PREFIX = "/plugins/anywork-library-plugin";
|
|
20
|
+
|
|
21
|
+
function send(res, status, body) {
|
|
22
|
+
const payload = Buffer.from(JSON.stringify(body));
|
|
23
|
+
res.writeHead(status, {
|
|
24
|
+
"content-type": "application/json; charset=utf-8",
|
|
25
|
+
"cache-control": "no-store",
|
|
26
|
+
"content-length": payload.length,
|
|
27
|
+
});
|
|
28
|
+
res.end(payload);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readJson(req) {
|
|
32
|
+
return new Promise((resolve, reject) => {
|
|
33
|
+
const chunks = [];
|
|
34
|
+
let size = 0;
|
|
35
|
+
req.on("data", (chunk) => {
|
|
36
|
+
size += chunk.length;
|
|
37
|
+
if (size > 1024 * 1024) {
|
|
38
|
+
reject(Object.assign(new Error("too large"), { status: 413 }));
|
|
39
|
+
req.destroy();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
chunks.push(chunk);
|
|
43
|
+
});
|
|
44
|
+
req.on("end", () => {
|
|
45
|
+
if (!chunks.length) return resolve({});
|
|
46
|
+
try { resolve(JSON.parse(Buffer.concat(chunks).toString("utf8"))); }
|
|
47
|
+
catch { reject(Object.assign(new Error("bad json"), { status: 400 })); }
|
|
48
|
+
});
|
|
49
|
+
req.on("error", reject);
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function readRaw(req) {
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
const chunks = [];
|
|
56
|
+
let size = 0;
|
|
57
|
+
req.on("data", (chunk) => {
|
|
58
|
+
size += chunk.length;
|
|
59
|
+
if (size > MAX_UPLOAD) {
|
|
60
|
+
reject(Object.assign(new Error("too large"), { status: 413 }));
|
|
61
|
+
req.destroy();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
chunks.push(chunk);
|
|
65
|
+
});
|
|
66
|
+
req.on("end", () => resolve(Buffer.concat(chunks)));
|
|
67
|
+
req.on("error", reject);
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function statusFor(result) {
|
|
72
|
+
if (!result || !result.error) return 200;
|
|
73
|
+
if (/不存在|not found/.test(result.error)) return 404;
|
|
74
|
+
return 400;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function handle(req, res) {
|
|
78
|
+
const url = new URL(req.url || "/", "http://library.local");
|
|
79
|
+
const pathname = url.pathname;
|
|
80
|
+
if (req.method === "GET" && pathname === PREFIX + "/drive/list") {
|
|
81
|
+
const result = listDrive(url.searchParams.get("path") || "");
|
|
82
|
+
return send(res, statusFor(result), result);
|
|
83
|
+
}
|
|
84
|
+
if (req.method === "POST" && pathname === PREFIX + "/drive/upload") {
|
|
85
|
+
const data = await readRaw(req);
|
|
86
|
+
const result = saveFile(url.searchParams.get("path") || "", url.searchParams.get("name") || "", data);
|
|
87
|
+
return send(res, statusFor(result), result);
|
|
88
|
+
}
|
|
89
|
+
if (req.method === "POST" && pathname === PREFIX + "/drive/mkdir") {
|
|
90
|
+
const body = await readJson(req);
|
|
91
|
+
const result = makeDir(body.path || "", body.name || "");
|
|
92
|
+
return send(res, statusFor(result), result);
|
|
93
|
+
}
|
|
94
|
+
if (req.method === "POST" && pathname === PREFIX + "/drive/rm") {
|
|
95
|
+
const body = await readJson(req);
|
|
96
|
+
const result = removePath(body.path || "");
|
|
97
|
+
return send(res, statusFor(result), result);
|
|
98
|
+
}
|
|
99
|
+
if (req.method === "POST" && pathname === PREFIX + "/drive/mv") {
|
|
100
|
+
const body = await readJson(req);
|
|
101
|
+
const result = movePath(body.from || "", body.to || "");
|
|
102
|
+
return send(res, statusFor(result), result);
|
|
103
|
+
}
|
|
104
|
+
if (req.method === "GET" && pathname === PREFIX + "/drive/download") {
|
|
105
|
+
const result = fileForDownload(url.searchParams.get("path") || "");
|
|
106
|
+
if (result.error) return send(res, statusFor(result), result);
|
|
107
|
+
res.writeHead(200, {
|
|
108
|
+
"content-type": "application/octet-stream",
|
|
109
|
+
"content-length": result.size,
|
|
110
|
+
"content-disposition": "attachment; filename*=UTF-8''" + encodeURIComponent(result.name),
|
|
111
|
+
"cache-control": "no-store",
|
|
112
|
+
});
|
|
113
|
+
createReadStream(result.abs).pipe(res);
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
if (req.method === "GET" && pathname === PREFIX + "/kb/list") {
|
|
117
|
+
return send(res, 200, listNotes());
|
|
118
|
+
}
|
|
119
|
+
if (req.method === "GET" && pathname === PREFIX + "/kb/search") {
|
|
120
|
+
return send(res, 200, searchNotes(url.searchParams.get("q") || ""));
|
|
121
|
+
}
|
|
122
|
+
if (req.method === "GET" && pathname === PREFIX + "/kb/note") {
|
|
123
|
+
const result = readNote(url.searchParams.get("name") || "");
|
|
124
|
+
return send(res, statusFor(result), result);
|
|
125
|
+
}
|
|
126
|
+
if (req.method === "POST" && pathname === PREFIX + "/kb/save") {
|
|
127
|
+
const body = await readJson(req);
|
|
128
|
+
const result = saveNote(body);
|
|
129
|
+
return send(res, statusFor(result), result);
|
|
130
|
+
}
|
|
131
|
+
if (req.method === "POST" && pathname === PREFIX + "/kb/rm") {
|
|
132
|
+
const body = await readJson(req);
|
|
133
|
+
const result = removeNote(body.name || "");
|
|
134
|
+
return send(res, statusFor(result), result);
|
|
135
|
+
}
|
|
136
|
+
send(res, 404, { error: "not found" });
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function routes() {
|
|
140
|
+
return [
|
|
141
|
+
"/drive/list",
|
|
142
|
+
"/drive/upload",
|
|
143
|
+
"/drive/mkdir",
|
|
144
|
+
"/drive/rm",
|
|
145
|
+
"/drive/mv",
|
|
146
|
+
"/drive/download",
|
|
147
|
+
"/kb/list",
|
|
148
|
+
"/kb/search",
|
|
149
|
+
"/kb/note",
|
|
150
|
+
"/kb/save",
|
|
151
|
+
"/kb/rm",
|
|
152
|
+
].map((suffix) => ({
|
|
153
|
+
kind: "exact",
|
|
154
|
+
path: PREFIX + suffix,
|
|
155
|
+
handler: (req, res) => handle(req, res).catch((err) => {
|
|
156
|
+
if (res.headersSent) return;
|
|
157
|
+
send(res, err.status || 500, { error: err.message || "failed" });
|
|
158
|
+
}),
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function bindRoutes(ctx) {
|
|
163
|
+
const web = ctx.get?.("webServer") ?? ctx.get?.("httpServer");
|
|
164
|
+
if (!web || typeof web.register !== "function") return null;
|
|
165
|
+
const stops = [];
|
|
166
|
+
for (const route of routes()) {
|
|
167
|
+
const stop = web.register(route);
|
|
168
|
+
if (typeof stop === "function") stops.push(stop);
|
|
169
|
+
}
|
|
170
|
+
return () => {
|
|
171
|
+
for (const stop of stops) stop();
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function apply(ctx) {
|
|
176
|
+
if (!ctx || typeof ctx.effect !== "function") return;
|
|
177
|
+
ctx.effect(() => {
|
|
178
|
+
let unbind = bindRoutes(ctx);
|
|
179
|
+
let alive = true;
|
|
180
|
+
const onService = (name) => {
|
|
181
|
+
if (!alive || unbind) return;
|
|
182
|
+
if (name === "webServer" || name === "httpServer") unbind = bindRoutes(ctx);
|
|
183
|
+
};
|
|
184
|
+
if (typeof ctx.on === "function") ctx.on("internal/service", onService);
|
|
185
|
+
return () => {
|
|
186
|
+
alive = false;
|
|
187
|
+
if (unbind) unbind();
|
|
188
|
+
};
|
|
189
|
+
});
|
|
190
|
+
}
|
package/lib/panel.js
CHANGED
|
@@ -54,9 +54,38 @@ var ZLK_CSS = `
|
|
|
54
54
|
.zlk-bc .lk { color:#3478f6; cursor:pointer; }
|
|
55
55
|
.zlk-drop { margin-top:12px; border:1.5px dashed #cfd3d8; border-radius:12px; background:#f7f8fa; text-align:center; padding:14px 10px; }
|
|
56
56
|
.zlk-drop.on { border-color:#3478f6; background:#f3f7ff; }
|
|
57
|
-
.zlk-
|
|
58
|
-
|
|
59
|
-
|
|
57
|
+
.zlk-intake {
|
|
58
|
+
position: relative;
|
|
59
|
+
display: flex;
|
|
60
|
+
flex-direction: column;
|
|
61
|
+
justify-content: flex-end;
|
|
62
|
+
gap: 14px;
|
|
63
|
+
min-height: 240px;
|
|
64
|
+
margin: 0 0 22px;
|
|
65
|
+
padding: 28px 26px 22px;
|
|
66
|
+
border-radius: 28px;
|
|
67
|
+
background: #141414;
|
|
68
|
+
color: #f6f1e8;
|
|
69
|
+
box-shadow: 0 24px 60px -36px rgba(0,0,0,.7);
|
|
70
|
+
cursor: pointer;
|
|
71
|
+
}
|
|
72
|
+
.zlk-intake.empty { min-height: 68vh; }
|
|
73
|
+
.zlk-intake.on { outline: 2px solid #f6f1e8; outline-offset: 4px; }
|
|
74
|
+
.zlk-kicker { font-size: 12px; letter-spacing: .16em; text-transform: uppercase; color: #b7aa96; }
|
|
75
|
+
.zlk-headline { font-size: 64px; line-height: .9; font-weight: 720; letter-spacing: -0.04em; }
|
|
76
|
+
.zlk-lead { max-width: 28em; font-size: 16px; line-height: 1.5; color: #d9d0c3; }
|
|
77
|
+
.zlk-drop-hint { font-size: 13px; color: #a89880; }
|
|
78
|
+
.zlk-line { display: flex; gap: 8px; align-items: center; }
|
|
79
|
+
.zlk-line input {
|
|
80
|
+
flex: 1; height: 52px; border: 0; border-radius: 16px; padding: 0 16px;
|
|
81
|
+
background: #f6f1e8; color: #161616; font: 500 16px/1 inherit; outline: none;
|
|
82
|
+
}
|
|
83
|
+
.zlk-line button {
|
|
84
|
+
height: 52px; padding: 0 18px; border: 0; border-radius: 16px;
|
|
85
|
+
background: #f6f1e8; color: #161616; font: 700 15px/1 inherit; cursor: pointer;
|
|
86
|
+
}
|
|
87
|
+
.zlk-shelf-title { margin: 8px 2px 4px; font-size: 13px; letter-spacing: .08em; color: #6b7280; }
|
|
88
|
+
.zlk-pick { margin-top: 12px; padding: 12px; border-radius: 12px; background: #fff; border: 1px solid #e6e6e6; }
|
|
60
89
|
`;
|
|
61
90
|
|
|
62
91
|
function ensureCss() {
|
|
@@ -310,169 +339,124 @@ function LibraryPanel(props) {
|
|
|
310
339
|
}).catch(function (e) { patchNs({ message: portalErrorText(e) }); });
|
|
311
340
|
}
|
|
312
341
|
|
|
342
|
+
function saveLine() {
|
|
343
|
+
var body = bodyRef.current ? String(bodyRef.current.value || "").trim() : "";
|
|
344
|
+
if (!body) { if (bodyRef.current) bodyRef.current.focus(); return; }
|
|
345
|
+
var title = body.split("\n")[0].slice(0, 36);
|
|
346
|
+
apiRef.current.saveNote({ title: title, content: body }, deskLang()).then(function () {
|
|
347
|
+
if (bodyRef.current) bodyRef.current.value = "";
|
|
348
|
+
return apiRef.current.listNotes();
|
|
349
|
+
}).then(function (d) {
|
|
350
|
+
patchNs({ phase: "ready", role: (d && d.role) || "admin", notes: (d && d.notes) || [], composing: false, q: "", hits: [], message: "已记下", tone: "ok" });
|
|
351
|
+
}).catch(function (e) { patchNs({ message: portalErrorText(e), tone: "err" }); });
|
|
352
|
+
}
|
|
353
|
+
|
|
313
354
|
var trigger = h("button", {
|
|
314
355
|
type: "button", className: "zlk-nav" + (open ? " on" : ""), onClick: toggle, title: "资料库", "data-zlk-open": "1",
|
|
315
356
|
}, h("span", { className: "ic" }, iconFolder(16)), wide ? h("span", { className: "lbl" }, "资料库") : null);
|
|
316
357
|
if (!open) return h("div", { "data-zlk-root": "1" }, trigger);
|
|
317
358
|
|
|
318
|
-
var
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
),
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
})
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
h("button", { type: "button",
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
"
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
if (
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
)
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
));
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
h("
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
h("button", { type: "button", className: "zlk-btn", "data-zlk-compose": "1", onClick: function () { patchNs({ composing: !ns.composing }); } }, "+ 记一条")
|
|
420
|
-
));
|
|
421
|
-
if (ns.composing) {
|
|
422
|
-
bodyKids.push(h("div", { key: "form", className: "zlk-form" },
|
|
423
|
-
h("input", { ref: titleRef, className: "zlk-inp", "data-zlk-title": "1", placeholder: "标题(不填就用第一行)" }),
|
|
424
|
-
h("textarea", { ref: bodyRef, className: "zlk-inp zlk-ta", "data-zlk-body": "1", placeholder: "想到什么写什么…… 一句话也算" }),
|
|
425
|
-
h("div", { className: "zlk-row" },
|
|
426
|
-
h("button", { type: "button", className: "zlk-btn", "data-zlk-save": "1", onClick: saveNote }, "保存 ✓"),
|
|
427
|
-
h("button", { type: "button", className: "zlk-btn gh", onClick: function () { patchNs({ composing: false }); } }, "取消")
|
|
428
|
-
)
|
|
429
|
-
));
|
|
430
|
-
}
|
|
431
|
-
if (ns.message) bodyKids.push(h("div", { key: "nm", className: "zlk-msg" + (ns.tone === "err" || ns.phase === "error" ? " err" : " ok") }, ns.message));
|
|
432
|
-
if (ns.phase === "loading") bodyKids.push(h("div", { key: "ld", className: "zlk-hint" }, "读取中…"));
|
|
433
|
-
if (ns.q) {
|
|
434
|
-
bodyKids.push(h("div", { key: "hc", className: "zlk-hint" }, "命中 " + ns.hits.length));
|
|
435
|
-
if (!ns.hits.length) bodyKids.push(h("div", { key: "miss", className: "zlk-empty" }, h("b", null, "没有命中,换个短点的词试试")));
|
|
436
|
-
ns.hits.forEach(function (hit, i) {
|
|
437
|
-
bodyKids.push(h("div", { key: "h" + i, className: "zlk-it" },
|
|
438
|
-
h("span", { className: "zlk-ico" }, "🔎"),
|
|
439
|
-
h("div", { className: "zlk-gr" },
|
|
440
|
-
h("div", { className: "zlk-meta" }, hit.file + " : " + hit.line),
|
|
441
|
-
h("div", { className: "zlk-body" }, hit.text)
|
|
442
|
-
)
|
|
443
|
-
));
|
|
444
|
-
});
|
|
445
|
-
bodyKids.push(h("div", { key: "back", className: "zlk-row" },
|
|
446
|
-
h("button", { type: "button", className: "zlk-btn gh", "data-zlk-back-list": "1", onClick: function () { if (qRef.current) qRef.current.value = ""; patchNs({ q: "", hits: [] }); } }, "返回笔记列表")
|
|
447
|
-
));
|
|
448
|
-
} else if (ns.phase === "ready") {
|
|
449
|
-
if (!ns.notes.length) {
|
|
450
|
-
bodyKids.push(h("div", { key: "empty", className: "zlk-empty", onClick: function () { patchNs({ composing: true }); } },
|
|
451
|
-
h("b", null, "还没有笔记 —— 点「+ 记一条」写第一句"),
|
|
452
|
-
h("span", null, "结论、口径、经验,一句话也算")
|
|
453
|
-
));
|
|
454
|
-
}
|
|
455
|
-
ns.notes.forEach(function (note) {
|
|
456
|
-
var shown = ns.expanded === note.name;
|
|
457
|
-
var acts = [h("button", { key: "v", type: "button", className: "zlk-mini", "data-zlk-view": note.name, onClick: function () { toggleNote(note.name); } }, shown ? "收起" : "查看")];
|
|
458
|
-
if (canDeleteNote(ns.role)) {
|
|
459
|
-
acts.push(h("button", { key: "x", type: "button", className: "zlk-mini", "data-zlk-del": note.name, onClick: function () { deleteNote(note.name); } }, "删除"));
|
|
460
|
-
}
|
|
461
|
-
bodyKids.push(h("div", { key: note.name, className: "zlk-it", "data-zlk-note": note.name },
|
|
462
|
-
h("span", { className: "zlk-ico" }, "📝"),
|
|
463
|
-
h("div", { className: "zlk-gr" },
|
|
464
|
-
h("div", { className: "zlk-top" },
|
|
465
|
-
h("div", { className: "zlk-title" }, note.title || note.name),
|
|
466
|
-
h("div", { className: "zlk-acts" }, acts)
|
|
467
|
-
),
|
|
468
|
-
h("div", { className: "zlk-meta" }, formatWhen(note.mtime) + (note.author ? " · " + note.author : "")),
|
|
469
|
-
shown ? h("div", { className: "zlk-body" }, ns.content) : null
|
|
470
|
-
)
|
|
471
|
-
));
|
|
472
|
-
});
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
359
|
+
var shelfEmpty = fs.phase !== "error" && ns.phase !== "error" && !fs.path && !(fs.entries && fs.entries.length) && !(ns.notes && ns.notes.length) && !ns.q;
|
|
360
|
+
var bodyKids = [];
|
|
361
|
+
bodyKids.push(h("div", {
|
|
362
|
+
key: "intake",
|
|
363
|
+
className: "zlk-intake" + (shelfEmpty ? " empty" : "") + (fs.drag ? " on" : ""),
|
|
364
|
+
"data-zlk-drop": "1",
|
|
365
|
+
onDragOver: function (e) { e.preventDefault(); if (!fs.drag) patchFs({ drag: true }); },
|
|
366
|
+
onDragLeave: function () { patchFs({ drag: false }); },
|
|
367
|
+
onDrop: function (e) { e.preventDefault(); patchFs({ drag: false }); uploadFiles(e.dataTransfer && e.dataTransfer.files); },
|
|
368
|
+
onClick: function (e) {
|
|
369
|
+
if (e.target.closest("input, button, a")) return;
|
|
370
|
+
if (fileRef.current) fileRef.current.click();
|
|
371
|
+
},
|
|
372
|
+
},
|
|
373
|
+
h("div", { className: "zlk-kicker" }, "SHARED SHELF"),
|
|
374
|
+
h("div", { className: "zlk-headline" }, shelfEmpty ? "丢进来" : "再丢一份"),
|
|
375
|
+
h("div", { className: "zlk-lead" }, "文件,或一句话。放在这块黑板上,用这个工作台的人都能看见。"),
|
|
376
|
+
h("div", { className: "zlk-drop-hint" }, "拖文件到这里 · 点这块黑板也能选文件 · 单个 ≤50MB"),
|
|
377
|
+
h("div", { className: "zlk-line", onClick: function (e) { e.stopPropagation(); } },
|
|
378
|
+
h("input", { ref: bodyRef, "data-zlk-body": "1", "data-zlk-line": "1", placeholder: "或写一句,回车记下", onKeyDown: function (e) { if (e.key === "Enter") saveLine(); } }),
|
|
379
|
+
h("button", { type: "button", "data-zlk-save": "1", onClick: saveLine }, "记下")
|
|
380
|
+
),
|
|
381
|
+
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 = ""; } })
|
|
382
|
+
));
|
|
383
|
+
if (fs.message) bodyKids.push(h("div", { key: "fmsg", className: "zlk-msg" + (fs.phase === "error" || fs.message.indexOf("失败") >= 0 ? " err" : "") }, fs.message));
|
|
384
|
+
if (ns.message) bodyKids.push(h("div", { key: "nmsg", className: "zlk-msg" + (ns.tone === "err" || ns.phase === "error" ? " err" : " ok") }, ns.message));
|
|
385
|
+
if (fs.phase === "loading" || ns.phase === "loading") bodyKids.push(h("div", { key: "ld", className: "zlk-hint" }, "读取中…"));
|
|
386
|
+
var segs = String(fs.path || "").split("/").filter(Boolean);
|
|
387
|
+
if (segs.length) {
|
|
388
|
+
var bc = [h("span", { key: "root", className: "lk", onClick: function () { loadFiles(""); } }, "架上")];
|
|
389
|
+
var acc = "";
|
|
390
|
+
segs.forEach(function (seg, i) {
|
|
391
|
+
acc = acc ? acc + "/" + seg : seg;
|
|
392
|
+
var here = acc;
|
|
393
|
+
bc.push(h("span", { key: "s" + i }, " / "));
|
|
394
|
+
bc.push(h("span", { key: "c" + i, className: "lk", onClick: function () { loadFiles(here); } }, seg));
|
|
395
|
+
});
|
|
396
|
+
bodyKids.push(h("div", { key: "bc", className: "zlk-bc" }, bc));
|
|
397
|
+
}
|
|
398
|
+
bodyKids.push(h("div", { key: "bar", className: "zlk-row" },
|
|
399
|
+
h("button", { type: "button", className: "zlk-btn gh", "data-zlk-mkdir": "1", onClick: function () { patchFs({ newFolder: !fs.newFolder }); } }, "新建文件夹"),
|
|
400
|
+
h("input", { ref: qRef, className: "zlk-inp zlk-search", "data-zlk-search": "1", placeholder: "在架上找一句", onKeyDown: function (e) { if (e.key === "Enter") runSearch(); } }),
|
|
401
|
+
h("button", { type: "button", className: "zlk-btn gh", "data-zlk-search-btn": "1", onClick: runSearch }, "找")
|
|
402
|
+
));
|
|
403
|
+
if (fs.newFolder) {
|
|
404
|
+
bodyKids.push(h("div", { key: "nf", className: "zlk-row" },
|
|
405
|
+
h("input", { ref: folderRef, className: "zlk-inp zlk-search", "data-zlk-folder-name": "1", placeholder: "文件夹名字", onKeyDown: function (e) { if (e.key === "Enter") createFolder(); } }),
|
|
406
|
+
h("button", { type: "button", className: "zlk-btn", "data-zlk-mkdir-save": "1", onClick: createFolder }, "创建"),
|
|
407
|
+
h("button", { type: "button", className: "zlk-btn gh", onClick: function () { patchFs({ newFolder: false }); } }, "取消")
|
|
408
|
+
));
|
|
409
|
+
}
|
|
410
|
+
if (ns.q) {
|
|
411
|
+
bodyKids.push(h("div", { key: "hc", className: "zlk-shelf-title" }, "找到 " + ns.hits.length));
|
|
412
|
+
if (!ns.hits.length) bodyKids.push(h("div", { key: "miss", className: "zlk-hint" }, "没有这句话"));
|
|
413
|
+
ns.hits.forEach(function (hit, i) {
|
|
414
|
+
bodyKids.push(h("div", { key: "h" + i, className: "zlk-it" },
|
|
415
|
+
h("span", { className: "zlk-ico" }, "🔎"),
|
|
416
|
+
h("div", { className: "zlk-gr" }, h("div", { className: "zlk-title" }, hit.text))
|
|
417
|
+
));
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
if (!ns.q && ns.notes && ns.notes.length) {
|
|
421
|
+
bodyKids.push(h("div", { key: "nt", className: "zlk-shelf-title" }, "句子"));
|
|
422
|
+
ns.notes.forEach(function (note) {
|
|
423
|
+
var shown = ns.expanded === note.name;
|
|
424
|
+
var acts = [h("button", { key: "v", type: "button", className: "zlk-mini", "data-zlk-view": note.name, onClick: function () { toggleNote(note.name); } }, shown ? "收起" : "查看")];
|
|
425
|
+
if (canDeleteNote(ns.role)) acts.push(h("button", { key: "x", type: "button", className: "zlk-mini", "data-zlk-del": note.name, onClick: function () { deleteNote(note.name); } }, "删除"));
|
|
426
|
+
bodyKids.push(h("div", { key: note.name, className: "zlk-it", "data-zlk-note": note.name },
|
|
427
|
+
h("span", { className: "zlk-ico" }, "✎"),
|
|
428
|
+
h("div", { className: "zlk-gr" },
|
|
429
|
+
h("div", { className: "zlk-top" },
|
|
430
|
+
h("div", { className: "zlk-title" }, note.title || note.name),
|
|
431
|
+
h("div", { className: "zlk-acts row" }, acts)
|
|
432
|
+
),
|
|
433
|
+
h("div", { className: "zlk-meta" }, formatWhen(note.mtime) + (note.author ? " · " + note.author : "")),
|
|
434
|
+
shown ? h("div", { className: "zlk-body" }, ns.content) : null
|
|
435
|
+
)
|
|
436
|
+
));
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
if (fs.entries && fs.entries.length) {
|
|
440
|
+
bodyKids.push(h("div", { key: "ft", className: "zlk-shelf-title" }, "文件"));
|
|
441
|
+
fs.entries.forEach(function (en) {
|
|
442
|
+
var full = joinPath(fs.path, en.name);
|
|
443
|
+
var acts = [];
|
|
444
|
+
if (en.dir) acts.push(h("button", { key: "o", type: "button", className: "zlk-mini", onClick: function () { loadFiles(full); } }, "打开"));
|
|
445
|
+
else acts.push(h("a", { key: "d", className: "zlk-mini", href: apiRef.current.downloadUrl(full) }, "下载"));
|
|
446
|
+
acts.push(h("button", { key: "x", type: "button", className: "zlk-mini", "data-zlk-del-file": en.name, onClick: function () { removeEntry(full, en.dir); } }, "删除"));
|
|
447
|
+
bodyKids.push(h("div", { key: "f-" + en.name, className: "zlk-it", "data-zlk-file": en.name },
|
|
448
|
+
h("span", { className: "zlk-ico" }, en.dir ? "📁" : "📄"),
|
|
449
|
+
h("div", { className: "zlk-gr" },
|
|
450
|
+
h("div", { className: "zlk-top" },
|
|
451
|
+
h("div", { className: "zlk-title" + (en.dir ? " link" : ""), onClick: en.dir ? function () { loadFiles(full); } : null }, en.name),
|
|
452
|
+
h("div", { className: "zlk-acts row" }, acts)
|
|
453
|
+
),
|
|
454
|
+
h("div", { className: "zlk-meta" }, (en.dir ? "文件夹" : formatSize(en.size)) + (en.mtime ? " · " + formatWhen(en.mtime) : ""))
|
|
455
|
+
)
|
|
456
|
+
));
|
|
457
|
+
});
|
|
458
|
+
}
|
|
459
|
+
|
|
476
460
|
return h("div", { "data-zlk-root": "1" },
|
|
477
461
|
trigger,
|
|
478
462
|
h("section", { className: "zlk-fv", "data-zlk-panel": "1" },
|
|
@@ -480,7 +464,7 @@ function LibraryPanel(props) {
|
|
|
480
464
|
h("span", { className: "zlk-mark" }, iconFolder(22)),
|
|
481
465
|
h("div", { className: "zlk-hd-text" },
|
|
482
466
|
h("div", { className: "zlk-t1" }, "资料库"),
|
|
483
|
-
h("div", { className: "zlk-t2" }, "
|
|
467
|
+
h("div", { className: "zlk-t2" }, "丢进来,全员就能看见")
|
|
484
468
|
),
|
|
485
469
|
h("button", { type: "button", className: "zlk-back", "data-zlk-back": "1", onClick: close }, "← 返回对话")
|
|
486
470
|
),
|