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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- AI Chat: `@` mention detection no longer breaks when the cursor sits right after an emoji or other non-BMP character
- AI Chat: Fix Error prompt now reads "MongoDB query" and "Redis command" using the database display name, instead of the raw query language label
- Internal: tab session registry binds automatically when a coordinator falls back to creating its own registry, so unit tests no longer trip the filter-state debug assertion

## [0.39.1] - 2026-05-08

Expand Down
19 changes: 16 additions & 3 deletions TablePro/Core/AI/AIPromptTemplates.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
//

import Foundation
import TableProPluginKit

/// Centralized prompt templates for AI-powered editor features
enum AIPromptTemplates {
Expand Down Expand Up @@ -42,8 +43,20 @@ enum AIPromptTemplates {
}

@MainActor private static func queryInfo(for databaseType: DatabaseType) -> (typeName: String, language: String) {
let langName = PluginManager.shared.queryLanguageName(for: databaseType)
let lang = PluginManager.shared.editorLanguage(for: databaseType).codeBlockTag
return ("\(langName) query", lang)
let snapshot = PluginMetadataRegistry.shared.snapshot(forTypeId: databaseType.pluginTypeId)
let editorLanguage = snapshot?.editorLanguage ?? .sql
let lang = editorLanguage.codeBlockTag
let typeName: String
switch editorLanguage {
case .sql:
typeName = "\(snapshot?.queryLanguageName ?? "SQL") query"
case .bash:
typeName = "\(snapshot?.displayName ?? databaseType.rawValue) command"
case .javascript:
typeName = "\(snapshot?.displayName ?? databaseType.rawValue) query"
case .custom:
typeName = "\(snapshot?.displayName ?? databaseType.rawValue) query"
}
return (typeName, lang)
}
}
7 changes: 7 additions & 0 deletions TablePro/Models/Query/QueryTabManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ final class QueryTabManager {
self.tabSessionRegistry = tabSessionRegistry
}

func bindTabSessionRegistry(_ registry: TabSessionRegistry) {
tabSessionRegistry = registry
for tab in tabs where registry.session(for: tab.id) == nil {
registry.register(TabSession(queryTab: tab))
}
}

private func syncTabSessionRegistry(oldTabs: [QueryTab], newTabs: [QueryTab]) {
guard let registry = tabSessionRegistry else { return }
let oldIds = Set(oldTabs.map(\.id))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ extension MainContentCoordinator {
filterStateLog.error(
"TabSession missing for selected tab \(tabId, privacy: .public); QueryTab updated but session mirror skipped"
)
assertionFailure("TabSession missing for selected tab registry sync regression")
assertionFailure("TabSession missing for selected tab: registry sync regression")
}
}
}
4 changes: 3 additions & 1 deletion TablePro/Views/Main/MainContentCoordinator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -343,7 +343,9 @@ final class MainContentCoordinator {
self.tabManager = tabManager
self.changeManager = changeManager
self.toolbarState = toolbarState
self.tabSessionRegistry = tabSessionRegistry ?? TabSessionRegistry()
let resolvedRegistry = tabSessionRegistry ?? TabSessionRegistry()
self.tabSessionRegistry = resolvedRegistry
tabManager.bindTabSessionRegistry(resolvedRegistry)
self.queryExecutor = queryExecutor ?? QueryExecutor(connection: connection)
let dialect = PluginManager.shared.sqlDialect(for: connection.type)
self.queryBuilder = TableQueryBuilder(
Expand Down
Loading