feat: add pink-purple theme, fix image paste race condition, allow @/commands anywhere in input

- Add Pink Purple theme (hot pink/purple/magenta on dark plum background)
- Fix race condition where clearPastedImages() in input-area ran before
  the async message handler could read the images, silently dropping them
- Allow @ file picker and / command menu to trigger at any cursor position,
  not just when the input is empty
- Update CHANGELOG and README with new changes
This commit is contained in:
2026-02-14 06:39:08 -05:00
parent ddbdb5eb3e
commit 6111530c08
84 changed files with 5643 additions and 1574 deletions

View File

@@ -1,14 +1,3 @@
/**
* Brain API Constants
*
* Configuration constants for the CodeTyper Brain service
*/
/**
* Feature flag to disable all Brain functionality.
* Set to true to hide Brain menu, disable Brain API calls,
* and remove Brain-related UI elements.
*/
export const BRAIN_DISABLED = true;
export const BRAIN_PROVIDER_NAME = "brain" as const;

View File

@@ -20,6 +20,21 @@ export const COPILOT_MODELS_CACHE_TTL = 5 * 60 * 1000; // 5 minutes
export const COPILOT_MAX_RETRIES = 3;
export const COPILOT_INITIAL_RETRY_DELAY = 1000; // 1 second
// Streaming timeout and connection retry
export const COPILOT_STREAM_TIMEOUT = 120_000; // 2 minutes
export const COPILOT_CONNECTION_RETRY_DELAY = 2000; // 2 seconds
// Connection error patterns for retry logic
export const CONNECTION_ERROR_PATTERNS = [
/socket.*closed/i,
/ECONNRESET/i,
/ECONNREFUSED/i,
/ETIMEDOUT/i,
/network.*error/i,
/fetch.*failed/i,
/aborted/i,
] as const;
// Default model
export const COPILOT_DEFAULT_MODEL = "gpt-5-mini";

180
src/constants/keybinds.ts Normal file
View File

@@ -0,0 +1,180 @@
/**
* Keybind Configuration
*
* Defines all configurable keybindings with defaults.
* Modeled after OpenCode's keybind system with leader key support,
* comma-separated alternatives, and `<leader>` prefix expansion.
*
* Format: "mod+key" or "mod+key,mod+key" for alternatives
* Special: "<leader>key" expands to "${leader}+key"
* "none" disables the binding
*/
// ============================================================================
// Keybind Action IDs
// ============================================================================
/**
* All possible keybind action identifiers.
* These map 1:1 to the defaults below.
*/
export type KeybindAction =
// Application
| "app_exit"
| "app_interrupt"
// Session / execution
| "session_interrupt"
| "session_abort_rollback"
| "session_pause_resume"
| "session_step_toggle"
| "session_step_advance"
// Navigation & scrolling
| "messages_page_up"
| "messages_page_down"
// Mode switching
| "mode_toggle"
// Input area
| "input_submit"
| "input_newline"
| "input_clear"
| "input_paste"
// Menus & pickers
| "command_menu"
| "file_picker"
| "model_list"
| "theme_list"
| "agent_list"
| "help_menu"
// Clipboard
| "clipboard_copy"
// Sidebar / panels
| "sidebar_toggle"
| "activity_toggle";
// ============================================================================
// Default Keybinds
// ============================================================================
/**
* Default leader key prefix (similar to vim leader or OpenCode's ctrl+x).
*/
export const DEFAULT_LEADER = "ctrl+x";
/**
* Default keybindings for all actions.
* Format: comma-separated list of key combos.
* - `ctrl+c` — modifier + key
* - `escape` — single key
* - `<leader>q` — leader prefix + key (expands to e.g. `ctrl+x,q`)
* - `none` — binding disabled
*/
export const DEFAULT_KEYBINDS: Readonly<Record<KeybindAction, string>> = {
// Application
app_exit: "ctrl+c",
app_interrupt: "ctrl+c",
// Session / execution control
session_interrupt: "escape",
session_abort_rollback: "ctrl+z",
session_pause_resume: "ctrl+p",
session_step_toggle: "ctrl+shift+s",
session_step_advance: "return",
// Navigation
messages_page_up: "pageup",
messages_page_down: "pagedown",
// Mode switching
mode_toggle: "ctrl+e",
// Input area
input_submit: "return",
input_newline: "shift+return,ctrl+return",
input_clear: "ctrl+c",
input_paste: "ctrl+v",
// Menus & pickers
command_menu: "/",
file_picker: "@",
model_list: "<leader>m",
theme_list: "<leader>t",
agent_list: "<leader>a",
help_menu: "<leader>h",
// Clipboard
clipboard_copy: "ctrl+y",
// Sidebar / panels
sidebar_toggle: "<leader>b",
activity_toggle: "<leader>s",
} as const;
/**
* Descriptions for each keybind action (used in help menus)
*/
export const KEYBIND_DESCRIPTIONS: Readonly<Record<KeybindAction, string>> = {
app_exit: "Exit the application",
app_interrupt: "Interrupt / abort current action",
session_interrupt: "Cancel current operation",
session_abort_rollback: "Abort with rollback",
session_pause_resume: "Toggle pause/resume execution",
session_step_toggle: "Toggle step-by-step mode",
session_step_advance: "Advance one step",
messages_page_up: "Scroll messages up by one page",
messages_page_down: "Scroll messages down by one page",
mode_toggle: "Toggle interaction mode (agent/ask/code-review)",
input_submit: "Submit input",
input_newline: "Insert newline in input",
input_clear: "Clear input field",
input_paste: "Paste from clipboard",
command_menu: "Open command menu",
file_picker: "Open file picker",
model_list: "List available models",
theme_list: "List available themes",
agent_list: "List agents",
help_menu: "Show help",
clipboard_copy: "Copy selection to clipboard",
sidebar_toggle: "Toggle sidebar panel",
activity_toggle: "Toggle activity/status panel",
} as const;
/**
* Categories for grouping keybinds in the help menu
*/
export const KEYBIND_CATEGORIES: Readonly<
Record<string, readonly KeybindAction[]>
> = {
Application: ["app_exit", "app_interrupt"],
"Execution Control": [
"session_interrupt",
"session_abort_rollback",
"session_pause_resume",
"session_step_toggle",
"session_step_advance",
],
Navigation: ["messages_page_up", "messages_page_down"],
"Mode & Input": [
"mode_toggle",
"input_submit",
"input_newline",
"input_clear",
"input_paste",
],
"Menus & Pickers": [
"command_menu",
"file_picker",
"model_list",
"theme_list",
"agent_list",
"help_menu",
],
Panels: ["sidebar_toggle", "activity_toggle"],
Clipboard: ["clipboard_copy"],
} as const;

