@slycode/slycode 0.2.13 → 0.2.15
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/bridge/claude-utils.d.ts +2 -3
- package/dist/bridge/claude-utils.js +28 -17
- package/dist/bridge/claude-utils.js.map +1 -1
- package/dist/bridge/pty-handler.js +40 -0
- package/dist/bridge/pty-handler.js.map +1 -1
- package/dist/bridge/session-manager.js +28 -3
- package/dist/bridge/session-manager.js.map +1 -1
- package/dist/bridge/types.d.ts +3 -0
- package/dist/scripts/kanban.js +26 -1
- package/dist/scripts/slycode-env-wrapper.sh +10 -1
- package/dist/web/.next/BUILD_ID +1 -1
- package/dist/web/.next/build-manifest.json +2 -2
- package/dist/web/.next/server/app/_global-error.html +2 -2
- package/dist/web/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/.next/server/app/_not-found.html +1 -1
- package/dist/web/.next/server/app/_not-found.rsc +2 -2
- package/dist/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/dist/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/dist/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/dist/web/.next/server/app/page/react-loadable-manifest.json +1 -1
- package/dist/web/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/.next/server/app/project/[id]/page/react-loadable-manifest.json +1 -1
- package/dist/web/.next/server/app/project/[id]/page_client-reference-manifest.js +1 -1
- package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__1f5fc489._.js +1 -1
- package/dist/web/.next/server/chunks/ssr/[root-of-the-server]__bcbe4bf2._.js +1 -1
- package/dist/web/.next/server/pages/404.html +1 -1
- package/dist/web/.next/server/pages/500.html +2 -2
- package/dist/web/.next/static/chunks/{4d5e1b83ce67acfa.js → 0a4b215957655f38.js} +1 -1
- package/dist/web/.next/static/chunks/2744d72103f49934.css +1 -0
- package/dist/web/.next/static/chunks/{29b5d391d655a999.js → 68a737843232927b.js} +2 -2
- package/dist/web/.next/static/chunks/7b456abf0afeeb60.js +4 -0
- package/dist/web/.next/static/chunks/{24c966c8b9e12be5.js → c7a853519f3ebcb8.js} +1 -1
- package/dist/web/.next/static/chunks/{02a9b2dcd9d8b87c.js → e52d73ad4544e983.js} +1 -1
- package/dist/web/src/components/ClaudeTerminalPanel.tsx +60 -1
- package/dist/web/src/components/Terminal.tsx +2 -2
- package/dist/web/tsconfig.tsbuildinfo +1 -1
- package/lib/cli/restart.d.ts.map +1 -1
- package/lib/cli/restart.js +5 -9
- package/lib/cli/restart.js.map +1 -1
- package/lib/cli/start.d.ts.map +1 -1
- package/lib/cli/start.js +38 -10
- package/lib/cli/start.js.map +1 -1
- package/lib/cli/update.js +1 -1
- package/lib/cli/update.js.map +1 -1
- package/lib/platform/service-common.d.ts +24 -0
- package/lib/platform/service-common.d.ts.map +1 -0
- package/lib/platform/service-common.js +119 -0
- package/lib/platform/service-common.js.map +1 -0
- package/lib/platform/service-detect.d.ts.map +1 -1
- package/lib/platform/service-detect.js +32 -20
- package/lib/platform/service-detect.js.map +1 -1
- package/lib/platform/service-linux.d.ts +1 -1
- package/lib/platform/service-linux.d.ts.map +1 -1
- package/lib/platform/service-linux.js +10 -77
- package/lib/platform/service-linux.js.map +1 -1
- package/lib/platform/service-macos.d.ts +1 -1
- package/lib/platform/service-macos.d.ts.map +1 -1
- package/lib/platform/service-macos.js +176 -49
- package/lib/platform/service-macos.js.map +1 -1
- package/package.json +1 -1
- package/templates/kanban-seed.json +1 -1
- package/dist/web/.next/static/chunks/17bf7ad67057dc74.js +0 -4
- package/dist/web/.next/static/chunks/3df3846316317676.css +0 -1
- /package/dist/web/.next/static/{5LMWBFPioBVfTpJ4F5OT6 → du1bb9CKBc_rQF_UArbIP}/_buildManifest.js +0 -0
- /package/dist/web/.next/static/{5LMWBFPioBVfTpJ4F5OT6 → du1bb9CKBc_rQF_UArbIP}/_clientMiddlewareManifest.json +0 -0
- /package/dist/web/.next/static/{5LMWBFPioBVfTpJ4F5OT6 → du1bb9CKBc_rQF_UArbIP}/_ssgManifest.js +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,33525,(e,t,n)=>{"use strict";Object.defineProperty(n,"__esModule",{value:!0}),Object.defineProperty(n,"warnOnce",{enumerable:!0,get:function(){return s}});let s=e=>{}},87365,e=>{"use strict";let t="slycode:terminal-prompt";function n(e,s=!0){let i=new CustomEvent(t,{detail:{prompt:e,autoSubmit:s}});window.dispatchEvent(i)}function s(e){let n=t=>{e(t.detail)};return window.addEventListener(t,n),()=>window.removeEventListener(t,n)}e.s(["onTerminalPrompt",()=>s,"pushToTerminal",()=>n])},21146,e=>{"use strict";function t(...e){"1"===localStorage.getItem("cm-debug")&&console.log(`[CM ${new Date().toISOString().slice(11,23)}]`,...e)}class n{connections=new Map;statusListeners=new Set;_status="connected";connectionIdCounter=0;isPageVisible=!0;visibilityChangeTime=null;healthCheckInterval=null;lastHealthCheckSuccess=null;consecutiveHealthFailures=0;statusDowngradeTimeout=null;constructor(){this.setupVisibilityListener(),this.setupNetworkListener(),this.setupHealthCheck()}setupVisibilityListener(){document.addEventListener("visibilitychange",()=>{let e=this.isPageVisible;this.isPageVisible="visible"===document.visibilityState,!e&&this.isPageVisible?(this.visibilityChangeTime=Date.now(),this.handleTabWake()):e&&!this.isPageVisible&&(this.visibilityChangeTime=Date.now())})}setupNetworkListener(){window.addEventListener("online",()=>{this.doHealthCheck()}),window.addEventListener("offline",()=>{this.updateStatus("disconnected")})}setupHealthCheck(){this.healthCheckInterval=setInterval(()=>{if(this.isPageVisible&&0!==this.connections.size){if(this.hasRecentlyActiveConnection()){this.consecutiveHealthFailures=0,"connected"!==this._status&&this.reconnectBroken();return}this.doHealthCheck()}},2e4)}hasRecentlyActiveConnection(){let e=Date.now();for(let t of this.connections.values())if(t.eventSource?.readyState===EventSource.OPEN&&t.lastConnected&&e-t.lastConnected<6e4)return!0;return!1}async doHealthCheck(){t("HEALTH_CHECK — fetching /api/bridge/health");try{let e=new AbortController,n=setTimeout(()=>e.abort(),3e3),s=await fetch("/api/bridge/health",{method:"GET",signal:e.signal});clearTimeout(n),s.ok?(t("HEALTH_CHECK — OK"),this.consecutiveHealthFailures=0,this.lastHealthCheckSuccess=Date.now(),"connected"!==this._status&&this.reconnectBroken()):(t(`HEALTH_CHECK — FAIL status=${s.status}`),this.handleHealthCheckFailure())}catch(e){t(`HEALTH_CHECK — ERROR`,e),this.handleHealthCheckFailure()}}handleHealthCheckFailure(){this.consecutiveHealthFailures++,t(`HEALTH_FAIL — consecutive=${this.consecutiveHealthFailures}`),this.consecutiveHealthFailures>=2&&"connected"===this._status&&this.updateStatus("disconnected")}handleTabWake(){t("TAB_WAKE — force-reconnecting all connections"),this.reconnectAll(!0)}calculateBackoff(e){let t=Math.min(1e3*Math.pow(2,e),3e4),n=.2*t*(2*Math.random()-1);return Math.round(t+n)}createManagedEventSource(e,t){let n=`conn-${++this.connectionIdCounter}`;return this.connections.set(n,{url:e,handlers:t,eventSource:null,retryCount:0,retryTimeout:null,lastConnected:null,id:n}),this.connect(n),n}connect(e){let n=this.connections.get(e);if(n){n.eventSource&&(n.eventSource.close(),n.eventSource=null),n.retryTimeout&&(clearTimeout(n.retryTimeout),n.retryTimeout=null);try{let s=new EventSource(n.url);n.eventSource=s,s.onopen=()=>{n.retryCount=0,n.lastConnected=Date.now(),this.consecutiveHealthFailures=0,t(`OPEN ${e} → ${n.url}`),this.updateOverallStatus(),n.handlers.onOpen?.()},s.onerror=i=>{t(`ERROR ${e} → ${n.url} readyState=${s.readyState} (0=CONNECTING, 1=OPEN, 2=CLOSED)`,i),n.handlers.onError?.(i),s.readyState===EventSource.CLOSED&&(t(`CLOSED ${e} — scheduling reconnect`),this.scheduleReconnect(e))},s.onmessage=e=>{n.lastConnected=Date.now(),n.handlers.onMessage?.(e)},s.addEventListener("heartbeat",()=>{n.lastConnected=Date.now(),t(`HEARTBEAT ${e} → ${n.url}`)}),Object.entries(n.handlers).forEach(([e,t])=>{"onOpen"!==e&&"onError"!==e&&"onMessage"!==e&&t&&s.addEventListener(e,e=>{n.lastConnected=Date.now(),t(e)})})}catch(t){console.error(`ConnectionManager: Failed to create EventSource for ${n.url}`,t),this.scheduleReconnect(e)}}}scheduleReconnect(e){let t=this.connections.get(e);if(!t)return;t.retryTimeout&&clearTimeout(t.retryTimeout);let n=this.calculateBackoff(t.retryCount);t.retryCount++,this.updateOverallStatus(),t.retryTimeout=setTimeout(()=>{t.retryTimeout=null,this.connect(e)},n)}reconnect(e,t=!1){let n=this.connections.get(e);n&&(t?(n.retryCount=0,this.updateStatus("reconnecting"),this.connect(e)):this.scheduleReconnect(e))}reconnectAll(e=!1){this.connections.size>0&&this.updateStatus("reconnecting"),this.connections.forEach((t,n)=>{this.reconnect(n,e)})}reconnectBroken(){let e=!1;this.connections.forEach((t,n)=>{t.eventSource?.readyState!==EventSource.OPEN&&(e=!0,this.reconnect(n,!0))}),e||this.updateOverallStatus()}closeConnection(e){let t=this.connections.get(e);t&&(t.retryTimeout&&clearTimeout(t.retryTimeout),t.eventSource&&t.eventSource.close(),this.connections.delete(e),this.updateOverallStatus())}updateOverallStatus(){if(0===this.connections.size){this.updateStatus("connected"),this.clearDowngradeTimeout();return}let e=0,t=0;this.connections.forEach(n=>{n.eventSource?.readyState===EventSource.OPEN?e++:(n.retryTimeout||n.eventSource?.readyState===EventSource.CONNECTING)&&t++}),e>0?(this.updateStatus("connected"),this.clearDowngradeTimeout()):t>0?this.scheduleDowngrade("reconnecting"):this.scheduleDowngrade("disconnected")}scheduleDowngrade(e){"connected"!==this._status?this.updateStatus(e):this.statusDowngradeTimeout||(this.statusDowngradeTimeout=setTimeout(()=>{this.statusDowngradeTimeout=null,this.updateOverallStatus()},3e3))}clearDowngradeTimeout(){this.statusDowngradeTimeout&&(clearTimeout(this.statusDowngradeTimeout),this.statusDowngradeTimeout=null)}updateStatus(e){if(this._status!==e){let n=Array.from(this.connections.values()).map(e=>({id:e.id,url:e.url.replace(/.*\/api\//,"/api/"),readyState:e.eventSource?.readyState,lastConnectedAge:e.lastConnected?Math.round((Date.now()-e.lastConnected)/1e3)+"s":"never",hasRetryPending:!!e.retryTimeout}));t(`STATUS ${this._status} → ${e}`,JSON.stringify(n,null,2)),this._status=e,this.statusListeners.forEach(t=>t(e))}}subscribe(e){return this.statusListeners.add(e),e(this._status),()=>{this.statusListeners.delete(e)}}get status(){return this._status}get pageVisible(){return this.isPageVisible}get connectionCount(){return this.connections.size}}let s=new n;e.s(["connectionManager",0,s])},54423,e=>{e.v(t=>Promise.all(["static/chunks/54d5670f5fa2abbe.css","static/chunks/
|
|
1
|
+
(globalThis.TURBOPACK||(globalThis.TURBOPACK=[])).push(["object"==typeof document?document.currentScript:void 0,33525,(e,t,n)=>{"use strict";Object.defineProperty(n,"__esModule",{value:!0}),Object.defineProperty(n,"warnOnce",{enumerable:!0,get:function(){return s}});let s=e=>{}},87365,e=>{"use strict";let t="slycode:terminal-prompt";function n(e,s=!0){let i=new CustomEvent(t,{detail:{prompt:e,autoSubmit:s}});window.dispatchEvent(i)}function s(e){let n=t=>{e(t.detail)};return window.addEventListener(t,n),()=>window.removeEventListener(t,n)}e.s(["onTerminalPrompt",()=>s,"pushToTerminal",()=>n])},21146,e=>{"use strict";function t(...e){"1"===localStorage.getItem("cm-debug")&&console.log(`[CM ${new Date().toISOString().slice(11,23)}]`,...e)}class n{connections=new Map;statusListeners=new Set;_status="connected";connectionIdCounter=0;isPageVisible=!0;visibilityChangeTime=null;healthCheckInterval=null;lastHealthCheckSuccess=null;consecutiveHealthFailures=0;statusDowngradeTimeout=null;constructor(){this.setupVisibilityListener(),this.setupNetworkListener(),this.setupHealthCheck()}setupVisibilityListener(){document.addEventListener("visibilitychange",()=>{let e=this.isPageVisible;this.isPageVisible="visible"===document.visibilityState,!e&&this.isPageVisible?(this.visibilityChangeTime=Date.now(),this.handleTabWake()):e&&!this.isPageVisible&&(this.visibilityChangeTime=Date.now())})}setupNetworkListener(){window.addEventListener("online",()=>{this.doHealthCheck()}),window.addEventListener("offline",()=>{this.updateStatus("disconnected")})}setupHealthCheck(){this.healthCheckInterval=setInterval(()=>{if(this.isPageVisible&&0!==this.connections.size){if(this.hasRecentlyActiveConnection()){this.consecutiveHealthFailures=0,"connected"!==this._status&&this.reconnectBroken();return}this.doHealthCheck()}},2e4)}hasRecentlyActiveConnection(){let e=Date.now();for(let t of this.connections.values())if(t.eventSource?.readyState===EventSource.OPEN&&t.lastConnected&&e-t.lastConnected<6e4)return!0;return!1}async doHealthCheck(){t("HEALTH_CHECK — fetching /api/bridge/health");try{let e=new AbortController,n=setTimeout(()=>e.abort(),3e3),s=await fetch("/api/bridge/health",{method:"GET",signal:e.signal});clearTimeout(n),s.ok?(t("HEALTH_CHECK — OK"),this.consecutiveHealthFailures=0,this.lastHealthCheckSuccess=Date.now(),"connected"!==this._status&&this.reconnectBroken()):(t(`HEALTH_CHECK — FAIL status=${s.status}`),this.handleHealthCheckFailure())}catch(e){t(`HEALTH_CHECK — ERROR`,e),this.handleHealthCheckFailure()}}handleHealthCheckFailure(){this.consecutiveHealthFailures++,t(`HEALTH_FAIL — consecutive=${this.consecutiveHealthFailures}`),this.consecutiveHealthFailures>=2&&"connected"===this._status&&this.updateStatus("disconnected")}handleTabWake(){t("TAB_WAKE — force-reconnecting all connections"),this.reconnectAll(!0)}calculateBackoff(e){let t=Math.min(1e3*Math.pow(2,e),3e4),n=.2*t*(2*Math.random()-1);return Math.round(t+n)}createManagedEventSource(e,t){let n=`conn-${++this.connectionIdCounter}`;return this.connections.set(n,{url:e,handlers:t,eventSource:null,retryCount:0,retryTimeout:null,lastConnected:null,id:n}),this.connect(n),n}connect(e){let n=this.connections.get(e);if(n){n.eventSource&&(n.eventSource.close(),n.eventSource=null),n.retryTimeout&&(clearTimeout(n.retryTimeout),n.retryTimeout=null);try{let s=new EventSource(n.url);n.eventSource=s,s.onopen=()=>{n.retryCount=0,n.lastConnected=Date.now(),this.consecutiveHealthFailures=0,t(`OPEN ${e} → ${n.url}`),this.updateOverallStatus(),n.handlers.onOpen?.()},s.onerror=i=>{t(`ERROR ${e} → ${n.url} readyState=${s.readyState} (0=CONNECTING, 1=OPEN, 2=CLOSED)`,i),n.handlers.onError?.(i),s.readyState===EventSource.CLOSED&&(t(`CLOSED ${e} — scheduling reconnect`),this.scheduleReconnect(e))},s.onmessage=e=>{n.lastConnected=Date.now(),n.handlers.onMessage?.(e)},s.addEventListener("heartbeat",()=>{n.lastConnected=Date.now(),t(`HEARTBEAT ${e} → ${n.url}`)}),Object.entries(n.handlers).forEach(([e,t])=>{"onOpen"!==e&&"onError"!==e&&"onMessage"!==e&&t&&s.addEventListener(e,e=>{n.lastConnected=Date.now(),t(e)})})}catch(t){console.error(`ConnectionManager: Failed to create EventSource for ${n.url}`,t),this.scheduleReconnect(e)}}}scheduleReconnect(e){let t=this.connections.get(e);if(!t)return;t.retryTimeout&&clearTimeout(t.retryTimeout);let n=this.calculateBackoff(t.retryCount);t.retryCount++,this.updateOverallStatus(),t.retryTimeout=setTimeout(()=>{t.retryTimeout=null,this.connect(e)},n)}reconnect(e,t=!1){let n=this.connections.get(e);n&&(t?(n.retryCount=0,this.updateStatus("reconnecting"),this.connect(e)):this.scheduleReconnect(e))}reconnectAll(e=!1){this.connections.size>0&&this.updateStatus("reconnecting"),this.connections.forEach((t,n)=>{this.reconnect(n,e)})}reconnectBroken(){let e=!1;this.connections.forEach((t,n)=>{t.eventSource?.readyState!==EventSource.OPEN&&(e=!0,this.reconnect(n,!0))}),e||this.updateOverallStatus()}closeConnection(e){let t=this.connections.get(e);t&&(t.retryTimeout&&clearTimeout(t.retryTimeout),t.eventSource&&t.eventSource.close(),this.connections.delete(e),this.updateOverallStatus())}updateOverallStatus(){if(0===this.connections.size){this.updateStatus("connected"),this.clearDowngradeTimeout();return}let e=0,t=0;this.connections.forEach(n=>{n.eventSource?.readyState===EventSource.OPEN?e++:(n.retryTimeout||n.eventSource?.readyState===EventSource.CONNECTING)&&t++}),e>0?(this.updateStatus("connected"),this.clearDowngradeTimeout()):t>0?this.scheduleDowngrade("reconnecting"):this.scheduleDowngrade("disconnected")}scheduleDowngrade(e){"connected"!==this._status?this.updateStatus(e):this.statusDowngradeTimeout||(this.statusDowngradeTimeout=setTimeout(()=>{this.statusDowngradeTimeout=null,this.updateOverallStatus()},3e3))}clearDowngradeTimeout(){this.statusDowngradeTimeout&&(clearTimeout(this.statusDowngradeTimeout),this.statusDowngradeTimeout=null)}updateStatus(e){if(this._status!==e){let n=Array.from(this.connections.values()).map(e=>({id:e.id,url:e.url.replace(/.*\/api\//,"/api/"),readyState:e.eventSource?.readyState,lastConnectedAge:e.lastConnected?Math.round((Date.now()-e.lastConnected)/1e3)+"s":"never",hasRetryPending:!!e.retryTimeout}));t(`STATUS ${this._status} → ${e}`,JSON.stringify(n,null,2)),this._status=e,this.statusListeners.forEach(t=>t(e))}}subscribe(e){return this.statusListeners.add(e),e(this._status),()=>{this.statusListeners.delete(e)}}get status(){return this._status}get pageVisible(){return this.isPageVisible}get connectionCount(){return this.connections.size}}let s=new n;e.s(["connectionManager",0,s])},54423,e=>{e.v(t=>Promise.all(["static/chunks/54d5670f5fa2abbe.css","static/chunks/0a4b215957655f38.js"].map(t=>e.l(t))).then(()=>t(81485)))},90178,e=>{e.v(e=>Promise.resolve().then(()=>e(87365)))}]);
|
|
@@ -140,6 +140,10 @@ export function ClaudeTerminalPanel({
|
|
|
140
140
|
const [screenshotToast, setScreenshotToast] = useState<{ filename: string; previewUrl: string; status: 'uploading' | 'done' | 'error'; message?: string } | null>(null);
|
|
141
141
|
const screenshotToastTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
142
142
|
const imagePasteInProgressRef = useRef(false);
|
|
143
|
+
// Exit output toast state (persists across terminal unmount)
|
|
144
|
+
const [exitToast, setExitToast] = useState<{ code: number; output: string } | null>(null);
|
|
145
|
+
// Spawn error toast — shown when session creation fails (e.g. posix_spawnp failed)
|
|
146
|
+
const [spawnError, setSpawnError] = useState<string | null>(null);
|
|
143
147
|
|
|
144
148
|
// Instruction file check state
|
|
145
149
|
const [instructionFileCheck, setInstructionFileCheck] = useState<{ needed: boolean; targetFile?: string; copySource?: string } | null>(null);
|
|
@@ -291,6 +295,8 @@ export function ClaudeTerminalPanel({
|
|
|
291
295
|
const startSession = async (command?: SlyActionItem | { prompt: string } | null, customPromptText?: string) => {
|
|
292
296
|
setIsStarting(true);
|
|
293
297
|
setShowCustomPrompt(false);
|
|
298
|
+
setExitToast(null);
|
|
299
|
+
setSpawnError(null);
|
|
294
300
|
try {
|
|
295
301
|
// Build prompt if action provided — context is opt-in via {{cardContext}} etc.
|
|
296
302
|
let prompt: string | undefined;
|
|
@@ -325,9 +331,13 @@ export function ClaudeTerminalPanel({
|
|
|
325
331
|
if (instructionFileCheck?.needed && createInstructionFile) {
|
|
326
332
|
setInstructionFileCheck(null);
|
|
327
333
|
}
|
|
334
|
+
} else {
|
|
335
|
+
const body = await res.json().catch(() => ({ error: 'Unknown error' }));
|
|
336
|
+
setSpawnError(body.error || `Failed to start session (HTTP ${res.status})`);
|
|
328
337
|
}
|
|
329
338
|
} catch (err) {
|
|
330
339
|
console.error('Failed to start session:', err);
|
|
340
|
+
setSpawnError('Could not reach the bridge server');
|
|
331
341
|
} finally {
|
|
332
342
|
setIsStarting(false);
|
|
333
343
|
setCustomPrompt('');
|
|
@@ -386,9 +396,12 @@ export function ClaudeTerminalPanel({
|
|
|
386
396
|
}
|
|
387
397
|
};
|
|
388
398
|
|
|
389
|
-
const handleSessionExit = () => {
|
|
399
|
+
const handleSessionExit = (code: number, output?: string) => {
|
|
390
400
|
setShowTerminal(false);
|
|
391
401
|
setIsConnected(false);
|
|
402
|
+
if (output && code !== 0) {
|
|
403
|
+
setExitToast({ code, output });
|
|
404
|
+
}
|
|
392
405
|
fetchSessionInfo();
|
|
393
406
|
};
|
|
394
407
|
|
|
@@ -753,6 +766,52 @@ export function ClaudeTerminalPanel({
|
|
|
753
766
|
</div>
|
|
754
767
|
)}
|
|
755
768
|
|
|
769
|
+
{/* Exit output toast — persists after terminal unmounts */}
|
|
770
|
+
{exitToast && (
|
|
771
|
+
<div className="absolute bottom-3 right-3 left-3 z-50 rounded-lg border border-red-500/30 bg-void-800/95 shadow-(--shadow-overlay) backdrop-blur-sm">
|
|
772
|
+
<div className="flex items-start justify-between gap-2 px-3 py-2">
|
|
773
|
+
<div className="flex items-center gap-1.5 text-xs font-medium text-red-400">
|
|
774
|
+
<svg className="h-3.5 w-3.5 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
|
|
775
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
|
776
|
+
</svg>
|
|
777
|
+
Session exited (code: {exitToast.code})
|
|
778
|
+
</div>
|
|
779
|
+
<button
|
|
780
|
+
onClick={() => setExitToast(null)}
|
|
781
|
+
className="text-void-500 hover:text-void-300 flex-shrink-0"
|
|
782
|
+
>
|
|
783
|
+
<svg className="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
|
|
784
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
785
|
+
</svg>
|
|
786
|
+
</button>
|
|
787
|
+
</div>
|
|
788
|
+
<pre className="max-h-32 overflow-auto px-3 pb-2 text-xs text-void-300 font-mono whitespace-pre-wrap">{exitToast.output}</pre>
|
|
789
|
+
</div>
|
|
790
|
+
)}
|
|
791
|
+
|
|
792
|
+
{/* Spawn error toast — shown when session creation fails entirely */}
|
|
793
|
+
{spawnError && (
|
|
794
|
+
<div className="absolute bottom-3 right-3 left-3 z-50 rounded-lg border border-red-500/30 bg-void-800/95 shadow-(--shadow-overlay) backdrop-blur-sm">
|
|
795
|
+
<div className="flex items-start justify-between gap-2 px-3 py-2">
|
|
796
|
+
<div className="flex items-center gap-1.5 text-xs font-medium text-red-400">
|
|
797
|
+
<svg className="h-3.5 w-3.5 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
|
|
798
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z" />
|
|
799
|
+
</svg>
|
|
800
|
+
Failed to start session
|
|
801
|
+
</div>
|
|
802
|
+
<button
|
|
803
|
+
onClick={() => setSpawnError(null)}
|
|
804
|
+
className="text-void-500 hover:text-void-300 flex-shrink-0"
|
|
805
|
+
>
|
|
806
|
+
<svg className="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
|
|
807
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
808
|
+
</svg>
|
|
809
|
+
</button>
|
|
810
|
+
</div>
|
|
811
|
+
<pre className="max-h-32 overflow-auto px-3 pb-2 text-xs text-void-300 font-mono whitespace-pre-wrap">{spawnError}</pre>
|
|
812
|
+
</div>
|
|
813
|
+
)}
|
|
814
|
+
|
|
756
815
|
{/* Footer controls - only when running */}
|
|
757
816
|
{isRunning && (
|
|
758
817
|
<div className={`flex flex-shrink-0 items-center gap-2 px-3 py-2 ${footerClassName || 'border-t border-void-700 bg-void-800'}`}>
|
|
@@ -17,7 +17,7 @@ interface TerminalProps {
|
|
|
17
17
|
bridgeUrl?: string;
|
|
18
18
|
tintColor?: string;
|
|
19
19
|
onConnectionChange?: (connected: boolean) => void;
|
|
20
|
-
onSessionExit?: (code: number) => void;
|
|
20
|
+
onSessionExit?: (code: number, output?: string) => void;
|
|
21
21
|
onReady?: (handle: TerminalHandle) => void;
|
|
22
22
|
onImagePaste?: (file: File) => void;
|
|
23
23
|
}
|
|
@@ -375,7 +375,7 @@ export const Terminal = forwardRef<TerminalHandle, TerminalProps>(function Termi
|
|
|
375
375
|
try {
|
|
376
376
|
const msg = JSON.parse(event.data);
|
|
377
377
|
terminal.write(`\r\n\x1b[33mSession exited (code: ${msg.code})\x1b[0m\r\n`);
|
|
378
|
-
onSessionExitRef.current?.(msg.code);
|
|
378
|
+
onSessionExitRef.current?.(msg.code, msg.output);
|
|
379
379
|
} catch {
|
|
380
380
|
terminal.write('\r\n\x1b[33mSession exited\x1b[0m\r\n');
|
|
381
381
|
}
|