onlyworlds_obsidian-plugin/Commands/CreateElementCommand.ts

72 lines
3.3 KiB
TypeScript
Raw Normal View History

2025-04-21 19:47:59 +00:00
import { App, normalizePath, Notice, TFile, TFolder } from 'obsidian';
2024-08-18 13:36:28 +00:00
import { WorldService } from 'Scripts/WorldService';
import { v7 as uuidv7 } from 'uuid';
Phase B: frontmatter flip (span -> YAML frontmatter) Wake the dormant vault/element-file.ts write path and make YAML frontmatter the note format for new writes, with read-tolerance for legacy span notes. Pure logic (Obsidian-free, unit-tested under `npm test`): - vault/element-transform.ts: frontmatter<->payload, span parse, span->frontmatter, link normalization (single string / multi list, never comma-joined), extension-key passthrough (atlas_/shadow_/x_), body<->description|story mapping, read-before-PATCH diff. - test/element-transform.test.ts + tsconfig.test.json: 16 node:test cases incl. a real span-format Character fixture. Wired to `npm test` (pretest compiles to test-dist/). Wiring: - element-file.ts: readElement preserves extension fields + maps body to story for Narrative (R3/R5); frontmatter-first with legacy span fallback; writeElement round-trips extension keys and respects count-suffixed folders. - DownloadWorldCommand + CreateElementCommand: emit frontmatter via writeElement (no more Handlebars element templates). - NoteLinker: reworked onto metadataCache + SDK FIELD_SCHEMA; writes link IDs into frontmatter (new FieldSelectionModal picks the field). NameChanger syncs frontmatter name. - SaveElementCommand: read-before-PATCH (GET, diff, PATCH only changed fields) (R7). - ExportWorldCommand + CopyWorldCommand: read frontmatter via readElement, span as fallback (mixed worlds upload/copy correctly). - MigrateWorldCommand: "Migrate world notes to frontmatter" — backup-first (abort on failure), idempotent, end report modal (converted/skipped/failed). Deferred (see docs report): PasteWorld still emits span; ValidateWorld is still a span linter (diagnostic only, no writes). docs/SMOKE-CHECKLIST.md is the manual release gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 11:43:56 +00:00
import { writeElement } from '../vault/element-file';
2024-08-18 13:36:28 +00:00
export class CreateElementCommand {
app: App;
manifest: any;
worldService: WorldService;
constructor(app: App, manifest: any, worldService: WorldService) {
this.app = app;
this.manifest = manifest;
this.worldService = worldService;
}
async execute(category: string, name: string, worldName?: string, openFile: boolean = true): Promise<void> {
2024-08-18 13:36:28 +00:00
const uuid = uuidv7();
Phase B: frontmatter flip (span -> YAML frontmatter) Wake the dormant vault/element-file.ts write path and make YAML frontmatter the note format for new writes, with read-tolerance for legacy span notes. Pure logic (Obsidian-free, unit-tested under `npm test`): - vault/element-transform.ts: frontmatter<->payload, span parse, span->frontmatter, link normalization (single string / multi list, never comma-joined), extension-key passthrough (atlas_/shadow_/x_), body<->description|story mapping, read-before-PATCH diff. - test/element-transform.test.ts + tsconfig.test.json: 16 node:test cases incl. a real span-format Character fixture. Wired to `npm test` (pretest compiles to test-dist/). Wiring: - element-file.ts: readElement preserves extension fields + maps body to story for Narrative (R3/R5); frontmatter-first with legacy span fallback; writeElement round-trips extension keys and respects count-suffixed folders. - DownloadWorldCommand + CreateElementCommand: emit frontmatter via writeElement (no more Handlebars element templates). - NoteLinker: reworked onto metadataCache + SDK FIELD_SCHEMA; writes link IDs into frontmatter (new FieldSelectionModal picks the field). NameChanger syncs frontmatter name. - SaveElementCommand: read-before-PATCH (GET, diff, PATCH only changed fields) (R7). - ExportWorldCommand + CopyWorldCommand: read frontmatter via readElement, span as fallback (mixed worlds upload/copy correctly). - MigrateWorldCommand: "Migrate world notes to frontmatter" — backup-first (abort on failure), idempotent, end report modal (converted/skipped/failed). Deferred (see docs report): PasteWorld still emits span; ValidateWorld is still a span linter (diagnostic only, no writes). docs/SMOKE-CHECKLIST.md is the manual release gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 11:43:56 +00:00
// Phase B: new elements are created directly as frontmatter notes via
// writeElement — no Handlebars template fetch. A fresh element carries
// only id + name; the user fills in fields (as Properties) and body.
2025-07-08 18:13:22 +00:00
const topWorld = worldName || await this.worldService.getWorldName();
2024-08-18 13:36:28 +00:00
try {
Phase B: frontmatter flip (span -> YAML frontmatter) Wake the dormant vault/element-file.ts write path and make YAML frontmatter the note format for new writes, with read-tolerance for legacy span notes. Pure logic (Obsidian-free, unit-tested under `npm test`): - vault/element-transform.ts: frontmatter<->payload, span parse, span->frontmatter, link normalization (single string / multi list, never comma-joined), extension-key passthrough (atlas_/shadow_/x_), body<->description|story mapping, read-before-PATCH diff. - test/element-transform.test.ts + tsconfig.test.json: 16 node:test cases incl. a real span-format Character fixture. Wired to `npm test` (pretest compiles to test-dist/). Wiring: - element-file.ts: readElement preserves extension fields + maps body to story for Narrative (R3/R5); frontmatter-first with legacy span fallback; writeElement round-trips extension keys and respects count-suffixed folders. - DownloadWorldCommand + CreateElementCommand: emit frontmatter via writeElement (no more Handlebars element templates). - NoteLinker: reworked onto metadataCache + SDK FIELD_SCHEMA; writes link IDs into frontmatter (new FieldSelectionModal picks the field). NameChanger syncs frontmatter name. - SaveElementCommand: read-before-PATCH (GET, diff, PATCH only changed fields) (R7). - ExportWorldCommand + CopyWorldCommand: read frontmatter via readElement, span as fallback (mixed worlds upload/copy correctly). - MigrateWorldCommand: "Migrate world notes to frontmatter" — backup-first (abort on failure), idempotent, end report modal (converted/skipped/failed). Deferred (see docs report): PasteWorld still emits span; ValidateWorld is still a span linter (diagnostic only, no writes). docs/SMOKE-CHECKLIST.md is the manual release gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 11:43:56 +00:00
// Resolve the (possibly count-suffixed) category folder + a collision-free
// filename, so we write into the same folder the vault already uses.
const existingFolder = await this.worldService.findCategoryFolderByBaseName(topWorld, category);
const folderPath = existingFolder
? existingFolder.path
: normalizePath(`OnlyWorlds/Worlds/${topWorld}/Elements/${category}`);
const fileName = await this.worldService.generateUniqueFileName(folderPath, name, uuid);
const file = await writeElement(this.app, topWorld, category, uuid, { name }, { folderPath, fileName });
2024-10-08 18:45:57 +00:00
new Notice(`New ${category.toLowerCase()} created: ${name}`);
Phase B: frontmatter flip (span -> YAML frontmatter) Wake the dormant vault/element-file.ts write path and make YAML frontmatter the note format for new writes, with read-tolerance for legacy span notes. Pure logic (Obsidian-free, unit-tested under `npm test`): - vault/element-transform.ts: frontmatter<->payload, span parse, span->frontmatter, link normalization (single string / multi list, never comma-joined), extension-key passthrough (atlas_/shadow_/x_), body<->description|story mapping, read-before-PATCH diff. - test/element-transform.test.ts + tsconfig.test.json: 16 node:test cases incl. a real span-format Character fixture. Wired to `npm test` (pretest compiles to test-dist/). Wiring: - element-file.ts: readElement preserves extension fields + maps body to story for Narrative (R3/R5); frontmatter-first with legacy span fallback; writeElement round-trips extension keys and respects count-suffixed folders. - DownloadWorldCommand + CreateElementCommand: emit frontmatter via writeElement (no more Handlebars element templates). - NoteLinker: reworked onto metadataCache + SDK FIELD_SCHEMA; writes link IDs into frontmatter (new FieldSelectionModal picks the field). NameChanger syncs frontmatter name. - SaveElementCommand: read-before-PATCH (GET, diff, PATCH only changed fields) (R7). - ExportWorldCommand + CopyWorldCommand: read frontmatter via readElement, span as fallback (mixed worlds upload/copy correctly). - MigrateWorldCommand: "Migrate world notes to frontmatter" — backup-first (abort on failure), idempotent, end report modal (converted/skipped/failed). Deferred (see docs report): PasteWorld still emits span; ValidateWorld is still a span linter (diagnostic only, no writes). docs/SMOKE-CHECKLIST.md is the manual release gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 11:43:56 +00:00
await this.worldService.updateCategoryFolderName(topWorld, category);
if (openFile) {
Phase B: frontmatter flip (span -> YAML frontmatter) Wake the dormant vault/element-file.ts write path and make YAML frontmatter the note format for new writes, with read-tolerance for legacy span notes. Pure logic (Obsidian-free, unit-tested under `npm test`): - vault/element-transform.ts: frontmatter<->payload, span parse, span->frontmatter, link normalization (single string / multi list, never comma-joined), extension-key passthrough (atlas_/shadow_/x_), body<->description|story mapping, read-before-PATCH diff. - test/element-transform.test.ts + tsconfig.test.json: 16 node:test cases incl. a real span-format Character fixture. Wired to `npm test` (pretest compiles to test-dist/). Wiring: - element-file.ts: readElement preserves extension fields + maps body to story for Narrative (R3/R5); frontmatter-first with legacy span fallback; writeElement round-trips extension keys and respects count-suffixed folders. - DownloadWorldCommand + CreateElementCommand: emit frontmatter via writeElement (no more Handlebars element templates). - NoteLinker: reworked onto metadataCache + SDK FIELD_SCHEMA; writes link IDs into frontmatter (new FieldSelectionModal picks the field). NameChanger syncs frontmatter name. - SaveElementCommand: read-before-PATCH (GET, diff, PATCH only changed fields) (R7). - ExportWorldCommand + CopyWorldCommand: read frontmatter via readElement, span as fallback (mixed worlds upload/copy correctly). - MigrateWorldCommand: "Migrate world notes to frontmatter" — backup-first (abort on failure), idempotent, end report modal (converted/skipped/failed). Deferred (see docs report): PasteWorld still emits span; ValidateWorld is still a span linter (diagnostic only, no writes). docs/SMOKE-CHECKLIST.md is the manual release gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 11:43:56 +00:00
await this.openNoteInNewPane(file);
}
2024-08-18 13:36:28 +00:00
} catch (error) {
Phase B: frontmatter flip (span -> YAML frontmatter) Wake the dormant vault/element-file.ts write path and make YAML frontmatter the note format for new writes, with read-tolerance for legacy span notes. Pure logic (Obsidian-free, unit-tested under `npm test`): - vault/element-transform.ts: frontmatter<->payload, span parse, span->frontmatter, link normalization (single string / multi list, never comma-joined), extension-key passthrough (atlas_/shadow_/x_), body<->description|story mapping, read-before-PATCH diff. - test/element-transform.test.ts + tsconfig.test.json: 16 node:test cases incl. a real span-format Character fixture. Wired to `npm test` (pretest compiles to test-dist/). Wiring: - element-file.ts: readElement preserves extension fields + maps body to story for Narrative (R3/R5); frontmatter-first with legacy span fallback; writeElement round-trips extension keys and respects count-suffixed folders. - DownloadWorldCommand + CreateElementCommand: emit frontmatter via writeElement (no more Handlebars element templates). - NoteLinker: reworked onto metadataCache + SDK FIELD_SCHEMA; writes link IDs into frontmatter (new FieldSelectionModal picks the field). NameChanger syncs frontmatter name. - SaveElementCommand: read-before-PATCH (GET, diff, PATCH only changed fields) (R7). - ExportWorldCommand + CopyWorldCommand: read frontmatter via readElement, span as fallback (mixed worlds upload/copy correctly). - MigrateWorldCommand: "Migrate world notes to frontmatter" — backup-first (abort on failure), idempotent, end report modal (converted/skipped/failed). Deferred (see docs report): PasteWorld still emits span; ValidateWorld is still a span linter (diagnostic only, no writes). docs/SMOKE-CHECKLIST.md is the manual release gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 11:43:56 +00:00
console.error(`Failed to create ${category} note`, error);
new Notice(`Failed to create ${category}: ${error instanceof Error ? error.message : 'unknown error'}`);
2024-08-18 13:36:28 +00:00
}
}
Phase B: frontmatter flip (span -> YAML frontmatter) Wake the dormant vault/element-file.ts write path and make YAML frontmatter the note format for new writes, with read-tolerance for legacy span notes. Pure logic (Obsidian-free, unit-tested under `npm test`): - vault/element-transform.ts: frontmatter<->payload, span parse, span->frontmatter, link normalization (single string / multi list, never comma-joined), extension-key passthrough (atlas_/shadow_/x_), body<->description|story mapping, read-before-PATCH diff. - test/element-transform.test.ts + tsconfig.test.json: 16 node:test cases incl. a real span-format Character fixture. Wired to `npm test` (pretest compiles to test-dist/). Wiring: - element-file.ts: readElement preserves extension fields + maps body to story for Narrative (R3/R5); frontmatter-first with legacy span fallback; writeElement round-trips extension keys and respects count-suffixed folders. - DownloadWorldCommand + CreateElementCommand: emit frontmatter via writeElement (no more Handlebars element templates). - NoteLinker: reworked onto metadataCache + SDK FIELD_SCHEMA; writes link IDs into frontmatter (new FieldSelectionModal picks the field). NameChanger syncs frontmatter name. - SaveElementCommand: read-before-PATCH (GET, diff, PATCH only changed fields) (R7). - ExportWorldCommand + CopyWorldCommand: read frontmatter via readElement, span as fallback (mixed worlds upload/copy correctly). - MigrateWorldCommand: "Migrate world notes to frontmatter" — backup-first (abort on failure), idempotent, end report modal (converted/skipped/failed). Deferred (see docs report): PasteWorld still emits span; ValidateWorld is still a span linter (diagnostic only, no writes). docs/SMOKE-CHECKLIST.md is the manual release gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 11:43:56 +00:00
2024-08-18 13:36:28 +00:00
async openNoteInNewPane(file: TFile) {
const leaf = this.app.workspace.getLeaf(true);
await leaf.openFile(file);
}
async determineTopWorldFolder(): Promise<string> {
const worldsPath = normalizePath('OnlyWorlds/Worlds');
const worldsFolder = this.app.vault.getAbstractFileByPath(worldsPath);
if (worldsFolder instanceof TFolder) {
const subFolders = worldsFolder.children.filter(child => child instanceof TFolder);
return subFolders.length > 0 ? subFolders[0].name : 'DefaultWorld';
}
return 'DefaultWorld';
}
async createFolderIfNeeded(folderPath: string): Promise<void> {
const normalizedPath = normalizePath(folderPath);
let existingFolder = this.app.vault.getAbstractFileByPath(normalizedPath);
if (!existingFolder) {
try {
await this.app.vault.createFolder(normalizedPath);
// new Notice(`Created folder: ${normalizedPath}`);
} catch (error) {
console.error(`Error creating folder: ${normalizedPath}`, error);
}
}
}
}