fixing imports

This commit is contained in:
2026-02-04 21:32:30 -05:00
parent 74b0a0dbab
commit db79856b08
166 changed files with 1986 additions and 982 deletions

View File

@@ -11,23 +11,20 @@ import { batch } from "solid-js";
import { getFiles } from "@services/file-picker/files";
import { abortCurrentOperation } from "@services/chat-tui-service";
import versionData from "@/version.json";
import { ExitProvider, useExit } from "@tui-solid/context/exit";
import { RouteProvider, useRoute } from "@tui-solid/context/route";
import {
ExitProvider,
useExit,
RouteProvider,
useRoute,
AppStoreProvider,
useAppStore,
setAppStoreRef,
ThemeProvider,
useTheme,
KeybindProvider,
DialogProvider,
} from "@tui-solid/context";
} from "@tui-solid/context/app";
import { ThemeProvider, useTheme } from "@tui-solid/context/theme";
import { KeybindProvider } from "@tui-solid/context/keybind";
import { DialogProvider } from "@tui-solid/context/dialog";
import { ToastProvider, Toast, useToast } from "@tui-solid/ui/toast";
import { Home } from "@tui-solid/routes/home";
import { Session } from "@tui-solid/routes/session";
import type { TuiInput, TuiOutput } from "@tui-solid/types";
import type { TuiInput, TuiOutput } from "@interfaces/index";
import type { MCPServerDisplay } from "@/types/tui";
import type { PermissionScope, LearningScope } from "@/types/tui";
import type { MCPAddFormData } from "@/types/mcp";

View File

