Skip to content

feat(*): deck template picker and full-frame deck preview for the web ui - #573

Merged
Tchen-data merged 5 commits into
refactor/ui_web_architecturefrom
feat/webui_deck_template_picker
Sep 21, 2026
Merged

Tchen-data merged 5 commits into
refactor/ui_web_architecturefrom
feat/webui_deck_template_picker

Conversation

@Tchen-data

Copy link
Copy Markdown
Contributor

Summary

Two deck features for the web UI, both landing on the refactor tree.

Deck template picker. The composer gets a "Deck template" button beside the attach control. It opens a sheet of the templates the deck engine ships, shown by their cover pages; opening one pages through its slides, and "Use this template" attaches it to the turn exactly as an uploaded file would be. That attachment is what opens the host's route to the template engine (the route reads the attached .pptx, not the tier, see the companion backend change on main). Without the engine installed the sheet says so.

Three console methods back it: deck.templates.list, deck.templates.pages, deck.templates.pick. pick copies the template into the agent's uploads and answers in the fs.upload shape so the frontend treats it as one more attachment. The sheet lives in the composer domain (cp-tpl-* classes, domain stylesheet, gui.tpl.* strings).

Delivered deck preview, ported from the pre-refactor deck bar branch. A .pptx opened in the workspace now fills the whole pane under a single bar: the browser viewer's own toolbar is hidden, the frame takes every pixel to the bottom edge, and the bar carries Download and Show in Finder / Explorer / file manager (worded per the gateway's platform; a reveal the host cannot perform surfaces as a toast with the failure sentence). The frame URL carries the delivery version so a re-delivered deck under the same path is refetched. The transcript tile of a delivered deck shows its first page (render=thumb on the files route, PNG through the LibreOffice path; to_pdf grew a format parameter). The file pane opens at 960 (was 720) and the workspace column may grow to 1200 (was 760).

Class names from the original branch were moved under the workspace namespace (workspace-fill, workspace-deck-acts) to satisfy the class-namespace gate.

Type

  • Fix
  • Feature
  • Docs
  • CI / tooling
  • Refactor
  • Other

Verification

  • ui-web: npx tsc --noEmit -p tsconfig.json -> no errors; npx vitest run src/features/desk src/chrome src/features/workspace src/features/transcript src/features/composer src/state -> 1229 passed, 0 failed; npx vitest run scripts/gates -> 160 passed (class namespace, i18n keys, offline coverage, css one owner, fixture shape); npm run gen:check -> 192 methods in sync; npx eslint . -> 0 errors (3 pre-existing exhaustive-deps warnings in files this PR does not touch).

  • ui-tui: npm run lint:i18n -> generated catalogue up to date.

  • Python: uv run --frozen pytest tests/test_rpc_console.py tests/test_rpc_files.py -q -> 156 passed.

  • Manual: a full gateway built from this tree (Playwright, full Chromium): picker opens, covers render, paging works, "Use this template" attaches the .pptx; a delivered deck opens full-frame with the one bar; Download serves the file; the tile shows page one. Reveal was exercised on a headless host and produced the toast reveal failed: ... 'xdg-open'; the darwin open -R branch was verified on macOS on the original deck bar branch.

  • End to end: a high-tier deck request with a picked template routed to the template engine and delivered on that template; the same request without a template stayed on Raven-Design.

  • Relevant tests pass locally

  • Relevant lint / type checks pass locally

  • User-facing docs or screenshots are updated when needed

Risk

  • New RPC methods are additive; the render=thumb query is additive on the files route and falls back to the existing behaviour when absent.

  • The wider default pane (960 / 1200) is the one user-visible layout change outside deck panes; it only affects the first open and the drag ceiling, saved widths are kept.

  • Rollback: revert the squash commit; no schema or storage migration is involved.

  • Security impact considered

  • Backward compatibility considered

  • Rollback path is clear for risky changes

Related Issues

N/A

@Tchen-data
Tchen-data requested a review from 0xKT September 21, 2026 03:03

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking: cap the cold-cache deck cover render fan-out before merge.

