@opencode/theme 2.0.13 → 2.0.15

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.
@@ -1,9 +1,10 @@
1
1
  import { RGBA } from "@opentui/core";
2
2
  import { Schema } from "effect";
3
3
  import { expandTheme, mergeTheme } from "./expand.js";
4
- import { ActionState, ActionVariant, BaseHue, HueAlias, HueStep, SurfaceName, ThemeDefinition, ThemeDocument, } from "./schema.js";
5
- import { selectThemeMode } from "./select.js";
4
+ import { ActionState, ActionVariant, HueStep, SurfaceName, ThemeDefinition, ThemeDocument, } from "./schema.js";
5
+ import { selectThemeMode, themeModes } from "./select.js";
6
6
  const decodeThemeDefinitionSchema = Schema.decodeUnknownSync(ThemeDefinition, { reportInput: true });
7
+ const decodeThemeDocumentSchema = Schema.decodeUnknownSync(ThemeDocument, { reportInput: true });
7
8
  function decodeThemeDefinition(input) {
8
9
  try {
9
10
  return decodeThemeDefinitionSchema(input);
@@ -17,6 +18,16 @@ export function themeDecodeError(error, name) {
17
18
  const value = /got ("[^"]*"|\S+)/.exec(message)?.[1] ?? "value";
18
19
  return new Error(`Invalid theme: ${name} ${value} is an invalid value`, { cause: error });
19
20
  }
21
+ export function parseThemeDocument(input, name = "theme") {
22
+ try {
23
+ const document = decodeThemeDocumentSchema(input);
24
+ themeModes(document).forEach((mode) => resolveThemeDocument(document, mode));
25
+ return document;
26
+ }
27
+ catch (error) {
28
+ throw themeDecodeError(error, name);
29
+ }
30
+ }
20
31
  export function resolveThemeDocument(document, mode) {
21
32
  const selected = selectThemeMode(document, mode);
22
33
  const definition = expandTheme(selected.theme);
@@ -27,7 +38,12 @@ export function resolveTheme(definition) {
27
38
  }
28
39
  function resolveExpandedTheme(definition) {
29
40
  const hue = resolveHue(definition.hue);
30
- const categorical = definition.categorical.map((name) => hue[name]);
41
+ const categorical = definition.categorical.map((name) => {
42
+ const scale = hue[name];
43
+ if (!scale)
44
+ throw new Error(`Categorical hue "${name}" was not found`);
45
+ return scale;
46
+ });
31
47
  const hueSteps = compileHueSteps(hue);
32
48
  const base = tokens(definition);
33
49
  const views = {};
@@ -132,11 +148,6 @@ function compileHueSteps(hue) {
132
148
  function resolveHue(definition) {
133
149
  const source = definition;
134
150
  const cache = new Map();
135
- const expected = new Set([...BaseHue.literals, ...HueAlias.literals]);
136
- for (const name of Object.keys(source)) {
137
- if (!expected.has(name))
138
- throw new Error(`Unknown hue "${name}"`);
139
- }
140
151
  function resolve(name, stack) {
141
152
  const hit = cache.get(name);
142
153
  if (hit)
@@ -145,9 +156,11 @@ function resolveHue(definition) {
145
156
  throw new Error(`Circular hue reference: ${[...stack, name].join(" -> ")}`);
146
157
  const value = source[name];
147
158
  if (typeof value === "string") {
148
- const match = /^\$hue\.([^.]+)$/.exec(value);
159
+ const match = /^\$hue\.(.+)$/.exec(value);
149
160
  if (!match?.[1])
150
161
  throw new Error(`Hue alias "${value}" must reference a hue scale`);
162
+ if (source[match[1]] === undefined)
163
+ throw new Error(`Hue alias "${value}" references a missing hue`);
151
164
  const target = resolve(match[1], [...stack, name]);
152
165
  const result = Object.fromEntries(HueStep.literals.map((step) => [step, RGBA.clone(target[step])]));
153
166
  cache.set(name, result);
@@ -168,7 +181,7 @@ function resolveHue(definition) {
168
181
  cache.set(name, result);
169
182
  return result;
170
183
  }
171
- return Object.fromEntries([...BaseHue.literals, ...HueAlias.literals].map((name) => [name, resolve(name, [])]));
184
+ return Object.fromEntries(Object.keys(source).map((name) => [name, resolve(name, [])]));
172
185
  }
173
186
  function createResolver(source) {
174
187
  const cache = new Map();
@@ -196,7 +209,7 @@ function createResolver(source) {
196
209
  return hit;
197
210
  if (stack.includes(target))
198
211
  throw new Error(`Circular theme reference: ${[...stack, target].join(" -> ")}`);
199
- const result = resolve(read(source, target), target, [...stack, target]);
212
+ const result = resolve(readColor(source, target), target, [...stack, target]);
200
213
  if (!(result instanceof RGBA))
201
214
  throw new Error(`Theme reference "${value}" at "${path}" is not a color`);
202
215
  cache.set(target, result);
@@ -204,6 +217,17 @@ function createResolver(source) {
204
217
  }
205
218
  return (value, path) => resolve(value, path);
206
219
  }
220
+ function readColor(source, path) {
221
+ const match = /^hue\.(.+)\.(100|200|300|400|500|600|700|800|900)$/.exec(path);
222
+ if (!match?.[1] || !match[2])
223
+ return read(source, path);
224
+ const scales = source.hue;
225
+ const scale = isRecord(scales) ? scales[match[1]] : undefined;
226
+ const color = isRecord(scale) ? scale[match[2]] : undefined;
227
+ if (color === undefined)
228
+ throw new Error(`Theme reference "$${path}" was not found`);
229
+ return color;
230
+ }
207
231
  function resolvedKey(key) {
208
232
  if (!key.startsWith("$"))
209
233
  return key;