dsh-plugin-versions 0.1.0 → 0.1.1

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 (3) hide show
  1. package/README.md +1 -0
  2. package/client.js +64 -9
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -10,6 +10,7 @@ DSH 官方的「版本」区块和更新按钮只出现在 **从 registry 安装
10
10
 
11
11
  - 插件页新增第 4 个标签页「版本一览」(`plugins.tab`,`order: 40`)
12
12
  - 先渲染本地版本,再逐行后台查 registry;只查 `updatable && !official` 的行,串行执行,避免同时打出一堆 pnpm 子进程
13
+ - 结果缓存在 `localStorage`(`dsh-plugin-versions:cache:v1`):再次打开先秒出上次的版本与状态,同时后台重新检查并逐行替换,表头显示「检查于 HH:MM」
13
14
  - 状态分档:`可更新到 x` / `已是最新` / `随 DSH 更新` / `无官方版本区块` / 检查中… / 检查失败(附错误原文前 300 字)
14
15
  - 可更新的行排前面,其余按包名排序
15
16
  - 点一行 → `openPackage(name)` 打开官方组合包详情页,并自动滚到底,直接落在「版本」区块
package/client.js CHANGED
@@ -129,11 +129,52 @@ window.__ModuleLoader__.load({
129
129
  );
130
130
  }
131
131
 
