@alchemy.run/sigil 0.0.0-alpha.4 → 0.0.0-alpha.6

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.
Files changed (138) hide show
  1. package/README.md +21 -9
  2. package/THIRD_PARTY_NOTICES.md +39 -1
  3. package/dist/Text-BobFKi74.d.ts +452 -0
  4. package/dist/ansi.d.ts +122 -131
  5. package/dist/ansi.js +86 -6
  6. package/dist/capabilities.d.ts +5 -0
  7. package/dist/capabilities.js +3 -0
  8. package/dist/cell-_ZVhbfl0.js +44 -0
  9. package/dist/color-CkbalRqK.js +2 -0
  10. package/dist/color-policy-BMzMwV7Q.d.ts +22 -0
  11. package/dist/color-policy-SVj1pYTA.js +560 -0
  12. package/dist/color-profile-DHhQHY55.js +36 -0
  13. package/dist/color-profile-u0Nhe9Nv.d.ts +97 -0
  14. package/dist/color.d.ts +21 -0
  15. package/dist/color.js +3 -0
  16. package/dist/cursor-position-D2LAkRG0.d.ts +7 -0
  17. package/dist/detect-Bh4yGP6w.d.ts +186 -0
  18. package/dist/detect-BuTXtY6e.js +373 -0
  19. package/dist/env-YVw64yZS.js +9 -0
  20. package/dist/escapes-CB_6CWOE.d.ts +72 -0
  21. package/dist/geometry-BxXOzJgo.d.ts +11 -0
  22. package/dist/index-DZ88EXJv.d.ts +21 -0
  23. package/dist/index.d.ts +143 -498
  24. package/dist/index.js +1026 -2244
  25. package/dist/osc-CCH7xDoS.js +71 -0
  26. package/dist/osc-Cn0fw77g.d.ts +23 -0
  27. package/dist/paint-C19minOS.d.ts +81 -0
  28. package/dist/query-vaIeGOkH.d.ts +152 -0
  29. package/dist/router.d.ts +392 -0
  30. package/dist/router.js +709 -0
  31. package/dist/sample-Cqw1bjUL.js +445 -0
  32. package/dist/screen-BOSLQ8fF.d.ts +49 -0
  33. package/dist/screen-CiPytswf.js +342 -0
  34. package/dist/screen.d.ts +5 -0
  35. package/dist/screen.js +5 -0
  36. package/dist/semantic-text-style-DIMzC7xt.js +91 -0
  37. package/dist/serialize-BTkAZgw1.js +79 -0
  38. package/dist/session-aZr9O8h3.js +665 -0
  39. package/dist/sgr-BhwaWAJB.js +246 -0
  40. package/dist/store-CgrG9K4y.d.ts +72 -0
  41. package/dist/string-width-CijQwpIk.js +69 -0
  42. package/dist/strip-BvU4toXG.js +6 -0
  43. package/dist/terminal.d.ts +118 -0
  44. package/dist/terminal.js +2 -0
  45. package/dist/tokenize-AjqbvtiT.js +1242 -0
  46. package/dist/tokenize-Dx1y_l5H.d.ts +57 -0
  47. package/dist/truncate-D31fhU6i.js +562 -0
  48. package/dist/use-focus-BzqAJi0n.js +1337 -0
  49. package/package.json +41 -9
  50. package/src/ansi/chalk.ts +5 -3
  51. package/src/ansi/escapes.ts +14 -0
  52. package/src/ansi/graphemes.ts +8 -0
  53. package/src/ansi/hyperlink.ts +44 -0
  54. package/src/ansi/index.ts +3 -1
  55. package/src/ansi/osc.ts +77 -0
  56. package/src/ansi/tokenize.ts +3 -4
  57. package/src/capabilities/color-policy.ts +34 -0
  58. package/src/capabilities/detect.ts +594 -0
  59. package/src/capabilities/index.ts +37 -0
  60. package/src/capabilities/query.ts +657 -0
  61. package/src/capabilities/store.ts +379 -0
  62. package/src/color/index.ts +3 -0
  63. package/src/color/paint.ts +169 -0
  64. package/src/color/palette.ts +48 -0
  65. package/src/color/sample.ts +323 -0
  66. package/src/color.ts +1 -0
  67. package/src/components/AnsiText.tsx +42 -0
  68. package/src/components/App.tsx +98 -10
  69. package/src/components/BackgroundContext.ts +2 -3
  70. package/src/components/Box.tsx +0 -8
  71. package/src/components/CursorContext.ts +1 -1
  72. package/src/components/Hyperlink.tsx +56 -0
  73. package/src/components/TerminalOscContext.ts +25 -0
  74. package/src/components/Text.tsx +22 -45
  75. package/src/components/Transform.tsx +1 -1
  76. package/src/dom.ts +11 -2
  77. package/src/global.d.ts +3 -0
  78. package/src/hooks/use-capabilities.ts +73 -0
  79. package/src/hooks/use-cursor.ts +2 -2
  80. package/src/hooks/use-terminal-osc.ts +59 -0
  81. package/src/index.ts +45 -1
  82. package/src/ink.tsx +213 -153
  83. package/src/{render-node-to-output.ts → paint-tree.ts} +75 -48
  84. package/src/reconciler.ts +24 -3
  85. package/src/render-background.ts +36 -15
  86. package/src/render-border.ts +94 -61
  87. package/src/render-frame.ts +83 -0
  88. package/src/render-to-string.ts +21 -6
  89. package/src/render.ts +15 -6
  90. package/src/router/components.tsx +343 -0
  91. package/src/router/context.ts +41 -0
  92. package/src/router/history.ts +194 -0
  93. package/src/router/hooks.tsx +391 -0
  94. package/src/router/index.ts +34 -0
  95. package/src/router/matcher.ts +571 -0
  96. package/src/screen/ansi.ts +184 -0
  97. package/src/screen/canvas.ts +160 -0
  98. package/src/screen/cell.ts +138 -0
  99. package/src/screen/color-profile.ts +47 -0
  100. package/src/screen/geometry.ts +9 -0
  101. package/src/screen/index.ts +6 -0
  102. package/src/screen/screen.ts +305 -0
  103. package/src/screen/serialize.ts +129 -0
  104. package/src/screen.ts +1 -0
  105. package/src/semantic-text-style.ts +118 -0
  106. package/src/squash-text-nodes.ts +2 -5
  107. package/src/structured-text.ts +325 -0
  108. package/src/styles.ts +19 -14
  109. package/src/terminal/index.ts +2 -0
  110. package/src/terminal/inline-presenter.ts +120 -0
  111. package/src/terminal/input.ts +86 -0
  112. package/src/terminal/render-scheduler.ts +37 -0
  113. package/src/terminal/screen-presenter.ts +188 -0
  114. package/src/terminal/session.ts +407 -0
  115. package/src/terminal.ts +1 -0
  116. package/src/testing/browser.ts +588 -0
  117. package/src/testing/emulators.ts +205 -0
  118. package/src/testing/explorer-app/index.html +12 -0
  119. package/src/testing/explorer-app/main.ts +381 -0
  120. package/src/testing/explorer-app/style.css +194 -0
  121. package/src/testing/explorer-app/tsconfig.json +15 -0
  122. package/src/testing/explorer-app/vite-env.d.ts +1 -0
  123. package/src/testing/index.ts +26 -0
  124. package/src/testing/keys.ts +56 -0
  125. package/src/testing/live.ts +85 -0
  126. package/src/testing/matchers.ts +70 -0
  127. package/src/testing/public.ts +94 -0
  128. package/src/testing/terminal.ts +349 -0
  129. package/src/testing/vitest.ts +157 -0
  130. package/src/transform-adapter.ts +14 -0
  131. package/src/wrap-text.ts +4 -0
  132. package/dist/sgr-CMfEpjSk.d.ts +0 -91
  133. package/dist/truncate-Cr6xVFMa.js +0 -2330
  134. package/src/ansi/supports-color.ts +0 -207
  135. package/src/colorize.ts +0 -60
  136. package/src/log-update.ts +0 -370
  137. package/src/output.ts +0 -308
  138. package/src/renderer.ts +0 -73
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alchemy.run/sigil",
3
- "version": "0.0.0-alpha.4",
3
+ "version": "0.0.0-alpha.6",
4
4
  "description": "React for CLIs. A self-contained fork of Ink with an integrated TypeScript Yoga layout engine.",
