@opencode/theme 2.0.8 → 2.0.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,6 +4,7 @@ export const BaseHue = Schema.Literals(["gray", "red", "orange", "yellow", "gree
4
4
  export const HueAlias = Schema.Literals(["accent", "interactive", "neutral"]);
5
5
  export const ActionVariant = Schema.Literals(["primary", "secondary", "destructive"]);
6
6
  export const ActionState = Schema.Literals(["disabled", "pressed", "focused", "selected", "hovered"]);
7
+ export const SurfaceName = Schema.Literal("dialog");
7
8
  export const FormfieldState = ActionState;
8
9
  export const FeedbackKind = Schema.Literals(["error", "warning", "success", "info"]);
9
10
  const Mode = Schema.Literals(["light", "dark"]);
@@ -16,7 +17,6 @@ const ColorValue = Schema.Union([
16
17
  export const HueName = Schema.Union([BaseHue, HueAlias]);
17
18
  export const CategoricalDefinition = Schema.Array(HueName).check(Schema.isMinLength(1));
18
19
  const HueColorValue = Schema.Union([HexColor, Schema.TemplateLiteral(["$hue.", HueName, ".", HueStep])]);
19
- const ContextKey = Schema.Literals(["@context:elevated", "@context:overlay"]);
20
20
  const HueScaleDefinition = Schema.Record(HueStep, HexColor);
21
21
  const HueValueDefinition = Schema.Union([Schema.TemplateLiteral(["$hue.", HueName]), HueScaleDefinition]);
22
22
  const HueDefinition = Schema.Struct({
@@ -32,21 +32,8 @@ const HueDefinition = Schema.Struct({
32
32
  interactive: HueValueDefinition,
33
33
  neutral: HueValueDefinition,
34
34
  });
35
- const HueOverrideDefinition = Schema.Struct({
36
- gray: Schema.optional(HueValueDefinition),
37
- red: Schema.optional(HueValueDefinition),
38
- orange: Schema.optional(HueValueDefinition),
39
- yellow: Schema.optional(HueValueDefinition),
40
- green: Schema.optional(HueValueDefinition),
41
- cyan: Schema.optional(HueValueDefinition),
42
- blue: Schema.optional(HueValueDefinition),
43
- purple: Schema.optional(HueValueDefinition),
44
- accent: Schema.optional(HueValueDefinition),
45
- interactive: Schema.optional(HueValueDefinition),
46
- neutral: Schema.optional(HueValueDefinition),
47
- });
48
35
  const StatefulColorDefinition = Schema.Struct({
49
- default: Schema.optional(ColorValue),
36
+ base: Schema.optional(ColorValue),
50
37
  $hovered: Schema.optional(ColorValue),
51
38
  $focused: Schema.optional(ColorValue),
52
39
  $pressed: Schema.optional(ColorValue),
@@ -59,23 +46,17 @@ const ActionColorDefinition = Schema.Struct({
59
46
  destructive: Schema.optional(StatefulColorDefinition),
60
47
  });
61
48
  const TextFeedbackDefinition = Schema.Struct({
62
- default: Schema.optional(ColorValue),
63
- subdued: Schema.optional(ColorValue),
49
+ base: Schema.optional(ColorValue),
50
+ muted: Schema.optional(ColorValue),
64
51
  });
65
52
  const BackgroundFeedbackDefinition = Schema.Struct({
66
- default: Schema.optional(ColorValue),
53
+ base: Schema.optional(ColorValue),
67
54
  });
68
55
  const TextDefinition = Schema.Struct({
69
- default: Schema.optional(ColorValue),
70
- subdued: Schema.optional(ColorValue),
56
+ base: Schema.optional(ColorValue),
57
+ muted: Schema.optional(ColorValue),
71
58
  action: Schema.optional(ActionColorDefinition),
72
59
  formfield: Schema.optional(StatefulColorDefinition),
73
- status: Schema.optional(Schema.Struct({
74
- running: Schema.optional(ColorValue),
75
- question: Schema.optional(ColorValue),
76
- permission: Schema.optional(ColorValue),
77
- unread: Schema.optional(ColorValue),
78
- })),
79
60
  feedback: Schema.optional(Schema.Struct({
80
61
  error: Schema.optional(TextFeedbackDefinition),
81
62
  warning: Schema.optional(TextFeedbackDefinition),
@@ -84,7 +65,7 @@ const TextDefinition = Schema.Struct({
84
65
  })),
85
66
  });
86
67
  const BackgroundDefinition = Schema.Struct({
87
- default: Schema.optional(ColorValue),
68
+ base: Schema.optional(ColorValue),
88
69
  raised: Schema.optional(Schema.Struct({
89
70
  base: Schema.optional(ColorValue),
90
71
  high: Schema.optional(ColorValue),
@@ -149,42 +130,98 @@ const DiffDefinition = Schema.Struct({
149
130
  const ThemeTokensDefinition = Schema.Struct({
150
131
  text: Schema.optional(TextDefinition),
151
132
  background: Schema.optional(BackgroundDefinition),
152
- border: Schema.optional(Schema.Struct({ default: Schema.optional(ColorValue) })),
153
- scrollbar: Schema.optional(Schema.Struct({ default: Schema.optional(ColorValue) })),
133
+ border: Schema.optional(Schema.Struct({ base: Schema.optional(ColorValue) })),
134
+ scrollbar: Schema.optional(Schema.Struct({ base: Schema.optional(ColorValue) })),
154
135
  diff: Schema.optional(DiffDefinition),
155
136
  syntax: Schema.optional(SyntaxDefinition),
156
137
  markdown: Schema.optional(MarkdownDefinition),
157
138
  });
139
+ const CompleteStatefulColorDefinition = Schema.Struct({
140
+ base: ColorValue,
141
+ $hovered: Schema.optional(ColorValue),
142
+ $focused: Schema.optional(ColorValue),
143
+ $pressed: Schema.optional(ColorValue),
144
+ $selected: Schema.optional(ColorValue),
145
+ $disabled: Schema.optional(ColorValue),
146
+ });
147
+ const CompleteActionColorDefinition = Schema.Struct({
148
+ primary: CompleteStatefulColorDefinition,
149
+ secondary: CompleteStatefulColorDefinition,
150
+ destructive: CompleteStatefulColorDefinition,
151
+ });
152
+ const CompleteTextFeedbackDefinition = Schema.Struct({ base: ColorValue, muted: Schema.optional(ColorValue) });
153
+ const CompleteBackgroundFeedbackDefinition = Schema.Struct({ base: ColorValue });
154
+ const CompleteThemeTokensDefinition = Schema.Struct({
155
+ text: Schema.Struct({
156
+ base: ColorValue,
157
+ muted: ColorValue,
158
+ action: CompleteActionColorDefinition,
159
+ formfield: CompleteStatefulColorDefinition,
160
+ feedback: Schema.Struct({
161
+ error: CompleteTextFeedbackDefinition,
162
+ warning: CompleteTextFeedbackDefinition,
163
+ success: CompleteTextFeedbackDefinition,
164
+ info: CompleteTextFeedbackDefinition,
165
+ }),
166
+ }),
167
+ background: Schema.Struct({
168
+ base: ColorValue,
169
+ raised: Schema.Struct({ base: ColorValue, high: ColorValue, max: ColorValue }),
170
+ action: CompleteActionColorDefinition,
171
+ formfield: CompleteStatefulColorDefinition,
172
+ feedback: Schema.Struct({
173
+ error: CompleteBackgroundFeedbackDefinition,
174
+ warning: CompleteBackgroundFeedbackDefinition,
175
+ success: CompleteBackgroundFeedbackDefinition,
176
+ info: CompleteBackgroundFeedbackDefinition,
177
+ }),
178
+ }),
179
+ border: Schema.Struct({ base: ColorValue }),
180
+ scrollbar: Schema.Struct({ base: ColorValue }),
181
+ diff: Schema.Struct({
182
+ text: Schema.Struct({ added: ColorValue, removed: ColorValue, context: ColorValue, hunkHeader: ColorValue }),
183
+ background: Schema.Struct({ added: ColorValue, removed: ColorValue, context: ColorValue }),
184
+ highlight: Schema.Struct({ added: ColorValue, removed: ColorValue }),
185
+ lineNumber: Schema.Struct({
186
+ text: ColorValue,
187
+ background: Schema.Struct({ added: ColorValue, removed: ColorValue }),
188
+ }),
189
+ }),
190
+ syntax: Schema.Record(SyntaxToken, HueColorValue),
191
+ markdown: Schema.Record(MarkdownToken, HueColorValue),
192
+ });
158
193
  const ThemeDefinitionFields = Schema.Struct({
159
194
  hue: HueDefinition,
160
- categorical: Schema.optional(CategoricalDefinition),
161
- ...ThemeTokensDefinition.fields,
162
- "@context:elevated": Schema.optional(ThemeTokensDefinition),
163
- "@context:overlay": Schema.optional(ThemeTokensDefinition),
195
+ categorical: CategoricalDefinition,
196
+ ...CompleteThemeTokensDefinition.fields,
197
+ "@dialog": Schema.optional(ThemeTokensDefinition),
164
198
  });
165
199
  export const ThemeDefinition = ThemeDefinitionFields;
166
- const FileThemeDefinition = Schema.Struct({
167
- hue: Schema.optional(HueOverrideDefinition),
168
- categorical: Schema.optional(CategoricalDefinition),
169
- ...ThemeTokensDefinition.fields,
170
- "@context:elevated": Schema.optional(ThemeTokensDefinition),
171
- "@context:overlay": Schema.optional(ThemeTokensDefinition),
200
+ export const BaseThemeDefinition = Schema.Struct({
201
+ categorical: CategoricalDefinition,
202
+ ...CompleteThemeTokensDefinition.fields,
203
+ "@dialog": Schema.optional(ThemeTokensDefinition),
172
204
  });
173
- const MergeModeDefinition = Schema.Struct({
174
- mergeMode: Schema.Literal(true),
175
- hue: Schema.optional(HueOverrideDefinition),
205
+ export const ModeDefinition = Schema.Struct({
206
+ hue: HueDefinition,
176
207
  categorical: Schema.optional(CategoricalDefinition),
177
208
  ...ThemeTokensDefinition.fields,
178
- "@context:elevated": Schema.optional(ThemeTokensDefinition),
179
- "@context:overlay": Schema.optional(ThemeTokensDefinition),
209
+ "@dialog": Schema.optional(ThemeTokensDefinition),
180
210
  });
181
- export const ModeDefinition = Schema.Union([MergeModeDefinition, FileThemeDefinition]);
182
211
  const FileMetadata = {
183
212
  $schema: Schema.optional(Schema.String),
184
- version: Schema.Literal(2),
185
- standalone: Schema.optional(Schema.Boolean),
186
213
  };
187
214
  export const ThemeDocument = Schema.Union([
188
- Schema.Struct({ ...FileMetadata, light: ModeDefinition, dark: Schema.optional(ModeDefinition) }),
189
- Schema.Struct({ ...FileMetadata, light: Schema.optional(ModeDefinition), dark: ModeDefinition }),
215
+ Schema.Struct({
216
+ ...FileMetadata,
217
+ base: BaseThemeDefinition,
218
+ light: ModeDefinition,
219
+ dark: Schema.optional(ModeDefinition),
220
+ }),
221
+ Schema.Struct({
222
+ ...FileMetadata,
223
+ base: BaseThemeDefinition,
224
+ light: Schema.optional(ModeDefinition),
225
+ dark: ModeDefinition,
226
+ }),
190
227
  ]);
@@ -1,13 +1,8 @@
1
- import type { FileThemeDefinition, Mode, ThemeDefinition, ThemeDocument } from "./index.js";
2
- export declare function selectTheme(document: ThemeDocument & {
3
- light: ThemeDefinition;
4
- dark: ThemeDefinition;
5
- }, mode?: Mode): ThemeDefinition;
6
- export declare function selectTheme(document: ThemeDocument, mode?: Mode): FileThemeDefinition;
1
+ import type { Mode, ThemeDefinition, ThemeDocument } from "./index.js";
2
+ export declare function selectTheme(document: ThemeDocument, mode?: Mode): ThemeDefinition;
7
3
  export declare function selectThemeMode(document: ThemeDocument, mode?: Mode): {
8
- theme: FileThemeDefinition;
4
+ theme: ThemeDefinition;
9
5
  mode: Mode;
10
- expanded: boolean;
11
6
  };
12
7
  export declare function themeModes(document: ThemeDocument): readonly Mode[];
13
8
  export declare function supportsThemeMode(document: ThemeDocument, mode: Mode): boolean;
@@ -1,4 +1,4 @@
1
- import { expandTheme, mergeTheme } from "./expand.js";
1
+ import { mergeTheme } from "./expand.js";
2
2
  export function selectTheme(document, mode) {
3
3
  return selectThemeMode(document, mode).theme;
4
4
  }
@@ -8,29 +8,11 @@ export function selectThemeMode(document, mode = "light") {
8
8
  const selected = document[selectedMode];
9
9
  if (!selected)
10
10
  throw new Error("Theme must provide at least one mode");
11
- if (merges(document.light) && merges(document.dark))
12
- throw new Error("Light and dark themes cannot both merge modes");
13
- if (!merges(selected))
14
- return { theme: selected, mode: selectedMode, expanded: false };
15
- const otherMode = selectedMode === "light" ? "dark" : "light";
16
- const other = document[otherMode];
17
- if (!other)
18
- throw new Error(`The ${selectedMode} theme cannot merge without a ${otherMode} theme`);
19
- const merged = mergeTheme(expandTheme(other), expandTheme(selected));
20
- if (!merged["hue"])
21
- throw new Error(`The ${otherMode} theme must provide hues when ${selectedMode} merges modes`);
22
- return { theme: merged, mode: selectedMode, expanded: true };
11
+ return { theme: mergeTheme(document.base, selected), mode: selectedMode };
23
12
  }
24
13
  export function themeModes(document) {
25
- if (merges(document.light) && !document.dark)
26
- throw new Error("The light theme cannot merge without a dark theme");
27
- if (merges(document.dark) && !document.light)
28
- throw new Error("The dark theme cannot merge without a light theme");
29
14
  return ["light", "dark"].filter((mode) => document[mode] !== undefined);
30
15
  }
31
16
  export function supportsThemeMode(document, mode) {
32
17
  return themeModes(document).includes(mode);
33
18
  }
34
- function merges(definition) {
35
- return definition !== undefined && "mergeMode" in definition && definition.mergeMode === true;
36
- }
@@ -1,3 +1,3 @@
1
1
  import { SyntaxStyle } from "@opentui/core";
2
- import type { Mode, ResolvedThemeTokens } from "./index.js";
3
- export declare function generateSyntax(theme: ResolvedThemeTokens, mode: Mode): SyntaxStyle;
2
+ import type { ResolvedThemeTokens } from "./index.js";
3
+ export declare function generateSyntax(theme: ResolvedThemeTokens): SyntaxStyle;
@@ -1,18 +1,18 @@
1
1
  import { SyntaxStyle } from "@opentui/core";
2
- export function generateSyntax(theme, mode) {
3
- const step = mode === "light" ? 800 : 200;
2
+ export function generateSyntax(theme) {
3
+ const step = 200;
4
4
  const syntax = theme.syntax;
5
5
  const markdown = theme.markdown;
6
6
  const feedback = theme.text.feedback;
7
7
  return SyntaxStyle.fromTheme([
8
- rule(["default"], theme.text.default),
8
+ rule(["default"], theme.text.base),
9
9
  rule(["prompt"], theme.hue.accent[step]),
10
- rule(["extmark.file"], feedback.warning.default, { bold: true }),
10
+ rule(["extmark.file"], feedback.warning.base, { bold: true }),
11
11
  rule(["extmark.agent"], theme.categorical[0][step], { bold: true }),
12
12
  rule(["extmark.skill"], (theme.categorical[1] ?? theme.categorical[0])[step], { bold: true }),
13
13
  // V1 migration preserves its selected/inverse foreground in this action state.
14
14
  rule(["extmark.paste"], theme.text.action.primary.focused, {
15
- background: feedback.warning.default,
15
+ background: feedback.warning.base,
16
16
  bold: true,
17
17
  }),
18
18
  rule(["comment", "comment.documentation"], syntax.comment, { italic: true }),
@@ -31,7 +31,7 @@ export function generateSyntax(theme, mode) {
31
31
  rule(["type", "module", "class", "namespace"], syntax.type),
32
32
  rule(["type.definition"], syntax.type, { bold: true }),
33
33
  rule(["punctuation", "punctuation.bracket"], syntax.punctuation),
34
- rule(["variable.builtin", "type.builtin", "function.builtin", "module.builtin", "constant.builtin", "variable.super"], feedback.error.default),
34
+ rule(["variable.builtin", "type.builtin", "function.builtin", "module.builtin", "constant.builtin", "variable.super"], feedback.error.base),
35
35
  rule(["keyword.directive", "keyword.modifier", "keyword.exception"], syntax.keyword, { italic: true }),
36
36
  rule(["punctuation.special", "tag.delimiter"], syntax.operator),
37
37
  rule([
@@ -48,27 +48,27 @@ export function generateSyntax(theme, mode) {
48
48
  rule(["markup.list"], markdown.listItem),
49
49
  rule(["markup.quote"], markdown.blockQuote, { italic: true }),
50
50
  rule(["markup.raw", "markup.raw.block"], markdown.code),
51
- rule(["markup.raw.inline"], markdown.code, { background: theme.background.default }),
51
+ rule(["markup.raw.inline"], markdown.code, { background: theme.background.base }),
52
52
  rule(["markup.link", "markup.link.url", "string.special", "string.special.url"], markdown.link, {
53
53
  underline: true,
54
54
  }),
55
55
  rule(["markup.link.label"], markdown.linkText, { underline: true }),
56
56
  rule(["label"], markdown.linkText),
57
- rule(["spell", "nospell"], theme.text.default),
58
- rule(["markup.underline"], theme.text.default, { underline: true }),
59
- rule(["comment.error"], feedback.error.default, { italic: true, bold: true }),
60
- rule(["comment.warning"], feedback.warning.default, { italic: true, bold: true }),
61
- rule(["comment.todo", "comment.note"], feedback.info.default, { italic: true, bold: true }),
62
- rule(["attribute", "annotation"], feedback.warning.default),
63
- rule(["tag"], feedback.error.default),
64
- rule(["markup.strikethrough", "markup.list.unchecked", "debug"], theme.text.subdued),
65
- rule(["markup.list.checked"], feedback.success.default),
57
+ rule(["spell", "nospell"], theme.text.base),
58
+ rule(["markup.underline"], theme.text.base, { underline: true }),
59
+ rule(["comment.error"], feedback.error.base, { italic: true, bold: true }),
60
+ rule(["comment.warning"], feedback.warning.base, { italic: true, bold: true }),
61
+ rule(["comment.todo", "comment.note"], feedback.info.base, { italic: true, bold: true }),
62
+ rule(["attribute", "annotation"], feedback.warning.base),
63
+ rule(["tag"], feedback.error.base),
64
+ rule(["markup.strikethrough", "markup.list.unchecked", "debug"], theme.text.muted),
65
+ rule(["markup.list.checked"], feedback.success.base),
66
66
  rule(["diff.plus"], theme.diff.text.added, { background: theme.diff.background.added }),
67
67
  rule(["diff.minus"], theme.diff.text.removed, { background: theme.diff.background.removed }),
68
68
  rule(["diff.delta"], theme.diff.text.context, { background: theme.diff.background.context }),
69
- rule(["error"], feedback.error.default, { bold: true }),
70
- rule(["warning"], feedback.warning.default, { bold: true }),
71
- rule(["info"], feedback.info.default),
69
+ rule(["error"], feedback.error.base, { bold: true }),
70
+ rule(["warning"], feedback.warning.base, { bold: true }),
71
+ rule(["info"], feedback.info.base),
72
72
  ]);
73
73
  }
74
74
  function rule(scope, foreground, style = {}) {
@@ -1,6 +1,6 @@
1
1
  import type { RGBA } from "@opentui/core";
2
- import type { ActionState, ActionVariant, BaseHue, FeedbackKind, HueAlias, HueStep, MarkdownToken, SyntaxToken } from "./schema.js";
3
- export type ResolvedActionState = "default" | ActionState;
2
+ import type { ActionState, ActionVariant, BaseHue, FeedbackKind, HueAlias, HueStep, MarkdownToken, SurfaceName, SyntaxToken } from "./schema.js";
3
+ export type ResolvedActionState = "base" | ActionState;
4
4
  export type ResolvedFormfieldState = ResolvedActionState;
5
5
  export type HueScale = Readonly<Record<HueStep, RGBA>>;
6
6
  export type Hue = Readonly<Record<BaseHue | HueAlias, HueScale>>;
@@ -9,7 +9,10 @@ export type HueSource = Readonly<{
9
9
  step: HueStep;
10
10
  }>;
11
11
  export type Categorical = readonly HueScale[];
12
- export type StatefulColor = Readonly<Record<ResolvedActionState, RGBA>>;
12
+ export type ActionStates = Readonly<Partial<Record<ActionState, boolean>>>;
13
+ export type StatefulColor = Readonly<Record<ResolvedActionState, RGBA>> & {
14
+ readonly state: (states: ActionStates) => RGBA;
15
+ };
13
16
  export type FormfieldColor = StatefulColor;
14
17
  export type ResolvedThemeTokens = {
15
18
  readonly hue: Hue;
@@ -18,23 +21,17 @@ export type ResolvedThemeTokens = {
18
21
  readonly increase: (color: RGBA, amount?: number) => RGBA;
19
22
  readonly decrease: (color: RGBA, amount?: number) => RGBA;
20
23
  readonly text: {
21
- readonly default: RGBA;
22
- readonly subdued: RGBA;
24
+ readonly base: RGBA;
25
+ readonly muted: RGBA;
23
26
  readonly action: Readonly<Record<ActionVariant, StatefulColor>>;
24
27
  readonly formfield: FormfieldColor;
25
- readonly status: {
26
- readonly running: RGBA;
27
- readonly question: RGBA;
28
- readonly permission: RGBA;
29
- readonly unread: RGBA;
30
- };
31
28
  readonly feedback: Readonly<Record<FeedbackKind, {
32
- readonly default: RGBA;
33
- readonly subdued: RGBA;
29
+ readonly base: RGBA;
30
+ readonly muted: RGBA;
34
31
  }>>;
35
32
  };
36
33
  readonly background: {
37
- readonly default: RGBA;
34
+ readonly base: RGBA;
38
35
  readonly raised: {
39
36
  readonly base: RGBA;
40
37
  readonly high: RGBA;
@@ -43,14 +40,14 @@ export type ResolvedThemeTokens = {
43
40
  readonly action: Readonly<Record<ActionVariant, StatefulColor>>;
44
41
  readonly formfield: FormfieldColor;
45
42
  readonly feedback: Readonly<Record<FeedbackKind, {
46
- readonly default: RGBA;
43
+ readonly base: RGBA;
47
44
  }>>;
48
45
  };
49
46
  readonly border: {
50
- readonly default: RGBA;
47
+ readonly base: RGBA;
51
48
  };
52
49
  readonly scrollbar: {
53
- readonly default: RGBA;
50
+ readonly base: RGBA;
54
51
  };
55
52
  readonly diff: {
56
53
  readonly text: {
@@ -79,7 +76,7 @@ export type ResolvedThemeTokens = {
79
76
  readonly syntax: Readonly<Record<SyntaxToken, RGBA>>;
80
77
  readonly markdown: Readonly<Record<MarkdownToken, RGBA>>;
81
78
  };
82
- export type ContextName = "elevated" | "overlay";
83
79
  export type ResolvedTheme = ResolvedThemeTokens & {
84
- readonly contextual: Readonly<Record<ContextName, ResolvedThemeTokens>>;
80
+ /** The same theme re-resolved on a raised surface. Absolute: every view's surfaces are the base theme's. */
81
+ readonly surface: (name: SurfaceName) => ResolvedTheme;
85
82
  };