Skip to content

feat(editor): show git blame for current line in status bar#910

Open
roberto-fernandino wants to merge 25 commits into
crynta:mainfrom
roberto-fernandino:feat/git-blame-statusbar
Open

feat(editor): show git blame for current line in status bar#910
roberto-fernandino wants to merge 25 commits into
crynta:mainfrom
roberto-fernandino:feat/git-blame-statusbar

Conversation

@roberto-fernandino

@roberto-fernandino roberto-fernandino commented Jun 30, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Adds a git_blame Tauri command that runs git blame -L <line>,<line> --porcelain for the current cursor line
  • Tracks cursor position in EditorPane via a CodeMirror updateListener extension
  • Debounces blame calls (300ms) to avoid hammering git on every keystroke
  • Displays "Author (X time ago)" in the status bar footer when editing a file in a git repo
  • Clears blame info automatically when switching to non-editor tabs or non-git files

Test plan

  • Open a file in a git repo — status bar shows author and relative time for current line
  • Move cursor to different lines — blame updates after 300ms debounce
  • Open an untracked file or switch to terminal tab — blame disappears
  • Works with files where some lines are uncommitted (new lines show nothing)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added an Agents sidebar tab with session list, badges, and navigation to active agent-related tabs.
    • Added editor blame details in the status bar, showing author and relative time for the current line.
    • Introduced a terminal cursor style setting and a new preference to show or hide the Agents sidebar tab.
    • Added support for switching between terminal tabs from the command palette.
  • Bug Fixes

    • Improved agent status handling and notifications for clearer “idle” and “waiting” states.

Roberto and others added 25 commits June 26, 2026 13:07
feat(tabs): select tabs by index within active space
…dow-shortcut

feat(shortcuts): add ai toggle mini shortcut
…nd-palette

feat: terminals in command-palette
Adds a new keyboard shortcut (Cmd+Shift+G by default) that switches the
sidebar to the Files/explorer view. Pairs naturally with Cmd+G (source
control) so the user can toggle between them without touching the mouse.

The shortcut uses cycleSidebarView("explorer") — same pattern as pane.source
uses for source-control — and is fully configurable in Settings > Shortcuts.
Also exposed as "Show Files sidebar" in the command palette.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tcut

feat(shortcuts): add configurable shortcut to show Files sidebar
Documents and enforces the mandatory workflow: branch → commit →
dual PR (fork + upstream cross-fork) → merge fork → back to main.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- New "Agents" sidebar tab lists every Claude Code, Codex, Gemini,
  OpenCode and Cursor agent running in terminals, with status badges
  (working / needs input) and one-click navigation to the terminal tab
- Terminal tabs whose agent is detected by the backend now show that
  tool's logo (Claude, Gemini, Codex/OpenCode, or robotic fallback)
  instead of the generic terminal icon
- "Show Agents sidebar tab" toggle in Settings > General (default on);
  disabling it falls back to the Files view
- Merged upstream additions: Codex/Gemini notification hooks,
  per-agent notification bell, jump-to-attention shortcut

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…-and-logos

feat(sidebar): Agents tab + agent logos in terminal tabs
When an agent process starts (claude, codex, etc.) it was immediately
shown as "working" even though it was just sitting at its idle prompt.

Added "idle" as a third AgentStatus. The "started" signal now sets idle;
only the explicit "working" signal (fired when the agent begins executing
a task) promotes it to working. The sidebar badge count excludes idle
sessions so the badge only lights up during real activity.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
fix(agents): show idle status when agent is at prompt, not working
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…efault

feat(ai): open AI panel by default on startup
- "finished" signal now sets status back to "idle" instead of "waiting"
  so the badge only lights up on genuine attention requests, not routine
  task completions
- Added a useEffect in App.tsx: when the active tab becomes a terminal
  that has a waiting agent, reset it to idle — the user is already
  looking at it so the notification is no longer relevant; covers all
  nav paths (tab click, keyboard shortcut, notification bell, agents panel)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…on-focus

