mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-04-19 23:41:18 +02:00
2024.5.0 (#39)
This commit is contained in:
31
src-web/lib/theme/appearance.ts
Normal file
31
src-web/lib/theme/appearance.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
import { getCurrent } from '@tauri-apps/api/webviewWindow';
|
||||
import type { Appearance } from './window';
|
||||
|
||||
export function getCSSAppearance(): Appearance {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
}
|
||||
|
||||
export async function getWindowAppearance(): Promise<Appearance> {
|
||||
const a = await getCurrent().theme();
|
||||
return a ?? getCSSAppearance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Subscribe to appearance (dark/light) changes. Note, we use Tauri Window appearance instead of
|
||||
* CSS appearance because CSS won't fire the way we handle window theme management.
|
||||
*/
|
||||
export function subscribeToWindowAppearanceChange(
|
||||
cb: (appearance: Appearance) => void,
|
||||
): () => void {
|
||||
const container = { unsubscribe: () => {} };
|
||||
|
||||
getCurrent()
|
||||
.onThemeChanged((t) => {
|
||||
cb(t.payload);
|
||||
})
|
||||
.then((l) => {
|
||||
container.unsubscribe = l;
|
||||
});
|
||||
|
||||
return () => container.unsubscribe();
|
||||
}
|
||||
94
src-web/lib/theme/color.ts
Normal file
94
src-web/lib/theme/color.ts
Normal file
@@ -0,0 +1,94 @@
|
||||
import parseColor from 'parse-color';
|
||||
|
||||
export class Color {
|
||||
private theme: 'dark' | 'light' = 'light';
|
||||
|
||||
private hue: number = 0;
|
||||
private saturation: number = 0;
|
||||
private lightness: number = 0;
|
||||
private alpha: number = 1;
|
||||
|
||||
constructor(cssColor: string, theme: 'dark' | 'light') {
|
||||
try {
|
||||
const { hsla } = parseColor(cssColor || '');
|
||||
this.hue = hsla[0];
|
||||
this.saturation = hsla[1];
|
||||
this.lightness = hsla[2];
|
||||
this.alpha = hsla[3] ?? 1;
|
||||
this.theme = theme;
|
||||
} catch (err) {
|
||||
console.log('Failed to parse CSS color', cssColor, err);
|
||||
}
|
||||
}
|
||||
|
||||
static transparent(): Color {
|
||||
return new Color('rgb(0,0,0)', 'light').translucify(1);
|
||||
}
|
||||
|
||||
static white(): Color {
|
||||
return new Color('rgb(0,0,0)', 'light').lower(1);
|
||||
}
|
||||
|
||||
static black(): Color {
|
||||
return new Color('rgb(0,0,0)', 'light').lift(1);
|
||||
}
|
||||
|
||||
private clone(): Color {
|
||||
return new Color(this.css(), this.theme);
|
||||
}
|
||||
|
||||
lower(mod: number): Color {
|
||||
return this.theme === 'dark' ? this._darken(mod) : this._lighten(mod);
|
||||
}
|
||||
|
||||
lift(mod: number): Color {
|
||||
return this.theme === 'dark' ? this._lighten(mod) : this._darken(mod);
|
||||
}
|
||||
|
||||
translucify(mod: number): Color {
|
||||
const c = this.clone();
|
||||
c.alpha = c.alpha - c.alpha * mod;
|
||||
return c;
|
||||
}
|
||||
|
||||
desaturate(mod: number): Color {
|
||||
const c = this.clone();
|
||||
c.saturation = c.saturation - c.saturation * mod;
|
||||
return c;
|
||||
}
|
||||
|
||||
saturate(mod: number): Color {
|
||||
const c = this.clone();
|
||||
c.saturation = this.saturation + (100 - this.saturation) * mod;
|
||||
return c;
|
||||
}
|
||||
|
||||
lighterThan(c: Color): boolean {
|
||||
return this.lightness > c.lightness;
|
||||
}
|
||||
|
||||
css(): string {
|
||||
// If opacity is 1, allow for Tailwind modification
|
||||
const h = Math.round(this.hue);
|
||||
const s = Math.round(this.saturation);
|
||||
const l = Math.round(this.lightness);
|
||||
const a = Math.round(this.alpha * 100) / 100;
|
||||
return `hsla(${h}, ${s}%, ${l}%, ${a})`;
|
||||
}
|
||||
|
||||
hex(): string {
|
||||
return parseColor(this.css()).hex;
|
||||
}
|
||||
|
||||
private _lighten(mod: number): Color {
|
||||
const c = this.clone();
|
||||
c.lightness = this.lightness + (100 - this.lightness) * mod;
|
||||
return c;
|
||||
}
|
||||
|
||||
private _darken(mod: number): Color {
|
||||
const c = this.clone();
|
||||
c.lightness = this.lightness - this.lightness * mod;
|
||||
return c;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { generateColorVariant, toTailwindVariable } from './theme';
|
||||
|
||||
describe('Generate colors', () => {
|
||||
it('Generates dark colors', () => {
|
||||
expect(generateColorVariant('hsl(0,0%,50%)', 50, 'dark', 0.2, 0.8)).toBe('hsl(0,0%,14.0%)');
|
||||
expect(generateColorVariant('hsl(0,0%,50%)', 950, 'dark', 0.2, 0.8)).toBe('hsl(0,0%,77.0%)');
|
||||
expect(generateColorVariant('hsl(0,0%,50%)', 50, 'dark', 0.4, 0.6)).toBe('hsl(0,0%,23.0%)');
|
||||
expect(generateColorVariant('hsl(0,0%,50%)', 950, 'dark', 0.4, 0.6)).toBe('hsl(0,0%,59.0%)');
|
||||
});
|
||||
it('Generates light colors', () => {
|
||||
expect(generateColorVariant('hsl(0,0%,50%)', 50, 'light', 0.2, 0.8)).toBe('hsl(0,0%,80.0%)');
|
||||
expect(generateColorVariant('hsl(0,0%,50%)', 950, 'light', 0.2, 0.8)).toBe('hsl(0,0%,14.0%)');
|
||||
expect(generateColorVariant('hsl(0,0%,50%)', 50, 'light', 0.4, 0.6)).toBe('hsl(0,0%,60.0%)');
|
||||
expect(generateColorVariant('hsl(0,0%,50%)', 950, 'light', 0.4, 0.6)).toBe('hsl(0,0%,23.0%)');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Generates Tailwind color', () => {
|
||||
it('Does it', () => {
|
||||
expect(
|
||||
toTailwindVariable({ name: 'blue', cssColor: 'hsl(10, 20%, 30%)', variant: 100 }),
|
||||
).toEqual('--color-blue-100: 10 20% 30%;');
|
||||
});
|
||||
});
|
||||
@@ -1,172 +0,0 @@
|
||||
import parseColor from 'parse-color';
|
||||
import type { Appearance } from './window';
|
||||
|
||||
export type AppThemeColor =
|
||||
| 'gray'
|
||||
| 'red'
|
||||
| 'orange'
|
||||
| 'yellow'
|
||||
| 'green'
|
||||
| 'blue'
|
||||
| 'pink'
|
||||
| 'violet';
|
||||
const colorNames: AppThemeColor[] = [
|
||||
'gray',
|
||||
'red',
|
||||
'orange',
|
||||
'yellow',
|
||||
'green',
|
||||
'blue',
|
||||
'pink',
|
||||
'violet',
|
||||
];
|
||||
export type AppThemeColorVariant =
|
||||
| 0
|
||||
| 50
|
||||
| 100
|
||||
| 200
|
||||
| 300
|
||||
| 400
|
||||
| 500
|
||||
| 600
|
||||
| 700
|
||||
| 800
|
||||
| 900
|
||||
| 950
|
||||
| 1000;
|
||||
|
||||
export const appThemeVariants: AppThemeColorVariant[] = [
|
||||
0, 50, 100, 200, 300, 400, 500, 600, 700, 800, 900, 950, 1000,
|
||||
];
|
||||
|
||||
export type AppThemeLayer = 'root' | 'sidebar' | 'titlebar' | 'content' | 'above';
|
||||
export type AppThemeColors = Record<AppThemeColor, string>;
|
||||
|
||||
export interface AppThemeLayerStyle {
|
||||
colors: AppThemeColors;
|
||||
blackPoint?: number;
|
||||
whitePoint?: number;
|
||||
}
|
||||
|
||||
interface ThemeColorObj {
|
||||
name: AppThemeColor;
|
||||
variant: AppThemeColorVariant;
|
||||
cssColor: string;
|
||||
}
|
||||
|
||||
export interface AppTheme {
|
||||
name: string;
|
||||
appearance: Appearance;
|
||||
layers: Partial<Record<AppThemeLayer, AppThemeLayerStyle>>;
|
||||
}
|
||||
|
||||
export function generateCSS(t: AppTheme): ThemeColorObj[] {
|
||||
const rootColors = t.layers.root?.colors;
|
||||
if (rootColors === undefined) return [];
|
||||
|
||||
const colors: ThemeColorObj[] = [];
|
||||
for (const color of colorNames) {
|
||||
const rawValue = rootColors[color];
|
||||
if (!rawValue) continue;
|
||||
colors.push(
|
||||
...generateColors(
|
||||
color,
|
||||
rawValue,
|
||||
t.appearance,
|
||||
t.layers.root?.blackPoint,
|
||||
t.layers.root?.whitePoint,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return colors;
|
||||
}
|
||||
|
||||
export function generateColors(
|
||||
name: AppThemeColor,
|
||||
color: string,
|
||||
appearance: Appearance,
|
||||
blackPoint = 0,
|
||||
whitePoint = 1,
|
||||
): ThemeColorObj[] {
|
||||
const colors = [];
|
||||
for (const variant of appThemeVariants) {
|
||||
colors.push({
|
||||
name,
|
||||
variant,
|
||||
cssColor: generateColorVariant(color, variant, appearance, blackPoint, whitePoint),
|
||||
});
|
||||
}
|
||||
return colors;
|
||||
}
|
||||
|
||||
const lightnessMap: Record<Appearance, Record<AppThemeColorVariant, number>> = {
|
||||
system: {
|
||||
// Not actually used
|
||||
0: 1,
|
||||
50: 1,
|
||||
100: 0.9,
|
||||
200: 0.7,
|
||||
300: 0.4,
|
||||
400: 0.2,
|
||||
500: 0,
|
||||
600: -0.2,
|
||||
700: -0.4,
|
||||
800: -0.6,
|
||||
900: -0.8,
|
||||
950: -0.9,
|
||||
1000: -1,
|
||||
},
|
||||
light: {
|
||||
0: 1,
|
||||
50: 1,
|
||||
100: 0.9,
|
||||
200: 0.7,
|
||||
300: 0.4,
|
||||
400: 0.2,
|
||||
500: 0,
|
||||
600: -0.2,
|
||||
700: -0.4,
|
||||
800: -0.6,
|
||||
900: -0.8,
|
||||
950: -0.9,
|
||||
1000: -1,
|
||||
},
|
||||
dark: {
|
||||
0: -1,
|
||||
50: -0.9,
|
||||
100: -0.8,
|
||||
200: -0.6,
|
||||
300: -0.4,
|
||||
400: -0.2,
|
||||
500: 0,
|
||||
600: 0.2,
|
||||
700: 0.4,
|
||||
800: 0.6,
|
||||
900: 0.8,
|
||||
950: 0.9,
|
||||
1000: 1,
|
||||
},
|
||||
};
|
||||
|
||||
export function generateColorVariant(
|
||||
color: string,
|
||||
variant: AppThemeColorVariant,
|
||||
appearance: Appearance,
|
||||
blackPoint = 0,
|
||||
whitePoint = 1,
|
||||
): string {
|
||||
const { hsl } = parseColor(color || '');
|
||||
const lightnessMod = lightnessMap[appearance][variant];
|
||||
// const lightnessMod = (appearance === 'dark' ? 1 : -1) * ((variant / 1000) * 2 - 1);
|
||||
const newL =
|
||||
lightnessMod > 0
|
||||
? hsl[2] + (100 * whitePoint - hsl[2]) * lightnessMod
|
||||
: hsl[2] + hsl[2] * (1 - blackPoint) * lightnessMod;
|
||||
return `hsl(${hsl[0]},${hsl[1]}%,${newL.toFixed(1)}%)`;
|
||||
}
|
||||
|
||||
export function toTailwindVariable({ name, variant, cssColor }: ThemeColorObj): string {
|
||||
const { hsl } = parseColor(cssColor || '');
|
||||
return `--color-${name}-${variant}: ${hsl[0]} ${hsl[1]}% ${hsl[2]}%;`;
|
||||
}
|
||||
22
src-web/lib/theme/themes.ts
Normal file
22
src-web/lib/theme/themes.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { catppuccin } from './themes/catppuccin';
|
||||
import { dracula } from './themes/dracula';
|
||||
import { github } from './themes/github';
|
||||
import { hotdogStand } from './themes/hotdog-stand';
|
||||
import { monokaiPro } from './themes/monokai-pro';
|
||||
import { moonlight } from './themes/moonlight';
|
||||
import { rosePine } from './themes/rose-pine';
|
||||
import { yaak, yaakDark, yaakLight } from './themes/yaak';
|
||||
|
||||
export const defaultDarkTheme = yaakDark;
|
||||
export const defaultLightTheme = yaakLight;
|
||||
|
||||
export const yaakThemes = [
|
||||
...yaak,
|
||||
...catppuccin,
|
||||
...rosePine,
|
||||
...monokaiPro,
|
||||
...github,
|
||||
...moonlight,
|
||||
...dracula,
|
||||
...hotdogStand,
|
||||
];
|
||||
172
src-web/lib/theme/themes/catppuccin.ts
Normal file
172
src-web/lib/theme/themes/catppuccin.ts
Normal file
@@ -0,0 +1,172 @@
|
||||
import { Color } from '../color';
|
||||
import type { YaakTheme } from '../window';
|
||||
|
||||
export const catppuccinLatte: YaakTheme = {
|
||||
name: 'Catppuccin Latte',
|
||||
id: 'catppuccin-latte',
|
||||
background: new Color('#eff1f5', 'light'),
|
||||
foreground: new Color('#4c4f69', 'dark'),
|
||||
foregroundSubtle: new Color('#6c6f85', 'light'),
|
||||
foregroundSubtler: new Color('#8c8fa1', 'light'),
|
||||
colors: {
|
||||
primary: new Color('#8839ef', 'light'),
|
||||
secondary: new Color('#6c6f85', 'light'),
|
||||
info: new Color('#7287fd', 'light'),
|
||||
success: new Color('#179299', 'light'),
|
||||
notice: new Color('#df8e1d', 'light'),
|
||||
warning: new Color('#fe640b', 'light'),
|
||||
danger: new Color('#e64553', 'light'),
|
||||
},
|
||||
components: {
|
||||
sidebar: {
|
||||
background: new Color('#e6e9ef', 'light'),
|
||||
backgroundHighlight: new Color('#e6e9ef', 'light').lift(0.05),
|
||||
foregroundSubtler: new Color('#7287fd', 'light'),
|
||||
},
|
||||
appHeader: {
|
||||
background: new Color('#dce0e8', 'light'),
|
||||
backgroundHighlight: new Color('#e6e9ef', 'light').lift(0.05),
|
||||
foregroundSubtler: new Color('#7287fd', 'light'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const catppuccinMacchiato: YaakTheme = {
|
||||
name: 'Catppuccin Macchiato',
|
||||
id: 'catppuccin-macchiato',
|
||||
background: new Color('#1e2030', 'dark'),
|
||||
foreground: new Color('#cad3f5', 'dark'),
|
||||
foregroundSubtle: new Color('#a5adcb', 'dark'),
|
||||
foregroundSubtler: new Color('#8087a2', 'dark'),
|
||||
colors: {
|
||||
primary: new Color('#c6a0f6', 'dark'),
|
||||
secondary: new Color('#b8c0e0', 'dark'),
|
||||
info: new Color('#8aadf4', 'dark'),
|
||||
success: new Color('#a6da95', 'dark'),
|
||||
notice: new Color('#eed49f', 'dark'),
|
||||
warning: new Color('#f5a97f', 'dark'),
|
||||
danger: new Color('#ed8796', 'dark'),
|
||||
},
|
||||
components: {
|
||||
dialog: {
|
||||
background: new Color('#181825', 'dark'),
|
||||
},
|
||||
sidebar: {
|
||||
background: new Color('#24273a', 'dark'),
|
||||
backgroundHighlight: new Color('#24273a', 'dark').lift(0.05),
|
||||
},
|
||||
appHeader: {
|
||||
background: new Color('#181926', 'dark'),
|
||||
backgroundHighlight: new Color('#181926', 'dark').lift(0.1),
|
||||
},
|
||||
responsePane: {
|
||||
background: new Color('#24273a', 'dark'),
|
||||
backgroundHighlight: new Color('#24273a', 'dark').lift(0.05),
|
||||
},
|
||||
button: {
|
||||
colors: {
|
||||
primary: new Color('#c6a0f6', 'dark').lower(0.1),
|
||||
secondary: new Color('#b8c0e0', 'dark').lower(0.1),
|
||||
info: new Color('#8aadf4', 'dark').lower(0.1),
|
||||
success: new Color('#a6da95', 'dark').lower(0.1),
|
||||
notice: new Color('#eed49f', 'dark').lower(0.1),
|
||||
warning: new Color('#f5a97f', 'dark').lower(0.1),
|
||||
danger: new Color('#ed8796', 'dark').lower(0.1),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const catppuccinFrappe: YaakTheme = {
|
||||
name: 'Catppuccin Frappé',
|
||||
id: 'catppuccin-frappe',
|
||||
background: new Color('#292c3c', 'dark'),
|
||||
foreground: new Color('#c6d0f5', 'dark'),
|
||||
foregroundSubtle: new Color('#a5adce', 'dark'),
|
||||
foregroundSubtler: new Color('#838ba7', 'dark'),
|
||||
colors: {
|
||||
primary: new Color('#ca9ee6', 'dark'),
|
||||
secondary: new Color('#b8c0e0', 'dark'),
|
||||
info: new Color('#8caaee', 'dark'),
|
||||
success: new Color('#a6d189', 'dark'),
|
||||
notice: new Color('#e5c890', 'dark'),
|
||||
warning: new Color('#ef9f76', 'dark'),
|
||||
danger: new Color('#e78284', 'dark'),
|
||||
},
|
||||
components: {
|
||||
dialog: {
|
||||
background: new Color('#181825', 'dark'),
|
||||
},
|
||||
sidebar: {
|
||||
background: new Color('#303446', 'dark'),
|
||||
backgroundHighlight: new Color('#303446', 'dark').lift(0.05),
|
||||
},
|
||||
appHeader: {
|
||||
background: new Color('#232634', 'dark'),
|
||||
backgroundHighlight: new Color('#232634', 'dark').lift(0.1),
|
||||
},
|
||||
responsePane: {
|
||||
background: new Color('#303446', 'dark'),
|
||||
backgroundHighlight: new Color('#303446', 'dark').lift(0.05),
|
||||
},
|
||||
button: {
|
||||
colors: {
|
||||
primary: new Color('#ca9ee6', 'dark').lower(0.1),
|
||||
secondary: new Color('#b8c0e0', 'dark').lower(0.1),
|
||||
info: new Color('#8caaee', 'dark').lower(0.1),
|
||||
success: new Color('#a6d189', 'dark').lower(0.1),
|
||||
notice: new Color('#e5c890', 'dark').lower(0.1),
|
||||
warning: new Color('#ef9f76', 'dark').lower(0.1),
|
||||
danger: new Color('#e78284', 'dark').lower(0.1),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const catppuccinMocha: YaakTheme = {
|
||||
name: 'Catppuccin Mocha',
|
||||
id: 'catppuccin-mocha',
|
||||
background: new Color('#181825', 'dark'),
|
||||
foreground: new Color('#cdd6f4', 'dark'),
|
||||
foregroundSubtle: new Color('#a6adc8', 'dark'),
|
||||
foregroundSubtler: new Color('#7f849c', 'dark'),
|
||||
colors: {
|
||||
primary: new Color('#c6a0f6', 'dark'),
|
||||
secondary: new Color('#bac2de', 'dark'),
|
||||
info: new Color('#89b4fa', 'dark'),
|
||||
success: new Color('#a6e3a1', 'dark'),
|
||||
notice: new Color('#f9e2af', 'dark'),
|
||||
warning: new Color('#fab387', 'dark'),
|
||||
danger: new Color('#f38ba8', 'dark'),
|
||||
},
|
||||
components: {
|
||||
dialog: {
|
||||
background: new Color('#181825', 'dark'),
|
||||
},
|
||||
sidebar: {
|
||||
background: new Color('#1e1e2e', 'dark'),
|
||||
backgroundHighlight: new Color('#1e1e2e', 'dark').lift(0.05),
|
||||
},
|
||||
appHeader: {
|
||||
background: new Color('#11111b', 'dark'),
|
||||
backgroundHighlight: new Color('#11111b', 'dark').lift(0.1),
|
||||
},
|
||||
responsePane: {
|
||||
background: new Color('#1e1e2e', 'dark'),
|
||||
backgroundHighlight: new Color('#1e1e2e', 'dark').lift(0.05),
|
||||
},
|
||||
button: {
|
||||
colors: {
|
||||
primary: new Color('#cba6f7', 'dark').lower(0.2).desaturate(0.2),
|
||||
secondary: new Color('#bac2de', 'dark').lower(0.2).desaturate(0.2),
|
||||
info: new Color('#89b4fa', 'dark').lower(0.2).desaturate(0.2),
|
||||
success: new Color('#a6e3a1', 'dark').lower(0.2).desaturate(0.2),
|
||||
notice: new Color('#f9e2af', 'dark').lower(0.2).desaturate(0.2),
|
||||
warning: new Color('#fab387', 'dark').lower(0.2).desaturate(0.2),
|
||||
danger: new Color('#f38ba8', 'dark').lower(0.2).desaturate(0.2),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const catppuccin = [catppuccinFrappe, catppuccinMacchiato, catppuccinMocha, catppuccinLatte];
|
||||
32
src-web/lib/theme/themes/dracula.ts
Normal file
32
src-web/lib/theme/themes/dracula.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Color } from '../color';
|
||||
import type { YaakTheme } from '../window';
|
||||
|
||||
const draculaDefault: YaakTheme = {
|
||||
id: 'dracula',
|
||||
name: 'Dracula',
|
||||
background: new Color('#282A36', 'dark'),
|
||||
backgroundHighlight: new Color('#343746', 'dark'),
|
||||
backgroundHighlightSecondary: new Color('#424450', 'dark'),
|
||||
foreground: new Color('#F8F8F2', 'dark'),
|
||||
foregroundSubtle: new Color('hsl(232,14%,65%)', 'dark'),
|
||||
foregroundSubtler: new Color('hsl(232,14%,50%)', 'dark'),
|
||||
colors: {
|
||||
primary: new Color('#BD93F9', 'dark'),
|
||||
secondary: new Color('#6272A4', 'dark'),
|
||||
info: new Color('#8BE9FD', 'dark'),
|
||||
success: new Color('#50FA7B', 'dark'),
|
||||
notice: new Color('#F1FA8C', 'dark'),
|
||||
warning: new Color('#FFB86C', 'dark'),
|
||||
danger: new Color('#FF5555', 'dark'),
|
||||
},
|
||||
components: {
|
||||
sidebar: {
|
||||
background: new Color('hsl(230,15%,24%)', 'dark'),
|
||||
},
|
||||
appHeader: {
|
||||
background: new Color('#21222C', 'dark'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const dracula = [draculaDefault];
|
||||
57
src-web/lib/theme/themes/github.ts
Normal file
57
src-web/lib/theme/themes/github.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { Color } from '../color';
|
||||
import type { YaakTheme } from '../window';
|
||||
|
||||
const githubDark: YaakTheme = {
|
||||
id: 'github-dark',
|
||||
name: 'GitHub Dark',
|
||||
background: new Color('#0d1218', 'dark'),
|
||||
backgroundHighlight: new Color('#171c23', 'dark'),
|
||||
backgroundHighlightSecondary: new Color('#1c2127', 'dark'),
|
||||
foreground: new Color('#dce3eb', 'dark'),
|
||||
foregroundSubtle: new Color('#88919b', 'dark'),
|
||||
foregroundSubtler: new Color('#6b727d', 'dark'),
|
||||
colors: {
|
||||
primary: new Color('#a579ef', 'dark').lift(0.1),
|
||||
secondary: new Color('#6b727d', 'dark').lift(0.1),
|
||||
info: new Color('#458def', 'dark').lift(0.1),
|
||||
success: new Color('#3eb24f', 'dark').lift(0.1),
|
||||
notice: new Color('#dca132', 'dark').lift(0.1),
|
||||
warning: new Color('#ec7934', 'dark').lift(0.1),
|
||||
danger: new Color('#ee5049', 'dark').lift(0.1),
|
||||
},
|
||||
components: {
|
||||
button: {
|
||||
colors: {
|
||||
primary: new Color('#a579ef', 'dark'),
|
||||
secondary: new Color('#6b727d', 'dark'),
|
||||
info: new Color('#458def', 'dark'),
|
||||
success: new Color('#3eb24f', 'dark'),
|
||||
notice: new Color('#dca132', 'dark'),
|
||||
warning: new Color('#ec7934', 'dark'),
|
||||
danger: new Color('#ee5049', 'dark'),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const githubLight: YaakTheme = {
|
||||
id: 'github-light',
|
||||
name: 'GitHub Light',
|
||||
background: new Color('#ffffff', 'light'),
|
||||
backgroundHighlight: new Color('hsl(210,15%,92%)', 'light'),
|
||||
backgroundHighlightSecondary: new Color('hsl(210,29%,94%)', 'light'),
|
||||
foreground: new Color('#1f2328', 'light'),
|
||||
foregroundSubtle: new Color('#636c76', 'light'),
|
||||
foregroundSubtler: new Color('#828d94', 'light'),
|
||||
colors: {
|
||||
primary: new Color('#8250df', 'light'),
|
||||
secondary: new Color('#6e7781', 'light'),
|
||||
info: new Color('hsl(212,92%,48%)', 'light'),
|
||||
success: new Color('hsl(137,66%,32%)', 'light'),
|
||||
notice: new Color('hsl(40,100%,40%)', 'light'),
|
||||
warning: new Color('hsl(24,100%,44%)', 'light'),
|
||||
danger: new Color('#d1242f', 'light'),
|
||||
},
|
||||
};
|
||||
|
||||
export const github = [githubDark, githubLight];
|
||||
61
src-web/lib/theme/themes/hotdog-stand.ts
Normal file
61
src-web/lib/theme/themes/hotdog-stand.ts
Normal file
@@ -0,0 +1,61 @@
|
||||
import { Color } from '../color';
|
||||
import type { YaakTheme } from '../window';
|
||||
|
||||
export const hotdogStandDefault: YaakTheme = {
|
||||
id: 'hotdog-stand',
|
||||
name: 'Hotdog Stand',
|
||||
background: new Color('#ff0000', 'dark'),
|
||||
backgroundHighlight: new Color('#000000', 'dark'),
|
||||
backgroundHighlightSecondary: new Color('#000000', 'dark'),
|
||||
foreground: new Color('#ffffff', 'dark'),
|
||||
foregroundSubtle: new Color('#ffffff', 'dark'),
|
||||
foregroundSubtler: new Color('#ffff00', 'dark'),
|
||||
colors: {
|
||||
primary: new Color('#ffff00', 'dark'),
|
||||
secondary: new Color('#ffff00', 'dark'),
|
||||
info: new Color('#ffff00', 'dark'),
|
||||
notice: new Color('#ffff00', 'dark'),
|
||||
warning: new Color('#ffff00', 'dark'),
|
||||
danger: new Color('#ffff00', 'dark'),
|
||||
},
|
||||
components: {
|
||||
appHeader: {
|
||||
background: new Color('#000000', 'dark'),
|
||||
foreground: new Color('#ffffff', 'dark'),
|
||||
foregroundSubtle: new Color('#ffff00', 'dark'),
|
||||
foregroundSubtler: new Color('#ff0000', 'dark'),
|
||||
},
|
||||
menu: {
|
||||
background: new Color('#000000', 'dark'),
|
||||
backgroundHighlight: new Color('#ff0000', 'dark'),
|
||||
backgroundHighlightSecondary: new Color('#ff0000', 'dark'),
|
||||
foreground: new Color('#ffffff', 'dark'),
|
||||
foregroundSubtle: new Color('#ffff00', 'dark'),
|
||||
foregroundSubtler: new Color('#ffff00', 'dark'),
|
||||
},
|
||||
button: {
|
||||
background: new Color('#000000', 'dark'),
|
||||
foreground: new Color('#ffffff', 'dark'),
|
||||
colors: {
|
||||
primary: new Color('#000000', 'dark'),
|
||||
secondary: new Color('#ffffff', 'dark'),
|
||||
info: new Color('#000000', 'dark'),
|
||||
notice: new Color('#ffff00', 'dark'),
|
||||
warning: new Color('#000000', 'dark'),
|
||||
danger: new Color('#ff0000', 'dark'),
|
||||
},
|
||||
},
|
||||
editor: {
|
||||
colors: {
|
||||
primary: new Color('#ffffff', 'dark'),
|
||||
secondary: new Color('#ffffff', 'dark'),
|
||||
info: new Color('#ffffff', 'dark'),
|
||||
notice: new Color('#ffff00', 'dark'),
|
||||
warning: new Color('#ffffff', 'dark'),
|
||||
danger: new Color('#ffffff', 'dark'),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const hotdogStand = [hotdogStandDefault];
|
||||
245
src-web/lib/theme/themes/monokai-pro.ts
Normal file
245
src-web/lib/theme/themes/monokai-pro.ts
Normal file
@@ -0,0 +1,245 @@
|
||||
import { Color } from '../color';
|
||||
import type { YaakTheme } from '../window';
|
||||
|
||||
export const monokaiProDefault: YaakTheme = {
|
||||
id: 'monokai-pro',
|
||||
name: 'Monokai Pro',
|
||||
background: new Color('#2d2a2e', 'dark'),
|
||||
foreground: new Color('#fcfcfa', 'dark'),
|
||||
foregroundSubtle: new Color('#c1c0c0', 'dark'),
|
||||
foregroundSubtler: new Color('#939293', 'dark'),
|
||||
|
||||
colors: {
|
||||
primary: new Color('#ab9df2', 'dark'),
|
||||
secondary: new Color('#c1c0c0', 'dark'),
|
||||
info: new Color('#78dce8', 'dark'),
|
||||
success: new Color('#a9dc76', 'dark'),
|
||||
notice: new Color('#ffd866', 'dark'),
|
||||
warning: new Color('#fc9867', 'dark'),
|
||||
danger: new Color('#ff6188', 'dark'),
|
||||
},
|
||||
|
||||
components: {
|
||||
appHeader: {
|
||||
background: new Color('#221f22', 'dark'),
|
||||
foreground: new Color('#c1c0c0', 'dark'),
|
||||
foregroundSubtle: new Color('#939293', 'dark'),
|
||||
foregroundSubtler: new Color('#727072', 'dark'),
|
||||
},
|
||||
button: {
|
||||
colors: {
|
||||
primary: new Color('#ab9df2', 'dark').lower(0.1),
|
||||
secondary: new Color('#c1c0c0', 'dark').lower(0.1),
|
||||
info: new Color('#78dce8', 'dark').lower(0.1),
|
||||
success: new Color('#a9dc76', 'dark').lower(0.1),
|
||||
notice: new Color('#ffd866', 'dark').lower(0.1),
|
||||
warning: new Color('#fc9867', 'dark').lower(0.1),
|
||||
danger: new Color('#ff6188', 'dark').lower(0.1),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const monokaiProClassic: YaakTheme = {
|
||||
id: 'monokai-pro-classic',
|
||||
name: 'Monokai Pro Classic',
|
||||
background: new Color('#272822', 'dark'),
|
||||
foreground: new Color('#fdfff1', 'dark'),
|
||||
foregroundSubtle: new Color('#c0c1b5', 'dark'),
|
||||
foregroundSubtler: new Color('#919288', 'dark'),
|
||||
|
||||
colors: {
|
||||
primary: new Color('#ae81ff', 'dark'),
|
||||
secondary: new Color('#b2b9bd', 'dark'),
|
||||
info: new Color('#66d9ef', 'dark'),
|
||||
success: new Color('#a6e22e', 'dark'),
|
||||
notice: new Color('#e6db74', 'dark'),
|
||||
warning: new Color('#fd971f', 'dark'),
|
||||
danger: new Color('#f92672', 'dark'),
|
||||
},
|
||||
|
||||
components: {
|
||||
appHeader: {
|
||||
background: new Color('#1d1e19', 'dark'),
|
||||
foreground: new Color('#b2b9bd', 'dark'),
|
||||
foregroundSubtle: new Color('#767b81', 'dark'),
|
||||
foregroundSubtler: new Color('#696d77', 'dark'),
|
||||
},
|
||||
button: {
|
||||
colors: {
|
||||
primary: new Color('#ae81ff', 'dark').lower(0.1),
|
||||
secondary: new Color('#b2b9bd', 'dark').lower(0.1),
|
||||
info: new Color('#66d9ef', 'dark').lower(0.1),
|
||||
success: new Color('#a6e22e', 'dark').lower(0.1),
|
||||
notice: new Color('#e6db74', 'dark').lower(0.1),
|
||||
warning: new Color('#fd971f', 'dark').lower(0.1),
|
||||
danger: new Color('#f92672', 'dark').lower(0.1),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const monokaiProMachine: YaakTheme = {
|
||||
id: 'monokai-pro-machine',
|
||||
name: 'Monokai Pro Machine',
|
||||
background: new Color('#273136', 'dark'),
|
||||
foreground: new Color('#eaf2f1', 'dark'),
|
||||
foregroundSubtle: new Color('#8b9798', 'dark'),
|
||||
foregroundSubtler: new Color('#6b7678', 'dark'),
|
||||
|
||||
colors: {
|
||||
primary: new Color('#baa0f8', 'dark'),
|
||||
secondary: new Color('#b8c4c3', 'dark'),
|
||||
info: new Color('#7cd5f1', 'dark'),
|
||||
success: new Color('#a2e57b', 'dark'),
|
||||
notice: new Color('#ffed72', 'dark'),
|
||||
warning: new Color('#ffb270', 'dark'),
|
||||
danger: new Color('#ff6d7e', 'dark'),
|
||||
},
|
||||
|
||||
components: {
|
||||
appHeader: {
|
||||
background: new Color('#1d2528', 'dark'),
|
||||
foreground: new Color('#b2b9bd', 'dark'),
|
||||
foregroundSubtle: new Color('#767b81', 'dark'),
|
||||
foregroundSubtler: new Color('#696d77', 'dark'),
|
||||
},
|
||||
button: {
|
||||
colors: {
|
||||
primary: new Color('#baa0f8', 'dark').lower(0.1),
|
||||
secondary: new Color('#b8c4c3', 'dark').lower(0.1),
|
||||
info: new Color('#7cd5f1', 'dark').lower(0.1),
|
||||
success: new Color('#a2e57b', 'dark').lower(0.1),
|
||||
notice: new Color('#ffed72', 'dark').lower(0.1),
|
||||
warning: new Color('#ffb270', 'dark').lower(0.1),
|
||||
danger: new Color('#ff6d7e', 'dark').lower(0.1),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const monokaiProOctagon: YaakTheme = {
|
||||
id: 'monokai-pro-octagon',
|
||||
name: 'Monokai Pro Octagon',
|
||||
background: new Color('#282a3a', 'dark'),
|
||||
foreground: new Color('#eaf2f1', 'dark'),
|
||||
foregroundSubtle: new Color('#b2b9bd', 'dark'),
|
||||
foregroundSubtler: new Color('#767b81', 'dark'),
|
||||
|
||||
colors: {
|
||||
primary: new Color('#c39ac9', 'dark'),
|
||||
secondary: new Color('#b2b9bd', 'dark'),
|
||||
info: new Color('#9cd1bb', 'dark'),
|
||||
success: new Color('#bad761', 'dark'),
|
||||
notice: new Color('#ffd76d', 'dark'),
|
||||
warning: new Color('#ff9b5e', 'dark'),
|
||||
danger: new Color('#ff657a', 'dark'),
|
||||
},
|
||||
|
||||
components: {
|
||||
appHeader: {
|
||||
background: new Color('#1e1f2b', 'dark'),
|
||||
foreground: new Color('#b2b9bd', 'dark'),
|
||||
foregroundSubtle: new Color('#767b81', 'dark'),
|
||||
foregroundSubtler: new Color('#696d77', 'dark'),
|
||||
},
|
||||
button: {
|
||||
colors: {
|
||||
primary: new Color('#c39ac9', 'dark').lower(0.1).desaturate(0.1),
|
||||
secondary: new Color('#b2b9bd', 'dark').lower(0.1).desaturate(0.1),
|
||||
info: new Color('#9cd1bb', 'dark').lower(0.1).desaturate(0.1),
|
||||
success: new Color('#bad761', 'dark').lower(0.1).desaturate(0.1),
|
||||
notice: new Color('#ffd76d', 'dark').lower(0.1).desaturate(0.1),
|
||||
warning: new Color('#ff9b5e', 'dark').lower(0.1).desaturate(0.1),
|
||||
danger: new Color('#ff657a', 'dark').lower(0.1).desaturate(0.1),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const monokaiProRistretto: YaakTheme = {
|
||||
id: 'monokai-pro-ristretto',
|
||||
name: 'Monokai Pro Ristretto',
|
||||
background: new Color('#2c2525', 'dark'),
|
||||
foreground: new Color('#fff1f3', 'dark'),
|
||||
foregroundSubtle: new Color('#c3b7b8', 'dark'),
|
||||
foregroundSubtler: new Color('#948a8b', 'dark'),
|
||||
|
||||
colors: {
|
||||
primary: new Color('#a8a9eb', 'dark'),
|
||||
secondary: new Color('#c3b7b8', 'dark'),
|
||||
info: new Color('#85dacc', 'dark'),
|
||||
success: new Color('#adda78', 'dark'),
|
||||
notice: new Color('#f9cc6c', 'dark'),
|
||||
warning: new Color('#f38d70', 'dark'),
|
||||
danger: new Color('#fd6883', 'dark'),
|
||||
},
|
||||
|
||||
components: {
|
||||
appHeader: {
|
||||
background: new Color('#211c1c', 'dark'),
|
||||
foreground: new Color('#c3b7b8', 'dark'),
|
||||
foregroundSubtle: new Color('#948a8b', 'dark'),
|
||||
foregroundSubtler: new Color('#72696a', 'dark'),
|
||||
},
|
||||
button: {
|
||||
colors: {
|
||||
primary: new Color('#a8a9eb', 'dark').lower(0.1),
|
||||
secondary: new Color('#c3b7b8', 'dark').lower(0.1),
|
||||
info: new Color('#85dacc', 'dark').lower(0.1),
|
||||
success: new Color('#adda78', 'dark').lower(0.1),
|
||||
notice: new Color('#f9cc6c', 'dark').lower(0.1),
|
||||
warning: new Color('#f38d70', 'dark').lower(0.1),
|
||||
danger: new Color('#fd6883', 'dark').lower(0.1),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const monokaiProSpectrum: YaakTheme = {
|
||||
id: 'monokai-pro-spectrum',
|
||||
name: 'Monokai Pro Spectrum',
|
||||
background: new Color('#222222', 'dark'),
|
||||
foreground: new Color('#f7f1ff', 'dark'),
|
||||
foregroundSubtle: new Color('#bab6c0', 'dark'),
|
||||
foregroundSubtler: new Color('#8b888f', 'dark'),
|
||||
|
||||
colors: {
|
||||
primary: new Color('#948ae3', 'dark'),
|
||||
secondary: new Color('#bab6c0', 'dark'),
|
||||
info: new Color('#5ad4e6', 'dark'),
|
||||
success: new Color('#7bd88f', 'dark'),
|
||||
notice: new Color('#fce566', 'dark'),
|
||||
warning: new Color('#fd9353', 'dark'),
|
||||
danger: new Color('#fc618d', 'dark'),
|
||||
},
|
||||
|
||||
components: {
|
||||
appHeader: {
|
||||
background: new Color('#191919', 'dark'),
|
||||
foreground: new Color('#bab6c0', 'dark'),
|
||||
foregroundSubtle: new Color('#8b888f', 'dark'),
|
||||
foregroundSubtler: new Color('#69676c', 'dark'),
|
||||
},
|
||||
button: {
|
||||
colors: {
|
||||
primary: new Color('#948ae3', 'dark').lower(0.1),
|
||||
secondary: new Color('#bab6c0', 'dark').lower(0.1),
|
||||
info: new Color('#5ad4e6', 'dark').lower(0.1),
|
||||
success: new Color('#7bd88f', 'dark').lower(0.1),
|
||||
notice: new Color('#fce566', 'dark').lower(0.1),
|
||||
warning: new Color('#fd9353', 'dark').lower(0.1),
|
||||
danger: new Color('#fc618d', 'dark').lower(0.1),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const monokaiPro = [
|
||||
monokaiProDefault,
|
||||
monokaiProClassic,
|
||||
monokaiProMachine,
|
||||
monokaiProOctagon,
|
||||
monokaiProRistretto,
|
||||
monokaiProSpectrum,
|
||||
];
|
||||
66
src-web/lib/theme/themes/moonlight.ts
Normal file
66
src-web/lib/theme/themes/moonlight.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import { Color } from '../color';
|
||||
import type { YaakTheme } from '../window';
|
||||
|
||||
export const colors = {
|
||||
lightRed: '#ff98a4',
|
||||
red: '#ff757f',
|
||||
darkRed: '#ff5370',
|
||||
lightOrange: '#f8b576',
|
||||
orange: '#ff966c',
|
||||
darkOrange: '#fc7b7b',
|
||||
yellow: '#ffc777',
|
||||
green: '#c3e88d',
|
||||
lightTeal: '#7af8ca',
|
||||
teal: '#3ad7c7',
|
||||
lightCyan: '#b4f9f8',
|
||||
cyan: '#78dbff',
|
||||
sky: '#60bdff',
|
||||
blue: '#7cafff',
|
||||
darkBlue: '#3d6fe0',
|
||||
darkestBlue: '#3b63cf',
|
||||
indigo: '#af9fff',
|
||||
purple: '#c4a2ff',
|
||||
pink: '#fca7ea',
|
||||
darkPink: '#fd8aca',
|
||||
saturatedGray: '#7a88cf',
|
||||
desaturatedGray: '#979bb6',
|
||||
gray11: '#d5def8',
|
||||
gray10: '#c8d3f5',
|
||||
gray9: '#b4c2f0',
|
||||
gray8: '#a9b8e8',
|
||||
gray7: '#828bb8',
|
||||
gray6: '#444a73',
|
||||
gray5: '#2f334d',
|
||||
gray4: '#222436',
|
||||
gray3: '#1e2030',
|
||||
gray2: '#191a2a',
|
||||
gray1: '#131421',
|
||||
} as const;
|
||||
|
||||
const moonlightDefault: YaakTheme = {
|
||||
id: 'moonlight',
|
||||
name: 'Moonlight',
|
||||
background: new Color('#222436', 'dark'),
|
||||
foreground: new Color('#d5def8', 'dark'),
|
||||
foregroundSubtle: new Color('#828bb8', 'dark'),
|
||||
foregroundSubtler: new Color('hsl(232,26%,43%)', 'dark'),
|
||||
colors: {
|
||||
primary: new Color(colors.purple, 'dark'),
|
||||
secondary: new Color(colors.desaturatedGray, 'dark'),
|
||||
info: new Color(colors.blue, 'dark'),
|
||||
success: new Color(colors.teal, 'dark'),
|
||||
notice: new Color(colors.yellow, 'dark'),
|
||||
warning: new Color(colors.orange, 'dark'),
|
||||
danger: new Color(colors.red, 'dark'),
|
||||
},
|
||||
components: {
|
||||
appHeader: {
|
||||
background: new Color(colors.gray3, 'dark'),
|
||||
},
|
||||
sidebar: {
|
||||
background: new Color(colors.gray3, 'dark'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const moonlight = [moonlightDefault];
|
||||
108
src-web/lib/theme/themes/rose-pine.ts
Normal file
108
src-web/lib/theme/themes/rose-pine.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { Color } from '../color';
|
||||
import type { YaakTheme } from '../window';
|
||||
|
||||
export const rosePineDefault: YaakTheme = {
|
||||
id: 'rose-pine',
|
||||
name: 'Rosé Pine',
|
||||
background: new Color('#191724', 'dark'),
|
||||
foreground: new Color('#e0def4', 'dark'),
|
||||
foregroundSubtle: new Color('#908caa', 'dark'),
|
||||
foregroundSubtler: new Color('#6e6a86', 'dark'),
|
||||
colors: {
|
||||
primary: new Color('#c4a7e7', 'dark'),
|
||||
secondary: new Color('#6e6a86', 'dark'),
|
||||
info: new Color('#67abcb', 'dark'),
|
||||
success: new Color('#9cd8d8', 'dark'),
|
||||
notice: new Color('#f6c177', 'dark'),
|
||||
warning: new Color('#f1a3a1', 'dark'),
|
||||
danger: new Color('#eb6f92', 'dark'),
|
||||
},
|
||||
components: {
|
||||
responsePane: {
|
||||
background: new Color('#1f1d2e', 'dark'),
|
||||
},
|
||||
sidebar: {
|
||||
background: new Color('#1f1d2e', 'dark'),
|
||||
},
|
||||
menu: {
|
||||
background: new Color('#393552', 'dark'),
|
||||
foregroundSubtle: new Color('#908caa', 'dark').lift(0.15),
|
||||
foregroundSubtler: new Color('#6e6a86', 'dark').lift(0.15),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const rosePineMoon: YaakTheme = {
|
||||
id: 'rose-pine-moon',
|
||||
name: 'Rosé Pine Moon',
|
||||
background: new Color('#232136', 'dark'),
|
||||
foreground: new Color('#e0def4', 'dark'),
|
||||
foregroundSubtle: new Color('#908caa', 'dark'),
|
||||
foregroundSubtler: new Color('#6e6a86', 'dark'),
|
||||
colors: {
|
||||
primary: new Color('#c4a7e7', 'dark'),
|
||||
secondary: new Color('#908caa', 'dark'),
|
||||
info: new Color('#68aeca', 'dark'),
|
||||
success: new Color('#9ccfd8', 'dark'),
|
||||
notice: new Color('#f6c177', 'dark'),
|
||||
warning: new Color('#ea9a97', 'dark'),
|
||||
danger: new Color('#eb6f92', 'dark'),
|
||||
},
|
||||
components: {
|
||||
responsePane: {
|
||||
background: new Color('#2a273f', 'dark'),
|
||||
},
|
||||
sidebar: {
|
||||
background: new Color('#2a273f', 'dark'),
|
||||
},
|
||||
menu: {
|
||||
background: new Color('#393552', 'dark'),
|
||||
foregroundSubtle: new Color('#908caa', 'dark').lift(0.15),
|
||||
foregroundSubtler: new Color('#6e6a86', 'dark').lift(0.15),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const rosePineDawn: YaakTheme = {
|
||||
id: 'rose-pine-dawn',
|
||||
name: 'Rosé Pine Dawn',
|
||||
background: new Color('#faf4ed', 'light'),
|
||||
backgroundHighlight: new Color('#dfdad9', 'light'),
|
||||
backgroundHighlightSecondary: new Color('#f4ede8', 'light'),
|
||||
foreground: new Color('#575279', 'light'),
|
||||
foregroundSubtle: new Color('#797593', 'light'),
|
||||
foregroundSubtler: new Color('#9893a5', 'light'),
|
||||
colors: {
|
||||
primary: new Color('#9070ad', 'light'),
|
||||
secondary: new Color('#6e6a86', 'light'),
|
||||
info: new Color('#2d728d', 'light'),
|
||||
success: new Color('#4f8c96', 'light'),
|
||||
notice: new Color('#cb862d', 'light'),
|
||||
warning: new Color('#ce7b78', 'light'),
|
||||
danger: new Color('#b4637a', 'light'),
|
||||
},
|
||||
components: {
|
||||
responsePane: {
|
||||
backgroundHighlight: new Color('#e8e4e2', 'light'),
|
||||
},
|
||||
sidebar: {
|
||||
backgroundHighlight: new Color('#e8e4e2', 'light'),
|
||||
},
|
||||
appHeader: {
|
||||
backgroundHighlight: new Color('#e8e4e2', 'light'),
|
||||
},
|
||||
input: {
|
||||
backgroundHighlight: new Color('#dfdad9', 'light'),
|
||||
},
|
||||
dialog: {
|
||||
backgroundHighlight: new Color('#e8e4e2', 'light'),
|
||||
},
|
||||
menu: {
|
||||
background: new Color('#f2e9e1', 'light'),
|
||||
backgroundHighlight: new Color('#dfdad9', 'light'),
|
||||
backgroundHighlightSecondary: new Color('#6e6a86', 'light'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const rosePine = [rosePineDefault, rosePineDawn, rosePineMoon];
|
||||
84
src-web/lib/theme/themes/yaak.ts
Normal file
84
src-web/lib/theme/themes/yaak.ts
Normal file
@@ -0,0 +1,84 @@
|
||||
import { Color } from '../color';
|
||||
import type { YaakTheme } from '../window';
|
||||
|
||||
export const yaakLight: YaakTheme = {
|
||||
id: 'yaak-light',
|
||||
name: 'Yaak Light',
|
||||
background: new Color('hsl(216,24%,100%)', 'light'),
|
||||
backgroundHighlight: new Color('hsl(216,24%,93%)', 'light'),
|
||||
backgroundHighlightSecondary: new Color('hsl(216,24%,87%)', 'light'),
|
||||
foreground: new Color('hsl(219,23%,15%)', 'light'),
|
||||
foregroundSubtle: new Color('hsl(219,23%,15%)', 'light').lower(0.3),
|
||||
foregroundSubtler: new Color('hsl(219,23%,15%)', 'light').lower(0.5),
|
||||
colors: {
|
||||
primary: new Color('hsl(266,100%,70%)', 'light'),
|
||||
secondary: new Color('hsl(220,24%,59%)', 'light'),
|
||||
info: new Color('hsl(206,100%,48%)', 'light'),
|
||||
success: new Color('hsl(155,95%,33%)', 'light'),
|
||||
notice: new Color('hsl(45,100%,41%)', 'light'),
|
||||
warning: new Color('hsl(30,100%,43%)', 'light'),
|
||||
danger: new Color('hsl(335,75%,57%)', 'light'),
|
||||
},
|
||||
components: {
|
||||
sidebar: {
|
||||
background: new Color('hsl(216,24%,97%)', 'light'),
|
||||
backgroundHighlight: new Color('hsl(216,24%,93%)', 'light'),
|
||||
backgroundHighlightSecondary: new Color('hsl(216,24%,90%)', 'light'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const yaakDark: YaakTheme = {
|
||||
id: 'yaak-dark',
|
||||
name: 'Yaak Dark',
|
||||
background: new Color('hsl(244,23%,14%)', 'dark'),
|
||||
backgroundHighlight: new Color('hsl(244,23%,23%)', 'dark'),
|
||||
backgroundHighlightSecondary: new Color('hsl(244,23%,20%)', 'dark'),
|
||||
foreground: new Color('hsl(245,23%,83%)', 'dark'),
|
||||
foregroundSubtle: new Color('hsl(245,20%,65%)', 'dark'),
|
||||
foregroundSubtler: new Color('hsl(245,18%,51%)', 'dark'),
|
||||
|
||||
colors: {
|
||||
primary: new Color('hsl(266,100%,79%)', 'dark'),
|
||||
secondary: new Color('hsl(245,23%,60%)', 'dark'),
|
||||
info: new Color('hsl(206,100%,63%)', 'dark'),
|
||||
success: new Color('hsl(150,99%,44%)', 'dark'),
|
||||
notice: new Color('hsl(48,80%,63%)', 'dark'),
|
||||
warning: new Color('hsl(28,100%,61%)', 'dark'),
|
||||
danger: new Color('hsl(342,90%,68%)', 'dark'),
|
||||
},
|
||||
|
||||
components: {
|
||||
button: {
|
||||
colors: {
|
||||
primary: new Color('hsl(266,100%,79%)', 'dark').lower(0.1),
|
||||
secondary: new Color('hsl(245,23%,60%)', 'dark').lower(0.1),
|
||||
info: new Color('hsl(206,100%,63%)', 'dark').lower(0.1),
|
||||
success: new Color('hsl(150,99%,44%)', 'dark').lower(0.15),
|
||||
notice: new Color('hsl(48,80%,63%)', 'dark').lower(0.2),
|
||||
warning: new Color('hsl(28,100%,61%)', 'dark').lower(0.1),
|
||||
danger: new Color('hsl(342,90%,68%)', 'dark').lower(0.1),
|
||||
},
|
||||
},
|
||||
input: {
|
||||
backgroundHighlight: new Color('hsl(244,23%,24%)', 'dark'),
|
||||
},
|
||||
dialog: {
|
||||
backgroundHighlight: new Color('hsl(244,23%,24%)', 'dark'),
|
||||
},
|
||||
sidebar: {
|
||||
background: new Color('hsl(243,23%,16%)', 'dark'),
|
||||
backgroundHighlight: new Color('hsl(244,23%,22%)', 'dark'),
|
||||
},
|
||||
responsePane: {
|
||||
background: new Color('hsl(243,23%,16%)', 'dark'),
|
||||
backgroundHighlight: new Color('hsl(244,23%,16%)', 'dark').lift(0.08),
|
||||
},
|
||||
appHeader: {
|
||||
background: new Color('hsl(244,23%,12%)', 'dark'),
|
||||
backgroundHighlight: new Color('hsl(244,23%,12%)', 'dark').lift(0.1),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export const yaak = [yaakDark, yaakLight];
|
||||
@@ -1,104 +1,244 @@
|
||||
import type { AppTheme, AppThemeColors } from './theme';
|
||||
import { generateCSS, toTailwindVariable } from './theme';
|
||||
import { Color } from './color';
|
||||
|
||||
export type Appearance = 'dark' | 'light' | 'system';
|
||||
|
||||
const DEFAULT_APPEARANCE: Appearance = 'system';
|
||||
|
||||
enum Theme {
|
||||
yaak = 'yaak',
|
||||
catppuccin = 'catppuccin',
|
||||
interface ThemeComponent {
|
||||
background?: Color;
|
||||
backgroundHighlight?: Color;
|
||||
backgroundHighlightSecondary?: Color;
|
||||
backgroundActive?: Color;
|
||||
foreground?: Color;
|
||||
foregroundSubtle?: Color;
|
||||
foregroundSubtler?: Color;
|
||||
shadow?: Color;
|
||||
colors?: Partial<RootColors>;
|
||||
}
|
||||
|
||||
const themes: Record<Theme, AppThemeColors> = {
|
||||
yaak: {
|
||||
gray: 'hsl(245, 23%, 45%)',
|
||||
red: 'hsl(342,100%, 63%)',
|
||||
orange: 'hsl(32, 98%, 54%)',
|
||||
yellow: 'hsl(52, 79%, 58%)',
|
||||
green: 'hsl(136, 62%, 54%)',
|
||||
blue: 'hsl(206, 100%, 56%)',
|
||||
pink: 'hsl(300, 100%, 71%)',
|
||||
violet: 'hsl(266, 100%, 73%)',
|
||||
},
|
||||
catppuccin: {
|
||||
gray: 'hsl(240, 23%, 47%)',
|
||||
red: 'hsl(343, 91%, 74%)',
|
||||
orange: 'hsl(23, 92%, 74%)',
|
||||
yellow: 'hsl(41, 86%, 72%)',
|
||||
green: 'hsl(115, 54%, 65%)',
|
||||
blue: 'hsl(217, 92%, 65%)',
|
||||
pink: 'hsl(316, 72%, 75%)',
|
||||
violet: 'hsl(267, 84%, 70%)',
|
||||
},
|
||||
};
|
||||
export interface YaakTheme extends ThemeComponent {
|
||||
id: string;
|
||||
name: string;
|
||||
components?: {
|
||||
dialog?: ThemeComponent;
|
||||
menu?: ThemeComponent;
|
||||
toast?: ThemeComponent;
|
||||
sidebar?: ThemeComponent;
|
||||
responsePane?: ThemeComponent;
|
||||
appHeader?: ThemeComponent;
|
||||
button?: ThemeComponent;
|
||||
banner?: ThemeComponent;
|
||||
placeholder?: ThemeComponent;
|
||||
urlBar?: ThemeComponent;
|
||||
editor?: ThemeComponent;
|
||||
input?: ThemeComponent;
|
||||
};
|
||||
}
|
||||
|
||||
const darkTheme: AppTheme = {
|
||||
name: 'Default Dark',
|
||||
appearance: 'dark',
|
||||
layers: {
|
||||
root: {
|
||||
blackPoint: 0.2,
|
||||
colors: themes.yaak,
|
||||
},
|
||||
},
|
||||
};
|
||||
interface RootColors {
|
||||
primary: Color;
|
||||
secondary: Color;
|
||||
info: Color;
|
||||
success: Color;
|
||||
notice: Color;
|
||||
warning: Color;
|
||||
danger: Color;
|
||||
}
|
||||
|
||||
const lightTheme: AppTheme = {
|
||||
name: 'Default Light',
|
||||
appearance: 'light',
|
||||
layers: {
|
||||
root: {
|
||||
colors: {
|
||||
gray: '#7f8fb0',
|
||||
red: '#ec3f87',
|
||||
orange: '#ff8000',
|
||||
yellow: '#e7cf24',
|
||||
green: '#00d365',
|
||||
blue: '#0090ff',
|
||||
pink: '#ea6cea',
|
||||
violet: '#ac6cff',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
type ColorName = keyof RootColors;
|
||||
type ComponentName = keyof NonNullable<YaakTheme['components']>;
|
||||
|
||||
export function setAppearanceOnDocument(appearance: Appearance = DEFAULT_APPEARANCE) {
|
||||
const resolvedAppearance = appearance === 'system' ? getPreferredAppearance() : appearance;
|
||||
const theme = resolvedAppearance === 'dark' ? darkTheme : lightTheme;
|
||||
type CSSVariables = Record<string, string | undefined>;
|
||||
|
||||
document.documentElement.setAttribute('data-resolved-appearance', resolvedAppearance);
|
||||
document.documentElement.setAttribute('data-theme', theme.name);
|
||||
function themeVariables(theme?: ThemeComponent, base?: CSSVariables): CSSVariables | null {
|
||||
const vars: CSSVariables = {
|
||||
'--background': theme?.background?.css(),
|
||||
'--background-highlight':
|
||||
theme?.backgroundHighlight?.css() ?? theme?.background?.lift(0.11).css(),
|
||||
'--background-highlight-secondary':
|
||||
theme?.backgroundHighlightSecondary?.css() ?? theme?.background?.lift(0.06).css(),
|
||||
'--background-active':
|
||||
theme?.backgroundActive?.css() ?? theme?.colors?.primary?.lower(0.2).translucify(0.8).css(),
|
||||
'--background-backdrop': theme?.background?.lower(0.2).translucify(0.2).css(),
|
||||
'--background-selection': theme?.colors?.primary?.lower(0.1).translucify(0.7).css(),
|
||||
'--fg': theme?.foreground?.css(),
|
||||
'--fg-subtle': theme?.foregroundSubtle?.css() ?? theme?.foreground?.lower(0.2).css(),
|
||||
'--fg-subtler': theme?.foregroundSubtler?.css() ?? theme?.foreground?.lower(0.3).css(),
|
||||
'--border-focus': theme?.colors?.info?.css(),
|
||||
'--shadow':
|
||||
theme?.shadow?.css() ??
|
||||
Color.black()
|
||||
.translucify(isThemeDark(theme ?? {}) ? 0.7 : 0.93)
|
||||
.css(),
|
||||
};
|
||||
|
||||
let existingStyleEl = document.head.querySelector(`style[data-theme-definition]`);
|
||||
if (!existingStyleEl) {
|
||||
const styleEl = document.createElement('style');
|
||||
document.head.appendChild(styleEl);
|
||||
existingStyleEl = styleEl;
|
||||
for (const [color, value] of Object.entries(theme?.colors ?? {})) {
|
||||
vars[`--fg-${color}`] = (value as Color).css();
|
||||
}
|
||||
|
||||
existingStyleEl.textContent = [
|
||||
`/* ${darkTheme.name} */`,
|
||||
`[data-resolved-appearance="dark"] {`,
|
||||
...generateCSS(darkTheme).map(toTailwindVariable),
|
||||
'}',
|
||||
`/* ${lightTheme.name} */`,
|
||||
`[data-resolved-appearance="light"] {`,
|
||||
...generateCSS(lightTheme).map(toTailwindVariable),
|
||||
'}',
|
||||
].join('\n');
|
||||
existingStyleEl.setAttribute('data-theme-definition', '');
|
||||
// Extend with base
|
||||
for (const [k, v] of Object.entries(vars)) {
|
||||
if (!v && base?.[k]) {
|
||||
vars[k] = base[k];
|
||||
}
|
||||
}
|
||||
|
||||
return vars;
|
||||
}
|
||||
|
||||
export function getPreferredAppearance(): Appearance {
|
||||
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||
function placeholderColorVariables(color: Color): CSSVariables {
|
||||
return {
|
||||
'--fg': color.lift(0.6).css(),
|
||||
'--fg-subtle': color.lift(0.4).css(),
|
||||
'--fg-subtler': color.css(),
|
||||
'--background': color.lower(0.2).translucify(0.8).css(),
|
||||
'--background-highlight': color.lower(0.2).translucify(0.2).css(),
|
||||
'--background-highlight-secondary': color.lower(0.1).translucify(0.7).css(),
|
||||
};
|
||||
}
|
||||
|
||||
export function subscribeToPreferredAppearanceChange(
|
||||
cb: (appearance: Appearance) => void,
|
||||
): () => void {
|
||||
const listener = (e: MediaQueryListEvent) => cb(e.matches ? 'dark' : 'light');
|
||||
const m = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
m.addEventListener('change', listener);
|
||||
return () => m.removeEventListener('change', listener);
|
||||
function bannerColorVariables(color: Color): CSSVariables {
|
||||
return {
|
||||
'--fg': color.lift(0.8).css(),
|
||||
'--fg-subtle': color.translucify(0.3).css(),
|
||||
'--fg-subtler': color.css(),
|
||||
'--background': color.css(),
|
||||
'--background-highlight': color.lift(0.3).translucify(0.4).css(),
|
||||
'--background-highlight-secondary': color.translucify(0.9).css(),
|
||||
};
|
||||
}
|
||||
|
||||
function buttonSolidColorVariables(color: Color): CSSVariables {
|
||||
return {
|
||||
'--fg': new Color('white', 'dark').css(),
|
||||
'--background': color.lower(0.15).css(),
|
||||
'--background-highlight': color.css(),
|
||||
'--background-highlight-secondary': color.lower(0.3).css(),
|
||||
};
|
||||
}
|
||||
|
||||
function buttonBorderColorVariables(color: Color): CSSVariables {
|
||||
return {
|
||||
'--fg': color.lift(0.6).css(),
|
||||
'--fg-subtle': color.lift(0.4).css(),
|
||||
'--fg-subtler': color.lift(0.4).translucify(0.6).css(),
|
||||
'--background': Color.transparent().css(),
|
||||
'--background-highlight': color.translucify(0.8).css(),
|
||||
};
|
||||
}
|
||||
|
||||
function variablesToCSS(selector: string | null, vars: CSSVariables | null): string | null {
|
||||
if (vars == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const css = Object.entries(vars ?? {})
|
||||
.filter(([, value]) => value)
|
||||
.map(([name, value]) => `${name}: ${value};`)
|
||||
.join('\n');
|
||||
|
||||
return selector == null ? css : `${selector} {\n${indent(css)}\n}`;
|
||||
}
|
||||
|
||||
function componentCSS(
|
||||
component: ComponentName,
|
||||
components?: YaakTheme['components'],
|
||||
): string | null {
|
||||
if (components == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const themeVars = themeVariables(components[component]);
|
||||
return variablesToCSS(`.x-theme-${component}`, themeVars);
|
||||
}
|
||||
|
||||
function buttonCSS(color: ColorName, colors?: Partial<RootColors>): string | null {
|
||||
const cssColor = colors?.[color];
|
||||
if (cssColor == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
variablesToCSS(`.x-theme-button--solid--${color}`, buttonSolidColorVariables(cssColor)),
|
||||
variablesToCSS(`.x-theme-button--border--${color}`, buttonBorderColorVariables(cssColor)),
|
||||
].join('\n\n');
|
||||
}
|
||||
|
||||
function bannerCSS(color: ColorName, colors?: Partial<RootColors>): string | null {
|
||||
const cssColor = colors?.[color];
|
||||
if (cssColor == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [variablesToCSS(`.x-theme-banner--${color}`, bannerColorVariables(cssColor))].join('\n\n');
|
||||
}
|
||||
|
||||
function placeholderCSS(color: ColorName, colors?: Partial<RootColors>): string | null {
|
||||
const cssColor = colors?.[color];
|
||||
if (cssColor == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return [
|
||||
variablesToCSS(`.x-theme-placeholder--${color}`, placeholderColorVariables(cssColor)),
|
||||
].join('\n\n');
|
||||
}
|
||||
|
||||
export function isThemeDark(theme: ThemeComponent): boolean {
|
||||
if (theme.background && theme.foreground) {
|
||||
return theme.foreground.lighterThan(theme.background);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function getThemeCSS(theme: YaakTheme): string {
|
||||
theme.components = theme.components ?? {};
|
||||
// Toast defaults to menu styles
|
||||
theme.components.toast = theme.components.toast ?? theme.components.menu;
|
||||
|
||||
let themeCSS = '';
|
||||
try {
|
||||
const baseCss = variablesToCSS(null, themeVariables(theme));
|
||||
const { components, colors } = theme;
|
||||
themeCSS = [
|
||||
baseCss,
|
||||
...Object.keys(components ?? {}).map((key) =>
|
||||
componentCSS(key as ComponentName, theme.components),
|
||||
),
|
||||
...Object.keys(colors ?? {}).map((key) =>
|
||||
buttonCSS(key as ColorName, theme.components?.button?.colors ?? colors),
|
||||
),
|
||||
...Object.keys(colors ?? {}).map((key) =>
|
||||
bannerCSS(key as ColorName, theme.components?.banner?.colors ?? colors),
|
||||
),
|
||||
...Object.keys(colors ?? {}).map((key) =>
|
||||
placeholderCSS(key as ColorName, theme.components?.placeholder?.colors ?? colors),
|
||||
),
|
||||
].join('\n\n');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
|
||||
return [`/* ${theme.name} */`, `[data-theme="${theme.id}"] {`, indent(themeCSS), '}'].join('\n');
|
||||
}
|
||||
|
||||
export function addThemeStylesToDocument(theme: YaakTheme) {
|
||||
let styleEl = document.head.querySelector(`style[data-theme]`);
|
||||
if (!styleEl) {
|
||||
styleEl = document.createElement('style');
|
||||
document.head.appendChild(styleEl);
|
||||
}
|
||||
|
||||
styleEl.setAttribute('data-theme', theme.id);
|
||||
styleEl.textContent = getThemeCSS(theme);
|
||||
}
|
||||
|
||||
export function setThemeOnDocument(theme: YaakTheme) {
|
||||
document.documentElement.setAttribute('data-theme', theme.id);
|
||||
}
|
||||
|
||||
export function indent(text: string, space = ' '): string {
|
||||
return text
|
||||
.split('\n')
|
||||
.map((line) => space + line)
|
||||
.join('\n');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user