I found one blocker inline. Otherwise the picker attachment path, sheet identity and session behavior, RPC models/schema/fixtures, full-frame preview versioning, delivery thumbnail fallback, older-gateway and missing-engine behavior, i18n, and the web architecture constraints are consistent.

I covered the project rules and context docs, the full diff, relevant callers and history, backward compatibility, test changes for weakening, and the architecture boundaries. Verification: uv run pytest tests/test_rpc_console.py tests/test_rpc_files.py tests/test_utils_office.py -x (164 passed); npm test in ui-web (187 files, 2561 tests passed); type-check, generated-client check, source-language gate, large-file gate, and git diff --check passed. ESLint had 0 errors and 5 pre-existing warnings in untouched files.

Comment thread raven/rpc/deck_templates.py

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Blocking: bound the cold-cache cover rendering fan-out tracked in the open thread.

The new a0d2e2dcbeae delta only adds the optional PyMuPDF deptry exceptions and updates the two boot goldens. I reviewed that delta and it is sound: uv run --all-extras deptry raven reports no dependency issues, and the web build reports both boot snapshots matching (254 and 255 nodes). The focused Python suite still passes 164 tests.

The prior inline finding remains reachable and unchanged because raven/rpc/deck_templates.py was not modified, so this revision is still blocked on that one item.

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No blockers; this can merge as far as I am concerned.

The semaphore fix settles the only open finding: ten cold cover requests now peak at three conversions while the picker continues answering immediately. I replied to the author and resolved my thread.

For this revision I covered the project rules and context, the fix diff and its caller path, relevant history, backward compatibility, test-strength changes, and the architecture boundary. Verification: the independent concurrency reproduction observed a peak of 3; uv run pytest tests/test_rpc_console.py tests/test_rpc_files.py tests/test_utils_office.py -x passed all 165 tests; the source-language gate, large-file gate, and git diff --check passed.

… shows page one

A .pptx opened from the workspace used to render inside a viewer that kept
its own toolbar under ours, and the frame stopped short of the pane's
bottom edge. Now the frame hides the viewer's controls and fills every
pixel under the bar, and the bar carries the deck's own two actions:
Download, and Show in Finder / Explorer / file manager, worded for the
platform the gateway runs on. A reveal that the host cannot perform comes
back as the failure sentence in a toast rather than a silent no-op. The
frame URL carries the delivery version so a deck delivered again under the
same path is fetched again instead of served from the frame's cache.

The transcript tile of a delivered deck shows its first page instead of a
generic file glyph: the files route accepts render=thumb and answers with a
PNG of page one, converted through the same LibreOffice path as the PDF
rendering, with the format now a parameter of to_pdf.

The file pane opens at 960 instead of 720 and the workspace column may
grow to 1200 instead of 760, so a 16:9 page is readable without dragging.

Ported from the pre-refactor deck bar branch onto the react tree, with the
class names moved under the workspace namespace the gates require.
The composer gains a Deck template button beside the attach control. It
opens a sheet of the templates the deck engine ships, shown as their cover
pages; a cover opens the template so its pages can be paged through, and
"Use this template" attaches it to the turn exactly as an uploaded file
would be, so the host's route to the template engine opens on it. When
the engine is not installed the sheet says so instead of showing nothing.

Three console methods back it: deck.templates.list names the templates
with a cover for each, deck.templates.pages renders one template's pages,
and deck.templates.pick copies the template into the agent's uploads and
answers in the fs.upload shape, so the frontend treats it as one more
attachment. The sheet lives in the composer domain with the cp-tpl class
prefix, its styles in the domain's own stylesheet, and its strings under
gui.tpl.
The dependency hygiene lint read the rasteriser behind the deck template
gallery as a direct import of a transitive package. PyMuPDF is pinned by
the ppt-engine plugin, which is also the only reason the gallery exists;
the import is inside a function behind a find_spec probe, so the core does
not have to carry an AGPL dependency for a feature that is absent without
the plugin. It joins the three guarded imports already exempted under both
rules, with the reason beside it.

