zcode-pro 0.16.0__py3-none-any.whl

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 (41) hide show
  1. zcode_pro/__init__.py +3 -0
  2. zcode_pro/__main__.py +4 -0
  3. zcode_pro/app/cli.mjs +9 -0
  4. zcode_pro/app/dist/inject.js +5821 -0
  5. zcode_pro/app/src/host/cdp.mjs +126 -0
  6. zcode_pro/app/src/host/helper.mjs +2134 -0
  7. zcode_pro/app/src/host/main.mjs +262 -0
  8. zcode_pro/app/src/host/paths.mjs +73 -0
  9. zcode_pro/app/src/host/pickFolder.mjs +152 -0
  10. zcode_pro/app/src/host/settings.mjs +61 -0
  11. zcode_pro/app/src/host/taskIndex.mjs +255 -0
  12. zcode_pro/app/src/host/taskOrder.mjs +117 -0
  13. zcode_pro/app/src/host/webdav.mjs +105 -0
  14. zcode_pro/app/src/inject/core.js +751 -0
  15. zcode_pro/app/src/inject/features/alias.js +211 -0
  16. zcode_pro/app/src/inject/features/copy-path.js +78 -0
  17. zcode_pro/app/src/inject/features/file-menu.js +147 -0
  18. zcode_pro/app/src/inject/features/idle-reclaim.js +179 -0
  19. zcode_pro/app/src/inject/features/image-menu.js +215 -0
  20. zcode_pro/app/src/inject/features/open-folder.js +43 -0
  21. zcode_pro/app/src/inject/features/pinned-collapse.js +132 -0
  22. zcode_pro/app/src/inject/features/pinned-expand.js +142 -0
  23. zcode_pro/app/src/inject/features/plugin-updates.js +28 -0
  24. zcode_pro/app/src/inject/features/project-menu.js +58 -0
  25. zcode_pro/app/src/inject/features/relocate-dialog.js +156 -0
  26. zcode_pro/app/src/inject/features/session-switch.js +244 -0
  27. zcode_pro/app/src/inject/features/settings-dialog.js +1503 -0
  28. zcode_pro/app/src/inject/features/settings-entry.js +21 -0
  29. zcode_pro/app/src/inject/features/styles.js +311 -0
  30. zcode_pro/app/src/inject/features/task-order.js +78 -0
  31. zcode_pro/app/src/inject/features/title-lock.js +242 -0
  32. zcode_pro/app/src/inject/features/toolbar-icons.js +243 -0
  33. zcode_pro/app/src/inject/features/ws-running.js +472 -0
  34. zcode_pro/app/src/inject/index.js +55 -0
  35. zcode_pro/app/src/inject/ui.js +734 -0
  36. zcode_pro/cli.py +71 -0
  37. zcode_pro-0.16.0.dist-info/METADATA +45 -0
  38. zcode_pro-0.16.0.dist-info/RECORD +41 -0
  39. zcode_pro-0.16.0.dist-info/WHEEL +4 -0
  40. zcode_pro-0.16.0.dist-info/entry_points.txt +2 -0
  41. zcode_pro-0.16.0.dist-info/licenses/LICENSE +201 -0
