Chat: drag handle with detents (fit, 1/3, 1/2, full screen), chat width setting; tall transparent panel

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
maksarsanjeev
2026-10-06 22:19:27 +03:00
parent fbf90fd4fb
commit a4d5032959
5 changed files with 119 additions and 16 deletions
@@ -56,9 +56,11 @@ final class IslandWindowController: NSWindowController {
let nW = geometry.width
let nH = geometry.height
let panelW: CGFloat = 720
let panelH: CGFloat = 320
// Tall, transparent panel: the chat can grow down to the full screen height
// and widen; clicks outside the island still pass through (pollFrame).
let sf = screen.frame
let panelW: CGFloat = 1100
let panelH: CGFloat = sf.height
let panel = IslandPanel(
contentRect: NSRect(x: sf.midX - panelW/2, y: sf.maxY - panelH,
width: panelW, height: panelH),
@@ -232,10 +234,12 @@ final class IslandWindowController: NSWindowController {
// in the app window immediately below the menu bar.
let hoverRect = !hasNotch && state.mode != .expanded
? islandRect : islandRect.insetBy(dx: -6, dy: -6)
let inIsland = hoverRect.contains(local)
// While the chat handle is dragged, count the pointer as inside (no collapse).
let inIsland = hoverRect.contains(local) || state.chatDragHeight != nil
// Toggle click-through
let shouldAcceptMouse = inIsland || inAttachDrag || attachDragStart != nil
|| state.chatDragHeight != nil
if panel.ignoresMouseEvents == shouldAcceptMouse {
panel.ignoresMouseEvents = !shouldAcceptMouse
if shouldAcceptMouse, let cv = panel.contentView {
@@ -1015,12 +1019,10 @@ final class IslandWindowController: NSWindowController {
let panelW = window?.frame.width ?? 720
let (islandW, fixedH) = islandSize(mode: s.mode, view: s.view,
progress: s.uploadProgress, nw: notchW, nh: notchH)
// Chat view resizes dynamically — must match IslandContainer.chatPromptHeight
// Chat view resizes dynamically — AppState.chatIslandHeight
let islandH: CGFloat
if s.mode == .expanded && s.view == .prompt {
let base: CGFloat = 240
let perMsg: CGFloat = 40
islandH = min(300, base + CGFloat(s.chatHistory.count) * perMsg)
islandH = s.chatIslandHeight
} else {
islandH = fixedH
}
@@ -1083,9 +1085,7 @@ final class IslandPanel: NSPanel {
progress: s.uploadProgress, nw: nw, nh: nh)
let h: CGFloat
if s.mode == .expanded && s.view == .prompt {
let base: CGFloat = 240
let perMsg: CGFloat = 40
h = min(300, base + CGFloat(s.chatHistory.count) * perMsg)
h = s.chatIslandHeight
} else {
h = fixedH
}
@@ -1140,6 +1140,7 @@ extension Notification.Name {
// MARK: - islandSize (takes real notch dimensions)
@MainActor
func islandSize(mode: IslandMode, view: IslandView,
progress: Double = 0,
nw: CGFloat = IslandConst.notchWidth,
@@ -1149,6 +1150,8 @@ func islandSize(mode: IslandMode, view: IslandView,
case .compact: return (nw + 160, nh)
case .expanded:
let layout = IslandConst.viewLayouts[view]!
// BroV: the chat has its own width (Settings → Чат).
if view == .prompt { return (AppState.shared.chatWidth, layout.height) }
return (IslandConst.expandedWidth, layout.height)
}
}