@@ -23,7 +23,22 @@ import type {
2323 ContentTypeMap ,
2424 ColorPaletteDefinition ,
2525 GrayscalePaletteDefinition ,
26+ Palette ,
27+ PaletteConfig ,
2628} from './types/domain.js'
29+ import { isColorPalette , isGrayscalePalette } from './types/domain.js'
30+ // Re-export browser-safe palette options (single source of truth for labels)
31+ export {
32+ PALETTE_OPTIONS ,
33+ GRAYSCALE_OPTIONS ,
34+ COLOR_OPTIONS ,
35+ type PaletteOption ,
36+ } from './html/js/palette-options.js'
37+ import { PALETTE_OPTIONS } from './html/js/palette-options.js'
38+
39+ /** Lookup label from palette-options (single source of truth) */
40+ const label = ( value : string ) : string =>
41+ PALETTE_OPTIONS . find ( ( p ) => p . value === value ) ?. label ?? value
2742import {
2843 isValidTimezone ,
2944 hasEnvConfig as checkEnvConfig ,
@@ -297,63 +312,56 @@ export const VALID_FORMATS: readonly ImageFormat[] = [
297312export const VALID_ROTATIONS : readonly RotationAngle [ ] = [ 90 , 180 , 270 ] as const
298313
299314/**
300- * Color palette definitions for e-ink displays
315+ * Unified palette definitions - SINGLE SOURCE OF TRUTH
316+ *
317+ * Add new palettes here and they automatically appear in UI + dithering.
318+ * Order determines dropdown display order.
301319 *
302320 * @see https://www.eink.com/brand/detail/Spectra6
303321 * @see https://shop.pimoroni.com/products/inky-impression-7-3
304322 */
305- export const COLOR_PALETTES : ColorPaletteDefinition = {
306- // 6-color: Basic RGB + Yellow + Black/White
307- 'color-6a' : [
308- '#000000' , // Black
309- '#FFFFFF' , // White
310- '#FF0000' , // Red
311- '#00FF00' , // Green
312- '#0000FF' , // Blue
313- '#FFFF00' , // Yellow
314- ] ,
315- // 7-color ACeP/Gallery with Orange (Waveshare, Pimoroni Inky Impression)
316- 'color-7a' : [
317- '#000000' , // Black
318- '#FFFFFF' , // White
319- '#FF0000' , // Red
320- '#00FF00' , // Green
321- '#0000FF' , // Blue
322- '#FFFF00' , // Yellow
323- '#FF8C00' , // Orange (Dark Orange - per Pimoroni spec)
324- ] ,
325- // 7-color with Cyan (for displays that have Cyan instead of Orange)
326- 'color-7b' : [
327- '#000000' , // Black
328- '#FFFFFF' , // White
329- '#FF0000' , // Red
330- '#00FF00' , // Green
331- '#0000FF' , // Blue
332- '#FFFF00' , // Yellow
333- '#00FFFF' , // Cyan
334- ] ,
335- // 8-color Spectra 6 T2000 (2025+) - has both Cyan AND Orange
336- 'color-8a' : [
337- '#000000' , // Black
338- '#FFFFFF' , // White
339- '#FF0000' , // Red
340- '#00FF00' , // Green
341- '#0000FF' , // Blue
342- '#FFFF00' , // Yellow
343- '#00FFFF' , // Cyan
344- '#FF8C00' , // Orange
345- ] ,
323+ export const PALETTES : Record < Palette , PaletteConfig > = {
324+ // Grayscale palettes (labels from palette-options.ts)
325+ bw : { label : label ( 'bw' ) , levels : 2 } ,
326+ 'gray-4' : { label : label ( 'gray-4' ) , levels : 4 } ,
327+ 'gray-16' : { label : label ( 'gray-16' ) , levels : 16 } ,
328+ 'gray-256' : { label : label ( 'gray-256' ) , levels : 256 } ,
329+ // Color palettes (labels from palette-options.ts)
330+ 'color-6a' : {
331+ label : label ( 'color-6a' ) ,
332+ colors : [ '#000000' , '#FFFFFF' , '#FF0000' , '#00FF00' , '#0000FF' , '#FFFF00' ] ,
333+ } ,
334+ 'color-7a' : {
335+ label : label ( 'color-7a' ) ,
336+ colors : [ '#000000' , '#FFFFFF' , '#FF0000' , '#00FF00' , '#0000FF' , '#FFFF00' , '#FF8C00' ] ,
337+ } ,
338+ 'color-7b' : {
339+ label : label ( 'color-7b' ) ,
340+ colors : [ '#000000' , '#FFFFFF' , '#FF0000' , '#00FF00' , '#0000FF' , '#FFFF00' , '#00FFFF' ] ,
341+ } ,
342+ 'color-8a' : {
343+ label : label ( 'color-8a' ) ,
344+ colors : [ '#000000' , '#FFFFFF' , '#FF0000' , '#00FF00' , '#0000FF' , '#FFFF00' , '#00FFFF' , '#FF8C00' ] ,
345+ } ,
346346}
347347
348- /**
349- * Grayscale palette definitions (number of gray levels)
350- */
351- export const GRAYSCALE_PALETTES : GrayscalePaletteDefinition = {
352- bw : 2 ,
353- 'gray-4' : 4 ,
354- 'gray-16' : 16 ,
355- 'gray-256' : 256 ,
356- }
348+ // =============================================================================
349+ // DERIVED PALETTE CONSTANTS (backward compatibility)
350+ // =============================================================================
351+
352+ /** Color palettes (hex arrays) - derived from PALETTES */
353+ export const COLOR_PALETTES : ColorPaletteDefinition = Object . fromEntries (
354+ Object . entries ( PALETTES )
355+ . filter ( ( [ _ , config ] ) => isColorPalette ( config ) )
356+ . map ( ( [ key , config ] ) => [ key , ( config as { colors : string [ ] } ) . colors ] )
357+ ) as ColorPaletteDefinition
358+
359+ /** Grayscale palettes (gray levels) - derived from PALETTES */
360+ export const GRAYSCALE_PALETTES : GrayscalePaletteDefinition = Object . fromEntries (
361+ Object . entries ( PALETTES )
362+ . filter ( ( [ _ , config ] ) => isGrayscalePalette ( config ) )
363+ . map ( ( [ key , config ] ) => [ key , ( config as { levels : number } ) . levels ] )
364+ ) as GrayscalePaletteDefinition
357365
358366/**
359367 * Default wait time after page load (milliseconds)
0 commit comments