@pixelbyte-software/pixcode 1.48.2 → 1.48.4
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/assets/{index-BFCPlLdw.js → index--CoGCNtl.js} +160 -160
- package/dist/assets/index-ipxWr2Zo.css +32 -0
- package/dist/assets/vendor-xterm-C7tpxJl7.js +9 -0
- package/dist/index.html +3 -3
- package/dist-server/server/index.js +51 -8
- package/dist-server/server/index.js.map +1 -1
- package/package.json +1 -1
- package/scripts/smoke/pixcode-workbench-1-48.mjs +16 -1
- package/scripts/smoke/vscode-workbench-polish.mjs +115 -2
- package/server/index.js +59 -6
- package/dist/assets/index-Dr-4mhE7.css +0 -32
- package/dist/assets/vendor-xterm-CJZjLICi.js +0 -66
|
@@ -12,6 +12,10 @@ const chatComposer = read('src/components/chat/view/subcomponents/ChatComposer.t
|
|
|
12
12
|
const workerSlots = read('src/components/chat/view/subcomponents/WorkerSlotsControl.tsx');
|
|
13
13
|
const wizard = read('src/components/project-creation-wizard/ProjectCreationWizard.tsx');
|
|
14
14
|
const sidebar = read('src/components/sidebar/view/Sidebar.tsx');
|
|
15
|
+
const shellTerminal = read('src/components/shell/hooks/useShellTerminal.ts');
|
|
16
|
+
const gitPanel = read('src/components/git-panel/view/GitPanel.tsx');
|
|
17
|
+
const gitPanelHeader = read('src/components/git-panel/view/GitPanelHeader.tsx');
|
|
18
|
+
const gitViewTabs = read('src/components/git-panel/view/GitViewTabs.tsx');
|
|
15
19
|
|
|
16
20
|
assert.match(
|
|
17
21
|
workbench,
|
|
@@ -59,6 +63,51 @@ assert.match(
|
|
|
59
63
|
'Workbench should render Chrome-style workspace tabs directly under the menu bar.',
|
|
60
64
|
);
|
|
61
65
|
|
|
66
|
+
assert.doesNotMatch(
|
|
67
|
+
workbench,
|
|
68
|
+
/\.\.\.currentTabs\.filter\(\(tab\) => tab\.id !== tabId\)/,
|
|
69
|
+
'Selecting an existing workspace tab must preserve tab order instead of moving it to the end.',
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
assert.match(
|
|
73
|
+
workbench,
|
|
74
|
+
/function WorkspaceTabContextMenu/,
|
|
75
|
+
'Workspace tabs should expose their actions through a right-click context menu.',
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
assert.match(
|
|
79
|
+
workbench,
|
|
80
|
+
/onContextMenu=\{\(event\) => openWorkspaceContextMenu\(event, tab\)\}/,
|
|
81
|
+
'Workspace tab actions should open from right-click on the tab.',
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
const workspaceTabsSource = workbench.slice(
|
|
85
|
+
workbench.indexOf('function WorkbenchWorkspaceTabs'),
|
|
86
|
+
workbench.indexOf('function EditorTabContextMenu'),
|
|
87
|
+
);
|
|
88
|
+
|
|
89
|
+
assert.doesNotMatch(
|
|
90
|
+
workspaceTabsSource,
|
|
91
|
+
/MoreHorizontal/,
|
|
92
|
+
'Workspace tabs should not render a three-dot action button.',
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
for (const token of ['closeOtherWorkspaces', 'closeAllWorkspaces']) {
|
|
96
|
+
assert.match(workbench, new RegExp(token), `Workspace tab context menu should support ${token}.`);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
assert.match(
|
|
100
|
+
workspaceTabsSource,
|
|
101
|
+
/items-center justify-center/,
|
|
102
|
+
'Workspace add button should center its plus icon instead of rendering an off-center bare icon.',
|
|
103
|
+
);
|
|
104
|
+
|
|
105
|
+
assert.match(
|
|
106
|
+
workspaceTabsSource,
|
|
107
|
+
/border-r border-border/,
|
|
108
|
+
'Workspace add button should read as part of the tab strip instead of a floating bare button.',
|
|
109
|
+
);
|
|
110
|
+
|
|
62
111
|
assert.match(
|
|
63
112
|
workbench,
|
|
64
113
|
/WORKBENCH_WORKSPACE_TABS_STORAGE_KEY/,
|
|
@@ -113,8 +162,8 @@ assert.match(
|
|
|
113
162
|
|
|
114
163
|
assert.match(
|
|
115
164
|
workbench,
|
|
116
|
-
/onClose=\{\
|
|
117
|
-
'Right CLI terminal close button should return to the picker
|
|
165
|
+
/onClose=\{closeTerminal\}/,
|
|
166
|
+
'Right CLI terminal close button should return to the picker through the persisted close flow.',
|
|
118
167
|
);
|
|
119
168
|
|
|
120
169
|
assert.match(
|
|
@@ -123,6 +172,48 @@ assert.match(
|
|
|
123
172
|
'CLI history should be integrated as a polished project-scoped panel.',
|
|
124
173
|
);
|
|
125
174
|
|
|
175
|
+
assert.match(
|
|
176
|
+
workbench,
|
|
177
|
+
/terminalSession/,
|
|
178
|
+
'Right CLI panel should track whether the terminal is running a new session or a selected history session.',
|
|
179
|
+
);
|
|
180
|
+
|
|
181
|
+
assert.match(
|
|
182
|
+
workbench,
|
|
183
|
+
/WORKBENCH_CLI_STATE_STORAGE_KEY/,
|
|
184
|
+
'Right CLI panel should persist per-project terminal state when switching workspaces.',
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
assert.match(
|
|
188
|
+
workbench,
|
|
189
|
+
/openNewCliSessionPicker/,
|
|
190
|
+
'Right CLI panel toolbar plus should stop the current terminal view and return to CLI selection.',
|
|
191
|
+
);
|
|
192
|
+
|
|
193
|
+
assert.match(
|
|
194
|
+
workbench,
|
|
195
|
+
/terminateCurrentCliSession\(selectedProvider\)/,
|
|
196
|
+
'Right CLI panel toolbar plus should explicitly terminate the current provider PTY before showing the picker.',
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
assert.match(
|
|
200
|
+
workbench,
|
|
201
|
+
/forceNewSession=\{terminalLaunch\.forceNewSession\}/,
|
|
202
|
+
'Right CLI panel should mark explicitly started new sessions so the backend does not reconnect the old PTY.',
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
assert.match(
|
|
206
|
+
workbench,
|
|
207
|
+
/function WorkbenchCliPanelToolbar/,
|
|
208
|
+
'Right CLI terminal should keep compact History and New Session actions visible while the terminal is open.',
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
assert.match(
|
|
212
|
+
workbench,
|
|
213
|
+
/onNewSession=\{openNewCliSessionPicker\}/,
|
|
214
|
+
'Right CLI terminal toolbar should wire the plus button to the new-session picker flow.',
|
|
215
|
+
);
|
|
216
|
+
|
|
126
217
|
assert.doesNotMatch(
|
|
127
218
|
workbench,
|
|
128
219
|
/return <Sidebar \{\.\.\.sidebarProps\} isMobile=\{false\} \/>/,
|
|
@@ -170,6 +261,28 @@ assert.match(
|
|
|
170
261
|
'Projects activity should stay selected while the center chat tab is active.',
|
|
171
262
|
);
|
|
172
263
|
|
|
264
|
+
assert.match(
|
|
265
|
+
workbench,
|
|
266
|
+
/setActivityPanel\('explorer'\)/,
|
|
267
|
+
'Selecting a project from Projects should switch the left pane back to Explorer.',
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
assert.match(
|
|
271
|
+
workbench,
|
|
272
|
+
/<GitPanel selectedProject=\{selectedProject\} isMobile=\{false\} compact onFileOpen=\{handleFileOpen\}/,
|
|
273
|
+
'Source Control should render in compact icon-first mode inside the VS Code workbench side panel.',
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
assert.match(gitPanel, /compact = false/, 'GitPanel should accept a compact prop.');
|
|
277
|
+
assert.match(gitPanelHeader, /compact/, 'GitPanelHeader should receive compact mode for narrow panes.');
|
|
278
|
+
assert.match(gitViewTabs, /compact/, 'GitViewTabs should render compact icon-only tabs.');
|
|
279
|
+
|
|
280
|
+
assert.doesNotMatch(
|
|
281
|
+
shellTerminal,
|
|
282
|
+
/new WebglAddon\(\)/,
|
|
283
|
+
'Shell terminal should avoid the WebGL renderer that can leave stale glyph trails with OpenCode output.',
|
|
284
|
+
);
|
|
285
|
+
|
|
173
286
|
assert.match(chatInterface, /compactComposer\?: boolean/, 'ChatInterface should expose compactComposer for narrow workbench panes.');
|
|
174
287
|
assert.match(chatComposer, /compact\?: boolean/, 'ChatComposer should expose a compact prop.');
|
|
175
288
|
assert.match(chatComposer, /flex-wrap/, 'ChatComposer footer should wrap controls in narrow panes.');
|
package/server/index.js
CHANGED
|
@@ -285,8 +285,44 @@ const server = http.createServer(app);
|
|
|
285
285
|
const ptySessionsMap = new Map();
|
|
286
286
|
const PTY_SESSION_TIMEOUT = 30 * 60 * 1000;
|
|
287
287
|
const SHELL_URL_PARSE_BUFFER_LIMIT = 32768;
|
|
288
|
+
const SHELL_CLI_PROVIDERS = new Set(['claude', 'codex', 'cursor', 'gemini', 'qwen', 'opencode']);
|
|
288
289
|
import { stripAnsiSequences, normalizeDetectedUrl, extractUrlsFromText, shouldAutoOpenUrlFromOutput } from './utils/url-detection.js';
|
|
289
290
|
|
|
291
|
+
function terminatePtySession(sessionKey, session, reason) {
|
|
292
|
+
if (!session) return false;
|
|
293
|
+
|
|
294
|
+
console.log(`🧹 Terminating PTY session (${reason}):`, sessionKey);
|
|
295
|
+
if (session.timeoutId) {
|
|
296
|
+
clearTimeout(session.timeoutId);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
try {
|
|
300
|
+
if (session.pty && session.pty.kill) {
|
|
301
|
+
session.pty.kill();
|
|
302
|
+
}
|
|
303
|
+
} catch (error) {
|
|
304
|
+
console.warn('Failed to kill PTY session:', error.message);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
ptySessionsMap.delete(sessionKey);
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function killProviderPtySessions(projectPath, provider) {
|
|
312
|
+
let killed = 0;
|
|
313
|
+
for (const [sessionKey, session] of ptySessionsMap.entries()) {
|
|
314
|
+
if (
|
|
315
|
+
session?.projectPath === projectPath &&
|
|
316
|
+
session?.provider === provider &&
|
|
317
|
+
!session?.isPlainShell
|
|
318
|
+
) {
|
|
319
|
+
killed += terminatePtySession(sessionKey, session, 'fresh provider session') ? 1 : 0;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return killed;
|
|
324
|
+
}
|
|
325
|
+
|
|
290
326
|
// Single WebSocket server that handles both paths
|
|
291
327
|
const wss = new WebSocketServer({
|
|
292
328
|
server,
|
|
@@ -358,6 +394,18 @@ app.get('/health', (req, res) => {
|
|
|
358
394
|
// Optional API key validation (if configured)
|
|
359
395
|
app.use('/api', validateApiKey);
|
|
360
396
|
|
|
397
|
+
app.post('/api/shell/sessions/terminate', authenticateToken, (req, res) => {
|
|
398
|
+
const provider = req.body?.provider || 'claude';
|
|
399
|
+
const projectPath = req.body?.projectPath || os.homedir();
|
|
400
|
+
|
|
401
|
+
if (!SHELL_CLI_PROVIDERS.has(provider)) {
|
|
402
|
+
return res.status(400).json({ error: 'Unsupported provider' });
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
const killedSessions = killProviderPtySessions(projectPath, provider);
|
|
406
|
+
res.json({ success: true, killedSessions });
|
|
407
|
+
});
|
|
408
|
+
|
|
361
409
|
// Authentication routes (public)
|
|
362
410
|
app.use('/api/auth', authRoutes);
|
|
363
411
|
|
|
@@ -2044,6 +2092,7 @@ function handleShellConnection(ws) {
|
|
|
2044
2092
|
const provider = data.provider || 'claude';
|
|
2045
2093
|
const initialCommand = data.initialCommand;
|
|
2046
2094
|
const isPlainShell = data.isPlainShell || (!!initialCommand && !hasSession) || provider === 'plain-shell';
|
|
2095
|
+
const forceNewSession = Boolean(data.forceNewSession && !isPlainShell);
|
|
2047
2096
|
urlDetectionBuffer = '';
|
|
2048
2097
|
announcedAuthUrls.clear();
|
|
2049
2098
|
|
|
@@ -2077,14 +2126,16 @@ function handleShellConnection(ws) {
|
|
|
2077
2126
|
if (isLoginCommand) {
|
|
2078
2127
|
const oldSession = ptySessionsMap.get(ptySessionKey);
|
|
2079
2128
|
if (oldSession) {
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2129
|
+
terminatePtySession(ptySessionKey, oldSession, 'fresh login');
|
|
2130
|
+
}
|
|
2131
|
+
} else if (forceNewSession) {
|
|
2132
|
+
const killedSessions = killProviderPtySessions(projectPath, provider);
|
|
2133
|
+
if (killedSessions > 0) {
|
|
2134
|
+
console.log(`🧹 Fresh ${provider} session requested; terminated ${killedSessions} cached PTY session(s).`);
|
|
2084
2135
|
}
|
|
2085
2136
|
}
|
|
2086
2137
|
|
|
2087
|
-
const existingSession = isLoginCommand ? null : ptySessionsMap.get(ptySessionKey);
|
|
2138
|
+
const existingSession = (isLoginCommand || forceNewSession) ? null : ptySessionsMap.get(ptySessionKey);
|
|
2088
2139
|
if (existingSession) {
|
|
2089
2140
|
console.log('♻️ Reconnecting to existing PTY session:', ptySessionKey);
|
|
2090
2141
|
shellProcess = existingSession.pty;
|
|
@@ -2292,7 +2343,9 @@ function handleShellConnection(ws) {
|
|
|
2292
2343
|
buffer: [],
|
|
2293
2344
|
timeoutId: null,
|
|
2294
2345
|
projectPath,
|
|
2295
|
-
sessionId
|
|
2346
|
+
sessionId,
|
|
2347
|
+
provider,
|
|
2348
|
+
isPlainShell
|
|
2296
2349
|
});
|
|
2297
2350
|
|
|
2298
2351
|
// Handle data output
|