diff --git a/apps/desktop/src/pipeline/providers/formatting/formatter-prompt.ts b/apps/desktop/src/pipeline/providers/formatting/formatter-prompt.ts index 5251067..061ef2b 100644 --- a/apps/desktop/src/pipeline/providers/formatting/formatter-prompt.ts +++ b/apps/desktop/src/pipeline/providers/formatting/formatter-prompt.ts @@ -134,8 +134,8 @@ export function constructFormatterPrompt(context: FormatParams["context"]): { const parts = [SYSTEM_PROMPT]; // Add vocabulary context if available - if (vocabulary && vocabulary.size > 0) { - const vocabTerms = Array.from(vocabulary.keys()).join(", "); + if (vocabulary && vocabulary.length > 0) { + const vocabTerms = vocabulary.join(", "); parts.push(`\nCustom vocabulary to use for corrections: ${vocabTerms}`); } diff --git a/apps/desktop/src/services/transcription-service.ts b/apps/desktop/src/services/transcription-service.ts index 1c54324..58b57ba 100644 --- a/apps/desktop/src/services/transcription-service.ts +++ b/apps/desktop/src/services/transcription-service.ts @@ -544,7 +544,7 @@ export class TranscriptionService { meta: { sessionId, source: session.context.sharedData.audioMetadata?.source, - vocabularySize: session.context.sharedData.vocabulary?.size || 0, + vocabularySize: session.context.sharedData.vocabulary?.length || 0, formattingStyle: session.context.sharedData.userPreferences?.formattingStyle, }, @@ -604,7 +604,7 @@ export class TranscriptionService { vad_enabled: !!this.vadService, session_type: "streaming", language: session.context.sharedData.userPreferences?.language || "en", - vocabulary_size: session.context.sharedData.vocabulary?.size || 0, + vocabulary_size: session.context.sharedData.vocabulary?.length || 0, }); this.streamingSessions.delete(sessionId); diff --git a/packages/native-helpers/swift-helper/Sources/SwiftHelper/AccessibilityContextService.swift b/packages/native-helpers/swift-helper/Sources/SwiftHelper/AccessibilityContextService.swift index 62e5711..a98116e 100644 --- a/packages/native-helpers/swift-helper/Sources/SwiftHelper/AccessibilityContextService.swift +++ b/packages/native-helpers/swift-helper/Sources/SwiftHelper/AccessibilityContextService.swift @@ -180,49 +180,58 @@ class AccessibilityContextService { return chain } + static let MAX_CONTEXT_LENGTH = 500 + static func getTextSelection(element: AXUIElement) -> TextSelection? { - // Get selected text - guard let selectedText = getAttributeValue(element: element, attribute: kAXSelectedTextAttribute), - !selectedText.isEmpty else { - return nil - } - - // Get full content + // Get full content first - we need this to provide context let fullContent = getAttributeValue(element: element, attribute: kAXValueAttribute) - - // Get selection range + + // Get selection/cursor range var selectionRange: SelectionRange? = nil var rangeValue: CFTypeRef? let rangeError = AXUIElementCopyAttributeValue(element, kAXSelectedTextRangeAttribute as CFString, &rangeValue) - + if rangeError == .success, let axValue = rangeValue { var range = CFRange() if AXValueGetValue(axValue as! AXValue, .cfRange, &range) { selectionRange = SelectionRange(length: Int(range.length), location: Int(range.location)) } } - - // Calculate pre and post selection text + + // If we have no cursor/selection position and no content, return nil + guard selectionRange != nil || fullContent != nil else { + return nil + } + + // Get selected text (may be empty if just cursor position) + let selectedText = getAttributeValue(element: element, attribute: kAXSelectedTextAttribute) + + // Calculate pre and post selection/cursor text var preSelectionText: String? = nil var postSelectionText: String? = nil - + if let fullContent = fullContent, let range = selectionRange { let nsString = fullContent as NSString - + + // Pre-selection text: last MAX_CONTEXT_LENGTH chars before cursor/selection if range.location > 0 { - let preRange = NSRange(location: 0, length: range.location) + let preLength = min(range.location, MAX_CONTEXT_LENGTH) + let preStart = range.location - preLength + let preRange = NSRange(location: preStart, length: preLength) preSelectionText = nsString.substring(with: preRange) } - + + // Post-selection text: first MAX_CONTEXT_LENGTH chars after cursor/selection let postStart = range.location + range.length if postStart < nsString.length { - let postRange = NSRange(location: postStart, length: nsString.length - postStart) + let postLength = min(nsString.length - postStart, MAX_CONTEXT_LENGTH) + let postRange = NSRange(location: postStart, length: postLength) postSelectionText = nsString.substring(with: postRange) } } - + let isEditable = isElementEditable(element: element) - + return TextSelection( fullContent: fullContent, isEditable: isEditable, diff --git a/packages/types/src/schemas/methods/get-accessibility-context.ts b/packages/types/src/schemas/methods/get-accessibility-context.ts index 261cf15..1239647 100644 --- a/packages/types/src/schemas/methods/get-accessibility-context.ts +++ b/packages/types/src/schemas/methods/get-accessibility-context.ts @@ -29,10 +29,10 @@ const FocusedElementInfoSchema = z.object({ }); const TextSelectionInfoSchema = z.object({ - selectedText: z.string(), + selectedText: z.string().nullable(), // Nullable when only cursor position is available (no selection) fullContent: z.string().nullable(), - preSelectionText: z.string().nullable(), - postSelectionText: z.string().nullable(), + preSelectionText: z.string().nullable(), // Last 500 chars before cursor/selection (closest to cursor) + postSelectionText: z.string().nullable(), // First 500 chars after cursor/selection (closest to cursor) selectionRange: SelectionRangeSchema.nullable(), isEditable: z.boolean(), });