@chance722/dsh-inbox 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.
Files changed (45) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +182 -0
  3. package/README.zh.md +182 -0
  4. package/cordis.patch.yml +11 -0
  5. package/lib/cli.js +259 -0
  6. package/lib/client.js +3823 -0
  7. package/lib/index.js +3718 -0
  8. package/lib/types/cli.d.ts +49 -0
  9. package/lib/types/client/card.d.ts +52 -0
  10. package/lib/types/client/dock.d.ts +52 -0
  11. package/lib/types/client/heading.d.ts +54 -0
  12. package/lib/types/client/index.d.ts +24 -0
  13. package/lib/types/client/manual.d.ts +22 -0
  14. package/lib/types/client/scheme.d.ts +47 -0
  15. package/lib/types/host/capture.d.ts +117 -0
  16. package/lib/types/host/classify/model.d.ts +112 -0
  17. package/lib/types/host/classify/redact.d.ts +18 -0
  18. package/lib/types/host/classify/rules.d.ts +58 -0
  19. package/lib/types/host/command.d.ts +18 -0
  20. package/lib/types/host/crypto/secret-box.d.ts +60 -0
  21. package/lib/types/host/index.d.ts +21 -0
  22. package/lib/types/host/link-title.d.ts +64 -0
  23. package/lib/types/host/remote/auto-push.d.ts +37 -0
  24. package/lib/types/host/remote/merge.d.ts +80 -0
  25. package/lib/types/host/remote/pull.d.ts +60 -0
  26. package/lib/types/host/remote/push.d.ts +101 -0
  27. package/lib/types/host/remote/remove.d.ts +65 -0
  28. package/lib/types/host/remote/writer.d.ts +43 -0
  29. package/lib/types/host/rpc.d.ts +36 -0
  30. package/lib/types/host/s3/client.d.ts +216 -0
  31. package/lib/types/host/s3/probe.d.ts +28 -0
  32. package/lib/types/host/tools.d.ts +56 -0
  33. package/lib/types/host/ui/config.d.ts +54 -0
  34. package/lib/types/host/vault/lease.d.ts +54 -0
  35. package/lib/types/host/vault/query.d.ts +38 -0
  36. package/lib/types/host/vault/spec.d.ts +193 -0
  37. package/lib/types/host/vault/vault.d.ts +269 -0
  38. package/lib/types/host/webdav/client.d.ts +102 -0
  39. package/lib/types/host/webdav/config.d.ts +129 -0
  40. package/lib/types/host/webdav/probe.d.ts +22 -0
  41. package/lib/types/host/webdav/run.d.ts +34 -0
  42. package/lib/types/shared/constants.d.ts +23 -0
  43. package/lib/types/shared/panel-wire.d.ts +474 -0
  44. package/lib/types/shared/vocabulary.d.ts +39 -0
  45. package/package.json +101 -0
