Skip to content

fix(app): align jump-to-bottom button to W1 preview (#601)#628

Merged
Astro-Han merged 8 commits into
devfrom
claude/i601-jump-button-align
May 15, 2026
Merged

fix(app): align jump-to-bottom button to W1 preview (#601)#628
Astro-Han merged 8 commits into
devfrom
claude/i601-jump-button-align

Conversation

@Astro-Han
Copy link
Copy Markdown
Owner

@Astro-Han Astro-Han commented May 15, 2026

Summary

Align the floating jump-to-bottom button at packages/app/src/pages/session/message-timeline.tsx:940-957 with the W1-locked spec from docs/design/preview/message-flow.html L263-271 / L1066, plus follow-up polish driven by manual review:

  • Size: size-8 (32 px) → w-[30px] h-[30px] (the locked default control height of 30).
  • Hover overlay: now uses hover:[background-image:linear-gradient(var(--row-hover-overlay),var(--row-hover-overlay))], layering a 4 % overlay on top of bg-surface-raised rather than replacing it. --row-hover-overlay flips through :root[data-color-scheme], so dark/light parity tracks the app theme attribute, not OS prefers-color-scheme.
  • Cursor: dropped cursor-pointer, button now keeps the default arrow. 偏离 from preview L267 (which locks cursor: pointer) — product call on 2026-05-15; preview / DESIGN sync is a follow-up.
  • Fade transition: simplified from transition-[opacity,transform] + translate-y-2 scale-95 to transition-opacity only. Enter / exit is now a single calm opacity transition with no position drift or scale jump.

Position, background, border, shadow, and overlap behaviour with staging are unchanged.

Why

First slice from the #601 W1 inventory comment (slice 1 / E1). The 2 px size delta and the wrong hover treatment were flagged by the crosscheck against the W1-locked preview. Two additional fixes came from in-PR review (manual Electron testing + multi-model crosscheck): the hover overlay had to layer over the raised surface rather than replace it, and the fade transition's drift / scale was hurting perceived smoothness. Both are inside the jump button's visual contract so they live in this PR rather than a follow-up.

Related Issue

#601 (slice 1 / E1 of the W1 inventory).

Human Review Status

Verified by the repo owner in Electron on 2026-05-15: geometry, light/dark hover overlay, cursor, and fade all confirmed visually. Final merge decision still pending — perf-probe-baseline must come back green.

Review Focus

  • Hover overlay layering: hover:[background-image:linear-gradient(var(--row-hover-overlay),var(--row-hover-overlay))] keeps bg-surface-raised as the background-color underneath. An earlier iteration used hover:bg-row-hover-overlay, which swapped the background-color and made the button look transparent on hover — confirmed and rejected during manual testing.
  • Theme source contract: --row-hover-overlay is the same token used by sidebar / model-picker / slash-popover and flips via :root[data-color-scheme]. The project deliberately avoids @media (prefers-color-scheme: dark) at the app entry (shell-frame-contract.test.ts:49 asserts this), and Tailwind v4's default dark: variant would have re-introduced exactly that media query. The new E2E flips document.documentElement.dataset.colorScheme = "dark" to lock the contract that the overlay follows the app attribute, not the OS preference.
  • Cursor deviation: the 偏离 comment in message-timeline.tsx:948 flags the intentional drift from preview L267. A follow-up will sync preview / DESIGN to match.
  • Fade simplification: removing translate-y / scale collapses the enter/exit to opacity only. No accessibility tests changed because focus / keyboard paths were not exercised by the original spec either.

Risk Notes

  • Cursor preview drift: preview L267 locks cursor: pointer; current code is the default arrow. The 偏离 comment in the production code is the only marker until the preview / DESIGN sync follow-up lands.
  • Hover transition property: transition-[background-image] does not animate linear-gradient to/from none — the hover overlay snaps in instantly rather than fading. This matches the preview (which only transitions background) and was visually confirmed; calling it out so the next reviewer doesn't expect a hover fade.
  • No platform / packaging / migration / data / dependency / permission impact.

How To Verify

Targeted local checks run in the worktree:

bun --cwd packages/app run typecheck                                              : clean (tsgo -b, exit 0)
bun --cwd packages/app script/e2e-local.ts -- e2e/session/session-w1-jump.spec.ts : 1/1 pass, ~4.6 s

The spec packages/app/e2e/session/session-w1-jump.spec.ts locks:

  • Geometry: getComputedStyle.width === '30px' and height === '30px'
  • Visibility gate: toBeVisible() after scrollTimelineToTop (covers the wrapper's opacity-0 hidden state)
  • Light hover: computed backgroundImage === "linear-gradient(rgba(0, 0, 0, 0.04), rgba(0, 0, 0, 0.04))"
  • Dark hover (P2 contract): flip document.documentElement.dataset.colorScheme = "dark", hover, assert backgroundImage === "linear-gradient(rgba(255, 255, 255, 0.04), rgba(255, 255, 255, 0.04))"
  • Click: timeline scrolls back to bottom (distance ≤ 8)

CI runs the full PR0 / e2e-artifacts / smoke-macos-arm64 / typecheck / unit-* matrix. As of 7905f7664 everything else is green; perf-probe-baseline is the remaining gate.

Manual desktop eyeball — completed 2026-05-15

The repo owner ran bun run dev:desktop, opened a long session, and confirmed:

  1. Geometry 30 × 30, correct position above composer
  2. Light hover: a faint warm-grey overlay appears over --surface-raised (not transparent)
  3. Cursor stays the default arrow (no pointer)
  4. Dark mode hover (Settings → 配色方案 → Dark): faint white overlay over the dark surface
  5. Fade in / out is opacity-only with no displacement or scaling

Screenshots or Recordings

Not attached. The manual verification above was done by the human reviewer; on green E2E runs no artifact is emitted.

Checklist

  • Human review status is stated above
  • I linked the related issue ([Task] UI rewrite v2 Area A: message flow modular rollout #601)
  • This PR has type, primary area, and priority labels (bug, app, ui, P2)
  • I described the review focus and any meaningful risks
  • I listed the relevant verification steps and the key result for each
  • I did not introduce unrelated refactors, dependencies, generated files, or file changes beyond the stated scope
  • I manually checked visible UI or copy changes — verified by the repo owner in Electron on 2026-05-15 (light + dark hover, cursor, fade)
  • I considered macOS and Windows impact for platform, packaging, updater, signing, paths, shell, or permissions changes (none — pure CSS class + E2E edit)
  • I called out docs, release notes, dependencies, permissions, credentials, deletion behavior, generated content, or local file changes when relevant (cursor 偏离 from preview L267 noted in code + Risk Notes; preview / DESIGN sync is a follow-up)
  • I reviewed the final diff for unrelated changes and suspicious dependency changes
  • I am targeting dev, and my PR title and commit messages use Conventional Commits in English

Summary by CodeRabbit

  • Style

    • Improved "Jump to latest" button appearance with theme-aware hover effects and consistent visual styling across light and dark modes.
  • Tests

    • Added comprehensive end-to-end tests validating "Jump to latest" button geometry, hover states, and behavior when switching between light and dark themes.

Review Change Stack

Match the W1-locked spec from docs/design/preview/message-flow.html
(L263-271, spec row L1066) for the floating jump button:

- size 32 → 30 (locked default control height)
- hover swap from border tint to 4% black overlay (light) /
  4% white overlay (dark), per the preview's hover gradient stack
  layered over --surface-raised

Position, background, border, shadow, and cursor were already
aligned. The 2 px size delta was a long-standing dev deviation
flagged by the W1 audit comment on #601.

E2E regression spec (session-w1-jump.spec.ts) locks the geometry
(getComputedStyle width/height === 30px), cursor: pointer, hover
gradient presence, and click-back-to-bottom behaviour.
@github-actions github-actions Bot added app Application behavior and product flows ui Design system and user interface labels May 15, 2026
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

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

Suggested priority: P2 (includes user-path files (packages/app/src/pages/session/message-timeline.tsx)).

P1/P0 are reserved for maintainer confirmation. Please relabel manually if this is a release blocker, security issue, data-loss risk, or updater/runtime failure.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 15, 2026

Warning

Rate limit exceeded

@Astro-Han has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 25 minutes and 23 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: b2859107-058b-488a-996b-d0b231d76efa

📥 Commits

Reviewing files that changed from the base of the PR and between 19422ab and d75e13c.

📒 Files selected for processing (1)
  • packages/app/e2e/session/session-w1-jump.spec.ts
📝 Walkthrough

Walkthrough

Adds a Playwright e2e spec that seeds a session, provides timeline DOM helpers, and validates the “Jump to latest” button is visible, sized 30×30 px, has cursor: pointer, shows the expected hover background overlay in light and dark modes, and returns the timeline to the bottom when clicked.

Changes

Jump to Latest Button Styling and Validation

Layer / File(s) Summary
E2E imports & constants
packages/app/e2e/session/session-w1-jump.spec.ts
Adds Playwright imports, session SDK type alias, and constant for expected jump button size (30px).
Session seeding helper
packages/app/e2e/session/session-w1-jump.spec.ts
seedSessionTurns appends numbered “w1 jump seed” turns using sdk.session.promptAsync for the created session.
Timeline DOM helpers
packages/app/e2e/session/session-w1-jump.spec.ts
scrollTimelineToTop resets the session turn list viewport scrollTop and dispatches a scroll event; timelineDistanceFromBottom computes remaining scroll distance.
Main e2e test flow
packages/app/e2e/session/session-w1-jump.spec.ts
Test creates a session, seeds turns, polls until timeline grows, surfaces the docked jump button, asserts visibility and computed size/cursor, hovers and polls for expected hover backgroundImage (light then dark), clicks the button, and polls until timeline is within 8px of bottom.
Jump button class update
packages/app/src/pages/session/message-timeline.tsx
Wrapper uses opacity-only transitions when toggling visibility; jump button class updated to explicit w-[30px] h-[30px] and hover background-image gradient overlay; click handling and icon unchanged.

Sequence Diagram

sequenceDiagram
  participant Test as Playwright Test
  participant SDK as Session SDK
  participant Page as Browser Page
  participant Timeline as Timeline Element

  Test->>SDK: create session via withSession()
  Test->>SDK: seedSessionTurns(sessionID) -> promptAsync xN
  Test->>Page: navigate to session URL
  Test->>Page: poll timeline height until threshold reached
  Test->>Page: scrollTimelineToTop() (set scrollTop=0 + dispatch scroll)
  Test->>Page: query jump-to-latest button (getComputedStyle checks)
  Test->>Page: hover button and poll computed backgroundImage (light)
  Test->>Page: set document.documentElement.dataset.colorScheme = "dark"
  Test->>Page: re-hover and poll computed backgroundImage (dark)
  Test->>Page: click jump-to-latest button
  Test->>Page: poll timelineDistanceFromBottom() until ≤ 8px
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • Astro-Han/pawwork#198: Related to color-scheme/dark-mode rendering that affects hover overlay assertions.

Poem

🐰 I seeded the session with hops galore,
The timeline grew tall and taller some more,
A tiny 30×30 button, poised to zoom,
Hovered, it glowed — then leapt to the room,
I clicked with a twitch and the timeline came home.

🚥 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 clearly summarizes the main change: aligning the jump-to-bottom button styling to match the W1 preview spec.
Description check ✅ Passed The description is comprehensive and covers all template sections: Summary, Why, Related Issue, Human Review Status, Review Focus, Risk Notes, How To Verify, Screenshots, and completed Checklist.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/i601-jump-button-align

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Astro-Han Astro-Han added bug Something isn't working P2 Medium priority labels May 15, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/app/e2e/session/session-w1-jump.spec.ts (1)

55-122: ⚡ Quick win

Use withSession(...) for temporary session lifecycle in this spec.

This test manually creates and cleans up a session; please switch this flow to the fixture-managed withSession(sdk, title, callback) pattern for consistency and teardown safety across E2E specs.

As per coding guidelines: "Use fixture-managed cleanup with withSession(sdk, title, callback) for temporary sessions instead of calling sdk.session.delete(...) directly".

🤖 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 `@packages/app/e2e/session/session-w1-jump.spec.ts` around lines 55 - 122,
Replace the manual session create/try/finally lifecycle with the fixture helper
withSession(project.sdk, "w1 jump spec", async (session) => { ... });: move the
body that currently runs after session creation (seedSessionTurns,
project.gotoSession, all expects, scrollTimelineToTop/jump click checks) into
the withSession callback and use session.id there (seedSessionTurns({ sdk:
project.sdk, sessionID: session.id, ... })), and remove the explicit
cleanupSession call and the outer try/finally; keep references to page,
jumpButton locator, JUMP_BUTTON_SIZE_PX, scrollTimelineToTop,
timelineDistanceFromBottom and other helpers unchanged.
🤖 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.

Nitpick comments:
In `@packages/app/e2e/session/session-w1-jump.spec.ts`:
- Around line 55-122: Replace the manual session create/try/finally lifecycle
with the fixture helper withSession(project.sdk, "w1 jump spec", async (session)
=> { ... });: move the body that currently runs after session creation
(seedSessionTurns, project.gotoSession, all expects, scrollTimelineToTop/jump
click checks) into the withSession callback and use session.id there
(seedSessionTurns({ sdk: project.sdk, sessionID: session.id, ... })), and remove
the explicit cleanupSession call and the outer try/finally; keep references to
page, jumpButton locator, JUMP_BUTTON_SIZE_PX, scrollTimelineToTop,
timelineDistanceFromBottom and other helpers unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 0defbd58-c7f2-4386-af13-c17aaf3d0521

📥 Commits

Reviewing files that changed from the base of the PR and between 76fe81b and 15125f0.

📒 Files selected for processing (2)
  • packages/app/e2e/session/session-w1-jump.spec.ts
  • packages/app/src/pages/session/message-timeline.tsx

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new E2E test for the session 'jump-to-bottom' button and updates the button's styling to a fixed 30x30px size with specific hover gradients. Feedback was provided to improve the reliability of the E2E test by using Infinity as a fallback value in the scroll assertion, preventing false positives if the viewport fails to resolve.

Comment thread packages/app/e2e/session/session-w1-jump.spec.ts Outdated
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 15, 2026

Perf delta summary

Comparator: pass

Profile / Scenario interaction median interaction worst long task max tbt frame gap p95 frame gap max jank count cls status
default / homepage-cold 32 -> 40 (+8) 56 -> 48 (-8) 73 -> 68 (-5) 23 -> 18 (-5) 33.3 -> 16.8 (-16.5) 100 -> 100 (0) 3 -> 4 (+1) 0 -> 0 (0) pass
default / long-session-input-lag 40 -> 40 (0) 48 -> 40 (-8) 0 -> 0 (0) 0 -> 0 (0) 16.7 -> 16.7 (0) 16.8 -> 16.8 (0) 0 -> 0 (0) 0 -> 0 (0) pass
default / session-streaming-long 48 -> 48 (0) 64 -> 72 (+8) 0 -> 0 (0) 0 -> 0 (0) 16.7 -> 16.8 (+0.1) 16.8 -> 33.4 (+16.6) 0 -> 0 (0) 0 -> 0 (0) pass
default / tool-call-expand 16 -> 16 (0) 24 -> 24 (0) 0 -> 0 (0) 0 -> 0 (0) 16.7 -> 16.7 (0) 16.7 -> 16.7 (0) 0 -> 0 (0) 0 -> 0 (0) pass
default / tool-default-open-heavy-bash 24 -> 24 (0) 32 -> 40 (+8) 65 -> 66 (+1) 15 -> 16 (+1) 66.7 -> 50 (-16.7) 116.6 -> 100 (-16.6) 4 -> 3 (-1) 0 -> 0 (0) pass
default / terminal-side-panel-open 40 -> 40 (0) 48 -> 48 (0) 0 -> 0 (0) 0 -> 0 (0) 33.3 -> 16.7 (-16.6) 33.3 -> 16.7 (-16.6) 0 -> 0 (0) 0 -> 0 (0) pass
default / session-scroll-reading 24 -> 24 (0) 32 -> 32 (0) 0 -> 0 (0) 0 -> 0 (0) 16.8 -> 16.7 (-0.1) 16.8 -> 16.7 (-0.1) 0 -> 0 (0) 0.505 -> 0.505 (0) warn: cls
low-end / session-timeline-recompute 136 -> 128 (-8) 160 -> 152 (-8) 119 -> 111 (-8) 222 -> 221 (-1) 100 -> 99.9 (-0.1) 216.6 -> 199.9 (-16.7) 4 -> 4 (0) 0.081 -> 0.081 (0) pass

Address PR #628 review feedback:

- coderabbitai: refactor manual session create/try/finally into the
  fixture-managed withSession(sdk, title, callback) pattern from
  e2e/actions.ts (L677). Consistency with other specs; teardown is
  guaranteed even if the callback throws.
- gemini-code-assist: replace the `?? -1` fallback in the
  distance-from-bottom poll with `?? Infinity`. The old fallback
  could make `toBeLessThanOrEqual(8)` pass immediately if the viewport
  selector failed to resolve; Infinity correctly keeps the poll
  failing until a real measurement comes through.
@Astro-Han
Copy link
Copy Markdown
Owner Author

Both review items addressed in 0078340.

  • coderabbitai (nitpick on the manual session create/cleanup at L55-122): refactored to withSession(sdk, title, callback) from e2e/actions.ts so teardown is guaranteed via the fixture path.
  • gemini-code-assist (medium, L118): replaced ?? -1 with ?? Infinity in the distance-from-bottom poll fallback.

Re-ran locally — typecheck clean, e2e/session/session-w1-jump.spec.ts 1/1 pass in 4.4 s.

Astro-Han added 3 commits May 15, 2026 12:03
The previous `dark:hover:bg-[...]` arbitrary-value class relied on
Tailwind's default `dark:` variant, which resolves to
`@media (prefers-color-scheme: dark)`. The app drives its color scheme
through `:root[data-color-scheme="dark"]` (Settings → Appearance), and
the project intentionally avoids `prefers-color-scheme` at the app
entry (`shell-frame-contract.test.ts` asserts this). Under conflicting
OS vs app preference, the hover overlay would invert: dark app on light
OS got a black overlay, light app on dark OS got a white overlay.

Use the existing `--row-hover-overlay` theme variable via the
`hover:bg-row-hover-overlay` utility, which is the standard pattern in
the sidebar, model picker, and slash popover. It flips through the same
`:root[data-color-scheme]` source as the rest of the surface.

Also strengthen the W1 jump E2E spec: scroll to top first, assert the
button is genuinely visible (toBeVisible covers the wrapper's
opacity-0 / scale-95 hidden state), drop the `force: true` from hover
and click so Playwright actionability catches a real regression, and
update the hover assertion to read computed `backgroundColor` against
`rgba(0, 0, 0, 0.04)` since the new utility paints a background-color
rather than a linear-gradient.
Add a dark theme assertion that flips data-color-scheme on the html
element (the same attribute Settings → Appearance writes) and verifies
the hover overlay resolves to rgba(255, 255, 255, 0.04). This locks
the P2 fix: the overlay must follow the app attribute, not the OS
prefers-color-scheme media query — Playwright defaults to a light OS
preference, so a regression that re-introduces the Tailwind `dark:`
variant would show the wrong overlay here and fail the test.
The previous `hover:bg-row-hover-overlay` swapped the background-color
entirely, so on hover bg-surface-raised disappeared and the button
looked transparent — the 4% overlay was painting straight onto whatever
sits behind the button. Preview L269-270 calls for a layered
background: a linear-gradient(overlay, overlay) image painted on top of
the surface-raised color, not in place of it.

Use an arbitrary-property utility to set background-image directly so
bg-surface-raised stays as the background-color underneath:

  hover:[background-image:linear-gradient(
    var(--row-hover-overlay), var(--row-hover-overlay))]

--row-hover-overlay flips through :root[data-color-scheme], keeping the
P2 fix: the overlay follows app theme, not OS prefers-color-scheme.
Switch the transition from transition-colors to
transition-[background-image] to match the property that actually
changes on hover.

Also drop cursor-pointer per product call on 2026-05-15 (preview L267
still locks pointer; flagged as `偏离` in the component and a follow-up
preview/DESIGN sync is owed).

E2E updated to match: drop the cursor assertion, assert
backgroundImage instead of backgroundColor for both light and dark
hover states. The dark assertion still flips data-color-scheme on the
html element to lock the P2 contract that the overlay tracks the app
attribute, not OS preference.
Astro-Han added 3 commits May 15, 2026 13:00
The wrapper used `transition-[opacity,transform]` with translate-y-2
and scale-95 in the hidden state, producing a two-stage exit where the
button visibly drifts and shrinks before fading. W1 preview L263-268
locks only a `transition: background` on the button itself and does
not call for an enter/exit animation, so the translate/scale layer
was an undocumented embellishment that hurt fade perception.

Drop translate-y/scale and switch to `transition-opacity` so the
button enters and leaves with a single, calm opacity transition.
Keep duration-200 ease-out and the pointer-events-none guard on the
hidden state.

Verified manually in Electron: enter/exit reads as a clean fade with
no displacement or scaling.
The hidden state no longer applies scale-95 / translate-y (PR #628
collapsed the fade to opacity-only), so the comment justifying CSS
reads via "parent's scale transform during transition does not skew
the measurement" is misleading. Reduce the geometry comment to the
preview citation it actually relies on.
The wrapper no longer applies scale-95 / translate-y in the hidden
state — those were dropped when the fade was simplified to
transition-opacity. Update the toBeVisible justification so it
references only the classes that actually exist (opacity-0 and
pointer-events-none).
@Astro-Han Astro-Han merged commit dbcaae9 into dev May 15, 2026
24 checks passed
@Astro-Han Astro-Han deleted the claude/i601-jump-button-align branch May 15, 2026 06:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

app Application behavior and product flows bug Something isn't working P2 Medium priority ui Design system and user interface

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant