44 */
55import { getUuid , setUuid , apiBind , apiFetchSessions , apiFetchAllSessions , apiFetchEnvironments , apiFetchSession , apiFetchSessionHistory , apiSendEvent , apiSendControl , apiInterrupt , apiCreateSession } from "./api.js" ;
66import { connectSSE , disconnectSSE } from "./sse.js" ;
7- import { appendEvent , renderPermissionRequest , showLoading , isLoading } from "./render.js" ;
7+ import { appendEvent , renderPermissionRequest , showLoading , isLoading , resetReplayState , renderReplayPendingRequests } from "./render.js" ;
88import { esc , formatTime , statusClass } from "./utils.js" ;
99
1010// ============================================================
@@ -70,7 +70,7 @@ async function handleRoute() {
7070 } catch ( err ) {
7171 console . error ( "Failed to bind session:" , err ) ;
7272 alert ( "Session not found or bind failed: " + err . message ) ;
73- history . replaceState ( null , "" , "/code" ) ;
73+ history . replaceState ( null , "" , "/code/ " ) ;
7474 }
7575 }
7676
@@ -170,14 +170,15 @@ async function renderSessionDetail(id) {
170170 badge . className = `status-badge status-${ statusClass ( session . status ) } ` ;
171171 } catch ( err ) {
172172 alert ( "Failed to load session: " + err . message ) ;
173- navigate ( "/code" ) ;
173+ navigate ( "/code/ " ) ;
174174 return ;
175175 }
176176 document . getElementById ( "event-stream" ) . innerHTML = "" ;
177177 document . getElementById ( "permission-area" ) . innerHTML = "" ;
178178 document . getElementById ( "permission-area" ) . classList . add ( "hidden" ) ;
179179
180180 // Load historical events before connecting to live stream
181+ resetReplayState ( ) ;
181182 let lastSeqNum = 0 ;
182183 try {
183184 const { events } = await apiFetchSessionHistory ( id ) ;
@@ -190,6 +191,8 @@ async function renderSessionDetail(id) {
190191 } catch ( err ) {
191192 console . warn ( "Failed to load session history:" , err ) ;
192193 }
194+ // Re-render any still-unresolved permission prompts from history
195+ renderReplayPendingRequests ( ) ;
193196
194197 connectSSE ( id , appendEvent , lastSeqNum ) ;
195198}
@@ -365,12 +368,83 @@ window._submitAnswers = async function (requestId, btn) {
365368} ;
366369
367370function removePermissionPrompt ( btn ) {
368- const prompt = btn . closest ( ".permission-prompt, .ask-panel" ) ;
371+ const prompt = btn . closest ( ".permission-prompt, .ask-panel, .plan-panel " ) ;
369372 if ( prompt ) prompt . remove ( ) ;
370373 const area = document . getElementById ( "permission-area" ) ;
371374 if ( area && area . children . length === 0 ) area . classList . add ( "hidden" ) ;
372375}
373376
377+ // ============================================================
378+ // ExitPlanMode interactions
379+ // ============================================================
380+
381+ window . _selectPlanOption = function ( btn , value ) {
382+ const panel = btn . closest ( ".plan-panel" ) ;
383+ if ( ! panel ) return ;
384+
385+ // Deselect all siblings
386+ panel . querySelectorAll ( ".plan-option" ) . forEach ( ( o ) => o . classList . remove ( "selected" ) ) ;
387+ btn . classList . add ( "selected" ) ;
388+ panel . _selectedValue = value ;
389+
390+ // Show/hide feedback textarea
391+ const feedbackArea = panel . querySelector ( ".plan-feedback-area" ) ;
392+ if ( feedbackArea ) {
393+ feedbackArea . classList . toggle ( "visible" , value === "no" ) ;
394+ }
395+ } ;
396+
397+ window . _submitPlanResponse = async function ( requestId , btn ) {
398+ const panel = btn . closest ( ".plan-panel" ) ;
399+ if ( ! panel ) return ;
400+
401+ const selectedValue = panel . _selectedValue ;
402+ if ( ! selectedValue ) {
403+ alert ( "Please select an option first." ) ;
404+ return ;
405+ }
406+
407+ btn . disabled = true ;
408+
409+ try {
410+ if ( selectedValue === "no" ) {
411+ // Rejection with optional feedback
412+ const feedbackInput = panel . querySelector ( ".plan-feedback-input" ) ;
413+ const feedback = feedbackInput ? feedbackInput . value . trim ( ) : "" ;
414+ await apiSendControl ( currentSessionId , {
415+ type : "permission_response" ,
416+ approved : false ,
417+ request_id : requestId ,
418+ ...( feedback ? { message : feedback } : { } ) ,
419+ } ) ;
420+ removePermissionPrompt ( btn ) ;
421+ } else {
422+ // Approval with permission mode
423+ const modeMap = {
424+ "yes-accept-edits" : "acceptEdits" ,
425+ "yes-default" : "default" ,
426+ } ;
427+ const mode = modeMap [ selectedValue ] || "default" ;
428+ const planContent = panel . _planContent || "" ;
429+
430+ await apiSendControl ( currentSessionId , {
431+ type : "permission_response" ,
432+ approved : true ,
433+ request_id : requestId ,
434+ ...( planContent ? { updated_input : { plan : planContent } } : { } ) ,
435+ updated_permissions : [
436+ { type : "setMode" , mode, destination : "session" } ,
437+ ] ,
438+ } ) ;
439+ removePermissionPrompt ( btn ) ;
440+ showLoading ( ) ;
441+ }
442+ } catch ( err ) {
443+ alert ( "Failed to submit: " + err . message ) ;
444+ btn . disabled = false ;
445+ }
446+ } ;
447+
374448// ============================================================
375449// New Session Dialog
376450// ============================================================
@@ -486,7 +560,7 @@ function setupIdentityPanel() {
486560 if ( importedUuid ) {
487561 setUuid ( importedUuid ) ;
488562 panel . classList . add ( "hidden" ) ;
489- navigate ( "/code" ) ;
563+ navigate ( "/code/ " ) ;
490564 renderDashboard ( ) ;
491565 return ;
492566 }
@@ -495,7 +569,7 @@ function setupIdentityPanel() {
495569 if ( code . data . length >= 32 ) {
496570 setUuid ( code . data ) ;
497571 panel . classList . add ( "hidden" ) ;
498- navigate ( "/code" ) ;
572+ navigate ( "/code/ " ) ;
499573 renderDashboard ( ) ;
500574 return ;
501575 }
0 commit comments