mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-01-31 06:42:03 +00:00
refactor(store): Extract types from app-store.ts into modular type files
- Create store/types/ directory with 8 modular type files: - usage-types.ts: ClaudeUsage, CodexUsage, isClaudeUsageAtLimit - ui-types.ts: ViewMode, ThemeMode, KeyboardShortcuts, etc. - settings-types.ts: ApiKeys - chat-types.ts: ChatMessage, ChatSession, FeatureImage - terminal-types.ts: TerminalState, TerminalTab, etc. - project-types.ts: Feature, FileTreeNode, ProjectAnalysis - state-types.ts: AppState, AppActions interfaces - index.ts: Re-exports all types - Update electron.ts to import from store/types/usage-types (breaks circular dependency between electron.ts and app-store.ts) - Update app-store.ts to import and re-export types for backward compatibility - existing imports from @/store/app-store continue to work This is PR 1 of the app-store refactoring plan. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
// Type definitions for Electron IPC API
|
||||
import type { SessionListItem, Message } from '@/types/electron';
|
||||
import type { ClaudeUsageResponse, CodexUsageResponse } from '@/store/app-store';
|
||||
import type { ClaudeUsageResponse, CodexUsageResponse } from '@/store/types/usage-types';
|
||||
import type {
|
||||
IssueValidationVerdict,
|
||||
IssueValidationConfidence,
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
43
apps/ui/src/store/types/chat-types.ts
Normal file
43
apps/ui/src/store/types/chat-types.ts
Normal file
@@ -0,0 +1,43 @@
|
||||
export interface ImageAttachment {
|
||||
id?: string; // Optional - may not be present in messages loaded from server
|
||||
data: string; // base64 encoded image data
|
||||
mimeType: string; // e.g., "image/png", "image/jpeg"
|
||||
filename: string;
|
||||
size?: number; // file size in bytes - optional for messages from server
|
||||
}
|
||||
|
||||
export interface TextFileAttachment {
|
||||
id: string;
|
||||
content: string; // text content of the file
|
||||
mimeType: string; // e.g., "text/plain", "text/markdown"
|
||||
filename: string;
|
||||
size: number; // file size in bytes
|
||||
}
|
||||
|
||||
export interface ChatMessage {
|
||||
id: string;
|
||||
role: 'user' | 'assistant';
|
||||
content: string;
|
||||
timestamp: Date;
|
||||
images?: ImageAttachment[];
|
||||
textFiles?: TextFileAttachment[];
|
||||
}
|
||||
|
||||
export interface ChatSession {
|
||||
id: string;
|
||||
title: string;
|
||||
projectId: string;
|
||||
messages: ChatMessage[];
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
archived: boolean;
|
||||
}
|
||||
|
||||
// UI-specific: base64-encoded images (not in shared types)
|
||||
export interface FeatureImage {
|
||||
id: string;
|
||||
data: string; // base64 encoded
|
||||
mimeType: string;
|
||||
filename: string;
|
||||
size: number;
|
||||
}
|
||||
7
apps/ui/src/store/types/index.ts
Normal file
7
apps/ui/src/store/types/index.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export * from './usage-types';
|
||||
export * from './ui-types';
|
||||
export * from './settings-types';
|
||||
export * from './chat-types';
|
||||
export * from './terminal-types';
|
||||
export * from './project-types';
|
||||
export * from './state-types';
|
||||
66
apps/ui/src/store/types/project-types.ts
Normal file
66
apps/ui/src/store/types/project-types.ts
Normal file
@@ -0,0 +1,66 @@
|
||||
import type {
|
||||
Feature as BaseFeature,
|
||||
FeatureImagePath,
|
||||
FeatureTextFilePath,
|
||||
ThinkingLevel,
|
||||
ReasoningEffort,
|
||||
FeatureStatusWithPipeline,
|
||||
PlanSpec,
|
||||
} from '@automaker/types';
|
||||
import type { FeatureImage } from './chat-types';
|
||||
|
||||
// Available models for feature execution
|
||||
export type ClaudeModel = 'opus' | 'sonnet' | 'haiku';
|
||||
|
||||
export interface Feature extends Omit<
|
||||
BaseFeature,
|
||||
| 'steps'
|
||||
| 'imagePaths'
|
||||
| 'textFilePaths'
|
||||
| 'status'
|
||||
| 'planSpec'
|
||||
| 'dependencies'
|
||||
| 'model'
|
||||
| 'branchName'
|
||||
| 'thinkingLevel'
|
||||
| 'reasoningEffort'
|
||||
| 'summary'
|
||||
> {
|
||||
id: string;
|
||||
title?: string;
|
||||
titleGenerating?: boolean;
|
||||
category: string;
|
||||
description: string;
|
||||
steps: string[]; // Required in UI (not optional)
|
||||
status: FeatureStatusWithPipeline;
|
||||
images?: FeatureImage[]; // UI-specific base64 images
|
||||
imagePaths?: FeatureImagePath[]; // Stricter type than base (no string | union)
|
||||
textFilePaths?: FeatureTextFilePath[]; // Text file attachments for context
|
||||
justFinishedAt?: string; // UI-specific: ISO timestamp when agent just finished
|
||||
prUrl?: string; // UI-specific: Pull request URL
|
||||
planSpec?: PlanSpec; // Explicit planSpec type to override BaseFeature's index signature
|
||||
dependencies?: string[]; // Explicit type to override BaseFeature's index signature
|
||||
model?: string; // Explicit type to override BaseFeature's index signature
|
||||
branchName?: string; // Explicit type to override BaseFeature's index signature
|
||||
thinkingLevel?: ThinkingLevel; // Explicit type to override BaseFeature's index signature
|
||||
reasoningEffort?: ReasoningEffort; // Explicit type to override BaseFeature's index signature
|
||||
summary?: string; // Explicit type to override BaseFeature's index signature
|
||||
}
|
||||
|
||||
// File tree node for project analysis
|
||||
export interface FileTreeNode {
|
||||
name: string;
|
||||
path: string;
|
||||
isDirectory: boolean;
|
||||
extension?: string;
|
||||
children?: FileTreeNode[];
|
||||
}
|
||||
|
||||
// Project analysis result
|
||||
export interface ProjectAnalysis {
|
||||
fileTree: FileTreeNode[];
|
||||
totalFiles: number;
|
||||
totalDirectories: number;
|
||||
filesByExtension: Record<string, number>;
|
||||
analyzedAt: string;
|
||||
}
|
||||
5
apps/ui/src/store/types/settings-types.ts
Normal file
5
apps/ui/src/store/types/settings-types.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export interface ApiKeys {
|
||||
anthropic: string;
|
||||
google: string;
|
||||
openai: string;
|
||||
}
|
||||
806
apps/ui/src/store/types/state-types.ts
Normal file
806
apps/ui/src/store/types/state-types.ts
Normal file
@@ -0,0 +1,806 @@
|
||||
import type { Project, TrashedProject } from '@/lib/electron';
|
||||
import type {
|
||||
ModelAlias,
|
||||
PlanningMode,
|
||||
ThinkingLevel,
|
||||
ReasoningEffort,
|
||||
ModelProvider,
|
||||
CursorModelId,
|
||||
CodexModelId,
|
||||
OpencodeModelId,
|
||||
GeminiModelId,
|
||||
CopilotModelId,
|
||||
PhaseModelConfig,
|
||||
PhaseModelKey,
|
||||
PhaseModelEntry,
|
||||
MCPServerConfig,
|
||||
PipelineConfig,
|
||||
PipelineStep,
|
||||
PromptCustomization,
|
||||
ModelDefinition,
|
||||
ServerLogLevel,
|
||||
EventHook,
|
||||
ClaudeApiProfile,
|
||||
ClaudeCompatibleProvider,
|
||||
SidebarStyle,
|
||||
} from '@automaker/types';
|
||||
|
||||
import type { ViewMode, ThemeMode, BoardViewMode, KeyboardShortcuts } from './ui-types';
|
||||
import type { ApiKeys } from './settings-types';
|
||||
import type { ChatMessage, ChatSession, FeatureImage } from './chat-types';
|
||||
import type { TerminalState, TerminalPanelContent, PersistedTerminalState } from './terminal-types';
|
||||
import type { Feature, ProjectAnalysis } from './project-types';
|
||||
import type { ClaudeUsage, CodexUsage } from './usage-types';
|
||||
|
||||
/** State for worktree init script execution */
|
||||
export interface InitScriptState {
|
||||
status: 'idle' | 'running' | 'success' | 'failed';
|
||||
branch: string;
|
||||
output: string[];
|
||||
error?: string;
|
||||
}
|
||||
|
||||
export interface AutoModeActivity {
|
||||
id: string;
|
||||
featureId: string;
|
||||
timestamp: Date;
|
||||
type:
|
||||
| 'start'
|
||||
| 'progress'
|
||||
| 'tool'
|
||||
| 'complete'
|
||||
| 'error'
|
||||
| 'planning'
|
||||
| 'action'
|
||||
| 'verification';
|
||||
message: string;
|
||||
tool?: string;
|
||||
passes?: boolean;
|
||||
phase?: 'planning' | 'action' | 'verification';
|
||||
errorType?: 'authentication' | 'execution';
|
||||
}
|
||||
|
||||
export interface AppState {
|
||||
// Project state
|
||||
projects: Project[];
|
||||
currentProject: Project | null;
|
||||
trashedProjects: TrashedProject[];
|
||||
projectHistory: string[]; // Array of project IDs in MRU order (most recent first)
|
||||
projectHistoryIndex: number; // Current position in project history for cycling
|
||||
|
||||
// View state
|
||||
currentView: ViewMode;
|
||||
sidebarOpen: boolean;
|
||||
sidebarStyle: SidebarStyle; // 'unified' (modern) or 'discord' (classic two-sidebar layout)
|
||||
collapsedNavSections: Record<string, boolean>; // Collapsed state of nav sections (key: section label)
|
||||
mobileSidebarHidden: boolean; // Completely hides sidebar on mobile
|
||||
|
||||
// Agent Session state (per-project, keyed by project path)
|
||||
lastSelectedSessionByProject: Record<string, string>; // projectPath -> sessionId
|
||||
|
||||
// Theme
|
||||
theme: ThemeMode;
|
||||
|
||||
// Fonts (global defaults)
|
||||
fontFamilySans: string | null; // null = use default Geist Sans
|
||||
fontFamilyMono: string | null; // null = use default Geist Mono
|
||||
|
||||
// Features/Kanban
|
||||
features: Feature[];
|
||||
|
||||
// App spec
|
||||
appSpec: string;
|
||||
|
||||
// IPC status
|
||||
ipcConnected: boolean;
|
||||
|
||||
// API Keys
|
||||
apiKeys: ApiKeys;
|
||||
|
||||
// Chat Sessions
|
||||
chatSessions: ChatSession[];
|
||||
currentChatSession: ChatSession | null;
|
||||
chatHistoryOpen: boolean;
|
||||
|
||||
// Auto Mode (per-worktree state, keyed by "${projectId}::${branchName ?? '__main__'}")
|
||||
autoModeByWorktree: Record<
|
||||
string,
|
||||
{
|
||||
isRunning: boolean;
|
||||
runningTasks: string[]; // Feature IDs being worked on
|
||||
branchName: string | null; // null = main worktree
|
||||
maxConcurrency?: number; // Maximum concurrent features for this worktree (defaults to 3)
|
||||
}
|
||||
>;
|
||||
autoModeActivityLog: AutoModeActivity[];
|
||||
maxConcurrency: number; // Legacy: Maximum number of concurrent agent tasks (deprecated, use per-worktree maxConcurrency)
|
||||
|
||||
// Kanban Card Display Settings
|
||||
boardViewMode: BoardViewMode; // Whether to show kanban or dependency graph view
|
||||
|
||||
// Feature Default Settings
|
||||
defaultSkipTests: boolean; // Default value for skip tests when creating new features
|
||||
enableDependencyBlocking: boolean; // When true, show blocked badges and warnings for features with incomplete dependencies (default: true)
|
||||
skipVerificationInAutoMode: boolean; // When true, auto-mode grabs features even if dependencies are not verified (only checks they're not running)
|
||||
enableAiCommitMessages: boolean; // When true, auto-generate commit messages using AI when opening commit dialog
|
||||
planUseSelectedWorktreeBranch: boolean; // When true, Plan dialog creates features on the currently selected worktree branch
|
||||
addFeatureUseSelectedWorktreeBranch: boolean; // When true, Add Feature dialog defaults to custom mode with selected worktree branch
|
||||
|
||||
// Worktree Settings
|
||||
useWorktrees: boolean; // Whether to use git worktree isolation for features (default: true)
|
||||
|
||||
// User-managed Worktrees (per-project)
|
||||
// projectPath -> { path: worktreePath or null for main, branch: branch name }
|
||||
currentWorktreeByProject: Record<string, { path: string | null; branch: string }>;
|
||||
worktreesByProject: Record<
|
||||
string,
|
||||
Array<{
|
||||
path: string;
|
||||
branch: string;
|
||||
isMain: boolean;
|
||||
isCurrent: boolean;
|
||||
hasWorktree: boolean;
|
||||
hasChanges?: boolean;
|
||||
changedFilesCount?: number;
|
||||
}>
|
||||
>;
|
||||
|
||||
// Keyboard Shortcuts
|
||||
keyboardShortcuts: KeyboardShortcuts; // User-defined keyboard shortcuts
|
||||
|
||||
// Audio Settings
|
||||
muteDoneSound: boolean; // When true, mute the notification sound when agents complete (default: false)
|
||||
|
||||
// Splash Screen Settings
|
||||
disableSplashScreen: boolean; // When true, skip showing the splash screen overlay on startup
|
||||
|
||||
// Server Log Level Settings
|
||||
serverLogLevel: ServerLogLevel; // Log level for the API server (error, warn, info, debug)
|
||||
enableRequestLogging: boolean; // Enable HTTP request logging (Morgan)
|
||||
|
||||
// Developer Tools Settings
|
||||
showQueryDevtools: boolean; // Show React Query DevTools panel (only in development mode)
|
||||
|
||||
// Enhancement Model Settings
|
||||
enhancementModel: ModelAlias; // Model used for feature enhancement (default: sonnet)
|
||||
|
||||
// Validation Model Settings
|
||||
validationModel: ModelAlias; // Model used for GitHub issue validation (default: opus)
|
||||
|
||||
// Phase Model Settings - per-phase AI model configuration
|
||||
phaseModels: PhaseModelConfig;
|
||||
favoriteModels: string[];
|
||||
|
||||
// Cursor CLI Settings (global)
|
||||
enabledCursorModels: CursorModelId[]; // Which Cursor models are available in feature modal
|
||||
cursorDefaultModel: CursorModelId; // Default Cursor model selection
|
||||
|
||||
// Codex CLI Settings (global)
|
||||
enabledCodexModels: CodexModelId[]; // Which Codex models are available in feature modal
|
||||
codexDefaultModel: CodexModelId; // Default Codex model selection
|
||||
codexAutoLoadAgents: boolean; // Auto-load .codex/AGENTS.md files
|
||||
codexSandboxMode: 'read-only' | 'workspace-write' | 'danger-full-access'; // Sandbox policy
|
||||
codexApprovalPolicy: 'untrusted' | 'on-failure' | 'on-request' | 'never'; // Approval policy
|
||||
codexEnableWebSearch: boolean; // Enable web search capability
|
||||
codexEnableImages: boolean; // Enable image processing
|
||||
|
||||
// OpenCode CLI Settings (global)
|
||||
// Static OpenCode settings are persisted via SETTINGS_FIELDS_TO_SYNC
|
||||
enabledOpencodeModels: OpencodeModelId[]; // Which static OpenCode models are available
|
||||
opencodeDefaultModel: OpencodeModelId; // Default OpenCode model selection
|
||||
// Dynamic models are session-only (not persisted) because they're discovered at runtime
|
||||
// from `opencode models` CLI and depend on current provider authentication state
|
||||
dynamicOpencodeModels: ModelDefinition[]; // Dynamically discovered models from OpenCode CLI
|
||||
enabledDynamicModelIds: string[]; // Which dynamic models are enabled
|
||||
cachedOpencodeProviders: Array<{
|
||||
id: string;
|
||||
name: string;
|
||||
authenticated: boolean;
|
||||
authMethod?: string;
|
||||
}>; // Cached providers
|
||||
opencodeModelsLoading: boolean; // Whether OpenCode models are being fetched
|
||||
opencodeModelsError: string | null; // Error message if fetch failed
|
||||
opencodeModelsLastFetched: number | null; // Timestamp of last successful fetch
|
||||
opencodeModelsLastFailedAt: number | null; // Timestamp of last failed fetch
|
||||
|
||||
// Gemini CLI Settings (global)
|
||||
enabledGeminiModels: GeminiModelId[]; // Which Gemini models are available in feature modal
|
||||
geminiDefaultModel: GeminiModelId; // Default Gemini model selection
|
||||
|
||||
// Copilot SDK Settings (global)
|
||||
enabledCopilotModels: CopilotModelId[]; // Which Copilot models are available in feature modal
|
||||
copilotDefaultModel: CopilotModelId; // Default Copilot model selection
|
||||
|
||||
// Provider Visibility Settings
|
||||
disabledProviders: ModelProvider[]; // Providers that are disabled and hidden from dropdowns
|
||||
|
||||
// Claude Agent SDK Settings
|
||||
autoLoadClaudeMd: boolean; // Auto-load CLAUDE.md files using SDK's settingSources option
|
||||
skipSandboxWarning: boolean; // Skip the sandbox environment warning dialog on startup
|
||||
|
||||
// MCP Servers
|
||||
mcpServers: MCPServerConfig[]; // List of configured MCP servers for agent use
|
||||
|
||||
// Editor Configuration
|
||||
defaultEditorCommand: string | null; // Default editor for "Open In" action
|
||||
|
||||
// Terminal Configuration
|
||||
defaultTerminalId: string | null; // Default external terminal for "Open In Terminal" action (null = integrated)
|
||||
|
||||
// Skills Configuration
|
||||
enableSkills: boolean; // Enable Skills functionality (loads from .claude/skills/ directories)
|
||||
skillsSources: Array<'user' | 'project'>; // Which directories to load Skills from
|
||||
|
||||
// Subagents Configuration
|
||||
enableSubagents: boolean; // Enable Custom Subagents functionality (loads from .claude/agents/ directories)
|
||||
subagentsSources: Array<'user' | 'project'>; // Which directories to load Subagents from
|
||||
|
||||
// Prompt Customization
|
||||
promptCustomization: PromptCustomization; // Custom prompts for Auto Mode, Agent, Backlog Plan, Enhancement
|
||||
|
||||
// Event Hooks
|
||||
eventHooks: EventHook[]; // Event hooks for custom commands or webhooks
|
||||
|
||||
// Claude-Compatible Providers (new system)
|
||||
claudeCompatibleProviders: ClaudeCompatibleProvider[]; // Providers that expose models to dropdowns
|
||||
|
||||
// Claude API Profiles (deprecated - kept for backward compatibility)
|
||||
claudeApiProfiles: ClaudeApiProfile[]; // Claude-compatible API endpoint profiles
|
||||
activeClaudeApiProfileId: string | null; // Active profile ID (null = use direct Anthropic API)
|
||||
|
||||
// Project Analysis
|
||||
projectAnalysis: ProjectAnalysis | null;
|
||||
isAnalyzing: boolean;
|
||||
|
||||
// Board Background Settings (per-project, keyed by project path)
|
||||
boardBackgroundByProject: Record<
|
||||
string,
|
||||
{
|
||||
imagePath: string | null; // Path to background image in .automaker directory
|
||||
imageVersion?: number; // Timestamp to bust browser cache when image is updated
|
||||
cardOpacity: number; // Opacity of cards (0-100)
|
||||
columnOpacity: number; // Opacity of columns (0-100)
|
||||
columnBorderEnabled: boolean; // Whether to show column borders
|
||||
cardGlassmorphism: boolean; // Whether to use glassmorphism (backdrop-blur) on cards
|
||||
cardBorderEnabled: boolean; // Whether to show card borders
|
||||
cardBorderOpacity: number; // Opacity of card borders (0-100)
|
||||
hideScrollbar: boolean; // Whether to hide the board scrollbar
|
||||
}
|
||||
>;
|
||||
|
||||
// Theme Preview (for hover preview in theme selectors)
|
||||
previewTheme: ThemeMode | null;
|
||||
|
||||
// Terminal state
|
||||
terminalState: TerminalState;
|
||||
|
||||
// Terminal layout persistence (per-project, keyed by project path)
|
||||
// Stores the tab/split structure so it can be restored when switching projects
|
||||
terminalLayoutByProject: Record<string, PersistedTerminalState>;
|
||||
|
||||
// Spec Creation State (per-project, keyed by project path)
|
||||
// Tracks which project is currently having its spec generated
|
||||
specCreatingForProject: string | null;
|
||||
|
||||
defaultPlanningMode: PlanningMode;
|
||||
defaultRequirePlanApproval: boolean;
|
||||
defaultFeatureModel: PhaseModelEntry;
|
||||
|
||||
// Plan Approval State
|
||||
// When a plan requires user approval, this holds the pending approval details
|
||||
pendingPlanApproval: {
|
||||
featureId: string;
|
||||
projectPath: string;
|
||||
planContent: string;
|
||||
planningMode: 'lite' | 'spec' | 'full';
|
||||
} | null;
|
||||
|
||||
// Claude Usage Tracking
|
||||
claudeRefreshInterval: number; // Refresh interval in seconds (default: 60)
|
||||
claudeUsage: ClaudeUsage | null;
|
||||
claudeUsageLastUpdated: number | null;
|
||||
|
||||
// Codex Usage Tracking
|
||||
codexUsage: CodexUsage | null;
|
||||
codexUsageLastUpdated: number | null;
|
||||
|
||||
// Codex Models (dynamically fetched)
|
||||
codexModels: Array<{
|
||||
id: string;
|
||||
label: string;
|
||||
description: string;
|
||||
hasThinking: boolean;
|
||||
supportsVision: boolean;
|
||||
tier: 'premium' | 'standard' | 'basic';
|
||||
isDefault: boolean;
|
||||
}>;
|
||||
codexModelsLoading: boolean;
|
||||
codexModelsError: string | null;
|
||||
codexModelsLastFetched: number | null;
|
||||
codexModelsLastFailedAt: number | null;
|
||||
|
||||
// Pipeline Configuration (per-project, keyed by project path)
|
||||
pipelineConfigByProject: Record<string, PipelineConfig>;
|
||||
|
||||
// Worktree Panel Visibility (per-project, keyed by project path)
|
||||
// Whether the worktree panel row is visible (default: true)
|
||||
worktreePanelVisibleByProject: Record<string, boolean>;
|
||||
|
||||
// Init Script Indicator Visibility (per-project, keyed by project path)
|
||||
// Whether to show the floating init script indicator panel (default: true)
|
||||
showInitScriptIndicatorByProject: Record<string, boolean>;
|
||||
|
||||
// Default Delete Branch With Worktree (per-project, keyed by project path)
|
||||
// Whether to default the "delete branch" checkbox when deleting a worktree (default: false)
|
||||
defaultDeleteBranchByProject: Record<string, boolean>;
|
||||
|
||||
// Auto-dismiss Init Script Indicator (per-project, keyed by project path)
|
||||
// Whether to auto-dismiss the indicator after completion (default: true)
|
||||
autoDismissInitScriptIndicatorByProject: Record<string, boolean>;
|
||||
|
||||
// Use Worktrees Override (per-project, keyed by project path)
|
||||
// undefined = use global setting, true/false = project-specific override
|
||||
useWorktreesByProject: Record<string, boolean | undefined>;
|
||||
|
||||
// UI State (previously in localStorage, now synced via API)
|
||||
/** Whether worktree panel is collapsed in board view */
|
||||
worktreePanelCollapsed: boolean;
|
||||
/** Last directory opened in file picker */
|
||||
lastProjectDir: string;
|
||||
/** Recently accessed folders for quick access */
|
||||
recentFolders: string[];
|
||||
|
||||
// Init Script State (keyed by "projectPath::branch" to support concurrent scripts)
|
||||
initScriptState: Record<string, InitScriptState>;
|
||||
}
|
||||
|
||||
export interface AppActions {
|
||||
// Project actions
|
||||
setProjects: (projects: Project[]) => void;
|
||||
addProject: (project: Project) => void;
|
||||
removeProject: (projectId: string) => void;
|
||||
moveProjectToTrash: (projectId: string) => void;
|
||||
restoreTrashedProject: (projectId: string) => void;
|
||||
deleteTrashedProject: (projectId: string) => void;
|
||||
emptyTrash: () => void;
|
||||
setCurrentProject: (project: Project | null) => void;
|
||||
upsertAndSetCurrentProject: (path: string, name: string, theme?: ThemeMode) => Project; // Upsert project by path and set as current
|
||||
reorderProjects: (oldIndex: number, newIndex: number) => void;
|
||||
cyclePrevProject: () => void; // Cycle back through project history (Q)
|
||||
cycleNextProject: () => void; // Cycle forward through project history (E)
|
||||
clearProjectHistory: () => void; // Clear history, keeping only current project
|
||||
toggleProjectFavorite: (projectId: string) => void; // Toggle project favorite status
|
||||
setProjectIcon: (projectId: string, icon: string | null) => void; // Set project icon (null to clear)
|
||||
setProjectCustomIcon: (projectId: string, customIconPath: string | null) => void; // Set custom project icon image path (null to clear)
|
||||
setProjectName: (projectId: string, name: string) => void; // Update project name
|
||||
|
||||
// View actions
|
||||
setCurrentView: (view: ViewMode) => void;
|
||||
toggleSidebar: () => void;
|
||||
setSidebarOpen: (open: boolean) => void;
|
||||
setSidebarStyle: (style: SidebarStyle) => void;
|
||||
setCollapsedNavSections: (sections: Record<string, boolean>) => void;
|
||||
toggleNavSection: (sectionLabel: string) => void;
|
||||
toggleMobileSidebarHidden: () => void;
|
||||
setMobileSidebarHidden: (hidden: boolean) => void;
|
||||
|
||||
// Theme actions
|
||||
setTheme: (theme: ThemeMode) => void;
|
||||
setProjectTheme: (projectId: string, theme: ThemeMode | null) => void; // Set per-project theme (null to clear)
|
||||
getEffectiveTheme: () => ThemeMode; // Get the effective theme (project, global, or preview if set)
|
||||
setPreviewTheme: (theme: ThemeMode | null) => void; // Set preview theme for hover preview (null to clear)
|
||||
|
||||
// Font actions (global + per-project override)
|
||||
setFontSans: (fontFamily: string | null) => void; // Set global UI/sans font (null to clear)
|
||||
setFontMono: (fontFamily: string | null) => void; // Set global code/mono font (null to clear)
|
||||
setProjectFontSans: (projectId: string, fontFamily: string | null) => void; // Set per-project UI/sans font override (null = use global)
|
||||
setProjectFontMono: (projectId: string, fontFamily: string | null) => void; // Set per-project code/mono font override (null = use global)
|
||||
getEffectiveFontSans: () => string | null; // Get effective UI font (project override -> global -> null for default)
|
||||
getEffectiveFontMono: () => string | null; // Get effective code font (project override -> global -> null for default)
|
||||
|
||||
// Claude API Profile actions (per-project override)
|
||||
/** @deprecated Use setProjectPhaseModelOverride instead */
|
||||
setProjectClaudeApiProfile: (projectId: string, profileId: string | null | undefined) => void; // Set per-project Claude API profile (undefined = use global, null = direct API, string = specific profile)
|
||||
|
||||
// Project Phase Model Overrides
|
||||
setProjectPhaseModelOverride: (
|
||||
projectId: string,
|
||||
phase: PhaseModelKey,
|
||||
entry: PhaseModelEntry | null // null = use global
|
||||
) => void;
|
||||
clearAllProjectPhaseModelOverrides: (projectId: string) => void;
|
||||
|
||||
// Project Default Feature Model Override
|
||||
setProjectDefaultFeatureModel: (
|
||||
projectId: string,
|
||||
entry: PhaseModelEntry | null // null = use global
|
||||
) => void;
|
||||
|
||||
// Feature actions
|
||||
setFeatures: (features: Feature[]) => void;
|
||||
updateFeature: (id: string, updates: Partial<Feature>) => void;
|
||||
addFeature: (feature: Omit<Feature, 'id'> & Partial<Pick<Feature, 'id'>>) => Feature;
|
||||
removeFeature: (id: string) => void;
|
||||
moveFeature: (id: string, newStatus: Feature['status']) => void;
|
||||
|
||||
// App spec actions
|
||||
setAppSpec: (spec: string) => void;
|
||||
|
||||
// IPC actions
|
||||
setIpcConnected: (connected: boolean) => void;
|
||||
|
||||
// API Keys actions
|
||||
setApiKeys: (keys: Partial<ApiKeys>) => void;
|
||||
|
||||
// Chat Session actions
|
||||
createChatSession: (title?: string) => ChatSession;
|
||||
updateChatSession: (sessionId: string, updates: Partial<ChatSession>) => void;
|
||||
addMessageToSession: (sessionId: string, message: ChatMessage) => void;
|
||||
setCurrentChatSession: (session: ChatSession | null) => void;
|
||||
archiveChatSession: (sessionId: string) => void;
|
||||
unarchiveChatSession: (sessionId: string) => void;
|
||||
deleteChatSession: (sessionId: string) => void;
|
||||
setChatHistoryOpen: (open: boolean) => void;
|
||||
toggleChatHistory: () => void;
|
||||
|
||||
// Auto Mode actions (per-worktree)
|
||||
setAutoModeRunning: (
|
||||
projectId: string,
|
||||
branchName: string | null,
|
||||
running: boolean,
|
||||
maxConcurrency?: number,
|
||||
runningTasks?: string[]
|
||||
) => void;
|
||||
addRunningTask: (projectId: string, branchName: string | null, taskId: string) => void;
|
||||
removeRunningTask: (projectId: string, branchName: string | null, taskId: string) => void;
|
||||
clearRunningTasks: (projectId: string, branchName: string | null) => void;
|
||||
getAutoModeState: (
|
||||
projectId: string,
|
||||
branchName: string | null
|
||||
) => {
|
||||
isRunning: boolean;
|
||||
runningTasks: string[];
|
||||
branchName: string | null;
|
||||
maxConcurrency?: number;
|
||||
};
|
||||
/** Helper to generate worktree key from projectId and branchName */
|
||||
getWorktreeKey: (projectId: string, branchName: string | null) => string;
|
||||
addAutoModeActivity: (activity: Omit<AutoModeActivity, 'id' | 'timestamp'>) => void;
|
||||
clearAutoModeActivity: () => void;
|
||||
setMaxConcurrency: (max: number) => void; // Legacy: kept for backward compatibility
|
||||
getMaxConcurrencyForWorktree: (projectId: string, branchName: string | null) => number;
|
||||
setMaxConcurrencyForWorktree: (
|
||||
projectId: string,
|
||||
branchName: string | null,
|
||||
maxConcurrency: number
|
||||
) => void;
|
||||
|
||||
// Kanban Card Settings actions
|
||||
setBoardViewMode: (mode: BoardViewMode) => void;
|
||||
|
||||
// Feature Default Settings actions
|
||||
setDefaultSkipTests: (skip: boolean) => void;
|
||||
setEnableDependencyBlocking: (enabled: boolean) => void;
|
||||
setSkipVerificationInAutoMode: (enabled: boolean) => Promise<void>;
|
||||
setEnableAiCommitMessages: (enabled: boolean) => Promise<void>;
|
||||
setPlanUseSelectedWorktreeBranch: (enabled: boolean) => Promise<void>;
|
||||
setAddFeatureUseSelectedWorktreeBranch: (enabled: boolean) => Promise<void>;
|
||||
|
||||
// Worktree Settings actions
|
||||
setUseWorktrees: (enabled: boolean) => void;
|
||||
setCurrentWorktree: (projectPath: string, worktreePath: string | null, branch: string) => void;
|
||||
setWorktrees: (
|
||||
projectPath: string,
|
||||
worktrees: Array<{
|
||||
path: string;
|
||||
branch: string;
|
||||
isMain: boolean;
|
||||
isCurrent: boolean;
|
||||
hasWorktree: boolean;
|
||||
hasChanges?: boolean;
|
||||
changedFilesCount?: number;
|
||||
}>
|
||||
) => void;
|
||||
getCurrentWorktree: (projectPath: string) => { path: string | null; branch: string } | null;
|
||||
getWorktrees: (projectPath: string) => Array<{
|
||||
path: string;
|
||||
branch: string;
|
||||
isMain: boolean;
|
||||
isCurrent: boolean;
|
||||
hasWorktree: boolean;
|
||||
hasChanges?: boolean;
|
||||
changedFilesCount?: number;
|
||||
}>;
|
||||
isPrimaryWorktreeBranch: (projectPath: string, branchName: string) => boolean;
|
||||
getPrimaryWorktreeBranch: (projectPath: string) => string | null;
|
||||
|
||||
// Keyboard Shortcuts actions
|
||||
setKeyboardShortcut: (key: keyof KeyboardShortcuts, value: string) => void;
|
||||
setKeyboardShortcuts: (shortcuts: Partial<KeyboardShortcuts>) => void;
|
||||
resetKeyboardShortcuts: () => void;
|
||||
|
||||
// Audio Settings actions
|
||||
setMuteDoneSound: (muted: boolean) => void;
|
||||
|
||||
// Splash Screen actions
|
||||
setDisableSplashScreen: (disabled: boolean) => void;
|
||||
|
||||
// Server Log Level actions
|
||||
setServerLogLevel: (level: ServerLogLevel) => void;
|
||||
setEnableRequestLogging: (enabled: boolean) => void;
|
||||
|
||||
// Developer Tools actions
|
||||
setShowQueryDevtools: (show: boolean) => void;
|
||||
|
||||
// Enhancement Model actions
|
||||
setEnhancementModel: (model: ModelAlias) => void;
|
||||
|
||||
// Validation Model actions
|
||||
setValidationModel: (model: ModelAlias) => void;
|
||||
|
||||
// Phase Model actions
|
||||
setPhaseModel: (phase: PhaseModelKey, entry: PhaseModelEntry) => Promise<void>;
|
||||
setPhaseModels: (models: Partial<PhaseModelConfig>) => Promise<void>;
|
||||
resetPhaseModels: () => Promise<void>;
|
||||
toggleFavoriteModel: (modelId: string) => void;
|
||||
|
||||
// Cursor CLI Settings actions
|
||||
setEnabledCursorModels: (models: CursorModelId[]) => void;
|
||||
setCursorDefaultModel: (model: CursorModelId) => void;
|
||||
toggleCursorModel: (model: CursorModelId, enabled: boolean) => void;
|
||||
|
||||
// Codex CLI Settings actions
|
||||
setEnabledCodexModels: (models: CodexModelId[]) => void;
|
||||
setCodexDefaultModel: (model: CodexModelId) => void;
|
||||
toggleCodexModel: (model: CodexModelId, enabled: boolean) => void;
|
||||
setCodexAutoLoadAgents: (enabled: boolean) => Promise<void>;
|
||||
setCodexSandboxMode: (
|
||||
mode: 'read-only' | 'workspace-write' | 'danger-full-access'
|
||||
) => Promise<void>;
|
||||
setCodexApprovalPolicy: (
|
||||
policy: 'untrusted' | 'on-failure' | 'on-request' | 'never'
|
||||
) => Promise<void>;
|
||||
setCodexEnableWebSearch: (enabled: boolean) => Promise<void>;
|
||||
setCodexEnableImages: (enabled: boolean) => Promise<void>;
|
||||
|
||||
// OpenCode CLI Settings actions
|
||||
setEnabledOpencodeModels: (models: OpencodeModelId[]) => void;
|
||||
setOpencodeDefaultModel: (model: OpencodeModelId) => void;
|
||||
toggleOpencodeModel: (model: OpencodeModelId, enabled: boolean) => void;
|
||||
setDynamicOpencodeModels: (models: ModelDefinition[]) => void;
|
||||
setEnabledDynamicModelIds: (ids: string[]) => void;
|
||||
toggleDynamicModel: (modelId: string, enabled: boolean) => void;
|
||||
setCachedOpencodeProviders: (
|
||||
providers: Array<{ id: string; name: string; authenticated: boolean; authMethod?: string }>
|
||||
) => void;
|
||||
|
||||
// Gemini CLI Settings actions
|
||||
setEnabledGeminiModels: (models: GeminiModelId[]) => void;
|
||||
setGeminiDefaultModel: (model: GeminiModelId) => void;
|
||||
toggleGeminiModel: (model: GeminiModelId, enabled: boolean) => void;
|
||||
|
||||
// Copilot SDK Settings actions
|
||||
setEnabledCopilotModels: (models: CopilotModelId[]) => void;
|
||||
setCopilotDefaultModel: (model: CopilotModelId) => void;
|
||||
toggleCopilotModel: (model: CopilotModelId, enabled: boolean) => void;
|
||||
|
||||
// Provider Visibility Settings actions
|
||||
setDisabledProviders: (providers: ModelProvider[]) => void;
|
||||
toggleProviderDisabled: (provider: ModelProvider, disabled: boolean) => void;
|
||||
isProviderDisabled: (provider: ModelProvider) => boolean;
|
||||
|
||||
// Claude Agent SDK Settings actions
|
||||
setAutoLoadClaudeMd: (enabled: boolean) => Promise<void>;
|
||||
setSkipSandboxWarning: (skip: boolean) => Promise<void>;
|
||||
|
||||
// Editor Configuration actions
|
||||
setDefaultEditorCommand: (command: string | null) => void;
|
||||
|
||||
// Terminal Configuration actions
|
||||
setDefaultTerminalId: (terminalId: string | null) => void;
|
||||
|
||||
// Prompt Customization actions
|
||||
setPromptCustomization: (customization: PromptCustomization) => Promise<void>;
|
||||
|
||||
// Event Hook actions
|
||||
setEventHooks: (hooks: EventHook[]) => void;
|
||||
|
||||
// Claude-Compatible Provider actions (new system)
|
||||
addClaudeCompatibleProvider: (provider: ClaudeCompatibleProvider) => Promise<void>;
|
||||
updateClaudeCompatibleProvider: (
|
||||
id: string,
|
||||
updates: Partial<ClaudeCompatibleProvider>
|
||||
) => Promise<void>;
|
||||
deleteClaudeCompatibleProvider: (id: string) => Promise<void>;
|
||||
setClaudeCompatibleProviders: (providers: ClaudeCompatibleProvider[]) => Promise<void>;
|
||||
toggleClaudeCompatibleProviderEnabled: (id: string) => Promise<void>;
|
||||
|
||||
// Claude API Profile actions (deprecated - kept for backward compatibility)
|
||||
addClaudeApiProfile: (profile: ClaudeApiProfile) => Promise<void>;
|
||||
updateClaudeApiProfile: (id: string, updates: Partial<ClaudeApiProfile>) => Promise<void>;
|
||||
deleteClaudeApiProfile: (id: string) => Promise<void>;
|
||||
setActiveClaudeApiProfile: (id: string | null) => Promise<void>;
|
||||
setClaudeApiProfiles: (profiles: ClaudeApiProfile[]) => Promise<void>;
|
||||
|
||||
// MCP Server actions
|
||||
addMCPServer: (server: Omit<MCPServerConfig, 'id'>) => void;
|
||||
updateMCPServer: (id: string, updates: Partial<MCPServerConfig>) => void;
|
||||
removeMCPServer: (id: string) => void;
|
||||
reorderMCPServers: (oldIndex: number, newIndex: number) => void;
|
||||
|
||||
// Project Analysis actions
|
||||
setProjectAnalysis: (analysis: ProjectAnalysis | null) => void;
|
||||
setIsAnalyzing: (analyzing: boolean) => void;
|
||||
clearAnalysis: () => void;
|
||||
|
||||
// Agent Session actions
|
||||
setLastSelectedSession: (projectPath: string, sessionId: string | null) => void;
|
||||
getLastSelectedSession: (projectPath: string) => string | null;
|
||||
|
||||
// Board Background actions
|
||||
setBoardBackground: (projectPath: string, imagePath: string | null) => void;
|
||||
setCardOpacity: (projectPath: string, opacity: number) => void;
|
||||
setColumnOpacity: (projectPath: string, opacity: number) => void;
|
||||
setColumnBorderEnabled: (projectPath: string, enabled: boolean) => void;
|
||||
getBoardBackground: (projectPath: string) => {
|
||||
imagePath: string | null;
|
||||
cardOpacity: number;
|
||||
columnOpacity: number;
|
||||
columnBorderEnabled: boolean;
|
||||
cardGlassmorphism: boolean;
|
||||
cardBorderEnabled: boolean;
|
||||
cardBorderOpacity: number;
|
||||
hideScrollbar: boolean;
|
||||
};
|
||||
setCardGlassmorphism: (projectPath: string, enabled: boolean) => void;
|
||||
setCardBorderEnabled: (projectPath: string, enabled: boolean) => void;
|
||||
setCardBorderOpacity: (projectPath: string, opacity: number) => void;
|
||||
setHideScrollbar: (projectPath: string, hide: boolean) => void;
|
||||
clearBoardBackground: (projectPath: string) => void;
|
||||
|
||||
// Terminal actions
|
||||
setTerminalUnlocked: (unlocked: boolean, token?: string) => void;
|
||||
setActiveTerminalSession: (sessionId: string | null) => void;
|
||||
toggleTerminalMaximized: (sessionId: string) => void;
|
||||
addTerminalToLayout: (
|
||||
sessionId: string,
|
||||
direction?: 'horizontal' | 'vertical',
|
||||
targetSessionId?: string,
|
||||
branchName?: string
|
||||
) => void;
|
||||
removeTerminalFromLayout: (sessionId: string) => void;
|
||||
swapTerminals: (sessionId1: string, sessionId2: string) => void;
|
||||
clearTerminalState: () => void;
|
||||
setTerminalPanelFontSize: (sessionId: string, fontSize: number) => void;
|
||||
setTerminalDefaultFontSize: (fontSize: number) => void;
|
||||
setTerminalDefaultRunScript: (script: string) => void;
|
||||
setTerminalScreenReaderMode: (enabled: boolean) => void;
|
||||
setTerminalFontFamily: (fontFamily: string) => void;
|
||||
setTerminalScrollbackLines: (lines: number) => void;
|
||||
setTerminalLineHeight: (lineHeight: number) => void;
|
||||
setTerminalMaxSessions: (maxSessions: number) => void;
|
||||
setTerminalLastActiveProjectPath: (projectPath: string | null) => void;
|
||||
setOpenTerminalMode: (mode: 'newTab' | 'split') => void;
|
||||
addTerminalTab: (name?: string) => string;
|
||||
removeTerminalTab: (tabId: string) => void;
|
||||
setActiveTerminalTab: (tabId: string) => void;
|
||||
renameTerminalTab: (tabId: string, name: string) => void;
|
||||
reorderTerminalTabs: (fromTabId: string, toTabId: string) => void;
|
||||
moveTerminalToTab: (sessionId: string, targetTabId: string | 'new') => void;
|
||||
addTerminalToTab: (
|
||||
sessionId: string,
|
||||
tabId: string,
|
||||
direction?: 'horizontal' | 'vertical',
|
||||
branchName?: string
|
||||
) => void;
|
||||
setTerminalTabLayout: (
|
||||
tabId: string,
|
||||
layout: TerminalPanelContent,
|
||||
activeSessionId?: string
|
||||
) => void;
|
||||
updateTerminalPanelSizes: (tabId: string, panelKeys: string[], sizes: number[]) => void;
|
||||
saveTerminalLayout: (projectPath: string) => void;
|
||||
getPersistedTerminalLayout: (projectPath: string) => PersistedTerminalState | null;
|
||||
clearPersistedTerminalLayout: (projectPath: string) => void;
|
||||
|
||||
// Spec Creation actions
|
||||
setSpecCreatingForProject: (projectPath: string | null) => void;
|
||||
isSpecCreatingForProject: (projectPath: string) => boolean;
|
||||
|
||||
setDefaultPlanningMode: (mode: PlanningMode) => void;
|
||||
setDefaultRequirePlanApproval: (require: boolean) => void;
|
||||
setDefaultFeatureModel: (entry: PhaseModelEntry) => void;
|
||||
|
||||
// Plan Approval actions
|
||||
setPendingPlanApproval: (
|
||||
approval: {
|
||||
featureId: string;
|
||||
projectPath: string;
|
||||
planContent: string;
|
||||
planningMode: 'lite' | 'spec' | 'full';
|
||||
} | null
|
||||
) => void;
|
||||
|
||||
// Pipeline actions
|
||||
setPipelineConfig: (projectPath: string, config: PipelineConfig) => void;
|
||||
getPipelineConfig: (projectPath: string) => PipelineConfig | null;
|
||||
addPipelineStep: (
|
||||
projectPath: string,
|
||||
step: Omit<PipelineStep, 'id' | 'createdAt' | 'updatedAt'>
|
||||
) => PipelineStep;
|
||||
updatePipelineStep: (
|
||||
projectPath: string,
|
||||
stepId: string,
|
||||
updates: Partial<Omit<PipelineStep, 'id' | 'createdAt'>>
|
||||
) => void;
|
||||
deletePipelineStep: (projectPath: string, stepId: string) => void;
|
||||
reorderPipelineSteps: (projectPath: string, stepIds: string[]) => void;
|
||||
|
||||
// Worktree Panel Visibility actions (per-project)
|
||||
setWorktreePanelVisible: (projectPath: string, visible: boolean) => void;
|
||||
getWorktreePanelVisible: (projectPath: string) => boolean;
|
||||
|
||||
// Init Script Indicator Visibility actions (per-project)
|
||||
setShowInitScriptIndicator: (projectPath: string, visible: boolean) => void;
|
||||
getShowInitScriptIndicator: (projectPath: string) => boolean;
|
||||
|
||||
// Default Delete Branch actions (per-project)
|
||||
setDefaultDeleteBranch: (projectPath: string, deleteBranch: boolean) => void;
|
||||
getDefaultDeleteBranch: (projectPath: string) => boolean;
|
||||
|
||||
// Auto-dismiss Init Script Indicator actions (per-project)
|
||||
setAutoDismissInitScriptIndicator: (projectPath: string, autoDismiss: boolean) => void;
|
||||
getAutoDismissInitScriptIndicator: (projectPath: string) => boolean;
|
||||
|
||||
// Use Worktrees Override actions (per-project)
|
||||
setProjectUseWorktrees: (projectPath: string, useWorktrees: boolean | null) => void; // null = use global
|
||||
getProjectUseWorktrees: (projectPath: string) => boolean | undefined; // undefined = using global
|
||||
getEffectiveUseWorktrees: (projectPath: string) => boolean; // Returns actual value (project or global fallback)
|
||||
|
||||
// UI State actions (previously in localStorage, now synced via API)
|
||||
setWorktreePanelCollapsed: (collapsed: boolean) => void;
|
||||
setLastProjectDir: (dir: string) => void;
|
||||
setRecentFolders: (folders: string[]) => void;
|
||||
addRecentFolder: (folder: string) => void;
|
||||
|
||||
// Claude Usage Tracking actions
|
||||
setClaudeRefreshInterval: (interval: number) => void;
|
||||
setClaudeUsageLastUpdated: (timestamp: number) => void;
|
||||
setClaudeUsage: (usage: ClaudeUsage | null) => void;
|
||||
|
||||
// Codex Usage Tracking actions
|
||||
setCodexUsage: (usage: CodexUsage | null) => void;
|
||||
|
||||
// Codex Models actions
|
||||
fetchCodexModels: (forceRefresh?: boolean) => Promise<void>;
|
||||
setCodexModels: (
|
||||
models: Array<{
|
||||
id: string;
|
||||
label: string;
|
||||
description: string;
|
||||
hasThinking: boolean;
|
||||
supportsVision: boolean;
|
||||
tier: 'premium' | 'standard' | 'basic';
|
||||
isDefault: boolean;
|
||||
}>
|
||||
) => void;
|
||||
|
||||
// OpenCode Models actions
|
||||
fetchOpencodeModels: (forceRefresh?: boolean) => Promise<void>;
|
||||
|
||||
// Init Script State actions (keyed by projectPath::branch to support concurrent scripts)
|
||||
setInitScriptState: (
|
||||
projectPath: string,
|
||||
branch: string,
|
||||
state: Partial<InitScriptState>
|
||||
) => void;
|
||||
appendInitScriptOutput: (projectPath: string, branch: string, content: string) => void;
|
||||
clearInitScriptState: (projectPath: string, branch: string) => void;
|
||||
getInitScriptState: (projectPath: string, branch: string) => InitScriptState | null;
|
||||
getInitScriptStatesForProject: (
|
||||
projectPath: string
|
||||
) => Array<{ key: string; state: InitScriptState }>;
|
||||
|
||||
// Reset
|
||||
reset: () => void;
|
||||
}
|
||||
82
apps/ui/src/store/types/terminal-types.ts
Normal file
82
apps/ui/src/store/types/terminal-types.ts
Normal file
@@ -0,0 +1,82 @@
|
||||
// Terminal panel layout types (recursive for splits)
|
||||
export type TerminalPanelContent =
|
||||
| { type: 'terminal'; sessionId: string; size?: number; fontSize?: number; branchName?: string }
|
||||
| { type: 'testRunner'; sessionId: string; size?: number; worktreePath: string }
|
||||
| {
|
||||
type: 'split';
|
||||
id: string; // Stable ID for React key stability
|
||||
direction: 'horizontal' | 'vertical';
|
||||
panels: TerminalPanelContent[];
|
||||
size?: number;
|
||||
};
|
||||
|
||||
// Terminal tab - each tab has its own layout
|
||||
export interface TerminalTab {
|
||||
id: string;
|
||||
name: string;
|
||||
layout: TerminalPanelContent | null;
|
||||
}
|
||||
|
||||
export interface TerminalState {
|
||||
isUnlocked: boolean;
|
||||
authToken: string | null;
|
||||
tabs: TerminalTab[];
|
||||
activeTabId: string | null;
|
||||
activeSessionId: string | null;
|
||||
maximizedSessionId: string | null; // Session ID of the maximized terminal pane (null if none)
|
||||
defaultFontSize: number; // Default font size for new terminals
|
||||
defaultRunScript: string; // Script to run when a new terminal is created (e.g., "claude" to start Claude Code)
|
||||
screenReaderMode: boolean; // Enable screen reader accessibility mode
|
||||
fontFamily: string; // Font family for terminal text
|
||||
scrollbackLines: number; // Number of lines to keep in scrollback buffer
|
||||
lineHeight: number; // Line height multiplier for terminal text
|
||||
maxSessions: number; // Maximum concurrent terminal sessions (server setting)
|
||||
lastActiveProjectPath: string | null; // Last project path to detect route changes vs project switches
|
||||
openTerminalMode: 'newTab' | 'split'; // How to open terminals from "Open in Terminal" action
|
||||
}
|
||||
|
||||
// Persisted terminal layout - now includes sessionIds for reconnection
|
||||
// Used to restore terminal layout structure when switching projects
|
||||
export type PersistedTerminalPanel =
|
||||
| { type: 'terminal'; size?: number; fontSize?: number; sessionId?: string; branchName?: string }
|
||||
| { type: 'testRunner'; size?: number; sessionId?: string; worktreePath?: string }
|
||||
| {
|
||||
type: 'split';
|
||||
id?: string; // Optional for backwards compatibility with older persisted layouts
|
||||
direction: 'horizontal' | 'vertical';
|
||||
panels: PersistedTerminalPanel[];
|
||||
size?: number;
|
||||
};
|
||||
|
||||
// Helper to generate unique split IDs
|
||||
export const generateSplitId = () =>
|
||||
`split-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
||||
|
||||
export interface PersistedTerminalTab {
|
||||
id: string;
|
||||
name: string;
|
||||
layout: PersistedTerminalPanel | null;
|
||||
}
|
||||
|
||||
export interface PersistedTerminalState {
|
||||
tabs: PersistedTerminalTab[];
|
||||
activeTabIndex: number; // Use index instead of ID since IDs are regenerated
|
||||
defaultFontSize: number;
|
||||
defaultRunScript?: string; // Optional to support existing persisted data
|
||||
screenReaderMode?: boolean; // Optional to support existing persisted data
|
||||
fontFamily?: string; // Optional to support existing persisted data
|
||||
scrollbackLines?: number; // Optional to support existing persisted data
|
||||
lineHeight?: number; // Optional to support existing persisted data
|
||||
}
|
||||
|
||||
// Persisted terminal settings - stored globally (not per-project)
|
||||
export interface PersistedTerminalSettings {
|
||||
defaultFontSize: number;
|
||||
defaultRunScript: string;
|
||||
screenReaderMode: boolean;
|
||||
fontFamily: string;
|
||||
scrollbackLines: number;
|
||||
lineHeight: number;
|
||||
maxSessions: number;
|
||||
openTerminalMode: 'newTab' | 'split';
|
||||
}
|
||||
222
apps/ui/src/store/types/ui-types.ts
Normal file
222
apps/ui/src/store/types/ui-types.ts
Normal file
@@ -0,0 +1,222 @@
|
||||
export type ViewMode =
|
||||
| 'welcome'
|
||||
| 'setup'
|
||||
| 'spec'
|
||||
| 'board'
|
||||
| 'agent'
|
||||
| 'settings'
|
||||
| 'interview'
|
||||
| 'context'
|
||||
| 'running-agents'
|
||||
| 'terminal'
|
||||
| 'wiki'
|
||||
| 'ideation';
|
||||
|
||||
export type ThemeMode =
|
||||
// Special modes
|
||||
| 'system'
|
||||
// Dark themes
|
||||
| 'dark'
|
||||
| 'retro'
|
||||
| 'dracula'
|
||||
| 'nord'
|
||||
| 'monokai'
|
||||
| 'tokyonight'
|
||||
| 'solarized'
|
||||
| 'gruvbox'
|
||||
| 'catppuccin'
|
||||
| 'onedark'
|
||||
| 'synthwave'
|
||||
| 'red'
|
||||
| 'sunset'
|
||||
| 'gray'
|
||||
| 'forest'
|
||||
| 'ocean'
|
||||
| 'ember'
|
||||
| 'ayu-dark'
|
||||
| 'ayu-mirage'
|
||||
| 'matcha'
|
||||
// Light themes
|
||||
| 'light'
|
||||
| 'cream'
|
||||
| 'solarizedlight'
|
||||
| 'github'
|
||||
| 'paper'
|
||||
| 'rose'
|
||||
| 'mint'
|
||||
| 'lavender'
|
||||
| 'sand'
|
||||
| 'sky'
|
||||
| 'peach'
|
||||
| 'snow'
|
||||
| 'sepia'
|
||||
| 'gruvboxlight'
|
||||
| 'nordlight'
|
||||
| 'blossom'
|
||||
| 'ayu-light'
|
||||
| 'onelight'
|
||||
| 'bluloco'
|
||||
| 'feather';
|
||||
|
||||
export type BoardViewMode = 'kanban' | 'graph';
|
||||
|
||||
// Keyboard Shortcut with optional modifiers
|
||||
export interface ShortcutKey {
|
||||
key: string; // The main key (e.g., "K", "N", "1")
|
||||
shift?: boolean; // Shift key modifier
|
||||
cmdCtrl?: boolean; // Cmd on Mac, Ctrl on Windows/Linux
|
||||
alt?: boolean; // Alt/Option key modifier
|
||||
}
|
||||
|
||||
// Helper to parse shortcut string to ShortcutKey object
|
||||
export function parseShortcut(shortcut: string | undefined | null): ShortcutKey {
|
||||
if (!shortcut) return { key: '' };
|
||||
const parts = shortcut.split('+').map((p) => p.trim());
|
||||
const result: ShortcutKey = { key: parts[parts.length - 1] };
|
||||
|
||||
// Normalize common OS-specific modifiers (Cmd/Ctrl/Win/Super symbols) into cmdCtrl
|
||||
for (let i = 0; i < parts.length - 1; i++) {
|
||||
const modifier = parts[i].toLowerCase();
|
||||
if (modifier === 'shift') result.shift = true;
|
||||
else if (
|
||||
modifier === 'cmd' ||
|
||||
modifier === 'ctrl' ||
|
||||
modifier === 'win' ||
|
||||
modifier === 'super' ||
|
||||
modifier === '⌘' ||
|
||||
modifier === '^' ||
|
||||
modifier === '⊞' ||
|
||||
modifier === '◆'
|
||||
)
|
||||
result.cmdCtrl = true;
|
||||
else if (modifier === 'alt' || modifier === 'opt' || modifier === 'option' || modifier === '⌥')
|
||||
result.alt = true;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Helper to format ShortcutKey to display string
|
||||
export function formatShortcut(shortcut: string | undefined | null, forDisplay = false): string {
|
||||
if (!shortcut) return '';
|
||||
const parsed = parseShortcut(shortcut);
|
||||
const parts: string[] = [];
|
||||
|
||||
// Prefer User-Agent Client Hints when available; fall back to legacy
|
||||
const platform: 'darwin' | 'win32' | 'linux' = (() => {
|
||||
if (typeof navigator === 'undefined') return 'linux';
|
||||
|
||||
const uaPlatform = (
|
||||
navigator as Navigator & { userAgentData?: { platform?: string } }
|
||||
).userAgentData?.platform?.toLowerCase?.();
|
||||
const legacyPlatform = navigator.platform?.toLowerCase?.();
|
||||
const platformString = uaPlatform || legacyPlatform || '';
|
||||
|
||||
if (platformString.includes('mac')) return 'darwin';
|
||||
if (platformString.includes('win')) return 'win32';
|
||||
return 'linux';
|
||||
})();
|
||||
|
||||
// Primary modifier - OS-specific
|
||||
if (parsed.cmdCtrl) {
|
||||
if (forDisplay) {
|
||||
parts.push(platform === 'darwin' ? '⌘' : platform === 'win32' ? '⊞' : '◆');
|
||||
} else {
|
||||
parts.push(platform === 'darwin' ? 'Cmd' : platform === 'win32' ? 'Win' : 'Super');
|
||||
}
|
||||
}
|
||||
|
||||
// Alt/Option
|
||||
if (parsed.alt) {
|
||||
parts.push(
|
||||
forDisplay ? (platform === 'darwin' ? '⌥' : 'Alt') : platform === 'darwin' ? 'Opt' : 'Alt'
|
||||
);
|
||||
}
|
||||
|
||||
// Shift
|
||||
if (parsed.shift) {
|
||||
parts.push(forDisplay ? '⇧' : 'Shift');
|
||||
}
|
||||
|
||||
parts.push(parsed.key.toUpperCase());
|
||||
|
||||
// Add spacing when displaying symbols
|
||||
return parts.join(forDisplay ? ' ' : '+');
|
||||
}
|
||||
|
||||
// Keyboard Shortcuts - stored as strings like "K", "Shift+N", "Cmd+K"
|
||||
export interface KeyboardShortcuts {
|
||||
// Navigation shortcuts
|
||||
board: string;
|
||||
graph: string;
|
||||
agent: string;
|
||||
spec: string;
|
||||
context: string;
|
||||
memory: string;
|
||||
settings: string;
|
||||
projectSettings: string;
|
||||
terminal: string;
|
||||
ideation: string;
|
||||
notifications: string;
|
||||
githubIssues: string;
|
||||
githubPrs: string;
|
||||
|
||||
// UI shortcuts
|
||||
toggleSidebar: string;
|
||||
|
||||
// Action shortcuts
|
||||
addFeature: string;
|
||||
addContextFile: string;
|
||||
startNext: string;
|
||||
newSession: string;
|
||||
openProject: string;
|
||||
projectPicker: string;
|
||||
cyclePrevProject: string;
|
||||
cycleNextProject: string;
|
||||
|
||||
// Terminal shortcuts
|
||||
splitTerminalRight: string;
|
||||
splitTerminalDown: string;
|
||||
closeTerminal: string;
|
||||
newTerminalTab: string;
|
||||
}
|
||||
|
||||
// Default keyboard shortcuts
|
||||
export const DEFAULT_KEYBOARD_SHORTCUTS: KeyboardShortcuts = {
|
||||
// Navigation
|
||||
board: 'K',
|
||||
graph: 'H',
|
||||
agent: 'A',
|
||||
spec: 'D',
|
||||
context: 'C',
|
||||
memory: 'Y',
|
||||
settings: 'S',
|
||||
projectSettings: 'Shift+S',
|
||||
terminal: 'T',
|
||||
ideation: 'I',
|
||||
notifications: 'X',
|
||||
githubIssues: 'G',
|
||||
githubPrs: 'R',
|
||||
|
||||
// UI
|
||||
toggleSidebar: '`',
|
||||
|
||||
// Actions
|
||||
// Note: Some shortcuts share the same key (e.g., "N" for addFeature, newSession)
|
||||
// This is intentional as they are context-specific and only active in their respective views
|
||||
addFeature: 'N', // Only active in board view
|
||||
addContextFile: 'N', // Only active in context view
|
||||
startNext: 'G', // Only active in board view
|
||||
newSession: 'N', // Only active in agent view
|
||||
openProject: 'O', // Global shortcut
|
||||
projectPicker: 'P', // Global shortcut
|
||||
cyclePrevProject: 'Q', // Global shortcut
|
||||
cycleNextProject: 'E', // Global shortcut
|
||||
|
||||
// Terminal shortcuts (only active in terminal view)
|
||||
// Using Alt modifier to avoid conflicts with both terminal signals AND browser shortcuts
|
||||
splitTerminalRight: 'Alt+D',
|
||||
splitTerminalDown: 'Alt+S',
|
||||
closeTerminal: 'Alt+W',
|
||||
newTerminalTab: 'Alt+T',
|
||||
};
|
||||
93
apps/ui/src/store/types/usage-types.ts
Normal file
93
apps/ui/src/store/types/usage-types.ts
Normal file
@@ -0,0 +1,93 @@
|
||||
// Claude Usage interface matching the server response
|
||||
export type ClaudeUsage = {
|
||||
sessionTokensUsed: number;
|
||||
sessionLimit: number;
|
||||
sessionPercentage: number;
|
||||
sessionResetTime: string;
|
||||
sessionResetText: string;
|
||||
|
||||
weeklyTokensUsed: number;
|
||||
weeklyLimit: number;
|
||||
weeklyPercentage: number;
|
||||
weeklyResetTime: string;
|
||||
weeklyResetText: string;
|
||||
|
||||
sonnetWeeklyTokensUsed: number;
|
||||
sonnetWeeklyPercentage: number;
|
||||
sonnetResetText: string;
|
||||
|
||||
costUsed: number | null;
|
||||
costLimit: number | null;
|
||||
costCurrency: string | null;
|
||||
|
||||
lastUpdated: string;
|
||||
userTimezone: string;
|
||||
};
|
||||
|
||||
// Response type for Claude usage API (can be success or error)
|
||||
export type ClaudeUsageResponse = ClaudeUsage | { error: string; message?: string };
|
||||
|
||||
// Codex Usage types
|
||||
export type CodexPlanType =
|
||||
| 'free'
|
||||
| 'plus'
|
||||
| 'pro'
|
||||
| 'team'
|
||||
| 'business'
|
||||
| 'enterprise'
|
||||
| 'edu'
|
||||
| 'unknown';
|
||||
|
||||
export interface CodexRateLimitWindow {
|
||||
limit: number;
|
||||
used: number;
|
||||
remaining: number;
|
||||
usedPercent: number; // Percentage used (0-100)
|
||||
windowDurationMins: number; // Duration in minutes
|
||||
resetsAt: number; // Unix timestamp in seconds
|
||||
}
|
||||
|
||||
export interface CodexUsage {
|
||||
rateLimits: {
|
||||
primary?: CodexRateLimitWindow;
|
||||
secondary?: CodexRateLimitWindow;
|
||||
planType?: CodexPlanType;
|
||||
} | null;
|
||||
lastUpdated: string;
|
||||
}
|
||||
|
||||
// Response type for Codex usage API (can be success or error)
|
||||
export type CodexUsageResponse = CodexUsage | { error: string; message?: string };
|
||||
|
||||
/**
|
||||
* Check if Claude usage is at its limit (any of: session >= 100%, weekly >= 100%, OR cost >= limit)
|
||||
* Returns true if any limit is reached, meaning auto mode should pause feature pickup.
|
||||
*/
|
||||
export function isClaudeUsageAtLimit(claudeUsage: ClaudeUsage | null): boolean {
|
||||
if (!claudeUsage) {
|
||||
// No usage data available - don't block
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check session limit (5-hour window)
|
||||
if (claudeUsage.sessionPercentage >= 100) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check weekly limit
|
||||
if (claudeUsage.weeklyPercentage >= 100) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check cost limit (if configured)
|
||||
if (
|
||||
claudeUsage.costLimit !== null &&
|
||||
claudeUsage.costLimit > 0 &&
|
||||
claudeUsage.costUsed !== null &&
|
||||
claudeUsage.costUsed >= claudeUsage.costLimit
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
Reference in New Issue
Block a user