dsh-ws-recency 0.9.1 → 0.9.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.
Files changed (3) hide show
  1. package/README.md +4 -2
  2. package/client.js +56 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -12,7 +12,9 @@ DSH 已经把**组内**会话按 `updatedAt` 倒序排好了(排序方式 =
12
12
 
13
13
  会话超过 `foldDays`(默认 **3 天**)没有更新就折叠起来;整个工作区都过期时,它只剩一行工作区标题——需要翻历史时点开就行。
14
14
 
15
- - **两个控件各管一件事**:宿主的「展开其余 N 条会话」负责让**宿主渲染**更多行(数字由宿主自己报,插件不再改写它);插件的 `展开历史 N` / `收起历史` 只负责把**已渲染但过期**的行折叠起来,点两次就是展开、收起一对;
15
+ - **每组一个控件**:宿主的「展开其余 N 条会话」被隐藏,由插件的 `展开历史 N` / `收起历史` 接管。N = 宿主还没渲染的行 + 已折叠的过期行;
16
+ - 点「展开历史」→ 插件逐轮驱动宿主那个隐藏按钮(每轮它会多渲染 5 条,剩下 ≤5 条时一次全出),直到整组都渲染出来,同时展开过期行——**一次点到全展开**;
17
+ - 点「收起历史」→ 重新折叠过期行(宿主已渲染的部分仍由插件按时间折叠,不会丢失);
16
18
  - 选中 / 运行中 / 等待处理的会话**永不折叠**(可在设置里关掉这个豁免);
17
19
  - 空白草稿行(新会话)不参与折叠判断;
18
20
  - 每 5 分钟自动重算一次,所以跨过 3 天边界的会话会自己折叠,不用刷新。
@@ -96,7 +98,7 @@ dsh plugin --profile <profile> add github:chyxhtc/dsh-ws-recency
96
98
  或固定版本的预构建包:
97
99
 
98
100
  ```powershell
99
- dsh plugin --profile <profile> add https://github.com/chyxhtc/dsh-ws-recency/releases/download/v0.9.1/dsh-ws-recency-0.9.1.tgz
101
+ dsh plugin --profile <profile> add https://github.com/chyxhtc/dsh-ws-recency/releases/download/v0.9.2/dsh-ws-recency-0.9.2.tgz
100
102
  ```
101
103
 
102
104
  装完把 `dsh-ws-recency` 加进 `~/.hypers-work/profiles/<profile>/package.json` 的 `dsh.profile.bundles`,重启应用。
package/client.js CHANGED
@@ -69,6 +69,8 @@
69
69
  var MIN_DAYS = 1;
70
70
  var MAX_DAYS = 90;
71
71
  var DEFAULT_DAYS = 3;
72
+ /** Safety stop for driving the host's own "show more" (5 rows per click). */
73
+ var MAX_EXPAND_CLICKS = 40;
72
74
  var TICK_MS = 5 * 60 * 1000;
73
75
 
74
76
  var data = { sessions: null, workspaces: null, settings: null, statuses: null, actions: null, service: null, t: null };
@@ -82,6 +84,7 @@
82
84
  var dots = new Set();
83
85
  var pinHidden = new Set();
84
86
  var pinHost = null;
87
+ var expandClicks = new Map();
85
88
  var expandedSections = new Set();
86
89
  var headerMarked = new WeakSet();
87
90
  var disposed = false;
@@ -91,6 +94,7 @@
91
94
  "[" + ROOT_ATTR + '="true"]>*{flex:0 0 auto}',
92
95
  "[" + HIDDEN_ATTR + '="true"]{display:none !important}',
93
96
  "[" + PIN_HIDDEN_ATTR + '="true"]{display:none !important}',
97
+ '[class*="' + OVERFLOW_TOKEN + '"]{display:none !important}',
94
98
  ".wsr-pin{display:flex;flex-direction:column;flex:0 0 auto;padding:2px 0 6px;margin-bottom:4px;border-bottom:.5px solid var(--dsw-alias-border-l2)}",
95
99
  ".wsr-pin-head{font-size:11px;line-height:16px;color:var(--dsw-alias-label-tertiary);padding:4px 12px 2px}",
96
100
  ".wsr-pin-row{display:flex;align-items:center;gap:8px;height:30px;padding:0 12px;border-radius:6px;cursor:pointer}",
@@ -364,6 +368,22 @@
364
368
  return session !== undefined && session !== null && session.blank === true;
365
369
  }
366
370
 
371
+ /** The host's own "show more" button, hidden by CSS but still clickable. */
372
+ function overflowButtonOf(section) {
373
+ if (typeof section.querySelector !== "function") return null;
374
+ return section.querySelector('button[class*="' + OVERFLOW_TOKEN + '"]');
375
+ }
376
+
377
+ /** The count the host reports on its own button, 0 when there is none. */
378
+ function labelNumber(button) {
379
+ if (button === null || button === undefined) return 0;
380
+ var text = typeof button.textContent === "string" ? button.textContent : "";
381
+ var match = /\d+/.exec(text);
382
+ if (match === null) return 0;
383
+ var value = Number(match[0]);
384
+ return Number.isFinite(value) && value > 0 ? value : 0;
385
+ }
386
+
367
387
  /** Collapsing a group resets its fold state, so the click is observed. */
368
388
  function markHeader(header, key) {
369
389
  if (typeof header.addEventListener !== "function" || headerMarked.has(header)) return;
@@ -411,6 +431,13 @@
411
431
  * Fold the rows the host rendered that have not changed for `foldDays`, and
412
432
  * offer the group's own control to bring them back. A workspace whose every
413
433
  * session is that old therefore renders as a bare header.
434
+ *
435
+ * The host's own "show more" button is hidden (CSS) and driven from here: it
436
+ * only renders five rows at a time and cannot collapse once everything is
437
+ * rendered, which made "show more" look broken next to a fold. Expanding the
438
+ * group now clicks that hidden button once per pass until the host has
439
+ * rendered every session, so one click reveals everything; collapsing just
440
+ * folds the stale rows again.
414
441
  */
415
442
  function foldRows(entries, settings, sessions, now) {
416
443
  var cutoff = now - settings.foldDays * DAY;
@@ -418,9 +445,29 @@
418
445
  var entry = entries[i];
419
446
  var section = entry.section;
420
447
  var rows = rowElements(section);
448
+ var hostButton = overflowButtonOf(section);
421
449
  markHeader(entry.header, entry.key);
422
450
 
423
451
  var expanded = expandedSections.has(entry.key);
452
+ var hostHidden = hostButton === null ? 0 : labelNumber(hostButton);
453
+ var hostExpanded = hostButton !== null
454
+ && typeof hostButton.getAttribute === "function"
455
+ && hostButton.getAttribute("aria-expanded") === "true";
456
+ if (expanded && hostButton !== null && !hostExpanded) {
457
+ /* The host renders five more per click (or everything when five or
458
+ fewer remain) and reports `aria-expanded` once nothing is hidden.
459
+ Stopping on that flag is what keeps the loop from collapsing the
460
+ very thing it just expanded - a click on an expanded host button
461
+ resets the watermark. */
462
+ var used = expandClicks.get(entry.key) || 0;
463
+ if (used < MAX_EXPAND_CLICKS) {
464
+ expandClicks.set(entry.key, used + 1);
465
+ hostButton.click();
466
+ }
467
+ } else if (!expanded) {
468
+ expandClicks.delete(entry.key);
469
+ }
470
+
424
471
  var folded = 0;
425
472
  for (var j = 0; j < rows.length; j += 1) {
426
473
  var row = rows[j];
@@ -439,22 +486,22 @@
439
486
  }
440
487
  }
441
488
 
442
- /* The host's own "show more" button and this fold are two different
443
- things: the host decides which rows it RENDERS, this plugin decides
444
- which rendered rows stay VISIBLE. Touching the host's label or its
445
- click made the two fight (a count nobody could trust, and a collapse
446
- that never came back), so they stay separate: the group gets its own
447
- "show history" control whenever something is folded. */
489
+ /* ONE control per group: its count is everything the group still hides,
490
+ both the rows the host did not render and the stale rows this plugin
491
+ folded. Expanding drives the hidden host button until nothing is left
492
+ unrendered, so one click reveals the whole group. */
493
+ var remaining = folded + hostHidden;
448
494
  if (expanded) {
449
495
  (function (entryKey) {
450
496
  ensureToggle(section, entryKey, t("fold.hide"), function () {
451
497
  expandedSections.delete(entryKey);
498
+ expandClicks.delete(entryKey);
452
499
  schedule();
453
500
  });
454
501
  })(entry.key);
455
- } else if (folded > 0) {
502
+ } else if (remaining > 0) {
456
503
  (function (entryKey) {
457
- ensureToggle(section, entryKey, t("fold.show", { n: folded }), function () {
504
+ ensureToggle(section, entryKey, t("fold.show", { n: remaining }), function () {
458
505
  expandedSections.add(entryKey);
459
506
  schedule();
460
507
  });
@@ -913,6 +960,7 @@
913
960
  pinHidden.clear();
914
961
  if (pinHost !== null && pinHost.parentElement !== null) pinHost.remove();
915
962
  pinHost = null;
963
+ expandClicks.clear();
916
964
  roots.forEach(function (root) { root.removeAttribute(ROOT_ATTR); });
917
965
  roots.clear();
918
966
  var marked = document.querySelectorAll("[" + STATE_ATTR + "]");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-ws-recency",
3
- "version": "0.9.1",
3
+ "version": "0.9.2",
4
4
  "description": "Sidebar session management for DeepSeek Harness: workspace groups sorted by latest activity, stale history folded, a global pinned section, follow-up marks, and session delete into a recoverable trash.",
5
5
  "license": "MIT",
6
6
  "author": "chyxhtc",