diff --git a/src/client/components/overviewButton.tsx b/src/client/components/overviewButton.tsx index 934c98b..b4e5085 100644 --- a/src/client/components/overviewButton.tsx +++ b/src/client/components/overviewButton.tsx @@ -3,9 +3,8 @@ * above Settings (the harness's own foot layout: footer actions, then the * settings row), mirroring the Settings trigger's geometry in both column * widths. The button carries the plugin emblem and, on the wide - * column, its label; a badge counts the sessions currently running (the one - * glanceable live fact a footer can carry). Clicking opens the overview - * overlay through the shared module store (overviewStore.ts). + * column, its label. Clicking opens the overview overlay through the shared + * module store (overviewStore.ts). * * The per-user `insightsEntry` preference takes the entry down (renders * null) without unregistering the seat. The subscription fails open: an @@ -15,7 +14,6 @@ import { useSyncExternalStore, type ReactElement } from 'react' import { ContextIcon } from '../icon' -import { runningCountOf, sessionsSnapshotOf } from '../overview' import { overviewStore } from '../overviewStore' import type { ContextSettings, InsightsEntry } from '../settings' import type { ViewKit } from '../viewkit' @@ -23,8 +21,6 @@ import type { ViewKit } from '../viewkit' export interface OverviewButtonProps { /** The footer-action owner share: false on the collapsed 56px rail (icon only). */ wide?: boolean - /** The root standard kit's sessions seat (absent on a harness without it). */ - useSessions?: unknown } /** Stable subscription faces for the settings-less degrade (no re-subscribes). */ @@ -40,8 +36,6 @@ export function makeOverviewButton(kit: ViewKit, settings?: ContextSettings): (p : (listener: () => void): (() => void) => settings.store.subscribe(listener) const getEntry = settings === undefined ? entryShown : (): InsightsEntry => settings.insightsEntry() return function OverviewButton(props: OverviewButtonProps): ReactElement | null { - // The hook-level read (unconditional, guarded inside); the badge hides at 0. - const running = runningCountOf(sessionsSnapshotOf(props)) // The entry toggle: subscribed, so a preference flip takes effect live. const entry = useSyncExternalStore(subscribeEntry, getEntry) if (entry === 'hide') return null @@ -55,7 +49,6 @@ export function makeOverviewButton(kit: ViewKit, settings?: ContextSettings): (p > {props.wide === true && {t('ov.entry')}} - {running > 0 && } ) } diff --git a/src/client/overview.ts b/src/client/overview.ts index c588214..6b1e39d 100644 --- a/src/client/overview.ts +++ b/src/client/overview.ts @@ -56,24 +56,6 @@ export function sessionsSnapshotOf(props: { useSessions?: unknown }): unknown { } } -/** The running-session tally for the footer entry's badge (0 on any shape failure). */ -export function runningCountOf(snapshot: unknown): number { - const state = asRecord(snapshot) - if (state === null) return 0 - const byId = asRecord(state.byId) - if (byId === null) return 0 - let count = 0 - try { - for (const id of Object.keys(byId)) { - const row = asRecord(byId[id]) - if (row !== null && row.running === true) count++ - } - } catch { - // A hostile row throwing on access stops the tally at what was counted. - } - return count -} - /** * The `useWorkspaces` standard prop read — same guarded-hook contract as * {@link sessionsSnapshotOf}. The overview joins its session → workspace diff --git a/src/client/styles/overview.css b/src/client/styles/overview.css index 2d662ef..a6bbdbb 100644 --- a/src/client/styles/overview.css +++ b/src/client/styles/overview.css @@ -15,14 +15,12 @@ /* Geometry mirrors the harness's own Settings trigger (ui-settings-general's .trigger): the 42px row, 12px radius, 14px/22px label, and its shared hover fill, so the plugin entry reads as a native foot control, not a visitor. */ -.lc-ov-entry { position: relative; display: flex; align-items: center; gap: 8px; width: calc(100% + 4px); height: 42px; box-sizing: border-box; margin: 0 -2px; border: 0; padding: 0 10px 0 8px; overflow: hidden; background: transparent; border-radius: 12px; color: var(--dsw-alias-label-primary); font-family: inherit; font-size: 14px; line-height: 22px; cursor: pointer; } +.lc-ov-entry { display: flex; align-items: center; gap: 8px; width: calc(100% + 4px); height: 42px; box-sizing: border-box; margin: 0 -2px; border: 0; padding: 0 10px 0 8px; overflow: hidden; background: transparent; border-radius: 12px; color: var(--dsw-alias-label-primary); font-family: inherit; font-size: 14px; line-height: 22px; cursor: pointer; } .lc-ov-entry:hover { background: var(--dsw-alias-interactive-bg-hover); } /* The collapsed rail takes the native foot controls' 36x36 circle, icon only. */ .lc-ov-entry-rail { flex: none; justify-content: center; gap: 0; width: 36px; height: 36px; margin: 0; padding: 0; border-radius: 50%; } .lc-ov-entry-icon { flex: none; } .lc-ov-entry-label { flex: 1 1 auto; min-width: 0; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } -/* The running-sessions badge: the footer's one live fact, pinned to the icon's corner in both column widths. */ -.lc-ov-badge { position: absolute; top: 1px; right: 4px; min-width: 15px; height: 15px; box-sizing: border-box; border-radius: 999px; background: var(--color-green-500); color: #fff; font-size: 9px; font-weight: 700; line-height: 1; display: flex; align-items: center; justify-content: center; padding: 0 4px; pointer-events: none; } /* ---- the overlay chrome ---- */ /* The backdrop carries the `lc-ov` container: it spans the viewport exactly, diff --git a/tests/client/components/overviewButton.spec.ts b/tests/client/components/overviewButton.spec.ts index 6438154..f6f9d23 100644 --- a/tests/client/components/overviewButton.spec.ts +++ b/tests/client/components/overviewButton.spec.ts @@ -1,6 +1,6 @@ // The Context Dashboard's sidebar-foot entry (src/client/components/ -// overviewButton.tsx): wide/rail rendering, the running badge, the store -// flip on click, and the per-user insights-entry toggle. +// overviewButton.tsx): wide/rail rendering, the store flip on click, and the +// per-user insights-entry toggle. import { createElement as h, act } from 'react' import assert from 'node:assert/strict' @@ -17,9 +17,6 @@ afterEach(() => { overviewStore.set(false) }) -/** A useSessions stand-in over a static snapshot. */ -const useSessionsOf = (snapshot: unknown) => ((sel: (s: unknown) => T): T => sel(snapshot)) - describe('OverviewButton', () => { test('the wide column renders icon and label; the rail renders the icon alone', async () => { const wide = await mount(h(Button, { wide: true })) @@ -41,23 +38,6 @@ describe('OverviewButton', () => { await bare.unmount() }) - test('the badge counts running sessions and hides at zero', async () => { - const running = useSessionsOf({ byId: { a: { running: true }, b: { running: false }, c: { running: true } } }) - const m = await mount(h(Button, { wide: true, useSessions: running })) - assert.equal(query(m.container, '.lc-ov-badge').textContent, '2') - await m.unmount() - - const idle = useSessionsOf({ byId: { a: { running: false } } }) - const calm = await mount(h(Button, { wide: true, useSessions: idle })) - assert.equal(queryAll(calm.container, '.lc-ov-badge').length, 0) - await calm.unmount() - - // No sessions seat at all: no badge, no throw. - const seatless = await mount(h(Button, { wide: true })) - assert.equal(queryAll(seatless.container, '.lc-ov-badge').length, 0) - await seatless.unmount() - }) - test('clicking opens the overview through the shared store', async () => { assert.equal(overviewStore.getSnapshot(), false) const m = await mount(h(Button, { wide: true })) diff --git a/tests/client/overview.spec.ts b/tests/client/overview.spec.ts index 743a91d..bae2e81 100644 --- a/tests/client/overview.spec.ts +++ b/tests/client/overview.spec.ts @@ -19,7 +19,6 @@ import { refreshSessions, relativeTime, rowsOfSnapshot, - runningCountOf, sessionGroupsOf, sessionsSnapshotOf, sortRows, @@ -89,20 +88,6 @@ describe('sessionsSnapshotOf', () => { }) }) -describe('runningCountOf', () => { - test('unusable snapshots tally zero', () => { - assert.equal(runningCountOf(null), 0) - assert.equal(runningCountOf(7), 0) - assert.equal(runningCountOf({ byId: 7 }), 0) - }) - - test('running rows tally; a hostile row stops the count at what was seen', () => { - const hostile = new Proxy({}, { get: () => { throw new Error('boom') } }) - assert.equal(runningCountOf({ byId: { a: { running: true }, b: { running: false }, c: { running: true } } }), 2) - assert.equal(runningCountOf({ byId: { a: { running: true }, x: hostile } }), 1) - }) -}) - describe('rowsOfSnapshot', () => { test('unusable snapshots read null (the unavailable note, not an empty list)', () => { assert.equal(rowsOfSnapshot(null), null)