@opencode/ui 0.0.0-dev-20607 → 0.0.0-dev-20610
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/package.json +1 -1
- package/src/actions/button/button.tsx +1 -0
- package/src/actions/icon-button/icon-button.tsx +1 -0
- package/src/actions/split-button/split-button.tsx +3 -0
- 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 +7 -0
- package/src/context/marked-parser.tsx +7 -0
- 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/no.ts +1 -0
- package/src/layout/divider/divider.tsx +1 -0
- package/src/navigation/menu/menu.tsx +13 -0
- package/src/navigation/segmented-control/segmented-control.tsx +16 -0
- package/src/navigation/tab-state-indicator.tsx +1 -0
- 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/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
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
|
|