color_scheme -> theme

This commit is contained in:
Nate Butler 2023-07-04 01:20:56 -04:00
parent c5a42c317a
commit 65dbb38926
15 changed files with 93 additions and 162 deletions

View File

@ -35,7 +35,7 @@ Match a property identifier and highlight it using the identifier `@property`. I
``` ```
```ts ```ts
function buildDefaultSyntax(colorScheme: ColorScheme): Partial<Syntax> { function buildDefaultSyntax(colorScheme: Theme): Partial<Syntax> {
// ... // ...
} }
``` ```

View File

@ -2,7 +2,7 @@ import * as fs from "fs"
import { tmpdir } from "os" import { tmpdir } from "os"
import * as path from "path" import * as path from "path"
import app from "./style_tree/app" import app from "./style_tree/app"
import { ColorScheme, create_color_scheme } from "./theme/color_scheme" import { Theme, create_theme } from "./theme/create_theme"
import { themes } from "./themes" import { themes } from "./themes"
import { useThemeStore } from "./theme" import { useThemeStore } from "./theme"
@ -21,22 +21,22 @@ function clear_themes(theme_directory: string) {
} }
} }
const all_themes: ColorScheme[] = themes.map((theme) => const all_themes: Theme[] = themes.map((theme) =>
create_color_scheme(theme) create_theme(theme)
) )
function write_themes(themes: ColorScheme[], output_directory: string) { function write_themes(themes: Theme[], output_directory: string) {
clear_themes(output_directory) clear_themes(output_directory)
for (const color_scheme of themes) { for (const theme of themes) {
const { setTheme } = useThemeStore.getState() const { setTheme } = useThemeStore.getState()
setTheme(color_scheme) setTheme(theme)
const style_tree = app() const style_tree = app()
const style_tree_json = JSON.stringify(style_tree, null, 2) const style_tree_json = JSON.stringify(style_tree, null, 2)
const temp_path = path.join(temp_directory, `${color_scheme.name}.json`) const temp_path = path.join(temp_directory, `${theme.name}.json`)
const out_path = path.join( const out_path = path.join(
output_directory, output_directory,
`${color_scheme.name}.json` `${theme.name}.json`
) )
fs.writeFileSync(temp_path, style_tree_json) fs.writeFileSync(temp_path, style_tree_json)
fs.renameSync(temp_path, out_path) fs.renameSync(temp_path, out_path)

View File

@ -1,9 +1,9 @@
import * as fs from "fs" import * as fs from "fs"
import * as path from "path" import * as path from "path"
import { ColorScheme, create_color_scheme } from "./common" import { Theme, create_theme, useThemeStore } from "./common"
import { themes } from "./themes" import { themes } from "./themes"
import { slugify } from "./utils/slugify" import { slugify } from "./utils/slugify"
import { theme_tokens } from "./theme/tokens/color_scheme" import { theme_tokens } from "./theme/tokens/theme"
const TOKENS_DIRECTORY = path.join(__dirname, "..", "target", "tokens") const TOKENS_DIRECTORY = path.join(__dirname, "..", "target", "tokens")
const TOKENS_FILE = path.join(TOKENS_DIRECTORY, "$themes.json") const TOKENS_FILE = path.join(TOKENS_DIRECTORY, "$themes.json")
@ -27,7 +27,7 @@ type TokenSet = {
selected_token_sets: { [key: string]: "enabled" } selected_token_sets: { [key: string]: "enabled" }
} }
function build_token_set_order(theme: ColorScheme[]): { function build_token_set_order(theme: Theme[]): {
token_set_order: string[] token_set_order: string[]
} { } {
const token_set_order: string[] = theme.map((scheme) => const token_set_order: string[] = theme.map((scheme) =>
@ -36,7 +36,7 @@ function build_token_set_order(theme: ColorScheme[]): {
return { token_set_order } return { token_set_order }
} }
function build_themes_index(theme: ColorScheme[]): TokenSet[] { function build_themes_index(theme: Theme[]): TokenSet[] {
const themes_index: TokenSet[] = theme.map((scheme, index) => { const themes_index: TokenSet[] = theme.map((scheme, index) => {
const id = `${scheme.is_light ? "light" : "dark"}_${scheme.name const id = `${scheme.is_light ? "light" : "dark"}_${scheme.name
.toLowerCase() .toLowerCase()
@ -55,10 +55,13 @@ function build_themes_index(theme: ColorScheme[]): TokenSet[] {
return themes_index return themes_index
} }
function write_tokens(themes: ColorScheme[], tokens_directory: string) { function write_tokens(themes: Theme[], tokens_directory: string) {
clear_tokens(tokens_directory) clear_tokens(tokens_directory)
for (const theme of themes) { for (const theme of themes) {
const { setTheme } = useThemeStore.getState()
setTheme(theme)
const file_name = slugify(theme.name) + ".json" const file_name = slugify(theme.name) + ".json"
const tokens = theme_tokens() const tokens = theme_tokens()
const tokens_json = JSON.stringify(tokens, null, 2) const tokens_json = JSON.stringify(tokens, null, 2)
@ -80,8 +83,8 @@ function write_tokens(themes: ColorScheme[], tokens_directory: string) {
console.log(`- ${METADATA_FILE} created`) console.log(`- ${METADATA_FILE} created`)
} }
const all_themes: ColorScheme[] = themes.map((theme) => const all_themes: Theme[] = themes.map((theme) =>
create_color_scheme(theme) create_theme(theme)
) )
write_tokens(all_themes, TOKENS_DIRECTORY) write_tokens(all_themes, TOKENS_DIRECTORY)

View File

@ -1,6 +1,6 @@
import { interactive, toggleable } from "../element" import { interactive, toggleable } from "../element"
import { background, foreground } from "../style_tree/components" import { background, foreground } from "../style_tree/components"
import { useTheme, ColorScheme } from "../theme" import { useTheme, Theme } from "../theme"
export type Margin = { export type Margin = {
top: number top: number
@ -11,15 +11,15 @@ export type Margin = {
interface IconButtonOptions { interface IconButtonOptions {
layer?: layer?:
| ColorScheme["lowest"] | Theme["lowest"]
| ColorScheme["middle"] | Theme["middle"]
| ColorScheme["highest"] | Theme["highest"]
color?: keyof ColorScheme["lowest"] color?: keyof Theme["lowest"]
margin?: Partial<Margin> margin?: Partial<Margin>
} }
type ToggleableIconButtonOptions = IconButtonOptions & { type ToggleableIconButtonOptions = IconButtonOptions & {
active_color?: keyof ColorScheme["lowest"] active_color?: keyof Theme["lowest"]
} }
export function icon_button({ color, margin, layer }: IconButtonOptions) { export function icon_button({ color, margin, layer }: IconButtonOptions) {
@ -67,7 +67,7 @@ export function icon_button({ color, margin, layer }: IconButtonOptions) {
} }
export function toggleable_icon_button( export function toggleable_icon_button(
theme: ColorScheme, theme: Theme,
{ color, active_color, margin }: ToggleableIconButtonOptions { color, active_color, margin }: ToggleableIconButtonOptions
) { ) {
if (!color) color = "base" if (!color) color = "base"

View File

@ -5,21 +5,21 @@ import {
foreground, foreground,
text, text,
} from "../style_tree/components" } from "../style_tree/components"
import { useTheme, ColorScheme } from "../theme" import { useTheme, Theme } from "../theme"
import { Margin } from "./icon_button" import { Margin } from "./icon_button"
interface TextButtonOptions { interface TextButtonOptions {
layer?: layer?:
| ColorScheme["lowest"] | Theme["lowest"]
| ColorScheme["middle"] | Theme["middle"]
| ColorScheme["highest"] | Theme["highest"]
color?: keyof ColorScheme["lowest"] color?: keyof Theme["lowest"]
margin?: Partial<Margin> margin?: Partial<Margin>
text_properties?: TextProperties text_properties?: TextProperties
} }
type ToggleableTextButtonOptions = TextButtonOptions & { type ToggleableTextButtonOptions = TextButtonOptions & {
active_color?: keyof ColorScheme["lowest"] active_color?: keyof Theme["lowest"]
} }
export function text_button({ export function text_button({
@ -75,7 +75,7 @@ export function text_button({
} }
export function toggleable_text_button( export function toggleable_text_button(
theme: ColorScheme, theme: Theme,
{ color, active_color, margin }: ToggleableTextButtonOptions { color, active_color, margin }: ToggleableTextButtonOptions
) { ) {
if (!color) color = "base" if (!color) color = "base"

View File

@ -1,5 +1,5 @@
import { font_families, font_sizes, FontWeight } from "../common" import { font_families, font_sizes, FontWeight } from "../common"
import { Layer, Styles, StyleSets, Style } from "../theme/color_scheme" import { Layer, Styles, StyleSets, Style } from "../theme/create_theme"
function is_style_set(key: any): key is StyleSets { function is_style_set(key: any): key is StyleSets {
return [ return [

View File

@ -1,5 +1,5 @@
import { with_opacity } from "../theme/color" import { with_opacity } from "../theme/color"
import { Layer, StyleSets } from "../theme/color_scheme" import { Layer, StyleSets } from "../theme/create_theme"
import { import {
background, background,
border, border,
@ -48,7 +48,7 @@ export default function editor(): any {
} }
} }
const syntax = build_syntax(theme) const syntax = build_syntax()
return { return {
text_color: syntax.primary.color, text_color: syntax.primary.color,

View File

@ -8,7 +8,7 @@ import {
} from "./theme_config" } from "./theme_config"
import { get_ramps } from "./ramps" import { get_ramps } from "./ramps"
export interface ColorScheme { export interface Theme {
name: string name: string
is_light: boolean is_light: boolean
@ -105,7 +105,7 @@ export interface Style {
foreground: string foreground: string
} }
export function create_color_scheme(theme: ThemeConfig): ColorScheme { export function create_theme(theme: ThemeConfig): Theme {
const { const {
name, name,
appearance, appearance,

View File

@ -1,9 +1,9 @@
import { create } from "zustand" import { create } from "zustand"
import { ColorScheme } from "./color_scheme" import { Theme } from "./create_theme"
type ThemeState = { type ThemeState = {
theme: ColorScheme | undefined theme: Theme | undefined
setTheme: (theme: ColorScheme) => void setTheme: (theme: Theme) => void
} }
export const useThemeStore = create<ThemeState>((set) => ({ export const useThemeStore = create<ThemeState>((set) => ({
@ -11,7 +11,7 @@ export const useThemeStore = create<ThemeState>((set) => ({
setTheme: (theme) => set(() => ({ theme })), setTheme: (theme) => set(() => ({ theme })),
})) }))
export const useTheme = (): ColorScheme => { export const useTheme = (): Theme => {
const { theme } = useThemeStore.getState() const { theme } = useThemeStore.getState()
if (!theme) throw new Error("Tried to use theme before it was loaded") if (!theme) throw new Error("Tried to use theme before it was loaded")
@ -19,7 +19,7 @@ export const useTheme = (): ColorScheme => {
return theme return theme
} }
export * from "./color_scheme" export * from "./create_theme"
export * from "./ramps" export * from "./ramps"
export * from "./syntax" export * from "./syntax"
export * from "./theme_config" export * from "./theme_config"

View File

@ -1,5 +1,5 @@
import chroma, { Color, Scale } from "chroma-js" import chroma, { Color, Scale } from "chroma-js"
import { RampSet } from "./color_scheme" import { RampSet } from "./create_theme"
import { import {
ThemeConfigInputColors, ThemeConfigInputColors,
ThemeConfigInputColorsKeys, ThemeConfigInputColorsKeys,

View File

@ -1,6 +1,5 @@
import deepmerge from "deepmerge" import deepmerge from "deepmerge"
import { FontWeight, font_weights } from "../common" import { FontWeight, font_weights, useTheme } from "../common"
import { ColorScheme } from "./color_scheme"
import chroma from "chroma-js" import chroma from "chroma-js"
export interface SyntaxHighlightStyle { export interface SyntaxHighlightStyle {
@ -123,7 +122,9 @@ const default_syntax_highlight_style: Omit<SyntaxHighlightStyle, "color"> = {
italic: false, italic: false,
} }
function build_default_syntax(color_scheme: ColorScheme): Syntax { function build_default_syntax(): Syntax {
const theme = useTheme()
// Make a temporary object that is allowed to be missing // Make a temporary object that is allowed to be missing
// the "color" property for each style // the "color" property for each style
const syntax: { const syntax: {
@ -141,8 +142,8 @@ function build_default_syntax(color_scheme: ColorScheme): Syntax {
// predictive color distinct from any other color in the theme // predictive color distinct from any other color in the theme
const predictive = chroma const predictive = chroma
.mix( .mix(
color_scheme.ramps.neutral(0.4).hex(), theme.ramps.neutral(0.4).hex(),
color_scheme.ramps.blue(0.4).hex(), theme.ramps.blue(0.4).hex(),
0.45, 0.45,
"lch" "lch"
) )
@ -151,32 +152,32 @@ function build_default_syntax(color_scheme: ColorScheme): Syntax {
// hint color distinct from any other color in the theme // hint color distinct from any other color in the theme
const hint = chroma const hint = chroma
.mix( .mix(
color_scheme.ramps.neutral(0.6).hex(), theme.ramps.neutral(0.6).hex(),
color_scheme.ramps.blue(0.4).hex(), theme.ramps.blue(0.4).hex(),
0.45, 0.45,
"lch" "lch"
) )
.hex() .hex()
const color = { const color = {
primary: color_scheme.ramps.neutral(1).hex(), primary: theme.ramps.neutral(1).hex(),
comment: color_scheme.ramps.neutral(0.71).hex(), comment: theme.ramps.neutral(0.71).hex(),
punctuation: color_scheme.ramps.neutral(0.86).hex(), punctuation: theme.ramps.neutral(0.86).hex(),
predictive: predictive, predictive: predictive,
hint: hint, hint: hint,
emphasis: color_scheme.ramps.blue(0.5).hex(), emphasis: theme.ramps.blue(0.5).hex(),
string: color_scheme.ramps.orange(0.5).hex(), string: theme.ramps.orange(0.5).hex(),
function: color_scheme.ramps.yellow(0.5).hex(), function: theme.ramps.yellow(0.5).hex(),
type: color_scheme.ramps.cyan(0.5).hex(), type: theme.ramps.cyan(0.5).hex(),
constructor: color_scheme.ramps.blue(0.5).hex(), constructor: theme.ramps.blue(0.5).hex(),
variant: color_scheme.ramps.blue(0.5).hex(), variant: theme.ramps.blue(0.5).hex(),
property: color_scheme.ramps.blue(0.5).hex(), property: theme.ramps.blue(0.5).hex(),
enum: color_scheme.ramps.orange(0.5).hex(), enum: theme.ramps.orange(0.5).hex(),
operator: color_scheme.ramps.orange(0.5).hex(), operator: theme.ramps.orange(0.5).hex(),
number: color_scheme.ramps.green(0.5).hex(), number: theme.ramps.green(0.5).hex(),
boolean: color_scheme.ramps.green(0.5).hex(), boolean: theme.ramps.green(0.5).hex(),
constant: color_scheme.ramps.green(0.5).hex(), constant: theme.ramps.green(0.5).hex(),
keyword: color_scheme.ramps.blue(0.5).hex(), keyword: theme.ramps.blue(0.5).hex(),
} }
// Then assign colors and use Syntax to enforce each style getting it's own color // Then assign colors and use Syntax to enforce each style getting it's own color
@ -211,11 +212,11 @@ function build_default_syntax(color_scheme: ColorScheme): Syntax {
weight: font_weights.bold, weight: font_weights.bold,
}, },
link_uri: { link_uri: {
color: color_scheme.ramps.green(0.5).hex(), color: theme.ramps.green(0.5).hex(),
underline: true, underline: true,
}, },
link_text: { link_text: {
color: color_scheme.ramps.orange(0.5).hex(), color: theme.ramps.orange(0.5).hex(),
italic: true, italic: true,
}, },
"text.literal": { "text.literal": {
@ -231,7 +232,7 @@ function build_default_syntax(color_scheme: ColorScheme): Syntax {
color: color.punctuation, color: color.punctuation,
}, },
"punctuation.special": { "punctuation.special": {
color: color_scheme.ramps.neutral(0.86).hex(), color: theme.ramps.neutral(0.86).hex(),
}, },
"punctuation.list_marker": { "punctuation.list_marker": {
color: color.punctuation, color: color.punctuation,
@ -252,10 +253,10 @@ function build_default_syntax(color_scheme: ColorScheme): Syntax {
color: color.string, color: color.string,
}, },
constructor: { constructor: {
color: color_scheme.ramps.blue(0.5).hex(), color: theme.ramps.blue(0.5).hex(),
}, },
variant: { variant: {
color: color_scheme.ramps.blue(0.5).hex(), color: theme.ramps.blue(0.5).hex(),
}, },
type: { type: {
color: color.type, color: color.type,
@ -264,16 +265,16 @@ function build_default_syntax(color_scheme: ColorScheme): Syntax {
color: color.primary, color: color.primary,
}, },
label: { label: {
color: color_scheme.ramps.blue(0.5).hex(), color: theme.ramps.blue(0.5).hex(),
}, },
tag: { tag: {
color: color_scheme.ramps.blue(0.5).hex(), color: theme.ramps.blue(0.5).hex(),
}, },
attribute: { attribute: {
color: color_scheme.ramps.blue(0.5).hex(), color: theme.ramps.blue(0.5).hex(),
}, },
property: { property: {
color: color_scheme.ramps.blue(0.5).hex(), color: theme.ramps.blue(0.5).hex(),
}, },
constant: { constant: {
color: color.constant, color: color.constant,
@ -307,17 +308,18 @@ function build_default_syntax(color_scheme: ColorScheme): Syntax {
return default_syntax return default_syntax
} }
function merge_syntax( export function build_syntax(): Syntax {
default_syntax: Syntax, const theme = useTheme()
color_scheme: ColorScheme
): Syntax { const default_syntax: Syntax = build_default_syntax()
if (!color_scheme.syntax) {
if (!theme.syntax) {
return default_syntax return default_syntax
} }
return deepmerge<Syntax, Partial<ThemeSyntax>>( const syntax = deepmerge<Syntax, Partial<ThemeSyntax>>(
default_syntax, default_syntax,
color_scheme.syntax, theme.syntax,
{ {
arrayMerge: (destinationArray, sourceArray) => [ arrayMerge: (destinationArray, sourceArray) => [
...destinationArray, ...destinationArray,
@ -325,12 +327,6 @@ function merge_syntax(
], ],
} }
) )
}
export function build_syntax(color_scheme: ColorScheme): Syntax {
const default_syntax: Syntax = build_default_syntax(color_scheme)
const syntax = merge_syntax(default_syntax, color_scheme)
return syntax return syntax
} }

View File

@ -66,35 +66,10 @@ type ThemeConfigProperties = ThemeMeta & {
override: ThemeConfigOverrides override: ThemeConfigOverrides
} }
// This should be the format a theme is defined as
export type ThemeConfig = { export type ThemeConfig = {
[K in keyof ThemeConfigProperties]: ThemeConfigProperties[K] [K in keyof ThemeConfigProperties]: ThemeConfigProperties[K]
} }
interface ThemeColors {
neutral: string[]
red: string[]
orange: string[]
yellow: string[]
green: string[]
cyan: string[]
blue: string[]
violet: string[]
magenta: string[]
}
type ThemeSyntax = Required<Syntax>
export type ThemeProperties = ThemeMeta & {
color: ThemeColors
syntax: ThemeSyntax
}
// This should be a theme after all its properties have been resolved
export type Theme = {
[K in keyof ThemeProperties]: ThemeProperties[K]
}
export enum ThemeAppearance { export enum ThemeAppearance {
Light = "light", Light = "light",
Dark = "dark", Dark = "dark",
@ -104,45 +79,3 @@ export enum ThemeLicenseType {
MIT = "MIT", MIT = "MIT",
Apache2 = "Apache License 2.0", Apache2 = "Apache License 2.0",
} }
export type ThemeFamilyItem =
| ThemeConfig
| { light: ThemeConfig; dark: ThemeConfig }
type ThemeFamilyProperties = Partial<Omit<ThemeMeta, "name" | "appearance">> & {
name: string
default: ThemeFamilyItem
variants: {
[key: string]: ThemeFamilyItem
}
}
// Idea: A theme family is a collection of themes that share the same name
// For example, a theme family could be `One Dark` and have a `light` and `dark` variant
// The Ayu family could have `light`, `mirage`, and `dark` variants
type ThemeFamily = {
[K in keyof ThemeFamilyProperties]: ThemeFamilyProperties[K]
}
/** The collection of all themes
*
* Example:
* ```ts
* {
* one_dark,
* one_light,
* ayu: {
* name: 'Ayu',
* default: 'ayu_mirage',
* variants: {
* light: 'ayu_light',
* mirage: 'ayu_mirage',
* dark: 'ayu_dark',
* },
* },
* ...
* }
* ```
*/
export type ThemeIndex = Record<string, ThemeFamily | ThemeConfig>

View File

@ -1,5 +1,5 @@
import { SingleColorToken } from "@tokens-studio/types" import { SingleColorToken } from "@tokens-studio/types"
import { Layer, Style, StyleSet } from "../color_scheme" import { Layer, Style, StyleSet } from "../create_theme"
import { color_token } from "./token" import { color_token } from "./token"
interface StyleToken { interface StyleToken {

View File

@ -1,7 +1,7 @@
import { SingleColorToken } from "@tokens-studio/types" import { SingleColorToken } from "@tokens-studio/types"
import { color_token } from "./token" import { color_token } from "./token"
import { Players } from "../color_scheme" import { Players } from "../create_theme"
import { useTheme } from "@/src/common" import { useTheme } from "../../../src/common"
export type PlayerToken = Record<"selection" | "cursor", SingleColorToken> export type PlayerToken = Record<"selection" | "cursor", SingleColorToken>

View File

@ -5,19 +5,18 @@ import {
TokenTypes, TokenTypes,
} from "@tokens-studio/types" } from "@tokens-studio/types"
import { import {
ColorScheme,
Shadow, Shadow,
SyntaxHighlightStyle, SyntaxHighlightStyle,
ThemeSyntax, ThemeSyntax,
} from "../color_scheme" } from "../create_theme"
import { LayerToken, layer_token } from "./layer" import { LayerToken, layer_token } from "./layer"
import { PlayersToken, players_token } from "./players" import { PlayersToken, players_token } from "./players"
import { color_token } from "./token" import { color_token } from "./token"
import { Syntax } from "../syntax" import { Syntax } from "../syntax"
import editor from "../../style_tree/editor" import editor from "../../style_tree/editor"
import { useTheme } from "@/src/common" import { useTheme } from "../../../src/common"
interface ColorSchemeTokens { interface ThemeTokens {
name: SingleOtherToken name: SingleOtherToken
appearance: SingleOtherToken appearance: SingleOtherToken
lowest: LayerToken lowest: LayerToken
@ -71,13 +70,13 @@ function syntax_highlight_style_color_tokens(
}, {} as ThemeSyntaxColorTokens) }, {} as ThemeSyntaxColorTokens)
} }
const syntax_tokens = (): ColorSchemeTokens["syntax"] => { const syntax_tokens = (): ThemeTokens["syntax"] => {
const syntax = editor().syntax const syntax = editor().syntax
return syntax_highlight_style_color_tokens(syntax) return syntax_highlight_style_color_tokens(syntax)
} }
export function theme_tokens(): ColorSchemeTokens { export function theme_tokens(): ThemeTokens {
const theme = useTheme() const theme = useTheme()
return { return {