codemaxxing 0.1.1 → 0.1.2

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/index.js CHANGED
@@ -9,7 +9,7 @@ import { loadConfig, detectLocalProvider, parseCLIArgs, applyOverrides, listMode
9
9
  import { listSessions, getSession, loadMessages } from "./utils/sessions.js";
10
10
  import { execSync } from "child_process";
11
11
  import { isGitRepo, getBranch, getStatus, getDiff, undoLastCommit } from "./utils/git.js";
12
- import { getTheme, THEMES, DEFAULT_THEME } from "./themes.js";
12
+ import { getTheme, listThemes, THEMES, DEFAULT_THEME } from "./themes.js";
13
13
  const VERSION = "0.1.0";
14
14
  // ── Helpers ──
15
15
  function formatTimeAgo(date) {
@@ -102,6 +102,8 @@ function App() {
102
102
  const [inputKey, setInputKey] = useState(0);
103
103
  const [sessionPicker, setSessionPicker] = useState(null);
104
104
  const [sessionPickerIndex, setSessionPickerIndex] = useState(0);
105
+ const [themePicker, setThemePicker] = useState(false);
106
+ const [themePickerIndex, setThemePickerIndex] = useState(0);
105
107
  const [approval, setApproval] = useState(null);
106
108
  // Listen for paste events from stdin interceptor
107
109
  useEffect(() => {
@@ -329,10 +331,10 @@ function App() {
329
331
  if (trimmed.startsWith("/theme")) {
330
332
  const themeName = trimmed.replace("/theme", "").trim();
331
333
  if (!themeName) {
332
- const available = Object.entries(THEMES)
333
- .map(([key, t]) => ` ${key === theme.name.toLowerCase() ? "▸ " : " "}${key} — ${t.description}`)
334
- .join("\n");
335
- addMsg("info", `Current theme: ${theme.name}\n\nAvailable themes:\n${available}\n\n Usage: /theme <name>`);
334
+ const themeKeys = listThemes();
335
+ const currentIdx = themeKeys.indexOf(theme.name.toLowerCase());
336
+ setThemePicker(true);
337
+ setThemePickerIndex(currentIdx >= 0 ? currentIdx : 0);
336
338
  return;
337
339
  }
338
340
  if (!THEMES[themeName]) {
@@ -485,6 +487,30 @@ function App() {
485
487
  return;
486
488
  }
487
489
  }
490
+ // Theme picker navigation
491
+ if (themePicker) {
492
+ const themeKeys = listThemes();
493
+ if (key.upArrow) {
494
+ setThemePickerIndex((prev) => (prev - 1 + themeKeys.length) % themeKeys.length);
495
+ return;
496
+ }
497
+ if (key.downArrow) {
498
+ setThemePickerIndex((prev) => (prev + 1) % themeKeys.length);
499
+ return;
500
+ }
501
+ if (key.return) {
502
+ const selected = themeKeys[themePickerIndex];
503
+ setTheme(getTheme(selected));
504
+ setThemePicker(false);
505
+ addMsg("info", `✅ Switched to theme: ${THEMES[selected].name}`);
506
+ return;
507
+ }
508
+ if (key.escape) {
509
+ setThemePicker(false);
510
+ return;
511
+ }
512
+ return;
513
+ }
488
514
  // Session picker navigation
489
515
  if (sessionPicker) {
490
516
  if (key.upArrow) {
@@ -616,7 +642,7 @@ function App() {
616
642
  default:
617
643
  return _jsx(Text, { children: msg.text }, msg.id);
618
644
  }
619
- }), loading && !approval && !streaming && _jsx(NeonSpinner, { message: spinnerMsg, colors: theme.colors }), streaming && !loading && _jsx(StreamingIndicator, { colors: theme.colors }), approval && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.warning, paddingX: 1, marginTop: 1, children: [_jsxs(Text, { bold: true, color: theme.colors.warning, children: ["\u26A0 Approve ", approval.tool, "?"] }), approval.tool === "write_file" && approval.args.path ? (_jsxs(Text, { color: theme.colors.muted, children: [" 📄 ", String(approval.args.path)] })) : null, approval.tool === "write_file" && approval.args.content ? (_jsxs(Text, { color: theme.colors.muted, children: [" ", String(approval.args.content).split("\n").length, " lines, ", String(approval.args.content).length, "B"] })) : null, approval.tool === "run_command" && approval.args.command ? (_jsxs(Text, { color: theme.colors.muted, children: [" $ ", String(approval.args.command)] })) : null, _jsxs(Text, { children: [_jsx(Text, { color: theme.colors.success, bold: true, children: " [y]" }), _jsx(Text, { children: "es " }), _jsx(Text, { color: theme.colors.error, bold: true, children: "[n]" }), _jsx(Text, { children: "o " }), _jsx(Text, { color: theme.colors.primary, bold: true, children: "[a]" }), _jsx(Text, { children: "lways" })] })] })), sessionPicker && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.secondary, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "Resume a session:" }), sessionPicker.map((s, i) => (_jsxs(Text, { children: [i === sessionPickerIndex ? _jsx(Text, { color: theme.colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === sessionPickerIndex ? theme.colors.suggestion : theme.colors.muted, children: s.display })] }, s.id))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] })), showSuggestions && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.muted, paddingX: 1, marginBottom: 0, children: [cmdMatches.slice(0, 6).map((c, i) => (_jsxs(Text, { children: [i === cmdIndex ? _jsx(Text, { color: theme.colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === cmdIndex ? theme.colors.suggestion : theme.colors.primary, bold: true, children: c.cmd }), _jsxs(Text, { color: theme.colors.muted, children: [" — ", c.desc] })] }, i))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Tab select" })] })), _jsxs(Box, { borderStyle: "single", borderColor: approval ? theme.colors.warning : theme.colors.border, paddingX: 1, children: [_jsx(Text, { color: theme.colors.secondary, bold: true, children: "> " }), approval ? (_jsx(Text, { color: theme.colors.warning, children: "waiting for approval..." })) : ready && !loading ? (_jsxs(Box, { children: [pastedChunks.map((p) => (_jsxs(Text, { color: theme.colors.muted, children: ["[Pasted text #", p.id, " +", p.lines, " lines]"] }, p.id))), _jsx(TextInput, { value: input, onChange: (v) => { setInput(v); setCmdIndex(0); }, onSubmit: handleSubmit }, inputKey)] })) : (_jsx(Text, { dimColor: true, children: loading ? "waiting for response..." : "initializing..." }))] }), agent && (_jsx(Box, { paddingX: 2, children: _jsxs(Text, { dimColor: true, children: ["💬 ", agent.getContextLength(), " messages · ~", (() => {
645
+ }), loading && !approval && !streaming && _jsx(NeonSpinner, { message: spinnerMsg, colors: theme.colors }), streaming && !loading && _jsx(StreamingIndicator, { colors: theme.colors }), approval && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.warning, paddingX: 1, marginTop: 1, children: [_jsxs(Text, { bold: true, color: theme.colors.warning, children: ["\u26A0 Approve ", approval.tool, "?"] }), approval.tool === "write_file" && approval.args.path ? (_jsxs(Text, { color: theme.colors.muted, children: [" 📄 ", String(approval.args.path)] })) : null, approval.tool === "write_file" && approval.args.content ? (_jsxs(Text, { color: theme.colors.muted, children: [" ", String(approval.args.content).split("\n").length, " lines, ", String(approval.args.content).length, "B"] })) : null, approval.tool === "run_command" && approval.args.command ? (_jsxs(Text, { color: theme.colors.muted, children: [" $ ", String(approval.args.command)] })) : null, _jsxs(Text, { children: [_jsx(Text, { color: theme.colors.success, bold: true, children: " [y]" }), _jsx(Text, { children: "es " }), _jsx(Text, { color: theme.colors.error, bold: true, children: "[n]" }), _jsx(Text, { children: "o " }), _jsx(Text, { color: theme.colors.primary, bold: true, children: "[a]" }), _jsx(Text, { children: "lways" })] })] })), themePicker && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.border, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "Choose a theme:" }), listThemes().map((key, i) => (_jsxs(Text, { children: [i === themePickerIndex ? _jsx(Text, { color: theme.colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === themePickerIndex ? theme.colors.suggestion : theme.colors.primary, bold: true, children: key }), _jsxs(Text, { color: theme.colors.muted, children: [" — ", THEMES[key].description] }), key === theme.name.toLowerCase() ? _jsx(Text, { color: theme.colors.muted, children: " (current)" }) : null] }, key))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] })), sessionPicker && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.secondary, paddingX: 1, marginBottom: 0, children: [_jsx(Text, { bold: true, color: theme.colors.secondary, children: "Resume a session:" }), sessionPicker.map((s, i) => (_jsxs(Text, { children: [i === sessionPickerIndex ? _jsx(Text, { color: theme.colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === sessionPickerIndex ? theme.colors.suggestion : theme.colors.muted, children: s.display })] }, s.id))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Enter select · Esc cancel" })] })), showSuggestions && (_jsxs(Box, { flexDirection: "column", borderStyle: "single", borderColor: theme.colors.muted, paddingX: 1, marginBottom: 0, children: [cmdMatches.slice(0, 6).map((c, i) => (_jsxs(Text, { children: [i === cmdIndex ? _jsx(Text, { color: theme.colors.suggestion, bold: true, children: "▸ " }) : _jsx(Text, { children: " " }), _jsx(Text, { color: i === cmdIndex ? theme.colors.suggestion : theme.colors.primary, bold: true, children: c.cmd }), _jsxs(Text, { color: theme.colors.muted, children: [" — ", c.desc] })] }, i))), _jsx(Text, { dimColor: true, children: " ↑↓ navigate · Tab select" })] })), _jsxs(Box, { borderStyle: "single", borderColor: approval ? theme.colors.warning : theme.colors.border, paddingX: 1, children: [_jsx(Text, { color: theme.colors.secondary, bold: true, children: "> " }), approval ? (_jsx(Text, { color: theme.colors.warning, children: "waiting for approval..." })) : ready && !loading ? (_jsxs(Box, { children: [pastedChunks.map((p) => (_jsxs(Text, { color: theme.colors.muted, children: ["[Pasted text #", p.id, " +", p.lines, " lines]"] }, p.id))), _jsx(TextInput, { value: input, onChange: (v) => { setInput(v); setCmdIndex(0); }, onSubmit: handleSubmit }, inputKey)] })) : (_jsx(Text, { dimColor: true, children: loading ? "waiting for response..." : "initializing..." }))] }), agent && (_jsx(Box, { paddingX: 2, children: _jsxs(Text, { dimColor: true, children: ["💬 ", agent.getContextLength(), " messages · ~", (() => {
620
646
  const tokens = agent.estimateTokens();
621
647
  return tokens >= 1000 ? `${(tokens / 1000).toFixed(1)}k` : String(tokens);
622
648
  })(), " tokens", modelName ? ` · 🤖 ${modelName}` : ""] }) }))] }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codemaxxing",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "Open-source terminal coding agent. Connect any LLM. Max your code.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
