Reorganize Codex Panel source layout

This commit is contained in:
murashit 2026-05-23 09:38:50 +09:00
parent 24f25cd3e6
commit 9cb71e113b
100 changed files with 334 additions and 329 deletions

View file

@ -17,15 +17,14 @@ The source tree is organized by responsibility rather than by the original singl
- `src/main.ts` registers Obsidian views, commands, settings, and lifecycle hooks.
- `src/app-server/` owns app-server transport, connection lifecycle, compatibility helpers, and RPC facades.
- `src/panel/` owns Codex panel orchestration, Obsidian `ItemView` classes, request/session controllers, thread actions, and panel-specific state.
- `src/features/chat/` owns the main Codex chat surface: Obsidian `ItemView` classes, panel orchestration, request/session controllers, composer behavior, display models, chat-only UI renderers, approvals, user input, thread actions, and panel-specific state.
- `src/features/selection-rewrite/` owns the Markdown editor selection rewrite command, popover, prompt/output handling, and short-lived rewrite session runner.
- `src/runtime/` owns Codex runtime configuration projection, model metadata, collaboration mode, and compact labels used by views.
- `src/display/` converts app-server turn items and streaming deltas into display model objects.
- `src/ui/` contains pure DOM renderers for reusable panel UI pieces.
- `src/composer/` contains composer input behavior, slash command definitions, suggestions, and Obsidian wikilink resolution.
- `src/shared/` contains feature-neutral helpers, including reusable DOM pieces and unified diff display helpers shared by chat and selection rewrite.
- `src/settings/` contains Obsidian settings models, settings-tab rendering, and app-server-backed dynamic settings data.
- `src/threads/` contains thread title and list presentation helpers shared outside the panel.
- `src/domain/threads/` contains thread title, reference, and archive export helpers shared outside the chat feature.
Keep new code near the state or API it owns. Shared domain helpers should not be placed under `src/panel/` unless they are panel-specific orchestration.
Keep new code near the state or API it owns. Feature code should not import from another feature directly; move shared behavior to `src/shared/`, `src/domain/`, `src/app-server/`, or `src/runtime/` when more than one feature needs it.
## App-Server Bindings

View file

@ -1,8 +1,8 @@
import type { Thread } from "../generated/app-server/v2/Thread";
import type { ThreadItem } from "../generated/app-server/v2/ThreadItem";
import type { Turn } from "../generated/app-server/v2/Turn";
import type { CodexPanelSettings } from "../settings/model";
import { inputToText, shortThreadId } from "../utils";
import type { Thread } from "../../generated/app-server/v2/Thread";
import type { ThreadItem } from "../../generated/app-server/v2/ThreadItem";
import type { Turn } from "../../generated/app-server/v2/Turn";
import type { CodexPanelSettings } from "../../settings/model";
import { inputToText, shortThreadId } from "../../utils";
import { getThreadTitle } from "./model";
import { referencedThreadDisplayFromPrompt } from "./reference";

View file

@ -1,5 +1,5 @@
import type { Thread } from "../generated/app-server/v2/Thread";
import { shortThreadId } from "../utils";
import type { Thread } from "../../generated/app-server/v2/Thread";
import { shortThreadId } from "../../utils";
const MAX_THREAD_DISPLAY_TITLE_LENGTH = 96;
const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

View file

@ -1,7 +1,7 @@
import type { Thread } from "../generated/app-server/v2/Thread";
import type { ThreadItem } from "../generated/app-server/v2/ThreadItem";
import type { Turn } from "../generated/app-server/v2/Turn";
import { inputToText, shortThreadId } from "../utils";
import type { Thread } from "../../generated/app-server/v2/Thread";
import type { ThreadItem } from "../../generated/app-server/v2/ThreadItem";
import type { Turn } from "../../generated/app-server/v2/Turn";
import { inputToText, shortThreadId } from "../../utils";
import { getThreadTitle } from "./model";
export const REFERENCED_THREAD_TURN_LIMIT = 20;

View file

@ -1,10 +1,10 @@
import type { RequestId } from "../generated/app-server/RequestId";
import type { ServerRequest } from "../generated/app-server/ServerRequest";
import type { CommandExecutionApprovalDecision } from "../generated/app-server/v2/CommandExecutionApprovalDecision";
import type { CommandExecutionRequestApprovalResponse } from "../generated/app-server/v2/CommandExecutionRequestApprovalResponse";
import type { FileChangeRequestApprovalResponse } from "../generated/app-server/v2/FileChangeRequestApprovalResponse";
import type { GrantedPermissionProfile } from "../generated/app-server/v2/GrantedPermissionProfile";
import type { PermissionsRequestApprovalResponse } from "../generated/app-server/v2/PermissionsRequestApprovalResponse";
import type { RequestId } from "../../../generated/app-server/RequestId";
import type { ServerRequest } from "../../../generated/app-server/ServerRequest";
import type { CommandExecutionApprovalDecision } from "../../../generated/app-server/v2/CommandExecutionApprovalDecision";
import type { CommandExecutionRequestApprovalResponse } from "../../../generated/app-server/v2/CommandExecutionRequestApprovalResponse";
import type { FileChangeRequestApprovalResponse } from "../../../generated/app-server/v2/FileChangeRequestApprovalResponse";
import type { GrantedPermissionProfile } from "../../../generated/app-server/v2/GrantedPermissionProfile";
import type { PermissionsRequestApprovalResponse } from "../../../generated/app-server/v2/PermissionsRequestApprovalResponse";
import { addOptional, nonEmptyString, permissionRows } from "./permission-details";
export type ApprovalAction = "accept" | "accept-session" | "decline" | "cancel" | CommandApprovalDecisionAction;

View file

