diff --git a/.gitignore b/.gitignore
index 832e80a1..0f0a986b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,3 +6,10 @@ build/
releases/
.claude/
coding-plans/
+
+/worker/wrangler.toml
+
+/worker/.wrangler/
+
+xcuserdata/
+*.xcuserdatad/
diff --git a/leanring-buddy.xcodeproj/project.pbxproj b/leanring-buddy.xcodeproj/project.pbxproj
index 9d88d4b9..8ca76b97 100644
--- a/leanring-buddy.xcodeproj/project.pbxproj
+++ b/leanring-buddy.xcodeproj/project.pbxproj
@@ -411,7 +411,7 @@
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = 2UDAY4J48G;
+ DEVELOPMENT_TEAM = ZY7Q367B6W;
ENABLE_APP_SANDBOX = NO;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
@@ -452,7 +452,7 @@
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
- DEVELOPMENT_TEAM = 2UDAY4J48G;
+ DEVELOPMENT_TEAM = ZY7Q367B6W;
ENABLE_APP_SANDBOX = NO;
ENABLE_HARDENED_RUNTIME = YES;
ENABLE_OUTGOING_NETWORK_CONNECTIONS = YES;
diff --git a/leanring-buddy.xcodeproj/xcuserdata/julianalvarez.xcuserdatad/xcschemes/xcschememanagement.plist b/leanring-buddy.xcodeproj/xcuserdata/julianalvarez.xcuserdatad/xcschemes/xcschememanagement.plist
deleted file mode 100644
index 6c13490a..00000000
--- a/leanring-buddy.xcodeproj/xcuserdata/julianalvarez.xcuserdatad/xcschemes/xcschememanagement.plist
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
- SchemeUserState
-
- leanring-buddy.xcscheme_^#shared#^_
-
- orderHint
- 0
-
-
-
-
diff --git a/leanring-buddy.xcodeproj/xcuserdata/thorfinn.xcuserdatad/xcschemes/xcschememanagement.plist b/leanring-buddy.xcodeproj/xcuserdata/thorfinn.xcuserdatad/xcschemes/xcschememanagement.plist
deleted file mode 100644
index 6c13490a..00000000
--- a/leanring-buddy.xcodeproj/xcuserdata/thorfinn.xcuserdatad/xcschemes/xcschememanagement.plist
+++ /dev/null
@@ -1,14 +0,0 @@
-
-
-
-
- SchemeUserState
-
- leanring-buddy.xcscheme_^#shared#^_
-
- orderHint
- 0
-
-
-
-
diff --git a/leanring-buddy/AppleSpeechTranscriptionProvider.swift b/leanring-buddy/AppleSpeechTranscriptionProvider.swift
index 600594fa..51ed9713 100644
--- a/leanring-buddy/AppleSpeechTranscriptionProvider.swift
+++ b/leanring-buddy/AppleSpeechTranscriptionProvider.swift
@@ -24,12 +24,13 @@ final class AppleSpeechTranscriptionProvider: BuddyTranscriptionProvider {
let unavailableExplanation: String? = nil
func startStreamingSession(
+ languageCode: String,
keyterms: [String],
onTranscriptUpdate: @escaping (String) -> Void,
onFinalTranscriptReady: @escaping (String) -> Void,
onError: @escaping (Error) -> Void
) async throws -> any BuddyStreamingTranscriptionSession {
- guard let speechRecognizer = Self.makeBestAvailableSpeechRecognizer() else {
+ guard let speechRecognizer = Self.makeBestAvailableSpeechRecognizer(languageCode: languageCode) else {
throw AppleSpeechTranscriptionProviderError(message: "dictation is not available on this mac.")
}
@@ -41,8 +42,9 @@ final class AppleSpeechTranscriptionProvider: BuddyTranscriptionProvider {
)
}
- private static func makeBestAvailableSpeechRecognizer() -> SFSpeechRecognizer? {
+ private static func makeBestAvailableSpeechRecognizer(languageCode: String) -> SFSpeechRecognizer? {
let preferredLocales = [
+ Locale(identifier: languageCode),
Locale.autoupdatingCurrent,
Locale(identifier: "en-US")
]
diff --git a/leanring-buddy/AssemblyAIStreamingTranscriptionProvider.swift b/leanring-buddy/AssemblyAIStreamingTranscriptionProvider.swift
index d21286b6..a8809015 100644
--- a/leanring-buddy/AssemblyAIStreamingTranscriptionProvider.swift
+++ b/leanring-buddy/AssemblyAIStreamingTranscriptionProvider.swift
@@ -17,10 +17,6 @@ struct AssemblyAIStreamingTranscriptionProviderError: LocalizedError {
}
final class AssemblyAIStreamingTranscriptionProvider: BuddyTranscriptionProvider {
- /// URL for the Cloudflare Worker endpoint that returns a short-lived
- /// AssemblyAI streaming token. The real API key never leaves the server.
- private static let tokenProxyURL = "https://your-worker-name.your-subdomain.workers.dev/transcribe-token"
-
let displayName = "AssemblyAI"
let requiresSpeechRecognitionPermission = false
@@ -34,6 +30,7 @@ final class AssemblyAIStreamingTranscriptionProvider: BuddyTranscriptionProvider
private let sharedWebSocketURLSession = URLSession(configuration: .default)
func startStreamingSession(
+ languageCode: String,
keyterms: [String],
onTranscriptUpdate: @escaping (String) -> Void,
onFinalTranscriptReady: @escaping (String) -> Void,
@@ -48,6 +45,7 @@ final class AssemblyAIStreamingTranscriptionProvider: BuddyTranscriptionProvider
temporaryToken: temporaryToken,
urlSession: sharedWebSocketURLSession,
keyterms: keyterms,
+ languageCode: languageCode,
onTranscriptUpdate: onTranscriptUpdate,
onFinalTranscriptReady: onFinalTranscriptReady,
onError: onError
@@ -59,7 +57,8 @@ final class AssemblyAIStreamingTranscriptionProvider: BuddyTranscriptionProvider
/// Calls the Cloudflare Worker to get a short-lived AssemblyAI token.
private func fetchTemporaryToken() async throws -> String {
- var request = URLRequest(url: URL(string: Self.tokenProxyURL)!)
+ let baseURL = await WorkerEnvironment.shared.getBaseURL()
+ var request = URLRequest(url: URL(string: "\(baseURL)/transcribe-token")!)
request.httpMethod = "POST"
let (data, response) = try await URLSession.shared.data(for: request)
@@ -117,6 +116,7 @@ private final class AssemblyAIStreamingTranscriptionSession: NSObject, BuddyStre
private let apiKey: String?
private let temporaryToken: String?
private let keyterms: [String]
+ private let languageCode: String
private let onTranscriptUpdate: (String) -> Void
private let onFinalTranscriptReady: (String) -> Void
private let onError: (Error) -> Void
@@ -142,6 +142,7 @@ private final class AssemblyAIStreamingTranscriptionSession: NSObject, BuddyStre
temporaryToken: String?,
urlSession: URLSession,
keyterms: [String],
+ languageCode: String,
onTranscriptUpdate: @escaping (String) -> Void,
onFinalTranscriptReady: @escaping (String) -> Void,
onError: @escaping (Error) -> Void
@@ -150,6 +151,7 @@ private final class AssemblyAIStreamingTranscriptionSession: NSObject, BuddyStre
self.temporaryToken = temporaryToken
self.urlSession = urlSession
self.keyterms = keyterms
+ self.languageCode = languageCode
self.onTranscriptUpdate = onTranscriptUpdate
self.onFinalTranscriptReady = onFinalTranscriptReady
self.onError = onError
@@ -158,7 +160,8 @@ private final class AssemblyAIStreamingTranscriptionSession: NSObject, BuddyStre
func open() async throws {
let websocketURL = try Self.makeWebsocketURL(
temporaryToken: temporaryToken,
- keyterms: keyterms
+ keyterms: keyterms,
+ languageCode: languageCode
)
var websocketRequest = URLRequest(url: websocketURL)
@@ -436,7 +439,8 @@ private final class AssemblyAIStreamingTranscriptionSession: NSObject, BuddyStre
private static func makeWebsocketURL(
temporaryToken: String?,
- keyterms: [String]
+ keyterms: [String],
+ languageCode: String
) throws -> URL {
guard var websocketURLComponents = URLComponents(string: websocketBaseURLString) else {
throw AssemblyAIStreamingTranscriptionProviderError(
@@ -451,6 +455,12 @@ private final class AssemblyAIStreamingTranscriptionSession: NSObject, BuddyStre
URLQueryItem(name: "speech_model", value: "u3-rt-pro")
]
+ if languageCode != "en" {
+ // AssemblyAI supports multiple language codes.
+ queryItems.append(URLQueryItem(name: "language_code", value: languageCode))
+ // u3-rt-pro generally supports multi lingual. If any errors happen, we might need a different speech_model or drop it.
+ }
+
let normalizedKeyterms = keyterms
.map { $0.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { !$0.isEmpty }
diff --git a/leanring-buddy/BuddyDictationManager.swift b/leanring-buddy/BuddyDictationManager.swift
index 5bca2677..dc1152ef 100644
--- a/leanring-buddy/BuddyDictationManager.swift
+++ b/leanring-buddy/BuddyDictationManager.swift
@@ -517,7 +517,9 @@ final class BuddyDictationManager: NSObject, ObservableObject {
print("🎙️ BuddyDictationManager: opening transcription provider \(transcriptionProvider.displayName)")
+ let languageCode = UserDefaults.standard.string(forKey: "selectedLanguageCode") ?? "en"
let activeTranscriptionSession = try await transcriptionProvider.startStreamingSession(
+ languageCode: languageCode,
keyterms: buildTranscriptionKeyterms(),
onTranscriptUpdate: { [weak self] transcriptText in
Task { @MainActor in
diff --git a/leanring-buddy/BuddyTranscriptionProvider.swift b/leanring-buddy/BuddyTranscriptionProvider.swift
index 0a75715d..800f7657 100644
--- a/leanring-buddy/BuddyTranscriptionProvider.swift
+++ b/leanring-buddy/BuddyTranscriptionProvider.swift
@@ -22,6 +22,7 @@ protocol BuddyTranscriptionProvider {
var unavailableExplanation: String? { get }
func startStreamingSession(
+ languageCode: String,
keyterms: [String],
onTranscriptUpdate: @escaping (String) -> Void,
onFinalTranscriptReady: @escaping (String) -> Void,
diff --git a/leanring-buddy/ClaudeAPI.swift b/leanring-buddy/ClaudeAPI.swift
index 0c7070b5..39153c60 100644
--- a/leanring-buddy/ClaudeAPI.swift
+++ b/leanring-buddy/ClaudeAPI.swift
@@ -10,12 +10,13 @@ class ClaudeAPI {
private static let tlsWarmupLock = NSLock()
private static var hasStartedTLSWarmup = false
- private let apiURL: URL
+ var proxyURL: String
+ private var apiURL: URL { URL(string: proxyURL)! }
var model: String
private let session: URLSession
init(proxyURL: String, model: String = "claude-sonnet-4-6") {
- self.apiURL = URL(string: proxyURL)!
+ self.proxyURL = proxyURL
self.model = model
// Use .default instead of .ephemeral so TLS session tickets are cached.
@@ -29,11 +30,6 @@ class ClaudeAPI {
config.urlCache = nil
config.httpCookieStorage = nil
self.session = URLSession(configuration: config)
-
- // Fire a lightweight HEAD request in the background to pre-establish the TLS
- // connection. This caches the TLS session ticket so the first real API call
- // (which carries a large image payload) doesn't need a cold TLS handshake.
- warmUpTLSConnectionIfNeeded()
}
private func makeAPIRequest() -> URLRequest {
@@ -63,7 +59,7 @@ class ClaudeAPI {
/// Sends a no-op HEAD request to the API host to establish and cache a TLS session.
/// Failures are silently ignored — this is purely an optimization.
- private func warmUpTLSConnectionIfNeeded() {
+ func warmUpTLSConnectionIfNeeded() {
Self.tlsWarmupLock.lock()
let shouldStartTLSWarmup = !Self.hasStartedTLSWarmup
if shouldStartTLSWarmup {
diff --git a/leanring-buddy/CompanionManager.swift b/leanring-buddy/CompanionManager.swift
index 0234cf19..1bd45fb5 100644
--- a/leanring-buddy/CompanionManager.swift
+++ b/leanring-buddy/CompanionManager.swift
@@ -70,14 +70,18 @@ final class CompanionManager: ObservableObject {
/// Base URL for the Cloudflare Worker proxy. All API requests route
/// through this so keys never ship in the app binary.
- private static let workerBaseURL = "https://your-worker-name.your-subdomain.workers.dev"
-
private lazy var claudeAPI: ClaudeAPI = {
- return ClaudeAPI(proxyURL: "\(Self.workerBaseURL)/chat", model: selectedModel)
+ let defaultURL = "https://your-worker-name.your-subdomain.workers.dev" // Default placeholder, will be dynamically updated in start()
+ return ClaudeAPI(proxyURL: "\(defaultURL)/chat", model: selectedModel)
+ }()
+
+ private lazy var openAIAPI: OpenAIAPI = {
+ return OpenAIAPI(apiKey: lmStudioAPIKey, model: selectedModel)
}()
private lazy var elevenLabsTTSClient: ElevenLabsTTSClient = {
- return ElevenLabsTTSClient(proxyURL: "\(Self.workerBaseURL)/tts")
+ let defaultURL = "https://your-worker-name.your-subdomain.workers.dev"
+ return ElevenLabsTTSClient(proxyURL: "\(defaultURL)/tts")
}()
/// Conversation history so Claude remembers prior exchanges within a session.
@@ -107,13 +111,65 @@ final class CompanionManager: ObservableObject {
/// Used by the panel to show accurate status text ("Active" vs "Ready").
@Published private(set) var isOverlayVisible: Bool = false
+ /// The user's preferred spoken and transcribing language code (e.g. "en", "fr").
+ @Published var selectedLanguageCode: String = UserDefaults.standard.string(forKey: "selectedLanguageCode") ?? "en" {
+ didSet {
+ UserDefaults.standard.set(selectedLanguageCode, forKey: "selectedLanguageCode")
+ }
+ }
+
/// The Claude model used for voice responses. Persisted to UserDefaults.
- @Published var selectedModel: String = UserDefaults.standard.string(forKey: "selectedClaudeModel") ?? "claude-sonnet-4-6"
+ @Published var selectedModel: String = UserDefaults.standard.string(forKey: "selectedGemmaModel") ?? ""
+
+ /// The API key for local LLM requests. Persisted to UserDefaults.
+ @Published var lmStudioAPIKey: String = UserDefaults.standard.string(forKey: "lmStudioAPIKey") ?? "" {
+ didSet {
+ UserDefaults.standard.set(lmStudioAPIKey, forKey: "lmStudioAPIKey")
+ openAIAPI.apiKey = lmStudioAPIKey
+ }
+ }
+
+ /// List of available models fetched from LM Studio API
+ @Published var availableModels: [String] = []
+
+ func fetchAvailableModels() {
+ guard let url = URL(string: "http://127.0.0.1:1234/v1/models") else { return }
+ var request = URLRequest(url: url)
+ request.timeoutInterval = 3
+ if !lmStudioAPIKey.isEmpty {
+ request.setValue("Bearer \(lmStudioAPIKey)", forHTTPHeaderField: "Authorization")
+ }
+
+ Task {
+ do {
+ let (data, _) = try await URLSession.shared.data(for: request)
+ if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any],
+ let dataArr = json["data"] as? [[String: Any]] {
+ var models = dataArr.compactMap { $0["id"] as? String }
+ // Filter out embedding models or non-chat models if needed,
+ // usually all are fine but we just list them.
+ models.sort()
+ await MainActor.run {
+ self.availableModels = models
+ // If current selected isn't in there, don't force change it yet
+ // unless it's completely empty. It's safe to let them manually select it.
+ }
+ }
+ } catch {
+ print("Failed to fetch models from LM Studio: \\(error)")
+ }
+ }
+ }
func setSelectedModel(_ model: String) {
selectedModel = model
- UserDefaults.standard.set(model, forKey: "selectedClaudeModel")
- claudeAPI.model = model
+ UserDefaults.standard.set(model, forKey: "selectedGemmaModel")
+
+ if model.lowercased().contains("claude") {
+ claudeAPI.model = model
+ } else {
+ openAIAPI.model = model
+ }
}
/// User preference for whether the Clicky cursor should be shown.
@@ -173,15 +229,24 @@ final class CompanionManager: ObservableObject {
}
func start() {
+ Task {
+ let baseURL = await WorkerEnvironment.shared.getBaseURL()
+ self.claudeAPI.proxyURL = "\(baseURL)/chat"
+ self.elevenLabsTTSClient.proxyURL = "\(baseURL)/tts"
+
+ // Eagerly touch APIs so their TLS warmup handshakes complete
+ // well before the onboarding demo fires at ~40s into the video.
+ self.claudeAPI.warmUpTLSConnectionIfNeeded()
+ _ = self.openAIAPI
+ }
+
+ fetchAvailableModels()
refreshAllPermissions()
print("🔑 Clicky start — accessibility: \(hasAccessibilityPermission), screen: \(hasScreenRecordingPermission), mic: \(hasMicrophonePermission), screenContent: \(hasScreenContentPermission), onboarded: \(hasCompletedOnboarding)")
startPermissionPolling()
bindVoiceStateObservation()
bindAudioPowerLevel()
bindShortcutTransitions()
- // Eagerly touch the Claude API so its TLS warmup handshake completes
- // well before the onboarding demo fires at ~40s into the video.
- _ = claudeAPI
// If the user already completed onboarding AND all permissions are
// still granted, show the cursor overlay immediately. If permissions
@@ -610,15 +675,30 @@ final class CompanionManager: ObservableObject {
(userPlaceholder: entry.userTranscript, assistantResponse: entry.assistantResponse)
}
- let (fullResponseText, _) = try await claudeAPI.analyzeImageStreaming(
- images: labeledImages,
- systemPrompt: Self.companionVoiceResponseSystemPrompt,
- conversationHistory: historyForAPI,
- userPrompt: transcript,
- onTextChunk: { _ in
- // No streaming text display — spinner stays until TTS plays
- }
- )
+ let fullResponseText: String
+ let duration: TimeInterval
+
+ if selectedModel.lowercased().contains("claude") {
+ (fullResponseText, duration) = try await claudeAPI.analyzeImageStreaming(
+ images: labeledImages,
+ systemPrompt: Self.companionVoiceResponseSystemPrompt + "\n\nCRITICAL: Respond EXCLUSIVELY in the following language code: \(selectedLanguageCode)",
+ conversationHistory: historyForAPI,
+ userPrompt: transcript,
+ onTextChunk: { _ in
+ // No streaming text display — spinner stays until TTS plays
+ }
+ )
+ } else {
+ (fullResponseText, duration) = try await openAIAPI.analyzeImageStreaming(
+ images: labeledImages,
+ systemPrompt: Self.companionVoiceResponseSystemPrompt + "\n\nCRITICAL: Respond EXCLUSIVELY in the following language code: \(selectedLanguageCode)",
+ conversationHistory: historyForAPI,
+ userPrompt: transcript,
+ onTextChunk: { _ in
+ // No streaming text display — spinner stays until TTS plays
+ }
+ )
+ }
guard !Task.isCancelled else { return }
@@ -982,12 +1062,24 @@ final class CompanionManager: ObservableObject {
let dimensionInfo = " (image dimensions: \(cursorScreenCapture.screenshotWidthInPixels)x\(cursorScreenCapture.screenshotHeightInPixels) pixels)"
let labeledImages = [(data: cursorScreenCapture.imageData, label: cursorScreenCapture.label + dimensionInfo)]
- let (fullResponseText, _) = try await claudeAPI.analyzeImageStreaming(
- images: labeledImages,
- systemPrompt: Self.onboardingDemoSystemPrompt,
- userPrompt: "look around my screen and find something interesting to point at",
- onTextChunk: { _ in }
- )
+ let fullResponseText: String
+ let duration: TimeInterval
+
+ if selectedModel.lowercased().contains("claude") {
+ (fullResponseText, duration) = try await claudeAPI.analyzeImageStreaming(
+ images: labeledImages,
+ systemPrompt: Self.onboardingDemoSystemPrompt + "\n\nCRITICAL: Respond EXCLUSIVELY in the following language code: \(selectedLanguageCode)",
+ userPrompt: "look around my screen and find something interesting to point at",
+ onTextChunk: { _ in }
+ )
+ } else {
+ (fullResponseText, duration) = try await openAIAPI.analyzeImageStreaming(
+ images: labeledImages,
+ systemPrompt: Self.onboardingDemoSystemPrompt + "\n\nCRITICAL: Respond EXCLUSIVELY in the following language code: \(selectedLanguageCode)",
+ userPrompt: "look around my screen and find something interesting to point at",
+ onTextChunk: { _ in }
+ )
+ }
let parseResult = Self.parsePointingCoordinates(from: fullResponseText)
diff --git a/leanring-buddy/CompanionPanelView.swift b/leanring-buddy/CompanionPanelView.swift
index 76789b4c..a65b28d5 100644
--- a/leanring-buddy/CompanionPanelView.swift
+++ b/leanring-buddy/CompanionPanelView.swift
@@ -13,42 +13,119 @@ import SwiftUI
struct CompanionPanelView: View {
@ObservedObject var companionManager: CompanionManager
@State private var emailInput: String = ""
-
+ @State private var isAPIKeyVisible: Bool = false
+ @State private var isShowingSettings: Bool = false
+
var body: some View {
VStack(alignment: .leading, spacing: 0) {
panelHeader
Divider()
.background(DS.Colors.borderSubtle)
.padding(.horizontal, 16)
-
- permissionsCopySection
- .padding(.top, 16)
+
+ if isShowingSettings {
+ settingsView
+ } else {
+ mainContentView
+ }
+
+ Spacer()
+ .frame(height: 12)
+
+ Divider()
+ .background(DS.Colors.borderSubtle)
.padding(.horizontal, 16)
+
+ footerSection // <-- This is now defined below
+ .padding(.horizontal, 16)
+ .padding(.vertical, 12)
+ }
+ .frame(width: 320)
+ .background(panelBackground) // <-- This is now defined below
+ }
+
+ private var settingsView: some View {
+ VStack(alignment: .leading, spacing: 16) {
+ modelPickerRow
+ localAPIKeyRow
+ languagePickerRow
+ }
+ .padding(.horizontal, 16)
+ .padding(.top, 16)
+ }
- if companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted {
- Spacer()
- .frame(height: 12)
-
- modelPickerRow
- .padding(.horizontal, 16)
+ private var localAPIKeyRow: some View {
+ HStack {
+ HStack(spacing: 8) {
+ ZStack(alignment: .leading) {
+ if companionManager.lmStudioAPIKey.isEmpty {
+ Text("LM Studio API Key (leave empty if none)")
+ .font(.system(size: 11))
+ .foregroundColor(.white.opacity(0.6))
+ }
+
+ if isAPIKeyVisible {
+ TextField("", text: Binding(
+ get: { companionManager.lmStudioAPIKey },
+ set: { companionManager.lmStudioAPIKey = $0 }
+ ))
+ .textFieldStyle(PlainTextFieldStyle())
+ .font(.system(size: 11))
+ .foregroundColor(.white)
+ } else {
+ SecureField("", text: Binding(
+ get: { companionManager.lmStudioAPIKey },
+ set: { companionManager.lmStudioAPIKey = $0 }
+ ))
+ .textFieldStyle(PlainTextFieldStyle())
+ .font(.system(size: 11))
+ .foregroundColor(.white)
+ }
+ }
+
+ Button(action: {
+ isAPIKeyVisible.toggle()
+ }) {
+ Image(systemName: isAPIKeyVisible ? "eye.slash.fill" : "eye.fill")
+ .font(.system(size: 11))
+ .foregroundColor(DS.Colors.textTertiary)
+ }
+ .buttonStyle(.plain)
+ .pointerCursor()
}
+ .padding(.horizontal, 10)
+ .padding(.vertical, 6)
+ .background(Color.white.opacity(0.06))
+ .cornerRadius(6)
+ .overlay(
+ RoundedRectangle(cornerRadius: 6, style: .continuous)
+ .stroke(DS.Colors.borderSubtle, lineWidth: 0.5)
+ )
+ }
+ }
+ private var mainContentView: some View {
+ VStack(alignment: .leading, spacing: 0) {
+ permissionsCopySection
+ .padding(.top, 16)
+ .padding(.horizontal, 16)
+
if !companionManager.allPermissionsGranted {
Spacer()
.frame(height: 16)
-
+
settingsSection
.padding(.horizontal, 16)
}
-
+
if !companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted {
Spacer()
.frame(height: 16)
-
+
startButton
.padding(.horizontal, 16)
}
-
+
// Show Clicky toggle — hidden for now
// if companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted {
// Spacer()
@@ -57,52 +134,58 @@ struct CompanionPanelView: View {
// showClickyCursorToggleRow
// .padding(.horizontal, 16)
// }
-
+
if companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted {
Spacer()
.frame(height: 16)
-
- dmFarzaButton
+
+ dmFarzaButton // <-- This is now defined below
.padding(.horizontal, 16)
}
-
- Spacer()
- .frame(height: 12)
-
- Divider()
- .background(DS.Colors.borderSubtle)
- .padding(.horizontal, 16)
-
- footerSection
- .padding(.horizontal, 16)
- .padding(.vertical, 12)
}
- .frame(width: 320)
- .background(panelBackground)
}
-
+
// MARK: - Header
-
+
private var panelHeader: some View {
HStack {
HStack(spacing: 8) {
// Animated status dot
Circle()
- .fill(statusDotColor)
+ .fill(statusDotColor) // <-- This is now defined below
.frame(width: 8, height: 8)
.shadow(color: statusDotColor.opacity(0.6), radius: 4)
-
+
Text("Clicky")
.font(.system(size: 14, weight: .semibold))
.foregroundColor(DS.Colors.textPrimary)
}
-
+
Spacer()
-
- Text(statusText)
+
+ Text(statusText) // <-- This is now defined below
.font(.system(size: 12, weight: .medium))
.foregroundColor(DS.Colors.textTertiary)
-
+
+ if companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted {
+ Button(action: {
+ withAnimation(.spring(response: 0.3, dampingFraction: 0.7)) {
+ isShowingSettings.toggle()
+ }
+ }) {
+ Image(systemName: "gear")
+ .font(.system(size: 11, weight: .semibold))
+ .foregroundColor(isShowingSettings ? DS.Colors.textPrimary : DS.Colors.textTertiary)
+ .frame(width: 20, height: 20)
+ .background(
+ Circle()
+ .fill(isShowingSettings ? Color.white.opacity(0.12) : Color.white.opacity(0.08))
+ )
+ }
+ .buttonStyle(.plain)
+ .pointerCursor()
+ }
+
Button(action: {
NotificationCenter.default.post(name: .clickyDismissPanel, object: nil)
}) {
@@ -121,9 +204,9 @@ struct CompanionPanelView: View {
.padding(.horizontal, 16)
.padding(.vertical, 14)
}
-
+
// MARK: - Permissions Copy
-
+
@ViewBuilder
private var permissionsCopySection: some View {
if companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted {
@@ -152,7 +235,7 @@ struct CompanionPanelView: View {
Text("Permissions needed")
.font(.system(size: 12, weight: .bold))
.foregroundColor(DS.Colors.textSecondary)
-
+
Text("Some permissions were revoked. Grant all four below to keep using Clicky.")
.font(.system(size: 11))
.foregroundColor(DS.Colors.textTertiary)
@@ -164,12 +247,12 @@ struct CompanionPanelView: View {
Text("Hi, I'm Farza. This is Clicky.")
.font(.system(size: 12, weight: .bold))
.foregroundColor(DS.Colors.textSecondary)
-
+
Text("A side project I made for fun to help me learn stuff as I use my computer.")
.font(.system(size: 11))
.foregroundColor(DS.Colors.textTertiary)
.fixedSize(horizontal: false, vertical: true)
-
+
Text("Nothing runs in the background. Clicky will only take a screenshot when you press the hot key. So, you can give that permission in peace. If you are still sus, eh, I can't do much there champ.")
.font(.system(size: 11))
.foregroundColor(Color(red: 0.9, green: 0.4, blue: 0.4))
@@ -178,9 +261,9 @@ struct CompanionPanelView: View {
.frame(maxWidth: .infinity, alignment: .leading)
}
}
-
+
// MARK: - Email + Start Button
-
+
@ViewBuilder
private var startButton: some View {
if !companionManager.hasCompletedOnboarding && companionManager.allPermissionsGranted {
@@ -200,7 +283,7 @@ struct CompanionPanelView: View {
RoundedRectangle(cornerRadius: DS.CornerRadius.medium, style: .continuous)
.stroke(DS.Colors.borderSubtle, lineWidth: 0.5)
)
-
+
Button(action: {
companionManager.submitEmail(emailInput)
}) {
@@ -239,9 +322,9 @@ struct CompanionPanelView: View {
}
}
}
-
+
// MARK: - Permissions
-
+
private var settingsSection: some View {
VStack(spacing: 2) {
Text("PERMISSIONS")
@@ -249,20 +332,20 @@ struct CompanionPanelView: View {
.foregroundColor(DS.Colors.textTertiary)
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.bottom, 6)
-
+
microphonePermissionRow
-
+
accessibilityPermissionRow
-
+
screenRecordingPermissionRow
-
+
if companionManager.hasScreenRecordingPermission {
screenContentPermissionRow
}
-
+
}
}
-
+
private var accessibilityPermissionRow: some View {
let isGranted = companionManager.hasAccessibilityPermission
return HStack {
@@ -271,14 +354,14 @@ struct CompanionPanelView: View {
.font(.system(size: 12, weight: .medium))
.foregroundColor(isGranted ? DS.Colors.textTertiary : DS.Colors.warning)
.frame(width: 16)
-
+
Text("Accessibility")
.font(.system(size: 13, weight: .medium))
.foregroundColor(DS.Colors.textSecondary)
}
-
+
Spacer()
-
+
if isGranted {
HStack(spacing: 4) {
Circle()
@@ -307,7 +390,7 @@ struct CompanionPanelView: View {
}
.buttonStyle(.plain)
.pointerCursor()
-
+
Button(action: {
// Reveals the app in Finder so the user can drag it into
// the Accessibility list if it doesn't appear automatically
@@ -332,7 +415,7 @@ struct CompanionPanelView: View {
}
.padding(.vertical, 6)
}
-
+
private var screenRecordingPermissionRow: some View {
let isGranted = companionManager.hasScreenRecordingPermission
return HStack {
@@ -341,22 +424,22 @@ struct CompanionPanelView: View {
.font(.system(size: 12, weight: .medium))
.foregroundColor(isGranted ? DS.Colors.textTertiary : DS.Colors.warning)
.frame(width: 16)
-
+
VStack(alignment: .leading, spacing: 1) {
Text("Screen Recording")
.font(.system(size: 13, weight: .medium))
.foregroundColor(DS.Colors.textSecondary)
-
+
Text(isGranted
? "Only takes a screenshot when you use the hotkey"
: "Quit and reopen after granting")
- .font(.system(size: 10))
- .foregroundColor(DS.Colors.textTertiary)
+ .font(.system(size: 10))
+ .foregroundColor(DS.Colors.textTertiary)
}
}
-
+
Spacer()
-
+
if isGranted {
HStack(spacing: 4) {
Circle()
@@ -389,7 +472,7 @@ struct CompanionPanelView: View {
}
.padding(.vertical, 6)
}
-
+
private var screenContentPermissionRow: some View {
let isGranted = companionManager.hasScreenContentPermission
return HStack {
@@ -398,14 +481,14 @@ struct CompanionPanelView: View {
.font(.system(size: 12, weight: .medium))
.foregroundColor(isGranted ? DS.Colors.textTertiary : DS.Colors.warning)
.frame(width: 16)
-
+
Text("Screen Content")
.font(.system(size: 13, weight: .medium))
.foregroundColor(DS.Colors.textSecondary)
}
-
+
Spacer()
-
+
if isGranted {
HStack(spacing: 4) {
Circle()
@@ -435,7 +518,7 @@ struct CompanionPanelView: View {
}
.padding(.vertical, 6)
}
-
+
private var microphonePermissionRow: some View {
let isGranted = companionManager.hasMicrophonePermission
return HStack {
@@ -444,14 +527,14 @@ struct CompanionPanelView: View {
.font(.system(size: 12, weight: .medium))
.foregroundColor(isGranted ? DS.Colors.textTertiary : DS.Colors.warning)
.frame(width: 16)
-
+
Text("Microphone")
.font(.system(size: 13, weight: .medium))
.foregroundColor(DS.Colors.textSecondary)
}
-
+
Spacer()
-
+
if isGranted {
HStack(spacing: 4) {
Circle()
@@ -490,7 +573,7 @@ struct CompanionPanelView: View {
}
.padding(.vertical, 6)
}
-
+
private func permissionRow(
label: String,
iconName: String,
@@ -503,14 +586,14 @@ struct CompanionPanelView: View {
.font(.system(size: 12, weight: .medium))
.foregroundColor(isGranted ? DS.Colors.textTertiary : DS.Colors.warning)
.frame(width: 16)
-
+
Text(label)
.font(.system(size: 13, weight: .medium))
.foregroundColor(DS.Colors.textSecondary)
}
-
+
Spacer()
-
+
if isGranted {
HStack(spacing: 4) {
Circle()
@@ -542,11 +625,11 @@ struct CompanionPanelView: View {
}
.padding(.vertical, 6)
}
-
-
-
+
+
+
// MARK: - Show Clicky Cursor Toggle
-
+
private var showClickyCursorToggleRow: some View {
HStack {
HStack(spacing: 8) {
@@ -554,14 +637,14 @@ struct CompanionPanelView: View {
.font(.system(size: 12, weight: .medium))
.foregroundColor(DS.Colors.textTertiary)
.frame(width: 16)
-
+
Text("Show Clicky")
.font(.system(size: 13, weight: .medium))
.foregroundColor(DS.Colors.textSecondary)
}
-
+
Spacer()
-
+
Toggle("", isOn: Binding(
get: { companionManager.isClickyCursorEnabled },
set: { companionManager.setClickyCursorEnabled($0) }
@@ -573,7 +656,7 @@ struct CompanionPanelView: View {
}
.padding(.vertical, 4)
}
-
+
private var speechToTextProviderRow: some View {
HStack {
HStack(spacing: 8) {
@@ -581,43 +664,174 @@ struct CompanionPanelView: View {
.font(.system(size: 12, weight: .medium))
.foregroundColor(DS.Colors.textTertiary)
.frame(width: 16)
-
+
Text("Speech to Text")
.font(.system(size: 13, weight: .medium))
.foregroundColor(DS.Colors.textSecondary)
}
-
+
Spacer()
-
+
Text(companionManager.buddyDictationManager.transcriptionProviderDisplayName)
.font(.system(size: 11, weight: .medium))
.foregroundColor(DS.Colors.textTertiary)
}
.padding(.vertical, 4)
}
-
- // MARK: - Model Picker
-
- private var modelPickerRow: some View {
+
+ // MARK: - Language Picker
+
+ private var languagePickerRow: some View {
HStack {
- Text("Model")
- .font(.system(size: 13, weight: .medium))
- .foregroundColor(DS.Colors.textSecondary)
-
+ HStack(spacing: 8) {
+ Image(systemName: "globe")
+ .font(.system(size: 12, weight: .medium))
+ .foregroundColor(DS.Colors.textTertiary)
+ .frame(width: 16)
+
+ Text("Language")
+ .font(.system(size: 13, weight: .medium))
+ .foregroundColor(DS.Colors.textSecondary)
+ }
+
Spacer()
+
+ Menu {
+ // ElevenLabs supports up to 32 base languages automatically on flash 2.5
+ Button("🇺🇸 English") { companionManager.selectedLanguageCode = "en" }
+ Button("🇫🇷 French") { companionManager.selectedLanguageCode = "fr" }
+ Button("🇪🇸 Spanish") { companionManager.selectedLanguageCode = "es" }
+ Button("🇩🇪 German") { companionManager.selectedLanguageCode = "de" }
+ Button("🇮🇹 Italian") { companionManager.selectedLanguageCode = "it" }
+ Button("🇯🇵 Japanese") { companionManager.selectedLanguageCode = "ja" }
+ Button("🇵🇹 Portuguese") { companionManager.selectedLanguageCode = "pt" }
+ Button("🇷🇺 Russian") { companionManager.selectedLanguageCode = "ru" }
+ // AssemblyAI supports these natively
+ } label: {
+ HStack(spacing: 4) {
+ Text(flagForLanguage(companionManager.selectedLanguageCode))
+ .font(.system(size: 14))
+ Image(systemName: "chevron.down")
+ .font(.system(size: 9))
+ .foregroundColor(DS.Colors.textTertiary)
+ }
+ .padding(.horizontal, 10)
+ .padding(.vertical, 5)
+ .background(
+ RoundedRectangle(cornerRadius: 6, style: .continuous)
+ .fill(Color.white.opacity(0.06))
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 6, style: .continuous)
+ .stroke(DS.Colors.borderSubtle, lineWidth: 0.5)
+ )
+ }
+ .menuStyle(.borderlessButton)
+ .menuIndicator(.hidden)
+ .frame(maxWidth: 80)
+ }
+ .padding(.vertical, 4)
+ }
- HStack(spacing: 0) {
- modelOptionButton(label: "Sonnet", modelID: "claude-sonnet-4-6")
- modelOptionButton(label: "Opus", modelID: "claude-opus-4-6")
+ private func flagForLanguage(_ code: String) -> String {
+ switch code {
+ case "fr": return "🇫🇷"
+ case "es": return "🇪🇸"
+ case "de": return "🇩🇪"
+ case "it": return "🇮🇹"
+ case "ja": return "🇯🇵"
+ case "pt": return "🇵🇹"
+ case "ru": return "🇷🇺"
+ default: return "🇺🇸"
+ }
+ }
+
+ // MARK: - Model Picker
+
+ private var modelPickerRow: some View {
+ VStack(spacing: 8) {
+ HStack {
+ Text("Model")
+ .font(.system(size: 13, weight: .medium))
+ .foregroundColor(DS.Colors.textSecondary)
+
+ Spacer()
+
+ Menu {
+ // Section for Local LM Studio Models
+ Section(header: Text("LM Studio (Local)")) {
+ if companionManager.availableModels.isEmpty {
+ Text("No models found").disabled(true)
+ } else {
+ ForEach(companionManager.availableModels, id: \.self) { modelID in
+ Button(action: {
+ companionManager.setSelectedModel(modelID)
+ }) {
+ HStack {
+ Text(modelID)
+ if companionManager.selectedModel == modelID {
+ Image(systemName: "checkmark")
+ }
+ }
+ }
+ }
+ }
+ Button("Refresh LM Studio Models") {
+ companionManager.fetchAvailableModels()
+ }
+ }
+
+ Divider()
+
+ // Section for Cloud / Claude Models
+ Section(header: Text("Anthropic (Cloud)")) {
+ let claudeModels = [
+ ("Claude 3.5 Sonnet", "claude-3-5-sonnet-20241022"),
+ ("Claude 3 Opus", "claude-3-opus-20240229"),
+ ("Claude 3 Haiku", "claude-3-haiku-20240307")
+ ]
+
+ ForEach(claudeModels, id: \.1) { label, modelID in
+ Button(action: {
+ companionManager.setSelectedModel(modelID)
+ }) {
+ HStack {
+ Text(label)
+ if companionManager.selectedModel == modelID {
+ Image(systemName: "checkmark")
+ }
+ }
+ }
+ }
+ }
+
+ } label: {
+ HStack(spacing: 4) {
+ Text(companionManager.selectedModel)
+ .font(.system(size: 11, weight: .medium))
+ .foregroundColor(DS.Colors.textPrimary)
+ .lineLimit(1)
+ .truncationMode(.middle)
+
+ Image(systemName: "chevron.down")
+ .font(.system(size: 9))
+ .foregroundColor(DS.Colors.textTertiary)
+ }
+ .padding(.horizontal, 10)
+ .padding(.vertical, 5)
+ .background(
+ RoundedRectangle(cornerRadius: 6, style: .continuous)
+ .fill(Color.white.opacity(0.06))
+ )
+ .overlay(
+ RoundedRectangle(cornerRadius: 6, style: .continuous)
+ .stroke(DS.Colors.borderSubtle, lineWidth: 0.5)
+ )
}
- .background(
- RoundedRectangle(cornerRadius: 6, style: .continuous)
- .fill(Color.white.opacity(0.06))
- )
- .overlay(
- RoundedRectangle(cornerRadius: 6, style: .continuous)
- .stroke(DS.Colors.borderSubtle, lineWidth: 0.5)
- )
+ .menuStyle(.borderlessButton)
+ .menuIndicator(.hidden)
+ .frame(maxWidth: 200)
+ }
}
.padding(.vertical, 4)
}
@@ -757,5 +971,4 @@ struct CompanionPanelView: View {
return "Responding"
}
}
-
}
diff --git a/leanring-buddy/ElevenLabsTTSClient.swift b/leanring-buddy/ElevenLabsTTSClient.swift
index 35545c9d..f93752ea 100644
--- a/leanring-buddy/ElevenLabsTTSClient.swift
+++ b/leanring-buddy/ElevenLabsTTSClient.swift
@@ -12,7 +12,8 @@ import Foundation
@MainActor
final class ElevenLabsTTSClient {
- private let proxyURL: URL
+ var proxyURL: String
+ private var apiURL: URL { URL(string: proxyURL)! }
private let session: URLSession
/// The audio player for the current TTS playback. Kept alive so the
@@ -20,7 +21,7 @@ final class ElevenLabsTTSClient {
private var audioPlayer: AVAudioPlayer?
init(proxyURL: String) {
- self.proxyURL = URL(string: proxyURL)!
+ self.proxyURL = proxyURL
let configuration = URLSessionConfiguration.default
configuration.timeoutIntervalForRequest = 30
@@ -31,7 +32,7 @@ final class ElevenLabsTTSClient {
/// Sends `text` to ElevenLabs TTS and plays the resulting audio.
/// Throws on network or decoding errors. Cancellation-safe.
func speakText(_ text: String) async throws {
- var request = URLRequest(url: proxyURL)
+ var request = URLRequest(url: apiURL)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.setValue("audio/mpeg", forHTTPHeaderField: "Accept")
diff --git a/leanring-buddy/OpenAIAPI.swift b/leanring-buddy/OpenAIAPI.swift
index d0c3f2ae..951ca71e 100644
--- a/leanring-buddy/OpenAIAPI.swift
+++ b/leanring-buddy/OpenAIAPI.swift
@@ -7,14 +7,14 @@ import Foundation
/// OpenAI API helper for vision analysis
class OpenAIAPI {
- private let apiKey: String
+ var apiKey: String
private let apiURL: URL
- private let model: String
+ var model: String
private let session: URLSession
- init(apiKey: String, model: String = "gpt-5.2-2025-12-11") {
+ init(apiKey: String = "", model: String = "") {
self.apiKey = apiKey
- self.apiURL = URL(string: "https://api.openai.com/v1/chat/completions")!
+ self.apiURL = URL(string: "http://127.0.0.1:1234/v1/chat/completions")!
self.model = model
// Use .default instead of .ephemeral so TLS session tickets are cached.
@@ -32,7 +32,8 @@ class OpenAIAPI {
// Fire a lightweight HEAD request in the background to pre-establish the TLS
// connection. This caches the TLS session ticket so the first real API call
// (which carries a large image payload) doesn't need a cold TLS handshake.
- warmUpTLSConnection()
+ // Disabled for LM Studio as it causes "Unexpected endpoint or method" errors.
+ // warmUpTLSConnection()
}
/// Sends a no-op HEAD request to the API host to establish and cache a TLS session.
@@ -139,4 +140,102 @@ class OpenAIAPI {
let duration = Date().timeIntervalSince(startTime)
return (text: text, duration: duration)
}
+
+ /// Send a request to OpenAI with one or more labeled images progressively, via SSE.
+ func analyzeImageStreaming(
+ images: [(data: Data, label: String)],
+ systemPrompt: String,
+ conversationHistory: [(userPlaceholder: String, assistantResponse: String)] = [],
+ userPrompt: String,
+ onTextChunk: @MainActor @Sendable (String) -> Void
+ ) async throws -> (text: String, duration: TimeInterval) {
+ let startTime = Date()
+
+ // Build request
+ var request = URLRequest(url: apiURL)
+ request.httpMethod = "POST"
+ request.timeoutInterval = 120
+ request.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
+ request.setValue("application/json", forHTTPHeaderField: "Content-Type")
+
+ // Build messages array
+ var messages: [[String: Any]] = []
+
+ // Add system message first
+ messages.append([
+ "role": "system",
+ "content": systemPrompt
+ ])
+
+ // Add conversation history
+ for (userPlaceholder, assistantResponse) in conversationHistory {
+ messages.append(["role": "user", "content": userPlaceholder])
+ messages.append(["role": "assistant", "content": assistantResponse])
+ }
+
+ // Build current message with all labeled images + prompt
+ var contentBlocks: [[String: Any]] = []
+ for image in images {
+ contentBlocks.append([
+ "type": "text",
+ "text": image.label
+ ])
+ contentBlocks.append([
+ "type": "image_url",
+ "image_url": [
+ "url": "data:image/jpeg;base64,\(image.data.base64EncodedString())"
+ ]
+ ])
+ }
+ contentBlocks.append([
+ "type": "text",
+ "text": userPrompt
+ ])
+ messages.append(["role": "user", "content": contentBlocks])
+
+ // Build request body
+ let body: [String: Any] = [
+ "model": model,
+ "max_tokens": 600, // some local models still require max_tokens instead of max_completion_tokens
+ "messages": messages,
+ "stream": true
+ ]
+
+ let bodyData = try JSONSerialization.data(withJSONObject: body)
+ request.httpBody = bodyData
+ let payloadMB = Double(bodyData.count) / 1_048_576.0
+ print("🌐 OpenAI streaming request: \(String(format: "%.1f", payloadMB))MB, \(images.count) image(s)")
+
+ let (result, response) = try await session.bytes(for: request)
+
+ guard let httpResponse = response as? HTTPURLResponse,
+ (200...299).contains(httpResponse.statusCode) else {
+ throw NSError(
+ domain: "OpenAIAPI",
+ code: (response as? HTTPURLResponse)?.statusCode ?? -1,
+ userInfo: [NSLocalizedDescriptionKey: "API Error: Invalid response"]
+ )
+ }
+
+ var fullText = ""
+
+ for try await line in result.lines {
+ guard line.hasPrefix("data: ") else { continue }
+ let jsonString = line.dropFirst(6).trimmingCharacters(in: .whitespaces)
+ guard jsonString != "[DONE]" else { break }
+ guard let data = jsonString.data(using: .utf8) else { continue }
+
+ if let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any],
+ let choices = json["choices"] as? [[String: Any]],
+ let firstChoice = choices.first,
+ let delta = firstChoice["delta"] as? [String: Any],
+ let contentChunk = delta["content"] as? String {
+ fullText += contentChunk
+ await onTextChunk(contentChunk)
+ }
+ }
+
+ let duration = Date().timeIntervalSince(startTime)
+ return (text: fullText, duration: duration)
+ }
}
diff --git a/leanring-buddy/OpenAIAudioTranscriptionProvider.swift b/leanring-buddy/OpenAIAudioTranscriptionProvider.swift
index 75092092..54dab211 100644
--- a/leanring-buddy/OpenAIAudioTranscriptionProvider.swift
+++ b/leanring-buddy/OpenAIAudioTranscriptionProvider.swift
@@ -34,6 +34,7 @@ final class OpenAIAudioTranscriptionProvider: BuddyTranscriptionProvider {
}
func startStreamingSession(
+ languageCode: String,
keyterms: [String],
onTranscriptUpdate: @escaping (String) -> Void,
onFinalTranscriptReady: @escaping (String) -> Void,
@@ -49,6 +50,7 @@ final class OpenAIAudioTranscriptionProvider: BuddyTranscriptionProvider {
apiKey: apiKey,
modelName: modelName,
keyterms: keyterms,
+ languageCode: languageCode,
onTranscriptUpdate: onTranscriptUpdate,
onFinalTranscriptReady: onFinalTranscriptReady,
onError: onError
@@ -69,6 +71,7 @@ private final class OpenAIAudioTranscriptionSession: BuddyStreamingTranscription
private let apiKey: String
private let modelName: String
private let keyterms: [String]
+ private let languageCode: String
private let onTranscriptUpdate: (String) -> Void
private let onFinalTranscriptReady: (String) -> Void
private let onError: (Error) -> Void
@@ -89,6 +92,7 @@ private final class OpenAIAudioTranscriptionSession: BuddyStreamingTranscription
apiKey: String,
modelName: String,
keyterms: [String],
+ languageCode: String,
onTranscriptUpdate: @escaping (String) -> Void,
onFinalTranscriptReady: @escaping (String) -> Void,
onError: @escaping (Error) -> Void
@@ -96,6 +100,7 @@ private final class OpenAIAudioTranscriptionSession: BuddyStreamingTranscription
self.apiKey = apiKey
self.modelName = modelName
self.keyterms = keyterms
+ self.languageCode = languageCode
self.onTranscriptUpdate = onTranscriptUpdate
self.onFinalTranscriptReady = onFinalTranscriptReady
self.onError = onError
@@ -234,7 +239,7 @@ private final class OpenAIAudioTranscriptionSession: BuddyStreamingTranscription
)
requestBodyData.appendMultipartFormField(
named: "language",
- value: "en",
+ value: languageCode,
usingBoundary: boundary
)
requestBodyData.appendMultipartFormField(
diff --git a/leanring-buddy/WorkerEnvironment.swift b/leanring-buddy/WorkerEnvironment.swift
new file mode 100644
index 00000000..88fbaa91
--- /dev/null
+++ b/leanring-buddy/WorkerEnvironment.swift
@@ -0,0 +1,52 @@
+//
+// WorkerEnvironment.swift
+// leanring-buddy
+//
+// Automatically detects if the local development Cloudflare Worker is running.
+// If yes, it connects to localhost out-of-the-box. Otherwise, it falls back to production.
+//
+
+import Foundation
+
+actor WorkerEnvironment {
+ static let shared = WorkerEnvironment()
+
+ private var cachedBaseURL: String?
+
+ // Replace this with your actual production Worker URL if known
+ private let defaultProdURL = "https://your-worker-name.your-subdomain.workers.dev"
+
+ func getBaseURL() async -> String {
+ if let cached = cachedBaseURL {
+ return cached
+ }
+
+ if let saved = UserDefaults.standard.string(forKey: "workerBaseURL"), !saved.isEmpty {
+ cachedBaseURL = saved
+ return saved // Allow optional user override
+ }
+
+ let localURL = "http://localhost:8787"
+ var request = URLRequest(url: URL(string: localURL)!)
+ request.httpMethod = "OPTIONS" // Quick lightweight pre-flight
+ request.timeoutInterval = 0.5 // Fail very fast if it's not running
+
+ do {
+ let (_, response) = try await URLSession.shared.data(for: request)
+ if (response as? HTTPURLResponse) != nil {
+ print("🌐 Auto-detected local worker. Using \(localURL)")
+ cachedBaseURL = localURL
+ return localURL
+ }
+ } catch {
+ print("🌐 Local worker unreachable. Falling back to prod: \(defaultProdURL)")
+ }
+
+ cachedBaseURL = defaultProdURL
+ return defaultProdURL
+ }
+
+ func resetCache() {
+ cachedBaseURL = nil
+ }
+}
\ No newline at end of file
diff --git a/worker/.gitignore b/worker/.gitignore
new file mode 100644
index 00000000..74f12373
--- /dev/null
+++ b/worker/.gitignore
@@ -0,0 +1,2 @@
+wrangler.toml
+.wrangler
diff --git a/worker/package-lock.json b/worker/package-lock.json
index c2383cc1..c3752f47 100644
--- a/worker/package-lock.json
+++ b/worker/package-lock.json
@@ -6,31 +6,28 @@
"": {
"name": "clicky-proxy",
"devDependencies": {
- "wrangler": "^3.0.0"
+ "wrangler": "^4.81.0"
}
},
"node_modules/@cloudflare/kv-asset-handler": {
- "version": "0.3.4",
- "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.4.tgz",
- "integrity": "sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q==",
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz",
+ "integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==",
"dev": true,
"license": "MIT OR Apache-2.0",
- "dependencies": {
- "mime": "^3.0.0"
- },
"engines": {
- "node": ">=16.13"
+ "node": ">=18.0.0"
}
},
"node_modules/@cloudflare/unenv-preset": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.0.2.tgz",
- "integrity": "sha512-nyzYnlZjjV5xT3LizahG1Iu6mnrCaxglJ04rZLpDwlDVDZ7v46lNsfxhV3A/xtfgQuSHmLnc6SVI+KwBpc3Lwg==",
+ "version": "2.16.0",
+ "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.0.tgz",
+ "integrity": "sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg==",
"dev": true,
"license": "MIT OR Apache-2.0",
"peerDependencies": {
- "unenv": "2.0.0-rc.14",
- "workerd": "^1.20250124.0"
+ "unenv": "2.0.0-rc.24",
+ "workerd": "1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0"
},
"peerDependenciesMeta": {
"workerd": {
@@ -39,9 +36,9 @@
}
},
"node_modules/@cloudflare/workerd-darwin-64": {
- "version": "1.20250718.0",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250718.0.tgz",
- "integrity": "sha512-FHf4t7zbVN8yyXgQ/r/GqLPaYZSGUVzeR7RnL28Mwj2djyw2ZergvytVc7fdGcczl6PQh+VKGfZCfUqpJlbi9g==",
+ "version": "1.20260405.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260405.1.tgz",
+ "integrity": "sha512-EbmdBcmeIGogKG4V1odSWQe7z4rHssUD4iaXv0cXA22/MFrzH3iQT0R+FJFyhucGtih/9B9E+6j0QbSQD8xT3w==",
"cpu": [
"x64"
],
@@ -56,9 +53,9 @@
}
},
"node_modules/@cloudflare/workerd-darwin-arm64": {
- "version": "1.20250718.0",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250718.0.tgz",
- "integrity": "sha512-fUiyUJYyqqp4NqJ0YgGtp4WJh/II/YZsUnEb6vVy5Oeas8lUOxnN+ZOJ8N/6/5LQCVAtYCChRiIrBbfhTn5Z8Q==",
+ "version": "1.20260405.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260405.1.tgz",
+ "integrity": "sha512-r44r418bOQtoP+Odu+L/BQM9q5cRSXRd1N167PgZQIo4MlqzTwHO4L0wwXhxbcV/PF46rrQre/uTFS8R0R+xSQ==",
"cpu": [
"arm64"
],
@@ -73,9 +70,9 @@
}
},
"node_modules/@cloudflare/workerd-linux-64": {
- "version": "1.20250718.0",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250718.0.tgz",
- "integrity": "sha512-5+eb3rtJMiEwp08Kryqzzu8d1rUcK+gdE442auo5eniMpT170Dz0QxBrqkg2Z48SFUPYbj+6uknuA5tzdRSUSg==",
+ "version": "1.20260405.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260405.1.tgz",
+ "integrity": "sha512-Aaq3RWnaTCzMBo77wC8fjOx+SFdO/rlcXa6HAf+PJs51LyMISFOBCJKqSlS6Irphen0WHHxFKPHUO9bjfj8g2g==",
"cpu": [
"x64"
],
@@ -90,9 +87,9 @@
}
},
"node_modules/@cloudflare/workerd-linux-arm64": {
- "version": "1.20250718.0",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250718.0.tgz",
- "integrity": "sha512-Aa2M/DVBEBQDdATMbn217zCSFKE+ud/teS+fFS+OQqKABLn0azO2qq6ANAHYOIE6Q3Sq4CxDIQr8lGdaJHwUog==",
+ "version": "1.20260405.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260405.1.tgz",
+ "integrity": "sha512-Lbp9Z2wiMzy3Sji3YwMHK5WDlejsH3jF4swAFEv7+jIf3NowZHga3GzwTypNRmcwnfz/XrqQ7Hc0Ul9OoU/lCw==",
"cpu": [
"arm64"
],
@@ -107,9 +104,9 @@
}
},
"node_modules/@cloudflare/workerd-windows-64": {
- "version": "1.20250718.0",
- "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250718.0.tgz",
- "integrity": "sha512-dY16RXKffmugnc67LTbyjdDHZn5NoTF1yHEf2fN4+OaOnoGSp3N1x77QubTDwqZ9zECWxgQfDLjddcH8dWeFhg==",
+ "version": "1.20260405.1",
+ "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260405.1.tgz",
+ "integrity": "sha512-FhE0kt93kj5JnSPVqi4BAXpQQENyKnuSOoJLd35mkMMGhtPrwv5EsReJdck0S8hUocCBlb+U0RmP8ta6k41HjQ==",
"cpu": [
"x64"
],
@@ -147,34 +144,27 @@
"tslib": "^2.4.0"
}
},
- "node_modules/@esbuild-plugins/node-globals-polyfill": {
- "version": "0.2.3",
- "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz",
- "integrity": "sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==",
- "dev": true,
- "license": "ISC",
- "peerDependencies": {
- "esbuild": "*"
- }
- },
- "node_modules/@esbuild-plugins/node-modules-polyfill": {
- "version": "0.2.2",
- "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.2.2.tgz",
- "integrity": "sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA==",
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
+ "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==",
+ "cpu": [
+ "ppc64"
+ ],
"dev": true,
- "license": "ISC",
- "dependencies": {
- "escape-string-regexp": "^4.0.0",
- "rollup-plugin-node-polyfills": "^0.2.1"
- },
- "peerDependencies": {
- "esbuild": "*"
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-arm": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz",
- "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz",
+ "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==",
"cpu": [
"arm"
],
@@ -185,13 +175,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-arm64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz",
- "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz",
+ "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==",
"cpu": [
"arm64"
],
@@ -202,13 +192,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/android-x64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz",
- "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz",
+ "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==",
"cpu": [
"x64"
],
@@ -219,13 +209,13 @@
"android"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/darwin-arm64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz",
- "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz",
+ "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==",
"cpu": [
"arm64"
],
@@ -236,13 +226,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/darwin-x64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz",
- "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz",
+ "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==",
"cpu": [
"x64"
],
@@ -253,13 +243,13 @@
"darwin"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/freebsd-arm64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz",
- "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==",
"cpu": [
"arm64"
],
@@ -270,13 +260,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/freebsd-x64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz",
- "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz",
+ "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==",
"cpu": [
"x64"
],
@@ -287,13 +277,13 @@
"freebsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-arm": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz",
- "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz",
+ "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==",
"cpu": [
"arm"
],
@@ -304,13 +294,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-arm64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz",
- "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz",
+ "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==",
"cpu": [
"arm64"
],
@@ -321,13 +311,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-ia32": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz",
- "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz",
+ "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==",
"cpu": [
"ia32"
],
@@ -338,13 +328,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-loong64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz",
- "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz",
+ "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==",
"cpu": [
"loong64"
],
@@ -355,13 +345,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-mips64el": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz",
- "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz",
+ "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==",
"cpu": [
"mips64el"
],
@@ -372,13 +362,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-ppc64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz",
- "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz",
+ "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==",
"cpu": [
"ppc64"
],
@@ -389,13 +379,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-riscv64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz",
- "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz",
+ "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==",
"cpu": [
"riscv64"
],
@@ -406,13 +396,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-s390x": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz",
- "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz",
+ "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==",
"cpu": [
"s390x"
],
@@ -423,13 +413,13 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/linux-x64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz",
- "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz",
+ "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==",
"cpu": [
"x64"
],
@@ -440,13 +430,30 @@
"linux"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/netbsd-x64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz",
- "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==",
"cpu": [
"x64"
],
@@ -457,13 +464,30 @@
"netbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz",
+ "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/openbsd-x64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz",
- "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz",
+ "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==",
"cpu": [
"x64"
],
@@ -474,13 +498,30 @@
"openbsd"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz",
+ "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
}
},
"node_modules/@esbuild/sunos-x64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz",
- "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz",
+ "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==",
"cpu": [
"x64"
],
@@ -491,13 +532,13 @@
"sunos"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-arm64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz",
- "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz",
+ "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==",
"cpu": [
"arm64"
],
@@ -508,13 +549,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-ia32": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz",
- "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz",
+ "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==",
"cpu": [
"ia32"
],
@@ -525,13 +566,13 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
"node_modules/@esbuild/win32-x64": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz",
- "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz",
+ "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==",
"cpu": [
"x64"
],
@@ -542,23 +583,23 @@
"win32"
],
"engines": {
- "node": ">=12"
+ "node": ">=18"
}
},
- "node_modules/@fastify/busboy": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz",
- "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==",
+ "node_modules/@img/colour": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
+ "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=14"
+ "node": ">=18"
}
},
"node_modules/@img/sharp-darwin-arm64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz",
- "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
"cpu": [
"arm64"
],
@@ -575,13 +616,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-darwin-arm64": "1.0.4"
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
}
},
"node_modules/@img/sharp-darwin-x64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz",
- "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
"cpu": [
"x64"
],
@@ -598,13 +639,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-darwin-x64": "1.0.4"
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
}
},
"node_modules/@img/sharp-libvips-darwin-arm64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz",
- "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
"cpu": [
"arm64"
],
@@ -619,9 +660,9 @@
}
},
"node_modules/@img/sharp-libvips-darwin-x64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz",
- "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
"cpu": [
"x64"
],
@@ -636,9 +677,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-arm": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz",
- "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
"cpu": [
"arm"
],
@@ -656,9 +697,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-arm64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz",
- "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
"cpu": [
"arm64"
],
@@ -675,10 +716,50 @@
"url": "https://opencollective.com/libvips"
}
},
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
"node_modules/@img/sharp-libvips-linux-s390x": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz",
- "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
"cpu": [
"s390x"
],
@@ -696,9 +777,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-x64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz",
- "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
"cpu": [
"x64"
],
@@ -716,9 +797,9 @@
}
},
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz",
- "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
"cpu": [
"arm64"
],
@@ -736,9 +817,9 @@
}
},
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz",
- "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
"cpu": [
"x64"
],
@@ -756,9 +837,9 @@
}
},
"node_modules/@img/sharp-linux-arm": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz",
- "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
"cpu": [
"arm"
],
@@ -778,13 +859,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-arm": "1.0.5"
+ "@img/sharp-libvips-linux-arm": "1.2.4"
}
},
"node_modules/@img/sharp-linux-arm64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz",
- "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
"cpu": [
"arm64"
],
@@ -804,13 +885,65 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-arm64": "1.0.4"
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "libc": [
+ "glibc"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
}
},
"node_modules/@img/sharp-linux-s390x": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz",
- "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
"cpu": [
"s390x"
],
@@ -830,13 +963,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-s390x": "1.0.4"
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
}
},
"node_modules/@img/sharp-linux-x64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz",
- "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
"cpu": [
"x64"
],
@@ -856,13 +989,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-x64": "1.0.4"
+ "@img/sharp-libvips-linux-x64": "1.2.4"
}
},
"node_modules/@img/sharp-linuxmusl-arm64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz",
- "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
"cpu": [
"arm64"
],
@@ -882,13 +1015,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-arm64": "1.0.4"
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
}
},
"node_modules/@img/sharp-linuxmusl-x64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz",
- "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
"cpu": [
"x64"
],
@@ -908,13 +1041,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-x64": "1.0.4"
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
}
},
"node_modules/@img/sharp-wasm32": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz",
- "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
"cpu": [
"wasm32"
],
@@ -922,7 +1055,7 @@
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true,
"dependencies": {
- "@emnapi/runtime": "^1.2.0"
+ "@emnapi/runtime": "^1.7.0"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
@@ -931,10 +1064,30 @@
"url": "https://opencollective.com/libvips"
}
},
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
"node_modules/@img/sharp-win32-ia32": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz",
- "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
"cpu": [
"ia32"
],
@@ -952,9 +1105,9 @@
}
},
"node_modules/@img/sharp-win32-x64": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz",
- "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
"cpu": [
"x64"
],
@@ -999,134 +1152,100 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
- "node_modules/acorn": {
- "version": "8.14.0",
- "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz",
- "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "acorn": "bin/acorn"
- },
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/acorn-walk": {
- "version": "8.3.2",
- "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz",
- "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==",
+ "node_modules/@poppinss/colors": {
+ "version": "4.1.6",
+ "resolved": "https://registry.npmjs.org/@poppinss/colors/-/colors-4.1.6.tgz",
+ "integrity": "sha512-H9xkIdFswbS8n1d6vmRd8+c10t2Qe+rZITbbDHHkQixH5+2x1FDGmi/0K+WgWiqQFKPSlIYB7jlH6Kpfn6Fleg==",
"dev": true,
"license": "MIT",
- "engines": {
- "node": ">=0.4.0"
+ "dependencies": {
+ "kleur": "^4.1.5"
}
},
- "node_modules/as-table": {
- "version": "1.0.55",
- "resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz",
- "integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==",
+ "node_modules/@poppinss/dumper": {
+ "version": "0.6.5",
+ "resolved": "https://registry.npmjs.org/@poppinss/dumper/-/dumper-0.6.5.tgz",
+ "integrity": "sha512-NBdYIb90J7LfOI32dOewKI1r7wnkiH6m920puQ3qHUeZkxNkQiFnXVWoE6YtFSv6QOiPPf7ys6i+HWWecDz7sw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "printable-characters": "^1.0.42"
+ "@poppinss/colors": "^4.1.5",
+ "@sindresorhus/is": "^7.0.2",
+ "supports-color": "^10.0.0"
}
},
- "node_modules/blake3-wasm": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz",
- "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==",
+ "node_modules/@poppinss/exception": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@poppinss/exception/-/exception-1.2.3.tgz",
+ "integrity": "sha512-dCED+QRChTVatE9ibtoaxc+WkdzOSjYTKi/+uacHWIsfodVfpsueo3+DKpgU5Px8qXjgmXkSvhXvSCz3fnP9lw==",
"dev": true,
"license": "MIT"
},
- "node_modules/color": {
- "version": "4.2.3",
- "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
- "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "node_modules/@sindresorhus/is": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz",
+ "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==",
"dev": true,
"license": "MIT",
- "optional": true,
- "dependencies": {
- "color-convert": "^2.0.1",
- "color-string": "^1.9.0"
- },
"engines": {
- "node": ">=12.5.0"
- }
- },
- "node_modules/color-convert": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
- "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "color-name": "~1.1.4"
+ "node": ">=18"
},
- "engines": {
- "node": ">=7.0.0"
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
}
},
- "node_modules/color-name": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
- "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "node_modules/@speed-highlight/core": {
+ "version": "1.2.15",
+ "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.15.tgz",
+ "integrity": "sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==",
"dev": true,
- "license": "MIT",
- "optional": true
+ "license": "CC0-1.0"
},
- "node_modules/color-string": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
- "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "node_modules/blake3-wasm": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz",
+ "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "color-name": "^1.0.0",
- "simple-swizzle": "^0.2.2"
- }
+ "license": "MIT"
},
"node_modules/cookie": {
- "version": "0.7.2",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
- "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz",
+ "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">= 0.6"
+ "node": ">=18"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
}
},
- "node_modules/data-uri-to-buffer": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz",
- "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/defu": {
- "version": "6.1.6",
- "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.6.tgz",
- "integrity": "sha512-f8mefEW4WIVg4LckePx3mALjQSPQgFlg9U8yaPdlsbdYcHQyj9n2zL2LJEA52smeYxOvmd/nB7TpMtHGMTHcug==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/detect-libc": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
"integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
"dev": true,
"license": "Apache-2.0",
- "optional": true,
"engines": {
"node": ">=8"
}
},
+ "node_modules/error-stack-parser-es": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/error-stack-parser-es/-/error-stack-parser-es-1.0.5.tgz",
+ "integrity": "sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
+ },
"node_modules/esbuild": {
- "version": "0.17.19",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz",
- "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==",
+ "version": "0.27.3",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz",
+ "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==",
"dev": true,
"hasInstallScript": true,
"license": "MIT",
@@ -1134,73 +1253,37 @@
"esbuild": "bin/esbuild"
},
"engines": {
- "node": ">=12"
+ "node": ">=18"
},
"optionalDependencies": {
- "@esbuild/android-arm": "0.17.19",
- "@esbuild/android-arm64": "0.17.19",
- "@esbuild/android-x64": "0.17.19",
- "@esbuild/darwin-arm64": "0.17.19",
- "@esbuild/darwin-x64": "0.17.19",
- "@esbuild/freebsd-arm64": "0.17.19",
- "@esbuild/freebsd-x64": "0.17.19",
- "@esbuild/linux-arm": "0.17.19",
- "@esbuild/linux-arm64": "0.17.19",
- "@esbuild/linux-ia32": "0.17.19",
- "@esbuild/linux-loong64": "0.17.19",
- "@esbuild/linux-mips64el": "0.17.19",
- "@esbuild/linux-ppc64": "0.17.19",
- "@esbuild/linux-riscv64": "0.17.19",
- "@esbuild/linux-s390x": "0.17.19",
- "@esbuild/linux-x64": "0.17.19",
- "@esbuild/netbsd-x64": "0.17.19",
- "@esbuild/openbsd-x64": "0.17.19",
- "@esbuild/sunos-x64": "0.17.19",
- "@esbuild/win32-arm64": "0.17.19",
- "@esbuild/win32-ia32": "0.17.19",
- "@esbuild/win32-x64": "0.17.19"
- }
- },
- "node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/estree-walker": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz",
- "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/exit-hook": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz",
- "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "@esbuild/aix-ppc64": "0.27.3",
+ "@esbuild/android-arm": "0.27.3",
+ "@esbuild/android-arm64": "0.27.3",
+ "@esbuild/android-x64": "0.27.3",
+ "@esbuild/darwin-arm64": "0.27.3",
+ "@esbuild/darwin-x64": "0.27.3",
+ "@esbuild/freebsd-arm64": "0.27.3",
+ "@esbuild/freebsd-x64": "0.27.3",
+ "@esbuild/linux-arm": "0.27.3",
+ "@esbuild/linux-arm64": "0.27.3",
+ "@esbuild/linux-ia32": "0.27.3",
+ "@esbuild/linux-loong64": "0.27.3",
+ "@esbuild/linux-mips64el": "0.27.3",
+ "@esbuild/linux-ppc64": "0.27.3",
+ "@esbuild/linux-riscv64": "0.27.3",
+ "@esbuild/linux-s390x": "0.27.3",
+ "@esbuild/linux-x64": "0.27.3",
+ "@esbuild/netbsd-arm64": "0.27.3",
+ "@esbuild/netbsd-x64": "0.27.3",
+ "@esbuild/openbsd-arm64": "0.27.3",
+ "@esbuild/openbsd-x64": "0.27.3",
+ "@esbuild/openharmony-arm64": "0.27.3",
+ "@esbuild/sunos-x64": "0.27.3",
+ "@esbuild/win32-arm64": "0.27.3",
+ "@esbuild/win32-ia32": "0.27.3",
+ "@esbuild/win32-x64": "0.27.3"
}
},
- "node_modules/exsolve": {
- "version": "1.0.8",
- "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz",
- "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/fsevents": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -1216,98 +1299,37 @@
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
}
},
- "node_modules/get-source": {
- "version": "2.0.12",
- "resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz",
- "integrity": "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==",
- "dev": true,
- "license": "Unlicense",
- "dependencies": {
- "data-uri-to-buffer": "^2.0.0",
- "source-map": "^0.6.1"
- }
- },
- "node_modules/glob-to-regexp": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz",
- "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==",
- "dev": true,
- "license": "BSD-2-Clause"
- },
- "node_modules/is-arrayish": {
- "version": "0.3.4",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz",
- "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/magic-string": {
- "version": "0.25.9",
- "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz",
- "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "sourcemap-codec": "^1.4.8"
- }
- },
- "node_modules/mime": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz",
- "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==",
+ "node_modules/kleur": {
+ "version": "4.1.5",
+ "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz",
+ "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==",
"dev": true,
"license": "MIT",
- "bin": {
- "mime": "cli.js"
- },
"engines": {
- "node": ">=10.0.0"
+ "node": ">=6"
}
},
"node_modules/miniflare": {
- "version": "3.20250718.3",
- "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20250718.3.tgz",
- "integrity": "sha512-JuPrDJhwLrNLEJiNLWO7ZzJrv/Vv9kZuwMYCfv0LskQDM6Eonw4OvywO3CH/wCGjgHzha/qyjUh8JQ068TjDgQ==",
+ "version": "4.20260405.0",
+ "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260405.0.tgz",
+ "integrity": "sha512-tpr4XdWMq7zFdsHH+CS0XS47nQzlRZH0rMJ1vobOZbkrs3cIj7qbD40ON616hDnzHxwqwB2qKHzmmuj6oRisSQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"@cspotcode/source-map-support": "0.8.1",
- "acorn": "8.14.0",
- "acorn-walk": "8.3.2",
- "exit-hook": "2.2.1",
- "glob-to-regexp": "0.4.1",
- "stoppable": "1.1.0",
- "undici": "^5.28.5",
- "workerd": "1.20250718.0",
+ "sharp": "^0.34.5",
+ "undici": "7.24.4",
+ "workerd": "1.20260405.1",
"ws": "8.18.0",
- "youch": "3.3.4",
- "zod": "3.22.3"
+ "youch": "4.1.0-beta.10"
},
"bin": {
"miniflare": "bootstrap.js"
},
"engines": {
- "node": ">=16.13"
- }
- },
- "node_modules/mustache": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
- "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "mustache": "bin/mustache"
+ "node": ">=18.0.0"
}
},
- "node_modules/ohash": {
- "version": "2.0.11",
- "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz",
- "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/path-to-regexp": {
"version": "6.3.0",
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz",
@@ -1322,53 +1344,12 @@
"dev": true,
"license": "MIT"
},
- "node_modules/printable-characters": {
- "version": "1.0.42",
- "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz",
- "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==",
- "dev": true,
- "license": "Unlicense"
- },
- "node_modules/rollup-plugin-inject": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz",
- "integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==",
- "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "estree-walker": "^0.6.1",
- "magic-string": "^0.25.3",
- "rollup-pluginutils": "^2.8.1"
- }
- },
- "node_modules/rollup-plugin-node-polyfills": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz",
- "integrity": "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "rollup-plugin-inject": "^3.0.0"
- }
- },
- "node_modules/rollup-pluginutils": {
- "version": "2.8.2",
- "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz",
- "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "estree-walker": "^0.6.1"
- }
- },
"node_modules/semver": {
"version": "7.7.4",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
"integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
"dev": true,
"license": "ISC",
- "optional": true,
"bin": {
"semver": "bin/semver.js"
},
@@ -1377,17 +1358,16 @@
}
},
"node_modules/sharp": {
- "version": "0.33.5",
- "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz",
- "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
"dev": true,
"hasInstallScript": true,
"license": "Apache-2.0",
- "optional": true,
"dependencies": {
- "color": "^4.2.3",
- "detect-libc": "^2.0.3",
- "semver": "^7.6.3"
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
@@ -1396,76 +1376,43 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-darwin-arm64": "0.33.5",
- "@img/sharp-darwin-x64": "0.33.5",
- "@img/sharp-libvips-darwin-arm64": "1.0.4",
- "@img/sharp-libvips-darwin-x64": "1.0.4",
- "@img/sharp-libvips-linux-arm": "1.0.5",
- "@img/sharp-libvips-linux-arm64": "1.0.4",
- "@img/sharp-libvips-linux-s390x": "1.0.4",
- "@img/sharp-libvips-linux-x64": "1.0.4",
- "@img/sharp-libvips-linuxmusl-arm64": "1.0.4",
- "@img/sharp-libvips-linuxmusl-x64": "1.0.4",
- "@img/sharp-linux-arm": "0.33.5",
- "@img/sharp-linux-arm64": "0.33.5",
- "@img/sharp-linux-s390x": "0.33.5",
- "@img/sharp-linux-x64": "0.33.5",
- "@img/sharp-linuxmusl-arm64": "0.33.5",
- "@img/sharp-linuxmusl-x64": "0.33.5",
- "@img/sharp-wasm32": "0.33.5",
- "@img/sharp-win32-ia32": "0.33.5",
- "@img/sharp-win32-x64": "0.33.5"
- }
- },
- "node_modules/simple-swizzle": {
- "version": "0.2.4",
- "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz",
- "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==",
+ "@img/sharp-darwin-arm64": "0.34.5",
+ "@img/sharp-darwin-x64": "0.34.5",
+ "@img/sharp-libvips-darwin-arm64": "1.2.4",
+ "@img/sharp-libvips-darwin-x64": "1.2.4",
+ "@img/sharp-libvips-linux-arm": "1.2.4",
+ "@img/sharp-libvips-linux-arm64": "1.2.4",
+ "@img/sharp-libvips-linux-ppc64": "1.2.4",
+ "@img/sharp-libvips-linux-riscv64": "1.2.4",
+ "@img/sharp-libvips-linux-s390x": "1.2.4",
+ "@img/sharp-libvips-linux-x64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4",
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4",
+ "@img/sharp-linux-arm": "0.34.5",
+ "@img/sharp-linux-arm64": "0.34.5",
+ "@img/sharp-linux-ppc64": "0.34.5",
+ "@img/sharp-linux-riscv64": "0.34.5",
+ "@img/sharp-linux-s390x": "0.34.5",
+ "@img/sharp-linux-x64": "0.34.5",
+ "@img/sharp-linuxmusl-arm64": "0.34.5",
+ "@img/sharp-linuxmusl-x64": "0.34.5",
+ "@img/sharp-wasm32": "0.34.5",
+ "@img/sharp-win32-arm64": "0.34.5",
+ "@img/sharp-win32-ia32": "0.34.5",
+ "@img/sharp-win32-x64": "0.34.5"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "10.2.2",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz",
+ "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==",
"dev": true,
"license": "MIT",
- "optional": true,
- "dependencies": {
- "is-arrayish": "^0.3.1"
- }
- },
- "node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "license": "BSD-3-Clause",
"engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/sourcemap-codec": {
- "version": "1.4.8",
- "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
- "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
- "deprecated": "Please use @jridgewell/sourcemap-codec instead",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/stacktracey": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.2.0.tgz",
- "integrity": "sha512-ETyQEz+CzXiLjEbyJqpbp+/T79RQD/6wqFucRBIlVNZfYq2Ay7wbretD4cxpbymZlaPWx58aIhPEY1Cr8DlVvg==",
- "dev": true,
- "license": "Unlicense",
- "dependencies": {
- "as-table": "^1.0.36",
- "get-source": "^2.0.12"
- }
- },
- "node_modules/stoppable": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz",
- "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4",
- "npm": ">=6"
+ "node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
"node_modules/tslib": {
@@ -1476,44 +1423,30 @@
"license": "0BSD",
"optional": true
},
- "node_modules/ufo": {
- "version": "1.6.3",
- "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz",
- "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/undici": {
- "version": "5.29.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz",
- "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==",
+ "version": "7.24.4",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz",
+ "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "@fastify/busboy": "^2.0.0"
- },
"engines": {
- "node": ">=14.0"
+ "node": ">=20.18.1"
}
},
"node_modules/unenv": {
- "version": "2.0.0-rc.14",
- "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.14.tgz",
- "integrity": "sha512-od496pShMen7nOy5VmVJCnq8rptd45vh6Nx/r2iPbrba6pa6p+tS2ywuIHRZ/OBvSbQZB0kWvpO9XBNVFXHD3Q==",
+ "version": "2.0.0-rc.24",
+ "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.24.tgz",
+ "integrity": "sha512-i7qRCmY42zmCwnYlh9H2SvLEypEFGye5iRmEMKjcGi7zk9UquigRjFtTLz0TYqr0ZGLZhaMHl/foy1bZR+Cwlw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "defu": "^6.1.4",
- "exsolve": "^1.0.1",
- "ohash": "^2.0.10",
- "pathe": "^2.0.3",
- "ufo": "^1.5.4"
+ "pathe": "^2.0.3"
}
},
"node_modules/workerd": {
- "version": "1.20250718.0",
- "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250718.0.tgz",
- "integrity": "sha512-kqkIJP/eOfDlUyBzU7joBg+tl8aB25gEAGqDap+nFWb+WHhnooxjGHgxPBy3ipw2hnShPFNOQt5lFRxbwALirg==",
+ "version": "1.20260405.1",
+ "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260405.1.tgz",
+ "integrity": "sha512-bSaRWCv9iO8/FWpgZRjHLGZLolX5s1AErRSYaTECMMHOZKuCbl2+ehnSyc+ZZ/70y+9owADmN6HoYEWvBlJdYw==",
"dev": true,
"hasInstallScript": true,
"license": "Apache-2.0",
@@ -1524,44 +1457,41 @@
"node": ">=16"
},
"optionalDependencies": {
- "@cloudflare/workerd-darwin-64": "1.20250718.0",
- "@cloudflare/workerd-darwin-arm64": "1.20250718.0",
- "@cloudflare/workerd-linux-64": "1.20250718.0",
- "@cloudflare/workerd-linux-arm64": "1.20250718.0",
- "@cloudflare/workerd-windows-64": "1.20250718.0"
+ "@cloudflare/workerd-darwin-64": "1.20260405.1",
+ "@cloudflare/workerd-darwin-arm64": "1.20260405.1",
+ "@cloudflare/workerd-linux-64": "1.20260405.1",
+ "@cloudflare/workerd-linux-arm64": "1.20260405.1",
+ "@cloudflare/workerd-windows-64": "1.20260405.1"
}
},
"node_modules/wrangler": {
- "version": "3.114.17",
- "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.114.17.tgz",
- "integrity": "sha512-tAvf7ly+tB+zwwrmjsCyJ2pJnnc7SZhbnNwXbH+OIdVas3zTSmjcZOjmLKcGGptssAA3RyTKhcF9BvKZzMUycA==",
+ "version": "4.81.0",
+ "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.81.0.tgz",
+ "integrity": "sha512-9fLPDuDcb8Nu6iXrl5E3HGYt3TVhQr/UvqtTvWr9Nl1X7PlQrmWMwQCfSioqN8VHYyQCyESV5jQsoKg8Sx+sEA==",
"dev": true,
"license": "MIT OR Apache-2.0",
"dependencies": {
- "@cloudflare/kv-asset-handler": "0.3.4",
- "@cloudflare/unenv-preset": "2.0.2",
- "@esbuild-plugins/node-globals-polyfill": "0.2.3",
- "@esbuild-plugins/node-modules-polyfill": "0.2.2",
+ "@cloudflare/kv-asset-handler": "0.4.2",
+ "@cloudflare/unenv-preset": "2.16.0",
"blake3-wasm": "2.1.5",
- "esbuild": "0.17.19",
- "miniflare": "3.20250718.3",
+ "esbuild": "0.27.3",
+ "miniflare": "4.20260405.0",
"path-to-regexp": "6.3.0",
- "unenv": "2.0.0-rc.14",
- "workerd": "1.20250718.0"
+ "unenv": "2.0.0-rc.24",
+ "workerd": "1.20260405.1"
},
"bin": {
"wrangler": "bin/wrangler.js",
"wrangler2": "bin/wrangler.js"
},
"engines": {
- "node": ">=16.17.0"
+ "node": ">=20.3.0"
},
"optionalDependencies": {
- "fsevents": "~2.3.2",
- "sharp": "^0.33.5"
+ "fsevents": "~2.3.2"
},
"peerDependencies": {
- "@cloudflare/workers-types": "^4.20250408.0"
+ "@cloudflare/workers-types": "^4.20260405.1"
},
"peerDependenciesMeta": {
"@cloudflare/workers-types": {
@@ -1592,25 +1522,28 @@
}
},
"node_modules/youch": {
- "version": "3.3.4",
- "resolved": "https://registry.npmjs.org/youch/-/youch-3.3.4.tgz",
- "integrity": "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==",
+ "version": "4.1.0-beta.10",
+ "resolved": "https://registry.npmjs.org/youch/-/youch-4.1.0-beta.10.tgz",
+ "integrity": "sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "cookie": "^0.7.1",
- "mustache": "^4.2.0",
- "stacktracey": "^2.1.8"
+ "@poppinss/colors": "^4.1.5",
+ "@poppinss/dumper": "^0.6.4",
+ "@speed-highlight/core": "^1.2.7",
+ "cookie": "^1.0.2",
+ "youch-core": "^0.3.3"
}
},
- "node_modules/zod": {
- "version": "3.22.3",
- "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz",
- "integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==",
+ "node_modules/youch-core": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/youch-core/-/youch-core-0.3.3.tgz",
+ "integrity": "sha512-ho7XuGjLaJ2hWHoK8yFnsUGy2Y5uDpqSTq1FkHLK4/oqKtyUU1AFbOOxY4IpC9f0fTLjwYbslUz0Po5BpD1wrA==",
"dev": true,
"license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/colinhacks"
+ "dependencies": {
+ "@poppinss/exception": "^1.2.2",
+ "error-stack-parser-es": "^1.0.5"
}
}
}
diff --git a/worker/package.json b/worker/package.json
index 445d90a6..857b744e 100644
--- a/worker/package.json
+++ b/worker/package.json
@@ -6,6 +6,6 @@
"deploy": "wrangler deploy"
},
"devDependencies": {
- "wrangler": "^3.0.0"
+ "wrangler": "^4.81.0"
}
}
diff --git a/worker/wrangler.toml b/worker/wrangler.toml
deleted file mode 100644
index b4bdbf38..00000000
--- a/worker/wrangler.toml
+++ /dev/null
@@ -1,6 +0,0 @@
-name = "clicky-proxy"
-main = "src/index.ts"
-compatibility_date = "2024-01-01"
-
-[vars]
-ELEVENLABS_VOICE_ID = "kPzsL2i3teMYv0FxEYQ6"