@opencode/ui 0.0.0-dev-20607 → 0.0.0-dev-20609

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 (68) hide show
  1. package/package.json +1 -1
  2. package/src/actions/button/button.tsx +1 -0
  3. package/src/actions/icon-button/icon-button.tsx +1 -0
  4. package/src/actions/split-button/split-button.tsx +3 -0
  5. package/src/components/animated-number.tsx +13 -0
  6. package/src/components/app-icon.tsx +2 -0
  7. package/src/components/card.tsx +21 -0
  8. package/src/components/collapsible.tsx +1 -0
  9. package/src/components/context-menu.tsx +34 -0
  10. package/src/components/dock-surface.tsx +3 -0
  11. package/src/components/file-icon.tsx +12 -0
  12. package/src/components/image-preview.tsx +1 -0
  13. package/src/components/list.tsx +32 -0
  14. package/src/components/motion-spring.tsx +5 -0
  15. package/src/components/popover.tsx +12 -0
  16. package/src/components/provider-icon.tsx +1 -0
  17. package/src/components/resize-handle.tsx +8 -0
  18. package/src/components/scroll-view.tsx +39 -0
  19. package/src/components/spinner.tsx +2 -0
  20. package/src/components/text-field.tsx +6 -0
  21. package/src/components/text-reveal.tsx +19 -0
  22. package/src/components/text-strikethrough.tsx +8 -0
  23. package/src/components/typewriter.tsx +4 -0
  24. package/src/context/dialog.tsx +12 -0
  25. package/src/context/file.tsx +1 -0
  26. package/src/context/helper.tsx +4 -0
  27. package/src/context/i18n.tsx +20 -0
  28. package/src/context/index.ts +3 -0
  29. package/src/context/marked-base.tsx +7 -0
  30. package/src/context/marked-parser.tsx +7 -0
  31. package/src/context/marked.tsx +3 -0
  32. package/src/context/worker-pool.tsx +2 -0
  33. package/src/data-display/avatar/avatar.tsx +4 -0
  34. package/src/data-display/badge/badge.tsx +1 -0
  35. package/src/data-display/diff-changes/diff-changes.tsx +2 -0
  36. package/src/data-display/keybind/keybind.tsx +1 -0
  37. package/src/data-display/line-comment/line-comment.tsx +22 -0
  38. package/src/data-display/project-avatar/project-avatar.tsx +10 -0
  39. package/src/feedback/loader/loader.tsx +1 -0
  40. package/src/feedback/toast/description.ts +1 -0
  41. package/src/forms/checkbox/checkbox.tsx +1 -0
  42. package/src/forms/field/field.tsx +8 -0
  43. package/src/forms/inline-input/inline-input.tsx +4 -0
  44. package/src/forms/radio/radio.tsx +2 -0
  45. package/src/forms/select/select.tsx +11 -0
  46. package/src/forms/switch/switch.tsx +1 -0
  47. package/src/forms/text-input/text-input.tsx +3 -0
  48. package/src/hooks/create-auto-scroll.tsx +34 -0
  49. package/src/hooks/index.ts +1 -0
  50. package/src/hooks/use-filtered-list.tsx +14 -0
  51. package/src/i18n/no.ts +1 -0
  52. package/src/layout/divider/divider.tsx +1 -0
  53. package/src/navigation/menu/menu.tsx +13 -0
  54. package/src/navigation/segmented-control/segmented-control.tsx +16 -0
  55. package/src/navigation/tab-state-indicator.tsx +1 -0
  56. package/src/navigation/tabs/tabs.tsx +18 -0
  57. package/src/overlays/dialog/dialog.tsx +3 -0
  58. package/src/overlays/tooltip/tooltip.tsx +15 -0
  59. package/src/storybook/scaffold.tsx +3 -0
  60. package/src/theme/color.ts +27 -0
  61. package/src/theme/context.tsx +58 -0
  62. package/src/theme/default-themes.ts +35 -0
  63. package/src/theme/index.ts +3 -0
  64. package/src/theme/loader.ts +11 -0
  65. package/src/theme/resolve.ts +28 -0
  66. package/src/theme/v2/foreground.ts +8 -0
  67. package/src/theme/v2/resolve.ts +10 -0
  68. package/src/typography/text-shimmer/text-shimmer.tsx +1 -0
@@ -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,
@@ -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
 
@@ -24,6 +24,7 @@ export const TextShimmer = <T extends ValidComponent = "span">(props: {
24
24
 
25
25
  if (active()) {
26
26
  setRun(true)
27
+
27
28
  return
28
29
  }
29
30