The boot snapshot goldens gain the template button the composer now
renders, in both the stub and the live-without-gateway trees.
A cold gallery asks for every bundled template in one walk, and every miss
started its own cover task at once: ten LibreOffice processes and a couple
of GB of rendering from a single click on a gateway that is also serving
turns. The cover renders now pass through a semaphore of three. That keeps
most of the gain of fanning out, since the time of one conversion barely
moves past four in parallel, at a third of the cost, and the picker still
answers at once with what is on disk while the rest are drawn behind it.
The diff coverage gate read the gallery at 79 percent: the rasteriser
helpers, the engine lookup and the failure branches of a cover and a page
set had no test of their own. They have one now, against a renderer that
draws nothing but leaves a file, so the tests run where PyMuPDF is not
installed; the console gains the case where the copy into uploads fails.
@Tchen-data
Tchen-data force-pushed the feat/webui_deck_template_picker branch from 2b6befb to 764120a Compare September 21, 2026 03:27

@gloryfromca gloryfromca left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

No blockers; this can merge as far as I am concerned.

The force-push rebased the already-reviewed feature onto the updated target; the only substantive new branch commit adds focused gallery tests and does not change production behavior. Those tests accurately exercise the previously uncovered discovery, rasterization, cache, failure, and duplicate-start paths. The settled concurrency thread remains resolved.

I covered the current target diff, the new test commit and surrounding implementation, relevant history, backward compatibility, test-strength changes, the repository rules/context, and the existing architecture boundaries. Verification: uv run pytest tests/test_rpc_deck_templates.py tests/test_rpc_console.py tests/test_rpc_files.py tests/test_utils_office.py -x passed all 176 tests; uv run --all-extras deptry raven, the source-language gate, large-file gate, and git diff --check passed.

