Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
- Conventional commits enforced by hook: `type(scope): subject` ≤ 50 chars; types: `feat|fix|docs|dx|style|refactor|perf|test|workflow|build|ci|chore|types|wip|release`.
- Do not include AI co-authoring footers in commits.
- PRs: clear description, link issues (`Closes #123`), screenshots/GIFs for UI, pass lint/typecheck/tests. Keep changes focused.
- Default PR base is `dev`; use `gh pr create --base dev` for routine feature, bugfix, docs, test, and refactor branches. Target `main` only for `release/<version>` branches following `docs/release-flow.md`.
- UI changes: include BEFORE/AFTER ASCII layout blocks to communicate structure.

## Architecture Notes & Security
Expand Down
101 changes: 101 additions & 0 deletions docs/issues/markdown-codeblock-session-scroll-regressions/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# Plan

## Diagnosis

### Code Block Toolbar

`src/renderer/src/assets/style.css` imports `markstream-vue/index.tailwind.css`, but Tailwind still
needs to scan the package's generated class candidates. The current source points at:

```css
@source '../../../../node_modules/markstream-vue/dist/tailwind.ts';
```

The installed `markstream-vue@1.0.0-rc.0` package ships `dist/tailwind.js` and
`dist/tailwind.d.ts`, not `dist/tailwind.ts`. Because the source target does not exist, Tailwind
does not see the class candidates used by the package's code block shell, including
`code-block-header`, `px-[var(--ms-inset-panel-x)]`, `py-[var(--ms-inset-panel-y)]`, and
`p-[var(--ms-action-btn-padding)]`.

The package CSS import still provides variables and base styles, so the failure appears as a partial
style regression instead of a fully unstyled component.

### Session Switch Scroll

`src/renderer/src/pages/ChatPage.vue` restores a session by loading messages, waiting for
`nextTick()`, syncing scroll metrics, and then calling `scrollToBottom(true)`.
`scrollToBottom(true)` currently performs a single `requestAnimationFrame` measurement and sets
`scrollTop` from the scroll height available in that frame.

Message rows use `content-visibility: auto` with `contain-intrinsic-size: auto 180px`, and rendered
message content can continue changing size after the first frame. Markdown blocks, code blocks,
images, status rows, and input-area layout can all increase the final scroll height after the forced
scroll has already run. Since no message revision necessarily changes after this late layout settle,
the existing auto-follow watchers do not perform another corrective scroll.

## Proposed Solution

### 1. Restore `markstream-vue` Tailwind Scanning

- Change the renderer Tailwind source from `dist/tailwind.ts` to `dist/tailwind.js`.
- Keep the existing `@import 'markstream-vue/index.tailwind.css'` import for package CSS and design
variables.
- Add a focused guard so future package path changes fail loudly. The guard should verify that
representative code block class candidates from `markstream-vue` are included in Tailwind's source
scanning or generated CSS.
- Manually verify a rendered code block in light and dark themes after the implementation.

### 2. Settle Bottom Scroll During Session Restore

- Add a dedicated session-restore bottom-scroll helper instead of changing normal streaming
auto-follow behavior.
- The helper should force bottom scroll immediately after session restore and then continue for a
short bounded settle window.
- Recommended implementation:
- Use a session-local request id so pending settle work cancels when the user switches sessions
again.
- Run a small number of animation-frame retries and stop once `scrollHeight` has remained stable
for consecutive frames.
- Attach a temporary `ResizeObserver` to the scroll area or message root for roughly the first
few hundred milliseconds, forcing bottom again when late layout changes arrive.
- Disconnect the observer and cancel queued frames once the settle window ends, a spotlight jump is
requested, the session changes, or the user intentionally scrolls away.
- Keep the current near-bottom logic for streaming updates so the previous bottom-shake fix remains
intact.

### 3. Force Bottom Scroll After User Submit

