mirror of
https://github.com/murashit/codex-panel.git
synced 2026-07-22 06:57:10 +00:00
Move DOM boundary lint to Biome
This commit is contained in:
parent
199a757618
commit
a23ccfbd82
62 changed files with 148 additions and 297 deletions
|
|
@ -17,6 +17,7 @@
|
|||
"./scripts/lint/no-chat-domain-outer-layer-imports.grit",
|
||||
"./scripts/lint/no-chat-signal-imports.grit",
|
||||
"./scripts/lint/no-chat-state-escape-hatches.grit",
|
||||
"./scripts/lint/no-implicit-dom-bridges.grit",
|
||||
"./scripts/lint/no-pure-chat-state-side-effects.grit",
|
||||
"./scripts/lint/no-ui-root-imports.grit",
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ Chat-visible state belongs in `ChatStateStore` and named reducer actions. Signal
|
|||
|
||||
Preact Signals are a shell-local projection adapter, not a second state system. Surface projections should read narrow shell-state contracts instead of making components or presenters depend on broad reducer slices. Domain, application, host, presentation, and component modules should keep using pure selectors, reducer actions, and explicit props rather than importing signals directly.
|
||||
|
||||
Imperative DOM bridges are allowed when an external API or measurement problem requires an `HTMLElement`. They should not become a second UI composition system inside Preact-owned surfaces.
|
||||
Imperative DOM bridges are allowed when an external API or measurement problem requires an `HTMLElement`. They should be named as `.dom`, `.obsidian`, or `.measure` files and should not become a second UI composition system inside Preact-owned surfaces.
|
||||
|
||||
## Interaction Principles
|
||||
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ Use this as the normal edit loop. `npm run format` applies Biome formatting and
|
|||
|
||||
Use focused scripts for tight loops: `npm run typecheck`, `npm run test`, `npm run build`, or the targeted `npm run check:*` scripts. CI and release preflight run the same `npm run check` command as local development.
|
||||
|
||||
Biome owns formatting, import organization, general JavaScript/TypeScript/JSON/CSS linting, GritQL source-shape plugins, GritQL import-boundary restrictions, and GritQL CSS source policy. Biome warnings fail `npm run check`; info diagnostics stay advisory, including accessibility while Obsidian-specific UI semantics are reviewed rule by rule. The CSS usage script still checks dead authored classes. TypeScript owns strict type checking, unused locals and parameters, implicit return coverage, switch fallthrough prevention, and deep-readonly `ChatState` snapshots. ESLint remains for typed strict TypeScript rules that are not compiler options, Obsidian plugin policy, and the imperative-DOM responsibility-boundary rule that needs TypeScript type information. The typed strict preset is used with `@typescript-eslint/require-await` disabled because Obsidian and panel-owned async-shaped boundaries sometimes return already-complete work.
|
||||
Biome owns formatting, import organization, general JavaScript/TypeScript/JSON/CSS linting, GritQL source-shape plugins, GritQL import-boundary restrictions, imperative-DOM bridge naming, and GritQL CSS source policy. Biome warnings fail `npm run check`; info diagnostics stay advisory, including accessibility while Obsidian-specific UI semantics are reviewed rule by rule. The CSS usage script still checks dead authored classes. TypeScript owns strict type checking, unused locals and parameters, implicit return coverage, switch fallthrough prevention, and deep-readonly `ChatState` snapshots. ESLint remains for typed strict TypeScript rules that are not compiler options and Obsidian plugin policy. The typed strict preset is used with `@typescript-eslint/require-await` disabled because Obsidian and panel-owned async-shaped boundaries sometimes return already-complete work.
|
||||
|
||||
## Generated and Loaded Files
|
||||
|
||||
|
|
@ -61,7 +61,7 @@ Generated app-server types should stay behind `src/app-server/` or chat-local ap
|
|||
|
||||
Chat panel-visible state belongs in `ChatStateStore` and should flow through named reducer actions and the shell-state adapter. Use Preact Signals only in `src/features/chat/panel/shell-state.tsx`; lint enforces this boundary. When a surface needs fewer dependencies, add or reuse a named shell-state projection instead of importing `@preact/signals` elsewhere.
|
||||
|
||||
Use imperative DOM writes only for explicit bridge modules, Obsidian-owned API boundaries, or rendering and measurement code that cannot be expressed cleanly as Preact components.
|
||||
Use imperative DOM writes only for explicit bridge modules, Obsidian-owned API boundaries, or rendering and measurement code that cannot be expressed cleanly as Preact components. Name those files with a `.dom`, `.obsidian`, or `.measure` suffix so Biome can enforce the boundary without file-specific allowlists.
|
||||
|
||||
## CSS Rules
|
||||
|
||||
|
|
|
|||
|
|
@ -1,91 +1,9 @@
|
|||
import { defineConfig } from "eslint/config";
|
||||
import obsidianmd from "eslint-plugin-obsidianmd";
|
||||
import ts from "typescript";
|
||||
import tseslint from "typescript-eslint";
|
||||
|
||||
const sourceTypeScriptFiles = ["src/**/*.{ts,tsx}"];
|
||||
const strictTypeCheckedTypeScriptRules = Object.assign({}, ...tseslint.configs.strictTypeChecked.map((config) => config.rules ?? {}));
|
||||
// This local rule needs TypeScript type information. Keep it validated by
|
||||
// eslint over real source files instead of synthetic Vitest fixtures, which
|
||||
// would start the TypeScript project service during the test suite.
|
||||
const chatExternalDomBridgeFiles = [
|
||||
"src/features/chat/ui/message-stream/markdown-renderer.ts",
|
||||
"src/features/chat/ui/message-stream/stream-markdown-renderer.ts",
|
||||
"src/features/chat/ui/message-stream/flow-scroll.ts",
|
||||
];
|
||||
const chatPreactDomBridgeFiles = [
|
||||
"src/features/chat/ui/message-stream/text-content.tsx",
|
||||
"src/features/chat/ui/message-stream/detail.tsx",
|
||||
"src/features/chat/ui/message-stream/viewport.tsx",
|
||||
"src/features/chat/ui/composer-dom.ts",
|
||||
"src/features/chat/panel/shell.tsx",
|
||||
"src/features/chat/ui/turn-diff/render.tsx",
|
||||
];
|
||||
const chatImperativeDomBridgeFiles = [...chatExternalDomBridgeFiles, ...chatPreactDomBridgeFiles];
|
||||
const nonChatImperativeDomBridgeFiles = [
|
||||
"src/features/selection-rewrite/popover.tsx",
|
||||
"src/features/thread-picker/modal.ts",
|
||||
"src/features/threads-view/renderer.tsx",
|
||||
"src/settings/tab.tsx",
|
||||
"src/shared/diff/render.ts",
|
||||
"src/shared/ui/components.tsx",
|
||||
"src/shared/ui/textarea-autogrow.ts",
|
||||
"src/shared/ui/textarea-caret.ts",
|
||||
"src/shared/ui/ui-root.tsx",
|
||||
];
|
||||
const imperativeDomWriteMethods = new Set([
|
||||
"addClass",
|
||||
"addClasses",
|
||||
"append",
|
||||
"appendChild",
|
||||
"after",
|
||||
"before",
|
||||
"createDiv",
|
||||
"createEl",
|
||||
"createSpan",
|
||||
"empty",
|
||||
"hide",
|
||||
"insertAdjacentElement",
|
||||
"insertAdjacentHTML",
|
||||
"insertAdjacentText",
|
||||
"insertBefore",
|
||||
"prepend",
|
||||
"removeClass",
|
||||
"removeClasses",
|
||||
"remove",
|
||||
"removeChild",
|
||||
"replaceChildren",
|
||||
"replaceWith",
|
||||
"setCssProps",
|
||||
"setCssStyles",
|
||||
"setText",
|
||||
"show",
|
||||
"setAttr",
|
||||
"toggleClass",
|
||||
]);
|
||||
const imperativeDomEventMethods = new Set(["addEventListener", "removeEventListener"]);
|
||||
const imperativeDomAssignmentProperties = new Set([
|
||||
"checked",
|
||||
"innerHTML",
|
||||
"onblur",
|
||||
"onchange",
|
||||
"onclick",
|
||||
"ondblclick",
|
||||
"onfocus",
|
||||
"oninput",
|
||||
"onkeydown",
|
||||
"onkeyup",
|
||||
"onmousedown",
|
||||
"onmousemove",
|
||||
"onmouseup",
|
||||
"onpointerdown",
|
||||
"onpointerup",
|
||||
"onscroll",
|
||||
"onselect",
|
||||
"outerHTML",
|
||||
"textContent",
|
||||
"value",
|
||||
]);
|
||||
|
||||
export default defineConfig([
|
||||
{
|
||||
|
|
@ -132,7 +50,6 @@ export default defineConfig([
|
|||
{
|
||||
files: sourceTypeScriptFiles,
|
||||
plugins: {
|
||||
"codex-panel": codexPanelEslintPlugin(),
|
||||
obsidianmd,
|
||||
},
|
||||
rules: {
|
||||
|
|
@ -145,20 +62,6 @@ export default defineConfig([
|
|||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: sourceTypeScriptFiles,
|
||||
ignores: ["src/features/chat/**/*.{ts,tsx}", ...nonChatImperativeDomBridgeFiles],
|
||||
rules: {
|
||||
"codex-panel/no-imperative-dom": "error",
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ["src/features/chat/**/*.{ts,tsx}"],
|
||||
ignores: chatImperativeDomBridgeFiles,
|
||||
rules: {
|
||||
"codex-panel/no-imperative-dom": "error",
|
||||
},
|
||||
},
|
||||
]);
|
||||
|
||||
function obsidianRecommendedConfig(config) {
|
||||
|
|
@ -172,117 +75,3 @@ function obsidianRecommendedConfig(config) {
|
|||
if (config.ignores) obsidianConfig.ignores = config.ignores;
|
||||
return obsidianConfig;
|
||||
}
|
||||
|
||||
function codexPanelEslintPlugin() {
|
||||
return {
|
||||
rules: {
|
||||
"no-imperative-dom": imperativeDomRule(),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function imperativeDomRule() {
|
||||
return {
|
||||
meta: {
|
||||
type: "problem",
|
||||
docs: {
|
||||
description: "Disallow imperative DOM writes and event wiring outside explicit bridge files.",
|
||||
},
|
||||
messages: {
|
||||
event: "Keep imperative DOM event wiring in an explicit bridge module or Obsidian-owned UI boundary.",
|
||||
write: "Keep imperative DOM writes in an explicit bridge module or Obsidian-owned UI boundary.",
|
||||
},
|
||||
schema: [],
|
||||
},
|
||||
create(context) {
|
||||
const typed = typedContext(context);
|
||||
const isDomTarget = (node) => typeIncludesDom(typed.typeAt(node), typed.typeChecker());
|
||||
|
||||
return {
|
||||
AssignmentExpression(node) {
|
||||
if (!isMemberExpression(node.left)) return;
|
||||
const property = staticPropertyName(node.left.property);
|
||||
if (!property || !imperativeDomAssignmentProperties.has(property)) return;
|
||||
if (isDomTarget(node.left.object)) context.report({ node: node.left, messageId: "write" });
|
||||
},
|
||||
CallExpression(node) {
|
||||
if (!isMemberExpression(node.callee)) return;
|
||||
const method = staticPropertyName(node.callee.property);
|
||||
if (!method) return;
|
||||
if (imperativeDomWriteMethods.has(method) && isDomTarget(node.callee.object)) {
|
||||
context.report({ node: node.callee, messageId: "write" });
|
||||
return;
|
||||
}
|
||||
if (imperativeDomEventMethods.has(method) && isDomTarget(node.callee.object)) {
|
||||
context.report({ node: node.callee, messageId: "event" });
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function typedContext(context) {
|
||||
let parserServices = null;
|
||||
let checker = null;
|
||||
|
||||
const services = () => {
|
||||
if (!parserServices) {
|
||||
parserServices = parserServicesFromContext(context);
|
||||
checker = parserServices.program.getTypeChecker();
|
||||
}
|
||||
return parserServices;
|
||||
};
|
||||
const typeChecker = () => {
|
||||
services();
|
||||
return checker;
|
||||
};
|
||||
|
||||
return {
|
||||
typeChecker,
|
||||
typeAt(node) {
|
||||
const tsNode = services().esTreeNodeToTSNodeMap.get(node);
|
||||
return typeChecker().getTypeAtLocation(tsNode);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function isMemberExpression(node) {
|
||||
return node?.type === "MemberExpression";
|
||||
}
|
||||
|
||||
function staticPropertyName(node) {
|
||||
return node?.type === "Identifier" ? node.name : node?.type === "Literal" && typeof node.value === "string" ? node.value : null;
|
||||
}
|
||||
|
||||
function parserServicesFromContext(context) {
|
||||
const services = context.sourceCode.parserServices;
|
||||
if (!services?.program || !services.esTreeNodeToTSNodeMap) {
|
||||
throw new Error("codex-panel typed ESLint rules require TypeScript parser services.");
|
||||
}
|
||||
return services;
|
||||
}
|
||||
|
||||
function typeIncludesDom(type, checker, seen = new Set()) {
|
||||
if (!type || seen.has(type.id)) return false;
|
||||
seen.add(type.id);
|
||||
|
||||
if (type.flags & (ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Never)) return false;
|
||||
if (type.isUnionOrIntersection()) return type.types.some((item) => typeIncludesDom(item, checker, seen));
|
||||
|
||||
const typeName = checker.typeToString(type);
|
||||
if (domTypeName(typeName)) return true;
|
||||
|
||||
const symbolName = type.getSymbol()?.getName() ?? type.aliasSymbol?.getName() ?? "";
|
||||
if (domTypeName(symbolName)) return true;
|
||||
|
||||
const apparent = checker.getApparentType(type);
|
||||
if (apparent !== type && typeIncludesDom(apparent, checker, seen)) return true;
|
||||
|
||||
const bases = typeof type.getBaseTypes === "function" ? (type.getBaseTypes() ?? []) : [];
|
||||
return bases.some((base) => typeIncludesDom(base, checker, seen));
|
||||
}
|
||||
|
||||
function domTypeName(name) {
|
||||
return /\b(?:Document|Element|HTML[A-Za-z]*Element|HTMLElement|Node|SVG[A-Za-z]*Element|SVGElement|Window)\b/.test(name);
|
||||
}
|
||||
|
|
|
|||
22
scripts/lint/no-implicit-dom-bridges.grit
Normal file
22
scripts/lint/no-implicit-dom-bridges.grit
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
language js
|
||||
|
||||
or {
|
||||
`$target.createElement($...)` as $stmt,
|
||||
`$target.createTextNode($...)` as $stmt,
|
||||
`$target.createEl($...)` as $stmt,
|
||||
`$target.createDiv($...)` as $stmt,
|
||||
`$target.createSpan($...)` as $stmt,
|
||||
`$target.empty()` as $stmt,
|
||||
`$target.appendChild($...)` as $stmt,
|
||||
`$target.replaceChildren($...)` as $stmt,
|
||||
`$target.insertAdjacentHTML($...)` as $stmt,
|
||||
`$target.addEventListener($...)` as $stmt,
|
||||
`$target.removeEventListener($...)` as $stmt
|
||||
} where {
|
||||
$filename <: r".*/src/.*\.tsx?$",
|
||||
not { $filename <: r".*\.dom\.tsx?$" },
|
||||
not { $filename <: r".*\.obsidian\.tsx?$" },
|
||||
not { $filename <: r".*\.measure\.tsx?$" },
|
||||
not { $target <: `signal` },
|
||||
register_diagnostic(span=$stmt, message="Keep imperative DOM writes and event wiring in files named with a .dom, .obsidian, or .measure suffix.", severity="error")
|
||||
}
|
||||
|
|
@ -2,12 +2,9 @@ language js
|
|||
|
||||
JsImport() as $stmt where {
|
||||
$filename <: r".*/src/.*",
|
||||
$stmt <: contains `$source` where { $source <: r"^[\"'].*shared/ui/ui-root[\"']$" },
|
||||
not { $filename <: r".*/src/features/chat/panel/shell\.tsx$" },
|
||||
not { $filename <: r".*/src/features/chat/ui/turn-diff/render\.tsx$" },
|
||||
not { $filename <: r".*/src/features/chat/ui/turn-diff/view\.ts$" },
|
||||
not { $filename <: r".*/src/features/selection-rewrite/popover\.tsx$" },
|
||||
not { $filename <: r".*/src/features/threads-view/renderer\.tsx$" },
|
||||
not { $filename <: r".*/src/settings/tab\.tsx$" },
|
||||
$stmt <: contains `$source` where { $source <: r"^[\"'].*shared/ui/ui-root\.dom[\"']$" },
|
||||
not { $filename <: r".*\.dom\.tsx?$" },
|
||||
not { $filename <: r".*\.obsidian\.tsx?$" },
|
||||
not { $filename <: r".*\.measure\.tsx?$" },
|
||||
register_diagnostic(span=$stmt, message="Import the Preact root adapter only from explicit root bridge files.", severity="error")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { ConnectionWorkTracker } from "../../../shared/lifecycle/connection-work
|
|||
import { ChatResumeWorkTracker, type ChatViewDeferredTasks, type RestoredThreadPlaceholderState } from "../application/lifecycle";
|
||||
import type { ChatState } from "../application/state/root-reducer";
|
||||
import { type ChatStateStore, createChatStateStore } from "../application/state/store";
|
||||
import { renderChatPanelShell, unmountChatPanelShell } from "../panel/shell";
|
||||
import { renderChatPanelShell, unmountChatPanelShell } from "../panel/shell.dom";
|
||||
import { type ChatPanelSnapshot, openPanelTurnLifecycle, parseRestoredThreadState } from "../panel/snapshot";
|
||||
import { type ChatMessageScrollController, createChatMessageScrollController } from "../panel/surface/message-stream-scroll";
|
||||
import { createChatViewDeferredTasks } from "./lifecycle";
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { CodexInput } from "../../../domain/chat/input";
|
||||
import { isComposerSendKey, type SendShortcut } from "../../../shared/ui/keyboard";
|
||||
import { textareaCursorAtVisualBoundary } from "../../../shared/ui/textarea-caret";
|
||||
import { textareaCursorAtVisualBoundary } from "../../../shared/ui/textarea-caret.measure";
|
||||
import { type ComposerBoundaryScrollAction, composerBoundaryScrollDirection } from "../application/composer/boundary-scroll";
|
||||
import type { NoteCandidateProvider } from "../application/composer/note-context";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import type { ComponentChild as UiNode } from "preact";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../../shared/ui/ui-root";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../../shared/ui/ui-root.dom";
|
||||
import type { ChatStateStore } from "../application/state/store";
|
||||
import type { ToolbarActions } from "../ui/toolbar";
|
||||
import { type ChatPanelShellState, ChatPanelShellStateContext, createChatPanelShellState, syncChatPanelShellState } from "./shell-state";
|
||||
|
|
@ -6,8 +6,8 @@ import type { PendingRequestBlockActions } from "../../application/pending-reque
|
|||
import type { ChatAction } from "../../application/state/root-reducer";
|
||||
import type { ChatStateStore } from "../../application/state/store";
|
||||
import type { ChatTurnDiffViewState } from "../../domain/turn-diff";
|
||||
import type { MessageStreamScrollControllerBinding } from "../../ui/message-stream/flow-scroll";
|
||||
import { MarkdownMessageRenderer, renderStreamMarkdown } from "../../ui/message-stream/markdown-renderer";
|
||||
import type { MessageStreamScrollControllerBinding } from "../../ui/message-stream/flow-scroll.measure";
|
||||
import { MarkdownMessageRenderer, renderStreamMarkdown } from "../../ui/message-stream/markdown-renderer.obsidian";
|
||||
import { MessageStreamViewport, type MessageStreamViewportState } from "../../ui/message-stream/stream-blocks";
|
||||
import { type ChatPanelMessageStreamShellState, messageStreamStateFromShellState, useChatPanelShellState } from "../shell-state";
|
||||
import {
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import type {
|
|||
MessageStreamScrollCommand,
|
||||
MessageStreamScrollControllerBinding,
|
||||
MessageStreamScrollPort,
|
||||
} from "../../ui/message-stream/flow-scroll";
|
||||
} from "../../ui/message-stream/flow-scroll.measure";
|
||||
|
||||
export interface ChatMessageScrollController extends MessageStreamScrollControllerBinding {
|
||||
showLatest(): void;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import type { ButtonHTMLAttributes, Ref, ComponentChild as UiNode } from "preact";
|
||||
import { useLayoutEffect, useRef, useState } from "preact/hooks";
|
||||
|
||||
import { IconButton } from "../../../shared/ui/components";
|
||||
import { disposeDomListeners, listenDomEvent } from "../../../shared/ui/dom-events";
|
||||
import { syncTextareaHeight } from "../../../shared/ui/textarea-autogrow";
|
||||
import { renderComposerMetaIcon, scrollComposerSuggestionIntoView, updateComposerMetaStatusOverflow } from "./composer-dom";
|
||||
import { IconButton } from "../../../shared/ui/components.obsidian";
|
||||
import { disposeDomListeners, listenDomEvent } from "../../../shared/ui/dom-events.dom";
|
||||
import { syncTextareaHeight } from "../../../shared/ui/textarea-autogrow.measure";
|
||||
import { renderComposerMetaIcon, scrollComposerSuggestionIntoView, updateComposerMetaStatusOverflow } from "./composer.dom";
|
||||
|
||||
export interface ComposerSuggestion {
|
||||
display: string;
|
||||
|
|
|
|||
|
|
@ -2,10 +2,10 @@ import type { ComponentChild as UiNode } from "preact";
|
|||
import { useEffect, useLayoutEffect, useRef, useState } from "preact/hooks";
|
||||
|
||||
import type { ThreadGoal, ThreadGoalStatus } from "../../../domain/threads/goal";
|
||||
import { IconButton } from "../../../shared/ui/components";
|
||||
import { disposeDomListeners, listenDomEvent } from "../../../shared/ui/dom-events";
|
||||
import { IconButton } from "../../../shared/ui/components.obsidian";
|
||||
import { disposeDomListeners, listenDomEvent } from "../../../shared/ui/dom-events.dom";
|
||||
import { isComposerSendKey, type SendShortcut } from "../../../shared/ui/keyboard";
|
||||
import { syncTextareaHeight } from "../../../shared/ui/textarea-autogrow";
|
||||
import { syncTextareaHeight } from "../../../shared/ui/textarea-autogrow.measure";
|
||||
|
||||
export interface GoalPanelActions {
|
||||
onSave: (objective: string, tokenBudget: number | null) => void;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { ComponentChild as UiNode } from "preact";
|
||||
import { useLayoutEffect, useRef } from "preact/hooks";
|
||||
|
||||
import { renderRawDiffLines } from "../../../../shared/diff/render";
|
||||
import { renderRawDiffLines } from "../../../../shared/diff/render.dom";
|
||||
import type { DetailSection, DetailView } from "../../presentation/message-stream/detail-view";
|
||||
import type { MessageStreamDisclosureState } from "./context";
|
||||
|
||||
|
|
@ -6,8 +6,8 @@ import type {
|
|||
MessageStreamViewBlock,
|
||||
} from "../../presentation/message-stream/view-model";
|
||||
import type { MessageStreamContext, PendingRequestBlockContext } from "./context";
|
||||
import { detailNode } from "./detail";
|
||||
import { MessageStreamFlowFrame, type MessageStreamScrollControllerBinding } from "./flow-scroll";
|
||||
import { detailNode } from "./detail.dom";
|
||||
import { MessageStreamFlowFrame, type MessageStreamScrollControllerBinding } from "./flow-scroll.measure";
|
||||
import { pendingRequestBlockNode } from "./pending-request-block";
|
||||
import { agentRunSummaryNode, statusNode } from "./status";
|
||||
import { textNode } from "./text";
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Fragment, type ComponentChild as UiNode } from "preact";
|
||||
import { useEffect, useRef } from "preact/hooks";
|
||||
import { IconButton } from "../../../../shared/ui/components";
|
||||
import { listenDomEvent } from "../../../../shared/ui/dom-events";
|
||||
import { IconButton } from "../../../../shared/ui/components.obsidian";
|
||||
import { listenDomEvent } from "../../../../shared/ui/dom-events.dom";
|
||||
import type {
|
||||
EditedFilesTextView,
|
||||
MentionedFileTextView,
|
||||
|
|
@ -10,7 +10,7 @@ import type {
|
|||
TextItemDetailSectionView,
|
||||
} from "../../presentation/message-stream/text-view";
|
||||
import type { TextItemActionContext, TextItemContext, TextItemDetailStateContext, TextItemMetadataContext } from "./context";
|
||||
import { CollapsibleTextContent, TextContent } from "./text-content";
|
||||
import { CollapsibleTextContent, TextContent } from "./text-content.dom";
|
||||
|
||||
export function textNode(view: MessageStreamTextView, context: TextItemContext): UiNode {
|
||||
return <Text view={view} context={context} />;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import type { ButtonHTMLAttributes, TargetedKeyboardEvent, ComponentChild as UiNode } from "preact";
|
||||
import { useLayoutEffect, useRef } from "preact/hooks";
|
||||
import { IconButton } from "../../../shared/ui/components";
|
||||
import { IconButton } from "../../../shared/ui/components.obsidian";
|
||||
import type { RateLimitSummary } from "../presentation/runtime/status";
|
||||
|
||||
type ButtonProps = ButtonHTMLAttributes & {
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
import type { ComponentChild as UiNode } from "preact";
|
||||
import { useLayoutEffect, useRef } from "preact/hooks";
|
||||
|
||||
import { renderDisplayDiffLines } from "../../../../shared/diff/render";
|
||||
import { renderDisplayDiffLines } from "../../../../shared/diff/render.dom";
|
||||
import { displayDiffLines } from "../../../../shared/diff/unified";
|
||||
import { shortThreadId } from "../../../../shared/id/thread-id";
|
||||
import { IconButton } from "../../../../shared/ui/components";
|
||||
import { renderUiRoot } from "../../../../shared/ui/ui-root";
|
||||
import { IconButton } from "../../../../shared/ui/components.obsidian";
|
||||
import { renderUiRoot } from "../../../../shared/ui/ui-root.dom";
|
||||
import type { ChatTurnDiffViewState, PersistedChatTurnDiffViewState } from "../../domain/turn-diff";
|
||||
|
||||
export interface ChatTurnDiffViewActions {
|
||||
|
|
@ -2,14 +2,14 @@ import { ItemView, type ViewStateResult } from "obsidian";
|
|||
|
||||
import { VIEW_TYPE_CODEX_TURN_DIFF } from "../../../../constants";
|
||||
import { copyTextWithNotice } from "../../../../shared/ui/clipboard";
|
||||
import { unmountUiRoot } from "../../../../shared/ui/ui-root";
|
||||
import { unmountUiRoot } from "../../../../shared/ui/ui-root.dom";
|
||||
import {
|
||||
type ChatTurnDiffViewState,
|
||||
isPersistedChatTurnDiffViewState,
|
||||
type PersistedChatTurnDiffViewState,
|
||||
persistedChatTurnDiffViewState,
|
||||
} from "../../domain/turn-diff";
|
||||
import { renderChatTurnDiffView } from "./render";
|
||||
import { renderChatTurnDiffView } from "./render.dom";
|
||||
|
||||
export class CodexChatTurnDiffView extends ItemView {
|
||||
private metadata: PersistedChatTurnDiffViewState | null = null;
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { type Editor, MarkdownView, Notice, type Plugin } from "obsidian";
|
||||
import type { SendShortcut } from "../../shared/ui/keyboard";
|
||||
import type { SelectionRewriteRuntimeSettings, SelectionRewriteState } from "./model";
|
||||
import { SelectionRewritePopover } from "./popover";
|
||||
import { SelectionRewritePopover } from "./popover.dom";
|
||||
|
||||
export interface SelectionRewriteCommandHost extends Plugin {
|
||||
settings: {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,13 @@ import { type Editor, Notice } from "obsidian";
|
|||
import type { TargetedKeyboardEvent, ComponentChild as UiNode } from "preact";
|
||||
import { useLayoutEffect, useRef } from "preact/hooks";
|
||||
|
||||
import { renderDisplayDiffLines } from "../../shared/diff/render";
|
||||
import { renderDisplayDiffLines } from "../../shared/diff/render.dom";
|
||||
import { displayDiffLines } from "../../shared/diff/unified";
|
||||
import { IconButton } from "../../shared/ui/components";
|
||||
import { IconButton } from "../../shared/ui/components.obsidian";
|
||||
import { isComposerSendKey, type SendShortcut } from "../../shared/ui/keyboard";
|
||||
import { syncTextareaHeight } from "../../shared/ui/textarea-autogrow";
|
||||
import { type TextareaCaretBoundaryDirection, textareaCursorAtVisualBoundary } from "../../shared/ui/textarea-caret";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../shared/ui/ui-root";
|
||||
import { syncTextareaHeight } from "../../shared/ui/textarea-autogrow.measure";
|
||||
import { type TextareaCaretBoundaryDirection, textareaCursorAtVisualBoundary } from "../../shared/ui/textarea-caret.measure";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../shared/ui/ui-root.dom";
|
||||
import { buildSelectionUnifiedDiff } from "./diff";
|
||||
import { canApplySelectionRewrite, type SelectionRewriteRuntimeSettings, type SelectionRewriteState } from "./model";
|
||||
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import type { TextareaCaretBoundaryDirection } from "../../shared/ui/textarea-caret";
|
||||
import type { TextareaCaretBoundaryDirection } from "../../shared/ui/textarea-caret.measure";
|
||||
import {
|
||||
type SelectionRewriteLifecycleEvent,
|
||||
type SelectionRewriteRuntimeSettings,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import type { ButtonHTMLAttributes, TargetedKeyboardEvent, ComponentChild as UiNode } from "preact";
|
||||
import { useLayoutEffect, useRef } from "preact/hooks";
|
||||
|
||||
import { IconButton } from "../../shared/ui/components";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../shared/ui/ui-root";
|
||||
import { IconButton } from "../../shared/ui/components.obsidian";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../shared/ui/ui-root.dom";
|
||||
import type { ThreadsRowModel } from "./state";
|
||||
|
||||
type ButtonProps = ButtonHTMLAttributes & {
|
||||
|
|
@ -12,7 +12,7 @@ import type { OpenCodexPanelSnapshot } from "../../workspace/panel-coordinator";
|
|||
import type { ThreadCatalogActiveReader, ThreadCatalogEventSink } from "../../workspace/thread-catalog";
|
||||
import { createThreadOperations, type ThreadOperations } from "../threads/thread-operations";
|
||||
import { createThreadTitleService, type ThreadTitleService } from "../threads/thread-title-service";
|
||||
import { renderThreadsView, unmountThreadsView } from "./renderer";
|
||||
import { renderThreadsView, unmountThreadsView } from "./renderer.dom";
|
||||
import {
|
||||
type ThreadsGeneratingRenameState,
|
||||
type ThreadsRenameLifecycleEvent,
|
||||
|
|
|
|||
|
|
@ -2,12 +2,12 @@ import { Plugin } from "obsidian";
|
|||
|
||||
import { VIEW_TYPE_CODEX_PANEL, VIEW_TYPE_CODEX_THREADS, VIEW_TYPE_CODEX_TURN_DIFF } from "./constants";
|
||||
import { CodexChatView } from "./features/chat/host/view";
|
||||
import { CodexChatTurnDiffView } from "./features/chat/ui/turn-diff/view";
|
||||
import { CodexChatTurnDiffView } from "./features/chat/ui/turn-diff/view.obsidian";
|
||||
import { registerSelectionRewriteCommand } from "./features/selection-rewrite/command";
|
||||
import { CodexThreadsView } from "./features/threads-view/view";
|
||||
import { CodexPanelRuntime } from "./plugin-runtime";
|
||||
import { type CodexPanelSettings, DEFAULT_SETTINGS, getVaultPath, normalizeSettings, settingsMatchNormalizedData } from "./settings/model";
|
||||
import { CodexPanelSettingTab } from "./settings/tab";
|
||||
import { CodexPanelSettingTab } from "./settings/tab.obsidian";
|
||||
|
||||
export default class CodexPanelPlugin extends Plugin {
|
||||
settings: CodexPanelSettings = DEFAULT_SETTINGS;
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ import type {
|
|||
ChatViewLifecycleSurface,
|
||||
ChatWorkspacePanelSurface,
|
||||
} from "./features/chat/host/surface-handle";
|
||||
import { CodexChatTurnDiffView } from "./features/chat/ui/turn-diff/view";
|
||||
import { openThreadPicker, type ThreadPickerHost } from "./features/thread-picker/modal";
|
||||
import { CodexChatTurnDiffView } from "./features/chat/ui/turn-diff/view.obsidian";
|
||||
import { openThreadPicker, type ThreadPickerHost } from "./features/thread-picker/modal.obsidian";
|
||||
import type { CodexThreadsHost, CodexThreadsSettingsAccess } from "./features/threads-view/session";
|
||||
import { CodexThreadsView } from "./features/threads-view/view";
|
||||
import type { CodexPanelSettingTabHost } from "./settings/host";
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ import type { ComponentChild as UiNode } from "preact";
|
|||
import type { Thread } from "../domain/threads/model";
|
||||
import { threadArchiveDisplayTitle } from "../domain/threads/title";
|
||||
import { shortThreadId } from "../shared/id/thread-id";
|
||||
import { ObsidianExtraButton, ObsidianTextInput, ObsidianToggle } from "../shared/ui/components";
|
||||
import { ObsidianExtraButton, ObsidianTextInput, ObsidianToggle } from "../shared/ui/components.obsidian";
|
||||
import type { ArchivedThreadSectionState } from "./section-state";
|
||||
import { SettingRow, SettingsGroup, SettingsHeading, SettingsItems, SettingsStatusRow } from "./setting-components";
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import type { ComponentChild as UiNode } from "preact";
|
|||
|
||||
import type { ModelMetadata, ReasoningEffort } from "../domain/catalog/metadata";
|
||||
import { findModelMetadataByIdOrName, supportedEffortsForModelMetadata } from "../domain/catalog/metadata";
|
||||
import { ObsidianDropdown } from "../shared/ui/components";
|
||||
import { ObsidianDropdown } from "../shared/ui/components.obsidian";
|
||||
import type { HelperSettingsState } from "./section-state";
|
||||
import { SettingRow, SettingsGroup, SettingsHeading, SettingsItems } from "./setting-components";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import type { ComponentChild as UiNode } from "preact";
|
||||
|
||||
import type { HookItem } from "../domain/catalog/metadata";
|
||||
import { ObsidianButton } from "../shared/ui/components";
|
||||
import { ObsidianButton } from "../shared/ui/components.obsidian";
|
||||
import type { HookSectionState } from "./section-state";
|
||||
import { SettingRow, SettingsGroup, SettingsHeading, SettingsItems, SettingsStatusRow } from "./setting-components";
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import { type App, Notice, type Plugin, PluginSettingTab, Setting, setIcon } fro
|
|||
|
||||
import { DEFAULT_CODEX_PATH } from "../constants";
|
||||
import type { ReasoningEffort } from "../domain/catalog/metadata";
|
||||
import { renderUiRoot, unmountUiRoot } from "../shared/ui/ui-root";
|
||||
import { renderUiRoot, unmountUiRoot } from "../shared/ui/ui-root.dom";
|
||||
import { ArchivedThreadSection } from "./archived-section";
|
||||
import { SettingsDynamicDataController, type SettingsDynamicDataDisplayTarget } from "./dynamic-data-controller";
|
||||
import { HelperSettingsSection } from "./helper-section";
|
||||
|
|
@ -9,7 +9,7 @@ import { createChatStateStore } from "../../../../../src/features/chat/applicati
|
|||
import { ChatComposerController, type ChatComposerRenderActions } from "../../../../../src/features/chat/panel/composer-controller";
|
||||
import type { ChatPanelComposerShellState } from "../../../../../src/features/chat/panel/shell-state";
|
||||
import { ComposerShell } from "../../../../../src/features/chat/ui/composer";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../../../../src/shared/ui/ui-root";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../../../../src/shared/ui/ui-root.dom";
|
||||
import { installObsidianDomShims } from "../../../../support/dom";
|
||||
import { composerShellStateFromChatState } from "../../support/shell-state";
|
||||
|
||||
|
|
|
|||
|
|
@ -4,13 +4,13 @@ import { act } from "preact/test-utils";
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
import { messageStreamItems } from "../../../../src/features/chat/application/state/message-stream";
|
||||
import { createChatStateStore } from "../../../../src/features/chat/application/state/store";
|
||||
import { type ChatPanelShellParts, renderChatPanelShell, unmountChatPanelShell } from "../../../../src/features/chat/panel/shell";
|
||||
import { type ChatPanelShellParts, renderChatPanelShell, unmountChatPanelShell } from "../../../../src/features/chat/panel/shell.dom";
|
||||
import type { ChatPanelComposerSurface } from "../../../../src/features/chat/panel/surface/composer-projection";
|
||||
import type { ChatPanelGoalSurface } from "../../../../src/features/chat/panel/surface/goal-projection";
|
||||
import type { ChatPanelToolbarSurface } from "../../../../src/features/chat/panel/surface/toolbar-projection";
|
||||
import { messageStreamViewBlocks } from "../../../../src/features/chat/presentation/message-stream/view-model";
|
||||
import type { MessageStreamContext } from "../../../../src/features/chat/ui/message-stream/context";
|
||||
import type { MessageStreamScrollControllerBinding } from "../../../../src/features/chat/ui/message-stream/flow-scroll";
|
||||
import type { MessageStreamScrollControllerBinding } from "../../../../src/features/chat/ui/message-stream/flow-scroll.measure";
|
||||
import { installObsidianDomShims } from "../../../support/dom";
|
||||
|
||||
installObsidianDomShims();
|
||||
|
|
|
|||
|
|
@ -16,9 +16,9 @@ import {
|
|||
createChatMessageScrollController,
|
||||
} from "../../../../../src/features/chat/panel/surface/message-stream-scroll";
|
||||
import { MESSAGE_CONTENT_RENDERED_EVENT } from "../../../../../src/features/chat/ui/message-stream/content-events";
|
||||
import { MarkdownMessageRenderer } from "../../../../../src/features/chat/ui/message-stream/markdown-renderer";
|
||||
import { MarkdownMessageRenderer } from "../../../../../src/features/chat/ui/message-stream/markdown-renderer.obsidian";
|
||||
import { MessageStreamViewport } from "../../../../../src/features/chat/ui/message-stream/stream-blocks";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../../../../src/shared/ui/ui-root";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../../../../src/shared/ui/ui-root.dom";
|
||||
import { notices } from "../../../../mocks/obsidian";
|
||||
import { installObsidianDomShims } from "../../../../support/dom";
|
||||
import { withChatStateMessageStreamItems } from "../../support/message-stream";
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import { ChatPanelGoal, type ChatPanelGoalSurface } from "../../../../../src/fea
|
|||
import { ChatPanelToolbar } from "../../../../../src/features/chat/panel/surface/toolbar-projection";
|
||||
import { effortStatusLines, modelStatusLines, statusSummaryLines } from "../../../../../src/features/chat/presentation/runtime/status";
|
||||
import type { ToolbarActions } from "../../../../../src/features/chat/ui/toolbar";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../../../../src/shared/ui/ui-root";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../../../../src/shared/ui/ui-root.dom";
|
||||
import { installObsidianDomShims } from "../../../../support/dom";
|
||||
import { withChatStateMessageStreamItems } from "../../support/message-stream";
|
||||
import { composerShellStateFromChatState } from "../../support/shell-state";
|
||||
|
|
|
|||
|
|
@ -6,13 +6,13 @@ import { describe, expect, it, vi } from "vitest";
|
|||
import type { Thread } from "../../../../src/domain/threads/model";
|
||||
import { createChatStateStore } from "../../../../src/features/chat/application/state/store";
|
||||
import type { ThreadManagementActions } from "../../../../src/features/chat/application/threads/thread-management-actions";
|
||||
import { type ChatPanelShellParts, renderChatPanelShell, unmountChatPanelShell } from "../../../../src/features/chat/panel/shell";
|
||||
import { type ChatPanelShellParts, renderChatPanelShell, unmountChatPanelShell } from "../../../../src/features/chat/panel/shell.dom";
|
||||
import type { ChatPanelComposerSurface } from "../../../../src/features/chat/panel/surface/composer-projection";
|
||||
import type { ChatPanelGoalSurface } from "../../../../src/features/chat/panel/surface/goal-projection";
|
||||
import type { ChatPanelToolbarSurface } from "../../../../src/features/chat/panel/surface/toolbar-projection";
|
||||
import { createToolbarPanelActions, type ToolbarPanelActions } from "../../../../src/features/chat/panel/toolbar-actions";
|
||||
import type { MessageStreamContext } from "../../../../src/features/chat/ui/message-stream/context";
|
||||
import type { MessageStreamScrollControllerBinding } from "../../../../src/features/chat/ui/message-stream/flow-scroll";
|
||||
import type { MessageStreamScrollControllerBinding } from "../../../../src/features/chat/ui/message-stream/flow-scroll.measure";
|
||||
import { installObsidianDomShims } from "../../../support/dom";
|
||||
|
||||
installObsidianDomShims();
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { describe, expect, it, vi } from "vitest";
|
|||
import { implementPlanTargetFromState } from "../../../../../src/features/chat/application/conversation/plan-implementation";
|
||||
import type { MessageStreamItem } from "../../../../../src/features/chat/domain/message-stream/items";
|
||||
import { MESSAGE_CONTENT_RENDERED_EVENT } from "../../../../../src/features/chat/ui/message-stream/content-events";
|
||||
import { MarkdownMessageRenderer } from "../../../../../src/features/chat/ui/message-stream/markdown-renderer";
|
||||
import { MarkdownMessageRenderer } from "../../../../../src/features/chat/ui/message-stream/markdown-renderer.obsidian";
|
||||
import { deferred } from "../../../../support/async";
|
||||
import { attributeValues, textContents, topLevelDetailsSummaries } from "../../../../support/dom";
|
||||
import "./setup";
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import {
|
|||
type MessageStreamScrollCommand,
|
||||
type MessageStreamScrollControllerBinding,
|
||||
type MessageStreamScrollPort,
|
||||
} from "../../../../../src/features/chat/ui/message-stream/flow-scroll";
|
||||
import { renderUiRoot } from "../../../../../src/shared/ui/ui-root";
|
||||
} from "../../../../../src/features/chat/ui/message-stream/flow-scroll.measure";
|
||||
import { renderUiRoot } from "../../../../../src/shared/ui/ui-root.dom";
|
||||
import { installObsidianDomShims } from "../../../../support/dom";
|
||||
|
||||
installObsidianDomShims();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { TFile } from "obsidian";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { renderStreamMarkdown } from "../../../../../src/features/chat/ui/message-stream/markdown-renderer";
|
||||
import { renderStreamMarkdown } from "../../../../../src/features/chat/ui/message-stream/markdown-renderer.obsidian";
|
||||
import { notices } from "../../../../mocks/obsidian";
|
||||
import { installObsidianDomShims } from "../../../../support/dom";
|
||||
|
||||
|
|
|
|||
|
|
@ -16,10 +16,10 @@ import type {
|
|||
PendingRequestBlockActions,
|
||||
PendingRequestBlockContext,
|
||||
} from "../../../../../src/features/chat/ui/message-stream/context";
|
||||
import type { MessageStreamScrollControllerBinding } from "../../../../../src/features/chat/ui/message-stream/flow-scroll";
|
||||
import type { MessageStreamScrollControllerBinding } from "../../../../../src/features/chat/ui/message-stream/flow-scroll.measure";
|
||||
import { pendingRequestBlockNode } from "../../../../../src/features/chat/ui/message-stream/pending-request-block";
|
||||
import { MessageStreamViewport } from "../../../../../src/features/chat/ui/message-stream/stream-blocks";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../../../../src/shared/ui/ui-root";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../../../../src/shared/ui/ui-root.dom";
|
||||
|
||||
export function messageStreamBlocks(context: TestMessageStreamContext): [MessageStreamViewBlock, ...MessageStreamViewBlock[]] {
|
||||
const normalized = normalizeMessageStreamContext(context);
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ import {
|
|||
type ComposerSuggestion,
|
||||
syncComposerHeight,
|
||||
} from "../../../../../src/features/chat/ui/composer";
|
||||
import { scrollComposerSuggestionIntoView } from "../../../../../src/features/chat/ui/composer-dom";
|
||||
import { renderUiRoot } from "../../../../../src/shared/ui/ui-root";
|
||||
import { scrollComposerSuggestionIntoView } from "../../../../../src/features/chat/ui/composer.dom";
|
||||
import { renderUiRoot } from "../../../../../src/shared/ui/ui-root.dom";
|
||||
import { waitForAsyncWork } from "../../../../support/async";
|
||||
import { changeInputValue, composerSuggestionScrollFixture, installObsidianDomShims } from "../../../../support/dom";
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import { describe, expect, it, vi } from "vitest";
|
|||
import type { ThreadGoal } from "../../../../../src/domain/threads/goal";
|
||||
import { GoalPanel, type GoalPanelActions, type GoalPanelEditorState } from "../../../../../src/features/chat/ui/goal";
|
||||
import type { SendShortcut } from "../../../../../src/shared/ui/keyboard";
|
||||
import { renderUiRoot } from "../../../../../src/shared/ui/ui-root";
|
||||
import { renderUiRoot } from "../../../../../src/shared/ui/ui-root.dom";
|
||||
import { installObsidianDomShims } from "../../../../support/dom";
|
||||
|
||||
installObsidianDomShims();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { h } from "preact";
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { Toolbar, type ToolbarActions, type ToolbarViewModel } from "../../../../../src/features/chat/ui/toolbar";
|
||||
import { renderUiRoot } from "../../../../../src/shared/ui/ui-root";
|
||||
import { renderUiRoot } from "../../../../../src/shared/ui/ui-root.dom";
|
||||
import { changeInputValue, installObsidianDomShims } from "../../../../support/dom";
|
||||
|
||||
installObsidianDomShims();
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ import type { WorkspaceLeaf } from "obsidian";
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { persistedChatTurnDiffViewState } from "../../../../../src/features/chat/domain/turn-diff";
|
||||
import { renderChatTurnDiffView } from "../../../../../src/features/chat/ui/turn-diff/render";
|
||||
import { CodexChatTurnDiffView } from "../../../../../src/features/chat/ui/turn-diff/view";
|
||||
import { renderChatTurnDiffView } from "../../../../../src/features/chat/ui/turn-diff/render.dom";
|
||||
import { CodexChatTurnDiffView } from "../../../../../src/features/chat/ui/turn-diff/view.obsidian";
|
||||
import { installObsidianDomShims } from "../../../../support/dom";
|
||||
|
||||
installObsidianDomShims();
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
import { useLayoutEffect } from "preact/hooks";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { renderUiRoot, unmountUiRoot } from "../../../../src/shared/ui/ui-root";
|
||||
import { renderUiRoot, unmountUiRoot } from "../../../../src/shared/ui/ui-root.dom";
|
||||
|
||||
describe("Preact root adapter", () => {
|
||||
it("reuses roots that render no host children", () => {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { MarkdownView, TFile } from "obsidian";
|
|||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { registerSelectionRewriteCommand } from "../../../src/features/selection-rewrite/command";
|
||||
import type { SelectionRewritePopoverOptions } from "../../../src/features/selection-rewrite/popover";
|
||||
import type { SelectionRewritePopoverOptions } from "../../../src/features/selection-rewrite/popover.dom";
|
||||
|
||||
const popoverMock = vi.hoisted(() => {
|
||||
const instances: { options: SelectionRewritePopoverOptions; open: ReturnType<typeof vi.fn>; close: ReturnType<typeof vi.fn> }[] = [];
|
||||
|
|
@ -16,7 +16,7 @@ const popoverMock = vi.hoisted(() => {
|
|||
};
|
||||
});
|
||||
|
||||
vi.mock("../../../src/features/selection-rewrite/popover", () => {
|
||||
vi.mock("../../../src/features/selection-rewrite/popover.dom", () => {
|
||||
class SelectionRewritePopover {
|
||||
readonly open = vi.fn();
|
||||
readonly close = vi.fn();
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import {
|
|||
transitionSelectionRewriteState,
|
||||
} from "../../../src/features/selection-rewrite/model";
|
||||
import { selectionRewriteOutputParseResultFromText } from "../../../src/features/selection-rewrite/output";
|
||||
import { SelectionRewritePopover } from "../../../src/features/selection-rewrite/popover";
|
||||
import { SelectionRewritePopover } from "../../../src/features/selection-rewrite/popover.dom";
|
||||
import { positionSelectionRewritePopover } from "../../../src/features/selection-rewrite/position";
|
||||
import { buildSelectionRewritePrompt } from "../../../src/features/selection-rewrite/prompt";
|
||||
import * as selectionRewriteRunner from "../../../src/features/selection-rewrite/runner";
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import { SuggestModal } from "obsidian";
|
|||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import type { Thread } from "../../../src/domain/threads/model";
|
||||
import { openThreadPicker, type ThreadPickerHost } from "../../../src/features/thread-picker/modal";
|
||||
import { openThreadPicker, type ThreadPickerHost } from "../../../src/features/thread-picker/modal.obsidian";
|
||||
|
||||
describe("threadPickerSuggestions", () => {
|
||||
it("orders title and id prefix matches before looser matches", async () => {
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import type { Thread } from "../../../src/domain/threads/model";
|
||||
import { renderThreadsView } from "../../../src/features/threads-view/renderer";
|
||||
import { renderThreadsView } from "../../../src/features/threads-view/renderer.dom";
|
||||
import { type ThreadsRowModel, threadRows } from "../../../src/features/threads-view/state";
|
||||
import type { OpenCodexPanelSnapshot } from "../../../src/workspace/panel-coordinator";
|
||||
import { changeInputValue, installObsidianDomShims } from "../../support/dom";
|
||||
|
|
|
|||
|
|
@ -471,31 +471,73 @@ type Resume = AppServerClient["resumeThread"];
|
|||
]);
|
||||
});
|
||||
|
||||
it("keeps imperative DOM bridges behind filename suffixes", async () => {
|
||||
const cwd = await tempBiomeWorkspace(["no-implicit-dom-bridges.grit"]);
|
||||
await writeFile(
|
||||
path.join(cwd, "src/features/chat/ui/composer.tsx"),
|
||||
`
|
||||
export function render(container: HTMLElement): void {
|
||||
container.createDiv();
|
||||
container.addEventListener("click", () => undefined);
|
||||
}
|
||||
`.trimStart(),
|
||||
);
|
||||
await writeFile(
|
||||
path.join(cwd, "src/features/chat/ui/composer.dom.tsx"),
|
||||
`
|
||||
export function render(container: HTMLElement): void {
|
||||
container.createDiv();
|
||||
container.addEventListener("click", () => undefined);
|
||||
}
|
||||
`.trimStart(),
|
||||
);
|
||||
await writeFile(
|
||||
path.join(cwd, "src/app-server/services/abortable-operation.ts"),
|
||||
`
|
||||
export function onAbort(signal: AbortSignal): void {
|
||||
signal.addEventListener("abort", () => undefined);
|
||||
}
|
||||
`.trimStart(),
|
||||
);
|
||||
|
||||
const report = biomeLint(
|
||||
["src/features/chat/ui/composer.tsx", "src/features/chat/ui/composer.dom.tsx", "src/app-server/services/abortable-operation.ts"],
|
||||
cwd,
|
||||
);
|
||||
|
||||
expect(pluginMessages(report, "src/features/chat/ui/composer.tsx")).toEqual([
|
||||
"Keep imperative DOM writes and event wiring in files named with a .dom, .obsidian, or .measure suffix.",
|
||||
"Keep imperative DOM writes and event wiring in files named with a .dom, .obsidian, or .measure suffix.",
|
||||
]);
|
||||
expect(pluginDiagnostics(report, "src/features/chat/ui/composer.dom.tsx")).toEqual([]);
|
||||
expect(pluginDiagnostics(report, "src/app-server/services/abortable-operation.ts")).toEqual([]);
|
||||
});
|
||||
|
||||
it("keeps the Preact root adapter in explicit root bridge files", async () => {
|
||||
const cwd = await tempBiomeWorkspace(["no-ui-root-imports.grit"]);
|
||||
await writeFile(
|
||||
path.join(cwd, "src/features/chat/ui/composer.tsx"),
|
||||
`
|
||||
import { renderUiRoot } from '../../../shared/ui/ui-root';
|
||||
import { renderUiRoot } from '../../../shared/ui/ui-root.dom';
|
||||
|
||||
export const render = renderUiRoot;
|
||||
`.trimStart(),
|
||||
);
|
||||
await writeFile(
|
||||
path.join(cwd, "src/features/chat/panel/shell.tsx"),
|
||||
path.join(cwd, "src/features/chat/panel/shell.dom.tsx"),
|
||||
`
|
||||
import { renderUiRoot } from "../../../shared/ui/ui-root";
|
||||
import { renderUiRoot } from "../../../shared/ui/ui-root.dom";
|
||||
|
||||
export const render = renderUiRoot;
|
||||
`.trimStart(),
|
||||
);
|
||||
|
||||
const report = biomeLint(["src/features/chat/ui/composer.tsx", "src/features/chat/panel/shell.tsx"], cwd);
|
||||
const report = biomeLint(["src/features/chat/ui/composer.tsx", "src/features/chat/panel/shell.dom.tsx"], cwd);
|
||||
|
||||
expect(pluginMessages(report, "src/features/chat/ui/composer.tsx")).toEqual([
|
||||
"Import the Preact root adapter only from explicit root bridge files.",
|
||||
]);
|
||||
expect(pluginDiagnostics(report, "src/features/chat/panel/shell.tsx")).toEqual([]);
|
||||
expect(pluginDiagnostics(report, "src/features/chat/panel/shell.dom.tsx")).toEqual([]);
|
||||
});
|
||||
|
||||
it("keeps chat state transforms pure and catches global scheduling calls", async () => {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import type { Thread } from "../../src/domain/threads/model";
|
|||
import { threadArchiveDisplayTitle } from "../../src/domain/threads/title";
|
||||
import { SettingsDynamicDataController, type SettingsDynamicDataSnapshot } from "../../src/settings/dynamic-data-controller";
|
||||
import type { CodexPanelSettingTabHost } from "../../src/settings/host";
|
||||
import { CodexPanelSettingTab } from "../../src/settings/tab";
|
||||
import { CodexPanelSettingTab } from "../../src/settings/tab.obsidian";
|
||||
import { notices } from "../mocks/obsidian";
|
||||
import { deferred } from "../support/async";
|
||||
import { installObsidianDomShims } from "../support/dom";
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { textareaCursorAtVisualBoundary } from "../../../src/shared/ui/textarea-caret";
|
||||
import { textareaCursorAtVisualBoundary } from "../../../src/shared/ui/textarea-caret.measure";
|
||||
import { installObsidianDomShims } from "../../support/dom";
|
||||
|
||||
installObsidianDomShims();
|
||||
|
|
|
|||
Loading…
Reference in a new issue