@ -1,6 +1,6 @@
import { jsonPreview } from "../utils";
import type { FileSystemPath } from "../generated/app-server/v2/FileSystemPath";
import type { RequestPermissionProfile } from "../generated/app-server/v2/RequestPermissionProfile";
import { jsonPreview } from "../../../utils";
import type { FileSystemPath } from "../../../generated/app-server/v2/FileSystemPath";
import type { RequestPermissionProfile } from "../../../generated/app-server/v2/RequestPermissionProfile";
export interface DetailRow {
key: string;

View file

@ -1,7 +1,7 @@
import type { App, EventRef } from "obsidian";
import { isComposerSendKey } from "../composer/keys";
import { noteCandidates as appNoteCandidates, resolveWikiLinkMention as resolveAppWikiLinkMention } from "../composer/obsidian-context";
import { isComposerSendKey } from "./composer/keys";
import { noteCandidates as appNoteCandidates, resolveWikiLinkMention as resolveAppWikiLinkMention } from "./composer/obsidian-context";
import {
activeComposerSuggestions,
applyComposerSuggestionInsertion,
@ -10,11 +10,11 @@ import {
nextComposerSuggestionIndex,
type ComposerSuggestion,
type NoteCandidate,
} from "../composer/suggestions";
import { userInputWithWikiLinkMentionsAndSkills } from "../composer/wikilink-context";
import type { UserInput } from "../generated/app-server/v2/UserInput";
import type { SendShortcut } from "../settings/model";
import { renderComposerShell, renderComposerSuggestions, syncComposerControls, syncComposerHeight } from "../ui/composer";
} from "./composer/suggestions";
import { userInputWithWikiLinkMentionsAndSkills } from "./composer/wikilink-context";
import type { UserInput } from "../../generated/app-server/v2/UserInput";
import type { SendShortcut } from "../../settings/model";
import { renderComposerShell, renderComposerSuggestions, syncComposerControls, syncComposerHeight } from "./ui/composer";
import type { PanelState } from "./state";
export interface PanelComposerControllerOptions {

View file

@ -1,4 +1,4 @@
import type { SendShortcut } from "../settings/model";
import type { SendShortcut } from "../../../settings/model";
export interface ComposerSendKeyEvent {
key: string;

View file

@ -1,16 +1,16 @@
import type { Model } from "../generated/app-server/v2/Model";
import type { SkillMetadata } from "../generated/app-server/v2/SkillMetadata";
import type { Thread } from "../generated/app-server/v2/Thread";
import type { Model } from "../../../generated/app-server/v2/Model";
import type { SkillMetadata } from "../../../generated/app-server/v2/SkillMetadata";
import type { Thread } from "../../../generated/app-server/v2/Thread";
import {
findModelByIdOrName,
isReasoningEffort,
REASONING_EFFORTS,
sortedAvailableModels,
supportedEffortsForModel,
} from "../runtime/model";
} from "../../../runtime/model";
import { SLASH_COMMANDS, type SlashCommandName } from "./slash-commands";
import { getThreadTitle } from "../threads/model";
import { shortThreadId } from "../utils";
import { getThreadTitle } from "../../../domain/threads/model";
import { shortThreadId } from "../../../utils";
export interface ComposerSuggestion {
display: string;

View file

@ -1,5 +1,5 @@
import type { SkillMetadata } from "../generated/app-server/v2/SkillMetadata";
import type { UserInput } from "../generated/app-server/v2/UserInput";
import type { SkillMetadata } from "../../../generated/app-server/v2/SkillMetadata";
import type { UserInput } from "../../../generated/app-server/v2/UserInput";
export interface ParsedWikiLink {
raw: string;

View file

@ -1,7 +1,7 @@
import { approvalResponse, type ApprovalAction, type PendingApproval } from "../approvals/model";
import { reportedServiceTier } from "../app-server/service-tier";
import { planProgressDisplayItem } from "../display/plan";
import { createAutoReviewResultItem, createReviewResultItem } from "../display/review";
import { approvalResponse, type ApprovalAction, type PendingApproval } from "./approvals/model";
import { reportedServiceTier } from "../../app-server/service-tier";
import { planProgressDisplayItem } from "./display/plan";
import { createAutoReviewResultItem, createReviewResultItem } from "./display/review";
import {
appendAssistantDelta,
appendItemOutput,
@ -10,24 +10,24 @@ import {
appendToolOutput,
completeReasoningItems,
upsertDisplayItem,
} from "../display/stream-updates";
import { createStructuredSystemItem, createSystemItem } from "../display/system";
} from "./display/stream-updates";
import { createStructuredSystemItem, createSystemItem } from "./display/system";
import {
displayItemFromThreadItem,
displayItemsFromTurns,
normalizeFileChanges,
shouldSuppressLifecycleItem,
} from "../display/thread-items";
import type { DisplayDetailSection, DisplayItem, DisplayKind, MessageDisplayItem } from "../display/types";
import type { RequestId } from "../generated/app-server/RequestId";
import type { ServerNotification } from "../generated/app-server/ServerNotification";
import type { ServerRequest } from "../generated/app-server/ServerRequest";
import type { FileUpdateChange } from "../generated/app-server/v2/FileUpdateChange";
import type { ThreadItem } from "../generated/app-server/v2/ThreadItem";
import type { Turn } from "../generated/app-server/v2/Turn";
} from "./display/thread-items";
import type { DisplayDetailSection, DisplayItem, DisplayKind, MessageDisplayItem } from "./display/types";
import type { RequestId } from "../../generated/app-server/RequestId";
import type { ServerNotification } from "../../generated/app-server/ServerNotification";
import type { ServerRequest } from "../../generated/app-server/ServerRequest";
import type { FileUpdateChange } from "../../generated/app-server/v2/FileUpdateChange";
import type { ThreadItem } from "../../generated/app-server/v2/ThreadItem";
import type { Turn } from "../../generated/app-server/v2/Turn";
import { clearActiveThreadState, type PanelState } from "./state";
import { userInputResponse, type PendingUserInput } from "../user-input/model";
import { jsonPreview } from "../utils";
import { userInputResponse, type PendingUserInput } from "./user-input/model";
import { jsonPreview } from "../../utils";
import { classifyAppServerLog } from "./app-server-logs";
import { attachHookRunsToTurn, hookRunDisplayItem } from "./hook-display";
import { routeServerNotification, routeServerRequest } from "./inbound-routing";

View file

@ -1,7 +1,7 @@
import { CAPABILITY_PROBE_METHODS, appServerIdentity, appServerPlatform } from "../app-server/compatibility";
import { CLIENT_VERSION } from "../constants";
import type { InitializeResponse } from "../generated/app-server/InitializeResponse";
import type { AppServerDiagnostics, CapabilityProbeResult, McpServerDiagnostic } from "../app-server/compatibility";
import { CAPABILITY_PROBE_METHODS, appServerIdentity, appServerPlatform } from "../../app-server/compatibility";
import { CLIENT_VERSION } from "../../constants";
import type { InitializeResponse } from "../../generated/app-server/InitializeResponse";
import type { AppServerDiagnostics, CapabilityProbeResult, McpServerDiagnostic } from "../../app-server/compatibility";
export interface DiagnosticRow {
label: string;

View file

@ -1,6 +1,6 @@
import type { ThreadItem } from "../generated/app-server/v2/ThreadItem";
import type { ThreadItem } from "../../../generated/app-server/v2/ThreadItem";
import type { AgentRunSummary, AgentRunSummaryAgent, AgentStateDisplay, DisplayItem, ExecutionState } from "./types";
import { definedProp } from "../utils";
import { definedProp } from "../../../utils";
import { agentActivitySummaryLabel, agentMessagePreview } from "./labels";
import { classifyExecutionState } from "./state";

View file

@ -1,5 +1,5 @@
import type { TurnPlanStep } from "../generated/app-server/v2/TurnPlanStep";
import { truncate } from "../utils";
import type { TurnPlanStep } from "../../../generated/app-server/v2/TurnPlanStep";
import { truncate } from "../../../utils";
import type { AgentRunSummary } from "./types";
export function taskStatusMarker(status: TurnPlanStep["status"]): string {

View file

@ -1,5 +1,5 @@
import type { DisplayItem } from "./types";
import type { TurnPlanStep } from "../generated/app-server/v2/TurnPlanStep";
import type { TurnPlanStep } from "../../../generated/app-server/v2/TurnPlanStep";
import { taskStatusMarker } from "./labels";
import { classifyExecutionState } from "./state";

View file

@ -1,7 +1,7 @@
import { permissionRows } from "../approvals/permission-details";
import type { GuardianApprovalReviewAction } from "../generated/app-server/v2/GuardianApprovalReviewAction";
import type { ItemGuardianApprovalReviewCompletedNotification } from "../generated/app-server/v2/ItemGuardianApprovalReviewCompletedNotification";
import type { ItemGuardianApprovalReviewStartedNotification } from "../generated/app-server/v2/ItemGuardianApprovalReviewStartedNotification";
import type { GuardianApprovalReviewAction } from "../../../generated/app-server/v2/GuardianApprovalReviewAction";
import type { ItemGuardianApprovalReviewCompletedNotification } from "../../../generated/app-server/v2/ItemGuardianApprovalReviewCompletedNotification";
import type { ItemGuardianApprovalReviewStartedNotification } from "../../../generated/app-server/v2/ItemGuardianApprovalReviewStartedNotification";
import type { DisplayItem } from "./types";
import { pathsRelativeToRoot } from "./paths";
import { classifyExecutionState } from "./state";

View file

@ -1,5 +1,5 @@
import type { DisplayItem, ExecutionState } from "./types";
import { definedProp } from "../utils";
import { definedProp } from "../../../utils";
export function executionState(item: DisplayItem): ExecutionState {
if (item.state) return item.state;

View file

@ -1,10 +1,10 @@
import type { DisplayDetailSection, DisplayFileChange, DisplayFileMention, DisplayItem } from "./types";
import type { FileUpdateChange } from "../generated/app-server/v2/FileUpdateChange";
import type { ThreadItem } from "../generated/app-server/v2/ThreadItem";
import type { Turn } from "../generated/app-server/v2/Turn";
import type { UserInput } from "../generated/app-server/v2/UserInput";
import { definedProp, inputToText, truncate } from "../utils";
import { referencedThreadDisplayFromPrompt } from "../threads/reference";
import type { FileUpdateChange } from "../../../generated/app-server/v2/FileUpdateChange";
import type { ThreadItem } from "../../../generated/app-server/v2/ThreadItem";
import type { Turn } from "../../../generated/app-server/v2/Turn";
import type { UserInput } from "../../../generated/app-server/v2/UserInput";
import { definedProp, inputToText, truncate } from "../../../utils";
import { referencedThreadDisplayFromPrompt } from "../../../domain/threads/reference";
import { agentDisplayItem } from "./agent";
import { pathRelativeToRoot } from "./paths";
import { normalizeProposedPlanMarkdown } from "./plan";

View file

@ -1,5 +1,5 @@
import type { DisplayDetailMetaRow, DisplayDetailSection } from "./types";
import { jsonPreview, truncate } from "../utils";
import { jsonPreview, truncate } from "../../../utils";
const TOOL_SUMMARY_LIMIT = 140;

View file

@ -1,6 +1,6 @@
import { pathRelativeToRoot } from "./paths";
import { executionState } from "./state";
import { definedProp } from "../utils";
import { definedProp } from "../../../utils";
import type {
ApprovalResultDisplayItem,
CommandDisplayItem,

View file

@ -1,4 +1,4 @@
import type { ReferencedThreadDisplay } from "../threads/reference";
import type { ReferencedThreadDisplay } from "../../../domain/threads/reference";
export type DisplayKind =
| "message"

View file

@ -1,6 +1,6 @@
import type { ServerNotification } from "../generated/app-server/ServerNotification";
import type { DisplayItem } from "../display/types";
import { definedProp } from "../utils";
import type { ServerNotification } from "../../generated/app-server/ServerNotification";
import type { DisplayItem } from "./display/types";
import { definedProp } from "../../utils";
export function hookRunDisplayItem(
run: Extract<ServerNotification, { method: "hook/started" }>["params"]["run"],

View file

@ -1,7 +1,7 @@
import { toPendingApproval, type PendingApproval } from "../approvals/model";
import type { ServerNotification } from "../generated/app-server/ServerNotification";
import type { ServerRequest } from "../generated/app-server/ServerRequest";
import { toPendingUserInput, type PendingUserInput } from "../user-input/model";
import { toPendingApproval, type PendingApproval } from "./approvals/model";
import type { ServerNotification } from "../../generated/app-server/ServerNotification";
import type { ServerRequest } from "../../generated/app-server/ServerRequest";
import { toPendingUserInput, type PendingUserInput } from "./user-input/model";
export interface ActiveRouteScope {
activeThreadId: string | null;

View file

@ -1,5 +1,5 @@
import type { McpServerStatus } from "../generated/app-server/v2/McpServerStatus";
import type { McpServerDiagnostic } from "../app-server/compatibility";
import type { McpServerStatus } from "../../generated/app-server/v2/McpServerStatus";
import type { McpServerDiagnostic } from "../../app-server/compatibility";
export function mcpStatusLines(servers: McpServerStatus[], diagnostics: McpServerDiagnostic[] = []): string[] {
if (servers.length === 0 && diagnostics.length === 0) {

View file

@ -1,11 +1,11 @@
import { MarkdownRenderer, type App, type Component } from "obsidian";
import type { DisplayItem } from "../display/types";
import { copyTextWithNotice } from "../ui/clipboard";
import { renderTextWithWikiLinks as renderInlineWikiLinks } from "../ui/dom";
import { messageRenderBlocks, notifyMessageContentRendered, syncMessageRenderBlocks } from "../ui/message-stream";
import { bottomScrollTop, captureScrollAnchor, isNearScrollBottom, restoreScrollAnchor } from "../ui/scroll";
import type { TurnDiffViewState } from "../ui/turn-diff";
import type { DisplayItem } from "./display/types";
import { copyTextWithNotice } from "../../shared/ui/clipboard";
import { renderTextWithWikiLinks as renderInlineWikiLinks } from "../../shared/ui/dom";
import { messageRenderBlocks, notifyMessageContentRendered, syncMessageRenderBlocks } from "./ui/message-stream";
import { bottomScrollTop, captureScrollAnchor, isNearScrollBottom, restoreScrollAnchor } from "./ui/scroll";
import type { TurnDiffViewState } from "./ui/turn-diff";
import { markdownFileLinkTarget } from "./markdown-file-links";
import { isRollbackCandidateItem, rollbackCandidateFromItems } from "./rollback";
import type { PanelState } from "./state";

View file

@ -1,4 +1,4 @@
import type { RequestId } from "../generated/app-server/RequestId";
import type { RequestId } from "../../generated/app-server/RequestId";
import {
approvalActionKind,
approvalDetails,
@ -6,10 +6,10 @@ import {
approvalTitle,
type ApprovalAction,
type PendingApproval,
} from "../approvals/model";
import type { DisplayDetailSection, DisplayItem } from "../display/types";
import type { PendingUserInput } from "../user-input/model";
import { definedProp } from "../utils";
} from "./approvals/model";
import type { DisplayDetailSection, DisplayItem } from "./display/types";
import type { PendingUserInput } from "./user-input/model";
import { definedProp } from "../../utils";
export function userInputDraftKey(requestId: RequestId, questionId: string): string {
return `${String(requestId)}:${questionId}`;

View file

@ -1,4 +1,4 @@
import type { DisplayItem, MessageDisplayItem } from "../display/types";
import type { DisplayItem, MessageDisplayItem } from "./display/types";
export interface RollbackCandidate {
turnId: string;

View file

@ -1,13 +1,13 @@
import type { AppServerClient } from "../app-server/client";
import type { AppServerClient } from "../../app-server/client";
import {
capabilityProbeError,
capabilityProbeOk,
upsertMcpServerDiagnostic,
type CapabilityProbeMethod,
} from "../app-server/compatibility";
import { reportedServiceTier } from "../app-server/service-tier";
import type { McpServerStatus } from "../generated/app-server/v2/McpServerStatus";
import { requestedOrConfiguredServiceTier, type RuntimeSnapshot } from "../runtime/state";
} from "../../app-server/compatibility";
import { reportedServiceTier } from "../../app-server/service-tier";
import type { McpServerStatus } from "../../generated/app-server/v2/McpServerStatus";
import { requestedOrConfiguredServiceTier, type RuntimeSnapshot } from "../../runtime/state";
import type { PanelState } from "./state";
export interface PanelSessionControllerHost {

View file

@ -1,16 +1,16 @@
import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort";
import type { Thread } from "../generated/app-server/v2/Thread";
import type { UserInput } from "../generated/app-server/v2/UserInput";
import { getThreadTitle } from "../threads/model";
import type { ReferencedThreadDisplay } from "../threads/reference";
import { slashCommandHelpRows, type SlashCommandName } from "../composer/slash-commands";
import type { DisplayDetailSection, DisplayDetailMetaRow } from "../display/types";
import type { ReasoningEffort } from "../../generated/app-server/ReasoningEffort";
import type { Thread } from "../../generated/app-server/v2/Thread";
import type { UserInput } from "../../generated/app-server/v2/UserInput";
import { getThreadTitle } from "../../domain/threads/model";
import type { ReferencedThreadDisplay } from "../../domain/threads/reference";
import { slashCommandHelpRows, type SlashCommandName } from "./composer/slash-commands";
import type { DisplayDetailSection, DisplayDetailMetaRow } from "./display/types";
import {
modelOverrideMessage,
parseModelOverride,
parseReasoningEffortOverride,
reasoningEffortOverrideMessage,
} from "../runtime/settings";
} from "../../runtime/settings";
export interface SlashCommandExecutionContext {
activeThreadId: string | null;

View file

@ -1,21 +1,21 @@
import type { InitializeResponse } from "../generated/app-server/InitializeResponse";
import type { ModeKind } from "../generated/app-server/ModeKind";
import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort";
import type { ApprovalsReviewer } from "../generated/app-server/v2/ApprovalsReviewer";
import type { ConfigReadResponse } from "../generated/app-server/v2/ConfigReadResponse";
import type { Model } from "../generated/app-server/v2/Model";
import type { RateLimitSnapshot } from "../generated/app-server/v2/RateLimitSnapshot";
import type { SkillMetadata } from "../generated/app-server/v2/SkillMetadata";
import type { Thread } from "../generated/app-server/v2/Thread";
import type { ThreadTokenUsage } from "../generated/app-server/v2/ThreadTokenUsage";
import type { AppServerDiagnostics } from "../app-server/compatibility";
import { createAppServerDiagnostics } from "../app-server/compatibility";
import type { PendingApproval } from "../approvals/model";
import type { ComposerSuggestion } from "../composer/suggestions";
import type { DisplayItem } from "../display/types";
import type { PendingUserInput } from "../user-input/model";
import type { ReportedServiceTier, ServiceTier } from "../app-server/service-tier";
import { defaultRuntimeOverride, type RuntimeOverride } from "../runtime/state";
import type { InitializeResponse } from "../../generated/app-server/InitializeResponse";
import type { ModeKind } from "../../generated/app-server/ModeKind";
import type { ReasoningEffort } from "../../generated/app-server/ReasoningEffort";
import type { ApprovalsReviewer } from "../../generated/app-server/v2/ApprovalsReviewer";
import type { ConfigReadResponse } from "../../generated/app-server/v2/ConfigReadResponse";
import type { Model } from "../../generated/app-server/v2/Model";
import type { RateLimitSnapshot } from "../../generated/app-server/v2/RateLimitSnapshot";
import type { SkillMetadata } from "../../generated/app-server/v2/SkillMetadata";
import type { Thread } from "../../generated/app-server/v2/Thread";
import type { ThreadTokenUsage } from "../../generated/app-server/v2/ThreadTokenUsage";
import type { AppServerDiagnostics } from "../../app-server/compatibility";
import { createAppServerDiagnostics } from "../../app-server/compatibility";
import type { PendingApproval } from "./approvals/model";
import type { ComposerSuggestion } from "./composer/suggestions";
import type { DisplayItem } from "./display/types";
import type { PendingUserInput } from "./user-input/model";
import type { ReportedServiceTier, ServiceTier } from "../../app-server/service-tier";
import { defaultRuntimeOverride, type RuntimeOverride } from "../../runtime/state";
export interface PendingTurnStart {
anchorItemId: string;

View file

@ -1,4 +1,4 @@
import type { RateLimitSummary } from "../runtime/view";
import type { RateLimitSummary } from "../../runtime/view";
export function statusValue(value: unknown, fallback: string): string {
if (typeof value === "string") return value;

View file

@ -1,5 +1,5 @@
import type { AppServerClient } from "../app-server/client";
import { displayItemsFromTurns } from "../display/thread-items";
import type { AppServerClient } from "../../app-server/client";
import { displayItemsFromTurns } from "./display/thread-items";
import type { PanelState } from "./state";
export interface ThreadHistoryLoaderHost {

View file

@ -1,14 +1,14 @@
import { AppServerClient } from "../app-server/client";
import type { DisplayItem } from "../display/types";
import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort";
import type { ServerNotification } from "../generated/app-server/ServerNotification";
import type { JsonValue } from "../generated/app-server/serde_json/JsonValue";
import type { Model } from "../generated/app-server/v2/Model";
import type { SortDirection } from "../generated/app-server/v2/SortDirection";
import type { ThreadItem } from "../generated/app-server/v2/ThreadItem";
import type { Turn } from "../generated/app-server/v2/Turn";
import { inputToText, truncate } from "../utils";
import { runtimeOverride, validatedRuntimeOverride } from "../runtime/model";
import { AppServerClient } from "../../app-server/client";
import type { DisplayItem } from "./display/types";
import type { ReasoningEffort } from "../../generated/app-server/ReasoningEffort";
import type { ServerNotification } from "../../generated/app-server/ServerNotification";
import type { JsonValue } from "../../generated/app-server/serde_json/JsonValue";
import type { Model } from "../../generated/app-server/v2/Model";
import type { SortDirection } from "../../generated/app-server/v2/SortDirection";
import type { ThreadItem } from "../../generated/app-server/v2/ThreadItem";
import type { Turn } from "../../generated/app-server/v2/Turn";
import { inputToText, truncate } from "../../utils";
import { runtimeOverride, validatedRuntimeOverride } from "../../runtime/model";
const NAMING_SERVICE_NAME = "codex-panel-naming";
const NAMING_TIMEOUT_MS = 60_000;

View file

@ -1,9 +1,9 @@
import type { AppServerClient } from "../app-server/client";
import type { Thread } from "../generated/app-server/v2/Thread";
import type { Turn } from "../generated/app-server/v2/Turn";
import type { CodexPanelSettings } from "../settings/model";
import type { AppServerClient } from "../../app-server/client";
import type { Thread } from "../../generated/app-server/v2/Thread";
import type { Turn } from "../../generated/app-server/v2/Turn";
import type { CodexPanelSettings } from "../../settings/model";
import type { PanelState } from "./state";
import { getThreadTitle } from "../threads/model";
import { getThreadTitle } from "../../domain/threads/model";
import {
findThreadNamingContext,
generateThreadTitleWithCodex,

View file

@ -1,14 +1,14 @@
import { ItemView, type ViewStateResult } from "obsidian";
import { VIEW_TYPE_CODEX_TURN_DIFF } from "../constants";
import { copyTextWithNotice } from "../ui/clipboard";
import { VIEW_TYPE_CODEX_TURN_DIFF } from "../../constants";
import { copyTextWithNotice } from "../../shared/ui/clipboard";
import {
isPersistedTurnDiffViewState,
persistedTurnDiffViewState,
renderTurnDiffView,
type PersistedTurnDiffViewState,
type TurnDiffViewState,
} from "../ui/turn-diff";
} from "./ui/turn-diff";
export class CodexTurnDiffView extends ItemView {
private metadata: PersistedTurnDiffViewState | null = null;

View file

@ -1,6 +1,6 @@
import type { ComposerSuggestion } from "../composer/suggestions";
import { createIconButton, setButtonIcon } from "./components";
import { syncTextareaHeight } from "./textarea-autogrow";
import { createIconButton, setButtonIcon } from "../../../shared/ui/components";
import { syncTextareaHeight } from "../../../shared/ui/textarea-autogrow";
export interface ComposerElements {
composer: HTMLTextAreaElement;

View file

@ -1,5 +1,5 @@
import type { EffectiveConfigSection } from "../runtime/view";
import { createDefinitionRow } from "./components";
import type { EffectiveConfigSection } from "../../../runtime/view";
import { createDefinitionRow } from "../../../shared/ui/components";
export function renderEffectiveConfig(parent: HTMLElement, sections: EffectiveConfigSection[]): void {
const panel = parent.createDiv({ cls: "codex-panel__config" });

View file

@ -2,8 +2,8 @@ import { displayBlocksForItems } from "../display/blocks";
import { displayItemSignature, isMessageCopyActionVisible } from "../display/signature";
import { executionState } from "../display/state";
import type { DisplayBlock, DisplayDetailSection, DisplayItem } from "../display/types";
import { createIconButton, createMetaPair, createRememberedDetails } from "./components";
import { shortSignature } from "./dom";
import { createIconButton, createMetaPair, createRememberedDetails } from "../../../shared/ui/components";
import { shortSignature } from "../../../shared/ui/dom";
import { applyExecutionStateClass } from "./execution-state";
import { renderToolResult } from "./tool-result";
import {

View file

@ -6,10 +6,10 @@ import {
type ApprovalAction,
type PendingApproval,
} from "../approvals/model";
import type { RequestId } from "../generated/app-server/RequestId";
import type { RequestId } from "../../../generated/app-server/RequestId";
import type { PendingUserInput } from "../user-input/model";
import { questionDefaultAnswer } from "../user-input/model";
import { createMetaPair, createRememberedDetails } from "./components";
import { createMetaPair, createRememberedDetails } from "../../../shared/ui/components";
import { createWorkMessage } from "./work-message";
export interface PendingRequestMessageActions {

View file

@ -1,7 +1,7 @@
import { toolResultView, type ToolResultDetailSection, type ToolResultDisplayItem, type ToolResultView } from "../display/tool-view";
import { createMetaPair } from "./components";
import { createMetaPair } from "../../../shared/ui/components";
import { applyExecutionStateClass } from "./execution-state";
import { diffLineClassFromText, displayDiffLineText } from "./turn-diff";
import { diffLineClassFromText, displayDiffLineText } from "../../../shared/diff/unified";
export interface ToolResultRenderContext {
workspaceRoot?: string | null;

View file

@ -1,7 +1,7 @@
import { setIcon } from "obsidian";
import type { EffectiveConfigSection, RateLimitSummary } from "../runtime/view";
import { createToolbarButton } from "./components";
import type { EffectiveConfigSection, RateLimitSummary } from "../../../runtime/view";
import { createToolbarButton } from "../../../shared/ui/components";
import { renderEffectiveConfig } from "./config";
export type ToolbarPanelKind = "history" | "status" | "runtime";

View file

@ -1,5 +1,8 @@
import { shortThreadId } from "../utils";
import { createIconButton } from "./components";
import { displayDiffLines, diffLineClass, displayDiffLineText } from "../../../shared/diff/unified";
import { createIconButton } from "../../../shared/ui/components";
import { shortThreadId } from "../../../utils";
export { displayDiffLines } from "../../../shared/diff/unified";
export interface TurnDiffViewState {
threadId: string;
@ -99,53 +102,6 @@ export function renderUnifiedDiff(parent: HTMLElement, diff: string): HTMLElemen
return pre;
}
interface DisplayDiffLine {
text: string;
kind?: "file";
}
export function displayDiffLines(diff: string): DisplayDiffLine[] {
const displayLines: DisplayDiffLine[] = [];
let inFileHeader = false;
for (const line of diff.split("\n")) {
const file = filePathFromGitDiffHeader(line);
if (file) {
displayLines.push({ text: file, kind: "file" });
inFileHeader = true;
continue;
}
if (line.startsWith("@@")) inFileHeader = false;
if (inFileHeader && (line.startsWith("index ") || line.startsWith("--- ") || line.startsWith("+++ "))) continue;
displayLines.push({ text: line });
}
return displayLines;
}
function fileCountLabel(files: string[]): string {
return files.length === 1 ? "Edited 1 file" : `Edited ${String(files.length)} files`;
}
function filePathFromGitDiffHeader(line: string): string | null {
const match = /^diff --git a\/(.+) b\/(.+)$/.exec(line);
if (!match) return null;
return match[2] ?? null;
}
export type DiffLineClass = "added" | "removed" | "hunk" | "context" | "file";
export function diffLineClass(line: DisplayDiffLine): DiffLineClass {
if (line.kind === "file") return "file";
return diffLineClassFromText(line.text);
}
export function diffLineClassFromText(text: string): Exclude<DiffLineClass, "file"> {
if (text.startsWith("+") && !text.startsWith("+++")) return "added";
if (text.startsWith("-") && !text.startsWith("---")) return "removed";
if (text.startsWith("@@")) return "hunk";
return "context";
}
export function displayDiffLineText(text: string, lineClass: DiffLineClass): string {
const displayText = lineClass === "added" || lineClass === "removed" ? text.slice(1) : text;
return displayText || " ";
}

View file

@ -4,9 +4,9 @@ import { executionState } from "../display/state";
import type { AgentDisplayItem, AgentRunSummary, AgentRunSummaryAgent, DisplayItem, TaskProgressDisplayItem } from "../display/types";
import { agentActivityMetaLabel, agentMessagePreview, agentRunSummaryLabel, taskStatusMarker } from "../display/labels";
import type { MessageStreamContext } from "./message-stream";
import { createMetaPair, createRememberedDetails } from "./components";
import { createMetaPair, createRememberedDetails } from "../../../shared/ui/components";
import { createWorkMessage } from "./work-message";
import { shortThreadId } from "../utils";
import { shortThreadId } from "../../../utils";
const AGENT_ROW_MESSAGE_PREVIEW_LIMIT = 120;

View file

@ -1,8 +1,8 @@
import type { RequestId } from "../generated/app-server/RequestId";
import type { ServerRequest } from "../generated/app-server/ServerRequest";
import type { ToolRequestUserInputParams } from "../generated/app-server/v2/ToolRequestUserInputParams";
import type { ToolRequestUserInputQuestion } from "../generated/app-server/v2/ToolRequestUserInputQuestion";
import type { ToolRequestUserInputResponse } from "../generated/app-server/v2/ToolRequestUserInputResponse";
import type { RequestId } from "../../../generated/app-server/RequestId";
import type { ServerRequest } from "../../../generated/app-server/ServerRequest";
import type { ToolRequestUserInputParams } from "../../../generated/app-server/v2/ToolRequestUserInputParams";
import type { ToolRequestUserInputQuestion } from "../../../generated/app-server/v2/ToolRequestUserInputQuestion";
import type { ToolRequestUserInputResponse } from "../../../generated/app-server/v2/ToolRequestUserInputResponse";
export type UserInputRequest = Extract<ServerRequest, { method: "item/tool/requestUserInput" }>;

View file

@ -1,29 +1,29 @@
import { ItemView, Notice, type WorkspaceLeaf } from "obsidian";
import type { AppServerClient } from "../app-server/client";
import { ConnectionManager, StaleConnectionError } from "../app-server/connection-manager";
import { reportedServiceTier, serviceTierRequestValue, type ServiceTier } from "../app-server/service-tier";
import type { ApprovalAction, PendingApproval } from "../approvals/model";
import type { SlashCommandName } from "../composer/slash-commands";
import { parseSlashCommand } from "../composer/suggestions";
import { VIEW_TYPE_CODEX_PANEL } from "../constants";
import { createSystemItem } from "../display/system";
import { fileMentionsFromInput } from "../display/thread-items";
import type { DisplayDetailSection, DisplayItem } from "../display/types";
import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort";
import type { ApprovalsReviewer } from "../generated/app-server/v2/ApprovalsReviewer";
import type { Thread } from "../generated/app-server/v2/Thread";
import type { ThreadSettingsUpdateParams } from "../generated/app-server/v2/ThreadSettingsUpdateParams";
import type { UserInput } from "../generated/app-server/v2/UserInput";
import type { AppServerClient } from "../../app-server/client";
import { ConnectionManager, StaleConnectionError } from "../../app-server/connection-manager";
import { reportedServiceTier, serviceTierRequestValue, type ServiceTier } from "../../app-server/service-tier";
import type { ApprovalAction, PendingApproval } from "./approvals/model";
import type { SlashCommandName } from "./composer/slash-commands";
import { parseSlashCommand } from "./composer/suggestions";
import { VIEW_TYPE_CODEX_PANEL } from "../../constants";
import { createSystemItem } from "./display/system";
import { fileMentionsFromInput } from "./display/thread-items";
import type { DisplayDetailSection, DisplayItem } from "./display/types";
import type { ReasoningEffort } from "../../generated/app-server/ReasoningEffort";
import type { ApprovalsReviewer } from "../../generated/app-server/v2/ApprovalsReviewer";
import type { Thread } from "../../generated/app-server/v2/Thread";
import type { ThreadSettingsUpdateParams } from "../../generated/app-server/v2/ThreadSettingsUpdateParams";
import type { UserInput } from "../../generated/app-server/v2/UserInput";
import {
collaborationModeLabel as formatCollaborationModeLabel,
collaborationModeToggleMessage,
nextCollaborationMode,
} from "../runtime/collaboration-mode";
} from "../../runtime/collaboration-mode";
import { PanelController } from "./controller";
import { connectionDiagnosticSections, diagnosticAlertLevel } from "./diagnostics";
import { rollbackCandidateFromItems } from "./rollback";
import { contextSummary, effectiveConfigSections, rateLimitSummary } from "../runtime/view";
import { contextSummary, effectiveConfigSections, rateLimitSummary } from "../../runtime/view";
import {
autoReviewActive,
currentModel,
@ -39,10 +39,10 @@ import {
setRuntimeOverride,
supportedReasoningEfforts,
type RuntimeSnapshot,
} from "../runtime/state";
import { readRuntimeConfig } from "../runtime/config";
import { sortedAvailableModels } from "../runtime/model";
import { compactContextLabel, modelOverrideMessage, reasoningEffortOverrideMessage } from "../runtime/settings";
} from "../../runtime/state";
import { readRuntimeConfig } from "../../runtime/config";
import { sortedAvailableModels } from "../../runtime/model";
import { compactContextLabel, modelOverrideMessage, reasoningEffortOverrideMessage } from "../../runtime/settings";
import { executeSlashCommand as runSlashCommand, type SlashCommandExecutionResult } from "./slash-commands";
import type { ThreadReferenceInput } from "./slash-commands";
import { mcpStatusLines } from "./mcp-status";
@ -51,13 +51,13 @@ import { statusValue, usageLimitStatusLines } from "./status-lines";
import { ThreadHistoryLoader } from "./thread-history";
import { ThreadRenameController } from "./thread-rename";
import { pendingRequestsSignature as requestStateSignature, userInputDraftKey, userInputOtherDraftKey } from "./request-state";
import type { CodexPanelSettings } from "../settings/model";
import { questionDefaultAnswer, type PendingUserInput } from "../user-input/model";
import type { CodexPanelSettings } from "../../settings/model";
import { questionDefaultAnswer, type PendingUserInput } from "./user-input/model";
import { PanelComposerController } from "./composer-controller";
import { attachHookRunsToTurn } from "./hook-display";
import { clearActiveThreadState, clearConnectionScopedState, createPanelState, type PanelState } from "./state";
import { codexPanelDisplayTitle, getThreadTitle, inheritedForkThreadName, upsertThread } from "../threads/model";
import { exportArchivedThreadMarkdown } from "../threads/export";
import { codexPanelDisplayTitle, getThreadTitle, inheritedForkThreadName, upsertThread } from "../../domain/threads/model";
import { exportArchivedThreadMarkdown } from "../../domain/threads/export";
import {
referencedThreadDisplay,
referencedThreadPrompt,
@ -65,10 +65,10 @@ import {
referencedThreadTurns,
REFERENCED_THREAD_TURN_LIMIT,
type ReferencedThreadDisplay,
} from "../threads/reference";
import { renderPendingRequestMessage } from "../ui/pending-request-message";
import { renderToolbar, toolbarSignature, type ToolbarChoice, type ToolbarViewModel } from "../ui/toolbar";
import type { TurnDiffViewState } from "../ui/turn-diff";
} from "../../domain/threads/reference";
import { renderPendingRequestMessage } from "./ui/pending-request-message";
import { renderToolbar, toolbarSignature, type ToolbarChoice, type ToolbarViewModel } from "./ui/toolbar";
import type { TurnDiffViewState } from "./ui/turn-diff";
import { PanelMessageRenderer } from "./message-renderer";
export interface CodexPanelHost {

View file

@ -2,7 +2,7 @@ import { MarkdownView, Notice, type Editor, type Plugin } from "obsidian";
import type { RewriteRuntimeSettings, RewriteSession } from "./model";
import { RewriteSelectionPopover } from "./popover";
import type { SendShortcut } from "../settings/model";
import type { SendShortcut } from "../../settings/model";
export interface RewriteSelectionCommandHost extends Plugin {
settings: {

View file

@ -1,5 +1,5 @@
import { isComposerSendKey, type ComposerSendKeyEvent } from "../composer/keys";
import type { SendShortcut } from "../settings/model";
import { isComposerSendKey, type ComposerSendKeyEvent } from "../chat/composer/keys";
import type { SendShortcut } from "../../settings/model";
export type RewriteGenerateKeyEvent = ComposerSendKeyEvent;

View file

@ -1,5 +1,5 @@
import type { EditorPosition } from "obsidian";
import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort";
import type { ReasoningEffort } from "../../generated/app-server/ReasoningEffort";
export type RewriteStatus = "editing-prompt" | "generating" | "preview" | "applied" | "cancelled" | "failed";

View file

@ -1,4 +1,4 @@
import type { Turn } from "../generated/app-server/v2/Turn";
import type { Turn } from "../../generated/app-server/v2/Turn";
export interface RewriteOutput {
replacementText: string;

View file

@ -1,8 +1,8 @@
import { Notice, type Editor } from "obsidian";
import { syncComposerHeight } from "../ui/composer";
import { createIconButton } from "../ui/components";
import { diffLineClass, displayDiffLineText, displayDiffLines } from "../ui/turn-diff";
import { diffLineClass, displayDiffLineText, displayDiffLines } from "../../shared/diff/unified";
import { createIconButton } from "../../shared/ui/components";
import { syncTextareaHeight } from "../../shared/ui/textarea-autogrow";
import { buildSelectionUnifiedDiff } from "./diff";
import { isRewriteActionKey, isRewriteGenerateKey } from "./keys";
import { canApplyRewrite, type RewriteRuntimeSettings, type RewriteSession } from "./model";
@ -10,7 +10,7 @@ import { RewriteOutputError } from "./output";
import { positionRewritePopover } from "./position";
import { buildRewritePrompt } from "./prompt";
import { runRewriteSelection, type RewriteActivity } from "./runner";
import type { SendShortcut } from "../settings/model";
import type { SendShortcut } from "../../settings/model";
const POPOVER_MARGIN = 8;
@ -319,7 +319,11 @@ export class RewriteSelectionPopover {
}
private syncInstructionHeight(): void {
syncComposerHeight(this.elements?.instruction ?? null);
const instruction = this.elements?.instruction ?? null;
syncTextareaHeight(instruction, {
minHeightFallback: 72,
maxHeightFallback: instruction ? Math.min(180, instruction.win.innerHeight * 0.3) : 180,
});
}
private position(): void {

View file

@ -1,11 +1,11 @@
import { AppServerClient } from "../app-server/client";
import type { ServerNotification } from "../generated/app-server/ServerNotification";
import type { JsonValue } from "../generated/app-server/serde_json/JsonValue";
import type { ReasoningEffort } from "../generated/app-server/ReasoningEffort";
import type { Model } from "../generated/app-server/v2/Model";
import type { ThreadItem } from "../generated/app-server/v2/ThreadItem";
import type { Turn } from "../generated/app-server/v2/Turn";
import { runtimeOverride, validatedRuntimeOverride } from "../runtime/model";
import { AppServerClient } from "../../app-server/client";
import type { ServerNotification } from "../../generated/app-server/ServerNotification";
import type { JsonValue } from "../../generated/app-server/serde_json/JsonValue";
import type { ReasoningEffort } from "../../generated/app-server/ReasoningEffort";
import type { Model } from "../../generated/app-server/v2/Model";
import type { ThreadItem } from "../../generated/app-server/v2/ThreadItem";
import type { Turn } from "../../generated/app-server/v2/Turn";
import { runtimeOverride, validatedRuntimeOverride } from "../../runtime/model";
import type { RewriteRuntimeSettings } from "./model";
import { REWRITE_DEVELOPER_INSTRUCTIONS, REWRITE_SERVICE_NAME } from "./prompt";
import { RewriteOutputError, rewriteOutputParseResultFromTurn, type RewriteOutput } from "./output";

View file

@ -1,12 +1,12 @@
import { Plugin, type WorkspaceLeaf } from "obsidian";
import { VIEW_TYPE_CODEX_PANEL, VIEW_TYPE_CODEX_TURN_DIFF } from "./constants";
import { registerRewriteSelectionCommand } from "./editor-rewrite/command";
import { CodexPanelView } from "./panel/view";
import { CodexTurnDiffView } from "./panel/turn-diff-view";
import { registerRewriteSelectionCommand } from "./features/selection-rewrite/command";
import { CodexPanelView } from "./features/chat/view";
import { CodexTurnDiffView } from "./features/chat/turn-diff-view";
import { DEFAULT_SETTINGS, getVaultPath, normalizeSettings, settingsMatchNormalizedData, type CodexPanelSettings } from "./settings/model";
import { CodexPanelSettingTab } from "./settings/tab";
import { persistedTurnDiffViewState, type TurnDiffViewState } from "./ui/turn-diff";
import { persistedTurnDiffViewState, type TurnDiffViewState } from "./features/chat/ui/turn-diff";
export default class CodexPanelPlugin extends Plugin {
settings: CodexPanelSettings = DEFAULT_SETTINGS;

View file

@ -6,7 +6,7 @@ import type { ConfigReadResponse } from "../generated/app-server/v2/ConfigReadRe
import type { Model } from "../generated/app-server/v2/Model";
import type { RateLimitSnapshot } from "../generated/app-server/v2/RateLimitSnapshot";
import type { ThreadTokenUsage } from "../generated/app-server/v2/ThreadTokenUsage";
import type { DisplayItem } from "../display/types";
import type { DisplayItem } from "../features/chat/display/types";
import { serviceTierRequestValue, type ReportedServiceTier, type ServiceTier, type ServiceTierRequest } from "../app-server/service-tier";
import { defaultCollaborationMode, planCollaborationMode } from "./collaboration-mode";
import { findModelByIdOrName, supportedEffortsForModel } from "./model";

View file

@ -2,7 +2,7 @@ import { Setting } from "obsidian";
import type { HookMetadata } from "../generated/app-server/v2/HookMetadata";
import type { Thread } from "../generated/app-server/v2/Thread";
import { archivedThreadDisplayTitle, fullThreadTitle } from "../threads/model";
import { archivedThreadDisplayTitle, fullThreadTitle } from "../domain/threads/model";
import { shortThreadId } from "../utils";
export interface ArchivedThreadSectionState {

View file

@ -9,7 +9,7 @@ import type { Model } from "../generated/app-server/v2/Model";
import type { Thread } from "../generated/app-server/v2/Thread";
import type CodexPanelPlugin from "../main";
import { findModelByIdOrName, REASONING_EFFORTS, sortedAvailableModels, supportedEffortsForModel } from "../runtime/model";
import { archivedThreadDisplayTitle } from "../threads/model";
import { archivedThreadDisplayTitle } from "../domain/threads/model";
import { errorMessage } from "../utils";
import { loadHookData, loadSettingsData } from "./data";
import { renderArchivedThreadSection, renderHookSection } from "./dynamic-sections";

View file

@ -0,0 +1,46 @@
export interface DisplayDiffLine {
text: string;
kind?: "file";
}
export type DiffLineClass = "added" | "removed" | "hunk" | "context" | "file";
export function displayDiffLines(diff: string): DisplayDiffLine[] {
const displayLines: DisplayDiffLine[] = [];
let inFileHeader = false;
for (const line of diff.split("\n")) {
const file = filePathFromGitDiffHeader(line);
if (file) {
displayLines.push({ text: file, kind: "file" });
inFileHeader = true;
continue;
}
if (line.startsWith("@@")) inFileHeader = false;
if (inFileHeader && (line.startsWith("index ") || line.startsWith("--- ") || line.startsWith("+++ "))) continue;
displayLines.push({ text: line });
}
return displayLines;
}
export function diffLineClass(line: DisplayDiffLine): DiffLineClass {
if (line.kind === "file") return "file";
return diffLineClassFromText(line.text);
}
export function diffLineClassFromText(text: string): Exclude<DiffLineClass, "file"> {
if (text.startsWith("+") && !text.startsWith("+++")) return "added";
if (text.startsWith("-") && !text.startsWith("---")) return "removed";
if (text.startsWith("@@")) return "hunk";
return "context";
}
export function displayDiffLineText(text: string, lineClass: DiffLineClass): string {
const displayText = lineClass === "added" || lineClass === "removed" ? text.slice(1) : text;
return displayText || " ";
}
function filePathFromGitDiffHeader(line: string): string | null {
const match = /^diff --git a\/(.+) b\/(.+)$/.exec(line);
if (!match) return null;
return match[2] ?? null;
}

View file

@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { classifyAppServerLog } from "../../src/panel/app-server-logs";
import { classifyAppServerLog } from "../../src/features/chat/app-server-logs";
describe("app-server log classification", () => {
it("suppresses raw MCP token refresh stderr", () => {

View file

@ -7,7 +7,7 @@ import {
approvalSummary,
approvalTitle,
toPendingApproval,
} from "../../src/approvals/model";
} from "../../src/features/chat/approvals/model";
import type { ServerRequest } from "../../src/generated/app-server/ServerRequest";
import type { CommandExecutionApprovalDecision } from "../../src/generated/app-server/v2/CommandExecutionApprovalDecision";

View file

@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { isComposerSendKey, type ComposerSendKeyEvent } from "../../src/composer/keys";
import { isComposerSendKey, type ComposerSendKeyEvent } from "../../src/features/chat/composer/keys";
const baseEvent: ComposerSendKeyEvent = {
key: "Enter",

View file

@ -11,7 +11,7 @@ import {
findWikiLinkSuggestions,
nextComposerSuggestionIndex,
parseSlashCommand,
} from "../../src/composer/suggestions";
} from "../../src/features/chat/composer/suggestions";
function expectPresent<T>(value: T | null | undefined): T {
if (value === null || value === undefined) throw new Error("Expected value to be present");

View file

@ -1,7 +1,7 @@
import { describe, expect, it, vi } from "vitest";
import { TFile, type App } from "obsidian";
import { noteCandidates, resolveWikiLinkMention } from "../../src/composer/obsidian-context";
import { noteCandidates, resolveWikiLinkMention } from "../../src/features/chat/composer/obsidian-context";
describe("Obsidian composer context", () => {
it("builds note candidates from markdown files", () => {

View file

@ -1,8 +1,8 @@
import { describe, expect, it, vi } from "vitest";
import { slashCommandHelpLines, slashCommandHelpRows } from "../../src/composer/slash-commands";
import { slashCommandHelpLines, slashCommandHelpRows } from "../../src/features/chat/composer/slash-commands";
import type { Thread } from "../../src/generated/app-server/v2/Thread";
import { executeSlashCommand, type SlashCommandExecutionContext } from "../../src/panel/slash-commands";
import { executeSlashCommand, type SlashCommandExecutionContext } from "../../src/features/chat/slash-commands";
function context(overrides: Partial<SlashCommandExecutionContext> = {}): SlashCommandExecutionContext {
return {

View file

@ -5,7 +5,7 @@ import {
parsedWikiLinks,
userInputWithWikiLinkMentions,
userInputWithWikiLinkMentionsAndSkills,
} from "../../src/composer/wikilink-context";
} from "../../src/features/chat/composer/wikilink-context";
describe("wikilink context", () => {
it("parses aliases, subpaths, and duplicate links", () => {

View file

@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import { activeAgentRunSummary } from "../../src/display/agent";
import { displayBlocksForItems } from "../../src/display/blocks";
import { activeAgentRunSummary } from "../../src/features/chat/display/agent";
import { displayBlocksForItems } from "../../src/features/chat/display/blocks";
import {
appendAssistantDelta,
appendItemOutput,
@ -9,13 +9,13 @@ import {
appendPlanDelta,
appendToolOutput,
upsertDisplayItem,
} from "../../src/display/stream-updates";
import { normalizeProposedPlanMarkdown, planProgressDisplayItem } from "../../src/display/plan";
import { pathRelativeToRoot } from "../../src/display/paths";
import { createAutoReviewResultItem, createReviewResultItem } from "../../src/display/review";
import { executionState } from "../../src/display/state";
import { displayItemFromThreadItem, displayItemsFromTurns } from "../../src/display/thread-items";
import type { DisplayItem } from "../../src/display/types";
} from "../../src/features/chat/display/stream-updates";
import { normalizeProposedPlanMarkdown, planProgressDisplayItem } from "../../src/features/chat/display/plan";
import { pathRelativeToRoot } from "../../src/features/chat/display/paths";
import { createAutoReviewResultItem, createReviewResultItem } from "../../src/features/chat/display/review";
import { executionState } from "../../src/features/chat/display/state";
import { displayItemFromThreadItem, displayItemsFromTurns } from "../../src/features/chat/display/thread-items";
import type { DisplayItem } from "../../src/features/chat/display/types";
import type { ThreadItem } from "../../src/generated/app-server/v2/ThreadItem";
import type { Turn } from "../../src/generated/app-server/v2/Turn";

View file

@ -1,11 +1,11 @@
import { describe, expect, it } from "vitest";
import { buildSelectionUnifiedDiff } from "../../src/editor-rewrite/diff";
import { isRewriteActionKey, isRewriteGenerateKey, type RewriteGenerateKeyEvent } from "../../src/editor-rewrite/keys";
import { canApplyRewrite, type RewriteSession } from "../../src/editor-rewrite/model";
import { parseRewriteOutput, rewriteOutputFromTurn, rewriteOutputParseResultFromTurn } from "../../src/editor-rewrite/output";
import { buildRewritePrompt } from "../../src/editor-rewrite/prompt";
import { rewriteRuntime, validatedRewriteRuntime } from "../../src/editor-rewrite/runner";
import { buildSelectionUnifiedDiff } from "../../src/features/selection-rewrite/diff";
import { isRewriteActionKey, isRewriteGenerateKey, type RewriteGenerateKeyEvent } from "../../src/features/selection-rewrite/keys";
import { canApplyRewrite, type RewriteSession } from "../../src/features/selection-rewrite/model";
import { parseRewriteOutput, rewriteOutputFromTurn, rewriteOutputParseResultFromTurn } from "../../src/features/selection-rewrite/output";
import { buildRewritePrompt } from "../../src/features/selection-rewrite/prompt";
import { rewriteRuntime, validatedRewriteRuntime } from "../../src/features/selection-rewrite/runner";
import type { Model } from "../../src/generated/app-server/v2/Model";
import type { Turn } from "../../src/generated/app-server/v2/Turn";

View file

@ -6,7 +6,7 @@ import {
createAppServerDiagnostics,
upsertMcpServerDiagnostic,
} from "../../src/app-server/compatibility";
import { connectionDiagnosticSections, diagnosticAlertLevel } from "../../src/panel/diagnostics";
import { connectionDiagnosticSections, diagnosticAlertLevel } from "../../src/features/chat/diagnostics";
describe("connection diagnostics", () => {
it("formats base rows, capability probes, and MCP issues for /doctor", () => {

View file

@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import { TFile, type App } from "obsidian";
import { markdownFileLinkTarget } from "../../src/panel/markdown-file-links";
import { markdownFileLinkTarget } from "../../src/features/chat/markdown-file-links";
describe("markdown file links", () => {
it("resolves absolute vault paths", () => {

View file

@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import type { McpServerStatus } from "../../src/generated/app-server/v2/McpServerStatus";
import { mcpStatusLines } from "../../src/panel/mcp-status";
import { mcpStatusLines } from "../../src/features/chat/mcp-status";
function mcpServer(overrides: Partial<McpServerStatus> = {}): McpServerStatus {
return {

View file

@ -1,8 +1,8 @@
import { describe, expect, it, vi } from "vitest";
import { PanelController } from "../../src/panel/controller";
import { attachHookRunsToTurn } from "../../src/panel/hook-display";
import { createPanelState } from "../../src/panel/state";
import { PanelController } from "../../src/features/chat/controller";
import { attachHookRunsToTurn } from "../../src/features/chat/hook-display";
import { createPanelState } from "../../src/features/chat/state";
import type { ServerNotification } from "../../src/generated/app-server/ServerNotification";
import type { ServerRequest } from "../../src/generated/app-server/ServerRequest";
import type { Thread } from "../../src/generated/app-server/v2/Thread";

View file

@ -6,7 +6,7 @@ import {
messageTurnId,
routeServerNotification,
routeServerRequest,
} from "../../src/panel/inbound-routing";
} from "../../src/features/chat/inbound-routing";
import type { ServerNotification } from "../../src/generated/app-server/ServerNotification";
import type { ServerRequest } from "../../src/generated/app-server/ServerRequest";

View file

@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import { isRollbackCandidateItem, rollbackCandidateFromItems } from "../../src/panel/rollback";
import type { DisplayItem } from "../../src/display/types";
import { isRollbackCandidateItem, rollbackCandidateFromItems } from "../../src/features/chat/rollback";
import type { DisplayItem } from "../../src/features/chat/display/types";
function expectPresent<T>(value: T | null | undefined): T {
if (value === null || value === undefined) throw new Error("Expected value to be present");

View file

@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { statusValue, usageLimitStatusLines } from "../../src/panel/status-lines";
import { statusValue, usageLimitStatusLines } from "../../src/features/chat/status-lines";
import type { RateLimitSummary } from "../../src/runtime/view";
describe("status line helpers", () => {

View file

@ -10,7 +10,7 @@ import {
normalizeGeneratedTitle,
titleFromNamingTurn,
validatedNamingRuntime,
} from "../../src/panel/thread-naming";
} from "../../src/features/chat/thread-naming";
import type { Model } from "../../src/generated/app-server/v2/Model";
import type { Turn } from "../../src/generated/app-server/v2/Turn";

View file

@ -7,7 +7,7 @@ import type { Model } from "../../src/generated/app-server/v2/Model";
import type { ReasoningEffort } from "../../src/generated/app-server/ReasoningEffort";
import { findModelByIdOrName, sortedAvailableModels, supportedEffortsForModel } from "../../src/runtime/model";
import { CodexPanelSettingTab } from "../../src/settings/tab";
import { archivedThreadDisplayTitle } from "../../src/threads/model";
import { archivedThreadDisplayTitle } from "../../src/domain/threads/model";
import { notices } from "../mocks/obsidian";
const require = createRequire(import.meta.url);

View file

@ -8,8 +8,8 @@ import {
markdownFromThread,
normalizedArchiveTags,
type ArchiveExportAdapter,
} from "../../src/threads/export";
import { referencedThreadPrompt } from "../../src/threads/reference";
} from "../../src/domain/threads/export";
import { referencedThreadPrompt } from "../../src/domain/threads/reference";
describe("thread archive export", () => {
it("writes frontmatter and readable user/codex turns with turn timestamps", () => {

View file

@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
import type { Thread } from "../../src/generated/app-server/v2/Thread";
import type { Turn } from "../../src/generated/app-server/v2/Turn";
import { referencedThreadDisplayFromPrompt, referencedThreadPrompt, referencedThreadTurns } from "../../src/threads/reference";
import { referencedThreadDisplayFromPrompt, referencedThreadPrompt, referencedThreadTurns } from "../../src/domain/threads/reference";
function thread(overrides: Partial<Thread> = {}): Thread {
return {

View file

@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import type { Thread } from "../../src/generated/app-server/v2/Thread";
import { codexPanelDisplayTitle, inheritedForkThreadName, upsertThread } from "../../src/threads/model";
import { codexPanelDisplayTitle, inheritedForkThreadName, upsertThread } from "../../src/domain/threads/model";
describe("thread helpers", () => {
it("formats Codex panel display titles from the active thread", () => {

View file

@ -2,7 +2,7 @@
import { describe, expect, it, vi } from "vitest";
import { renderTextWithWikiLinks, shortSignature } from "../../src/ui/dom";
import { renderTextWithWikiLinks, shortSignature } from "../../src/shared/ui/dom";
import { installObsidianDomShims } from "./dom-test-helpers";
installObsidianDomShims();

View file

@ -2,21 +2,21 @@
import { describe, expect, it, vi } from "vitest";
import type { PendingApproval } from "../../src/approvals/model";
import type { PendingUserInput } from "../../src/user-input/model";
import type { PendingApproval } from "../../src/features/chat/approvals/model";
import type { PendingUserInput } from "../../src/features/chat/user-input/model";
import {
renderComposerShell,
renderComposerSuggestions,
scrollComposerSuggestionIntoView,
syncComposerControls,
syncComposerHeight,
} from "../../src/ui/composer";
import { renderPendingRequestMessage } from "../../src/ui/pending-request-message";
import { renderToolbar, toolbarSignature, type ToolbarViewModel } from "../../src/ui/toolbar";
import { displayItemSignature } from "../../src/display/signature";
import { implementPlanCandidateFromState } from "../../src/panel/message-renderer";
import { messageRenderBlocks as rawMessageRenderBlocks, syncMessageRenderBlocks } from "../../src/ui/message-stream";
import { displayDiffLines, persistedTurnDiffViewState, renderTurnDiffView } from "../../src/ui/turn-diff";
} from "../../src/features/chat/ui/composer";
import { renderPendingRequestMessage } from "../../src/features/chat/ui/pending-request-message";
import { renderToolbar, toolbarSignature, type ToolbarViewModel } from "../../src/features/chat/ui/toolbar";
import { displayItemSignature } from "../../src/features/chat/display/signature";
import { implementPlanCandidateFromState } from "../../src/features/chat/message-renderer";
import { messageRenderBlocks as rawMessageRenderBlocks, syncMessageRenderBlocks } from "../../src/features/chat/ui/message-stream";
import { displayDiffLines, persistedTurnDiffViewState, renderTurnDiffView } from "../../src/features/chat/ui/turn-diff";
import { composerSuggestionScrollFixture, installObsidianDomShims, topLevelDetailsSummaries } from "./dom-test-helpers";
installObsidianDomShims();

View file

@ -2,7 +2,7 @@
import { describe, expect, it } from "vitest";
import { bottomScrollTop, captureScrollAnchor, isNearScrollBottom, restoreScrollAnchor } from "../../src/ui/scroll";
import { bottomScrollTop, captureScrollAnchor, isNearScrollBottom, restoreScrollAnchor } from "../../src/features/chat/ui/scroll";
describe("message scroll helpers", () => {
it("detects whether the transcript is pinned near the bottom", () => {

View file

@ -1,7 +1,7 @@
import { describe, expect, it } from "vitest";
import { questionDefaultAnswer, toPendingUserInput, userInputResponse } from "../../src/user-input/model";
import { pendingRequestsSignature } from "../../src/panel/request-state";
import { questionDefaultAnswer, toPendingUserInput, userInputResponse } from "../../src/features/chat/user-input/model";
import { pendingRequestsSignature } from "../../src/features/chat/request-state";
import type { ServerRequest } from "../../src/generated/app-server/ServerRequest";
function expectPresent<T>(value: T | null | undefined): T {