Security + upstream fixes
- Network core moves to a root-only folder; BroV talks to a narrow root helper (netctl.py) over a user-only socket; install script verifies the mihomo SHA256 and migrates keys (scripts/netcore) - Hardened runtime, no get-task-allow; bypassPermissions removed from the chat; concealed clipboard items are not restored; DangerCheck knows core, LaunchAgents and hook paths; dropped-file copies expire after 7 days - Ported from upstream Coucou: 1h crash fix (d05f22b), safe settings.json writes (918d30e), Escape/fold for pending approvals (6012900, 40e3ba8), auto-close delay + reopen (74984f2, ea244a7), full AskUserQuestion (52b1562) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ final class IslandWindowController: NSWindowController {
|
||||
private var frameTimer: Timer?
|
||||
private var keyMonitor: Any?
|
||||
private var viewSubscription: AnyCancellable?
|
||||
private var autoCloseSubscription: AnyCancellable?
|
||||
|
||||
// Confused recovery timer (set by handleDizzy)
|
||||
private var confusedRecoveryTimer: DispatchWorkItem?
|
||||
@@ -163,6 +164,11 @@ final class IslandWindowController: NSWindowController {
|
||||
// MARK: - FSM wiring
|
||||
|
||||
private func wireFSM() {
|
||||
// Apply the persisted auto-close preference immediately and keep live edits in sync.
|
||||
autoCloseSubscription = state.$autoCloseInterval.sink { [weak self] delay in
|
||||
self?.fsm.homeToPetitDelay = delay
|
||||
}
|
||||
|
||||
fsm.onTransition = { [weak self] from, to in
|
||||
guard let self else { return }
|
||||
switch to {
|
||||
@@ -387,15 +393,19 @@ final class IslandWindowController: NSWindowController {
|
||||
state.lastActivity = .now
|
||||
}
|
||||
|
||||
/// `byUser`: the ⌃ button or the toggle hotkey — folds a chat/terminal tab away too;
|
||||
/// only a pending approval still keeps the island open.
|
||||
func collapse(byUser: Bool = false) {
|
||||
if byUser {
|
||||
guard state.pendingApproval == nil else { return }
|
||||
} else {
|
||||
/// `byUser`: the ⌃ button or the toggle hotkey — folds a chat/terminal tab away too.
|
||||
/// `allowPendingApproval`: the notch's own Escape, jump-to-terminal — may fold the
|
||||
/// approval card (but not a chat/terminal tab).
|
||||
/// Folding a pending approval never answers it: the request stays pending, the island
|
||||
/// stays compact (held open) and a click or ⌃⌥A brings the card back.
|
||||
func collapse(byUser: Bool = false, allowPendingApproval: Bool = false) {
|
||||
let onTallTab = state.mode == .expanded && state.view.isTall
|
||||
let keepsApprovalPending = state.pendingApproval != nil
|
||||
&& (byUser || (allowPendingApproval && !onTallTab))
|
||||
if !byUser && !keepsApprovalPending {
|
||||
guard fsm.isHeldOpen?() != true else { return }
|
||||
}
|
||||
state.isPinned = false
|
||||
if !keepsApprovalPending { state.isPinned = false }
|
||||
finishedPinTimer?.cancel()
|
||||
// Keep the FSM in step with what is on screen (home/coucou → petit now).
|
||||
fsm.collapse()
|
||||
@@ -418,6 +428,7 @@ final class IslandWindowController: NSWindowController {
|
||||
collapse(byUser: true)
|
||||
} else {
|
||||
islandPanel.makeKey()
|
||||
fsm.openedExternally()
|
||||
expand(to: defaultView())
|
||||
}
|
||||
|
||||
@@ -428,6 +439,7 @@ final class IslandWindowController: NSWindowController {
|
||||
case .goToAlert:
|
||||
if state.pendingApproval != nil {
|
||||
islandPanel.makeKey()
|
||||
fsm.openedExternally()
|
||||
expand(to: .approval)
|
||||
} else if state.pendingQuestion != nil {
|
||||
islandPanel.makeKey()
|
||||
@@ -535,8 +547,9 @@ final class IslandWindowController: NSWindowController {
|
||||
// ⎋ Escape — focused views (.onExitCommand) have first crack; fall back to collapse
|
||||
if event.keyCode == 53 && raw.isEmpty {
|
||||
let consumed = NSApp.sendAction(Selector(("cancelOperation:")), to: nil, from: nil)
|
||||
if !consumed && state.mode == .expanded && !state.isPinned {
|
||||
collapse()
|
||||
let canCollapse = !state.isPinned || state.pendingApproval != nil
|
||||
if !consumed && state.mode == .expanded && canCollapse {
|
||||
collapse(allowPendingApproval: true)
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -590,7 +603,7 @@ final class IslandWindowController: NSWindowController {
|
||||
NSWorkspace.shared.open(
|
||||
URL(fileURLWithPath: "/System/Applications/Utilities/Terminal.app"))
|
||||
}
|
||||
collapse()
|
||||
collapse(allowPendingApproval: true)
|
||||
}
|
||||
|
||||
private func performAttachFrontWindow() {
|
||||
@@ -615,6 +628,8 @@ final class IslandWindowController: NSWindowController {
|
||||
Task { @MainActor in
|
||||
guard let self = self else { return }
|
||||
if event.keyCode == 53 { // Escape
|
||||
// Escape typed in another app (Claude Code's own interrupt, an editor…)
|
||||
// never folds a pending approval away: only Escape in the notch does.
|
||||
if self.state.mode == .expanded && !self.state.isPinned {
|
||||
self.collapse()
|
||||
}
|
||||
@@ -1187,6 +1202,10 @@ func islandSize(mode: IslandMode, view: IslandView,
|
||||
let layout = IslandConst.viewLayouts[view]!
|
||||
// BroV: the chat has its own width (Settings → Чат).
|
||||
if view.isTall { return (AppState.shared.chatWidth, layout.height) }
|
||||
// The question card grows to fit the full question and option descriptions.
|
||||
if view == .question, let h = QuestionLayout.height {
|
||||
return (IslandConst.expandedWidth, h)
|
||||
}
|
||||
return (IslandConst.expandedWidth, layout.height)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user