@kidsinai/kids-client 0.0.8 → 0.0.9

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "@kidsinai/kids-client",
4
- "version": "0.0.8",
4
+ "version": "0.0.9",
5
5
  "type": "module",
6
6
  "description": "Own-client TUI for Kids OpenCode — talks to local `opencode serve` via @opencode-ai/sdk v2 with kid-warm rendering, mission progress, permission dialog, and stderr-tail audit pipeline.",
7
7
  "license": "MIT",
@@ -31,11 +31,13 @@ const SPARKLE_ROW_BOTTOM = " ⭐ ✦ ⭐ ✦ "
31
31
 
32
32
  export function KidsLogo(): React.ReactElement {
33
33
  const theme = getTheme()
34
- // Colors approximate the brand logo. Use "Bright" variants so dark terminals pop.
35
- const cK = "cyanBright"
36
- const cI = "yellow"
37
- const cD = "greenBright"
38
- const cS = "magentaBright"
34
+ // Theme-driven DARK uses cyanBright/yellow/greenBright/magentaBright,
35
+ // LIGHT swaps to non-Bright variants (blue/yellow/green/magenta) that
36
+ // hold contrast on white macOS Terminal default.
37
+ const cK = theme.logoK
38
+ const cI = theme.logoI
39
+ const cD = theme.logoD
40
+ const cS = theme.logoS
39
41
  const gap = " " // 2-col gap between letters
40
42
  return (
41
43
  <Box flexDirection="column" alignItems="center">
@@ -1,10 +1,17 @@
1
1
  /**
2
- * Kid-warm color tokens. Hand-tuned to satisfy WCAG AA on most terminals
3
- * with dark backgrounds. High-contrast variant available via $KIDS_HC=1.
2
+ * Kid-warm color tokens. Three themes, picked automatically based on the
3
+ * terminal's background color (white-bg macOS Terminal vs dark-bg iTerm2
4
+ * vs anything we can't infer).
4
5
  *
5
- * Sourced conceptually from kids-tui-plugin/themes/kids-warm.json but
6
- * inlined here because Ink consumes raw ANSI/chalk color names, not
7
- * opentui theme JSON.
6
+ * Pick order:
7
+ * 1. KIDS_THEME=dark|light|hc (explicit override; wins)
8
+ * 2. KIDS_HC=1 (legacy flag, maps to "hc")
9
+ * 3. COLORFGBG env var (terminal hint; "fg;bg" — macOS Terminal,
10
+ * iTerm2, VS Code, gnome-terminal all set this)
11
+ * 4. fallback : "dark"
12
+ *
13
+ * Logo colors (logoK/I/D/S) are part of the theme so KidsLogo doesn't
14
+ * have to know which terminal it's on.
8
15
  */
9
16
 
10
17
  export interface Theme {
@@ -20,9 +27,15 @@ export interface Theme {
20
27
  system: string
21
28
  border: string
22
29
  stars: string
30
+ // Kids OpenCode brand mark, per-letter
31
+ logoK: string
32
+ logoI: string
33
+ logoD: string
34
+ logoS: string
23
35
  }
24
36
 
25
- const DEFAULT: Theme = {
37
+ /** Default vibrant on a dark terminal. */
38
+ const DARK: Theme = {
26
39
  fg: "white",
27
40
  bg: "black",
28
41
  fgDim: "gray",
@@ -35,9 +48,39 @@ const DEFAULT: Theme = {
35
48
  system: "blueBright",
36
49
  border: "yellow",
37
50
  stars: "yellowBright",
51
+ logoK: "cyanBright",
52
+ logoI: "yellow",
53
+ logoD: "greenBright",
54
+ logoS: "magentaBright",
38
55
  }
39
56
 
40
- const HIGH_CONTRAST: Theme = {
57
+ /**
58
+ * Light terminal (macOS Terminal.app default, light VS Code, etc.).
59
+ * Avoids Bright variants — they wash out to near-invisible on white.
60
+ * Uses non-Bright ANSI codes (33/32/36/35) which most terminals render
61
+ * as darker shades that hold contrast against white.
62
+ */
63
+ const LIGHT: Theme = {
64
+ fg: "black",
65
+ bg: "white",
66
+ fgDim: "blackBright", // dark gray — still readable on white
67
+ accent: "magenta", // not yellow/cyan, those wash out
68
+ warn: "red", // yellow text is unreadable on white
69
+ danger: "red",
70
+ success: "green",
71
+ agent: "blue", // not cyan
72
+ kid: "magenta",
73
+ system: "blue",
74
+ border: "blackBright", // medium gray frame
75
+ stars: "yellow", // ANSI 33 = olive/brown on white = "gold-ish"
76
+ logoK: "blue", // brand K — blue instead of teal
77
+ logoI: "yellow", // olive on white, readable
78
+ logoD: "green", // dark green
79
+ logoS: "magenta", // dark magenta
80
+ }
81
+
82
+ /** High-contrast on dark — for poor-vision / low-light. */
83
+ const HC: Theme = {
41
84
  fg: "whiteBright",
42
85
  bg: "black",
43
86
  fgDim: "white",
@@ -50,9 +93,56 @@ const HIGH_CONTRAST: Theme = {
50
93
  system: "blueBright",
51
94
  border: "whiteBright",
52
95
  stars: "yellowBright",
96
+ logoK: "cyanBright",
97
+ logoI: "yellowBright",
98
+ logoD: "greenBright",
99
+ logoS: "magentaBright",
100
+ }
101
+
102
+ export type ThemeKind = "dark" | "light" | "hc"
103
+
104
+ export function resolveThemeKind(): ThemeKind {
105
+ // 1. Explicit env var
106
+ const explicit = (process.env.KIDS_THEME ?? "").toLowerCase()
107
+ if (explicit === "dark" || explicit === "light" || explicit === "hc") return explicit
108
+ // 2. Legacy flag
109
+ if (process.env.KIDS_HC === "1") return "hc"
110
+ // 3. COLORFGBG terminal hint
111
+ const detected = detectFromColorFgBg(process.env.COLORFGBG)
112
+ if (detected) return detected
113
+ // 4. Fallback
114
+ return "dark"
53
115
  }
54
116
 
55
117
  export function getTheme(): Theme {
56
- if (process.env.KIDS_HC === "1") return HIGH_CONTRAST
57
- return DEFAULT
118
+ const kind = resolveThemeKind()
119
+ if (kind === "light") return LIGHT
120
+ if (kind === "hc") return HC
121
+ return DARK
122
+ }
123
+
124
+ /**
125
+ * Parse the COLORFGBG env var. Format: "fg;bg" or "fg;default;bg" — a
126
+ * semicolon-separated list where the LAST field is the background color
127
+ * code (ANSI 0-15). 0-6 are dark; 7 is light gray; 8 is "bright black"
128
+ * (dark gray); 9-14 are bright dark-family; 15 is bright white.
129
+ *
130
+ * Empirically: macOS Terminal.app sets COLORFGBG=0;15 in light mode and
131
+ * 15;0 in Pro/Homebrew dark themes. iTerm2 mirrors. VS Code's integrated
132
+ * terminal also sets it.
133
+ *
134
+ * We treat 7 (light gray) and 15 (white) as "light"; everything else
135
+ * (incl. 8 dark-gray) as dark. Some terminals send "default" instead of
136
+ * a digit — we return null and let the caller fall through.
137
+ */
138
+ export function detectFromColorFgBg(raw: string | undefined): ThemeKind | null {
139
+ if (!raw) return null
140
+ const parts = raw.split(";").map((s) => s.trim()).filter(Boolean)
141
+ if (parts.length === 0) return null
142
+ const bgStr = parts[parts.length - 1]!
143
+ if (bgStr === "default") return null
144
+ const bg = Number.parseInt(bgStr, 10)
145
+ if (!Number.isFinite(bg)) return null
146
+ if (bg === 7 || bg === 15) return "light"
147
+ return "dark"
58
148
  }