diff --git a/src/model/draft-utils.ts b/src/model/draft-utils.ts index bcbbfe6..ab34d14 100644 --- a/src/model/draft-utils.ts +++ b/src/model/draft-utils.ts @@ -18,11 +18,20 @@ type SceneInsertionLocation = { export async function createScene( app: App, path: string, + index: number, draft: MultipleSceneDraft, open: boolean ): Promise { const template = draft.sceneTemplate ?? get(pluginSettings).sceneTemplate; - createNoteWithPotentialTemplate(app, path, template); + const note = await createNoteWithPotentialTemplate(app, path, template); + if (note === null) return; + + if (get(pluginSettings).writeProperty) { + await app.fileManager.processFrontMatter(note, (fm) => { + fm["longform-order"] = index; + }); + } + if (open) { app.workspace.openLinkText(path, "/", false); } @@ -63,7 +72,14 @@ export async function insertScene( return d; }); }); - await createScene(app, newScenePath, draft, open); + + await createScene( + app, + newScenePath, + draft.scenes.findIndex((s) => s.title === sceneName), + draft, + open + ); } export function setDraftOnFrontmatterObject( diff --git a/src/model/note-utils.ts b/src/model/note-utils.ts index f844f21..c1205e9 100644 --- a/src/model/note-utils.ts +++ b/src/model/note-utils.ts @@ -12,14 +12,17 @@ export function fileNameFromPath(path: string): string { * Prefers Templater, then the core Templates plugin, then a plain note without using the template. * @param path Path to note to create. * @param template Path to template to use. + * + * @returns `null` if it fails to create the note. `TFile` for the new note, if successful. */ export async function createNoteWithPotentialTemplate( app: App, path: string, template: string | null -): Promise { +): Promise { const file = await createNote(app, path); - if (template && file) { + if (!file) return null; + if (template) { let contents = ""; let pluginUsed = ""; try { @@ -37,6 +40,7 @@ export async function createNoteWithPotentialTemplate( await app.vault.adapter.write(path, contents); } } + return file; } /** diff --git a/src/model/store-vault-sync.ts b/src/model/store-vault-sync.ts index 7fdd037..c37bf34 100644 --- a/src/model/store-vault-sync.ts +++ b/src/model/store-vault-sync.ts @@ -10,15 +10,20 @@ import { cloneDeep, isEqual } from "lodash"; import { get, type Unsubscriber } from "svelte/store"; import type { Draft } from "./types"; -import { drafts as draftsStore, selectedDraftVaultPath } from "./stores"; +import { + drafts as draftsStore, + pluginSettings, + waitingForSync, + selectedDraftVaultPath, +} from "./stores"; import { arraysToIndentedScenes, + formatSceneNumber, + numberScenes, setDraftOnFrontmatterObject, } from "src/model/draft-utils"; import { fileNameFromPath } from "./note-utils"; -import { findScene, sceneFolderPath } from "./scene-navigation"; -import { pluginSettings } from "./stores"; -import { waitingForSync } from "./stores"; +import { findScene, sceneFolderPath, scenePath } from "./scene-navigation"; type FileWithMetadata = { file: TFile; @@ -568,9 +573,71 @@ export class StoreVaultSync { await this.app.fileManager.processFrontMatter(file, (fm) => { setDraftOnFrontmatterObject(fm, draft); }); + + // for multi-scene projects, optionally set a property on each scene that holds its order within the project + if (get(pluginSettings).writeProperty) { + if (draft.format === "scenes") { + const writes: Promise[] = []; + const sceneNumbers = numberScenes(draft.scenes); + sceneNumbers.forEach((numberedScene, index) => { + const sceneFilePath = scenePath( + numberedScene.title, + draft, + this.app.vault + ); + + const sceneFile = this.app.vault.getAbstractFileByPath(sceneFilePath); + // false if a folder, or not found + if (!(sceneFile instanceof TFile)) { + return; + } + writes.push( + writeSceneNumbers( + this.app, + sceneFile, + index, + numberedScene.numbering + ) + ); + }); + + await Promise.all(writes); + } + } } } +export function syncSceneIndices(app: App): void | Promise { + const writes: Promise[] = []; + get(draftsStore).forEach((draft) => { + if (draft.format !== "scenes") return; + numberScenes(draft.scenes).map((numberedScene, index) => { + const sceneFilePath = scenePath(numberedScene.title, draft, app.vault); + + const sceneFile = app.vault.getAbstractFileByPath(sceneFilePath); + // false if a folder, or not found + if (!(sceneFile instanceof TFile)) { + return; + } + return writeSceneNumbers(app, sceneFile, index, numberedScene.numbering); + }); + }); + if (writes.length === 0) return; + return Promise.all(writes); +} + +function writeSceneNumbers( + app: App, + file: TFile, + index: number, + numbering: number[] +) { + return app.fileManager.processFrontMatter(file, (fm) => { + fm["longform-order"] = index; + fm["longform-number"] = formatSceneNumber(numbering); + }); +} + const ESCAPED_CHARACTERS = new Set("/&$^+.()=!|[]{},".split("")); function ignoredPatternToRegex(pattern: string): RegExp { let regex = ""; diff --git a/src/model/types.ts b/src/model/types.ts index 74a2d07..4cd202e 100644 --- a/src/model/types.ts +++ b/src/model/types.ts @@ -98,6 +98,7 @@ export interface LongformPluginSettings { waitForSync: boolean; fallbackWaitEnabled: boolean; fallbackWaitTime: number; + writeProperty: boolean; // DEPRECATED. To be removed in future, needed now for migrations. projects: { [path: string]: { @@ -126,13 +127,14 @@ export const DEFAULT_SETTINGS: LongformPluginSettings = { sessionFile: DEFAULT_SESSION_FILE, numberScenes: false, sceneTemplate: null, + writeProperty: false, projects: {}, waitForSync: false, fallbackWaitEnabled: true, fallbackWaitTime: 5, }; -export const TRACKED_SETTINGS_PATHS = [ +export const TRACKED_SETTINGS_PATHS: (keyof LongformPluginSettings)[] = [ "version", "projects", "selectedDraftVaultPath", @@ -152,22 +154,25 @@ export const TRACKED_SETTINGS_PATHS = [ "waitForSync", "fallbackWaitEnabled", "fallbackWaitTime", + "writeProperty", ]; -export const PASSTHROUGH_SAVE_SETTINGS_PATHS = [ - "sessionStorage", - "userScriptFolder", - "showWordCountInStatusBar", - "startNewSessionEachDay", - "sessionGoal", - "applyGoalTo", - "notifyOnGoal", - "countDeletionsForGoal", - "keepSessionCount", - "sessionFile", - "numberScenes", - "sceneTemplate", - "waitForSync", - "fallbackWaitEnabled", - "fallbackWaitTime", -]; +export const PASSTHROUGH_SAVE_SETTINGS_PATHS: (keyof LongformPluginSettings)[] = + [ + "sessionStorage", + "userScriptFolder", + "showWordCountInStatusBar", + "startNewSessionEachDay", + "sessionGoal", + "applyGoalTo", + "notifyOnGoal", + "countDeletionsForGoal", + "keepSessionCount", + "sessionFile", + "numberScenes", + "sceneTemplate", + "waitForSync", + "fallbackWaitEnabled", + "fallbackWaitTime", + "writeProperty", + ]; diff --git a/src/view/settings/LongformSettings.ts b/src/view/settings/LongformSettings.ts index aa5b12e..a1716bc 100644 --- a/src/view/settings/LongformSettings.ts +++ b/src/view/settings/LongformSettings.ts @@ -13,6 +13,7 @@ import { pluginSettings, userScriptSteps } from "src/model/stores"; import { FolderSuggest } from "./folder-suggest"; import { DEFAULT_SESSION_FILE } from "src/model/types"; import { FileSuggest } from "./file-suggest"; +import { syncSceneIndices } from "src/model/store-vault-sync"; export class LongformSettingsTab extends PluginSettingTab { plugin: LongformPlugin; @@ -65,6 +66,24 @@ export class LongformSettingsTab extends PluginSettingTab { }); }); + new Setting(containerEl) + .setName("Write scene index to frontmatter") + .setDesc( + "If enabled, will add a scene index, and scene number, to the frontmatter of scene files." + ) + .addToggle((toggle) => { + toggle.setValue(settings.writeProperty); + toggle.onChange((value) => { + pluginSettings.update((settings) => ({ + ...settings, + writeProperty: value, + })); + if (value) { + syncSceneIndices(this.app); + } + }); + }); + new Setting(containerEl).setName("Compile").setHeading(); new Setting(containerEl)