Skip to content

Fix Windows desktop chrome and project recovery#44

Open
justelson wants to merge 3 commits intoEmanuele-web04:mainfrom
justelson:fix/windows-titlebar-and-project-recovery
Open

Fix Windows desktop chrome and project recovery#44
justelson wants to merge 3 commits intoEmanuele-web04:mainfrom
justelson:fix/windows-titlebar-and-project-recovery

Conversation

@justelson
Copy link
Copy Markdown

@justelson justelson commented Apr 20, 2026

Summary

This PR fixes three desktop issues:

  1. #35: existing threads could disappear because desktop startup accepted a broken snapshot where live threads existed without matching live project rows.
  2. #43: project.create could succeed without surfacing the created project, and retrying the same folder then failed with the duplicate workspace-root invariant.
  3. Windows desktop chrome: the app was always taking the macOS title-bar path, which left the DP header offset incorrectly on Windows and Linux.

What changed

Issue #35: thread/sidebar recovery on desktop startup

  • Detect broken desktop snapshots where live threads exist without matching live project rows.
  • Repair that snapshot shape during desktop bootstrap instead of only repairing fully empty snapshots.
  • Prevent the sidebar from accepting that broken state and coming up empty even though thread data still exists.

Issue #43: project-add recovery after silent success

  • Centralize project recovery helpers so add-project and duplicate-create flows share the same read-model lookup behavior.
  • Retry the read model briefly after project creation.
  • Fall back to repairState() when polling still cannot surface the created project.
  • Recover duplicate-create flows by reopening the existing project instead of leaving the UI empty and surfacing the duplicate workspace-root error on retry.

Windows desktop chrome

  • Split desktop window chrome by platform instead of always using the macOS inset title bar.
  • Keep the native macOS traffic-light/title-bar behavior on macOS.
  • Use a hidden title bar on Windows and Linux and expose renderer-driven window controls through the desktop bridge.
  • Sync maximize/fullscreen state back to the renderer so maximize/restore icons stay in the correct state.
  • Remove the extra right-edge gap in the custom desktop header so the control strip sits flush against the window edge.
  • Render the same Windows controls across:
    • active chat header
    • empty-thread header
    • settings header
    • dedicated workspace header
  • Update the custom control icons to the intended Windows-style minimize / maximize / restore / close states.

Why

There were two separate recovery failures in the desktop app:

  • In #35, desktop startup could accept a broken local/read-model snapshot and show an empty sidebar even though live threads still existed.
  • In #43, the first add-project attempt could succeed internally but fail to appear in the UI, and retrying the same folder then produced:

Orchestration command invariant failed (project.create): Project '<id>' already uses workspace root '<path>'

Separately, the Windows title bar/header was still following the macOS chrome path, which left the branding and window controls misaligned on Windows/Linux.

Validation

Ran targeted tests:

  • bun run test src/windowChrome.test.ts in apps/desktop
  • bun run test src/lib/desktopWindow.test.ts src/lib/projectCreateRecovery.test.ts src/lib/desktopProjectRecovery.test.ts in apps/web

Results:

  • desktop: 1 file, 4 tests passed
  • web: 3 files, 17 tests passed

Notes

  • I intentionally left unrelated/generated changes out of this PR, including bun.lock, apps/web/src/routeTree.gen.ts, and apps/web/public/mockServiceWorker.js.
  • Before/after screenshots for the Windows chrome surfaces still need to be attached because part of this PR is a UI fix.

@github-actions github-actions Bot added size:XL vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Apr 20, 2026
@justelson justelson marked this pull request as ready for review April 21, 2026 06:56
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 93ce809c72

ℹ️ 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".

Comment on lines +399 to +401
"drag-region flex h-[52px] items-center",
usesCustomDesktopTitleBar ? "pl-5 pr-0" : "px-5",
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Exclude workspace title from draggable Electron region

The new Electron header makes the whole workspace top bar a drag-region, but the title still enters rename mode via onDoubleClick and is not marked as a no-drag element. In apps/web/src/index.css:238-247, only button/input/textarea/select/a are exempted from drag capture, so on desktop (Windows/Linux) the title no longer receives pointer events and users cannot trigger workspace rename by double-clicking.

Useful? React with 👍 / 👎.

Comment on lines 4593 to 4597
message: description,
workspaceRoot: firstSendTarget.creation.workspaceRoot,
loadSnapshot: () => api.orchestration.getSnapshot().catch(() => null),
repairSnapshot: () => api.orchestration.repairState().catch(() => null),
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Gate repair fallback to duplicate project-create errors

This path now always passes repairSnapshot into duplicate-recovery, but the call is made for any project.create failure in this catch block. When the error is not a duplicate-workspace invariant, we still do recovery polling and then call api.orchestration.repairState() before rethrowing, which adds avoidable delay and can mutate state on unrelated failures; this should be conditioned on isDuplicateProjectCreateError (or only provide repairSnapshot in that case).

Useful? React with 👍 / 👎.

@justelson
Copy link
Copy Markdown
Author

Follow-up pushed in 5c87c016.

Addressed the two Codex review findings:

  • Made the workspace title explicitly non-draggable inside the custom Electron drag region, so double-click rename still works on Windows/Linux.
  • Gated duplicate-project recovery behind isDuplicateProjectCreateError, so unrelated project.create failures no longer trigger recovery polling or repairState().

Re-ran the targeted tests:

  • bun run test src/windowChrome.test.ts
  • bun run test src/lib/projectCreateRecovery.test.ts src/lib/desktopWindow.test.ts src/lib/desktopProjectRecovery.test.ts

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5c87c01675

ℹ️ 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".

Comment on lines +399 to +401
"drag-region flex h-[52px] items-center",
usesCustomDesktopTitleBar ? "pl-5 pr-0" : "px-5",
)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Add macOS right-sidebar inset to workspace header

The new Electron header in WorkspaceView never applies the macOS left inset when the sidebar is on the right, so on macOS (hiddenInset traffic lights on the left) the workspace header’s left controls can sit under the native window buttons and become difficult to interact with. This is a regression specific to the isElectron && !supportsCustomDesktopTitleBar() && settings.sidebarSide === "right" case, and other headers in this commit already preserve that offset logic.

Useful? React with 👍 / 👎.

@justelson
Copy link
Copy Markdown
Author

Follow-up pushed in d3cf500c.

Addressed the latest Codex review finding by applying the same macOS hidden-titlebar inset logic to WorkspaceView that the chat/settings headers already use.

This fixes the isElectron && !supportsCustomDesktopTitleBar() && settings.sidebarSide === "right" case, so the workspace header controls do not sit under the native macOS traffic-light buttons.

Re-ran the targeted tests:

  • bun run test src/windowChrome.test.ts
  • bun run test src/lib/desktopWindow.test.ts src/lib/projectCreateRecovery.test.ts src/lib/desktopProjectRecovery.test.ts

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

Labels

size:XL vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant