feat(driver-store): add dedicated "Updates Available" section for pending driver updates
Move drivers with available updates to a prominent amber-highlighted section
at the top of the Agent drivers tab, improving discoverability. After updating,
drivers automatically return to their original position in the "All Drivers" list.
Changes:
- DriverStoreDialog.vue: add orderedFilteredDrivers, filteredUpdatableCount,
filteredUpdatableDrivers, filteredStableDrivers computed properties;
replace flat v-for with two-tier layout using separate v-for loops
(updates header + updatable list + section divider + stable list)
- 7 i18n locales: add updatesAvailableTitle, updatesAvailableDescription,
allDriversTitle keys (zh-CN, zh-TW, en, ja, es, it, pt-BR)
Review fixes:
- #C01: rename single-letter variables d -> driver
- Eliminate nested <template> tags for oxfmt compatibility
@
Key Viewer panel now defaults auto-refresh ON, showing a live TTL
countdown without requiring the user to click the Clock button.
- Default autoRefreshEnabled from false to true
- Move countdown init + timer start into startAutoRefresh() as single entry
- Remove redundant countdownTtl reset from the tick callback's .then()
- load() finally block calls startAutoRefresh() for all call paths
fix(ai): restore global reasoning toggle accidentally reverted by d90847bb
Commit 09f9e446 replaced per-message expandedReasoning (Set<number>) with
a global reasoningExpanded boolean ref, defaulting to collapsed so all
messages share the same toggle state. This avoided the flip-flop where
reasoning auto-expanded during streaming and collapsed afterward.
Commit d90847bb (SQL file references) inadvertently reverted this when
resolving merge conflicts during its large template restructure.
Changes:
- Replace expandedReasoning (Set<number>) with reasoningExpanded (ref(false))
- Replace per-index toggleReasoning(i) with global toggleReasoning()
- Template uses reasoningExpanded instead of expandedReasoning.has(i) || msg.isThinking
@
* feat(objects): add checkbox column toggle for object browser
- Add objectBrowserShowCheckbox setting (default: false) to EditorSettings
- Checkbox column hidden by default; auto-shows when any tables are selected
- Toolbar button toggles multi-select mode (active state highlighted in primary color)
- Clearing the toggle also clears all selections
- Add i18n key objects.toggleCheckbox in en/zh-CN
Closes#2748 (part 2)
* feat(objects): add grid view mode for object browser
- Add objectBrowserViewMode setting (list | grid, default: list) to EditorSettings
- Toolbar segmented control toggles between list and grid views, persisted across sessions
- Grid view renders cards (icon + name + type + row count) in a responsive auto-fill layout
- Reuses existing sort, search, filter, multi-select and context menu
- Partition children not expandable in grid mode (minimal version)
- Add i18n keys objects.viewList/objects.viewGrid across all locales
- Backfill objects.toggleCheckbox in es/it/ja/pt-BR/zh-TW (missed in prior commit)
Refs #2748
* fix(objects): redesign grid view cards for visual polish
- Center-align content (icon + name + metadata) for a proper tile look
- Enlarge type icons (h-7) so color coding is visible at a glance
- Merge type label and row count on one line with separator
- Reduce internal spacing (gap-1.5) and tighten padding
- Replace background hover with border glow + shadow for cleaner feedback
- Narrow min card width (150px) for denser grid, add align-content:start
- Softer rounded-lg corners
Refs #2748
* fix(objects): polish grid view card visual details
- Add colored circle background behind icons (type-matched opacity-10)
- Increase name font from text-xs to text-sm for better legibility
- Separate row count into a primary-tinted badge (only shown when > 0)
- Keep type label clean without mixing row count inline
Refs #2748
* fix(objects): refine grid card min-width, icon shadow, and type label size
* @
feat(object-browser): align tile/grid card metadata with list view columns
Add the metadata categories present in the list view but missing from
the tile/grid card view (objectBrowserViewMode === "grid"):
- Size pill (totalBytes) next to the existing rows pill in the stats row
- Timestamps row showing created_at · updated_at (per-row conditional)
- Comment row (truncated, per-row conditional, with hover tooltip)
Each card now shows: icon, name, type label, rows pill, size pill,
timestamps (when present), and comment (when present) — matching the
same metadata categories the list view displays across its 8 columns.
Also fix a pre-existing bug in the list view where the totalBytes
column tooltip (:title) used formatObjectBrowserCount instead of
formatObjectBrowserBytes, showing a count-formatted number instead
of a byte-formatted size string.
@
* @
feat(object-browser): add sort selector in toolbar for grid/list views
The list view has column header click-to-sort but the grid/tile view
had no way to change sort order. Add a compact sort selector in the
toolbar that works identically in both views:
- Native <select> dropdown with all 7 sort keys (name, type, rows,
size, created, updated, comment) using existing column i18n labels
- ArrowUp/ArrowDown direction toggle button with localized tooltip
- Positioned between schema selector and view toggle in the toolbar
- Shares the existing sortKey/sortDirection/toggleSort logic — list
view column headers continue to work as before
- Accessible: aria-label on select, title on direction button
i18n: added sortAsc/sortDesc/sortBy to en.ts and zh-CN.ts
@
* @
fix(object-browser): make sort key options reactive to available data
sortKeyOptions was a static array that always included created_at and
updated_at even when no row had data for those fields. Sorting by an
empty field is a no-op that silently confuses users.
Convert to a computed that mirrors the list view column visibility logic
(hasCreatedAt / hasUpdatedAt): the two time-based sort options only
appear when at least one loaded row has that field populated.
Follows design decision: sort selector remains visible in both list and
grid views (plan B), acting as a shared sort state indicator.
@
* @
fix(object-browser): reset sortKey when selected option disappears from options
When sortKeyOptions loses created_at or updated_at (e.g. after a schema
switch clears all rows), sortKey could remain set to a key no longer in
the dropdown, causing the <select> to show a blank selection.
Add a watch on sortKeyOptions that resets sortKey to "name" and
sortDirection to "asc" whenever the current key is no longer present.
@
* chore(i18n): autofill new translations
* perf(object-browser): virtualize tile/grid view with RecycleScroller
Replaces the flat v-for over filteredRows with a row-chunked
RecycleScroller, matching the list view. Previously, switching to tile
mode on a schema with thousands of tables mounted every card and its
CustomContextMenu at once, stalling the UI for several seconds.
Changes:
- gridRows computed: chunks filteredRows into rows of gridColumns cards
- ResizeObserver on the grid container drives gridColumns; initial read
uses getComputedStyle to match contentRect.width and avoid a 1-frame
column jump on mount
- objectGridRowHeight computed: item-size adapts to whether the dataset
has timestamp/comment rows, minimising wasted row space
- watch(gridContainerRef) re-attaches the observer across list<->grid
toggles; onBeforeUnmount cleans up
- scrollObjectsToTop() resets scroll position on sort, search,
object-type filter and view-mode changes
- CSS: new .object-browser-grid-wrapper / -scroller / -row classes with
will-change / contain parity with the list scroller
---------
Co-authored-by: dbx-i18n-bot <github-actions[bot]@users.noreply.github.com>
The stop button called aiCancelStream -> notify_one(), but the four
stream_*_with_tools SSE loops only `break`ed and returned Ok(token_usage)
when cancelled. run_agent_loop treated that as a normal completed turn and
kept going (often into contract-repair or the next turn), so isGenerating
stayed true and a single click did not stop the agent.
Return Err(AGENT_CANCELLED_ERROR) from the cancel branch so the existing
Err-cancel match arm sets loop_exit = Cancelled and stops the loop cleanly,
preserving whatever was streamed so far.
Additional:
- Promote AGENT_CANCELLED_ERROR to a shared `pub const` in ai.rs; agent_loop
now references ai::AGENT_CANCELLED_ERROR.
- Check cancellation before executing requested tools, so a stop landing
between stream-end and tool execution is honored.
- Wire cancellation into the text-only fallback (previously `_cancelled`,
ignored) via tokio::select! racing ai::complete against cancelled.notified().
Known limitation left: in-flight tool execution (e.g. execute_query) is
still not interruptible mid-call.
* @
fix(grid): preserve current page on data refresh instead of resetting to page 1
When refreshing table data (toolbar button, Ctrl+R, or auto-refresh), the
offset was hardcoded to 0, causing pagination to always jump back to the
first page. This was frustrating for users viewing later pages.
Use the current page offset (currentPage - 1) * pageSize so the user
stays on the same page after refresh. In infinite-scroll mode, the
offset still evaluates to 0 because resetInfiniteScrollState() sets
currentPage = 1, so behavior is unchanged.
@
* @
fix(grid): auto-redirect to last page when refreshed page no longer exists
Extends the refresh pagination fix with a safety net: when data is deleted
while viewing a later page (e.g. page 5 with only 2 pages remaining), the
grid auto-navigates to the last available page instead of showing an empty
page.
Uses a transient isRefreshingData flag to distinguish refresh/rollback
from normal pagination navigation, avoiding false triggers.
@
* Revert "@"
This reverts commit ff0bf0bb31.
* @
fix(grid): auto-redirect to last page when refreshed page no longer exists
When data is deleted while viewing a later page, the grid now auto-navigates
to the last available page instead of showing an empty page.
Uses a transient isRefreshingData flag to distinguish refresh/rollback
from normal pagination, and a loading-transition watcher (true->false)
placed after displayedTotalRowCount declaration to avoid TDZ errors.
@
* @
test(grid): add pagination offset-preservation and auto-redirect tests
Cover the two new behaviors added to DataGrid.vue:
1. dataGridPagination.test.ts — auto-redirect page calculation:
- page beyond last page after data deletion → triggers redirect
- page still valid → no redirect
- fewer rows than one page → redirects to page 1
- total=0 / total=undefined → guard prevents redirect
2. useDataGridActions.test.ts — offset preserved on reload:
- onReloadData(offset=400) passes offset=400 to build-table-select-sql
- resultPageOffset stored as 400 (not reset to 0)
@
fix(ai): make reasoning expand/collapse consistent across messages
Replace per-message expandedReasoning Set with a global reasoningExpanded
boolean ref so all messages share the same toggle state. Default to collapsed.
This avoids the flip-flop where reasoning auto-expands during streaming and
collapses afterward, requiring repeated manual toggles.
- Replace expandedReasoning (Set<number>) with reasoningExpanded (boolean)
- Remove per-index toggleReasoning; use global flip instead
- Template uses reasoningExpanded instead of expandedReasoning.has(i) || msg.isThinking
@
* feat(ai): add task-oriented Agent actions with per-action contract
Introduce three Agent-native actions — query, exploreSchema,
executeAndExplain — so Agent mode surfaces task-oriented operations
instead of reusing Ask's SQL-producing action set.
- ai.ts: extend AiAction with the new actions; add ASK_ACTIONS /
AGENT_ACTIONS and defaultActionForMode / isValidActionForMode
helpers; raise maxTokens for the explanatory task actions.
- aiSkills.ts: add skill definitions (zh/en) for the three actions.
- aiAgentPlan.ts: task actions no longer emit a misleading
"unsupported_action" execute step; they execute via the
execute_query tool events, not the legacy client-side path.
- agent_loop.rs: per-action task-contract rules in both the prompt
augmentation and repair paths (query -> execute & answer from real
data; exploreSchema -> list_tables/get_columns; executeAndExplain
-> run current SQL & explain).
- i18n (en/zh-CN/zh-TW): new action labels, placeholders, a
generateNoExec label for Agent mode, and a task-oriented agent
modeHint.
es/it/ja/pt-BR have no `ai` section and fall back to `en`, matching
existing coverage.
* feat(ai): split Ask/Agent action menus with mode-aware defaults
Wire the new Agent task actions into the assistant UI so each mode
shows a distinct, intent-matching action set.
- actionButtons is now a computed returning Ask or Agent buttons
based on assistantMode. Agent menu: query / exploreSchema /
executeAndExplain / generate (labeled "生成但不执行" via
generateNoExec). Ask menu is unchanged.
- Switching mode lands on that mode's default (Ask -> generate,
Agent -> query); the shared `generate` is not carried across
because its label/semantics differ per mode. Vector DBs stay on
`generate` since their action menu is hidden and they lack SQL
tools (no execute_query contract).
- External Ask-style triggers (Fix with AI, Explain history) switch
to Ask mode when invoked from Agent mode so the action is valid
and the menu reflects what runs.
- Reset the active action to the mode default after each send.
* test(ai): cover Agent task-action contract and mode/action mapping
Lock in the deterministic parts of the Ask/Agent split so only the
LLM-behavior criteria need manual verification.
Backend (agent_loop.rs):
- query contract instructs the LLM to call execute_query and is not
treated as a SQL-producing action.
- exploreSchema contract points to list_tables/get_columns and says
not to execute data queries.
- executeAndExplain contract tells the LLM to run the current SQL.
- task actions do not require a SQL deliverable (a conclusion without
a fenced SQL block still satisfies the contract).
- query repair prompt also targets execute_query.
Frontend (aiActions.spec.ts):
- defaultActionForMode: Ask -> generate, Agent -> query (not generate).
- isValidActionForMode: generate valid in both; Ask-only actions
rejected in Agent mode and vice versa.
- ASK_ACTIONS / AGENT_ACTIONS composition (Agent menu starts with
query and still offers generate).
* fix(ai): correct action menu on mode-switched triggers and i18n gaps
Issues found during review of the Ask/Agent action split:
- [#I01] triggerAction (Fix with AI / Explain history) invoked from
Agent mode switched to Ask and set the action, but the mode-switch
watch then reset activeAction to the Ask default ("generate"),
overwriting the action just set — so the menu showed "Generate SQL"
during a fix. Add a suppressModeActionReset flag so programmatic
mode switches don't clobber the explicitly-set action.
- [#C01] Remove dead `unsupported_action` reason: buildAiAgentPlan no
longer produces it, so drop the type variant and the unreachable
skippedTitleKey case.
- [#I02] es/it/ja/pt-BR were missing the new Agent action keys
(query/exploreSchema/executeAndExplain/generateNoExec) and their
placeholders, causing English fallback in the Agent menu. Add the
translations to match each locale's existing style.
* test(ai): align app-tests with task-action split
- aiSkills: add query/exploreSchema/executeAndExplain to the expected
action set; broaden the id check (new ids are not _sql-suffixed) and
relax the userInstruction assertion (schema inspection need not mention SQL).
- aiAgentPlan: drop the unsupported_action skipped step for non-generate
actions (#C01 removed it from the plan and the reason union); add coverage
for task-oriented Agent actions not driving client-side execution.
* test(ai): pin locale in dialect prompt spec
buildSystemPrompt selects zh/en copy via currentLocale(), which reflects the
host navigator.language. The spec asserts English strings, so it failed on
zh-CN machines (e.g. Windows with a Chinese system locale) while passing on
CI's en-US runners. Pin the locale to en in beforeAll so the assertions are
deterministic regardless of the host OS locale.
* feat(ai): support editing sent user messages
Adds inline edit support for user messages in the AI chat panel.
Hover over a user message to reveal the edit button; confirming the
edit truncates all subsequent history and re-sends with the new content.
* fix(ai): fix IME composition and ref-focus regression in message edit
- Handle IME composition events in onEditKeydown to prevent accidental
submission on Enter during CJK candidate selection
- Replace per-render :ref focus callback with nextTick + data attribute
to avoid cursor jumping on every keystroke
- Extract visibleToActualIndex into aiMessageEdit.ts for testability
- Add 8 unit tests covering index mapping with contextSummary messages
* fix(ai): prevent data loss and mention pollution in message edit
- Guard connection/config before truncating messages to prevent silent data
loss when send() bails early
- Clear selectedMentions before resend to prevent stale chips from polluting
edited message content
* fix(ai): limit indexes and foreign keys per table in schema context
Prevent AI input token spikes caused by tables with large numbers of
indexes or foreign keys being fully serialized into the system prompt.
Apply a default cap of 10 indexes and 10 FKs per table in all three
schema-loading paths: focused_table mode, database mode, and
loadMentionedTableContext for @-mentioned tables.
Both limits are configurable via buildAiContext options
(maxIndexesPerTable, maxFksPerTable).
* fix(ai): truncate large column values in result preview
Prevent input token explosion when the active tab contains large text
or JSON fields (e.g. request_body, response_body in audit log tables).
formatResultPreview previously serialized each value without any length
limit, so 5 rows from a wide table with multi-KB text columns could
produce 100K+ tokens in the system prompt.
Cap each serialized value at 200 characters with an ellipsis suffix.
* fix(ai): merge tool call status cards
* fix(ai): constrain markdown table overflow
* fix(ai): restore streaming of answer text via TextDelta events
The on_chunk closure was missing live AgentEvent::TextDelta emission for text content, causing the AI answer text to appear all at once after the turn completed. Reasoning deltas were unaffected.
Root cause: commit c30033628 rewrote the on_chunk closure and accidentally dropped the TextDelta emit line while keeping ReasoningDelta intact.
Fix: restore TextDelta emit in on_chunk for incremental streaming, and remove the post-turn full-TextDelta workaround which would duplicate content.
* test(ai): extract chunk_to_events and add streaming event tests
Extract the event-generation logic from the on_chunk closure into a pure function chunk_to_events() to make it testable, and add 6 unit tests covering all chunk content combinations: text-only, reasoning-only, mixed, empty, and edge cases.
This ensures a regression like the one fixed in the previous commit (where TextDelta emission was dropped while ReasoningDelta was kept) would be caught by tests.
* fix(ai): add quick proposal confirmation buttons (all locales)
When the assistant proposes an action, render Yes/No buttons below the
message so the user does not need to type a short reply — fixing the
issue where short replies caused the LLM to re-ask instead of executing.
- aiProposalDetect.ts: pure detectors for proposal sentences/short replies
- AiAssistant.vue: inline Yes/No buttons on last assistant proposal message
- ai.ts: system-prompt rule to skip re-confirmation on short affirmatives
- i18n: 3 keys added to all 7 locales (en/zh-CN/zh-TW/ja/es/it/pt-BR)
Tests: 55/55. TypeCheck: passed.
* fix(ai): add i18n keys and UI changes for proposal confirmation
* fix(i18n): add missing agent tool step translations for zh-TW, pt-BR, it, ja
* propagate explainData to agent step items for ExplainPlanViewer
* feat(ai): show safety check result for execute_query tool steps
- Add explain_query tool with structured EXPLAIN result, gated on supports_explain_plan()
- Add supports_sql_query() to gate execute_query/get_sample_data for non-SQL DBs (Redis, ES, MongoDB, etc.)
- Split agent loop tool dispatch: execute_query runs sequentially, all others run in parallel via join_all
- Wire explain_query result button to ExplainPlanViewer in AiAssistant + App.vue
- Fix O(n²) agentSteps rebuild: incremental O(1) push per event during streaming
- finally block falls back to full rebuild only when streaming push did not occur
AiAssistant.vue:
- Tool call steps now support click-to-expand/collapse for SQL args
and results (expandedSteps set); chevron rotates on open
- Remove fixed-open result display (was always visible, no toggle)
- Max result height raised to 48 (max-h-48) with scroll
ai.ts (buildModePromptLines agent branch):
- Replace vague "prioritize SQL" hint with explicit tool-use
instruction: must call execute_query, not just emit SQL text
- List available tools (list_tables, get_columns, execute_query,
get_sample_data) so LLM knows what it can call
- Clarify allowed statement types for execute_query