- User submit is an explicit intent to continue at the newest message, so submit and command-submit
paths should schedule a forced bottom scroll after input state has cleared.
- This scroll should complement, not replace, the normal message-list watcher. The forced pass makes
the watcher robust when `isNearBottom` was stale after session restore or late layout changes.

## Affected Interfaces

- `src/renderer/src/assets/style.css`
- `src/renderer/src/pages/ChatPage.vue`
- Potential focused tests under `test/renderer/**`

No main-process, IPC, persisted data, or i18n surfaces are expected to change.

## Compatibility

- The Tailwind path fix should be compatible with the current pnpm-linked package layout because it
targets the file that exists in the installed package.
- Session scroll settling is renderer-only and should not change saved session data.
- The bounded observer/retry design keeps the existing `content-visibility` optimization in place
and limits extra work to the initial restore window.

## Test Strategy

- Add or update a renderer-side guard that fails if `markstream-vue` code block utility candidates
are no longer visible to Tailwind scanning.
- Add a focused `ChatPage` test that simulates session restore followed by a late `scrollHeight`
increase without a message revision change, then asserts the scroll position reaches the new
bottom.
- Keep or extend existing streaming auto-follow tests to verify the session-restore helper does not
force bottom after the user scrolls away.
- Add a focused submit test that first records a non-bottom scroll metric, sends a message, and then
asserts the submit path still forces bottom scroll.
- After implementation, run the required project checks: `pnpm run format`, `pnpm run i18n`, and
`pnpm run lint`, plus targeted renderer tests.
62 changes: 62 additions & 0 deletions docs/issues/markdown-codeblock-session-scroll-regressions/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
# Markdown Codeblock Session Scroll Regressions

## User Need

Chat markdown rendering should keep code blocks visually readable, and switching sessions should land
at the actual bottom of the restored conversation. Two regressions currently make the chat view feel
unfinished:

- Fenced code block chrome, especially the toolbar, renders too compact because the expected
`markstream-vue` Tailwind utility classes are not generated.
- Switching conversations often scrolls close to the bottom but stops slightly short after message
content finishes laying out.

## Goals

- Restore the intended `markstream-vue` code block toolbar spacing, background, border, and action
button styles.
- Make session restore scroll to the final settled bottom of the message list when no message
spotlight jump is requested.
- Preserve existing streaming auto-follow behavior, scroll-away behavior, and message rendering
performance optimizations.

## Acceptance Criteria

- Generated renderer CSS includes representative `markstream-vue` code block utility candidates,
including `py-[var(--ms-inset-panel-y)]`, `px-[var(--ms-inset-panel-x)]`,
`p-[var(--ms-action-btn-padding)]`, `bg-[var(--code-header-bg)]`, and
`text-[var(--code-action-fg)]`.
- Code block headers, language labels, copy buttons, and overflow controls render with the intended
spacing in light and dark themes.
- Switching to an existing session without a spotlight target scrolls to the real bottom after
markdown, code blocks, images, status rows, and input-area layout settle.
- Switching sessions does not reintroduce bottom shaking or overscroll during streaming updates.
- Sending a new message forces the conversation back to the bottom even if the previous bottom
proximity metric was stale.
- If a spotlight target is requested, the message jump remains the winning scroll behavior.
- User-initiated scroll-away from the bottom is respected after the initial session restore has
completed.

## Constraints

- Keep the fix scoped to renderer markdown styling and chat scroll restoration.
- Keep `markstream-vue` as a package dependency; do not fork or patch the package unless the package
path fix proves insufficient.
- Keep the message row `content-visibility` performance optimization unless a later benchmark shows
it is the actual blocker.
- Use bounded scroll settling so the renderer does not keep observers or animation-frame loops alive
after session restore.
- Do not introduce new runtime dependencies.

## Non-goals

- Redesign the markdown renderer or code block component.
- Rewrite chat virtualization, message storage, or streaming message flow.
- Change the composer layout, sticky input behavior, or session loading UX.
- Add a new user-facing setting for scroll behavior.

