refactor(archive): consolidate export ownership

This commit is contained in:
murashit 2026-07-21 20:47:56 +09:00
parent 5b6fce1a4d
commit 6bdd9e5972
10 changed files with 28 additions and 43 deletions

View file

@ -1,7 +0,0 @@
export function yamlFrontmatterString(value: string): string {
return JSON.stringify(value);
}
export function yamlFrontmatterInlineList(values: readonly string[]): string {
return `[${values.map(yamlFrontmatterString).join(", ")}]`;
}

View file

@ -3,7 +3,6 @@ import { fromMarkdown } from "mdast-util-from-markdown";
import { toMarkdown } from "mdast-util-to-markdown";
import { visit } from "unist-util-visit";
import { yamlFrontmatterInlineList, yamlFrontmatterString } from "../markdown/frontmatter";
import { parseFileHref } from "../vault/file-hrefs";
import { isFilesystemAbsolutePath, isVaultConfigPath, normalizeFilePath, vaultRelativePath } from "../vault/paths";
import type { Thread } from "./model";
@ -16,9 +15,7 @@ interface MarkdownSourceReplacement {
value: string;
}
export interface ArchiveExportSettings {
archiveExportFolderTemplate: string;
archiveExportFilenameTemplate: string;
export interface ArchiveMarkdownOptions {
archiveExportTags?: string;
vaultPath?: string;
vaultConfigDir?: string;
@ -28,13 +25,9 @@ export interface ArchiveThreadInput extends Thread {
transcriptEntries: readonly ThreadTranscriptEntry[];
}
export function archivedThreadMarkdown(
thread: ArchiveThreadInput,
exportedAt = new Date(),
settings?: Partial<ArchiveExportSettings>,
): string {
export function archivedThreadMarkdown(thread: ArchiveThreadInput, exportedAt = new Date(), settings: ArchiveMarkdownOptions = {}): string {
const title = threadArchiveTitle(thread);
const tags = normalizedArchiveTags(settings?.archiveExportTags ?? "");
const tags = normalizedArchiveTags(settings.archiveExportTags ?? "");
const frontmatter = [
"---",
`title: ${yamlFrontmatterString(title)}`,
@ -44,7 +37,7 @@ export function archivedThreadMarkdown(
"---",
].join("\n");
const body = `${trimTrailingBlankLines([`# ${title}`, "", ...transcriptMarkdownLines(thread.transcriptEntries)]).join("\n")}\n`;
const normalizedBody = settings?.vaultPath ? normalizeExportedMarkdownLinks(body, settings.vaultPath, settings.vaultConfigDir) : body;
const normalizedBody = settings.vaultPath ? normalizeExportedMarkdownLinks(body, settings.vaultPath, settings.vaultConfigDir) : body;
return `${frontmatter}\n\n${normalizedBody}`;
}
@ -198,3 +191,11 @@ function trimTrailingBlankLines(lines: string[]): string[] {
while (result[result.length - 1] === "") result.pop();
return result;
}
function yamlFrontmatterString(value: string): string {
return JSON.stringify(value);
}
function yamlFrontmatterInlineList(values: readonly string[]): string {
return `[${values.map(yamlFrontmatterString).join(", ")}]`;
}

View file

@ -6,10 +6,9 @@ import type { ObservedResultListener } from "../../../app-server/query/observed-
import type { ModelMetadata } from "../../../domain/catalog/metadata";
import type { SendShortcut } from "../../../domain/input/send-shortcut";
import type { SharedServerMetadata, SharedServerMetadataResource } from "../../../domain/server/metadata";
import type { ArchiveExportSettings } from "../../../domain/threads/archive-markdown";
import type { KeyedOperationQueue } from "../../../shared/runtime/keyed-operation-queue";
import type { ThreadCatalogEventSink, ThreadCatalogPaginatedActiveReader } from "../../threads/catalog/thread-catalog";
import type { ArchiveExportDestination } from "../../threads/workflows/archive-export";
import type { ArchiveExportDestination, ArchiveExportSettings } from "../../threads/workflows/archive-export";
import type { ThreadTitleTransport } from "../../threads/workflows/ports";
import type { ThreadNameMutationCoordinator } from "../../threads/workflows/thread-name-mutation-coordinator";
import type { TurnDiffViewState } from "../../turn-diff/model";

View file

@ -1,7 +1,7 @@
import { Component, ItemView, type ViewStateResult, type WorkspaceLeaf } from "obsidian";
import { VIEW_TYPE_CODEX_PANEL } from "../../../constants";
import { createObsidianArchiveExportDestination } from "../../threads/obsidian/archive-export-destination.obsidian";
import { createObsidianVaultMarkdownDestination } from "../../../shared/obsidian/vault-write-destination.obsidian";
import { createLocalIdSource } from "../application/local-id-source";
import type { ChatPanelHandle, ChatPanelRuntimeSnapshot, ChatViewRuntimeOwner, CodexChatHost } from "./contracts";
import { ChatPanelSession } from "./session";
@ -46,7 +46,7 @@ export class CodexChatView extends ItemView {
registerPointerDown: (handler) => {
owner.registerDomEvent(this.containerEl.doc, "pointerdown", handler);
},
archiveDestination: () => createObsidianArchiveExportDestination(this.app.vault),
archiveDestination: () => createObsidianVaultMarkdownDestination(this.app.vault),
requestWorkspaceLayoutSave: () => {
void this.app.workspace.requestSaveLayout();
},

View file

@ -2,14 +2,13 @@ import { Notice } from "obsidian";
import type { ObservedPaginatedResult } from "../../app-server/query/observed-result";
import { observedInitialError, observedInitialLoading } from "../../app-server/query/observed-result";
import type { ArchiveExportSettings } from "../../domain/threads/archive-markdown";
import type { Thread } from "../../domain/threads/model";
import type { ThreadRenameLifecycleEvent } from "../../domain/threads/rename-lifecycle";
import { DeferredTask } from "../../shared/runtime/deferred-task";
import { isStaleExecutionRuntimeError } from "../../shared/runtime/execution-runtime-lifetime";
import { OwnerLifetime } from "../../shared/runtime/owner-lifetime";
import type { ThreadCatalogEventSink, ThreadCatalogPaginatedActiveReader } from "../threads/catalog/thread-catalog";
import type { ArchiveExportDestination } from "../threads/workflows/archive-export";
import type { ArchiveExportDestination, ArchiveExportSettings } from "../threads/workflows/archive-export";
import type { ThreadOperationsTransport, ThreadTitleTransport } from "../threads/workflows/ports";
import type { ThreadNameMutationCoordinator } from "../threads/workflows/thread-name-mutation-coordinator";
import { createThreadOperations, type ThreadOperations } from "../threads/workflows/thread-operations";

View file

@ -1,7 +1,7 @@
import { Component, ItemView, type WorkspaceLeaf } from "obsidian";
import { VIEW_TYPE_CODEX_THREADS } from "../../constants";
import { createObsidianArchiveExportDestination } from "../threads/obsidian/archive-export-destination.obsidian";
import { createObsidianVaultMarkdownDestination } from "../../shared/obsidian/vault-write-destination.obsidian";
import { type ThreadsViewHost, ThreadsViewSession } from "./session";
export interface ThreadsRuntimeView {
@ -38,7 +38,7 @@ export class CodexThreadsView extends ItemView {
registerPointerDown: (handler) => {
owner.registerDomEvent(this.containerEl.doc, "pointerdown", handler);
},
archiveDestination: () => createObsidianArchiveExportDestination(this.app.vault),
archiveDestination: () => createObsidianVaultMarkdownDestination(this.app.vault),
vaultConfigDir: () => this.app.vault.configDir,
viewWindow: () => this.containerEl.doc.defaultView,
});

View file

@ -1,7 +0,0 @@
import type { Vault } from "obsidian";
import { createObsidianVaultMarkdownDestination } from "../../../shared/obsidian/vault-write-destination.obsidian";
import type { ArchiveExportDestination } from "../workflows/archive-export";
export function createObsidianArchiveExportDestination(vault: Vault): ArchiveExportDestination {
return createObsidianVaultMarkdownDestination(vault);
}

View file

@ -1,4 +1,4 @@
import { type ArchiveExportSettings, type ArchiveThreadInput, archivedThreadMarkdown } from "../../../domain/threads/archive-markdown";
import { type ArchiveMarkdownOptions, type ArchiveThreadInput, archivedThreadMarkdown } from "../../../domain/threads/archive-markdown";
import { shortThreadId } from "../../../domain/threads/id";
import { threadArchiveTitle } from "../../../domain/threads/title";
import {
@ -19,6 +19,11 @@ export interface ArchiveExportResult {
path: string;
}
export interface ArchiveExportSettings extends ArchiveMarkdownOptions {
archiveExportFolderTemplate: string;
archiveExportFilenameTemplate: string;
}
export type ArchiveExportDestination = VaultMarkdownDestination;
interface TemplateContext {

View file

@ -1,8 +1,7 @@
import type { ArchiveExportSettings } from "../../../domain/threads/archive-markdown";
import { normalizeExplicitThreadName, type Thread } from "../../../domain/threads/model";
import { threadDisplayTitle } from "../../../domain/threads/title";
import type { ThreadCatalogEventSink } from "../catalog/thread-catalog";
import { type ArchiveExportDestination, exportArchivedThreadMarkdown } from "./archive-export";
import { type ArchiveExportDestination, type ArchiveExportSettings, exportArchivedThreadMarkdown } from "./archive-export";
import type { ThreadOperationsTransport } from "./ports";
import type { ThreadNameMutationCoordinator } from "./thread-name-mutation-coordinator";

View file

@ -1,6 +1,6 @@
import { describe, expect, it } from "vitest";
import { type ArchiveExportSettings, archivedThreadMarkdown } from "../../../src/domain/threads/archive-markdown";
import { type ArchiveMarkdownOptions, archivedThreadMarkdown } from "../../../src/domain/threads/archive-markdown";
import type { Thread } from "../../../src/domain/threads/model";
import type { ThreadTranscriptEntry } from "../../../src/domain/threads/transcript";
@ -300,13 +300,9 @@ describe("thread archive export", () => {
function exportedMarkdown(
source: Thread & { transcriptEntries: ThreadTranscriptEntry[] },
now: Date,
settings: Partial<ArchiveExportSettings> = {},
settings: ArchiveMarkdownOptions = {},
): string {
return archivedThreadMarkdown(source, now, {
archiveExportFolderTemplate: "Exports",
archiveExportFilenameTemplate: "{{title}}",
...settings,
});
return archivedThreadMarkdown(source, now, settings);
}
function thread(