general: semi-type masturbation

This commit is contained in:
Michael Freno
2026-01-12 12:39:39 -05:00
parent 4f799544b7
commit 359389f326
6 changed files with 76 additions and 103 deletions

1
.gitignore vendored
View File

@@ -5,3 +5,4 @@ AGENTS.md
*.env *.env
*.dmg *.dmg
*.delta *.delta
*.pem

22
Gaze/Data/structs.swift Normal file
View File

@@ -0,0 +1,22 @@
struct Range: Codable {
var bounds: ClosedRange<Int>
var step: Int
}
struct RangeChoice: Equatable {
var val: Int?
let range: Range?
static func == (lhs: RangeChoice, rhs: RangeChoice) -> Bool {
lhs.val == rhs.val && lhs.range?.bounds.lowerBound == rhs.range?.bounds.lowerBound
&& lhs.range?.bounds.upperBound == rhs.range?.bounds.upperBound
}
init(val: Int? = nil, range: Range? = nil) {
self.val = val
self.range = range
}
var isNil: Bool {
return val == nil || range == nil
}
}

View File

@@ -1,42 +1,44 @@
import SwiftUI import SwiftUI
struct SliderSection: View { struct SliderSection: View {
@Binding var intervalMinutes: Int @Binding var intervalSettings: RangeChoice
@Binding var countdownSeconds: Int @Binding var countdownSettings: RangeChoice
@Binding var enabled: Bool @Binding var enabled: Bool
let intervalRange: ClosedRange<Int>
let countdownRange: ClosedRange<Int>?
let type: String let type: String
let previewFunc: () -> Void let previewFunc: () -> Void
let reminderText: String
init( init(
intervalMinutes: Binding<Int>, intervalSettings: Binding<RangeChoice>,
countdownSeconds: Binding<Int>, countdownSettings: Binding<RangeChoice>?,
intervalRange: ClosedRange<Int>,
countdownRange: ClosedRange<Int>? = nil,
enabled: Binding<Bool>, enabled: Binding<Bool>,
type: String, type: String,
reminderText: String,
previewFunc: @escaping () -> Void previewFunc: @escaping () -> Void
) { ) {
self._intervalMinutes = intervalMinutes self._intervalSettings = intervalSettings
self._countdownSeconds = countdownSeconds self._countdownSettings = countdownSettings ?? .constant(RangeChoice(val: nil, range: nil))
self.intervalRange = intervalRange
self.countdownRange = countdownRange
self._enabled = enabled self._enabled = enabled
self.type = type self.type = type
self.reminderText = reminderText
self.previewFunc = previewFunc self.previewFunc = previewFunc
} }
var reminderText: String {
guard enabled else {
return "\(type) reminders are currently disabled."
}
if countdownSettings.isNil && intervalSettings.isNil {
return "You will be reminded every \(intervalSettings.val) minutes"
}
return
"You will be \(countdownSettings.isNil ? "subtly" : "") reminded every \(intervalSettings.val) minutes for \(countdownSettings.val!) seconds"
}
var body: some View { var body: some View {
VStack(alignment: .leading, spacing: 20) { VStack(alignment: .leading, spacing: 20) {
Toggle("Enable \(type.titleCase) Reminders", isOn: $enabled) Toggle("Enable \(type.titleCase) Reminders", isOn: $enabled)
.font(.headline) .font(.headline)
if enabled { if enabled && !intervalSettings.isNil {
VStack(alignment: .leading, spacing: 12) { VStack(alignment: .leading, spacing: 12) {
Text("Remind me every:") Text("Remind me every:")
.font(.subheadline) .font(.subheadline)
@@ -44,29 +46,34 @@ struct SliderSection: View {
HStack { HStack {
Slider( Slider(
value: Binding( value: Binding(
get: { Double(intervalMinutes) }, get: { Double(intervalSettings.val ?? 0) },
set: { intervalMinutes = Int($0) } set: { intervalSettings.val = Int($0) }
), ),
in: in:
Double(intervalRange.lowerBound)...Double(intervalRange.upperBound), Double(
intervalSettings.range.bounds.lowerBound)...Double(
intervalSettings.range.bounds.upperBound),
step: 5.0) step: 5.0)
Text("\(intervalMinutes) min") Text("\(intervalSettings.val) min")
.frame(width: 60, alignment: .trailing) .frame(width: 60, alignment: .trailing)
.monospacedDigit() .monospacedDigit()
} }
if let range = countdownRange { if let range = countdownSettings.range {
Text("Look away for:") Text("Look away for:")
.font(.subheadline) .font(.subheadline)
.foregroundColor(.secondary) .foregroundColor(.secondary)
HStack { HStack {
Slider( Slider(
value: Binding( value: Binding(
get: { Double(countdownSeconds) }, get: { Double(countdownSettings.seconds ?? 0) },
set: { countdownSeconds = Int($0) } set: { countdownSettings.seconds = Int($0) }
), in: Double(range.lowerBound)...Double(range.upperBound), ),
in:
Double(
range.bounds.lowerBound)...Double(range.bounds.upperBound),
step: 5.0) step: 5.0)
Text("\(countdownSeconds) sec") Text("\(countdownSettings.seconds ?? 0) sec")
.frame(width: 60, alignment: .trailing) .frame(width: 60, alignment: .trailing)
.monospacedDigit() .monospacedDigit()
} }
@@ -77,6 +84,7 @@ struct SliderSection: View {
.glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12)) .glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12))
if enabled { if enabled {
Text( Text(
reminderText reminderText
) )
@@ -111,3 +119,4 @@ struct SliderSection: View {
) )
} }
} }

View File

@@ -14,8 +14,8 @@ import SwiftUI
struct LookAwaySetupView: View { struct LookAwaySetupView: View {
@Binding var enabled: Bool @Binding var enabled: Bool
@Binding var intervalMinutes: Int @Binding var intervalSettings: RangeChoice
@Binding var countdownSeconds: Int @Binding var countdownSettings: RangeChoice
@State private var previewWindowController: NSWindowController? @State private var previewWindowController: NSWindowController?
var body: some View { var body: some View {
@@ -62,17 +62,12 @@ struct LookAwaySetupView: View {
GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8)) GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8))
SliderSection( SliderSection(
intervalMinutes: $intervalMinutes, intervalSettings: $intervalSettings,
countdownSeconds: $countdownSeconds, countdownSettings: $countdownSettings,
intervalRange: 5...90,
countdownRange: 10...30,
enabled: $enabled, enabled: $enabled,
type: "Look away", type: "Look away",
reminderText:
"You will be reminded every \(intervalMinutes) minutes to look in the distance for \(countdownSeconds) seconds",
previewFunc: showPreviewWindow previewFunc: showPreviewWindow
) )
} }
Spacer() Spacer()
@@ -98,7 +93,7 @@ struct LookAwaySetupView: View {
window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary] window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
window.acceptsMouseMovedEvents = true window.acceptsMouseMovedEvents = true
let contentView = LookAwayReminderView(countdownSeconds: countdownSeconds) { let contentView = LookAwayReminderView(countdownSeconds: countdownSettings.val ?? 20) {
[weak window] in [weak window] in
window?.close() window?.close()
} }
@@ -114,10 +109,11 @@ struct LookAwaySetupView: View {
} }
} }
#Preview("Look Away Setup View") { //TODO: add this back
LookAwaySetupView( /*#Preview("Look Away Setup View") {*/
enabled: .constant(true), /*LookAwaySetupView(*/
intervalMinutes: .constant(20), /*enabled: .constant(true),*/
countdownSeconds: .constant(20) /*intervalMinutes: .constant(20),*/
) /*countdownSeconds: .constant(20)*/
} /*)*/
/*}*/

View File

@@ -10,7 +10,7 @@ import SwiftUI
struct PostureSetupView: View { struct PostureSetupView: View {
@Binding var enabled: Bool @Binding var enabled: Bool
@Binding var intervalMinutes: Int @Binding var intervalSettings: RangeChoice
var subtleReminderSize: ReminderSize = .medium var subtleReminderSize: ReminderSize = .medium
@State private var previewWindowController: NSWindowController? @State private var previewWindowController: NSWindowController?
@@ -60,65 +60,11 @@ struct PostureSetupView: View {
.glassEffectIfAvailable( .glassEffectIfAvailable(
GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8)) GlassStyle.regular.tint(.accentColor), in: .rect(cornerRadius: 8))
VStack(alignment: .leading, spacing: 20) { SliderSection(
Toggle("Enable Posture Reminders", isOn: $enabled) intervalSettings: $intervalSettings,
.font(.headline) enabled: $enabled,
type: "Posture",
if enabled { previewFunc: showPreviewWindow
VStack(alignment: .leading, spacing: 12) {
Text("Remind me every:")
.font(.subheadline)
.foregroundColor(.secondary)
HStack {
Slider(
value: Binding(
get: { Double(intervalMinutes) },
set: { intervalMinutes = Int($0) }
), in: 15...90, step: 5)
Text("\(intervalMinutes) min")
.frame(width: 60, alignment: .trailing)
.monospacedDigit()
}
}
}
}
.padding()
.glassEffectIfAvailable(GlassStyle.regular, in: .rect(cornerRadius: 12))
if enabled {
Text(
"You will be subtly reminded every \(intervalMinutes) minutes to check your posture"
)
.font(.subheadline)
.foregroundColor(.secondary)
} else {
Text(
"Posture reminders are currently disabled."
)
.font(.caption)
.foregroundColor(.secondary)
}
// Preview button
Button(action: {
showPreviewWindow()
}) {
HStack(spacing: 8) {
Image(systemName: "eye")
.foregroundColor(.white)
Text("Preview Reminder")
.font(.headline)
.foregroundColor(.white)
}
.padding(.horizontal, 16)
.padding(.vertical, 10)
.contentShape(RoundedRectangle(cornerRadius: 10))
}
.buttonStyle(.plain)
.glassEffectIfAvailable(
GlassStyle.regular.tint(.accentColor).interactive(), in: .rect(cornerRadius: 10)
) )
} }

View File

@@ -1 +0,0 @@
/Users/mike/Code/freno-dev