Whisper model weights excluded from git — auto-downloaded at first Xcode build via Scripts/download_whisper_model.sh (~600 MB, one-time).
121 lines
4.8 KiB
Swift
121 lines
4.8 KiB
Swift
//
|
|
// LockInBroWidgetLiveActivity.swift
|
|
// LockInBroWidget
|
|
//
|
|
|
|
import ActivityKit
|
|
import WidgetKit
|
|
import SwiftUI
|
|
|
|
struct LockInBroWidgetLiveActivity: Widget {
|
|
var body: some WidgetConfiguration {
|
|
ActivityConfiguration(for: FocusSessionAttributes.self) { context in
|
|
// Lock screen/banner UI
|
|
VStack(alignment: .leading, spacing: 6) {
|
|
HStack {
|
|
Image(systemName: "clock.fill")
|
|
.foregroundStyle(.blue)
|
|
Text("Focus Mode")
|
|
.font(.headline)
|
|
.fontWeight(.bold)
|
|
Spacer()
|
|
Text(Date(timeIntervalSince1970: TimeInterval(context.state.startedAt)), style: .timer)
|
|
.font(.title3.monospacedDigit())
|
|
.foregroundStyle(.blue)
|
|
.multilineTextAlignment(.trailing)
|
|
}
|
|
Text(context.state.taskTitle)
|
|
.font(.subheadline)
|
|
.foregroundStyle(.secondary)
|
|
.lineLimit(1)
|
|
if context.state.stepsTotal > 0 {
|
|
HStack(spacing: 6) {
|
|
ProgressView(value: Double(context.state.stepsCompleted), total: Double(context.state.stepsTotal))
|
|
.tint(.blue)
|
|
Text("\(context.state.stepsCompleted)/\(context.state.stepsTotal)")
|
|
.font(.caption2.monospacedDigit())
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
if let step = context.state.currentStepTitle {
|
|
Text("Now: \(step)")
|
|
.font(.caption)
|
|
.foregroundStyle(.white.opacity(0.7))
|
|
.lineLimit(1)
|
|
}
|
|
}
|
|
}
|
|
.padding()
|
|
.activityBackgroundTint(Color.black.opacity(0.8))
|
|
.activitySystemActionForegroundColor(Color.white)
|
|
|
|
} dynamicIsland: { context in
|
|
DynamicIsland {
|
|
// Expanded UI
|
|
DynamicIslandExpandedRegion(.leading) {
|
|
Label("Focus", systemImage: "clock.fill")
|
|
.font(.caption)
|
|
.foregroundStyle(.blue)
|
|
}
|
|
DynamicIslandExpandedRegion(.trailing) {
|
|
Text(Date(timeIntervalSince1970: TimeInterval(context.state.startedAt)), style: .timer)
|
|
.font(.caption.monospacedDigit())
|
|
.foregroundStyle(.blue)
|
|
}
|
|
DynamicIslandExpandedRegion(.center) {
|
|
Text(context.state.taskTitle)
|
|
.font(.subheadline)
|
|
.lineLimit(1)
|
|
}
|
|
DynamicIslandExpandedRegion(.bottom) {
|
|
if context.state.stepsTotal > 0 {
|
|
Text("\(context.state.stepsCompleted)/\(context.state.stepsTotal) steps — \(context.state.currentStepTitle ?? "Stay locked in!")")
|
|
.font(.caption2)
|
|
.foregroundStyle(.secondary)
|
|
.lineLimit(1)
|
|
} else {
|
|
Text("Stay locked in!")
|
|
.font(.caption2)
|
|
.foregroundStyle(.secondary)
|
|
}
|
|
}
|
|
} compactLeading: {
|
|
Image(systemName: "clock.fill")
|
|
.foregroundStyle(.blue)
|
|
} compactTrailing: {
|
|
Text(Date(timeIntervalSince1970: TimeInterval(context.state.startedAt)), style: .timer)
|
|
.font(.caption2.monospacedDigit())
|
|
.frame(maxWidth: 40)
|
|
} minimal: {
|
|
Image(systemName: "clock.fill")
|
|
.foregroundStyle(.blue)
|
|
}
|
|
.widgetURL(URL(string: "lockinbro://resume-session"))
|
|
.keylineTint(Color.blue)
|
|
}
|
|
}
|
|
}
|
|
|
|
extension FocusSessionAttributes {
|
|
fileprivate static var preview: FocusSessionAttributes {
|
|
FocusSessionAttributes(sessionType: "Focus")
|
|
}
|
|
}
|
|
|
|
extension FocusSessionAttributes.ContentState {
|
|
fileprivate static var dummy: FocusSessionAttributes.ContentState {
|
|
FocusSessionAttributes.ContentState(
|
|
taskTitle: "Finish Physics Assignment",
|
|
startedAt: Int(Date().addingTimeInterval(-120).timeIntervalSince1970),
|
|
stepsCompleted: 2,
|
|
stepsTotal: 5,
|
|
currentStepTitle: "Solve problem set 3"
|
|
)
|
|
}
|
|
}
|
|
|
|
#Preview("Notification", as: .content, using: FocusSessionAttributes.preview) {
|
|
LockInBroWidgetLiveActivity()
|
|
} contentStates: {
|
|
FocusSessionAttributes.ContentState.dummy
|
|
}
|