@opencode/ui 2.0.23 → 2.0.25
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.
- package/dist/context/marked-parser.d.ts +2 -0
- package/dist/i18n/en.d.ts +1 -0
- package/dist/icons/catalog.d.ts +36 -0
- package/dist/navigation/segmented-control/segmented-control.d.ts +21 -2
- package/package.json +2 -1
- package/src/actions/button/button.css +11 -9
- package/src/actions/button/button.tsx +1 -0
- package/src/actions/icon-button/icon-button.css +1 -5
- package/src/actions/icon-button/icon-button.tsx +1 -0
- package/src/actions/split-button/split-button.tsx +3 -0
- package/src/actions/submit.css +14 -12
- package/src/components/animated-number.tsx +13 -0
- package/src/components/app-icon.tsx +2 -0
- package/src/components/card.tsx +21 -0
- package/src/components/collapsible.tsx +1 -0
- package/src/components/context-menu.tsx +34 -0
- package/src/components/dock-surface.tsx +3 -0
- package/src/components/file-icon.tsx +12 -0
- package/src/components/image-preview.tsx +1 -0
- package/src/components/list.tsx +32 -0
- package/src/components/motion-spring.tsx +5 -0
- package/src/components/popover.tsx +12 -0
- package/src/components/provider-icon.tsx +1 -0
- package/src/components/resize-handle.tsx +8 -0
- package/src/components/scroll-view.tsx +39 -0
- package/src/components/spinner.tsx +2 -0
- package/src/components/text-field.tsx +6 -0
- package/src/components/text-reveal.tsx +19 -0
- package/src/components/text-strikethrough.tsx +8 -0
- package/src/components/typewriter.tsx +4 -0
- package/src/context/dialog.tsx +12 -0
- package/src/context/file.tsx +1 -0
- package/src/context/helper.tsx +4 -0
- package/src/context/i18n.tsx +20 -0
- package/src/context/index.ts +3 -0
- package/src/context/marked-base.tsx +9 -1
- package/src/context/marked-parser.tsx +57 -18
- package/src/context/marked.tsx +3 -0
- package/src/context/worker-pool.tsx +2 -0
- package/src/data-display/avatar/avatar.tsx +4 -0
- package/src/data-display/badge/badge.tsx +1 -0
- package/src/data-display/diff-changes/diff-changes.tsx +2 -0
- package/src/data-display/keybind/keybind.tsx +1 -0
- package/src/data-display/line-comment/line-comment.tsx +22 -0
- package/src/data-display/project-avatar/project-avatar.tsx +10 -0
- package/src/feedback/loader/loader.tsx +1 -0
- package/src/feedback/toast/description.ts +1 -0
- package/src/forms/checkbox/checkbox.tsx +1 -0
- package/src/forms/field/field.tsx +8 -0
- package/src/forms/inline-input/inline-input.tsx +4 -0
- package/src/forms/radio/radio.tsx +2 -0
- package/src/forms/select/select.tsx +11 -0
- package/src/forms/switch/switch.tsx +1 -0
- package/src/forms/text-input/text-input.tsx +3 -0
- package/src/hooks/create-auto-scroll.tsx +34 -0
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-filtered-list.tsx +14 -0
- package/src/i18n/en.ts +1 -0
- package/src/i18n/no.ts +1 -0
- package/src/icons/catalog.ts +36 -0
- package/src/icons/icon/icon.tsx +2 -0
- package/src/layout/divider/divider.tsx +1 -0
- package/src/navigation/menu/menu.tsx +13 -0
- package/src/navigation/segmented-control/segmented-control.css +37 -4
- package/src/navigation/segmented-control/segmented-control.tsx +210 -14
- package/src/navigation/tab-state-indicator.tsx +1 -0
- package/src/navigation/tabs/tabs-current.css +1 -5
- package/src/navigation/tabs/tabs.css +5 -22
- package/src/navigation/tabs/tabs.tsx +18 -0
- package/src/overlays/dialog/dialog.tsx +3 -0
- package/src/overlays/tooltip/tooltip.tsx +15 -0
- package/src/storybook/scaffold.tsx +3 -0
- package/src/styles/theme.css +1 -1
- package/src/theme/color.ts +27 -0
- package/src/theme/context.tsx +58 -0
- package/src/theme/default-themes.ts +35 -0
- package/src/theme/index.ts +3 -0
- package/src/theme/loader.ts +11 -0
- package/src/theme/resolve.ts +28 -0
- package/src/theme/v2/foreground.ts +8 -0
- package/src/theme/v2/resolve.ts +10 -0
- package/src/typography/text-shimmer/text-shimmer.tsx +1 -0
|
@@ -37,40 +37,75 @@ import vesperThemeJson from "./themes/vesper.json"
|
|
|
37
37
|
import zenburnThemeJson from "./themes/zenburn.json"
|
|
38
38
|
|
|
39
39
|
export const oc2Theme = oc2ThemeJson as DesktopTheme
|
|
40
|
+
|
|
40
41
|
export const amoledTheme = amoledThemeJson as DesktopTheme
|
|
42
|
+
|
|
41
43
|
export const auraTheme = auraThemeJson as DesktopTheme
|
|
44
|
+
|
|
42
45
|
export const ayuTheme = ayuThemeJson as DesktopTheme
|
|
46
|
+
|
|
43
47
|
export const carbonfoxTheme = carbonfoxThemeJson as DesktopTheme
|
|
48
|
+
|
|
44
49
|
export const catppuccinTheme = catppuccinThemeJson as DesktopTheme
|
|
50
|
+
|
|
45
51
|
export const catppuccinFrappeTheme = catppuccinFrappeThemeJson as DesktopTheme
|
|
52
|
+
|
|
46
53
|
export const catppuccinMacchiatoTheme = catppuccinMacchiatoThemeJson as DesktopTheme
|
|
54
|
+
|
|
47
55
|
export const cobalt2Theme = cobalt2ThemeJson as DesktopTheme
|
|
56
|
+
|
|
48
57
|
export const cursorTheme = cursorThemeJson as DesktopTheme
|
|
58
|
+
|
|
49
59
|
export const draculaTheme = draculaThemeJson as DesktopTheme
|
|
60
|
+
|
|
50
61
|
export const everforestTheme = everforestThemeJson as DesktopTheme
|
|
62
|
+
|
|
51
63
|
export const flexokiTheme = flexokiThemeJson as DesktopTheme
|
|
64
|
+
|
|
52
65
|
export const githubTheme = githubThemeJson as DesktopTheme
|
|
66
|
+
|
|
53
67
|
export const gruvboxTheme = gruvboxThemeJson as DesktopTheme
|
|
68
|
+
|
|
54
69
|
export const kanagawaTheme = kanagawaThemeJson as DesktopTheme
|
|
70
|
+
|
|
55
71
|
export const lucentOrngTheme = lucentOrngThemeJson as DesktopTheme
|
|
72
|
+
|
|
56
73
|
export const materialTheme = materialThemeJson as DesktopTheme
|
|
74
|
+
|
|
57
75
|
export const matrixTheme = matrixThemeJson as DesktopTheme
|
|
76
|
+
|
|
58
77
|
export const mercuryTheme = mercuryThemeJson as DesktopTheme
|
|
78
|
+
|
|
59
79
|
export const monokaiTheme = monokaiThemeJson as DesktopTheme
|
|
80
|
+
|
|
60
81
|
export const nightowlTheme = nightowlThemeJson as DesktopTheme
|
|
82
|
+
|
|
61
83
|
export const nordTheme = nordThemeJson as DesktopTheme
|
|
84
|
+
|
|
62
85
|
export const oneDarkTheme = oneDarkThemeJson as DesktopTheme
|
|
86
|
+
|
|
63
87
|
export const oneDarkProTheme = oneDarkProThemeJson as DesktopTheme
|
|
88
|
+
|
|
64
89
|
export const orngTheme = orngThemeJson as DesktopTheme
|
|
90
|
+
|
|
65
91
|
export const osakaJadeTheme = osakaJadeThemeJson as DesktopTheme
|
|
92
|
+
|
|
66
93
|
export const palenightTheme = palenightThemeJson as DesktopTheme
|
|
94
|
+
|
|
67
95
|
export const rosepineTheme = rosepineThemeJson as DesktopTheme
|
|
96
|
+
|
|
68
97
|
export const shadesOfPurpleTheme = shadesOfPurpleThemeJson as DesktopTheme
|
|
98
|
+
|
|
69
99
|
export const solarizedTheme = solarizedThemeJson as DesktopTheme
|
|
100
|
+
|
|
70
101
|
export const synthwave84Theme = synthwave84ThemeJson as DesktopTheme
|
|
102
|
+
|
|
71
103
|
export const tokyonightTheme = tokyonightThemeJson as DesktopTheme
|
|
104
|
+
|
|
72
105
|
export const vercelTheme = vercelThemeJson as DesktopTheme
|
|
106
|
+
|
|
73
107
|
export const vesperTheme = vesperThemeJson as DesktopTheme
|
|
108
|
+
|
|
74
109
|
export const zenburnTheme = zenburnThemeJson as DesktopTheme
|
|
75
110
|
|
|
76
111
|
export const DEFAULT_THEMES: Record<string, DesktopTheme> = {
|
package/src/theme/index.ts
CHANGED
|
@@ -32,8 +32,11 @@ export {
|
|
|
32
32
|
} from "./color"
|
|
33
33
|
|
|
34
34
|
export { resolveThemeVariant, resolveTheme, themeToCss } from "./resolve"
|
|
35
|
+
|
|
35
36
|
export { resolveThemeVariantV2, resolveThemeV2, themeV2ToCss, generateV2Primitives } from "./v2/resolve"
|
|
37
|
+
|
|
36
38
|
export { applyTheme, loadThemeFromUrl, getActiveTheme, removeTheme, setColorScheme } from "./loader"
|
|
39
|
+
|
|
37
40
|
export { ThemeProvider, useTheme, type ColorScheme } from "./context"
|
|
38
41
|
|
|
39
42
|
export {
|
package/src/theme/loader.ts
CHANGED
|
@@ -3,16 +3,20 @@ import { resolveThemeVariant, themeToCss } from "./resolve"
|
|
|
3
3
|
import { resolveThemeVariantV2, themeV2ToCss } from "./v2/resolve"
|
|
4
4
|
|
|
5
5
|
let activeTheme: DesktopTheme | null = null
|
|
6
|
+
|
|
6
7
|
const THEME_STYLE_ID = "opencode-theme"
|
|
7
8
|
|
|
8
9
|
function ensureLoaderStyleElement(): HTMLStyleElement {
|
|
9
10
|
const existing = document.getElementById(THEME_STYLE_ID) as HTMLStyleElement | null
|
|
11
|
+
|
|
10
12
|
if (existing) {
|
|
11
13
|
return existing
|
|
12
14
|
}
|
|
15
|
+
|
|
13
16
|
const element = document.createElement("style")
|
|
14
17
|
element.id = THEME_STYLE_ID
|
|
15
18
|
document.head.appendChild(element)
|
|
19
|
+
|
|
16
20
|
return element
|
|
17
21
|
}
|
|
18
22
|
|
|
@@ -77,29 +81,36 @@ html[data-theme="${themeId}"] {
|
|
|
77
81
|
|
|
78
82
|
export async function loadThemeFromUrl(url: string): Promise<DesktopTheme> {
|
|
79
83
|
const response = await fetch(url)
|
|
84
|
+
|
|
80
85
|
if (!response.ok) {
|
|
81
86
|
throw new Error(`Failed to load theme from ${url}: ${response.statusText}`)
|
|
82
87
|
}
|
|
88
|
+
|
|
83
89
|
return response.json()
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
export function getActiveTheme(): DesktopTheme | null {
|
|
87
93
|
const activeId = document.documentElement.getAttribute("data-theme")
|
|
94
|
+
|
|
88
95
|
if (!activeId) {
|
|
89
96
|
return null
|
|
90
97
|
}
|
|
98
|
+
|
|
91
99
|
if (activeTheme?.id === activeId) {
|
|
92
100
|
return activeTheme
|
|
93
101
|
}
|
|
102
|
+
|
|
94
103
|
return null
|
|
95
104
|
}
|
|
96
105
|
|
|
97
106
|
export function removeTheme(): void {
|
|
98
107
|
activeTheme = null
|
|
99
108
|
const existingElement = document.getElementById(THEME_STYLE_ID)
|
|
109
|
+
|
|
100
110
|
if (existingElement) {
|
|
101
111
|
existingElement.remove()
|
|
102
112
|
}
|
|
113
|
+
|
|
103
114
|
document.documentElement.removeAttribute("data-theme")
|
|
104
115
|
}
|
|
105
116
|
|
package/src/theme/resolve.ts
CHANGED
|
@@ -13,48 +13,62 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res
|
|
|
13
13
|
const error = generateScale(colors.error, isDark)
|
|
14
14
|
const info = generateScale(colors.info, isDark)
|
|
15
15
|
const interactive = generateScale(colors.interactive, isDark)
|
|
16
|
+
|
|
16
17
|
const amber = generateScale(
|
|
17
18
|
shift(colors.warning, isDark ? { h: -16, l: -0.058, c: 1.14 } : { h: -22, l: -0.082, c: 0.94 }),
|
|
18
19
|
isDark,
|
|
19
20
|
)
|
|
21
|
+
|
|
20
22
|
const blue = generateScale(shift(colors.interactive, { h: -12, l: 0.128, c: 1.12 }), isDark)
|
|
23
|
+
|
|
21
24
|
const diffAdd = generateScale(
|
|
22
25
|
colors.diffAdd ?? shift(colors.success, { c: isDark ? 0.7 : 0.55, l: isDark ? -0.18 : 0.14 }),
|
|
23
26
|
isDark,
|
|
24
27
|
)
|
|
28
|
+
|
|
25
29
|
const diffDelete = generateScale(
|
|
26
30
|
colors.diffDelete ?? shift(colors.error, { c: isDark ? 0.82 : 0.7, l: isDark ? -0.08 : 0.08 }),
|
|
27
31
|
isDark,
|
|
28
32
|
)
|
|
33
|
+
|
|
29
34
|
const ink = colors.ink ?? colors.neutral
|
|
30
35
|
const tint = colors.compact ? hexToOklch(ink) : undefined
|
|
36
|
+
|
|
31
37
|
const body = tint
|
|
32
38
|
? shift(ink, {
|
|
33
39
|
l: isDark ? Math.max(0, 0.88 - tint.l) * 0.4 : -Math.max(0, tint.l - 0.18) * 0.24,
|
|
34
40
|
c: isDark ? 1.04 : 1.02,
|
|
35
41
|
})
|
|
36
42
|
: undefined
|
|
43
|
+
|
|
37
44
|
const backgroundOverride = overrides["background-base"]
|
|
38
45
|
const backgroundHex = getHex(backgroundOverride)
|
|
39
46
|
const overlay = Boolean(backgroundOverride) && !backgroundHex
|
|
47
|
+
|
|
40
48
|
const content = (seed: HexColor, scale: HexColor[]) => {
|
|
41
49
|
const base = hexToOklch(seed)
|
|
42
50
|
const value = isDark ? (base.l > 0.84 ? shift(seed, { c: 1.18 }) : scale[10]) : scale[10]
|
|
51
|
+
|
|
43
52
|
return shift(value, { l: isDark ? 0.034 : -0.024, c: isDark ? 1.3 : 1.18 })
|
|
44
53
|
}
|
|
54
|
+
|
|
45
55
|
const modified = () => {
|
|
46
56
|
if (!colors.compact) return isDark ? "#ffba92" : "#FF8C00"
|
|
47
57
|
const warningHue = hexToOklch(colors.warning).h
|
|
48
58
|
const deleteHue = hexToOklch(colors.diffDelete ?? colors.error).h
|
|
49
59
|
const delta = Math.abs(((((deleteHue - warningHue) % 360) + 540) % 360) - 180)
|
|
60
|
+
|
|
50
61
|
if (delta < 48) return isDark ? "#ffba92" : "#FF8C00"
|
|
62
|
+
|
|
51
63
|
return content(colors.warning, warning)
|
|
52
64
|
}
|
|
65
|
+
|
|
53
66
|
const surface = (
|
|
54
67
|
seed: HexColor,
|
|
55
68
|
alpha: { base: number; weak: number; weaker: number; strong: number; stronger: number },
|
|
56
69
|
) => {
|
|
57
70
|
const base = alphaTone(seed, alpha.base)
|
|
71
|
+
|
|
58
72
|
return {
|
|
59
73
|
base,
|
|
60
74
|
weak: alphaTone(seed, alpha.weak),
|
|
@@ -63,11 +77,15 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res
|
|
|
63
77
|
stronger: alphaTone(seed, alpha.stronger),
|
|
64
78
|
}
|
|
65
79
|
}
|
|
80
|
+
|
|
66
81
|
const background = backgroundHex ?? neutral[0]
|
|
82
|
+
|
|
67
83
|
const alphaTone = (color: HexColor, alpha: number) =>
|
|
68
84
|
overlay ? (withAlpha(color, alpha) as ColorValue) : blend(color, background, alpha)
|
|
85
|
+
|
|
69
86
|
const borderTone = (light: number, dark: number) =>
|
|
70
87
|
alphaTone(ink, isDark ? Math.min(1, dark + 0.024 + (colors.compact ? 0.08 : 0)) : Math.min(1, light + 0.024))
|
|
88
|
+
|
|
71
89
|
const diffHiddenSurface = surface(
|
|
72
90
|
isDark ? shift(colors.interactive, { c: 0.55, l: 0 }) : shift(colors.interactive, { c: 0.45, l: 0.08 }),
|
|
73
91
|
isDark
|
|
@@ -93,21 +111,27 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res
|
|
|
93
111
|
const infob = info[isDark ? 6 : 4]
|
|
94
112
|
const infow = info[isDark ? 5 : 3]
|
|
95
113
|
const infos = info[10]
|
|
114
|
+
|
|
96
115
|
const lum = (hex: HexColor) => {
|
|
97
116
|
const rgb = hexToRgb(hex)
|
|
98
117
|
const lift = (v: number) => (v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4))
|
|
118
|
+
|
|
99
119
|
return 0.2126 * lift(rgb.r) + 0.7152 * lift(rgb.g) + 0.0722 * lift(rgb.b)
|
|
100
120
|
}
|
|
121
|
+
|
|
101
122
|
const hit = (a: HexColor, b: HexColor) => {
|
|
102
123
|
const x = lum(a)
|
|
103
124
|
const y = lum(b)
|
|
104
125
|
const light = Math.max(x, y)
|
|
105
126
|
const dark = Math.min(x, y)
|
|
127
|
+
|
|
106
128
|
return (light + 0.05) / (dark + 0.05)
|
|
107
129
|
}
|
|
130
|
+
|
|
108
131
|
const on = (fill: HexColor) => {
|
|
109
132
|
const light = "#ffffff" as HexColor
|
|
110
133
|
const dark = "#000000" as HexColor
|
|
134
|
+
|
|
111
135
|
return hit(light, fill) > hit(dark, fill) ? light : dark
|
|
112
136
|
}
|
|
113
137
|
|
|
@@ -432,6 +456,7 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res
|
|
|
432
456
|
|
|
433
457
|
if (colors.compact && "text-weak" in overrides && !("text-weaker" in overrides)) {
|
|
434
458
|
const weak = tokens["text-weak"]
|
|
459
|
+
|
|
435
460
|
if (weak.startsWith("#")) {
|
|
436
461
|
tokens["text-weaker"] = shift(weak as HexColor, { l: isDark ? -0.12 : 0.12, c: 0.75 })
|
|
437
462
|
} else {
|
|
@@ -443,6 +468,7 @@ export function resolveThemeVariant(variant: ThemeVariant, isDark: boolean): Res
|
|
|
443
468
|
if (!("markdown-text" in overrides)) {
|
|
444
469
|
tokens["markdown-text"] = tokens["text-base"]
|
|
445
470
|
}
|
|
471
|
+
|
|
446
472
|
if (!("markdown-code-block" in overrides)) {
|
|
447
473
|
tokens["markdown-code-block"] = tokens["text-base"]
|
|
448
474
|
}
|
|
@@ -472,6 +498,7 @@ interface ThemeColors {
|
|
|
472
498
|
|
|
473
499
|
function getColors(variant: ThemeVariant): ThemeColors {
|
|
474
500
|
const input = variant as { palette?: unknown; seeds?: unknown }
|
|
501
|
+
|
|
475
502
|
if (input.palette && input.seeds) {
|
|
476
503
|
throw new Error("Theme variant cannot define both `palette` and `seeds`")
|
|
477
504
|
}
|
|
@@ -523,6 +550,7 @@ function generateNeutralAlphaScale(neutralScale: HexColor[], isDark: boolean): H
|
|
|
523
550
|
|
|
524
551
|
function getHex(value: ColorValue | undefined): HexColor | undefined {
|
|
525
552
|
if (!value?.startsWith("#")) return
|
|
553
|
+
|
|
526
554
|
return value as HexColor
|
|
527
555
|
}
|
|
528
556
|
|
|
@@ -8,23 +8,30 @@ const greyRef = (step: number): V2ColorValue => `var(--v2-grey-${step})`
|
|
|
8
8
|
|
|
9
9
|
function greyHex(primitives: Record<string, V2ColorValue>, step: number) {
|
|
10
10
|
const hex = primitives[`v2-grey-${step}`]
|
|
11
|
+
|
|
11
12
|
if (typeof hex === "string" && hex.startsWith("#")) return hex as HexColor
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
function resolveGreyRef(value: V2ColorValue, primitives: Record<string, V2ColorValue>) {
|
|
15
16
|
const step = value.match(/^var\(--v2-grey-(\d+)\)$/)?.[1]
|
|
17
|
+
|
|
16
18
|
if (!step) throw new Error(`Expected grey primitive ref, got ${value}`)
|
|
17
19
|
const hex = greyHex(primitives, Number(step))
|
|
20
|
+
|
|
18
21
|
if (!hex) throw new Error(`Missing grey primitive v2-grey-${step}`)
|
|
22
|
+
|
|
19
23
|
return hex
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
function pickGrey(primitives: Record<string, V2ColorValue>, background: HexColor, minContrast: number, target: number) {
|
|
23
27
|
const matches = GREY_STEPS.filter((step) => {
|
|
24
28
|
const hex = greyHex(primitives, step)
|
|
29
|
+
|
|
25
30
|
return hex && contrastRatio(hex, background) >= minContrast
|
|
26
31
|
})
|
|
32
|
+
|
|
27
33
|
if (matches.length === 0) return target
|
|
34
|
+
|
|
28
35
|
return matches.reduce((best, step) => (Math.abs(step - target) < Math.abs(best - target) ? step : best))
|
|
29
36
|
}
|
|
30
37
|
|
|
@@ -35,6 +42,7 @@ export function mapV2Foreground(
|
|
|
35
42
|
overrides: Record<string, ColorValue> = {},
|
|
36
43
|
): Record<string, V2ColorValue> {
|
|
37
44
|
const tint = hexToOklch(ink)
|
|
45
|
+
|
|
38
46
|
const body = shift(ink, {
|
|
39
47
|
l: isDark ? Math.max(0, 0.88 - tint.l) * 0.4 : -Math.max(0, tint.l - 0.18) * 0.24,
|
|
40
48
|
c: isDark ? 1.04 : 1.02,
|
package/src/theme/v2/resolve.ts
CHANGED
|
@@ -30,6 +30,7 @@ function clamp(v: number, min: number, max: number) {
|
|
|
30
30
|
function generateV2HueScale(seed: HexColor, isDark: boolean): HexColor[] {
|
|
31
31
|
const base = hexToOklch(seed)
|
|
32
32
|
const chromaBoost = isDark ? 1 : 1.05
|
|
33
|
+
|
|
33
34
|
const lightSteps = [
|
|
34
35
|
0.99,
|
|
35
36
|
0.965,
|
|
@@ -44,6 +45,7 @@ function generateV2HueScale(seed: HexColor, isDark: boolean): HexColor[] {
|
|
|
44
45
|
clamp(base.l - 0.34, 0.12, 0.3),
|
|
45
46
|
clamp(base.l - 0.4, 0.08, 0.22),
|
|
46
47
|
]
|
|
48
|
+
|
|
47
49
|
const chromaMultipliers = [0.28, 0.48, 0.68, 0.86, 1.02, 1.28, 1.34, 1.28, 1.18, 1.08, 0.98, 0.88]
|
|
48
50
|
|
|
49
51
|
return lightSteps.map((l, i) =>
|
|
@@ -58,20 +60,24 @@ function generateV2HueScale(seed: HexColor, isDark: boolean): HexColor[] {
|
|
|
58
60
|
/** Grey ramp: 100 = lightest, 1200 = darkest. Derived from palette neutral → ink like v1. */
|
|
59
61
|
function generateV2NeutralScale(neutral: HexColor, ink: HexColor, isDark: boolean): HexColor[] {
|
|
60
62
|
const scale = generateNeutralScale(neutral, isDark, ink)
|
|
63
|
+
|
|
61
64
|
return isDark ? scale.toReversed() : scale
|
|
62
65
|
}
|
|
63
66
|
|
|
64
67
|
function assignHueRamp(prefix: string, scale: HexColor[]): Record<string, V2ColorValue> {
|
|
65
68
|
const tokens: Record<string, V2ColorValue> = {}
|
|
69
|
+
|
|
66
70
|
for (let i = 0; i < V2_STEPS.length; i++) {
|
|
67
71
|
tokens[`v2-${prefix}-${V2_STEPS[i]}`] = scale[i]!
|
|
68
72
|
}
|
|
73
|
+
|
|
69
74
|
return tokens
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
function readPalette(variant: ThemeVariant): PaletteInput {
|
|
73
78
|
if ("palette" in variant && variant.palette) {
|
|
74
79
|
const palette = variant.palette
|
|
80
|
+
|
|
75
81
|
return {
|
|
76
82
|
neutral: palette.neutral,
|
|
77
83
|
ink: palette.ink,
|
|
@@ -86,8 +92,10 @@ function readPalette(variant: ThemeVariant): PaletteInput {
|
|
|
86
92
|
diffDelete: palette.diffDelete ?? palette.error,
|
|
87
93
|
}
|
|
88
94
|
}
|
|
95
|
+
|
|
89
96
|
if ("seeds" in variant && variant.seeds) {
|
|
90
97
|
const seeds = variant.seeds
|
|
98
|
+
|
|
91
99
|
return {
|
|
92
100
|
neutral: seeds.neutral,
|
|
93
101
|
ink: seeds.neutral,
|
|
@@ -102,6 +110,7 @@ function readPalette(variant: ThemeVariant): PaletteInput {
|
|
|
102
110
|
diffDelete: seeds.diffDelete,
|
|
103
111
|
}
|
|
104
112
|
}
|
|
113
|
+
|
|
105
114
|
throw new Error("Theme variant requires `palette` or `seeds`")
|
|
106
115
|
}
|
|
107
116
|
|
|
@@ -136,6 +145,7 @@ export function resolveThemeVariantV2(variant: ThemeVariant, isDark: boolean): R
|
|
|
136
145
|
const primitives = generateV2Primitives(variant, isDark)
|
|
137
146
|
const semantics = mapV2Semantics(isDark)
|
|
138
147
|
const foreground = mapV2Foreground(readPalette(variant).ink, isDark, primitives, variant.overrides)
|
|
148
|
+
|
|
139
149
|
return mergeV2Tokens(primitives, semantics, foreground, variant.v2Overrides ?? {})
|
|
140
150
|
}
|
|
141
151
|
|