SLOPSHOPPER

palette

Shares the wezterm color scheme with other mods as $.palette

newprocess
v0.1.0no licenseupdated 2026-10-09djavrell/dotfile/claude/mods/palette
A shopper browsing a rack in a slop shop
README

palette

Shares the terminal's color scheme with every Claude Code mod. Mods cannot import code from one another's folders, so the scheme lives in one shared service on $, and each pane or band paints with the same colors as wezterm.

The colors come from wezterm's nordic scheme, so the Claude Code UI matches the terminal. The mod keeps no state and draws nothing of its own.

Example

Another mod lists palette under dependencies in its plugin.json, then reads the theme with $.palette.get():

const theme = await $.palette.get();
const accent = theme.ansi.blue;

Used by session (the side pane) and pr-viewer (the band above the prompt).

Source 2 files
hooks/register.ts 82 lines
1import type { EngineInterface, Register } from 'claude-code'
2
3import type { Palette, PaletteColors, PaletteTheme } from '../types'
4
5// relative to $HOME
6const SCHEME = '.bashrc.d/wezterm/colors/nordic.toml'
7const SLOTS = ['black', 'red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white'] as const
8const SCALARS = ['foreground', 'background', 'cursor_bg', 'cursor_border', 'cursor_fg', 'selection_fg', 'selection_bg'] as const
9
10// full copy of wezterm/colors/nordic.toml, used field by field where the file is missing or lacks one;
11// `comment` is nordic.nvim's comment gray, which the toml has no slot for
12export const FALLBACK: PaletteTheme = {
13  foreground: '#D8DEE9',
14  background: '#242933',
15  cursor_bg: '#D8DEE9',
16  cursor_border: '#D8DEE9',
17  cursor_fg: '#242933',
18  selection_fg: '#D8DEE9',
19  selection_bg: '#2E3440',
20  ansi: {
21    black: '#191D24',
22    red: '#BF616A',
23    green: '#A3BE8C',
24    yellow: '#EBCB8B',
25    blue: '#81A1C1',
26    magenta: '#B48EAD',
27    cyan: '#8FBCBB',
28    white: '#D8DEE9',
29  },
30  brights: {
31    black: '#3B4252',
32    red: '#D06F79',
33    green: '#B1D196',
34    yellow: '#F0D399',
35    blue: '#88C0D0',
36    magenta: '#C895BF',
37    cyan: '#93CCDC',
38    white: '#E5E9F0',
39  },
40  comment: '#60728A',
41}
42
43const HEX = /"(#[0-9A-Fa-f]{6})"/g
44
45// ponytail: reads only the shapes a wezterm scheme uses (key = "#hex", key = [ "#hex", ... ]), not general TOML
46export const parseScheme = (text: string): PaletteTheme => {
47  const scalar = (key: string) => text.match(new RegExp(`^\\s*${key}\\s*=\\s*"(#[0-9A-Fa-f]{6})"`, 'm'))?.[1]
48  const array = (key: 'ansi' | 'brights'): PaletteColors => {
49    const body = text.match(new RegExp(`^\\s*${key}\\s*=\\s*\\[([^\\]]*)\\]`, 'm'))?.[1] ?? ''
50    const hex = [...body.matchAll(HEX)].map(match => match[1])
51    return Object.fromEntries(
52      SLOTS.map((slot, slotIndex) => [slot, hex[slotIndex] ?? FALLBACK[key][slot]]),
53    ) as PaletteColors
54  }
55  const theme = { ...FALLBACK, ansi: array('ansi'), brights: array('brights') }
56  for (const key of SCALARS) theme[key] = scalar(key) ?? FALLBACK[key]
57  return theme
58}
59
60async function load($: EngineInterface): Promise<PaletteTheme> {
61  try {
62    const home = (await $.process.run(['printenv', 'HOME'])).stdout.trim()
63    return parseScheme(await $.fs.read(`${home}/${SCHEME}`))
64  } catch {
65    return FALLBACK
66  }
67}
68
69export const register: Register = on => {
70  let cached: Promise<PaletteTheme> | undefined
71
72  // the noun's own body is the bottom of its chain: the built-in copy
73  on('engine.create', async (_, event, next) => {
74    const palette: Palette = { get: async () => FALLBACK }
75
76    return { ...(await next(event)), palette }
77  })
78
79  // a call on $.palette.get runs this first, with a full $ to read the scheme file
80  on('palette.get', async $ => ({ value: await (cached ??= load($)) }))
81}
82
types/index.d.ts 37 lines
1// the 8 ANSI slots of a wezterm `ansi` or `brights` array, in order
2export type PaletteColors = {
3  black: string
4  red: string
5  green: string
6  yellow: string
7  blue: string
8  magenta: string
9  cyan: string
10  white: string
11}
12
13// wezterm's [colors] table, plus `comment`, the theme's gray the toml has no slot for
14export type PaletteTheme = {
15  foreground: string
16  background: string
17  cursor_bg: string
18  cursor_border: string
19  cursor_fg: string
20  selection_fg: string
21  selection_bg: string
22  ansi: PaletteColors
23  brights: PaletteColors
24  comment: string
25}
26
27export type Palette = {
28  // read once per load from the wezterm scheme file, the built-in copy where it cannot be read
29  get: () => Promise<PaletteTheme>
30}
31
32declare module 'claude-code' {
33  interface EngineInterface {
34    palette: Palette
35  }
36}
37