diff --git a/.changeset/web-sidebar-rows.md b/.changeset/web-sidebar-rows.md new file mode 100644 index 00000000..d97d6551 --- /dev/null +++ b/.changeset/web-sidebar-rows.md @@ -0,0 +1,5 @@ +--- +"@pymodel/pythinker-code": patch +--- + +Quieten the sidebar session rows. Hover becomes a translucent wash instead of a solid fill, the selected row becomes a faint tint instead of a solid accent, and the radius and sizing match the shared menu row, so the row scales with the UI font-size setting. The same change is applied to the per-theme overrides, so all three themes agree. diff --git a/apps/pythinker-web/src/components/SessionRow.vue b/apps/pythinker-web/src/components/SessionRow.vue index b0d0a8da..db7b33d3 100644 --- a/apps/pythinker-web/src/components/SessionRow.vue +++ b/apps/pythinker-web/src/components/SessionRow.vue @@ -227,15 +227,20 @@ defineExpose({ closeMenu, cancelArchive }); .se { /* --sb-* vars come from .side in Sidebar.vue. The outer margin and reduced inner padding keep the title at --sb-pad-x + --sb-gutter + --sb-gap. */ + /* Default 14px: 14 + 13 = 27px; 14 - 1 = 13px. A MINIMUM, not a fixed + height: the row also carries an 18px tag pill and the archive-confirm + strip, and a fixed height would clip both. */ display: block; margin: 0 8px; - padding: 7px calc(var(--sb-pad-x, 12px) - 8px); - border-radius: 8px; + padding: 4px calc(var(--sb-pad-x, 12px) - 8px); + min-height: calc(var(--ui-font-size) + 13px); + box-sizing: border-box; + border-radius: var(--r-md); cursor: pointer; position: relative; } -.se:hover { background: var(--panel2); } -.se.on { background: var(--soft); } +.se:hover { background: var(--hover); } +.se.on { background: color-mix(in srgb, var(--soft) 45%, var(--panel)); } .row { display: flex; @@ -278,7 +283,7 @@ defineExpose({ closeMenu, cancelArchive }); .t { color: var(--ink); - font-size: var(--ui-font-size); + font-size: calc(var(--ui-font-size) - 1px); font-weight: 400; flex: 1; min-width: 0; diff --git a/apps/pythinker-web/src/components/Sidebar.vue b/apps/pythinker-web/src/components/Sidebar.vue index 2c462def..6c9204e9 100644 --- a/apps/pythinker-web/src/components/Sidebar.vue +++ b/apps/pythinker-web/src/components/Sidebar.vue @@ -894,7 +894,7 @@ onBeforeUnmount(() => { } .ws-head-label { color: var(--muted); - font-size: var(--ui-font-size); + font-size: calc(var(--ui-font-size) - 2px); min-width: 0; } .ws-head-actions { diff --git a/apps/pythinker-web/src/style.css b/apps/pythinker-web/src/style.css index 006f84cf..fc544525 100644 --- a/apps/pythinker-web/src/style.css +++ b/apps/pythinker-web/src/style.css @@ -349,14 +349,14 @@ html[data-color-scheme="dark"][data-theme="modern"] { .sessions because .se is also a (different) class in MobileTopBar. */ :is(html[data-theme="modern"], html[data-theme="pythinker"]) .sessions .se { margin: 1px 6px; - border-radius: var(--r-sm); + border-radius: var(--r-md); /* Trim the row padding by the inset margin so the title still starts at the same x as the workspace name (whose header has no inset). */ padding: 7px calc(var(--sb-pad-x, 12px) - 6px); } -:is(html[data-theme="modern"], html[data-theme="pythinker"]) .sessions .se:hover { background: var(--panel2); } +:is(html[data-theme="modern"], html[data-theme="pythinker"]) .sessions .se:hover { background: var(--hover); } :is(html[data-theme="modern"], html[data-theme="pythinker"]) .sessions .se.on { - background: var(--soft); + background: color-mix(in srgb, var(--soft) 45%, var(--panel)); } /* Tab bar → clean white strip with a single hairline. diff --git a/apps/pythinker-web/test/session-row.test.ts b/apps/pythinker-web/test/session-row.test.ts index 23705c8b..d546fead 100644 --- a/apps/pythinker-web/test/session-row.test.ts +++ b/apps/pythinker-web/test/session-row.test.ts @@ -4,6 +4,8 @@ // task), and surfaces the 5-state lifecycle status: awaiting shows its pending // tag, aborted shows a distinct "stopped" tag — neither spins. +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; import { mount } from '@vue/test-utils'; import { createI18n } from 'vue-i18n'; import { describe, expect, it } from 'vitest'; @@ -21,6 +23,30 @@ const i18n = createI18n({ fallbackWarn: false, }); +const sessionRowSource = readFileSync( + resolve(import.meta.dirname, '../src/components/SessionRow.vue'), + 'utf8', +); +const sessionRowStyle = sessionRowSource.match(/