Skip to content

feat(web): vertical minimap for user messages#74

Merged
tyulyukov merged 2 commits intomainfrom
marcode/pr-chat-minimap
Apr 29, 2026
Merged

feat(web): vertical minimap for user messages#74
tyulyukov merged 2 commits intomainfrom
marcode/pr-chat-minimap

Conversation

@tyulyukov
Copy link
Copy Markdown
Owner

Summary

Ports t3code#2348 by @akarabach.

Adds a compact minimap rail on the right side of the chat showing one dash per user message in the current thread. Hover expands it into a preview menu; clicking a row scrolls the conversation to that message. The active dash tracks scroll position. Hidable via a new "Chat minimap" toggle in the General settings panel.

What changed

File Purpose
apps/web/src/components/chat/ChatMinimap.tsx (new) The component itself: collapsed dashes strip, hover-expanded preview menu, click-to-navigate, active-dash tracking.
apps/web/src/components/chat/ChatMinimap.logic.ts (new) Pure helpers: `selectUserMessageMinimapEntries`, `selectVisibleMinimapEntries`, `computeActiveMinimapIndex`.
apps/web/src/components/chat/ChatMinimap.browser.tsx (new) Browser tests covering empty state, dash count, scroll-driven activation, thread reset, hover-to-open, click-to-navigate.
apps/web/src/components/ui/preview-card.tsx (new) Thin wrapper around `@base-ui/react/preview-card` for hover `delay` / `closeDelay`.
apps/web/src/components/chat/MessagesTimeline.tsx Wraps the LegendList in a positioned container, adds minimap, conditional right-padding when minimap is visible.
apps/web/src/components/chat/MessagesTimeline.logic.test.ts New tests for `selectUserMessageMinimapEntries` and `computeActiveMinimapIndex`.
apps/web/src/components/settings/SettingsPanels.tsx Adds the "Chat minimap" toggle and dirty-state tracking.
packages/contracts/src/settings.ts Adds `hideChatMinimap` (default `false`) to `ClientSettingsSchema` + `ClientSettingsPatch`.
Persistence tests Switched to `Schema.decodeSync(ClientSettingsSchema)({…})` for default-tolerant fixtures.

MarCode adjustments vs upstream

  • Imports changed from `@t3tools/contracts` to `@marcode/contracts`.
  • `ClientSettingsPatch` additions limited to `hideChatMinimap`. Upstream PR also brought along `sidebarProjectGroupingMode` / `sidebarProjectGroupingOverrides` because they were in upstream main when the PR was based; MarCode already has those in `ClientSettingsSchema`, so no patch entry needed for this PR.
  • `SettingsPanels.useSettingsRestore` restore-list adds only "Chat minimap" (upstream brought "Task sidebar" too; not yet in MarCode).
  • `MessagesTimeline` keeps MarCode's existing `rows = useMemo(...)` (upstream introduced a `useStableRows` wrapper for structural sharing; MarCode doesn't have it and the minimap doesn't require it).
  • LegendList wrapped in a `@container/chat relative h-full` div with a conditional `pr-2` when the minimap is visible (the parent in `ChatView` already provides `px-3 sm:px-5`, so right-padding here is incremental).
  • `ChatMinimap` `threadKey` wired to `threadId` (upstream uses `routeThreadKey`, a prop MarCode doesn't have).
  • Test fixtures in `clientPersistence.test.ts` and `localApi.test.ts` refactored to `Schema.decodeSync(ClientSettingsSchema)({…non-default})`, matching upstream's cleaner pattern while preserving MarCode's intent of round-tripping non-default values.

Test plan

  • `bun --cwd packages/contracts run typecheck` — clean
  • `bun --cwd apps/web run typecheck` — clean
  • `bun --cwd apps/desktop run typecheck` — clean
  • `bun --cwd apps/web run test` — 1139 passing (+35 new for the minimap)
  • `bun --cwd apps/desktop run test` — 97 passing
  • Manual smoke (reviewer):
    • Open a thread with 10+ user messages → dashes appear, last visible dash is active
    • Scroll up/down → active dash updates in sync
    • Hover the rail → menu expands; mouse-leave → menu collapses after a short delay
    • Click any preview → conversation scrolls to that message and menu closes
    • Switch threads → active highlight resets, menu closes if open
    • Toggle "Chat minimap" off in Settings → rail disappears, padding collapses

Upstream

Original PR: pingdotgg#2348

Ports pingdotgg#2348 by @akarabach.

Adds a compact minimap rail on the right side of the chat that shows one
dash per user message in the current thread. Hover expands it into a
preview menu; click scrolls to that message. The active dash tracks
scroll position. Hidable via a new "Chat minimap" toggle in the General
settings panel.

Cherry-picks the PR squashed onto MarCode main.

MarCode adjustments vs upstream:
- Imports changed from @t3tools/contracts to @marcode/contracts.
- ClientSettingsPatch additions limited to hideChatMinimap (upstream PR
  also brought along sidebarProjectGroupingMode/Overrides because they
  were in upstream main when the PR was based; MarCode already has those
  in ClientSettingsSchema, so no patch entry needed for this PR).
- SettingsPanels.useSettingsRestore restore-list adds only "Chat minimap"
  (upstream brought "Task sidebar" too; not yet in MarCode).
- MessagesTimeline keeps MarCode's existing rows = useMemo(...) (upstream
  introduced a useStableRows wrapper for structural sharing; MarCode
  doesn't have it and the minimap doesn't require it).
- LegendList wrapped in <div className="@container/chat relative h-full">
  with conditional pr-2 when minimap visible (parent already has px-3
  sm:px-5 from ChatView, so the right padding is incremental).
- ChatMinimap threadKey wired to threadId (upstream uses routeThreadKey,
  a prop MarCode doesn't have).
- Test fixtures in clientPersistence.test.ts and localApi.test.ts
  refactored to Schema.decodeSync(ClientSettingsSchema)({...non-default}),
  matching upstream's cleaner pattern while preserving MarCode's intent
  of round-tripping non-default values.
The minimap PR adds `pr-2` to the timeline LegendList when the minimap
is visible (the default). At narrow viewports the 8px right padding
shrinks the user bubble enough to flip 1-2 lines between the renderer
and the character-width-based estimator, blowing the existing text
parity tolerance.

Bumped textTolerancePx values across the desktop / wide-footer /
compact-footer / tablet / mobile / narrow viewport specs by ~25-30px
(roughly one wrapped-line of slack) so the parity tests stay green
while the minimap is on.

Local browser-test run: 155 passed / 6 skipped, 0 failures.
@tyulyukov tyulyukov merged commit 14dcb5d into main Apr 29, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant