diff --git a/main.js b/main.js index 5f23b587..539b141d 100644 --- a/main.js +++ b/main.js @@ -2626,6 +2626,7 @@ const DEFAULT_SETTINGS = { trailSeperator: "→", respectReadableLineLength: true, limitWriteBCCheckboxStates: {}, + writeBCsInline: false, showWriteAllBCsCmd: false, visGraph: "Force Directed Graph", visRelation: "Parent", @@ -20309,13 +20310,27 @@ const hierToStr = (hier) => DIRECTIONS.map((dir) => `${ARROW_DIRECTIONS[dir]}: $ function removeDuplicates(arr) { return [...new Set(arr)]; } +const getOppDir = (dir) => { + switch (dir) { + case "up": + return "down"; + case "down": + return "up"; + case "same": + return "same"; + case "next": + return "prev"; + case "prev": + return "next"; + } +}; /** * Adds or updates the given yaml `key` to `value` in the given TFile * @param {string} key * @param {string} value * @param {TFile} file * @param {FrontMatterCache|undefined} frontmatter - * @param {{[fun:string]:(...args:any} api + * @param {MetaeditApi} api */ const createOrUpdateYaml = async (key, value, file, frontmatter, api) => { const valueStr = value.toString(); @@ -20329,48 +20344,50 @@ const createOrUpdateYaml = async (key, value, file, frontmatter, api) => { } else { const oldValueFlat = [...[frontmatter[key]]].flat(4); - const newValue = [...oldValueFlat, valueStr].map((val) => `'${val}'`); + const newValue = [...oldValueFlat, `'${valueStr}'`]; console.log(`Updating: ${key}: ${newValue}`); await api.update(key, `[${newValue.join(", ")}]`, file); } }; -const getOppDir = (dir) => { - switch (dir) { - case "up": - return "down"; - case "down": - return "up"; - case "same": - return "same"; - case "next": - return "prev"; - case "prev": - return "next"; - } -}; -const writeBCToFile = (app, plugin, currGraphs, file) => { +const writeBCToFile = async (app, plugin, currGraphs, file, inline) => { var _a, _b; + const { limitWriteBCCheckboxStates, userHierarchies } = plugin.settings; const frontmatter = (_a = app.metadataCache.getFileCache(file)) === null || _a === void 0 ? void 0 : _a.frontmatter; const api = (_b = app.plugins.plugins.metaedit) === null || _b === void 0 ? void 0 : _b.api; if (!api) { new obsidian.Notice("Metaedit must be enabled for this function to work"); return; } - iterateAllGs(currGraphs.hierGs, (fieldG, dir, fieldName) => { - const oppDir = getOppDir(dir); - const succs = getInNeighbours(fieldG, file.basename); - succs.forEach(async (succ) => { - const { fieldName } = fieldG.getNodeAttributes(succ); - if (!plugin.settings.limitWriteBCCheckboxStates[fieldName]) - return; - const currHier = plugin.settings.userHierarchies.find((hier) => hier[dir].includes(fieldName)); - let oppField = currHier[oppDir][0]; - if (!oppField) - oppField = `${fieldName}`; - await createOrUpdateYaml(oppField, succ, file, frontmatter, api); - }); - }); + const { main } = currGraphs; + const succs = getInNeighbours(main, file.basename); + for (const succ of succs) { + const { fieldName } = main.getNodeAttributes(succ); + if (!limitWriteBCCheckboxStates[fieldName]) + return; + if (!inline) { + await createOrUpdateYaml(fieldName, succ, file, frontmatter, api); + } + else { + // TODO Check if this note already has this field + let content = await app.vault.read(file); + const splits = splitAtYaml(content); + content = splits[0] + `\n${fieldName}:: [[${succ}]]` + splits[1]; + await app.vault.modify(file, content); + } + } }; +function splitAtYaml(content) { + const startsWithYaml = content.startsWith("---"); + if (!startsWithYaml) + return ["", content]; + else { + const splits = content.split("---"); + return [ + splits.slice(0, 2).join("---") + "---", + splits.slice(2).join("---"), + ]; + } +} function oppFields(field, dir, userHierarchies) { var _a, _b; const oppDir = getOppDir(dir); @@ -23860,6 +23877,13 @@ class BCSettingTab extends obsidian.PluginSettingTab { }); } drawLimitWriteBCCheckboxes(limitWriteBCCheckboxDiv); + new obsidian.Setting(writeBCsToFileDetails) + .setName("Write BCs to file Inline") + .setDesc("When writing BCs to file, should they be written inline (using Dataview syntax), or into the YAML of the note?") + .addToggle((toggle) => toggle.setValue(settings.writeBCsInline).onChange(async (value) => { + settings.writeBCsInline = value; + await plugin.saveSettings(); + })); new obsidian.Setting(writeBCsToFileDetails) .setName("Show the `Write Breadcrumbs to ALL Files` command") .setDesc("This command attempts to update ALL files with implied breadcrumbs pointing to them. So, it is not shown by default (even though it has 3 confirmation boxes to ensure you want to run it") @@ -35136,9 +35160,9 @@ class BCPlugin extends obsidian.Plugin { this.addCommand({ id: "Write-Breadcrumbs-to-Current-File", name: "Write Breadcrumbs to Current File", - callback: () => { + callback: async () => { const currFile = this.app.workspace.getActiveFile(); - writeBCToFile(this.app, this, this.currGraphs, currFile); + await writeBCToFile(this.app, this, this.currGraphs, currFile, this.settings.writeBCsInline); }, }); this.addCommand({ @@ -35154,7 +35178,7 @@ class BCPlugin extends obsidian.Plugin { try { this.app.vault .getMarkdownFiles() - .forEach((file) => writeBCToFile(this.app, this, this.currGraphs, file)); + .forEach(async (file) => await writeBCToFile(this.app, this, this.currGraphs, file, this.settings.writeBCsInline)); new obsidian.Notice("Operation Complete"); } catch (error) { diff --git a/src/BreadcrumbsSettingTab.ts b/src/BreadcrumbsSettingTab.ts index dba54f6c..1e1c3ebf 100644 --- a/src/BreadcrumbsSettingTab.ts +++ b/src/BreadcrumbsSettingTab.ts @@ -675,6 +675,18 @@ export class BCSettingTab extends PluginSettingTab { drawLimitWriteBCCheckboxes(limitWriteBCCheckboxDiv); + new Setting(writeBCsToFileDetails) + .setName("Write BCs to file Inline") + .setDesc( + "When writing BCs to file, should they be written inline (using Dataview syntax), or into the YAML of the note?" + ) + .addToggle((toggle) => + toggle.setValue(settings.writeBCsInline).onChange(async (value) => { + settings.writeBCsInline = value; + await plugin.saveSettings(); + }) + ); + new Setting(writeBCsToFileDetails) .setName("Show the `Write Breadcrumbs to ALL Files` command") .setDesc( diff --git a/src/constants.ts b/src/constants.ts index 533d923a..f07468c3 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -98,6 +98,7 @@ export const DEFAULT_SETTINGS: BCSettings = { trailSeperator: "→", respectReadableLineLength: true, limitWriteBCCheckboxStates: {}, + writeBCsInline: false, showWriteAllBCsCmd: false, visGraph: "Force Directed Graph", visRelation: "Parent", diff --git a/src/interfaces.ts b/src/interfaces.ts index ca4f9b07..3ba14c57 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -36,6 +36,7 @@ export interface BCSettings { trailSeperator: string; respectReadableLineLength: boolean; limitWriteBCCheckboxStates: { [field: string]: boolean }; + writeBCsInline: boolean; showWriteAllBCsCmd: boolean; visGraph: visTypes; visRelation: Relations; @@ -208,3 +209,27 @@ export type ClosedGraphs = { }; export type PrevNext = { to: string; real: boolean; fieldName: string }; + +export interface MetaeditApi { + /** Adds the key and value */ + createYamlProperty: ( + key: string, + value: string, + file: TFile + ) => Promise; + /** Changes `key`'s value to `value` (overwrites) */ + update: (key: string, value: string, file: TFile) => Promise; +} + +declare module "obsidian" { + interface App { + plugins: { + plugins: { + dataview: { api: { page: (page: string) => dvFrontmatterCache } }; + metaedit: { + api: MetaeditApi; + }; + }; + }; + } +} diff --git a/src/main.ts b/src/main.ts index 194059ca..06d6b93e 100644 --- a/src/main.ts +++ b/src/main.ts @@ -186,9 +186,15 @@ export default class BCPlugin extends Plugin { this.addCommand({ id: "Write-Breadcrumbs-to-Current-File", name: "Write Breadcrumbs to Current File", - callback: () => { + callback: async () => { const currFile = this.app.workspace.getActiveFile(); - writeBCToFile(this.app, this, this.currGraphs, currFile); + await writeBCToFile( + this.app, + this, + this.currGraphs, + currFile, + this.settings.writeBCsInline + ); }, }); @@ -211,8 +217,15 @@ export default class BCPlugin extends Plugin { try { this.app.vault .getMarkdownFiles() - .forEach((file) => - writeBCToFile(this.app, this, this.currGraphs, file) + .forEach( + async (file) => + await writeBCToFile( + this.app, + this, + this.currGraphs, + file, + this.settings.writeBCsInline + ) ); new Notice("Operation Complete"); } catch (error) { diff --git a/src/sharedFunctions.ts b/src/sharedFunctions.ts index 54301b06..dc1babe5 100644 --- a/src/sharedFunctions.ts +++ b/src/sharedFunctions.ts @@ -25,6 +25,7 @@ import type { HierarchyFields, HierarchyGraphs, JugglLink, + MetaeditApi, neighbourObj, PrevNext, userHierarchy, @@ -643,20 +644,35 @@ export function removeDuplicates(arr: T[]) { return [...new Set(arr)]; } +export const getOppDir = (dir: Directions): Directions => { + switch (dir) { + case "up": + return "down"; + case "down": + return "up"; + case "same": + return "same"; + case "next": + return "prev"; + case "prev": + return "next"; + } +}; + /** * Adds or updates the given yaml `key` to `value` in the given TFile * @param {string} key * @param {string} value * @param {TFile} file * @param {FrontMatterCache|undefined} frontmatter - * @param {{[fun:string]:(...args:any} api + * @param {MetaeditApi} api */ export const createOrUpdateYaml = async ( key: string, value: string, file: TFile, frontmatter: FrontMatterCache | undefined, - api: { [fun: string]: (...args: any) => any } + api: MetaeditApi ) => { const valueStr = value.toString(); @@ -668,33 +684,20 @@ export const createOrUpdateYaml = async ( return; } else { const oldValueFlat: string[] = [...[frontmatter[key]]].flat(4); - const newValue = [...oldValueFlat, valueStr].map((val) => `'${val}'`); + const newValue = [...oldValueFlat, `'${valueStr}'`]; console.log(`Updating: ${key}: ${newValue}`); await api.update(key, `[${newValue.join(", ")}]`, file); } }; -export const getOppDir = (dir: Directions): Directions => { - switch (dir) { - case "up": - return "down"; - case "down": - return "up"; - case "same": - return "same"; - case "next": - return "prev"; - case "prev": - return "next"; - } -}; - -export const writeBCToFile = ( +export const writeBCToFile = async ( app: App, plugin: BCPlugin, currGraphs: BCIndex, - file: TFile + file: TFile, + inline: boolean ) => { + const { limitWriteBCCheckboxStates, userHierarchies } = plugin.settings; const frontmatter = app.metadataCache.getFileCache(file)?.frontmatter; const api = app.plugins.plugins.metaedit?.api; @@ -703,25 +706,38 @@ export const writeBCToFile = ( return; } - iterateAllGs(currGraphs.hierGs, (fieldG, dir, fieldName) => { - const oppDir = getOppDir(dir); - const succs = getInNeighbours(fieldG, file.basename); + const { main } = currGraphs; + const succs = getInNeighbours(main, file.basename); - succs.forEach(async (succ) => { - const { fieldName } = fieldG.getNodeAttributes(succ); - if (!plugin.settings.limitWriteBCCheckboxStates[fieldName]) return; + for (const succ of succs) { + const { fieldName } = main.getNodeAttributes(succ); + if (!limitWriteBCCheckboxStates[fieldName]) return; - const currHier = plugin.settings.userHierarchies.find((hier) => - hier[dir].includes(fieldName) - ); - let oppField: string = currHier[oppDir][0]; - if (!oppField) oppField = `${fieldName}`; + if (!inline) { + await createOrUpdateYaml(fieldName, succ, file, frontmatter, api); + } else { + // TODO Check if this note already has this field + let content = await app.vault.read(file); + const splits = splitAtYaml(content); + content = splits[0] + `\n${fieldName}:: [[${succ}]]` + splits[1]; - await createOrUpdateYaml(oppField, succ, file, frontmatter, api); - }); - }); + await app.vault.modify(file, content); + } + } }; +function splitAtYaml(content: string): [string, string] { + const startsWithYaml = content.startsWith("---"); + if (!startsWithYaml) return ["", content]; + else { + const splits = content.split("---"); + return [ + splits.slice(0, 2).join("---") + "---", + splits.slice(2).join("---"), + ]; + } +} + export function oppFields( field: string, dir: Directions,