diff --git a/docs/development.md b/docs/development.md index e02d1dfd..6188be74 100644 --- a/docs/development.md +++ b/docs/development.md @@ -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 diff --git a/src/threads/export.ts b/src/domain/threads/export.ts similarity index 96% rename from src/threads/export.ts rename to src/domain/threads/export.ts index dce306cb..39a1162c 100644 --- a/src/threads/export.ts +++ b/src/domain/threads/export.ts @@ -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"; diff --git a/src/threads/model.ts b/src/domain/threads/model.ts similarity index 94% rename from src/threads/model.ts rename to src/domain/threads/model.ts index dcb1bf90..44a570e7 100644 --- a/src/threads/model.ts +++ b/src/domain/threads/model.ts @@ -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; diff --git a/src/threads/reference.ts b/src/domain/threads/reference.ts similarity index 93% rename from src/threads/reference.ts rename to src/domain/threads/reference.ts index 4a9f54ca..3a9b0edb 100644 --- a/src/threads/reference.ts +++ b/src/domain/threads/reference.ts @@ -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; diff --git a/src/panel/app-server-logs.ts b/src/features/chat/app-server-logs.ts similarity index 100% rename from src/panel/app-server-logs.ts rename to src/features/chat/app-server-logs.ts diff --git a/src/approvals/model.ts b/src/features/chat/approvals/model.ts similarity index 93% rename from src/approvals/model.ts rename to src/features/chat/approvals/model.ts index 2e7a7520..5c52219e 100644 --- a/src/approvals/model.ts +++ b/src/features/chat/approvals/model.ts @@ -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; diff --git a/src/approvals/permission-details.ts b/src/features/chat/approvals/permission-details.ts similarity index 90% rename from src/approvals/permission-details.ts rename to src/features/chat/approvals/permission-details.ts index e4aeca98..359e5a29 100644 --- a/src/approvals/permission-details.ts +++ b/src/features/chat/approvals/permission-details.ts @@ -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; diff --git a/src/panel/composer-controller.ts b/src/features/chat/composer-controller.ts similarity index 94% rename from src/panel/composer-controller.ts rename to src/features/chat/composer-controller.ts index 183a8890..7164f1db 100644 --- a/src/panel/composer-controller.ts +++ b/src/features/chat/composer-controller.ts @@ -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 { diff --git a/src/composer/keys.ts b/src/features/chat/composer/keys.ts similarity index 89% rename from src/composer/keys.ts rename to src/features/chat/composer/keys.ts index fd5bdd23..8b1c0919 100644 --- a/src/composer/keys.ts +++ b/src/features/chat/composer/keys.ts @@ -1,4 +1,4 @@ -import type { SendShortcut } from "../settings/model"; +import type { SendShortcut } from "../../../settings/model"; export interface ComposerSendKeyEvent { key: string; diff --git a/src/composer/obsidian-context.ts b/src/features/chat/composer/obsidian-context.ts similarity index 100% rename from src/composer/obsidian-context.ts rename to src/features/chat/composer/obsidian-context.ts diff --git a/src/composer/slash-commands.ts b/src/features/chat/composer/slash-commands.ts similarity index 100% rename from src/composer/slash-commands.ts rename to src/features/chat/composer/slash-commands.ts diff --git a/src/composer/suggestions.ts b/src/features/chat/composer/suggestions.ts similarity index 96% rename from src/composer/suggestions.ts rename to src/features/chat/composer/suggestions.ts index f73698ee..d90d73af 100644 --- a/src/composer/suggestions.ts +++ b/src/features/chat/composer/suggestions.ts @@ -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; diff --git a/src/composer/wikilink-context.ts b/src/features/chat/composer/wikilink-context.ts similarity index 95% rename from src/composer/wikilink-context.ts rename to src/features/chat/composer/wikilink-context.ts index cd8623a5..6d844597 100644 --- a/src/composer/wikilink-context.ts +++ b/src/features/chat/composer/wikilink-context.ts @@ -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; diff --git a/src/panel/controller.ts b/src/features/chat/controller.ts similarity index 95% rename from src/panel/controller.ts rename to src/features/chat/controller.ts index c45be872..75e70bf7 100644 --- a/src/panel/controller.ts +++ b/src/features/chat/controller.ts @@ -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"; diff --git a/src/panel/diagnostics.ts b/src/features/chat/diagnostics.ts similarity index 93% rename from src/panel/diagnostics.ts rename to src/features/chat/diagnostics.ts index 835a8c30..6fb33d84 100644 --- a/src/panel/diagnostics.ts +++ b/src/features/chat/diagnostics.ts @@ -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; diff --git a/src/display/agent.ts b/src/features/chat/display/agent.ts similarity index 97% rename from src/display/agent.ts rename to src/features/chat/display/agent.ts index be2b2d91..a1470c92 100644 --- a/src/display/agent.ts +++ b/src/features/chat/display/agent.ts @@ -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"; diff --git a/src/display/blocks.ts b/src/features/chat/display/blocks.ts similarity index 100% rename from src/display/blocks.ts rename to src/features/chat/display/blocks.ts diff --git a/src/display/labels.ts b/src/features/chat/display/labels.ts similarity index 92% rename from src/display/labels.ts rename to src/features/chat/display/labels.ts index 3e306757..38c2fddc 100644 --- a/src/display/labels.ts +++ b/src/features/chat/display/labels.ts @@ -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 { diff --git a/src/display/paths.ts b/src/features/chat/display/paths.ts similarity index 100% rename from src/display/paths.ts rename to src/features/chat/display/paths.ts diff --git a/src/display/plan.ts b/src/features/chat/display/plan.ts similarity index 93% rename from src/display/plan.ts rename to src/features/chat/display/plan.ts index 76413eef..e319c41e 100644 --- a/src/display/plan.ts +++ b/src/features/chat/display/plan.ts @@ -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"; diff --git a/src/display/review.ts b/src/features/chat/display/review.ts similarity index 95% rename from src/display/review.ts rename to src/features/chat/display/review.ts index 1a3ffa70..8ff4e610 100644 --- a/src/display/review.ts +++ b/src/features/chat/display/review.ts @@ -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"; diff --git a/src/display/signature.ts b/src/features/chat/display/signature.ts similarity index 100% rename from src/display/signature.ts rename to src/features/chat/display/signature.ts diff --git a/src/display/state.ts b/src/features/chat/display/state.ts similarity index 97% rename from src/display/state.ts rename to src/features/chat/display/state.ts index 1f6457c2..a21c625d 100644 --- a/src/display/state.ts +++ b/src/features/chat/display/state.ts @@ -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; diff --git a/src/display/stream-updates.ts b/src/features/chat/display/stream-updates.ts similarity index 100% rename from src/display/stream-updates.ts rename to src/features/chat/display/stream-updates.ts diff --git a/src/display/system.ts b/src/features/chat/display/system.ts similarity index 100% rename from src/display/system.ts rename to src/features/chat/display/system.ts diff --git a/src/display/thread-items.ts b/src/features/chat/display/thread-items.ts similarity index 97% rename from src/display/thread-items.ts rename to src/features/chat/display/thread-items.ts index 3765bee6..65194c53 100644 --- a/src/display/thread-items.ts +++ b/src/features/chat/display/thread-items.ts @@ -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"; diff --git a/src/display/tool-format.ts b/src/features/chat/display/tool-format.ts similarity index 97% rename from src/display/tool-format.ts rename to src/features/chat/display/tool-format.ts index db48b756..36487dfb 100644 --- a/src/display/tool-format.ts +++ b/src/features/chat/display/tool-format.ts @@ -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; diff --git a/src/display/tool-view.ts b/src/features/chat/display/tool-view.ts similarity index 99% rename from src/display/tool-view.ts rename to src/features/chat/display/tool-view.ts index 9858cf13..c4d774e7 100644 --- a/src/display/tool-view.ts +++ b/src/features/chat/display/tool-view.ts @@ -1,6 +1,6 @@ import { pathRelativeToRoot } from "./paths"; import { executionState } from "./state"; -import { definedProp } from "../utils"; +import { definedProp } from "../../../utils"; import type { ApprovalResultDisplayItem, CommandDisplayItem, diff --git a/src/display/types.ts b/src/features/chat/display/types.ts similarity index 97% rename from src/display/types.ts rename to src/features/chat/display/types.ts index 744761de..856fea62 100644 --- a/src/display/types.ts +++ b/src/features/chat/display/types.ts @@ -1,4 +1,4 @@ -import type { ReferencedThreadDisplay } from "../threads/reference"; +import type { ReferencedThreadDisplay } from "../../../domain/threads/reference"; export type DisplayKind = | "message" diff --git a/src/panel/hook-display.ts b/src/features/chat/hook-display.ts similarity index 93% rename from src/panel/hook-display.ts rename to src/features/chat/hook-display.ts index 47d53cc4..e04261c6 100644 --- a/src/panel/hook-display.ts +++ b/src/features/chat/hook-display.ts @@ -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["params"]["run"], diff --git a/src/panel/inbound-routing.ts b/src/features/chat/inbound-routing.ts similarity index 94% rename from src/panel/inbound-routing.ts rename to src/features/chat/inbound-routing.ts index 88707084..c13b3af2 100644 --- a/src/panel/inbound-routing.ts +++ b/src/features/chat/inbound-routing.ts @@ -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; diff --git a/src/panel/markdown-file-links.ts b/src/features/chat/markdown-file-links.ts similarity index 100% rename from src/panel/markdown-file-links.ts rename to src/features/chat/markdown-file-links.ts diff --git a/src/panel/mcp-status.ts b/src/features/chat/mcp-status.ts similarity index 93% rename from src/panel/mcp-status.ts rename to src/features/chat/mcp-status.ts index dca965aa..d128d058 100644 --- a/src/panel/mcp-status.ts +++ b/src/features/chat/mcp-status.ts @@ -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) { diff --git a/src/panel/message-renderer.ts b/src/features/chat/message-renderer.ts similarity index 95% rename from src/panel/message-renderer.ts rename to src/features/chat/message-renderer.ts index e8697388..b069c8b5 100644 --- a/src/panel/message-renderer.ts +++ b/src/features/chat/message-renderer.ts @@ -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"; diff --git a/src/panel/request-state.ts b/src/features/chat/request-state.ts similarity index 93% rename from src/panel/request-state.ts rename to src/features/chat/request-state.ts index 8576b1c1..1afaaddc 100644 --- a/src/panel/request-state.ts +++ b/src/features/chat/request-state.ts @@ -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}`; diff --git a/src/panel/rollback.ts b/src/features/chat/rollback.ts similarity index 93% rename from src/panel/rollback.ts rename to src/features/chat/rollback.ts index 63a8626c..2fffe8ec 100644 --- a/src/panel/rollback.ts +++ b/src/features/chat/rollback.ts @@ -1,4 +1,4 @@ -import type { DisplayItem, MessageDisplayItem } from "../display/types"; +import type { DisplayItem, MessageDisplayItem } from "./display/types"; export interface RollbackCandidate { turnId: string; diff --git a/src/panel/session-controller.ts b/src/features/chat/session-controller.ts similarity index 95% rename from src/panel/session-controller.ts rename to src/features/chat/session-controller.ts index b45baf9c..b315d56a 100644 --- a/src/panel/session-controller.ts +++ b/src/features/chat/session-controller.ts @@ -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 { diff --git a/src/panel/slash-commands.ts b/src/features/chat/slash-commands.ts similarity index 94% rename from src/panel/slash-commands.ts rename to src/features/chat/slash-commands.ts index dff9cce6..053a81a2 100644 --- a/src/panel/slash-commands.ts +++ b/src/features/chat/slash-commands.ts @@ -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; diff --git a/src/panel/state.ts b/src/features/chat/state.ts similarity index 77% rename from src/panel/state.ts rename to src/features/chat/state.ts index 8079f3ba..20aa82f9 100644 --- a/src/panel/state.ts +++ b/src/features/chat/state.ts @@ -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; diff --git a/src/panel/status-lines.ts b/src/features/chat/status-lines.ts similarity index 92% rename from src/panel/status-lines.ts rename to src/features/chat/status-lines.ts index 2bfc04b8..2aa3a9e0 100644 --- a/src/panel/status-lines.ts +++ b/src/features/chat/status-lines.ts @@ -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; diff --git a/src/panel/thread-history.ts b/src/features/chat/thread-history.ts similarity index 95% rename from src/panel/thread-history.ts rename to src/features/chat/thread-history.ts index adaef7ab..5717a222 100644 --- a/src/panel/thread-history.ts +++ b/src/features/chat/thread-history.ts @@ -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 { diff --git a/src/panel/thread-naming.ts b/src/features/chat/thread-naming.ts similarity index 93% rename from src/panel/thread-naming.ts rename to src/features/chat/thread-naming.ts index c57b87e9..468bce5b 100644 --- a/src/panel/thread-naming.ts +++ b/src/features/chat/thread-naming.ts @@ -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; diff --git a/src/panel/thread-rename.ts b/src/features/chat/thread-rename.ts similarity index 95% rename from src/panel/thread-rename.ts rename to src/features/chat/thread-rename.ts index 91a4c40e..888161c0 100644 --- a/src/panel/thread-rename.ts +++ b/src/features/chat/thread-rename.ts @@ -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, diff --git a/src/panel/turn-diff-view.ts b/src/features/chat/turn-diff-view.ts similarity index 91% rename from src/panel/turn-diff-view.ts rename to src/features/chat/turn-diff-view.ts index 92f9d1d7..504ddc08 100644 --- a/src/panel/turn-diff-view.ts +++ b/src/features/chat/turn-diff-view.ts @@ -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; diff --git a/src/ui/composer.ts b/src/features/chat/ui/composer.ts similarity index 97% rename from src/ui/composer.ts rename to src/features/chat/ui/composer.ts index 589b8a27..45464fcb 100644 --- a/src/ui/composer.ts +++ b/src/features/chat/ui/composer.ts @@ -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; diff --git a/src/ui/config.ts b/src/features/chat/ui/config.ts similarity index 80% rename from src/ui/config.ts rename to src/features/chat/ui/config.ts index a8a13b68..b1c320a3 100644 --- a/src/ui/config.ts +++ b/src/features/chat/ui/config.ts @@ -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" }); diff --git a/src/ui/execution-state.ts b/src/features/chat/ui/execution-state.ts similarity index 100% rename from src/ui/execution-state.ts rename to src/features/chat/ui/execution-state.ts diff --git a/src/ui/message-stream.ts b/src/features/chat/ui/message-stream.ts similarity index 99% rename from src/ui/message-stream.ts rename to src/features/chat/ui/message-stream.ts index c087b45e..e7da31e7 100644 --- a/src/ui/message-stream.ts +++ b/src/features/chat/ui/message-stream.ts @@ -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 { diff --git a/src/ui/pending-request-message.ts b/src/features/chat/ui/pending-request-message.ts similarity index 98% rename from src/ui/pending-request-message.ts rename to src/features/chat/ui/pending-request-message.ts index dbabc8e8..9135dd90 100644 --- a/src/ui/pending-request-message.ts +++ b/src/features/chat/ui/pending-request-message.ts @@ -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 { diff --git a/src/ui/scroll.ts b/src/features/chat/ui/scroll.ts similarity index 100% rename from src/ui/scroll.ts rename to src/features/chat/ui/scroll.ts diff --git a/src/ui/tool-result.ts b/src/features/chat/ui/tool-result.ts similarity index 96% rename from src/ui/tool-result.ts rename to src/features/chat/ui/tool-result.ts index 5a3a45dc..fcaabac6 100644 --- a/src/ui/tool-result.ts +++ b/src/features/chat/ui/tool-result.ts @@ -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; diff --git a/src/ui/toolbar.ts b/src/features/chat/ui/toolbar.ts similarity index 99% rename from src/ui/toolbar.ts rename to src/features/chat/ui/toolbar.ts index e09ab030..bd988ca7 100644 --- a/src/ui/toolbar.ts +++ b/src/features/chat/ui/toolbar.ts @@ -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"; diff --git a/src/ui/turn-diff.ts b/src/features/chat/ui/turn-diff.ts similarity index 67% rename from src/ui/turn-diff.ts rename to src/features/chat/ui/turn-diff.ts index c22d1974..55acbefc 100644 --- a/src/ui/turn-diff.ts +++ b/src/features/chat/ui/turn-diff.ts @@ -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 { - 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 || " "; -} diff --git a/src/ui/work-items.ts b/src/features/chat/ui/work-items.ts similarity index 98% rename from src/ui/work-items.ts rename to src/features/chat/ui/work-items.ts index 074932a6..3d81b1d0 100644 --- a/src/ui/work-items.ts +++ b/src/features/chat/ui/work-items.ts @@ -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; diff --git a/src/ui/work-message.ts b/src/features/chat/ui/work-message.ts similarity index 100% rename from src/ui/work-message.ts rename to src/features/chat/ui/work-message.ts diff --git a/src/user-input/model.ts b/src/features/chat/user-input/model.ts similarity index 65% rename from src/user-input/model.ts rename to src/features/chat/user-input/model.ts index 588ff023..09895569 100644 --- a/src/user-input/model.ts +++ b/src/features/chat/user-input/model.ts @@ -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; diff --git a/src/panel/view.ts b/src/features/chat/view.ts similarity index 96% rename from src/panel/view.ts rename to src/features/chat/view.ts index 576581e0..2d67a983 100644 --- a/src/panel/view.ts +++ b/src/features/chat/view.ts @@ -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 { diff --git a/src/editor-rewrite/command.ts b/src/features/selection-rewrite/command.ts similarity index 97% rename from src/editor-rewrite/command.ts rename to src/features/selection-rewrite/command.ts index 07c55a3b..d8b0b81b 100644 --- a/src/editor-rewrite/command.ts +++ b/src/features/selection-rewrite/command.ts @@ -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: { diff --git a/src/editor-rewrite/diff.ts b/src/features/selection-rewrite/diff.ts similarity index 100% rename from src/editor-rewrite/diff.ts rename to src/features/selection-rewrite/diff.ts diff --git a/src/editor-rewrite/keys.ts b/src/features/selection-rewrite/keys.ts similarity index 85% rename from src/editor-rewrite/keys.ts rename to src/features/selection-rewrite/keys.ts index 95a5ce37..2daf413d 100644 --- a/src/editor-rewrite/keys.ts +++ b/src/features/selection-rewrite/keys.ts @@ -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; diff --git a/src/editor-rewrite/model.ts b/src/features/selection-rewrite/model.ts similarity index 89% rename from src/editor-rewrite/model.ts rename to src/features/selection-rewrite/model.ts index a5d7c55f..0f6da635 100644 --- a/src/editor-rewrite/model.ts +++ b/src/features/selection-rewrite/model.ts @@ -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"; diff --git a/src/editor-rewrite/output.ts b/src/features/selection-rewrite/output.ts similarity index 95% rename from src/editor-rewrite/output.ts rename to src/features/selection-rewrite/output.ts index d95a593f..72633bda 100644 --- a/src/editor-rewrite/output.ts +++ b/src/features/selection-rewrite/output.ts @@ -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; diff --git a/src/editor-rewrite/popover.ts b/src/features/selection-rewrite/popover.ts similarity index 96% rename from src/editor-rewrite/popover.ts rename to src/features/selection-rewrite/popover.ts index f383ba38..fbc145c9 100644 --- a/src/editor-rewrite/popover.ts +++ b/src/features/selection-rewrite/popover.ts @@ -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 { diff --git a/src/editor-rewrite/position.ts b/src/features/selection-rewrite/position.ts similarity index 100% rename from src/editor-rewrite/position.ts rename to src/features/selection-rewrite/position.ts diff --git a/src/editor-rewrite/prompt.ts b/src/features/selection-rewrite/prompt.ts similarity index 100% rename from src/editor-rewrite/prompt.ts rename to src/features/selection-rewrite/prompt.ts diff --git a/src/editor-rewrite/runner.ts b/src/features/selection-rewrite/runner.ts similarity index 91% rename from src/editor-rewrite/runner.ts rename to src/features/selection-rewrite/runner.ts index 7234ac5f..725f724a 100644 --- a/src/editor-rewrite/runner.ts +++ b/src/features/selection-rewrite/runner.ts @@ -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"; diff --git a/src/main.ts b/src/main.ts index cf6adcf1..99a62c19 100644 --- a/src/main.ts +++ b/src/main.ts @@ -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; diff --git a/src/runtime/state.ts b/src/runtime/state.ts index 079e3fc9..3440f67c 100644 --- a/src/runtime/state.ts +++ b/src/runtime/state.ts @@ -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"; diff --git a/src/settings/dynamic-sections.ts b/src/settings/dynamic-sections.ts index a275ad01..300fe539 100644 --- a/src/settings/dynamic-sections.ts +++ b/src/settings/dynamic-sections.ts @@ -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 { diff --git a/src/settings/tab.ts b/src/settings/tab.ts index 825ab939..469747d0 100644 --- a/src/settings/tab.ts +++ b/src/settings/tab.ts @@ -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"; diff --git a/src/shared/diff/unified.ts b/src/shared/diff/unified.ts new file mode 100644 index 00000000..89ddc73e --- /dev/null +++ b/src/shared/diff/unified.ts @@ -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 { + 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; +} diff --git a/src/ui/clipboard.ts b/src/shared/ui/clipboard.ts similarity index 100% rename from src/ui/clipboard.ts rename to src/shared/ui/clipboard.ts diff --git a/src/ui/components.ts b/src/shared/ui/components.ts similarity index 100% rename from src/ui/components.ts rename to src/shared/ui/components.ts diff --git a/src/ui/dom.ts b/src/shared/ui/dom.ts similarity index 100% rename from src/ui/dom.ts rename to src/shared/ui/dom.ts diff --git a/src/ui/textarea-autogrow.ts b/src/shared/ui/textarea-autogrow.ts similarity index 100% rename from src/ui/textarea-autogrow.ts rename to src/shared/ui/textarea-autogrow.ts diff --git a/tests/app-server/app-server-logs.test.ts b/tests/app-server/app-server-logs.test.ts index c9248867..ac0f3866 100644 --- a/tests/app-server/app-server-logs.test.ts +++ b/tests/app-server/app-server-logs.test.ts @@ -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", () => { diff --git a/tests/approvals/approvals.test.ts b/tests/approvals/approvals.test.ts index 26141f8d..c40105ef 100644 --- a/tests/approvals/approvals.test.ts +++ b/tests/approvals/approvals.test.ts @@ -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"; diff --git a/tests/composer/composer-keys.test.ts b/tests/composer/composer-keys.test.ts index d47936c5..1a47ec9f 100644 --- a/tests/composer/composer-keys.test.ts +++ b/tests/composer/composer-keys.test.ts @@ -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", diff --git a/tests/composer/composer-suggestions.test.ts b/tests/composer/composer-suggestions.test.ts index 76811ecb..c41c3786 100644 --- a/tests/composer/composer-suggestions.test.ts +++ b/tests/composer/composer-suggestions.test.ts @@ -11,7 +11,7 @@ import { findWikiLinkSuggestions, nextComposerSuggestionIndex, parseSlashCommand, -} from "../../src/composer/suggestions"; +} from "../../src/features/chat/composer/suggestions"; function expectPresent(value: T | null | undefined): T { if (value === null || value === undefined) throw new Error("Expected value to be present"); diff --git a/tests/composer/obsidian-context.test.ts b/tests/composer/obsidian-context.test.ts index f6bfc859..7c21e124 100644 --- a/tests/composer/obsidian-context.test.ts +++ b/tests/composer/obsidian-context.test.ts @@ -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", () => { diff --git a/tests/composer/slash-commands.test.ts b/tests/composer/slash-commands.test.ts index 7986c66f..169425e4 100644 --- a/tests/composer/slash-commands.test.ts +++ b/tests/composer/slash-commands.test.ts @@ -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 { return { diff --git a/tests/composer/wikilink-context.test.ts b/tests/composer/wikilink-context.test.ts index 18394efc..d2c81993 100644 --- a/tests/composer/wikilink-context.test.ts +++ b/tests/composer/wikilink-context.test.ts @@ -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", () => { diff --git a/tests/display/display-model.test.ts b/tests/display/display-model.test.ts index dea4d944..13de510c 100644 --- a/tests/display/display-model.test.ts +++ b/tests/display/display-model.test.ts @@ -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"; diff --git a/tests/editor-rewrite/editor-rewrite.test.ts b/tests/editor-rewrite/editor-rewrite.test.ts index 3341048e..78fb5ff2 100644 --- a/tests/editor-rewrite/editor-rewrite.test.ts +++ b/tests/editor-rewrite/editor-rewrite.test.ts @@ -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"; diff --git a/tests/panel/diagnostics.test.ts b/tests/panel/diagnostics.test.ts index 20b57c3a..db5e9be4 100644 --- a/tests/panel/diagnostics.test.ts +++ b/tests/panel/diagnostics.test.ts @@ -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", () => { diff --git a/tests/panel/markdown-file-links.test.ts b/tests/panel/markdown-file-links.test.ts index 5ff10dc7..ae5efb8e 100644 --- a/tests/panel/markdown-file-links.test.ts +++ b/tests/panel/markdown-file-links.test.ts @@ -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", () => { diff --git a/tests/panel/mcp-status.test.ts b/tests/panel/mcp-status.test.ts index da2a656a..38353992 100644 --- a/tests/panel/mcp-status.test.ts +++ b/tests/panel/mcp-status.test.ts @@ -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 { return { diff --git a/tests/panel/panel-controller.test.ts b/tests/panel/panel-controller.test.ts index a61303ab..7dc9334d 100644 --- a/tests/panel/panel-controller.test.ts +++ b/tests/panel/panel-controller.test.ts @@ -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"; diff --git a/tests/panel/panel-inbound-routing.test.ts b/tests/panel/panel-inbound-routing.test.ts index 438d25d3..a5f96a08 100644 --- a/tests/panel/panel-inbound-routing.test.ts +++ b/tests/panel/panel-inbound-routing.test.ts @@ -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"; diff --git a/tests/panel/rollback.test.ts b/tests/panel/rollback.test.ts index 178c8ce8..71b1b0bd 100644 --- a/tests/panel/rollback.test.ts +++ b/tests/panel/rollback.test.ts @@ -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(value: T | null | undefined): T { if (value === null || value === undefined) throw new Error("Expected value to be present"); diff --git a/tests/panel/status-lines.test.ts b/tests/panel/status-lines.test.ts index ee66474d..686aa5e2 100644 --- a/tests/panel/status-lines.test.ts +++ b/tests/panel/status-lines.test.ts @@ -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", () => { diff --git a/tests/panel/thread-naming.test.ts b/tests/panel/thread-naming.test.ts index 9bb68047..bb86b070 100644 --- a/tests/panel/thread-naming.test.ts +++ b/tests/panel/thread-naming.test.ts @@ -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"; diff --git a/tests/settings/settings-tab.test.ts b/tests/settings/settings-tab.test.ts index ead56f03..ac88766b 100644 --- a/tests/settings/settings-tab.test.ts +++ b/tests/settings/settings-tab.test.ts @@ -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); diff --git a/tests/threads/export.test.ts b/tests/threads/export.test.ts index bae26158..cae6ea0b 100644 --- a/tests/threads/export.test.ts +++ b/tests/threads/export.test.ts @@ -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", () => { diff --git a/tests/threads/reference.test.ts b/tests/threads/reference.test.ts index b84de1d3..2c5a28fd 100644 --- a/tests/threads/reference.test.ts +++ b/tests/threads/reference.test.ts @@ -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 { return { diff --git a/tests/threads/threads.test.ts b/tests/threads/threads.test.ts index 91bf8be0..bc8c5d2b 100644 --- a/tests/threads/threads.test.ts +++ b/tests/threads/threads.test.ts @@ -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", () => { diff --git a/tests/ui/view-dom.test.ts b/tests/ui/view-dom.test.ts index 7b212259..3c25f9e8 100644 --- a/tests/ui/view-dom.test.ts +++ b/tests/ui/view-dom.test.ts @@ -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(); diff --git a/tests/ui/view-renderers.test.ts b/tests/ui/view-renderers.test.ts index dcbe3e95..bd765828 100644 --- a/tests/ui/view-renderers.test.ts +++ b/tests/ui/view-renderers.test.ts @@ -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(); diff --git a/tests/ui/view-scroll.test.ts b/tests/ui/view-scroll.test.ts index 843f8966..868fd6c5 100644 --- a/tests/ui/view-scroll.test.ts +++ b/tests/ui/view-scroll.test.ts @@ -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", () => { diff --git a/tests/user-input/user-input.test.ts b/tests/user-input/user-input.test.ts index 95e5dcbf..3c25d1bc 100644 --- a/tests/user-input/user-input.test.ts +++ b/tests/user-input/user-input.test.ts @@ -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(value: T | null | undefined): T {