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
26 changes: 25 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ so an update never silently changes what an existing user sees; the bridge to ma
same"). `core/renderer/settings`: the field primitives, `TerminalAppearanceSettings` (theme wall and
editor, font, size, acrylic, the two indicator colours) and the rows `ShellSetting` /
`AgentIndicatorSetting`. Each app composes its OWN page round them (`components/Settings.tsx` here
is only the page plus this app's rows: new tabs, Explorer menu, window edges, version). Values are per app (own
is only the page plus this app's rows: new tabs, Explorer menu, window edges, accent, version). Values are per app (own
userData), never shared. `settings/options.ts` lists every terminal option by id; a unit test holds
the list and the sections together, and each app's e2e (`options`) asserts its page shows that
list and no terminal-looking row of its own. A row outside the list is a fork.
Expand Down Expand Up @@ -161,6 +161,9 @@ so an update never silently changes what an existing user sees; the bridge to ma
on the icon's own dark grey `#383c44` (Cinder). They were the two ORIGINALS whose names already
fitted; the public schemes (Dracula, Nord, Gruvbox...) keep their real colours, since a scheme
called Dracula that is not Dracula is a lie. Ids unchanged: they are saved-settings keys.
- **THE HELP POPUP BLURS THE WINDOW BEHIND IT AND CASTS NO SHADOW** (owner, 2026-09-22: "remove the
shadow behind this and make the bg blurred when it's open"): `backdrop-blur` on a lighter scrim; the
blur already lifts the panel off the page, and a shadow on top of it read as a dark halo.
- **COMMAND HELP IS A POPUP THAT SHOWS AND COPIES, AND NOTHING ELSE** (#12; owner, 2026-09-19: "an
easy to use panel where you can find shell commands... searchable... metadata on each command so a
natural-language search finds it... optional in settings", and 2026-09-20: "a pop up with copy
Expand Down Expand Up @@ -395,6 +398,27 @@ terminal theme, anything that reads or shows files.
asserts what main HEARD, and how the border looks is on the hands-on list. The `edges` e2e
measures real edges (a tab separator, the title bar's rule, the settings rail, a settings row),
WAITING for each to arrive, since the strip's border colour transitions over 550ms.
- **THE ACCENT AND THE BACKGROUND ARE SETTINGS, AND UNSET IS THE THEME'S** (owner, 2026-09-22:
"an accent colour option which would pick the accents you see, like the blue highlight effect
and tab effect"; "let background colour be a setting ... move those settings, bg and accent, to
the top of the list right under font and font size"). Settings > Appearance > Background colour
and Accent colour (`window-background`, `window-accent`; localStorage `prism.window.background`
/ `.accent`; stores `lib/backgroundPrefs.ts` / `accentPrefs.ts`, both `lib/colourPref.ts`). Each
shows the theme's own colour until one is picked, then a plain **Reset** word (the core's
`RESET_LINK`, Prism's own style) forgets it. This app's rows, for the edges' reason (in Prism the
window's colours are the app style's); they sit under Font size because the core lends the place
(`TerminalAppearanceSettings`' `afterFont`; Prism passes nothing). Applied ONCE, in `paintChrome`:
the background replaces the theme's before `chromeTokens` measures it, so the mode, every ink and
the accent's floor follow; the terminal follows too because the panel paints its ground from the
same token (`paintsGround`). Known gap: xterm's ANSI floor and the character under the block
cursor are still measured against the theme's own background, which is the core's. A chosen
accent is KEPT where the ground can show it and only MOVED to the 3:1 floor where it cannot. The
agent working indicator still follows the THEME's accent (a core change and an owner decision).
The `accent` e2e measures the active tab's rule, a pressed segment, the row order and the ground.
- **PT DEFAULT IS THE DEFAULT THEME HERE, AND FIRST IN THE WALL** (owner, 2026-09-22, handing over the palette he had saved as
Custom: "let this be the default theme ... for prism terminal"): Wombat's colours on #121212, the
two blacks lifted, the icon's orange `#fe8f34` as the accent. A core preset like any other, and
this host's `defaults.theme`, so anyone who never picked a theme moves to it with the update.
- **THE CLOSE QUESTION IS ONE RULE, NOT A SETTING** (owner, 2026-09-19, #15: "remove the setting but
just have it on smart mode by default, so it won't ask if you're in a normal shell but if you're
working with an agent it will ask"). `core/renderer/lib/agentClose.ts`, the same in Prism: a plain
Expand Down
2 changes: 1 addition & 1 deletion core/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "prism-term-core",
"version": "0.7.1",
"version": "0.8.0",
"description": "What Prism Terminal and Prism share: the terminal (pty, shells, agent detection and indicator, themes, links, the panel, dictation) and the update chip with its window. TypeScript source, compiled by the host.",
"license": "MIT",
"private": true,
Expand Down
4 changes: 2 additions & 2 deletions core/renderer/components/HelpPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ export default function HelpPanel({
<div
data-owns-escape
data-help-scrim
className="no-drag fixed inset-0 z-50 grid place-items-center bg-black/55 p-6"
className="no-drag fixed inset-0 z-50 grid place-items-center bg-black/35 p-6 backdrop-blur-[6px]"
role="presentation"
onMouseDown={onClose}
>
Expand All @@ -499,7 +499,7 @@ export default function HelpPanel({
aria-label="Command help"
onMouseDown={(e) => e.stopPropagation()}
style={{ width: clampHelpWidth(width) }}
className="relative flex h-[min(82vh,680px)] max-w-full flex-col overflow-hidden rounded-[var(--p-radius)] border border-[color:var(--p-divider)] bg-[var(--p-side-flat)] shadow-[0_24px_70px_rgba(0,0,0,.6)]"
className="relative flex h-[min(82vh,680px)] max-w-full flex-col overflow-hidden rounded-[var(--p-radius)] border border-[color:var(--p-divider)] bg-[var(--p-side-flat)]"
>
{grip('left')}
{grip('right')}
Expand Down
7 changes: 5 additions & 2 deletions core/renderer/lib/termTheme.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@ import { describe, expect, it } from 'vitest'
import { DEFAULT_TERM_THEME, TERM_PRESETS, resolveTermTheme } from './termTheme'

describe('resolveTermTheme', () => {
it('leads the list with the prism preset, which is the default', () => {
expect(TERM_PRESETS[0]?.id).toBe('prism')
it("keeps the core's own default, prism, in the list (PT Default now leads it)", () => {
// The wall's first card is the HOST's default (Prism Terminal's is PT
// Default); the core's neutral fallback is still 'prism', found by id.
expect(TERM_PRESETS[0]?.id).toBe('pt-default')
expect(DEFAULT_TERM_THEME).toBe('prism')
expect(TERM_PRESETS.some((p) => p.id === DEFAULT_TERM_THEME)).toBe(true)
})

it('resolves a preset by id, with a full sixteen', () => {
Expand Down
17 changes: 16 additions & 1 deletion core/renderer/lib/termTheme.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,21 @@ export interface TermPreset {
* Prism's app styles' names.
*/
export const TERM_PRESETS: TermPreset[] = [
// PT DEFAULT is the owner's own palette (2026-09-22, saved as Custom and handed
// over: "let this be the default theme ... for prism terminal"): Wombat's
// colours on a darker #121212, the two blacks lifted so they read, and the
// app icon's orange as the accent. Prism Terminal's default, and FIRST in
// the wall (owner: "it should be first in the list and be called PT
// default"); a preset like any other everywhere else.
{
id: 'pt-default',
name: 'PT Default',
bg: '#121212',
fg: '#dedacf',
cursor: '#bbbbbb',
accent: '#fe8f34',
ansi: { black: '#646464', red: '#ff615a', green: '#b1e969', yellow: '#ebd99c', blue: '#5da9f6', magenta: '#e86aff', cyan: '#82fff7', white: '#dedacf', brightBlack: '#6b6b6b', brightRed: '#f58c80', brightGreen: '#ddf88f', brightYellow: '#eee5b2', brightBlue: '#a5c7ff', brightMagenta: '#ddaaff', brightCyan: '#b7fff9', brightWhite: '#ffffff' }
},
// Prism's own dark look: what the terminal wore by default inside Prism.
{ id: 'prism', name: 'Prism', bg: '#0b0b0f', fg: '#e7e7ee', cursor: '#7c7cf0', accent: '#5b5bd6' },
{
Expand Down Expand Up @@ -418,7 +433,7 @@ export function resolveTermTheme(themeId: string): TermTheme {
...legiblePalette(c.ansi, c.bg)
}
}
// TERM_PRESETS[0] is 'prism'; the find cannot miss, the fallback is for the type.
// DEFAULT_TERM_THEME is in the list; that find cannot miss, the last fallback is for the type.
const p =
TERM_PRESETS.find((x) => x.id === themeId) ??
// An id nothing answers to: the host's own default, where that is a preset.
Expand Down
72 changes: 44 additions & 28 deletions core/renderer/settings/TerminalAppearance.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useEffect, useMemo, useRef, useState, type JSX } from 'react'
import { followsHostStyle, termHost } from '../host'
import { useEffect, useMemo, useRef, useState, type JSX, type ReactNode } from 'react'
import { followsHostStyle, hostDefaults, termHost } from '../host'
import {
FONT_PCTS,
TERM_FONTS,
Expand Down Expand Up @@ -273,7 +273,14 @@ function pickPreset(id: string): void {
if (volume !== termExtraDefaults().indicator) setAgentIndicator(volume)
}

export function TerminalAppearanceSettings(): JSX.Element {
/**
* `afterFont`: an app's OWN rows, placed right under Font size (owner,
* 2026-09-22: "move those settings, bg and accent, to the top of the list
* right under font and font size"). The rows stay the app's - Prism passes
* nothing, since its window colours belong to its app style - and the core
* only lends them the place.
*/
export function TerminalAppearanceSettings({ afterFont }: { afterFont?: ReactNode } = {}): JSX.Element {
const themeId = useTermThemeId()
const fontPct = useTermFontPct()
const fontId = useTermFontId()
Expand Down Expand Up @@ -317,10 +324,39 @@ export function TerminalAppearanceSettings(): JSX.Element {
const [lightFirst] = useState(
() => luminance(normalizeColor(resolveTermTheme(termThemeId()).background, '#000000')) > 0.4
)
// THE HOST'S OWN DEFAULT LEADS THE WALL, ahead of Custom (owner, 2026-09-22:
// "it should be first in the list"). Prism Terminal's is a preset (PT
// Default); Prism's is 'style', which is no preset, so its wall is untouched.
const defaultPreset = TERM_PRESETS.find((p) => p.id === hostDefaults().theme)
const sortedPresets = useMemo(() => {
const lum = (bg: string): number => luminance(normalizeColor(bg, '#000000'))
return [...TERM_PRESETS].sort((a, b) => (lightFirst ? lum(b.bg) - lum(a.bg) : lum(a.bg) - lum(b.bg)))
}, [lightFirst])
return TERM_PRESETS.filter((p) => p !== defaultPreset).sort((a, b) =>
lightFirst ? lum(b.bg) - lum(a.bg) : lum(a.bg) - lum(b.bg)
)
}, [lightFirst, defaultPreset])
const presetCard = (p: (typeof TERM_PRESETS)[number]): JSX.Element => {
const t = resolveTermTheme(p.id)
return (
<TermThemeCard
key={p.id}
id={p.id}
name={p.name}
on={themeId === p.id}
bg={t.background}
fg={t.foreground}
cursor={t.cursor}
ansi={{
green: t.green ?? '',
yellow: t.yellow ?? '',
blue: t.blue ?? '',
cyan: t.cyan ?? '',
red: t.red ?? ''
}}
onPick={() => pickPreset(p.id)}
onEdit={() => setEditing(paletteOf(p.id))}
/>
)
}
// "Save changes": the WHOLE look - palette of the selected theme, font,
// size, agent colours, acrylic and its opacity - lands in the Custom slot,
// reselectable after any theme switch. The indicator's volume is not part
Expand Down Expand Up @@ -394,6 +430,7 @@ export function TerminalAppearanceSettings(): JSX.Element {
style={{ maxHeight: wallHeight }}
>
<div className="flex flex-wrap gap-3">
{defaultPreset && presetCard(defaultPreset)}
{followsHostStyle() && (
<TermThemeCard
id="style"
Expand Down Expand Up @@ -425,29 +462,7 @@ export function TerminalAppearanceSettings(): JSX.Element {
onEdit={() => setEditing(paletteOf('custom'))}
/>
)}
{sortedPresets.map((p) => {
const t = resolveTermTheme(p.id)
return (
<TermThemeCard
key={p.id}
id={p.id}
name={p.name}
on={themeId === p.id}
bg={t.background}
fg={t.foreground}
cursor={t.cursor}
ansi={{
green: t.green ?? '',
yellow: t.yellow ?? '',
blue: t.blue ?? '',
cyan: t.cyan ?? '',
red: t.red ?? ''
}}
onPick={() => pickPreset(p.id)}
onEdit={() => setEditing(paletteOf(p.id))}
/>
)
})}
{sortedPresets.map(presetCard)}
</div>
</div>
<div className="mt-2 flex justify-center">
Expand Down Expand Up @@ -496,6 +511,7 @@ export function TerminalAppearanceSettings(): JSX.Element {
options={FONT_PCTS.map((p) => ({ id: String(p), name: `${p}%` }))}
/>
</Pref>
{afterFont}
{/* The material does not exist before Windows 11, so there the row says
why instead of offering a switch that would do nothing. */}
<Pref
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,7 +1,7 @@
{
"name": "prism-terminal",
"productName": "Prism Terminal",
"version": "0.9.1",
"version": "0.10.0",
"description": "A tabbed Windows terminal for AI CLIs.",
"main": "./out/main/index.js",
"author": "Max",
Expand Down
15 changes: 13 additions & 2 deletions src/renderer/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,8 @@ import {
import { presetAccent, resolveTermTheme } from '@core/renderer/lib/termTheme'
import { applyChrome, chromeTokens } from './lib/chromeTheme'
import { onWindowEdgesChange, windowEdges } from './lib/edgesPrefs'
import { onWindowAccentChange, windowAccent } from './lib/accentPrefs'
import { onWindowBackgroundChange, windowBackground } from './lib/backgroundPrefs'

const Settings = lazy(() => import('./components/Settings'))
// Loaded when it is first opened: the popup brings the whole catalogue with it,
Expand Down Expand Up @@ -86,11 +88,16 @@ function paintChrome(): void {
const acrylic = termAcrylic()
const id = termThemeId()
const edges = windowEdges()
// A picked background replaces the theme's for the whole window; the panel
// paints the terminal's ground from the same token, so it follows too.
const theme = resolveTermTheme(id)
const background = windowBackground()
const tokens = chromeTokens(
resolveTermTheme(id),
background ? { ...theme, background } : theme,
acrylic ? termOpacity() : 100,
presetAccent(id),
edges
edges,
windowAccent()
)
applyChrome(tokens)
window.prism.setWindowBg(tokens.vars['--p-bg-solid'])
Expand Down Expand Up @@ -133,9 +140,13 @@ export default function App(): JSX.Element {
paintChrome()
const offLook = onTermLookChange(paintChrome)
const offEdges = onWindowEdgesChange(paintChrome)
const offAccent = onWindowAccentChange(paintChrome)
const offBackground = onWindowBackgroundChange(paintChrome)
return () => {
offBackground()
offLook()
offEdges()
offAccent()
}
}, [])

Expand Down
Loading
Loading