Skip to content

Commit 64ab6a4

Browse files
authored
feature: opt-in to write index and scene number to scene files (#288)
* write basic scene index to note on draft change * sync scene indices when setting is enabled * write scene number to file * persist "writeProperty" setting * inform users that two properties will be written to scenes * check opt-in during scene creation not sure how this got removed during the rebase, but this commit adds the check back in so that the scene index is not written to the new file if the user has not opted-in. * move writting scene number to draft utils it made more sense for the writing of draft information about the scene to be within the "createScene" usecase code than the note utils file. createNoteWithPotentialTemplate now returns the TFile, if successful. Updated documentation to reflect this new behavior.
1 parent 2a14698 commit 64ab6a4

File tree

5 files changed

+137
-26
lines changed

5 files changed

+137
-26
lines changed

src/model/draft-utils.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,20 @@ type SceneInsertionLocation = {
1818
export async function createScene(
1919
app: App,
2020
path: string,
21+
index: number,
2122
draft: MultipleSceneDraft,
2223
open: boolean
2324
): Promise<void> {
2425
const template = draft.sceneTemplate ?? get(pluginSettings).sceneTemplate;
25-
createNoteWithPotentialTemplate(app, path, template);
26+
const note = await createNoteWithPotentialTemplate(app, path, template);
27+
if (note === null) return;
28+
29+
if (get(pluginSettings).writeProperty) {
30+
await app.fileManager.processFrontMatter(note, (fm) => {
31+
fm["longform-order"] = index;
32+
});
33+
}
34+
2635
if (open) {
2736
app.workspace.openLinkText(path, "/", false);
2837
}
@@ -63,7 +72,14 @@ export async function insertScene(
6372
return d;
6473
});
6574
});
66-
await createScene(app, newScenePath, draft, open);
75+
76+
await createScene(
77+
app,
78+
newScenePath,
79+
draft.scenes.findIndex((s) => s.title === sceneName),
80+
draft,
81+
open
82+
);
6783
}
6884

