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
4 changes: 4 additions & 0 deletions boringNotch.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
5917FD112E57891600E87F1C /* MediaKeyInterceptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5917FD102E57891600E87F1C /* MediaKeyInterceptor.swift */; };
5955950D2E900ED800C66711 /* ApplicationRelauncher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5955950C2E900ED800C66711 /* ApplicationRelauncher.swift */; };
59D8C23C2E589FAA00147B33 /* VolumeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 59D8C23B2E589FAA00147B33 /* VolumeManager.swift */; };
64FA50FB2F4D6F9E00008A28 /* WebcamSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64FA50FA2F4D6F9E00008A28 /* WebcamSettingsView.swift */; };
9A0887322C7A693000C160EA /* TabButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0887312C7A693000C160EA /* TabButton.swift */; };
9A0887352C7AFF8E00C160EA /* TabSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A0887342C7AFF8E00C160EA /* TabSelectionView.swift */; };
9A987A0D2C73CA66005CA465 /* ShelfView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9A987A032C73CA66005CA465 /* ShelfView.swift */; };
Expand Down Expand Up @@ -308,6 +309,7 @@
5917FD102E57891600E87F1C /* MediaKeyInterceptor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaKeyInterceptor.swift; sourceTree = "<group>"; };
5955950C2E900ED800C66711 /* ApplicationRelauncher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApplicationRelauncher.swift; sourceTree = "<group>"; };
59D8C23B2E589FAA00147B33 /* VolumeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumeManager.swift; sourceTree = "<group>"; };
64FA50FA2F4D6F9E00008A28 /* WebcamSettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebcamSettingsView.swift; sourceTree = "<group>"; };
9A0887312C7A693000C160EA /* TabButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabButton.swift; sourceTree = "<group>"; };
9A0887342C7AFF8E00C160EA /* TabSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabSelectionView.swift; sourceTree = "<group>"; };
9A987A032C73CA66005CA465 /* ShelfView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ShelfView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -530,6 +532,7 @@
11DB266F2EDD0CDF001EA0CF /* SettingsHelpers.swift */,
11DB26702EDD0CDF001EA0CF /* ShelfSettingsView.swift */,
11DB26712EDD0CDF001EA0CF /* ShortcutsSettingsView.swift */,
64FA50FA2F4D6F9E00008A28 /* WebcamSettingsView.swift */,
);
path = Views;
sourceTree = "<group>";
Expand Down Expand Up @@ -1069,6 +1072,7 @@
11C5E3132DFE85970065821E /* SettingsWindowController.swift in Sources */,
110029272E84FD4C00035A57 /* TemporaryFileStorageService.swift in Sources */,
11CFC6652E09C7B300748C80 /* OnboardingFinishView.swift in Sources */,
64FA50FB2F4D6F9E00008A28 /* WebcamSettingsView.swift in Sources */,
507266DB2C908E2E00A2D00D /* HoverButton.swift in Sources */,
1471A8592C6281BD0058408D /* BoringNotchWindow.swift in Sources */,
14CEF4182C5CAED300855D72 /* ContentView.swift in Sources */,
Expand Down
10 changes: 9 additions & 1 deletion boringNotch/Localizable.xcstrings
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an autogenerated file and should not be updated (by you or by AI). Discard the changes in this file and rebuild the app to allow Xcode to generate these keys.

