create-theokit 1.18.0 → 1.19.0

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,6 +1,6 @@
1
1
  {
2
2
  "name": "create-theokit",
3
- "version": "1.18.0",
3
+ "version": "1.19.0",
4
4
  "type": "module",
5
5
  "description": "Scaffold a new TheoKit project",
6
6
  "license": "Apache-2.0",
@@ -24,6 +24,18 @@ import { streamAgentTurnInProcess } from 'theokit/server/agent'
24
24
 
25
25
  import * as chatAgent from '../agents/chat.js'
26
26
  import { AGENT } from '../shared/agent.js'
27
+ // Everything visual (accent, banner wordmark + copy, spinner words, placeholder) lives in ONE file —
28
+ // edit `tui/theme.ts` to restyle; this component never needs touching for a rebrand.
29
+ import {
30
+ ACCENT,
31
+ BANNER_TIPS,
32
+ BANNER_WHATS_NEW,
33
+ LOGO,
34
+ PLACEHOLDER,
35
+ THEME,
36
+ THINKING_PHRASES,
37
+ WIDE_COLS,
38
+ } from './theme.js'
27
39
 
28
40
  /** Shown in the status bar (from `shared/agent.ts`). */
29
41
  const MODEL = AGENT.model
@@ -32,32 +44,6 @@ const CWD = process.cwd().replace(homedir(), '~')
32
44
  /** Compact token count for the footer: `128000 → 128k`, `12300 → 12.3k`, `462 → 462`. */
33
45
  const fmtK = (n: number): string =>
34
46
  n >= 1000 ? `${(Math.round(n / 100) / 10).toString().replace(/\.0$/, '')}k` : `${n}`
35
- /** Claude Code's warm accent (truecolor; degrades gracefully on 256-color terminals). */
36
- const ACCENT = '#d97757'
37
- /** The "Theo" wordmark (figlet ANSI-Shadow, block glyphs — single-width, renders everywhere). */
38
- const THEO_LOGO = [
39
- '████████╗██╗ ██╗███████╗ ██████╗',
40
- '╚══██╔══╝██║ ██║██╔════╝██╔═══██╗',
41
- ' ██║ ███████║█████╗ ██║ ██║',
42
- ' ██║ ██╔══██║██╔══╝ ██║ ██║',
43
- ' ██║ ██║ ██║███████╗╚██████╔╝',
44
- ' ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ',
45
- ].join('\n')
46
- /** Below this column count the welcome box collapses to a single column (the two-column aside needs room). */
47
- const WIDE_COLS = 90
48
- /** Whimsical status words cycled while the agent thinks (Claude Code idiom — the ✻ spinner rotates these). */
49
- const THINKING_PHRASES = [
50
- 'Thinking',
51
- 'Pondering',
52
- 'Noodling',
53
- 'Percolating',
54
- 'Baking',
55
- 'Brewing',
56
- 'Simmering',
57
- 'Conjuring',
58
- 'Musing',
59
- 'Marinating',
60
- ] as const
61
47
 
62
48
  /** The agent's opening line — a short warm intro; the banner already carries the how-to tips. */
63
49
  const GREETING: UIMessageLike = {
@@ -110,7 +96,7 @@ function Banner(): ReactElement {
110
96
  {/* Fixed-width left column (fits the 34-wide wordmark) so a long cwd truncates instead of pushing
111
97
  the right column off-screen — the box stays full width, the content stays grouped on the left. */}
112
98
  <Box flexDirection="column" width={38} flexShrink={0}>
113
- <Text color={ACCENT}>{THEO_LOGO}</Text>
99
+ <Text color={ACCENT}>{LOGO}</Text>
114
100
  <Box marginTop={1} flexDirection="column">
115
101
  <Text color={ACCENT} bold wrap="truncate-end">
116
102
  ✻ Welcome to {APP_NAME}
@@ -129,16 +115,22 @@ function Banner(): ReactElement {
129
115
  Tips for getting started
130
116
  </Text>
131
117
  <Box marginTop={1} flexDirection="column">
132
- <Text dimColor>Ask me anything — I stream a reply</Text>
133
- <Text dimColor>/help · @ mention a file · esc interrupts</Text>
118
+ {BANNER_TIPS.map((tip) => (
119
+ <Text key={tip} dimColor>
120
+ {tip}
121
+ </Text>
122
+ ))}
134
123
  </Box>
135
124
  <Box marginTop={1} flexDirection="column">
136
125
  <Text color={ACCENT} bold>
137
126
  What&apos;s new
138
127
  </Text>
139
128
  <Box marginTop={1} flexDirection="column">
140
- <Text dimColor>Human-in-the-loop approvals</Text>
141
- <Text dimColor>Live token usage in the footer</Text>
129
+ {BANNER_WHATS_NEW.map((line) => (
130
+ <Text key={line} dimColor>
131
+ {line}
132
+ </Text>
133
+ ))}
142
134
  </Box>
143
135
  </Box>
144
136
  </Box>
@@ -217,7 +209,7 @@ export function App(): ReactElement {
217
209
  }
218
210
 
219
211
  return (
220
- <TheoTUIProvider theme={{ base: 'dark', override: { accent: ACCENT } }}>
212
+ <TheoTUIProvider theme={THEME}>
221
213
  {/* InkInputProvider bridges Ink's stdin to @theokit/tui's interactive surfaces (ApprovalPrompt's
222
214
  choice bar) so they receive keys under plain Ink — the ChatComposer keeps using Ink's own hooks. */}
223
215
  <InkInputProvider>
@@ -255,7 +247,7 @@ export function App(): ReactElement {
255
247
  </ApprovalPrompt>
256
248
  ) : (
257
249
  <ChatComposer
258
- placeholder="Ask {{name}} anything…"
250
+ placeholder={PLACEHOLDER}
259
251
  bordered
260
252
  hint={exitArmed ? 'Press Ctrl+C again to quit' : undefined}
261
253
  commands={[
@@ -0,0 +1,73 @@
1
+ /**
2
+ * ════════════════════════════════════════════════════════════════════════════════════════════════
3
+ * tui/theme.ts — the ONE place to restyle your terminal UI.
4
+ *
5
+ * Everything visual lives here: the accent color, the welcome-banner wordmark + copy, the "thinking"
6
+ * words, and the input placeholder. Edit this file to rebrand — `tui/App.tsx` reads from it and never
7
+ * needs touching for a restyle.
8
+ * ════════════════════════════════════════════════════════════════════════════════════════════════
9
+ */
10
+ import type { TheoThemeProp } from '@theokit/tui'
11
+
12
+ /**
13
+ * The accent color — used for the banner, the `✻`/`⏺` markers, the box borders, and the `>` prompt.
14
+ * Any Ink color: a name (`'cyan'`, `'green'`, `'magenta'`) or a hex (`'#d97757'`, truecolor).
15
+ */
16
+ export const ACCENT = '#d97757'
17
+
18
+ /**
19
+ * The global `@theokit/tui` theme applied by `<TheoTUIProvider>`. Partial-merge: override only what you
20
+ * pass — everything else inherits the base theme. Uncomment a block below to restyle the bullets:
21
+ *
22
+ * role: { assistant: { glyph: '▸ ' } }, // the assistant message bullet
23
+ * toolStatus: { success: { glyph: '◆' }, failed: { glyph: '✗' } }, // per-status tool bullets
24
+ */
25
+ export const THEME: TheoThemeProp = {
26
+ base: 'dark', // 'dark' · 'light' · 'no-color'
27
+ override: {
28
+ accent: ACCENT,
29
+ // role: { assistant: { glyph: '⏺ ' } },
30
+ // toolStatus: { pending: { glyph: '○' }, success: { glyph: '●' }, failed: { glyph: '✗' } },
31
+ },
32
+ }
33
+
34
+ /**
35
+ * The wordmark at the top of the welcome banner (ASCII art). Swap it for your own — generate one with
36
+ * `npx figlet -f "ANSI Shadow" "YourApp"` — or set it to `''` to hide it. Use single-width glyphs so it
37
+ * renders on every terminal.
38
+ */
39
+ export const LOGO = [
40
+ '████████╗██╗ ██╗███████╗ ██████╗',
41
+ '╚══██╔══╝██║ ██║██╔════╝██╔═══██╗',
42
+ ' ██║ ███████║█████╗ ██║ ██║',
43
+ ' ██║ ██╔══██║██╔══╝ ██║ ██║',
44
+ ' ██║ ██║ ██║███████╗╚██████╔╝',
45
+ ' ╚═╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ',
46
+ ].join('\n')
47
+
48
+ /** The welcome banner's right column (shown on wide terminals). One string per line. */
49
+ export const BANNER_TIPS = [
50
+ 'Ask me anything — I stream a reply',
51
+ '/help · @ mention a file · esc interrupts',
52
+ ]
53
+ export const BANNER_WHATS_NEW = ['Human-in-the-loop approvals', 'Live token usage in the footer']
54
+
55
+ /** Whimsical words the "thinking" spinner cycles through while the agent works. */
56
+ export const THINKING_PHRASES = [
57
+ 'Thinking',
58
+ 'Pondering',
59
+ 'Noodling',
60
+ 'Percolating',
61
+ 'Baking',
62
+ 'Brewing',
63
+ 'Simmering',
64
+ 'Conjuring',
65
+ 'Musing',
66
+ 'Marinating',
67
+ ]
68
+
69
+ /** The dim placeholder shown in the empty input box. */
70
+ export const PLACEHOLDER = 'Ask {{name}} anything…'
71
+
72
+ /** Below this terminal width the welcome banner collapses from two columns to one. */
73
+ export const WIDE_COLS = 90