@@ -170,14 +170,19 @@ export function InputArea(props: InputAreaProps) {
}
// Normalize line endings (Windows ConPTY sends CR-only newlines)
const normalizedText = event.text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
const normalizedText = event.text
.replace(/\r\n/g, "\n")
.replace(/\r/g, "\n");
const pastedContent = normalizedText.trim();
if (!pastedContent) return;
// Check if paste should be summarized
const lineCount = (pastedContent.match(/\n/g)?.length ?? 0) + 1;
if (lineCount >= MIN_PASTE_LINES || pastedContent.length > MIN_PASTE_CHARS) {
if (
lineCount >= MIN_PASTE_LINES ||
pastedContent.length > MIN_PASTE_CHARS
) {
event.preventDefault();
pasteText(pastedContent, `[Pasted ~${lineCount} lines]`);
}

View File

@@ -24,7 +24,7 @@ const FIELD_LABELS: Record<FormField, string> = {
const FIELD_PLACEHOLDERS: Record<FormField, string> = {
name: "e.g., filesystem",
command: "e.g., npx",
args: "e.g., -y @modelcontextprotocol/server-filesystem \"/path/to/dir\"",
args: 'e.g., -y @modelcontextprotocol/server-filesystem "/path/to/dir"',
scope: "",
};
@@ -206,21 +206,31 @@ export function MCPAddForm(props: MCPAddFormProps) {
<box flexDirection="row" marginBottom={1}>
<text
fg={isCurrentField ? theme.colors.primary : theme.colors.text}
attributes={isCurrentField ? TextAttributes.BOLD : TextAttributes.NONE}
attributes={
isCurrentField ? TextAttributes.BOLD : TextAttributes.NONE
}
>
{isCurrentField ? "> " : " "}
{FIELD_LABELS[field]}:{" "}
</text>
<text
fg={!isGlobal() ? theme.colors.success : theme.colors.textDim}
attributes={!isGlobal() && isCurrentField ? TextAttributes.BOLD : TextAttributes.NONE}
attributes={
!isGlobal() && isCurrentField
? TextAttributes.BOLD
: TextAttributes.NONE
}
>
[Local]
</text>
<text fg={theme.colors.textDim}> / </text>
<text
fg={isGlobal() ? theme.colors.warning : theme.colors.textDim}
attributes={isGlobal() && isCurrentField ? TextAttributes.BOLD : TextAttributes.NONE}
attributes={
isGlobal() && isCurrentField
? TextAttributes.BOLD
: TextAttributes.NONE
}
>
[Global]
</text>
@@ -232,7 +242,9 @@ export function MCPAddForm(props: MCPAddFormProps) {
<box flexDirection="row" marginBottom={1}>
<text
fg={isCurrentField ? theme.colors.primary : theme.colors.text}
attributes={isCurrentField ? TextAttributes.BOLD : TextAttributes.NONE}
attributes={
isCurrentField ? TextAttributes.BOLD : TextAttributes.NONE
}
>
{isCurrentField ? "> " : " "}
{FIELD_LABELS[field]}:{" "}
@@ -287,9 +299,7 @@ export function MCPAddForm(props: MCPAddFormProps) {
<text fg={theme.colors.textDim}>
Tab/Enter next | Shift+Tab prev | navigate | Esc cancel
</text>
<text fg={theme.colors.textDim}>
Enter on Scope to submit
</text>
<text fg={theme.colors.textDim}>Enter on Scope to submit</text>
</box>
</box>
);

View File

@@ -155,7 +155,10 @@ export function Header(props: HeaderProps) {
[{MODE_LABELS[app.interactionMode()]}]
</text>
<Show when={app.currentAgent() !== "default"}>
<text fg={theme.colors.secondary} attributes={TextAttributes.BOLD}>
<text
fg={theme.colors.secondary}
attributes={TextAttributes.BOLD}
>
{" "}
@{app.currentAgent()}
</text>

View File

@@ -18,11 +18,14 @@ export function StreamingMessage(props: StreamingMessageProps) {
// This ensures proper reactivity with the store
const [displayContent, setDisplayContent] = createSignal(props.entry.content);
const [isActiveStreaming, setIsActiveStreaming] = createSignal(
props.entry.metadata?.isStreaming ?? false
props.entry.metadata?.isStreaming ?? false,
);
onMount(() => {
addDebugLog("render", `StreamingMessage mounted for entry: ${props.entry.id}`);
addDebugLog(
"render",
`StreamingMessage mounted for entry: ${props.entry.id}`,
);
});
// Effect to sync content from store's streamingLog
@@ -36,7 +39,10 @@ export function StreamingMessage(props: StreamingMessageProps) {
// Check if this entry is the currently streaming log
const isCurrentLog = logId === props.entry.id;
addDebugLog("render", `Effect: logId=${logId}, entryId=${props.entry.id}, isActive=${isActive}, contentLen=${storeContent?.length ?? 0}`);
addDebugLog(
"render",
`Effect: logId=${logId}, entryId=${props.entry.id}, isActive=${isActive}, contentLen=${storeContent?.length ?? 0}`,
);
if (isCurrentLog && isActive) {
setDisplayContent(storeContent);

View File

@@ -146,13 +146,17 @@ export function BrainMenu(props: BrainMenuProps) {
// Main menu navigation
if (view() === "main") {
if (evt.name === "up") {
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : menuItems().length - 1));
setSelectedIndex((prev) =>
prev > 0 ? prev - 1 : menuItems().length - 1,
);
evt.preventDefault();
return;
}
if (evt.name === "down") {
setSelectedIndex((prev) => (prev < menuItems().length - 1 ? prev + 1 : 0));
setSelectedIndex((prev) =>
prev < menuItems().length - 1 ? prev + 1 : 0,
);
evt.preventDefault();
return;
}
@@ -267,7 +271,8 @@ export function BrainMenu(props: BrainMenuProps) {
<text fg={getStatusColor()}>{getStatusText()}</text>
<Show when={isConnected()}>
<text fg={theme.colors.textDim}>
{" "}({app.brain().knowledgeCount}K / {app.brain().memoryCount}M)
{" "}
({app.brain().knowledgeCount}K / {app.brain().memoryCount}M)
</text>
</Show>
</box>
@@ -299,13 +304,17 @@ export function BrainMenu(props: BrainMenuProps) {
<box flexDirection="row">
<text
fg={isSelected() ? theme.colors.accent : undefined}
attributes={isSelected() ? TextAttributes.BOLD : TextAttributes.NONE}
attributes={
isSelected() ? TextAttributes.BOLD : TextAttributes.NONE
}
>
{isSelected() ? "> " : " "}
</text>
<text
fg={isSelected() ? theme.colors.accent : undefined}
attributes={isSelected() ? TextAttributes.BOLD : TextAttributes.NONE}
attributes={
isSelected() ? TextAttributes.BOLD : TextAttributes.NONE
}
>
{item.label}
</text>
@@ -320,7 +329,9 @@ export function BrainMenu(props: BrainMenuProps) {
</box>
<box marginTop={1} flexDirection="column">
<text fg={theme.colors.info}>{BRAIN_BANNER.CTA}: {BRAIN_BANNER.URL}</text>
<text fg={theme.colors.info}>
{BRAIN_BANNER.CTA}: {BRAIN_BANNER.URL}
</text>
<text fg={theme.colors.textDim}>
Arrow keys navigate | Enter select | Esc close
</text>
@@ -339,17 +350,19 @@ export function BrainMenu(props: BrainMenuProps) {
</text>
</box>
<box marginBottom={1}>
<text fg={theme.colors.text}>2. After logging in, copy your JWT token</text>
<text fg={theme.colors.text}>
2. After logging in, copy your JWT token
</text>
</box>
<box marginBottom={1}>
<text fg={theme.colors.text}>3. Press Enter to input your token</text>
<text fg={theme.colors.text}>
3. Press Enter to input your token
</text>
</box>
</box>
<box marginTop={1}>
<text fg={theme.colors.textDim}>
Enter continue | Esc back
</text>
<text fg={theme.colors.textDim}>Enter continue | Esc back</text>
</box>
</Show>

View File

@@ -4,7 +4,14 @@
* UI component for displaying and resolving file conflicts between agents.
*/
import { For, Show, createSignal, createMemo, onMount, onCleanup } from "solid-js";
import {
For,
Show,
createSignal,
createMemo,
onMount,
onCleanup,
} from "solid-js";
import { TextAttributes } from "@opentui/core";
import { useTheme } from "@tui-solid/context/theme";
import { multiAgentStore } from "@stores/core/multi-agent-store";
@@ -46,7 +53,9 @@ export function ConflictResolver(props: ConflictResolverProps) {
onCleanup(unsubscribe);
});
const currentConflict = createMemo(() => conflicts()[selectedConflictIndex()]);
const currentConflict = createMemo(
() => conflicts()[selectedConflictIndex()],
);
const getAgentNames = (agentIds: string[]): string[] => {
const state = multiAgentStore.getState();
@@ -61,7 +70,9 @@ export function ConflictResolver(props: ConflictResolverProps) {
return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
};
const selectedStrategy = createMemo(() => STRATEGY_OPTIONS[selectedStrategyIndex()]);
const selectedStrategy = createMemo(
() => STRATEGY_OPTIONS[selectedStrategyIndex()],
);
return (
<Show when={visible() && conflicts().length > 0}>
@@ -73,7 +84,11 @@ export function ConflictResolver(props: ConflictResolverProps) {
backgroundColor={theme.colors.background}
>
{/* Header */}
<box flexDirection="row" justifyContent="space-between" marginBottom={1}>
<box
flexDirection="row"
justifyContent="space-between"
marginBottom={1}
>
<text fg={theme.colors.warning} attributes={TextAttributes.BOLD}>
File Conflict Detected
</text>
@@ -95,7 +110,9 @@ export function ConflictResolver(props: ConflictResolverProps) {
<box flexDirection="row" gap={1}>
<text fg={theme.colors.textDim}>Agents:</text>
<text fg={theme.colors.text}>
{getAgentNames(currentConflict()!.conflictingAgentIds).join(" vs ")}
{getAgentNames(currentConflict()!.conflictingAgentIds).join(
" vs ",
)}
</text>
</box>
@@ -119,15 +136,33 @@ export function ConflictResolver(props: ConflictResolverProps) {
<box
flexDirection="row"
gap={1}
backgroundColor={index() === selectedStrategyIndex() ? theme.colors.bgHighlight : undefined}
backgroundColor={
index() === selectedStrategyIndex()
? theme.colors.bgHighlight
: undefined
}
paddingLeft={1}
>
<text fg={index() === selectedStrategyIndex() ? theme.colors.primary : theme.colors.textDim}>
<text
fg={
index() === selectedStrategyIndex()
? theme.colors.primary
: theme.colors.textDim
}
>
{index() === selectedStrategyIndex() ? "▸" : " "}
</text>
<text
fg={index() === selectedStrategyIndex() ? theme.colors.text : theme.colors.textDim}
attributes={index() === selectedStrategyIndex() ? TextAttributes.BOLD : TextAttributes.NONE}
fg={
index() === selectedStrategyIndex()
? theme.colors.text
: theme.colors.textDim
}
attributes={
index() === selectedStrategyIndex()
? TextAttributes.BOLD
: TextAttributes.NONE
}
>
{option.label}
</text>
@@ -152,7 +187,7 @@ export function ConflictResolver(props: ConflictResolverProps) {
{/* Actions */}
<box flexDirection="row" gap={2} justifyContent="flex-end">
<text fg={theme.colors.textDim}>
[/] Select [Enter] Resolve [Esc] Dismiss
[/] Select [Enter] Resolve [Esc] Dismiss
</text>
</box>
</box>
@@ -169,7 +204,9 @@ export function ConflictIndicator() {
onMount(() => {
const unsubscribe = multiAgentStore.subscribe((state) => {
const unresolvedCount = state.conflicts.filter((c) => !c.resolution).length;
const unresolvedCount = state.conflicts.filter(
(c) => !c.resolution,
).length;
setConflictCount(unresolvedCount);
});

View File

@@ -78,7 +78,7 @@ export function HelpDetail(props: HelpDetailProps) {
<text fg={theme.colors.warning} attributes={TextAttributes.BOLD}>
Usage
</text>
<text fg={theme.colors.success}> {currentTopic.usage}</text>
<text fg={theme.colors.success}> {currentTopic.usage}</text>
</Show>
<Show when={currentTopic.examples && currentTopic.examples.length > 0}>
@@ -87,9 +87,7 @@ export function HelpDetail(props: HelpDetailProps) {
Examples
</text>
<For each={currentTopic.examples}>
{(example) => (
<text fg={theme.colors.text}> {example}</text>
)}
{(example) => <text fg={theme.colors.text}> {example}</text>}
</For>
</Show>
@@ -99,17 +97,13 @@ export function HelpDetail(props: HelpDetailProps) {
Shortcuts
</text>
<For each={currentTopic.shortcuts}>
{(shortcut) => (
<text fg={theme.colors.primary}> {shortcut}</text>
)}
{(shortcut) => <text fg={theme.colors.primary}> {shortcut}</text>}
</For>
</Show>
<box height={1} />
<text fg={theme.colors.textDim}>
Esc/Backspace back | Enter close
</text>
<text fg={theme.colors.textDim}>Esc/Backspace back | Enter close</text>
</box>
);
}

View File

@@ -4,7 +4,14 @@
* Displays active agents, their status, and execution progress.
*/
import { For, Show, createMemo, createSignal, onMount, onCleanup } from "solid-js";
import {
For,
Show,
createMemo,
createSignal,
onMount,
onCleanup,
} from "solid-js";
import { TextAttributes } from "@opentui/core";
import { useTheme } from "@tui-solid/context/theme";
import { multiAgentStore } from "@stores/core/multi-agent-store";
@@ -45,7 +52,9 @@ export function MultiAgentPanel(props: MultiAgentPanelProps) {
running: all.filter((i) => i.status === "running").length,
waiting: all.filter((i) => i.status === "waiting_conflict").length,
completed: all.filter((i) => i.status === "completed").length,
failed: all.filter((i) => i.status === "error" || i.status === "cancelled").length,
failed: all.filter(
(i) => i.status === "error" || i.status === "cancelled",
).length,
total: all.length,
};
});
@@ -107,24 +116,16 @@ export function MultiAgentPanel(props: MultiAgentPanelProps) {
{/* Status Summary */}
<box flexDirection="row" gap={1} marginBottom={1}>
<Show when={stats().running > 0}>
<text fg={theme.colors.info}>
{stats().running}
</text>
<text fg={theme.colors.info}> {stats().running}</text>
</Show>
<Show when={stats().waiting > 0}>
<text fg={theme.colors.warning}>
{stats().waiting}
</text>
<text fg={theme.colors.warning}> {stats().waiting}</text>
</Show>
<Show when={stats().completed > 0}>
<text fg={theme.colors.success}>
{stats().completed}
</text>
<text fg={theme.colors.success}> {stats().completed}</text>
</Show>
<Show when={stats().failed > 0}>
<text fg={theme.colors.error}>
{stats().failed}
</text>
<text fg={theme.colors.error}> {stats().failed}</text>
</Show>
</box>
@@ -136,7 +137,11 @@ export function MultiAgentPanel(props: MultiAgentPanelProps) {
<box
flexDirection="column"
marginBottom={1}
backgroundColor={index() === selectedIndex() ? theme.colors.bgHighlight : undefined}
backgroundColor={
index() === selectedIndex()
? theme.colors.bgHighlight
: undefined
}
paddingLeft={1}
paddingRight={1}
>

View File

@@ -131,7 +131,7 @@ export function ModeSelect(props: ModeSelectProps) {
)}
</box>
<box>
<text fg={theme.colors.textDim}> {mode.description}</text>
<text fg={theme.colors.textDim}> {mode.description}</text>
</box>
</box>
);

View File

@@ -3,7 +3,7 @@ import { useKeyboard } from "@opentui/solid";
import { TextAttributes } from "@opentui/core";
import { useTheme } from "@tui-solid/context/theme";
import { useAppStore } from "@tui-solid/context/app";
import type { ProviderStatus } from "@services/cascading-provider";
import type { ProviderStatus } from "@services/cascading-provider/availability";
interface ProviderOption {
id: string;
@@ -217,7 +217,11 @@ export function ProviderSelect(props: ProviderSelectProps) {
<box flexDirection="row" marginLeft={4}>
<text fg={theme.colors.textDim}>{provider.description}</text>
</box>
<Show when={provider.id === "ollama" && provider.score !== undefined}>
<Show
when={
provider.id === "ollama" && provider.score !== undefined
}
>
<box flexDirection="row" marginLeft={4}>
<text fg={theme.colors.textDim}>Quality Score: </text>
<text fg={getScoreColor(provider.score)}>

View File

@@ -190,7 +190,10 @@ interface AppContextValue {
// MCP actions
setMcpServers: (servers: MCPServerDisplay[]) => void;
addMcpServer: (server: MCPServerDisplay) => void;
updateMcpServerStatus: (id: string, status: MCPServerDisplay["status"]) => void;
updateMcpServerStatus: (
id: string,
status: MCPServerDisplay["status"],
) => void;
// Computed
isInputLocked: () => boolean;
@@ -309,7 +312,8 @@ export const { provider: AppStoreProvider, use: useAppStore } =
// Individual property accessors for fine-grained reactivity
const streamingLogId = (): string | null => store.streamingLog.logId;
const streamingLogContent = (): string => store.streamingLog.content;
const streamingLogIsActive = (): boolean => store.streamingLog.isStreaming;
const streamingLogIsActive = (): boolean =>
store.streamingLog.isStreaming;
const suggestions = (): SuggestionState => store.suggestions;
const cascadeEnabled = (): boolean => store.cascadeEnabled;
const mcpServers = (): MCPServerDisplay[] => store.mcpServers;
@@ -520,7 +524,10 @@ export const { provider: AppStoreProvider, use: useAppStore } =
setStore("brain", { ...store.brain, user });
};
const setBrainCounts = (knowledgeCount: number, memoryCount: number): void => {
const setBrainCounts = (
knowledgeCount: number,
memoryCount: number,
): void => {
setStore("brain", { ...store.brain, knowledgeCount, memoryCount });
};
@@ -541,7 +548,9 @@ export const { provider: AppStoreProvider, use: useAppStore } =
setStore(
produce((s) => {
// Replace if exists, otherwise add
const existingIndex = s.mcpServers.findIndex((srv) => srv.id === server.id);
const existingIndex = s.mcpServers.findIndex(
(srv) => srv.id === server.id,
);
if (existingIndex !== -1) {
s.mcpServers[existingIndex] = server;
} else {
@@ -551,7 +560,10 @@ export const { provider: AppStoreProvider, use: useAppStore } =
);
};
const updateMcpServerStatus = (id: string, status: MCPServerDisplay["status"]): void => {
const updateMcpServerStatus = (
id: string,
status: MCPServerDisplay["status"],
): void => {
setStore(
produce((s) => {
const server = s.mcpServers.find((srv) => srv.id === id);
@@ -1193,7 +1205,10 @@ export const appStore = {
storeRef.addMcpServer(server);
},
updateMcpServerStatus: (id: string, status: MCPServerDisplay["status"]): void => {
updateMcpServerStatus: (
id: string,
status: MCPServerDisplay["status"],
): void => {
if (!storeRef) return;
storeRef.updateMcpServerStatus(id, status);
},

View File

@@ -1,4 +1,11 @@
import { Show, Switch, Match, createSignal, createMemo, onMount } from "solid-js";
import {
Show,
Switch,
Match,
createSignal,
createMemo,
onMount,
} from "solid-js";
import { useTheme } from "@tui-solid/context/theme";
import { useAppStore } from "@tui-solid/context/app";
import { Header } from "@tui-solid/components/layout/header";
@@ -23,8 +30,13 @@ import { CenteredModal } from "@tui-solid/components/modals/centered-modal";
import { DebugLogPanel } from "@tui-solid/components/logs/debug-log-panel";
import { BrainMenu } from "@tui-solid/components/menu/brain-menu";
import { BRAIN_DISABLED } from "@constants/brain";
import { initializeMCP, getServerInstances } from "@services/mcp";
import type { PermissionScope, LearningScope, InteractionMode, MCPServerDisplay } from "@/types/tui";
import { initializeMCP, getServerInstances } from "@services/mcp/manager";
import type {
PermissionScope,
LearningScope,
InteractionMode,
MCPServerDisplay,
} from "@/types/tui";
import type { MCPAddFormData } from "@/types/mcp";
interface AgentOption {
@@ -93,8 +105,12 @@ export function Session(props: SessionProps) {
servers.push({
id,
name: instance.config.name || id,
status: instance.state === "connected" ? "connected" :
instance.state === "error" ? "error" : "disconnected",
status:
instance.state === "connected"
? "connected"
: instance.state === "error"
? "error"
: "disconnected",
description: instance.config.command,
});
}
@@ -112,7 +128,7 @@ export function Session(props: SessionProps) {
// Local state for help menu
const [selectedHelpTopic, setSelectedHelpTopic] = createSignal<string | null>(
null
null,
);
const handleCommandSelect = (command: string): void => {
@@ -142,7 +158,11 @@ export function Session(props: SessionProps) {
app.transitionFromCommandMenu("provider_select");
return;
}
if (lowerCommand === "help" || lowerCommand === "h" || lowerCommand === "?") {
if (
lowerCommand === "help" ||
lowerCommand === "h" ||
lowerCommand === "?"
) {
app.transitionFromCommandMenu("help_menu");
return;
}