Fixing the permissions
This commit is contained in:
@@ -2,7 +2,11 @@ import { tui } from "@tui-solid/app";
|
||||
import { getProviderInfo } from "@services/chat-tui-service";
|
||||
import type { ChatServiceState } from "@services/chat-tui-service";
|
||||
import type { AgentConfig } from "@/types/agent-config";
|
||||
import type { PermissionScope, LearningScope } from "@/types/tui";
|
||||
import type {
|
||||
PermissionScope,
|
||||
LearningScope,
|
||||
PlanApprovalPromptResponse,
|
||||
} from "@/types/tui";
|
||||
|
||||
export interface RenderAppSolidProps {
|
||||
sessionId: string;
|
||||
@@ -15,6 +19,7 @@ export interface RenderAppSolidProps {
|
||||
allowed: boolean,
|
||||
scope?: PermissionScope,
|
||||
) => void;
|
||||
handlePlanApprovalResponse?: (response: PlanApprovalPromptResponse) => void;
|
||||
handleLearningResponse?: (
|
||||
save: boolean,
|
||||
scope?: LearningScope,
|
||||
@@ -47,6 +52,7 @@ export const renderAppSolid = async (
|
||||
onModelSelect: props.handleModelSelect,
|
||||
onThemeSelect: props.handleThemeSelect,
|
||||
onPermissionResponse: props.handlePermissionResponse ?? (() => {}),
|
||||
onPlanApprovalResponse: props.handlePlanApprovalResponse ?? (() => {}),
|
||||
onLearningResponse: props.handleLearningResponse ?? (() => {}),
|
||||
plan: props.plan,
|
||||
});
|
||||
|
||||
@@ -5,7 +5,11 @@ import { addServer, connectServer } from "@services/mcp/manager";
|
||||
import * as brainService from "@services/brain";
|
||||
import type { ChatServiceState } from "@services/chat-tui-service";
|
||||
import type { AgentConfig } from "@/types/agent-config";
|
||||
import type { PermissionScope, LearningScope } from "@/types/tui";
|
||||
import type {
|
||||
PermissionScope,
|
||||
LearningScope,
|
||||
PlanApprovalPromptResponse,
|
||||
} from "@/types/tui";
|
||||
import type { ProviderModel } from "@/types/providers";
|
||||
import type { MCPAddFormData } from "@/types/mcp";
|
||||
|
||||
@@ -29,6 +33,7 @@ export interface RenderAppProps {
|
||||
allowed: boolean,
|
||||
scope?: PermissionScope,
|
||||
) => void;
|
||||
handlePlanApprovalResponse?: (response: PlanApprovalPromptResponse) => void;
|
||||
handleLearningResponse?: (
|
||||
save: boolean,
|
||||
scope?: LearningScope,
|
||||
@@ -189,6 +194,7 @@ export const renderApp = async (props: RenderAppProps): Promise<void> => {
|
||||
},
|
||||
onMCPAdd: props.handleMCPAdd ?? defaultHandleMCPAdd,
|
||||
onPermissionResponse: props.handlePermissionResponse ?? (() => {}),
|
||||
onPlanApprovalResponse: props.handlePlanApprovalResponse ?? (() => {}),
|
||||
onLearningResponse: props.handleLearningResponse ?? (() => {}),
|
||||
onBrainSetJwtToken:
|
||||
props.handleBrainSetJwtToken ?? defaultHandleBrainSetJwtToken,
|
||||
|
||||
63
src/services/chat-tui/plan-approval.ts
Normal file
63
src/services/chat-tui/plan-approval.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* Chat TUI plan approval handling
|
||||
*
|
||||
* Follows the same pattern as permissions.ts:
|
||||
* Creates a blocking promise that resolves when the user responds
|
||||
* to the plan approval modal.
|
||||
*/
|
||||
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import type { PlanApprovalPromptResponse } from "@/types/tui";
|
||||
import type { ImplementationPlan } from "@/types/plan-mode";
|
||||
import { appStore } from "@tui-solid/context/app";
|
||||
|
||||
export interface PlanApprovalHandlerRequest {
|
||||
plan: ImplementationPlan;
|
||||
planFilePath?: string;
|
||||
}
|
||||
|
||||
export type PlanApprovalHandler = (
|
||||
request: PlanApprovalHandlerRequest,
|
||||
) => Promise<PlanApprovalPromptResponse>;
|
||||
|
||||
let planApprovalHandler: PlanApprovalHandler | null = null;
|
||||
|
||||
export const setPlanApprovalHandler = (
|
||||
handler: PlanApprovalHandler | null,
|
||||
): void => {
|
||||
planApprovalHandler = handler;
|
||||
};
|
||||
|
||||
export const getPlanApprovalHandler = (): PlanApprovalHandler | null =>
|
||||
planApprovalHandler;
|
||||
|
||||
export const createPlanApprovalHandler = (): PlanApprovalHandler => {
|
||||
return (
|
||||
request: PlanApprovalHandlerRequest,
|
||||
): Promise<PlanApprovalPromptResponse> => {
|
||||
return new Promise((resolve) => {
|
||||
appStore.setMode("plan_approval");
|
||||
|
||||
appStore.setPlanApprovalPrompt({
|
||||
id: uuidv4(),
|
||||
planTitle: request.plan.title,
|
||||
planSummary: request.plan.summary,
|
||||
planFilePath: request.planFilePath,
|
||||
resolve: (response) => {
|
||||
appStore.setPlanApprovalPrompt(null);
|
||||
appStore.setMode("thinking");
|
||||
resolve(response);
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
export const setupPlanApprovalHandler = (): void => {
|
||||
setPlanApprovalHandler(createPlanApprovalHandler());
|
||||
};
|
||||
|
||||
export const cleanupPlanApprovalHandler = (): void => {
|
||||
setPlanApprovalHandler(null);
|
||||
};
|
||||
@@ -2,77 +2,62 @@ import { createSignal, createMemo, For, Show } from "solid-js";
|
||||
import { useKeyboard } from "@opentui/solid";
|
||||
import { TextAttributes } from "@opentui/core";
|
||||
import { useTheme } from "@tui-solid/context/theme";
|
||||
import type { ImplementationPlan, PlanStep } from "@/types/plan-mode";
|
||||
|
||||
/**
|
||||
* Plan approval request for the TUI
|
||||
*/
|
||||
export interface PlanApprovalRequest {
|
||||
plan: ImplementationPlan;
|
||||
resolve?: (response: { approved: boolean; message?: string }) => void;
|
||||
}
|
||||
import type { PlanApprovalPrompt, PlanApprovalPromptResponse } from "@/types/tui";
|
||||
import {
|
||||
PLAN_APPROVAL_OPTIONS,
|
||||
PLAN_APPROVAL_FOOTER_TEXT,
|
||||
} from "@constants/plan-approval";
|
||||
import type { PlanApprovalOption, PlanEditMode } from "@constants/plan-approval";
|
||||
|
||||
interface PlanApprovalModalProps {
|
||||
request: PlanApprovalRequest;
|
||||
onRespond?: (approved: boolean, message?: string) => void;
|
||||
prompt: PlanApprovalPrompt;
|
||||
onRespond: (response: PlanApprovalPromptResponse) => void;
|
||||
isActive?: boolean;
|
||||
}
|
||||
|
||||
interface ApprovalOption {
|
||||
key: string;
|
||||
label: string;
|
||||
approved: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
const APPROVAL_OPTIONS: ApprovalOption[] = [
|
||||
{ key: "y", label: "Approve - proceed with implementation", approved: true },
|
||||
{ key: "e", label: "Edit - modify plan before execution", approved: false, message: "edit" },
|
||||
{ key: "n", label: "Reject - cancel this plan", approved: false },
|
||||
];
|
||||
|
||||
export function PlanApprovalModal(props: PlanApprovalModalProps) {
|
||||
const theme = useTheme();
|
||||
const [selectedIndex, setSelectedIndex] = createSignal(0);
|
||||
const [scrollOffset, setScrollOffset] = createSignal(0);
|
||||
const [feedbackMode, setFeedbackMode] = createSignal(false);
|
||||
const [feedbackText, setFeedbackText] = createSignal("");
|
||||
const isActive = () => props.isActive ?? true;
|
||||
|
||||
const MAX_VISIBLE_STEPS = 5;
|
||||
const optionCount = PLAN_APPROVAL_OPTIONS.length;
|
||||
|
||||
const plan = () => props.request.plan;
|
||||
|
||||
const visibleSteps = createMemo(() => {
|
||||
const steps = plan().steps;
|
||||
const offset = scrollOffset();
|
||||
return steps.slice(offset, offset + MAX_VISIBLE_STEPS);
|
||||
});
|
||||
|
||||
const canScrollUp = () => scrollOffset() > 0;
|
||||
const canScrollDown = () => scrollOffset() + MAX_VISIBLE_STEPS < plan().steps.length;
|
||||
|
||||
const handleResponse = (approved: boolean, message?: string): void => {
|
||||
if (props.request.resolve) {
|
||||
props.request.resolve({ approved, message });
|
||||
const handleApproval = (option: PlanApprovalOption): void => {
|
||||
if (option.editMode === "feedback") {
|
||||
setFeedbackMode(true);
|
||||
return;
|
||||
}
|
||||
props.onRespond?.(approved, message);
|
||||
|
||||
props.onRespond({
|
||||
approved: true,
|
||||
editMode: option.editMode,
|
||||
});
|
||||
};
|
||||
|
||||
const getRiskIcon = (risk: PlanStep["riskLevel"]): string => {
|
||||
const icons: Record<PlanStep["riskLevel"], string> = {
|
||||
high: "\u26A0\uFE0F",
|
||||
medium: "\u26A1",
|
||||
low: "\u2713",
|
||||
};
|
||||
return icons[risk];
|
||||
const handleFeedbackSubmit = (): void => {
|
||||
const text = feedbackText().trim();
|
||||
if (!text) return;
|
||||
|
||||
props.onRespond({
|
||||
approved: true,
|
||||
editMode: "feedback",
|
||||
feedback: text,
|
||||
});
|
||||
};
|
||||
|
||||
const getRiskColor = (risk: PlanStep["riskLevel"]): string => {
|
||||
const colors: Record<PlanStep["riskLevel"], string> = {
|
||||
high: theme.colors.error,
|
||||
medium: theme.colors.warning,
|
||||
low: theme.colors.success,
|
||||
};
|
||||
return colors[risk];
|
||||
const handleCancel = (): void => {
|
||||
if (feedbackMode()) {
|
||||
setFeedbackMode(false);
|
||||
setFeedbackText("");
|
||||
return;
|
||||
}
|
||||
|
||||
props.onRespond({
|
||||
approved: false,
|
||||
editMode: "manual_approve",
|
||||
});
|
||||
};
|
||||
|
||||
useKeyboard((evt) => {
|
||||
@@ -80,57 +65,71 @@ export function PlanApprovalModal(props: PlanApprovalModalProps) {
|
||||
|
||||
evt.stopPropagation();
|
||||
|
||||
if (evt.name === "up") {
|
||||
if (evt.shift) {
|
||||
// Scroll plan view
|
||||
if (canScrollUp()) {
|
||||
setScrollOffset((prev) => prev - 1);
|
||||
}
|
||||
} else {
|
||||
// Navigate options
|
||||
setSelectedIndex((prev) =>
|
||||
prev > 0 ? prev - 1 : APPROVAL_OPTIONS.length - 1,
|
||||
);
|
||||
// Feedback mode: handle text input
|
||||
if (feedbackMode()) {
|
||||
if (evt.name === "return") {
|
||||
handleFeedbackSubmit();
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (evt.name === "escape") {
|
||||
handleCancel();
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (evt.name === "backspace") {
|
||||
setFeedbackText((prev) => prev.slice(0, -1));
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
if (evt.name.length === 1 && !evt.ctrl && !evt.meta) {
|
||||
setFeedbackText((prev) => prev + evt.name);
|
||||
evt.preventDefault();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Normal mode: navigate options
|
||||
if (evt.name === "up") {
|
||||
setSelectedIndex((prev) =>
|
||||
prev > 0 ? prev - 1 : optionCount - 1,
|
||||
);
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt.name === "down") {
|
||||
if (evt.shift) {
|
||||
// Scroll plan view
|
||||
if (canScrollDown()) {
|
||||
setScrollOffset((prev) => prev + 1);
|
||||
}
|
||||
} else {
|
||||
// Navigate options
|
||||
setSelectedIndex((prev) =>
|
||||
prev < APPROVAL_OPTIONS.length - 1 ? prev + 1 : 0,
|
||||
);
|
||||
}
|
||||
setSelectedIndex((prev) =>
|
||||
prev < optionCount - 1 ? prev + 1 : 0,
|
||||
);
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt.name === "return") {
|
||||
const option = APPROVAL_OPTIONS[selectedIndex()];
|
||||
handleResponse(option.approved, option.message);
|
||||
handleApproval(PLAN_APPROVAL_OPTIONS[selectedIndex()]);
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
if (evt.name === "escape") {
|
||||
handleResponse(false, "cancelled");
|
||||
handleCancel();
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle shortcut keys
|
||||
// Shift+Tab shortcut for option 1 (auto-accept clear)
|
||||
if (evt.name === "tab" && evt.shift) {
|
||||
handleApproval(PLAN_APPROVAL_OPTIONS[0]);
|
||||
evt.preventDefault();
|
||||
return;
|
||||
}
|
||||
|
||||
// Number key shortcuts (1-4)
|
||||
if (evt.name.length === 1 && !evt.ctrl && !evt.meta) {
|
||||
const charLower = evt.name.toLowerCase();
|
||||
const option = APPROVAL_OPTIONS.find((o) => o.key === charLower);
|
||||
const option = PLAN_APPROVAL_OPTIONS.find((o) => o.key === evt.name);
|
||||
if (option) {
|
||||
handleResponse(option.approved, option.message);
|
||||
handleApproval(option);
|
||||
evt.preventDefault();
|
||||
}
|
||||
}
|
||||
@@ -139,130 +138,110 @@ export function PlanApprovalModal(props: PlanApprovalModalProps) {
|
||||
return (
|
||||
<box
|
||||
flexDirection="column"
|
||||
borderColor={theme.colors.warning}
|
||||
borderColor={theme.colors.borderModal}
|
||||
border={["top", "bottom", "left", "right"]}
|
||||
backgroundColor={theme.colors.background}
|
||||
paddingLeft={2}
|
||||
paddingRight={2}
|
||||
paddingTop={1}
|
||||
paddingBottom={1}
|
||||
maxHeight={25}
|
||||
>
|
||||
{/* Header */}
|
||||
<box marginBottom={1}>
|
||||
<text fg={theme.colors.warning} attributes={TextAttributes.BOLD}>
|
||||
Plan Approval Required
|
||||
</text>
|
||||
</box>
|
||||
|
||||
{/* Title and Summary */}
|
||||
<box flexDirection="column" marginBottom={1}>
|
||||
<text fg={theme.colors.primary} attributes={TextAttributes.BOLD}>
|
||||
{plan().title}
|
||||
</text>
|
||||
<text fg={theme.colors.textDim}>{plan().summary}</text>
|
||||
</box>
|
||||
|
||||
{/* Estimated Changes */}
|
||||
<box flexDirection="row" marginBottom={1}>
|
||||
<text fg={theme.colors.success}>
|
||||
+{plan().estimatedChanges.filesCreated} create{" "}
|
||||
</text>
|
||||
<text fg={theme.colors.warning}>
|
||||
~{plan().estimatedChanges.filesModified} modify{" "}
|
||||
</text>
|
||||
<text fg={theme.colors.error}>
|
||||
-{plan().estimatedChanges.filesDeleted} delete
|
||||
CodeTyper has written up a plan and is ready to execute. Would you
|
||||
like to proceed?
|
||||
</text>
|
||||
</box>
|
||||
|
||||
{/* Steps */}
|
||||
<box flexDirection="column" marginBottom={1}>
|
||||
<text fg={theme.colors.text} attributes={TextAttributes.BOLD}>
|
||||
Implementation Steps ({plan().steps.length} total):
|
||||
</text>
|
||||
<Show when={canScrollUp()}>
|
||||
<text fg={theme.colors.textDim}> {"\u25B2"} more above...</text>
|
||||
</Show>
|
||||
<For each={visibleSteps()}>
|
||||
{(step, index) => (
|
||||
<box flexDirection="row">
|
||||
<text fg={getRiskColor(step.riskLevel)}>
|
||||
{getRiskIcon(step.riskLevel)}{" "}
|
||||
</text>
|
||||
<text fg={theme.colors.textDim}>
|
||||
{scrollOffset() + index() + 1}.{" "}
|
||||
</text>
|
||||
<text fg={theme.colors.text}>
|
||||
{step.title.substring(0, 50)}
|
||||
{step.title.length > 50 ? "..." : ""}
|
||||
</text>
|
||||
</box>
|
||||
)}
|
||||
</For>
|
||||
<Show when={canScrollDown()}>
|
||||
<text fg={theme.colors.textDim}> {"\u25BC"} more below...</text>
|
||||
</Show>
|
||||
</box>
|
||||
|
||||
{/* Risks */}
|
||||
<Show when={plan().risks.length > 0}>
|
||||
<box flexDirection="column" marginBottom={1}>
|
||||
<text fg={theme.colors.error} attributes={TextAttributes.BOLD}>
|
||||
Risks ({plan().risks.length}):
|
||||
{/* Plan info */}
|
||||
<Show when={props.prompt.planTitle}>
|
||||
<box marginBottom={1}>
|
||||
<text fg={theme.colors.text} attributes={TextAttributes.BOLD}>
|
||||
{props.prompt.planTitle}
|
||||
</text>
|
||||
<For each={plan().risks.slice(0, 2)}>
|
||||
{(risk) => (
|
||||
<box flexDirection="row">
|
||||
<text fg={theme.colors.error}> - </text>
|
||||
<text fg={theme.colors.text}>
|
||||
{risk.description.substring(0, 60)}
|
||||
</text>
|
||||
</box>
|
||||
)}
|
||||
</For>
|
||||
<Show when={plan().risks.length > 2}>
|
||||
<text fg={theme.colors.textDim}>
|
||||
{" "}
|
||||
... and {plan().risks.length - 2} more
|
||||
</text>
|
||||
</Show>
|
||||
</box>
|
||||
</Show>
|
||||
|
||||
<Show when={props.prompt.planSummary}>
|
||||
<box marginBottom={1}>
|
||||
<text fg={theme.colors.textDim}>{props.prompt.planSummary}</text>
|
||||
</box>
|
||||
</Show>
|
||||
|
||||
{/* Options */}
|
||||
<box flexDirection="column" marginTop={1}>
|
||||
<For each={APPROVAL_OPTIONS}>
|
||||
{(option, index) => {
|
||||
const isSelected = () => index() === selectedIndex();
|
||||
const keyColor = () =>
|
||||
option.approved ? theme.colors.success : theme.colors.error;
|
||||
return (
|
||||
<box flexDirection="row">
|
||||
<text
|
||||
fg={isSelected() ? theme.colors.primary : undefined}
|
||||
attributes={
|
||||
isSelected() ? TextAttributes.BOLD : TextAttributes.NONE
|
||||
}
|
||||
>
|
||||
{isSelected() ? "> " : " "}
|
||||
</text>
|
||||
<text fg={keyColor()}>[{option.key}] </text>
|
||||
<text fg={isSelected() ? theme.colors.primary : undefined}>
|
||||
{option.label}
|
||||
</text>
|
||||
</box>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</box>
|
||||
<Show when={!feedbackMode()}>
|
||||
<box flexDirection="column" marginTop={1}>
|
||||
<For each={PLAN_APPROVAL_OPTIONS}>
|
||||
{(option, index) => {
|
||||
const isSelected = () => index() === selectedIndex();
|
||||
return (
|
||||
<box flexDirection="row">
|
||||
<text
|
||||
fg={isSelected() ? theme.colors.primary : theme.colors.textDim}
|
||||
attributes={
|
||||
isSelected() ? TextAttributes.BOLD : TextAttributes.NONE
|
||||
}
|
||||
>
|
||||
{isSelected() ? "> " : " "}
|
||||
</text>
|
||||
<text fg={theme.colors.textDim}>{option.key}. </text>
|
||||
<text
|
||||
fg={isSelected() ? theme.colors.text : theme.colors.textDim}
|
||||
>
|
||||
{option.label}
|
||||
</text>
|
||||
<Show when={option.shortcut}>
|
||||
<text fg={theme.colors.textMuted}>
|
||||
{" "}({option.shortcut})
|
||||
</text>
|
||||
</Show>
|
||||
</box>
|
||||
);
|
||||
}}
|
||||
</For>
|
||||
</box>
|
||||
</Show>
|
||||
|
||||
{/* Feedback input mode */}
|
||||
<Show when={feedbackMode()}>
|
||||
<box flexDirection="column" marginTop={1}>
|
||||
<text fg={theme.colors.text}>
|
||||
Tell CodeTyper what to change:
|
||||
</text>
|
||||
<box
|
||||
border={["left"]}
|
||||
borderColor={theme.colors.borderFocus}
|
||||
paddingLeft={1}
|
||||
marginTop={1}
|
||||
>
|
||||
<text fg={theme.colors.text}>
|
||||
{feedbackText() || " "}
|
||||
<text fg={theme.colors.bgCursor}>_</text>
|
||||
</text>
|
||||
</box>
|
||||
<text fg={theme.colors.textDim}>
|
||||
Enter to submit | Esc to cancel
|
||||
</text>
|
||||
</box>
|
||||
</Show>
|
||||
|
||||
{/* Footer */}
|
||||
<box marginTop={1}>
|
||||
<text fg={theme.colors.textDim}>
|
||||
{"\u2191\u2193"} options | Shift+{"\u2191\u2193"} scroll steps | Enter select | y/e/n shortcut
|
||||
</text>
|
||||
</box>
|
||||
<Show when={!feedbackMode()}>
|
||||
<box marginTop={1} flexDirection="row">
|
||||
<Show when={props.prompt.planFilePath}>
|
||||
<text fg={theme.colors.textDim}>
|
||||
{PLAN_APPROVAL_FOOTER_TEXT} - {props.prompt.planFilePath}
|
||||
</text>
|
||||
</Show>
|
||||
<Show when={!props.prompt.planFilePath}>
|
||||
<text fg={theme.colors.textDim}>
|
||||
{"\u2191\u2193"} options | Enter select | 1-4 shortcut | Esc
|
||||
cancel
|
||||
</text>
|
||||
</Show>
|
||||
</box>
|
||||
</Show>
|
||||
</box>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import { ModeSelect } from "@tui-solid/components/submenu/mode-select";
|
||||
import { ProviderSelect } from "@tui-solid/components/submenu/provider-select";
|
||||
import { FilePicker } from "@tui-solid/components/inputs/file-picker";
|
||||
import { PermissionModal } from "@tui-solid/components/modals/permission-modal";
|
||||
import { PlanApprovalModal } from "@tui-solid/components/modals/plan-approval-modal";
|
||||
import { LearningModal } from "@tui-solid/components/modals/learning-modal";
|
||||
import { HelpMenu } from "@tui-solid/components/menu/help-menu";
|
||||
import { HelpDetail } from "@tui-solid/components/panels/help-detail";
|
||||
@@ -36,6 +37,7 @@ import type {
|
||||
LearningScope,
|
||||
InteractionMode,
|
||||
MCPServerDisplay,
|
||||
PlanApprovalPromptResponse,
|
||||
} from "@/types/tui";
|
||||
import type { MCPAddFormData } from "@/types/mcp";
|
||||
|
||||
@@ -63,6 +65,7 @@ interface SessionProps {
|
||||
onProviderSelect?: (providerId: string) => void;
|
||||
onCascadeToggle?: () => void;
|
||||
onPermissionResponse: (allowed: boolean, scope?: PermissionScope) => void;
|
||||
onPlanApprovalResponse: (response: PlanApprovalPromptResponse) => void;
|
||||
onLearningResponse: (
|
||||
save: boolean,
|
||||
scope?: LearningScope,
|
||||
@@ -297,8 +300,18 @@ export function Session(props: SessionProps) {
|
||||
/>
|
||||
</Show>
|
||||
|
||||
<Show
|
||||
when={app.mode() === "plan_approval" && app.planApprovalPrompt()}
|
||||
>
|
||||
<PlanApprovalModal
|
||||
prompt={app.planApprovalPrompt()!}
|
||||
onRespond={props.onPlanApprovalResponse}
|
||||
isActive={app.mode() === "plan_approval"}
|
||||
/>
|
||||
</Show>
|
||||
|
||||
<StatusBar />
|
||||
<Show when={app.mode() !== "permission_prompt"}>
|
||||
<Show when={app.mode() !== "permission_prompt" && app.mode() !== "plan_approval"}>
|
||||
<InputArea onSubmit={props.onSubmit} />
|
||||
</Show>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user