Skip to content

feat(frontend): mobile-friendly config editor with find bar and unsaved-changes guard - #326

Merged
chriswritescode-dev merged 2 commits into
mainfrom
feat/mobile-config-editor
Jul 27, 2026
Merged

feat(frontend): mobile-friendly config editor with find bar and unsaved-changes guard#326
chriswritescode-dev merged 2 commits into
mainfrom
feat/mobile-config-editor

Conversation

@chriswritescode-dev

@chriswritescode-dev chriswritescode-dev commented Jul 27, 2026

Copy link
Copy Markdown
Owner

Summary

Adds a mobile-friendly config editor experience and supporting infrastructure:

  • New reusable CodeEditor component with an editor-find-bar (find/replace), unsaved-changes-dialog guard, and safe-area-aware layout for mobile keyboards.
  • Refactors OpenCodeConfigEditor and AgentsMdEditor to use the new editor; SettingsDialog now hosts the unsaved-changes guard.
  • Adds useVisualViewport hook and editorScroll helper to keep the cursor visible above the on-screen keyboard.
  • Backend: repo service retry/auth handling for env-derived credentials, with new db query and route coverage; shared jsonc utilities hardened with tests.

Type of Change

  • Bug fix
  • New feature
  • Refactor
  • Documentation

Checklist

  • Code follows project style (no comments, named imports)
  • TypeScript types are properly defined
  • Tests added/updated (80% coverage target)
  • pnpm lint passes locally
  • pnpm typecheck passes locally

Summary by CodeRabbit

  • New Features
    • Added enhanced code editors with line numbers, syntax highlighting, active-line navigation, and find-in-content controls.
    • Added clear, line-specific validation feedback for JSON/JSONC configuration errors.
    • Added unsaved-changes confirmation when closing or discarding edits.
    • Improved mobile dialog behavior when the on-screen keyboard is open.
  • Bug Fixes
    • Prevented nested dialogs from closing unexpectedly when pressing Escape.
    • Preserved edits during saves and background refreshes.
    • Standardized destructive-action confirmation dialogs across settings.

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown

Review Change Stack

Important

Review skipped

No new commits to review since the last review.

⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2d0f4765-f1cc-49c5-affe-e58bb3638f09

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR adds a mirrored code editor with search and highlighting, integrates it into AGENTS.md and OpenCode configuration editors, adds JSONC validation line mapping, consolidates destructive dialogs, and improves dialog behavior for nested Escape events and mobile keyboard insets.

Changes

Settings editor controls

Layer / File(s) Summary
Editor controls and confirmation dialogs
frontend/src/components/ui/code-editor.tsx, frontend/src/components/ui/editor-find-bar.tsx, frontend/src/components/ui/*dialog.tsx, frontend/src/lib/editorScroll.ts
Adds mirrored textarea rendering, line numbers, highlights, match navigation, reveal scrolling, reusable confirmation dialogs, and associated tests.
JSONC issue line mapping
shared/src/utils/jsonc.ts, frontend/src/lib/jsonc.ts, frontend/src/lib/jsonc.test.ts
Adds JSONC syntax-line extraction and path-to-source-line resolution for validation issues.
AGENTS.md editor synchronization
frontend/src/components/settings/AgentsMdEditor.tsx, frontend/src/components/settings/AgentsMdEditor.test.tsx
Tracks saved content separately from local edits, preserves edits during saves and refetches, and integrates search highlighting with save/reset state handling.
OpenCode configuration editing
frontend/src/components/settings/OpenCodeConfigEditor.tsx, frontend/src/components/settings/OpenCodeConfigEditor.test.tsx
Uses CodeEditor for raw JSONC editing, maps validation issues to lines, supports unsaved-change confirmation, and manages pending save behavior.
Configuration refresh behavior
frontend/src/components/settings/OpenCodeConfigManager.tsx, frontend/src/components/settings/OpenCodeConfigManager.test.tsx
Adds silent post-save refreshes, preserves the editor while refreshes are pending, and changes the AGENTS.md container overflow class.

Dialog and viewport behavior

Layer / File(s) Summary
Keyboard-aware dialog layout
frontend/src/hooks/useVisualViewport.ts, frontend/src/components/ui/dialog.tsx, frontend/src/components/ui/dialog.test.tsx, frontend/src/hooks/useVisualViewport.test.tsx
Adds optional visual-viewport keyboard padding and updates mobile swipe-container tracking.
Nested-dialog keyboard handling
frontend/src/components/settings/SettingsDialog.tsx, frontend/src/components/settings/SettingsDialog.test.tsx
Prevents the settings dialog from closing when Escape originates inside another dialog.
Responsive viewport initialization
frontend/src/hooks/useMobile.ts
Initializes mobile state from the current viewport width with an SSR-safe fallback.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant OpenCodeConfigEditor
  participant CodeEditor
  participant OpenCodeConfigManager
  participant JSONCUtilities
  User->>CodeEditor: edit raw JSONC
  CodeEditor->>OpenCodeConfigEditor: emit content change
  OpenCodeConfigEditor->>JSONCUtilities: resolve validation issue lines
  OpenCodeConfigEditor->>OpenCodeConfigManager: submit validated content
  OpenCodeConfigManager-->>OpenCodeConfigEditor: complete save and refresh
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately captures the main frontend editor change.
Description check ✅ Passed The description includes all required sections and a filled checklist, with enough detail for the main changes.
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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/mobile-config-editor

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: 5

🧹 Nitpick comments (5)
frontend/src/hooks/useVisualViewport.test.tsx (1)

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

Remove the TypeScript suppression comments. Use Reflect.deleteProperty(window, 'visualViewport') instead.

  • frontend/src/hooks/useVisualViewport.test.tsx#L41-L42: replace the suppression and delete expression.
  • frontend/src/components/ui/dialog.test.tsx#L409-L410: replace the suppression and delete expression.
Proposed fix
-      // `@ts-expect-error` allow delete
-      delete window.visualViewport
+      Reflect.deleteProperty(window, 'visualViewport')
🤖 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 `@frontend/src/hooks/useVisualViewport.test.tsx` around lines 41 - 42, Remove
the TypeScript suppression and direct delete expression in
frontend/src/hooks/useVisualViewport.test.tsx lines 41-42, replacing it with
Reflect.deleteProperty(window, 'visualViewport'). Apply the same replacement in
frontend/src/components/ui/dialog.test.tsx lines 409-410; no other changes are
needed.

Source: Coding guidelines

backend/src/routes/repos.ts (1)

363-385: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Pre-flight validation duplicates retryCloneRepo's own checks.

Lines 371-380 mirror the service's existence/repoUrl/state guards. Keeping both means two places to update when retryable states change (already diverging with claimRepoForRetry). Consider having the service throw status-annotated errors and letting the route just map them.

🤖 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 `@backend/src/routes/repos.ts` around lines 363 - 385, Remove the duplicated
repository existence, remote URL, and clone-state validation from the
`/:id/retry-clone` route, while retaining request ID parsing and the
assistant-repository guard. Update `repoService.retryCloneRepo` to own these
checks and throw status-annotated errors, then have the route catch and map
those errors to the corresponding JSON responses and HTTP statuses.

Source: Coding guidelines

backend/src/services/repo.ts (1)

885-889: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Explanatory comments across the new code conflict with the repo's no-comments rule.

  • backend/src/services/repo.ts#L885-L889: remove this and the other rationale blocks (Lines 645-648, 899-902, 924-927, 956-962).
  • backend/src/db/queries.ts#L294-L299: remove the JSDoc block; the function name conveys the intent.
  • backend/test/services/repo-retry.test.ts#L230-L232: remove the inline comments here and at Lines 277-278, 328-330, 352-354, 374-375.

As per coding guidelines: "Do not add comments; code should be self-documenting."

🤖 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 `@backend/src/services/repo.ts` around lines 885 - 889, Remove the explanatory
comments added for the retry/concurrency changes, keeping the implementation
unchanged: delete the rationale blocks in backend/src/services/repo.ts at lines
645-648, 885-889, 899-902, 924-927, and 956-962; remove the JSDoc above the
relevant function in backend/src/db/queries.ts at lines 294-299; and remove the
inline comments in backend/test/services/repo-retry.test.ts at lines 230-232,
277-278, 328-330, 352-354, and 374-375.

Source: Coding guidelines

backend/test/routes/repos.test.ts (1)

342-394: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for the cloning retryable state and the guard branches.

The suite covers error only. A test with cloneStatus: 'cloning' would surface the claim mismatch flagged in backend/src/db/queries.ts. The Number.isNaN(id) and ASSISTANT_REPO_ID branches (route Lines 366-369) are also untested.

🤖 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 `@backend/test/routes/repos.test.ts` around lines 342 - 394, The retry-clone
route tests only cover the error state and miss key guard branches. Extend the
suite around createRepoRoutes and the retry-clone endpoint with a successful
retry case using cloneStatus: 'cloning', plus requests that exercise invalid
numeric IDs triggering Number.isNaN(id) and the ASSISTANT_REPO_ID guard; assert
each branch’s expected status and that retryCloneRepo is not called when
validation rejects the request.

Source: Coding guidelines

frontend/src/components/ui/code-editor.test.tsx (1)

247-269: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

This test doesn't actually exercise the "fallback" path it claims to test.

With highlights={[{ startIndex: 0, endIndex: 4 }]} and activeHighlightIndex={0}, a mark[data-active-match="true"] element is always rendered (per the first && h.index === activeHighlightIndex logic in code-editor.tsx), so revealActiveMark() succeeds and short-circuits before revealLine runs. The assertion passes only because both code paths coincidentally compute scrollTop === 0 here, not because the fallback branch was verified.

Consider forcing the mark lookup to genuinely fail (e.g., stub mirror.querySelector to return null for the active-match selector, or pass an activeHighlightIndex that has no corresponding row rendering) so the fallback logic is actually covered.

🤖 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 `@frontend/src/components/ui/code-editor.test.tsx` around lines 247 - 269, Make
the fallback test genuinely exercise revealLine by forcing the active mark
lookup in revealActiveMark to return no element, such as stubbing the mirror’s
querySelector for the active-match selector, while keeping the existing rerender
and scrollTop assertion. Ensure the test no longer succeeds through the rendered
mark path in CodeEditor.
🤖 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 `@backend/src/db/queries.ts`:
- Around line 300-306: Align retryable clone-state validation with the atomic
claim: update claimRepoForRetry in backend/src/db/queries.ts (lines 300-306) to
support error and cloning with a staleness guard, or consistently remove cloning
from validation. Apply the same chosen definition in backend/src/routes/repos.ts
(lines 378-380) and backend/src/services/repo.ts (lines 881-883), preferably
through a shared isRetryableCloneStatus helper.

In `@backend/src/services/repo.ts`:
- Around line 890-892: Update the concurrent retry error in
backend/src/services/repo.ts#L890-L892 to carry statusCode 409, following the
existing directory-collision pattern. Leave
backend/src/routes/repos.ts#L386-L389 unchanged because it will map the
annotated error automatically, and add a route test covering the concurrent
retry case with an HTTP 409 response.
- Line 963: Restrict the missing-branch fallback in the repository handling
logic to messages matching the branch-specific “Remote branch ... not found in
upstream” phrasing, removing the broad message.includes('not found') check.
Apply the same predicate to the create-time path, preferably by extracting and
reusing a shared helper.
- Around line 850-856: Update the catch block containing updateRepoStatus so any
error from the status update is handled without replacing the original clone
failure. Preserve propagation of the original caught error while still
attempting to mark the repository as errored, including when the repository was
deleted concurrently.

In `@backend/test/services/repo-retry.test.ts`:
- Around line 246-251: Update the assertions for newBranchAddCalls and the
corresponding assertion at the other affected location to use the captured call
argument list directly, removing the unknown-to-string[] cast. Assert
command-array membership or the appropriate command element explicitly,
preserving the intended verification that the command includes “develop”.

---

Nitpick comments:
In `@backend/src/routes/repos.ts`:
- Around line 363-385: Remove the duplicated repository existence, remote URL,
and clone-state validation from the `/:id/retry-clone` route, while retaining
request ID parsing and the assistant-repository guard. Update
`repoService.retryCloneRepo` to own these checks and throw status-annotated
errors, then have the route catch and map those errors to the corresponding JSON
responses and HTTP statuses.

In `@backend/src/services/repo.ts`:
- Around line 885-889: Remove the explanatory comments added for the
retry/concurrency changes, keeping the implementation unchanged: delete the
rationale blocks in backend/src/services/repo.ts at lines 645-648, 885-889,
899-902, 924-927, and 956-962; remove the JSDoc above the relevant function in
backend/src/db/queries.ts at lines 294-299; and remove the inline comments in
backend/test/services/repo-retry.test.ts at lines 230-232, 277-278, 328-330,
352-354, and 374-375.

In `@backend/test/routes/repos.test.ts`:
- Around line 342-394: The retry-clone route tests only cover the error state
and miss key guard branches. Extend the suite around createRepoRoutes and the
retry-clone endpoint with a successful retry case using cloneStatus: 'cloning',
plus requests that exercise invalid numeric IDs triggering Number.isNaN(id) and
the ASSISTANT_REPO_ID guard; assert each branch’s expected status and that
retryCloneRepo is not called when validation rejects the request.

In `@frontend/src/components/ui/code-editor.test.tsx`:
- Around line 247-269: Make the fallback test genuinely exercise revealLine by
forcing the active mark lookup in revealActiveMark to return no element, such as
stubbing the mirror’s querySelector for the active-match selector, while keeping
the existing rerender and scrollTop assertion. Ensure the test no longer
succeeds through the rendered mark path in CodeEditor.

In `@frontend/src/hooks/useVisualViewport.test.tsx`:
- Around line 41-42: Remove the TypeScript suppression and direct delete
expression in frontend/src/hooks/useVisualViewport.test.tsx lines 41-42,
replacing it with Reflect.deleteProperty(window, 'visualViewport'). Apply the
same replacement in frontend/src/components/ui/dialog.test.tsx lines 409-410; no
other changes are needed.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d22fae1f-ffe8-4770-93ef-e626d79ad121

📥 Commits

Reviewing files that changed from the base of the PR and between f8e361c and 32bea8d.

📒 Files selected for processing (31)
  • backend/src/db/queries.ts
  • backend/src/routes/repos.ts
  • backend/src/services/repo.ts
  • backend/test/db/queries.test.ts
  • backend/test/routes/repos.test.ts
  • backend/test/services/repo-auth-env.test.ts
  • backend/test/services/repo-retry.test.ts
  • frontend/src/components/settings/AgentsMdEditor.test.tsx
  • frontend/src/components/settings/AgentsMdEditor.tsx
  • frontend/src/components/settings/OpenCodeConfigEditor.test.tsx
  • frontend/src/components/settings/OpenCodeConfigEditor.tsx
  • frontend/src/components/settings/OpenCodeConfigManager.test.tsx
  • frontend/src/components/settings/OpenCodeConfigManager.tsx
  • frontend/src/components/settings/SettingsDialog.test.tsx
  • frontend/src/components/settings/SettingsDialog.tsx
  • frontend/src/components/ui/code-editor.test.tsx
  • frontend/src/components/ui/code-editor.tsx
  • frontend/src/components/ui/dialog.test.tsx
  • frontend/src/components/ui/dialog.tsx
  • frontend/src/components/ui/editor-find-bar.test.tsx
  • frontend/src/components/ui/editor-find-bar.tsx
  • frontend/src/components/ui/unsaved-changes-dialog.tsx
  • frontend/src/hooks/useMobile.ts
  • frontend/src/hooks/useVisualViewport.test.tsx
  • frontend/src/hooks/useVisualViewport.ts
  • frontend/src/index.css
  • frontend/src/lib/editorScroll.test.ts
  • frontend/src/lib/editorScroll.ts
  • frontend/src/lib/jsonc.test.ts
  • frontend/src/lib/jsonc.ts
  • shared/src/utils/jsonc.ts

Comment thread backend/src/db/queries.ts Outdated
Comment thread backend/src/services/repo.ts
Comment thread backend/src/services/repo.ts Outdated
Comment thread backend/src/services/repo.ts Outdated
Comment thread backend/test/services/repo-retry.test.ts Outdated
Backend drops POST /:id/retry-clone and the supporting retryCloneRepo service
and claimRepoForRetry query; failed clones now delete the repo row rather than
lingering in error state. Frontend extracts ConfirmDestructiveDialog shared by
Delete/Discard/UnsavedChanges dialogs, memoizes CodeEditor rows, fixes scroll
jump while typing away from the active match, and aligns the responsive
breakpoint to 768px.
@chriswritescode-dev

Copy link
Copy Markdown
Owner Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 27, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@chriswritescode-dev
chriswritescode-dev merged commit a73ac19 into main Jul 27, 2026
5 checks passed
@chriswritescode-dev
chriswritescode-dev deleted the feat/mobile-config-editor branch July 27, 2026 20:46
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