View File

@@ -25,6 +25,36 @@ export const SKILL_DIRS = {
PROJECT: ".codetyper/skills",
} as const;
/**
* External agent directories (relative to project root)
* These directories may contain agent definition files from
* various tools (Claude, GitHub Copilot, CodeTyper, etc.)
*/
export const EXTERNAL_AGENT_DIRS = {
CLAUDE: ".claude",
GITHUB: ".github",
CODETYPER: ".codetyper",
} as const;
/**
* Recognized external agent file patterns
*/
export const EXTERNAL_AGENT_FILES = {
/** File extensions recognized as agent definitions */
EXTENSIONS: [".md", ".yaml", ".yml"],
/** Known agent file names (case-insensitive) */
KNOWN_FILES: [
"AGENTS.md",
"agents.md",
"AGENT.md",
"agent.md",
"SKILL.md",
"copilot-instructions.md",
],
/** Subdirectories to scan for agents */
SUBDIRS: ["agents", "skills", "prompts"],
} as const;
/**
* Skill loading configuration
*/
@@ -89,8 +119,196 @@ export const BUILTIN_SKILLS = {
REVIEW_PR: "review-pr",
EXPLAIN: "explain",
FEATURE_DEV: "feature-dev",
TYPESCRIPT: "typescript",
REACT: "react",
CSS_SCSS: "css-scss",
SECURITY: "security",
CODE_AUDIT: "code-audit",
RESEARCHER: "researcher",
TESTING: "testing",
PERFORMANCE: "performance",
API_DESIGN: "api-design",
DATABASE: "database",
DEVOPS: "devops",
ACCESSIBILITY: "accessibility",
DOCUMENTATION: "documentation",
REFACTORING: "refactoring",
GIT_WORKFLOW: "git-workflow",
NODE_BACKEND: "node-backend",
} as const;
/**
* Skill auto-detection keyword map.
* Maps keywords found in user prompts to skill IDs.
* Each entry: [keyword, skillId, category, weight]
*/
export const SKILL_DETECTION_KEYWORDS: ReadonlyArray<
readonly [string, string, string, number]
> = [
// TypeScript
["typescript", "typescript", "language", 0.9],
["type error", "typescript", "language", 0.85],
["ts error", "typescript", "language", 0.85],
["generics", "typescript", "language", 0.8],
["type system", "typescript", "language", 0.85],
["interface", "typescript", "language", 0.5],
["type alias", "typescript", "language", 0.8],
[".ts", "typescript", "language", 0.4],
[".tsx", "typescript", "language", 0.5],
// React
["react", "react", "framework", 0.9],
["component", "react", "framework", 0.5],
["hooks", "react", "framework", 0.7],
["usestate", "react", "framework", 0.9],
["useeffect", "react", "framework", 0.9],
["jsx", "react", "framework", 0.8],
["tsx", "react", "framework", 0.7],
["react component", "react", "framework", 0.95],
["props", "react", "framework", 0.5],
["useState", "react", "framework", 0.9],
// CSS/SCSS
["css", "css-scss", "styling", 0.8],
["scss", "css-scss", "styling", 0.9],
["sass", "css-scss", "styling", 0.9],
["styling", "css-scss", "styling", 0.6],
["flexbox", "css-scss", "styling", 0.9],
["grid layout", "css-scss", "styling", 0.85],
["responsive", "css-scss", "styling", 0.6],
["animation", "css-scss", "styling", 0.5],
["tailwind", "css-scss", "styling", 0.7],
// Security
["security", "security", "domain", 0.9],
["vulnerability", "security", "domain", 0.95],
["xss", "security", "domain", 0.95],
["sql injection", "security", "domain", 0.95],
["csrf", "security", "domain", 0.95],
["authentication", "security", "domain", 0.6],
["authorization", "security", "domain", 0.6],
["owasp", "security", "domain", 0.95],
["cve", "security", "domain", 0.9],
["penetration", "security", "domain", 0.85],
// Code Audit
["audit", "code-audit", "domain", 0.85],
["code quality", "code-audit", "domain", 0.9],
["tech debt", "code-audit", "domain", 0.9],
["dead code", "code-audit", "domain", 0.9],
["complexity", "code-audit", "domain", 0.6],
["code smell", "code-audit", "domain", 0.9],
["code review", "code-audit", "domain", 0.5],
// Research
["research", "researcher", "workflow", 0.8],
["find out", "researcher", "workflow", 0.5],
["look up", "researcher", "workflow", 0.5],
["documentation", "researcher", "workflow", 0.5],
["best practice", "researcher", "workflow", 0.6],
["compare", "researcher", "workflow", 0.4],
// Testing
["test", "testing", "workflow", 0.5],
["testing", "testing", "workflow", 0.8],
["unit test", "testing", "workflow", 0.9],
["integration test", "testing", "workflow", 0.9],
["e2e", "testing", "workflow", 0.85],
["tdd", "testing", "workflow", 0.9],
["jest", "testing", "workflow", 0.85],
["vitest", "testing", "workflow", 0.9],
["playwright", "testing", "workflow", 0.9],
["coverage", "testing", "workflow", 0.6],
// Performance
["performance", "performance", "domain", 0.8],
["optimization", "performance", "domain", 0.7],
["optimize", "performance", "domain", 0.7],
["slow", "performance", "domain", 0.5],
["bundle size", "performance", "domain", 0.9],
["memory leak", "performance", "domain", 0.9],
["latency", "performance", "domain", 0.7],
["profiling", "performance", "domain", 0.85],
// API Design
["api", "api-design", "domain", 0.5],
["endpoint", "api-design", "domain", 0.6],
["rest", "api-design", "domain", 0.7],
["graphql", "api-design", "domain", 0.9],
["openapi", "api-design", "domain", 0.9],
["swagger", "api-design", "domain", 0.9],
// Database
["database", "database", "domain", 0.9],
["sql", "database", "domain", 0.8],
["query", "database", "domain", 0.4],
["migration", "database", "domain", 0.7],
["schema", "database", "domain", 0.7],
["orm", "database", "domain", 0.85],
["prisma", "database", "domain", 0.9],
["drizzle", "database", "domain", 0.9],
["postgres", "database", "domain", 0.9],
["mysql", "database", "domain", 0.9],
["mongodb", "database", "domain", 0.9],
// DevOps
["devops", "devops", "domain", 0.9],
["docker", "devops", "domain", 0.9],
["ci/cd", "devops", "domain", 0.9],
["pipeline", "devops", "domain", 0.7],
["deploy", "devops", "domain", 0.7],
["kubernetes", "devops", "domain", 0.95],
["k8s", "devops", "domain", 0.95],
["github actions", "devops", "domain", 0.9],
// Accessibility
["accessibility", "accessibility", "domain", 0.95],
["a11y", "accessibility", "domain", 0.95],
["wcag", "accessibility", "domain", 0.95],
["aria", "accessibility", "domain", 0.85],
["screen reader", "accessibility", "domain", 0.9],
// Documentation
["documentation", "documentation", "workflow", 0.7],
["readme", "documentation", "workflow", 0.8],
["jsdoc", "documentation", "workflow", 0.9],
["document this", "documentation", "workflow", 0.7],
// Refactoring
["refactor", "refactoring", "workflow", 0.9],
["refactoring", "refactoring", "workflow", 0.9],
["clean up", "refactoring", "workflow", 0.6],
["restructure", "refactoring", "workflow", 0.7],
["simplify", "refactoring", "workflow", 0.5],
["solid principles", "refactoring", "workflow", 0.85],
["design pattern", "refactoring", "workflow", 0.7],
// Git
["git", "git-workflow", "tool", 0.5],
["branch", "git-workflow", "tool", 0.4],
["merge conflict", "git-workflow", "tool", 0.9],
["rebase", "git-workflow", "tool", 0.85],
["cherry-pick", "git-workflow", "tool", 0.9],
// Node.js Backend
["express", "node-backend", "framework", 0.85],
["fastify", "node-backend", "framework", 0.9],
["middleware", "node-backend", "framework", 0.6],
["api server", "node-backend", "framework", 0.8],
["backend", "node-backend", "framework", 0.5],
["server", "node-backend", "framework", 0.4],
] as const;
/**
* Minimum confidence for auto-detection to trigger
*/
export const SKILL_AUTO_DETECT_THRESHOLD = 0.6;
/**
* Maximum number of skills to auto-activate per prompt
*/
export const SKILL_AUTO_DETECT_MAX = 3;
/**
* Skill trigger patterns for common commands
*/

