@dsh-sparrow/dsh-file-manage 0.1.0-alpha.1 → 0.1.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +4 -0
- package/CHANGELOG.zh-CN.md +4 -0
- package/lib/client/FileManageDock.js +26 -13
- package/lib/client/styles.js +25 -0
- package/lib/client.js +52 -15
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,3 +8,7 @@ English | [简体中文](CHANGELOG.zh-CN.md)
|
|
|
8
8
|
- Sidebar "Cloud Files" entry + panel (styled like the official Settings / Archive): list pages of 20, Load more cursor pagination, per-file delete (stronger notice for `dsh-` auto-uploaded files), one-click copy file_id
|
|
9
9
|
- Total count + drive-style quota bar: used / 25 GiB, adaptive-precision percentage, striped empty area, "Loaded X / N" synced with pagination
|
|
10
10
|
- Reuses the official DeepSeekFilesClient (connection facts from the llm-deepseek settings section + ctx.credentials); no local persistence
|
|
11
|
+
|
|
12
|
+
## 0.1.0-alpha.2 (2026-09-01 · pre-release)
|
|
13
|
+
|
|
14
|
+
- Panel opens with a full-panel loading state: content is revealed only after both the file list and the total-count request settle, eliminating the loading flicker
|
package/CHANGELOG.zh-CN.md
CHANGED
|
@@ -8,3 +8,7 @@
|
|
|
8
8
|
- 侧边栏「云端文件」入口 + 面板(样式对齐官方设置 / 归档面板):每页 20 条列表、Load more 游标翻页、单条删除(`dsh-` 自动上传文件给更强提示)、一键复制 file_id
|
|
9
9
|
- 总数 + 网盘式配额条:已用 / 25 GiB、自适应精度百分比、条纹空白区、「已加载 X / N」随翻页同步
|
|
10
10
|
- 复用官方 DeepSeekFilesClient(连接事实取自 llm-deepseek 设置节 + ctx.credentials);无本地持久化
|
|
11
|
+
|
|
12
|
+
## 0.1.0-alpha.2 (2026-09-01 · pre-release)
|
|
13
|
+
|
|
14
|
+
- 面板打开改为整页 loading:文件列表与总数统计都就绪后才展示内容,消除加载闪动
|
|
@@ -17,14 +17,19 @@ export function FileManageDock({ wide, listFiles, deleteFile, countFiles, t }) {
|
|
|
17
17
|
const [busyDelete, setBusyDelete] = useState(false);
|
|
18
18
|
const [copied, setCopied] = useState(null);
|
|
19
19
|
const [summary, setSummary] = useState(null);
|
|
20
|
+
/** 总数统计请求尚未落定:与 loading 一起构成首屏 ready 门(2026-09-01 整页 loading)。 */
|
|
21
|
+
const [summaryPending, setSummaryPending] = useState(false);
|
|
20
22
|
const closeButtonRef = useRef(null);
|
|
21
23
|
/** 刷新代际:快速关/开面板产生并发请求时,只让最新一次的结果落地(陈旧响应竞态,archive 同款)。 */
|
|
22
24
|
const refreshSeqRef = useRef(0);
|
|
23
25
|
/** 「已复制」反馈 2s 复位(archive 同款)。 */
|
|
24
26
|
const copiedTimerRef = useRef(null);
|
|
25
|
-
|
|
27
|
+
/** 首屏 / 重试共用:列表与总数统计并发拉取,两者都落定才揭开内容(整页 loading 防闪动,2026-09-01)。 */
|
|
28
|
+
const reload = useCallback(() => {
|
|
26
29
|
const seq = ++refreshSeqRef.current;
|
|
27
30
|
setLoading(true);
|
|
31
|
+
setSummaryPending(true);
|
|
32
|
+
setSummary(null);
|
|
28
33
|
setError(null);
|
|
29
34
|
listFiles()
|
|
30
35
|
.then(page => {
|
|
@@ -43,7 +48,18 @@ export function FileManageDock({ wide, listFiles, deleteFile, countFiles, t }) {
|
|
|
43
48
|
if (seq === refreshSeqRef.current)
|
|
44
49
|
setLoading(false);
|
|
45
50
|
});
|
|
46
|
-
|
|
51
|
+
// 总数统计为 best-effort:接口失败只隐藏统计行,不影响列表;同样受刷新代际约束。
|
|
52
|
+
void countFiles()
|
|
53
|
+
.then(next => {
|
|
54
|
+
if (seq === refreshSeqRef.current)
|
|
55
|
+
setSummary(next);
|
|
56
|
+
})
|
|
57
|
+
.catch(() => { })
|
|
58
|
+
.finally(() => {
|
|
59
|
+
if (seq === refreshSeqRef.current)
|
|
60
|
+
setSummaryPending(false);
|
|
61
|
+
});
|
|
62
|
+
}, [listFiles, countFiles]);
|
|
47
63
|
// 官方弹窗行为:打开时聚焦关闭按钮,Esc 关闭。
|
|
48
64
|
useEffect(() => {
|
|
49
65
|
if (!open)
|
|
@@ -61,16 +77,11 @@ export function FileManageDock({ wide, listFiles, deleteFile, countFiles, t }) {
|
|
|
61
77
|
return;
|
|
62
78
|
setConfirming(null);
|
|
63
79
|
setSummary(null);
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
if (seq === refreshSeqRef.current)
|
|
70
|
-
setSummary(next);
|
|
71
|
-
})
|
|
72
|
-
.catch(() => { });
|
|
73
|
-
}, [open, loadFirst, countFiles]);
|
|
80
|
+
setRows([]);
|
|
81
|
+
setHasMore(false);
|
|
82
|
+
setLastId(undefined);
|
|
83
|
+
reload();
|
|
84
|
+
}, [open, reload]);
|
|
74
85
|
useEffect(() => () => {
|
|
75
86
|
if (copiedTimerRef.current !== null)
|
|
76
87
|
window.clearTimeout(copiedTimerRef.current);
|
|
@@ -101,6 +112,8 @@ export function FileManageDock({ wide, listFiles, deleteFile, countFiles, t }) {
|
|
|
101
112
|
setError(t('copyFailed'));
|
|
102
113
|
}
|
|
103
114
|
}, [t]);
|
|
115
|
+
/** 首屏 ready 门:列表 + 总数都落定前展示整页 loading(2026-09-01)。 */
|
|
116
|
+
const initialLoading = loading || summaryPending;
|
|
104
117
|
const quotaRatio = summary === null ? 0 : storageUsageRatio(summary.totalBytes, summary.quotaBytes);
|
|
105
118
|
const submitDelete = useCallback(() => {
|
|
106
119
|
if (confirming === null || busyDelete)
|
|
@@ -126,7 +139,7 @@ export function FileManageDock({ wide, listFiles, deleteFile, countFiles, t }) {
|
|
|
126
139
|
? _jsx("div", { className: "dsh-file-manage-quota-fill", style: { width: `${Math.round(quotaRatio * 100)}%` } })
|
|
127
140
|
: null] }), _jsx("p", { className: "dsh-file-manage-count", children: rows.length < summary.count
|
|
128
141
|
? t('summary.loaded', { loaded: rows.length, count: summary.count })
|
|
129
|
-
: t('summary.count', { count: summary.count }) })] })) : null,
|
|
142
|
+
: t('summary.count', { count: summary.count }) })] })) : null, _jsx("div", { style: { ...styles.body, padding: summary !== null ? '0 24px 24px' : '12px 24px 24px' }, className: "dsh-file-manage-body", children: initialLoading ? (_jsxs("div", { className: "dsh-file-manage-loading", role: "status", children: [_jsx("span", { className: "dsh-file-manage-spinner", "aria-hidden": true }), _jsx("span", { children: t('loading') })] })) : (_jsxs(_Fragment, { children: [error !== null ? (_jsxs("p", { role: "alert", style: { ...styles.secondarySmall, margin: '0 0 12px', color: 'var(--dsw-alias-state-error-primary, #c62828)' }, children: [error, ' ', _jsx("button", { type: "button", className: "dsh-file-manage-btn", onClick: () => { reload(); }, children: t('retry') })] })) : null, error === null && rows.length === 0 ? _jsx("p", { style: styles.secondarySmall, children: t('empty') }) : null, rows.length > 0 ? (_jsx("div", { className: "dsh-file-manage-card", children: rows.map(row => (_jsxs("div", { style: styles.row, children: [_jsxs("div", { style: { minWidth: 0 }, children: [_jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 6, minWidth: 0 }, children: [_jsx("span", { style: styles.title, title: row.filename, children: row.filename }), row.dshOwned ? _jsx("span", { className: "dsh-file-manage-badge", children: t('dshBadge') }) : null] }), _jsxs("div", { style: styles.secondarySmall, children: [row.sizeLabel, " \u00B7 ", row.createdAtLabel, row.expiresAtLabel !== undefined ? ` · ${t('expires', { time: row.expiresAtLabel })}` : ''] })] }), _jsxs("div", { style: styles.actions, children: [_jsx("button", { type: "button", className: "dsh-file-manage-btn", onClick: () => { void copyId(row); }, children: copied === row.id ? t('copied') : t('copy') }), _jsx("button", { type: "button", className: "dsh-file-manage-btn dsh-file-manage-btn-danger", onClick: () => { setConfirming(row); }, children: t('delete') })] })] }, row.id))) })) : null] })) }), hasMore && !initialLoading ? (_jsx("div", { style: styles.footerBar, children: _jsx("button", { type: "button", className: "dsh-file-manage-btn", disabled: loadingMore, onClick: () => { void loadMore(); }, children: loadingMore ? t('loading') : t('loadMore') }) })) : null] }) })) : null, confirming !== null ? (_jsx("div", { className: "dsh-file-manage-confirm-overlay", role: "presentation", onMouseDown: (event) => {
|
|
130
143
|
if (event.target === event.currentTarget && !busyDelete)
|
|
131
144
|
setConfirming(null);
|
|
132
145
|
}, children: _jsxs("div", { className: "dsh-file-manage-confirm-card", role: "alertdialog", "aria-modal": "true", "aria-label": t('delete'), onKeyDown: (event) => {
|
package/lib/client/styles.js
CHANGED
|
@@ -54,6 +54,31 @@ export function ensureFileManageStyles() {
|
|
|
54
54
|
text-overflow: ellipsis;
|
|
55
55
|
white-space: nowrap;
|
|
56
56
|
}
|
|
57
|
+
/* 整页 loading:首屏数据(列表 + 总数)落定前占满内容区,避免「打开后加载闪动」(2026-09-01)。 */
|
|
58
|
+
.dsh-file-manage-loading {
|
|
59
|
+
display: flex;
|
|
60
|
+
align-items: center;
|
|
61
|
+
justify-content: center;
|
|
62
|
+
gap: 10px;
|
|
63
|
+
min-height: 240px;
|
|
64
|
+
color: var(--dsw-alias-label-secondary, #6b7280);
|
|
65
|
+
font-size: 14px;
|
|
66
|
+
line-height: 22px;
|
|
67
|
+
}
|
|
68
|
+
.dsh-file-manage-spinner {
|
|
69
|
+
width: 16px;
|
|
70
|
+
height: 16px;
|
|
71
|
+
border: 2px solid var(--dsw-alias-border-l1, #d4d8e0);
|
|
72
|
+
border-top-color: var(--dsw-alias-button-info-fill, #4d6bfe);
|
|
73
|
+
border-radius: 50%;
|
|
74
|
+
animation: dsh-file-manage-spin 0.8s linear infinite;
|
|
75
|
+
}
|
|
76
|
+
@keyframes dsh-file-manage-spin {
|
|
77
|
+
to { transform: rotate(360deg); }
|
|
78
|
+
}
|
|
79
|
+
@media (prefers-reduced-motion: reduce) {
|
|
80
|
+
.dsh-file-manage-spinner { animation: none; }
|
|
81
|
+
}
|
|
57
82
|
/* 面板滚动区:官方 settings 同款——elevated surface 重绑 l2 滚动条 token(base 默认 l1,浮层上对比度不对)。 */
|
|
58
83
|
.dsh-file-manage-body {
|
|
59
84
|
--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
|
package/lib/client.js
CHANGED
|
@@ -151,6 +151,31 @@ function ensureFileManageStyles() {
|
|
|
151
151
|
text-overflow: ellipsis;
|
|
152
152
|
white-space: nowrap;
|
|
153
153
|
}
|
|
154
|
+
/* \u6574\u9875 loading\uFF1A\u9996\u5C4F\u6570\u636E\uFF08\u5217\u8868 + \u603B\u6570\uFF09\u843D\u5B9A\u524D\u5360\u6EE1\u5185\u5BB9\u533A\uFF0C\u907F\u514D\u300C\u6253\u5F00\u540E\u52A0\u8F7D\u95EA\u52A8\u300D\uFF082026-09-01\uFF09\u3002 */
|
|
155
|
+
.dsh-file-manage-loading {
|
|
156
|
+
display: flex;
|
|
157
|
+
align-items: center;
|
|
158
|
+
justify-content: center;
|
|
159
|
+
gap: 10px;
|
|
160
|
+
min-height: 240px;
|
|
161
|
+
color: var(--dsw-alias-label-secondary, #6b7280);
|
|
162
|
+
font-size: 14px;
|
|
163
|
+
line-height: 22px;
|
|
164
|
+
}
|
|
165
|
+
.dsh-file-manage-spinner {
|
|
166
|
+
width: 16px;
|
|
167
|
+
height: 16px;
|
|
168
|
+
border: 2px solid var(--dsw-alias-border-l1, #d4d8e0);
|
|
169
|
+
border-top-color: var(--dsw-alias-button-info-fill, #4d6bfe);
|
|
170
|
+
border-radius: 50%;
|
|
171
|
+
animation: dsh-file-manage-spin 0.8s linear infinite;
|
|
172
|
+
}
|
|
173
|
+
@keyframes dsh-file-manage-spin {
|
|
174
|
+
to { transform: rotate(360deg); }
|
|
175
|
+
}
|
|
176
|
+
@media (prefers-reduced-motion: reduce) {
|
|
177
|
+
.dsh-file-manage-spinner { animation: none; }
|
|
178
|
+
}
|
|
154
179
|
/* \u9762\u677F\u6EDA\u52A8\u533A\uFF1A\u5B98\u65B9 settings \u540C\u6B3E\u2014\u2014elevated surface \u91CD\u7ED1 l2 \u6EDA\u52A8\u6761 token\uFF08base \u9ED8\u8BA4 l1\uFF0C\u6D6E\u5C42\u4E0A\u5BF9\u6BD4\u5EA6\u4E0D\u5BF9\uFF09\u3002 */
|
|
155
180
|
.dsh-file-manage-body {
|
|
156
181
|
--dsh-scrollbar-thumb: var(--dsw-alias-scrollbar-bg-l2);
|
|
@@ -405,12 +430,15 @@ function FileManageDock({ wide, listFiles, deleteFile, countFiles, t }) {
|
|
|
405
430
|
const [busyDelete, setBusyDelete] = (0, import_react.useState)(false);
|
|
406
431
|
const [copied, setCopied] = (0, import_react.useState)(null);
|
|
407
432
|
const [summary, setSummary] = (0, import_react.useState)(null);
|
|
433
|
+
const [summaryPending, setSummaryPending] = (0, import_react.useState)(false);
|
|
408
434
|
const closeButtonRef = (0, import_react.useRef)(null);
|
|
409
435
|
const refreshSeqRef = (0, import_react.useRef)(0);
|
|
410
436
|
const copiedTimerRef = (0, import_react.useRef)(null);
|
|
411
|
-
const
|
|
437
|
+
const reload = (0, import_react.useCallback)(() => {
|
|
412
438
|
const seq = ++refreshSeqRef.current;
|
|
413
439
|
setLoading(true);
|
|
440
|
+
setSummaryPending(true);
|
|
441
|
+
setSummary(null);
|
|
414
442
|
setError(null);
|
|
415
443
|
listFiles().then((page) => {
|
|
416
444
|
if (seq !== refreshSeqRef.current) return;
|
|
@@ -423,7 +451,13 @@ function FileManageDock({ wide, listFiles, deleteFile, countFiles, t }) {
|
|
|
423
451
|
}).finally(() => {
|
|
424
452
|
if (seq === refreshSeqRef.current) setLoading(false);
|
|
425
453
|
});
|
|
426
|
-
|
|
454
|
+
void countFiles().then((next) => {
|
|
455
|
+
if (seq === refreshSeqRef.current) setSummary(next);
|
|
456
|
+
}).catch(() => {
|
|
457
|
+
}).finally(() => {
|
|
458
|
+
if (seq === refreshSeqRef.current) setSummaryPending(false);
|
|
459
|
+
});
|
|
460
|
+
}, [listFiles, countFiles]);
|
|
427
461
|
(0, import_react.useEffect)(() => {
|
|
428
462
|
if (!open) return;
|
|
429
463
|
closeButtonRef.current?.focus();
|
|
@@ -439,13 +473,11 @@ function FileManageDock({ wide, listFiles, deleteFile, countFiles, t }) {
|
|
|
439
473
|
if (!open) return;
|
|
440
474
|
setConfirming(null);
|
|
441
475
|
setSummary(null);
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
void
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
});
|
|
448
|
-
}, [open, loadFirst, countFiles]);
|
|
476
|
+
setRows([]);
|
|
477
|
+
setHasMore(false);
|
|
478
|
+
setLastId(void 0);
|
|
479
|
+
reload();
|
|
480
|
+
}, [open, reload]);
|
|
449
481
|
(0, import_react.useEffect)(() => () => {
|
|
450
482
|
if (copiedTimerRef.current !== null) window.clearTimeout(copiedTimerRef.current);
|
|
451
483
|
}, []);
|
|
@@ -475,6 +507,7 @@ function FileManageDock({ wide, listFiles, deleteFile, countFiles, t }) {
|
|
|
475
507
|
setError(t("copyFailed"));
|
|
476
508
|
}
|
|
477
509
|
}, [t]);
|
|
510
|
+
const initialLoading = loading || summaryPending;
|
|
478
511
|
const quotaRatio = summary === null ? 0 : storageUsageRatio(summary.totalBytes, summary.quotaBytes);
|
|
479
512
|
const submitDelete = (0, import_react.useCallback)(() => {
|
|
480
513
|
if (confirming === null || busyDelete) return;
|
|
@@ -525,14 +558,18 @@ function FileManageDock({ wide, listFiles, deleteFile, countFiles, t }) {
|
|
|
525
558
|
] }),
|
|
526
559
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { className: "dsh-file-manage-count", children: rows.length < summary.count ? t("summary.loaded", { loaded: rows.length, count: summary.count }) : t("summary.count", { count: summary.count }) })
|
|
527
560
|
] }) : null,
|
|
528
|
-
/* @__PURE__ */ (0, import_jsx_runtime.
|
|
561
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { ...styles.body, padding: summary !== null ? "0 24px 24px" : "12px 24px 24px" }, className: "dsh-file-manage-body", children: initialLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "dsh-file-manage-loading", role: "status", children: [
|
|
562
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "dsh-file-manage-spinner", "aria-hidden": true }),
|
|
563
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: t("loading") })
|
|
564
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
529
565
|
error !== null ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("p", { role: "alert", style: { ...styles.secondarySmall, margin: "0 0 12px", color: "var(--dsw-alias-state-error-primary, #c62828)" }, children: [
|
|
530
566
|
error,
|
|
531
567
|
" ",
|
|
532
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-file-manage-btn", onClick:
|
|
568
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", className: "dsh-file-manage-btn", onClick: () => {
|
|
569
|
+
reload();
|
|
570
|
+
}, children: t("retry") })
|
|
533
571
|
] }) : null,
|
|
534
|
-
|
|
535
|
-
!loading && error === null && rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.secondarySmall, children: t("empty") }) : null,
|
|
572
|
+
error === null && rows.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.secondarySmall, children: t("empty") }) : null,
|
|
536
573
|
rows.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "dsh-file-manage-card", children: rows.map((row) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.row, children: [
|
|
537
574
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { minWidth: 0 }, children: [
|
|
538
575
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", alignItems: "center", gap: 6, minWidth: 0 }, children: [
|
|
@@ -571,8 +608,8 @@ function FileManageDock({ wide, listFiles, deleteFile, countFiles, t }) {
|
|
|
571
608
|
)
|
|
572
609
|
] })
|
|
573
610
|
] }, row.id)) }) : null
|
|
574
|
-
] }),
|
|
575
|
-
hasMore && !
|
|
611
|
+
] }) }),
|
|
612
|
+
hasMore && !initialLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.footerBar, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
576
613
|
"button",
|
|
577
614
|
{
|
|
578
615
|
type: "button",
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dsh-sparrow/dsh-file-manage",
|
|
3
3
|
"description": "DSH web plugin: manage DeepSeek Files API cloud files from the sidebar — list with cursor pagination, delete, and copy file_id (reuses the official DeepSeekFilesClient, no extra credentials)",
|
|
4
|
-
"version": "0.1.0-alpha.
|
|
4
|
+
"version": "0.1.0-alpha.2",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|