@slycode/slycode 0.2.13 → 0.2.14
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/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/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/c5d50c930f4e26a6.js +4 -0
- package/dist/web/.next/static/chunks/{24c966c8b9e12be5.js → c7a853519f3ebcb8.js} +1 -1
- package/dist/web/.next/static/chunks/{29b5d391d655a999.js → cfec56fdddd52361.js} +2 -2
- package/dist/web/.next/static/chunks/{02a9b2dcd9d8b87c.js → e52d73ad4544e983.js} +1 -1
- package/dist/web/src/components/ClaudeTerminalPanel.tsx +30 -1
- package/dist/web/src/components/Terminal.tsx +2 -2
- package/dist/web/tsconfig.tsbuildinfo +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 → y4wg5RLBkOtdMpGszS8Fs}/_buildManifest.js +0 -0
- /package/dist/web/.next/static/{5LMWBFPioBVfTpJ4F5OT6 → y4wg5RLBkOtdMpGszS8Fs}/_clientMiddlewareManifest.json +0 -0
- /package/dist/web/.next/static/{5LMWBFPioBVfTpJ4F5OT6 → y4wg5RLBkOtdMpGszS8Fs}/_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,8 @@ 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);
|
|
143
145
|
|
|
144
146
|
// Instruction file check state
|
|
145
147
|
const [instructionFileCheck, setInstructionFileCheck] = useState<{ needed: boolean; targetFile?: string; copySource?: string } | null>(null);
|
|
@@ -291,6 +293,7 @@ export function ClaudeTerminalPanel({
|
|
|
291
293
|
const startSession = async (command?: SlyActionItem | { prompt: string } | null, customPromptText?: string) => {
|
|
292
294
|
setIsStarting(true);
|
|
293
295
|
setShowCustomPrompt(false);
|
|
296
|
+
setExitToast(null);
|
|
294
297
|
try {
|
|
295
298
|
// Build prompt if action provided — context is opt-in via {{cardContext}} etc.
|
|
296
299
|
let prompt: string | undefined;
|
|
@@ -386,9 +389,12 @@ export function ClaudeTerminalPanel({
|
|
|
386
389
|
}
|
|
387
390
|
};
|
|
388
391
|
|
|
389
|
-
const handleSessionExit = () => {
|
|
392
|
+
const handleSessionExit = (code: number, output?: string) => {
|
|
390
393
|
setShowTerminal(false);
|
|
391
394
|
setIsConnected(false);
|
|
395
|
+
if (output && code !== 0) {
|
|
396
|
+
setExitToast({ code, output });
|
|
397
|
+
}
|
|
392
398
|
fetchSessionInfo();
|
|
393
399
|
};
|
|
394
400
|
|
|
@@ -753,6 +759,29 @@ export function ClaudeTerminalPanel({
|
|
|
753
759
|
</div>
|
|
754
760
|
)}
|
|
755
761
|
|
|
762
|
+
{/* Exit output toast — persists after terminal unmounts */}
|
|
763
|
+
{exitToast && (
|
|
764
|
+
<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">
|
|
765
|
+
<div className="flex items-start justify-between gap-2 px-3 py-2">
|
|
766
|
+
<div className="flex items-center gap-1.5 text-xs font-medium text-red-400">
|
|
767
|
+
<svg className="h-3.5 w-3.5 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
|
|
768
|
+
<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" />
|
|
769
|
+
</svg>
|
|
770
|
+
Session exited (code: {exitToast.code})
|
|
771
|
+
</div>
|
|
772
|
+
<button
|
|
773
|
+
onClick={() => setExitToast(null)}
|
|
774
|
+
className="text-void-500 hover:text-void-300 flex-shrink-0"
|
|
775
|
+
>
|
|
776
|
+
<svg className="h-3.5 w-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth="2">
|
|
777
|
+
<path strokeLinecap="round" strokeLinejoin="round" d="M6 18L18 6M6 6l12 12" />
|
|
778
|
+
</svg>
|
|
779
|
+
</button>
|
|
780
|
+
</div>
|
|
781
|
+
<pre className="max-h-32 overflow-auto px-3 pb-2 text-xs text-void-300 font-mono whitespace-pre-wrap">{exitToast.output}</pre>
|
|
782
|
+
</div>
|
|
783
|
+
)}
|
|
784
|
+
|
|
756
785
|
{/* Footer controls - only when running */}
|
|
757
786
|
{isRunning && (
|
|
758
787
|
<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
|
}
|