5
5
  "keywords": [
6
6
  "cli",
@@ -37,25 +37,44 @@
37
37
  },
38
38
  "exports": {
39
39
  ".": {
40
- "typescript": "./src/index.ts",
41
- "bun": "./src/index.ts",
42
40
  "import": "./dist/index.js",
43
41
  "types": "./dist/index.d.ts",
44
42
  "default": "./dist/index.js"
45
43
  },
46
44
  "./ansi": {
47
- "typescript": "./src/ansi/index.ts",
48
- "bun": "./src/ansi/index.ts",
49
45
  "import": "./dist/ansi.js",
50
46
  "types": "./dist/ansi.d.ts",
51
47
  "default": "./dist/ansi.js"
52
48
  },
53
49
  "./yoga": {
54
- "typescript": "./src/yoga/index.ts",
55
- "bun": "./src/yoga/index.ts",
56
50
  "import": "./dist/yoga.js",
57
51
  "types": "./dist/yoga.d.ts",
58
52
  "default": "./dist/yoga.js"
53
+ },
54
+ "./router": {
55
+ "import": "./dist/router.js",
56
+ "types": "./dist/router.d.ts",
57
+ "default": "./dist/router.js"
58
+ },
59
+ "./capabilities": {
60
+ "import": "./dist/capabilities.js",
61
+ "types": "./dist/capabilities.d.ts",
62
+ "default": "./dist/capabilities.js"
63
+ },
64
+ "./color": {
65
+ "import": "./dist/color.js",
66
+ "types": "./dist/color.d.ts",
67
+ "default": "./dist/color.js"
68
+ },
69
+ "./screen": {
70
+ "import": "./dist/screen.js",
71
+ "types": "./dist/screen.d.ts",
72
+ "default": "./dist/screen.js"
73
+ },
74
+ "./terminal": {
75
+ "import": "./dist/terminal.js",
76
+ "types": "./dist/terminal.d.ts",
77
+ "default": "./dist/terminal.js"
59
78
  }
60
79
  },