fix(agents): clear needs-input when tab focused; finished resets to idle
Ctrl+C interrupts Claude Code without firing a finished signal, leaving
the status permanently stuck on "working". Extend the tab-focus effect
to reset any non-idle status (waiting or working) to idle when the user
navigates to the terminal. If the agent is genuinely still running, the
backend will fire the next working signal and promote it back immediately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ck-on-interrupt

fix(agents): reset working status to idle when terminal tab is focused
…working status

Depending on tabs (not tabsRef) caused the effect to re-run on every tab
title update. While the agent was working and the user was already on the
terminal tab, the next title change re-ran the effect and reset working→idle,
breaking real-time status display. Switching to tabsRef means the effect only
fires when activeId actually changes (true navigation), not on content updates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds a terminalCursorStyle preference that lets users choose between
bar, block, and underline cursor shapes in the terminal, matching VS
Code's terminal cursor style options. Defaults to bar (previous
hardcoded value).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tyle

feat(terminal): add cursor style setting (bar/block/underline)
…pboard)

Resolves conflicts between fork additions (showAgentsTab, cursor style)
and upstream additions (defaultWorkspaceEnv, sidebar collapsed persistence,
native clipboard on Linux). Both sides preserved.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When editing a file, the status bar now shows who last committed the
current cursor line and how long ago, e.g. "Roberto (2 days ago)".

Adds a git_blame Tauri command (git blame -L line,line --porcelain),
cursor tracking via a CodeMirror updateListener, debounced blame
fetching (300ms), and a relative-time display in the status bar footer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Jun 30, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a Rust git-blame backend command surfaced through a debounced editor cursor callback into the status bar. Introduces an Agents sidebar tab/panel with agent session tracking (new "idle" status), terminal cursor-style preference, command palette terminal-switching support, and several shortcut/UI tweaks.

Changes

Git Blame Feature

Layer / File(s) Summary
Backend blame command
src-tauri/src/modules/git/types.rs, src-tauri/src/modules/git/operations.rs, src-tauri/src/modules/git/commands.rs, src-tauri/src/lib.rs
Adds GitBlameLineInfo struct, blame_line operation parsing git blame --porcelain, the git_blame Tauri command, and handler registration.
Frontend blame wiring and display
src/modules/ai/lib/native.ts, src/modules/editor/EditorPane.tsx, src/modules/editor/EditorStack.tsx, src/app/components/WorkspaceSurface.tsx, src/app/App.tsx, src/modules/statusbar/StatusBar.tsx
Adds native.gitBlame, per-tab cursor-change callbacks through the editor stack, debounced blame fetch in App, and status bar rendering of author/relative time.

Agents Sidebar Feature

