| 1 | package main |
| 2 | |
| 3 | func (a *App) setTabReadOnly(tabID string, readOnly bool) { |
| 4 | a.setTabReadOnlyWithTerminalPolicy(tabID, readOnly, false) |
| 5 | } |
| 6 | |
| 7 | // setTabReadOnlyPreservingTerminals revokes the tab's session writer without |
| 8 | // treating that session handoff as a workspace-terminal capability change. A |
| 9 | // CLI running in the integrated terminal must survive so it can switch to a |
| 10 | // different session after the current one is reclaimed. |
| 11 | func (a *App) setTabReadOnlyPreservingTerminals(tabID string, readOnly bool) { |
| 12 | a.setTabReadOnlyWithTerminalPolicy(tabID, readOnly, true) |
| 13 | } |
| 14 | |
| 15 | func (a *App) setTabReadOnlyWithTerminalPolicy(tabID string, readOnly, preserveTerminals bool) { |
| 16 | var terminalSessions []*terminalSession |
| 17 | a.mu.Lock() |
| 18 | tab := a.tabs[tabID] |
| 19 | if tab == nil || (tab.ReadOnly == readOnly && (readOnly || !tab.Takeover.Spectator)) { |
| 20 | a.mu.Unlock() |
| 21 | return |
| 22 | } |
| 23 | if a.terminals != nil { |
| 24 | if readOnly { |
| 25 | if !preserveTerminals { |
| 26 | // Close the creation gate and detach existing sessions before |
| 27 | // exposing the tab as read-only. The process I/O cleanup happens |
| 28 | // after App.mu is released. |
| 29 | terminalSessions = a.terminals.detachForTab(tabID) |
| 30 | } |
| 31 | } else { |
| 32 | // Reopen the terminal gate before exposing the tab as writable. A |
| 33 | // concurrent create must never observe writable App state while |
| 34 | // the terminal manager still treats this tab as closed. |
| 35 | a.terminals.reopenForTab(tabID) |
| 36 | } |
| 37 | } |
| 38 | tab.ReadOnly = readOnly |
| 39 | if !readOnly { |
| 40 | tab.Takeover.Spectator = false |
| 41 | } |
| 42 | a.saveTabsLocked() |
| 43 | a.mu.Unlock() |
| 44 | if len(terminalSessions) > 0 { |
| 45 | // Existing shells can keep modifying the workspace without renderer |
| 46 | // input, so entering a read-only channel must terminate them as part of |
| 47 | // the same capability transition. |
| 48 | a.terminals.closeSessions(terminalSessions) |
| 49 | } |
| 50 | } |
| 51 | |
| 52 | // terminalReadOnlyForTab reports whether the tab's terminals must be locked. |
| 53 | // A takeover spectator is read-only for the session writer only: its shells |
| 54 | // keep running so the user can switch the CLI to another session. |
| 55 | func terminalReadOnlyForTab(tab *WorkspaceTab) bool { |
| 56 | return tab != nil && tab.ReadOnly && !tab.Takeover.Spectator |
| 57 | } |
| 58 |