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
11 changes: 9 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -825,8 +825,15 @@ native `<input type=color>`, no Acrylic or Accent opacity slider. Spec and plan:
Switch (on: `--p-text-soft` track, `--p-bg` knob), the pressed Segmented option and the row
buttons (Default apps, Clear) are the terminal core's own `fields.tsx` since #292 (PrismTerminal
#42 holds them there), and no page keeps a copy. Selection marks (style cards, swatches) and
Reset links are not buttons and keep the accent, as does the Win+E switch; the chosen rail page
is grey since #292. `settings/settingsControls.test.ts` holds it.
Reset links are not buttons and keep the accent; the chosen rail page is grey since #292.
`settings/settingsControls.test.ts` holds it. **EVERY SWITCH IS THE CORE'S** (#318; owner,
2026-10-07: "toggles differ in look i like the teal with black not the green and white", then
"it should depend on the theme so only teal on the teal theme"): the Win+E row (once its own
accent-and-white exception), the phone server switch and the player menu's toggles use the
core's `Switch` or its `SWITCH_ON` / `SWITCH_KNOB_ON`, and no file outside the core draws a
`role="switch"`. The LOOK is the core's call: from core 0.26.0 (PrismTerminal #139) an on switch
is `--p-sel-bg` with an `--p-on-accent` knob. The `settingsLook` e2e holds every switch on the
Explorer page to one track and one knob colour when on.
- **NOTHING WEARS A FOCUS BOX; FOCUS IS THE HOVER'S FILL** (#272; owner, 2026-10-04, of a white box
round the title bar's menu button: "remove the focus effect. go through the ui and remove focus
effects like this"). `index.css`'s base-layer rule takes Chromium's ring off every focused
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "prism",
"version": "0.95.0",
"version": "0.95.1",
"description": "A fast, universal media viewer for Windows.",
"author": "Max",
"license": "MIT",
Expand Down
20 changes: 4 additions & 16 deletions src/renderer/src/components/PhoneDialog.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { useCallback, useEffect, useState, type JSX } from 'react'
import type { PhoneState } from '@shared/types'
import { Switch } from 'prism-term-core/renderer/settings/fields'
import { Dialog } from './Dialog'

