mini-coder 0.7.4 → 0.8.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.
Files changed (47) hide show
  1. package/AGENTS.md +114 -0
  2. package/README.md +53 -66
  3. package/bin/mini-coder.ts +2 -0
  4. package/demo.gif +0 -0
  5. package/package.json +17 -20
  6. package/src/agent.ts +181 -274
  7. package/src/cli.ts +101 -0
  8. package/src/config.ts +150 -0
  9. package/src/prompt.ts +54 -207
  10. package/src/session.ts +124 -69
  11. package/src/tools/bash.ts +89 -0
  12. package/src/tools/common.ts +32 -0
  13. package/src/tools/edit.ts +41 -0
  14. package/src/tools/index.ts +47 -0
  15. package/src/tools/read.ts +64 -0
  16. package/src/tui/commands.ts +63 -0
  17. package/src/tui/editor.ts +291 -0
  18. package/src/tui/highlight.ts +189 -0
  19. package/src/tui/stream.ts +142 -0
  20. package/src/tui/styles.ts +42 -0
  21. package/src/tui/term.ts +436 -0
  22. package/src/tui/theme.ts +121 -0
  23. package/src/tui/tui.ts +595 -0
  24. package/src/tui/usage.ts +67 -0
  25. package/tsconfig.json +8 -8
  26. package/bin/mc.ts +0 -11
  27. package/bun.lock +0 -350
  28. package/nono-mini-coder.json +0 -42
  29. package/src/args.ts +0 -252
  30. package/src/error-handling.test.ts +0 -163
  31. package/src/git.ts +0 -23
  32. package/src/headless.ts +0 -66
  33. package/src/index.ts +0 -43
  34. package/src/models.ts +0 -191
  35. package/src/oauth.ts +0 -147
  36. package/src/shared.ts +0 -119
  37. package/src/themes.ts +0 -234
  38. package/src/tool-bash.ts +0 -77
  39. package/src/tool-edit.ts +0 -121
  40. package/src/tool-read.ts +0 -100
  41. package/src/tui-components.ts +0 -127
  42. package/src/tui-conversation.ts +0 -218
  43. package/src/tui-editor.ts +0 -29
  44. package/src/tui-overlay.ts +0 -604
  45. package/src/tui.ts +0 -314
  46. package/src/types.ts +0 -194
  47. package/src/update.ts +0 -171
