- The sidebar container had overflow-y-auto which caused the entire sidebar
to scroll when FILES panel content grew
- FILES panel already has proper flex-1 and internal overflow-auto handling
- Now only the file tree scrolls in place while SESSION and PROJECT panels
remain fixed as expected
- Fixes UX issue where entire sidebar would scroll vertically
- Add flex-1 to FilesPanel root div
- Sidebar is flex flex-col, FILES panel now grows to fill space
- SESSION and PROJECT panels remain natural height
- FILES panel expands into all remaining vertical space
This eliminates the gap below the FILES panel footer by making
the panel itself grow rather than just making the tree bigger.
- Remove max-h-80 constraint from FileTree
- FileTree uses flex-1 to expand and fill available space
- Panel now properly fills sidebar from top to bottom
- File tree scrolls internally when content overflows
The FILES panel header, file tree, and footer now form a proper
flex column that fills all remaining vertical space in the sidebar.
- Change FilesPanel overflow-hidden to overflow-auto for scrolling
- Add max-h-80 to FileTree to limit height while allowing scroll
- Add select-none to FileTreeNode to prevent text selection
- Cursor-pointer already present for clickable indication
Now the file tree scrolls independently within the FILES panel,
and text cannot be selected during rapid clicking.
- Replace recursive buildTree with buildVisibleNodes approach
- Build flat list of visible nodes based on expanded state
- Use processNode helper to traverse tree structure correctly
- Each node appears exactly once in the rendered output
- Eliminates duplication when expanding directories
- Change buildTree from .map() to for-loop to properly render children
- Append expanded directory children to nodes array immediately
- Fix TypeScript error by using ReactElement instead of JSX.Element
- Children now render correctly when directories are expanded
- Create comprehensive plan document for agent instructions text area
- Define requirements, acceptance criteria, and technical implementation
- Include UI/UX mockups and testing strategy
- Plan discovered during FILES panel implementation
- Addresses need for project-specific acceptance criteria
Add end-to-end abort support: AbortSignal in @gadget/ai providers,
abortWorkOrder socket message, drone AbortController handling,
Cancel button and double-Esc in frontend, and aborted turn status display.
The Home icon in the PROJECT panel now navigates to /projects/{slug}
instead of /projects/{id}. This opens Project Manager with the
project already selected and displayed.
Adds type definitions + forwarding for status, reconnect_attempt, reconnect_failed, reconnect events.
Frontend build now runs tsc --noEmit before vite build so undefined socket events cause failures.
Fixes pre-existing type errors exposed by strict mode in the frontend.
- Switch frontend sign-in to /api/v1/auth/sign-in endpoint (includes persona)
- Add updateUser() to App context for proper state management
- Fix Settings.tsx save flow to use updateUser() instead of broken localStorage merge
- Remove unused web AuthController (gadget-code/src/controllers/auth.ts)
- Fix UserApiControllerV1 to return flat user object instead of double-wrapped
- Remove SessionType enum and references (dead code)
- Add proper server sign-out call before clearing local state
Resolves issue where User Settings view didn't display persona text even though it existed in the database.
1. Vite config: make HTTPS conditional on SSL cert/key files existing
(pre-existing issue, broke builds when certs not present)
2. Drone reconnect handler: use socket.io Manager-level 'reconnect'
event (this.socket.io.on) instead of Socket-level event, and add
explicit type annotation for attemptNumber parameter
This commit addresses two interrelated issues causing drones to
de-register and users to be forcibly signed out:
## Heartbeat Timeout Fixes
1. Move heartbeat interval to a Web Worker (not subject to browser
tab throttling). Chrome throttles setInterval in background tabs
to ~1/min, which causes the 19s heartbeat to miss the drone's
timeout timer. The Web Worker fires reliably regardless of tab
visibility.
2. Add visibilitychange handler: when the tab becomes visible again,
send an immediate heartbeat to reset the drone's timer after any
throttling that may have occurred.
3. Fix onReleaseSessionLock to clear the heartbeat timer. Previously,
releasing the lock left the 60s timer running, causing a spurious
timeout and status emit after the lock was already released.
4. Increase drone heartbeat timeout from 60s to 120s. With the Web
Worker fix, heartbeats should be reliable, but doubling the timeout
provides a generous safety margin.
5. Add socket disconnect/reconnect handlers on the drone side. On
disconnect, clear the heartbeat timer. On reconnect, re-emit drone
status so the platform knows the drone is alive.
6. Configure Socket.IO pingInterval/pingTimeout explicitly (25s/60s)
instead of relying on defaults.
## JWT Expiration Fixes
1. Increase WebToken DB record expiration from 1 hour to 7 days. The
1-hour expiration was the real session lifetime gate (the JWT crypto
exp was already 24h), and it was far too aggressive for a dev tool.
2. Fix web /auth/renew-token endpoint to use req.user from the session
cookie instead of verifyJsonWebToken(req.body.token). This eliminates
the catch-22 where an expired token cannot be used to request its
own renewal.
3. Fix token refresh response parsing. The API v1 renew-token endpoint
returns { success: true, token } at the top level, but the frontend
was looking for json.data?.token, causing every refresh to fail.
4. Add proactive token refresh: check the JWT exp claim before each
request and refresh if expiring within 5 minutes. This avoids
unnecessary 401 errors and the resulting socket disconnections.
5. Update socket JWT on token renewal via a callback registered in
App.tsx. This ensures that future socket reconnections use the new
token instead of the expired one.
## Files Modified
- gadget-code/frontend/src/workers/heartbeat.worker.ts (NEW)
- gadget-code/frontend/src/lib/socket.ts
- gadget-code/frontend/src/lib/api.ts
- gadget-code/frontend/src/App.tsx
- gadget-code/src/services/session.ts
- gadget-code/src/controllers/auth.ts
- gadget-code/src/services/socket.ts
- gadget-drone/src/gadget-drone.ts
The Chat View area of the view was growing in width (unbounded), and has
been fixed. From the agent:
> The root cause was a flexbox sizing rule: in ChatSessionView.tsx:768
the parent row flex-1 flex bg-bg-primary overflow-hidden relative has
two children — the content area and the sidebar. The content area (line
777) was flex-1 flex flex-col relative, which as a flex child defaults
to min-width: auto. This means its intrinsic content width (driven by
the wide <pre>) is used as a minimum, forcing the flex item to expand
the whole row beyond the viewport.
>
> Adding min-w-0 overrides min-width: auto to min-width: 0, letting the
flex item shrink below its content's natural width. Now the
overflow-x-auto on the markdown wrapper actually has somewhere to scroll
into.
Replace the broken provider/model <select> elements (which sent empty model
on provider change, rejected by backend) with a cog-icon-driven edit flow:
- Default view shows current provider/model as text labels with a cog icon
- Clicking cog enters edit mode with <select> elements + checkmark/cancel
- Save atomically sends both provider and model; Save disabled until both set
- Cancel restores original values; whole view grays out during edit
Apply the same cog metaphor to the session Name field — inline text edit
with save/cancel, Enter to confirm, Escape to cancel. No global gray-out.
- JWT refresh logic to prevent dead sessions
- drone status messages now arrive in IDE for display
- WorkspaceService.deployProject method added to clone into a repo or
create the directory (new project not yet in git)
- Add isProcessingWorkOrder flag to track Agent work order processing
- Update onRequestWorkspaceMode with mode transition matrix validation
- Idle → User/Agent: Always allowed
- User → Agent: Always allowed (file editor checks for future)
- Agent → User: Only if !isProcessingWorkOrder
- All other transitions: Rejected with reason
- Extend RequestWorkspaceModeCallback with optional reason parameter
- Update frontend socket client to capture rejection reason
- Update handleWorkspaceModeChange to display rejection reason in toast
- Update WorkspaceModeIndicator to allow mode transitions per matrix
- Fix FilesPanel RW/RO indicator swap bug
- Document mode transition matrix and behavior in workspace-management.md