61
80
  "publishConfig": {
@@ -66,15 +85,25 @@
66
85
  "scheduler": "^0.27.0"
67
86
  },
68
87
  "devDependencies": {
88
+ "@slopus/ghostty-wasm": "^1.3.6",
69
89
  "@types/node": "^25.5.2",
70
90
  "@types/react": "^19.2.18",
71
91
  "@types/react-reconciler": "^0.33.0",
72
92
  "@types/scheduler": "^0.26.0",
93
+ "@vitest/ui": "^4.1.11",
94
+ "@xterm/addon-clipboard": "^0.2.0",
95
+ "@xterm/addon-fit": "^0.11.0",
96
+ "@xterm/addon-image": "^0.9.0",
97
+ "@xterm/addon-unicode11": "^0.9.0",
98
+ "@xterm/addon-web-links": "^0.12.0",
99
+ "@xterm/addon-webgl": "^0.19.0",
100
+ "@xterm/headless": "^6.0.0",
101
+ "@xterm/xterm": "^6.0.0",
73
102
  "react": "^19.2.8",
74
103
  "react-devtools-core": "^7.0.1",
75
- "react-router": "^8.3.0",
76
104
  "tsx": "^4.21.0",
77
105
  "typescript": "^7.0.2",
106
+ "vite": "npm:@voidzero-dev/vite-plus-core@^0.2.9",
78
107
  "vite-plus": "^0.2.9",
79
108
  "vitest": "4.1.11",
80
109
  "zigpty": "^0.2.1"
@@ -99,12 +128,15 @@
99
128
  "dev": "vp pack -w",
100
129
  "build": "vp pack",
101
130
  "test": "vp test",
131
+ "e2e": "vp test test/e2e.test.ts",
132
+ "explorer": "node --import=tsx scripts/explorer.ts",
102
133
  "lint": "vp lint",
103
134
  "format": "vp fmt .",
104
135
  "check": "vp check",
105
- "typecheck": "tsc --noEmit",
136
+ "typecheck": "tsc --noEmit && tsc --noEmit -p test/contracts/tsconfig.json",
106
137
  "inspect": "vpx react-devtools",
107
138
  "build:reference": "tsx scripts/build-reference.ts",
139
+ "bench:terminal": "tsx benchmark/terminal-core.tsx --check",
108
140
  "fuzz": "tsx test/yoga/fuzz/typescript.ts",
109
141
  "fuzz:differential": "vpr build:reference && tsx test/yoga/fuzz/differential.ts",
110
142
  "fuzz:loop": "vpr build:reference && tsx test/yoga/fuzz/loop.ts"
package/src/ansi/chalk.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import { isatty } from "node:tty";
2
+
1
3
  // Terminal string styling with a chalk-compatible API for the styles Ink uses.
2
4
  // Ported from `chalk` (MIT, Sindre Sorhus) without the chaining builder —
3
5
  // Ink only ever applies one style per call.
@@ -17,16 +19,16 @@ import {
17
19
  type ModifierName,
18
20
  } from "#/ansi/sgr.ts";
19
21
  import {
20
- supportsColor as supportsColorDetection,
22
+ createSupportsColor,
21
23
  type ColorInfo,
22
24
  type ColorSupportLevel,
23
- } from "#/ansi/supports-color.ts";
25
+ } from "#/capabilities/detect.ts";
24
26
 
