termcut 0.2.2 → 0.4.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/src/themes.ts CHANGED
@@ -1,6 +1,11 @@
1
+ import generatedJson from "./themes.generated.json";
1
2
  import type { Theme, ThemeName } from "./types";
2
3
 
3
- export const themes: Record<ThemeName, Theme> = {
4
+ /**
5
+ * Hand-maintained themes. These stay byte-stable across releases (demos rendered with them must not drift),
6
+ * and take precedence over the generated bundle when names collide.
7
+ */
8
+ export const builtinThemes = {
4
9
  "catppuccin-mocha": {
5
10
  name: "catppuccin-mocha",
6
11
  background: "#1e1e2e",
@@ -121,16 +126,40 @@ export const themes: Record<ThemeName, Theme> = {
121
126
  brightCyan: "#56b6c2",
122
127
  brightWhite: "#ffffff",
123
128
  },
124
- };
129
+ } satisfies Record<string, Theme>;
125
130
 
126
- export const themeNames = Object.keys(themes) as ThemeName[];
131
+ export type BuiltinThemeName = keyof typeof builtinThemes;
132
+
133
+ /** The Ghostty theme collection (mbadolato/iTerm2-Color-Schemes, MIT), generated by scripts/build-themes.ts. */
134
+ const generated = generatedJson as Record<string, Theme>;
135
+
136
+ /** All themes by slug: the generated bundle, with the built-ins overriding colliding names. */
137
+ export const themes: Record<string, Theme> = { ...generated, ...builtinThemes };
138
+
139
+ export const themeNames: string[] = Object.keys(themes).sort();
140
+
141
+ /** "Catppuccin Mocha" → "catppuccin-mocha" (also accepts slugs, spaces, underscores, any case). */
142
+ export function themeSlug(name: string): string {
143
+ return name
144
+ .toLowerCase()
145
+ .replace(/[^a-z0-9]+/g, "-")
146
+ .replace(/^-+|-+$/g, "");
147
+ }
148
+
149
+ export function findThemes(query: string): string[] {
150
+ const q = themeSlug(query);
151
+ return themeNames.filter((n) => n.includes(q));
152
+ }
127
153
 
128
154
  export function resolveTheme(theme: ThemeName | Theme | undefined): Theme {
129
- if (!theme) return themes["catppuccin-mocha"];
155
+ if (!theme) return builtinThemes["catppuccin-mocha"];
130
156
  if (typeof theme === "string") {
131
- const found = themes[theme];
157
+ const found = themes[themeSlug(theme)];
132
158
  if (!found) {
133
- throw new Error(`Unknown theme "${theme}". Available: ${themeNames.join(", ")}`);
159
+ const near = findThemes(theme.split(/[\s-]+/)[0] ?? theme).slice(0, 5);
160
+ throw new Error(
161
+ `Unknown theme "${theme}". ${near.length ? `Did you mean: ${near.join(", ")}?` : ""} Run \`tcut themes [query]\` to list all ${themeNames.length}.`.trim(),
162
+ );
134
163
  }
135
164
  return found;
136
165
  }
package/src/types.ts CHANGED
@@ -23,12 +23,11 @@ export interface Theme {
23
23
  brightWhite: string;
24
24
  }
25
25
 
26
- export type ThemeName =
27
- | "catppuccin-mocha"
28
- | "dracula"
29
- | "github-dark"
30
- | "tokyo-night"
31
- | "one-dark";
26
+ /**
27
+ * A theme name: one of the built-ins (autocompleted) or any of the ~600 Ghostty themes, matched case- and
28
+ * punctuation-insensitively ("Catppuccin Mocha", "catppuccin-mocha", "GitHub Dark" …). `tcut themes` lists them.
29
+ */
30
+ export type ThemeName = "catppuccin-mocha" | "dracula" | "github-dark" | "tokyo-night" | "one-dark" | (string & {});
32
31
 
33
32
  export type ShellName = "bash" | "zsh" | "fish" | "sh";
34
33
  /** Terminal emulator core: libghostty (full VT, answers queries) or wterm's lite Zig core (faster, fewer features). */
@@ -74,9 +73,17 @@ export interface VideoConfig {
74
73
  cwd?: string;
75
74
  env?: Record<string, string>;
76
75
 
77
- /** Terminal grid. Default 80 × 24. */
76
+ /** Terminal grid. Default 80 × 24 — or derived from `width`/`height` when those are given. */
78
77
  cols?: number;
79
78
  rows?: number;
79
+ /**
80
+ * Video size in pixels (including margin). When set, the output is exactly this size and the terminal is
81
+ * centred inside; `cols`/`rows` default to what fits.
82
+ */
83
+ width?: number;
84
+ height?: number;
85
+ /** Where looping outputs (GIF, WebP) start: a frame number or a percentage like "50%". */
86
+ loopOffset?: number | string;
80
87
 
81
88
  /** Frames per second of the output. Default 60. */
82
89
  fps?: number;
@@ -126,6 +133,9 @@ export interface ResolvedConfig {
126
133
  env: Record<string, string>;
127
134
  cols: number;
128
135
  rows: number;
136
+ width?: number;
137
+ height?: number;
138
+ loopOffset?: number | string;
129
139
  fps: number;
130
140
  typingSpeed: number;
131
141
  typingJitter: number;
@@ -210,6 +220,11 @@ export interface TerminalSession {
210
220
  ctrl(letter: string, times?: number): Promise<void>;
211
221
  /** Alt/Meta+<key>. */
212
222
  alt(key: string, times?: number): Promise<void>;
223
+ /** Shift+<key>: "tab" (back-tab), arrows/home/end/pageUp/pageDown/delete, or a letter (sent uppercase). */
224
+ shift(key: KeyName | string, times?: number): Promise<void>;
225
+ /** Mouse-wheel up/down at the cursor. Only programs with mouse tracking (vim, less --mouse, TUIs) react. */
226
+ scrollUp(times?: number): Promise<void>;
227
+ scrollDown(times?: number): Promise<void>;
213
228
  /** Send raw bytes to the PTY. */
214
229
  raw(data: string | Uint8Array): Promise<void>;
215
230