132
+ const CACHE_KEY = "dsh-plugin-versions:cache:v1";
133
+
134
+ function readCache() {
135
+ try {
136
+ const raw = window.localStorage.getItem(CACHE_KEY);
137
+ if (!raw) return null;
138
+ const d = JSON.parse(raw);
139
+ if (!d || typeof d !== "object" || !Array.isArray(d.bundles) || !d.rows) return null;
140
+ return d;
141
+ } catch (e) { return null; }
142
+ }
143
+
144
+ function writeCache(bundles, rows, at) {
145
+ try {
146
+ const keep = {};
147
+ Object.keys(rows || {}).forEach((k) => {
148
+ const r = rows[k];
149
+ if (r && r.status === "ready") keep[k] = r;
150
+ });
151
+ window.localStorage.setItem(CACHE_KEY, JSON.stringify({
152
+ at: at || Date.now(),
153
+ bundles: bundles || [],
154
+ rows: keep
155
+ }));
156
+ } catch (e) { /* 缓存写失败不影响使用 */ }
157
+ }
158
+
159
+ function clockText(ms) {
160
+ try {
161
+ const d = new Date(ms);
162
+ return String(d.getHours()).padStart(2, "0") + ":" + String(d.getMinutes()).padStart(2, "0");
163
+ } catch (e) { return ""; }
164
+ }
165
+
132
166
  function VersionsTab(props) {
133
167
  const ctx = props.ctx;
134
168
  const openPackage = props.openPackage;
135
- const [state, setState] = React.useState({ status: "loading", bundles: [], error: null });
136
- const [rows, setRows] = React.useState({});
169
+ const cached = readCache();
170
+ const [state, setState] = React.useState(
171
+ cached
172
+ ? { status: "ready", bundles: cached.bundles, error: null }
173
+ : { status: "loading", bundles: [], error: null }
174
+ );
175
+ const [rows, setRows] = React.useState(cached ? cached.rows : {});
176
+ const [checkedAt, setCheckedAt] = React.useState(cached ? cached.at || 0 : 0);
177
+ const [refreshing, setRefreshing] = React.useState(true);
137
178
  const [nonce, setNonce] = React.useState(0);
138
179
  const [pmState, setPmState] = React.useState(null);
139
180
 
@@ -153,8 +194,7 @@ window.__ModuleLoader__.load({
153
194
 
154
195
  const load = (pm) => {
155
196
  setPmState(pm);
156
- setState({ status: "loading", bundles: [], error: null });
157
- setRows({});
197
+ setRefreshing(true);
158
198
  Promise.resolve().then(() => pm.listBundles()).then((res) => {
159
199
  if (cancelled) return;
160
200
  if (res && res.ok === false) {
@@ -163,7 +203,9 @@ window.__ModuleLoader__.load({
163
203
  return;
164
204
  }
165
205
  const value = res && res.ok === true ? res.value : res;
166
- setState({ status: "ready", bundles: Array.isArray(value) ? value : [], error: null });
206
+ const list = Array.isArray(value) ? value : [];
207
+ setState({ status: "ready", bundles: list, error: null });
208
+ setRows((prev) => { writeCache(list, prev, Date.now()); return prev; });
167
209
  }).catch((err) => {
168
210
  if (cancelled) return;
169
211
  setState({ status: "error", bundles: [], error: String((err && err.message) || err) });
@@ -182,7 +224,6 @@ window.__ModuleLoader__.load({
182
224
  setState({ status: "error", bundles: [], error: "拿不到 pluginManager。" + why });
183
225
  };
184
226
 
185
- setState({ status: "loading", bundles: [], error: null });
186
227
  attempt();
187
228
  return () => { cancelled = true; if (timer) clearTimeout(timer); };
188
229
  }, [nonce]);
@@ -194,7 +235,9 @@ window.__ModuleLoader__.load({
194
235
  (async () => {
195
236
  for (const b of queue) {
196
237
  if (cancelled) return;
197
- setRows((prev) => Object.assign({}, prev, { [b.name]: { status: "loading" } }));
238
+ setRows((prev) => (prev[b.name] && prev[b.name].status === "ready")
239
+ ? prev
240
+ : Object.assign({}, prev, { [b.name]: { status: "loading" } }));
198
241
  let next;
199
242
  try {
200
243
  const res = await pmState.listVersions(b.name, {});
@@ -208,8 +251,15 @@ window.__ModuleLoader__.load({
208
251
  next = { status: "error", error: String((err && err.message) || err) };
209
252
  }
210
253
  if (cancelled) return;
211
- setRows((prev) => Object.assign({}, prev, { [b.name]: next }));
254
+ setRows((prev) => {
255
+ const nextRows = Object.assign({}, prev, { [b.name]: next });
256
+ writeCache(state.bundles, nextRows, Date.now());
257
+ return nextRows;
258
+ });
212
259
  }
260
+ if (cancelled) return;
261
+ setCheckedAt(Date.now());
262
+ setRefreshing(false);
213
263
  })();
214
264
  return () => { cancelled = true; };
215
265
  }, [state.status, state.bundles, pmState]);
@@ -220,11 +270,16 @@ window.__ModuleLoader__.load({
220
270
  const failed = installed.filter((b) => rows[b.name] && rows[b.name].status === "error").length;
221
271
  const pending = installed.filter((b) => b.updatable === true && b.official !== true && !rows[b.name]).length;
222
272
  const header = h("div", { style: S.bar },
223
- h("span", { style: { opacity: 0.45, fontSize: "11px" } }, "版本一览 v10"),
273
+ h("span", { style: { opacity: 0.45, fontSize: "11px" } }, "版本一览 v11"),
224
274
  h("span", { style: S.muted },
225
275
  state.status === "loading" ? "正在读取已安装组合包…" :
226
276
  state.status === "error" ? "读取失败" :
227
277
  ((newer > 0 ? newer + " 个可更新" : (pending > 0 ? "正在检查…" : "全部已是最新")) + (failed > 0 ? " · " + failed + " 个检查失败" : "") + " · 共 " + installed.length + " 个已安装")),
278
+ h("span", { style: { opacity: 0.5, fontSize: "11px", marginLeft: "8px" } },
279
+ checkedAt ? ("检查于 " + clockText(checkedAt)) : ""),
280
+ refreshing && state.status !== "loading"
281
+ ? h("span", { style: { opacity: 0.5, fontSize: "11px", marginLeft: "6px" } }, "刷新中…")
282
+ : null,
228
283
  h("span", { style: { flex: "1 1 auto" } }),
229
284
  h("button", { style: S.btn, onClick: () => setNonce(nonce + 1), disabled: state.status === "loading" }, "刷新")
230
285
  );
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-plugin-versions",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "List installed DeepSeek Harness bundles with versions and update status, and jump straight to the official update page.",
5
5
  "type": "module",
6
6
  "main": "index.js",