25
27
  export type { BackgroundColorName, ForegroundColorName, ModifierName };
26
28
 
27
29
  export type StyleFunction = (text: string) => string;
28
30
 
29
- const { stdout: stdoutColor } = supportsColorDetection;
31
+ const stdoutColor = createSupportsColor({ isTTY: isatty(1) });
30
32
 
31
33
  // `level` → color model for hex/rgb downsampling
32
34
  const levelMapping = ["ansi", "ansi", "ansi256", "ansi16m"] as const;
@@ -58,6 +58,17 @@ export const exitAlternativeScreen = CSI + "?1049l";
58
58
 
59
59
  export const cursorShow = CSI + "?25h";
60
60
  export const cursorHide = CSI + "?25l";
61
+ export type CursorShape = "block" | "underline" | "bar";
62
+ export const cursorShape = (shape: CursorShape, blinking = true): string => {
63
+ const value = {
64
+ block: blinking ? 1 : 2,
65
+ underline: blinking ? 3 : 4,
66
+ bar: blinking ? 5 : 6,
67
+ }[shape];
68
+ return `${CSI}${value} q`;
69
+ };
70
+ export const cursorColor = (color: string): string => `${OSC}12;${color}${BEL}`;
71
+ export const resetCursorColor = `${OSC}112${BEL}`;
61
72
 
62
73
  export const enableBracketedPaste = CSI + "?2004h";
63
74
  export const disableBracketedPaste = CSI + "?2004l";