package/src/models.ts DELETED
@@ -1,191 +0,0 @@
1
- import type {
2
- Api,
3
- Credential,
4
- CredentialStore,
5
- KnownApi,
6
- KnownProvider,
7
- Model,
8
- ProviderStreams,
9
- } from "@earendil-works/pi-ai";
10
- import { createProvider } from "@earendil-works/pi-ai";
11
- import { anthropicMessagesApi } from "@earendil-works/pi-ai/api/anthropic-messages.lazy";
12
- import { azureOpenAIResponsesApi } from "@earendil-works/pi-ai/api/azure-openai-responses.lazy";
13
- import { bedrockConverseStreamApi } from "@earendil-works/pi-ai/api/bedrock-converse-stream.lazy";
14
- import { googleGenerativeAIApi } from "@earendil-works/pi-ai/api/google-generative-ai.lazy";
15
- import { googleVertexApi } from "@earendil-works/pi-ai/api/google-vertex.lazy";
16
- import { mistralConversationsApi } from "@earendil-works/pi-ai/api/mistral-conversations.lazy";
17
- import { openAICodexResponsesApi } from "@earendil-works/pi-ai/api/openai-codex-responses.lazy";
18
- import { openAICompletionsApi } from "@earendil-works/pi-ai/api/openai-completions.lazy";
19
- import { openAIResponsesApi } from "@earendil-works/pi-ai/api/openai-responses.lazy";
20
- import {
21
- builtinModels,
22
- getBuiltinModels,
23
- getBuiltinProviders,
24
- } from "@earendil-works/pi-ai/providers/all";
25
- import { AUTH_PATH } from "./shared.ts";
26
-
27
- const API_STREAMS: Record<KnownApi, () => ProviderStreams> = {
28
- "anthropic-messages": anthropicMessagesApi,
29
- "azure-openai-responses": azureOpenAIResponsesApi,
30
- "bedrock-converse-stream": bedrockConverseStreamApi,
31
- "google-generative-ai": googleGenerativeAIApi,
32
- "google-vertex": googleVertexApi,
33
- "mistral-conversations": mistralConversationsApi,
34
- "openai-codex-responses": openAICodexResponsesApi,
35
- "openai-completions": openAICompletionsApi,
36
- "openai-responses": openAIResponsesApi,
37
- };
38
-
39
- type SavedCredentials = Record<string, Credential>;
40
-
41
- async function readCredentials(): Promise<SavedCredentials> {
42
- const file = Bun.file(AUTH_PATH);
43
- if (await file.exists()) {
44
- return JSON.parse(await file.text()) as SavedCredentials;
45
- }
46
-
47
- return {};
48
- }
49
-
50
- async function writeCredentials(credentials: SavedCredentials) {
51
- await Bun.write(AUTH_PATH, JSON.stringify(credentials));
52
- }
53
-
54
- export function createCredentialStore(): CredentialStore {
55
- return {
56
- async read(providerId) {
57
- const credentials = await readCredentials();
58
- return credentials[providerId];
59
- },
60
-
61
- async modify(providerId, fn) {
62
- const credentials = await readCredentials();
63
- const next = await fn(credentials[providerId]);
64
-
65
- if (next) {
66
- credentials[providerId] = next;
67
- await writeCredentials(credentials);
68
- }
69
-
70
- return credentials[providerId];
71
- },
72
-
73
- async delete(providerId) {
74
- const credentials = await readCredentials();
75
- delete credentials[providerId];
76
- await writeCredentials(credentials);
77
- },
78
- };
79
- }
80
-
81
- export function isBuiltinProvider(provider: string): provider is KnownProvider {
82
- return getBuiltinProviders().includes(provider as KnownProvider);
83
- }
84
-
85
- export function findModelConfig(
86
- modelId: string,
87
- provider: string,
88
- customProviders?: Model<Api>[],
89
- ) {
90
- if (isBuiltinProvider(provider)) {
91
- const models = getBuiltinModels(provider);
92
- if (models.length === 0) throw new Error("Provider has no models");
93
- return models.find((m) => m.id === modelId);
94
- }
95
- return customProviders?.find(
96
- (m) => m.provider === provider && m.id === modelId,
97
- );
98
- }
99
-
100
- export function getFirstModelConfig(
101
- provider: string,
102
- customProviders?: Model<Api>[],
103
- ): Model<Api> {
104
- if (isBuiltinProvider(provider)) {
105
- const models = getBuiltinModels(provider);
106
- if (models.length > 0) return models[0];
107
- }
108
- const custom = customProviders?.find((m) => m.provider === provider);
109
- if (custom) return custom;
110
- throw new Error("Provider has no models");
111
- }
112
-
113
- export function getFallbackModel(
114
- provider: string,
115
- explicitModel: boolean,
116
- providerChanged: boolean,
117
- customProviders?: Model<Api>[],
118
- ): Model<Api> {
119
- if (explicitModel) {
120
- throw new Error("Model not found");
121
- }
122
-
123
- if (!providerChanged) {
124
- throw new Error("Model not found");
125
- }
126
-
127
- return getFirstModelConfig(provider, customProviders);
128
- }
129
-
130
- export function getProviderModels(
131
- provider: string,
132
- customProviders?: Model<Api>[],
133
- ): Model<Api>[] {
134
- const builtIn = isBuiltinProvider(provider) ? getBuiltinModels(provider) : [];
135
- const custom = customProviders?.filter((m) => m.provider === provider) ?? [];
136
-
137
- return [...builtIn, ...custom];
138
- }
139
-
140
- export function createAppModels(customProviders?: Model<Api>[]) {
141
- const models = builtinModels({ credentials: createCredentialStore() });
142
- const customProviderGroups = new Map<string, Model<Api>[]>();
143
-
144
- for (const model of customProviders ?? []) {
145
- const group = customProviderGroups.get(model.provider) ?? [];
146
- group.push(model);
147
- customProviderGroups.set(model.provider, group);
148
- }
149
-
150
- for (const [provider, providerModels] of customProviderGroups) {
151
- models.setProvider(
152
- createProvider({
153
- id: provider,
154
- name: provider,
155
- models: providerModels,
156
- auth: {
157
- apiKey: {
158
- name: provider,
159
- resolve: async () => ({ auth: { apiKey: "dummy" } }),
160
- },
161
- },
162
- api: Object.fromEntries(
163
- Object.entries(API_STREAMS)
164
- .filter(([api]) =>
165
- providerModels.some((model) => model.api === api),
166
- )
167
- .map(([api, createStreams]) => [api, createStreams()]),
168
- ),
169
- }),
170
- );
171
- }
172
-
173
- return models;
174
- }
175
-
176
- export async function getConfiguredBuiltinProviders(): Promise<string[]> {
177
- const models = builtinModels({ credentials: createCredentialStore() });
178
- const providers: string[] = [];
179
-
180
- for (const provider of models.getProviders()) {
181
- const model = provider.getModels()[0];
182
- if (!model) continue;
183
-
184
- try {
185
- const auth = await models.getAuth(model);
186
- if (auth) providers.push(provider.id);
187
- } catch {}
188
- }
189
-
190
- return providers;
191
- }
package/src/oauth.ts DELETED
@@ -1,147 +0,0 @@
1
- import { mkdir } from "node:fs/promises";
2
- import readline from "node:readline";
3
-
4
- import {
5
- getOAuthProvider,
6
- getOAuthProviders,
7
- type OAuthLoginCallbacks,
8
- type OAuthPrompt,
9
- type OAuthProviderId,
10
- type OAuthSelectPrompt,
11
- } from "@earendil-works/pi-ai/oauth";
12
- import {
13
- createAppModels,
14
- getConfiguredBuiltinProviders,
15
- isBuiltinProvider,
16
- } from "./models.ts";
17
- import { AUTH_PATH as AUTH_FILE, DATA_DIR } from "./shared";
18
- import type { CliOptions, SavedOAuthCreds } from "./types";
19
-
20
- type ReadlineInterface = ReturnType<typeof readline.createInterface>;
21
-
22
- function ask(rl: ReadlineInterface, question: string): Promise<string> {
23
- return new Promise((resolve) => rl.question(question, resolve));
24
- }
25
-
26
- function formatPrompt(prompt: OAuthPrompt): string {
27
- const placeholder = prompt.placeholder ? ` (${prompt.placeholder})` : "";
28
- return `${prompt.message}${placeholder}: `;
29
- }
30
-
31
- async function selectOption(
32
- rl: ReadlineInterface,
33
- prompt: OAuthSelectPrompt,
34
- ): Promise<string | undefined> {
35
- console.log(prompt.message);
36
- for (let i = 0; i < prompt.options.length; i++) {
37
- console.log(`${i + 1}. ${prompt.options[i]?.label}`);
38
- }
39
-
40
- const choice = await ask(rl, `Enter number (1-${prompt.options.length}): `);
41
- const index = Number.parseInt(choice, 10) - 1;
42
- return prompt.options[index]?.id;
43
- }
44
-
45
- function createLoginCallbacks(rl: ReadlineInterface): OAuthLoginCallbacks {
46
- return {
47
- onAuth: ({ url, instructions }) => {
48
- console.log(`Open: ${url}`);
49
- if (instructions) console.log(instructions);
50
- },
51
- onDeviceCode: ({ userCode, verificationUri }) => {
52
- console.log(`Open: ${verificationUri}`);
53
- console.log(`Enter code: ${userCode}`);
54
- },
55
- onPrompt: (prompt) => ask(rl, formatPrompt(prompt)),
56
- onProgress: (message) => console.log(message),
57
- onSelect: (prompt) => selectOption(rl, prompt),
58
- };
59
- }
60
-
61
- export function isOAuthProvider(provider: string): boolean {
62
- return getOAuthProviders().some(
63
- (oauthProvider) => oauthProvider.id === provider,
64
- );
65
- }
66
-
67
- export async function getAvailableProviders(): Promise<string[]> {
68
- const auth = await readCreds();
69
- const loggedInOAuthProviders = getOAuthProviders()
70
- .map((provider) => provider.id)
71
- .filter((provider) => auth[provider]);
72
- const envKeyProviders = await getConfiguredBuiltinProviders();
73
- const providers: string[] = [];
74
- const providerIds = new Set<string>();
75
-
76
- for (const provider of [...loggedInOAuthProviders, ...envKeyProviders]) {
77
- if (providerIds.has(provider)) continue;
78
-
79
- providers.push(provider);
80
- providerIds.add(provider);
81
- }
82
-
83
- return providers;
84
- }
85
-
86
- export async function loginOAuth(provider: OAuthProviderId) {
87
- const oauthProvider = getOAuthProvider(provider);
88
- if (!oauthProvider) throw new Error(`Unknown OAuth provider: ${provider}`);
89
-
90
- const rl = readline.createInterface({
91
- input: process.stdin,
92
- output: process.stdout,
93
- });
94
-
95
- try {
96
- const creds = await oauthProvider.login(createLoginCallbacks(rl));
97
-
98
- await writeCreds({ [provider]: { type: "oauth", ...creds } });
99
- } finally {
100
- rl.close();
101
- }
102
-
103
- return await readCreds();
104
- }
105
-
106
- export async function getApiKey(options: CliOptions) {
107
- const provider = options.model.provider;
108
- const models = createAppModels(options.customProviders);
109
- const auth = await models.getAuth(options.model);
110
-
111
- if (auth?.auth.apiKey) return auth.auth.apiKey;
112
-
113
- if (!isBuiltinProvider(provider)) {
114
- if (options.model.api === "openai-completions") {
115
- // pi-ai requires a truthy apiKey for OpenAI-compatible local providers like Ollama.
116
- return "dummy";
117
- }
118
-
119
- return undefined;
120
- }
121
-
122
- throw new Error("Not logged in");
123
- }
124
-
125
- export async function readCreds(): Promise<SavedOAuthCreds> {
126
- const file = Bun.file(AUTH_FILE);
127
- if (await file.exists()) {
128
- try {
129
- return JSON.parse(await file.text());
130
- } catch (err) {
131
- const message = err instanceof Error ? err.message : String(err);
132
- throw new Error(`Invalid auth JSON: ${message}`);
133
- }
134
- }
135
-
136
- return {};
137
- }
138
-
139
- async function writeCreds(creds: SavedOAuthCreds) {
140
- await mkdir(DATA_DIR, { recursive: true });
141
- await Bun.write(AUTH_FILE, JSON.stringify(await mergeCreds(creds)));
142
- }
143
-
144
- async function mergeCreds(newCreds: SavedOAuthCreds) {
145
- const oldCreds = await readCreds();
146
- return { ...oldCreds, ...newCreds };
147
- }
package/src/shared.ts DELETED
@@ -1,119 +0,0 @@
1
- import { homedir } from "node:os";
2
- import { join } from "node:path";
3
- import { parseDocument } from "yaml";
4
-
5
- // Mixed bag of helpers that can be shared across the codebase
6
-
7
- export const DATA_DIR =
8
- Bun.env.MINI_CODER_DATA_DIR ?? join(homedir(), ".config", "mini-coder");
9
- export const SESSIONS_DIR = join(DATA_DIR, "sessions");
10
- export const AUTH_PATH = join(DATA_DIR, "auth.json");
11
- export const SETTINGS_PATH = join(DATA_DIR, "settings.json");
12
-
13
- export function secureRandomString(
14
- length: number,
15
- chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
16
- ): string {
17
- const result: string[] = [];
18
- const charsLength = chars.length;
19
- const maxValid = Math.floor(256 / charsLength) * charsLength;
20
- const randomBytes = new Uint8Array(length * 2);
21
-
22
- while (result.length < length) {
23
- crypto.getRandomValues(randomBytes);
24
-
25
- for (const byte of randomBytes) {
26
- if (byte < maxValid) {
27
- result.push(chars[byte % charsLength]);
28
- if (result.length === length) break;
29
- }
30
- }
31
- }
32
-
33
- return result.join("");
34
- }
35
-
36
- export function elapsedTime(seconds: number): string {
37
- if (seconds < 60) return `${seconds}s`;
38
-
39
- const minutes = Math.floor(seconds / 60);
40
- if (minutes < 60) return `${minutes}m`;
41
-
42
- const hours = Math.floor(minutes / 60);
43
- if (hours < 24) return `${hours}h`;
44
-
45
- const days = Math.floor(hours / 24);
46
- if (days < 7) return `${days}d`;
47
-
48
- const weeks = Math.floor(days / 7);
49
- if (weeks < 4) return `${weeks}w`;
50
-
51
- const months = Math.floor(days / 30);
52
- if (months < 12) return `${months}mo`;
53
-
54
- const years = Math.floor(days / 365);
55
- return `${years}y`;
56
- }
57
-
58
- export function relativeTime(timestamp: number): string {
59
- const seconds = Math.floor((Date.now() - timestamp) / 1000);
60
- return elapsedTime(seconds);
61
- }
62
-
63
- export function onceEvery<T extends unknown[]>(
64
- n: number,
65
- fn: (...args: T) => void,
66
- ) {
67
- let calls = 0;
68
-
69
- return (...args: T) => {
70
- calls++;
71
-
72
- if (calls % n === 0) {
73
- fn(...args);
74
- }
75
- };
76
- }
77
-
78
- export function takeTail<T>(arr: T[], x: number): T[] {
79
- return x <= 0 ? [] : arr.slice(-x);
80
- }
81
-
82
- export function estimateTokens(text: string): number {
83
- return Math.ceil(text.length / 4);
84
- }
85
-
86
- export function parseSkillFrontmatter(content: string) {
87
- const match = /^---\s*\n([\s\S]*?)\n---/.exec(content);
88
-
89
- if (!match) {
90
- return undefined;
91
- }
92
-
93
- const doc = parseDocument(match[1]);
94
- const data = doc.toJS() as unknown;
95
-
96
- if (!data || typeof data !== "object") {
97
- return undefined;
98
- }
99
-
100
- const record = data as Record<string, unknown>;
101
- const name = typeof record.name === "string" ? record.name.trim() : "";
102
- const description =
103
- typeof record.description === "string" ? record.description.trim() : "";
104
-
105
- if (!name || !description) {
106
- return undefined;
107
- }
108
-
109
- return { name, description };
110
- }
111
-
112
- export function formatTimestamp(timestampMs: number): string {
113
- return new Date(timestampMs).toLocaleTimeString("en-GB", {
114
- hour: "2-digit",
115
- minute: "2-digit",
116
- second: "2-digit",
117
- hour12: false,
118
- });
119
- }
package/src/themes.ts DELETED
@@ -1,234 +0,0 @@
1
- import type { SyntaxHighlightTheme } from "@cel-tui/components";
2
- import type { Color, Theme } from "@cel-tui/core";
3
-
4
- export const TUI_THEME_IDS = [
5
- "ansi16",
6
- "molokai-dark",
7
- "molokai-light",
8
- ] as const;
9
-
10
- export type TUIThemeId = (typeof TUI_THEME_IDS)[number];
11
-
12
- export interface TUIThemeDefinition {
13
- id: TUIThemeId;
14
- label: string;
15
- palette: Theme;
16
- syntax: SyntaxHighlightTheme;
17
- rootFgColor?: Color;
18
- rootBgColor?: Color;
19
- userMessageBgColor?: Color;
20
- }
21
-
22
- const ANSI_SLOT_HEX: Record<Color, string> = {
23
- color00: "#000000",
24
- color01: "#cd3131",
25
- color02: "#0dbc79",
26
- color03: "#e5e510",
27
- color04: "#2472c8",
28
- color05: "#bc3fbc",
29
- color06: "#11a8cd",
30
- color07: "#e5e5e5",
31
- color08: "#666666",
32
- color09: "#f14c4c",
33
- color10: "#23d18b",
34
- color11: "#f5f543",
35
- color12: "#3b8eea",
36
- color13: "#d670d6",
37
- color14: "#29b8db",
38
- color15: "#ffffff",
39
- };
40
-
41
- export const theme = {
42
- black: "color00",
43
- red: "color01",
44
- green: "color02",
45
- yellow: "color03",
46
- blue: "color04",
47
- magenta: "color05",
48
- cyan: "color06",
49
- white: "color07",
50
- bblack: "color08",
51
- bred: "color09",
52
- bgreen: "color10",
53
- byellow: "color11",
54
- bblue: "color12",
55
- bmagenta: "color13",
56
- bcyan: "color14",
57
- bwhite: "color15",
58
- } as const satisfies Record<string, Color>;
59
-
60
- const ansi16Palette: Theme = {
61
- color00: 0,
62
- color01: 1,
63
- color02: 2,
64
- color03: 3,
65
- color04: 4,
66
- color05: 5,
67
- color06: 6,
68
- color07: 7,
69
- color08: 8,
70
- color09: 9,
71
- color10: 10,
72
- color11: 11,
73
- color12: 12,
74
- color13: 13,
75
- color14: 14,
76
- color15: 15,
77
- };
78
-
79
- const molokaiDarkPalette: Theme = {
80
- color00: "#272822",
81
- color01: "#f92672",
82
- color02: "#a6e22e",
83
- color03: "#e6db74",
84
- color04: "#66d9ef",
85
- color05: "#f92672",
86
- color06: "#66d9ef",
87
- color07: "#f8f8f2",
88
- color08: "#6f705f",
89
- color09: "#ff6188",
90
- color10: "#a6e22e",
91
- color11: "#ffd866",
92
- color12: "#78dce8",
93
- color13: "#ae81ff",
94
- color14: "#66d9ef",
95
- color15: "#ffffff",
96
- };
97
-
98
- const molokaiLightPalette: Theme = {
99
- color00: "#272822",
100
- color01: "#ff5f87",
101
- color02: "#8bcf26",
102
- color03: "#c7a100",
103
- color04: "#61aeee",
104
- color05: "#d16dff",
105
- color06: "#00a8b5",
106
- color07: "#f2efe4",
107
- color08: "#5f6060",
108
- color09: "#b0003a",
109
- color10: "#3f7d00",
110
- color11: "#725f00",
111
- color12: "#005f9f",
112
- color13: "#7f2caf",
113
- color14: "#007885",
114
- color15: "#fffdf5",
115
- };
116
-
117
- const slotColor = (slot: Color) => ANSI_SLOT_HEX[slot];
118
-
119
- function syntaxScope(
120
- scope: string | readonly string[],
121
- foreground: Color,
122
- fontStyle?: string,
123
- ) {
124
- return {
125
- scope,
126
- settings: {
127
- foreground: slotColor(foreground),
128
- ...(fontStyle ? { fontStyle } : {}),
129
- },
130
- };
131
- }
132
-
133
- const molokaiDarkSyntax = {
134
- name: "mini-coder-molokai-dark",
135
- type: "dark",
136
- fg: slotColor("color07"),
137
- tokenColors: [
138
- syntaxScope(["comment", "markup.quote"], "color08", "italic"),
139
- syntaxScope(["keyword", "operator"], "color05"),
140
- syntaxScope(["string", "escape", "markup.list"], "color03"),
141
- syntaxScope(["number", "regexp"], "color13"),
142
- syntaxScope(["function", "command", "markup.code"], "color10"),
143
- syntaxScope(["builtin", "property", "type", "meta"], "color06"),
144
- syntaxScope("markup.heading", "color04", "bold"),
145
- syntaxScope(["diff.deleted", "diff.file.old"], "color09"),
146
- syntaxScope(["diff.inserted", "diff.file.new"], "color10"),
147
- syntaxScope("diff.hunk", "color13"),
148
- syntaxScope(["diff.header", "diff.no-newline"], "color08"),
149
- ],
150
- } as const satisfies SyntaxHighlightTheme;
151
-
152
- const molokaiLightSyntax = {
153
- name: "mini-coder-molokai-light",
154
- type: "light",
155
- fg: slotColor("color00"),
156
- tokenColors: [
157
- syntaxScope(["comment", "markup.quote"], "color08", "italic"),
158
- syntaxScope(["keyword", "operator"], "color13"),
159
- syntaxScope(["string", "escape", "markup.list"], "color11"),
160
- syntaxScope(["number", "regexp"], "color09"),
161
- syntaxScope(["function", "command", "markup.code"], "color10"),
162
- syntaxScope(["builtin", "property", "type", "meta"], "color14"),
163
- syntaxScope("markup.heading", "color12", "bold"),
164
- syntaxScope(["diff.deleted", "diff.file.old"], "color09"),
165
- syntaxScope(["diff.inserted", "diff.file.new"], "color10"),
166
- syntaxScope("diff.hunk", "color13"),
167
- syntaxScope(["diff.header", "diff.no-newline"], "color08"),
168
- ],
169
- } as const satisfies SyntaxHighlightTheme;
170
-
171
- export const DEFAULT_TUI_THEME_ID: TUIThemeId = "ansi16";
172
-
173
- export const TUI_THEMES = {
174
- ansi16: {
175
- id: "ansi16",
176
- label: "ansi16",
177
- palette: ansi16Palette,
178
- syntax: "default",
179
- userMessageBgColor: theme.bblack,
180
- },
181
- "molokai-dark": {
182
- id: "molokai-dark",
183
- label: "molokai dark",
184
- palette: molokaiDarkPalette,
185
- syntax: molokaiDarkSyntax,
186
- rootFgColor: theme.bwhite,
187
- rootBgColor: theme.black,
188
- userMessageBgColor: theme.bblack,
189
- },
190
- "molokai-light": {
191
- id: "molokai-light",
192
- label: "molokai light",
193
- palette: molokaiLightPalette,
194
- syntax: molokaiLightSyntax,
195
- rootFgColor: theme.black,
196
- rootBgColor: theme.bwhite,
197
- userMessageBgColor: theme.white,
198
- },
199
- } as const satisfies Record<TUIThemeId, TUIThemeDefinition>;
200
-
201
- export const activeTuiTheme: Theme = { ...ansi16Palette };
202
-
203
- export function applyTUITheme(id: TUIThemeId): void {
204
- Object.assign(activeTuiTheme, TUI_THEMES[id].palette);
205
- }
206
-
207
- export function getTUITheme(id: TUIThemeId): TUIThemeDefinition {
208
- return TUI_THEMES[id];
209
- }
210
-
211
- export function textColorForBackground(
212
- bgColor: Color,
213
- themeId: TUIThemeId,
214
- ): Color {
215
- if (bgColor === theme.bblack) return theme.bwhite;
216
-
217
- if (
218
- themeId === "molokai-light" &&
219
- (
220
- [
221
- theme.bred,
222
- theme.bgreen,
223
- theme.byellow,
224
- theme.bblue,
225
- theme.bmagenta,
226
- theme.bcyan,
227
- ] as readonly Color[]
228
- ).includes(bgColor)
229
- ) {
230
- return theme.bwhite;
231
- }
232
-
233
- return theme.black;
234
- }