package/lib/client.js ADDED
@@ -0,0 +1,3823 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@chance722/dsh-inbox",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ "use strict";
7
+ var __create = Object.create;
8
+ var __defProp = Object.defineProperty;
9
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
10
+ var __getOwnPropNames = Object.getOwnPropertyNames;
11
+ var __getProtoOf = Object.getPrototypeOf;
12
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
13
+ var __export = (target, all) => {
14
+ for (var name2 in all)
15
+ __defProp(target, name2, { get: all[name2], enumerable: true });
16
+ };
17
+ var __copyProps = (to, from, except, desc) => {
18
+ if (from && typeof from === "object" || typeof from === "function") {
19
+ for (let key of __getOwnPropNames(from))
20
+ if (!__hasOwnProp.call(to, key) && key !== except)
21
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
22
+ }
23
+ return to;
24
+ };
25
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
26
+ // If the importer is in node compatibility mode or this is not an ESM
27
+ // file that has been converted to a CommonJS file using a Babel-
28
+ // compatible transform (i.e. "__esModule" has not been set), then set
29
+ // "default" to the CommonJS "module.exports" for node compatibility.
30
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
31
+ mod
32
+ ));
33
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
34
+
35
+ // src/client/index.tsx
36
+ var index_exports = {};
37
+ __export(index_exports, {
38
+ apply: () => apply,
39
+ inject: () => inject,
40
+ name: () => name
41
+ });
42
+ module.exports = __toCommonJS(index_exports);
43
+ var import_react7 = __toESM(require("react"), 1);
44
+
45
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/createLucideIcon.mjs
46
+ var import_react3 = require("react");
47
+
48
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/utils/toKebabCase.mjs
49
+ var toKebabCase = (string) => string?.replace(/([a-z0-9])([A-Z])/g, "$1-$2").toLowerCase();
50
+
51
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/utils/toLucideIconData.mjs
52
+ function toLucideIconData(iconName, iconNode, aliases = []) {
53
+ if (iconNode == null) {
54
+ throw new Error("[lucide]: iconNode is required when icon name is used");
55
+ }
56
+ return {
57
+ name: toKebabCase(iconName),
58
+ size: 24,
59
+ node: iconNode,
60
+ ...aliases.length > 0 ? { aliases } : {}
61
+ };
62
+ }
63
+
64
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/utils/toCamelCase.mjs
65
+ var toCamelCase = (string) => {
66
+ let out = "";
67
+ let upperNext = false;
68
+ for (const ch of string) {
69
+ if (ch === "-" || ch === "_" || ch <= " ") {
70
+ upperNext = out.length > 0;
71
+ continue;
72
+ }
73
+ if (out.length === 0) {
74
+ out += ch.toLowerCase();
75
+ } else {
76
+ out += upperNext ? ch.toUpperCase() : ch;
77
+ }
78
+ upperNext = false;
79
+ }
80
+ return out;
81
+ };
82
+
83
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/utils/toPascalCase.mjs
84
+ var toPascalCase = (string) => {
85
+ const camelCase = toCamelCase(string);
86
+ return camelCase.charAt(0).toUpperCase() + camelCase.slice(1);
87
+ };
88
+
89
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/Icon.mjs
90
+ var import_react2 = require("react");
91
+
92
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/utils/mergeClasses.mjs
93
+ var mergeClasses = (...classes) => classes.filter((className, index, array) => {
94
+ return Boolean(className) && className.trim() !== "" && array.indexOf(className) === index;
95
+ }).join(" ").trim();
96
+
97
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/build/defaultAttributes.mjs
98
+ var defaultAttributes = {
99
+ xmlns: "http://www.w3.org/2000/svg",
100
+ width: 24,
101
+ height: 24,
102
+ viewBox: "0 0 24 24",
103
+ fill: "none",
104
+ stroke: "currentColor",
105
+ "stroke-width": 2,
106
+ "stroke-linecap": "round",
107
+ "stroke-linejoin": "round"
108
+ };
109
+
110
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/build/buildLucideIconNode.mjs
111
+ function isDefined(value) {
112
+ return value !== null && value !== void 0;
113
+ }
114
+ function buildLucideIconNode(icon, params = {}) {
115
+ const attributeNames = params.attributeNames ?? {};
116
+ const getAttributeName = (attributeName) => attributeNames[attributeName] ?? attributeName;
117
+ const viewBoxWidth = icon.size ?? icon.width ?? defaultAttributes["width"];
118
+ const viewBoxHeight = icon.size ?? icon.height ?? defaultAttributes["height"];
119
+ const aliasClassNames = icon.aliases?.filter((alias) => typeof alias === "string" && alias.trim() !== "").map((alias) => `lucide-${alias}`) ?? [];
120
+ const iconClassNames = [...icon.name ? [`lucide-${icon.name}`] : [], ...aliasClassNames];
121
+ const classNamesFromClassName = params.className?.split(" ").filter(Boolean) ?? [];
122
+ const className = params.includeDefaultClasses === false ? mergeClasses(...classNamesFromClassName) : mergeClasses("lucide", ...iconClassNames, ...classNamesFromClassName);
123
+ const calculatedStrokeWidth = params.absoluteStrokeWidth ? Number(params.strokeWidth ?? defaultAttributes["stroke-width"]) * Number(icon.size ?? icon.width ?? defaultAttributes["width"]) / Number(params.size ?? params.width ?? defaultAttributes["width"]) : params.strokeWidth ?? defaultAttributes["stroke-width"];
124
+ const attributes = {
125
+ ...Object.entries(defaultAttributes).reduce((attrs, [attrName, value]) => {
126
+ attrs[getAttributeName(attrName)] = value;
127
+ return attrs;
128
+ }, {}),
129
+ ..."color" in params && params.color && {
130
+ [getAttributeName("stroke")]: params.color
131
+ },
132
+ ..."size" in params && isDefined(params.size) && {
133
+ [getAttributeName("width")]: params.size,
134
+ [getAttributeName("height")]: params.size
135
+ },
136
+ ..."width" in params && isDefined(params.width) && {
137
+ [getAttributeName("width")]: params.width
138
+ },
139
+ ..."height" in params && isDefined(params.height) && {
140
+ [getAttributeName("height")]: params.height
141
+ },
142
+ [getAttributeName("stroke-width")]: calculatedStrokeWidth,
143
+ ...className && {
144
+ [getAttributeName("class")]: className
145
+ },
146
+ [getAttributeName("viewBox")]: `0 0 ${viewBoxWidth} ${viewBoxHeight}`,
147
+ ...params.hasA11yProp === false ? {
148
+ [getAttributeName("aria-hidden")]: "true"
149
+ } : {},
150
+ ..."attributes" in params && params.attributes
151
+ };
152
+ return [
153
+ "svg",
154
+ attributes,
155
+ icon.node.map((child) => {
156
+ const [name2, attrs, children] = child;
157
+ const nextAttrs = params.nonScalingStroke ? { [getAttributeName("vector-effect")]: "non-scaling-stroke", ...attrs } : attrs;
158
+ return children ? [name2, nextAttrs, children] : [name2, nextAttrs];
159
+ })
160
+ ];
161
+ }
162
+
163
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/build/buildLucideIconForReact.mjs
164
+ function buildLucideIconForReact(icon, params = {}) {
165
+ return buildLucideIconNode(icon, {
166
+ ...params,
167
+ attributeNames: {
168
+ ...params.attributeNames,
169
+ class: "className",
170
+ "stroke-width": "strokeWidth",
171
+ "stroke-linecap": "strokeLinecap",
172
+ "stroke-linejoin": "strokeLinejoin",
173
+ "vector-effect": "vectorEffect"
174
+ }
175
+ });
176
+ }
177
+
178
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/shared/src/utils/hasA11yProp.mjs
179
+ var hasA11yProp = (props) => {
180
+ for (const prop in props) {
181
+ if (prop.startsWith("aria-") || prop === "role" || prop === "title") {
182
+ return true;
183
+ }
184
+ }
185
+ return false;
186
+ };
187
+
188
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/context.mjs
189
+ var import_react = require("react");
190
+ var LucideContext = (0, import_react.createContext)({});
191
+ var useLucideContext = () => (0, import_react.useContext)(LucideContext);
192
+
193
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/Icon.mjs
194
+ var Icon = (0, import_react2.forwardRef)(
195
+ ({
196
+ color,
197
+ size,
198
+ width,
199
+ height,
200
+ strokeWidth,
201
+ absoluteStrokeWidth,
202
+ nonScalingStroke,
203
+ className = "",
204
+ children,
205
+ iconNode = [],
206
+ icon = {
207
+ node: iconNode,
208
+ aliases: [],
209
+ size: 24
210
+ },
211
+ ...rest
212
+ }, ref) => {
213
+ const {
214
+ size: contextSize = 24,
215
+ strokeWidth: contextStrokeWidth = 2,
216
+ absoluteStrokeWidth: contextAbsoluteStrokeWidth = false,
217
+ nonScalingStroke: contextNonScalingStroke = false,
218
+ color: contextColor = "currentColor",
219
+ className: contextClass = ""
220
+ } = useLucideContext() ?? {};
221
+ const hasAccessibleProp = Boolean(children) || hasA11yProp(rest);
222
+ const [name2, svgAttributes, builtIconNode = []] = buildLucideIconForReact(icon, {
223
+ color: color ?? contextColor,
224
+ width: width ?? size ?? contextSize,
225
+ height: height ?? size ?? contextSize,
226
+ strokeWidth: strokeWidth ?? contextStrokeWidth,
227
+ absoluteStrokeWidth: absoluteStrokeWidth ?? contextAbsoluteStrokeWidth,
228
+ nonScalingStroke: nonScalingStroke ?? contextNonScalingStroke,
229
+ className: mergeClasses(contextClass, className),
230
+ hasA11yProp: hasAccessibleProp,
231
+ attributes: rest
232
+ });
233
+ return (0, import_react2.createElement)(
234
+ name2,
235
+ {
236
+ ref,
237
+ ...svgAttributes
238
+ },
239
+ [
240
+ ...builtIconNode.map(([tag, attrs]) => (0, import_react2.createElement)(tag, attrs)),
241
+ ...Array.isArray(children) ? children : [children]
242
+ ]
243
+ );
244
+ }
245
+ );
246
+
247
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/createLucideIcon.mjs
248
+ function createLucideIcon(iconDataOrName, iconNode = [], aliases = []) {
249
+ const iconData = typeof iconDataOrName === "string" ? toLucideIconData(iconDataOrName, iconNode, aliases) : iconDataOrName;
250
+ const Component = (0, import_react3.forwardRef)(
251
+ ({ className, ...props }, ref) => (0, import_react3.createElement)(Icon, {
252
+ ref,
253
+ icon: iconData,
254
+ className,
255
+ ...props
256
+ })
257
+ );
258
+ if (iconData.name) {
259
+ Component.displayName = toPascalCase(iconData.name);
260
+ }
261
+ return Component;
262
+ }
263
+
264
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/bookmark.mjs
265
+ var __iconData = {
266
+ name: "bookmark",
267
+ size: 24,
268
+ node: [
269
+ [
270
+ "path",
271
+ {
272
+ d: "M17 3a2 2 0 0 1 2 2v15a1 1 0 0 1-1.496.868l-4.512-2.578a2 2 0 0 0-1.984 0l-4.512 2.578A1 1 0 0 1 5 20V5a2 2 0 0 1 2-2z",
273
+ key: "oz39mx"
274
+ }
275
+ ]
276
+ ]
277
+ };
278
+ __iconData.node;
279
+ var Bookmark = createLucideIcon(__iconData);
280
+
281
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/check.mjs
282
+ var __iconData2 = {
283
+ name: "check",
284
+ size: 24,
285
+ node: [["path", { d: "M20 6 9 17l-5-5", key: "1gmf2c" }]]
286
+ };
287
+ __iconData2.node;
288
+ var Check = createLucideIcon(__iconData2);
289
+
290
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/chevron-down.mjs
291
+ var __iconData3 = {
292
+ name: "chevron-down",
293
+ size: 24,
294
+ node: [["path", { d: "m6 9 6 6 6-6", key: "qrunsl" }]]
295
+ };
296
+ __iconData3.node;
297
+ var ChevronDown = createLucideIcon(__iconData3);
298
+
299
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/chevron-left.mjs
300
+ var __iconData4 = {
301
+ name: "chevron-left",
302
+ size: 24,
303
+ node: [["path", { d: "m15 18-6-6 6-6", key: "1wnfg3" }]]
304
+ };
305
+ __iconData4.node;
306
+ var ChevronLeft = createLucideIcon(__iconData4);
307
+
308
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/chevron-right.mjs
309
+ var __iconData5 = {
310
+ name: "chevron-right",
311
+ size: 24,
312
+ node: [["path", { d: "m9 18 6-6-6-6", key: "mthhwq" }]]
313
+ };
314
+ __iconData5.node;
315
+ var ChevronRight = createLucideIcon(__iconData5);
316
+
317
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/circle.mjs
318
+ var __iconData6 = {
319
+ name: "circle",
320
+ size: 24,
321
+ node: [["circle", { cx: "12", cy: "12", r: "10", key: "1mglay" }]]
322
+ };
323
+ __iconData6.node;
324
+ var Circle = createLucideIcon(__iconData6);
325
+
326
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/external-link.mjs
327
+ var __iconData7 = {
328
+ name: "external-link",
329
+ size: 24,
330
+ node: [
331
+ ["path", { d: "M15 3h6v6", key: "1q9fwt" }],
332
+ ["path", { d: "M10 14 21 3", key: "gplh6r" }],
333
+ ["path", { d: "M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6", key: "a6xqqp" }]
334
+ ]
335
+ };
336
+ __iconData7.node;
337
+ var ExternalLink = createLucideIcon(__iconData7);
338
+
339
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/file-text.mjs
340
+ var __iconData8 = {
341
+ name: "file-text",
342
+ size: 24,
343
+ node: [
344
+ [
345
+ "path",
346
+ {
347
+ d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z",
348
+ key: "1oefj6"
349
+ }
350
+ ],
351
+ ["path", { d: "M14 2v5a1 1 0 0 0 1 1h5", key: "wfsgrz" }],
352
+ ["path", { d: "M10 9H8", key: "b1mrlr" }],
353
+ ["path", { d: "M16 13H8", key: "t4e002" }],
354
+ ["path", { d: "M16 17H8", key: "z1uh3a" }]
355
+ ]
356
+ };
357
+ __iconData8.node;
358
+ var FileText = createLucideIcon(__iconData8);
359
+
360
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/film.mjs
361
+ var __iconData9 = {
362
+ name: "film",
363
+ size: 24,
364
+ node: [
365
+ ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", key: "afitv7" }],
366
+ ["path", { d: "M7 3v18", key: "bbkbws" }],
367
+ ["path", { d: "M3 7.5h4", key: "zfgn84" }],
368
+ ["path", { d: "M3 12h18", key: "1i2n21" }],
369
+ ["path", { d: "M3 16.5h4", key: "1230mu" }],
370
+ ["path", { d: "M17 3v18", key: "in4fa5" }],
371
+ ["path", { d: "M17 7.5h4", key: "myr1c1" }],
372
+ ["path", { d: "M17 16.5h4", key: "go4c1d" }]
373
+ ]
374
+ };
375
+ __iconData9.node;
376
+ var Film = createLucideIcon(__iconData9);
377
+
378
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/id-card.mjs
379
+ var __iconData10 = {
380
+ name: "id-card",
381
+ size: 24,
382
+ node: [
383
+ ["path", { d: "M13 19a4 4 0 00-8 0", key: "cugzd5" }],
384
+ ["path", { d: "M16 10h2", key: "8sgtl7" }],
385
+ ["path", { d: "M16 14h2", key: "epxaof" }],
386
+ ["circle", { cx: "9", cy: "12", r: "3", key: "u3jwor" }],
387
+ ["rect", { x: "2", y: "5", width: "20", height: "14", rx: "2", key: "qneu4z" }]
388
+ ]
389
+ };
390
+ __iconData10.node;
391
+ var IdCard = createLucideIcon(__iconData10);
392
+
393
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/image.mjs
394
+ var __iconData11 = {
395
+ name: "image",
396
+ size: 24,
397
+ node: [
398
+ ["rect", { width: "18", height: "18", x: "3", y: "3", rx: "2", ry: "2", key: "1m3agn" }],
399
+ ["circle", { cx: "9", cy: "9", r: "2", key: "af1f0g" }],
400
+ ["path", { d: "m21 15-3.086-3.086a2 2 0 0 0-2.828 0L6 21", key: "1xmnt7" }]
401
+ ]
402
+ };
403
+ __iconData11.node;
404
+ var Image = createLucideIcon(__iconData11);
405
+
406
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/inbox.mjs
407
+ var __iconData12 = {
408
+ name: "inbox",
409
+ size: 24,
410
+ node: [
411
+ ["polyline", { points: "22 12 16 12 14 15 10 15 8 12 2 12", key: "o97t9d" }],
412
+ [
413
+ "path",
414
+ {
415
+ d: "M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z",
416
+ key: "oot6mr"
417
+ }
418
+ ]
419
+ ]
420
+ };
421
+ __iconData12.node;
422
+ var Inbox = createLucideIcon(__iconData12);
423
+
424
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/key-round.mjs
425
+ var __iconData13 = {
426
+ name: "key-round",
427
+ size: 24,
428
+ node: [
429
+ [
430
+ "path",
431
+ {
432
+ d: "M2.586 17.414A2 2 0 0 0 2 18.828V21a1 1 0 0 0 1 1h3a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h1a1 1 0 0 0 1-1v-1a1 1 0 0 1 1-1h.172a2 2 0 0 0 1.414-.586l.814-.814a6.5 6.5 0 1 0-4-4z",
433
+ key: "1s6t7t"
434
+ }
435
+ ],
436
+ ["circle", { cx: "16.5", cy: "7.5", r: ".5", fill: "currentColor", key: "w0ekpg" }]
437
+ ]
438
+ };
439
+ __iconData13.node;
440
+ var KeyRound = createLucideIcon(__iconData13);
441
+
442
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/layers.mjs
443
+ var __iconData14 = {
444
+ name: "layers",
445
+ size: 24,
446
+ node: [
447
+ [
448
+ "path",
449
+ {
450
+ d: "M12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83z",
451
+ key: "zw3jo"
452
+ }
453
+ ],
454
+ [
455
+ "path",
456
+ {
457
+ d: "M2 12a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 12",
458
+ key: "1wduqc"
459
+ }
460
+ ],
461
+ [
462
+ "path",
463
+ {
464
+ d: "M2 17a1 1 0 0 0 .58.91l8.6 3.91a2 2 0 0 0 1.65 0l8.58-3.9A1 1 0 0 0 22 17",
465
+ key: "kqbvx6"
466
+ }
467
+ ]
468
+ ],
469
+ aliases: ["layers-3"]
470
+ };
471
+ __iconData14.node;
472
+ var Layers = createLucideIcon(__iconData14);
473
+
474
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/layout-grid.mjs
475
+ var __iconData15 = {
476
+ name: "layout-grid",
477
+ size: 24,
478
+ node: [
479
+ ["rect", { width: "7", height: "7", x: "3", y: "3", rx: "1", key: "1g98yp" }],
480
+ ["rect", { width: "7", height: "7", x: "14", y: "3", rx: "1", key: "6d4xhi" }],
481
+ ["rect", { width: "7", height: "7", x: "14", y: "14", rx: "1", key: "nxv5o0" }],
482
+ ["rect", { width: "7", height: "7", x: "3", y: "14", rx: "1", key: "1bb6yr" }]
483
+ ]
484
+ };
485
+ __iconData15.node;
486
+ var LayoutGrid = createLucideIcon(__iconData15);
487
+
488
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/lightbulb.mjs
489
+ var __iconData16 = {
490
+ name: "lightbulb",
491
+ size: 24,
492
+ node: [
493
+ [
494
+ "path",
495
+ {
496
+ d: "M15 14c.2-1 .7-1.7 1.5-2.5 1-.9 1.5-2.2 1.5-3.5A6 6 0 0 0 6 8c0 1 .2 2.2 1.5 3.5.7.7 1.3 1.5 1.5 2.5",
497
+ key: "1gvzjb"
498
+ }
499
+ ],
500
+ ["path", { d: "M9 18h6", key: "x1upvd" }],
501
+ ["path", { d: "M10 22h4", key: "ceow96" }]
502
+ ]
503
+ };
504
+ __iconData16.node;
505
+ var Lightbulb = createLucideIcon(__iconData16);
506
+
507
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/play.mjs
508
+ var __iconData17 = {
509
+ name: "play",
510
+ size: 24,
511
+ node: [
512
+ [
513
+ "path",
514
+ {
515
+ d: "M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z",
516
+ key: "10ikf1"
517
+ }
518
+ ]
519
+ ]
520
+ };
521
+ __iconData17.node;
522
+ var Play = createLucideIcon(__iconData17);
523
+
524
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/refresh-cw.mjs
525
+ var __iconData18 = {
526
+ name: "refresh-cw",
527
+ size: 24,
528
+ node: [
529
+ ["path", { d: "M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8", key: "v9h5vc" }],
530
+ ["path", { d: "M21 3v5h-5", key: "1q7to0" }],
531
+ ["path", { d: "M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16", key: "3uifl3" }],
532
+ ["path", { d: "M8 16H3v5", key: "1cv678" }]
533
+ ]
534
+ };
535
+ __iconData18.node;
536
+ var RefreshCw = createLucideIcon(__iconData18);
537
+
538
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/rotate-ccw.mjs
539
+ var __iconData19 = {
540
+ name: "rotate-ccw",
541
+ size: 24,
542
+ node: [
543
+ ["path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8", key: "1357e3" }],
544
+ ["path", { d: "M3 3v5h5", key: "1xhq8a" }]
545
+ ]
546
+ };
547
+ __iconData19.node;
548
+ var RotateCcw = createLucideIcon(__iconData19);
549
+
550
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/settings-2.mjs
551
+ var __iconData20 = {
552
+ name: "settings-2",
553
+ size: 24,
554
+ node: [
555
+ ["path", { d: "M14 17H5", key: "gfn3mx" }],
556
+ ["path", { d: "M19 7h-9", key: "6i9tg" }],
557
+ ["circle", { cx: "17", cy: "17", r: "3", key: "18b49y" }],
558
+ ["circle", { cx: "7", cy: "7", r: "3", key: "dfmy0x" }]
559
+ ]
560
+ };
561
+ __iconData20.node;
562
+ var Settings2 = createLucideIcon(__iconData20);
563
+
564
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/tag.mjs
565
+ var __iconData21 = {
566
+ name: "tag",
567
+ size: 24,
568
+ node: [
569
+ [
570
+ "path",
571
+ {
572
+ d: "M12.586 2.586A2 2 0 0 0 11.172 2H4a2 2 0 0 0-2 2v7.172a2 2 0 0 0 .586 1.414l8.704 8.704a2.426 2.426 0 0 0 3.42 0l6.58-6.58a2.426 2.426 0 0 0 0-3.42z",
573
+ key: "vktsd0"
574
+ }
575
+ ],
576
+ ["circle", { cx: "7.5", cy: "7.5", r: ".5", fill: "currentColor", key: "kqv944" }]
577
+ ]
578
+ };
579
+ __iconData21.node;
580
+ var Tag = createLucideIcon(__iconData21);
581
+
582
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/trash.mjs
583
+ var __iconData22 = {
584
+ name: "trash",
585
+ size: 24,
586
+ node: [
587
+ ["path", { d: "M10 11v6", key: "nco0om" }],
588
+ ["path", { d: "M14 11v6", key: "outv1u" }],
589
+ ["path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6", key: "miytrc" }],
590
+ ["path", { d: "M3 6h18", key: "d0wm0j" }],
591
+ ["path", { d: "M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2", key: "e791ji" }]
592
+ ],
593
+ aliases: ["trash-2"]
594
+ };
595
+ __iconData22.node;
596
+ var Trash = createLucideIcon(__iconData22);
597
+
598
+ // node_modules/.pnpm/lucide-react@1.47.0_react@18.3.1/node_modules/lucide-react/dist/esm/icons/x.mjs
599
+ var __iconData23 = {
600
+ name: "x",
601
+ size: 24,
602
+ node: [
603
+ ["path", { d: "M18 6 6 18", key: "1bl5f8" }],
604
+ ["path", { d: "m6 6 12 12", key: "d8bk6v" }]
605
+ ]
606
+ };
607
+ __iconData23.node;
608
+ var X = createLucideIcon(__iconData23);
609
+
610
+ // src/shared/panel-wire.ts
611
+ var INBOX_API_PREFIX = "/api/inbox";
612
+ var INBOX_ENDPOINT_CAPTURE = "capture";
613
+ var INBOX_ENDPOINT_LIST = "list";
614
+ var INBOX_ENDPOINT_DETAIL = "detail";
615
+ var INBOX_ENDPOINT_UPDATE = "update";
616
+ var INBOX_ENDPOINT_DELETE = "delete";
617
+ var INBOX_ENDPOINT_RESTORE = "restore";
618
+ var INBOX_ENDPOINT_PURGE = "purge";
619
+ var INBOX_ENDPOINT_ATTACHMENT = "attachment";
620
+ var INBOX_ENDPOINT_WEBDAV = "webdav";
621
+ var INBOX_ENDPOINT_PULL = "pull";
622
+ var INBOX_ENDPOINT_PROBE = "probe";
623
+ var INBOX_ENDPOINT_UI = "ui";
624
+ var INBOX_ENDPOINT_TAGS = "tags";
625
+ var INBOX_ENDPOINT_SECRET = "secret";
626
+ var INBOX_ENDPOINT_PUSH = "push";
627
+ function isSuccess(status) {
628
+ return status >= 200 && status < 300;
629
+ }
630
+ function probeVerdict(rows) {
631
+ if (rows.length === 0) return { ok: false, title: "\u6CA1\u6709\u53EF\u7528\u7684\u81EA\u68C0\u7ED3\u679C" };
632
+ const winner = rows.find((row) => isSuccess(row.status));
633
+ if (winner !== void 0) {
634
+ const shape = winner.label.replace(/\s*([^)]*)\s*/g, "").trim();
635
+ return { ok: true, title: `\u901A\u9053\u53EF\u7528\uFF08\u53EF\u7528\u5F62\u72B6\uFF1A${shape}\uFF09` };
636
+ }
637
+ const statuses = new Set(rows.map((row) => row.status));
638
+ if (statuses.has(0)) {
639
+ const failed = rows.find((row) => row.status === 0);
640
+ return { ok: false, title: "\u8FDE\u4E0D\u4E0A", hint: failed?.detail ?? "" };
641
+ }
642
+ if (statuses.size === 1 && statuses.has(401)) {
643
+ return {
644
+ ok: false,
645
+ title: "\u8BA4\u8BC1\u88AB\u62D2\uFF08\u6BCF\u4E00\u884C\u90FD\u662F 401\uFF09",
646
+ hint: "\u4E0D\u662F\u7B7E\u540D\u5199\u6CD5\u7684\u95EE\u9898\uFF1A\u628A\u300C\u5BA2\u6237\u7AEF\u6807\u8BC6\u300D\u586B\u6210 AccessKey \u7ED1\u5B9A\u7684\u5E94\u7528\u540D\uFF08\u6570\u636E\u80F6\u56CA\u63A7\u5236\u53F0\u91CC\u521B\u5EFA key \u65F6\u9009\u7684\u90A3\u4E2A\uFF09\uFF0C\u6216\u8005\u6362\u4E00\u6B21\u5BC6\u94A5\uFF0C\u518D\u81EA\u68C0\u3002"
647
+ };
648
+ }
649
+ if (statuses.size === 1 && statuses.has(403)) {
650
+ return {
651
+ ok: false,
652
+ title: "\u6743\u9650\u88AB\u62D2\uFF08\u6BCF\u4E00\u884C\u90FD\u662F 403\uFF09",
653
+ hint: "\u5BC6\u94A5\u8BA4\u51FA\u6765\u4E86\uFF0C\u4F46\u4E0D\u5141\u8BB8\u8FD9\u4E2A\u64CD\u4F5C\uFF1A\u68C0\u67E5\u6876/\u76EE\u5F55\u7684\u6388\u6743\uFF0C\u4EE5\u53CA\u5BA2\u6237\u7AEF\u6807\u8BC6\u662F\u5426\u4E0E\u8BE5 AccessKey \u7ED1\u5B9A\u7684\u5E94\u7528\u4E00\u81F4\u3002"
654
+ };
655
+ }
656
+ if (statuses.size === 1 && statuses.has(404)) {
657
+ return { ok: false, title: "\u8DEF\u5F84\u4E0D\u5B58\u5728\uFF08\u6BCF\u4E00\u884C\u90FD\u662F 404\uFF09", hint: "\u68C0\u67E5\u6876\u540D\u3001endpoint \u4E0E\u76EE\u5F55\u524D\u7F00\u3002" };
658
+ }
659
+ const first = rows[0]?.status ?? 0;
660
+ return {
661
+ ok: false,
662
+ title: `\u901A\u9053\u4E0D\u53EF\u7528\uFF08\u6536\u5230\u7684\u72B6\u6001\uFF1A${[...statuses].join(" / ")}\uFF09`,
663
+ hint: `\u9010\u884C\u770B\u4E0B\u9762\u7684\u8BE6\u60C5\uFF1B\u7B2C\u4E00\u884C\u662F ${String(first)}\u3002`
664
+ };
665
+ }
666
+ var INBOX_IMAGE_TYPES = ["image/png", "image/jpeg", "image/webp", "image/gif"];
667
+ var PAGE_SIZE = 12;
668
+ var UI_LIST_MODES = ["grid", "compact"];
669
+ var MAX_TITLE_CHARS = 300;
670
+
671
+ // src/shared/constants.ts
672
+ var PACKAGE_NAME = "@chance722/dsh-inbox";
673
+ var PANEL_ID = "inbox";
674
+
675
+ // src/client/card.tsx
676
+ var import_react5 = require("react");
677
+
678
+ // src/client/dock.tsx
679
+ var import_react4 = __toESM(require("react"), 1);
680
+
681
+ // src/shared/vocabulary.ts
682
+ var CATEGORIES = [
683
+ "idea",
684
+ "article",
685
+ "media",
686
+ "image",
687
+ "document",
688
+ "secret",
689
+ "other"
690
+ ];
691
+ var CATEGORY_LABELS = {
692
+ idea: "\u7075\u611F/\u5F85\u529E",
693
+ article: "\u6587\u7AE0",
694
+ media: "\u89C6\u9891/\u97F3\u9891",
695
+ image: "\u56FE\u7247",
696
+ document: "\u8BC1\u4EF6",
697
+ secret: "\u5BC6\u94A5/\u8D26\u5BC6",
698
+ other: "\u5176\u5B83"
699
+ };
700
+ var KIND_LABELS = {
701
+ text: "\u6587\u672C",
702
+ link: "\u94FE\u63A5",
703
+ image: "\u56FE\u7247",
704
+ file: "\u6587\u4EF6"
705
+ };
706
+ var CATEGORY_SOURCE_LABELS = {
707
+ rule: "\u89C4\u5219\u5224\u5B9A",
708
+ model: "\u6A21\u578B\u5224\u5B9A",
709
+ user: "\u624B\u52A8\u5224\u5B9A"
710
+ };
711
+ var CATEGORY_SOURCE_HINTS = {
712
+ rule: "\u672C\u5730\u89C4\u5219\u6309\u94FE\u63A5\u3001\u6587\u672C\u3001\u56FE\u7247\u7684\u5F62\u72B6\u5224\u7684\uFF0C\u6CA1\u6709\u8054\u7F51",
713
+ model: "\u89C4\u5219\u5224\u4E0D\u51FA\u6765\u624D\u4EA4\u7ED9\u6A21\u578B\u5224\u7684\uFF1B\u4F60\u8BF4\u7684\u8BDD\u6C38\u8FDC\u4F18\u5148\uFF0C\u968F\u65F6\u53EF\u4EE5\u6539",
714
+ user: "\u4F60\u81EA\u5DF1\u9009\u7684\u7C7B\u76EE\uFF0C\u89C4\u5219\u548C\u6A21\u578B\u90FD\u4E0D\u4F1A\u8986\u76D6\u5B83"
715
+ };
716
+
717
+ // src/client/heading.ts
718
+ var NOTE_IN_HEADING_CHARS = 24;
719
+ function isSecret(entry) {
720
+ return entry.category === "secret";
721
+ }
722
+ var UNTITLED = "\uFF08\u65E0\u6807\u9898\uFF09";
723
+ function firstFilled(...values) {
724
+ return values.find((value) => value !== void 0 && value.trim().length > 0);
725
+ }
726
+ function noteLine(note, chars) {
727
+ const collapsed = (note ?? "").replace(/\s+/g, " ").trim();
728
+ if (collapsed.length === 0) return "";
729
+ return collapsed.length <= chars ? collapsed : `${collapsed.slice(0, chars)}\u2026`;
730
+ }
731
+ function ownName(entry) {
732
+ if (isSecret(entry)) return void 0;
733
+ return firstFilled(entry.linkTitle, entry.url, entry.preview, entry.attachmentName);
734
+ }
735
+ function build(entry, noteChars) {
736
+ const title = firstFilled(entry.title);
737
+ if (title !== void 0) return title;
738
+ const own = ownName(entry);
739
+ if (own !== void 0) return own;
740
+ return noteLine(entry.note, noteChars) || (isSecret(entry) ? "\u5BC6\u94A5 / \u8D26\u5BC6" : UNTITLED);
741
+ }
742
+ function headingOf(entry) {
743
+ return build(entry, NOTE_IN_HEADING_CHARS);
744
+ }
745
+ function headingTooltipOf(entry) {
746
+ const name2 = build(entry, Number.MAX_SAFE_INTEGER);
747
+ const note = noteLine(entry.note, Number.MAX_SAFE_INTEGER);
748
+ return note.length === 0 || note === name2 ? name2 : `${name2}\uFF08${note}\uFF09`;
749
+ }
750
+
751
+ // src/client/dock.tsx
752
+ var import_jsx_runtime = require("react/jsx-runtime");
753
+ var DOCK_KIND = "inbox-vault";
754
+ var DOCK_TAB_ID = PACKAGE_NAME;
755
+ var DOCK_LIMIT = 12;
756
+ var openVaultDock;
757
+ function dockFocusOf(info) {
758
+ const navigation = info?.tab?.navigation;
759
+ const wanted = navigation?.params?.id;
760
+ const revision = navigation?.revision;
761
+ return {
762
+ ...typeof wanted === "string" && wanted.length > 0 ? { id: wanted } : {},
763
+ revision: typeof revision === "number" ? revision : 0
764
+ };
765
+ }
766
+ function registerInboxDock(ctx) {
767
+ ctx.inject(["sidebarRightTabs"], (scoped) => {
768
+ const slots = scoped.get("slots");
769
+ const tabs = scoped.get("sidebarRightTabs");
770
+ if (slots === void 0 || tabs === void 0) return;
771
+ try {
772
+ tabs.register({
773
+ id: DOCK_TAB_ID,
774
+ kind: DOCK_KIND,
775
+ title: () => "\u4ED3\u5E93",
776
+ guide: [
777
+ { order: 20, title: () => "\u4ED3\u5E93", description: () => "\u628A inbox \u653E\u5728\u5BF9\u8BDD\u65C1\u8FB9\uFF0C\u968F\u624B\u770B" }
778
+ ]
779
+ });
780
+ slots.inject(
781
+ "sidebar.right.pane.tab",
782
+ () => slots.register({ name: "sidebar.right.pane.tab", key: DOCK_TAB_ID }, InboxDock)
783
+ );
784
+ const controller = scoped.get("sidebarRight");
785
+ openVaultDock = (id) => {
786
+ controller?.openTab?.(
787
+ DOCK_KIND,
788
+ id === void 0 ? void 0 : { params: { id }, revealIfOpened: true }
789
+ );
790
+ };
791
+ } catch {
792
+ }
793
+ });
794
+ }
795
+ function InboxDock(props) {
796
+ const info = (props?.useTabInfo ?? props?.hooks?.tabInfo)?.();
797
+ const { id: focused, revision } = dockFocusOf(info);
798
+ return focused === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DockList, {}) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(DockRecord, { id: focused, revision });
799
+ }
800
+ function DockList() {
801
+ const [entries, setEntries] = import_react4.default.useState();
802
+ const [failed, setFailed] = import_react4.default.useState(false);
803
+ import_react4.default.useEffect(() => {
804
+ let live = true;
805
+ void (async () => {
806
+ try {
807
+ const response = await fetch(`${INBOX_API_PREFIX}/${INBOX_ENDPOINT_LIST}`, {
808
+ method: "POST",
809
+ headers: { "content-type": "application/json" },
810
+ body: JSON.stringify({ limit: DOCK_LIMIT })
811
+ });
812
+ const answer = await response.json();
813
+ if (!live) return;
814
+ if (answer.ok) setEntries(answer.value.entries);
815
+ else setFailed(true);
816
+ } catch {
817
+ if (live) setFailed(true);
818
+ }
819
+ })();
820
+ return () => {
821
+ live = false;
822
+ };
823
+ }, []);
824
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "10px 12px", fontSize: 13 }, children: [
825
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { opacity: 0.6, marginBottom: 8 }, children: entries === void 0 ? "\u8BFB\u53D6\u4E2D\u2026" : `\u6700\u8FD1 ${String(entries.length)} \u6761` }),
826
+ failed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { opacity: 0.7 }, children: "\u8BFB\u4E0D\u5230\u4ED3\u5E93\uFF0C\u53BB\u5DE6\u4FA7\u300CInbox\u300D\u9762\u677F\u770B\u770B\u3002" }),
827
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: { listStyle: "none", margin: 0, padding: 0, display: "flex", flexDirection: "column", gap: 6 }, children: entries?.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
828
+ "li",
829
+ {
830
+ style: {
831
+ border: "1px solid color-mix(in srgb, currentColor 12%, transparent)",
832
+ borderRadius: 8,
833
+ padding: "6px 8px",
834
+ opacity: 1
835
+ },
836
+ children: [
837
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { overflowWrap: "anywhere" }, children: headingOf(entry) }),
838
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontSize: 12, opacity: 0.6, marginTop: 2 }, children: [
839
+ KIND_LABELS[entry.kind],
840
+ " \xB7 ",
841
+ CATEGORY_LABELS[entry.category],
842
+ entry.watchLater ? " \xB7 \u5F85\u770B" : ""
843
+ ] })
844
+ ]
845
+ },
846
+ entry.id
847
+ )) }),
848
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { opacity: 0.6, marginTop: 10 }, children: "\u8FD9\u91CC\u662F\u968F\u624B\u770B\u3002\u6539\u7C7B\u76EE\u3001\u5220\u8BB0\u5F55\u3001\u5165\u5E93\u8BBE\u7F6E\u5728\u5DE6\u4FA7\u300CInbox\u300D\u9762\u677F\u91CC\u3002" })
849
+ ] });
850
+ }
851
+ function DockRecord({ id, revision }) {
852
+ const [entry, setEntry] = import_react4.default.useState();
853
+ const [failed, setFailed] = import_react4.default.useState(false);
854
+ import_react4.default.useEffect(() => {
855
+ let live = true;
856
+ setEntry(void 0);
857
+ setFailed(false);
858
+ void (async () => {
859
+ try {
860
+ const response = await fetch(`${INBOX_API_PREFIX}/${INBOX_ENDPOINT_DETAIL}`, {
861
+ method: "POST",
862
+ headers: { "content-type": "application/json" },
863
+ body: JSON.stringify({ id })
864
+ });
865
+ const answer = await response.json();
866
+ if (!live) return;
867
+ if (answer.ok) setEntry(answer.value.entry);
868
+ else setFailed(true);
869
+ } catch {
870
+ if (live) setFailed(true);
871
+ }
872
+ })();
873
+ return () => {
874
+ live = false;
875
+ };
876
+ }, [id, revision]);
877
+ const clause = { margin: "6px 0 0", overflowWrap: "anywhere" };
878
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { padding: "10px 12px", fontSize: 13 }, children: [
879
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }, children: [
880
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("strong", { style: { minWidth: 0, overflowWrap: "anywhere" }, children: "\u4ED3\u5E93" }),
881
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
882
+ "button",
883
+ {
884
+ type: "button",
885
+ onClick: () => openVaultDock?.(),
886
+ title: "\u56DE\u5230\u6700\u8FD1\u8BB0\u5F55",
887
+ style: {
888
+ marginLeft: "auto",
889
+ font: "inherit",
890
+ fontSize: 12,
891
+ padding: "2px 8px",
892
+ borderRadius: 999,
893
+ border: "1px solid color-mix(in srgb, currentColor 25%, transparent)",
894
+ background: "transparent",
895
+ color: "inherit",
896
+ cursor: "pointer"
897
+ },
898
+ children: "\u8FD4\u56DE\u6700\u8FD1"
899
+ }
900
+ )
901
+ ] }),
902
+ failed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { opacity: 0.7 }, children: "\u8BFB\u4E0D\u5230\u8FD9\u6761\u8BB0\u5F55\uFF0C\u5B83\u53EF\u80FD\u5DF2\u7ECF\u88AB\u5220\u6389\u4E86\u3002" }),
903
+ entry === void 0 && !failed && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { opacity: 0.6 }, children: "\u8BFB\u53D6\u4E2D\u2026" }),
904
+ entry !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
905
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { overflowWrap: "anywhere", fontWeight: 500 }, children: headingOf(entry) }),
906
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { fontSize: 12, opacity: 0.6, marginTop: 2 }, children: [
907
+ KIND_LABELS[entry.kind],
908
+ " \xB7 ",
909
+ CATEGORY_LABELS[entry.category],
910
+ entry.watchLater ? " \xB7 \u5F85\u770B" : "",
911
+ ` \xB7 ${new Date(entry.createdAt).toLocaleString()}`
912
+ ] }),
913
+ entry.note !== void 0 && entry.note.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { style: clause, children: [
914
+ "\u5907\u6CE8\uFF1A",
915
+ entry.note
916
+ ] }),
917
+ entry.url !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: clause, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("a", { href: entry.url, target: "_blank", rel: "noreferrer", children: entry.url }) }),
918
+ entry.attachments.map((attachment) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { marginTop: 8 }, children: attachment.image ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
919
+ "img",
920
+ {
921
+ src: `${INBOX_API_PREFIX}/${INBOX_ENDPOINT_ATTACHMENT}?id=${encodeURIComponent(attachment.id)}`,
922
+ alt: attachment.filename ?? "",
923
+ style: { maxWidth: "100%", borderRadius: 6, display: "block" }
924
+ }
925
+ ) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { opacity: 0.7 }, children: [
926
+ "\u{1F4C4} ",
927
+ attachment.filename ?? attachment.mime,
928
+ ` \xB7 ${String(Math.round(attachment.bytes / 1024))} KB`
929
+ ] }) }, attachment.id)),
930
+ entry.text !== void 0 && entry.text.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
931
+ "pre",
932
+ {
933
+ style: {
934
+ margin: "10px 0 0",
935
+ padding: 8,
936
+ maxHeight: 260,
937
+ overflow: "auto",
938
+ borderRadius: 8,
939
+ border: "1px solid color-mix(in srgb, currentColor 15%, transparent)",
940
+ font: "12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace",
941
+ whiteSpace: "pre-wrap"
942
+ },
943
+ children: entry.text
944
+ }
945
+ ),
946
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: { opacity: 0.6, marginTop: 10 }, children: "\u6539\u7C7B\u76EE\u3001\u6539\u5907\u6CE8\u3001\u5220\u9664\u90FD\u5728\u5DE6\u4FA7\u300CInbox\u300D\u9762\u677F\u91CC\u3002" })
947
+ ] })
948
+ ] });
949
+ }
950
+
951
+ // src/client/card.tsx
952
+ var import_jsx_runtime2 = require("react/jsx-runtime");
953
+ var MARKER = /\[attachment:([A-Za-z0-9_-]+)\]/g;
954
+ var URL_PATTERN = /(https?:\/\/[^\s<>()]+)/g;
955
+ var RECORD_ID = /id: ([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})/g;
956
+ function resultText(block) {
957
+ if (typeof block.text === "string" && block.text.length > 0) return block.text;
958
+ if (!Array.isArray(block.content)) return void 0;
959
+ const parts = [];
960
+ for (const part of block.content) {
961
+ if (typeof part !== "object" || part === null) continue;
962
+ const candidate = part;
963
+ if (candidate.type === "text" && typeof candidate.text === "string") parts.push(candidate.text);
964
+ }
965
+ return parts.length === 0 ? void 0 : parts.join("\n");
966
+ }
967
+ function RecordLink({ id }) {
968
+ if (openVaultDock === void 0) return null;
969
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
970
+ "button",
971
+ {
972
+ type: "button",
973
+ title: "\u5728\u53F3\u4FA7\u300C\u4ED3\u5E93\u300D\u91CC\u6253\u5F00\u8FD9\u6761",
974
+ onClick: () => openVaultDock?.(id),
975
+ style: {
976
+ font: "inherit",
977
+ marginLeft: 6,
978
+ padding: "0 8px",
979
+ borderRadius: 999,
980
+ border: "1px solid color-mix(in srgb, currentColor 25%, transparent)",
981
+ background: "color-mix(in srgb, currentColor 8%, transparent)",
982
+ color: "inherit",
983
+ cursor: "pointer"
984
+ },
985
+ children: "\u6253\u5F00 \u2197"
986
+ }
987
+ );
988
+ }
989
+ function withLinks(text, keyPrefix) {
990
+ const nodes = [];
991
+ let last = 0;
992
+ let match;
993
+ for (const chunk of splitOnRecordIds(text, keyPrefix)) {
994
+ if (typeof chunk !== "string") {
995
+ nodes.push(chunk);
996
+ continue;
997
+ }
998
+ URL_PATTERN.lastIndex = 0;
999
+ last = 0;
1000
+ while ((match = URL_PATTERN.exec(chunk)) !== null) {
1001
+ if (match.index > last) nodes.push(chunk.slice(last, match.index));
1002
+ const url = match[0];
1003
+ nodes.push(
1004
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("a", { href: url, target: "_blank", rel: "noreferrer", children: url }, `${keyPrefix}-${String(match.index)}`)
1005
+ );
1006
+ last = match.index + url.length;
1007
+ }
1008
+ if (last < chunk.length) nodes.push(chunk.slice(last));
1009
+ }
1010
+ return nodes;
1011
+ }
1012
+ function splitOnRecordIds(text, keyPrefix) {
1013
+ const parts = [];
1014
+ let last = 0;
1015
+ RECORD_ID.lastIndex = 0;
1016
+ let match;
1017
+ while ((match = RECORD_ID.exec(text)) !== null) {
1018
+ if (match.index > last) parts.push(text.slice(last, match.index));
1019
+ const id = match[1];
1020
+ parts.push(
1021
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("span", { style: { opacity: 0.55, fontSize: 12 }, children: [
1022
+ "id: ",
1023
+ id
1024
+ ] }, `${keyPrefix}-r${String(match.index)}`),
1025
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(RecordLink, { id: id ?? "" }, `${keyPrefix}-open${String(match.index)}`)
1026
+ );
1027
+ last = match.index + match[0].length;
1028
+ }
1029
+ if (last < text.length) parts.push(text.slice(last));
1030
+ return parts;
1031
+ }
1032
+ function Thumbnail({ id }) {
1033
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
1034
+ "img",
1035
+ {
1036
+ src: `${INBOX_API_PREFIX}/${INBOX_ENDPOINT_ATTACHMENT}?id=${encodeURIComponent(id)}`,
1037
+ alt: "",
1038
+ style: {
1039
+ maxWidth: 220,
1040
+ maxHeight: 220,
1041
+ borderRadius: 6,
1042
+ display: "block",
1043
+ margin: "6px 0"
1044
+ }
1045
+ }
1046
+ );
1047
+ }
1048
+ function render(text) {
1049
+ const nodes = [];
1050
+ let last = 0;
1051
+ let match;
1052
+ MARKER.lastIndex = 0;
1053
+ while ((match = MARKER.exec(text)) !== null) {
1054
+ const before = text.slice(last, match.index);
1055
+ if (before.length > 0) nodes.push(...withLinks(before, `t${String(last)}`));
1056
+ const id = match[1];
1057
+ if (id !== void 0) nodes.push(/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Thumbnail, { id }, `a-${id}`));
1058
+ last = match.index + match[0].length;
1059
+ }
1060
+ const tail = text.slice(last);
1061
+ if (tail.length > 0) nodes.push(...withLinks(tail, `t${String(last)}`));
1062
+ return nodes;
1063
+ }
1064
+ function InboxToolCard({ toolName, block }) {
1065
+ const text = resultText(block);
1066
+ const isError = block.isError === true;
1067
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
1068
+ "div",
1069
+ {
1070
+ style: {
1071
+ border: "1px solid color-mix(in srgb, currentColor 18%, transparent)",
1072
+ borderRadius: 10,
1073
+ padding: "10px 12px",
1074
+ font: "13px/1.6 system-ui, sans-serif",
1075
+ overflowWrap: "anywhere"
1076
+ },
1077
+ children: [
1078
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { style: { opacity: 0.65, marginBottom: 6 }, children: [
1079
+ toolName === "inbox_search" ? "\u{1F5C2} dsh-inbox \xB7 \u641C\u7D22\u4ED3\u5E93" : "\u{1F5C2} dsh-inbox \xB7 \u6253\u5F00\u8BB0\u5F55",
1080
+ isError ? " \xB7 \u51FA\u9519" : ""
1081
+ ] }),
1082
+ text === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { opacity: 0.6 }, children: "\uFF08\u8FD9\u6761\u8C03\u7528\u8FD8\u6CA1\u6709\u7ED3\u679C\uFF09" }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { style: { whiteSpace: "pre-wrap" }, children: render(text) })
1083
+ ]
1084
+ }
1085
+ );
1086
+ }
1087
+ function registerToolCards(slots) {
1088
+ for (const key of ["inbox_search", "inbox_get"]) {
1089
+ slots.inject(
1090
+ "tool.call.toolview",
1091
+ () => slots.register({ name: "tool.call.toolview", key }, InboxToolCard)
1092
+ );
1093
+ }
1094
+ }
1095
+
1096
+ // src/client/manual.tsx
1097
+ var import_react6 = require("react");
1098
+ var import_jsx_runtime3 = require("react/jsx-runtime");
1099
+ function Section({
1100
+ title,
1101
+ children
1102
+ }) {
1103
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("section", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [
1104
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { children: title }),
1105
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { style: { display: "flex", flexDirection: "column", gap: 2, opacity: 0.85 }, children })
1106
+ ] });
1107
+ }
1108
+ function Line({ label, children }) {
1109
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("p", { style: { margin: 0 }, children: [
1110
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { style: { fontWeight: 600 }, children: label }),
1111
+ label.length === 0 ? "" : "\uFF1A",
1112
+ children
1113
+ ] });
1114
+ }
1115
+ function ManualDialog({ onClose }) {
1116
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1117
+ "div",
1118
+ {
1119
+ role: "dialog",
1120
+ "aria-label": "\u4F7F\u7528\u624B\u518C",
1121
+ style: {
1122
+ position: "fixed",
1123
+ inset: 0,
1124
+ zIndex: 45,
1125
+ background: "color-mix(in srgb, #000 55%, transparent)",
1126
+ display: "flex",
1127
+ alignItems: "center",
1128
+ justifyContent: "center",
1129
+ padding: "4vh 16px"
1130
+ },
1131
+ onClick: (event) => {
1132
+ if (event.target === event.currentTarget) onClose();
1133
+ },
1134
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1135
+ "div",
1136
+ {
1137
+ style: {
1138
+ /*
1139
+ A fixed width and a bounded height, with the *sections* scrolling.
1140
+ The first version let the card grow as tall as its content and put
1141
+ the scroll on the backdrop instead — which on a laptop meant the
1142
+ manual ran off the bottom of the screen with no visible frame.
1143
+ */
1144
+ width: "min(560px, 100%)",
1145
+ maxHeight: "92vh",
1146
+ // Border inside the width and the cap: without it the card is 2px
1147
+ // wider than the number and 2px taller than the overlay's room.
1148
+ boxSizing: "border-box",
1149
+ background: "Canvas",
1150
+ color: "CanvasText",
1151
+ border: "1px solid color-mix(in srgb, currentColor 18%, transparent)",
1152
+ borderRadius: 12,
1153
+ display: "flex",
1154
+ flexDirection: "column",
1155
+ boxShadow: "0 18px 40px #0007",
1156
+ overflow: "hidden"
1157
+ },
1158
+ children: [
1159
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1160
+ "div",
1161
+ {
1162
+ style: {
1163
+ display: "flex",
1164
+ alignItems: "baseline",
1165
+ gap: 8,
1166
+ padding: "14px 18px 12px",
1167
+ borderBottom: "1px solid color-mix(in srgb, currentColor 12%, transparent)"
1168
+ },
1169
+ children: [
1170
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("strong", { style: { fontSize: 16 }, children: "dsh-inbox \u600E\u4E48\u7528" }),
1171
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { style: { opacity: 0.6, fontSize: 12 }, children: "\u4E09\u5206\u949F\u770B\u5B8C" }),
1172
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
1173
+ "button",
1174
+ {
1175
+ type: "button",
1176
+ onClick: onClose,
1177
+ style: {
1178
+ marginLeft: "auto",
1179
+ font: "inherit",
1180
+ padding: "4px 10px",
1181
+ borderRadius: 8,
1182
+ border: "1px solid color-mix(in srgb, currentColor 25%, transparent)",
1183
+ background: "transparent",
1184
+ color: "inherit",
1185
+ cursor: "pointer"
1186
+ },
1187
+ children: "\u5173\u95ED"
1188
+ }
1189
+ )
1190
+ ]
1191
+ }
1192
+ ),
1193
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
1194
+ "div",
1195
+ {
1196
+ style: {
1197
+ padding: 18,
1198
+ display: "flex",
1199
+ flexDirection: "column",
1200
+ gap: 14,
1201
+ overflowY: "auto",
1202
+ minHeight: 0
1203
+ },
1204
+ children: [
1205
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { title: "1. \u5F80\u91CC\u5B58", children: [
1206
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u9762\u677F", children: "\u7C98\u8D34\u6587\u5B57/\u94FE\u63A5\u3001\u62D6\u8FDB\u56FE\u7247\u6587\u4EF6\uFF0C\u6216\u70B9\u300C\u9009\u62E9\u6587\u4EF6\u2026\u300D\uFF0C\u7136\u540E\u300C\u5B58\u5165\u4ED3\u5E93\u300D\uFF08Ctrl+Enter \u4E5F\u884C\uFF09\u3002" }),
1207
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Line, { label: "\u5BF9\u8BDD\u91CC", children: [
1208
+ "\u8F93\u5165 ",
1209
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("code", { children: "/inbox \u6587\u5B57\u6216\u94FE\u63A5" }),
1210
+ "\uFF0C\u56FE\u7247\u76F4\u63A5\u9644\u5728\u8F93\u5165\u6846\u4E0A\uFF1B\u4E0D\u53D1\u7ED9\u6A21\u578B\u3002"
1211
+ ] }),
1212
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u91CD\u590D", children: "\u540C\u4E00\u6837\u4E1C\u897F\u518D\u5B58\u4E00\u6B21\u4E0D\u4F1A\u65B0\u589E\uFF0C\u4F1A\u5E76\u8FDB\u539F\u8BB0\u5F55\uFF1B\u5982\u679C\u5B83\u4E4B\u524D\u5728\u56DE\u6536\u7AD9\uFF0C\u4F1A\u987A\u624B\u53D6\u56DE\u6765\u3002" })
1213
+ ] }),
1214
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { title: "2. \u600E\u4E48\u7FFB\u3001\u600E\u4E48\u6539", children: [
1215
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u7B5B\u9009", children: "\u5DE6\u4FA7\u300C\u5168\u90E8 / \u5F85\u770B / \u56DE\u6536\u7AD9\u300D\u4E0E\u7C7B\u76EE\u3001\u6807\u7B7E\u4E92\u65A5\uFF1B\u300C\u5F85\u770B\u300D\u662F\u4F60\u81EA\u5DF1\u6253\u7684\u6807\u8BB0\uFF0C\u65B0\u8BB0\u5F55\u4E0D\u5E26\u4EFB\u4F55\u6807\u8BB0\u3002" }),
1216
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u5217\u8868", children: "\u4E24\u79CD\u5BC6\u5EA6\uFF08\u4E24\u5217 / \u7D27\u51D1\uFF09\u968F\u624B\u5207\uFF0C\u4F1A\u8BB0\u4F4F\uFF1B\u4E0A\u9762\u641C\u7D22\u6846\u641C\u6807\u9898\u3001\u6B63\u6587\u3001\u94FE\u63A5\u3001\u5907\u6CE8\u3002" }),
1217
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u8BE6\u60C5", children: "\u53F3\u8FB9\u4E00\u680F\u53EF\u4EE5\u6539\u540D\u79F0\u3001\u7C7B\u76EE\u3001\u5907\u6CE8\u3001\u6807\u7B7E\uFF0C\u4E5F\u80FD\u6807\u5F85\u770B\u3001\u5220\u9664\u3001\u6062\u590D\u3002" })
1218
+ ] }),
1219
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { title: "3. \u5217\u8868\u91CC\u663E\u793A\u7684\u540D\u5B57\u662F\u54EA\u513F\u6765\u7684", children: [
1220
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u987A\u5E8F", children: "\u4F60\u8D77\u7684\u540D\u79F0 \u2192 \u6293\u6765\u7684\u9875\u9762\u6807\u9898 \u2192 \u94FE\u63A5/\u6B63\u6587/\u6587\u4EF6\u540D \u2192 \u5907\u6CE8\uFF08\u6700\u540E\u515C\u5E95\uFF09\u3002" }),
1221
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Line, { label: "\u7C7B\u76EE\u56FE\u6807", children: [
1222
+ Object.values(CATEGORY_LABELS).join(" / "),
1223
+ " \u5404\u6709\u56FE\u6807\uFF1B\u5BC6\u94A5/\u8D26\u5BC6\u662F\u94A5\u5319\uFF0C\u4E00\u773C\u80FD\u8BA4\u51FA\u6765\u3002"
1224
+ ] }),
1225
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Line, { label: "\u8C01\u5224\u7684", children: [
1226
+ "\u7C7B\u76EE\u65C1\u7684\u6807\u7B7E\u5206\u4E09\u79CD\uFF1A",
1227
+ Object.values(CATEGORY_SOURCE_LABELS).join(" / "),
1228
+ "\uFF08\u4F60\u9009\u8FC7\u7684\u7C7B\u76EE\uFF0C\u89C4\u5219\u548C\u6A21\u578B\u90FD\u4E0D\u4F1A\u8986\u76D6\uFF09\u3002"
1229
+ ] })
1230
+ ] }),
1231
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { title: "4. \u5BC6\u94A5\u4E0E\u8D26\u5BC6", children: [
1232
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u5148\u8BBE\u4E3B\u5BC6\u7801", children: "\u8BBE\u7F6E \u2192 \u8D26\u5BC6\u52A0\u5BC6\u3002\u8BBE\u4E86\u4E4B\u540E\uFF0C\u8D26\u5BC6\u6B63\u6587\u4EE5\u5BC6\u6587\u5199\u76D8\uFF1B\u6CA1\u8BBE\u7684\u65F6\u5019\uFF0C\u8D26\u5BC6\u4E0D\u4F1A\u88AB\u5B58\u8FDB\u53BB\uFF08\u5B81\u53EF\u4E0D\u5B58\uFF0C\u4E5F\u4E0D\u5199\u660E\u6587\uFF09\u3002" }),
1233
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u6BCF\u6B21\u91CD\u542F\u8981\u89E3\u9501", children: "\u4E3B\u5BC6\u7801\u548C\u5BC6\u94A5\u90FD\u4E0D\u843D\u76D8\uFF0C\u6240\u4EE5\u670D\u52A1\u4E00\u91CD\u542F\u5C31\u8981\u5728\u540C\u4E00\u4E2A\u5730\u65B9\u89E3\u9501\u4E00\u6B21\uFF1B\u5BC6\u7801\u5FD8\u4E86\u5C31\u89E3\u4E0D\u5F00\uFF0C\u6CA1\u6709\u627E\u56DE\u3002" }),
1234
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u6C38\u4E0D\u5916\u663E", children: "\u5217\u8868\u91CC\u53EA\u663E\u793A\u4F60\u8D77\u7684\u540D\u5B57\uFF1B\u5BF9\u8BDD\u91CC\u53EA\u56DE\u4E00\u53E5\u300C\u660E\u6587\u4E0D\u4F1A\u901A\u8FC7\u5BF9\u8BDD\u8F93\u51FA\u300D\uFF1B\u53D1\u7ED9\u6A21\u578B\u7684\u5206\u7C7B\u8BF7\u6C42\u5148\u8131\u654F\u3002" })
1235
+ ] }),
1236
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { title: "5. \u540C\u6B65\uFF08\u672C\u673A \u2194 \u4E91\u76D8\uFF09", children: [
1237
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u81EA\u52A8", children: "\u5165\u5E93/\u6539\u52A8\u540E\u51E0\u79D2\u81EA\u52A8\u63A8\u9001\u4E00\u6B21\uFF08\u9632\u6296\uFF0C\u8FDE\u7740\u5B58\u4E94\u6761\u53EA\u4F1A\u63A8\u4E00\u6B21\uFF09\u3002" }),
1238
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u624B\u52A8", children: "\u53F3\u4E0A\u89D2\u300C\u5237\u65B0\u300D= \u4E00\u6B21\u5B8C\u6574\u540C\u6B65\uFF1A\u5148\u63A8\u672C\u673A\u6539\u52A8\uFF0C\u518D\u62C9\u522B\u4EBA\u7684\uFF0C\u7136\u540E\u91CD\u8BFB\u5217\u8868\u3002" }),
1239
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u5220\u9664", children: "\u9762\u677F\u91CC\u7684\u300C\u5220\u9664\u300D\u53EA\u662F\u628A\u5B83\u653E\u8FDB\u56DE\u6536\u7AD9\uFF08\u522B\u7684\u8BBE\u5907\u4E5F\u4F1A\u77E5\u9053\u5B83\u88AB\u5220\u4E86\uFF0C\u4E0D\u4F1A\u53C8\u88AB\u62C9\u56DE\u6765\uFF09\uFF1B \u300C\u6E05\u7A7A\u56DE\u6536\u7AD9\u300D\u624D\u662F\u771F\u5220\uFF0C\u4E91\u7AEF\u90A3\u4EFD\u4E5F\u4F1A\u4E00\u8D77\u5220\u3002" }),
1240
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u51B2\u7A81", children: "\u591A\u8BBE\u5907\u6539\u540C\u4E00\u6761\u6309\u65F6\u95F4\u540E\u5199\u8D62\uFF0C\u4E0D\u7559\u51B2\u7A81\u526F\u672C\u3002\u4E0A\u4E91\u7684\u5BC6\u6587\u53EA\u6709\u8D26\u5BC6\u6B63\u6587\uFF0C\u5176\u4F59\u662F\u660E\u6587\u2014\u2014\u6876\u52A1\u5FC5\u8981\u6709\u8BBF\u95EE\u63A7\u5236\u3002" })
1241
+ ] }),
1242
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { title: "6. \u5728\u5BF9\u8BDD\u91CC\u53D6\u56DE\u6765", children: [
1243
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u67E5", children: "\u5728\u5BF9\u8BDD\u91CC\u76F4\u63A5\u95EE\uFF1A\u300C\u6211\u7684\u6536\u4EF6\u7BB1\u91CC\u6709\u54EA\u4E9B\u8FD8\u6CA1\u770B\u7684\u94FE\u63A5\uFF1F\u300D\u52A9\u624B\u4F1A\u53BB\u4ED3\u5E93\u91CC\u627E\uFF0C \u6700\u591A\u5217 10 \u6761\uFF0C\u5E76\u544A\u8BC9\u4F60\u8FD8\u5269\u51E0\u6761\u3002\u53EB\u5B83\u300C\u6536\u4EF6\u7BB1\u300D\u300C\u4ED3\u5E93\u300D\u300C\u4E2A\u4EBA\u4ED3\u5E93\u300D\u8FD8\u662F\u300Cinbox\u300D\u90FD\u8BA4\u3002" }),
1244
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u53D6", children: "\u63A5\u7740\u8BF4\u300C\u6253\u5F00\u7B2C 3 \u6761\u300D\uFF0C\u52A9\u624B\u5C31\u628A\u90A3\u6761\u62FF\u56DE\u6765\uFF1A\u6B63\u6587\uFF08\u6700\u591A 1000 \u5B57\uFF09\u3001\u94FE\u63A5\u3001\u5907\u6CE8\u3001\u6807\u7B7E\u3001\u9644\u4EF6\u4FE1\u606F\u3002" }),
1245
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u4E24\u6761\u786C\u62D2\u7EDD", children: "\u8D26\u5BC6\u6C38\u4E0D\u56DE\u660E\u6587\uFF1B\u56FE\u7247\u9ED8\u8BA4\u53EA\u56DE\u4E00\u4E2A\u6807\u8BB0\uFF0C\u7531\u754C\u9762\u5728\u672C\u673A\u753B\u51FA\u6765\u3002\u60F3\u8BA9\u52A9\u624B\u4EB2\u773C\u770B\u56FE\uFF08\u6BD4\u5982\u300C\u8FD9\u5F20\u662F\u4EC0\u4E48\u7801\u300D\uFF09\uFF0C \u76F4\u63A5\u8BF4\u300C\u5E2E\u6211\u770B\u8FD9\u5F20\u56FE\u300D\u2014\u2014\u90A3\u4E00\u6B21\u624D\u4F1A\u628A\u8FD9\u5F20\u56FE\u53D1\u7ED9\u5B83\u3002" }),
1246
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u52A9\u624B\u8BF4\u4E0D\u8BA4\u8BC6\u8FD9\u4E2A\u4ED3\u5E93", children: "\u8BF4\u660E\u8FD9\u4E2A\u4F1A\u8BDD\u6CA1\u5E26\u4E0A\u6536\u4EF6\u7BB1\u63D2\u4EF6\u3002\u65B0\u5F00\u4E00\u4E2A\u4F1A\u8BDD\u518D\u95EE\u4E00\u6B21\u5373\u53EF\u2014\u2014\u9762\u677F\u80FD\u7528\u3001\u52A9\u624B\u770B\u4E0D\u5230\uFF0C \u8FD9\u4E24\u4EF6\u4E8B\u662F\u5206\u5F00\u7684\u3002" })
1247
+ ] }),
1248
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Section, { title: "7. \u51FA\u95EE\u9898\u5148\u770B\u8FD9\u91CC", children: [
1249
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u94FE\u63A5\u6CA1\u540D\u5B57", children: "\u6B63\u5E38\uFF1A\u6709\u4E9B\u7AD9\u70B9\uFF08\u5982\u5FAE\u4FE1\uFF09\u5BF9\u975E\u6D4F\u89C8\u5668\u8BF7\u6C42\u53EA\u56DE\u7A7A\u58F3\u9875\uFF0C\u6293\u4E0D\u5230\u6807\u9898\uFF1B\u70B9\u8FDB\u8BE6\u60C5\u81EA\u5DF1\u8D77\u4E2A\u540D\u5B57\u5373\u53EF\u3002" }),
1250
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u4E91\u76D8\u91CC\u6709 0 \u5B57\u8282\u76EE\u5F55", children: "\u4E91\u76D8\u81EA\u5DF1\u5EFA\u7684\u5360\u4F4D\u5BF9\u8C61\uFF0C\u4E0D\u662F\u63D2\u4EF6\u5199\u7684\uFF0C\u53EF\u4EE5\u5FFD\u7565\u3002" }),
1251
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Line, { label: "\u540C\u6B65\u4E0D\u5BF9", children: "\u8BBE\u7F6E\u91CC\u70B9\u300C\u81EA\u68C0\u300D\uFF1A\u4F1A\u544A\u8BC9\u4F60\u901A\u9053\u901A\u4E0D\u901A\u3001\u54EA\u79CD\u7B7E\u540D\u53EF\u7528\uFF1B\u8BA4\u8BC1\u88AB\u62D2\u901A\u5E38\u662F\u300C\u5BA2\u6237\u7AEF\u6807\u8BC6\u300D\u4E0E AccessKey \u7ED1\u5B9A\u7684\u5E94\u7528\u4E0D\u4E00\u81F4\u3002" })
1252
+ ] })
1253
+ ]
1254
+ }
1255
+ )
1256
+ ]
1257
+ }
1258
+ )
1259
+ }
1260
+ );
1261
+ }
1262
+
1263
+ // src/client/scheme.ts
1264
+ function brightnessOf(cssColor) {
1265
+ const match = /rgba?\(\s*(\d+)[,\s]+(\d+)[,\s]+(\d+)/.exec(cssColor);
1266
+ if (match === null) return void 0;
1267
+ const [r, g, b] = [Number(match[1]), Number(match[2]), Number(match[3])];
1268
+ if ([r, g, b].some((value) => Number.isNaN(value))) return void 0;
1269
+ return 0.299 * r + 0.587 * g + 0.114 * b;
1270
+ }
1271
+ function schemeOfColor(cssColor) {
1272
+ const brightness = brightnessOf(cssColor);
1273
+ if (brightness === void 0) return "dark";
1274
+ return brightness > 140 ? "dark" : "light";
1275
+ }
1276
+ function schemeFrom(declared, inheritedTextColor) {
1277
+ const single = declared.trim().toLowerCase();
1278
+ if (single === "light" || single === "dark") return single;
1279
+ return schemeOfColor(inheritedTextColor);
1280
+ }
1281
+ function schemeOf(element) {
1282
+ if (typeof document === "undefined" || typeof getComputedStyle !== "function") return "dark";
1283
+ const root = document.documentElement;
1284
+ const declared = root === null ? "" : getComputedStyle(root).colorScheme;
1285
+ const target = element ?? document.body;
1286
+ if (target === null || target === void 0) return schemeFrom(declared, "");
1287
+ return schemeFrom(declared, getComputedStyle(target).color);
1288
+ }
1289
+
1290
+ // src/client/index.tsx
1291
+ var import_jsx_runtime4 = require("react/jsx-runtime");
1292
+ var name = "dsh-inbox-client";
1293
+ var inject = ["slots"];
1294
+ function apply(ctx) {
1295
+ const slots = ctx.get("slots");
1296
+ if (slots === void 0) return;
1297
+ slots.inject(
1298
+ "sidebar.panellist",
1299
+ () => slots.register(
1300
+ { name: "sidebar.panellist", id: PANEL_ID, order: 20, label: "Inbox" },
1301
+ InboxPanelIcon
1302
+ )
1303
+ );
1304
+ slots.inject("main", () => slots.register({ name: "main", key: PANEL_ID }, InboxPanel));
1305
+ registerToolCards(slots);
1306
+ registerInboxDock(ctx);
1307
+ }
1308
+ function InboxPanelIcon({ size, active }) {
1309
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1310
+ "svg",
1311
+ {
1312
+ width: size,
1313
+ height: size,
1314
+ viewBox: "0 0 24 24",
1315
+ fill: "none",
1316
+ stroke: "currentColor",
1317
+ strokeWidth: active ? 2 : 1.6,
1318
+ strokeLinecap: "round",
1319
+ strokeLinejoin: "round",
1320
+ "aria-hidden": "true",
1321
+ children: [
1322
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M3 12h5l2 3h4l2-3h5" }),
1323
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { d: "M5 5h14l2 7v7H3v-7z" })
1324
+ ]
1325
+ }
1326
+ );
1327
+ }
1328
+ var panelStyle = {
1329
+ padding: "16px 20px",
1330
+ font: "14px/1.6 system-ui, sans-serif",
1331
+ display: "flex",
1332
+ flexDirection: "column",
1333
+ gap: 12,
1334
+ height: "100%",
1335
+ minHeight: 0,
1336
+ boxSizing: "border-box",
1337
+ // No page scroll: the list is the only thing that scrolls, so it gets the
1338
+ // whole screen minus the chrome above it and the pager can sit at its foot.
1339
+ overflow: "hidden"
1340
+ };
1341
+ var actionStyle = {
1342
+ display: "inline-flex",
1343
+ alignItems: "center",
1344
+ gap: 6,
1345
+ padding: "6px 12px",
1346
+ borderRadius: 8,
1347
+ border: "1px solid color-mix(in srgb, currentColor 22%, transparent)",
1348
+ background: "color-mix(in srgb, currentColor 9%, transparent)",
1349
+ color: "inherit",
1350
+ cursor: "pointer",
1351
+ fontWeight: 500
1352
+ };
1353
+ var primaryStyle = {
1354
+ ...actionStyle,
1355
+ // Deliberately *not* an inverted fill. `background: currentColor; color:
1356
+ // Canvas` looked right until it rendered white-on-white: `Canvas` is the
1357
+ // canvas colour, which is white in a document that never declared a dark
1358
+ // scheme. Text stays the inherited colour, on a fill strong enough to read as
1359
+ // the primary action.
1360
+ background: "color-mix(in srgb, currentColor 20%, transparent)",
1361
+ borderColor: "color-mix(in srgb, currentColor 55%, transparent)",
1362
+ fontWeight: 600
1363
+ };
1364
+ var dangerStyle = {
1365
+ ...actionStyle,
1366
+ borderColor: "color-mix(in srgb, salmon 60%, transparent)",
1367
+ background: "color-mix(in srgb, salmon 18%, transparent)",
1368
+ color: "salmon",
1369
+ fontWeight: 600
1370
+ };
1371
+ var WATCH_COLOR = "#6e9ef7";
1372
+ var ACCENT_COLOR = "#6e9ef7";
1373
+ var MODEL_COLOR = "#a78bfa";
1374
+ var CONTROL_HEIGHT = "calc(1.6em + 12px)";
1375
+ function titleFailureText(code) {
1376
+ if (code === "no-title") {
1377
+ return "\u90A3\u4E2A\u9875\u9762\u91CC\u6CA1\u6709\u6807\u9898\uFF08\u6709\u4E9B\u7AD9\u70B9\u5BF9\u975E\u6D4F\u89C8\u5668\u7684\u8BF7\u6C42\u53EA\u56DE\u7A7A\u58F3\u9875\uFF0C\u6BD4\u5982\u5FAE\u4FE1\uFF09";
1378
+ }
1379
+ if (code.startsWith("http:")) return `\u5BF9\u65B9\u8FD4\u56DE HTTP ${code.slice("http:".length)}`;
1380
+ if (code.startsWith("not-html:")) return "\u90A3\u4E2A\u5730\u5740\u4E0D\u662F\u7F51\u9875";
1381
+ if (code.startsWith("network:")) return "\u8BF7\u6C42\u6CA1\u6210\u529F\uFF08\u7F51\u7EDC\u4E0D\u901A\u6216\u5BF9\u65B9\u62D2\u7EDD\uFF09";
1382
+ return "\u539F\u56E0\u4E0D\u660E";
1383
+ }
1384
+ var paneRowStyle = { flex: "none" };
1385
+ var selectStyle = {
1386
+ ...actionStyle,
1387
+ // Opaque on purpose: a translucent background is why the popup's options
1388
+ // stayed white-on-white. `appearance: none` lets us draw the caret instead of
1389
+ // letting the browser park it against the border.
1390
+ appearance: "none",
1391
+ paddingRight: 26,
1392
+ background: "Canvas",
1393
+ color: "CanvasText",
1394
+ // Inherited from the panel root, which declares the app's own scheme.
1395
+ colorScheme: "inherit"
1396
+ };
1397
+ function SelectBox({
1398
+ value,
1399
+ options,
1400
+ disabled,
1401
+ block,
1402
+ label,
1403
+ onChange
1404
+ }) {
1405
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1406
+ "span",
1407
+ {
1408
+ style: {
1409
+ position: "relative",
1410
+ display: block === true ? "flex" : "inline-flex",
1411
+ alignItems: "center",
1412
+ // A control in a scrolling column keeps its own height (see
1413
+ // `paneRowStyle`); without this the popup's box squeezes to nothing.
1414
+ flex: "none",
1415
+ ...block === true ? { width: "100%" } : {}
1416
+ },
1417
+ children: [
1418
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1419
+ "select",
1420
+ {
1421
+ value,
1422
+ disabled,
1423
+ "aria-label": label,
1424
+ onChange: (event) => onChange(event.target.value),
1425
+ style: { ...selectStyle, ...block === true ? { flex: 1, minWidth: 0 } : {} },
1426
+ children: options.map(([id, label2]) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: id, children: label2 }, id))
1427
+ }
1428
+ ),
1429
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1430
+ ChevronDown,
1431
+ {
1432
+ size: 13,
1433
+ style: { position: "absolute", right: 9, pointerEvents: "none", opacity: 0.7 }
1434
+ }
1435
+ )
1436
+ ]
1437
+ }
1438
+ );
1439
+ }
1440
+ var cardStyle = {
1441
+ border: "1px solid color-mix(in srgb, currentColor 18%, transparent)",
1442
+ borderRadius: 10,
1443
+ padding: 12
1444
+ };
1445
+ var buttonStyle = {
1446
+ font: "inherit",
1447
+ padding: "5px 12px",
1448
+ borderRadius: 8,
1449
+ border: "1px solid color-mix(in srgb, currentColor 25%, transparent)",
1450
+ background: "transparent",
1451
+ color: "inherit",
1452
+ cursor: "pointer",
1453
+ /*
1454
+ A flex row rather than an inline box.
1455
+
1456
+ Every one of these buttons is either a bare glyph or a glyph next to a word,
1457
+ and an inline SVG lands on the text baseline: the icon reads as sitting low
1458
+ in its own frame. It is the same defect the pager and the list-mode buttons
1459
+ were fixed for one at a time (M7.12 / M7.13); the difference here is that it
1460
+ is fixed once, for all of them.
1461
+ */
1462
+ display: "inline-flex",
1463
+ alignItems: "center",
1464
+ justifyContent: "center",
1465
+ gap: 6
1466
+ };
1467
+ var pagerButtonStyle = {
1468
+ ...buttonStyle,
1469
+ whiteSpace: "nowrap"
1470
+ };
1471
+ var inputStyle = {
1472
+ font: "inherit",
1473
+ color: "inherit",
1474
+ background: "transparent",
1475
+ border: "1px solid color-mix(in srgb, currentColor 20%, transparent)",
1476
+ borderRadius: 8,
1477
+ padding: "5px 8px"
1478
+ };
1479
+ function InboxPanel() {
1480
+ const [text, setText] = import_react7.default.useState("");
1481
+ const [staged, setStaged] = import_react7.default.useState([]);
1482
+ const [notice, setNotice] = import_react7.default.useState();
1483
+ const [busy, setBusy] = import_react7.default.useState(false);
1484
+ const [dragging, setDragging] = import_react7.default.useState(false);
1485
+ const picker = import_react7.default.useRef(null);
1486
+ const [scope, setScope] = import_react7.default.useState("live");
1487
+ const [watchOnly, setWatchOnly] = import_react7.default.useState(false);
1488
+ const [category, setCategory] = import_react7.default.useState();
1489
+ const [tag, setTag] = import_react7.default.useState();
1490
+ const [search, setSearch] = import_react7.default.useState("");
1491
+ const [query, setQuery] = import_react7.default.useState("");
1492
+ const [list, setList] = import_react7.default.useState();
1493
+ const [selectedId, setSelectedId] = import_react7.default.useState();
1494
+ const [detail, setDetail] = import_react7.default.useState();
1495
+ const [settingsOpen, setSettingsOpen] = import_react7.default.useState(false);
1496
+ const [manualOpen, setManualOpen] = import_react7.default.useState(false);
1497
+ const [listMode, setListMode] = import_react7.default.useState("grid");
1498
+ const [page, setPage] = import_react7.default.useState(0);
1499
+ const [zoom, setZoom] = import_react7.default.useState();
1500
+ const [panelWidth, setPanelWidth] = import_react7.default.useState(1200);
1501
+ const [railOpen, setRailOpen] = import_react7.default.useState(false);
1502
+ const panelRef = import_react7.default.useRef(null);
1503
+ const [scheme, setScheme] = import_react7.default.useState(() => schemeOf(null));
1504
+ import_react7.default.useEffect(() => {
1505
+ const element = panelRef.current;
1506
+ if (element === null || typeof ResizeObserver === "undefined") return;
1507
+ const observer = new ResizeObserver((entries) => {
1508
+ for (const entry of entries) setPanelWidth(entry.contentRect.width);
1509
+ });
1510
+ observer.observe(element);
1511
+ return () => observer.disconnect();
1512
+ }, []);
1513
+ import_react7.default.useEffect(() => {
1514
+ const read = () => setScheme(schemeOf(panelRef.current));
1515
+ read();
1516
+ if (typeof MutationObserver === "undefined") return;
1517
+ const observer = new MutationObserver(read);
1518
+ observer.observe(document.documentElement, { attributes: true });
1519
+ if (document.body !== null) observer.observe(document.body, { attributes: true });
1520
+ const media = typeof window.matchMedia === "function" ? window.matchMedia("(prefers-color-scheme: dark)") : void 0;
1521
+ media?.addEventListener("change", read);
1522
+ return () => {
1523
+ observer.disconnect();
1524
+ media?.removeEventListener("change", read);
1525
+ };
1526
+ }, []);
1527
+ const narrow = panelWidth < 960;
1528
+ const hairline = "color-mix(in srgb, currentColor 12%, transparent)";
1529
+ const call = import_react7.default.useCallback(
1530
+ async (endpoint, payload) => {
1531
+ try {
1532
+ const response = await fetch(`${INBOX_API_PREFIX}/${endpoint}`, {
1533
+ method: "POST",
1534
+ headers: { "content-type": "application/json" },
1535
+ body: JSON.stringify(payload)
1536
+ });
1537
+ const answer = await response.json();
1538
+ if (!isRpcResult(answer)) {
1539
+ return {
1540
+ ok: false,
1541
+ error: {
1542
+ code: "inbox/malformed-answer",
1543
+ message: `\u5BBF\u4E3B\u8FD4\u56DE\u4E86\u770B\u4E0D\u61C2\u7684\u54CD\u5E94\uFF08HTTP ${String(response.status)}\uFF09`,
1544
+ details: {}
1545
+ }
1546
+ };
1547
+ }
1548
+ return answer;
1549
+ } catch (error) {
1550
+ return {
1551
+ ok: false,
1552
+ error: {
1553
+ code: "inbox/transport",
1554
+ message: error instanceof Error ? error.message : String(error),
1555
+ details: {}
1556
+ }
1557
+ };
1558
+ }
1559
+ },
1560
+ []
1561
+ );
1562
+ const refresh = import_react7.default.useCallback(
1563
+ async (keepSelection = true) => {
1564
+ const result = await call(INBOX_ENDPOINT_LIST, {
1565
+ scope,
1566
+ ...watchOnly ? { watchLater: true } : {},
1567
+ ...category === void 0 ? {} : { categories: [category] },
1568
+ ...tag === void 0 ? {} : { tags: [tag] },
1569
+ ...query.trim().length === 0 ? {} : { text: query.trim() },
1570
+ limit: PAGE_SIZE,
1571
+ offset: page * PAGE_SIZE
1572
+ });
1573
+ if (!result.ok) {
1574
+ setNotice(`\u8BFB\u53D6\u5217\u8868\u5931\u8D25\uFF1A${result.error.message}`);
1575
+ return;
1576
+ }
1577
+ const next = result.value;
1578
+ if (!Array.isArray(next.entries)) {
1579
+ setNotice("\u8BFB\u53D6\u5217\u8868\u5931\u8D25\uFF1A\u5BBF\u4E3B\u8FD4\u56DE\u7684\u5185\u5BB9\u770B\u4E0D\u61C2");
1580
+ return;
1581
+ }
1582
+ setList(next);
1583
+ if (!keepSelection || !next.entries.some((entry) => entry.id === selectedId)) {
1584
+ setSelectedId(void 0);
1585
+ setDetail(void 0);
1586
+ }
1587
+ },
1588
+ [call, category, page, query, scope, selectedId, tag, watchOnly]
1589
+ );
1590
+ import_react7.default.useEffect(() => {
1591
+ void refresh();
1592
+ }, [refresh]);
1593
+ import_react7.default.useEffect(() => {
1594
+ refreshRef.current = refresh;
1595
+ }, [refresh]);
1596
+ import_react7.default.useEffect(() => {
1597
+ setPage(0);
1598
+ }, [scope, watchOnly, category, tag, query]);
1599
+ import_react7.default.useEffect(() => {
1600
+ const onKey = (event) => {
1601
+ if (event.key !== "Escape") return;
1602
+ setZoom(void 0);
1603
+ setSettingsOpen(false);
1604
+ };
1605
+ window.addEventListener("keydown", onKey);
1606
+ return () => window.removeEventListener("keydown", onKey);
1607
+ }, []);
1608
+ import_react7.default.useEffect(() => {
1609
+ if (notice === void 0 || notice.length === 0) return;
1610
+ const timer = window.setTimeout(() => setNotice(void 0), 4e3);
1611
+ return () => window.clearTimeout(timer);
1612
+ }, [notice]);
1613
+ const refreshRef = import_react7.default.useRef(async () => {
1614
+ });
1615
+ import_react7.default.useEffect(() => {
1616
+ void (async () => {
1617
+ const answer = await call(INBOX_ENDPOINT_UI, { action: "read" });
1618
+ if (!answer.ok) return;
1619
+ const prefs = answer.value;
1620
+ if (UI_LIST_MODES.includes(prefs.listMode)) setListMode(prefs.listMode);
1621
+ })();
1622
+ }, [call]);
1623
+ const chooseListMode = import_react7.default.useCallback(
1624
+ (mode) => {
1625
+ setListMode(mode);
1626
+ void call(INBOX_ENDPOINT_UI, { action: "save", listMode: mode });
1627
+ },
1628
+ [call]
1629
+ );
1630
+ const refreshAll = import_react7.default.useCallback(async () => {
1631
+ setBusy(true);
1632
+ setNotice("\u540C\u6B65\u4E2D\u2026");
1633
+ try {
1634
+ const pushed = await call(INBOX_ENDPOINT_PUSH, {});
1635
+ const pushLine = pushed.ok ? describePush(pushed.value) : `\u63A8\u9001\u5931\u8D25\uFF1A${pushed.error.message}`;
1636
+ const pulled = await call(INBOX_ENDPOINT_PULL, {});
1637
+ let suffix = "";
1638
+ if (pulled.ok) {
1639
+ const result = pulled.value;
1640
+ suffix = ` \xB7 ${describePull(result)}`;
1641
+ } else {
1642
+ suffix = ` \xB7 \u62C9\u53D6\u5931\u8D25\uFF1A${pulled.error.message}`;
1643
+ }
1644
+ await refresh();
1645
+ const total = list?.matched;
1646
+ setNotice(
1647
+ `${pushLine}${suffix}${total === void 0 ? "" : ` \xB7 \u672C\u9875 ${String(total)} \u6761`}`
1648
+ );
1649
+ } finally {
1650
+ setBusy(false);
1651
+ }
1652
+ }, [call, list?.matched, refresh]);
1653
+ const removeTagEverywhere = import_react7.default.useCallback(
1654
+ async (name2, count) => {
1655
+ if (!window.confirm(`\u628A\u6807\u7B7E\u300C${name2}\u300D\u4ECE ${String(count)} \u6761\u8BB0\u5F55\u4E0A\u79FB\u9664\uFF1F\u8BB0\u5F55\u672C\u8EAB\u4E0D\u4F1A\u88AB\u5220\u9664\u3002`))
1656
+ return;
1657
+ setBusy(true);
1658
+ try {
1659
+ const result = await call(INBOX_ENDPOINT_TAGS, {
1660
+ action: "remove",
1661
+ tag: name2
1662
+ });
1663
+ if (!result.ok) {
1664
+ setNotice(`\u5220\u6807\u7B7E\u5931\u8D25\uFF1A${result.error.message}`);
1665
+ return;
1666
+ }
1667
+ setTag(void 0);
1668
+ await refresh(false);
1669
+ setNotice(`\u6807\u7B7E\u300C${name2}\u300D\u5DF2\u79FB\u9664`);
1670
+ } finally {
1671
+ setBusy(false);
1672
+ }
1673
+ },
1674
+ [call, refresh]
1675
+ );
1676
+ import_react7.default.useEffect(() => {
1677
+ const timer = window.setTimeout(() => setQuery(search), 250);
1678
+ return () => window.clearTimeout(timer);
1679
+ }, [search]);
1680
+ const openDetail = import_react7.default.useCallback(
1681
+ async (id) => {
1682
+ setSelectedId(id);
1683
+ const result = await call(INBOX_ENDPOINT_DETAIL, { id });
1684
+ if (!result.ok) {
1685
+ setNotice(`\u8BFB\u53D6\u8BE6\u60C5\u5931\u8D25\uFF1A${result.error.message}`);
1686
+ setDetail(void 0);
1687
+ return;
1688
+ }
1689
+ setDetail(result.value.entry);
1690
+ },
1691
+ [call]
1692
+ );
1693
+ const mutate = import_react7.default.useCallback(
1694
+ async (endpoint, payload, { dropSelection = false } = {}) => {
1695
+ setBusy(true);
1696
+ try {
1697
+ const result = await call(endpoint, payload);
1698
+ if (!result.ok) {
1699
+ setNotice(`${result.error.message}`);
1700
+ return false;
1701
+ }
1702
+ setNotice(void 0);
1703
+ if (dropSelection) {
1704
+ setSelectedId(void 0);
1705
+ setDetail(void 0);
1706
+ } else if (selectedId !== void 0 && !dropSelection) {
1707
+ await openDetail(selectedId);
1708
+ }
1709
+ await refresh();
1710
+ return true;
1711
+ } finally {
1712
+ setBusy(false);
1713
+ }
1714
+ },
1715
+ [call, openDetail, refresh, selectedId]
1716
+ );
1717
+ const purge = import_react7.default.useCallback(async () => {
1718
+ setBusy(true);
1719
+ try {
1720
+ const result = await call(INBOX_ENDPOINT_PURGE, {});
1721
+ if (!result.ok) {
1722
+ setNotice(`\u6E05\u7A7A\u5931\u8D25\uFF1A${result.error.message}`);
1723
+ return;
1724
+ }
1725
+ const value = result.value;
1726
+ const remote = value.remoteSkipped === true ? "\uFF08\u6CA1\u914D\u8FDC\u7AEF\uFF09" : value.remoteRemoved === void 0 ? "" : `\uFF0C\u4E91\u7AEF\u5220\u4E86 ${String(value.remoteRemoved)} \u4E2A\u5BF9\u8C61`;
1727
+ setNotice(
1728
+ `\u5DF2\u6E05\u7A7A ${String(value.removed)} \u6761${remote}${value.reason === void 0 ? "" : ` \xB7 \u4E91\u7AEF\u6709\u5931\u8D25\uFF1A${value.reason}`}`
1729
+ );
1730
+ setSelectedId(void 0);
1731
+ setDetail(void 0);
1732
+ await refresh(false);
1733
+ } finally {
1734
+ setBusy(false);
1735
+ }
1736
+ }, [call, refresh]);
1737
+ const stage = (files, extraText) => {
1738
+ const next = [];
1739
+ for (const file of Array.from(files)) {
1740
+ const isImage = INBOX_IMAGE_TYPES.includes(file.type);
1741
+ next.push({
1742
+ id: `${file.name}:${file.size}:${file.lastModified}:${next.length}`,
1743
+ name: file.name.length === 0 ? "\uFF08\u672A\u547D\u540D\uFF09" : file.name,
1744
+ slot: isImage ? "image" : "file",
1745
+ bytes: file.size,
1746
+ file,
1747
+ ...isImage ? { previewUrl: URL.createObjectURL(file) } : {}
1748
+ });
1749
+ }
1750
+ if (next.length > 0) setStaged((current) => [...current, ...next]);
1751
+ if (extraText !== void 0 && extraText.length > 0) {
1752
+ setText((current) => current.length === 0 ? extraText : `${current}
1753
+ ${extraText}`);
1754
+ }
1755
+ };
1756
+ const onPaste = (event) => {
1757
+ const files = event.clipboardData.files;
1758
+ if (files.length === 0) return;
1759
+ event.preventDefault();
1760
+ stage(files, event.clipboardData.getData("text/plain"));
1761
+ };
1762
+ const onDrop = (event) => {
1763
+ event.preventDefault();
1764
+ setDragging(false);
1765
+ if (event.dataTransfer.files.length > 0) stage(event.dataTransfer.files);
1766
+ };
1767
+ const submit = async () => {
1768
+ if (busy) return;
1769
+ const trimmed = text.trim();
1770
+ if (trimmed.length === 0 && staged.length === 0) {
1771
+ setNotice("\u8FD8\u6CA1\u4E1C\u897F\u53EF\u5B58\uFF1A\u7C98\u4E00\u6BB5\u6587\u5B57\u3001\u4E00\u4E2A\u94FE\u63A5\uFF0C\u6216\u8005\u628A\u56FE\u7247\u62D6\u8FDB\u6765");
1772
+ return;
1773
+ }
1774
+ setBusy(true);
1775
+ setNotice(void 0);
1776
+ try {
1777
+ const images = [];
1778
+ const files = [];
1779
+ for (const entry of staged) {
1780
+ const data = await toBase64(entry.file);
1781
+ if (entry.slot === "image") {
1782
+ images.push({ mediaType: entry.file.type, data, name: entry.name });
1783
+ } else {
1784
+ files.push({
1785
+ data,
1786
+ name: entry.name,
1787
+ ...entry.file.type.length === 0 ? {} : { mediaType: entry.file.type }
1788
+ });
1789
+ }
1790
+ }
1791
+ const result = await call(INBOX_ENDPOINT_CAPTURE, { text: trimmed, images, files });
1792
+ if (!result.ok) {
1793
+ setNotice(`\u6CA1\u5B58\u8FDB\u53BB\uFF1A${result.error.message}`);
1794
+ return;
1795
+ }
1796
+ setNotice(describe(result.value));
1797
+ setText("");
1798
+ for (const entry of staged) {
1799
+ if (entry.previewUrl !== void 0) URL.revokeObjectURL(entry.previewUrl);
1800
+ }
1801
+ setStaged([]);
1802
+ const wasInBin = scope === "bin";
1803
+ if (wasInBin) {
1804
+ setScope("live");
1805
+ setWatchOnly(false);
1806
+ } else {
1807
+ await refresh(false);
1808
+ }
1809
+ window.setTimeout(() => void refreshRef.current(), 2500);
1810
+ } finally {
1811
+ setBusy(false);
1812
+ }
1813
+ };
1814
+ const onKeyDown = (event) => {
1815
+ if ((event.metaKey || event.ctrlKey) && event.key === "Enter") {
1816
+ event.preventDefault();
1817
+ void submit();
1818
+ }
1819
+ };
1820
+ const pageCount = Math.max(1, Math.ceil((list?.matched ?? 0) / PAGE_SIZE));
1821
+ const listTitle = (() => {
1822
+ const parts = [];
1823
+ if (scope === "bin") parts.push("\u56DE\u6536\u7AD9");
1824
+ else {
1825
+ if (category !== void 0) parts.push(CATEGORY_LABELS[category]);
1826
+ if (watchOnly) parts.push("\u5F85\u770B");
1827
+ }
1828
+ if (tag !== void 0) parts.push(`#${tag}`);
1829
+ return parts.length === 0 ? "\u5168\u90E8" : parts.join(" \xB7 ");
1830
+ })();
1831
+ const zoomKind = zoom?.mime?.startsWith("video/") === true ? "video" : zoom?.mime?.startsWith("audio/") === true ? "audio" : "image";
1832
+ const openEntryPreview = import_react7.default.useCallback((entry) => {
1833
+ if (entry.previewId !== void 0) {
1834
+ setZoom({
1835
+ src: attachmentUrl(entry.previewId),
1836
+ ...entry.previewMime === void 0 ? {} : { mime: entry.previewMime },
1837
+ label: headingOf(entry)
1838
+ });
1839
+ return;
1840
+ }
1841
+ if (entry.url !== void 0) setZoom({ href: entry.url, label: headingOf(entry) });
1842
+ }, []);
1843
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { ref: panelRef, style: { ...panelStyle, colorScheme: scheme }, children: [
1844
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("header", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "flex-start", gap: 10 }, children: [
1845
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { children: [
1846
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { style: { margin: "0 0 4px" }, children: "dsh-inbox" }),
1847
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { style: { margin: 0, opacity: 0.7 }, children: [
1848
+ PACKAGE_NAME,
1849
+ list === void 0 ? "" : ` \xB7 \u5171 ${String(list.total)} \u6761 \xB7 \u5F85\u770B ${String(list.watchLater)} \u6761 \xB7 \u56DE\u6536\u7AD9 ${String(list.deleted)} \u6761`
1850
+ ] })
1851
+ ] }),
1852
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1853
+ "button",
1854
+ {
1855
+ type: "button",
1856
+ style: { ...buttonStyle, marginLeft: "auto" },
1857
+ onClick: () => setManualOpen(true),
1858
+ children: "\u4F7F\u7528\u624B\u518C"
1859
+ }
1860
+ ),
1861
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1862
+ "button",
1863
+ {
1864
+ type: "button",
1865
+ style: buttonStyle,
1866
+ "aria-expanded": settingsOpen,
1867
+ onClick: () => setSettingsOpen((open) => !open),
1868
+ children: [
1869
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Settings2, { size: 14 }),
1870
+ " \u8BBE\u7F6E"
1871
+ ]
1872
+ }
1873
+ )
1874
+ ] }) }),
1875
+ settingsOpen && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1876
+ "div",
1877
+ {
1878
+ role: "dialog",
1879
+ "aria-label": "\u8BBE\u7F6E",
1880
+ style: {
1881
+ position: "fixed",
1882
+ inset: 0,
1883
+ zIndex: 40,
1884
+ background: "color-mix(in srgb, #000 55%, transparent)",
1885
+ display: "flex",
1886
+ alignItems: "flex-start",
1887
+ justifyContent: "center",
1888
+ padding: "6vh 16px",
1889
+ overflow: "auto"
1890
+ },
1891
+ onClick: (event) => {
1892
+ if (event.target === event.currentTarget) setSettingsOpen(false);
1893
+ },
1894
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { ...cardStyle, width: "min(560px, 100%)", background: "Canvas" }, children: [
1895
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(EncryptionSettings, { call }),
1896
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(WebdavSettings, { call, onClose: () => setSettingsOpen(false) })
1897
+ ] })
1898
+ }
1899
+ ),
1900
+ manualOpen && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ManualDialog, { onClose: () => setManualOpen(false) }),
1901
+ zoom !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1902
+ "div",
1903
+ {
1904
+ role: "dialog",
1905
+ "aria-label": "\u653E\u5927\u67E5\u770B",
1906
+ style: {
1907
+ position: "fixed",
1908
+ inset: 0,
1909
+ zIndex: 50,
1910
+ background: "color-mix(in srgb, #000 78%, transparent)",
1911
+ display: "flex",
1912
+ flexDirection: "column",
1913
+ alignItems: "center",
1914
+ justifyContent: "center",
1915
+ gap: 10,
1916
+ padding: 20
1917
+ },
1918
+ onClick: () => setZoom(void 0),
1919
+ children: [
1920
+ zoom.src !== void 0 && zoomKind === "video" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1921
+ "video",
1922
+ {
1923
+ src: zoom.src,
1924
+ controls: true,
1925
+ autoPlay: true,
1926
+ playsInline: true,
1927
+ onClick: (event) => event.stopPropagation(),
1928
+ style: { maxWidth: "92vw", maxHeight: "80vh", borderRadius: 8, background: "#000" }
1929
+ }
1930
+ ),
1931
+ zoom.src !== void 0 && zoomKind === "audio" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1932
+ "audio",
1933
+ {
1934
+ src: zoom.src,
1935
+ controls: true,
1936
+ autoPlay: true,
1937
+ onClick: (event) => event.stopPropagation(),
1938
+ style: { width: "min(560px, 92vw)" }
1939
+ }
1940
+ ),
1941
+ zoom.src !== void 0 && zoomKind === "image" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1942
+ "img",
1943
+ {
1944
+ src: zoom.src,
1945
+ alt: zoom.label,
1946
+ style: { maxWidth: "92vw", maxHeight: "80vh", borderRadius: 8, background: "#000" }
1947
+ }
1948
+ ),
1949
+ zoom.src === void 0 && /*
1950
+ A video on someone else's site. Embedding their player would mean a
1951
+ remote frame inside the panel and their own terms; handing the page
1952
+ to the browser is the honest version, and it is also the big screen.
1953
+ */
1954
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1955
+ "div",
1956
+ {
1957
+ onClick: (event) => event.stopPropagation(),
1958
+ style: {
1959
+ ...cardStyle,
1960
+ maxWidth: "min(560px, 92vw)",
1961
+ background: "Canvas",
1962
+ color: "CanvasText",
1963
+ display: "flex",
1964
+ flexDirection: "column",
1965
+ gap: 10,
1966
+ alignItems: "flex-start"
1967
+ },
1968
+ children: [
1969
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: "\u8FD9\u6761\u662F\u5916\u7AD9\u7684\u5185\u5BB9" }),
1970
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.75 }, children: "\u9762\u677F\u4E0D\u5185\u5D4C\u522B\u4EBA\u7684\u64AD\u653E\u5668\uFF0C\u6240\u4EE5\u5728\u6D4F\u89C8\u5668\u91CC\u6253\u5F00\u2014\u2014\u90A3\u91CC\u624D\u662F\u5927\u5C4F\u3002" }),
1971
+ zoom.href !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1972
+ "a",
1973
+ {
1974
+ href: zoom.href,
1975
+ target: "_blank",
1976
+ rel: "noreferrer",
1977
+ style: { ...primaryStyle, textDecoration: "none" },
1978
+ children: [
1979
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ExternalLink, { size: 14 }),
1980
+ " \u5728\u6D4F\u89C8\u5668\u91CC\u64AD\u653E"
1981
+ ]
1982
+ }
1983
+ )
1984
+ ]
1985
+ }
1986
+ ),
1987
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: 10, alignItems: "center", fontSize: 12, opacity: 0.85 }, children: [
1988
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { overflowWrap: "anywhere" }, children: zoom.label }),
1989
+ zoom.src !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1990
+ "a",
1991
+ {
1992
+ href: zoom.src,
1993
+ target: "_blank",
1994
+ rel: "noreferrer",
1995
+ style: { color: "inherit", display: "inline-flex", alignItems: "center", gap: 4 },
1996
+ children: [
1997
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ExternalLink, { size: 13 }),
1998
+ " \u5728\u6D4F\u89C8\u5668\u6253\u5F00"
1999
+ ]
2000
+ }
2001
+ ),
2002
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("button", { type: "button", style: buttonStyle, onClick: () => setZoom(void 0), children: [
2003
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(X, { size: 13 }),
2004
+ " \u5173\u95ED"
2005
+ ] })
2006
+ ] })
2007
+ ]
2008
+ }
2009
+ ),
2010
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2011
+ "div",
2012
+ {
2013
+ style: {
2014
+ ...cardStyle,
2015
+ borderStyle: dragging ? "dashed" : "solid",
2016
+ borderColor: dragging ? "currentColor" : cardStyle.borderColor
2017
+ },
2018
+ onDragOver: (event) => {
2019
+ event.preventDefault();
2020
+ setDragging(true);
2021
+ },
2022
+ onDragLeave: () => setDragging(false),
2023
+ onDrop,
2024
+ children: [
2025
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2026
+ "textarea",
2027
+ {
2028
+ value: text,
2029
+ onChange: (event) => setText(event.target.value),
2030
+ onPaste,
2031
+ onKeyDown,
2032
+ placeholder: "\u7C98\u8D34\u6587\u5B57\u3001\u94FE\u63A5\uFF0C\u6216\u628A\u56FE\u7247/\u6587\u4EF6\u62D6\u5230\u8FD9\u91CC\uFF08Ctrl+Enter \u5B58\u5165\uFF09",
2033
+ rows: 3,
2034
+ style: {
2035
+ width: "100%",
2036
+ boxSizing: "border-box",
2037
+ resize: "vertical",
2038
+ font: "inherit",
2039
+ color: "inherit",
2040
+ background: "transparent",
2041
+ border: "none",
2042
+ outline: "none"
2043
+ }
2044
+ }
2045
+ ),
2046
+ staged.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2047
+ "ul",
2048
+ {
2049
+ style: {
2050
+ listStyle: "none",
2051
+ margin: "8px 0 0",
2052
+ padding: 0,
2053
+ display: "flex",
2054
+ flexWrap: "wrap",
2055
+ gap: 8
2056
+ },
2057
+ children: staged.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2058
+ "li",
2059
+ {
2060
+ style: {
2061
+ display: "flex",
2062
+ alignItems: "center",
2063
+ gap: 6,
2064
+ padding: "4px 8px",
2065
+ borderRadius: 8,
2066
+ border: "1px solid color-mix(in srgb, currentColor 20%, transparent)"
2067
+ },
2068
+ children: [
2069
+ entry.previewUrl !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2070
+ "img",
2071
+ {
2072
+ src: entry.previewUrl,
2073
+ alt: "",
2074
+ style: { width: 28, height: 28, objectFit: "cover", borderRadius: 4 }
2075
+ }
2076
+ ),
2077
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { opacity: 0.85 }, children: [
2078
+ entry.slot === "image" ? "\u{1F5BC}" : "\u{1F4C4}",
2079
+ " ",
2080
+ entry.name,
2081
+ " \xB7 ",
2082
+ formatBytes(entry.bytes)
2083
+ ] }),
2084
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2085
+ "button",
2086
+ {
2087
+ type: "button",
2088
+ "aria-label": `\u79FB\u9664 ${entry.name}`,
2089
+ onClick: () => setStaged((current) => current.filter((item) => item.id !== entry.id)),
2090
+ style: { ...buttonStyle, padding: "0 6px", border: "none", opacity: 0.6 },
2091
+ children: "\xD7"
2092
+ }
2093
+ )
2094
+ ]
2095
+ },
2096
+ entry.id
2097
+ ))
2098
+ }
2099
+ ),
2100
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 10, marginTop: 10 }, children: [
2101
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2102
+ "input",
2103
+ {
2104
+ ref: picker,
2105
+ type: "file",
2106
+ multiple: true,
2107
+ style: { display: "none" },
2108
+ onChange: (event) => {
2109
+ if (event.target.files !== null) stage(event.target.files);
2110
+ event.target.value = "";
2111
+ }
2112
+ }
2113
+ ),
2114
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2115
+ "button",
2116
+ {
2117
+ type: "button",
2118
+ style: buttonStyle,
2119
+ disabled: busy,
2120
+ onClick: () => picker.current?.click(),
2121
+ children: "\u9009\u62E9\u6587\u4EF6\u2026"
2122
+ }
2123
+ ),
2124
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", style: buttonStyle, disabled: busy, onClick: () => void submit(), children: busy ? "\u5904\u7406\u4E2D\u2026" : "\u5B58\u5165\u4ED3\u5E93" })
2125
+ ] })
2126
+ ]
2127
+ }
2128
+ ),
2129
+ notice !== void 0 && notice.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2130
+ "div",
2131
+ {
2132
+ role: "status",
2133
+ style: {
2134
+ position: "fixed",
2135
+ left: "50%",
2136
+ top: "50%",
2137
+ // Dead centre, not near the foot of the window: at `bottom: 28` the
2138
+ // toast sat in the corner you are least likely to be looking at,
2139
+ // and on a tall window it was a long way from the thing it was
2140
+ // reporting on.
2141
+ transform: "translate(-50%, -50%)",
2142
+ zIndex: 60,
2143
+ padding: "8px 14px",
2144
+ borderRadius: 999,
2145
+ border: "1px solid color-mix(in srgb, currentColor 25%, transparent)",
2146
+ background: "Canvas",
2147
+ color: "CanvasText",
2148
+ boxShadow: "0 10px 30px #0006",
2149
+ fontSize: 13,
2150
+ maxWidth: "80vw",
2151
+ // It reports; it does not invite a click. Centred over the list it
2152
+ // would otherwise swallow the first click of whatever is under it.
2153
+ pointerEvents: "none"
2154
+ },
2155
+ children: notice
2156
+ }
2157
+ ),
2158
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
2159
+ narrow && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2160
+ "button",
2161
+ {
2162
+ type: "button",
2163
+ style: {
2164
+ ...buttonStyle,
2165
+ height: CONTROL_HEIGHT,
2166
+ boxSizing: "border-box",
2167
+ ...railOpen ? { borderColor: "currentColor" } : {}
2168
+ },
2169
+ "aria-expanded": railOpen,
2170
+ onClick: () => setRailOpen((open) => !open),
2171
+ children: [
2172
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Layers, { size: 13 }),
2173
+ " \u7B5B\u9009"
2174
+ ]
2175
+ }
2176
+ ),
2177
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2178
+ "input",
2179
+ {
2180
+ value: search,
2181
+ onChange: (event) => setSearch(event.target.value),
2182
+ placeholder: "\u641C\u6807\u9898\u3001\u6B63\u6587\u3001\u94FE\u63A5\u3001\u5907\u6CE8\u2026",
2183
+ style: {
2184
+ ...inputStyle,
2185
+ flex: 1,
2186
+ minWidth: 180,
2187
+ // The same edge as the two buttons beside it: an input sized by its
2188
+ // own line box happened to land within half a pixel, which is the
2189
+ // kind of "almost" that still reads as crooked.
2190
+ height: CONTROL_HEIGHT,
2191
+ boxSizing: "border-box"
2192
+ }
2193
+ }
2194
+ ),
2195
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2196
+ "span",
2197
+ {
2198
+ role: "group",
2199
+ "aria-label": "\u5217\u8868\u6A21\u5F0F",
2200
+ style: {
2201
+ display: "inline-flex",
2202
+ border: "1px solid color-mix(in srgb, currentColor 15%, transparent)",
2203
+ borderRadius: 8,
2204
+ overflow: "hidden",
2205
+ height: CONTROL_HEIGHT,
2206
+ boxSizing: "border-box"
2207
+ },
2208
+ children: LIST_MODES.map((mode) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2209
+ "button",
2210
+ {
2211
+ type: "button",
2212
+ title: `\u5217\u8868\u6A21\u5F0F\uFF1A${mode.label}`,
2213
+ "aria-pressed": listMode === mode.id,
2214
+ onClick: () => chooseListMode(mode.id),
2215
+ style: {
2216
+ ...buttonStyle,
2217
+ // The frame itself: no padding of its own (the group's height
2218
+ // decides it), a fixed 38px width, and `height: 100%` of the
2219
+ // group's inner box. With the glyph centred by `buttonStyle` the
2220
+ // icon lands on the row's centre line — measured offset 0.
2221
+ flex: "none",
2222
+ width: 38,
2223
+ height: "100%",
2224
+ padding: 0,
2225
+ boxSizing: "border-box",
2226
+ border: "none",
2227
+ borderRadius: 0,
2228
+ opacity: listMode === mode.id ? 1 : 0.5
2229
+ },
2230
+ children: mode.icon
2231
+ },
2232
+ mode.id
2233
+ ))
2234
+ }
2235
+ ),
2236
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2237
+ "button",
2238
+ {
2239
+ type: "button",
2240
+ style: { ...buttonStyle, height: CONTROL_HEIGHT, boxSizing: "border-box" },
2241
+ disabled: busy,
2242
+ onClick: () => void refreshAll(),
2243
+ children: [
2244
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RefreshCw, { size: 13 }),
2245
+ " ",
2246
+ busy ? "\u5237\u65B0\u4E2D\u2026" : "\u5237\u65B0"
2247
+ ]
2248
+ }
2249
+ )
2250
+ ] }),
2251
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2252
+ "div",
2253
+ {
2254
+ style: {
2255
+ display: "grid",
2256
+ // The list is the working surface; the detail is a reader pane beside
2257
+ // it, so it gets a width rather than half the room. 320–380 rather
2258
+ // than 250–300 because the detail's own action row ("保存描述与标签"
2259
+ // next to "标为待看" next to "删除") is only one line at that width —
2260
+ // and below 960px the whole thing collapses to one column.
2261
+ gridTemplateColumns: narrow ? "minmax(0, 1fr)" : "176px minmax(320px, 1fr) minmax(320px, 380px)",
2262
+ gap: 14,
2263
+ // Fill what the chrome above left, so the list can scroll inside it.
2264
+ flex: 1,
2265
+ minHeight: 0
2266
+ },
2267
+ children: [
2268
+ (!narrow || railOpen) && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2269
+ "nav",
2270
+ {
2271
+ "aria-label": "\u7B5B\u9009",
2272
+ style: {
2273
+ ...cardStyle,
2274
+ display: "flex",
2275
+ flexDirection: "column",
2276
+ gap: 2,
2277
+ alignSelf: "start"
2278
+ },
2279
+ children: [
2280
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2281
+ RailRow,
2282
+ {
2283
+ active: scope === "live" && !watchOnly && category === void 0,
2284
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Inbox, { size: 15 }),
2285
+ label: "\u5168\u90E8",
2286
+ ...list === void 0 ? {} : { count: list.total },
2287
+ onClick: () => {
2288
+ setScope("live");
2289
+ setWatchOnly(false);
2290
+ setCategory(void 0);
2291
+ }
2292
+ }
2293
+ ),
2294
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2295
+ RailRow,
2296
+ {
2297
+ active: scope === "live" && watchOnly,
2298
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Circle, { size: 15 }),
2299
+ label: "\u5F85\u770B",
2300
+ ...list === void 0 ? {} : { count: list.watchLater },
2301
+ onClick: () => {
2302
+ setScope("live");
2303
+ setWatchOnly(true);
2304
+ setCategory(void 0);
2305
+ }
2306
+ }
2307
+ ),
2308
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2309
+ RailRow,
2310
+ {
2311
+ active: scope === "bin",
2312
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Trash, { size: 15 }),
2313
+ label: "\u56DE\u6536\u7AD9",
2314
+ ...list === void 0 ? {} : { count: list.deleted },
2315
+ onClick: () => {
2316
+ setScope("bin");
2317
+ setWatchOnly(false);
2318
+ setCategory(void 0);
2319
+ }
2320
+ }
2321
+ ),
2322
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { margin: "8px 0 4px", padding: "0 9px", fontSize: 11, opacity: 0.6 }, children: "\u7C7B\u76EE" }),
2323
+ CATEGORIES.map((value) => {
2324
+ const count = list?.categories.find((facet) => facet.value === value)?.count;
2325
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2326
+ RailRow,
2327
+ {
2328
+ active: category === value,
2329
+ icon: categoryGlyph(value, 15),
2330
+ label: CATEGORY_LABELS[value],
2331
+ ...list === void 0 ? {} : { count: count ?? 0 },
2332
+ onClick: () => {
2333
+ setCategory(category === value ? void 0 : value);
2334
+ setScope("live");
2335
+ setWatchOnly(false);
2336
+ }
2337
+ },
2338
+ value
2339
+ );
2340
+ }),
2341
+ list !== void 0 && list.tags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
2342
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { margin: "8px 0 4px", padding: "0 9px", fontSize: 11, opacity: 0.6 }, children: "\u6807\u7B7E" }),
2343
+ list.tags.map((facet) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 2 }, children: [
2344
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2345
+ RailRow,
2346
+ {
2347
+ active: tag === facet.value,
2348
+ icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Tag, { size: 14 }),
2349
+ label: `#${facet.value}`,
2350
+ count: facet.count,
2351
+ onClick: () => setTag(tag === facet.value ? void 0 : facet.value)
2352
+ }
2353
+ ),
2354
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2355
+ "button",
2356
+ {
2357
+ type: "button",
2358
+ title: `\u628A\u6807\u7B7E\u300C${facet.value}\u300D\u4ECE\u6240\u6709\u8BB0\u5F55\u4E0A\u79FB\u9664\uFF08\u8BB0\u5F55\u672C\u8EAB\u4E0D\u5220\uFF09`,
2359
+ onClick: () => void removeTagEverywhere(facet.value, facet.count),
2360
+ style: { ...buttonStyle, border: "none", padding: "4px 5px", opacity: 0.6 },
2361
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(X, { size: 12 })
2362
+ }
2363
+ )
2364
+ ] }, facet.value))
2365
+ ] })
2366
+ ]
2367
+ }
2368
+ ),
2369
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2370
+ "section",
2371
+ {
2372
+ style: {
2373
+ ...cardStyle,
2374
+ minWidth: 0,
2375
+ minHeight: 0,
2376
+ display: "flex",
2377
+ flexDirection: "column"
2378
+ },
2379
+ children: [
2380
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2381
+ "div",
2382
+ {
2383
+ style: {
2384
+ display: "flex",
2385
+ alignItems: "center",
2386
+ justifyContent: "space-between",
2387
+ gap: 8,
2388
+ marginBottom: 6
2389
+ },
2390
+ children: [
2391
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: listTitle }),
2392
+ scope === "bin" && (list?.deleted ?? 0) > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2393
+ "button",
2394
+ {
2395
+ type: "button",
2396
+ style: buttonStyle,
2397
+ disabled: busy,
2398
+ onClick: () => {
2399
+ if (!window.confirm(
2400
+ "\u6E05\u7A7A\u56DE\u6536\u7AD9\u4F1A\u771F\u7684\u5220\u6389\u8FD9\u4E9B\u8BB0\u5F55\uFF1A\u672C\u673A\u8BB0\u5F55\u3001\u4EE5\u53CA\u4E91\u76D8\u4E0A\u5BF9\u5E94\u7684\u540C\u6B65\u5BF9\u8C61\uFF08\u8BB0\u5F55 JSON/\u6587\u672C\u3001\u53EA\u88AB\u8FD9\u4E9B\u8BB0\u5F55\u5F15\u7528\u7684\u9644\u4EF6\uFF09\u3002\u4E0D\u80FD\u64A4\u9500\u3002dsh \u9644\u4EF6\u4ED3\u5E93\u91CC\u7684\u539F\u59CB\u5B57\u8282\u4ECD\u7136\u7559\u7740\u3002\u7EE7\u7EED\uFF1F"
2401
+ ))
2402
+ return;
2403
+ void purge();
2404
+ },
2405
+ children: [
2406
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Trash, { size: 13 }),
2407
+ " \u6E05\u7A7A\u56DE\u6536\u7AD9"
2408
+ ]
2409
+ }
2410
+ )
2411
+ ]
2412
+ }
2413
+ ),
2414
+ (list === void 0 || list.matched > 0) && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { textAlign: "right", fontSize: 12, opacity: 0.6, marginBottom: 6 }, children: list === void 0 ? "\u8BFB\u53D6\u4E2D\u2026" : `${String(list.matched)} \u6761\u5339\u914D` }),
2415
+ list?.entries.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { margin: "8px 0 0", opacity: 0.7 }, children: scope === "bin" ? "\u56DE\u6536\u7AD9\u662F\u7A7A\u7684\u3002" : "\u6CA1\u6709\u5339\u914D\u7684\u8BB0\u5F55\u3002" }),
2416
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2417
+ "div",
2418
+ {
2419
+ style: listMode === "compact" ? {
2420
+ display: "flex",
2421
+ flexDirection: "column",
2422
+ gap: 0,
2423
+ flex: 1,
2424
+ minHeight: 0,
2425
+ overflowY: "auto"
2426
+ } : {
2427
+ display: "grid",
2428
+ gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
2429
+ gap: 12,
2430
+ alignContent: "start",
2431
+ flex: 1,
2432
+ minHeight: 0,
2433
+ overflowY: "auto"
2434
+ },
2435
+ children: list?.entries.map((entry) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2436
+ EntryCard,
2437
+ {
2438
+ entry,
2439
+ mode: listMode,
2440
+ selected: entry.id === selectedId,
2441
+ onOpen: () => void openDetail(entry.id),
2442
+ onPreview: () => openEntryPreview(entry)
2443
+ },
2444
+ entry.id
2445
+ ))
2446
+ }
2447
+ ),
2448
+ (list?.matched ?? 0) > 0 && pageCount > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2449
+ "div",
2450
+ {
2451
+ style: {
2452
+ display: "flex",
2453
+ alignItems: "center",
2454
+ gap: 8,
2455
+ marginTop: 10,
2456
+ opacity: 0.75,
2457
+ fontSize: 12
2458
+ },
2459
+ children: [
2460
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
2461
+ "\u7B2C ",
2462
+ String(page + 1),
2463
+ " / ",
2464
+ String(pageCount),
2465
+ " \u9875"
2466
+ ] }),
2467
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { marginLeft: "auto" } }),
2468
+ page > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2469
+ "button",
2470
+ {
2471
+ type: "button",
2472
+ style: pagerButtonStyle,
2473
+ onClick: () => setPage((current) => Math.max(0, current - 1)),
2474
+ children: [
2475
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChevronLeft, { size: 13 }),
2476
+ " \u4E0A\u4E00\u9875"
2477
+ ]
2478
+ }
2479
+ ),
2480
+ page + 1 < pageCount && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2481
+ "button",
2482
+ {
2483
+ type: "button",
2484
+ style: pagerButtonStyle,
2485
+ onClick: () => setPage((current) => current + 1),
2486
+ children: [
2487
+ "\u4E0B\u4E00\u9875 ",
2488
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(ChevronRight, { size: 13 })
2489
+ ]
2490
+ }
2491
+ )
2492
+ ]
2493
+ }
2494
+ )
2495
+ ]
2496
+ }
2497
+ ),
2498
+ narrow ? detail !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2499
+ "div",
2500
+ {
2501
+ role: "dialog",
2502
+ "aria-label": "\u8BB0\u5F55\u8BE6\u60C5",
2503
+ style: {
2504
+ position: "fixed",
2505
+ left: 12,
2506
+ right: 12,
2507
+ top: "5vh",
2508
+ bottom: "5vh",
2509
+ zIndex: 45,
2510
+ // The same shape as the wide column: a bounded box, a scrolling
2511
+ // content column inside it, and a pinned stamp — so the sheet
2512
+ // scrolls the record rather than the whole overlay.
2513
+ display: "flex",
2514
+ flexDirection: "column",
2515
+ minHeight: 0,
2516
+ background: "Canvas",
2517
+ border: `1px solid ${hairline}`,
2518
+ borderRadius: 12,
2519
+ padding: 12,
2520
+ boxShadow: "0 18px 40px #0007"
2521
+ },
2522
+ children: [
2523
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { display: "flex", justifyContent: "flex-end", marginBottom: 6 }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2524
+ "button",
2525
+ {
2526
+ type: "button",
2527
+ style: buttonStyle,
2528
+ onClick: () => {
2529
+ setSelectedId(void 0);
2530
+ setDetail(void 0);
2531
+ },
2532
+ children: [
2533
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(X, { size: 13 }),
2534
+ " \u5173\u95ED"
2535
+ ]
2536
+ }
2537
+ ) }),
2538
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2539
+ EntryPane,
2540
+ {
2541
+ detail,
2542
+ busy,
2543
+ onUpdate: (patch) => mutate(INBOX_ENDPOINT_UPDATE, { id: detail.id, ...patch }),
2544
+ onDelete: () => mutate(INBOX_ENDPOINT_DELETE, { id: detail.id }, { dropSelection: true }),
2545
+ onRestore: () => mutate(INBOX_ENDPOINT_RESTORE, { id: detail.id }, { dropSelection: true }),
2546
+ onZoom: setZoom
2547
+ }
2548
+ )
2549
+ ]
2550
+ }
2551
+ ) : (
2552
+ /*
2553
+ Two things are load-bearing here. `position: relative` anchors the
2554
+ pane's own timestamps, which are absolutely positioned against this
2555
+ card so they stay inside the frame 16px above its bottom edge. The
2556
+ flex column gives the pane a bounded height to scroll inside: a long
2557
+ record (a 3k-character text, a stack of photos) used to grow the card
2558
+ past the panel, taking the actions and the stamps out of reach.
2559
+ */
2560
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2561
+ "section",
2562
+ {
2563
+ style: {
2564
+ ...cardStyle,
2565
+ minWidth: 0,
2566
+ position: "relative",
2567
+ display: "flex",
2568
+ flexDirection: "column",
2569
+ minHeight: 0
2570
+ },
2571
+ children: detail === void 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { margin: 0, opacity: 0.7 }, children: "\u9009\u5DE6\u8FB9\u4E00\u6761\u770B\u770B\u8BE6\u60C5\u3002" }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2572
+ EntryPane,
2573
+ {
2574
+ detail,
2575
+ busy,
2576
+ onUpdate: (patch) => mutate(INBOX_ENDPOINT_UPDATE, { id: detail.id, ...patch }),
2577
+ onDelete: () => mutate(INBOX_ENDPOINT_DELETE, { id: detail.id }, { dropSelection: true }),
2578
+ onRestore: () => mutate(INBOX_ENDPOINT_RESTORE, { id: detail.id }, { dropSelection: true }),
2579
+ onZoom: setZoom
2580
+ }
2581
+ )
2582
+ }
2583
+ )
2584
+ )
2585
+ ]
2586
+ }
2587
+ )
2588
+ ] });
2589
+ }
2590
+ function describePush(result) {
2591
+ if (result.status === "unconfigured") return `\u63A8\u9001\uFF1A${result.reason ?? "\u8FD8\u6CA1\u914D\u7F6E\u8FDC\u7AEF"}`;
2592
+ if (result.status === "failed") return `\u63A8\u9001\u5931\u8D25\uFF1A${result.reason ?? "\u672A\u77E5\u539F\u56E0"}`;
2593
+ const head = result.pushed === 0 && result.attachments === 0 ? `\u63A8\u9001\uFF1A\u6CA1\u6709\u65B0\u5185\u5BB9\uFF08${String(result.skipped)} \u6761\u5DF2\u662F\u6700\u65B0\uFF09` : `\u63A8\u9001\uFF1A${String(result.pushed)} \u6761\u8BB0\u5F55 / ${String(result.attachments)} \u4E2A\u9644\u4EF6`;
2594
+ return result.status === "partial" ? `${head} \xB7 \u90E8\u5206\u5931\u8D25\uFF1A${result.reason ?? ""}` : head;
2595
+ }
2596
+ function describePull(result) {
2597
+ if (result.status === "unconfigured") return result.reason ?? "\u8FD8\u6CA1\u914D\u7F6E\u5730\u5740";
2598
+ if (result.status === "failed") return `\u62C9\u53D6\u5931\u8D25\uFF1A${result.reason ?? "\u672A\u77E5\u539F\u56E0"}`;
2599
+ const merged = result.merged ?? 0;
2600
+ const attachments = result.attachments ?? 0;
2601
+ const syncPart = merged === 0 && attachments === 0 ? "\u4E91\u7AEF\u7684\u8BB0\u5F55\u6CA1\u6709\u65B0\u7684" : `\u4ECE\u4E91\u7AEF\u5408\u5E76 ${String(merged)} \u6761${attachments === 0 ? "" : ` / ${String(attachments)} \u4E2A\u9644\u4EF6`}`;
2602
+ if (result.pulled === 0 && result.skipped === 0 && result.failed === 0) return syncPart;
2603
+ return `\u62C9\u53D6\u5B8C\u6210\uFF1A\u8FDC\u7AEF\u5217\u51FA ${String(result.listed)} \u9879\uFF0C\u65B0\u5165\u5E93 ${String(result.pulled)} \u6761\uFF0C\u8DF3\u8FC7 ${String(
2604
+ result.skipped
2605
+ )} \u6761 \xB7 ${syncPart}${result.failed > 0 ? ` \xB7 \u5931\u8D25 ${String(result.failed)} \u6761` : ""}`;
2606
+ }
2607
+ function EncryptionSettings({ call }) {
2608
+ const [status, setStatus] = import_react7.default.useState();
2609
+ const [password, setPassword] = import_react7.default.useState("");
2610
+ const [notice, setNotice] = import_react7.default.useState();
2611
+ const [busy, setBusy] = import_react7.default.useState(false);
2612
+ const send = import_react7.default.useCallback(
2613
+ async (action) => {
2614
+ setBusy(true);
2615
+ try {
2616
+ const result = await call(INBOX_ENDPOINT_SECRET, {
2617
+ action,
2618
+ ...password.length === 0 ? {} : { password }
2619
+ });
2620
+ if (!result.ok) {
2621
+ setNotice(result.error.message);
2622
+ return;
2623
+ }
2624
+ const next = result.value;
2625
+ setStatus(next);
2626
+ setPassword("");
2627
+ setNotice(
2628
+ action === "set" ? next.sealed === void 0 || next.sealed === 0 ? "\u4E3B\u5BC6\u7801\u5DF2\u8BBE\u7F6E\uFF0C\u8D26\u5BC6\u4ECE\u6B64\u52A0\u5BC6\u843D\u76D8" : `\u4E3B\u5BC6\u7801\u5DF2\u8BBE\u7F6E\uFF0C\u53E6\u6709 ${String(next.sealed)} \u6761\u65E7\u8BB0\u5F55\u5DF2\u4ECE\u660E\u6587\u6539\u4E3A\u5BC6\u6587` : action === "unlock" ? "\u5DF2\u89E3\u9501" : action === "lock" ? "\u5DF2\u9501\u5B9A\uFF1A\u8D26\u5BC6\u6B63\u6587\u4E0D\u53EF\u8BFB\uFF0C\u76F4\u5230\u518D\u6B21\u89E3\u9501" : void 0
2629
+ );
2630
+ } finally {
2631
+ setBusy(false);
2632
+ }
2633
+ },
2634
+ [call, password]
2635
+ );
2636
+ import_react7.default.useEffect(() => {
2637
+ void send("status");
2638
+ }, []);
2639
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { style: { ...cardStyle, marginBottom: 12 }, children: [
2640
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }, children: [
2641
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: "\u8D26\u5BC6\u52A0\u5BC6" }),
2642
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, fontSize: 12 }, children: status === void 0 ? "\u8BFB\u53D6\u4E2D\u2026" : status.unlocked ? "\u5DF2\u89E3\u9501" : status.configured ? "\u5DF2\u9501\u5B9A" : "\u8FD8\u6CA1\u8BBE\u4E3B\u5BC6\u7801" })
2643
+ ] }),
2644
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { margin: "0 0 8px", opacity: 0.7, fontSize: 12 }, children: "\u8D26\u5BC6\u6B63\u6587\u4EE5\u5BC6\u6587\u5199\u76D8\uFF1B\u4E3B\u5BC6\u7801\u548C\u5BC6\u94A5\u90FD\u4E0D\u843D\u76D8\uFF0C**\u91CD\u542F\u540E\u8981\u91CD\u65B0\u89E3\u9501**\u3002\u5BC6\u7801\u5FD8\u4E86\u5C31\u89E3\u4E0D\u5F00\uFF0C\u6CA1\u6709\u627E\u56DE\u3002" }),
2645
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }, children: [
2646
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2647
+ "input",
2648
+ {
2649
+ type: "password",
2650
+ value: password,
2651
+ disabled: busy,
2652
+ autoComplete: "new-password",
2653
+ onChange: (event) => setPassword(event.target.value),
2654
+ "aria-label": "\u4E3B\u5BC6\u7801",
2655
+ placeholder: status?.configured === true ? "\u8F93\u5165\u4E3B\u5BC6\u7801" : "\u8BBE\u4E00\u4E2A\u4E3B\u5BC6\u7801",
2656
+ style: { ...inputStyle, flex: 1, minWidth: 160 }
2657
+ }
2658
+ ),
2659
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2660
+ "button",
2661
+ {
2662
+ type: "button",
2663
+ style: { ...buttonStyle, height: CONTROL_HEIGHT, boxSizing: "border-box" },
2664
+ disabled: busy || password.length === 0,
2665
+ onClick: () => void send("set"),
2666
+ children: "\u8BBE\u7F6E / \u66F4\u6362"
2667
+ }
2668
+ ),
2669
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2670
+ "button",
2671
+ {
2672
+ type: "button",
2673
+ style: { ...buttonStyle, height: CONTROL_HEIGHT, boxSizing: "border-box" },
2674
+ disabled: busy || password.length === 0 || status?.configured !== true,
2675
+ onClick: () => void send("unlock"),
2676
+ children: "\u89E3\u9501"
2677
+ }
2678
+ ),
2679
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2680
+ "button",
2681
+ {
2682
+ type: "button",
2683
+ style: { ...buttonStyle, height: CONTROL_HEIGHT, boxSizing: "border-box" },
2684
+ disabled: busy || status?.unlocked !== true,
2685
+ onClick: () => void send("lock"),
2686
+ children: "\u9501\u5B9A"
2687
+ }
2688
+ )
2689
+ ] }),
2690
+ notice !== void 0 && notice.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { margin: "6px 0 0", opacity: 0.8, fontSize: 12 }, children: notice })
2691
+ ] });
2692
+ }
2693
+ function WebdavSettings({
2694
+ call,
2695
+ onClose
2696
+ }) {
2697
+ const [status, setStatus] = import_react7.default.useState();
2698
+ const [baseUrl, setBaseUrl] = import_react7.default.useState("");
2699
+ const [directory, setDirectory] = import_react7.default.useState("/inbox");
2700
+ const [username, setUsername] = import_react7.default.useState("");
2701
+ const [password, setPassword] = import_react7.default.useState("");
2702
+ const [protocol, setProtocol] = import_react7.default.useState("webdav");
2703
+ const [endpoint, setEndpoint] = import_react7.default.useState("");
2704
+ const [bucket, setBucket] = import_react7.default.useState("");
2705
+ const [region, setRegion] = import_react7.default.useState("us-east-1");
2706
+ const [signatureVersion, setSignatureVersion] = import_react7.default.useState("v4");
2707
+ const [accessKeyId, setAccessKeyId] = import_react7.default.useState("");
2708
+ const [s3UserAgent, setS3UserAgent] = import_react7.default.useState("");
2709
+ const [webdavUserAgent, setWebdavUserAgent] = import_react7.default.useState("");
2710
+ const [accessKeySecret, setAccessKeySecret] = import_react7.default.useState("");
2711
+ const [probe, setProbe] = import_react7.default.useState();
2712
+ const [notice, setNotice] = import_react7.default.useState();
2713
+ const [busy, setBusy] = import_react7.default.useState(false);
2714
+ const read = import_react7.default.useCallback(async () => {
2715
+ const result = await call(INBOX_ENDPOINT_WEBDAV, { action: "read" });
2716
+ if (!result.ok) {
2717
+ setNotice(`\u8BFB\u4E0D\u5230\u8BBE\u7F6E\uFF1A${result.error.message}`);
2718
+ return;
2719
+ }
2720
+ const next = result.value;
2721
+ setStatus(next);
2722
+ setBaseUrl(next.settings.baseUrl);
2723
+ setDirectory(next.settings.directory);
2724
+ setUsername(next.settings.username);
2725
+ setProtocol(next.settings.protocol);
2726
+ setEndpoint(next.settings.endpoint);
2727
+ setBucket(next.settings.bucket);
2728
+ setRegion(next.settings.region);
2729
+ setSignatureVersion(next.settings.signatureVersion);
2730
+ setAccessKeyId(next.settings.accessKeyId);
2731
+ setS3UserAgent(next.settings.userAgent);
2732
+ setWebdavUserAgent(next.settings.webdavUserAgent);
2733
+ setPassword("");
2734
+ setAccessKeySecret("");
2735
+ }, [call]);
2736
+ import_react7.default.useEffect(() => {
2737
+ void read();
2738
+ }, [read]);
2739
+ const userAgent = protocol === "s3" ? s3UserAgent : webdavUserAgent;
2740
+ const verdict = probeVerdict(probe ?? []);
2741
+ const setUserAgent = (value) => {
2742
+ if (protocol === "s3") setS3UserAgent(value);
2743
+ else setWebdavUserAgent(value);
2744
+ };
2745
+ const save = async () => {
2746
+ setBusy(true);
2747
+ try {
2748
+ const request = {
2749
+ action: "save",
2750
+ protocol,
2751
+ baseUrl,
2752
+ directory,
2753
+ username,
2754
+ // Sending nothing leaves the stored password alone; sending "" clears it.
2755
+ ...password.length === 0 ? {} : { password },
2756
+ endpoint,
2757
+ bucket,
2758
+ region,
2759
+ signatureVersion,
2760
+ accessKeyId,
2761
+ userAgent,
2762
+ ...accessKeySecret.length === 0 ? {} : { accessKeySecret }
2763
+ };
2764
+ const result = await call(INBOX_ENDPOINT_WEBDAV, request);
2765
+ if (!result.ok) {
2766
+ setNotice(`\u6CA1\u5B58\u4E0A\uFF1A${result.error.message}`);
2767
+ return;
2768
+ }
2769
+ setStatus(result.value);
2770
+ setPassword("");
2771
+ setAccessKeySecret("");
2772
+ setNotice("\u8BBE\u7F6E\u5DF2\u4FDD\u5B58");
2773
+ } finally {
2774
+ setBusy(false);
2775
+ }
2776
+ };
2777
+ const selfTest = async () => {
2778
+ setBusy(true);
2779
+ setProbe(void 0);
2780
+ try {
2781
+ const result = await call(INBOX_ENDPOINT_PROBE, {});
2782
+ if (!result.ok) {
2783
+ setNotice(`\u81EA\u68C0\u5931\u8D25\uFF1A${result.error.message}`);
2784
+ return;
2785
+ }
2786
+ setProbe(result.value);
2787
+ setNotice("\u81EA\u68C0\u7ED3\u679C\u89C1\u4E0B\u65B9");
2788
+ } finally {
2789
+ setBusy(false);
2790
+ }
2791
+ };
2792
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("section", { style: { ...cardStyle, display: "flex", flexDirection: "column", gap: 8 }, children: [
2793
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", alignItems: "baseline", gap: 10 }, children: [
2794
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: "\u8FDC\u7AEF\u5165\u5E93" }),
2795
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.65 }, children: status === void 0 ? "\u8BFB\u53D6\u4E2D\u2026" : `${status.settingsAvailable ? "\u8BBE\u7F6E\u670D\u52A1\u5728" : "\u6CA1\u6709\u8BBE\u7F6E\u670D\u52A1"} \xB7 ${status.passwordSet ? "WebDAV \u5BC6\u7801\u5DF2\u5B58" : "\u8FD8\u6CA1\u5B58 WebDAV \u5BC6\u7801"} \xB7 ${status.secretSet ? "S3 \u5BC6\u94A5\u5DF2\u5B58" : "\u8FD8\u6CA1\u5B58 S3 \u5BC6\u94A5"}` }),
2796
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", style: { ...buttonStyle, marginLeft: "auto" }, onClick: onClose, children: "\u5173\u95ED" })
2797
+ ] }),
2798
+ status !== void 0 && !status.settingsAvailable && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { margin: 0, opacity: 0.75 }, children: "\u8FD9\u4E2A\u7EC4\u5408\u91CC\u6CA1\u6709\u8BBE\u7F6E\u670D\u52A1\uFF0C\u5730\u5740\u6539\u4E0D\u4E86\u2014\u2014\u4F60\u591A\u534A\u5728\u7528 headless \u5F62\u6001\u5F00\u53D1\u3002" }),
2799
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2800
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: "\u534F\u8BAE" }),
2801
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2802
+ SelectBox,
2803
+ {
2804
+ value: protocol,
2805
+ options: [
2806
+ ["webdav", "WebDAV"],
2807
+ ["s3", "S3"]
2808
+ ],
2809
+ disabled: busy,
2810
+ onChange: (next) => setProtocol(next === "s3" ? "s3" : "webdav")
2811
+ }
2812
+ ),
2813
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.6 }, children: "\u6362\u534F\u8BAE\u540E\u8BB0\u5F97\u70B9\u4FDD\u5B58" })
2814
+ ] }),
2815
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2816
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: "\u5BA2\u6237\u7AEF\u6807\u8BC6" }),
2817
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2818
+ "input",
2819
+ {
2820
+ value: userAgent,
2821
+ disabled: busy,
2822
+ onChange: (event) => setUserAgent(event.target.value),
2823
+ placeholder: protocol === "s3" ? "\u7559\u7A7A\u5373 dsh-inbox\uFF1B\u6709\u4E9B\u7F51\u5173\u6309\u5B83\u8BA4\u4EBA\uFF0C\u586B\u6210\u8FD9\u4E2A AccessKey \u7ED1\u5B9A\u7684\u5E94\u7528\u540D" : "\u7559\u7A7A\u5373 dsh-inbox\uFF1B\u6709\u4E9B\u7F51\u5173\u6309\u5B83\u8BA4\u4EBA\uFF0C\u586B\u6210\u8FD9\u4E2A WebDAV \u8D26\u53F7\u7ED1\u5B9A\u7684\u5E94\u7528\u540D",
2824
+ style: { ...inputStyle, flex: 1 }
2825
+ }
2826
+ )
2827
+ ] }),
2828
+ protocol === "webdav" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
2829
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2830
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: "\u5730\u5740" }),
2831
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2832
+ "input",
2833
+ {
2834
+ value: baseUrl,
2835
+ disabled: busy,
2836
+ onChange: (event) => setBaseUrl(event.target.value),
2837
+ placeholder: "https://data.cstcloud.cn/dav",
2838
+ style: { ...inputStyle, flex: 1 }
2839
+ }
2840
+ )
2841
+ ] }),
2842
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2843
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: "\u76EE\u5F55" }),
2844
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2845
+ "input",
2846
+ {
2847
+ value: directory,
2848
+ disabled: busy,
2849
+ onChange: (event) => setDirectory(event.target.value),
2850
+ placeholder: "/inbox",
2851
+ style: { ...inputStyle, flex: 1 }
2852
+ }
2853
+ ),
2854
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.6 }, children: "\u522B\u7684\u8BBE\u5907\u5F80\u8FD9\u91CC\u6254\u4E1C\u897F" })
2855
+ ] }),
2856
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2857
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: "\u7528\u6237\u540D" }),
2858
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2859
+ "input",
2860
+ {
2861
+ value: username,
2862
+ disabled: busy,
2863
+ onChange: (event) => setUsername(event.target.value),
2864
+ style: { ...inputStyle, flex: 1 }
2865
+ }
2866
+ )
2867
+ ] }),
2868
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2869
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: "\u5BC6\u7801" }),
2870
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2871
+ "input",
2872
+ {
2873
+ type: "password",
2874
+ value: password,
2875
+ disabled: busy || status?.credentialsAvailable === false,
2876
+ onChange: (event) => setPassword(event.target.value),
2877
+ placeholder: status?.passwordSet === true ? "\u5DF2\u5B58\uFF08\u7559\u7A7A\u5219\u4E0D\u6539\uFF09" : "\u5B58\u5728 dsh \u7684\u51ED\u8BC1\u5E93\u91CC",
2878
+ style: { ...inputStyle, flex: 1 }
2879
+ }
2880
+ )
2881
+ ] })
2882
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
2883
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2884
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: "\u63A5\u5165\u70B9" }),
2885
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2886
+ "input",
2887
+ {
2888
+ value: endpoint,
2889
+ disabled: busy,
2890
+ onChange: (event) => setEndpoint(event.target.value),
2891
+ placeholder: "s3.cstcloud.cn\uFF08\u4E0D\u5199\u534F\u8BAE\u9ED8\u8BA4 https\uFF09",
2892
+ style: { ...inputStyle, flex: 1 }
2893
+ }
2894
+ )
2895
+ ] }),
2896
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2897
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: "Bucket" }),
2898
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2899
+ "input",
2900
+ {
2901
+ value: bucket,
2902
+ disabled: busy,
2903
+ onChange: (event) => setBucket(event.target.value),
2904
+ style: { ...inputStyle, flex: 1 }
2905
+ }
2906
+ )
2907
+ ] }),
2908
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2909
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: "\u7B7E\u540D" }),
2910
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2911
+ SelectBox,
2912
+ {
2913
+ value: signatureVersion,
2914
+ options: [
2915
+ ["v4", "v4"],
2916
+ ["v2", "v2\uFF08\u8001\u7F51\u5173\u591A\u534A\u8981\u8FD9\u4E2A\uFF09"]
2917
+ ],
2918
+ disabled: busy,
2919
+ onChange: setSignatureVersion
2920
+ }
2921
+ ),
2922
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 40 }, children: "\u533A\u57DF" }),
2923
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2924
+ "input",
2925
+ {
2926
+ value: region,
2927
+ disabled: busy,
2928
+ onChange: (event) => setRegion(event.target.value),
2929
+ placeholder: "us-east-1",
2930
+ style: { ...inputStyle, width: 140 }
2931
+ }
2932
+ )
2933
+ ] }),
2934
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2935
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: "AccessKey ID" }),
2936
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2937
+ "input",
2938
+ {
2939
+ value: accessKeyId,
2940
+ disabled: busy,
2941
+ onChange: (event) => setAccessKeyId(event.target.value),
2942
+ style: { ...inputStyle, flex: 1 }
2943
+ }
2944
+ )
2945
+ ] }),
2946
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { style: { display: "flex", gap: 8, alignItems: "center" }, children: [
2947
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.7, minWidth: 64 }, children: "Secret" }),
2948
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2949
+ "input",
2950
+ {
2951
+ type: "password",
2952
+ value: accessKeySecret,
2953
+ disabled: busy || status?.credentialsAvailable === false,
2954
+ onChange: (event) => setAccessKeySecret(event.target.value),
2955
+ placeholder: status?.secretSet === true ? "\u5DF2\u5B58\uFF08\u7559\u7A7A\u5219\u4E0D\u6539\uFF09" : "\u5B58\u5728 dsh \u7684\u51ED\u8BC1\u5E93\u91CC",
2956
+ style: { ...inputStyle, flex: 1 }
2957
+ }
2958
+ )
2959
+ ] }),
2960
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { style: { margin: 0, opacity: 0.6 }, children: [
2961
+ "\u76EE\u5F55\u90A3\u4E00\u680F\u540C\u65F6\u662F S3 \u7684 key \u524D\u7F00\uFF08\u9ED8\u8BA4 ",
2962
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { children: "/inbox" }),
2963
+ "\uFF0C\u4F1A\u8F6C\u6210 ",
2964
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("code", { children: "inbox/" }),
2965
+ "\uFF09\u3002"
2966
+ ] })
2967
+ ] }),
2968
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }, children: [
2969
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", style: buttonStyle, disabled: busy, onClick: () => void save(), children: "\u4FDD\u5B58" }),
2970
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", style: buttonStyle, disabled: busy, onClick: () => void selfTest(), children: "\u81EA\u68C0" }),
2971
+ detailNotice(notice)
2972
+ ] }),
2973
+ probe !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { margin: 0, display: "flex", flexDirection: "column", gap: 6 }, children: [
2974
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
2975
+ "p",
2976
+ {
2977
+ style: {
2978
+ margin: 0,
2979
+ color: verdict.ok ? "inherit" : "salmon",
2980
+ fontWeight: 600
2981
+ },
2982
+ children: [
2983
+ verdict.ok ? "\u2705 " : "\u274C ",
2984
+ verdict.title
2985
+ ]
2986
+ }
2987
+ ),
2988
+ verdict.hint !== void 0 && verdict.hint.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { margin: 0, opacity: 0.7 }, children: verdict.hint }),
2989
+ probe.length > 1 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("details", { children: [
2990
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("summary", { style: { cursor: "pointer", opacity: 0.7, fontSize: 12 }, children: [
2991
+ "\u8BE6\u60C5\uFF08",
2992
+ probe.length,
2993
+ " \u6B21\u8BF7\u6C42\uFF09"
2994
+ ] }),
2995
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
2996
+ "pre",
2997
+ {
2998
+ style: {
2999
+ ...inputStyle,
3000
+ margin: "6px 0 0",
3001
+ maxHeight: 220,
3002
+ overflow: "auto",
3003
+ whiteSpace: "pre-wrap",
3004
+ fontSize: 12
3005
+ },
3006
+ children: probe.map(
3007
+ (row) => `${row.status === 0 ? "ERR" : String(row.status)} ${row.label}
3008
+ ${row.url}
3009
+ ${row.detail}`
3010
+ ).join("\n")
3011
+ }
3012
+ )
3013
+ ] })
3014
+ ] }),
3015
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { margin: 0, opacity: 0.6 }, children: "\u53EA\u505A\u5355\u5411\uFF1A\u8FDC\u7AEF\u5F80\u91CC\u6254\uFF0C\u672C\u673A\u62C9\u4E0B\u6765\u5165\u5E93\u3002\u5BC6\u7801\u8D70 dsh \u7684\u51ED\u8BC1\u5E93\uFF0C\u4E0D\u5199\u8FDB\u914D\u7F6E\u3002" })
3016
+ ] });
3017
+ }
3018
+ function detailNotice(notice) {
3019
+ return notice === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.8 }, children: notice });
3020
+ }
3021
+ var LIST_MODES = [
3022
+ { id: "grid", label: "\u7F51\u683C", icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(LayoutGrid, { size: 14 }) },
3023
+ { id: "compact", label: "\u7D27\u51D1", icon: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Layers, { size: 14 }) }
3024
+ ];
3025
+ function categoryGlyph(category, size) {
3026
+ switch (category) {
3027
+ case "idea":
3028
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Lightbulb, { size });
3029
+ case "article":
3030
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FileText, { size });
3031
+ case "media":
3032
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Film, { size });
3033
+ case "image":
3034
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Image, { size });
3035
+ case "document":
3036
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(IdCard, { size });
3037
+ case "secret":
3038
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(KeyRound, { size });
3039
+ case "other":
3040
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Layers, { size });
3041
+ }
3042
+ }
3043
+ function tileBackground(entry) {
3044
+ if (entry.kind === "image") {
3045
+ return "repeating-linear-gradient(135deg, color-mix(in srgb, currentColor 14%, transparent) 0 10px, color-mix(in srgb, currentColor 7%, transparent) 10px 20px)";
3046
+ }
3047
+ if (entry.platform === "bilibili" || entry.platform === "xiaoyuzhou") {
3048
+ return "linear-gradient(135deg, color-mix(in srgb, currentColor 16%, transparent), color-mix(in srgb, currentColor 6%, transparent))";
3049
+ }
3050
+ return "color-mix(in srgb, currentColor 8%, transparent)";
3051
+ }
3052
+ function SourceBadge({ source }) {
3053
+ const tint = source === "user" ? WATCH_COLOR : source === "model" ? MODEL_COLOR : void 0;
3054
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3055
+ "span",
3056
+ {
3057
+ title: CATEGORY_SOURCE_HINTS[source],
3058
+ style: {
3059
+ flex: "none",
3060
+ display: "inline-flex",
3061
+ alignItems: "center",
3062
+ padding: "1px 8px",
3063
+ borderRadius: 999,
3064
+ fontSize: 11,
3065
+ fontWeight: 600,
3066
+ whiteSpace: "nowrap",
3067
+ ...tint === void 0 ? {
3068
+ // The rule is the default, and the default is not news: it stays
3069
+ // grey so the two that mean "somebody made a judgement" stand out.
3070
+ background: "color-mix(in srgb, currentColor 8%, transparent)",
3071
+ border: "1px solid color-mix(in srgb, currentColor 20%, transparent)",
3072
+ color: "inherit",
3073
+ opacity: 0.7
3074
+ } : {
3075
+ background: `color-mix(in srgb, ${tint} 18%, transparent)`,
3076
+ border: `1px solid color-mix(in srgb, ${tint} 55%, transparent)`,
3077
+ color: tint
3078
+ }
3079
+ },
3080
+ children: CATEGORY_SOURCE_LABELS[source]
3081
+ }
3082
+ );
3083
+ }
3084
+ function RailRow({
3085
+ active,
3086
+ icon,
3087
+ label,
3088
+ count,
3089
+ onClick
3090
+ }) {
3091
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3092
+ "button",
3093
+ {
3094
+ type: "button",
3095
+ "aria-pressed": active,
3096
+ onClick,
3097
+ style: {
3098
+ display: "flex",
3099
+ alignItems: "center",
3100
+ gap: 8,
3101
+ width: "100%",
3102
+ textAlign: "left",
3103
+ font: "inherit",
3104
+ color: "inherit",
3105
+ background: active ? "color-mix(in srgb, currentColor 12%, transparent)" : "transparent",
3106
+ border: "none",
3107
+ borderRadius: 8,
3108
+ padding: "6px 9px",
3109
+ cursor: "pointer",
3110
+ opacity: active ? 1 : 0.72
3111
+ },
3112
+ children: [
3113
+ icon,
3114
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { flex: 1, minWidth: 0, overflowWrap: "anywhere" }, children: label }),
3115
+ count === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.6, fontSize: 12 }, children: count })
3116
+ ]
3117
+ }
3118
+ );
3119
+ }
3120
+ function EntryCard({
3121
+ entry,
3122
+ mode,
3123
+ selected,
3124
+ onOpen,
3125
+ onPreview
3126
+ }) {
3127
+ const secret = isSecret(entry);
3128
+ const headingText = headingOf(entry);
3129
+ const headingTitle = headingTooltipOf(entry);
3130
+ const compact = mode === "compact";
3131
+ const hairline = "color-mix(in srgb, currentColor 10%, transparent)";
3132
+ const accent = `color-mix(in srgb, ${ACCENT_COLOR} 55%, transparent)`;
3133
+ const glyph = /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3134
+ "span",
3135
+ {
3136
+ "aria-hidden": true,
3137
+ style: {
3138
+ flex: "none",
3139
+ display: "grid",
3140
+ placeItems: "center",
3141
+ background: tileBackground(entry),
3142
+ width: 38,
3143
+ height: 38,
3144
+ borderRadius: 8,
3145
+ border: `1px solid ${hairline}`
3146
+ },
3147
+ children: categoryGlyph(entry.category, 22)
3148
+ }
3149
+ );
3150
+ const previewMime = entry.previewMime ?? "";
3151
+ const previewKind = previewMime.startsWith("video/") ? "video" : previewMime.startsWith("audio/") ? "audio" : previewMime.startsWith("image/") ? "image" : void 0;
3152
+ const mediaLink = entry.kind === "link" && (entry.platform === "bilibili" || entry.platform === "xiaoyuzhou");
3153
+ const preview = compact || previewKind === void 0 && !mediaLink ? null : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3154
+ "button",
3155
+ {
3156
+ type: "button",
3157
+ title: previewKind === void 0 ? "\u5728\u6D4F\u89C8\u5668\u91CC\u64AD\u653E" : "\u5728\u9762\u677F\u91CC\u653E\u5927",
3158
+ onClick: (event) => {
3159
+ event.stopPropagation();
3160
+ onPreview();
3161
+ },
3162
+ style: {
3163
+ flex: "none",
3164
+ width: 64,
3165
+ height: 64,
3166
+ padding: 0,
3167
+ display: "grid",
3168
+ placeItems: "center",
3169
+ overflow: "hidden",
3170
+ background: tileBackground(entry),
3171
+ border: `1px solid ${hairline}`,
3172
+ borderRadius: 8,
3173
+ color: "inherit",
3174
+ cursor: previewKind === void 0 ? "pointer" : "zoom-in"
3175
+ },
3176
+ children: previewKind === "image" && entry.previewId !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3177
+ "img",
3178
+ {
3179
+ src: attachmentUrl(entry.previewId),
3180
+ alt: "",
3181
+ loading: "lazy",
3182
+ style: {
3183
+ width: "100%",
3184
+ height: "100%",
3185
+ objectFit: "cover",
3186
+ objectPosition: "center",
3187
+ display: "block"
3188
+ }
3189
+ }
3190
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Play, { size: 26 })
3191
+ }
3192
+ );
3193
+ return (
3194
+ /*
3195
+ A div carrying a button's semantics rather than a `<button>`: the preview
3196
+ slot is a real button of its own, and interactive content may not nest.
3197
+ */
3198
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3199
+ "div",
3200
+ {
3201
+ role: "button",
3202
+ tabIndex: 0,
3203
+ "aria-pressed": selected,
3204
+ onClick: onOpen,
3205
+ onKeyDown: (event) => {
3206
+ if (event.key !== "Enter" && event.key !== " ") return;
3207
+ event.preventDefault();
3208
+ onOpen();
3209
+ },
3210
+ style: {
3211
+ position: "relative",
3212
+ display: "flex",
3213
+ alignItems: "center",
3214
+ gap: compact ? 8 : 10,
3215
+ width: "100%",
3216
+ // A div is content-box by default, a `<button>` was not.
3217
+ boxSizing: "border-box",
3218
+ // One height for every card in the grid, whether or not it has a preview
3219
+ // line to fill: the tallest natural card is the one with a picture
3220
+ // (three text lines beside a 64px thumbnail), while a record whose
3221
+ // heading doubles as its preview only fills two. 94 is what that tallest
3222
+ // card measures with the panel's own font — the panel sets 14px/1.6
3223
+ // system-ui rather than inheriting dsh's, so the number does not move.
3224
+ ...compact ? {} : { minHeight: 94 },
3225
+ textAlign: "left",
3226
+ font: "inherit",
3227
+ color: "inherit",
3228
+ background: selected ? `color-mix(in srgb, ${ACCENT_COLOR} 16%, transparent)` : compact ? "transparent" : "color-mix(in srgb, currentColor 4%, transparent)",
3229
+ ...compact ? { border: "none", borderBottom: `1px solid ${hairline}`, borderRadius: 0 } : {
3230
+ border: `1px solid ${selected ? accent : hairline}`,
3231
+ borderRadius: 10
3232
+ },
3233
+ padding: compact ? "5px 10px" : "9px 10px",
3234
+ cursor: "pointer",
3235
+ opacity: 1
3236
+ },
3237
+ children: [
3238
+ glyph,
3239
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3240
+ "span",
3241
+ {
3242
+ style: {
3243
+ minWidth: 0,
3244
+ flex: 1,
3245
+ display: "flex",
3246
+ // Grid stacks title / preview / metadata; compact puts the title and
3247
+ // the right-hand category-and-date on one line, all centred on the
3248
+ // same axis as the glyph.
3249
+ flexDirection: compact ? "row" : "column",
3250
+ alignItems: compact ? "center" : "stretch",
3251
+ gap: compact ? 8 : 3
3252
+ },
3253
+ children: [
3254
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3255
+ "span",
3256
+ {
3257
+ style: {
3258
+ display: "flex",
3259
+ alignItems: "center",
3260
+ gap: 6,
3261
+ minWidth: 0,
3262
+ ...compact ? { flex: 1 } : {}
3263
+ },
3264
+ children: [
3265
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3266
+ "span",
3267
+ {
3268
+ title: headingTitle,
3269
+ style: {
3270
+ display: "block",
3271
+ minWidth: 0,
3272
+ // Width has a ceiling and a tail: no heading may stretch the card
3273
+ // or push the metadata around, it ellipsises instead.
3274
+ maxWidth: "100%",
3275
+ overflow: "hidden",
3276
+ textOverflow: "ellipsis",
3277
+ whiteSpace: "nowrap"
3278
+ },
3279
+ children: headingText
3280
+ }
3281
+ ),
3282
+ entry.watchLater && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3283
+ "span",
3284
+ {
3285
+ style: {
3286
+ flex: "none",
3287
+ display: "inline-flex",
3288
+ alignItems: "center",
3289
+ gap: 3,
3290
+ padding: "0 7px",
3291
+ borderRadius: 999,
3292
+ fontSize: 11,
3293
+ fontWeight: 600,
3294
+ background: `color-mix(in srgb, ${WATCH_COLOR} 20%, transparent)`,
3295
+ border: `1px solid color-mix(in srgb, ${WATCH_COLOR} 55%, transparent)`,
3296
+ color: WATCH_COLOR
3297
+ },
3298
+ children: [
3299
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Bookmark, { size: 11 }),
3300
+ " \u5F85\u770B"
3301
+ ]
3302
+ }
3303
+ )
3304
+ ]
3305
+ }
3306
+ ),
3307
+ !compact && !secret && entry.preview !== void 0 && entry.preview !== headingText && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3308
+ "span",
3309
+ {
3310
+ title: entry.preview,
3311
+ style: {
3312
+ display: "block",
3313
+ minWidth: 0,
3314
+ opacity: 0.65,
3315
+ overflow: "hidden",
3316
+ textOverflow: "ellipsis",
3317
+ whiteSpace: "nowrap"
3318
+ },
3319
+ children: entry.preview.slice(0, 90)
3320
+ }
3321
+ ),
3322
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3323
+ "span",
3324
+ {
3325
+ style: {
3326
+ display: "flex",
3327
+ gap: 6,
3328
+ alignItems: "center",
3329
+ minWidth: 0,
3330
+ overflow: "hidden",
3331
+ whiteSpace: "nowrap",
3332
+ ...compact ? {} : { marginTop: 4 },
3333
+ fontSize: 12,
3334
+ opacity: 0.6
3335
+ },
3336
+ children: [
3337
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { children: CATEGORY_LABELS[entry.category] }),
3338
+ entry.platform !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
3339
+ "\xB7 ",
3340
+ entry.platform
3341
+ ] }),
3342
+ entry.attachmentCount > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { children: [
3343
+ "\xB7 ",
3344
+ entry.attachmentCount,
3345
+ " \u9644\u4EF6"
3346
+ ] }),
3347
+ entry.deletedAt !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { color: "salmon" }, children: "\xB7 \u5DF2\u5220" }),
3348
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { flex: "none" }, children: [
3349
+ "\xB7 ",
3350
+ new Date(entry.createdAt).toLocaleDateString()
3351
+ ] }),
3352
+ entry.tags.map((tag) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { style: { minWidth: 0, overflow: "hidden", textOverflow: "ellipsis" }, children: [
3353
+ "#",
3354
+ tag
3355
+ ] }, tag))
3356
+ ]
3357
+ }
3358
+ )
3359
+ ]
3360
+ }
3361
+ ),
3362
+ preview
3363
+ ]
3364
+ }
3365
+ )
3366
+ );
3367
+ }
3368
+ function EntryPane({
3369
+ detail,
3370
+ busy,
3371
+ onUpdate,
3372
+ onDelete,
3373
+ onRestore,
3374
+ onZoom
3375
+ }) {
3376
+ const [note, setNote] = import_react7.default.useState(detail.note ?? "");
3377
+ const [title, setTitle] = import_react7.default.useState(detail.title ?? "");
3378
+ const [tags, setTags] = import_react7.default.useState(detail.tags.join(", "));
3379
+ import_react7.default.useEffect(() => {
3380
+ setNote(detail.note ?? "");
3381
+ setTitle(detail.title ?? "");
3382
+ setTags(detail.tags.join(", "));
3383
+ }, [detail]);
3384
+ const inBin = detail.deletedAt !== void 0;
3385
+ return (
3386
+ /*
3387
+ Two siblings rather than one column: the content scrolls inside the card
3388
+ (a long text, a stack of photos, and the form all have to stay reachable),
3389
+ while the timestamps hang off the *frame* — absolutely positioned against
3390
+ the card, 16px above its bottom edge, where neither the content's length
3391
+ nor its scrolling can move them. The content's `paddingBottom` keeps the
3392
+ last control clear of the band they occupy.
3393
+ */
3394
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
3395
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3396
+ "div",
3397
+ {
3398
+ style: {
3399
+ flex: 1,
3400
+ minHeight: 0,
3401
+ overflowY: "auto",
3402
+ // The band the stamps sit in is reserved structurally rather than as
3403
+ // padding: a scroll container's padding-bottom is *inside* the scroll
3404
+ // area, so the last control would still slide under the stamps while
3405
+ // scrolling. A margin takes the room out of the scrollport instead.
3406
+ marginBottom: 40,
3407
+ // Reserve the scrollbar's lane whether or not this record needs one,
3408
+ // so the controls do not change width as you click from record to
3409
+ // record (354px wide when they fit, 339px once a scrollbar appears).
3410
+ scrollbarGutter: "stable",
3411
+ display: "flex",
3412
+ flexDirection: "column",
3413
+ gap: 10
3414
+ },
3415
+ children: [
3416
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3417
+ "div",
3418
+ {
3419
+ style: {
3420
+ ...paneRowStyle,
3421
+ display: "flex",
3422
+ gap: 8,
3423
+ // Centre, not baseline: the source is a pill now, and a pill aligned
3424
+ // on the text baseline hangs off the bottom of the line.
3425
+ alignItems: "center",
3426
+ flexWrap: "wrap"
3427
+ },
3428
+ children: [
3429
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("strong", { children: KIND_LABELS[detail.kind] }),
3430
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.6 }, children: CATEGORY_LABELS[detail.category] }),
3431
+ detail.categorySource !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(SourceBadge, { source: detail.categorySource }),
3432
+ detail.platform !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { style: { opacity: 0.6 }, children: detail.platform })
3433
+ ]
3434
+ }
3435
+ ),
3436
+ detail.url !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3437
+ "a",
3438
+ {
3439
+ href: detail.url,
3440
+ target: "_blank",
3441
+ rel: "noreferrer",
3442
+ style: { ...paneRowStyle, overflowWrap: "anywhere" },
3443
+ children: detail.url
3444
+ }
3445
+ ),
3446
+ detail.text !== void 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3447
+ "pre",
3448
+ {
3449
+ style: {
3450
+ ...paneRowStyle,
3451
+ ...cardStyle,
3452
+ margin: 0,
3453
+ whiteSpace: "pre-wrap",
3454
+ overflowWrap: "anywhere",
3455
+ font: "13px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace",
3456
+ maxHeight: 260,
3457
+ overflow: "auto"
3458
+ },
3459
+ children: detail.text
3460
+ }
3461
+ ),
3462
+ detail.text === void 0 && detail.category === "secret" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { style: { ...paneRowStyle, margin: 0, opacity: 0.75 }, children: "\u8FD9\u6761\u8D26\u5BC6\u7684\u6B63\u6587\u662F\u5BC6\u6587\uFF0C\u73B0\u5728\u89E3\u4E0D\u5F00\u3002\u5230\u300C\u8BBE\u7F6E \u2192 \u8D26\u5BC6\u52A0\u5BC6\u300D\u89E3\u9501\uFF08\u6216\u5148\u8BBE\u4E00\u4E2A\u4E3B\u5BC6\u7801\uFF09\u5C31\u80FD\u770B\u5230\u3002" }),
3463
+ detail.attachments.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3464
+ "div",
3465
+ {
3466
+ style: {
3467
+ ...paneRowStyle,
3468
+ display: "flex",
3469
+ gap: 10,
3470
+ flexWrap: "wrap",
3471
+ justifyContent: "center"
3472
+ },
3473
+ children: detail.attachments.map((attachment) => {
3474
+ const src = attachmentUrl(attachment.id);
3475
+ const caption = attachmentCaption(attachment);
3476
+ const playable = attachment.mime.startsWith("video/") || attachment.mime.startsWith("audio/");
3477
+ if (!attachment.image && !playable) {
3478
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3479
+ "figure",
3480
+ {
3481
+ style: { ...cardStyle, margin: 0, padding: 8, textAlign: "center" },
3482
+ children: [
3483
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { style: { opacity: 0.7 }, children: "\u{1F4C4}" }),
3484
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("figcaption", { style: { opacity: 0.7, marginTop: 4, fontSize: 12 }, children: caption })
3485
+ ]
3486
+ },
3487
+ attachment.id
3488
+ );
3489
+ }
3490
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3491
+ "figure",
3492
+ {
3493
+ style: {
3494
+ margin: 0,
3495
+ maxWidth: "100%",
3496
+ display: "flex",
3497
+ flexDirection: "column",
3498
+ alignItems: "center",
3499
+ gap: 4
3500
+ },
3501
+ children: [
3502
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3503
+ "button",
3504
+ {
3505
+ type: "button",
3506
+ title: playable ? "\u64AD\u653E" : "\u653E\u5927\u67E5\u770B",
3507
+ onClick: () => onZoom({ src, mime: attachment.mime, label: caption }),
3508
+ style: {
3509
+ padding: 0,
3510
+ border: "none",
3511
+ background: "none",
3512
+ cursor: playable ? "pointer" : "zoom-in"
3513
+ },
3514
+ children: playable ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3515
+ "span",
3516
+ {
3517
+ style: {
3518
+ display: "grid",
3519
+ placeItems: "center",
3520
+ width: 160,
3521
+ height: 90,
3522
+ borderRadius: 6,
3523
+ border: "1px solid color-mix(in srgb, currentColor 10%, transparent)",
3524
+ background: tileBackground(detail),
3525
+ color: "inherit"
3526
+ },
3527
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Play, { size: 28 })
3528
+ }
3529
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3530
+ "img",
3531
+ {
3532
+ src,
3533
+ alt: attachment.filename ?? "",
3534
+ style: { maxWidth: 220, maxHeight: 220, borderRadius: 6, display: "block" }
3535
+ }
3536
+ )
3537
+ }
3538
+ ),
3539
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3540
+ "figcaption",
3541
+ {
3542
+ style: {
3543
+ opacity: 0.7,
3544
+ fontSize: 12,
3545
+ textAlign: "center",
3546
+ overflowWrap: "anywhere"
3547
+ },
3548
+ children: caption
3549
+ }
3550
+ )
3551
+ ]
3552
+ },
3553
+ attachment.id
3554
+ );
3555
+ })
3556
+ }
3557
+ ),
3558
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3559
+ "input",
3560
+ {
3561
+ value: title,
3562
+ disabled: busy,
3563
+ maxLength: MAX_TITLE_CHARS,
3564
+ onChange: (event) => setTitle(event.target.value),
3565
+ "aria-label": "\u540D\u79F0",
3566
+ title: "\u5217\u8868\u3001\u5361\u7247\u548C\u5BF9\u8BDD\u5361\u7247\u663E\u793A\u8FD9\u4E2A\u540D\u5B57\uFF1B\u7559\u7A7A\u5219\u7528\u6587\u4EF6\u540D\u6216\u5907\u6CE8\u515C\u5E95",
3567
+ placeholder: "\u540D\u79F0\uFF0C\u5982\uFF1A\u8EAB\u4EFD\u8BC1\u6B63\u9762\uFF08\u7559\u7A7A\u5219\u7528\u6587\u4EF6\u540D\u515C\u5E95\uFF09",
3568
+ style: { ...paneRowStyle, ...inputStyle, width: "100%", boxSizing: "border-box" }
3569
+ }
3570
+ ),
3571
+ detail.title === void 0 && detail.linkTitle === void 0 && detail.linkTitleError !== void 0 && detail.kind === "link" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { style: { ...paneRowStyle, margin: 0, fontSize: 12, opacity: 0.6 }, children: [
3572
+ "\u6CA1\u6293\u5230\u9875\u9762\u6807\u9898\uFF1A",
3573
+ titleFailureText(detail.linkTitleError),
3574
+ "\u3002\u53EF\u4EE5\u81EA\u5DF1\u8D77\u4E2A\u540D\u5B57\u3002"
3575
+ ] }),
3576
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3577
+ SelectBox,
3578
+ {
3579
+ block: true,
3580
+ label: "\u7C7B\u76EE",
3581
+ value: detail.category,
3582
+ options: CATEGORIES.map((value) => [value, CATEGORY_LABELS[value]]),
3583
+ disabled: busy,
3584
+ onChange: (next) => void onUpdate({ category: next })
3585
+ }
3586
+ ),
3587
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3588
+ "textarea",
3589
+ {
3590
+ value: note,
3591
+ disabled: busy,
3592
+ onChange: (event) => setNote(event.target.value),
3593
+ rows: 2,
3594
+ "aria-label": "\u5907\u6CE8",
3595
+ title: "\u4F60\u5199\u7684\u6C38\u8FDC\u4F18\u5148\u4E8E\u6A21\u578B\u7684\u5224\u65AD",
3596
+ placeholder: "\u5907\u6CE8\uFF0C\u5982\uFF1A\u8EAB\u4EFD\u8BC1\u7167 / \u5F85\u770B\u89C6\u9891 / \u8FD9\u4E2A key \u662F\u6D4B\u8BD5\u73AF\u5883\u7684\uFF08\u6CA1\u8D77\u540D\u5B57\u65F6\uFF0C\u5B83\u4F1A\u9876\u4E0A\u5F53\u5217\u8868\u91CC\u7684\u540D\u5B57\uFF09",
3597
+ style: {
3598
+ ...paneRowStyle,
3599
+ ...inputStyle,
3600
+ resize: "vertical",
3601
+ width: "100%",
3602
+ boxSizing: "border-box"
3603
+ }
3604
+ }
3605
+ ),
3606
+ detail.tags.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3607
+ "div",
3608
+ {
3609
+ style: {
3610
+ ...paneRowStyle,
3611
+ display: "flex",
3612
+ gap: 6,
3613
+ alignItems: "center",
3614
+ flexWrap: "wrap"
3615
+ },
3616
+ children: detail.tags.map((value) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3617
+ "span",
3618
+ {
3619
+ style: {
3620
+ display: "inline-flex",
3621
+ alignItems: "center",
3622
+ gap: 4,
3623
+ // Never let a flex row squeeze a chip to nothing: they collapsed
3624
+ // to zero width once, which is why nobody could see them.
3625
+ flex: "none",
3626
+ background: "color-mix(in srgb, currentColor 9%, transparent)",
3627
+ border: "1px solid color-mix(in srgb, currentColor 15%, transparent)",
3628
+ borderRadius: 999,
3629
+ padding: "1px 4px 1px 8px",
3630
+ fontSize: 12
3631
+ },
3632
+ children: [
3633
+ "#",
3634
+ value,
3635
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3636
+ "button",
3637
+ {
3638
+ type: "button",
3639
+ title: `\u4ECE\u8FD9\u6761\u8BB0\u5F55\u4E0A\u79FB\u9664\u300C${value}\u300D`,
3640
+ style: { ...actionStyle, padding: "2px 6px", gap: 2 },
3641
+ onClick: () => void onUpdate({ tags: detail.tags.filter((tag) => tag !== value) }),
3642
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(X, { size: 11 })
3643
+ }
3644
+ )
3645
+ ]
3646
+ },
3647
+ value
3648
+ ))
3649
+ }
3650
+ ),
3651
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
3652
+ "input",
3653
+ {
3654
+ value: tags,
3655
+ disabled: busy,
3656
+ onChange: (event) => setTags(event.target.value),
3657
+ "aria-label": "\u6807\u7B7E",
3658
+ placeholder: "\u8F93\u5165\u6807\u7B7E\uFF0C\u5982\uFF1A\u524D\u7AEF, \u62A5\u9500\uFF08\u9017\u53F7\u5206\u9694\uFF09",
3659
+ style: { ...paneRowStyle, ...inputStyle, width: "100%", boxSizing: "border-box" }
3660
+ }
3661
+ ),
3662
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { style: { ...paneRowStyle, display: "flex", gap: 6 }, children: [
3663
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3664
+ "button",
3665
+ {
3666
+ type: "button",
3667
+ style: { ...primaryStyle, flex: 1, justifyContent: "center", whiteSpace: "nowrap" },
3668
+ disabled: busy,
3669
+ onClick: () => void onUpdate({
3670
+ title: title.trim(),
3671
+ note,
3672
+ tags: tags.split(",").map((value) => value.trim()).filter((value) => value.length > 0)
3673
+ }),
3674
+ children: [
3675
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Check, { size: 14 }),
3676
+ "\u4FDD\u5B58\u4EE5\u4E0A"
3677
+ ]
3678
+ }
3679
+ ),
3680
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3681
+ "button",
3682
+ {
3683
+ type: "button",
3684
+ style: { ...actionStyle, flex: 1, justifyContent: "center", whiteSpace: "nowrap" },
3685
+ disabled: busy || inBin,
3686
+ onClick: () => void onUpdate({ watchLater: detail.watchLater !== true }),
3687
+ children: [
3688
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Bookmark, { size: 14 }),
3689
+ detail.watchLater === true ? "\u53D6\u6D88\u5F85\u770B" : "\u6807\u4E3A\u5F85\u770B"
3690
+ ]
3691
+ }
3692
+ ),
3693
+ inBin ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3694
+ "button",
3695
+ {
3696
+ type: "button",
3697
+ style: { ...actionStyle, flex: 1, justifyContent: "center", whiteSpace: "nowrap" },
3698
+ disabled: busy,
3699
+ onClick: () => void onRestore(),
3700
+ children: [
3701
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RotateCcw, { size: 14 }),
3702
+ " \u6062\u590D"
3703
+ ]
3704
+ }
3705
+ ) : /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3706
+ "button",
3707
+ {
3708
+ type: "button",
3709
+ style: { ...dangerStyle, flex: 1, justifyContent: "center", whiteSpace: "nowrap" },
3710
+ disabled: busy,
3711
+ onClick: () => void onDelete(),
3712
+ children: [
3713
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(Trash, { size: 14 }),
3714
+ " \u5220\u9664"
3715
+ ]
3716
+ }
3717
+ )
3718
+ ] })
3719
+ ]
3720
+ }
3721
+ ),
3722
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
3723
+ "div",
3724
+ {
3725
+ style: {
3726
+ position: "absolute",
3727
+ left: 12,
3728
+ right: 12,
3729
+ bottom: 16,
3730
+ fontSize: 12,
3731
+ opacity: 0.6,
3732
+ overflowWrap: "anywhere"
3733
+ },
3734
+ children: [
3735
+ "\u5B58\u5165 ",
3736
+ new Date(detail.createdAt).toLocaleString(),
3737
+ detail.updatedAt === detail.createdAt ? "" : ` \xB7 \u66F4\u65B0 ${new Date(detail.updatedAt).toLocaleString()}`
3738
+ ]
3739
+ }
3740
+ )
3741
+ ] })
3742
+ );
3743
+ }
3744
+ function describe(summary) {
3745
+ const parts = [];
3746
+ if (summary.stored > 0) parts.push(`\u5DF2\u5B58\u5165 ${summary.stored} \u6761`);
3747
+ const repeats = summary.merged - summary.restored;
3748
+ if (repeats > 0) parts.push(`\u5408\u5E76 ${repeats} \u6761\u91CD\u590D\u9879`);
3749
+ if (summary.restored > 0) parts.push(`\u4ECE\u56DE\u6536\u7AD9\u53D6\u56DE ${summary.restored} \u6761`);
3750
+ return parts.join("\uFF0C");
3751
+ }
3752
+ function isRpcResult(value) {
3753
+ return typeof value === "object" && value !== null && "ok" in value;
3754
+ }
3755
+ function formatBytes(bytes) {
3756
+ if (bytes < 1024) return `${bytes} B`;
3757
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
3758
+ return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
3759
+ }
3760
+ function attachmentCaption(attachment) {
3761
+ const pixels = attachment.width === void 0 || attachment.height === void 0 ? "" : ` \xB7 ${String(attachment.width)}\xD7${String(attachment.height)}`;
3762
+ return `${attachment.filename ?? attachment.mime}${pixels} \xB7 ${formatBytes(attachment.bytes)}`;
3763
+ }
3764
+ function attachmentUrl(id) {
3765
+ return `${INBOX_API_PREFIX}/${INBOX_ENDPOINT_ATTACHMENT}?id=${encodeURIComponent(id)}`;
3766
+ }
3767
+ async function toBase64(file) {
3768
+ const bytes = new Uint8Array(await file.arrayBuffer());
3769
+ let binary = "";
3770
+ const chunk = 32768;
3771
+ for (let offset = 0; offset < bytes.length; offset += chunk) {
3772
+ binary += String.fromCharCode(...bytes.subarray(offset, offset + chunk));
3773
+ }
3774
+ return btoa(binary);
3775
+ }
3776
+ /*! Bundled license information:
3777
+
3778
+ lucide-react/dist/esm/shared/src/utils/toKebabCase.mjs:
3779
+ lucide-react/dist/esm/shared/src/utils/toLucideIconData.mjs:
3780
+ lucide-react/dist/esm/shared/src/utils/toCamelCase.mjs:
3781
+ lucide-react/dist/esm/shared/src/utils/toPascalCase.mjs:
3782
+ lucide-react/dist/esm/shared/src/utils/mergeClasses.mjs:
3783
+ lucide-react/dist/esm/shared/src/build/defaultAttributes.mjs:
3784
+ lucide-react/dist/esm/shared/src/build/buildLucideIconNode.mjs:
3785
+ lucide-react/dist/esm/shared/src/build/buildLucideIconForReact.mjs:
3786
+ lucide-react/dist/esm/shared/src/utils/hasA11yProp.mjs:
3787
+ lucide-react/dist/esm/context.mjs:
3788
+ lucide-react/dist/esm/Icon.mjs:
3789
+ lucide-react/dist/esm/createLucideIcon.mjs:
3790
+ lucide-react/dist/esm/icons/bookmark.mjs:
3791
+ lucide-react/dist/esm/icons/check.mjs:
3792
+ lucide-react/dist/esm/icons/chevron-down.mjs:
3793
+ lucide-react/dist/esm/icons/chevron-left.mjs:
3794
+ lucide-react/dist/esm/icons/chevron-right.mjs:
3795
+ lucide-react/dist/esm/icons/circle.mjs:
3796
+ lucide-react/dist/esm/icons/external-link.mjs:
3797
+ lucide-react/dist/esm/icons/file-text.mjs:
3798
+ lucide-react/dist/esm/icons/film.mjs:
3799
+ lucide-react/dist/esm/icons/id-card.mjs:
3800
+ lucide-react/dist/esm/icons/image.mjs:
3801
+ lucide-react/dist/esm/icons/inbox.mjs:
3802
+ lucide-react/dist/esm/icons/key-round.mjs:
3803
+ lucide-react/dist/esm/icons/layers.mjs:
3804
+ lucide-react/dist/esm/icons/layout-grid.mjs:
3805
+ lucide-react/dist/esm/icons/lightbulb.mjs:
3806
+ lucide-react/dist/esm/icons/play.mjs:
3807
+ lucide-react/dist/esm/icons/refresh-cw.mjs:
3808
+ lucide-react/dist/esm/icons/rotate-ccw.mjs:
3809
+ lucide-react/dist/esm/icons/settings-2.mjs:
3810
+ lucide-react/dist/esm/icons/tag.mjs:
3811
+ lucide-react/dist/esm/icons/trash.mjs:
3812
+ lucide-react/dist/esm/icons/x.mjs:
3813
+ lucide-react/dist/esm/lucide-react.mjs:
3814
+ (**
3815
+ * @license lucide-react v1.47.0 - ISC
3816
+ *
3817
+ * This source code is licensed under the ISC license.
3818
+ * See the LICENSE file in the root directory of this source tree.
3819
+ *)
3820
+ */
3821
+ return module.exports;
3822
+ },
3823
+ });