diff --git a/Sources/CodeIsland/L10n.swift b/Sources/CodeIsland/L10n.swift index 0d13158..9aec481 100644 --- a/Sources/CodeIsland/L10n.swift +++ b/Sources/CodeIsland/L10n.swift @@ -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 高度", diff --git a/Sources/CodeIsland/NotchPanelView.swift b/Sources/CodeIsland/NotchPanelView.swift index 22c6fc5..19a4fa0 100644 --- a/Sources/CodeIsland/NotchPanelView.swift +++ b/Sources/CodeIsland/NotchPanelView.swift @@ -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 @@ -46,11 +53,12 @@ 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 @@ -58,7 +66,7 @@ struct NotchPanelView: View { 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 @@ -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) @@ -89,7 +97,7 @@ struct NotchPanelView: View { IdleIndicatorBar( mascotSize: mascotSize, compactWingWidth: compactWingWidth, - notchW: notchW, + notchW: effectiveNotchW, notchHeight: notchHeight, hasNotch: hasNotch, hovered: idleHovered diff --git a/Sources/CodeIsland/Settings.swift b/Sources/CodeIsland/Settings.swift index abca6e8..301aa56 100644 --- a/Sources/CodeIsland/Settings.swift +++ b/Sources/CodeIsland/Settings.swift @@ -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") diff --git a/Tests/CodeIslandTests/NotchPanelViewTests.swift b/Tests/CodeIslandTests/NotchPanelViewTests.swift index 8e0b6d9..498b20d 100644 --- a/Tests/CodeIslandTests/NotchPanelViewTests.swift +++ b/Tests/CodeIslandTests/NotchPanelViewTests.swift @@ -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])) }