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,734 @@
1
+ // ZCode 风格的弹窗与提示组件。
2
+ // 复用应用自身的 Tailwind 设计令牌(bg-surface、border-border、text-foreground 等),
3
+ // 因此自动适配亮/暗主题,与原生弹窗观感一致。
4
+ import { h, t } from './core.js';
5
+
6
+ let toastTimer = null;
7
+
8
+ export function showToast(text, kind = 'info') {
9
+ document.getElementById('__zcodepro_toast__')?.remove();
10
+ const el = h('div', {
11
+ id: '__zcodepro_toast__',
12
+ class: 'fixed bottom-6 right-6 z-[100] flex max-w-md items-center gap-2 rounded-lg border px-4 py-3 text-ui-sm shadow-lg ' +
13
+ (kind === 'error' ? 'border-destructive/40 bg-menu text-foreground' : 'border-border bg-menu text-foreground'),
14
+ style: 'animation:zcodepro-fade-in .18s ease',
15
+ },
16
+ h('span', { class: 'truncate' }, text));
17
+ document.body.append(el);
18
+ clearTimeout(toastTimer);
19
+ toastTimer = setTimeout(() => el.remove(), kind === 'error' ? 5000 : 2600);
20
+ }
21
+
22
+ // 复用应用自身 AlertDialog 的真实类名组合(逆向自 app.asar 渲染层源码):
23
+ // 遮罩 fixed inset-0 z-50 bg-black/60 + 毛玻璃 + Linux 下 top-12(让出自绘标题栏),
24
+ // 内容卡 rounded-2xl bg-popover/98 ring-border shadow-2xl。
25
+ // 注意:绝不能用 bg-surface 做面板底色——该 token 在应用里是 ~3% 不透明度的着色层(#0d0d0d08),
26
+ // 会导致整卡透明。关键视觉另在 ensureStyle 里以自有样式表兜底(ID 选择器 + 主题变量 + 字面量回退),
27
+ // 应用 Tailwind 类随版本更新变动时遮罩/面板仍能正确绘制。
28
+ const overlayClass = 'fixed inset-0 isolate z-50 flex items-center justify-center bg-black/60 supports-backdrop-filter:backdrop-blur-xs duration-100 p-4 platform-linux-desktop:top-12';
29
+ const overlayClassBare = 'fixed inset-0 isolate z-50 flex items-center justify-center duration-100 p-4 platform-linux-desktop:top-12';
30
+ const contentClass = 'w-full sm:max-w-md rounded-2xl border-none bg-popover/98 p-5 text-ui-base/relaxed text-foreground ring-border shadow-2xl';
31
+
32
+ // overlay: 'dim'(默认,半透明遮罩)| 'none'(透明遮罩)——设置弹窗用 'none',方便边调样式边看会话。
33
+ // dismissOnOutside: 点弹窗外部是否关闭(默认 true)。为 false 时遮罩不再拦截鼠标,
34
+ // 可以直接操作弹窗后面的会话(滚动/选中),弹窗只通过关闭按钮或 Esc 关闭。
35
+ // draggable: 允许按住标题栏拖动弹窗。posKey: 记住拖动位置(localStorage)。
36
+ // 弹窗栈:同时打开多层弹窗(如“切换文件夹”上叠二次确认)时,
37
+ // Esc 只关最上层,避免一次 Esc 把两层一起关掉
38
+ let dialogStack = [];
39
+
40
+ export function openDialog({ title, description, onMount, onClose, width = 'sm:max-w-md', overlay = 'dim', draggable = false, posKey = '', dismissOnOutside = true }) {
41
+ const L = t();
42
+ const prevActive = document.activeElement;
43
+ let cleanupDrag = null;
44
+ const close = () => {
45
+ overlayEl.remove();
46
+ dialogStack = dialogStack.filter((e) => e !== overlayEl);
47
+ document.removeEventListener('keydown', onKey);
48
+ if (cleanupDrag) cleanupDrag();
49
+ if (prevActive && prevActive.focus) { try { prevActive.focus(); } catch { /* ignore */ } }
50
+ onClose && onClose();
51
+ };
52
+ const onKey = (e) => {
53
+ if (e.key !== 'Escape') return;
54
+ e.stopPropagation();
55
+ if (dialogStack[dialogStack.length - 1] === overlayEl) close();
56
+ };
57
+ const content = h('div', {
58
+ role: 'dialog',
59
+ 'aria-modal': 'true',
60
+ class: contentClass.replace('sm:max-w-md', width) + ' zcodepro-card',
61
+ });
62
+ const overlayEl = h('div', {
63
+ 'data-overlay': overlay,
64
+ class: (overlay === 'none' ? overlayClassBare : overlayClass) + ' zcodepro-overlay',
65
+ onMousedown: (e) => { if (dismissOnOutside && e.target === overlayEl) close(); },
66
+ }, content);
67
+ dialogStack.push(overlayEl);
68
+ if (!dismissOnOutside) {
69
+ overlayEl.style.pointerEvents = 'none';
70
+ content.style.pointerEvents = 'auto';
71
+ }
72
+
73
+ const titleEl = h('h2', { class: 'text-lg font-semibold leading-none tracking-tight text-foreground' }, title);
74
+ content.append(
75
+ titleEl,
76
+ // 注意不能把 null 直接传给 append:DOM 会把 null 渲染成字面量 "null" 文本
77
+ ...(description ? [h('p', { class: 'mt-2 text-ui-sm/relaxed text-foreground-subtle' }, description)] : [])
78
+ );
79
+ const body = h('div', { class: 'mt-4 zcodepro-dialog-body' });
80
+ content.append(body);
81
+ let restorePos = null;
82
+ if (draggable) {
83
+ titleEl.style.cursor = 'move';
84
+ titleEl.style.userSelect = 'none';
85
+ let dragging = false, sx = 0, sy = 0, ox = 0, oy = 0;
86
+ // 边界按遮罩实际范围算(Linux 下遮罩顶部让出自绘标题栏),不能按窗口算:
87
+ // 否则卡片能被拖进标题栏下面,拖动把手和关闭按钮被标题栏盖住就都点不到了
88
+ const clampPos = (x, y) => {
89
+ const ob = overlayEl.getBoundingClientRect();
90
+ // 卡片在遮罩内居中,平移量不能超过卡片边缘到遮罩边缘的距离(留 8px 余量)
91
+ const mx = Math.max(0, (ob.width - content.offsetWidth) / 2 - 8);
92
+ const my = Math.max(0, (ob.height - content.offsetHeight) / 2 - 8);
93
+ return [Math.min(mx, Math.max(-mx, x)), Math.min(my, Math.max(-my, y))];
94
+ };
95
+ const applyPos = () => {
96
+ content.style.transform = (ox || oy) ? `translate(${ox}px, ${oy}px)` : '';
97
+ };
98
+ // 内容异步加载变高、窗口尺寸变化都会改变可拖范围,随时把当前位置收回边界内
99
+ const reclamp = () => {
100
+ if (dragging) return;
101
+ [ox, oy] = clampPos(ox, oy);
102
+ applyPos();
103
+ };
104
+ const restore = () => {
105
+ if (!posKey) return;
106
+ try {
107
+ const saved = JSON.parse(localStorage.getItem('zcodepro-dialog-pos:' + posKey) || 'null');
108
+ if (saved && Number.isFinite(saved.x) && Number.isFinite(saved.y)) {
109
+ [ox, oy] = clampPos(saved.x, saved.y);
110
+ applyPos();
111
+ }
112
+ } catch { /* ignore */ }
113
+ };
114
+ const onDown = (e) => {
115
+ if (e.button !== 0) return;
116
+ dragging = true;
117
+ sx = e.clientX - ox; sy = e.clientY - oy;
118
+ e.preventDefault();
119
+ };
120
+ const onMove = (e) => {
121
+ if (!dragging) return;
122
+ [ox, oy] = clampPos(e.clientX - sx, e.clientY - sy);
123
+ applyPos();
124
+ };
125
+ const onUp = () => {
126
+ if (!dragging) return;
127
+ dragging = false;
128
+ if (posKey) {
129
+ try { localStorage.setItem('zcodepro-dialog-pos:' + posKey, JSON.stringify({ x: ox, y: oy })); } catch { /* ignore */ }
130
+ }
131
+ };
132
+ titleEl.addEventListener('mousedown', onDown);
133
+ document.addEventListener('mousemove', onMove);
134
+ document.addEventListener('mouseup', onUp);
135
+ const posObserver = new ResizeObserver(reclamp);
136
+ posObserver.observe(content);
137
+ window.addEventListener('resize', reclamp);
138
+ cleanupDrag = () => {
139
+ titleEl.removeEventListener('mousedown', onDown);
140
+ document.removeEventListener('mousemove', onMove);
141
+ document.removeEventListener('mouseup', onUp);
142
+ posObserver.disconnect();
143
+ window.removeEventListener('resize', reclamp);
144
+ };
145
+ restorePos = restore;
146
+ }
147
+ document.addEventListener('keydown', onKey, true);
148
+ document.body.append(overlayEl);
149
+ // 恢复位置要在挂载后测尺寸(未挂载时 offsetWidth 为 0,边界算不出来)
150
+ if (restorePos) restorePos();
151
+ try {
152
+ onMount && onMount({ body, close, content });
153
+ } catch (err) {
154
+ console.error('[zcodepro] dialog onMount 失败:', err);
155
+ }
156
+ return { close };
157
+ }
158
+
159
+ export function dialogFooter(...buttons) {
160
+ return h('div', { class: 'mt-6 flex flex-col-reverse gap-2 sm:flex-row sm:justify-end' }, buttons);
161
+ }
162
+
163
+ export function btnSecondary(text, onClick, extra = '') {
164
+ return h('button', {
165
+ type: 'button',
166
+ class: 'inline-flex h-9 items-center justify-center whitespace-nowrap rounded-lg border border-border bg-surface px-4 text-ui-sm font-medium text-foreground transition-colors hover:bg-surface-hover disabled:pointer-events-none disabled:opacity-50 ' + extra,
167
+ onClick,
168
+ }, text);
169
+ }
170
+
171
+ export function btnPrimary(text, onClick, extra = '') {
172
+ return h('button', {
173
+ type: 'button',
174
+ class: 'inline-flex h-9 items-center justify-center whitespace-nowrap rounded-lg bg-primary px-4 text-ui-sm font-medium text-primary-foreground transition-colors hover:bg-primary/90 disabled:pointer-events-none disabled:opacity-50 ' + extra,
175
+ onClick,
176
+ }, text);
177
+ }
178
+
179
+ // 紧凑小按钮(面板内与文本同行的操作按钮):高度对齐 .zcodepro-tab(24px)。
180
+ // 不能走 btnSecondary + 'h-7' 追加类——基类的 h-9 与 h-7 同属性冲突,
181
+ // Tailwind 按样式表顺序决胜,h-7 永远盖不过 h-9,按钮会一直是 36px。
182
+ // variant='primary' 用于需要强调的小按钮(如“保存”)。
183
+ export function btnSmall(text, onClick, extra = '', variant = '') {
184
+ return h('button', {
185
+ type: 'button',
186
+ class: 'zcodepro-btn-sm ' + extra,
187
+ 'data-variant': variant || undefined,
188
+ onClick,
189
+ }, text);
190
+ }
191
+
192
+ export function textInput({ value = '', placeholder = '', onInput, onEnter, autofocus = true } = {}) {
193
+ const input = h('input', {
194
+ type: 'text',
195
+ value,
196
+ placeholder,
197
+ class: 'h-9 w-full rounded-lg border border-border bg-input px-3 text-ui-sm text-foreground outline-none transition-shadow placeholder:text-foreground-subtle focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40',
198
+ });
199
+ if (onInput) input.addEventListener('input', onInput);
200
+ if (onEnter) input.addEventListener('keydown', (e) => { if (e.key === 'Enter') onEnter(); });
201
+ if (autofocus) {
202
+ const tryFocus = () => {
203
+ if (!input.isConnected) return;
204
+ input.focus();
205
+ if (document.activeElement === input) input.select();
206
+ };
207
+ tryFocus();
208
+ // 原生弹层(Radix 菜单等)关闭时会把焦点还给触发元素,可能晚于首次聚焦;
209
+ // 分段检查:只要焦点落到弹窗外就收回输入框,用户点到弹窗内其他控件则不打扰。
210
+ for (const ms of [120, 300, 600]) {
211
+ setTimeout(() => {
212
+ if (!input.isConnected) return;
213
+ const dialog = input.closest('[role="dialog"]');
214
+ if (dialog && !dialog.contains(document.activeElement)) tryFocus();
215
+ }, ms);
216
+ }
217
+ }
218
+ return input;
219
+ }
220
+
221
+ // 数字输入框(样式调整用,交互对齐 gemini-pro 的滚轮数字框):
222
+ // 滚轮向上增大/向下减小(步进 step)、回车/失焦提交手输值、非法或越界回退到上次有效值。
223
+ // onCommit 只在值确定变化时回调;显示层始终是纯数字,单位由调用方放在框外标签。
224
+ export function numberField({ value = null, fallback = 0, min = 0, max = 48, step = 1, onCommit }) {
225
+ let current = typeof value === 'number' && Number.isFinite(value) ? value : fallback;
226
+ const input = h('input', {
227
+ type: 'text',
228
+ inputmode: 'decimal',
229
+ value: String(current),
230
+ class: 'h-8 w-16 rounded-lg border border-border bg-input px-2 text-right text-ui-sm tabular-nums text-foreground outline-none transition-shadow focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40',
231
+ });
232
+ const clamp = (v) => Math.min(max, Math.max(min, v));
233
+ // 按步长对齐后再清洗浮点误差(0.05 步长下 24*0.05 会得到 1.2000000000000002)
234
+ const quantize = (v) => step >= 1 ? Math.round(v) : Math.round(Math.round(v / step) * step * 1e4) / 1e4;
235
+ const display = (v) => { input.value = String(v); };
236
+ const commit = (v) => {
237
+ const next = clamp(v);
238
+ if (next === current) { display(next); return; }
239
+ current = next;
240
+ display(next);
241
+ onCommit && onCommit(next);
242
+ };
243
+ // 滚轮调值(需 preventDefault,阻止页面滚动);有小数步进时按 step 对齐
244
+ input.addEventListener('wheel', (e) => {
245
+ e.preventDefault();
246
+ const dir = (e.deltaY || 0) < 0 ? 1 : -1;
247
+ commit(quantize(current + dir * step));
248
+ }, { passive: false });
249
+ // 手输:回车/失焦提交;非法或空回退。整数步进项保留手输小数(如 3.5px),
250
+ // 仅小数步进项按步长对齐
251
+ const submitTyped = () => {
252
+ const parsed = parseFloat(String(input.value).trim());
253
+ if (!Number.isFinite(parsed)) { display(current); return; }
254
+ commit(step >= 1 ? parsed : quantize(parsed));
255
+ };
256
+ input.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); submitTyped(); } });
257
+ input.addEventListener('blur', submitTyped);
258
+ return {
259
+ el: input,
260
+ set(v) { current = clamp(v); display(current); },
261
+ reset() { current = fallback; display(current); },
262
+ get() { return current; },
263
+ };
264
+ }
265
+
266
+ // 数值+单位同框的输入框(内容宽度等需要 px/% 切换的设置项,交互对齐 gemini-pro):
267
+ // 框内显示如“900px”“85%”;滚轮按单位各自步进(px 大步、% 小步);
268
+ // 手输可带单位(缺省沿用当前单位),非法或越界回退。onCommit 回调 { value, unit }。
269
+ export function unitField({ value = null, fallback = { value: 100, unit: '%' }, step = { px: 10, '%': 1 }, onCommit }) {
270
+ const RANGES = { px: [320, 3840], '%': [20, 100] };
271
+ const norm = (v) => {
272
+ if (!v || !RANGES[v.unit]) return null;
273
+ const [min, max] = RANGES[v.unit];
274
+ const n = v.unit === 'px' ? Math.round(v.value) : Math.round(v.value * 10) / 10;
275
+ return Number.isFinite(n) ? { value: Math.min(max, Math.max(min, n)), unit: v.unit } : null;
276
+ };
277
+ let current = norm(value) || norm(fallback) || { value: 100, unit: '%' };
278
+ const input = h('input', {
279
+ type: 'text',
280
+ inputmode: 'decimal',
281
+ value: `${current.value}${current.unit}`,
282
+ class: 'h-8 w-20 rounded-lg border border-border bg-input px-2 text-right text-ui-sm tabular-nums text-foreground outline-none transition-shadow focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40',
283
+ });
284
+ const display = () => { input.value = `${current.value}${current.unit}`; };
285
+ const commit = (v) => {
286
+ const next = norm(v);
287
+ if (!next || (next.value === current.value && next.unit === current.unit)) { display(); return; }
288
+ current = next;
289
+ display();
290
+ onCommit && onCommit({ ...current });
291
+ };
292
+ input.addEventListener('wheel', (e) => {
293
+ e.preventDefault();
294
+ const dir = (e.deltaY || 0) < 0 ? 1 : -1;
295
+ commit({ value: current.value + dir * (step[current.unit] || 1), unit: current.unit });
296
+ }, { passive: false });
297
+ const parseTyped = (s) => {
298
+ const m = String(s).trim().match(/^(\d+(?:\.\d+)?)\s*(px|%)?$/i);
299
+ if (!m) return null;
300
+ return { value: parseFloat(m[1]), unit: (m[2] || current.unit).toLowerCase() === 'px' ? 'px' : '%' };
301
+ };
302
+ const submitTyped = () => {
303
+ const parsed = parseTyped(input.value);
304
+ if (!parsed) { display(); return; }
305
+ commit(parsed);
306
+ };
307
+ input.addEventListener('keydown', (e) => { if (e.key === 'Enter') { e.preventDefault(); submitTyped(); } });
308
+ input.addEventListener('blur', submitTyped);
309
+ return {
310
+ el: input,
311
+ reset() { current = norm(fallback) || current; display(); },
312
+ get() { return { ...current } },
313
+ };
314
+ }
315
+
316
+ // 设置项行:名称 + 描述 + 开关;extra 可携带一个行内控件(如数值输入),
317
+ // 排在开关左侧,其上的点击/滚轮不触发行开关
318
+ // 开关样式完全由 ensureStyle 中的自有规则驱动(几何/配色固定写入,
319
+ // 颜色取主题变量),不依赖应用的 Tailwind 工具类——v4 只为应用源码
320
+ // 实际用过的类生成 CSS,注入标记里"长得像"的类名不保证有样式。
321
+ // desc 留空时只渲染名称行、内边距也收紧(紧凑变体,Vision/rtk 面板用,那里顶部已有简介)。
322
+ export function settingRow(name, desc, checked, onToggle, extra) {
323
+ const knob = h('span', { class: 'zcodepro-switch-knob' });
324
+ const track = h('span', {
325
+ class: 'zcodepro-switch',
326
+ 'data-zcodepro-switch': '',
327
+ 'data-state': checked ? 'on' : 'off',
328
+ }, knob);
329
+ const extraEl = extra
330
+ ? h('div', {
331
+ class: 'flex shrink-0 items-center pt-0.5',
332
+ onClick: (e) => { e.stopPropagation(); },
333
+ onMousedown: (e) => { e.stopPropagation(); },
334
+ onWheel: (e) => { e.stopPropagation(); },
335
+ }, extra)
336
+ : null;
337
+ const row = h('div', {
338
+ class: `flex cursor-pointer items-start gap-2 rounded-lg ${desc ? 'p-2' : 'p-1.5'} transition-colors hover:bg-surface-hover`,
339
+ onClick: () => { onToggle(); },
340
+ },
341
+ h('div', { class: 'min-w-0 flex-1' },
342
+ h('div', { class: 'text-ui-sm font-medium text-foreground' }, name),
343
+ ...(desc ? [h('div', { class: 'mt-0.5 text-ui-xs/relaxed text-foreground-subtle' }, desc)] : [])),
344
+ ...(extraEl ? [extraEl] : []),
345
+ h('button', {
346
+ type: 'button',
347
+ role: 'switch',
348
+ 'aria-checked': String(checked),
349
+ class: 'relative inline-flex shrink-0 cursor-pointer items-center',
350
+ }, track));
351
+ return row;
352
+ }
353
+
354
+ // 把一段 <style> 注入页面(弹窗关键视觉兜底 + toast 动画 keyframes)
355
+ export function ensureStyle() {
356
+ if (document.getElementById('__zcodepro_style__')) return;
357
+ const root = document.head || document.documentElement;
358
+ if (!root) return;
359
+ root.append(h('style', { id: '__zcodepro_style__' }, `
360
+ @keyframes zcodepro-fade-in { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }
361
+ /* 弹窗遮罩/面板兜底:不依赖应用 Tailwind 类是否仍存在;主题色走 --color-* 变量,缺失时回退字面量 */
362
+ .zcodepro-overlay { background-color: rgba(0, 0, 0, 0.6); }
363
+ .zcodepro-overlay[data-overlay="none"] { background-color: transparent; }
364
+ .zcodepro-card {
365
+ background-color: var(--color-popover, #fff);
366
+ border-radius: 16px;
367
+ outline: none;
368
+ box-shadow: 0 0 0 1px var(--color-border, rgba(0, 0, 0, 0.1)), 0 25px 50px -12px rgba(0, 0, 0, 0.25);
369
+ /* 限高 + 纵向布局:内容比窗口高时(如视觉代理多卡片)不再溢出窗口,
370
+ 由正文区内部滚动;标题与关闭按钮固定可见。max-height 用百分比跟随
371
+ 遮罩实际高度(Linux 下顶部让出自绘标题栏),不用 vh 手算 */
372
+ display: flex;
373
+ flex-direction: column;
374
+ max-height: 100%;
375
+ }
376
+ /* 标题/描述不参与压缩,正文区独占收缩(flex 布局下的保险写法) */
377
+ .zcodepro-card > * { flex-shrink: 0; }
378
+ .zcodepro-dialog-body {
379
+ flex: 1 1 auto;
380
+ min-height: 0;
381
+ overflow-y: auto;
382
+ /* 滚到底不再把滚动传给弹窗后面的页面 */
383
+ overscroll-behavior: contain;
384
+ }
385
+ /* 开关(设置弹窗):几何固定写入,颜色随主题变量 */
386
+ [data-zcodepro-switch] {
387
+ position: relative;
388
+ display: inline-flex;
389
+ align-items: center;
390
+ width: 32px;
391
+ height: 18px;
392
+ border-radius: 9999px;
393
+ padding: 1px;
394
+ transition: background-color 0.15s ease;
395
+ background-color: color-mix(in oklab, var(--color-primary, #000) 30%, transparent);
396
+ }
397
+ [data-zcodepro-switch][data-state="on"] { background-color: var(--color-primary, #000); }
398
+ .zcodepro-switch-knob {
399
+ display: block;
400
+ width: 16px;
401
+ height: 16px;
402
+ border-radius: 9999px;
403
+ background-color: var(--color-primary-foreground, #fff);
404
+ transition: transform 0.15s ease;
405
+ }
406
+ [data-zcodepro-switch][data-state="on"] .zcodepro-switch-knob { transform: translateX(14px); }
407
+ /* 标签页切换(设置弹窗):胶囊容器 + 激活高亮,视觉参考侧栏“分组/项目”切换;
408
+ 与开关同理,几何/配色写入自有规则并取主题变量,不依赖应用 Tailwind 类 */
409
+ .zcodepro-tablist {
410
+ display: inline-flex;
411
+ align-items: center;
412
+ height: 28px;
413
+ padding: 2px;
414
+ border-radius: 9999px;
415
+ background-color: color-mix(in oklab, var(--color-foreground, #888) 8%, transparent);
416
+ }
417
+ .zcodepro-tab {
418
+ display: inline-flex;
419
+ align-items: center;
420
+ justify-content: center;
421
+ height: 24px;
422
+ padding: 0 14px;
423
+ border: none;
424
+ border-radius: 9999px;
425
+ background: transparent;
426
+ cursor: pointer;
427
+ font-size: 12px;
428
+ line-height: 1;
429
+ color: var(--color-muted-foreground, #888);
430
+ transition: color 0.15s ease, background-color 0.15s ease;
431
+ }
432
+ .zcodepro-tab[data-state="active"] {
433
+ background-color: var(--color-background, #fff);
434
+ color: var(--color-foreground, #111);
435
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.12);
436
+ }
437
+ /* 紧凑小按钮:几何/配色写入自有规则并取主题变量(高度=24px,与标签页一致) */
438
+ .zcodepro-btn-sm {
439
+ display: inline-flex;
440
+ align-items: center;
441
+ justify-content: center;
442
+ height: 24px;
443
+ padding: 0 10px;
444
+ border: 1px solid var(--color-border, rgba(0, 0, 0, 0.12));
445
+ border-radius: 8px;
446
+ background-color: transparent;
447
+ cursor: pointer;
448
+ font-size: 12px;
449
+ line-height: 1;
450
+ white-space: nowrap;
451
+ color: var(--color-foreground, #111);
452
+ transition: background-color 0.12s ease;
453
+ }
454
+ .zcodepro-btn-sm:hover:not(:disabled) {
455
+ background-color: color-mix(in oklab, var(--color-foreground, #888) 8%, transparent);
456
+ }
457
+ .zcodepro-btn-sm:disabled {
458
+ opacity: 0.5;
459
+ pointer-events: none;
460
+ }
461
+ .zcodepro-btn-sm[data-variant="primary"] {
462
+ border-color: transparent;
463
+ background-color: var(--color-primary, #111);
464
+ color: var(--color-primary-foreground, #fff);
465
+ }
466
+ .zcodepro-btn-sm[data-variant="primary"]:hover:not(:disabled) {
467
+ background-color: color-mix(in oklab, var(--color-primary, #111) 88%, #fff);
468
+ }
469
+ /* 弹窗右上角关闭按钮:用文字字形而非 SVG 描边(应用环境里注入的 SVG 描边不可见),
470
+ 几何/配色写入自有规则并取主题变量,保证常显 */
471
+ .zcodepro-close {
472
+ display: inline-flex;
473
+ align-items: center;
474
+ justify-content: center;
475
+ width: 28px;
476
+ height: 28px;
477
+ border: none;
478
+ border-radius: 8px;
479
+ background: transparent;
480
+ cursor: pointer;
481
+ font-size: 15px;
482
+ line-height: 1;
483
+ color: var(--color-muted-foreground, #888);
484
+ transition: background-color 0.12s ease, color 0.12s ease;
485
+ }
486
+ .zcodepro-close:hover {
487
+ background-color: color-mix(in oklab, var(--color-foreground, #888) 8%, transparent);
488
+ color: var(--color-foreground, #111);
489
+ }
490
+ /* 全局提示词编辑框(设置弹窗):与开关/标签页同理,几何/配色写入自有规则并取主题变量。
491
+ 边框用 border 而非 box-shadow:应用全局样式对 :focus/:focus-visible 强制
492
+ box-shadow:none !important、outline:none !important,box-shadow 边框聚焦瞬间会被清掉;
493
+ 应用自身输入框的聚焦反馈同样只走 border 变色 */
494
+ .zcodepro-textarea {
495
+ display: block;
496
+ width: 100%;
497
+ min-height: 11rem;
498
+ max-height: 22rem;
499
+ resize: vertical;
500
+ padding: 10px 12px;
501
+ border: 1px solid var(--color-border, rgba(0, 0, 0, 0.12));
502
+ border-radius: 10px;
503
+ outline: none;
504
+ background-color: var(--color-input, var(--color-popover, #fff));
505
+ color: var(--color-foreground, #111);
506
+ font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Consolas, monospace);
507
+ font-size: 12px;
508
+ line-height: 1.6;
509
+ }
510
+ .zcodepro-textarea:focus-visible {
511
+ border-color: var(--color-primary, #111);
512
+ }
513
+ .zcodepro-textarea::placeholder { color: var(--color-muted-foreground, #888); }
514
+ .zcodepro-textarea:disabled { opacity: 0.5; }
515
+ /* 小型文本域(视觉代理提示词等):11rem 最小高度只属于全局提示词大编辑框,这里按 rows 定高 */
516
+ .zcodepro-textarea.zcodepro-textarea-sm { min-height: 0; }
517
+ /* 行高滑杆(样式调整标签页) */
518
+ .zcodepro-range {
519
+ -webkit-appearance: none;
520
+ appearance: none;
521
+ width: 100%;
522
+ height: 4px;
523
+ border-radius: 9999px;
524
+ background: color-mix(in oklab, var(--color-foreground, #888) 18%, transparent);
525
+ outline: none;
526
+ }
527
+ /* 会话快捷切换弹窗(alt+x/c):居中列表卡,类 alt+tab */
528
+ .zcodepro-switcher-overlay {
529
+ position: fixed;
530
+ inset: 0;
531
+ z-index: 90;
532
+ display: flex;
533
+ align-items: center;
534
+ justify-content: center;
535
+ background-color: rgba(0, 0, 0, 0.35);
536
+ }
537
+ .zcodepro-switcher {
538
+ display: flex;
539
+ flex-direction: column;
540
+ min-width: 440px;
541
+ max-width: min(560px, calc(100vw - 2rem));
542
+ max-height: 60vh;
543
+ padding: 8px;
544
+ border-radius: 14px;
545
+ background-color: var(--color-popover, #fff);
546
+ box-shadow: 0 0 0 1px var(--color-border, rgba(0, 0, 0, 0.1)), 0 25px 50px -12px rgba(0, 0, 0, 0.35);
547
+ }
548
+ .zcodepro-switcher-hint {
549
+ padding: 4px 10px 8px;
550
+ font-size: 11px;
551
+ color: var(--color-muted-foreground, #888);
552
+ }
553
+ .zcodepro-switcher-list {
554
+ overflow-y: auto;
555
+ display: flex;
556
+ flex-direction: column;
557
+ gap: 2px;
558
+ }
559
+ /* 两行布局(标题 + 项目名)全部居左;标题行的“当前”标签与文本垂直居中 */
560
+ .zcodepro-switcher-row {
561
+ display: flex;
562
+ flex-direction: column;
563
+ align-items: stretch;
564
+ gap: 2px;
565
+ padding: 8px 12px;
566
+ border-radius: 8px;
567
+ cursor: pointer;
568
+ text-align: left;
569
+ }
570
+ .zcodepro-switcher-row:hover { background-color: color-mix(in oklab, var(--color-foreground, #888) 6%, transparent); }
571
+ .zcodepro-switcher-row[data-active] {
572
+ background-color: color-mix(in oklab, var(--color-foreground, #888) 12%, transparent);
573
+ }
574
+ .zcodepro-switcher-title {
575
+ display: flex;
576
+ align-items: center;
577
+ gap: 6px;
578
+ min-width: 0;
579
+ text-align: left;
580
+ font-size: 13px;
581
+ color: var(--color-foreground, #111);
582
+ }
583
+ /* 标题文本承载 span:截断省略放在文本节点上,避免 flex 容器 ellipsis 失效 */
584
+ .zcodepro-switcher-title > span:last-child {
585
+ min-width: 0;
586
+ overflow: hidden;
587
+ white-space: nowrap;
588
+ text-overflow: ellipsis;
589
+ }
590
+ .zcodepro-switcher-tag {
591
+ flex-shrink: 0;
592
+ padding: 1px 5px;
593
+ border-radius: 5px;
594
+ font-size: 10px;
595
+ background-color: color-mix(in oklab, var(--color-primary, #111) 12%, transparent);
596
+ color: var(--color-foreground-subtle, #666);
597
+ }
598
+ .zcodepro-switcher-ws {
599
+ font-size: 11px;
600
+ color: var(--color-muted-foreground, #888);
601
+ overflow: hidden;
602
+ white-space: nowrap;
603
+ text-overflow: ellipsis;
604
+ }
605
+ .zcodepro-range::-webkit-slider-thumb {
606
+ -webkit-appearance: none;
607
+ width: 14px;
608
+ height: 14px;
609
+ border-radius: 9999px;
610
+ background: var(--color-primary, #111);
611
+ cursor: pointer;
612
+ }
613
+ /* 图片右键菜单项悬停高亮:同上不依赖应用 Tailwind hover 类是否存在 */
614
+ .zcodepro-imgmenu-item { transition: background-color 0.12s ease; }
615
+ .zcodepro-imgmenu-item:hover {
616
+ background-color: var(--color-accent, color-mix(in oklab, var(--color-foreground, #888) 10%, transparent));
617
+ }
618
+ /* 折叠项目运行提示(ws-running.js):项目图标旋转。动画类挂在图标外层的
619
+ 稳定 span 上,展开/收起时应用重建 svg 也不受影响;时长与应用 loader 一致 */
620
+ @keyframes zcodepro-ws-spin { to { transform: rotate(360deg); } }
621
+ span.zcodepro-ws-running > svg {
622
+ animation: zcodepro-ws-spin 1s linear infinite;
623
+ transform-origin: center;
624
+ }
625
+ /* 无感探查期间盖在项目行上的冻结克隆:固定定位、不响应指针 */
626
+ .zcodepro-ws-frozen {
627
+ position: fixed;
628
+ z-index: 2147483000;
629
+ pointer-events: none;
630
+ margin: 0;
631
+ }
632
+ /* 侧栏菜单并入顶栏(toolbar-icons.js):自有图标按钮 + 悬停提示 + 原菜单隐藏。
633
+ 几何对齐应用导航按钮(icon-md:28×28、圆角 8px),颜色走主题变量,
634
+ 与开关/标签页同理不依赖应用 Tailwind 类 */
635
+ .zcodepro-tb-row {
636
+ display: flex;
637
+ align-items: center;
638
+ gap: 4px;
639
+ margin-left: 6px;
640
+ -webkit-app-region: no-drag;
641
+ }
642
+ .zcodepro-tb-btn {
643
+ position: relative;
644
+ display: inline-flex;
645
+ align-items: center;
646
+ justify-content: center;
647
+ width: 28px;
648
+ height: 28px;
649
+ padding: 0;
650
+ border: none;
651
+ border-radius: 8px;
652
+ background-color: transparent;
653
+ color: var(--color-foreground-subtle, #666);
654
+ cursor: pointer;
655
+ flex-shrink: 0;
656
+ transition: background-color 0.12s ease, color 0.12s ease;
657
+ }
658
+ .zcodepro-tb-btn:hover,
659
+ .zcodepro-tb-btn:focus-visible {
660
+ outline: none;
661
+ background-color: var(--color-hover, color-mix(in oklab, var(--color-foreground, #888) 8%, transparent));
662
+ color: var(--color-foreground, #111);
663
+ }
664
+ .zcodepro-tb-btn[data-active="1"] {
665
+ background-color: color-mix(in oklab, var(--color-foreground, #888) 10%, transparent);
666
+ color: var(--color-foreground, #111);
667
+ }
668
+ .zcodepro-tb-icon {
669
+ display: block;
670
+ width: 16px;
671
+ height: 16px;
672
+ }
673
+ /* 悬停提示:按钮下方居中出现(约 0.3s 延迟,对齐应用提示节奏),移开立即消失 */
674
+ .zcodepro-tb-btn::after {
675
+ content: attr(data-zcodepro-tip);
676
+ position: absolute;
677
+ top: calc(100% + 6px);
678
+ left: 50%;
679
+ transform: translateX(-50%);
680
+ padding: 4px 8px;
681
+ border-radius: 6px;
682
+ border: 1px solid var(--color-border, rgba(127, 127, 127, 0.25));
683
+ background-color: var(--color-menu, #171717);
684
+ color: var(--color-foreground, #fff);
685
+ font-size: 11px;
686
+ line-height: 1.5;
687
+ white-space: nowrap;
688
+ pointer-events: none;
689
+ opacity: 0;
690
+ visibility: hidden;
691
+ z-index: 60;
692
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.25);
693
+ }
694
+ .zcodepro-tb-btn:hover::after,
695
+ .zcodepro-tb-btn:focus-visible::after {
696
+ opacity: 1;
697
+ visibility: visible;
698
+ transition: opacity 0.15s ease 0.3s;
699
+ }
700
+ /* 原侧栏菜单整体隐藏(toolbar-icons 开启时) */
701
+ .zcodepro-tb-hidden { display: none !important; }
702
+ /* 已置顶分区可折叠(pinned-collapse.js):标题整行可点,折叠时藏起任务列表,
703
+ chevron 旋向与“项目”分区一致(收起指向右侧) */
704
+ .zcodepro-pin-head {
705
+ display: flex;
706
+ align-items: center;
707
+ gap: 4px;
708
+ cursor: pointer;
709
+ transition: color 0.15s ease;
710
+ }
711
+ .zcodepro-pin-head:hover { color: var(--color-foreground, #111); }
712
+ .zcodepro-pin-chevron {
713
+ display: inline-flex;
714
+ align-items: center;
715
+ justify-content: center;
716
+ padding: 0;
717
+ border: none;
718
+ background-color: transparent;
719
+ color: inherit;
720
+ cursor: pointer;
721
+ }
722
+ .zcodepro-pin-chevron svg {
723
+ width: 14px;
724
+ height: 14px;
725
+ opacity: 0.55;
726
+ transition: transform 0.15s ease, opacity 0.15s ease;
727
+ }
728
+ .zcodepro-pin-head:hover .zcodepro-pin-chevron svg { opacity: 1; }
729
+ .zcodepro-pin-collapsed .zcodepro-pin-chevron svg { transform: rotate(-90deg); }
730
+ .zcodepro-pin-collapsed > ul { display: none !important; }
731
+ /* 折叠后去掉标题自带的底部留白,与下一分区的间隔和其他分区节奏一致 */
732
+ .zcodepro-pin-collapsed .zcodepro-pin-head { padding-bottom: 0; }
733
+ `));
734
+ }