View File

@@ -790,6 +790,62 @@ const CARGDEV_CYBERPUNK_COLORS: ThemeColors = {
headerGradient: ["#ff79c6", "#bd93f9", "#8be9fd"],
};
const PINK_PURPLE_COLORS: ThemeColors = {
primary: "#ff69b4",
secondary: "#b47ee5",
accent: "#e84393",
success: "#a3e048",
error: "#ff4757",
warning: "#ffa502",
info: "#cf6fef",
text: "#f5e6f0",
textDim: "#9a7aa0",
textMuted: "#4a3050",
background: "#1a0a20",
backgroundPanel: "#120818",
backgroundElement: "#2a1535",
border: "#3d1f4e",
borderFocus: "#ff69b4",
borderWarning: "#ffa502",
borderModal: "#b47ee5",
bgHighlight: "#2a1535",
bgCursor: "#e84393",
bgAdded: "#a3e048",
bgRemoved: "#ff4757",
diffAdded: "#a3e048",
diffRemoved: "#ff4757",
diffContext: "#9a7aa0",
diffHeader: "#f5e6f0",
diffHunk: "#cf6fef",
diffLineBgAdded: "#1a2d1a",
diffLineBgRemoved: "#2d1a1a",
diffLineText: "#f5e6f0",
roleUser: "#ff69b4",
roleAssistant: "#b47ee5",
roleSystem: "#ffa502",
roleTool: "#cf6fef",
modeIdle: "#b47ee5",
modeEditing: "#ff69b4",
modeThinking: "#e84393",
modeToolExecution: "#ffa502",
modePermission: "#cf6fef",
toolPending: "#9a7aa0",
toolRunning: "#ffa502",
toolSuccess: "#a3e048",
toolError: "#ff4757",
headerGradient: ["#ff69b4", "#e84393", "#b47ee5"],
};
export const THEMES: Record<string, Theme> = {
default: {
name: "default",
@@ -861,6 +917,11 @@ export const THEMES: Record<string, Theme> = {
displayName: "Cargdev Cyberpunk",
colors: CARGDEV_CYBERPUNK_COLORS,
},
"pink-purple": {
name: "pink-purple",
displayName: "Pink Purple",
colors: PINK_PURPLE_COLORS,
},
};
export const THEME_NAMES = Object.keys(THEMES);

View File

@@ -13,6 +13,12 @@ export type SchemaSkipKey = (typeof SCHEMA_SKIP_KEYS)[number];
export const TOOL_NAMES = ["read", "glob", "grep"];
/**
* Tools that can modify files
* Tools that can modify files — used for tracking modified files in the TUI
*/
export const FILE_MODIFYING_TOOLS = ["write", "edit"] as const;
export const FILE_MODIFYING_TOOLS = [
"write",
"edit",
"multi_edit",
"apply_patch",
"bash",
] as const;