aicodeman 1.26.2 → 1.28.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/dist/config/cli-registry/stock.d.ts.map +1 -1
- package/dist/config/cli-registry/stock.js +13 -2
- package/dist/config/cli-registry/stock.js.map +1 -1
- package/dist/session-cli-builder.d.ts.map +1 -1
- package/dist/session-cli-builder.js +31 -5
- package/dist/session-cli-builder.js.map +1 -1
- package/dist/session.d.ts.map +1 -1
- package/dist/session.js +19 -0
- package/dist/session.js.map +1 -1
- package/dist/tmux-manager.d.ts +3 -0
- package/dist/tmux-manager.d.ts.map +1 -1
- package/dist/tmux-manager.js +65 -8
- package/dist/tmux-manager.js.map +1 -1
- package/dist/types/common.d.ts +2 -0
- package/dist/types/common.d.ts.map +1 -1
- package/dist/utils/omp-session-resolver.d.ts +14 -0
- package/dist/utils/omp-session-resolver.d.ts.map +1 -1
- package/dist/utils/omp-session-resolver.js +21 -2
- package/dist/utils/omp-session-resolver.js.map +1 -1
- package/dist/web/public/admin-ui.js.gz +0 -0
- package/dist/web/public/api-client.c9b1cddc.js.gz +0 -0
- package/dist/web/public/app.78bf0bf8.js +38 -0
- package/dist/web/public/app.78bf0bf8.js.br +0 -0
- package/dist/web/public/app.78bf0bf8.js.gz +0 -0
- package/dist/web/public/approvals-ui.js.gz +0 -0
- package/dist/web/public/{constants.e6d48776.js → constants.9cd8ed69.js} +7 -1
- package/dist/web/public/constants.9cd8ed69.js.br +0 -0
- package/dist/web/public/constants.9cd8ed69.js.gz +0 -0
- package/dist/web/public/cron-ui.js.gz +0 -0
- package/dist/web/public/entrance-animations.js.gz +0 -0
- package/dist/web/public/home-sessions.js.gz +0 -0
- package/dist/web/public/i18n.9db8c4e0.js.gz +0 -0
- package/dist/web/public/image-input.cd4b97c4.js.gz +0 -0
- package/dist/web/public/index.html +22 -10
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/input-cjk.87f14251.js.gz +0 -0
- package/dist/web/public/{keyboard-accessory.daf18302.js → keyboard-accessory.1d0dda9b.js} +182 -13
- package/dist/web/public/keyboard-accessory.1d0dda9b.js.br +0 -0
- package/dist/web/public/keyboard-accessory.1d0dda9b.js.gz +0 -0
- package/dist/web/public/mobile-handlers.2e9c214a.js.gz +0 -0
- package/dist/web/public/mobile-overview.js.gz +0 -0
- package/dist/web/public/mobile.65176832.css.gz +0 -0
- package/dist/web/public/notification-manager.36ea4624.js.gz +0 -0
- package/dist/web/public/orchestrator-panel.js.gz +0 -0
- package/dist/web/public/{panels-ui.9eb5b235.js → panels-ui.4e9ea307.js} +1 -1
- package/dist/web/public/panels-ui.4e9ea307.js.br +0 -0
- package/dist/web/public/panels-ui.4e9ea307.js.gz +0 -0
- package/dist/web/public/ralph-panel.6de2d0f8.js.gz +0 -0
- package/dist/web/public/ralph-wizard.13a1831e.js.gz +0 -0
- package/dist/web/public/readmymind-ui.js.gz +0 -0
- package/dist/web/public/respawn-ui.ff0dae4c.js.gz +0 -0
- package/dist/web/public/sanitize-html.bc7078d6.js.gz +0 -0
- package/dist/web/public/session-lineage.js.gz +0 -0
- package/dist/web/public/session-ui.cd222710.js.gz +0 -0
- package/dist/web/public/settings-ui.c175db9a.js +67 -0
- package/dist/web/public/settings-ui.c175db9a.js.br +0 -0
- package/dist/web/public/settings-ui.c175db9a.js.gz +0 -0
- package/dist/web/public/{styles.89069bbc.css → styles.0d41361c.css} +1 -1
- package/dist/web/public/styles.0d41361c.css.br +0 -0
- package/dist/web/public/styles.0d41361c.css.gz +0 -0
- package/dist/web/public/subagent-windows.e6ca799f.js.gz +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/public/tab-rail-resize.42c24949.js.gz +0 -0
- package/dist/web/public/terminal-keycode229-recovery.6ea8fe37.js +1 -0
- package/dist/web/public/terminal-keycode229-recovery.6ea8fe37.js.br +0 -0
- package/dist/web/public/terminal-keycode229-recovery.6ea8fe37.js.gz +0 -0
- package/dist/web/public/terminal-ui.89e4eb1a.js +2 -0
- package/dist/web/public/terminal-ui.89e4eb1a.js.br +0 -0
- package/dist/web/public/terminal-ui.89e4eb1a.js.gz +0 -0
- package/dist/web/public/ultracode-panel.js.gz +0 -0
- package/dist/web/public/ultracode-windows.js.gz +0 -0
- package/dist/web/public/upload.html.gz +0 -0
- package/dist/web/public/vendor/dompurify.min.js.gz +0 -0
- package/dist/web/public/vendor/marked.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-fit.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-serialize.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-unicode11.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-addon-webgl.min.js.gz +0 -0
- package/dist/web/public/vendor/xterm-predictive-echo.bd6882b8.js.gz +0 -0
- package/dist/web/public/vendor/xterm-zerolag-input.6fee72f2.js.gz +0 -0
- package/dist/web/public/vendor/xterm.css.gz +0 -0
- package/dist/web/public/vendor/xterm.min.js.gz +0 -0
- package/dist/web/public/voice-input.c4b51eb6.js.gz +0 -0
- package/dist/web/public/voice-pcm-worklet.js.gz +0 -0
- package/dist/web/public/webview-tabs.js +199 -9
- package/dist/web/public/webview-tabs.js.br +0 -0
- package/dist/web/public/webview-tabs.js.gz +0 -0
- package/dist/web/response-viewer-transcript.d.ts +20 -0
- package/dist/web/response-viewer-transcript.d.ts.map +1 -1
- package/dist/web/response-viewer-transcript.js +27 -0
- package/dist/web/response-viewer-transcript.js.map +1 -1
- package/dist/web/routes/file-routes.d.ts.map +1 -1
- package/dist/web/routes/file-routes.js +11 -3
- package/dist/web/routes/file-routes.js.map +1 -1
- package/dist/web/routes/session-routes.d.ts.map +1 -1
- package/dist/web/routes/session-routes.js +16 -4
- package/dist/web/routes/session-routes.js.map +1 -1
- package/dist/web/schemas.d.ts +4 -0
- package/dist/web/schemas.d.ts.map +1 -1
- package/dist/web/schemas.js +9 -0
- package/dist/web/schemas.js.map +1 -1
- package/package.json +1 -1
- package/skills/codeman/reference/endpoints.md +1 -0
- package/dist/web/public/app.c4dbfcfb.js +0 -38
- package/dist/web/public/app.c4dbfcfb.js.br +0 -0
- package/dist/web/public/app.c4dbfcfb.js.gz +0 -0
- package/dist/web/public/constants.e6d48776.js.br +0 -0
- package/dist/web/public/constants.e6d48776.js.gz +0 -0
- package/dist/web/public/keyboard-accessory.daf18302.js.br +0 -0
- package/dist/web/public/keyboard-accessory.daf18302.js.gz +0 -0
- package/dist/web/public/panels-ui.9eb5b235.js.br +0 -0
- package/dist/web/public/panels-ui.9eb5b235.js.gz +0 -0
- package/dist/web/public/settings-ui.554f9cdd.js +0 -67
- package/dist/web/public/settings-ui.554f9cdd.js.br +0 -0
- package/dist/web/public/settings-ui.554f9cdd.js.gz +0 -0
- package/dist/web/public/styles.89069bbc.css.br +0 -0
- package/dist/web/public/styles.89069bbc.css.gz +0 -0
- package/dist/web/public/terminal-ui.1547ce5e.js +0 -2
- package/dist/web/public/terminal-ui.1547ce5e.js.br +0 -0
- package/dist/web/public/terminal-ui.1547ce5e.js.gz +0 -0
|
@@ -47,11 +47,24 @@
|
|
|
47
47
|
// the picker browses Home and every configured root, so wanting dotfiles in a
|
|
48
48
|
// project does not imply wanting them in ~.
|
|
49
49
|
const PATH_PICKER_SHOW_HIDDEN_KEY = 'codeman:pathPickerShowHidden';
|
|
50
|
+
// Per-device like the hidden toggle: how you scan a folder is a habit of the
|
|
51
|
+
// hand, not of the workspace.
|
|
52
|
+
const PATH_PICKER_SORT_KEY = 'codeman:pathPickerSort';
|
|
53
|
+
const PATH_PICKER_SORT_MODES = [
|
|
54
|
+
{ value: 'name-asc', label: 'Name A→Z' },
|
|
55
|
+
{ value: 'name-desc', label: 'Name Z→A' },
|
|
56
|
+
{ value: 'mtime-desc', label: 'Newest first' },
|
|
57
|
+
{ value: 'mtime-asc', label: 'Oldest first' },
|
|
58
|
+
];
|
|
59
|
+
const PATH_PICKER_DEFAULT_SORT = 'name-asc';
|
|
50
60
|
|
|
51
61
|
const PathPicker = {
|
|
52
62
|
overlay: null,
|
|
53
63
|
_options: null,
|
|
54
64
|
_selectedPath: '',
|
|
65
|
+
_currentPath: '',
|
|
66
|
+
_entries: [],
|
|
67
|
+
_truncated: false,
|
|
55
68
|
_previousFocus: null,
|
|
56
69
|
_keydownHandler: null,
|
|
57
70
|
_loadSequence: 0,
|
|
@@ -59,6 +72,7 @@ const PathPicker = {
|
|
|
59
72
|
_previewRequestSequence: 0,
|
|
60
73
|
_previewPreviousFocus: null,
|
|
61
74
|
_showHidden: false,
|
|
75
|
+
_sortMode: PATH_PICKER_DEFAULT_SORT,
|
|
62
76
|
|
|
63
77
|
/**
|
|
64
78
|
* Open the lazy filesystem browser.
|
|
@@ -69,7 +83,10 @@ const PathPicker = {
|
|
|
69
83
|
this.close(false);
|
|
70
84
|
this._options = options;
|
|
71
85
|
this._selectedPath = '';
|
|
86
|
+
this._currentPath = '';
|
|
87
|
+
this._entries = [];
|
|
72
88
|
this._showHidden = this._loadShowHidden();
|
|
89
|
+
this._sortMode = this._loadSortMode();
|
|
73
90
|
this._previousFocus = document.activeElement;
|
|
74
91
|
this._previousFocus?.blur?.();
|
|
75
92
|
|
|
@@ -90,11 +107,19 @@ const PathPicker = {
|
|
|
90
107
|
</div>
|
|
91
108
|
<div class="path-picker-nav">
|
|
92
109
|
<button type="button" class="path-picker-up" title="Parent folder" aria-label="Parent folder">↑</button>
|
|
93
|
-
<
|
|
110
|
+
<form class="path-picker-jump" title="Current folder — edit and press Enter to jump">
|
|
111
|
+
<input type="text" class="path-picker-current" aria-label="Current folder path" autocomplete="off" autocapitalize="off" autocorrect="off" spellcheck="false" enterkeyhint="go">
|
|
112
|
+
<button type="submit" class="path-picker-go" title="Go to this path" aria-label="Go to this path">Go</button>
|
|
113
|
+
</form>
|
|
94
114
|
<button type="button" class="path-picker-hidden" title="Show hidden files and folders" aria-label="Show hidden files and folders" aria-pressed="false">.*</button>
|
|
95
115
|
<button type="button" class="path-picker-refresh" title="Refresh" aria-label="Refresh">↻</button>
|
|
96
116
|
</div>
|
|
97
|
-
<div class="path-picker-
|
|
117
|
+
<div class="path-picker-toolbar">
|
|
118
|
+
<div class="path-picker-status" aria-live="polite">Loading...</div>
|
|
119
|
+
<label class="path-picker-sort-label">Sort
|
|
120
|
+
<select class="path-picker-sort" aria-label="Sort entries"></select>
|
|
121
|
+
</label>
|
|
122
|
+
</div>
|
|
98
123
|
<div class="path-picker-list" role="listbox"></div>
|
|
99
124
|
<div class="path-picker-selection">
|
|
100
125
|
<span class="path-picker-selection-label">Selected</span>
|
|
@@ -114,12 +139,27 @@ const PathPicker = {
|
|
|
114
139
|
overlay.querySelector('.path-picker-cancel').addEventListener('click', () => this.close(true));
|
|
115
140
|
overlay.querySelector('.path-picker-confirm').addEventListener('click', () => this.confirm());
|
|
116
141
|
overlay.querySelector('.path-picker-current-select').addEventListener('click', () => {
|
|
117
|
-
|
|
118
|
-
if (current) this.select(current);
|
|
142
|
+
if (this._currentPath) this.select(this._currentPath);
|
|
119
143
|
});
|
|
120
|
-
overlay.querySelector('.path-picker-refresh').addEventListener('click', () => this.load());
|
|
144
|
+
overlay.querySelector('.path-picker-refresh').addEventListener('click', () => this.load(this._currentPath));
|
|
121
145
|
overlay.querySelector('.path-picker-hidden').addEventListener('click', () => this.toggleHidden());
|
|
122
146
|
this._syncHiddenButton();
|
|
147
|
+
// Typing a path is the fast way there. The listing is loaded ONLY on Enter/Go,
|
|
148
|
+
// never on each keystroke: a half-typed path is a 404 the server has to
|
|
149
|
+
// answer for nothing, and jumping mid-edit would yank the field around.
|
|
150
|
+
overlay.querySelector('.path-picker-jump').addEventListener('submit', (event) => {
|
|
151
|
+
event.preventDefault();
|
|
152
|
+
this.jumpTo(overlay.querySelector('.path-picker-current').value);
|
|
153
|
+
});
|
|
154
|
+
const sortSelect = overlay.querySelector('.path-picker-sort');
|
|
155
|
+
for (const mode of PATH_PICKER_SORT_MODES) {
|
|
156
|
+
const option = document.createElement('option');
|
|
157
|
+
option.value = mode.value;
|
|
158
|
+
option.textContent = mode.label;
|
|
159
|
+
sortSelect.appendChild(option);
|
|
160
|
+
}
|
|
161
|
+
sortSelect.value = this._sortMode;
|
|
162
|
+
sortSelect.addEventListener('change', (event) => this.setSortMode(event.target.value));
|
|
123
163
|
overlay.querySelector('.path-picker-up').addEventListener('click', () => {
|
|
124
164
|
const parent = overlay.querySelector('.path-picker-up').dataset.parent;
|
|
125
165
|
if (parent) this.load(parent);
|
|
@@ -169,15 +209,97 @@ const PathPicker = {
|
|
|
169
209
|
// OFF inside a hidden folder makes the current path unbrowsable again; the
|
|
170
210
|
// server answers 403 and load()'s catch falls back to the default root,
|
|
171
211
|
// which is the only place left to stand.
|
|
172
|
-
this.load(this.
|
|
212
|
+
this.load(this._currentPath || '');
|
|
213
|
+
},
|
|
214
|
+
|
|
215
|
+
_loadSortMode() {
|
|
216
|
+
try {
|
|
217
|
+
const stored = localStorage.getItem(PATH_PICKER_SORT_KEY);
|
|
218
|
+
return PATH_PICKER_SORT_MODES.some((mode) => mode.value === stored) ? stored : PATH_PICKER_DEFAULT_SORT;
|
|
219
|
+
} catch {
|
|
220
|
+
return PATH_PICKER_DEFAULT_SORT;
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
|
|
224
|
+
setSortMode(mode) {
|
|
225
|
+
if (!PATH_PICKER_SORT_MODES.some((candidate) => candidate.value === mode)) return;
|
|
226
|
+
this._sortMode = mode;
|
|
227
|
+
try {
|
|
228
|
+
localStorage.setItem(PATH_PICKER_SORT_KEY, mode);
|
|
229
|
+
} catch {}
|
|
230
|
+
const select = this.overlay?.querySelector('.path-picker-sort');
|
|
231
|
+
if (select && select.value !== mode) select.value = mode;
|
|
232
|
+
// Re-order what is already on screen; no round trip, no lost selection.
|
|
233
|
+
if (this.overlay) this.renderEntries();
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Order entries for display. Folders always come first, whatever the mode:
|
|
238
|
+
* a date sort is for finding the file you just made, and the folders are the
|
|
239
|
+
* way past it, not the thing being looked for. An entry without an mtime (an
|
|
240
|
+
* older server, the in-container source) sorts after every dated one and then
|
|
241
|
+
* by name, so a listing never degrades into an unstable order.
|
|
242
|
+
*
|
|
243
|
+
* ⚠️ This re-orders the listing the SERVER returned, and the server cuts at
|
|
244
|
+
* FILESYSTEM_PICKER_ENTRY_LIMIT (500) after sorting by name. So in a folder past
|
|
245
|
+
* that limit, "Newest first" is the newest of the first 500 BY NAME, not the newest
|
|
246
|
+
* in the folder, which is the one case this sort exists for. The status line says
|
|
247
|
+
* "(first 500 by name)" rather than "(first 500)" so the cut is legible; ordering
|
|
248
|
+
* before the cut would have to happen server-side, and would cost a stat on every
|
|
249
|
+
* entry in the directory rather than on the 500 that are returned.
|
|
250
|
+
*/
|
|
251
|
+
_sortEntries(entries) {
|
|
252
|
+
const [key, direction] = this._sortMode.split('-');
|
|
253
|
+
const sign = direction === 'desc' ? -1 : 1;
|
|
254
|
+
const byName = (a, b) => a.name.localeCompare(b.name, undefined, { numeric: true, sensitivity: 'base' });
|
|
255
|
+
return entries.slice().sort((a, b) => {
|
|
256
|
+
if (a.type !== b.type) return a.type === 'directory' ? -1 : 1;
|
|
257
|
+
if (key === 'mtime') {
|
|
258
|
+
const aTime = typeof a.mtimeMs === 'number' ? a.mtimeMs : null;
|
|
259
|
+
const bTime = typeof b.mtimeMs === 'number' ? b.mtimeMs : null;
|
|
260
|
+
if (aTime !== null && bTime !== null && aTime !== bTime) return sign * (aTime - bTime);
|
|
261
|
+
if (aTime === null && bTime !== null) return 1;
|
|
262
|
+
if (aTime !== null && bTime === null) return -1;
|
|
263
|
+
return byName(a, b);
|
|
264
|
+
}
|
|
265
|
+
return sign * byName(a, b);
|
|
266
|
+
});
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
/** Compact modified-time label: time of day today, month-day this year, else the date. */
|
|
270
|
+
_formatModified(mtimeMs) {
|
|
271
|
+
if (typeof mtimeMs !== 'number' || !Number.isFinite(mtimeMs)) return '';
|
|
272
|
+
const date = new Date(mtimeMs);
|
|
273
|
+
if (Number.isNaN(date.getTime())) return '';
|
|
274
|
+
const now = new Date();
|
|
275
|
+
const pad = (n) => String(n).padStart(2, '0');
|
|
276
|
+
if (date.toDateString() === now.toDateString()) return `${pad(date.getHours())}:${pad(date.getMinutes())}`;
|
|
277
|
+
if (date.getFullYear() === now.getFullYear()) return `${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
278
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
279
|
+
},
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Go to a path the user typed. A file path lands in its folder with the
|
|
283
|
+
* file selected, so pasting a full path from a log or a message is one Enter
|
|
284
|
+
* away from Select. A path that does not resolve lands in its parent folder
|
|
285
|
+
* when that exists (the closest place to stand) and otherwise keeps the
|
|
286
|
+
* current listing, and says so either way — unlike a stale initialPath, a
|
|
287
|
+
* typo is not a reason to throw the user back to the root.
|
|
288
|
+
*/
|
|
289
|
+
jumpTo(rawPath) {
|
|
290
|
+
const path = String(rawPath || '').trim();
|
|
291
|
+
if (!path) return;
|
|
292
|
+
this.load(path, { typed: true });
|
|
173
293
|
},
|
|
174
294
|
|
|
175
|
-
async load(path) {
|
|
295
|
+
async load(path, options = {}) {
|
|
176
296
|
if (!this.overlay || !this._options) return;
|
|
177
297
|
const loadSequence = ++this._loadSequence;
|
|
178
298
|
const list = this.overlay.querySelector('.path-picker-list');
|
|
179
299
|
const status = this.overlay.querySelector('.path-picker-status');
|
|
180
|
-
|
|
300
|
+
const typed = !!options.typed;
|
|
301
|
+
if (!typed) list.replaceChildren();
|
|
302
|
+
status.classList.remove('error');
|
|
181
303
|
status.textContent = 'Loading...';
|
|
182
304
|
|
|
183
305
|
const params = new URLSearchParams();
|
|
@@ -194,13 +316,40 @@ const PathPicker = {
|
|
|
194
316
|
if (!result?.success) throw new Error(result?.error || 'Failed to browse this folder');
|
|
195
317
|
if (!this.overlay || loadSequence !== this._loadSequence) return;
|
|
196
318
|
this.render(result.data);
|
|
319
|
+
if (options.selectIfListed) {
|
|
320
|
+
// Landed in the typed path's folder: select the entry if it is there
|
|
321
|
+
// (a file path), otherwise say what the server said about the full
|
|
322
|
+
// path — the listing is still the closest place to stand.
|
|
323
|
+
if (this._entries.some((entry) => entry.path === options.selectIfListed)) {
|
|
324
|
+
this.select(options.selectIfListed);
|
|
325
|
+
} else {
|
|
326
|
+
status.textContent = options.failMessage || 'Path not found';
|
|
327
|
+
status.classList.add('error');
|
|
328
|
+
}
|
|
329
|
+
}
|
|
197
330
|
} catch (error) {
|
|
198
331
|
if (!this.overlay || loadSequence !== this._loadSequence) return;
|
|
332
|
+
const message = error.message || 'Failed to browse this folder';
|
|
333
|
+
if (typed) {
|
|
334
|
+
// The browse endpoint answers a FILE path with "not found" (it resolves
|
|
335
|
+
// folders only), so one retry lands in the parent folder and selects
|
|
336
|
+
// the entry from the listing. Only one level: a typo two segments up
|
|
337
|
+
// is an error, not a reason to climb to the root.
|
|
338
|
+
const slash = path.lastIndexOf('/');
|
|
339
|
+
if (slash > 0 && !options.selectIfListed) {
|
|
340
|
+
this.load(path.slice(0, slash), { typed: true, selectIfListed: path, failMessage: message });
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
this.renderEntries();
|
|
344
|
+
status.textContent = options.failMessage || message;
|
|
345
|
+
status.classList.add('error');
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
199
348
|
if (path) {
|
|
200
349
|
this.load('');
|
|
201
350
|
return;
|
|
202
351
|
}
|
|
203
|
-
status.textContent =
|
|
352
|
+
status.textContent = message;
|
|
204
353
|
status.classList.add('error');
|
|
205
354
|
}
|
|
206
355
|
},
|
|
@@ -216,19 +365,29 @@ const PathPicker = {
|
|
|
216
365
|
rootSelect.appendChild(option);
|
|
217
366
|
}
|
|
218
367
|
|
|
219
|
-
this.
|
|
368
|
+
this._currentPath = data.path;
|
|
369
|
+
this.overlay.querySelector('.path-picker-current').value = data.path;
|
|
220
370
|
const up = this.overlay.querySelector('.path-picker-up');
|
|
221
371
|
up.dataset.parent = data.parent || '';
|
|
222
372
|
up.disabled = !data.parent;
|
|
373
|
+
this._entries = Array.isArray(data.entries) ? data.entries : [];
|
|
374
|
+
this._truncated = !!data.truncated;
|
|
375
|
+
this.renderEntries();
|
|
376
|
+
},
|
|
377
|
+
|
|
378
|
+
/** (Re)build the list from the last listing in the current sort order. */
|
|
379
|
+
renderEntries() {
|
|
380
|
+
if (!this.overlay) return;
|
|
381
|
+
const entries = this._sortEntries(this._entries);
|
|
223
382
|
const status = this.overlay.querySelector('.path-picker-status');
|
|
224
383
|
status.classList.remove('error');
|
|
225
|
-
status.textContent =
|
|
384
|
+
status.textContent = entries.length === 0
|
|
226
385
|
? 'This folder is empty'
|
|
227
|
-
: `${
|
|
386
|
+
: `${entries.length} item${entries.length === 1 ? '' : 's'}${this._truncated ? ' (first 500 by name)' : ''}`;
|
|
228
387
|
|
|
229
388
|
const list = this.overlay.querySelector('.path-picker-list');
|
|
230
389
|
list.replaceChildren();
|
|
231
|
-
for (const entry of
|
|
390
|
+
for (const entry of entries) {
|
|
232
391
|
const row = document.createElement('div');
|
|
233
392
|
row.className = 'path-picker-item';
|
|
234
393
|
if (entry.type === 'file' && this._options.directoriesOnly && !entry.previewKind) {
|
|
@@ -248,6 +407,14 @@ const PathPicker = {
|
|
|
248
407
|
name.className = 'path-picker-item-name';
|
|
249
408
|
name.textContent = entry.name;
|
|
250
409
|
open.append(icon, name);
|
|
410
|
+
const modified = this._formatModified(entry.mtimeMs);
|
|
411
|
+
if (modified) {
|
|
412
|
+
const meta = document.createElement('span');
|
|
413
|
+
meta.className = 'path-picker-item-meta';
|
|
414
|
+
meta.textContent = modified;
|
|
415
|
+
meta.title = new Date(entry.mtimeMs).toLocaleString();
|
|
416
|
+
open.appendChild(meta);
|
|
417
|
+
}
|
|
251
418
|
if (entry.symlink) {
|
|
252
419
|
const link = document.createElement('span');
|
|
253
420
|
link.className = 'path-picker-item-link';
|
|
@@ -417,6 +584,8 @@ const PathPicker = {
|
|
|
417
584
|
this._previousFocus = null;
|
|
418
585
|
this._options = null;
|
|
419
586
|
this._selectedPath = '';
|
|
587
|
+
this._currentPath = '';
|
|
588
|
+
this._entries = [];
|
|
420
589
|
if (restoreFocus) previousFocus?.focus?.();
|
|
421
590
|
},
|
|
422
591
|
};
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -187,7 +187,7 @@
|
|
|
187
187
|
<span class="tool-detail">${escapeHtml(i)}</span>
|
|
188
188
|
</div>`}else if(e.type==="message"){const s=e.text.length>150?e.text.substring(0,150)+"...":e.text;return`<div class="message-line">
|
|
189
189
|
<span class="time">${t}</span> \u{1F4AC} ${escapeHtml(s)}
|
|
190
|
-
</div>`}return""},updateSubagentWindows(){for(const e of this.subagentWindows.keys())this.renderSubagentWindowContent(e),this.updateSubagentWindowHeader(e)},updateSubagentWindowHeader(e){const t=this.subagents.get(e);if(!t)return;const s=document.getElementById(`subagent-window-${e}`);if(!s)return;const n=s.querySelector(".subagent-window-title .id");if(n){const c=this.getTeammateInfo(t),h=c?c.name:t.description||e.substring(0,7),u=h.length>50?h.substring(0,50)+"...":h;n.textContent=u}let i=s.querySelector(".teammate-badge");const a=this.getTeammateInfo(t);if(a&&!i){const c=s.querySelector(".subagent-window-title");if(c){const h=document.createElement("span");h.className=`teammate-badge teammate-color-${a.color}`,h.title=`Team: ${a.teamName}`,h.textContent=`@${a.name}`;const u=c.querySelector(".status");u&&u.insertAdjacentElement("beforebegin",h)}}const r=s.querySelector(".subagent-window-title");r&&(r.title=t.description||e);let o=s.querySelector(".subagent-window-title .subagent-model-badge");if(t.modelShort){if(!o){o=document.createElement("span"),o.className=`subagent-model-badge ${t.modelShort}`;const c=s.querySelector(".subagent-window-title .status");c&&c.insertAdjacentElement("beforebegin",o)}o.className=`subagent-model-badge ${t.modelShort}`,o.textContent=t.modelShort}const l=s.querySelector(".subagent-window-title .status");l&&(l.className=`status ${t.status}`,l.textContent=t.status)},openAllActiveSubagentWindows(){for(const[e,t]of this.subagents)t.status==="active"&&!this.subagentWindows.has(e)&&this.openSubagentWindow(e)},initTeammateTerminal(e,t,s){const n=s.querySelector(".subagent-window-body");if(!n)return;n.innerHTML="",n.classList.add("teammate-terminal-body"),s.classList.add("has-terminal");const i=t.sessionId,a=[];this.teammateTerminals.set(e,{terminal:null,fitAddon:null,paneTarget:t.paneTarget,sessionId:i,resizeObserver:null,pendingData:a}),requestAnimationFrame(()=>{if(!document.contains(n)){this.teammateTerminals.delete(e);return}const r=new Terminal({theme:{...window.codemanCurrentXtermTheme()},minimumContrastRatio:window.codemanCurrentSkinIsLight()?4.5:1,fontFamily:window.CodemanTerminalFont.resolve(this.loadAppSettingsFromStorage?.().terminalFontFamily),fontSize:12,lineHeight:1.2,cursorBlink:!0,cursorStyle:"block",scrollback:DEFAULT_SCROLLBACK,allowTransparency:!0,allowProposedApi:!0}),o=new FitAddon.FitAddon;if(r.loadAddon(o),typeof Unicode11Addon<"u")try{const h=new Unicode11Addon.Unicode11Addon;r.loadAddon(h),r.unicode.activeVersion="11"}catch{}try{r.open(n)}catch(h){console.warn("[TeammateTerminal] Failed to open terminal:",h),this.teammateTerminals.delete(e);return}setTimeout(()=>{try{o.fit()}catch{}fetch(`/api/sessions/${i}/teammate-pane-buffer/${encodeURIComponent(t.paneTarget)}`).then(h=>h.json()).then(h=>{if(h.success&&h.data?.buffer)try{r.write(h.data.buffer)}catch{}}).catch(h=>console.error("[TeammateTerminal] Failed to fetch buffer:",h));for(const h of a)try{r.write(h)}catch{}a.length=0},100),r.onData(h=>{fetch(`/api/sessions/${i}/teammate-pane-input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paneTarget:t.paneTarget,input:h})}).catch(u=>console.error("[TeammateTerminal] Failed to send input:",u))});const l=new ResizeObserver(()=>{requestAnimationFrame(()=>{try{o.fit()}catch{}})});l.observe(n);const c=this.teammateTerminals.get(e);c&&(c.terminal=r,c.fitAddon=o,c.resizeObserver=l)})},openTeammateTerminalWindow(e){if(!this.sessions.has(e.sessionId))return;const t=`pane-${e.paneTarget}`;if(this.subagentWindows.has(t)){const v=this.subagentWindows.get(t);v.hidden&&(v.element.style.display="flex",v.hidden=!1),v.element.style.zIndex=++this.subagentWindowZIndex,v.minimized&&this.restoreSubagentWindow(t);return}const s=this.subagentWindows.size,n=550,i=400,a=20,r=window.innerWidth,o=window.innerHeight,l=50,c=120,h=Math.floor((r-l-50)/(n+a))||1,u=Math.floor((o-c-50)/(i+a))||1,d=s%h,p=Math.floor(s/h)%u;let m=l+d*(n+a),g=c+p*(i+a);m=Math.max(10,Math.min(m,r-n-10)),g=Math.max(10,Math.min(g,o-i-10));const y=e.color||"blue",w=document.createElement("div");w.className="subagent-window has-terminal",w.id=`subagent-window-${t}`,w.style.zIndex=++this.subagentWindowZIndex,w.style.left=`${m}px`,w.style.top=`${g}px`,w.style.width=`${n}px`,w.style.height=`${i}px`,w.innerHTML=`
|
|
190
|
+
</div>`}return""},updateSubagentWindows(){for(const e of this.subagentWindows.keys())this.renderSubagentWindowContent(e),this.updateSubagentWindowHeader(e)},updateSubagentWindowHeader(e){const t=this.subagents.get(e);if(!t)return;const s=document.getElementById(`subagent-window-${e}`);if(!s)return;const n=s.querySelector(".subagent-window-title .id");if(n){const c=this.getTeammateInfo(t),h=c?c.name:t.description||e.substring(0,7),u=h.length>50?h.substring(0,50)+"...":h;n.textContent=u}let i=s.querySelector(".teammate-badge");const a=this.getTeammateInfo(t);if(a&&!i){const c=s.querySelector(".subagent-window-title");if(c){const h=document.createElement("span");h.className=`teammate-badge teammate-color-${a.color}`,h.title=`Team: ${a.teamName}`,h.textContent=`@${a.name}`;const u=c.querySelector(".status");u&&u.insertAdjacentElement("beforebegin",h)}}const r=s.querySelector(".subagent-window-title");r&&(r.title=t.description||e);let o=s.querySelector(".subagent-window-title .subagent-model-badge");if(t.modelShort){if(!o){o=document.createElement("span"),o.className=`subagent-model-badge ${t.modelShort}`;const c=s.querySelector(".subagent-window-title .status");c&&c.insertAdjacentElement("beforebegin",o)}o.className=`subagent-model-badge ${t.modelShort}`,o.textContent=t.modelShort}const l=s.querySelector(".subagent-window-title .status");l&&(l.className=`status ${t.status}`,l.textContent=t.status)},openAllActiveSubagentWindows(){for(const[e,t]of this.subagents)t.status==="active"&&!this.subagentWindows.has(e)&&this.openSubagentWindow(e)},initTeammateTerminal(e,t,s){const n=s.querySelector(".subagent-window-body");if(!n)return;n.innerHTML="",n.classList.add("teammate-terminal-body"),s.classList.add("has-terminal");const i=t.sessionId,a=[];this.teammateTerminals.set(e,{terminal:null,fitAddon:null,paneTarget:t.paneTarget,sessionId:i,resizeObserver:null,pendingData:a}),requestAnimationFrame(()=>{if(!document.contains(n)){this.teammateTerminals.delete(e);return}const r=new Terminal({theme:{...window.codemanCurrentXtermTheme()},minimumContrastRatio:window.codemanCurrentSkinIsLight()?4.5:1,fontFamily:window.CodemanTerminalFont.resolve(this.loadAppSettingsFromStorage?.().terminalFontFamily),fontSize:12,lineHeight:1.2,cursorBlink:!0,cursorStyle:"block",scrollback:DEFAULT_SCROLLBACK,allowTransparency:!0,allowProposedApi:!0}),o=new FitAddon.FitAddon;if(r.loadAddon(o),typeof Unicode11Addon<"u")try{const h=new Unicode11Addon.Unicode11Addon;r.loadAddon(h),r.unicode.activeVersion="11"}catch{}try{r.open(n)}catch(h){console.warn("[TeammateTerminal] Failed to open terminal:",h),this.teammateTerminals.delete(e);return}r.attachCustomKeyEventHandler(h=>h.type==="keydown"&&h.key.toLowerCase()==="z"&&h.ctrlKey&&!h.altKey&&!h.metaKey&&!h.shiftKey?(h.preventDefault(),!1):!0),setTimeout(()=>{try{o.fit()}catch{}fetch(`/api/sessions/${i}/teammate-pane-buffer/${encodeURIComponent(t.paneTarget)}`).then(h=>h.json()).then(h=>{if(h.success&&h.data?.buffer)try{r.write(h.data.buffer)}catch{}}).catch(h=>console.error("[TeammateTerminal] Failed to fetch buffer:",h));for(const h of a)try{r.write(h)}catch{}a.length=0},100),r.onData(h=>{fetch(`/api/sessions/${i}/teammate-pane-input`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({paneTarget:t.paneTarget,input:h})}).catch(u=>console.error("[TeammateTerminal] Failed to send input:",u))});const l=new ResizeObserver(()=>{requestAnimationFrame(()=>{try{o.fit()}catch{}})});l.observe(n);const c=this.teammateTerminals.get(e);c&&(c.terminal=r,c.fitAddon=o,c.resizeObserver=l)})},openTeammateTerminalWindow(e){if(!this.sessions.has(e.sessionId))return;const t=`pane-${e.paneTarget}`;if(this.subagentWindows.has(t)){const v=this.subagentWindows.get(t);v.hidden&&(v.element.style.display="flex",v.hidden=!1),v.element.style.zIndex=++this.subagentWindowZIndex,v.minimized&&this.restoreSubagentWindow(t);return}const s=this.subagentWindows.size,n=550,i=400,a=20,r=window.innerWidth,o=window.innerHeight,l=50,c=120,h=Math.floor((r-l-50)/(n+a))||1,u=Math.floor((o-c-50)/(i+a))||1,d=s%h,p=Math.floor(s/h)%u;let m=l+d*(n+a),g=c+p*(i+a);m=Math.max(10,Math.min(m,r-n-10)),g=Math.max(10,Math.min(g,o-i-10));const y=e.color||"blue",w=document.createElement("div");w.className="subagent-window has-terminal",w.id=`subagent-window-${t}`,w.style.zIndex=++this.subagentWindowZIndex,w.style.left=`${m}px`,w.style.top=`${g}px`,w.style.width=`${n}px`,w.style.height=`${i}px`,w.innerHTML=`
|
|
191
191
|
<div class="subagent-window-header">
|
|
192
192
|
<div class="subagent-window-title" title="Teammate terminal: ${escapeHtml(e.teammateName)} (pane ${e.paneTarget})">
|
|
193
193
|
<span class="icon" style="color: var(--team-color-${y}, #339af0)">\u2B24</span>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|