@Tchen-data
Tchen-data merged commit 4935129 into refactor/ui_web_architecture Sep 21, 2026
21 checks passed
@Tchen-data
Tchen-data deleted the feat/webui_deck_template_picker branch September 21, 2026 08:17
Tchen-data added a commit that referenced this pull request Sep 21, 2026
… tier (#574)

## Summary

The route from Raven-Design to the Raven-PPT template engine used to
open on the max tier alone. The same request then came back in three
unrelated styles across the tiers, and the top one was built on a
template the user never asked for. This PR moves the decision to the one
thing that actually calls for the engine: a .pptx the user handed over.

- **Route on an attached template.** `SubagentRouteConfig` gains
`needsFile` (a suffix), a third independent condition beside `needs` and
`minTier`. Raven-Design's route to Raven-PPT now declares `needsFile:
".pptx"` and drops `minTier: "max"`. Every tier designs its deck on
Raven-Design; the engine is reached only when a .pptx was handed over,
either a bundled template picked in the web UI (companion PR #573) or
one the user attached.
- **Handed over means attached in this turn.** A new `turn_attachments`
ContextVar is frozen beside `turn_tier` on the turn path. A direct
chat's media all count; a spawn or DAG node counts an attachment only
when its task text names that attachment's path or file name. Task text
alone never opens the route: a deck brief spells the deck's destination
path, the format in words, or a stray file in the directory exactly like
a template, and the first text-only scan sent a no-template run into the
engine on its destination path.
- **Tiers change effort, not product.** medium asks for low reasoning
effort, max for xhigh (the highest word a model may know; models that do
not know it fall back to their default), and the baseline inherits the
host's setting as before.
- **Design lane retries after streamed output.** `llmRetryAfterOutput:
true` on the design lane's defaults, matching the ppt lane. A max run
died at its thirtieth iteration to an upstream idle timeout after the
body had already streamed to the host; the lane now retries instead of
failing the turn.
- **Skill: use what the user handed over first.** Documents and data are
the material, pictures are the pictures, and an attached .pptx is the
engine's work, routed before the skill is read.

Docs: `CONTEXT.md` admission paragraph, `agents/README.md`, regenerated
`schemas/subagent.schema.json`.

Also removed: the engine's internal decision log
(`plugins-dist/ppt-engine/docs/ppt-raven-design.md`), a single-language
working journal for the team building the deck lanes that has no reader
in a public repository. The maintainer keeps it outside the tree; the
two code comments that cited it by path now state their reason in place.

## Type

- [ ] Fix
- [x] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

- `uv run --frozen pytest tests/test_subagent_routing_backend.py
tests/test_agents_design_launcher.py tests/test_config_schema.py
tests/test_subagent_registry.py -q` -> 130 passed (after rebase onto
main).
- `ruff check` / `ruff format --check` on the changed Python files ->
clean.
- End to end on a full gateway: a high-tier request with a picked
template logged `classified for 'Raven-PPT'; routing there` and the
engine delivered a 20-page deck on that template in about 60 minutes;
the same request without a template logged `no .pptx the user attached
was handed over; running here` and stayed on Raven-Design. A brief that
only spelled the deck's destination path stayed on Raven-Design.
- Effort labels probed against `z-ai/glm-5.3-flash` over OpenRouter:
`low`, `high`, `xhigh` all accepted; `max` spent the whole output window
on reasoning, which is why the top tier is xhigh.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [x] User-facing docs or screenshots are updated when needed

## Risk

- User-visible: medium and high tier deck requests no longer differ in
product, only in effort; a deck on a template now requires attaching
one. A bound direct-chat handle to Raven-Design stays on Raven-Design
even with a .pptx attached (the route is evaluated at dispatch), a known
boundary.
- `needsFile` is additive; routes that do not declare it are dispatched
exactly as before.
- Rollback: revert the squash commit; no schema migration or storage
change.

- [ ] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

## Related Issues

N/A
gloryfromca pushed a commit that referenced this pull request Sep 21, 2026
… ui (#573)

## Summary

Two deck features for the web UI, both landing on the refactor tree.

**Deck template picker.** The composer gets a "Deck template" button
beside the attach control. It opens a sheet of the templates the deck
engine ships, shown by their cover pages; opening one pages through its
slides, and "Use this template" attaches it to the turn exactly as an
uploaded file would be. That attachment is what opens the host's route
to the template engine (the route reads the attached .pptx, not the
tier, see the companion backend change on main). Without the engine
installed the sheet says so.

Three console methods back it: `deck.templates.list`,
`deck.templates.pages`, `deck.templates.pick`. `pick` copies the
template into the agent's uploads and answers in the `fs.upload` shape
so the frontend treats it as one more attachment. The sheet lives in the
composer domain (`cp-tpl-*` classes, domain stylesheet, `gui.tpl.*`
strings).

**Delivered deck preview, ported from the pre-refactor deck bar
branch.** A .pptx opened in the workspace now fills the whole pane under
a single bar: the browser viewer's own toolbar is hidden, the frame
takes every pixel to the bottom edge, and the bar carries Download and
Show in Finder / Explorer / file manager (worded per the gateway's
platform; a reveal the host cannot perform surfaces as a toast with the
failure sentence). The frame URL carries the delivery version so a
re-delivered deck under the same path is refetched. The transcript tile
of a delivered deck shows its first page (`render=thumb` on the files
route, PNG through the LibreOffice path; `to_pdf` grew a format
parameter). The file pane opens at 960 (was 720) and the workspace
column may grow to 1200 (was 760).

Class names from the original branch were moved under the workspace
namespace (`workspace-fill`, `workspace-deck-acts`) to satisfy the
class-namespace gate.

## Type

- [ ] Fix
- [x] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

- `ui-web`: `npx tsc --noEmit -p tsconfig.json` -> no errors; `npx
vitest run src/features/desk src/chrome src/features/workspace
src/features/transcript src/features/composer src/state` -> 1229 passed,
0 failed; `npx vitest run scripts/gates` -> 160 passed (class namespace,
i18n keys, offline coverage, css one owner, fixture shape); `npm run
gen:check` -> 192 methods in sync; `npx eslint .` -> 0 errors (3
pre-existing exhaustive-deps warnings in files this PR does not touch).
- `ui-tui`: `npm run lint:i18n` -> generated catalogue up to date.
- Python: `uv run --frozen pytest tests/test_rpc_console.py
tests/test_rpc_files.py -q` -> 156 passed.
- Manual: a full gateway built from this tree (Playwright, full
Chromium): picker opens, covers render, paging works, "Use this
template" attaches the .pptx; a delivered deck opens full-frame with the
one bar; Download serves the file; the tile shows page one. Reveal was
exercised on a headless host and produced the toast `reveal failed: ...
'xdg-open'`; the darwin `open -R` branch was verified on macOS on the
original deck bar branch.
- End to end: a high-tier deck request with a picked template routed to
the template engine and delivered on that template; the same request
without a template stayed on Raven-Design.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [ ] User-facing docs or screenshots are updated when needed

## Risk

- New RPC methods are additive; the `render=thumb` query is additive on
the files route and falls back to the existing behaviour when absent.
- The wider default pane (960 / 1200) is the one user-visible layout
change outside deck panes; it only affects the first open and the drag
ceiling, saved widths are kept.
- Rollback: revert the squash commit; no schema or storage migration is
involved.

- [ ] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

## Related Issues

N/A
LivXue pushed a commit that referenced this pull request Oct 12, 2026
… tier (#574)

## Summary

The route from Raven-Design to the Raven-PPT template engine used to
open on the max tier alone. The same request then came back in three
unrelated styles across the tiers, and the top one was built on a
template the user never asked for. This PR moves the decision to the one
thing that actually calls for the engine: a .pptx the user handed over.

- **Route on an attached template.** `SubagentRouteConfig` gains
`needsFile` (a suffix), a third independent condition beside `needs` and
`minTier`. Raven-Design's route to Raven-PPT now declares `needsFile:
".pptx"` and drops `minTier: "max"`. Every tier designs its deck on
Raven-Design; the engine is reached only when a .pptx was handed over,
either a bundled template picked in the web UI (companion PR #573) or
one the user attached.
- **Handed over means attached in this turn.** A new `turn_attachments`
ContextVar is frozen beside `turn_tier` on the turn path. A direct
chat's media all count; a spawn or DAG node counts an attachment only
when its task text names that attachment's path or file name. Task text
alone never opens the route: a deck brief spells the deck's destination
path, the format in words, or a stray file in the directory exactly like
a template, and the first text-only scan sent a no-template run into the
engine on its destination path.
- **Tiers change effort, not product.** medium asks for low reasoning
effort, max for xhigh (the highest word a model may know; models that do
not know it fall back to their default), and the baseline inherits the
host's setting as before.
- **Design lane retries after streamed output.** `llmRetryAfterOutput:
true` on the design lane's defaults, matching the ppt lane. A max run
died at its thirtieth iteration to an upstream idle timeout after the
body had already streamed to the host; the lane now retries instead of
failing the turn.
- **Skill: use what the user handed over first.** Documents and data are
the material, pictures are the pictures, and an attached .pptx is the
engine's work, routed before the skill is read.

Docs: `CONTEXT.md` admission paragraph, `agents/README.md`, regenerated
`schemas/subagent.schema.json`.

Also removed: the engine's internal decision log
(`plugins-dist/ppt-engine/docs/ppt-raven-design.md`), a single-language
working journal for the team building the deck lanes that has no reader
in a public repository. The maintainer keeps it outside the tree; the
two code comments that cited it by path now state their reason in place.

## Type

- [ ] Fix
- [x] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

- `uv run --frozen pytest tests/test_subagent_routing_backend.py
tests/test_agents_design_launcher.py tests/test_config_schema.py
tests/test_subagent_registry.py -q` -> 130 passed (after rebase onto
main).
- `ruff check` / `ruff format --check` on the changed Python files ->
clean.
- End to end on a full gateway: a high-tier request with a picked
template logged `classified for 'Raven-PPT'; routing there` and the
engine delivered a 20-page deck on that template in about 60 minutes;
the same request without a template logged `no .pptx the user attached
was handed over; running here` and stayed on Raven-Design. A brief that
only spelled the deck's destination path stayed on Raven-Design.
- Effort labels probed against `z-ai/glm-5.3-flash` over OpenRouter:
`low`, `high`, `xhigh` all accepted; `max` spent the whole output window
on reasoning, which is why the top tier is xhigh.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [x] User-facing docs or screenshots are updated when needed

## Risk

- User-visible: medium and high tier deck requests no longer differ in
product, only in effort; a deck on a template now requires attaching
one. A bound direct-chat handle to Raven-Design stays on Raven-Design
even with a .pptx attached (the route is evaluated at dispatch), a known
boundary.
- `needsFile` is additive; routes that do not declare it are dispatched
exactly as before.
- Rollback: revert the squash commit; no schema migration or storage
change.

- [ ] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

## Related Issues

N/A
LivXue pushed a commit that referenced this pull request Oct 12, 2026
… ui (#573)

## Summary

Two deck features for the web UI, both landing on the refactor tree.

**Deck template picker.** The composer gets a "Deck template" button
beside the attach control. It opens a sheet of the templates the deck
engine ships, shown by their cover pages; opening one pages through its
slides, and "Use this template" attaches it to the turn exactly as an
uploaded file would be. That attachment is what opens the host's route
to the template engine (the route reads the attached .pptx, not the
tier, see the companion backend change on main). Without the engine
installed the sheet says so.

Three console methods back it: `deck.templates.list`,
`deck.templates.pages`, `deck.templates.pick`. `pick` copies the
template into the agent's uploads and answers in the `fs.upload` shape
so the frontend treats it as one more attachment. The sheet lives in the
composer domain (`cp-tpl-*` classes, domain stylesheet, `gui.tpl.*`
strings).

**Delivered deck preview, ported from the pre-refactor deck bar
branch.** A .pptx opened in the workspace now fills the whole pane under
a single bar: the browser viewer's own toolbar is hidden, the frame
takes every pixel to the bottom edge, and the bar carries Download and
Show in Finder / Explorer / file manager (worded per the gateway's
platform; a reveal the host cannot perform surfaces as a toast with the
failure sentence). The frame URL carries the delivery version so a
re-delivered deck under the same path is refetched. The transcript tile
of a delivered deck shows its first page (`render=thumb` on the files
route, PNG through the LibreOffice path; `to_pdf` grew a format
parameter). The file pane opens at 960 (was 720) and the workspace
column may grow to 1200 (was 760).

Class names from the original branch were moved under the workspace
namespace (`workspace-fill`, `workspace-deck-acts`) to satisfy the
class-namespace gate.

## Type

- [ ] Fix
- [x] Feature
- [ ] Docs
- [ ] CI / tooling
- [ ] Refactor
- [ ] Other

## Verification

- `ui-web`: `npx tsc --noEmit -p tsconfig.json` -> no errors; `npx
vitest run src/features/desk src/chrome src/features/workspace
src/features/transcript src/features/composer src/state` -> 1229 passed,
0 failed; `npx vitest run scripts/gates` -> 160 passed (class namespace,
i18n keys, offline coverage, css one owner, fixture shape); `npm run
gen:check` -> 192 methods in sync; `npx eslint .` -> 0 errors (3
pre-existing exhaustive-deps warnings in files this PR does not touch).
- `ui-tui`: `npm run lint:i18n` -> generated catalogue up to date.
- Python: `uv run --frozen pytest tests/test_rpc_console.py
tests/test_rpc_files.py -q` -> 156 passed.
- Manual: a full gateway built from this tree (Playwright, full
Chromium): picker opens, covers render, paging works, "Use this
template" attaches the .pptx; a delivered deck opens full-frame with the
one bar; Download serves the file; the tile shows page one. Reveal was
exercised on a headless host and produced the toast `reveal failed: ...
'xdg-open'`; the darwin `open -R` branch was verified on macOS on the
original deck bar branch.
- End to end: a high-tier deck request with a picked template routed to
the template engine and delivered on that template; the same request
without a template stayed on Raven-Design.

- [x] Relevant tests pass locally
- [x] Relevant lint / type checks pass locally
- [ ] User-facing docs or screenshots are updated when needed

## Risk

- New RPC methods are additive; the `render=thumb` query is additive on
the files route and falls back to the existing behaviour when absent.
- The wider default pane (960 / 1200) is the one user-visible layout
change outside deck panes; it only affects the first open and the drag
ceiling, saved widths are kept.
- Rollback: revert the squash commit; no schema or storage migration is
involved.

- [ ] Security impact considered
- [x] Backward compatibility considered
- [x] Rollback path is clear for risky changes

## Related Issues

N/A
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.

3 participants