diff --git a/src/BreadcrumbsSettingTab.ts b/src/BreadcrumbsSettingTab.ts index 00f67196..a96dd3de 100644 --- a/src/BreadcrumbsSettingTab.ts +++ b/src/BreadcrumbsSettingTab.ts @@ -210,6 +210,70 @@ export class BreadcrumbsSettingTab extends PluginSettingTab { fieldDetails.append(addHierarchyRow(userHier, true)); }); + const hierarchyNoteDetails: HTMLDetailsElement = + containerEl.createEl("details"); + hierarchyNoteDetails.createEl("summary", { text: "Hierarchy Notes" }); + + new Setting(hierarchyNoteDetails) + .setName("Hierarchy Note(s)") + .setDesc("A list of notes used to create external breadcrumb structures.") + .addText((text) => { + let finalValue: string[]; + text + .setPlaceholder("Hierarchy Note(s)") + .setValue([plugin.settings.hierarchyNotes].flat().join(", ")) + .onChange(async (value) => { + finalValue = splitAndTrim(value); + }); + + text.inputEl.onblur = async () => { + if (finalValue[0] === "") { + plugin.settings.hierarchyNotes = finalValue; + await plugin.saveSettings(); + } else if (finalValue.every((note) => isInVault(this.app, note))) { + plugin.settings.hierarchyNotes = finalValue; + await plugin.saveSettings(); + } else { + new Notice("Atleast one of the notes is not in your vault"); + } + }; + }); + + new Setting(hierarchyNoteDetails) + .setName("Hierarchy Note Field Name") + .setDesc( + "Using the breadcrumbs generated by the hierarchy note, which ↓ type should they count as? This has to be a of the ↓ types of one of your exisiting hierarchies. If you want it to be something else, you can make a new hierarchy just for it." + ) + .addText((text) => { + let finalValue: string; + text + .setPlaceholder("Hierarchy Note(s)") + .setValue(plugin.settings.hierarchyNoteFieldName) + .onChange(async (value) => { + finalValue = value; + }); + + text.inputEl.onblur = async () => { + if (finalValue === "") { + plugin.settings.hierarchyNoteFieldName = finalValue; + await plugin.saveSettings(); + } else { + const downFieldNames = plugin.settings.userHierarchies + .map((hier) => hier.down) + .flat(); + + if (downFieldNames.includes(finalValue)) { + plugin.settings.hierarchyNoteFieldName = finalValue; + await plugin.saveSettings(); + } else { + new Notice( + "The field name must be one of the exisitng ↓ fields in your hierarchies." + ); + } + } + }; + }); + const generalDetails: HTMLDetailsElement = containerEl.createEl("details"); generalDetails.createEl("summary", { text: "General Options" }); @@ -414,7 +478,6 @@ export class BreadcrumbsSettingTab extends PluginSettingTab { ) .addText((text) => { text - .setPlaceholder("Index Note") .setValue(plugin.settings.trailOrTable.toString()) .onChange(async (value) => { const num = parseInt(value); @@ -499,7 +562,7 @@ export class BreadcrumbsSettingTab extends PluginSettingTab { text.inputEl.onblur = async () => { // TODO Refactor this to general purpose isInVault function - if (finalValue === [""]) { + if (finalValue[0] === "") { plugin.settings.indexNote = finalValue; await plugin.saveSettings(); } else if (finalValue.every((index) => isInVault(this.app, index))) { diff --git a/src/interfaces.ts b/src/interfaces.ts index f8a2f60b..5c649c90 100644 --- a/src/interfaces.ts +++ b/src/interfaces.ts @@ -4,6 +4,8 @@ import type { FrontMatterCache, Pos, TFile } from "obsidian"; export interface BreadcrumbsSettings { userHierarchies: userHierarchy[]; indexNote: string[]; + hierarchyNotes: string[]; + hierarchyNoteFieldName: string; refreshIndexOnActiveLeafChange: boolean; useAllMetadata: boolean; parseJugglLinksWithoutJuggl: boolean; diff --git a/src/main.ts b/src/main.ts index 75ab5f95..0c598164 100644 --- a/src/main.ts +++ b/src/main.ts @@ -32,6 +32,7 @@ import { getNeighbourObjArr, getObsMetadataCache, mergeGs, + removeDuplicates, } from "src/sharedFunctions"; import StatsView from "src/StatsView"; import { VisModal } from "src/VisModal"; @@ -41,6 +42,8 @@ import TrailPath from "./Components/TrailPath.svelte"; const DEFAULT_SETTINGS: BreadcrumbsSettings = { userHierarchies: [], indexNote: [""], + hierarchyNotes: [""], + hierarchyNoteFieldName: "", refreshIndexOnActiveLeafChange: false, useAllMetadata: true, parseJugglLinksWithoutJuggl: false, @@ -307,6 +310,72 @@ export default class BreadcrumbsPlugin extends Plugin { return null; } + hierarchyNoteAdjList(str: string) { + const layers = str.split("\n"); + console.log({ layers }); + const depth = (line: string) => line.split("-")[0].length; + const depths = layers.map(depth); + const hier: { note: string; depth: number; children: string[] }[] = []; + + const lineRegex = new RegExp(/\s*- \[\[(.*)\]\]/); + + let lineNo = 0; + while (lineNo < layers.length) { + const currLine = layers[lineNo]; + if (currLine === "") continue; + const currNote = currLine.match(lineRegex)[1]; + const currDepth = depth(currLine); + + hier[lineNo] = { note: currNote, depth: currDepth, children: [] }; + + if (lineNo !== layers.length - 1) { + const nextLine = layers[lineNo + 1]; + const nextNote = nextLine.match(lineRegex)[1]; + const nextDepth = depth(nextLine); + + if (nextDepth > currDepth) { + debug(this.settings, { currNote, nextNote }); + hier[lineNo].children.push(nextNote); + + const copy = [...hier]; + const noteUp = copy.reverse().find((adjItem, i) => { + debug(this.settings, { i, currNote, currDepth }); + return adjItem.depth === currDepth - 1; + }); + debug(this.settings, { noteUp }); + if (noteUp) { + hier[hier.indexOf(noteUp)].children.push(currNote); + } + } else { + const copy = [...hier]; + const noteUp = copy.reverse().find((adjItem, i) => { + debug(this.settings, { i, currNote, currDepth }); + return adjItem.depth === currDepth - 1; + }); + debug(this.settings, { noteUp }); + hier[hier.indexOf(noteUp)].children.push(currNote); + } + } else { + const prevLine = layers[lineNo - 1]; + const prevDepth = depth(prevLine); + + if (prevDepth >= currDepth) { + const copy = [...hier]; + const noteUp = copy.reverse().find((adjItem, i) => { + return adjItem.depth === currDepth - 1; + }); + hier[hier.indexOf(noteUp)].children.push(currNote); + } + } + + lineNo++; + } + hier.forEach((item) => { + item.children = removeDuplicates(item.children); + }); + return hier; + } + // SECTION OneSource populateGraph( @@ -335,6 +404,28 @@ export default class BreadcrumbsPlugin extends Plugin { const relObjArr = await getNeighbourObjArr(this, fileFrontmatterArr); + let hierarchyNotesArr: { + note: string; + depth: number; + children: string[]; + }[]; + if (this.settings.hierarchyNotes[0] !== "") { + const currPath = this.app.workspace.getActiveFile().path; + const contentArr = await Promise.all( + this.settings.hierarchyNotes.map(async (note) => { + const file = this.app.metadataCache.getFirstLinkpathDest( + note, + currPath + ); + const content = await this.app.vault.cachedRead(file); + return content; + }) + ); + + hierarchyNotesArr = contentArr.map(this.hierarchyNoteAdjList).flat(); + debug(this.settings, { hierarchyNotesArr }); + } + const { userHierarchies } = this.settings; const graphs: BCIndex = { @@ -370,6 +461,23 @@ export default class BreadcrumbsPlugin extends Plugin { }); }); + if (hierarchyNotesArr.length) { + const { hierarchyNoteFieldName } = this.settings; + + const g = graphs.hierGs.find( + (hierG) => hierG.down[hierarchyNoteFieldName] + ).down[hierarchyNoteFieldName]; + + hierarchyNotesArr.forEach((adjListItem) => { + adjListItem.children.forEach((child) => { + g.setEdge(adjListItem.note, child, { + dir: "down", + fieldName: hierarchyNoteFieldName, + }); + }); + }); + } + DIRECTIONS.forEach((dir) => { const allXGs = getAllGsInDir(userHierarchies, graphs.hierGs, dir); const dirMerged = mergeGs(...Object.values(allXGs)); diff --git a/src/sharedFunctions.ts b/src/sharedFunctions.ts index 9db1b183..0192e950 100644 --- a/src/sharedFunctions.ts +++ b/src/sharedFunctions.ts @@ -550,3 +550,7 @@ export function hierToStr(hier: userHierarchy) { →: ${hier.same.join(", ")} ↓: ${hier.down.join(", ")}`; } + +export function removeDuplicates(arr: string[]) { + return [...new Set(arr)]; +}