Merge branch 'main' into update-assistant-styles
This commit is contained in:
@@ -0,0 +1,291 @@
|
||||
import { Scale, Color } from "chroma-js"
|
||||
import { Syntax, ThemeSyntax, SyntaxHighlightStyle } from "./syntax"
|
||||
export { Syntax, ThemeSyntax, SyntaxHighlightStyle }
|
||||
import {
|
||||
ThemeConfig,
|
||||
ThemeAppearance,
|
||||
ThemeConfigInputColors,
|
||||
} from "./theme_config"
|
||||
import { get_ramps } from "./ramps"
|
||||
|
||||
export interface Theme {
|
||||
name: string
|
||||
is_light: boolean
|
||||
|
||||
/**
|
||||
* App background, other elements that should sit directly on top of the background.
|
||||
*/
|
||||
lowest: Layer
|
||||
/**
|
||||
* Panels, tabs, other UI surfaces that sit on top of the background.
|
||||
*/
|
||||
middle: Layer
|
||||
/**
|
||||
* Editors like code buffers, conversation editors, etc.
|
||||
*/
|
||||
highest: Layer
|
||||
|
||||
ramps: RampSet
|
||||
|
||||
popover_shadow: Shadow
|
||||
modal_shadow: Shadow
|
||||
|
||||
players: Players
|
||||
syntax?: Partial<ThemeSyntax>
|
||||
}
|
||||
|
||||
export interface Meta {
|
||||
name: string
|
||||
author: string
|
||||
url: string
|
||||
license: License
|
||||
}
|
||||
|
||||
export interface License {
|
||||
SPDX: SPDXExpression
|
||||
}
|
||||
|
||||
// License name -> License text
|
||||
export interface Licenses {
|
||||
[key: string]: string
|
||||
}
|
||||
|
||||
// FIXME: Add support for the SPDX expression syntax
|
||||
export type SPDXExpression = "MIT"
|
||||
|
||||
export interface Player {
|
||||
cursor: string
|
||||
selection: string
|
||||
}
|
||||
|
||||
export interface Players {
|
||||
"0": Player
|
||||
"1": Player
|
||||
"2": Player
|
||||
"3": Player
|
||||
"4": Player
|
||||
"5": Player
|
||||
"6": Player
|
||||
"7": Player
|
||||
}
|
||||
|
||||
export interface Shadow {
|
||||
blur: number
|
||||
color: string
|
||||
offset: number[]
|
||||
}
|
||||
|
||||
export type StyleSets = keyof Layer
|
||||
export interface Layer {
|
||||
base: StyleSet
|
||||
variant: StyleSet
|
||||
on: StyleSet
|
||||
accent: StyleSet
|
||||
positive: StyleSet
|
||||
warning: StyleSet
|
||||
negative: StyleSet
|
||||
}
|
||||
|
||||
export interface RampSet {
|
||||
neutral: Scale
|
||||
red: Scale
|
||||
orange: Scale
|
||||
yellow: Scale
|
||||
green: Scale
|
||||
cyan: Scale
|
||||
blue: Scale
|
||||
violet: Scale
|
||||
magenta: Scale
|
||||
}
|
||||
|
||||
export type Styles = keyof StyleSet
|
||||
export interface StyleSet {
|
||||
default: Style
|
||||
active: Style
|
||||
disabled: Style
|
||||
hovered: Style
|
||||
pressed: Style
|
||||
inverted: Style
|
||||
}
|
||||
|
||||
export interface Style {
|
||||
background: string
|
||||
border: string
|
||||
foreground: string
|
||||
}
|
||||
|
||||
export function create_theme(theme: ThemeConfig): Theme {
|
||||
const {
|
||||
name,
|
||||
appearance,
|
||||
input_color,
|
||||
override: { syntax },
|
||||
} = theme
|
||||
|
||||
const is_light = appearance === ThemeAppearance.Light
|
||||
const color_ramps: ThemeConfigInputColors = input_color
|
||||
|
||||
// Chromajs scales from 0 to 1 flipped if is_light is true
|
||||
const ramps = get_ramps(is_light, color_ramps)
|
||||
const lowest = lowest_layer(ramps)
|
||||
const middle = middle_layer(ramps)
|
||||
const highest = highest_layer(ramps)
|
||||
|
||||
const popover_shadow = {
|
||||
blur: 4,
|
||||
color: ramps
|
||||
.neutral(is_light ? 7 : 0)
|
||||
.darken()
|
||||
.alpha(0.2)
|
||||
.hex(), // TODO used blend previously. Replace with something else
|
||||
offset: [1, 2],
|
||||
}
|
||||
|
||||
const modal_shadow = {
|
||||
blur: 16,
|
||||
color: ramps
|
||||
.neutral(is_light ? 7 : 0)
|
||||
.darken()
|
||||
.alpha(0.2)
|
||||
.hex(), // TODO used blend previously. Replace with something else
|
||||
offset: [0, 2],
|
||||
}
|
||||
|
||||
const players = {
|
||||
"0": player(ramps.blue),
|
||||
"1": player(ramps.green),
|
||||
"2": player(ramps.magenta),
|
||||
"3": player(ramps.orange),
|
||||
"4": player(ramps.violet),
|
||||
"5": player(ramps.cyan),
|
||||
"6": player(ramps.red),
|
||||
"7": player(ramps.yellow),
|
||||
}
|
||||
|
||||
return {
|
||||
name,
|
||||
is_light,
|
||||
|
||||
ramps,
|
||||
|
||||
lowest,
|
||||
middle,
|
||||
highest,
|
||||
|
||||
popover_shadow,
|
||||
modal_shadow,
|
||||
|
||||
players,
|
||||
syntax,
|
||||
}
|
||||
}
|
||||
|
||||
function player(ramp: Scale): Player {
|
||||
return {
|
||||
selection: ramp(0.5).alpha(0.24).hex(),
|
||||
cursor: ramp(0.5).hex(),
|
||||
}
|
||||
}
|
||||
|
||||
function lowest_layer(ramps: RampSet): Layer {
|
||||
return {
|
||||
base: build_style_set(ramps.neutral, 0.2, 1),
|
||||
variant: build_style_set(ramps.neutral, 0.2, 0.7),
|
||||
on: build_style_set(ramps.neutral, 0.1, 1),
|
||||
accent: build_style_set(ramps.blue, 0.1, 0.5),
|
||||
positive: build_style_set(ramps.green, 0.1, 0.5),
|
||||
warning: build_style_set(ramps.yellow, 0.1, 0.5),
|
||||
negative: build_style_set(ramps.red, 0.1, 0.5),
|
||||
}
|
||||
}
|
||||
|
||||
function middle_layer(ramps: RampSet): Layer {
|
||||
return {
|
||||
base: build_style_set(ramps.neutral, 0.1, 1),
|
||||
variant: build_style_set(ramps.neutral, 0.1, 0.7),
|
||||
on: build_style_set(ramps.neutral, 0, 1),
|
||||
accent: build_style_set(ramps.blue, 0.1, 0.5),
|
||||
positive: build_style_set(ramps.green, 0.1, 0.5),
|
||||
warning: build_style_set(ramps.yellow, 0.1, 0.5),
|
||||
negative: build_style_set(ramps.red, 0.1, 0.5),
|
||||
}
|
||||
}
|
||||
|
||||
function highest_layer(ramps: RampSet): Layer {
|
||||
return {
|
||||
base: build_style_set(ramps.neutral, 0, 1),
|
||||
variant: build_style_set(ramps.neutral, 0, 0.7),
|
||||
on: build_style_set(ramps.neutral, 0.1, 1),
|
||||
accent: build_style_set(ramps.blue, 0.1, 0.5),
|
||||
positive: build_style_set(ramps.green, 0.1, 0.5),
|
||||
warning: build_style_set(ramps.yellow, 0.1, 0.5),
|
||||
negative: build_style_set(ramps.red, 0.1, 0.5),
|
||||
}
|
||||
}
|
||||
|
||||
function build_style_set(
|
||||
ramp: Scale,
|
||||
background_base: number,
|
||||
foreground_base: number,
|
||||
step = 0.08
|
||||
): StyleSet {
|
||||
const style_definitions = build_style_definition(
|
||||
background_base,
|
||||
foreground_base,
|
||||
step
|
||||
)
|
||||
|
||||
function color_string(index_or_color: number | Color): string {
|
||||
if (typeof index_or_color === "number") {
|
||||
return ramp(index_or_color).hex()
|
||||
} else {
|
||||
return index_or_color.hex()
|
||||
}
|
||||
}
|
||||
|
||||
function build_style(style: Styles): Style {
|
||||
return {
|
||||
background: color_string(style_definitions.background[style]),
|
||||
border: color_string(style_definitions.border[style]),
|
||||
foreground: color_string(style_definitions.foreground[style]),
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
default: build_style("default"),
|
||||
hovered: build_style("hovered"),
|
||||
pressed: build_style("pressed"),
|
||||
active: build_style("active"),
|
||||
disabled: build_style("disabled"),
|
||||
inverted: build_style("inverted"),
|
||||
}
|
||||
}
|
||||
|
||||
function build_style_definition(bg_base: number, fg_base: number, step = 0.08) {
|
||||
return {
|
||||
background: {
|
||||
default: bg_base,
|
||||
hovered: bg_base + step,
|
||||
pressed: bg_base + step * 1.5,
|
||||
active: bg_base + step * 2.2,
|
||||
disabled: bg_base,
|
||||
inverted: fg_base + step * 6,
|
||||
},
|
||||
border: {
|
||||
default: bg_base + step * 1,
|
||||
hovered: bg_base + step,
|
||||
pressed: bg_base + step,
|
||||
active: bg_base + step * 3,
|
||||
disabled: bg_base + step * 0.5,
|
||||
inverted: bg_base - step * 3,
|
||||
},
|
||||
foreground: {
|
||||
default: fg_base,
|
||||
hovered: fg_base,
|
||||
pressed: fg_base,
|
||||
active: fg_base + step * 6,
|
||||
disabled: bg_base + step * 4,
|
||||
inverted: bg_base + step * 2,
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user