## Discussion Points

- The recommended scroll-settling approach is a short `ResizeObserver` window plus bounded animation
frame retries. A smaller bounded-rAF-only fix is possible, but it is less robust when late content
changes arrive outside the first few frames.
14 changes: 14 additions & 0 deletions docs/issues/markdown-codeblock-session-scroll-regressions/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Tasks

- [x] Investigate the compact code block toolbar regression.
- [x] Investigate the session-switch bottom scroll regression.
- [x] Record proposed fixes for discussion before implementation.
- [x] Confirm the implementation approach with the reviewer.
- [x] Update the `markstream-vue` Tailwind source path from `dist/tailwind.ts` to
`dist/tailwind.js`.
- [x] Add a focused guard for representative `markstream-vue` code block utility candidates.
- [x] Add bounded session-restore scroll settling with cancellation and user-scroll guards.
- [x] Add renderer coverage for late layout growth after session restore.
- [x] Add renderer coverage for forced bottom scroll after submit.
- [ ] Manually verify code block rendering and session switching in the app.
- [x] Run `pnpm run format`, `pnpm run i18n`, `pnpm run lint`, and targeted renderer tests.
11 changes: 11 additions & 0 deletions docs/issues/pr-base-dev-default/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Plan

## Approach

- Add an explicit PR base rule to `AGENTS.md` under commit and pull request guidelines.
- Keep the rule aligned with `docs/release-flow.md`, where `dev` is the long-lived integration
branch and `main` is the stable release mirror.

## Validation

- Run formatting and lint checks for documentation consistency.
24 changes: 24 additions & 0 deletions docs/issues/pr-base-dev-default/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# Default PR Base Branch

## User Need

Contributors and coding agents need a clear repository-level instruction that routine pull requests
target `dev` by default instead of accidentally targeting `main`.

## Problem

`docs/release-flow.md` defines `dev` as the integration branch and `main` as the release mirror, but
`AGENTS.md` did not state the default PR base branch in the commit and pull request guidelines.
Automation can therefore fall back to `main` when creating PRs.

## Acceptance Criteria

- `AGENTS.md` states that routine PRs default to `dev`.
- `AGENTS.md` states that `main` is only for `release/<version>` PRs following the release flow.
- The instruction is located in the section that coding agents read before creating commits and PRs.

## Non-goals

- Change the release flow.
- Change GitHub repository settings.
- Change CI branch filters.
5 changes: 5 additions & 0 deletions docs/issues/pr-base-dev-default/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Tasks

- [x] Inspect existing branch and release guidance.
- [x] Add the default PR base rule to `AGENTS.md`.
- [x] Run required checks.
27 changes: 27 additions & 0 deletions docs/issues/reasoning-heading-font-size/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Plan

## Diagnosis

`ThinkContent.vue` renders reasoning content through `markstream-vue` `NodeRenderer`. The component
sets compact body variables on `.think-prose`, but `markstream-vue` also defines heading-specific
variables such as `--ms-text-h1`, `--ms-text-h2`, and `--ms-text-h3`. Without overriding those
heading variables, markdown headings inside thinking content can render larger than the surrounding
text.

Because `ThinkContent.vue` uses scoped styles and `NodeRenderer` is a child component, selector
fallbacks that target rendered heading elements must use `:deep(...)`.

## Approach

- Override heading font-size and line-height CSS variables in `.think-prose` so markstream headings
inherit the compact thinking body size.
- Add a scoped `:deep(...)` fallback for rendered `h1` through `h6` and `.heading-node` elements.
- Add a small source-level guard test to keep the reasoning heading overrides from being removed
accidentally.

## Test Strategy

- Run a focused renderer test that verifies `ThinkContent.vue` contains the heading variable
overrides and deep heading fallback.
- Run existing thinking block tests.
- Run formatting and renderer quality checks.
20 changes: 20 additions & 0 deletions docs/issues/reasoning-heading-font-size/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Reasoning Heading Font Size

