From 7201f96909b3c6fdb791233c82bd818a938a1bfc Mon Sep 17 00:00:00 2001 From: Sugukaga Date: Wed, 21 Oct 2026 16:20:23 +0900 Subject: [PATCH] Add opt-in toggle to allow custom runner frames up to 300px wide Keep the default 10...100px width range for custom runner frames, and add an "Allow wide frames (up to 300px)" toggle to the custom runner editor. When switched on, a warning explains that frames wider than 100px may be hidden behind the camera notch on MacBooks while the app keeps running. The toggle is off by default and applies only to the runner being created. This lets classic runners such as Metal Cluster Cat (298x36) be revived as custom runners without changing the behavior for anyone who does not opt in. New strings are provided in English and Japanese only. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_019XxvkQY8mttsmifV5LhyeQ --- .../Model/Stores/CustomRunnerEditor.swift | 13 ++++++- .../Resources/Localizable.xcstrings | 34 ++++++++++++++++++ .../CustomRunnerEditorView.swift | 9 +++++ .../ModelTests/Resources/solid_red_200x36.png | Bin 0 -> 179 bytes .../StoreTests/CustomRunnerEditorTests.swift | 20 +++++++++++ 5 files changed, 75 insertions(+), 1 deletion(-) create mode 100644 LocalPackage/Tests/ModelTests/Resources/solid_red_200x36.png diff --git a/LocalPackage/Sources/Model/Stores/CustomRunnerEditor.swift b/LocalPackage/Sources/Model/Stores/CustomRunnerEditor.swift index 9fe74f4..80cd3da 100644 --- a/LocalPackage/Sources/Model/Stores/CustomRunnerEditor.swift +++ b/LocalPackage/Sources/Model/Stores/CustomRunnerEditor.swift @@ -35,6 +35,7 @@ public final class CustomRunnerEditor: Composable, Identifiable { public let id: UUID public var runnerName: String public var isTemplate: Bool + public var allowsWideFrames: Bool public var frameImages: [FrameImage] public var selectingFrameImage: FrameImage? public var previewingFrameImage: FrameImage? @@ -53,6 +54,7 @@ public final class CustomRunnerEditor: Composable, Identifiable { id: UUID, runnerName: String = "", isTemplate: Bool = true, + allowsWideFrames: Bool = false, frameImages: [FrameImage] = [], selectingFrameImage: FrameImage? = nil, previewingFrameImage: FrameImage? = nil, @@ -69,6 +71,7 @@ public final class CustomRunnerEditor: Composable, Identifiable { self.id = id self.runnerName = runnerName self.isTemplate = isTemplate + self.allowsWideFrames = allowsWideFrames self.frameImages = frameImages self.selectingFrameImage = selectingFrameImage self.previewingFrameImage = previewingFrameImage @@ -98,6 +101,9 @@ public final class CustomRunnerEditor: Composable, Identifiable { case let .renderingModePickerSelected(renderingMode): isTemplate = renderingMode.isTemplate + case let .allowWideFramesToggleSwitched(allowsWideFrames): + self.allowsWideFrames = allowsWideFrames + case let .frameImageCellTapped(frameImage): selectingFrameImage = frameImage @@ -200,11 +206,15 @@ public final class CustomRunnerEditor: Composable, Identifiable { } } + private var maxFrameWidth: Int { + allowsWideFrames ? 300 : 100 + } + private func appendFrameImage(from url: URL) throws { guard let nsImage = NSImage(contentsOf: url), let cgImage = nsImage.cgImage(forProposedRect: nil, context: nil, hints: nil), cgImage.height == 36, - (10 ... 100).contains(cgImage.width) else { + (10 ... maxFrameWidth).contains(cgImage.width) else { throw RCNError.customRunner(.invalidFrameImage) } guard frameImages.count < 30 else { @@ -217,6 +227,7 @@ public final class CustomRunnerEditor: Composable, Identifiable { case viewAppeared case viewDisappeared case renderingModePickerSelected(RenderingMode) + case allowWideFramesToggleSwitched(Bool) case frameImageCellTapped(FrameImage) case collectionBackgroundTapped case filesDropped([URL]) diff --git a/LocalPackage/Sources/UserInterface/Resources/Localizable.xcstrings b/LocalPackage/Sources/UserInterface/Resources/Localizable.xcstrings index 1fc44e9..e6ae171 100644 --- a/LocalPackage/Sources/UserInterface/Resources/Localizable.xcstrings +++ b/LocalPackage/Sources/UserInterface/Resources/Localizable.xcstrings @@ -397,6 +397,23 @@ } } }, + "allowWideFrames" : { + "comment" : "A label for a toggle that allows custom runner frames wider than 100px.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Allow wide frames (up to 300px)" + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "幅広フレームを許可(最大300px)" + } + } + } + }, "appearance" : { "comment" : "A heading for the appearance settings.", "isCommentAutoGenerated" : true, @@ -5742,6 +5759,23 @@ } } } + }, + "wideFramesWarning" : { + "comment" : "A warning shown when wide custom runner frames are allowed.", + "localizations" : { + "en" : { + "stringUnit" : { + "state" : "translated", + "value" : "Frames wider than 100px may be hidden behind the camera notch on MacBooks. RunCat Neo keeps running, but the runner can look as if it has stopped." + } + }, + "ja" : { + "stringUnit" : { + "state" : "translated", + "value" : "幅が100pxを超えるフレームは、MacBookのカメラノッチに隠れて表示されないことがあります。RunCat Neoは動作し続けますが、ランナーが止まったように見えることがあります。" + } + } + } } }, "version" : "1.2" diff --git a/LocalPackage/Sources/UserInterface/Views/Settings/RunnerSettings/CustomRunnerEditorView.swift b/LocalPackage/Sources/UserInterface/Views/Settings/RunnerSettings/CustomRunnerEditorView.swift index f454f8f..dacf819 100644 --- a/LocalPackage/Sources/UserInterface/Views/Settings/RunnerSettings/CustomRunnerEditorView.swift +++ b/LocalPackage/Sources/UserInterface/Views/Settings/RunnerSettings/CustomRunnerEditorView.swift @@ -43,6 +43,15 @@ struct CustomRunnerEditorView: View { } label: { Text("renderingMode", bundle: .module) } + Toggle(isOn: Binding( + get: { store.allowsWideFrames }, + asyncSet: { await store.send(.allowWideFramesToggleSwitched($0)) } + )) { + Text("allowWideFrames", bundle: .module) + if store.allowsWideFrames { + Text("wideFramesWarning", bundle: .module) + } + } LabeledContent { FrameImagesCollectionView(store: store) } label: { diff --git a/LocalPackage/Tests/ModelTests/Resources/solid_red_200x36.png b/LocalPackage/Tests/ModelTests/Resources/solid_red_200x36.png new file mode 100644 index 0000000000000000000000000000000000000000..53cddb2f40510848991daa3f241402d9cb4660f0 GIT binary patch literal 179 zcmeAS@N?(olHy`uVBq!ia0vp^CxBRmgAGWYxmTJFq^dk!978JRyuG|p@PLBAfeq@v z-}6kDvuO<3_5GG{b;IiS^BU(nDoC(2QJHx5mSKA%lbg$tkX1leFnGH9xvX