/**
Expand Down Expand Up @@ -53,22 +54,9 @@ export function PhoneDialog({
<div className="flex flex-col gap-3" data-phone-dialog>
<label className="flex items-center justify-between gap-3 text-[var(--p-text)]">
<span>Serve this PC&apos;s open folders to phones on this network</span>
<button
role="switch"
aria-checked={state.on}
aria-label="Phone server"
disabled={busy}
onClick={() => void toggle()}
className={`relative h-5 w-9 shrink-0 rounded-full transition-colors ${
state.on ? 'bg-[var(--p-accent)]' : 'bg-[var(--p-hover)]'
}`}
>
<span
className={`absolute top-0.5 left-0 h-4 w-4 rounded-full bg-white transition-transform ${
state.on ? 'translate-x-[18px]' : 'translate-x-0.5'
}`}
/>
</button>
{/* The core's switch, like every other in the app (#318): one look,
the style's accent with the ink chosen for it when on. */}
<Switch on={state.on} onChange={() => void toggle()} label="Phone server" disabled={busy} />
</label>
{/* Said BEFORE the switch goes on: a per-user installer cannot add the
firewall rule, and a declined prompt is a phone that cannot connect
Expand Down
9 changes: 6 additions & 3 deletions src/renderer/src/components/PlayerMenu.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { useEffect, useRef, useState, type JSX } from 'react'
import { SWITCH_KNOB_ON, SWITCH_ON } from 'prism-term-core/renderer/settings/fields'
import type { MediaControls } from '../lib/useMediaControls'
import { setPlayerPref, usePlayerPrefs } from '../lib/playerPrefs'
import type { SubTrackInfo } from '../lib/useSubtitles'
Expand Down Expand Up @@ -33,8 +34,10 @@ function Toggle({
>
{label}
<span
// The core switch's colours at a menu's size (#318): every switch in
// the app wears one look, the style's accent with its own ink when on.
className={`relative h-[14px] w-[26px] rounded-full transition-colors ${
on ? 'bg-[var(--p-accent)]' : 'bg-[var(--p-track)]'
on ? SWITCH_ON : 'bg-[var(--p-track)]'
}`}
aria-hidden
// Markers for the phone's size pass (phone.css, #145); the sizes
Expand All @@ -43,8 +46,8 @@ function Toggle({
data-on={on || undefined}
>
<span
className={`absolute top-[2px] h-[10px] w-[10px] rounded-full bg-white transition-[left] ${
on ? 'left-[14px]' : 'left-[2px]'
className={`absolute top-[2px] h-[10px] w-[10px] rounded-full transition-[left] ${
on ? `left-[14px] ${SWITCH_KNOB_ON}` : 'left-[2px] bg-white'
}`}
data-menu-knob
/>
Expand Down
27 changes: 12 additions & 15 deletions src/renderer/src/components/settings/WinEShortcutRow.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { WinEShortcutStatus } from '@shared/winEShortcut'
import { useEffect, useRef, useState, type JSX } from 'react'
import { Switch } from 'prism-term-core/renderer/settings/fields'
import { SettingRow } from 'prism-term-core/renderer/settings/layout/SettingRow'
import { appOpt } from './appOptions'
import { iconPath } from './icons'
Expand All @@ -11,8 +12,12 @@ import { iconPath } from './icons'
* live state lives in the subtext, so a sighted user and a screen reader get
* the same words), and a live region carries it only while there is one.
*
* The switch keeps the ACCENT when on, the one switch in Settings that does
* (#202's existing exception): it hands a Windows shortcut to Prism.
* The switch is the core's, like every other (#318; owner, 2026-10-07:
* "toggles differ in look i like the teal with black not the green and
* white", then "it should depend on the theme"). It used to draw its own,
* the accent with a white knob (#202's exception), the one switch in
* Settings that did; the core's on switch now wears the style's accent with
* the ink chosen for it, so there is nothing left to be an exception for.
*/
export function WinEShortcutRow(): JSX.Element {
const [status, setStatus] = useState<WinEShortcutStatus>({
Expand Down Expand Up @@ -94,20 +99,12 @@ export function WinEShortcutRow(): JSX.Element {
{said}
</span>
)}
<button
id="win-e-shortcut"
role="switch"
aria-label={o.label}
aria-checked={status.enabled}
<Switch
on={status.enabled}
onChange={() => toggle()}
label={o.label}
disabled={busy || !status.available || (status.conflict && !status.enabled)}
onClick={toggle}
className={`relative h-[20px] w-[36px] shrink-0 rounded-full transition-colors disabled:opacity-50 ${status.enabled ? 'bg-[var(--p-accent)]' : 'bg-[var(--p-track)]'}`}
>
<span
className="absolute left-[2px] top-[2px] h-4 w-4 rounded-full bg-white shadow-sm transition-transform duration-150 ease-out"
style={{ transform: status.enabled ? 'translateX(16px)' : 'none' }}
/>
</button>
/>
</SettingRow>
)
}
20 changes: 16 additions & 4 deletions src/renderer/src/components/settings/settingsControls.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,27 @@ describe('settings controls', () => {
})

// Still accented, since they are not buttons: the chosen card's ring, the
// chosen swatch's ring, the band slider's thumb, the progress bars drawn
// inside a style's preview card, and the Win+E switch (its existing
// exception: it hands a Windows shortcut to Prism).
// chosen swatch's ring, the band slider's thumb and the progress bars drawn
// inside a style's preview card. The Win+E switch was an exception until
// #318; it is the core's switch now, like every other.
it('the accent is only on marks, never on a button', () => {
const allowed = ['cards.tsx', 'ColourSchemes.tsx', 'MediaPage.tsx', 'TransportMini.tsx', 'WinEShortcutRow.tsx']
const allowed = ['cards.tsx', 'ColourSchemes.tsx', 'MediaPage.tsx', 'TransportMini.tsx']
const worn = files.filter(({ src }) => ACCENT.test(src)).map(({ f }) => f)
expect(worn.filter((f) => !allowed.includes(f))).toEqual([])
})

// EVERY SWITCH IS THE CORE'S (#318; owner, 2026-10-07: "toggles differ in
// look"). The Win+E row and the phone server each drew their own, the accent
// with a white knob, beside the core's. No file in the renderer may draw a
// switch of its own: one look, and the core decides it.
it("every switch in the app is the core's", () => {
const root = join(__dirname, '..', '..')
const own = (readdirSync(root, { recursive: true }) as string[])
.filter((f) => /\.tsx$/.test(f) && !/\.test\.tsx$/.test(f))
.filter((f) => /role="switch"/.test(readFileSync(join(root, f), 'utf8')))
expect(own).toEqual([])
})

it('Default apps and Clear are row buttons', () => {
const explorer = files.find(({ f }) => f === 'ExplorerPage.tsx')!.src
expect(explorer).toMatch(/id="default-apps"[\s\S]{0,120}className=\{ROW_BUTTON\}/)
Expand Down
38 changes: 38 additions & 0 deletions tools/e2e/run.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -14292,6 +14292,27 @@ const settingsLookOf = (win) =>
}
})