## User Need

Reasoning/thinking blocks should feel like compact diagnostic text. Markdown heading syntax inside a
thinking block must not enlarge the text, because model reasoning often uses `#`, `##`, or `###` as
internal outline markers rather than user-facing document headings.

## Acceptance Criteria

- `h1` through `h6` rendered inside a reasoning/thinking block use the same font size as the rest of
the thinking text.
- The fix is scoped to `ThinkContent` and does not change normal assistant markdown heading styles.
- The thinking block continues to render markdown, lists, links, and code blocks.

## Non-goals

- Redesign the thinking block.
- Change how normal assistant message markdown headings are rendered.
- Disable markdown parsing inside reasoning content.
7 changes: 7 additions & 0 deletions docs/issues/reasoning-heading-font-size/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Tasks

- [x] Diagnose why reasoning headings inherit large markdown heading styles.
- [x] Document the scoped fix.
- [x] Override thinking heading font-size and line-height styles.
- [x] Add a focused style guard test.
- [x] Run targeted renderer tests and required checks.
16 changes: 16 additions & 0 deletions docs/issues/stop-pauses-pending-queue/plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Plan

## Approach

- Track sessions whose pending turn queue was paused by an explicit user stop.
- Set that pause in `cancelGeneration` when a queue drain is active or pending turn input exists.
- Prevent automatic queue drains for `enqueue` and `completed` while the pause is active.
- Clear the pause when the user explicitly calls `resumePendingQueue`, when the session is
destroyed, and when all pending inputs are gone.

## Test Strategy

- Add a main-process `AgentRuntimePresenter` regression test that starts a queued pending item,
makes `processStream` return `aborted`, and verifies the item is released but not immediately
claimed again.
- Keep existing queue and cancellation tests passing.
27 changes: 27 additions & 0 deletions docs/issues/stop-pauses-pending-queue/spec.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Stop Pauses Pending Queue

## User Need

When a user stops an active generation, DeepChat must stop the current turn and must not immediately
continue queued pending inputs. The pending queue should remain visible so the user can resume it
explicitly.

## Problem

If the active turn was launched from the pending queue, stopping the stream aborts that turn and
releases the claimed queue item back to `pending`. `drainPendingQueueIfPossible` then sees the
session is idle and still has pending input, so it automatically drains the same item again.

## Acceptance Criteria

- Stopping an active queued turn releases the queued input back to the waiting lane but does not
auto-start it again.
- Stopping a normal active turn while queued items exist pauses automatic queue draining.
- Clicking resume queue clears the pause and allows pending items to drain.
- Destroying or emptying a session clears any stale pause state.

## Non-goals

- Remove the pending queue feature.
- Change rate-limit provider queues.
- Change normal stream cancellation behavior when no pending inputs are involved.
7 changes: 7 additions & 0 deletions docs/issues/stop-pauses-pending-queue/tasks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Tasks

- [x] Diagnose repeated restart after stopping a queued turn.
- [x] Document expected stop and resume behavior.
- [x] Add pending queue pause state in `AgentRuntimePresenter`.
- [x] Add regression coverage for stop-paused queue drain.
- [x] Run focused and required checks.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@
"katex": "^0.16.47",
"lint-staged": "^16.4.0",
"lucide-vue-next": "^0.544.0",
"markstream-vue": "1.0.0-rc.0",
"markstream-vue": "1.0.1-beta.4",
"mermaid": "^11.15.0",
"minimatch": "^10.2.5",
"monaco-editor": "^0.55.1",
Expand All @@ -186,7 +186,7 @@
"pinia": "^3.0.4",
"reka-ui": "^2.9.7",
"simple-git-hooks": "^2.13.1",
"stream-monaco": "^0.0.40",
"stream-monaco": "^0.0.41",
"tailwind-merge": "^3.6.0",
"tailwind-scrollbar-hide": "^4.0.0",
"tailwindcss": "^4.3.0",
Expand Down
Loading