@@ -0,0 +1,243 @@
1
+ // 侧栏菜单并入顶栏:侧边栏顶部的“新建任务 / 搜索 / 自动化 / 插件市场”四项
2
+ // 收成顶部导航栏(后退/前进旁)的四个图标按钮,原菜单区域整体隐藏。
3
+ // 悬停显示名称(带快捷键的项照带快捷键),按钮点亮表示对应界面正打开:
4
+ // 自动化/插件市场跟随原按钮的 aria-pressed,搜索跟随命令面板是否在场,
5
+ // 新建任务跟随“当前无选中会话”(即处于新建任务视图)。状态都从应用现状推导,
6
+ // 键盘快捷键或其他入口触发同样能点亮。点击转发合成 click 给隐藏的原按钮
7
+ //(display:none 不影响事件派发,已实测),原生行为全部保留。
8
+ // 不移动应用的任何节点(避免与界面框架自身的增删冲突),自有按钮行追加在
9
+ // 导航按钮容器末尾——框架不排斥外来子节点;界面重渲染换新节点后由观察器重建。
10
+ import { h, t, getConfig } from '../core.js';
11
+ import { ensureStyle } from '../ui.js';
12
+
13
+ const HIDE_CLASS = 'zcodepro-tb-hidden';
14
+ const SYNC_DEBOUNCE_MS = 150; // 相关 DOM 变动后的去抖
15
+ const SCAN_FALLBACK_MS = 5000; // 兜底轮询周期:仅补观察器覆盖不到的路径
16
+
17
+ // 会话行(识别当前有无选中会话 → 新建任务态)
18
+ const ROW_SEL = '[data-task-item-key]';
19
+ // 与本功能相关的新增/删除节点:菜单四项、命令面板、顶栏导航按钮
20
+ const MENU_SEL = '[data-testid="task-new-button"], [data-testid="automations-open"],'
21
+ + ' [data-testid="plugin-store-sidebar-open"], [data-slot="command"],'
22
+ + ' [data-testid="desktop-top-nav-back"]';
23
+
24
+ let enabled = true; // 配置开关(features.toolbarIcons !== false)
25
+ let rowEl = null; // 顶栏自有按钮行
26
+ let origs = null; // { menu, newTask, search, auto, plugin } 原元素引用
27
+ let syncTimer = 0;
28
+ let widthObserver = null; // 监听侧栏宽度变化(拖分隔条/缩放窗口)
29
+ let widthObserved = null;
30
+
31
+ function findMenu() {
32
+ const nt = document.querySelector('[data-testid="task-new-button"]');
33
+ const menu = nt?.parentElement;
34
+ return menu && menu.querySelector('[data-testid="automations-open"]') ? menu : null;
35
+ }
36
+
37
+ function originalsOf(menu) {
38
+ const newTask = menu.querySelector('[data-testid="task-new-button"]');
39
+ // 搜索按钮没有 testid,按 lucide-search 图标识别(快捷键文案随平台/语言变,不作依据)
40
+ const search = newTask && menu.querySelector('button [class*="lucide-search"]')?.closest('button');
41
+ const auto = menu.querySelector('[data-testid="automations-open"]');
42
+ const plugin = menu.querySelector('[data-testid="plugin-store-sidebar-open"]');
43
+ return newTask && search && auto && plugin ? { menu, newTask, search, auto, plugin } : null;
44
+ }
45
+
46
+ // 名称从原按钮提取(跟随界面语言);提取不到再回退词典
47
+ function nameOf(orig, nameSel, fallback) {
48
+ let name = nameSel && (orig.querySelector(nameSel)?.textContent || '').trim();
49
+ if (!name) name = (orig.textContent || '').trim().split(/\s+/)[0]?.slice(0, 20) || '';
50
+ return name || fallback;
51
+ }
52
+
53
+ // 悬停提示 = 名称 + 快捷键(快捷键 span 带 ml-auto,自动化/插件市场没有)
54
+ function tipOf(orig, nameSel, fallback) {
55
+ const name = nameOf(orig, nameSel, fallback);
56
+ const kbd = (orig.querySelector('span.ml-auto')?.textContent || '').trim();
57
+ return kbd ? `${name} · ${kbd}` : name;
58
+ }
59
+
60
+ function iconOf(orig) {
61
+ const svg = orig.querySelector('svg');
62
+ if (!svg) return null;
63
+ const clone = svg.cloneNode(true);
64
+ clone.setAttribute('class', 'zcodepro-tb-icon');
65
+ return clone;
66
+ }
67
+
68
+ function setActive(kind, on) {
69
+ const btn = rowEl?.querySelector(`[data-kind="${kind}"]`);
70
+ if (!btn) return;
71
+ if (on) btn.setAttribute('data-active', '1');
72
+ else btn.removeAttribute('data-active');
73
+ // 自动化/插件市场是开关型按钮,同步按压语义
74
+ if (kind === 'auto' || kind === 'plugin') btn.setAttribute('aria-pressed', on ? 'true' : 'false');
75
+ }
76
+
77
+ function syncActive() {
78
+ if (!origs || !rowEl) return;
79
+ const auto = origs.auto.getAttribute('aria-pressed') === 'true';
80
+ const plugin = origs.plugin.getAttribute('aria-pressed') === 'true';
81
+ const palette = !!document.querySelector('[data-slot="command"]');
82
+ // 当前会话的识别方式与“会话快捷切换”一致(侧栏条目的 bg-selected 标记)
83
+ let selected = false;
84
+ for (const el of document.querySelectorAll(ROW_SEL)) {
85
+ if ((el.className + '').includes('bg-selected')) { selected = true; break; }
86
+ }
87
+ // 新建任务态 = 无选中会话且无面板打开(自动化/插件市场面板会清掉会话选中,
88
+ // 不排除的话那些面板打开时本按钮会跟着误亮;命令面板是浮层不遮挡底层视图,不排除)
89
+ setActive('newTask', !selected && !auto && !plugin);
90
+ setActive('search', palette);
91
+ setActive('auto', auto);
92
+ setActive('plugin', plugin);
93
+ }
94
+
95
+ function rebuild() {
96
+ const menu = findMenu();
97
+ if (!menu) return false;
98
+ const next = originalsOf(menu);
99
+ const navRow = document.querySelector('[data-testid="desktop-top-nav-back"]')?.parentElement;
100
+ if (!next || !navRow) return false;
101
+ // 界面重渲染换了新节点:给旧菜单摘掉隐藏类,新菜单藏起来
102
+ if (origs && origs.menu !== menu) origs.menu.classList.remove(HIDE_CLASS);
103
+ menu.classList.add(HIDE_CLASS);
104
+ origs = next;
105
+ rowEl?.remove();
106
+ const L = t();
107
+ const mk = (kind, orig, nameSel, fallback) => h('button', {
108
+ type: 'button',
109
+ class: 'zcodepro-tb-btn',
110
+ 'data-kind': kind,
111
+ 'aria-label': nameOf(orig, nameSel, fallback),
112
+ 'data-zcodepro-tip': tipOf(orig, nameSel, fallback),
113
+ onClick: () => {
114
+ orig.dispatchEvent(new MouseEvent('click', { bubbles: true, cancelable: true }));
115
+ // 状态变化由观察器捕捉;这里补一次延时同步,点击后点亮更即时
116
+ setTimeout(syncActive, 150);
117
+ },
118
+ });
119
+ const newBtn = mk('newTask', next.newTask, 'span.truncate', L.tbNewTask);
120
+ const icon = iconOf(next.newTask);
121
+ if (icon) newBtn.append(icon);
122
+ const searchBtn = mk('search', next.search, 'span.truncate', L.tbSearch);
123
+ const sIcon = iconOf(next.search);
124
+ if (sIcon) searchBtn.append(sIcon);
125
+ const autoBtn = mk('auto', next.auto, null, L.tbAutomations);
126
+ const aIcon = iconOf(next.auto);
127
+ if (aIcon) autoBtn.append(aIcon);
128
+ const pluginBtn = mk('plugin', next.plugin, null, L.tbPluginStore);
129
+ const pIcon = iconOf(next.plugin);
130
+ if (pIcon) pluginBtn.append(pIcon);
131
+ rowEl = h('div', { class: 'zcodepro-tb-row' }, newBtn, searchBtn, autoBtn, pluginBtn);
132
+ navRow.append(rowEl);
133
+ return true;
134
+ }
135
+
136
+ // 侧栏收起判定:收起后顶栏“切换侧边栏”按钮的图标换成 panel-left-open
137
+ // (展开时是 panel-left-close)。图标类名不含本地化文案,比 aria-label 稳;
138
+ // 识别不出时按展开处理。收起时应用会让自带的顶栏新建任务按钮展开顶上,
139
+ // 我们的按钮行要藏起来避免出现两个相同入口
140
+ function sidebarCollapsed() {
141
+ const navRow = document.querySelector('[data-testid="desktop-top-nav-back"]')?.parentElement;
142
+ const svg = navRow?.querySelector('button svg[class*="panel-left-"]');
143
+ return !!svg && (svg.getAttribute('class') || '').includes('panel-left-open');
144
+ }
145
+
146
+ // 侧栏过窄判定:图标行 + 原有导航按钮约需 260px,再窄会伸出侧栏压到主视图。
147
+ // 过窄时回退原生菜单布局(隐藏图标行、还原菜单),宽度恢复后自动切回,
148
+ // 任何入口都不丢。宽度取侧栏滚动容器(侧栏打开时始终在场)
149
+ const NARROW_SIDEBAR_PX = 260;
150
+ function sidebarTooNarrow() {
151
+ const scroll = document.querySelector('.flex.flex-1.min-h-0.flex-col.gap-3.overflow-y-auto');
152
+ if (!scroll) return false;
153
+ // 顺带盯住宽度变化(容器重建后换目标重新观察)
154
+ if (!widthObserver) widthObserver = new ResizeObserver(() => scheduleSync());
155
+ if (widthObserved !== scroll) {
156
+ if (widthObserved) widthObserver.unobserve(widthObserved);
157
+ widthObserved = scroll;
158
+ widthObserver.observe(scroll);
159
+ }
160
+ return scroll.getBoundingClientRect().width < NARROW_SIDEBAR_PX;
161
+ }
162
+
163
+ function sync() {
164
+ if (!enabled) return;
165
+ const menu = findMenu();
166
+ // 侧栏收起/未渲染/过窄:藏起图标行。过窄时还原原生菜单(收起时菜单本就不可见)
167
+ if (!menu) {
168
+ if (rowEl) rowEl.style.display = 'none';
169
+ return;
170
+ }
171
+ if (sidebarCollapsed() || sidebarTooNarrow()) {
172
+ if (rowEl) rowEl.style.display = 'none';
173
+ menu.classList.remove(HIDE_CLASS);
174
+ return;
175
+ }
176
+ const alive = origs && origs.menu === menu
177
+ && origs.newTask.isConnected && origs.search.isConnected
178
+ && origs.auto.isConnected && origs.plugin.isConnected
179
+ && rowEl && rowEl.isConnected;
180
+ if (!alive && !rebuild()) return;
181
+ // 界面重渲染可能整体重写容器的 class,隐藏类兜底重加
182
+ menu.classList.add(HIDE_CLASS);
183
+ if (rowEl) rowEl.style.display = '';
184
+ syncActive();
185
+ }
186
+
187
+ function scheduleSync() {
188
+ if (syncTimer) return;
189
+ syncTimer = setTimeout(() => { syncTimer = 0; sync(); }, SYNC_DEBOUNCE_MS);
190
+ }
191
+
192
+ // 只有相关变动才值得重扫:会话行 class 变化(选中标记移动)、开关按钮
193
+ // aria-pressed 变化、菜单/命令面板/导航按钮的新增或删除、侧栏收起图标翻转。
194
+ // 聊天区流式输出等高频变动在此滤掉。
195
+ function relevantMutations(muts) {
196
+ for (const m of muts) {
197
+ if (m.type === 'attributes') {
198
+ const target = m.target;
199
+ if (target instanceof Element
200
+ && (target.matches(ROW_SEL) || target.matches('[data-testid="automations-open"], [data-testid="plugin-store-sidebar-open"]')
201
+ || target.matches('svg[class*="panel-left-"]'))) {
202
+ return true;
203
+ }
204
+ continue;
205
+ }
206
+ for (const list of [m.addedNodes, m.removedNodes]) {
207
+ for (const n of list) {
208
+ if (!(n instanceof Element)) continue;
209
+ if (n.matches(MENU_SEL) || n.matches(ROW_SEL)
210
+ || n.querySelector(MENU_SEL) || n.querySelector(ROW_SEL)) return true;
211
+ // 侧栏收起/展开时切换按钮的图标被整节点替换(panel-left-open/close 互换)
212
+ if (n.matches('svg[class*="panel-left-"]') || n.querySelector?.('svg[class*="panel-left-"]')) return true;
213
+ }
214
+ }
215
+ }
216
+ return false;
217
+ }
218
+
219
+ function teardown() {
220
+ rowEl?.remove();
221
+ rowEl = null;
222
+ if (origs) { origs.menu.classList.remove(HIDE_CLASS); origs = null; }
223
+ }
224
+
225
+ async function refreshConfig() {
226
+ // 强制拉取:配置可能经设置弹窗以外的途径改动(缓存 3 秒内会是旧值)
227
+ const cfg = await getConfig(true).catch(() => null);
228
+ enabled = !cfg || !cfg.features || cfg.features.toolbarIcons !== false;
229
+ if (enabled) sync();
230
+ else teardown();
231
+ }
232
+
233
+ export function startToolbarIcons() {
234
+ ensureStyle();
235
+ // 设置弹窗保存开关后即时生效(settings-dialog 保存成功会派发该事件)
236
+ window.addEventListener('zcodepro:config-changed', () => { void refreshConfig(); });
237
+ void refreshConfig();
238
+ new MutationObserver((muts) => { if (relevantMutations(muts)) scheduleSync(); })
239
+ .observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'aria-pressed'] });
240
+ // 低频兜底 + 页面隐藏时不扫、回到前台立即补一次
241
+ setInterval(() => { if (!document.hidden) sync(); }, SCAN_FALLBACK_MS);
242
+ document.addEventListener('visibilitychange', () => { if (!document.hidden) sync(); });
243
+ }