// EVERY SWITCH IS ONE SWITCH (#318; owner, 2026-10-07: "toggles differ in
// look i like the teal with black not the green and white"). The switches on
// the page, grouped by state: each group must be ONE look (the button's and
// the knob's classes, the track's and the knob's computed colours). The Win+E
// row drew its own, the accent with a white knob, beside the core's.
const switchLooksOf = (win) =>
win.evaluate(() => {
const bg = (el) => getComputedStyle(el).backgroundColor
return [...document.querySelectorAll('[data-settings-page] [role="switch"]')].map((sw) => {
const knob = sw.firstElementChild
return {
label: sw.getAttribute('aria-label'),
on: sw.getAttribute('aria-checked') === 'true',
look: JSON.stringify([sw.className, knob?.className ?? '', bg(sw), knob ? bg(knob) : '']),
// The markup with the state's own colour classes taken out: the same
// for every switch drawn by the one component, on or off.
shape: JSON.stringify([sw.className, knob?.className ?? ''].map((c) => c.split(/\s+/).filter((t) => !/^(enabled:hover:|disabled:opacity-)?(bg|brightness|opacity)-/.test(t)).join(' ')))
}
})
})

/**
* THE SETTINGS LOOK (#292; owner, 2026-10-05: the approved v1 "Grouped cards",
* with no accent bar on the chosen rail item). Every page in a dark and a
Expand Down Expand Up @@ -14360,6 +14381,23 @@ async function settingsLookScenario(fixtures) {
await win.mouse.move(5, 5)
await sleep(450)
const name = view ? `${page}-${view}` : page
if (page === 'explorer') {
const sws = await switchLooksOf(win)
for (const state of [true, false]) {
const group = sws.filter((x) => x.on === state)
const looks = [...new Set(group.map((x) => x.look))]
ok(
looks.length <= 1,
`${scheme} explorer: every ${state ? 'on' : 'off'} switch has one look (${group.map((x) => x.label).join(', ')}: ${looks.join(' | ')})`
)
}
ok(sws.some((x) => x.on), `${scheme} explorer: an on switch is measured (${sws.filter((x) => x.on).map((x) => x.label).join(', ')})`)
// Win+E is off and unavailable outside a packaged build, so it cannot
// be measured on: it is held to the same markup as the others, which
// is the core's component and so the core's on look.
const winE = sws.find((x) => x.label === 'Open in place of File Explorer')
ok(!!winE && sws.every((x) => x.shape === winE.shape), `${scheme} explorer: the Win+E switch is the core's (${winE?.shape})`)
}
const m = await settingsLookOf(win)
ok(m.label >= 4.5 && m.sub >= 4.5, `${scheme} ${name}: label and subtext read on the panel (${m.label.toFixed(1)}:1, ${m.sub.toFixed(1)}:1)`)
ok(m.icon >= 3, `${scheme} ${name}: the icon reads 3:1 on its tile (${m.icon.toFixed(1)}:1)`)
Expand Down
Loading