Files
feuerwerk/src/ui/Theme.ts
T
rootandClaude Fable 5.1 42cafa7543
game-maker / suite (push) Failing after 3m59s
Shader assets: graph-built shaders, the Shader tab and sprite rows from the graph (#62)
A shader is an asset carrying a node graph (Texture color, Output, Property,
Color, Add/Subtract/Multiply/Divide, Mix, Luminance) that compiles to the GLSL
fragment of a Pixi filter. The Shader tab reuses the Behavior canvas; a sprite
wears a shader through its Shader picker, and the rows below it are the
graph's Property nodes. Property schema, resolution and deviation take the
project because a sprite's rows now live in another asset. The built-in
registry of the prototype is gone; "Adjust colors" ships as a graph in the
fixture and the three example games.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-12 22:41:14 +02:00

154 lines
4.6 KiB
TypeScript

/** Design tokens for the canvas UI. Sizes only one widget uses may stay local to it. */
import { TextStyle } from 'pixi.js';
export const Theme = {
font: 'Inter, "Segoe UI", Roboto, Helvetica, Arial, sans-serif',
monoFont: '"JetBrains Mono", Consolas, "Courier New", monospace',
fontSize: 13,
smallFontSize: 11,
viewportLabelSize: 10,
rowHeight: 26,
controlHeight: 24,
padding: 8,
gap: 6,
radius: 4,
fieldPadding: 6,
checkboxSize: 16,
swatchSize: 12,
scrollbarWidth: 6,
/** Horizontal indent per nesting level in tree lists. */
treeIndent: 14,
hoverCursor: 'default',
colors: {
background: 0x1b1d22,
panel: 0x24272e,
panelHeader: 0x2c3038,
border: 0x383c46,
control: 0x16181d,
controlHover: 0x1e2127,
controlActive: 0x16181d,
accent: 0x4c8dff,
accentText: 0xffffff,
text: 0xe6e8ee,
textMuted: 0x8a90a0,
selection: 0x2b4a80,
danger: 0xe0553f,
viewport: 0x121316,
gizmo: 0x4c8dff,
/** Behavior symbols and owner links. */
behavior: 0xb066ff,
/** Collider outlines: solid (blocking) and trigger (events only). */
collider: 0x4fd18b,
trigger: 0xf2c84b,
container: 0x3fb8c9,
marquee: 0x4c8dff,
/** A value deviating from what it follows: an override on a placed object, a non-default on an asset. */
override: 0xf2c84b,
/** Node graph: canvas, node chrome, per-category title colors, port colors, live highlight. */
graphBackground: 0x15171b,
node: 0x262a32,
nodeBorder: 0x3b404b,
nodeSelected: 0x4c8dff,
categoryEvent: 0xc0392b,
categoryFlow: 0x7f8c8d,
categoryData: 0x2e8b57,
categoryVariable: 0xb8860b,
categoryAction: 0x2f6fd6,
categoryState: 0x8e5ad6,
categoryShader: 0x1f8f8f,
portExec: 0xffffff,
portNumber: 0x6fcf97,
portBoolean: 0xe57373,
portString: 0xe6a8ff,
portObject: 0x56ccf2,
portList: 0xf2a75c,
portStruct: 0xd4c25a,
portVec3: 0xff9ad5,
portAny: 0xbdbdbd,
/** Ring around the port a running edge drag would connect to. */
portTarget: 0xffffff,
hot: 0xffc857,
},
} as const;
const styleCache = new Map<string, TextStyle>();
export type TextStyleToken =
| 'body'
| 'bodyBold'
| 'muted'
| 'override'
| 'small'
| 'warning'
| 'live'
| 'title'
| 'mono'
| 'monoMuted'
| 'monoWarn'
| 'monoError'
| 'accent'
| 'viewportLabel'
| 'viewportLabelMuted';
/** Shared TextStyle instances so Pixi can cache glyph measurements per token. */
export function textStyle(token: TextStyleToken = 'body'): TextStyle {
let style = styleCache.get(token);
if (style) return style;
const base = { fontFamily: Theme.font, fontSize: Theme.fontSize, fill: Theme.colors.text };
switch (token) {
case 'body':
style = new TextStyle(base);
break;
case 'bodyBold':
style = new TextStyle({ ...base, fontWeight: '600' });
break;
case 'muted':
style = new TextStyle({ ...base, fill: Theme.colors.textMuted });
break;
case 'override':
style = new TextStyle({ ...base, fill: Theme.colors.override });
break;
case 'small':
style = new TextStyle({ ...base, fontSize: Theme.smallFontSize, fill: Theme.colors.textMuted });
break;
case 'warning':
style = new TextStyle({ ...base, fontSize: Theme.smallFontSize, fill: Theme.colors.danger });
break;
case 'live':
style = new TextStyle({ ...base, fontSize: Theme.smallFontSize, fill: Theme.colors.hot });
break;
case 'title':
style = new TextStyle({
...base,
fontSize: Theme.smallFontSize,
fontWeight: '700',
letterSpacing: 0.5,
fill: Theme.colors.textMuted,
});
break;
case 'mono':
style = new TextStyle({ ...base, fontFamily: Theme.monoFont });
break;
case 'monoMuted':
style = new TextStyle({ ...base, fontFamily: Theme.monoFont, fill: Theme.colors.textMuted });
break;
case 'monoWarn':
style = new TextStyle({ ...base, fontFamily: Theme.monoFont, fill: Theme.colors.override });
break;
case 'monoError':
style = new TextStyle({ ...base, fontFamily: Theme.monoFont, fill: Theme.colors.danger });
break;
case 'accent':
style = new TextStyle({ ...base, fill: Theme.colors.accentText, fontWeight: '600' });
break;
case 'viewportLabel':
style = new TextStyle({ ...base, fontSize: Theme.viewportLabelSize });
break;
case 'viewportLabelMuted':
style = new TextStyle({ ...base, fontSize: Theme.viewportLabelSize, fill: Theme.colors.textMuted });
break;
}
styleCache.set(token, style);
return style;
}