package/src/index.tsx CHANGED
@@ -131,6 +131,8 @@ function App() {
131
131
  const [inputKey, setInputKey] = useState(0);
132
132
  const [sessionPicker, setSessionPicker] = useState<Array<{ id: string; display: string }> | null>(null);
133
133
  const [sessionPickerIndex, setSessionPickerIndex] = useState(0);
134
+ const [themePicker, setThemePicker] = useState(false);
135
+ const [themePickerIndex, setThemePickerIndex] = useState(0);
134
136
  const [approval, setApproval] = useState<{
135
137
  tool: string;
136
138
  args: Record<string, unknown>;
@@ -376,10 +378,10 @@ function App() {
376
378
  if (trimmed.startsWith("/theme")) {
377
379
  const themeName = trimmed.replace("/theme", "").trim();
378
380
  if (!themeName) {
379
- const available = Object.entries(THEMES)
380
- .map(([key, t]) => ` ${key === theme.name.toLowerCase() ? "▸ " : " "}${key} — ${t.description}`)
381
- .join("\n");
382
- addMsg("info", `Current theme: ${theme.name}\n\nAvailable themes:\n${available}\n\n Usage: /theme <name>`);
381
+ const themeKeys = listThemes();
382
+ const currentIdx = themeKeys.indexOf(theme.name.toLowerCase());
383
+ setThemePicker(true);
384
+ setThemePickerIndex(currentIdx >= 0 ? currentIdx : 0);
383
385
  return;
384
386
  }
385
387
  if (!THEMES[themeName]) {
@@ -531,6 +533,31 @@ function App() {
531
533
  }
532
534
  }
533
535
 
536
+ // Theme picker navigation
537
+ if (themePicker) {
538
+ const themeKeys = listThemes();
539
+ if (key.upArrow) {
540
+ setThemePickerIndex((prev) => (prev - 1 + themeKeys.length) % themeKeys.length);
541
+ return;
542
+ }
543
+ if (key.downArrow) {
544
+ setThemePickerIndex((prev) => (prev + 1) % themeKeys.length);
545
+ return;
546
+ }
547
+ if (key.return) {
548
+ const selected = themeKeys[themePickerIndex];
549
+ setTheme(getTheme(selected));
550
+ setThemePicker(false);
551
+ addMsg("info", `✅ Switched to theme: ${THEMES[selected].name}`);
552
+ return;
553
+ }
554
+ if (key.escape) {
555
+ setThemePicker(false);
556
+ return;
557
+ }
558
+ return;
559
+ }
560
+
534
561
  // Session picker navigation
535
562
  if (sessionPicker) {
536
563
  if (key.upArrow) {
@@ -737,6 +764,22 @@ function App() {
737
764
  </Box>
738
765
  )}
739
766
 
767
+ {/* ═══ THEME PICKER ═══ */}
768
+ {themePicker && (
769
+ <Box flexDirection="column" borderStyle="single" borderColor={theme.colors.border} paddingX={1} marginBottom={0}>
770
+ <Text bold color={theme.colors.secondary}>Choose a theme:</Text>
771
+ {listThemes().map((key, i) => (
772
+ <Text key={key}>
773
+ {i === themePickerIndex ? <Text color={theme.colors.suggestion} bold>{"▸ "}</Text> : <Text>{" "}</Text>}
774
+ <Text color={i === themePickerIndex ? theme.colors.suggestion : theme.colors.primary} bold>{key}</Text>
775
+ <Text color={theme.colors.muted}>{" — "}{THEMES[key].description}</Text>
776
+ {key === theme.name.toLowerCase() ? <Text color={theme.colors.muted}> (current)</Text> : null}
777
+ </Text>
778
+ ))}
779
+ <Text dimColor>{" ↑↓ navigate · Enter select · Esc cancel"}</Text>
780
+ </Box>
781
+ )}
782
+
740
783
  {/* ═══ SESSION PICKER ═══ */}
741
784
  {sessionPicker && (
742
785
  <Box flexDirection="column" borderStyle="single" borderColor={theme.colors.secondary} paddingX={1} marginBottom={0}>