From 5a1d55de22786c0ecb866061fd3aaf3ebbb22f54 Mon Sep 17 00:00:00 2001 From: murashit Date: Tue, 30 Jun 2026 23:47:59 +0900 Subject: [PATCH] Rehome shared modules by ownership boundary --- biome.jsonc | 101 +++++------------- docs/development.md | 12 +-- scripts/grit/dom/no-dom-events-imports.grit | 2 +- ...ports.grit => no-preact-root-imports.grit} | 2 +- .../no-domain-outer-layer-imports.grit | 4 +- .../no-lower-level-feature-imports.grit | 2 +- src/app-server/protocol/server-requests.ts | 4 +- src/app-server/query/cache.ts | 2 +- .../query/observed-result.ts | 0 src/app-server/query/shared-queries.ts | 2 +- .../services/ephemeral-structured-turn.ts | 2 +- src/domain/display/json-preview.ts | 7 ++ .../display/text-preview.ts} | 8 -- .../input/send-shortcut.ts} | 0 src/domain/threads/archive-markdown.ts | 10 +- .../id/thread-id.ts => domain/threads/id.ts} | 0 src/domain/threads/title-generation-model.ts | 2 +- src/domain/threads/title.ts | 2 +- src/domain/vault/file-hrefs.ts | 56 ++++++++++ .../file-paths.ts => domain/vault/paths.ts} | 32 ------ .../chat/app-server/inbound/handler.ts | 2 +- .../app-server/inbound/notification-plan.ts | 2 +- .../message-stream/review-result-items.ts | 2 +- .../mappers/message-stream/turn-items.ts | 2 +- .../app-server/thread-reference-resolver.ts | 2 +- .../chat/application/composer/suggestions.ts | 2 +- .../connection/connection-controller.ts | 2 +- .../connection}/connection-work.ts | 0 .../application/conversation/composition.ts | 2 +- .../conversation/slash-command-execution.ts | 2 +- .../conversation/turn-submission-actions.ts | 2 +- .../chat/application/local-id-source.ts} | 0 .../chat/application/threads/goal-actions.ts | 2 +- .../message-stream/factories/goal-items.ts | 2 +- .../message-stream/format/permission-rows.ts | 2 +- .../message-stream/semantics/active-turn.ts | 2 +- .../chat/host/bundles/connection-bundle.ts | 4 +- .../chat/host/bundles/reconnect-bundle.ts | 2 +- .../chat/host/bundles/thread-bundle.ts | 2 +- src/features/chat/host/bundles/turn-bundle.ts | 2 +- src/features/chat/host/contracts.ts | 4 +- src/features/chat/host/session-graph.ts | 4 +- src/features/chat/host/session.ts | 2 +- .../chat/host/session/deferred-work.ts | 2 +- .../chat/host/session/shared-state-binding.ts | 4 +- src/features/chat/host/view.obsidian.ts | 4 +- .../chat/panel/composer-controller.dom.ts | 2 +- .../chat/panel/composer-controller.ts | 2 +- src/features/chat/panel/shell.dom.tsx | 4 +- .../chat/panel/surface/goal-projection.tsx | 2 +- .../panel/surface/message-stream-presenter.ts | 2 +- src/features/chat/panel/toolbar-actions.ts | 2 +- .../message-stream/detail-view.ts | 6 +- .../presentation/message-stream/layout.ts | 2 +- .../message-stream/status-view.ts | 2 +- .../chat/presentation/runtime/status.ts | 2 +- src/features/chat/ui/composer.dom.ts | 4 +- src/features/chat/ui/composer.tsx | 2 +- src/features/chat/ui/goal.dom.ts | 4 +- src/features/chat/ui/goal.tsx | 5 +- .../chat/ui/message-stream/detail.tsx | 2 +- .../ui/message-stream/flow-scroll.measure.ts | 2 +- .../markdown-renderer.obsidian.ts | 3 +- .../ui/message-stream/text-content.dom.tsx | 2 +- .../chat/ui/message-stream/text.dom.ts | 2 +- src/features/chat/ui/message-stream/text.tsx | 2 +- src/features/chat/ui/toolbar.tsx | 2 +- .../selection-rewrite/command.obsidian.ts | 2 +- .../selection-rewrite/popover.dom.tsx | 15 ++- src/features/thread-picker/modal.obsidian.ts | 3 +- src/features/threads-view/session.ts | 4 +- src/features/threads-view/shell.dom.tsx | 5 +- src/features/threads-view/view-lifecycle.ts | 2 +- src/features/threads-view/view.obsidian.ts | 2 +- .../threads/catalog/thread-catalog.ts | 2 +- .../archive-export-destination.obsidian.ts} | 0 .../threads/workflows/archive-export.ts | 2 +- src/features/turn-diff/render.dom.tsx | 8 +- src/features/turn-diff/view.obsidian.ts | 4 +- src/settings/app-server-dynamic-data.ts | 2 +- src/settings/archived-section.tsx | 5 +- src/settings/dynamic-data.ts | 2 +- src/settings/dynamic-sections-controller.ts | 4 +- src/settings/helper-section.tsx | 2 +- src/settings/hook-section.tsx | 2 +- src/settings/model.ts | 2 +- src/settings/tab-shell.tsx | 4 +- src/settings/tab.obsidian.tsx | 4 +- .../dom-events.dom.ts => dom/events.dom.ts} | 0 .../preact-root.dom.tsx} | 0 .../{ui => dom}/textarea-autogrow.measure.ts | 0 .../{ui => dom}/textarea-caret.measure.ts | 0 .../clipboard.obsidian.ts} | 0 .../{ui => obsidian}/components.obsidian.tsx | 2 +- src/shared/obsidian/file-links.ts | 34 ------ .../obsidian/vault-file-links.obsidian.ts | 11 ++ .../{lifecycle => runtime}/abort-signal.ts | 0 .../{lifecycle => runtime}/deferred-task.ts | 0 src/shared/ui/{diff.tsx => diff-view.tsx} | 0 .../query/observed-result.test.ts | 4 +- tests/app-server/shared-queries.test.ts | 2 +- .../input/send-shortcut.test.ts} | 2 +- .../vault/file-hrefs-and-paths.test.ts} | 9 +- .../chat/app-server/inbound/handler.test.ts | 2 +- .../app-server/mappers/message-stream.test.ts | 2 +- .../connection/connection-controller.test.ts | 2 +- .../connection}/connection-work.test.ts | 2 +- .../turn-submission-actions.test.ts | 2 +- .../chat/application/local-id-source.test.ts} | 2 +- .../application/threads/goal-actions.test.ts | 2 +- .../chat/host/reconnect-action.test.ts | 2 +- .../features/chat/host/session-graph.test.ts | 2 +- .../chat/host/view-connection.test.ts | 2 +- .../chat/panel/composer-controller.test.ts | 2 +- .../surface/message-stream-presenter.test.ts | 2 +- .../chat/panel/surface/projections.test.ts | 2 +- tests/features/chat/ui/composer.test.ts | 2 +- tests/features/chat/ui/goal.test.tsx | 5 +- .../blocks-and-messages.test.tsx | 6 +- .../ui/message-stream/flow-scroll.test.ts | 2 +- .../chat/ui/message-stream/test-helpers.tsx | 2 +- tests/features/chat/ui/toolbar.test.ts | 2 +- tests/features/chat/ui/ui-root.test.tsx | 2 +- tests/features/threads-view/view.test.ts | 2 +- .../archive-export-destination.test.ts | 2 +- tests/scripts/grit-policy.test.mjs | 87 +++++++++------ tests/settings/settings-tab.test.ts | 2 +- .../shared/{ui => dom}/textarea-caret.test.ts | 2 +- ...links.test.ts => vault-file-links.test.ts} | 3 +- .../ui/{diff.test.ts => diff-view.test.ts} | 2 +- 130 files changed, 306 insertions(+), 345 deletions(-) rename scripts/grit/dom/{no-ui-root-imports.grit => no-preact-root-imports.grit} (91%) rename src/{shared => app-server}/query/observed-result.ts (100%) create mode 100644 src/domain/display/json-preview.ts rename src/{shared/text/preview.ts => domain/display/text-preview.ts} (50%) rename src/{shared/ui/keyboard.ts => domain/input/send-shortcut.ts} (100%) rename src/{shared/id/thread-id.ts => domain/threads/id.ts} (100%) create mode 100644 src/domain/vault/file-hrefs.ts rename src/{shared/path/file-paths.ts => domain/vault/paths.ts} (66%) rename src/{shared/lifecycle => features/chat/application/connection}/connection-work.ts (100%) rename src/{shared/id/local-id.ts => features/chat/application/local-id-source.ts} (100%) rename src/{shared/obsidian/archive-export-destination.ts => features/threads/obsidian/archive-export-destination.obsidian.ts} (100%) rename src/shared/{ui/dom-events.dom.ts => dom/events.dom.ts} (100%) rename src/shared/{ui/ui-root.dom.tsx => dom/preact-root.dom.tsx} (100%) rename src/shared/{ui => dom}/textarea-autogrow.measure.ts (100%) rename src/shared/{ui => dom}/textarea-caret.measure.ts (100%) rename src/shared/{ui/clipboard.ts => obsidian/clipboard.obsidian.ts} (100%) rename src/shared/{ui => obsidian}/components.obsidian.tsx (99%) delete mode 100644 src/shared/obsidian/file-links.ts create mode 100644 src/shared/obsidian/vault-file-links.obsidian.ts rename src/shared/{lifecycle => runtime}/abort-signal.ts (100%) rename src/shared/{lifecycle => runtime}/deferred-task.ts (100%) rename src/shared/ui/{diff.tsx => diff-view.tsx} (100%) rename tests/{shared => app-server}/query/observed-result.test.ts (90%) rename tests/{shared/ui/keyboard.test.ts => domain/input/send-shortcut.test.ts} (97%) rename tests/{shared/path/file-paths.test.ts => domain/vault/file-hrefs-and-paths.test.ts} (89%) rename tests/{shared/lifecycle => features/chat/application/connection}/connection-work.test.ts (92%) rename tests/{shared/local-id.test.ts => features/chat/application/local-id-source.test.ts} (91%) rename tests/{shared => features/threads}/obsidian/archive-export-destination.test.ts (95%) rename tests/shared/{ui => dom}/textarea-caret.test.ts (98%) rename tests/shared/obsidian/{file-links.test.ts => vault-file-links.test.ts} (95%) rename tests/shared/ui/{diff.test.ts => diff-view.test.ts} (99%) diff --git a/biome.jsonc b/biome.jsonc index 056f6837..a437355f 100644 --- a/biome.jsonc +++ b/biome.jsonc @@ -9,43 +9,30 @@ // Project-wide source shape and placement. { "path": "./scripts/grit/source-shape/no-handwritten-reexports.grit", - "includes": ["**/*.ts", "**/*.tsx", "**/*.mjs"] + "includes": ["**/*.{ts,tsx,mjs}"] }, { "path": "./scripts/grit/source-shape/no-responsibility-root-module-files.grit", - "includes": [ - "**/src/app-server/*.ts", - "**/src/app-server/*.tsx", - "**/src/domain/*.ts", - "**/src/domain/*.tsx", - "**/src/features/chat/*.ts", - "**/src/features/chat/*.tsx", - "**/src/features/threads/*.ts", - "**/src/features/threads/*.tsx", - "**/src/shared/*.ts", - "**/src/shared/*.tsx" - ] + "includes": ["**/src/{app-server,domain}/*.{ts,tsx}", "**/src/features/{chat,threads}/*.{ts,tsx}"] }, { "path": "./scripts/grit/source-shape/no-misplaced-tsx.grit", "includes": [ "**/src/**/*.tsx", - "!**/src/features/chat/panel/**", - "!**/src/features/chat/ui/**", - "!**/src/features/selection-rewrite/*.dom.tsx", - "!**/src/features/turn-diff/*.dom.tsx", - "!**/src/features/threads-view/*.dom.tsx", - "!**/src/settings/**", - "!**/src/shared/ui/**" + "!**/src/{settings,shared/ui}/**", + "!**/src/features/chat/{panel,ui}/**", + "!**/src/features/{selection-rewrite,threads-view,turn-diff}/*.dom.tsx", + "!**/src/shared/dom/*.dom.tsx", + "!**/src/shared/obsidian/*.obsidian.tsx" ] }, { "path": "./scripts/grit/source-shape/no-self-referential-initializer-callback.grit", - "includes": ["**/*.ts", "**/*.tsx", "**/*.mjs"] + "includes": ["**/*.{ts,tsx,mjs}"] }, { "path": "./scripts/grit/source-shape/no-unsafe-iterator-value.grit", - "includes": ["**/*.ts", "**/*.tsx", "**/*.mjs"] + "includes": ["**/*.{ts,tsx,mjs}"] }, { "path": "./scripts/grit/source-shape/no-uncontrolled-preact-form-state.grit", @@ -59,7 +46,7 @@ }, { "path": "./scripts/grit/import-boundaries/no-lower-level-feature-imports.grit", - "includes": ["**/src/app-server/**/*.ts", "**/src/shared/**/*.ts", "**/src/shared/**/*.tsx"] + "includes": ["**/src/app-server/**/*.ts", "**/src/shared/**/*.{ts,tsx}"] }, // App-server and generated protocol boundaries. @@ -69,17 +56,16 @@ }, { "path": "./scripts/grit/import-boundaries/no-app-server-connection-boundary-imports.grit", - "includes": ["**/src/app-server/protocol/**/*.ts", "**/src/domain/**/*.ts", "**/src/shared/**/*.ts", "**/src/shared/**/*.tsx"] + "includes": ["**/src/app-server/protocol/**/*.ts", "**/src/domain/**/*.ts", "**/src/shared/**/*.{ts,tsx}"] }, { "path": "./scripts/grit/import-boundaries/no-app-server-protocol-boundary-imports.grit", - "includes": ["**/src/**/*.ts", "**/src/**/*.tsx", "!**/src/app-server/**"] + "includes": ["**/src/**/*.{ts,tsx}", "!**/src/app-server/**"] }, { "path": "./scripts/grit/import-boundaries/no-generated-app-server-boundary-imports.grit", "includes": [ - "**/src/**/*.ts", - "**/src/**/*.tsx", + "**/src/**/*.{ts,tsx}", "!**/src/app-server/connection/**", "!**/src/app-server/protocol/server-requests.ts", "!**/src/app-server/protocol/turn.ts" @@ -87,16 +73,16 @@ }, { "path": "./scripts/grit/runtime/no-app-server-direct-rpcs.grit", - "includes": ["**/src/**/*.ts", "**/src/**/*.tsx", "!**/src/app-server/**", "!**/src/features/chat/app-server/**"] + "includes": ["**/src/**/*.{ts,tsx}", "!**/src/app-server/**", "!**/src/features/chat/app-server/**"] }, // Workspace and feature coordination boundaries. { "path": "./scripts/grit/import-boundaries/no-chat-workspace-boundary-imports.grit", - "includes": ["**/src/features/chat/**/*.ts", "**/src/features/chat/**/*.tsx"] + "includes": ["**/src/features/chat/**/*.{ts,tsx}"] }, { "path": "./scripts/grit/import-boundaries/no-feature-workspace-boundary-imports.grit", - "includes": ["**/src/features/**/*.ts", "**/src/features/**/*.tsx", "!**/src/features/chat/**"] + "includes": ["**/src/features/**/*.{ts,tsx}", "!**/src/features/chat/**"] }, { "path": "./scripts/grit/import-boundaries/no-workspace-chat-internal-imports.grit", @@ -118,7 +104,7 @@ }, { "path": "./scripts/grit/import-boundaries/no-chat-panel-runtime-boundary-imports.grit", - "includes": ["**/src/features/chat/panel/**/*.ts", "**/src/features/chat/panel/**/*.tsx"] + "includes": ["**/src/features/chat/panel/**/*.{ts,tsx}"] }, { "path": "./scripts/grit/import-boundaries/no-chat-presentation-outer-layer-imports.grit", @@ -126,40 +112,34 @@ }, { "path": "./scripts/grit/import-boundaries/no-chat-ui-outer-layer-imports.grit", - "includes": ["**/src/features/chat/ui/**/*.ts", "**/src/features/chat/ui/**/*.tsx"] + "includes": ["**/src/features/chat/ui/**/*.{ts,tsx}"] }, // Chat state and runtime ownership. { "path": "./scripts/grit/import-boundaries/no-preact-signal-imports.grit", "includes": [ - "**/src/**/*.ts", - "**/src/**/*.tsx", + "**/src/**/*.{ts,tsx}", "!**/src/features/chat/panel/shell-read-model.ts", - "!**/src/features/chat/panel/surface/**/*.ts", - "!**/src/features/chat/panel/surface/**/*.tsx" + "!**/src/features/chat/panel/surface/**/*.{ts,tsx}" ] }, { "path": "./scripts/grit/import-boundaries/no-chat-shell-read-model-imports.grit", "includes": [ - "**/src/features/chat/**/*.ts", - "**/src/features/chat/**/*.tsx", + "**/src/features/chat/**/*.{ts,tsx}", "!**/src/features/chat/panel/shell-read-model.ts", "!**/src/features/chat/panel/shell.dom.tsx", "!**/src/features/chat/panel/composer-controller.ts", - "!**/src/features/chat/panel/surface/**/*.ts", - "!**/src/features/chat/panel/surface/**/*.tsx" + "!**/src/features/chat/panel/surface/**/*.{ts,tsx}" ] }, { "path": "./scripts/grit/import-boundaries/no-chat-signal-type-references.grit", "includes": [ - "**/src/features/chat/**/*.ts", - "**/src/features/chat/**/*.tsx", + "**/src/features/chat/**/*.{ts,tsx}", "!**/src/features/chat/panel/shell-read-model.ts", - "!**/src/features/chat/panel/surface/**/*.ts", - "!**/src/features/chat/panel/surface/**/*.tsx" + "!**/src/features/chat/panel/surface/**/*.{ts,tsx}" ] }, { @@ -170,40 +150,15 @@ // DOM bridge and Preact root boundaries. { "path": "./scripts/grit/dom/no-implicit-dom-bridges.grit", - "includes": [ - "**/src/**/*.ts", - "**/src/**/*.tsx", - "!**/src/**/*.dom.ts", - "!**/src/**/*.dom.tsx", - "!**/src/**/*.obsidian.ts", - "!**/src/**/*.obsidian.tsx", - "!**/src/**/*.measure.ts", - "!**/src/**/*.measure.tsx" - ] + "includes": ["**/src/**/*.{ts,tsx}", "!**/src/**/*.{dom,obsidian,measure}.{ts,tsx}"] }, { "path": "./scripts/grit/dom/no-dom-events-imports.grit", - "includes": [ - "**/src/**/*.ts", - "**/src/**/*.tsx", - "!**/src/**/*.dom.ts", - "!**/src/**/*.dom.tsx", - "!**/src/**/*.obsidian.ts", - "!**/src/**/*.obsidian.tsx", - "!**/src/**/*.measure.ts", - "!**/src/**/*.measure.tsx" - ] + "includes": ["**/src/**/*.{ts,tsx}", "!**/src/**/*.{dom,obsidian,measure}.{ts,tsx}"] }, { - "path": "./scripts/grit/dom/no-ui-root-imports.grit", - "includes": [ - "**/src/**/*.ts", - "**/src/**/*.tsx", - "!**/src/**/*.dom.ts", - "!**/src/**/*.dom.tsx", - "!**/src/**/*.obsidian.ts", - "!**/src/**/*.obsidian.tsx" - ] + "path": "./scripts/grit/dom/no-preact-root-imports.grit", + "includes": ["**/src/**/*.{ts,tsx}", "!**/src/**/*.{dom,obsidian}.{ts,tsx}"] }, // CSS source policy. diff --git a/docs/development.md b/docs/development.md index eedd0477..808f9e6f 100644 --- a/docs/development.md +++ b/docs/development.md @@ -31,17 +31,15 @@ npm run check The generation script uses `codex app-server generate-ts --experimental` because the panel depends on experimental app-server fields. Do not hand-edit generated bindings. -## Source Layout +## Placement Rules -The source tree is organized by implementation ownership, not by the single Obsidian plugin entrypoint. Put behavior where its reason to change lives: app-server protocol adaptation at the app-server boundary, app-server-independent domain models in domain code, feature-neutral helpers in shared code, feature workflows under their owning feature, and Obsidian lifecycle or workspace wiring at Obsidian-facing boundaries. +The source tree is organized by implementation ownership, not by the single Obsidian plugin entrypoint. Put behavior where its reason to change lives: app-server protocol adaptation at the app-server boundary, app-server-independent domain models in domain code, cross-feature reusable adapters and primitives under `src/shared/`, and feature workflows under their owning feature. Keep shared subfolders named by the boundary or primitive they own, such as DOM, Obsidian, runtime, or UI rendering. Within chat, keep state transitions and workflow orchestration separate from app-server adaptation, session/Obsidian wiring, and rendering surfaces. Tests should mirror the ownership boundary of the code under test. -## Placement Rules +Keep new code near the state or API it owns. A feature may import another feature only for a capability that feature owns. When moving behavior across an ownership boundary or adding a new shared classification, update the lint policy and its tests with that boundary in the same change. -Keep new code near the state or API it owns. A feature may import another feature only for a capability that feature owns. Move feature-neutral helpers to `src/shared/`, panel-wide domain models to `src/domain/`, and app-server protocol adaptation to `src/app-server/`. - -Generated app-server types should stay behind app-server connection and protocol adapter modules. If domain, shared, settings, workspace, or UI code needs app-server data, add or reuse a panel-owned projection at the boundary instead of importing generated payload types directly. +Generated app-server types should stay behind app-server connection and protocol adapter modules. If domain, shared code, settings, workspace, or feature code needs app-server data, add or reuse a panel-owned projection at the boundary instead of importing generated payload types directly. Chat application workflows should receive chat-owned contracts, not root `src/app-server/` modules or direct `AppServerClient` access. Keep app-server access, connection freshness checks, vault-path injection, and payload projection in `src/features/chat/app-server/` transports or host-owned wiring. @@ -53,7 +51,7 @@ Chat modules should not import `src/workspace/` directly; workspace operations e Use DOM reads, writes, measurements, hit-tests, focus/selection operations, and DOM event listener wiring only from explicit bridge modules, Obsidian-owned API boundaries, or rendering and measurement code that cannot be expressed cleanly as Preact components. Normal modules may keep refs and call named adapters, but they should not interpret DOM structure or layout directly. Name bridge files with a `.dom`, `.obsidian`, or `.measure` suffix. -Use `.tsx` only in rendering-owned source folders: chat panel and UI modules, Obsidian/settings surfaces, shared UI components, and explicit `.dom.tsx` rendering boundaries such as the selection rewrite popover and threads view shell. Non-rendering source should use `.ts`. +Use `.tsx` only where the module owns rendering or a host rendering bridge. Non-rendering source should use `.ts`; when a new rendering-owned location is intentional, update the source-shape lint policy and its tests rather than relying on convention alone. ## CSS Rules diff --git a/scripts/grit/dom/no-dom-events-imports.grit b/scripts/grit/dom/no-dom-events-imports.grit index 76482649..61922544 100644 --- a/scripts/grit/dom/no-dom-events-imports.grit +++ b/scripts/grit/dom/no-dom-events-imports.grit @@ -8,6 +8,6 @@ private pattern js_module_reference() { } js_module_reference() as $stmt where { - $stmt <: contains `$source` where { $source <: r"^[\"'].*shared/ui/dom-events\.dom[\"']$" }, + $stmt <: contains `$source` where { $source <: r"^[\"'].*shared/dom/events\.dom[\"']$" }, register_diagnostic(span=$stmt, message="Import DOM event listener helpers only from explicit .dom, .obsidian, or .measure bridge files.", severity="error") } diff --git a/scripts/grit/dom/no-ui-root-imports.grit b/scripts/grit/dom/no-preact-root-imports.grit similarity index 91% rename from scripts/grit/dom/no-ui-root-imports.grit rename to scripts/grit/dom/no-preact-root-imports.grit index c30a4f12..c6278d6c 100644 --- a/scripts/grit/dom/no-ui-root-imports.grit +++ b/scripts/grit/dom/no-preact-root-imports.grit @@ -8,6 +8,6 @@ private pattern js_module_reference() { } js_module_reference() as $stmt where { - $stmt <: contains `$source` where { $source <: r"^[\"'].*shared/ui/ui-root\.dom[\"']$" }, + $stmt <: contains `$source` where { $source <: r"^[\"'].*shared/dom/preact-root\.dom[\"']$" }, register_diagnostic(span=$stmt, message="Import the Preact root adapter only from explicit root bridge files.", severity="error") } diff --git a/scripts/grit/import-boundaries/no-domain-outer-layer-imports.grit b/scripts/grit/import-boundaries/no-domain-outer-layer-imports.grit index 0996358e..1dab0e82 100644 --- a/scripts/grit/import-boundaries/no-domain-outer-layer-imports.grit +++ b/scripts/grit/import-boundaries/no-domain-outer-layer-imports.grit @@ -9,8 +9,8 @@ private pattern js_module_reference() { private pattern root_outer_layer_source() { or { - r"^[\"'](?:\.\./)+(?:app-server|settings|workspace|shared/ui|features)(?:/.*)?[\"']$", - r"^[\"']src/(?:app-server|settings|workspace|shared/ui|features)(?:/.*)?[\"']$", + r"^[\"'](?:\.\./)+(?:app-server|settings|workspace|shared|features)(?:/.*)?[\"']$", + r"^[\"']src/(?:app-server|settings|workspace|shared|features)(?:/.*)?[\"']$", r"^[\"']obsidian[\"']$" } } diff --git a/scripts/grit/import-boundaries/no-lower-level-feature-imports.grit b/scripts/grit/import-boundaries/no-lower-level-feature-imports.grit index 13094956..2b92f64f 100644 --- a/scripts/grit/import-boundaries/no-lower-level-feature-imports.grit +++ b/scripts/grit/import-boundaries/no-lower-level-feature-imports.grit @@ -9,5 +9,5 @@ private pattern js_module_reference() { js_module_reference() as $stmt where { $stmt <: contains `$source` where { $source <: r"^[\"'](?:(?:\.\./)+features|src/features)(?:/.*)?[\"']$" }, - register_diagnostic(span=$stmt, message="Do not import feature modules from this layer. Move shared behavior to shared, domain, or app-server adapters.", severity="error") + register_diagnostic(span=$stmt, message="Do not import feature modules from this layer. Move reusable behavior to domain, shared DOM/Obsidian/runtime/UI, or app-server adapters.", severity="error") } diff --git a/src/app-server/protocol/server-requests.ts b/src/app-server/protocol/server-requests.ts index 93d4c44e..17f9f953 100644 --- a/src/app-server/protocol/server-requests.ts +++ b/src/app-server/protocol/server-requests.ts @@ -1,3 +1,4 @@ +import { jsonPreview } from "../../domain/display/json-preview"; import type { ApprovalAction, ApprovalActionIntent, @@ -12,9 +13,8 @@ import type { PendingUserInput, PendingUserInputQuestion, } from "../../domain/pending-requests/model"; +import { pathRelativeToRoot } from "../../domain/vault/paths"; import type { ServerRequest as GeneratedServerRequest } from "../../generated/app-server/ServerRequest"; -import { pathRelativeToRoot } from "../../shared/path/file-paths"; -import { jsonPreview } from "../../shared/text/preview"; interface AppServerGrantedPermissionProfile { network?: unknown; diff --git a/src/app-server/query/cache.ts b/src/app-server/query/cache.ts index 04d3aa93..82d553e8 100644 --- a/src/app-server/query/cache.ts +++ b/src/app-server/query/cache.ts @@ -3,7 +3,6 @@ import type { ModelMetadata } from "../../domain/catalog/metadata"; import { createServerDiagnostics, diagnosticProbeError, diagnosticProbeOk, diagnosticsWithProbe } from "../../domain/server/diagnostics"; import type { SharedServerMetadata } from "../../domain/server/metadata"; import type { Thread } from "../../domain/threads/model"; -import type { ObservedResult, ObservedResultListener } from "../../shared/query/observed-result"; import type { AppServerClient } from "../connection/client"; import type { AppServerClientAccessOptions } from "../connection/client-access"; import { runtimeConfigSnapshotFromAppServerConfig } from "../protocol/runtime-config"; @@ -21,6 +20,7 @@ import { cloneAppServerQueryContext, } from "./keys"; import { readRateLimitMetadataProbe, readSkillMetadataProbe } from "./metadata-probes"; +import type { ObservedResult, ObservedResultListener } from "./observed-result"; import { cloneModelMetadata, cloneSharedServerMetadata, cloneThreads } from "./snapshots"; const THREAD_LIST_STALE_TIME_MS = 10_000; diff --git a/src/shared/query/observed-result.ts b/src/app-server/query/observed-result.ts similarity index 100% rename from src/shared/query/observed-result.ts rename to src/app-server/query/observed-result.ts diff --git a/src/app-server/query/shared-queries.ts b/src/app-server/query/shared-queries.ts index 94affeb2..33b54a08 100644 --- a/src/app-server/query/shared-queries.ts +++ b/src/app-server/query/shared-queries.ts @@ -1,7 +1,6 @@ import type { ModelMetadata } from "../../domain/catalog/metadata"; import type { SharedServerMetadata } from "../../domain/server/metadata"; import type { Thread } from "../../domain/threads/model"; -import type { ObservedResultListener } from "../../shared/query/observed-result"; import type { AppServerQueryCache } from "./cache"; import { type AppServerQueryContext, @@ -9,6 +8,7 @@ import { appServerQueryContextRawEquals, cloneAppServerQueryContext, } from "./keys"; +import type { ObservedResultListener } from "./observed-result"; export interface AppServerSharedQueriesOptions { cache: AppServerQueryCache; diff --git a/src/app-server/services/ephemeral-structured-turn.ts b/src/app-server/services/ephemeral-structured-turn.ts index 70ff39cc..d695bdc7 100644 --- a/src/app-server/services/ephemeral-structured-turn.ts +++ b/src/app-server/services/ephemeral-structured-turn.ts @@ -1,4 +1,4 @@ -import { listenAbortSignal } from "../../shared/lifecycle/abort-signal"; +import { listenAbortSignal } from "../../shared/runtime/abort-signal"; import { AppServerClient, type AppServerClientHandlers } from "../connection/client"; import type { AppServerClientRequestPolicy } from "../connection/client-access"; import type { ServerNotification } from "../connection/rpc-messages"; diff --git a/src/domain/display/json-preview.ts b/src/domain/display/json-preview.ts new file mode 100644 index 00000000..6318481c --- /dev/null +++ b/src/domain/display/json-preview.ts @@ -0,0 +1,7 @@ +export function jsonPreview(value: unknown): string { + try { + return JSON.stringify(value, null, 2); + } catch { + return String(value); + } +} diff --git a/src/shared/text/preview.ts b/src/domain/display/text-preview.ts similarity index 50% rename from src/shared/text/preview.ts rename to src/domain/display/text-preview.ts index ea80e9c9..30d6ccc9 100644 --- a/src/shared/text/preview.ts +++ b/src/domain/display/text-preview.ts @@ -1,11 +1,3 @@ export function truncate(value: string, maxLength: number): string { return value.length > maxLength ? `${value.slice(0, maxLength - 1)}...` : value; } - -export function jsonPreview(value: unknown): string { - try { - return JSON.stringify(value, null, 2); - } catch { - return String(value); - } -} diff --git a/src/shared/ui/keyboard.ts b/src/domain/input/send-shortcut.ts similarity index 100% rename from src/shared/ui/keyboard.ts rename to src/domain/input/send-shortcut.ts diff --git a/src/domain/threads/archive-markdown.ts b/src/domain/threads/archive-markdown.ts index 08acfeeb..473ddf1d 100644 --- a/src/domain/threads/archive-markdown.ts +++ b/src/domain/threads/archive-markdown.ts @@ -1,11 +1,5 @@ -import { - isExternalFileHref, - isFilesystemAbsolutePath, - isVaultConfigPath, - normalizeFilePath, - parseFileHref, - vaultRelativePath, -} from "../../shared/path/file-paths"; +import { isExternalFileHref, parseFileHref } from "../vault/file-hrefs"; +import { isFilesystemAbsolutePath, isVaultConfigPath, normalizeFilePath, vaultRelativePath } from "../vault/paths"; import type { Thread } from "./model"; import { referencedThreadMetadataFromPrompt } from "./reference"; import { threadArchiveTitle } from "./title"; diff --git a/src/shared/id/thread-id.ts b/src/domain/threads/id.ts similarity index 100% rename from src/shared/id/thread-id.ts rename to src/domain/threads/id.ts diff --git a/src/domain/threads/title-generation-model.ts b/src/domain/threads/title-generation-model.ts index d58b9d64..22c5f083 100644 --- a/src/domain/threads/title-generation-model.ts +++ b/src/domain/threads/title-generation-model.ts @@ -1,4 +1,4 @@ -import { truncate } from "../../shared/text/preview"; +import { truncate } from "../display/text-preview"; import type { ThreadConversationSummary } from "./transcript"; const THREAD_TITLE_CONTEXT_MAX_CHARS = 4_000; diff --git a/src/domain/threads/title.ts b/src/domain/threads/title.ts index 2638e028..efaa4f75 100644 --- a/src/domain/threads/title.ts +++ b/src/domain/threads/title.ts @@ -1,4 +1,4 @@ -import { shortThreadId } from "../../shared/id/thread-id"; +import { shortThreadId } from "./id"; import type { Thread } from "./model"; const MAX_ARCHIVED_THREAD_DISPLAY_TITLE_LENGTH = 96; diff --git a/src/domain/vault/file-hrefs.ts b/src/domain/vault/file-hrefs.ts new file mode 100644 index 00000000..a13a41bb --- /dev/null +++ b/src/domain/vault/file-hrefs.ts @@ -0,0 +1,56 @@ +import { isFilesystemAbsolutePath, isVaultConfigPath, normalizeFilePath, vaultRelativePath } from "./paths"; + +export interface ParsedFileHref { + path: string; + subpath: string; +} + +export function parseFileHref(href: string): ParsedFileHref | null { + const trimmed = href.trim(); + if (!trimmed || isExternalFileHref(trimmed)) return null; + + const fragmentIndex = trimmed.indexOf("#"); + const withoutFragment = fragmentIndex === -1 ? trimmed : trimmed.slice(0, fragmentIndex); + if (!withoutFragment) return null; + + const decoded = decodeFileHref(withoutFragment); + const path = decoded.replace(/:(\d+)(?::\d+)?$/, ""); + const subpath = fragmentIndex === -1 ? "" : decodeFileHref(trimmed.slice(fragmentIndex)); + return path ? { path, subpath } : null; +} + +export function isExternalFileHref(href: string): boolean { + if (/^[a-z]:[\\/]/i.test(href)) return false; + return /^[a-z][a-z\d+.-]*:/i.test(href) || href.startsWith("//"); +} + +export function vaultRelativeFileLinkTarget(vaultPath: string, configDir: string, href: string): string | null { + const relativePath = vaultRelativeFileHref(vaultPath, configDir, href); + return relativePath ? `${relativePath.path}${relativePath.subpath}` : null; +} + +export function isAbsoluteFileHref(href: string): boolean { + const parsed = parseFileHref(href); + return parsed ? isFilesystemAbsolutePath(normalizeFilePath(parsed.path)) : false; +} + +export function vaultRelativeFileHref(vaultPath: string, configDir: string, href: string): { path: string; subpath: string } | null { + const parsed = parseFileHref(href); + if (!parsed) return null; + + const relativePath = vaultRelativePath(vaultPath, parsed.path, { allowRelative: true }); + if (!relativePath) return null; + + const normalized = normalizeFilePath(relativePath); + if (isVaultConfigPath(normalized, configDir)) return null; + + return { path: normalized, subpath: parsed.subpath }; +} + +function decodeFileHref(href: string): string { + try { + return decodeURI(href); + } catch { + return href; + } +} diff --git a/src/shared/path/file-paths.ts b/src/domain/vault/paths.ts similarity index 66% rename from src/shared/path/file-paths.ts rename to src/domain/vault/paths.ts index 5f9927e0..f3c213ab 100644 --- a/src/shared/path/file-paths.ts +++ b/src/domain/vault/paths.ts @@ -1,31 +1,7 @@ -export interface ParsedFileHref { - path: string; - subpath: string; -} - export interface VaultRelativePathOptions { allowRelative?: boolean; } -export function parseFileHref(href: string): ParsedFileHref | null { - const trimmed = href.trim(); - if (!trimmed || isExternalFileHref(trimmed)) return null; - - const fragmentIndex = trimmed.indexOf("#"); - const withoutFragment = fragmentIndex === -1 ? trimmed : trimmed.slice(0, fragmentIndex); - if (!withoutFragment) return null; - - const decoded = decodeFileHref(withoutFragment); - const path = decoded.replace(/:(\d+)(?::\d+)?$/, ""); - const subpath = fragmentIndex === -1 ? "" : decodeFileHref(trimmed.slice(fragmentIndex)); - return path ? { path, subpath } : null; -} - -export function isExternalFileHref(href: string): boolean { - if (isWindowsAbsolutePath(href)) return false; - return /^[a-z][a-z\d+.-]*:/i.test(href) || href.startsWith("//"); -} - export function vaultRelativePath(vaultPath: string, path: string, options: VaultRelativePathOptions = {}): string | null { const normalizedPath = normalizeFilePath(path); const normalizedVaultPath = normalizeFilePath(vaultPath); @@ -61,14 +37,6 @@ export function normalizeFilePath(path: string): string { return normalized.replace(/^\.\//, ""); } -function decodeFileHref(href: string): string { - try { - return decodeURI(href); - } catch { - return href; - } -} - function isWindowsAbsolutePath(path: string): boolean { return /^[a-z]:[\\/]/i.test(path); } diff --git a/src/features/chat/app-server/inbound/handler.ts b/src/features/chat/app-server/inbound/handler.ts index d9d8b97b..64c7b960 100644 --- a/src/features/chat/app-server/inbound/handler.ts +++ b/src/features/chat/app-server/inbound/handler.ts @@ -14,9 +14,9 @@ import { type PendingUserInput, } from "../../../../domain/pending-requests/model"; import type { ThreadConversationSummary } from "../../../../domain/threads/transcript"; -import type { LocalIdSource } from "../../../../shared/id/local-id"; import type { ThreadCatalogEvent } from "../../../threads/catalog/thread-catalog"; import { activeTurnId } from "../../application/conversation/turn-state"; +import type { LocalIdSource } from "../../application/local-id-source"; import type { ChatAction, ChatState } from "../../application/state/root-reducer"; import type { ChatStateStore } from "../../application/state/store"; import { createStructuredSystemItem, createSystemItem } from "../../domain/message-stream/factories/system-items"; diff --git a/src/features/chat/app-server/inbound/notification-plan.ts b/src/features/chat/app-server/inbound/notification-plan.ts index 9b35ca33..36827e3d 100644 --- a/src/features/chat/app-server/inbound/notification-plan.ts +++ b/src/features/chat/app-server/inbound/notification-plan.ts @@ -1,9 +1,9 @@ import type { ServerNotification } from "../../../../app-server/connection/rpc-messages"; import { threadFromAppServerRecord } from "../../../../app-server/services/threads"; +import { jsonPreview } from "../../../../domain/display/json-preview"; import { threadTokenUsageFromRuntimeUsage } from "../../../../domain/runtime/metrics"; import { normalizeExplicitThreadName } from "../../../../domain/threads/model"; import type { ThreadConversationSummary } from "../../../../domain/threads/transcript"; -import { jsonPreview } from "../../../../shared/text/preview"; import type { ThreadCatalogEvent } from "../../../threads/catalog/thread-catalog"; import { activeTurnId, pendingTurnStart as pendingTurnStartForState } from "../../application/conversation/turn-state"; import { activeThreadSettingsAppliedAction } from "../../application/state/actions"; diff --git a/src/features/chat/app-server/mappers/message-stream/review-result-items.ts b/src/features/chat/app-server/mappers/message-stream/review-result-items.ts index 7737ece9..2c669110 100644 --- a/src/features/chat/app-server/mappers/message-stream/review-result-items.ts +++ b/src/features/chat/app-server/mappers/message-stream/review-result-items.ts @@ -1,4 +1,4 @@ -import { pathRelativeToRoot } from "../../../../../shared/path/file-paths"; +import { pathRelativeToRoot } from "../../../../../domain/vault/paths"; import { permissionRows } from "../../../domain/message-stream/format/permission-rows"; import type { ExecutionState, MessageStreamAuditFact, MessageStreamItem } from "../../../domain/message-stream/items"; import { type ExecutionStateByStatus, executionStateFromStatus, RUNNING_EXECUTION_STATE } from "./execution-state"; diff --git a/src/features/chat/app-server/mappers/message-stream/turn-items.ts b/src/features/chat/app-server/mappers/message-stream/turn-items.ts index 0880b974..95a77635 100644 --- a/src/features/chat/app-server/mappers/message-stream/turn-items.ts +++ b/src/features/chat/app-server/mappers/message-stream/turn-items.ts @@ -4,10 +4,10 @@ import { type TurnRecord, turnUserItemText, } from "../../../../../app-server/protocol/turn"; +import { jsonPreview } from "../../../../../domain/display/json-preview"; import type { HistoricalTurn } from "../../../../../domain/threads/history"; import { referencedThreadMetadataFromPrompt } from "../../../../../domain/threads/reference"; import type { ThreadConversationSummary } from "../../../../../domain/threads/transcript"; -import { jsonPreview } from "../../../../../shared/text/preview"; import { fileMentionsFromInput } from "../../../domain/message-stream/format/file-mentions"; import { normalizeProposedPlanMarkdown } from "../../../domain/message-stream/format/proposed-plan"; import { userMessageDisplayText } from "../../../domain/message-stream/format/user-message-text"; diff --git a/src/features/chat/app-server/thread-reference-resolver.ts b/src/features/chat/app-server/thread-reference-resolver.ts index 26cf6489..0f339c3b 100644 --- a/src/features/chat/app-server/thread-reference-resolver.ts +++ b/src/features/chat/app-server/thread-reference-resolver.ts @@ -1,8 +1,8 @@ import { readReferencedThreadConversationSummaries, type ThreadConversationSummaryClient } from "../../../app-server/services/threads"; import { type CodexInput, codexTextInputWithAttachments } from "../../../domain/chat/input"; +import { shortThreadId } from "../../../domain/threads/id"; import type { Thread } from "../../../domain/threads/model"; import { REFERENCED_THREAD_TURN_LIMIT, referencedThreadPromptBundle } from "../../../domain/threads/reference"; -import { shortThreadId } from "../../../shared/id/thread-id"; import type { ThreadReferenceInput } from "../application/conversation/slash-command-execution"; interface ThreadReferenceResolverHost { diff --git a/src/features/chat/application/composer/suggestions.ts b/src/features/chat/application/composer/suggestions.ts index 517c9908..d465a3d0 100644 --- a/src/features/chat/application/composer/suggestions.ts +++ b/src/features/chat/application/composer/suggestions.ts @@ -1,9 +1,9 @@ import { prepareFuzzySearch, type SearchResult, sortSearchResults } from "obsidian"; import type { ModelMetadata, SkillMetadata } from "../../../../domain/catalog/metadata"; import { findModelMetadataByIdOrName, sortedModelMetadata, supportedEffortsForModelMetadata } from "../../../../domain/catalog/metadata"; +import { shortThreadId } from "../../../../domain/threads/id"; import type { Thread } from "../../../../domain/threads/model"; import { threadDisplayTitle } from "../../../../domain/threads/title"; -import { shortThreadId } from "../../../../shared/id/thread-id"; import { type ActiveNoteContextReference, activeNoteContextReferenceMarker, diff --git a/src/features/chat/application/connection/connection-controller.ts b/src/features/chat/application/connection/connection-controller.ts index 59dc3314..dec3ed84 100644 --- a/src/features/chat/application/connection/connection-controller.ts +++ b/src/features/chat/application/connection/connection-controller.ts @@ -1,7 +1,7 @@ import type { ServerInitialization } from "../../../../domain/server/initialization"; -import type { ActiveConnectionWork, ConnectionWorkTracker } from "../../../../shared/lifecycle/connection-work"; import type { ChatConnectionPhase } from "../state/root-reducer"; import type { ChatStateStore } from "../state/store"; +import type { ActiveConnectionWork, ConnectionWorkTracker } from "./connection-work"; const STATUS_CONNECTION_STOPPED = "Codex app-server stopped."; const STATUS_CONNECTION_STARTING = "Starting Codex app-server..."; diff --git a/src/shared/lifecycle/connection-work.ts b/src/features/chat/application/connection/connection-work.ts similarity index 100% rename from src/shared/lifecycle/connection-work.ts rename to src/features/chat/application/connection/connection-work.ts diff --git a/src/features/chat/application/conversation/composition.ts b/src/features/chat/application/conversation/composition.ts index 1313a673..0290c727 100644 --- a/src/features/chat/application/conversation/composition.ts +++ b/src/features/chat/application/conversation/composition.ts @@ -1,7 +1,7 @@ import type { CodexInput } from "../../../../domain/chat/input"; import type { Thread } from "../../../../domain/threads/model"; -import type { LocalIdSource } from "../../../../shared/id/local-id"; import type { MessageStreamNoticeSection } from "../../domain/message-stream/items"; +import type { LocalIdSource } from "../local-id-source"; import type { ChatRuntimeSettingsActions } from "../runtime/settings-actions"; import type { ChatStateStore } from "../state/store"; import type { GoalActions } from "../threads/goal-actions"; diff --git a/src/features/chat/application/conversation/slash-command-execution.ts b/src/features/chat/application/conversation/slash-command-execution.ts index 31ac28ee..b4e1e8db 100644 --- a/src/features/chat/application/conversation/slash-command-execution.ts +++ b/src/features/chat/application/conversation/slash-command-execution.ts @@ -2,10 +2,10 @@ import type { ReasoningEffort } from "../../../../domain/catalog/metadata"; import { normalizeReasoningEffort } from "../../../../domain/catalog/metadata"; import type { CodexInput } from "../../../../domain/chat/input"; import type { ThreadGoal } from "../../../../domain/threads/goal"; +import { shortThreadId } from "../../../../domain/threads/id"; import type { Thread } from "../../../../domain/threads/model"; import type { ReferencedThreadMetadata } from "../../../../domain/threads/reference"; import { threadDisplayTitle } from "../../../../domain/threads/title"; -import { shortThreadId } from "../../../../shared/id/thread-id"; import type { MessageStreamAuditFact, MessageStreamNoticeSection } from "../../domain/message-stream/items"; import { modelOverrideMessage, reasoningEffortOverrideMessage } from "../../domain/runtime/labels"; import { diff --git a/src/features/chat/application/conversation/turn-submission-actions.ts b/src/features/chat/application/conversation/turn-submission-actions.ts index db883287..30d5ca59 100644 --- a/src/features/chat/application/conversation/turn-submission-actions.ts +++ b/src/features/chat/application/conversation/turn-submission-actions.ts @@ -1,6 +1,6 @@ import type { CodexInput } from "../../../../domain/chat/input"; import type { ReferencedThreadMetadata } from "../../../../domain/threads/reference"; -import type { LocalIdSource } from "../../../../shared/id/local-id"; +import type { LocalIdSource } from "../local-id-source"; import type { ChatStateStore } from "../state/store"; import { acknowledgeOptimisticTurnStart, diff --git a/src/shared/id/local-id.ts b/src/features/chat/application/local-id-source.ts similarity index 100% rename from src/shared/id/local-id.ts rename to src/features/chat/application/local-id-source.ts diff --git a/src/features/chat/application/threads/goal-actions.ts b/src/features/chat/application/threads/goal-actions.ts index 9fbccf8d..4fd066ba 100644 --- a/src/features/chat/application/threads/goal-actions.ts +++ b/src/features/chat/application/threads/goal-actions.ts @@ -1,7 +1,7 @@ import type { ThreadGoal, ThreadGoalStatus, ThreadGoalUpdate } from "../../../../domain/threads/goal"; -import type { LocalIdSource } from "../../../../shared/id/local-id"; import { goalChangeItem } from "../../domain/message-stream/factories/goal-items"; import type { GoalMessageStreamItem } from "../../domain/message-stream/items"; +import type { LocalIdSource } from "../local-id-source"; import type { ChatStateStore } from "../state/store"; import type { ThreadGoalReadTransport, ThreadGoalTransport } from "./goal-transport"; diff --git a/src/features/chat/domain/message-stream/factories/goal-items.ts b/src/features/chat/domain/message-stream/factories/goal-items.ts index d8df0412..27754977 100644 --- a/src/features/chat/domain/message-stream/factories/goal-items.ts +++ b/src/features/chat/domain/message-stream/factories/goal-items.ts @@ -1,5 +1,5 @@ +import { truncate } from "../../../../../domain/display/text-preview"; import type { ThreadGoal, ThreadGoalStatus } from "../../../../../domain/threads/goal"; -import { truncate } from "../../../../../shared/text/preview"; import type { GoalMessageStreamItem } from "../items"; const GOAL_SUMMARY_LIMIT = 140; diff --git a/src/features/chat/domain/message-stream/format/permission-rows.ts b/src/features/chat/domain/message-stream/format/permission-rows.ts index 695f9847..5e52a405 100644 --- a/src/features/chat/domain/message-stream/format/permission-rows.ts +++ b/src/features/chat/domain/message-stream/format/permission-rows.ts @@ -1,4 +1,4 @@ -import { jsonPreview } from "../../../../../shared/text/preview"; +import { jsonPreview } from "../../../../../domain/display/json-preview"; interface DetailRow { key: string; diff --git a/src/features/chat/domain/message-stream/semantics/active-turn.ts b/src/features/chat/domain/message-stream/semantics/active-turn.ts index 7edda3dc..81f9c1e1 100644 --- a/src/features/chat/domain/message-stream/semantics/active-turn.ts +++ b/src/features/chat/domain/message-stream/semantics/active-turn.ts @@ -1,4 +1,4 @@ -import { truncate } from "../../../../../shared/text/preview"; +import { truncate } from "../../../../../domain/display/text-preview"; import type { AgentRunSummary, AgentRunSummaryAgent, diff --git a/src/features/chat/host/bundles/connection-bundle.ts b/src/features/chat/host/bundles/connection-bundle.ts index 2e849f70..ca55b16f 100644 --- a/src/features/chat/host/bundles/connection-bundle.ts +++ b/src/features/chat/host/bundles/connection-bundle.ts @@ -3,8 +3,6 @@ import { Notice } from "obsidian"; import type { AppServerClient } from "../../../../app-server/connection/client"; import { type ConnectionManager, StaleConnectionError } from "../../../../app-server/connection/connection-manager"; import { isStaleAppServerSharedQueryContextError } from "../../../../app-server/query/shared-queries"; -import type { LocalIdSource } from "../../../../shared/id/local-id"; -import type { ConnectionWorkTracker } from "../../../../shared/lifecycle/connection-work"; import { type ChatServerDiagnosticsActions, createChatServerDiagnosticsActions } from "../../app-server/actions/diagnostics"; import { type ChatServerMetadataActions, createChatServerMetadataActions } from "../../app-server/actions/metadata"; import { type ChatServerThreadActions, createChatServerThreadActions } from "../../app-server/actions/threads"; @@ -14,6 +12,8 @@ import { createChatConnectionController, handleChatConnectionExit, } from "../../application/connection/connection-controller"; +import type { ConnectionWorkTracker } from "../../application/connection/connection-work"; +import type { LocalIdSource } from "../../application/local-id-source"; import { runtimeSnapshotForChatState } from "../../application/runtime/snapshot"; import type { ChatConnectionPhase } from "../../application/state/root-reducer"; import type { ChatStateStore } from "../../application/state/store"; diff --git a/src/features/chat/host/bundles/reconnect-bundle.ts b/src/features/chat/host/bundles/reconnect-bundle.ts index 0fcbd8b3..a88d8210 100644 --- a/src/features/chat/host/bundles/reconnect-bundle.ts +++ b/src/features/chat/host/bundles/reconnect-bundle.ts @@ -1,5 +1,5 @@ import type { ConnectionManager } from "../../../../app-server/connection/connection-manager"; -import type { ConnectionWorkTracker } from "../../../../shared/lifecycle/connection-work"; +import type { ConnectionWorkTracker } from "../../application/connection/connection-work"; import { type ChatReconnectActionsHost, reconnectPanel } from "../../application/connection/reconnect-actions"; import type { ChatConnectionPhase } from "../../application/state/root-reducer"; import type { ChatStateStore } from "../../application/state/store"; diff --git a/src/features/chat/host/bundles/thread-bundle.ts b/src/features/chat/host/bundles/thread-bundle.ts index 9eb7f650..7af175ae 100644 --- a/src/features/chat/host/bundles/thread-bundle.ts +++ b/src/features/chat/host/bundles/thread-bundle.ts @@ -2,11 +2,11 @@ import { Notice } from "obsidian"; import { recoverRolloutTokenUsage } from "../../../../app-server/services/rollout-token-usage"; import { normalizeExplicitThreadName } from "../../../../domain/threads/model"; -import type { LocalIdSource } from "../../../../shared/id/local-id"; import { createThreadOperations, type ThreadOperations } from "../../../threads/workflows/thread-operations"; import { createThreadTitleService, type ThreadTitleService } from "../../../threads/workflows/thread-title-service"; import type { ChatServerThreadActions } from "../../app-server/actions/threads"; import type { ChatAppServerGateway } from "../../app-server/session-gateway"; +import type { LocalIdSource } from "../../application/local-id-source"; import { messageStreamItems } from "../../application/state/message-stream"; import type { ChatStateStore } from "../../application/state/store"; import type { ActiveThreadIdentitySync } from "../../application/threads/active-thread-identity-sync"; diff --git a/src/features/chat/host/bundles/turn-bundle.ts b/src/features/chat/host/bundles/turn-bundle.ts index 96a261ca..2db367f9 100644 --- a/src/features/chat/host/bundles/turn-bundle.ts +++ b/src/features/chat/host/bundles/turn-bundle.ts @@ -1,4 +1,3 @@ -import type { LocalIdSource } from "../../../../shared/id/local-id"; import type { ChatServerThreadActions } from "../../app-server/actions/threads"; import type { ChatInboundHandler } from "../../app-server/inbound/handler"; import type { ChatAppServerGateway } from "../../app-server/session-gateway"; @@ -6,6 +5,7 @@ import { type ConversationTurnActions as ChatPanelConversationTurnActions, createConversationTurnActions, } from "../../application/conversation/composition"; +import type { LocalIdSource } from "../../application/local-id-source"; import { createPendingRequestActions, type PendingRequestActions } from "../../application/pending-requests/pending-request-actions"; import type { ChatStateStore } from "../../application/state/store"; import type { AutoTitleCoordinator } from "../../application/threads/auto-title-coordinator"; diff --git a/src/features/chat/host/contracts.ts b/src/features/chat/host/contracts.ts index 0df65953..be944f64 100644 --- a/src/features/chat/host/contracts.ts +++ b/src/features/chat/host/contracts.ts @@ -2,12 +2,12 @@ import type { App, Component, EventRef } from "obsidian"; import type { AppServerClient } from "../../../app-server/connection/client"; import type { AppServerQueryContext } from "../../../app-server/query/keys"; +import type { ObservedResultListener } from "../../../app-server/query/observed-result"; import type { ModelMetadata, ReasoningEffort } from "../../../domain/catalog/metadata"; +import type { SendShortcut } from "../../../domain/input/send-shortcut"; import type { PendingRequestCounts } from "../../../domain/pending-requests/aggregate"; import type { SharedServerMetadata } from "../../../domain/server/metadata"; import type { ArchiveExportSettings } from "../../../domain/threads/archive-markdown"; -import type { ObservedResultListener } from "../../../shared/query/observed-result"; -import type { SendShortcut } from "../../../shared/ui/keyboard"; import type { ThreadCatalogActiveReader, ThreadCatalogEventSink } from "../../threads/catalog/thread-catalog"; import type { ArchiveExportDestination } from "../../threads/workflows/archive-export"; import type { TurnDiffViewState } from "../../turn-diff/model"; diff --git a/src/features/chat/host/session-graph.ts b/src/features/chat/host/session-graph.ts index 31c72b02..d3c45e94 100644 --- a/src/features/chat/host/session-graph.ts +++ b/src/features/chat/host/session-graph.ts @@ -1,8 +1,8 @@ import { ConnectionManager } from "../../../app-server/connection/connection-manager"; import { isStaleAppServerSharedQueryContextError } from "../../../app-server/query/shared-queries"; -import { createLocalIdSource, type LocalIdSource } from "../../../shared/id/local-id"; -import type { ConnectionWorkTracker } from "../../../shared/lifecycle/connection-work"; import { createChatAppServerGateway } from "../app-server/session-gateway"; +import type { ConnectionWorkTracker } from "../application/connection/connection-work"; +import { createLocalIdSource, type LocalIdSource } from "../application/local-id-source"; import type { ChatAction, ChatConnectionPhase } from "../application/state/root-reducer"; import type { ChatStateStore } from "../application/state/store"; import type { ActiveThreadIdentitySync } from "../application/threads/active-thread-identity-sync"; diff --git a/src/features/chat/host/session.ts b/src/features/chat/host/session.ts index 1dc384dc..2ac368c6 100644 --- a/src/features/chat/host/session.ts +++ b/src/features/chat/host/session.ts @@ -2,7 +2,7 @@ import type { AppServerClient } from "../../../app-server/connection/client"; import { type AppServerQueryContext, appServerQueryContextMatches, appServerQueryContextRawEquals } from "../../../app-server/query/keys"; import { pendingRequestCountsFromQueues } from "../../../domain/pending-requests/aggregate"; import { threadMeaningfulTitle, threadWindowTitle } from "../../../domain/threads/title"; -import { ConnectionWorkTracker } from "../../../shared/lifecycle/connection-work"; +import { ConnectionWorkTracker } from "../application/connection/connection-work"; import type { ChatState } from "../application/state/root-reducer"; import { type ChatStateStore, createChatStateStore } from "../application/state/store"; import { parseRestoredThreadState, type RestoredThreadPlaceholderState } from "../application/threads/restored-thread-lifecycle"; diff --git a/src/features/chat/host/session/deferred-work.ts b/src/features/chat/host/session/deferred-work.ts index f3fdd5e7..25c89aa9 100644 --- a/src/features/chat/host/session/deferred-work.ts +++ b/src/features/chat/host/session/deferred-work.ts @@ -1,4 +1,4 @@ -import { DeferredTask, type DeferredTaskWindow } from "../../../../shared/lifecycle/deferred-task"; +import { DeferredTask, type DeferredTaskWindow } from "../../../../shared/runtime/deferred-task"; export interface ChatViewDeferredTasks { scheduleDiagnostics(callback: () => void): void; diff --git a/src/features/chat/host/session/shared-state-binding.ts b/src/features/chat/host/session/shared-state-binding.ts index dfa73bde..3e3f65fb 100644 --- a/src/features/chat/host/session/shared-state-binding.ts +++ b/src/features/chat/host/session/shared-state-binding.ts @@ -1,8 +1,8 @@ +import type { ObservedResult } from "../../../../app-server/query/observed-result"; +import { observedValue } from "../../../../app-server/query/observed-result"; import type { ModelMetadata } from "../../../../domain/catalog/metadata"; import type { SharedServerMetadata } from "../../../../domain/server/metadata"; import type { Thread } from "../../../../domain/threads/model"; -import type { ObservedResult } from "../../../../shared/query/observed-result"; -import { observedValue } from "../../../../shared/query/observed-result"; import type { ChatStateStore } from "../../application/state/store"; import type { ChatPanelConnectionBundle } from "../bundles/connection-bundle"; diff --git a/src/features/chat/host/view.obsidian.ts b/src/features/chat/host/view.obsidian.ts index 2a9130c6..117ee076 100644 --- a/src/features/chat/host/view.obsidian.ts +++ b/src/features/chat/host/view.obsidian.ts @@ -1,8 +1,8 @@ import { ItemView, type ViewStateResult, type WorkspaceLeaf } from "obsidian"; import { VIEW_TYPE_CODEX_PANEL } from "../../../constants"; -import { createLocalIdSource } from "../../../shared/id/local-id"; -import { createObsidianArchiveExportDestination } from "../../../shared/obsidian/archive-export-destination"; +import { createObsidianArchiveExportDestination } from "../../threads/obsidian/archive-export-destination.obsidian"; +import { createLocalIdSource } from "../application/local-id-source"; import type { ChatPanelHandle, CodexChatHost } from "./contracts"; import { ChatPanelSession } from "./session"; diff --git a/src/features/chat/panel/composer-controller.dom.ts b/src/features/chat/panel/composer-controller.dom.ts index 058855b7..f87d6983 100644 --- a/src/features/chat/panel/composer-controller.dom.ts +++ b/src/features/chat/panel/composer-controller.dom.ts @@ -1,4 +1,4 @@ -import { textareaCursorAtVisualBoundary } from "../../../shared/ui/textarea-caret.measure"; +import { textareaCursorAtVisualBoundary } from "../../../shared/dom/textarea-caret.measure"; import { type ComposerBoundaryScrollAction, composerBoundaryScrollDirection } from "../application/composer/boundary-scroll"; import { composerSuggestionSignature } from "../application/composer/suggestions"; import { syncComposerHeight } from "../ui/composer.dom"; diff --git a/src/features/chat/panel/composer-controller.ts b/src/features/chat/panel/composer-controller.ts index 29800c23..761463dd 100644 --- a/src/features/chat/panel/composer-controller.ts +++ b/src/features/chat/panel/composer-controller.ts @@ -1,5 +1,5 @@ import type { CodexInput } from "../../../domain/chat/input"; -import { isComposerSendKey, type SendShortcut } from "../../../shared/ui/keyboard"; +import { isComposerSendKey, type SendShortcut } from "../../../domain/input/send-shortcut"; import { type ComposerAttachment, type ComposerAttachmentHandler, diff --git a/src/features/chat/panel/shell.dom.tsx b/src/features/chat/panel/shell.dom.tsx index 6459cb4f..957229ee 100644 --- a/src/features/chat/panel/shell.dom.tsx +++ b/src/features/chat/panel/shell.dom.tsx @@ -1,6 +1,6 @@ import type { ComponentChild as UiNode } from "preact"; -import { listenDomEvent } from "../../../shared/ui/dom-events.dom"; -import { renderUiRoot, unmountUiRoot } from "../../../shared/ui/ui-root.dom"; +import { listenDomEvent } from "../../../shared/dom/events.dom"; +import { renderUiRoot, unmountUiRoot } from "../../../shared/dom/preact-root.dom"; import type { ChatStateStore } from "../application/state/store"; import type { ToolbarActions } from "../ui/toolbar"; import { type ChatPanelShellReadModelBinding, createChatPanelShellReadModelBinding } from "./shell-read-model"; diff --git a/src/features/chat/panel/surface/goal-projection.tsx b/src/features/chat/panel/surface/goal-projection.tsx index 4824b7a5..12a99d37 100644 --- a/src/features/chat/panel/surface/goal-projection.tsx +++ b/src/features/chat/panel/surface/goal-projection.tsx @@ -1,6 +1,6 @@ import type { ComponentChild as UiNode } from "preact"; import { h } from "preact"; -import type { SendShortcut } from "../../../../shared/ui/keyboard"; +import type { SendShortcut } from "../../../../domain/input/send-shortcut"; import type { GoalPanelActions, GoalPanelDisplayState, GoalPanelEditorState, GoalPanelOptions } from "../../ui/goal"; import { GoalPanel } from "../../ui/goal"; import type { ChatPanelGoalReadModel } from "../shell-read-model"; diff --git a/src/features/chat/panel/surface/message-stream-presenter.ts b/src/features/chat/panel/surface/message-stream-presenter.ts index 6dc98a8d..cbebc47f 100644 --- a/src/features/chat/panel/surface/message-stream-presenter.ts +++ b/src/features/chat/panel/surface/message-stream-presenter.ts @@ -1,7 +1,7 @@ import type { App, Component } from "obsidian"; import type { ComponentChild as UiNode } from "preact"; import { h } from "preact"; -import { copyTextWithNotice } from "../../../../shared/ui/clipboard"; +import { copyTextWithNotice } from "../../../../shared/obsidian/clipboard.obsidian"; import type { TurnDiffViewState } from "../../../turn-diff/model"; import type { PendingRequestBlockActions } from "../../application/pending-requests/block"; import type { ChatAction } from "../../application/state/root-reducer"; diff --git a/src/features/chat/panel/toolbar-actions.ts b/src/features/chat/panel/toolbar-actions.ts index 3b9d89ce..7d0bc3ff 100644 --- a/src/features/chat/panel/toolbar-actions.ts +++ b/src/features/chat/panel/toolbar-actions.ts @@ -1,4 +1,4 @@ -import { copyTextWithNotice } from "../../../shared/ui/clipboard"; +import { copyTextWithNotice } from "../../../shared/obsidian/clipboard.obsidian"; import type { ChatConnectionController } from "../application/connection/connection-controller"; import type { ChatAction, ChatState } from "../application/state/root-reducer"; import type { ChatStateStore } from "../application/state/store"; diff --git a/src/features/chat/presentation/message-stream/detail-view.ts b/src/features/chat/presentation/message-stream/detail-view.ts index 0105535c..c62bb409 100644 --- a/src/features/chat/presentation/message-stream/detail-view.ts +++ b/src/features/chat/presentation/message-stream/detail-view.ts @@ -1,6 +1,6 @@ -import { shortThreadId } from "../../../../shared/id/thread-id"; -import { pathRelativeToRoot } from "../../../../shared/path/file-paths"; -import { truncate } from "../../../../shared/text/preview"; +import { truncate } from "../../../../domain/display/text-preview"; +import { shortThreadId } from "../../../../domain/threads/id"; +import { pathRelativeToRoot } from "../../../../domain/vault/paths"; import type { AgentMessageStreamItem, ApprovalResultMessageStreamItem, diff --git a/src/features/chat/presentation/message-stream/layout.ts b/src/features/chat/presentation/message-stream/layout.ts index ad7581af..f2916441 100644 --- a/src/features/chat/presentation/message-stream/layout.ts +++ b/src/features/chat/presentation/message-stream/layout.ts @@ -1,4 +1,4 @@ -import { pathRelativeToRoot } from "../../../../shared/path/file-paths"; +import { pathRelativeToRoot } from "../../../../domain/vault/paths"; import type { MessageStreamItem } from "../../domain/message-stream/items"; import { messageStreamSemanticClassifications } from "../../domain/message-stream/semantics/classify"; import { diff --git a/src/features/chat/presentation/message-stream/status-view.ts b/src/features/chat/presentation/message-stream/status-view.ts index 42c2b77c..a5243358 100644 --- a/src/features/chat/presentation/message-stream/status-view.ts +++ b/src/features/chat/presentation/message-stream/status-view.ts @@ -1,4 +1,4 @@ -import { shortThreadId } from "../../../../shared/id/thread-id"; +import { shortThreadId } from "../../../../domain/threads/id"; import type { AgentRunSummary, AgentRunSummaryAgent, diff --git a/src/features/chat/presentation/runtime/status.ts b/src/features/chat/presentation/runtime/status.ts index 42a22915..3dca5ee3 100644 --- a/src/features/chat/presentation/runtime/status.ts +++ b/src/features/chat/presentation/runtime/status.ts @@ -1,6 +1,6 @@ +import { jsonPreview } from "../../../../domain/display/json-preview"; import { type RuntimeConfigSnapshot, runtimeConfigOrDefault } from "../../../../domain/runtime/config"; import type { RateLimitWindow, SpendControlLimitSnapshot, ThreadTokenUsage } from "../../../../domain/runtime/metrics"; -import { jsonPreview } from "../../../../shared/text/preview"; import { serviceTierLabel as formatServiceTierLabel, pendingRuntimeSettingLabel } from "../../domain/runtime/labels"; import { resolveRuntimeControls } from "../../domain/runtime/resolution"; import type { RuntimeSnapshot } from "../../domain/runtime/snapshot"; diff --git a/src/features/chat/ui/composer.dom.ts b/src/features/chat/ui/composer.dom.ts index da748234..e50a6ac2 100644 --- a/src/features/chat/ui/composer.dom.ts +++ b/src/features/chat/ui/composer.dom.ts @@ -1,7 +1,7 @@ import { setIcon } from "obsidian"; -import { disposeDomListeners, listenDomEscapeKey, listenDomEvent, listenOutsideDomEvent } from "../../../shared/ui/dom-events.dom"; -import { syncTextareaHeight } from "../../../shared/ui/textarea-autogrow.measure"; +import { disposeDomListeners, listenDomEscapeKey, listenDomEvent, listenOutsideDomEvent } from "../../../shared/dom/events.dom"; +import { syncTextareaHeight } from "../../../shared/dom/textarea-autogrow.measure"; const COMPOSER_META_EFFORT_HIDDEN_CLASS = "is-effort-hidden"; const COMPOSER_META_MODEL_HIDDEN_CLASS = "is-model-hidden"; diff --git a/src/features/chat/ui/composer.tsx b/src/features/chat/ui/composer.tsx index 00f7ee08..f66a2119 100644 --- a/src/features/chat/ui/composer.tsx +++ b/src/features/chat/ui/composer.tsx @@ -1,7 +1,7 @@ import type { ButtonHTMLAttributes, Ref, ComponentChild as UiNode } from "preact"; import { useLayoutEffect, useRef, useState } from "preact/hooks"; -import { IconButton } from "../../../shared/ui/components.obsidian"; +import { IconButton } from "../../../shared/obsidian/components.obsidian"; import { type ComposerMetaPickerState, closeComposerMetaPickerOnOutsidePointer, diff --git a/src/features/chat/ui/goal.dom.ts b/src/features/chat/ui/goal.dom.ts index 230cee36..8096d03e 100644 --- a/src/features/chat/ui/goal.dom.ts +++ b/src/features/chat/ui/goal.dom.ts @@ -1,5 +1,5 @@ -import { disposeDomListeners, listenDomEscapeKey, listenOutsideDomEvent } from "../../../shared/ui/dom-events.dom"; -import { syncTextareaHeight } from "../../../shared/ui/textarea-autogrow.measure"; +import { disposeDomListeners, listenDomEscapeKey, listenOutsideDomEvent } from "../../../shared/dom/events.dom"; +import { syncTextareaHeight } from "../../../shared/dom/textarea-autogrow.measure"; export function syncGoalObjectiveHeight(textarea: HTMLTextAreaElement | null): void { syncTextareaHeight(textarea, { diff --git a/src/features/chat/ui/goal.tsx b/src/features/chat/ui/goal.tsx index e8fca62a..aad63af0 100644 --- a/src/features/chat/ui/goal.tsx +++ b/src/features/chat/ui/goal.tsx @@ -1,9 +1,8 @@ import type { ComponentChild as UiNode } from "preact"; import { useEffect, useLayoutEffect, useRef, useState } from "preact/hooks"; - +import { isComposerSendKey, type SendShortcut } from "../../../domain/input/send-shortcut"; import type { ThreadGoal, ThreadGoalStatus } from "../../../domain/threads/goal"; -import { IconButton } from "../../../shared/ui/components.obsidian"; -import { isComposerSendKey, type SendShortcut } from "../../../shared/ui/keyboard"; +import { IconButton } from "../../../shared/obsidian/components.obsidian"; import { closeGoalEditorOnOutsidePointer, collapseGoalObjectiveOnOutsidePointer, diff --git a/src/features/chat/ui/message-stream/detail.tsx b/src/features/chat/ui/message-stream/detail.tsx index d700567d..f3e1c886 100644 --- a/src/features/chat/ui/message-stream/detail.tsx +++ b/src/features/chat/ui/message-stream/detail.tsx @@ -1,6 +1,6 @@ import type { ComponentChild as UiNode } from "preact"; -import { RawDiffView } from "../../../../shared/ui/diff"; +import { RawDiffView } from "../../../../shared/ui/diff-view"; import type { DetailSection, DetailView } from "../../presentation/message-stream/detail-view"; import type { MessageStreamDisclosureState } from "./context"; diff --git a/src/features/chat/ui/message-stream/flow-scroll.measure.ts b/src/features/chat/ui/message-stream/flow-scroll.measure.ts index 2d4af5f6..7c2ad6f3 100644 --- a/src/features/chat/ui/message-stream/flow-scroll.measure.ts +++ b/src/features/chat/ui/message-stream/flow-scroll.measure.ts @@ -1,6 +1,6 @@ import { Component, h, type ComponentChild as UiNode } from "preact"; -import { disposeDomListeners, listenDomEvent } from "../../../../shared/ui/dom-events.dom"; +import { disposeDomListeners, listenDomEvent } from "../../../../shared/dom/events.dom"; import { MESSAGE_CONTENT_RENDERED_EVENT } from "./content-rendered-event.dom"; type MessageScrollDirection = -1 | 1; diff --git a/src/features/chat/ui/message-stream/markdown-renderer.obsidian.ts b/src/features/chat/ui/message-stream/markdown-renderer.obsidian.ts index 312d8bb3..2793848b 100644 --- a/src/features/chat/ui/message-stream/markdown-renderer.obsidian.ts +++ b/src/features/chat/ui/message-stream/markdown-renderer.obsidian.ts @@ -1,7 +1,8 @@ import { micromark } from "micromark"; import { type App, type Component, MarkdownRenderer, Notice } from "obsidian"; -import { isAbsoluteFileHref, vaultFileLinkTarget, vaultRelativeFileLinkTarget } from "../../../../shared/obsidian/file-links"; +import { isAbsoluteFileHref, vaultRelativeFileLinkTarget } from "../../../../domain/vault/file-hrefs"; +import { vaultFileLinkTarget } from "../../../../shared/obsidian/vault-file-links.obsidian"; import { notifyMessageContentRendered } from "./content-rendered-event.dom"; interface MarkdownMessageRendererOptions { diff --git a/src/features/chat/ui/message-stream/text-content.dom.tsx b/src/features/chat/ui/message-stream/text-content.dom.tsx index 945c0c7d..80b7a6e9 100644 --- a/src/features/chat/ui/message-stream/text-content.dom.tsx +++ b/src/features/chat/ui/message-stream/text-content.dom.tsx @@ -1,7 +1,7 @@ import type { Ref, ComponentChild as UiNode } from "preact"; import { useEffect, useLayoutEffect, useRef, useState } from "preact/hooks"; -import { listenDomEvent, listenOutsideDomEvent } from "../../../../shared/ui/dom-events.dom"; +import { listenDomEvent, listenOutsideDomEvent } from "../../../../shared/dom/events.dom"; import type { MessageStreamTextView } from "../../presentation/message-stream/text-view"; import { MESSAGE_CONTENT_RENDERED_EVENT } from "./content-rendered-event.dom"; import type { TextItemContentContext } from "./context"; diff --git a/src/features/chat/ui/message-stream/text.dom.ts b/src/features/chat/ui/message-stream/text.dom.ts index 46fd93bc..49def949 100644 --- a/src/features/chat/ui/message-stream/text.dom.ts +++ b/src/features/chat/ui/message-stream/text.dom.ts @@ -1,4 +1,4 @@ -import { listenOutsideDomEvent } from "../../../../shared/ui/dom-events.dom"; +import { listenOutsideDomEvent } from "../../../../shared/dom/events.dom"; export function closeMessageRoleMenuOnOutsidePointer(root: HTMLElement, onClose: () => void): () => void { return listenOutsideDomEvent(root, "pointerdown", onClose, true); diff --git a/src/features/chat/ui/message-stream/text.tsx b/src/features/chat/ui/message-stream/text.tsx index 6b120e2c..5fdbeea7 100644 --- a/src/features/chat/ui/message-stream/text.tsx +++ b/src/features/chat/ui/message-stream/text.tsx @@ -1,6 +1,6 @@ import { Fragment, type ComponentChild as UiNode } from "preact"; import { useEffect, useRef } from "preact/hooks"; -import { IconButton } from "../../../../shared/ui/components.obsidian"; +import { IconButton } from "../../../../shared/obsidian/components.obsidian"; import type { EditedFilesTextView, MentionedFileTextView, diff --git a/src/features/chat/ui/toolbar.tsx b/src/features/chat/ui/toolbar.tsx index 3da82a15..6ac6c11a 100644 --- a/src/features/chat/ui/toolbar.tsx +++ b/src/features/chat/ui/toolbar.tsx @@ -1,6 +1,6 @@ import type { ButtonHTMLAttributes, ComponentChild as UiNode } from "preact"; import { useLayoutEffect, useRef } from "preact/hooks"; -import { IconButton, ObsidianToolbarAction, type ObsidianToolbarActionProps } from "../../../shared/ui/components.obsidian"; +import { IconButton, ObsidianToolbarAction, type ObsidianToolbarActionProps } from "../../../shared/obsidian/components.obsidian"; import type { RateLimitSummary } from "../presentation/runtime/status"; import { focusToolbarRenameInput } from "./toolbar.dom"; diff --git a/src/features/selection-rewrite/command.obsidian.ts b/src/features/selection-rewrite/command.obsidian.ts index 0a7d66d4..e497fe06 100644 --- a/src/features/selection-rewrite/command.obsidian.ts +++ b/src/features/selection-rewrite/command.obsidian.ts @@ -1,5 +1,5 @@ import { type Editor, MarkdownView, Notice, type Plugin } from "obsidian"; -import type { SendShortcut } from "../../shared/ui/keyboard"; +import type { SendShortcut } from "../../domain/input/send-shortcut"; import type { SelectionRewriteRuntimeSettings, SelectionRewriteState } from "./model"; import { SelectionRewritePopover } from "./popover.dom"; diff --git a/src/features/selection-rewrite/popover.dom.tsx b/src/features/selection-rewrite/popover.dom.tsx index f0ed8ff4..c1c06d83 100644 --- a/src/features/selection-rewrite/popover.dom.tsx +++ b/src/features/selection-rewrite/popover.dom.tsx @@ -1,13 +1,12 @@ import { type Editor, Notice } from "obsidian"; import type { TargetedKeyboardEvent, ComponentChild as UiNode } from "preact"; - -import { IconButton } from "../../shared/ui/components.obsidian"; -import { DiffLineList, unifiedDiffDisplayLines } from "../../shared/ui/diff"; -import { listenDomEscapeKey, listenDomEvent, listenOutsideDomEvent } from "../../shared/ui/dom-events.dom"; -import { isComposerSendKey, type SendShortcut } from "../../shared/ui/keyboard"; -import { syncTextareaHeight } from "../../shared/ui/textarea-autogrow.measure"; -import { textareaCursorAtVisualBoundary } from "../../shared/ui/textarea-caret.measure"; -import { renderUiRoot, unmountUiRoot } from "../../shared/ui/ui-root.dom"; +import { isComposerSendKey, type SendShortcut } from "../../domain/input/send-shortcut"; +import { listenDomEscapeKey, listenDomEvent, listenOutsideDomEvent } from "../../shared/dom/events.dom"; +import { renderUiRoot, unmountUiRoot } from "../../shared/dom/preact-root.dom"; +import { syncTextareaHeight } from "../../shared/dom/textarea-autogrow.measure"; +import { textareaCursorAtVisualBoundary } from "../../shared/dom/textarea-caret.measure"; +import { IconButton } from "../../shared/obsidian/components.obsidian"; +import { DiffLineList, unifiedDiffDisplayLines } from "../../shared/ui/diff-view"; import { buildSelectionUnifiedDiff } from "./diff"; import { canApplySelectionRewrite, diff --git a/src/features/thread-picker/modal.obsidian.ts b/src/features/thread-picker/modal.obsidian.ts index e68299bd..cc664515 100644 --- a/src/features/thread-picker/modal.obsidian.ts +++ b/src/features/thread-picker/modal.obsidian.ts @@ -1,8 +1,7 @@ import { type App, Notice, Platform, SuggestModal } from "obsidian"; - +import { shortThreadId } from "../../domain/threads/id"; import { type Thread, threadRecencyAt } from "../../domain/threads/model"; import { threadDisplayTitle } from "../../domain/threads/title"; -import { shortThreadId } from "../../shared/id/thread-id"; import type { ThreadCatalogActiveReader } from "../threads/catalog/thread-catalog"; export interface ThreadPickerHost { diff --git a/src/features/threads-view/session.ts b/src/features/threads-view/session.ts index 599dfdbc..de8740bb 100644 --- a/src/features/threads-view/session.ts +++ b/src/features/threads-view/session.ts @@ -1,12 +1,12 @@ import { Notice } from "obsidian"; import type { AppServerClientAccess } from "../../app-server/connection/client-access"; +import type { ObservedResult } from "../../app-server/query/observed-result"; +import { observedInitialError, observedInitialLoading, observedValue } from "../../app-server/query/observed-result"; import { isStaleAppServerSharedQueryContextError } from "../../app-server/query/shared-queries"; import type { ReasoningEffort } from "../../domain/catalog/metadata"; import type { ArchiveExportSettings } from "../../domain/threads/archive-markdown"; import type { Thread } from "../../domain/threads/model"; -import type { ObservedResult } from "../../shared/query/observed-result"; -import { observedInitialError, observedInitialLoading, observedValue } from "../../shared/query/observed-result"; import type { ThreadCatalogActiveReader, ThreadCatalogEventSink } from "../threads/catalog/thread-catalog"; import type { ArchiveExportDestination } from "../threads/workflows/archive-export"; import { createThreadOperations, type ThreadOperations } from "../threads/workflows/thread-operations"; diff --git a/src/features/threads-view/shell.dom.tsx b/src/features/threads-view/shell.dom.tsx index 1781b53f..046cbab7 100644 --- a/src/features/threads-view/shell.dom.tsx +++ b/src/features/threads-view/shell.dom.tsx @@ -1,8 +1,7 @@ import type { ButtonHTMLAttributes, ComponentChild as UiNode } from "preact"; import { useLayoutEffect, useRef } from "preact/hooks"; - -import { IconButton, ObsidianToolbarAction, type ObsidianToolbarActionProps } from "../../shared/ui/components.obsidian"; -import { renderUiRoot, unmountUiRoot } from "../../shared/ui/ui-root.dom"; +import { renderUiRoot, unmountUiRoot } from "../../shared/dom/preact-root.dom"; +import { IconButton, ObsidianToolbarAction, type ObsidianToolbarActionProps } from "../../shared/obsidian/components.obsidian"; import type { ThreadsRowModel } from "./state"; type ButtonProps = ButtonHTMLAttributes & { diff --git a/src/features/threads-view/view-lifecycle.ts b/src/features/threads-view/view-lifecycle.ts index c4151162..f0190a9b 100644 --- a/src/features/threads-view/view-lifecycle.ts +++ b/src/features/threads-view/view-lifecycle.ts @@ -1,4 +1,4 @@ -import { DeferredTask, type DeferredTaskWindow } from "../../shared/lifecycle/deferred-task"; +import { DeferredTask, type DeferredTaskWindow } from "../../shared/runtime/deferred-task"; export type ThreadsViewRefreshLifecycleState = { kind: "idle" } | { kind: "loading" }; export type ActiveThreadsViewRefresh = Extract; diff --git a/src/features/threads-view/view.obsidian.ts b/src/features/threads-view/view.obsidian.ts index a34352ae..ad7e6937 100644 --- a/src/features/threads-view/view.obsidian.ts +++ b/src/features/threads-view/view.obsidian.ts @@ -1,7 +1,7 @@ import { ItemView, type WorkspaceLeaf } from "obsidian"; import { VIEW_TYPE_CODEX_THREADS } from "../../constants"; -import { createObsidianArchiveExportDestination } from "../../shared/obsidian/archive-export-destination"; +import { createObsidianArchiveExportDestination } from "../threads/obsidian/archive-export-destination.obsidian"; import { type ThreadsViewHost, ThreadsViewSession } from "./session"; export class CodexThreadsView extends ItemView { diff --git a/src/features/threads/catalog/thread-catalog.ts b/src/features/threads/catalog/thread-catalog.ts index a5aee486..65670b2e 100644 --- a/src/features/threads/catalog/thread-catalog.ts +++ b/src/features/threads/catalog/thread-catalog.ts @@ -1,5 +1,5 @@ +import type { ObservedResultListener } from "../../../app-server/query/observed-result"; import type { Thread } from "../../../domain/threads/model"; -import type { ObservedResultListener } from "../../../shared/query/observed-result"; type ThreadListObserver = ObservedResultListener; diff --git a/src/shared/obsidian/archive-export-destination.ts b/src/features/threads/obsidian/archive-export-destination.obsidian.ts similarity index 100% rename from src/shared/obsidian/archive-export-destination.ts rename to src/features/threads/obsidian/archive-export-destination.obsidian.ts diff --git a/src/features/threads/workflows/archive-export.ts b/src/features/threads/workflows/archive-export.ts index 191c7711..d7d3887e 100644 --- a/src/features/threads/workflows/archive-export.ts +++ b/src/features/threads/workflows/archive-export.ts @@ -1,6 +1,6 @@ import { type ArchiveExportSettings, type ArchiveThreadInput, archivedThreadMarkdown } from "../../../domain/threads/archive-markdown"; +import { shortThreadId } from "../../../domain/threads/id"; import { threadArchiveTitle } from "../../../domain/threads/title"; -import { shortThreadId } from "../../../shared/id/thread-id"; export interface ArchiveExportResult { path: string; diff --git a/src/features/turn-diff/render.dom.tsx b/src/features/turn-diff/render.dom.tsx index 1842cd12..ece6fb88 100644 --- a/src/features/turn-diff/render.dom.tsx +++ b/src/features/turn-diff/render.dom.tsx @@ -1,9 +1,9 @@ import type { ComponentChild as UiNode } from "preact"; -import { shortThreadId } from "../../shared/id/thread-id"; -import { IconButton } from "../../shared/ui/components.obsidian"; -import { UnifiedDiffView } from "../../shared/ui/diff"; -import { renderUiRoot } from "../../shared/ui/ui-root.dom"; +import { shortThreadId } from "../../domain/threads/id"; +import { renderUiRoot } from "../../shared/dom/preact-root.dom"; +import { IconButton } from "../../shared/obsidian/components.obsidian"; +import { UnifiedDiffView } from "../../shared/ui/diff-view"; import type { PersistedTurnDiffViewState, TurnDiffViewState } from "./model"; export interface TurnDiffViewActions { diff --git a/src/features/turn-diff/view.obsidian.ts b/src/features/turn-diff/view.obsidian.ts index a9594387..557b09ee 100644 --- a/src/features/turn-diff/view.obsidian.ts +++ b/src/features/turn-diff/view.obsidian.ts @@ -1,8 +1,8 @@ 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.dom"; +import { unmountUiRoot } from "../../shared/dom/preact-root.dom"; +import { copyTextWithNotice } from "../../shared/obsidian/clipboard.obsidian"; import { isPersistedTurnDiffViewState, type PersistedTurnDiffViewState, persistedTurnDiffViewState, type TurnDiffViewState } from "./model"; import { renderTurnDiffView } from "./render.dom"; diff --git a/src/settings/app-server-dynamic-data.ts b/src/settings/app-server-dynamic-data.ts index 9f79241d..1e32a8a4 100644 --- a/src/settings/app-server-dynamic-data.ts +++ b/src/settings/app-server-dynamic-data.ts @@ -1,11 +1,11 @@ import type { AppServerClient } from "../app-server/connection/client"; import type { AppServerClientAccess } from "../app-server/connection/client-access"; +import type { ObservedResultListener } from "../app-server/query/observed-result"; import { isStaleAppServerSharedQueryContextError } from "../app-server/query/shared-queries"; import { listHookCatalog, setHookItemEnabled, trustHookItem } from "../app-server/services/catalog"; import { deleteThread, restoreArchivedThread as restoreArchivedThreadOnAppServer } from "../app-server/services/threads"; import type { ModelMetadata } from "../domain/catalog/metadata"; import type { ThreadCatalogArchivedReader, ThreadCatalogEventSink } from "../features/threads/catalog/thread-catalog"; -import type { ObservedResultListener } from "../shared/query/observed-result"; import { type SettingsDynamicDataAccess, type SettingsHookCatalog, StaleSettingsDynamicDataContextError } from "./dynamic-data"; interface SettingsAppServerQueries { diff --git a/src/settings/archived-section.tsx b/src/settings/archived-section.tsx index 45552dce..0899eef7 100644 --- a/src/settings/archived-section.tsx +++ b/src/settings/archived-section.tsx @@ -1,9 +1,8 @@ import type { ComponentChild as UiNode } from "preact"; - +import { shortThreadId } from "../domain/threads/id"; 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.obsidian"; +import { ObsidianExtraButton, ObsidianTextInput, ObsidianToggle } from "../shared/obsidian/components.obsidian"; import type { ArchivedThreadSectionState } from "./section-state"; import { SettingRow, SettingsGroup, SettingsHeading, SettingsItems, SettingsStatusRow } from "./setting-components"; diff --git a/src/settings/dynamic-data.ts b/src/settings/dynamic-data.ts index da63872a..aae0a4dc 100644 --- a/src/settings/dynamic-data.ts +++ b/src/settings/dynamic-data.ts @@ -1,6 +1,6 @@ +import type { ObservedResultListener } from "../app-server/query/observed-result"; import type { HookItem, ModelMetadata } from "../domain/catalog/metadata"; import type { Thread } from "../domain/threads/model"; -import type { ObservedResultListener } from "../shared/query/observed-result"; export interface SettingsHookCatalog { hooks: readonly HookItem[]; diff --git a/src/settings/dynamic-sections-controller.ts b/src/settings/dynamic-sections-controller.ts index b6fffc4b..621cadcb 100644 --- a/src/settings/dynamic-sections-controller.ts +++ b/src/settings/dynamic-sections-controller.ts @@ -1,9 +1,9 @@ +import type { ObservedResult } from "../app-server/query/observed-result"; +import { observedValue } from "../app-server/query/observed-result"; import type { HookItem, ModelMetadata, ReasoningEffort } from "../domain/catalog/metadata"; import { findModelMetadataByIdOrName, sortedModelMetadata, supportedEffortsForModelMetadata } from "../domain/catalog/metadata"; import type { Thread } from "../domain/threads/model"; import { threadArchiveDisplayTitle } from "../domain/threads/title"; -import type { ObservedResult } from "../shared/query/observed-result"; -import { observedValue } from "../shared/query/observed-result"; import { isStaleSettingsDynamicDataContextError } from "./dynamic-data"; import type { SettingsDynamicSectionsHost } from "./host"; import { diff --git a/src/settings/helper-section.tsx b/src/settings/helper-section.tsx index d7c8ceec..903e38b1 100644 --- a/src/settings/helper-section.tsx +++ b/src/settings/helper-section.tsx @@ -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.obsidian"; +import { ObsidianDropdown } from "../shared/obsidian/components.obsidian"; import type { HelperSettingsState } from "./section-state"; import { SettingRow, SettingsGroup, SettingsHeading, SettingsItems } from "./setting-components"; diff --git a/src/settings/hook-section.tsx b/src/settings/hook-section.tsx index fc5c1c23..559ce0fb 100644 --- a/src/settings/hook-section.tsx +++ b/src/settings/hook-section.tsx @@ -1,7 +1,7 @@ import type { ComponentChild as UiNode } from "preact"; import type { HookItem } from "../domain/catalog/metadata"; -import { ObsidianButton } from "../shared/ui/components.obsidian"; +import { ObsidianButton } from "../shared/obsidian/components.obsidian"; import type { HookSectionState } from "./section-state"; import { SettingRow, SettingsGroup, SettingsHeading, SettingsItems, SettingsStatusRow } from "./setting-components"; diff --git a/src/settings/model.ts b/src/settings/model.ts index 03614109..deb2121b 100644 --- a/src/settings/model.ts +++ b/src/settings/model.ts @@ -3,7 +3,7 @@ import { type App, FileSystemAdapter } from "obsidian"; import { DEFAULT_CODEX_PATH } from "../constants"; import type { ReasoningEffort } from "../domain/catalog/metadata"; import { normalizeReasoningEffort } from "../domain/catalog/metadata"; -import type { SendShortcut } from "../shared/ui/keyboard"; +import type { SendShortcut } from "../domain/input/send-shortcut"; export interface CodexPanelSettings { codexPath: string; diff --git a/src/settings/tab-shell.tsx b/src/settings/tab-shell.tsx index 90ef23d1..290bb415 100644 --- a/src/settings/tab-shell.tsx +++ b/src/settings/tab-shell.tsx @@ -1,8 +1,8 @@ import type { ComponentChild as UiNode } from "preact"; import { DEFAULT_CODEX_PATH } from "../constants"; -import { IconButton, ObsidianCommitTextInput, ObsidianDropdown, ObsidianToggle } from "../shared/ui/components.obsidian"; -import type { SendShortcut } from "../shared/ui/keyboard"; +import type { SendShortcut } from "../domain/input/send-shortcut"; +import { IconButton, ObsidianCommitTextInput, ObsidianDropdown, ObsidianToggle } from "../shared/obsidian/components.obsidian"; import { ArchivedThreadSection } from "./archived-section"; import { HelperSettingsSection } from "./helper-section"; import { HookSection } from "./hook-section"; diff --git a/src/settings/tab.obsidian.tsx b/src/settings/tab.obsidian.tsx index 40296422..90875a0f 100644 --- a/src/settings/tab.obsidian.tsx +++ b/src/settings/tab.obsidian.tsx @@ -2,8 +2,8 @@ import { type App, Notice, type Plugin, PluginSettingTab } from "obsidian"; import { DEFAULT_CODEX_PATH } from "../constants"; import type { ReasoningEffort } from "../domain/catalog/metadata"; -import { listenDomEvent } from "../shared/ui/dom-events.dom"; -import { renderUiRoot, unmountUiRoot } from "../shared/ui/ui-root.dom"; +import { listenDomEvent } from "../shared/dom/events.dom"; +import { renderUiRoot, unmountUiRoot } from "../shared/dom/preact-root.dom"; import { SettingsDynamicSectionsController } from "./dynamic-sections-controller"; import type { CodexPanelSettingTabHost } from "./host"; import { DEFAULT_ATTACHMENT_FOLDER } from "./model"; diff --git a/src/shared/ui/dom-events.dom.ts b/src/shared/dom/events.dom.ts similarity index 100% rename from src/shared/ui/dom-events.dom.ts rename to src/shared/dom/events.dom.ts diff --git a/src/shared/ui/ui-root.dom.tsx b/src/shared/dom/preact-root.dom.tsx similarity index 100% rename from src/shared/ui/ui-root.dom.tsx rename to src/shared/dom/preact-root.dom.tsx diff --git a/src/shared/ui/textarea-autogrow.measure.ts b/src/shared/dom/textarea-autogrow.measure.ts similarity index 100% rename from src/shared/ui/textarea-autogrow.measure.ts rename to src/shared/dom/textarea-autogrow.measure.ts diff --git a/src/shared/ui/textarea-caret.measure.ts b/src/shared/dom/textarea-caret.measure.ts similarity index 100% rename from src/shared/ui/textarea-caret.measure.ts rename to src/shared/dom/textarea-caret.measure.ts diff --git a/src/shared/ui/clipboard.ts b/src/shared/obsidian/clipboard.obsidian.ts similarity index 100% rename from src/shared/ui/clipboard.ts rename to src/shared/obsidian/clipboard.obsidian.ts diff --git a/src/shared/ui/components.obsidian.tsx b/src/shared/obsidian/components.obsidian.tsx similarity index 99% rename from src/shared/ui/components.obsidian.tsx rename to src/shared/obsidian/components.obsidian.tsx index 56ca6f07..6d5552cc 100644 --- a/src/shared/ui/components.obsidian.tsx +++ b/src/shared/obsidian/components.obsidian.tsx @@ -2,7 +2,7 @@ import { ButtonComponent, DropdownComponent, ExtraButtonComponent, setIcon, Text import type { ButtonHTMLAttributes, HTMLAttributes, Ref, ComponentChild as UiNode } from "preact"; import { useLayoutEffect, useRef } from "preact/hooks"; -import { disposeDomListeners, listenDomEvent } from "./dom-events.dom"; +import { disposeDomListeners, listenDomEvent } from "../dom/events.dom"; interface ObsidianIconProps { icon: string; diff --git a/src/shared/obsidian/file-links.ts b/src/shared/obsidian/file-links.ts deleted file mode 100644 index f924fdc0..00000000 --- a/src/shared/obsidian/file-links.ts +++ /dev/null @@ -1,34 +0,0 @@ -import { type App, TFile } from "obsidian"; - -import { isFilesystemAbsolutePath, isVaultConfigPath, normalizeFilePath, parseFileHref, vaultRelativePath } from "../path/file-paths"; - -export function vaultFileLinkTarget(app: App, vaultPath: string, href: string): string | null { - const relativePath = vaultRelativeFileHref(vaultPath, app.vault.configDir, href); - if (!relativePath) return null; - - const abstractFile = app.vault.getAbstractFileByPath(relativePath.path); - return abstractFile instanceof TFile ? `${relativePath.path}${relativePath.subpath}` : null; -} - -export function vaultRelativeFileLinkTarget(vaultPath: string, configDir: string, href: string): string | null { - const relativePath = vaultRelativeFileHref(vaultPath, configDir, href); - return relativePath ? `${relativePath.path}${relativePath.subpath}` : null; -} - -export function isAbsoluteFileHref(href: string): boolean { - const parsed = parseFileHref(href); - return parsed ? isFilesystemAbsolutePath(normalizeFilePath(parsed.path)) : false; -} - -function vaultRelativeFileHref(vaultPath: string, configDir: string, href: string): { path: string; subpath: string } | null { - const parsed = parseFileHref(href); - if (!parsed) return null; - - const relativePath = vaultRelativePath(vaultPath, parsed.path, { allowRelative: true }); - if (!relativePath) return null; - - const normalized = normalizeFilePath(relativePath); - if (isVaultConfigPath(normalized, configDir)) return null; - - return { path: normalized, subpath: parsed.subpath }; -} diff --git a/src/shared/obsidian/vault-file-links.obsidian.ts b/src/shared/obsidian/vault-file-links.obsidian.ts new file mode 100644 index 00000000..5eda1f4c --- /dev/null +++ b/src/shared/obsidian/vault-file-links.obsidian.ts @@ -0,0 +1,11 @@ +import { type App, TFile } from "obsidian"; + +import { vaultRelativeFileHref } from "../../domain/vault/file-hrefs"; + +export function vaultFileLinkTarget(app: App, vaultPath: string, href: string): string | null { + const relativePath = vaultRelativeFileHref(vaultPath, app.vault.configDir, href); + if (!relativePath) return null; + + const abstractFile = app.vault.getAbstractFileByPath(relativePath.path); + return abstractFile instanceof TFile ? `${relativePath.path}${relativePath.subpath}` : null; +} diff --git a/src/shared/lifecycle/abort-signal.ts b/src/shared/runtime/abort-signal.ts similarity index 100% rename from src/shared/lifecycle/abort-signal.ts rename to src/shared/runtime/abort-signal.ts diff --git a/src/shared/lifecycle/deferred-task.ts b/src/shared/runtime/deferred-task.ts similarity index 100% rename from src/shared/lifecycle/deferred-task.ts rename to src/shared/runtime/deferred-task.ts diff --git a/src/shared/ui/diff.tsx b/src/shared/ui/diff-view.tsx similarity index 100% rename from src/shared/ui/diff.tsx rename to src/shared/ui/diff-view.tsx diff --git a/tests/shared/query/observed-result.test.ts b/tests/app-server/query/observed-result.test.ts similarity index 90% rename from tests/shared/query/observed-result.test.ts rename to tests/app-server/query/observed-result.test.ts index 3312526f..07968db9 100644 --- a/tests/shared/query/observed-result.test.ts +++ b/tests/app-server/query/observed-result.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it } from "vitest"; -import type { ObservedResult } from "../../../src/shared/query/observed-result"; -import { observedInitialError, observedInitialLoading, observedValue } from "../../../src/shared/query/observed-result"; +import type { ObservedResult } from "../../../src/app-server/query/observed-result"; +import { observedInitialError, observedInitialLoading, observedValue } from "../../../src/app-server/query/observed-result"; describe("observed query result helpers", () => { it("treats successful empty arrays as current values", () => { diff --git a/tests/app-server/shared-queries.test.ts b/tests/app-server/shared-queries.test.ts index fca11fd4..6445f990 100644 --- a/tests/app-server/shared-queries.test.ts +++ b/tests/app-server/shared-queries.test.ts @@ -1,12 +1,12 @@ import { describe, expect, it, vi } from "vitest"; import type { AppServerQueryCache } from "../../src/app-server/query/cache"; +import type { ObservedResult } from "../../src/app-server/query/observed-result"; import { AppServerSharedQueries, StaleAppServerSharedQueryContextError } from "../../src/app-server/query/shared-queries"; import type { ModelMetadata } from "../../src/domain/catalog/metadata"; import { createServerDiagnostics, diagnosticProbeOk, diagnosticsWithProbe } from "../../src/domain/server/diagnostics"; import type { SharedServerMetadata } from "../../src/domain/server/metadata"; import type { Thread } from "../../src/domain/threads/model"; -import type { ObservedResult } from "../../src/shared/query/observed-result"; import { deferred } from "../support/async"; describe("AppServerSharedQueries", () => { diff --git a/tests/shared/ui/keyboard.test.ts b/tests/domain/input/send-shortcut.test.ts similarity index 97% rename from tests/shared/ui/keyboard.test.ts rename to tests/domain/input/send-shortcut.test.ts index fc057d59..8e04af1b 100644 --- a/tests/shared/ui/keyboard.test.ts +++ b/tests/domain/input/send-shortcut.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { type ComposerSendKeyEvent, isComposerSendKey } from "../../../src/shared/ui/keyboard"; +import { type ComposerSendKeyEvent, isComposerSendKey } from "../../../src/domain/input/send-shortcut"; const baseEvent: ComposerSendKeyEvent = { key: "Enter", diff --git a/tests/shared/path/file-paths.test.ts b/tests/domain/vault/file-hrefs-and-paths.test.ts similarity index 89% rename from tests/shared/path/file-paths.test.ts rename to tests/domain/vault/file-hrefs-and-paths.test.ts index d771e190..290a2bca 100644 --- a/tests/shared/path/file-paths.test.ts +++ b/tests/domain/vault/file-hrefs-and-paths.test.ts @@ -1,12 +1,7 @@ import { describe, expect, it } from "vitest"; -import { - isExternalFileHref, - normalizeFilePath, - parseFileHref, - pathRelativeToRoot, - vaultRelativePath, -} from "../../../src/shared/path/file-paths"; +import { isExternalFileHref, parseFileHref } from "../../../src/domain/vault/file-hrefs"; +import { normalizeFilePath, pathRelativeToRoot, vaultRelativePath } from "../../../src/domain/vault/paths"; describe("file path helpers", () => { it("normalizes separators, duplicate slashes, and leading dot segments", () => { diff --git a/tests/features/chat/app-server/inbound/handler.test.ts b/tests/features/chat/app-server/inbound/handler.test.ts index ccbbc292..80814dbb 100644 --- a/tests/features/chat/app-server/inbound/handler.test.ts +++ b/tests/features/chat/app-server/inbound/handler.test.ts @@ -9,11 +9,11 @@ import { createChatInboundHandler, } from "../../../../../src/features/chat/app-server/inbound/handler"; import { pendingTurnStart } from "../../../../../src/features/chat/application/conversation/turn-state"; +import { createLocalIdSource } from "../../../../../src/features/chat/application/local-id-source"; import { type ChatAction, type ChatState, chatReducer } from "../../../../../src/features/chat/application/state/root-reducer"; import type { ChatStateStore } from "../../../../../src/features/chat/application/state/store"; import { attachHookRunsToTurn } from "../../../../../src/features/chat/domain/message-stream/updates"; import type { ThreadCatalogEvent } from "../../../../../src/features/threads/catalog/thread-catalog"; -import { createLocalIdSource } from "../../../../../src/shared/id/local-id"; import { chatStateMessageStreamItems, withChatStateMessageStreamItems } from "../../support/message-stream"; import { chatStateFixture, chatStateWith } from "../../support/state"; diff --git a/tests/features/chat/app-server/mappers/message-stream.test.ts b/tests/features/chat/app-server/mappers/message-stream.test.ts index a6629514..744c37b8 100644 --- a/tests/features/chat/app-server/mappers/message-stream.test.ts +++ b/tests/features/chat/app-server/mappers/message-stream.test.ts @@ -2,6 +2,7 @@ import { describe, expect, it } from "vitest"; import type { TurnItem, TurnRecord } from "../../../../../src/app-server/protocol/turn"; import type { Thread } from "../../../../../src/domain/threads/model"; import { referencedThreadPromptBundle } from "../../../../../src/domain/threads/reference"; +import { pathRelativeToRoot } from "../../../../../src/domain/vault/paths"; import { collabAgentStateExecutionState } from "../../../../../src/features/chat/app-server/mappers/message-stream/execution-state"; import { hookRunMessageStreamItem } from "../../../../../src/features/chat/app-server/mappers/message-stream/hook-run-items"; import { @@ -18,7 +19,6 @@ import type { MessageStreamItem } from "../../../../../src/features/chat/domain/ import { activeTurnLiveItems } from "../../../../../src/features/chat/domain/message-stream/semantics/active-turn"; import { upsertMessageStreamItemById } from "../../../../../src/features/chat/domain/message-stream/updates"; import { messageStreamLayoutBlocks } from "../../../../../src/features/chat/presentation/message-stream/layout"; -import { pathRelativeToRoot } from "../../../../../src/shared/path/file-paths"; function commandItem(id: string, text: string, turnId: string): MessageStreamItem { return { diff --git a/tests/features/chat/application/connection/connection-controller.test.ts b/tests/features/chat/application/connection/connection-controller.test.ts index 847b75e0..b216711a 100644 --- a/tests/features/chat/application/connection/connection-controller.test.ts +++ b/tests/features/chat/application/connection/connection-controller.test.ts @@ -8,9 +8,9 @@ import { type ChatConnectionMetadataActions, createChatConnectionController, } from "../../../../../src/features/chat/application/connection/connection-controller"; +import { ConnectionWorkTracker } from "../../../../../src/features/chat/application/connection/connection-work"; import { createChatState } from "../../../../../src/features/chat/application/state/root-reducer"; import { createChatStateStore } from "../../../../../src/features/chat/application/state/store"; -import { ConnectionWorkTracker } from "../../../../../src/shared/lifecycle/connection-work"; function createController({ connected = false } = {}) { const stateStore = createChatStateStore(createChatState()); diff --git a/tests/shared/lifecycle/connection-work.test.ts b/tests/features/chat/application/connection/connection-work.test.ts similarity index 92% rename from tests/shared/lifecycle/connection-work.test.ts rename to tests/features/chat/application/connection/connection-work.test.ts index 890f1ff4..65040789 100644 --- a/tests/shared/lifecycle/connection-work.test.ts +++ b/tests/features/chat/application/connection/connection-work.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { ConnectionWorkTracker } from "../../../src/shared/lifecycle/connection-work"; +import { ConnectionWorkTracker } from "../../../../../src/features/chat/application/connection/connection-work"; describe("ConnectionWorkTracker", () => { it("tracks active connection work by identity", () => { diff --git a/tests/features/chat/application/conversation/turn-submission-actions.test.ts b/tests/features/chat/application/conversation/turn-submission-actions.test.ts index 4db45a4a..a7d506b7 100644 --- a/tests/features/chat/application/conversation/turn-submission-actions.test.ts +++ b/tests/features/chat/application/conversation/turn-submission-actions.test.ts @@ -7,9 +7,9 @@ import { createTurnSubmissionActions, type TurnSubmissionActionsHost, } from "../../../../../src/features/chat/application/conversation/turn-submission-actions"; +import { createLocalIdSource } from "../../../../../src/features/chat/application/local-id-source"; import { createChatState } from "../../../../../src/features/chat/application/state/root-reducer"; import { createChatStateStore } from "../../../../../src/features/chat/application/state/store"; -import { createLocalIdSource } from "../../../../../src/shared/id/local-id"; import { chatStateMessageStreamItems } from "../../support/message-stream"; const textInput = (text: string): CodexInput => [{ type: "text", text }]; diff --git a/tests/shared/local-id.test.ts b/tests/features/chat/application/local-id-source.test.ts similarity index 91% rename from tests/shared/local-id.test.ts rename to tests/features/chat/application/local-id-source.test.ts index 5fcf91ee..40870d04 100644 --- a/tests/shared/local-id.test.ts +++ b/tests/features/chat/application/local-id-source.test.ts @@ -1,6 +1,6 @@ import { describe, expect, it } from "vitest"; -import { createLocalIdSource } from "../../src/shared/id/local-id"; +import { createLocalIdSource } from "../../../../src/features/chat/application/local-id-source"; describe("local ID source", () => { it("generates monotonic IDs per source with a stable prefix and timestamp", () => { diff --git a/tests/features/chat/application/threads/goal-actions.test.ts b/tests/features/chat/application/threads/goal-actions.test.ts index d23203be..45834e52 100644 --- a/tests/features/chat/application/threads/goal-actions.test.ts +++ b/tests/features/chat/application/threads/goal-actions.test.ts @@ -1,10 +1,10 @@ import { describe, expect, it, vi } from "vitest"; import type { ThreadGoal } from "../../../../../src/domain/threads/goal"; +import { createLocalIdSource } from "../../../../../src/features/chat/application/local-id-source"; import { createChatStateStore } from "../../../../../src/features/chat/application/state/store"; import { createGoalActions } from "../../../../../src/features/chat/application/threads/goal-actions"; import type { ThreadGoalTransport } from "../../../../../src/features/chat/application/threads/goal-transport"; -import { createLocalIdSource } from "../../../../../src/shared/id/local-id"; import { deferred } from "../../../../support/async"; import { chatStateFixture, chatStateWith } from "../../support/state"; diff --git a/tests/features/chat/host/reconnect-action.test.ts b/tests/features/chat/host/reconnect-action.test.ts index 0187c3af..79826d9b 100644 --- a/tests/features/chat/host/reconnect-action.test.ts +++ b/tests/features/chat/host/reconnect-action.test.ts @@ -1,10 +1,10 @@ import { describe, expect, it, vi } from "vitest"; import { ConnectionManager } from "../../../../src/app-server/connection/connection-manager"; +import { ConnectionWorkTracker } from "../../../../src/features/chat/application/connection/connection-work"; import { createChatStateStore } from "../../../../src/features/chat/application/state/store"; import { createReconnectAction } from "../../../../src/features/chat/host/bundles/reconnect-bundle"; import { createChatViewDeferredTasks } from "../../../../src/features/chat/host/session/deferred-work"; -import { ConnectionWorkTracker } from "../../../../src/shared/lifecycle/connection-work"; describe("createReconnectAction", () => { it("wires host connection lifecycle cleanup into panel reconnect", async () => { diff --git a/tests/features/chat/host/session-graph.test.ts b/tests/features/chat/host/session-graph.test.ts index 8c770360..937624dc 100644 --- a/tests/features/chat/host/session-graph.test.ts +++ b/tests/features/chat/host/session-graph.test.ts @@ -5,6 +5,7 @@ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { StaleAppServerSharedQueryContextError } from "../../../../src/app-server/query/shared-queries"; import type { ModelMetadata } from "../../../../src/domain/catalog/metadata"; import type { Thread } from "../../../../src/domain/threads/model"; +import { ConnectionWorkTracker } from "../../../../src/features/chat/application/connection/connection-work"; import { type ChatStateStore, createChatStateStore } from "../../../../src/features/chat/application/state/store"; import { HistoryController } from "../../../../src/features/chat/application/threads/history-controller"; import { ChatResumeWorkTracker } from "../../../../src/features/chat/application/threads/resume-work"; @@ -15,7 +16,6 @@ import { ChatComposerController } from "../../../../src/features/chat/panel/comp import { createChatMessageScrollController } from "../../../../src/features/chat/panel/message-stream-scroll-controller"; import { MessageStreamPresenter } from "../../../../src/features/chat/panel/surface/message-stream-presenter"; import { type CodexPanelSettings, DEFAULT_SETTINGS } from "../../../../src/settings/model"; -import { ConnectionWorkTracker } from "../../../../src/shared/lifecycle/connection-work"; import { installObsidianDomShims } from "../../../support/dom"; import { chatPanelSettingsAccess } from "../support/settings"; diff --git a/tests/features/chat/host/view-connection.test.ts b/tests/features/chat/host/view-connection.test.ts index a3fe9a03..b03470f0 100644 --- a/tests/features/chat/host/view-connection.test.ts +++ b/tests/features/chat/host/view-connection.test.ts @@ -4,6 +4,7 @@ import { afterEach, beforeAll, beforeEach, describe, expect, it, vi } from "vite import type { ServerNotification } from "../../../../src/app-server/connection/rpc-messages"; import { modelMetadataFromCatalogModels } from "../../../../src/app-server/protocol/catalog"; import type { ThreadRecord } from "../../../../src/app-server/protocol/thread"; +import type { ObservedResult } from "../../../../src/app-server/query/observed-result"; import type { ModelMetadata } from "../../../../src/domain/catalog/metadata"; import { emptyRuntimeConfigSnapshot } from "../../../../src/domain/runtime/config"; import { createServerDiagnostics } from "../../../../src/domain/server/diagnostics"; @@ -12,7 +13,6 @@ import type { Thread } from "../../../../src/domain/threads/model"; import type { CodexChatHost } from "../../../../src/features/chat/host/contracts"; import type { ThreadCatalogEvent } from "../../../../src/features/threads/catalog/thread-catalog"; import { type CodexPanelSettings, DEFAULT_SETTINGS } from "../../../../src/settings/model"; -import type { ObservedResult } from "../../../../src/shared/query/observed-result"; import { notices } from "../../../mocks/obsidian"; import { deferred, waitForAsyncWork } from "../../../support/async"; import { installObsidianDomShims } from "../../../support/dom"; diff --git a/tests/features/chat/panel/composer-controller.test.ts b/tests/features/chat/panel/composer-controller.test.ts index a1bc6c2f..bcb0a98b 100644 --- a/tests/features/chat/panel/composer-controller.test.ts +++ b/tests/features/chat/panel/composer-controller.test.ts @@ -14,7 +14,7 @@ import { createChatStateStore } from "../../../../src/features/chat/application/ import { ChatComposerController, type ChatComposerRenderActions } from "../../../../src/features/chat/panel/composer-controller"; import type { ChatPanelComposerReadModel } from "../../../../src/features/chat/panel/shell-read-model"; import { ComposerShell } from "../../../../src/features/chat/ui/composer"; -import { renderUiRoot, unmountUiRoot } from "../../../../src/shared/ui/ui-root.dom"; +import { renderUiRoot, unmountUiRoot } from "../../../../src/shared/dom/preact-root.dom"; import { installObsidianDomShims } from "../../../support/dom"; import { composerReadModelFromChatState } from "../support/shell-read-model"; diff --git a/tests/features/chat/panel/surface/message-stream-presenter.test.ts b/tests/features/chat/panel/surface/message-stream-presenter.test.ts index 23f773ba..ec524455 100644 --- a/tests/features/chat/panel/surface/message-stream-presenter.test.ts +++ b/tests/features/chat/panel/surface/message-stream-presenter.test.ts @@ -17,7 +17,7 @@ import { } from "../../../../../src/features/chat/panel/surface/message-stream-projection"; 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.dom"; +import { renderUiRoot, unmountUiRoot } from "../../../../../src/shared/dom/preact-root.dom"; import { notices } from "../../../../mocks/obsidian"; import { installObsidianDomShims } from "../../../../support/dom"; import { withChatStateMessageStreamItems } from "../../support/message-stream"; diff --git a/tests/features/chat/panel/surface/projections.test.ts b/tests/features/chat/panel/surface/projections.test.ts index 9ff5c559..f68dd0f0 100644 --- a/tests/features/chat/panel/surface/projections.test.ts +++ b/tests/features/chat/panel/surface/projections.test.ts @@ -15,7 +15,7 @@ import { chatPanelComposerProjection } from "../../../../../src/features/chat/pa import { ChatPanelGoal, type ChatPanelGoalSurface } from "../../../../../src/features/chat/panel/surface/goal-projection"; import { ChatPanelToolbar } from "../../../../../src/features/chat/panel/surface/toolbar-projection"; import type { ToolbarActions } from "../../../../../src/features/chat/ui/toolbar"; -import { renderUiRoot, unmountUiRoot } from "../../../../../src/shared/ui/ui-root.dom"; +import { renderUiRoot, unmountUiRoot } from "../../../../../src/shared/dom/preact-root.dom"; import { installObsidianDomShims } from "../../../../support/dom"; import { withChatStateMessageStreamItems } from "../../support/message-stream"; import { composerReadModelFromChatState } from "../../support/shell-read-model"; diff --git a/tests/features/chat/ui/composer.test.ts b/tests/features/chat/ui/composer.test.ts index 50cc395c..6053963e 100644 --- a/tests/features/chat/ui/composer.test.ts +++ b/tests/features/chat/ui/composer.test.ts @@ -5,7 +5,7 @@ import { describe, expect, it, vi } from "vitest"; import type { ComposerMetaViewModel } from "../../../../src/features/chat/ui/composer"; import { type ComposerCallbacks, ComposerShell, type ComposerSuggestion } from "../../../../src/features/chat/ui/composer"; import { scrollComposerSuggestionIntoView, syncComposerHeight } from "../../../../src/features/chat/ui/composer.dom"; -import { renderUiRoot } from "../../../../src/shared/ui/ui-root.dom"; +import { renderUiRoot } from "../../../../src/shared/dom/preact-root.dom"; import { waitForAsyncWork } from "../../../support/async"; import { changeInputValue, composerSuggestionScrollFixture, installObsidianDomShims } from "../../../support/dom"; diff --git a/tests/features/chat/ui/goal.test.tsx b/tests/features/chat/ui/goal.test.tsx index f28f2027..a28c3c18 100644 --- a/tests/features/chat/ui/goal.test.tsx +++ b/tests/features/chat/ui/goal.test.tsx @@ -3,11 +3,10 @@ import { useState } from "preact/hooks"; import { act } from "preact/test-utils"; import { describe, expect, it, vi } from "vitest"; - +import type { SendShortcut } from "../../../../src/domain/input/send-shortcut"; 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.dom"; +import { renderUiRoot } from "../../../../src/shared/dom/preact-root.dom"; import { installObsidianDomShims } from "../../../support/dom"; installObsidianDomShims(); diff --git a/tests/features/chat/ui/message-stream/blocks-and-messages.test.tsx b/tests/features/chat/ui/message-stream/blocks-and-messages.test.tsx index 4550668b..10106aad 100644 --- a/tests/features/chat/ui/message-stream/blocks-and-messages.test.tsx +++ b/tests/features/chat/ui/message-stream/blocks-and-messages.test.tsx @@ -193,7 +193,7 @@ describe("message stream rendering and message action menu", () => { auditFacts: [ { key: "status", value: "approved" }, { key: "action", value: "apply patch" }, - { key: "files", value: "src/ui/detail-view.ts\nsrc/ui/message-stream.ts" }, + { key: "files", value: "src/shared/ui/detail-view.ts\nsrc/shared/ui/message-stream.ts" }, ], }, }, @@ -209,7 +209,9 @@ describe("message stream rendering and message action menu", () => { expect(element.textContent).not.toContain("â–¶Review"); expect(element.querySelector(".codex-panel__meta-grid")?.textContent).toContain("statusapproved"); expect(element.querySelector(".codex-panel__meta-grid")?.textContent).toContain("actionapply patch"); - expect(element.querySelector(".codex-panel__meta-grid")?.textContent).toContain("filessrc/ui/detail-view.ts\nsrc/ui/message-stream.ts"); + expect(element.querySelector(".codex-panel__meta-grid")?.textContent).toContain( + "filessrc/shared/ui/detail-view.ts\nsrc/shared/ui/message-stream.ts", + ); expect(textContents(element, ".codex-panel__output-title")).toEqual([]); }); diff --git a/tests/features/chat/ui/message-stream/flow-scroll.test.ts b/tests/features/chat/ui/message-stream/flow-scroll.test.ts index 7cb95437..54071ee1 100644 --- a/tests/features/chat/ui/message-stream/flow-scroll.test.ts +++ b/tests/features/chat/ui/message-stream/flow-scroll.test.ts @@ -10,7 +10,7 @@ import { type MessageStreamScrollControllerBinding, type MessageStreamScrollPort, } from "../../../../../src/features/chat/ui/message-stream/flow-scroll.measure"; -import { renderUiRoot } from "../../../../../src/shared/ui/ui-root.dom"; +import { renderUiRoot } from "../../../../../src/shared/dom/preact-root.dom"; import { installObsidianDomShims } from "../../../../support/dom"; installObsidianDomShims(); diff --git a/tests/features/chat/ui/message-stream/test-helpers.tsx b/tests/features/chat/ui/message-stream/test-helpers.tsx index 2f0aa4bb..c400e088 100644 --- a/tests/features/chat/ui/message-stream/test-helpers.tsx +++ b/tests/features/chat/ui/message-stream/test-helpers.tsx @@ -19,7 +19,7 @@ import type { 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.dom"; +import { renderUiRoot, unmountUiRoot } from "../../../../../src/shared/dom/preact-root.dom"; export function messageStreamBlocks(context: TestMessageStreamContext): [MessageStreamViewBlock, ...MessageStreamViewBlock[]] { const normalized = normalizeMessageStreamContext(context); diff --git a/tests/features/chat/ui/toolbar.test.ts b/tests/features/chat/ui/toolbar.test.ts index 375fd352..6a923cb6 100644 --- a/tests/features/chat/ui/toolbar.test.ts +++ b/tests/features/chat/ui/toolbar.test.ts @@ -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.dom"; +import { renderUiRoot } from "../../../../src/shared/dom/preact-root.dom"; import { changeInputValue, installObsidianDomShims } from "../../../support/dom"; installObsidianDomShims(); diff --git a/tests/features/chat/ui/ui-root.test.tsx b/tests/features/chat/ui/ui-root.test.tsx index 6824dcd8..b4fdd9ae 100644 --- a/tests/features/chat/ui/ui-root.test.tsx +++ b/tests/features/chat/ui/ui-root.test.tsx @@ -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.dom"; +import { renderUiRoot, unmountUiRoot } from "../../../../src/shared/dom/preact-root.dom"; describe("Preact root adapter", () => { it("reuses roots that render no host children", () => { diff --git a/tests/features/threads-view/view.test.ts b/tests/features/threads-view/view.test.ts index f8dff7f9..2e7e9214 100644 --- a/tests/features/threads-view/view.test.ts +++ b/tests/features/threads-view/view.test.ts @@ -2,10 +2,10 @@ import { beforeEach, describe, expect, it, vi } from "vitest"; import type { TurnRecord } from "../../../src/app-server/protocol/turn"; +import type { ObservedResult } from "../../../src/app-server/query/observed-result"; import type * as ThreadTitleGeneratorModule from "../../../src/app-server/services/thread-title-generation"; import type { Thread } from "../../../src/domain/threads/model"; import { DEFAULT_SETTINGS } from "../../../src/settings/model"; -import type { ObservedResult } from "../../../src/shared/query/observed-result"; import { deferred, waitForAsyncWork } from "../../support/async"; import { changeInputValue, installObsidianDomShims } from "../../support/dom"; diff --git a/tests/shared/obsidian/archive-export-destination.test.ts b/tests/features/threads/obsidian/archive-export-destination.test.ts similarity index 95% rename from tests/shared/obsidian/archive-export-destination.test.ts rename to tests/features/threads/obsidian/archive-export-destination.test.ts index 65ca21e9..6e85ce66 100644 --- a/tests/shared/obsidian/archive-export-destination.test.ts +++ b/tests/features/threads/obsidian/archive-export-destination.test.ts @@ -1,7 +1,7 @@ import type { Vault } from "obsidian"; import { describe, expect, it, vi } from "vitest"; -import { createObsidianArchiveExportDestination } from "../../../src/shared/obsidian/archive-export-destination"; +import { createObsidianArchiveExportDestination } from "../../../../src/features/threads/obsidian/archive-export-destination.obsidian"; describe("createObsidianArchiveExportDestination", () => { it("uses normalized Vault paths for existence checks, folder creation, and file writes", async () => { diff --git a/tests/scripts/grit-policy.test.mjs b/tests/scripts/grit-policy.test.mjs index 53151997..bca138b0 100644 --- a/tests/scripts/grit-policy.test.mjs +++ b/tests/scripts/grit-policy.test.mjs @@ -79,7 +79,7 @@ export { local }; `.trimStart(), ); await writeFile( - path.join(cwd, "src/shared/iterator.ts"), + path.join(cwd, "src/domain/iterator.ts"), ` export function first(iterator: Iterator): T | undefined { return iterator.next().value; @@ -144,7 +144,7 @@ runner = new Runner(() => runner.stop()); const report = biomeLint( [ "reexports.ts", - "src/shared/iterator.ts", + "src/domain/iterator.ts", "src/features/chat/ui/form-state.tsx", "src/features/chat/ui/controlled-form-state.tsx", "src/plugin-runtime.ts", @@ -158,7 +158,7 @@ runner = new Runner(() => runner.stop()); { line: 1, column: 8, endLine: 1, endColumn: 33 }, { line: 2, column: 8, endLine: 2, endColumn: 26 }, ]); - expect(pluginMessages(report, "src/shared/iterator.ts")).toEqual([ + expect(pluginMessages(report, "src/domain/iterator.ts")).toEqual([ "Avoid reading iterator.next().value directly; use for...of or inspect the typed IteratorResult first.", ]); expect(pluginMessages(report, "src/features/chat/ui/form-state.tsx")).toEqual([ @@ -183,7 +183,7 @@ runner = new Runner(() => runner.stop()); "no-chat-signal-type-references.grit", "no-preact-signal-imports.grit", "no-state-module-side-effects.grit", - "no-ui-root-imports.grit", + "no-preact-root-imports.grit", ]); await writeFile( path.join(cwd, "src/features/chat/panel/shell-read-model.ts"), @@ -214,7 +214,7 @@ export const status = signal("idle"); `.trimStart(), ); await writeFile( - path.join(cwd, "src/shared/ui/components.tsx"), + path.join(cwd, "src/shared/obsidian/components.obsidian.tsx"), ` import { signal } from '@preact/signals'; @@ -331,7 +331,7 @@ export function render(container: HTMLElement): void { await writeFile( path.join(cwd, "src/features/chat/ui/dom-event-escape.tsx"), ` -import { listenDomEvent } from "../../../shared/ui/dom-events.dom"; +import { listenDomEvent } from "../../../shared/dom/events.dom"; export const listen = listenDomEvent; `.trimStart(), @@ -347,7 +347,7 @@ export function onAbort(signal: AbortSignal): void { await writeFile( path.join(cwd, "src/features/chat/ui/root-import.tsx"), ` -import { renderUiRoot } from '../../../shared/ui/ui-root.dom'; +import { renderUiRoot } from '../../../shared/dom/preact-root.dom'; export const render = renderUiRoot; `.trimStart(), @@ -355,7 +355,7 @@ export const render = renderUiRoot; await writeFile( path.join(cwd, "src/features/chat/panel/shell.dom.tsx"), ` -import { renderUiRoot } from "../../../shared/ui/ui-root.dom"; +import { renderUiRoot } from "../../../shared/dom/preact-root.dom"; import { createChatPanelShellReadModelBinding } from "./shell-read-model"; export const render = renderUiRoot; @@ -373,15 +373,15 @@ export type ComposerModel = ChatPanelComposerReadModel; await writeFile( path.join(cwd, "src/features/chat/ui/root-escapes.tsx"), ` -import type { RootRenderer } from "../../../shared/ui/ui-root.dom"; +import type { RootRenderer } from "../../../shared/dom/preact-root.dom"; export type { RootRenderer }; export async function loadRoot() { - return import("../../../shared/ui/ui-root.dom"); + return import("../../../shared/dom/preact-root.dom"); } -const root = await import("../../../shared/ui/ui-root.dom"); +const root = await import("../../../shared/dom/preact-root.dom"); export const loadedRoot = root; `.trimStart(), ); @@ -409,7 +409,7 @@ export function timestamp(): number { "src/features/chat/panel/shell-read-model.ts", "src/features/chat/panel/surface/signal-surface.tsx", "src/features/chat/presentation/signal-helper.ts", - "src/shared/ui/components.tsx", + "src/shared/obsidian/components.obsidian.tsx", "src/shared/ui/signal-escapes.tsx", "src/features/chat/ui/dom-bridge-escape.tsx", "src/features/chat/ui/dom-event-escape.tsx", @@ -430,7 +430,9 @@ export function timestamp(): number { expect([...pluginMessages(report, "src/features/chat/presentation/signal-helper.ts")].sort()).toEqual( [CHAT_SHELL_READ_MODEL_IMPORT_MESSAGE, CHAT_SIGNAL_TYPE_REFERENCE_MESSAGE, CHAT_SIGNAL_TYPE_REFERENCE_MESSAGE].sort(), ); - expect(pluginMessages(report, "src/shared/ui/components.tsx")).toEqual(["Do not import @preact/signals from this module."]); + expect(pluginMessages(report, "src/shared/obsidian/components.obsidian.tsx")).toEqual([ + "Do not import @preact/signals from this module.", + ]); expect(pluginMessages(report, "src/shared/ui/signal-escapes.tsx")).toEqual([ "Do not import @preact/signals from this module.", "Do not import @preact/signals from this module.", @@ -598,7 +600,7 @@ export const misplaced = true; `.trimStart(), ); await writeFile( - path.join(cwd, "src/shared/mixed-root.ts"), + path.join(cwd, "src/domain/mixed-root.ts"), ` export const misplaced = true; `.trimStart(), @@ -704,7 +706,7 @@ export const value = statusText; "src/workspace/chat-internal-escape.ts", "src/workspace/chat-host-allowed.ts", "src/domain/mixed-root.ts", - "src/shared/mixed-root.ts", + "src/domain/mixed-root.ts", "src/features/chat/mixed-root.ts", "src/features/threads/mixed-root.ts", "src/features/threads/list/rename-lifecycle.ts", @@ -738,7 +740,7 @@ export const value = statusText; expect(pluginMessages(report, "src/workspace/chat-internal-escape.ts")).toEqual([WORKSPACE_CHAT_INTERNAL_MESSAGE]); expect(pluginDiagnostics(report, "src/workspace/chat-host-allowed.ts")).toEqual([]); expect(pluginMessages(report, "src/domain/mixed-root.ts")).toEqual([RESPONSIBILITY_ROOT_MODULE_FILE_MESSAGE]); - expect(pluginMessages(report, "src/shared/mixed-root.ts")).toEqual([RESPONSIBILITY_ROOT_MODULE_FILE_MESSAGE]); + expect(pluginMessages(report, "src/domain/mixed-root.ts")).toEqual([RESPONSIBILITY_ROOT_MODULE_FILE_MESSAGE]); expect(pluginMessages(report, "src/features/chat/mixed-root.ts")).toEqual([RESPONSIBILITY_ROOT_MODULE_FILE_MESSAGE]); expect(pluginMessages(report, "src/features/threads/mixed-root.ts")).toEqual([RESPONSIBILITY_ROOT_MODULE_FILE_MESSAGE]); expect(pluginDiagnostics(report, "src/features/threads/list/rename-lifecycle.ts")).toEqual([]); @@ -795,7 +797,9 @@ export const value = statusText; expect(pluginDiagnostics(report, "src/features/threads-view/shell.dom.tsx")).toEqual([]); expect(pluginDiagnostics(report, "src/features/turn-diff/render.dom.tsx")).toEqual([]); expect(pluginDiagnostics(report, "src/settings/section.tsx")).toEqual([]); - expect(pluginDiagnostics(report, "src/shared/ui/diff.tsx")).toEqual([]); + expect(pluginDiagnostics(report, "src/shared/dom/preact-root.dom.tsx")).toEqual([]); + expect(pluginDiagnostics(report, "src/shared/obsidian/components.obsidian.tsx")).toEqual([]); + expect(pluginDiagnostics(report, "src/shared/ui/diff-view.tsx")).toEqual([]); }); it("keeps app-server protocol modules behind app-server and chat ingestion boundaries", async () => { @@ -850,12 +854,12 @@ export const value = statusText; const report = await appServerBoundaryPolicyReport(); expect(pluginMessages(report, "src/app-server/protocol/diagnostics.ts")).toEqual([ - "Do not import feature modules from this layer. Move shared behavior to shared, domain, or app-server adapters.", + "Do not import feature modules from this layer. Move reusable behavior to domain, shared DOM/Obsidian/runtime/UI, or app-server adapters.", ]); - expect(pluginMessages(report, "src/shared/thread-picker.ts")).toEqual([ - "Do not import feature modules from this layer. Move shared behavior to shared, domain, or app-server adapters.", + expect(pluginMessages(report, "src/shared/obsidian/thread-picker.ts")).toEqual([ + "Do not import feature modules from this layer. Move reusable behavior to domain, shared DOM/Obsidian/runtime/UI, or app-server adapters.", ]); - expect(pluginDiagnostics(report, "src/shared/date.ts")).toEqual([]); + expect(pluginDiagnostics(report, "src/domain/display/date.ts")).toEqual([]); }); it("keeps app-server connection internals behind app-server adapters", async () => { @@ -873,7 +877,7 @@ export const value = statusText; "Do not import app-server connection internals from this module. Keep connection usage at app-server adapters.", ]), ); - expect(pluginMessages(report, "src/shared/connection-client.ts")).toEqual([ + expect(pluginMessages(report, "src/shared/runtime/connection-client.ts")).toEqual([ "Do not import app-server connection internals from this module. Keep connection usage at app-server adapters.", ]); }); @@ -1040,9 +1044,21 @@ export const value =
; `.trimStart(), ); await writeFile( - path.join(cwd, "src/shared/ui/diff.tsx"), + path.join(cwd, "src/shared/ui/diff-view.tsx"), ` export const value =
;
+`.trimStart(),
+  );
+  await writeFile(
+    path.join(cwd, "src/shared/dom/preact-root.dom.tsx"),
+    `
+export const value = 
; +`.trimStart(), + ); + await writeFile( + path.join(cwd, "src/shared/obsidian/components.obsidian.tsx"), + ` +export const value =