6985
export function setDraftOnFrontmatterObject(

src/model/note-utils.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,17 @@ export function fileNameFromPath(path: string): string {
1212
* Prefers Templater, then the core Templates plugin, then a plain note without using the template.
1313
* @param path Path to note to create.
1414
* @param template Path to template to use.
15+
*
16+
* @returns `null` if it fails to create the note. `TFile` for the new note, if successful.
1517
*/
1618
export async function createNoteWithPotentialTemplate(
1719
app: App,
1820
path: string,
1921
template: string | null
20-
): Promise<void> {
22+
): Promise<TFile | null> {
2123
const file = await createNote(app, path);
22-
if (template && file) {
24+
if (!file) return null;
25+
if (template) {
2326
let contents = "";
2427
let pluginUsed = "";
2528
try {
@@ -37,6 +40,7 @@ export async function createNoteWithPotentialTemplate(
3740
await app.vault.adapter.write(path, contents);
3841
}
3942
}
43+
return file;
4044
}
4145

4246
/**

src/model/store-vault-sync.ts

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,20 @@ import { cloneDeep, isEqual } from "lodash";
1010
import { get, type Unsubscriber } from "svelte/store";
1111

1212
import type { Draft } from "./types";
13-
import { drafts as draftsStore, selectedDraftVaultPath } from "./stores";
13+
import {
14+
drafts as draftsStore,
15+
pluginSettings,
16+
waitingForSync,
17+
selectedDraftVaultPath,
18+
} from "./stores";
1419
import {
1520
arraysToIndentedScenes,
21+
formatSceneNumber,
22+
numberScenes,
1623
setDraftOnFrontmatterObject,
1724
} from "src/model/draft-utils";
1825
import { fileNameFromPath } from "./note-utils";
19-
import { findScene, sceneFolderPath } from "./scene-navigation";
20-
import { pluginSettings } from "./stores";
21-
import { waitingForSync } from "./stores";
26+
import { findScene, sceneFolderPath, scenePath } from "./scene-navigation";
2227

2328
type FileWithMetadata = {
2429
file: TFile;
@@ -568,9 +573,71 @@ export class StoreVaultSync {
568573
await this.app.fileManager.processFrontMatter(file, (fm) => {
569574
setDraftOnFrontmatterObject(fm, draft);
570575
});
576+
577+
// for multi-scene projects, optionally set a property on each scene that holds its order within the project
578+
if (get(pluginSettings).writeProperty) {
579+
if (draft.format === "scenes") {
580+
const writes: Promise<void>[] = [];
581+
const sceneNumbers = numberScenes(draft.scenes);
582+
sceneNumbers.forEach((numberedScene, index) => {
583+
const sceneFilePath = scenePath(
584+
numberedScene.title,
585+
draft,
586+
this.app.vault
587+
);
588+
589+
const sceneFile = this.app.vault.getAbstractFileByPath(sceneFilePath);
590+
// false if a folder, or not found
591+
if (!(sceneFile instanceof TFile)) {
592+
return;
593+
}
594+
writes.push(
595+
writeSceneNumbers(
596+
this.app,
597+
sceneFile,
598+
index,
599+
numberedScene.numbering
600+
)
601+
);
602+
});
603+
604+
await Promise.all(writes);
605+
}
606+
}
571607
}
572608
}
573609

610+
export function syncSceneIndices(app: App): void | Promise<void[]> {
611+
const writes: Promise<void>[] = [];
612+
get(draftsStore).forEach((draft) => {
613+
if (draft.format !== "scenes") return;
614+
numberScenes(draft.scenes).map((numberedScene, index) => {
615+
const sceneFilePath = scenePath(numberedScene.title, draft, app.vault);
616+
617+
const sceneFile = app.vault.getAbstractFileByPath(sceneFilePath);
618+
// false if a folder, or not found
619+
if (!(sceneFile instanceof TFile)) {
620+
return;
621+
}
622+
return writeSceneNumbers(app, sceneFile, index, numberedScene.numbering);
623+
});
624+
});
625+
if (writes.length === 0) return;
626+
return Promise.all(writes);
627+
}
628+
629+
function writeSceneNumbers(
630+
app: App,
631+
file: TFile,
632+
index: number,
633+
numbering: number[]
634+
) {
635+
return app.fileManager.processFrontMatter(file, (fm) => {
636+
fm["longform-order"] = index;
637+
fm["longform-number"] = formatSceneNumber(numbering);
638+
});
639+
}
640+
574641
const ESCAPED_CHARACTERS = new Set("/&$^+.()=!|[]{},".split(""));
575642
function ignoredPatternToRegex(pattern: string): RegExp {
576643
let regex = "";

src/model/types.ts

Lines changed: 23 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,7 @@ export interface LongformPluginSettings {
9898
waitForSync: boolean;
9999
fallbackWaitEnabled: boolean;
100100
fallbackWaitTime: number;
101+
writeProperty: boolean;
101102
// DEPRECATED. To be removed in future, needed now for migrations.
102103
projects: {
103104
[path: string]: {
@@ -126,13 +127,14 @@ export const DEFAULT_SETTINGS: LongformPluginSettings = {
126127
sessionFile: DEFAULT_SESSION_FILE,
127128
numberScenes: false,
128129
sceneTemplate: null,
130+
writeProperty: false,
129131
projects: {},
130132
waitForSync: false,
131133
fallbackWaitEnabled: true,
132134
fallbackWaitTime: 5,
133135
};
134136

135-
export const TRACKED_SETTINGS_PATHS = [
137+
export const TRACKED_SETTINGS_PATHS: (keyof LongformPluginSettings)[] = [
136138
"version",
137139
"projects",
138140
"selectedDraftVaultPath",
@@ -152,22 +154,25 @@ export const TRACKED_SETTINGS_PATHS = [
152154
"waitForSync",
153155
"fallbackWaitEnabled",
154156
"fallbackWaitTime",
157+
"writeProperty",
155158
];
156159

157-
export const PASSTHROUGH_SAVE_SETTINGS_PATHS = [
158-
"sessionStorage",
159-
"userScriptFolder",
160-
"showWordCountInStatusBar",
161-
"startNewSessionEachDay",
162-
"sessionGoal",
163-
"applyGoalTo",
164-
"notifyOnGoal",
165-
"countDeletionsForGoal",
166-
"keepSessionCount",
167-
"sessionFile",
168-
"numberScenes",
169-
"sceneTemplate",
170-
"waitForSync",
171-
"fallbackWaitEnabled",
172-
"fallbackWaitTime",
173-
];
160+
export const PASSTHROUGH_SAVE_SETTINGS_PATHS: (keyof LongformPluginSettings)[] =
161+
[
162+
"sessionStorage",
163+
"userScriptFolder",
164+
"showWordCountInStatusBar",
165+
"startNewSessionEachDay",
166+
"sessionGoal",
167+
"applyGoalTo",
168+
"notifyOnGoal",
169+
"countDeletionsForGoal",
170+
"keepSessionCount",
171+
"sessionFile",
172+
"numberScenes",
173+
"sceneTemplate",
174+
"waitForSync",
175+
"fallbackWaitEnabled",
176+
"fallbackWaitTime",
177+
"writeProperty",
178+
];

src/view/settings/LongformSettings.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { pluginSettings, userScriptSteps } from "src/model/stores";
1313
import { FolderSuggest } from "./folder-suggest";
1414
import { DEFAULT_SESSION_FILE } from "src/model/types";
1515
import { FileSuggest } from "./file-suggest";
16+
import { syncSceneIndices } from "src/model/store-vault-sync";
1617

1718
export class LongformSettingsTab extends PluginSettingTab {
1819
plugin: LongformPlugin;
@@ -65,6 +66,24 @@ export class LongformSettingsTab extends PluginSettingTab {
6566
});
6667
});
6768

69+
new Setting(containerEl)
70+
.setName("Write scene index to frontmatter")
71+
.setDesc(
72+
"If enabled, will add a scene index, and scene number, to the frontmatter of scene files."
73+
)
74+
.addToggle((toggle) => {
75+
toggle.setValue(settings.writeProperty);
76+
toggle.onChange((value) => {
77+
pluginSettings.update((settings) => ({
78+
...settings,
79+
writeProperty: value,
80+
}));
81+
if (value) {
82+
syncSceneIndices(this.app);
83+
}
84+
});
85+
});
86+
6887
new Setting(containerEl).setName("Compile").setHeading();
6988

7089
new Setting(containerEl)

0 commit comments

Comments
 (0)