Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Cotabby.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,14 @@
6014B31E2570EFFE45557E33 /* TickMarkSlider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 67586807ACE8EB13C9014535 /* TickMarkSlider.swift */; };
6106B16C0DBA94EBF838D93E /* PermissionOverlayTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E6423D6CC8CC371D2DA899DE /* PermissionOverlayTracker.swift */; };
61EC9D635D416115E7C96E0F /* PermissionOverlayWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 92C6EB9FDA48ADF425A116A9 /* PermissionOverlayWindowController.swift */; };
63054CBDCA87560130BF5ADC /* ExtendedContextTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 54BC85605541E913EE57B258 /* ExtendedContextTests.swift */; };
644EEF959D07D54CC779BBF6 /* SettingsCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3350EDE01ED5125520C79D53 /* SettingsCoordinator.swift */; };
65478B0DABF5460C32D4C458 /* ModelFileValidatorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A829F28F01FAE76CA7244BBC /* ModelFileValidatorTests.swift */; };
66C23A7C2FCDE0266FF425F8 /* ApplicationBundleMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = 352AF5B2834FEE1F597394E4 /* ApplicationBundleMetadata.swift */; };
66D9E37B12A9265D4733E72E /* LlamaRuntimeCore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 944065A858D9BC936CB12B23 /* LlamaRuntimeCore.swift */; };
6955C3A4D7AB3EEF7FA7C469 /* InputSuppressionController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2D1F9CEBAB0F330F8E7B61D8 /* InputSuppressionController.swift */; };
6AE0B46FB52D189D94E1F79A /* WordCountFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1E0513E3B23937B099A3CFF2 /* WordCountFormatterTests.swift */; };
6B0186B9F3E6F654296B6D76 /* AdvancedPaneView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7E644072C123DC2D2B40274D /* AdvancedPaneView.swift */; };
6D0E79CF3C1A8CE53046FCE5 /* AXTextGeometryResolverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C046CB4F3CB4BFE9391DB5DE /* AXTextGeometryResolverTests.swift */; };
6DD1E22151571E1A22FF22F4 /* FoundationModelSuggestionEngine.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5664E34B23FBDF69292FEF43 /* FoundationModelSuggestionEngine.swift */; };
6E01052209B73D7361C12CEF /* ClipboardContentDistiller.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96495E4147D828C0B1B22765 /* ClipboardContentDistiller.swift */; };
Expand Down Expand Up @@ -283,6 +285,7 @@
53CF416511099C6818110F01 /* CompletionRenderModePolicy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CompletionRenderModePolicy.swift; sourceTree = "<group>"; };
54150A507B03221F137D539B /* MirrorOverlayLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MirrorOverlayLayout.swift; sourceTree = "<group>"; };
5484C8A04B9C00CF79D589EB /* ScreenFrameReader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScreenFrameReader.swift; sourceTree = "<group>"; };
54BC85605541E913EE57B258 /* ExtendedContextTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExtendedContextTests.swift; sourceTree = "<group>"; };
54EF3C7F5D9D6F3FA50FD51C /* ContextBuffer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextBuffer.swift; sourceTree = "<group>"; };
5664E34B23FBDF69292FEF43 /* FoundationModelSuggestionEngine.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FoundationModelSuggestionEngine.swift; sourceTree = "<group>"; };
58C0F017699EE44C81C095CA /* TagsInputView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TagsInputView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -316,6 +319,7 @@
78E280F4F39A9D86840800D2 /* SuggestionCoordinator+Lifecycle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "SuggestionCoordinator+Lifecycle.swift"; sourceTree = "<group>"; };
78E49BDA7F3A42455C4C5350 /* HuggingFaceModelBrowserView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HuggingFaceModelBrowserView.swift; sourceTree = "<group>"; };
7E44E393DD58B978B1EAB6CF /* GhostTextColorPreset.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GhostTextColorPreset.swift; sourceTree = "<group>"; };
7E644072C123DC2D2B40274D /* AdvancedPaneView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdvancedPaneView.swift; sourceTree = "<group>"; };
7F4C4A7EAF886E0CC945BFEF /* TerminalAppDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalAppDetector.swift; sourceTree = "<group>"; };
815F2ABAF6AB75DA3AFBBCEF /* WordCountFormatter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WordCountFormatter.swift; sourceTree = "<group>"; };
82F7F7355967725162DF2D1B /* CustomRulesEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRulesEditor.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -503,6 +507,7 @@
children = (
A3FA53BBC3D81503C1D17477 /* AboutPaneView.swift */,
2B7A28471B8526C2693FFF65 /* AcknowledgementsView.swift */,
7E644072C123DC2D2B40274D /* AdvancedPaneView.swift */,
D9C1C921A1CDA2ADFC39EA01 /* AppsPaneView.swift */,
FC9ECD5408B0F5708149B5C0 /* EngineAndModelPaneView.swift */,
07480CE96ED0EBD94817C6B1 /* GeneralPaneView.swift */,
Expand Down Expand Up @@ -650,6 +655,7 @@
75396860978E81EFAA506CD4 /* EmojiQueryRunTests.swift */,
723E1EFA85D2E61B6C5F33E8 /* EmojiTriggerStateMachineTests.swift */,
EE8BB19D8EC9A75CD3458A6B /* EmojiVariantResolverTests.swift */,
54BC85605541E913EE57B258 /* ExtendedContextTests.swift */,
D4F6D5F94B238F7B4BE7C247 /* FocusCapabilityResolverTests.swift */,
273B4DC844F79B4BE2C8910F /* FocusPollBackoffTests.swift */,
BA705EDFE1C41294F0E381F1 /* FocusSnapshotResolverSelectionTests.swift */,
Expand Down Expand Up @@ -943,6 +949,7 @@
DDEDCBAA2196303455F6926A /* AcceptanceModePickerView.swift in Sources */,
0A658BF137DBD0898E40B87F /* AcknowledgementsView.swift in Sources */,
26E0331E9E2F92FAE531BDEE /* ActivationIndicatorController.swift in Sources */,
6B0186B9F3E6F654296B6D76 /* AdvancedPaneView.swift in Sources */,
0A3443AEE6540F11E5E6BF8F /* AppDelegate.swift in Sources */,
C4C6734678797669055988E0 /* AppUpdateManager.swift in Sources */,
66C23A7C2FCDE0266FF425F8 /* ApplicationBundleMetadata.swift in Sources */,
Expand Down Expand Up @@ -1110,6 +1117,7 @@
0D15CBF45EB1DB725B9F1A6A /* EmojiQueryRunTests.swift in Sources */,
ED0843752B297D7E9DB2C468 /* EmojiTriggerStateMachineTests.swift in Sources */,
C9B815652CED38966C53A5E8 /* EmojiVariantResolverTests.swift in Sources */,
63054CBDCA87560130BF5ADC /* ExtendedContextTests.swift in Sources */,
C71B594433F3B411CAE5DE7E /* FocusCapabilityResolverTests.swift in Sources */,
A147C5EC3F2214A670F7556E /* FocusPollBackoffTests.swift in Sources */,
156E6AB3D24134EEC29FDB93 /* FocusSnapshotResolverSelectionTests.swift in Sources */,
Expand Down
8 changes: 8 additions & 0 deletions Cotabby/App/Coordinators/SettingsCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ final class SettingsCoordinator: NSObject, NSWindowDelegate {
private let runtimeModel: RuntimeBootstrapModel
private let modelDownloadManager: ModelDownloadManager
private let huggingFaceSearchService: HuggingFaceSearchService
private let suggestionEngine: any SuggestionGenerating
private let configuration: SuggestionConfiguration
private let onShowWelcome: () -> Void

private var settingsWindowController: NSWindowController?
Expand All @@ -32,6 +34,8 @@ final class SettingsCoordinator: NSObject, NSWindowDelegate {
runtimeModel: RuntimeBootstrapModel,
modelDownloadManager: ModelDownloadManager,
huggingFaceSearchService: HuggingFaceSearchService,
suggestionEngine: any SuggestionGenerating,
configuration: SuggestionConfiguration,
onShowWelcome: @escaping () -> Void
) {
self.appUpdateManager = appUpdateManager
Expand All @@ -42,6 +46,8 @@ final class SettingsCoordinator: NSObject, NSWindowDelegate {
self.runtimeModel = runtimeModel
self.modelDownloadManager = modelDownloadManager
self.huggingFaceSearchService = huggingFaceSearchService
self.suggestionEngine = suggestionEngine
self.configuration = configuration
self.onShowWelcome = onShowWelcome
}

Expand All @@ -66,6 +72,8 @@ final class SettingsCoordinator: NSObject, NSWindowDelegate {
runtimeModel: runtimeModel,
modelDownloadManager: modelDownloadManager,
huggingFaceSearchService: huggingFaceSearchService,
suggestionEngine: suggestionEngine,
configuration: configuration,
onShowWelcome: onShowWelcome
)
)
Expand Down
38 changes: 25 additions & 13 deletions Cotabby/App/Core/CotabbyAppEnvironment.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ final class CotabbyAppEnvironment {
let foundationModelAvailabilityService: FoundationModelAvailabilityService
let clipboardContextProvider: ClipboardContextProvider
let suggestionCoordinator: SuggestionCoordinator
/// Shared with the Advanced settings pane so the user can fire an ad-hoc generation against
/// the currently-selected engine and verify that Extended Context (and other prompt inputs)
/// are actually shaping the output. Reusing the live router means the playground produces the
/// same answer the autocomplete pipeline would, not a stand-in.
let suggestionEngine: any SuggestionGenerating
let emojiPickerController: EmojiPickerController
let welcomeCoordinator: WelcomeCoordinator
let huggingFaceSearchService: HuggingFaceSearchService
Expand Down Expand Up @@ -87,19 +92,9 @@ final class CotabbyAppEnvironment {
foundationModelAvailabilityService: foundationModelAvailabilityService
)
let huggingFaceSearchService = HuggingFaceSearchService()
let settingsCoordinator = SettingsCoordinator(
appUpdateManager: appUpdateManager,
launchAtLoginService: launchAtLoginService,
permissionManager: permissionManager,
suggestionSettings: suggestionSettings,
foundationModelAvailabilityService: foundationModelAvailabilityService,
runtimeModel: runtimeModel,
modelDownloadManager: modelDownloadManager,
huggingFaceSearchService: huggingFaceSearchService,
onShowWelcome: { [weak welcomeCoordinator] in
welcomeCoordinator?.showWelcome()
}
)
// Settings coordinator construction is deferred below until after `suggestionEngine` is
// built — the Advanced pane's "try it" playground needs the engine so it can fire ad-hoc
// generations using the same router the autocomplete pipeline does.
let suggestionInserter = SuggestionInserter(suppressionController: suppressionController)
let overlayController = OverlayController(suggestionSettings: suggestionSettings)
let activationIndicatorController = ActivationIndicatorController()
Expand Down Expand Up @@ -137,6 +132,22 @@ final class CotabbyAppEnvironment {
llamaEngine: LlamaSuggestionEngine(runtimeManager: runtimeManager)
)

let settingsCoordinator = SettingsCoordinator(
appUpdateManager: appUpdateManager,
launchAtLoginService: launchAtLoginService,
permissionManager: permissionManager,
suggestionSettings: suggestionSettings,
foundationModelAvailabilityService: foundationModelAvailabilityService,
runtimeModel: runtimeModel,
modelDownloadManager: modelDownloadManager,
huggingFaceSearchService: huggingFaceSearchService,
suggestionEngine: suggestionEngine,
configuration: configuration,
onShowWelcome: { [weak welcomeCoordinator] in
welcomeCoordinator?.showWelcome()
}
)

let interactionState = SuggestionInteractionState()
let workController = SuggestionWorkController()
let suggestionCoordinator = SuggestionCoordinator(
Expand Down Expand Up @@ -185,6 +196,7 @@ final class CotabbyAppEnvironment {
self.foundationModelAvailabilityService = foundationModelAvailabilityService
self.clipboardContextProvider = clipboardContextProvider
self.suggestionCoordinator = suggestionCoordinator
self.suggestionEngine = suggestionEngine
self.emojiPickerController = emojiPickerController
self.welcomeCoordinator = welcomeCoordinator
self.huggingFaceSearchService = huggingFaceSearchService
Expand Down
4 changes: 4 additions & 0 deletions Cotabby/Models/SuggestionEngineModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,10 @@ struct SuggestionSettingsSnapshot: Equatable, Sendable {
/// User-authored style rules, carried in the snapshot so generation uses the same value the
/// Settings UI shows.
let customRules: [String]
/// Free-form glossary / terminology / style notes pasted by the user in the Extended Context
/// settings pane. Already trimmed and length-capped by `SuggestionSettingsModel`; empty string
/// when the user has not set it. Travels in the snapshot so generation reflects the live value.
let extendedContext: String
/// The languages the user has declared they write in. Used to build a soft prompt hint; an empty
/// set emits no directive (the renderers then just match the surrounding text). Never forces a
/// language, so a code-switcher's other languages are preserved.
Expand Down
7 changes: 7 additions & 0 deletions Cotabby/Models/SuggestionModels.swift
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ struct SuggestionRequest: Equatable, Sendable {
/// User-authored style rules rendered as additional prompt directives, subordinate to the base
/// autocomplete/safety rules. Empty when the user has none.
let customRules: [String]
/// User-authored free-form context (glossary, jargon, style notes) injected verbatim into the
/// prompt. Already trimmed and length-capped upstream so renderers can treat it as a ready-to-use
/// string. `nil` when the user has not set it, distinguishing it from an empty-but-set value so
/// renderers can skip the heading entirely.
let extendedContext: String?
/// Pre-rendered language hint built from the user's declared languages (e.g. "The user usually
/// writes in German and English…"). `nil` when none are declared. Deliberately a hint, not an
/// override: it tells the model to match the surrounding text and only fall back to the declared
Expand Down Expand Up @@ -269,6 +274,7 @@ struct SuggestionRequest: Equatable, Sendable {
completionLengthInstruction: String,
userName: String?,
customRules: [String],
extendedContext: String? = nil,
languageInstruction: String?,
clipboardContext: String?,
visualContextSummary: String?,
Expand All @@ -290,6 +296,7 @@ struct SuggestionRequest: Equatable, Sendable {
self.completionLengthInstruction = completionLengthInstruction
self.userName = userName
self.customRules = customRules
self.extendedContext = extendedContext
self.languageInstruction = languageInstruction
self.clipboardContext = clipboardContext
self.visualContextSummary = visualContextSummary
Expand Down
Loading