@@ -93,6 +104,9 @@ export const ansiEscapes = {
93
104
  exitAlternativeScreen,
94
105
  cursorShow,
95
106
  cursorHide,
107
+ cursorShape,
108
+ cursorColor,
109
+ resetCursorColor,
96
110
  enableBracketedPaste,
97
111
  disableBracketedPaste,
98
112
  pasteStart,
@@ -0,0 +1,8 @@
1
+ const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
2
+
3
+ /** The canonical grapheme segmentation service for measurement and rasterization. */
4
+ export function* graphemes(text: string): Generator<string> {
5
+ for (const { segment } of segmenter.segment(text)) {
6
+ yield segment;
7
+ }
8
+ }
@@ -0,0 +1,44 @@
1
+ // Standalone OSC 8 hyperlink builder with fallback — the string counterpart
2
+ // to the <Hyperlink> component, for use outside the React renderer.
3
+ import { link } from "#/ansi/escapes.ts";
4
+ import { detectHyperlinkSupport } from "#/capabilities/detect.ts";
5
+
6
+ type HyperlinkOptions = {
7
+ /**
8
+ When the terminal does not support OSC 8 hyperlinks, append the URL in
9
+ parentheses after the text so it stays reachable. Set to `false` to
10
+ return the text alone.
11
+
12
+ @default true
13
+ */
14
+ fallback?: boolean;
15
+
16
+ /**
17
+ The stream the text will be written to, for support detection.
18
+
19
+ @default process.stdout
20
+ */
21
+ stream?: { isTTY?: boolean };
22
+ };
23
+
24
+ /**
25
+ Wraps text in a clickable OSC 8 hyperlink when the terminal supports it,
26
+ falling back to `text (url)`.
27
+
28
+ ```ts
29
+ import { hyperlink } from "@alchemy.run/sigil/ansi";
30
+
31
+ console.log(hyperlink("Documentation", "https://example.com"));
32
+ ```
33
+ */
34
+ export const hyperlink = (
35
+ text: string,
36
+ url: string,
37
+ { fallback = true, stream = process.stdout }: HyperlinkOptions = {},
38
+ ): string => {
39
+ if (detectHyperlinkSupport(stream)) {
40
+ return link(text, url);
41
+ }
42
+
43
+ return fallback ? `${text} (${url})` : text;
44
+ };
package/src/ansi/index.ts CHANGED
@@ -4,11 +4,13 @@
4
4
 
5
5
  // Styling
6
6
  export { chalk } from "#/ansi/chalk.ts";
7
- export { supportsColor } from "#/ansi/supports-color.ts";
7
+ export { supportsColor } from "#/ansi/chalk.ts";
8
+ export { hyperlink } from "#/ansi/hyperlink.ts";
8
9
  export * from "#/ansi/sgr.ts";
9
10
 
10
11
  // Escape sequences (raw building blocks + named sequences)
11
12
  export * from "#/ansi/escapes.ts";
13
+ export * from "#/ansi/osc.ts";
12
14
  export { cliCursor } from "#/ansi/cursor.ts";
13
15
 
14
16
  // Measurement
@@ -0,0 +1,77 @@
1
+ import { pathToFileURL } from "node:url";
2
+
3
+ import { ESC, OSC, ST } from "#/ansi/escapes.ts";
4
+
5
+ export type ClipboardSelection = "clipboard" | "primary" | "selection";
6
+ export type TerminalProgressState = "inactive" | "normal" | "error" | "indeterminate" | "paused";
7
+
8
+ const clipboardCode: Record<ClipboardSelection, string> = {
9
+ clipboard: "c",
10
+ primary: "p",
11
+ selection: "s",
12
+ };
13
+
14
+ const progressCode: Record<TerminalProgressState, number> = {
15
+ inactive: 0,
16
+ normal: 1,
17
+ error: 2,
18
+ indeterminate: 3,
19
+ paused: 4,
20
+ };
21
+
22
+ const field = (value: string): string => value.replace(/[\u0000-\u001F\u007F-\u009F]/g, "");
23
+
24
+ const command = (code: number, payload: string): string => `${OSC}${code};${payload}${ST}`;
25
+
26
+ /** Change the terminal window title (OSC 2). */
27
+ export const setWindowTitle = (title: string): string => command(2, field(title));
28
+
29
+ /** Announce the current directory as a file URI (OSC 7). */
30
+ export const setWorkingDirectory = (directory: URL | string): string => {
31
+ const uri =
32
+ directory instanceof URL
33
+ ? directory
34
+ : directory.startsWith("file:")
35
+ ? new URL(directory)
36
+ : pathToFileURL(directory);
37
+ if (uri.protocol !== "file:") throw new Error("Terminal working directory must be a file URI");
38
+ return command(7, uri.href);
39
+ };
40
+
41
+ /** Show a desktop notification (OSC 9). */
42
+ export const notify = (title: string): string => {
43
+ const safe = field(title);
44
+ return command(9, /^\d+;/.test(safe) ? ` ${safe}` : safe);
45
+ };
46
+
47
+ /** Change the terminal pointer shape (OSC 22; terminal support varies). */
48
+ export const setPointerShape = (shape: string): string => command(22, field(shape));
49
+
50
+ /** Write UTF-8 text to a terminal clipboard using OSC 52. */
51
+ export const setClipboard = (text: string, selection: ClipboardSelection = "clipboard"): string =>
52
+ command(52, `${clipboardCode[selection]};${Buffer.from(text).toString("base64")}`);
53
+
54
+ /** Request clipboard contents. The response is another OSC 52 sequence. */
55
+ export const queryClipboard = (selection: ClipboardSelection = "clipboard"): string =>
56
+ command(52, `${clipboardCode[selection]};?`);
57
+
58
+ /** Clear a terminal clipboard. */
59
+ export const clearClipboard = (selection: ClipboardSelection = "clipboard"): string =>
60
+ command(52, `${clipboardCode[selection]};`);
61
+
62
+ /** Report terminal-native progress using OSC 9;4. */
63
+ export const setTerminalProgress = (state: TerminalProgressState, value?: number): string => {
64
+ const code = progressCode[state];
65
+ if (state === "inactive" || state === "indeterminate") {
66
+ return command(9, `4;${code}`);
67
+ }
68
+ if (value === undefined && (state === "error" || state === "paused")) {
69
+ return command(9, `4;${code}`);
70
+ }
71
+ const percentage = Math.max(0, Math.min(100, Math.round(value ?? 0)));
72
+ return command(9, `4;${code};${percentage}`);
73
+ };
74
+
75
+ /** Wrap a sequence for transport through tmux's DCS passthrough. */
76
+ export const tmuxPassthrough = (sequence: string): string =>
77
+ `${ESC}Ptmux;${sequence.replaceAll(ESC, `${ESC}${ESC}`)}${ST}`;
@@ -8,6 +8,7 @@ import { isFullwidthGrapheme } from "#/ansi/east-asian-width.ts";
8
8
  // `src/ansi-tokenizer.ts` (also behind sanitize-ansi), and grapheme widths
9
9
  // come from `east-asian-width.ts` — one grammar, one width table.
10
10
  import { BEL, C1_ST, ESC } from "#/ansi/escapes.ts";
11
+ import { graphemes } from "#/ansi/graphemes.ts";
11
12
  import { codes as sgrCodes, foreground, background, styles } from "#/ansi/sgr.ts";
12
13
 
13
14
  // Single-character introducer suffixes (`ESC [` = CSI, `ESC ]` = OSC).
@@ -119,8 +120,6 @@ export function isIntensityCode(code: AnsiCode): boolean {
119
120
  return code.code === styles.bold.open || code.code === styles.dim.open;
120
121
  }
121
122
 
122
- const segmenter = new Intl.Segmenter(undefined, { granularity: "grapheme" });
123
-
124
123
  /**
125
124
  Splits compound SGR sequences like `\u001B[1;3;31m` into individual components.
126
125
  */
@@ -139,7 +138,7 @@ function splitCompoundSgrSequences(code: string): string[] {
139
138
  for (let index = 0; index < codeParts.length; index++) {
140
139
  const rawCode = codeParts[index]!;
141
140
  // Keep 8-bit and 24-bit color codes (containing multiple ";") together
142
- if (rawCode === "38" || rawCode === "48") {
141
+ if (rawCode === "38" || rawCode === "48" || rawCode === "58") {
143
142
  if (index + 2 < codeParts.length && codeParts[index + 1] === "5") {
144
143
  // 8-bit color, followed by another number
145
144
  result.push(codeParts.slice(index, index + 3).join(";"));
@@ -171,7 +170,7 @@ export function tokenize(string: string, endChar = Number.POSITIVE_INFINITY): To
171
170
 
172
171
  outer: for (const ansiToken of tokenizeAnsi(string)) {
173
172
  if (ansiToken.type === "text") {
174
- for (const { segment } of segmenter.segment(ansiToken.value)) {
173
+ for (const segment of graphemes(ansiToken.value)) {
175
174
  const fullWidth = isFullwidthGrapheme(segment, segment.codePointAt(0)!);
176
175
  result.push({
177
176
  type: "char",
@@ -0,0 +1,34 @@
1
+ import type { ColorSupportLevel } from "#/capabilities/detect.ts";
2
+ import { colorProfileFromLevel, type ColorProfile } from "#/screen/color-profile.ts";
3
+
4
+ /** A user-selected output policy. `auto` follows the target stream. */
5
+ export type ColorPolicy = "auto" | ColorProfile;
6
+
7
+ /** The three distinct inputs and result of terminal color negotiation. */
8
+ export type ColorState = {
9
+ /** The stream/terminal fact after environment and query detection. */
10
+ readonly detected: ColorProfile;
11
+ /** The application's policy for this render instance. */
12
+ readonly policy: ColorPolicy;
13
+ /** The profile the serializer must actually emit. */
14
+ readonly effective: ColorProfile;
15
+ };
16
+
17
+ export function resolveColorProfile(
18
+ detectedLevel: ColorSupportLevel,
19
+ policy: ColorPolicy = "auto",
20
+ ): ColorProfile {
21
+ return policy === "auto" ? colorProfileFromLevel(detectedLevel) : policy;
22
+ }
23
+
24
+ export function colorState(
25
+ capabilities: { readonly color: { readonly level: ColorSupportLevel } },
26
+ policy: ColorPolicy = "auto",
27
+ ): ColorState {
28
+ const detected = colorProfileFromLevel(capabilities.color.level);
29
+ return {
30
+ detected,
31
+ policy,
32
+ effective: policy === "auto" ? detected : policy,
33
+ };
34
+ }