1import { Scale, Color } from "chroma-js"
2import { Syntax } from "./syntax"
3
4interface ThemeMeta {
5 /** The name of the theme */
6 name: string
7 /** The theme's appearance. Either `light` or `dark`. */
8 appearance: ThemeAppearance
9 /** The author of the theme
10 *
11 * Ideally formatted as `Full Name <email>`
12 *
13 * Example: `John Doe <john@doe.com>`
14 */
15 author: string
16 /** SPDX License string
17 *
18 * Example: `MIT`
19 */
20 licenseType?: string | ThemeLicenseType
21 licenseUrl?: string
22 licenseFile: string
23 themeUrl?: string
24}
25
26export type ThemeFamilyMeta = Pick<
27 ThemeMeta,
28 "name" | "author" | "licenseType" | "licenseUrl"
29>
30
31export interface ThemeConfigInputColors {
32 neutral: Scale<Color>
33 red: Scale<Color>
34 orange: Scale<Color>
35 yellow: Scale<Color>
36 green: Scale<Color>
37 cyan: Scale<Color>
38 blue: Scale<Color>
39 violet: Scale<Color>
40 magenta: Scale<Color>
41}
42
43export type ThemeConfigInputColorsKeys = keyof ThemeConfigInputColors
44
45/** Allow any part of a syntax highlight style to be overriden by the theme
46 *
47 * Example:
48 * ```ts
49 * override: {
50 * syntax: {
51 * boolean: {
52 * underline: true,
53 * },
54 * },
55 * }
56 * ```
57 */
58export type ThemeConfigInputSyntax = Partial<Syntax>
59
60interface ThemeConfigOverrides {
61 syntax: ThemeConfigInputSyntax
62}
63
64type ThemeConfigProperties = ThemeMeta & {
65 inputColor: ThemeConfigInputColors
66 override: ThemeConfigOverrides
67}
68
69// This should be the format a theme is defined as
70export type ThemeConfig = {
71 [K in keyof ThemeConfigProperties]: ThemeConfigProperties[K]
72}
73
74interface ThemeColors {
75 neutral: string[]
76 red: string[]
77 orange: string[]
78 yellow: string[]
79 green: string[]
80 cyan: string[]
81 blue: string[]
82 violet: string[]
83 magenta: string[]
84}
85
86type ThemeSyntax = Required<Syntax>
87
88export type ThemeProperties = ThemeMeta & {
89 color: ThemeColors
90 syntax: ThemeSyntax
91}
92
93// This should be a theme after all its properties have been resolved
94export type Theme = {
95 [K in keyof ThemeProperties]: ThemeProperties[K]
96}
97
98export enum ThemeAppearance {
99 Light = "light",
100 Dark = "dark",
101}
102
103export enum ThemeLicenseType {
104 MIT = "MIT",
105 Apache2 = "Apache License 2.0",
106}
107
108export type ThemeFamilyItem =
109 | ThemeConfig
110 | { light: ThemeConfig; dark: ThemeConfig }
111
112type ThemeFamilyProperties = Partial<Omit<ThemeMeta, "name" | "appearance">> & {
113 name: string
114 default: ThemeFamilyItem
115 variants: {
116 [key: string]: ThemeFamilyItem
117 }
118}
119
120// Idea: A theme family is a collection of themes that share the same name
121// For example, a theme family could be `One Dark` and have a `light` and `dark` variant
122// The Ayu family could have `light`, `mirage`, and `dark` variants
123
124type ThemeFamily = {
125 [K in keyof ThemeFamilyProperties]: ThemeFamilyProperties[K]
126}
127
128/** The collection of all themes
129 *
130 * Example:
131 * ```ts
132 * {
133 * one_dark,
134 * one_light,
135 * ayu: {
136 * name: 'Ayu',
137 * default: 'ayu_mirage',
138 * variants: {
139 * light: 'ayu_light',
140 * mirage: 'ayu_mirage',
141 * dark: 'ayu_dark',
142 * },
143 * },
144 * ...
145 * }
146 * ```
147 */
148export type ThemeIndex = Record<string, ThemeFamily | ThemeConfig>