feat(ai-chat): Codiff-style AI walkthroughs for generated SQL#1948
feat(ai-chat): Codiff-style AI walkthroughs for generated SQL#1948datlechin wants to merge 5 commits into
Conversation
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 72988d7bb1
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| : String(localized: "Replace the editor content with the suggested SQL")) | ||
| } | ||
| .alert(String(localized: "Apply suggested SQL?"), isPresented: $showApplyConfirmation) { | ||
| Button(String(localized: "Apply")) { actions?.insertQueryFromAI(afterSQL) } |
There was a problem hiding this comment.
Replace the active query when applying a walkthrough
For the normal Optimize/Fix flow, the selected editor tab already contains the query being reviewed. insertQueryFromAI only mutates an empty selected query tab and otherwise opens a new query tab (MainContentCoordinator.swift:1017-1036), so confirming this action does not perform the replacement promised by the button and confirmation text; it leaves the original editor content unchanged.
Useful? React with 👍 / 👎.
| func resolveWalkthroughIfNeeded(id: UUID) { | ||
| guard let beforeSQL = pendingWalkthroughBeforeSQL else { return } | ||
| pendingWalkthroughBeforeSQL = nil |
There was a problem hiding this comment.
Retain walkthrough context for regeneration and retry
A successful walkthrough clears the only stored beforeSQL here, but regenerate() deletes that assistant turn and restarts streaming without restoring it; likewise the error path clears it before retry(). The hidden prompt still instructs the model to emit the envelope, but resolveWalkthroughIfNeeded then returns immediately, so regenerated/retried Explain, Optimize, and Fix responses display their raw JSON instead of the walkthrough UI.
Useful? React with 👍 / 👎.
| private func activateAnchor(_ anchor: SqlWalkthroughAnchor) { | ||
| activeAnchor = anchor | ||
| scrollTarget = unifiedScrollTarget(for: anchor) |
There was a problem hiding this comment.
Use split-row targets when the split diff is selected
When users select Split and press “Jump to lines,” this always calculates an ID from the unified diff. Split rows are identified by their DiffPair index, while unified IDs gain an extra entry for each changed pair, so after any prior replacement the target either scrolls to a different row or does not exist in the split view. The highlighted lines are correct, but the advertised jump action is not.
Useful? React with 👍 / 👎.
…parse it from any block
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 22d7563d4e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| func sendWithWalkthroughContext(prompt: String, beforeSQL: String) { | ||
| pendingWalkthroughBeforeSQL = beforeSQL | ||
| sendWithContext(prompt: prompt) |
There was a problem hiding this comment.
Clear walkthrough state when the request does not start
When a walkthrough command cannot start—for example, no provider is configured, the connection policy is never, or preflight rejects an oversized prompt—this state remains set because none of those early exits clears it. The next ordinary chat message then receives the walkthrough system directive and is parsed against the stale SQL, so a normal response can be rendered as an unrelated SQL walkthrough.
Useful? React with 👍 / 👎.
| private func unifiedDiff(before: [String], after: [String]) -> some View { | ||
| let lines = Array(DiffComputer.computeUnified(before: before, after: after).prefix(Self.maxDiffLines)) |
There was a problem hiding this comment.
Count rendered unified diff rows when truncating
For a query with 500 changed lines, computeUnified emits 1,000 display rows (a removal and addition per change), but this prefixes the result at 500 while the footer compares against max(before.count, after.count), also 500. Consequently the entire added half of the diff is silently hidden with no truncation notice, preventing users from reviewing the proposed SQL before applying it.
Useful? React with 👍 / 👎.
|
Preview deployment for your docs. Learn more about Mintlify Previews.
💡 Tip: Enable Workflows to automatically generate PRs for you. |
…y, not opening a tab
Closes #1945.
AI Explain, Optimize, and Fix Error now answer with a reviewable walkthrough in the chat panel instead of plain markdown, so a user can inspect an AI SQL change before trusting it.
What you get
Design
The AI writes only the narrative (the rewritten
afterSQLplus ordered steps). The app computes the diff itself from the original query, so the diff always reflects the real change and a model cannot show a diff that does not exist.Structured output travels as a fenced JSON envelope parsed out of the streamed text, not tool calling. This works on every provider including the local ones (Ollama, llama.cpp, MLX), and degrades to plain markdown when a model ignores the format: the fence is stripped so no raw JSON shows, and the response renders as normal text.
Native and HIG details: semantic red/green with
+/-gutter markers (never color alone),DisclosureGroupsteps, reduce-motion gated highlight, VoiceOver status on diff rows, confirm-before-apply per Apple's Generative AI guidance. Anchors that fall out of range render no highlight rather than pointing at the wrong line.Structure
Three commits:
refactor(datagrid)extracts the line-diff engine (DiffComputer/DiffPair) out ofFileConflictDiffSheetintoCore/Diff/SqlDiff.swift, adding unified-diff output and SQL normalization. The conflict sheet consumes it unchanged.feat(ai-chat)adds the.sqlWalkthroughchat block (additive wire Codable, old conversations still decode) andAIChatWalkthroughBlockView, and encodes the walkthrough as proposed-SQL text in every provider's history so follow-ups keep context.feat(ai-chat)adds the envelope parser, the three walkthrough prompt templates, and the view-model wiring, plus CHANGELOG and docs.Tests
New unit tests: envelope parsing (well-formed, code-fenced, no-close-fence, malformed, no-fence, null
afterSQL, unique step ids,stripFence), diff extraction parity and unified diff, SQL normalization, anchor validity (in-range / out-of-range / nil), and.sqlWalkthroughwire round-trip plus legacy decode. UI automation is deferred: the flow depends on non-deterministic AI responses and needs a mock provider injection point.Verified locally: build succeeds, and all four new suites pass.
Scope note
In-editor cross-highlighting (highlighting the anchored lines in the main editor, not just the in-chat diff) is intentionally left out of this first version to keep the panel decoupled from editor state. Worth a follow-up.