Layer / File(s) Summary
Agent status model and notifications
src/modules/agents/lib/types.ts, src/modules/agents/store/agentStore.ts, src/modules/agents/components/AgentNotificationsBridge.tsx, src/modules/agents/components/NotificationBell.tsx, src/modules/agents/lib/agentIcon.tsx, src/modules/agents/lib/format.ts
Adds "idle" status, changes session start default, reorders finished-signal handling, updates status label rendering, and adds opencode/cursor icon/label entries.
Agents panel and sidebar tab
src/modules/agents-panel/*, src/modules/sidebar/..., src/modules/settings/store.ts, src/app/App.tsx, src/modules/header/Header.tsx, src/modules/tabs/TabBar.tsx, src/settings/sections/GeneralSection.tsx
Adds AgentsPanel, "agents" sidebar view, showAgentsTab preference, agent-by-tab icon decoration, and settings toggle.

Terminal & Settings Wiring

Layer / File(s) Summary
Terminal cursor style preference
src/modules/settings/store.ts, src/modules/terminal/lib/rendererPool.ts, src/modules/terminal/lib/useTerminalSession.ts, src/settings/sections/GeneralSection.tsx
Adds terminalCursorStyle preference, applyCursorStyle renderer helper, reactive effect, and settings UI selector.
Shortcuts and command palette
src/modules/shortcuts/shortcuts.ts, src/modules/command-palette/commands.ts, src/app/App.tsx, src/modules/ai/components/AiStatusBarControls.tsx, src/modules/ai/store/chatStore.ts
Adds ai.toggleMini/sidebar.files shortcuts, terminal-switch palette commands, and minor AI panel tweaks.

PR Workflow Documentation

Layer / File(s) Summary
Skill doc
.claude/skills/terax-pr-workflow/SKILL.md
Adds enforced PR workflow documentation.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • crynta/terax-ai#569: Both PRs extend rendererPool.ts's termOptions() and useTerminalSession with new reactive cursor/font preference helpers.
  • crynta/terax-ai#815: Both PRs modify termOptions() in rendererPool.ts for different styling properties (cursorStyle vs fontFamily).
  • crynta/terax-ai#885: Both PRs modify AgentNotificationsBridge.tsx's notification handling logic.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title uses Conventional Commits and accurately summarizes the main change to show git blame in the editor status bar.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/App.tsx (1)

334-346: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Stale blame response can land on the wrong tab.

handleEditorCursorChange schedules a 300ms-debounced native.gitBlame(...) call, but the resolved .then(setBlameInfo)/.catch never re-checks that the originating tab is still active. Since editor tabs stay mounted (not unmounted) on switch, if the user switches tabs while a request for the previous tab is in flight, that stale result overwrites blameInfo for whatever tab is now active.

Compounding this, the cleanup effect at lines 341-345 only clears blameInfo when leaving editor tabs entirely — switching between two editor tabs leaves the old tab's author/time displayed until the new tab happens to fire a cursor event.

As per path instructions, "Tabs are kept mounted and hidden on switch... so do not accept logic that assumes unmount-on-switch."

🛡️ Proposed fix: guard against stale async results and clear on every tab switch
+  const activeIdRef = useRef(activeId);
+  activeIdRef.current = activeId;
+
   useEffect(() => {
     setActiveSearchAddon(
       activeLeafId !== null
         ? (searchAddons.current.get(activeLeafId) ?? null)
         : null,
     );
     setActiveEditorHandle(editorRefs.current.get(activeId) ?? null);
-    const tab = tabsRef.current.find((t) => t.id === activeId);
-    if (!tab || tab.kind !== "editor") {
-      if (blameDebounceRef.current) clearTimeout(blameDebounceRef.current);
-      setBlameInfo(null);
-    }
+    if (blameDebounceRef.current) clearTimeout(blameDebounceRef.current);
+    setBlameInfo(null);
   }, [activeId, activeLeafId]);
       if (blameDebounceRef.current) clearTimeout(blameDebounceRef.current);
       blameDebounceRef.current = setTimeout(() => {
-        native.gitBlame(cwd, filePath, line).then(setBlameInfo).catch(() => {
-          setBlameInfo(null);
-        });
+        native.gitBlame(cwd, filePath, line)
+          .then((info) => {
+            if (id === activeIdRef.current) setBlameInfo(info);
+          })
+          .catch(() => {
+            if (id === activeIdRef.current) setBlameInfo(null);
+          });
       }, 300);

Note: clearing unconditionally on every tab switch means a brief "no blame" flash until the new tab's first cursor event fires — acceptable trade-off versus showing the wrong author.

Also applies to: 908-928

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/app/App.tsx` around lines 334 - 346, The blame state update is stale
across tab switches: handleEditorCursorChange’s debounced native.gitBlame result
can resolve after the user has switched tabs, and the current useEffect only
clears blameInfo when leaving editor tabs entirely. Update the App.tsx logic
around handleEditorCursorChange and the activeId/activeLeafId effect to either
guard the async setBlameInfo/catch path with the originating tab id or active
tab check, and clear blameInfo on every tab switch so hidden mounted editor tabs
cannot overwrite the currently visible tab’s blame data.

Source: Path instructions

🧹 Nitpick comments (1)
.claude/skills/terax-pr-workflow/SKILL.md (1)

34-34: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Replace npx tsc --noEmit with pnpm check-types.

The project mandates pnpm exclusively and defines pnpm check-types as the type-check command in its quality bar. Using npx contradicts the pnpm only convention and creates a rug-pull risk (RP1). As per coding guidelines, pnpm only, never npm/npx/yarn.

Also applies to: 129-129

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/terax-pr-workflow/SKILL.md at line 34, The workflow docs
still use npx tsc --noEmit, which conflicts with the pnpm-only convention.
Update the affected command in SKILL.md to use pnpm check-types instead, and
make the same replacement at the additional referenced location so all
type-check guidance points to the project’s standard command.

Sources: Coding guidelines, Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.claude/skills/terax-pr-workflow/SKILL.md:
- Line 90: The PR body template contains an emoji, which violates the project’s
no-emoji rule. Update the generated attribution text in SKILL.md to plain text
only, using the existing “Generated with Claude Code” wording without any emoji
or special symbols, and keep the change localized to the template content.
- Line 3: The skill description in SKILL.md contains em dashes, which violate
the project’s punctuation rules. Update the description text to use hyphens or
commas instead, and make sure the wording in the skill’s description remains
equivalent while removing every em dash from that entry.

In `@src-tauri/src/modules/git/operations.rs`:
- Around line 1150-1201: Refactor blame_line so the porcelain parsing is moved
into a pure helper, such as parse_blame_porcelain, and keep blame_line focused
on the run_git I/O and exit-code handling. Extract the text.lines()
author/timestamp loop into that helper using GitBlameLineInfo as the return
value, then add unit tests directly against the parser with fixture strings
covering valid output, malformed output, missing author-time, and the "Not
Committed Yet" filter.

In `@src/modules/ai/store/chatStore.ts`:
- Line 242: The default for `panelOpen` in `chatStore` now causes the AI panel
to open on every startup, ignoring the user’s previous choice. Update the
`chatStore` state handling so `panelOpen` is persisted and restored across
launches, and keep the in-memory default from forcing it open unless a saved
value is available.

In `@src/modules/editor/EditorPane.tsx`:
- Around line 238-245: Switching editor tabs leaves stale blameInfo because the
current cursor listener in EditorPane only updates on selection changes; reset
or refresh it whenever activeId changes so the status bar reflects the newly
active file immediately. Update the logic around EditorPane and the
onCursorChangeRef/updateListener flow to either clear blame state on every
activeId switch or read the active editor’s current cursor position and invoke
the existing callback right away.

In `@src/modules/tabs/TabBar.tsx`:
- Around line 626-636: The TabIcon rendering path is returning the agent brand
mark before the private-tab incognito state is checked, so private terminal tabs
can lose their privacy indicator. Update TabIcon so the tab.private branch takes
precedence over the agent lookup/AgentIcon branch, keeping the incognito icon
visible for private tabs even when an agent is detected.

---

Outside diff comments:
In `@src/app/App.tsx`:
- Around line 334-346: The blame state update is stale across tab switches:
handleEditorCursorChange’s debounced native.gitBlame result can resolve after
the user has switched tabs, and the current useEffect only clears blameInfo when
leaving editor tabs entirely. Update the App.tsx logic around
handleEditorCursorChange and the activeId/activeLeafId effect to either guard
the async setBlameInfo/catch path with the originating tab id or active tab
check, and clear blameInfo on every tab switch so hidden mounted editor tabs
cannot overwrite the currently visible tab’s blame data.

---

Nitpick comments:
In @.claude/skills/terax-pr-workflow/SKILL.md:
- Line 34: The workflow docs still use npx tsc --noEmit, which conflicts with
the pnpm-only convention. Update the affected command in SKILL.md to use pnpm
check-types instead, and make the same replacement at the additional referenced
location so all type-check guidance points to the project’s standard command.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 3ac4b4e6-2c60-42a0-bda7-03f1432857ef

📥 Commits

Reviewing files that changed from the base of the PR and between 3f4d680 and e454866.

📒 Files selected for processing (32)
  • .claude/skills/terax-pr-workflow/SKILL.md
  • src-tauri/src/lib.rs
  • src-tauri/src/modules/git/commands.rs
  • src-tauri/src/modules/git/operations.rs
  • src-tauri/src/modules/git/types.rs
  • src/app/App.tsx
  • src/app/components/WorkspaceSurface.tsx
  • src/modules/agents-panel/AgentsPanel.tsx
  • src/modules/agents-panel/index.ts
  • src/modules/agents/components/AgentNotificationsBridge.tsx
  • src/modules/agents/components/NotificationBell.tsx
  • src/modules/agents/lib/agentIcon.tsx
  • src/modules/agents/lib/format.ts
  • src/modules/agents/lib/types.ts
  • src/modules/agents/store/agentStore.ts
  • src/modules/ai/components/AiStatusBarControls.tsx
  • src/modules/ai/lib/native.ts
  • src/modules/ai/store/chatStore.ts
  • src/modules/command-palette/commands.ts
  • src/modules/editor/EditorPane.tsx
  • src/modules/editor/EditorStack.tsx
  • src/modules/header/Header.tsx
  • src/modules/settings/store.ts
  • src/modules/shortcuts/shortcuts.ts
  • src/modules/sidebar/SidebarRail.tsx
  • src/modules/sidebar/types.ts
  • src/modules/sidebar/useSidebarPanel.ts
  • src/modules/statusbar/StatusBar.tsx
  • src/modules/tabs/TabBar.tsx
  • src/modules/terminal/lib/rendererPool.ts
  • src/modules/terminal/lib/useTerminalSession.ts
  • src/settings/sections/GeneralSection.tsx

@@ -0,0 +1,130 @@
---
name: terax-pr-workflow
description: Use this skill whenever making ANY change to this repository — features, fixes, shortcuts, refactors, anything. It enforces the mandatory branch → commit → dual-PR (fork + upstream) → merge-fork → back-to-main workflow. Trigger on phrases like "create a PR", "add a feature", "fix this", "push this", "open a pull request", or any task that involves committing and shipping code changes.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove em-dashes from skill description.

The project prohibits em dashes in all files. Replace with hyphens or commas. As per coding guidelines, do not use em dashes anywhere, including code, comments, commits, and docs.

🧰 Tools
🪛 SkillSpector (2.3.7)

[warning] 34: [RP1] null: npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Remediation: Pin the version: npx @scope/server@1.2.3

(MCP Rug Pull (RP1))


[warning] 129: [RP1] null: npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Remediation: Pin the version: npx @scope/server@1.2.3

(MCP Rug Pull (RP1))

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/terax-pr-workflow/SKILL.md at line 3, The skill description
in SKILL.md contains em dashes, which violate the project’s punctuation rules.
Update the description text to use hyphens or commas instead, and make sure the
wording in the skill’s description remains equivalent while removing every em
dash from that entry.

Source: Coding guidelines

## Test plan
- [ ] item

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove emoji from PR body template.

The project prohibits emojis in all files. Replace with plain text, e.g., [Generated with Claude Code]. As per coding guidelines, do not use emojis anywhere.

🧰 Tools
🪛 SkillSpector (2.3.7)

[warning] 34: [RP1] null: npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Remediation: Pin the version: npx @scope/server@1.2.3

(MCP Rug Pull (RP1))


[warning] 129: [RP1] null: npx commands without a version suffix (e.g. @1.0.0) create a rug-pull risk if the upstream server is compromised and publishes a malicious update.

Remediation: Pin the version: npx @scope/server@1.2.3

(MCP Rug Pull (RP1))

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.claude/skills/terax-pr-workflow/SKILL.md at line 90, The PR body template
contains an emoji, which violates the project’s no-emoji rule. Update the
generated attribution text in SKILL.md to plain text only, using the existing
“Generated with Claude Code” wording without any emoji or special symbols, and
keep the change localized to the template content.

Source: Coding guidelines

Comment on lines +1150 to +1201
pub fn blame_line(
registry: &WorkspaceRegistry,
cwd: &str,
path: &str,
line: u32,
workspace: &WorkspaceEnv,
) -> Result<Option<GitBlameLineInfo>> {
if line == 0 {
return Ok(None);
}
let dir = canonical_dir(registry, cwd, workspace)?;
if !registry.is_authorized(&dir.local_path) {
return Err(GitError::PathOutsideWorkspace(dir.local_path));
}
ensure_git_available(&dir.workspace)?;

let line_spec = format!("{},{}", line, line);
let output = run_git(
&dir.workspace,
Some(&dir.git_path),
["blame", "-L", &line_spec, "--porcelain", "--", path],
DEFAULT_TIMEOUT_SECS,
)?;

if output.exit_code != Some(0) {
return Ok(None);
}

let text = String::from_utf8_lossy(&output.stdout);
let mut author: Option<String> = None;
let mut timestamp: Option<i64> = None;

for line_text in text.lines() {
if let Some(name) = line_text.strip_prefix("author ") {
author = Some(name.to_string());
} else if let Some(ts) = line_text.strip_prefix("author-time ") {
if let Ok(t) = ts.trim().parse::<i64>() {
timestamp = Some(t);
}
}
if author.is_some() && timestamp.is_some() {
break;
}
}

Ok(match (author, timestamp) {
(Some(a), Some(t)) if a != "Not Committed Yet" => {
Some(GitBlameLineInfo { author: a, timestamp: t })
}
_ => None,
})
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Extract the porcelain parser into a pure function and add a test for it.

The parsing logic (lines 1178-1200) is sound, but it's fused to the run_git IO call, so it can't be unit-tested without shelling out to a real git process. TERAX.md's quality bar requires a test that locks the invariant for any git-subsystem change, and none is included for this parser (e.g. malformed porcelain output, "Not Committed Yet" filtering, missing author-time).

Splitting the text.lines() loop into a standalone fn parse_blame_porcelain(text: &str) -> Option<GitBlameLineInfo> keeps blame_line as the thin IO orchestrator and makes the parsing trivially testable with fixture strings.

As per coding guidelines, "A change to a core subsystem (terminal/shell spawn, workspace auth, git, fs, IPC or AI tool surface) needs a test that locks the invariant," and "new or changed logic lives in pure, dependency-light functions (functional core)."

♻️ Suggested split
fn parse_blame_porcelain(text: &str) -> Option<GitBlameLineInfo> {
    let mut author: Option<String> = None;
    let mut timestamp: Option<i64> = None;
    for line_text in text.lines() {
        if let Some(name) = line_text.strip_prefix("author ") {
            author = Some(name.to_string());
        } else if let Some(ts) = line_text.strip_prefix("author-time ") {
            if let Ok(t) = ts.trim().parse::<i64>() {
                timestamp = Some(t);
            }
        }
        if author.is_some() && timestamp.is_some() {
            break;
        }
    }
    match (author, timestamp) {
        (Some(a), Some(t)) if a != "Not Committed Yet" => Some(GitBlameLineInfo { author: a, timestamp: t }),
        _ => None,
    }
}

Then blame_line just calls Ok(output.exit_code == Some(0)).then(|| parse_blame_porcelain(&String::from_utf8_lossy(&output.stdout))).flatten()) (or an equivalent if), and a #[cfg(test)] module can exercise parse_blame_porcelain directly with porcelain fixtures.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src-tauri/src/modules/git/operations.rs` around lines 1150 - 1201, Refactor
blame_line so the porcelain parsing is moved into a pure helper, such as
parse_blame_porcelain, and keep blame_line focused on the run_git I/O and
exit-code handling. Extract the text.lines() author/timestamp loop into that
helper using GitBlameLineInfo as the return value, then add unit tests directly
against the parser with fixture strings covering valid output, malformed output,
missing author-time, and the "Not Committed Yet" filter.

Source: Coding guidelines

toggleMini: () => set((s) => ({ mini: { open: !s.mini.open } })),

panelOpen: false,
panelOpen: true,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '--- chatStore.ts outline ---\n'
ast-grep outline src/modules/ai/store/chatStore.ts --view expanded || true

printf '\n--- search panelOpen usages ---\n'
rg -n "panelOpen" src/modules src/App.tsx

printf '\n--- chatStore.ts relevant slice ---\n'
sed -n '1,340p' src/modules/ai/store/chatStore.ts

Repository: crynta/terax-ai

Length of output: 244


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- chatStore.ts outline ---'
ast-grep outline src/modules/ai/store/chatStore.ts --view expanded || true

echo
echo '--- search panelOpen usages ---'
rg -n "panelOpen" src/modules src/App.tsx

echo
echo '--- chatStore.ts relevant slice ---'
sed -n '1,340p' src/modules/ai/store/chatStore.ts

Repository: crynta/terax-ai

Length of output: 2158


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo '--- chatStore.ts around panelOpen ---'
sed -n '100,290p' src/modules/ai/store/chatStore.ts

echo
echo '--- search for persisted panelOpen / hydration ---'
rg -n "panelOpen|persist|hydrateSessions|set\\(\\{ panelOpen|openPanel\\(|closePanel\\(" src/modules/ai src/modules/statusbar src/modules

Repository: crynta/terax-ai

Length of output: 16797


AI panel now opens on every launch

panelOpen is still in-memory, so changing the default to true makes the docked AI surface start open on every app launch instead of respecting the user's last state. Persist this flag if that state should stick.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/ai/store/chatStore.ts` at line 242, The default for `panelOpen`
in `chatStore` now causes the AI panel to open on every startup, ignoring the
user’s previous choice. Update the `chatStore` state handling so `panelOpen` is
persisted and restored across launches, and keep the in-memory default from
forcing it open unless a saved value is available.

Comment on lines +238 to +245
EditorView.updateListener.of((update) => {
if (!update.selectionSet) return;
const cb = onCursorChangeRef.current;
if (!cb) return;
const pos = update.state.selection.main.head;
const lineInfo = update.state.doc.lineAt(pos);
cb(lineInfo.number, pos - lineInfo.from + 1);
}),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear blameInfo on editor-tab switches. Switching between two mounted editor tabs leaves the previous file’s blame in the status bar until the new tab emits a cursor move. Reset it on every activeId change, or refresh from the active editor’s current cursor when focus moves.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/editor/EditorPane.tsx` around lines 238 - 245, Switching editor
tabs leaves stale blameInfo because the current cursor listener in EditorPane
only updates on selection changes; reset or refresh it whenever activeId changes
so the status bar reflects the newly active file immediately. Update the logic
around EditorPane and the onCursorChangeRef/updateListener flow to either clear
blame state on every activeId switch or read the active editor’s current cursor
position and invoke the existing callback right away.

Comment on lines +626 to +636
export function TabIcon({
tab,
agentsByTabId,
}: {
tab: Tab;
agentsByTabId?: Map<number, string>;
}) {
const agent = tab.kind === "terminal" ? agentsByTabId?.get(tab.id) : undefined;
if (agent) {
return <AgentIcon agent={agent} size={14} className="shrink-0" />;
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Agent icon silently overrides the private-tab incognito indicator.

The new agent-icon branch returns before the tab.private check further down, so a private terminal running a detected coding agent loses its incognito icon entirely in favor of the agent brand mark. Since the private icon is the only visual cue that a tab is in private/incognito mode, this is a small but real regression for that feature.

Proposed fix: keep the private indicator in front
   const agent = tab.kind === "terminal" ? agentsByTabId?.get(tab.id) : undefined;
-  if (agent) {
+  if (agent && !(tab.kind === "terminal" && tab.private)) {
     return <AgentIcon agent={agent} size={14} className="shrink-0" />;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function TabIcon({
tab,
agentsByTabId,
}: {
tab: Tab;
agentsByTabId?: Map<number, string>;
}) {
const agent = tab.kind === "terminal" ? agentsByTabId?.get(tab.id) : undefined;
if (agent) {
return <AgentIcon agent={agent} size={14} className="shrink-0" />;
}
export function TabIcon({
tab,
agentsByTabId,
}: {
tab: Tab;
agentsByTabId?: Map<number, string>;
}) {
const agent = tab.kind === "terminal" ? agentsByTabId?.get(tab.id) : undefined;
if (agent && !(tab.kind === "terminal" && tab.private)) {
return <AgentIcon agent={agent} size={14} className="shrink-0" />;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/modules/tabs/TabBar.tsx` around lines 626 - 636, The TabIcon rendering
path is returning the agent brand mark before the private-tab incognito state is
checked, so private terminal tabs can lose their privacy indicator. Update
TabIcon so the tab.private branch takes precedence over the agent
lookup/AgentIcon branch, keeping the incognito icon visible for private tabs
even when an agent is detected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant