zcode-pro 0.20.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +8 -5
- package/README.md +7 -4
- package/dist/inject.js +1433 -434
- package/package.json +1 -1
- package/src/host/helper.mjs +147 -60
- package/src/host/main.mjs +7 -4
- package/src/host/taskIndex.mjs +97 -0
- package/src/host/version.mjs +51 -0
- package/src/inject/core.js +101 -18
- package/src/inject/features/alias-migration.js +470 -0
- package/src/inject/features/alias.js +6 -1
- package/src/inject/features/idle-reclaim.js +23 -48
- package/src/inject/features/pinned-collapse.js +4 -3
- package/src/inject/features/project-menu.js +209 -19
- package/src/inject/features/session-switch.js +6 -2
- package/src/inject/features/settings-dialog.js +88 -59
- package/src/inject/features/styles.js +8 -1
- package/src/inject/features/title-gen.js +41 -0
- package/src/inject/features/title-lock.js +20 -2
- package/src/inject/features/toolbar-icons.js +85 -45
- package/src/inject/features/ws-rows.js +62 -0
- package/src/inject/features/ws-running.js +384 -27
- package/src/inject/index.js +3 -2
- package/src/inject/ui.js +20 -11
- package/src/inject/features/pinned-expand.js +0 -142
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
// 置顶会话展开抑制(v4,实验性):
|
|
2
|
-
// 已知缺陷:无法真正拦截展开(store 在应用闭包内不可达,渲染层无拦截点),
|
|
3
|
-
// 只能在展开发生后收起,因此视觉上“先展开再缩起”,大会话期间可能闪烁。
|
|
4
|
-
|
|
5
|
-
// 点击折叠项目的置顶会话时,应用展开该项目;且会话内容加载完成(正文渲染)后还会再展开一次。
|
|
6
|
-
// 固定延时不可靠(会话有大有小:空白时就收起会被“内容出现后的二次展开”覆盖)。
|
|
7
|
-
// v4 改为事件驱动:点击后开始监听该项目的 DOM 变化,
|
|
8
|
-
// 以“会话区渲染出实质内容(文本节点增多/任务行出现)且短暂稳定”作为加载完成信号,
|
|
9
|
-
// 之后若项目仍展开则收起;最长等待 15s 兜底。
|
|
10
|
-
// 开关:helper pinnedKeepCollapsed(默认 true = 保持折叠,增强)。
|
|
11
|
-
import { rpc } from '../core.js';
|
|
12
|
-
|
|
13
|
-
let installed = false;
|
|
14
|
-
let keepCollapsed = false; // 默认关闭:实现为“先展开再缩起”,有闪烁(应用无拦截点,详见文件头)
|
|
15
|
-
|
|
16
|
-
const CONFIG_POLL_MIN_MS = 5000; // 配置未变化时逐次拉长轮询间隔,减轻常驻开销
|
|
17
|
-
const CONFIG_POLL_MAX_MS = 60000;
|
|
18
|
-
let pollMs = CONFIG_POLL_MIN_MS;
|
|
19
|
-
|
|
20
|
-
// 返回本次请求是否改变了开关值(用于轮询间隔回弹)
|
|
21
|
-
async function refreshConfig() {
|
|
22
|
-
try {
|
|
23
|
-
const res = await rpc('/config');
|
|
24
|
-
if (res && res.ok && res.config && res.config.features) {
|
|
25
|
-
const next = res.config.features.pinnedKeepCollapsed !== false;
|
|
26
|
-
if (next !== keepCollapsed) {
|
|
27
|
-
keepCollapsed = next;
|
|
28
|
-
return true;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
} catch { /* helper 未就绪时保持默认 */ }
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// 自适应轮询:值未变化间隔 ×1.5 直至上限,变化即回到最短;页面隐藏时跳过请求
|
|
36
|
-
function pollConfig() {
|
|
37
|
-
setTimeout(async () => {
|
|
38
|
-
if (document.hidden) pollMs = CONFIG_POLL_MAX_MS;
|
|
39
|
-
else pollMs = (await refreshConfig()) ? CONFIG_POLL_MIN_MS : Math.min(Math.round(pollMs * 1.5), CONFIG_POLL_MAX_MS);
|
|
40
|
-
pollConfig();
|
|
41
|
-
}, pollMs);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export function startPinnedExpandSuppression() {
|
|
45
|
-
if (installed || typeof document === 'undefined') return;
|
|
46
|
-
installed = true;
|
|
47
|
-
void refreshConfig();
|
|
48
|
-
pollConfig();
|
|
49
|
-
// 回到前台立即补一次,避免长退避期间拿不到刚改的配置
|
|
50
|
-
document.addEventListener('visibilitychange', () => { if (!document.hidden) void refreshConfig(); });
|
|
51
|
-
|
|
52
|
-
document.addEventListener('click', (e) => {
|
|
53
|
-
if (!keepCollapsed) return;
|
|
54
|
-
const taskRow = e.target.closest && e.target.closest('[data-testid^="task-item-"]');
|
|
55
|
-
if (!taskRow) return;
|
|
56
|
-
const key = taskRow.getAttribute('data-task-item-key') || '';
|
|
57
|
-
const sep = key.lastIndexOf(':');
|
|
58
|
-
if (sep <= 0) return;
|
|
59
|
-
const wsKey = key.slice(0, sep);
|
|
60
|
-
const wsRow = document.querySelector(`[data-testid="workspace-item-${cssEscape(wsKey)}"]`);
|
|
61
|
-
if (!wsRow || wsRow.getAttribute('aria-expanded') !== 'false') return;
|
|
62
|
-
|
|
63
|
-
const head = wsRow.matches('[aria-expanded]') ? wsRow : wsRow.querySelector('[aria-expanded]');
|
|
64
|
-
if (!head) return;
|
|
65
|
-
// 等会话加载完成(内容渲染且稳定)后再收起
|
|
66
|
-
collapseAfterContentLoaded(wsRow);
|
|
67
|
-
}, true);
|
|
68
|
-
|
|
69
|
-
// 用户点项目行头部 = 主动展开,永不干预(事件捕获,先于应用处理)
|
|
70
|
-
document.addEventListener('click', (e) => {
|
|
71
|
-
const row = e.target.closest && e.target.closest('[data-testid^="workspace-item-"]');
|
|
72
|
-
if (row) row.__zcodeproUserTouched = Date.now();
|
|
73
|
-
}, true);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
// 收起用的点击必须点回当前会话:应用把项目行点击当作“切换到该项目”,只点项目行
|
|
77
|
-
// 会把当前会话取消选中、主视图跳进新建任务。还原行必须在点项目行之前就记住——
|
|
78
|
-
// 点击会立刻取消选中(3.14.4 实测),事后再按选中标记找就找不到了,回点落空
|
|
79
|
-
// 会把视图留在该项目的新会话页;元素被 React 重挂载时按记住的会话键重找。
|
|
80
|
-
function currentTaskRow() {
|
|
81
|
-
for (const el of document.querySelectorAll('[data-task-item-key]')) {
|
|
82
|
-
if ((el.className + '').includes('bg-selected')) return el;
|
|
83
|
-
}
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
function collapseNavSafe(head) {
|
|
87
|
-
const cur = currentTaskRow();
|
|
88
|
-
const backKey = cur ? cur.getAttribute('data-task-item-key') : '';
|
|
89
|
-
head.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
90
|
-
if (!backKey) return;
|
|
91
|
-
const back = (cur && cur.isConnected)
|
|
92
|
-
? cur
|
|
93
|
-
: document.querySelector(`[data-task-item-key="${cssEscape(backKey)}"]`);
|
|
94
|
-
if (back) back.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
// 实测时序(探针 v2):点击后 ~6s 出现第一次自动展开并被收起;
|
|
98
|
-
// 正文文字在点击后 ~8-14s 才开始增长,期间应用还会再次展开项目。
|
|
99
|
-
// 因此收起不能是单次动作:在 25s 监听期内持续压制(发现展开即收起),
|
|
100
|
-
// 直到“正文出现增长且稳定 1s”(会话真正加载完成)后再做最后一次收起并结束。
|
|
101
|
-
function collapseAfterContentLoaded(wsRow) {
|
|
102
|
-
const deadline = Date.now() + 25000;
|
|
103
|
-
let lastLen = (document.querySelector('main') || document.body).innerText.length;
|
|
104
|
-
let grewAt = 0;
|
|
105
|
-
let settledAt = 0;
|
|
106
|
-
|
|
107
|
-
const headOf = () => {
|
|
108
|
-
const h = wsRow.matches('[aria-expanded]') ? wsRow : wsRow.querySelector('[aria-expanded]');
|
|
109
|
-
return h || null;
|
|
110
|
-
};
|
|
111
|
-
const userTouched = () => wsRow.__zcodeproUserTouched && Date.now() - wsRow.__zcodeproUserTouched < 800;
|
|
112
|
-
|
|
113
|
-
const tick = () => {
|
|
114
|
-
if (!keepCollapsed || Date.now() > deadline || !wsRow.isConnected) return done();
|
|
115
|
-
const len = (document.querySelector('main') || document.body).innerText.length;
|
|
116
|
-
if (len > lastLen) grewAt = Date.now(); // 正文开始/持续增长
|
|
117
|
-
if (grewAt && Date.now() - grewAt >= 1000) settledAt = settledAt || Date.now(); // 增长后稳定 1s
|
|
118
|
-
lastLen = len;
|
|
119
|
-
|
|
120
|
-
const head = headOf();
|
|
121
|
-
// 持续压制:监听期内任何展开都收起(用户刚点过头部则跳过)
|
|
122
|
-
if (head && head.getAttribute('aria-expanded') === 'true' && !userTouched()) {
|
|
123
|
-
collapseNavSafe(head);
|
|
124
|
-
}
|
|
125
|
-
// 结束条件:正文已增长且稳定 1s(此时收起已完成)——再做最后一次确认收起
|
|
126
|
-
if (settledAt && Date.now() - settledAt >= 1000) {
|
|
127
|
-
const h2 = headOf();
|
|
128
|
-
if (h2 && h2.getAttribute('aria-expanded') === 'true' && !userTouched()) {
|
|
129
|
-
collapseNavSafe(h2);
|
|
130
|
-
}
|
|
131
|
-
return done();
|
|
132
|
-
}
|
|
133
|
-
setTimeout(tick, 200);
|
|
134
|
-
};
|
|
135
|
-
const done = () => { wsRow.__zcodeproSuppending = false; };
|
|
136
|
-
wsRow.__zcodeproSuppending = true;
|
|
137
|
-
setTimeout(tick, 400);
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function cssEscape(s) {
|
|
141
|
-
return String(s).replace(/(["\\\]])/g, '\\$1');
|
|
142
|
-
}
|