Original file line number Diff line number Diff line change
Expand Up @@ -7616,6 +7616,10 @@
}
}
},
"Enable toggle to flip webcam" : {
"comment" : "A label for a toggle that enables or disables the option to flip the webcam.",
"isCommentAutoGenerated" : true
},
"Enable window shadow" : {
"localizations" : {
"ar" : {
Expand Down Expand Up @@ -20234,6 +20238,10 @@
},
"Visibility" : {

},
"Webcam" : {
"comment" : "A label displayed above a button that allows the user to configure webcam settings.",
"isCommentAutoGenerated" : true
},
"Welcome" : {
"localizations" : {
Expand Down Expand Up @@ -20936,5 +20944,5 @@
}
}
},
"version" : "1.0"
"version" : "1.1"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the Strings Catalog format version and shouldn't be modified.

}
5 changes: 5 additions & 0 deletions boringNotch/components/Settings/SettingsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ struct SettingsView: View {
NavigationLink(value: "Shelf") {
Label("Shelf", systemImage: "books.vertical")
}
NavigationLink(value: "Webcam") {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rename to Mirror to match the feature name

Label("Webcam", systemImage: "camera")
}
NavigationLink(value: "Shortcuts") {
Label("Shortcuts", systemImage: "keyboard")
}
Expand Down Expand Up @@ -74,6 +77,8 @@ struct SettingsView: View {
Charge()
case "Shelf":
Shelf()
case "Webcam":
WebcamSettings()
case "Shortcuts":
Shortcuts()
case "Advanced":
Expand Down
26 changes: 26 additions & 0 deletions boringNotch/components/Settings/Views/WebcamSettingsView.swift
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move all mirror settings here. I think its the setting to enable the mirror and the mirror shape.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// WebcamSettings.swift
// boringNotch
//
// Created by Anmol Malhotra on 2026-02-24.
//

import SwiftUI
import Defaults

struct WebcamSettings: View {

@Default(.enableFlipWebcamToggle) private var enableFlipWebcamToggle

var body: some View {
Form {
Defaults.Toggle(key: .enableFlipWebcamToggle) {
Text("Enable toggle to flip webcam")
}
}
.formStyle(.grouped)
.frame(maxWidth: .infinity, maxHeight: .infinity)
.padding()
.navigationTitle("Webcam")
}
}
32 changes: 26 additions & 6 deletions boringNotch/components/Webcam/WebcamView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,36 @@ struct CameraPreviewView: View {

// Track if authorization request is in progress to avoid multiple requests
@State private var isRequestingAuthorization: Bool = false

// Track the current state of mirror effect and the mirror icon in camera preview
@Default(.isMirrored) private var isMirrored
@Default(.enableFlipWebcamToggle) private var enableFlipWebcamToggle
Copy link
Member

@Alexander5015 Alexander5015 Mar 1, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am open to discussion about this, but I don't think we need a toggle button overlay at all. My suggestion was that rather than having a visible toggle button, we just make this a setting in settings as I don't think this will be frequently changed.


var body: some View {
GeometryReader { geometry in
ZStack {
if let previewLayer = webcamManager.previewLayer {
CameraPreviewLayerView(previewLayer: previewLayer)
.scaleEffect(x: -1, y: 1)
.clipShape(RoundedRectangle(cornerRadius: Defaults[.mirrorShape] == .rectangle ? MusicPlayerImageSizes.cornerRadiusInset.opened : 100))
.frame(width: geometry.size.width, height: geometry.size.width)
.opacity(webcamManager.isSessionRunning ? 1 : 0)
ZStack(alignment: .bottomTrailing) {
CameraPreviewLayerView(previewLayer: previewLayer)
.scaleEffect(x: isMirrored ? -1 : 1, y: 1)
.clipShape(RoundedRectangle(cornerRadius: Defaults[.mirrorShape] == .rectangle ? MusicPlayerImageSizes.cornerRadiusInset.opened : 100))
.frame(width: geometry.size.width, height: geometry.size.width)
.opacity(webcamManager.isSessionRunning ? 1 : 0)

// The mirror toggle button should only be visible if the webcam session is running and the setting to enable it is turned on
if enableFlipWebcamToggle && webcamManager.isSessionRunning {
Button {
isMirrored.toggle()
} label: {
Image(systemName: isMirrored ? "arrow.left.and.right.circle.fill" : "arrow.left.and.right.circle")
.font(.system(size: 14, weight: .semibold))
.foregroundStyle(.white.opacity(0.9))
.padding(6)
.background(.black.opacity(0.35), in: Circle())
}
.buttonStyle(.plain)
.padding(8)
}
}
}

if !webcamManager.isSessionRunning {
Expand Down
2 changes: 2 additions & 0 deletions boringNotch/models/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ extension Defaults.Keys {
// MARK: Appearance
//static let alwaysShowTabs = Key<Bool>("alwaysShowTabs", default: true)
static let showMirror = Key<Bool>("showMirror", default: false)
static let isMirrored = Key<Bool>("isMirrored", default: false)
static let enableFlipWebcamToggle = Key<Bool>("enableFlipWebcamToggle", default: false)
static let mirrorShape = Key<MirrorShapeEnum>("mirrorShape", default: MirrorShapeEnum.rectangle)
static let settingsIconInNotch = Key<Bool>("settingsIconInNotch", default: true)
static let lightingEffect = Key<Bool>("lightingEffect", default: true)
Expand Down