Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Sources/CodeIsland/L10n.swift
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ final class L10n: ObservableObject {
"max_visible_sessions": "最大显示会话数",
"max_visible_sessions_desc": "超出数量的会话将通过滚动查看",
"collapsed_width_scale": "灵动岛宽度",
"collapsed_width_scale_desc": "调整非刘海屏幕上灵动岛的收起宽度",
"collapsed_width_scale_desc": "调整灵动岛的收起宽度",
"notch_height_mode": "顶部高度对齐",
"notch_height_mode_desc": "让面板与真实 notch 高度、菜单栏高度或自定义值对齐",
"notch_height_match_notch": "对齐 notch 高度",
Expand Down
22 changes: 15 additions & 7 deletions Sources/CodeIsland/NotchPanelView.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import SwiftUI
import CodeIslandCore

enum NotchWidthMetrics {
static func effectiveNotchWidth(notchW: CGFloat, collapsedWidthScale: Int) -> CGFloat {
let clampedScale = max(50, min(collapsedWidthScale, 150))
return notchW * CGFloat(clampedScale) / 100.0
}
}

struct NotchPanelView: View {
var appState: AppState
let hasNotch: Bool
Expand Down Expand Up @@ -46,19 +53,20 @@ struct NotchPanelView: View {
/// Minimum wing width needed to display compact bar content
private var compactWingWidth: CGFloat { mascotSize + 14 }

/// Effective notch width — applies user scale on non-notch screens (#56).
/// Effective island width — applies user scale on both notch and non-notch screens.
private var effectiveNotchW: CGFloat {
guard !hasNotch else { return notchW }
let scale = CGFloat(max(collapsedWidthScale, 50)) / 100.0
return notchW * scale
NotchWidthMetrics.effectiveNotchWidth(
notchW: notchW,
collapsedWidthScale: collapsedWidthScale
)
}

/// Total panel width — adapts based on state and screen geometry
private var panelWidth: CGFloat {
let nw = effectiveNotchW
let maxWidth = min(620, screenWidth - 40)
if showIdleIndicator { return idleHovered ? nw + compactWingWidth * 2 + 80 : nw + compactWingWidth * 2 }
if !isActive { return hasNotch ? notchW - 20 : nw }
if !isActive { return hasNotch ? nw - 20 : nw }
if shouldShowExpanded { return min(max(nw + 200, 580), maxWidth) }
let wing = compactWingWidth
let extra: CGFloat = appState.status == .idle ? 0 : 20
Expand All @@ -75,7 +83,7 @@ struct NotchPanelView: View {
HStack(spacing: 0) {
CompactLeftWing(appState: appState, expanded: shouldShowExpanded, mascotSize: mascotSize, hasNotch: hasNotch, showToolStatus: showToolStatus)
if hasNotch && !shouldShowExpanded {
Spacer(minLength: notchW)
Spacer(minLength: effectiveNotchW)
} else if !shouldShowExpanded && showToolStatus {
CompactToolStatus(appState: appState)
Spacer(minLength: 0)
Expand All @@ -89,7 +97,7 @@ struct NotchPanelView: View {
IdleIndicatorBar(
mascotSize: mascotSize,
compactWingWidth: compactWingWidth,
notchW: notchW,
notchW: effectiveNotchW,
notchHeight: notchHeight,
hasNotch: hasNotch,
hovered: idleHovered
Expand Down
2 changes: 1 addition & 1 deletion Sources/CodeIsland/Settings.swift
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ enum SettingsKey {
// Tool status display
static let showToolStatus = "showToolStatus" // true = detailed, false = simple

// Island collapsed width scale for non-notch screens (percentage: 50–150, default 100)
// Island collapsed width scale (percentage: 50–150, default 100)
static let collapsedWidthScale = "collapsedWidthScale"

// Default mascot source when no sessions exist (falls back to this instead of always "claude")
Expand Down
26 changes: 26 additions & 0 deletions Tests/CodeIslandTests/NotchPanelViewTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,32 @@ import XCTest
@testable import CodeIsland

final class NotchPanelViewTests: XCTestCase {
func testEffectiveNotchWidthAppliesCollapsedWidthScale() {
XCTAssertEqual(
NotchWidthMetrics.effectiveNotchWidth(notchW: 200, collapsedWidthScale: 50),
100,
accuracy: 0.001
)
XCTAssertEqual(
NotchWidthMetrics.effectiveNotchWidth(notchW: 200, collapsedWidthScale: 150),
300,
accuracy: 0.001
)
}

func testEffectiveNotchWidthClampsOutOfRangeScale() {
XCTAssertEqual(
NotchWidthMetrics.effectiveNotchWidth(notchW: 200, collapsedWidthScale: 10),
100,
accuracy: 0.001
)
XCTAssertEqual(
NotchWidthMetrics.effectiveNotchWidth(notchW: 200, collapsedWidthScale: 250),
300,
accuracy: 0.001
)
}

func testShouldTriggerJumpFailureFeedbackWhenAllAttemptsFail() {
XCTAssertTrue(shouldTriggerJumpFailureFeedback([false, false, false]))
}
Expand Down