mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Clarify chat panel composition boundaries
This commit is contained in:
parent
fc5f80125d
commit
7a065a6e87
14 changed files with 193 additions and 169 deletions
|
|
@ -1,14 +1,20 @@
|
|||
import type { SharedAppServerMetadata } from "../../../app-server/shared-cache-state";
|
||||
import type { Thread } from "../../../domain/threads/model";
|
||||
import type { ChatServerMetadataActions } from "../server-actions/metadata-actions";
|
||||
import type { ChatServerThreadActions } from "../server-actions/thread-actions";
|
||||
import type { ChatPanelContext } from "./context";
|
||||
|
||||
export interface CachedSharedAppServerStateSource {
|
||||
cachedThreadList: () => readonly Thread[] | null;
|
||||
cachedAppServerMetadata: () => SharedAppServerMetadata | null;
|
||||
}
|
||||
|
||||
export function applyCachedSharedAppServerState(
|
||||
context: ChatPanelContext,
|
||||
source: CachedSharedAppServerStateSource,
|
||||
serverThreads: ChatServerThreadActions,
|
||||
serverMetadata: ChatServerMetadataActions,
|
||||
): void {
|
||||
const threads = context.plugin.cachedThreadList();
|
||||
const threads = source.cachedThreadList();
|
||||
if (threads) serverThreads.applyThreadList(threads);
|
||||
const metadata = context.plugin.cachedAppServerMetadata();
|
||||
const metadata = source.cachedAppServerMetadata();
|
||||
if (metadata) serverMetadata.applyAppServerMetadata(metadata);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import type { ChatThreadActions } from "../threads/thread-actions";
|
|||
import type { ThreadHistoryController } from "../threads/thread-history-controller";
|
||||
import type { ThreadRenameController } from "../threads/thread-rename-controller";
|
||||
import type { ToolbarPanelController } from "./toolbar-controller";
|
||||
import type { AppServerWarmupActions } from "../session/app-server-warmup-controller";
|
||||
import type { ChatConnectionController } from "../session/connection-controller";
|
||||
import type { ChatReconnectActions } from "../session/reconnect-actions";
|
||||
import type { PendingRequestController } from "../requests/pending-request-controller";
|
||||
|
|
@ -20,11 +19,9 @@ import type { RestoredThreadController } from "../threads/restored-thread-contro
|
|||
import type { ThreadIdentityActions } from "../threads/thread-identity-actions";
|
||||
import type { ThreadResumeController } from "../threads/thread-resume-controller";
|
||||
import type { ThreadSelectionActions } from "../threads/thread-selection-controller";
|
||||
import type { ChatViewOpenCloseActions } from "./open-close-actions";
|
||||
import type { ChatViewRenderController } from "./view-render-controller";
|
||||
import type { ChatViewStateActions } from "./view-state-controller";
|
||||
import type { ChatMessageRenderer } from "../ui/message-stream";
|
||||
import type { ChatPanelContext } from "./context";
|
||||
import type { ChatControllerCompositionPorts } from "./controller-ports";
|
||||
import {
|
||||
createChatServerActionControllers,
|
||||
createChatConnectionControllers,
|
||||
|
|
@ -40,7 +37,7 @@ export interface ChatViewControllers {
|
|||
manager: ConnectionManager;
|
||||
controller: ChatConnectionController;
|
||||
reconnect: ChatReconnectActions;
|
||||
warmup: AppServerWarmupActions;
|
||||
scheduleWarmup: () => void;
|
||||
};
|
||||
inbound: {
|
||||
controller: ChatInboundController;
|
||||
|
|
@ -76,12 +73,13 @@ export interface ChatViewControllers {
|
|||
render: {
|
||||
controller: ChatViewRenderController;
|
||||
messages: ChatMessageRenderer;
|
||||
openClose: ChatViewOpenCloseActions;
|
||||
viewState: ChatViewStateActions;
|
||||
openView: () => void;
|
||||
closeView: () => void;
|
||||
applyViewState: (state: unknown) => void;
|
||||
};
|
||||
}
|
||||
|
||||
export function createChatViewControllers(ports: ChatPanelContext): ChatViewControllers {
|
||||
export function createChatViewControllers(ports: ChatControllerCompositionPorts): ChatViewControllers {
|
||||
const connection = new ConnectionManager(() => ports.plugin.settings.codexPath, ports.plugin.vaultPath);
|
||||
const { renderController } = createViewRenderControllerGroup(ports, { connection });
|
||||
const runtimeSettings = createChatRuntimeSettingsActions({
|
||||
|
|
@ -94,7 +92,7 @@ export function createChatViewControllers(ports: ChatPanelContext): ChatViewCont
|
|||
const { history, threadActions, goals, restoredThread, threadResume, threadIdentity, threadRename } = createThreadControllerGroup(ports, {
|
||||
connection,
|
||||
});
|
||||
const { toolbarPanels, viewStateController } = createPanelUiControllerGroup(ports, {
|
||||
const { toolbarPanels, applyViewState } = createPanelUiControllerGroup(ports, {
|
||||
threadActions,
|
||||
});
|
||||
const { threadSelection } = createThreadSelectionControllerGroup(ports, {
|
||||
|
|
@ -152,7 +150,7 @@ export function createChatViewControllers(ports: ChatPanelContext): ChatViewCont
|
|||
goals,
|
||||
history,
|
||||
});
|
||||
const { appServerWarmup, openCloseController } = createConnectionLifecycleControllerGroup(ports, {
|
||||
const { scheduleAppServerWarmup, openView, closeView } = createConnectionLifecycleControllerGroup(ports, {
|
||||
connection,
|
||||
composerController,
|
||||
messageRenderer,
|
||||
|
|
@ -165,7 +163,7 @@ export function createChatViewControllers(ports: ChatPanelContext): ChatViewCont
|
|||
manager: connection,
|
||||
controller: connectionController,
|
||||
reconnect: reconnectActions,
|
||||
warmup: appServerWarmup,
|
||||
scheduleWarmup: scheduleAppServerWarmup,
|
||||
},
|
||||
inbound: {
|
||||
controller,
|
||||
|
|
@ -201,8 +199,9 @@ export function createChatViewControllers(ports: ChatPanelContext): ChatViewCont
|
|||
render: {
|
||||
controller: renderController,
|
||||
messages: messageRenderer,
|
||||
openClose: openCloseController,
|
||||
viewState: viewStateController,
|
||||
openView,
|
||||
closeView,
|
||||
applyViewState,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import type {
|
|||
} from "./lifecycle";
|
||||
import type { ComposerMetaViewModel } from "./model";
|
||||
|
||||
export interface ChatPanelContext {
|
||||
export interface ChatControllerCompositionPorts {
|
||||
obsidian: ChatPanelObsidianContext;
|
||||
plugin: CodexChatHost;
|
||||
state: ChatPanelStateContext;
|
||||
|
|
@ -3,18 +3,20 @@ import type { ChatServerMetadataActions } from "../server-actions/metadata-actio
|
|||
import type { ChatServerThreadActions } from "../server-actions/thread-actions";
|
||||
import type { ChatComposerController } from "../composer/controller";
|
||||
import type { ChatThreadActions } from "../threads/thread-actions";
|
||||
import { createAppServerWarmupActions } from "../session/app-server-warmup-controller";
|
||||
import { createChatViewOpenCloseActions } from "./open-close-actions";
|
||||
import { scheduleAppServerWarmup } from "../session/app-server-warmup-controller";
|
||||
import { closeChatView, openChatView, type ChatViewLifecycleHost } from "./view-lifecycle";
|
||||
import { ToolbarPanelController } from "./toolbar-controller";
|
||||
import { ChatViewRenderController } from "./view-render-controller";
|
||||
import { createChatViewStateActions } from "./view-state-controller";
|
||||
import { applyChatViewState } from "./view-state-controller";
|
||||
import type { ChatMessageRenderer } from "../ui/message-stream";
|
||||
import { applyCachedSharedAppServerState } from "./cached-app-server-state";
|
||||
import type { ChatPanelContext } from "./context";
|
||||
import type { ChatControllerCompositionPorts } from "./controller-ports";
|
||||
import { createChatShellRenderPort } from "./shell-render";
|
||||
|
||||
type ViewRenderControllerGroupPorts = Pick<ChatControllerCompositionPorts, "lifecycle" | "plugin" | "render" | "state">;
|
||||
|
||||
export function createViewRenderControllerGroup(
|
||||
context: ChatPanelContext,
|
||||
context: ViewRenderControllerGroupPorts,
|
||||
refs: {
|
||||
connection: ConnectionManager;
|
||||
},
|
||||
|
|
@ -42,8 +44,13 @@ export function createViewRenderControllerGroup(
|
|||
};
|
||||
}
|
||||
|
||||
type ConnectionLifecycleControllerGroupPorts = Pick<
|
||||
ChatControllerCompositionPorts,
|
||||
"client" | "lifecycle" | "liveState" | "obsidian" | "plugin" | "render"
|
||||
>;
|
||||
|
||||
export function createConnectionLifecycleControllerGroup(
|
||||
context: ChatPanelContext,
|
||||
context: ConnectionLifecycleControllerGroupPorts,
|
||||
refs: {
|
||||
connection: ConnectionManager;
|
||||
composerController: ChatComposerController;
|
||||
|
|
@ -52,76 +59,93 @@ export function createConnectionLifecycleControllerGroup(
|
|||
serverMetadata: ChatServerMetadataActions;
|
||||
},
|
||||
) {
|
||||
const { obsidian, lifecycle, render, liveState, client } = context;
|
||||
const { obsidian, plugin, lifecycle, render, liveState, client } = context;
|
||||
const { deferredTasks } = lifecycle;
|
||||
|
||||
const warmupHost = {
|
||||
deferredTasks,
|
||||
opened: lifecycle.getOpened,
|
||||
closing: lifecycle.getClosing,
|
||||
connected: () => refs.connection.isConnected(),
|
||||
ensureConnected: client.ensureConnected,
|
||||
};
|
||||
|
||||
const viewLifecycleHost: ChatViewLifecycleHost = {
|
||||
setOpened: lifecycle.setOpened,
|
||||
setClosing: lifecycle.setClosing,
|
||||
registerEvent: obsidian.registerEvent,
|
||||
registerComposerNoteIndexInvalidation: (register) => {
|
||||
refs.composerController.registerNoteIndexInvalidation(register);
|
||||
},
|
||||
registerPointerDown: obsidian.registerPointerDown,
|
||||
registerActiveLeafChange: obsidian.registerActiveLeafChange,
|
||||
handleActiveLeafChange: obsidian.handleActiveLeafChange,
|
||||
applyCachedSharedAppServerState: () => {
|
||||
applyCachedSharedAppServerState(plugin, refs.serverThreads, refs.serverMetadata);
|
||||
},
|
||||
render: render.now,
|
||||
scheduleDeferredAppServerWarmup: lifecycle.scheduleDeferredAppServerWarmup,
|
||||
scheduleDeferredRestoredThreadHydration: lifecycle.scheduleDeferredRestoredThreadHydration,
|
||||
closeToolbarPanelOnOutsidePointer: render.closeToolbarPanelOnOutsidePointer,
|
||||
invalidateConnectionWork: lifecycle.invalidateConnectionWork,
|
||||
invalidateResumeWork: lifecycle.invalidateResumeWork,
|
||||
clearDeferredTasks: () => {
|
||||
deferredTasks.clearAll();
|
||||
},
|
||||
panelRoot: render.panelRoot,
|
||||
disposeMessages: () => {
|
||||
refs.messageRenderer.dispose();
|
||||
},
|
||||
disposeComposer: () => {
|
||||
refs.composerController.dispose();
|
||||
},
|
||||
disconnect: () => {
|
||||
refs.connection.disconnect();
|
||||
},
|
||||
clearClient: client.clear,
|
||||
refreshLiveState: liveState.refresh,
|
||||
deferRefreshLiveState: liveState.deferRefresh,
|
||||
};
|
||||
|
||||
return {
|
||||
appServerWarmup: createAppServerWarmupActions({
|
||||
deferredTasks,
|
||||
opened: lifecycle.getOpened,
|
||||
closing: lifecycle.getClosing,
|
||||
connected: () => refs.connection.isConnected(),
|
||||
ensureConnected: client.ensureConnected,
|
||||
}),
|
||||
openCloseController: createChatViewOpenCloseActions({
|
||||
setOpened: lifecycle.setOpened,
|
||||
setClosing: lifecycle.setClosing,
|
||||
registerEvent: obsidian.registerEvent,
|
||||
registerComposerNoteIndexInvalidation: (register) => {
|
||||
refs.composerController.registerNoteIndexInvalidation(register);
|
||||
},
|
||||
registerPointerDown: obsidian.registerPointerDown,
|
||||
registerActiveLeafChange: obsidian.registerActiveLeafChange,
|
||||
handleActiveLeafChange: obsidian.handleActiveLeafChange,
|
||||
applyCachedSharedAppServerState: () => {
|
||||
applyCachedSharedAppServerState(context, refs.serverThreads, refs.serverMetadata);
|
||||
},
|
||||
render: render.now,
|
||||
scheduleDeferredAppServerWarmup: lifecycle.scheduleDeferredAppServerWarmup,
|
||||
scheduleDeferredRestoredThreadHydration: lifecycle.scheduleDeferredRestoredThreadHydration,
|
||||
closeToolbarPanelOnOutsidePointer: render.closeToolbarPanelOnOutsidePointer,
|
||||
invalidateConnectionWork: lifecycle.invalidateConnectionWork,
|
||||
invalidateResumeWork: lifecycle.invalidateResumeWork,
|
||||
clearDeferredTasks: () => {
|
||||
deferredTasks.clearAll();
|
||||
},
|
||||
panelRoot: render.panelRoot,
|
||||
disposeMessages: () => {
|
||||
refs.messageRenderer.dispose();
|
||||
},
|
||||
disposeComposer: () => {
|
||||
refs.composerController.dispose();
|
||||
},
|
||||
disconnect: () => {
|
||||
refs.connection.disconnect();
|
||||
},
|
||||
clearClient: client.clear,
|
||||
refreshLiveState: liveState.refresh,
|
||||
deferRefreshLiveState: liveState.deferRefresh,
|
||||
}),
|
||||
scheduleAppServerWarmup: () => {
|
||||
scheduleAppServerWarmup(warmupHost);
|
||||
},
|
||||
openView: () => {
|
||||
openChatView(viewLifecycleHost);
|
||||
},
|
||||
closeView: () => {
|
||||
closeChatView(viewLifecycleHost);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
type PanelUiControllerGroupPorts = Pick<ChatControllerCompositionPorts, "lifecycle" | "render" | "state" | "thread">;
|
||||
|
||||
export function createPanelUiControllerGroup(
|
||||
context: ChatPanelContext,
|
||||
context: PanelUiControllerGroupPorts,
|
||||
refs: {
|
||||
threadActions: ChatThreadActions;
|
||||
},
|
||||
) {
|
||||
const { lifecycle, render, thread } = context;
|
||||
|
||||
const toolbarPanels = new ToolbarPanelController({
|
||||
stateStore: context.state.stateStore,
|
||||
threadActions: refs.threadActions,
|
||||
scheduleRender: render.schedule,
|
||||
});
|
||||
const viewStateController = createChatViewStateActions({
|
||||
const viewStateHost = {
|
||||
invalidateResumeWork: lifecycle.invalidateResumeWork,
|
||||
clearRestoredThreadLifecycle: thread.clearRestoredLifecycle,
|
||||
clearDeferredRestoredThreadHydration: lifecycle.clearDeferredRestoredThreadHydration,
|
||||
scheduleDeferredAppServerWarmup: lifecycle.scheduleDeferredAppServerWarmup,
|
||||
restoreThreadPlaceholder: thread.restorePlaceholder,
|
||||
});
|
||||
};
|
||||
|
||||
return { toolbarPanels, viewStateController };
|
||||
const toolbarPanels = new ToolbarPanelController({
|
||||
stateStore: context.state.stateStore,
|
||||
threadActions: refs.threadActions,
|
||||
scheduleRender: render.schedule,
|
||||
});
|
||||
const applyViewState = (state: unknown) => {
|
||||
applyChatViewState(viewStateHost, state);
|
||||
};
|
||||
|
||||
return { toolbarPanels, applyViewState };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { EventRef, WorkspaceLeaf } from "obsidian";
|
|||
|
||||
import { unmountChatPanelShell } from "../ui/shell";
|
||||
|
||||
export interface ChatViewOpenCloseActionsHost {
|
||||
export interface ChatViewLifecycleHost {
|
||||
setOpened: (opened: boolean) => void;
|
||||
setClosing: (closing: boolean) => void;
|
||||
registerEvent: (eventRef: EventRef) => void;
|
||||
|
|
@ -27,23 +27,7 @@ export interface ChatViewOpenCloseActionsHost {
|
|||
deferRefreshLiveState: () => void;
|
||||
}
|
||||
|
||||
export interface ChatViewOpenCloseActions {
|
||||
open: () => void;
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
export function createChatViewOpenCloseActions(host: ChatViewOpenCloseActionsHost): ChatViewOpenCloseActions {
|
||||
return {
|
||||
open: () => {
|
||||
openChatView(host);
|
||||
},
|
||||
close: () => {
|
||||
closeChatView(host);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function openChatView(host: ChatViewOpenCloseActionsHost): void {
|
||||
export function openChatView(host: ChatViewLifecycleHost): void {
|
||||
host.setOpened(true);
|
||||
host.setClosing(false);
|
||||
host.registerComposerNoteIndexInvalidation((eventRef) => {
|
||||
|
|
@ -59,7 +43,7 @@ function openChatView(host: ChatViewOpenCloseActionsHost): void {
|
|||
host.scheduleDeferredRestoredThreadHydration();
|
||||
}
|
||||
|
||||
function closeChatView(host: ChatViewOpenCloseActionsHost): void {
|
||||
export function closeChatView(host: ChatViewLifecycleHost): void {
|
||||
host.setOpened(false);
|
||||
host.setClosing(true);
|
||||
host.invalidateConnectionWork();
|
||||
|
|
@ -9,23 +9,15 @@ export interface ChatViewStateControllerHost {
|
|||
restoreThreadPlaceholder: (restoredThread: RestoredThreadState) => void;
|
||||
}
|
||||
|
||||
export interface ChatViewStateActions {
|
||||
applyState(state: unknown): void;
|
||||
}
|
||||
export function applyChatViewState(host: ChatViewStateControllerHost, state: unknown): void {
|
||||
const restoredThread = parseRestoredThreadState(state);
|
||||
if (!restoredThread) {
|
||||
host.invalidateResumeWork();
|
||||
host.clearRestoredThreadLifecycle();
|
||||
host.clearDeferredRestoredThreadHydration();
|
||||
host.scheduleDeferredAppServerWarmup();
|
||||
return;
|
||||
}
|
||||
|
||||
export function createChatViewStateActions(host: ChatViewStateControllerHost): ChatViewStateActions {
|
||||
return {
|
||||
applyState(state) {
|
||||
const restoredThread = parseRestoredThreadState(state);
|
||||
if (!restoredThread) {
|
||||
host.invalidateResumeWork();
|
||||
host.clearRestoredThreadLifecycle();
|
||||
host.clearDeferredRestoredThreadHydration();
|
||||
host.scheduleDeferredAppServerWarmup();
|
||||
return;
|
||||
}
|
||||
|
||||
host.restoreThreadPlaceholder(restoredThread);
|
||||
},
|
||||
};
|
||||
host.restoreThreadPlaceholder(restoredThread);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,20 +8,13 @@ export interface AppServerWarmupControllerHost {
|
|||
ensureConnected: () => Promise<void>;
|
||||
}
|
||||
|
||||
export interface AppServerWarmupActions {
|
||||
schedule(): void;
|
||||
}
|
||||
|
||||
export function createAppServerWarmupActions(host: AppServerWarmupControllerHost): AppServerWarmupActions {
|
||||
export function scheduleAppServerWarmup(host: AppServerWarmupControllerHost): void {
|
||||
const shouldWarmup = (): boolean => host.opened() && !host.connected();
|
||||
return {
|
||||
schedule() {
|
||||
if (!shouldWarmup()) return;
|
||||
|
||||
host.deferredTasks.scheduleAppServerWarmup(() => {
|
||||
if (!shouldWarmup() || host.closing()) return;
|
||||
void host.ensureConnected();
|
||||
});
|
||||
},
|
||||
};
|
||||
if (!shouldWarmup()) return;
|
||||
|
||||
host.deferredTasks.scheduleAppServerWarmup(() => {
|
||||
if (!shouldWarmup() || host.closing()) return;
|
||||
void host.ensureConnected();
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,10 +10,12 @@ import type { ServerRequestActions } from "../requests/server-request-actions";
|
|||
import type { ChatThreadGoalActions } from "../threads/thread-goal-actions";
|
||||
import type { ThreadRenameController } from "../threads/thread-rename-controller";
|
||||
import { ChatInboundController } from "../inbound/controller";
|
||||
import type { ChatPanelContext } from "../panel/context";
|
||||
import type { ChatControllerCompositionPorts } from "../panel/controller-ports";
|
||||
|
||||
type ChatServerActionControllerPorts = Pick<ChatControllerCompositionPorts, "plugin" | "runtime" | "state">;
|
||||
|
||||
export function createChatServerActionControllers(
|
||||
context: ChatPanelContext,
|
||||
context: ChatServerActionControllerPorts,
|
||||
refs: {
|
||||
connection: ConnectionManager;
|
||||
goals: ChatThreadGoalActions;
|
||||
|
|
@ -53,8 +55,10 @@ export function createChatServerActionControllers(
|
|||
return { serverThreads, serverMetadata, serverDiagnostics };
|
||||
}
|
||||
|
||||
type ChatInboundControllerPorts = Pick<ChatControllerCompositionPorts, "plugin" | "render" | "state" | "thread">;
|
||||
|
||||
export function createChatInboundController(
|
||||
context: ChatPanelContext,
|
||||
context: ChatInboundControllerPorts,
|
||||
refs: {
|
||||
serverMetadata: ChatServerMetadataActions;
|
||||
serverDiagnostics: ChatServerDiagnosticsActions;
|
||||
|
|
@ -87,8 +91,13 @@ export function createChatInboundController(
|
|||
});
|
||||
}
|
||||
|
||||
type ChatConnectionControllerPorts = Pick<
|
||||
ChatControllerCompositionPorts,
|
||||
"client" | "lifecycle" | "liveState" | "plugin" | "render" | "state" | "status" | "thread"
|
||||
>;
|
||||
|
||||
export function createChatConnectionControllers(
|
||||
context: ChatPanelContext,
|
||||
context: ChatConnectionControllerPorts,
|
||||
refs: {
|
||||
connection: ConnectionManager;
|
||||
serverMetadata: ChatServerMetadataActions;
|
||||
|
|
@ -130,8 +139,13 @@ export function createChatConnectionControllers(
|
|||
};
|
||||
}
|
||||
|
||||
type ChatReconnectControllerGroupPorts = Pick<
|
||||
ChatControllerCompositionPorts,
|
||||
"client" | "lifecycle" | "render" | "state" | "status" | "thread"
|
||||
>;
|
||||
|
||||
export function createChatReconnectControllerGroup(
|
||||
context: ChatPanelContext,
|
||||
context: ChatReconnectControllerGroupPorts,
|
||||
refs: {
|
||||
connection: ConnectionManager;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -9,10 +9,15 @@ import { ThreadResumeController } from "./thread-resume-controller";
|
|||
import { createThreadSelectionActions } from "./thread-selection-controller";
|
||||
import { RestoredThreadController } from "./restored-thread-controller";
|
||||
import type { ToolbarPanelController } from "../panel/toolbar-controller";
|
||||
import type { ChatPanelContext } from "../panel/context";
|
||||
import type { ChatControllerCompositionPorts } from "../panel/controller-ports";
|
||||
|
||||
type ThreadControllerGroupPorts = Pick<
|
||||
ChatControllerCompositionPorts,
|
||||
"client" | "composer" | "lifecycle" | "liveState" | "obsidian" | "plugin" | "render" | "scroll" | "state" | "status" | "thread"
|
||||
>;
|
||||
|
||||
export function createThreadControllerGroup(
|
||||
context: ChatPanelContext,
|
||||
context: ThreadControllerGroupPorts,
|
||||
refs: {
|
||||
connection: ConnectionManager;
|
||||
},
|
||||
|
|
@ -132,8 +137,10 @@ export function createThreadControllerGroup(
|
|||
};
|
||||
}
|
||||
|
||||
type ThreadSelectionControllerGroupPorts = Pick<ChatControllerCompositionPorts, "plugin" | "state" | "status" | "thread">;
|
||||
|
||||
export function createThreadSelectionControllerGroup(
|
||||
context: ChatPanelContext,
|
||||
context: ThreadSelectionControllerGroupPorts,
|
||||
refs: {
|
||||
toolbarPanels: ToolbarPanelController;
|
||||
},
|
||||
|
|
|
|||
|
|
@ -15,10 +15,15 @@ import type { ThreadRenameController } from "../threads/thread-rename-controller
|
|||
import type { ChatInboundController } from "../inbound/controller";
|
||||
import { currentModel } from "../runtime/effective-settings";
|
||||
import { ChatMessageRenderer } from "../ui/message-stream";
|
||||
import type { ChatPanelContext } from "../panel/context";
|
||||
import type { ChatControllerCompositionPorts } from "../panel/controller-ports";
|
||||
|
||||
type ConversationSurfaceControllerGroupPorts = Pick<
|
||||
ChatControllerCompositionPorts,
|
||||
"client" | "lifecycle" | "liveState" | "obsidian" | "plugin" | "render" | "runtime" | "scroll" | "state" | "status" | "thread"
|
||||
>;
|
||||
|
||||
export function createConversationSurfaceControllerGroup(
|
||||
context: ChatPanelContext,
|
||||
context: ConversationSurfaceControllerGroupPorts,
|
||||
refs: {
|
||||
controller: ChatInboundController;
|
||||
serverThreads: ChatServerThreadActions;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ import {
|
|||
type ChatViewRenderScheduleOptions,
|
||||
} from "./panel/lifecycle";
|
||||
import { ChatMessageScrollIntentController } from "./panel/message-scroll-intent-controller";
|
||||
import type { ChatPanelContext } from "./panel/context";
|
||||
import type { ChatControllerCompositionPorts } from "./panel/controller-ports";
|
||||
import { createChatViewControllers, type ChatViewControllers } from "./panel/composition";
|
||||
import { activeComposerThreadName, composerMetaViewModel, composerPlaceholder, renderComposerSlot } from "./panel/slots/composer";
|
||||
import { renderGoalSlot } from "./panel/slots/goal";
|
||||
|
|
@ -65,7 +65,7 @@ export class CodexChatView extends ItemView {
|
|||
this.slotPorts = this.createSlotRendererPorts();
|
||||
}
|
||||
|
||||
private createControllerPorts(): ChatPanelContext {
|
||||
private createControllerPorts(): ChatControllerCompositionPorts {
|
||||
// Some callbacks are late-bound to controllers assigned immediately after this object is created.
|
||||
// Controller constructors must not invoke those callbacks synchronously during composition.
|
||||
return {
|
||||
|
|
@ -398,7 +398,7 @@ export class CodexChatView extends ItemView {
|
|||
|
||||
override async setState(state: unknown, result: ViewStateResult): Promise<void> {
|
||||
await super.setState(state, result);
|
||||
this.controllers.render.viewState.applyState(state);
|
||||
this.controllers.render.applyViewState(state);
|
||||
}
|
||||
|
||||
refreshSettings(): void {
|
||||
|
|
@ -464,11 +464,11 @@ export class CodexChatView extends ItemView {
|
|||
}
|
||||
|
||||
override async onOpen(): Promise<void> {
|
||||
this.controllers.render.openClose.open();
|
||||
this.controllers.render.openView();
|
||||
}
|
||||
|
||||
override async onClose(): Promise<void> {
|
||||
this.controllers.render.openClose.close();
|
||||
this.controllers.render.closeView();
|
||||
}
|
||||
|
||||
setComposerText(text: string): void {
|
||||
|
|
@ -549,7 +549,7 @@ export class CodexChatView extends ItemView {
|
|||
}
|
||||
|
||||
private scheduleDeferredAppServerWarmup(): void {
|
||||
this.controllers.connection.warmup.schedule();
|
||||
this.controllers.connection.scheduleWarmup();
|
||||
}
|
||||
|
||||
private activeThreadTitle(): string | null {
|
||||
|
|
|
|||
|
|
@ -3,16 +3,16 @@
|
|||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { EventRef } from "obsidian";
|
||||
|
||||
import { createChatViewOpenCloseActions, type ChatViewOpenCloseActionsHost } from "../../../../src/features/chat/panel/open-close-actions";
|
||||
import { closeChatView, openChatView, type ChatViewLifecycleHost } from "../../../../src/features/chat/panel/view-lifecycle";
|
||||
import { unmountChatPanelShell } from "../../../../src/features/chat/ui/shell";
|
||||
|
||||
vi.mock("../../../../src/features/chat/ui/shell", () => ({
|
||||
unmountChatPanelShell: vi.fn(),
|
||||
}));
|
||||
|
||||
function createHost(overrides: Partial<ChatViewOpenCloseActionsHost> = {}) {
|
||||
function createHost(overrides: Partial<ChatViewLifecycleHost> = {}) {
|
||||
const root = document.createElement("div");
|
||||
const host: ChatViewOpenCloseActionsHost = {
|
||||
const host: ChatViewLifecycleHost = {
|
||||
setOpened: vi.fn(),
|
||||
setClosing: vi.fn(),
|
||||
registerEvent: vi.fn(),
|
||||
|
|
@ -39,18 +39,18 @@ function createHost(overrides: Partial<ChatViewOpenCloseActionsHost> = {}) {
|
|||
deferRefreshLiveState: vi.fn(),
|
||||
...overrides,
|
||||
};
|
||||
return { controller: createChatViewOpenCloseActions(host), host, root };
|
||||
return { host, root };
|
||||
}
|
||||
|
||||
describe("createChatViewOpenCloseActions", () => {
|
||||
describe("chat view lifecycle", () => {
|
||||
beforeEach(() => {
|
||||
vi.mocked(unmountChatPanelShell).mockClear();
|
||||
});
|
||||
|
||||
it("registers open events and schedules startup work", () => {
|
||||
const { controller, host } = createHost();
|
||||
const { host } = createHost();
|
||||
|
||||
controller.open();
|
||||
openChatView(host);
|
||||
|
||||
expect(host.setOpened).toHaveBeenCalledWith(true);
|
||||
expect(host.setClosing).toHaveBeenCalledWith(false);
|
||||
|
|
@ -64,9 +64,9 @@ describe("createChatViewOpenCloseActions", () => {
|
|||
});
|
||||
|
||||
it("disposes mounted resources and refreshes live state on close", () => {
|
||||
const { controller, host, root } = createHost();
|
||||
const { host, root } = createHost();
|
||||
|
||||
controller.close();
|
||||
closeChatView(host);
|
||||
|
||||
expect(host.setOpened).toHaveBeenCalledWith(false);
|
||||
expect(host.setClosing).toHaveBeenCalledWith(true);
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { createChatViewStateActions, type ChatViewStateControllerHost } from "../../../../src/features/chat/panel/view-state-controller";
|
||||
import { applyChatViewState, type ChatViewStateControllerHost } from "../../../../src/features/chat/panel/view-state-controller";
|
||||
|
||||
function createController(overrides: Partial<ChatViewStateControllerHost> = {}) {
|
||||
const host: ChatViewStateControllerHost = {
|
||||
|
|
@ -11,14 +11,14 @@ function createController(overrides: Partial<ChatViewStateControllerHost> = {})
|
|||
restoreThreadPlaceholder: vi.fn(),
|
||||
...overrides,
|
||||
};
|
||||
return { controller: createChatViewStateActions(host), host };
|
||||
return { host };
|
||||
}
|
||||
|
||||
describe("ChatViewStateController", () => {
|
||||
it("restores a thread placeholder from persisted view state", () => {
|
||||
const { controller, host } = createController();
|
||||
const { host } = createController();
|
||||
|
||||
controller.applyState({ threadId: "thread", threadTitle: "Title" });
|
||||
applyChatViewState(host, { threadId: "thread", threadTitle: "Title" });
|
||||
|
||||
expect(host.restoreThreadPlaceholder).toHaveBeenCalledWith({
|
||||
threadId: "thread",
|
||||
|
|
@ -30,9 +30,9 @@ describe("ChatViewStateController", () => {
|
|||
});
|
||||
|
||||
it("clears restored lifecycle and schedules warmup when no thread is restored", () => {
|
||||
const { controller, host } = createController();
|
||||
const { host } = createController();
|
||||
|
||||
controller.applyState({ version: 1 });
|
||||
applyChatViewState(host, { version: 1 });
|
||||
|
||||
expect(host.invalidateResumeWork).toHaveBeenCalledOnce();
|
||||
expect(host.clearRestoredThreadLifecycle).toHaveBeenCalledOnce();
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { createAppServerWarmupActions } from "../../../../src/features/chat/session/app-server-warmup-controller";
|
||||
import { scheduleAppServerWarmup } from "../../../../src/features/chat/session/app-server-warmup-controller";
|
||||
import { ChatViewDeferredTasks } from "../../../../src/features/chat/panel/lifecycle";
|
||||
|
||||
function createController({
|
||||
|
|
@ -11,14 +11,14 @@ function createController({
|
|||
connected = false,
|
||||
}: { opened?: boolean; closing?: boolean; connected?: boolean } = {}) {
|
||||
const ensureConnected = vi.fn().mockResolvedValue(undefined);
|
||||
const controller = createAppServerWarmupActions({
|
||||
const host = {
|
||||
deferredTasks: new ChatViewDeferredTasks(() => window),
|
||||
opened: () => opened,
|
||||
closing: () => closing,
|
||||
connected: () => connected,
|
||||
ensureConnected,
|
||||
});
|
||||
return { controller, ensureConnected };
|
||||
};
|
||||
return { host, ensureConnected };
|
||||
}
|
||||
|
||||
describe("AppServerWarmupController", () => {
|
||||
|
|
@ -27,9 +27,9 @@ describe("AppServerWarmupController", () => {
|
|||
});
|
||||
|
||||
it("connects on the next tick when the opened view is disconnected", async () => {
|
||||
const { controller, ensureConnected } = createController();
|
||||
const { host, ensureConnected } = createController();
|
||||
|
||||
controller.schedule();
|
||||
scheduleAppServerWarmup(host);
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
|
||||
expect(ensureConnected).toHaveBeenCalledOnce();
|
||||
|
|
@ -39,8 +39,8 @@ describe("AppServerWarmupController", () => {
|
|||
const closed = createController({ opened: false });
|
||||
const connected = createController({ connected: true });
|
||||
|
||||
closed.controller.schedule();
|
||||
connected.controller.schedule();
|
||||
scheduleAppServerWarmup(closed.host);
|
||||
scheduleAppServerWarmup(connected.host);
|
||||
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
|
||||
|
|
@ -49,9 +49,9 @@ describe("AppServerWarmupController", () => {
|
|||
});
|
||||
|
||||
it("skips a scheduled warmup if the view is closing", async () => {
|
||||
const { controller, ensureConnected } = createController({ closing: true });
|
||||
const { host, ensureConnected } = createController({ closing: true });
|
||||
|
||||
controller.schedule();
|
||||
scheduleAppServerWarmup(host);
|
||||
await vi.advanceTimersByTimeAsync(0);
|
||||
|
||||
expect(ensureConnected).not.toHaveBeenCalled();
|
||||
|
|
|
|||
Loading…
Reference in a new issue