diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index acac1c0..be89db0 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,11 +8,12 @@ on: jobs: build: runs-on: ubuntu-latest - + permissions: + contents: write steps: - uses: actions/checkout@v3 - - uses: oven-sh/setup-bun@v1 + - uses: oven-sh/setup-bun@v2 with: bun-version: latest diff --git a/.gitignore b/.gitignore index f4c68df..861b995 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,7 @@ main.js .obsidian dist + +# gh-pages +_site +.tweet-cache diff --git a/README.md b/README.md index 8463548..3f320a9 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Obsidian N-brace Plugin -![Obsidian N-brace Plugin](https://github.com/nn-ninja/n-brace) +[Obsidian N-brace Plugin](https://nn-ninja.github.io/n-brace/) Homepage A more intuitive Graph navigation and visualization for Obsidian. @@ -13,7 +13,7 @@ https://buymeacoffee.com/nbrace ### Through community plugin store -You can install this on community plugin store (not yet). +You can install this on community plugin store. ### Manual installation @@ -22,11 +22,34 @@ You can install this on community plugin store (not yet). 3. `cd obsidian-n-brace && bun install && bun run build` 4. there you go 🎉 +## Usage + +![graph.png](docs/graph.png) + +In a note tab context menu (right click), click on `use N-brace` to open a local graph of viewed note. +![menu.png](docs/menu.png) + +The graph appears in the right-hand view and is designed to be used alongside the note on the left. Its purpose is to help you quickly understand how the current note connects to others and to visualize its overall structure. + +G-span defines how large a portion of the graph is displayed. When the number of visible nodes reaches this limit, the view automatically shrinks by hiding the most distant nodes. + +### Navigation + +You can navigate using the keyboard arrow keys. Notes that haven’t been expanded yet in the local graph are shown with a dotted border. + +If you need to explore the parent notes, hold Ctrl to reverse the navigation direction — the link directions will swap accordingly. + +## Notes + +At present, the n-brace graph model is initialized during application startup, which may result in longer loading times. + +I have in my plans to optimize this process. + ## Features ### Local Graph -In a note tab context menu (right click), click on `N-brace` to open a local graph of viewed note. +It is designed to rather operate on a local graph, not on huge and unclear global graph. ### Save Setting diff --git a/TECHNICAL_DOC.md b/TECHNICAL_DOC.md deleted file mode 100644 index 40bab8e..0000000 --- a/TECHNICAL_DOC.md +++ /dev/null @@ -1,65 +0,0 @@ -## Plugin architecture - -The plugin consist of following component: - -1. plugin setting manager - 1. it controls the settings of the plugin - 2. responsible for loading and saving setting -2. global graph view, a wrapper of graph setting manager and force graph -3. local graph view, a wrapper of graph setting manager and force graph -4. graph setting manager - 1. each graph view will have a graph setting manager - 2. it resposible for the control of the whole graph, then tell the force graph how to respond for setting changes - 3. the setting manager can be isolated from force graph because it follow separation of concern principle -5. force graph - 1. the graph itself render the the nodes and links - 2. interaction manager - 1. it is responsible to handle all the interaction of the user and the force graph -6. Graph Command - 1. has very simple API to extends commands - -```mermaid -classDiagram - class pluginSettingManager { - + controls the settings of the plugin - + loads and saves setting - } - - class globalGraphView { - } - - class localGraphView { - } - - class graphSettingManager { - + controls the whole graph - + communicates setting changes to force graph - } - - class forceGraph { - + renders nodes and links - } - - class interactionManager { - + handles user interaction with force graph - } - - class GraphCommand { - + extends commands through simple API - } - - pluginSettingManager --|> globalGraphView: contains - pluginSettingManager --|> localGraphView: contains - graphSettingManager --|> globalGraphView: contains - graphSettingManager --|> localGraphView: contains - forceGraph --|> globalGraphView: contains - forceGraph --|> localGraphView: contains - interactionManager --|> forceGraph: is part of - GraphCommand --|> globalGraphView: can be used by - GraphCommand --|> localGraphView: can be used by - -``` - -## Key guideline for plugin development - -1. Each component here follow separation of concern principle. They can be isolated easily and still functions properly. diff --git a/bun.lockb b/bun.lockb index a74fdd9..3e49903 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/data.json b/data.json index cd341a3..966296e 100644 --- a/data.json +++ b/data.json @@ -21,8 +21,7 @@ "showExtension": false, "showFullPath": false, "showLinkArrow": true, - "dontMoveWhenDrag": false, - "dagOrientation": "null" + "dontMoveWhenDrag": false } }, "pluginSetting": { diff --git a/docs/graph.png b/docs/graph.png new file mode 100644 index 0000000..a9628e9 Binary files /dev/null and b/docs/graph.png differ diff --git a/docs/menu.png b/docs/menu.png new file mode 100644 index 0000000..b778762 Binary files /dev/null and b/docs/menu.png differ diff --git a/manifest.json b/manifest.json index 2f4d95e..7893402 100644 --- a/manifest.json +++ b/manifest.json @@ -1,11 +1,11 @@ { - "id": "n-brace-plugin", + "id": "n-brace", "name": "N-brace", "version": "1.0.0", - "minAppVersion": "0.15.0", - "description": "The N-brace plugin enhances navigation and clarity in the local graph view.", + "minAppVersion": "1.9.14", + "description": "Altered local graph's visual layout. Quick graph navigation.", "author": "Rafael Hazan", - "authorUrl": "https://hayio.github.io/n-brace/", + "authorUrl": "https://nn-ninja.github.io/n-brace", "fundingUrl": "https://buymeacoffee.com/nbrace", "isDesktopOnly": false } diff --git a/package.json b/package.json index 0e7285a..0705bb9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n-brace", - "version": "1.1.11", + "version": "1.0.0", "description": "A graph navigation and visualization for Obsidian", "main": "main.js", "scripts": { @@ -55,7 +55,7 @@ "react-dom": "18.3.1", "lucide-react": "^0.511.0", "jotai": "^2.9.1", - "force-graph": "^1.43.5", + "react-force-graph-2d": "^1.29.0", "@tweenjs/tween.js": "^21.0.0", "copy-anything": "^3.0.5", "d3": "^7.9.0", diff --git a/release.sh b/release.sh index b1f6782..b4a84ab 100644 --- a/release.sh +++ b/release.sh @@ -63,4 +63,4 @@ echo "Created tag $NEW_VERSION" git push origin "$NEW_VERSION" echo "Pushed tag $NEW_VERSION to the origin branch $NEW_VERSION" git push -echo "Pushed to the origin master branch" +echo "Pushed to the origin main branch" diff --git a/src/SettingManager.ts b/src/SettingManager.ts index e566432..8520b15 100644 --- a/src/SettingManager.ts +++ b/src/SettingManager.ts @@ -1,18 +1,7 @@ import type { ISettingManager } from "@/Interfaces"; import { AsyncQueue } from "@/util/AsyncQueue"; -import type { - GraphSetting, - LocalGraphSettings, - MarkdownPostProcessorGraphSettings, - Setting, -} from "@/SettingsSchemas"; -import { - defaultLocalGraphSetting, - defaultMarkdownPostProcessorGraphSetting, - GraphType, - SearchEngineType, - SettingSchema, -} from "@/SettingsSchemas"; +import type { Setting } from "@/SettingsSchemas"; +import { defaultLocalGraphSetting, SearchEngineType, SettingSchema } from "@/SettingsSchemas"; import { createNotice } from "@/util/createNotice"; import { State } from "@/util/State"; import type { Plugin } from "obsidian"; @@ -105,17 +94,6 @@ export class PluginSettingManager implements ISettingManager { } await this.plugin.saveData(this.setting.value); } - - static getNewSetting(type: GraphType.local): LocalGraphSettings; - static getNewSetting(type: GraphType.postProcessor): MarkdownPostProcessorGraphSettings; - static getNewSetting(type: GraphType): GraphSetting; - static getNewSetting(type: GraphType) { - if (type === GraphType.local) { - return defaultLocalGraphSetting; - } else { - return defaultMarkdownPostProcessorGraphSetting; - } - } } export const DEFAULT_SETTING: Setting = { diff --git a/src/SettingsSchemas.ts b/src/SettingsSchemas.ts index d277076..fbd3d61 100644 --- a/src/SettingsSchemas.ts +++ b/src/SettingsSchemas.ts @@ -49,23 +49,6 @@ export enum SearchEngineType { builtIn = "builtIn", } -export enum DagOrientation { - td = "td", - bu = "bu", - lr = "lr", - rl = "rl", - zout = "zout", - zin = "zin", - radialout = "radialout", - radialin = "radialin", - null = "null", -} - -export enum CommandClickNodeAction { - openNodeInNewTab = "openNodeInNewTab", - focusNode = "focusNode", -} - const commonSetting = { display: { nodeSize: nodeSize.default, @@ -101,7 +84,6 @@ export const BaseDisplaySettingsSchema = z.object({ showFullPath: z.boolean().default(commonSetting.display.showFullPath), showLinkArrow: z.boolean().default(commonSetting.display.showLinkArrow), dontMoveWhenDrag: z.boolean().default(commonSetting.display.dontMoveWhenDrag), - dagOrientation: z.undefined().or(z.nativeEnum(DagOrientation)).default(DagOrientation.null), }); export const LocalDisplaySettingsSchema = z.object({ @@ -189,24 +171,12 @@ export const SettingSchema = z.object({ }), }); -export type BaseFilterSettings = Prettify>; - -export type LocalFilterSetting = Prettify>; - -export type GroupSettings = Prettify>; - -export type BaseDisplaySettings = Prettify>; - -export type LocalDisplaySettings = Prettify>; - export type LocalGraphSettings = Prettify>; export type MarkdownPostProcessorGraphSettings = Prettify< z.TypeOf >; -export type SavedSetting = Prettify>; - export type Setting = Prettify>; export type GraphSetting = Exclude< @@ -225,7 +195,6 @@ export const defaultLocalGraphSetting: LocalGraphSettings = { groups: [], display: { ...commonSetting.display, - dagOrientation: DagOrientation.null, }, }; @@ -240,6 +209,5 @@ export const defaultMarkdownPostProcessorGraphSetting: MarkdownPostProcessorGrap groups: [], display: { ...commonSetting.display, - dagOrientation: DagOrientation.null, }, }; diff --git a/src/config.ts b/src/config.ts index 8c59c5c..886ccbd 100644 --- a/src/config.ts +++ b/src/config.ts @@ -4,6 +4,6 @@ export const config = { local: "n-brace-view", }, displayText: { - local: "N-brace your brain", + local: "N-brace view", }, }; diff --git a/src/graph/Graph.ts b/src/graph/Graph.ts index 5b4a191..95ce053 100644 --- a/src/graph/Graph.ts +++ b/src/graph/Graph.ts @@ -1,8 +1,7 @@ import type { LinkCache } from "@/graph/Link"; import { Link } from "@/graph/Link"; import { Node } from "@/graph/Node"; -import { copy } from "copy-anything"; -import type { App, TAbstractFile } from "obsidian"; +import type { App } from "obsidian"; export class Graph { public rootPath: string | undefined; @@ -31,14 +30,6 @@ export class Graph { this.nodes.sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0)); } - public getNodeByPath = (path: string): Node | null => { - const index = this.nodeIndex.get(path); - if (typeof index === "number") { - return this.nodes[index] ?? null; - } - return null; - }; - public getNodeById(id: string): Node | null { const index = this.nodeIndex.get(id); if (typeof index === "number") { @@ -47,19 +38,6 @@ export class Graph { return null; } - // Returns a link by its source and target node ids - public getLinkByIds(sourceNodeId: string, targetNodeId: string): Link | null { - const sourceLinkMap = this.linkIndex.get(sourceNodeId); - if (sourceLinkMap) { - const index = sourceLinkMap.get(targetNodeId); - if (index !== undefined) { - // @ts-ignore - return this.links[index]; - } - } - return null; - } - public getLinksFromNode(sourceNodeId: string): Link[] { const linkIndexes = this.linkIndex.get(sourceNodeId); if (linkIndexes) { @@ -70,41 +48,6 @@ export class Graph { return []; } - /** - * given a node id, return all the links that contains this node - */ - public getLinksWithNode(nodeId: string): Link[] { - const links: Link[] = []; - - this.linkIndex.forEach((targetMap, sourceId) => { - if (sourceId === nodeId) { - links.push( - // @ts-ignore - ...Array.from(targetMap.values()).map((index) => this.links[index]) - ); - } else if (targetMap.has(nodeId)) { - const index = targetMap.get(nodeId); - if (typeof index === "number") { - // @ts-ignore - links.push(this.links[index]); - } - } - }); - - return links; - } - - // Clones the graph - public clone = (): Graph => { - return new Graph( - this.rootPath, - copy(this.nodes), - copy(this.links), - copy(this.nodeIndex), - copy(this.linkIndex) - ); - }; - public static createEmpty = (): Graph => { return new Graph(undefined, [], [], new Map(), new Map()); }; @@ -231,64 +174,6 @@ export class Graph { this.nodes.forEach(fun); }; - public static compare = (graph1: Graph, graph2: Graph): boolean => { - // Quick checks for lengths - if (graph1.nodes.length !== graph2.nodes.length) { - return false; - } - if (graph1.links.length !== graph2.links.length) { - return false; - } - - // Compare nodes - for (const node1 of graph1.nodes) { - const node2Index = graph2.nodeIndex.get(node1.id); - // @ts-ignore - if (node2Index === undefined || graph2.nodes[node2Index].id !== node1.id) { - return false; - } - } - - // Compare links - for (const link1 of graph1.links) { - const graph2SourceLinkMap = graph2.linkIndex.get(link1.source.id); - if (!graph2SourceLinkMap) { - return false; - } - - const graph2LinkIndex = graph2SourceLinkMap.get(link1.target.id); - if (graph2LinkIndex === undefined) { - return false; - } - - const link2 = graph2.links[graph2LinkIndex]; - if (!link2 || link2.source.id !== link1.source.id || link2.target.id !== link1.target.id) { - return false; - } - } - - return true; - }; - - /** - * get the files from the graph - */ - public static getFiles(app: App, graph: Graph): TAbstractFile[] { - return graph.nodes.map((node) => app.vault.getAbstractFileByPath(node.path)).filter(Boolean); - } - - public isAcyclic = (): boolean => { - const visited = new Set(); - const recStack = new Set(); - - for (const node of this.nodes) { - if (this.isCyclicUtil(node.id, visited, recStack)) { - return false; - } - } - return true; - }; - private isCyclicUtil = (nodeId: string, visited: Set, recStack: Set): boolean => { if (!visited.has(nodeId)) { // Add to visited and recursion stack diff --git a/src/main.ts b/src/main.ts index 8afcae0..b8fa1f9 100644 --- a/src/main.ts +++ b/src/main.ts @@ -114,7 +114,6 @@ export default class ForceGraphPlugin extends Plugin implements HoverParent { // unregister the resolved cache listener this.app.metadataCache.off("resolved", this.onGraphCacheReady); this.app.metadataCache.off("resolve", this.onGraphCacheChanged); - this.app.workspace.detachLeavesOfType(VIEW_TYPE_REACT_FORCE_GRAPH); } private initListeners() { @@ -129,7 +128,7 @@ export default class ForceGraphPlugin extends Plugin implements HoverParent { if (!file) return; menu.addItem((item) => { item - .setTitle("N-brace") + .setTitle("use N-brace") .setIcon(config.icon) .onClick(() => this.openGraph()); }); @@ -146,22 +145,6 @@ export default class ForceGraphPlugin extends Plugin implements HoverParent { if (!file || !(file instanceof TFile)) { return; } - // if (markdownLeaves.length === 0) { - // const rootLeaf = workspace.getMostRecentLeaf() || workspace.getLeaf(); - // const newLeaf = workspace.createLeafBySplit(rootLeaf, "vertical", true); // Split left - // await newLeaf.openFile(file); - // workspace.revealLeaf(newLeaf); - // return; - // } - // - // // Use the first existing Markdown leaf - // const targetLeaf = markdownLeaves[markdownLeaves.length - 1]; - // if (file) { - // await targetLeaf.openFile(file as any); // Open the file in the leaf - // workspace.revealLeaf(targetLeaf); // Ensure it’s visible - // } else { - // console.error(`File not found: ${filePath}`); - // } const graphLeaf = workspace.getLeavesOfType(VIEW_TYPE_REACT_FORCE_GRAPH)[0]; if (!graphLeaf) { diff --git a/src/views/ReactForceGraphView.tsx b/src/views/ReactForceGraphView.tsx index 4784d4a..0c3811c 100644 --- a/src/views/ReactForceGraphView.tsx +++ b/src/views/ReactForceGraphView.tsx @@ -22,7 +22,6 @@ export class ReactForceGraphView extends ItemView { readonly plugin: ForceGraphPlugin; readonly store = getDefaultStore(); root: Root | null = null; - private width: number = this.contentEl.offsetWidth; private settingManager: PluginSettingManager; constructor(leaf: WorkspaceLeaf, plugin: ForceGraphPlugin) { @@ -85,7 +84,7 @@ export class ReactForceGraphView extends ItemView { console.debug("node to expand ", nodePath); if (!nodePath || !this.plugin.globalGraph) { console.debug("Returning empty graph."); - return Promise.resolve(Graph.createEmpty()); + return Graph.createEmpty(); } const graph = getNewLocalGraph(this.plugin, { centerFilePath: nodePath, diff --git a/src/views/SettingTab.ts b/src/views/SettingTab.ts index d359108..e8dae9e 100644 --- a/src/views/SettingTab.ts +++ b/src/views/SettingTab.ts @@ -37,11 +37,11 @@ export class SettingTab extends PluginSettingTab { containerEl.empty(); containerEl.addClasses(["n-brace-setting-tab"]); - containerEl.createEl("h2", { text: "Settings" }); - new Setting(containerEl) .setName("Base Folder") - .setDesc("Base folder, within which the mind map can navigate.") + .setDesc( + "The base directory where your notes for exploration live — navigation builds from here." + ) .addText((text) => { text .setPlaceholder(`${DEFAULT_BASE_FOLDER}`) @@ -68,35 +68,6 @@ export class SettingTab extends PluginSettingTab { text.inputEl.setAttribute("type", "string"); }); - new Setting(containerEl) - .setName("Note title font size") - .setDesc("The font size of the title displayed on a graph node.") - .addText((text) => { - text - .setPlaceholder(`${DEFAULT_TITLE_FONT_SIZE}`) - .setValue(String(pluginSetting.titleFontSize ?? DEFAULT_TITLE_FONT_SIZE)) - .onChange(async (value) => { - // check if value is a number - if (isNaN(Number(value)) || Number(value) <= 3) { - // set the error to the input - text.inputEl.setCustomValidity("Please enter a number higher than 3"); - this.plugin.settingManager.updateSettings((setting) => { - setting.value.pluginSetting.titleFontSize = DEFAULT_TITLE_FONT_SIZE; - }); - } else { - text.inputEl.setCustomValidity(""); - this.plugin.settingManager.updateSettings((setting) => { - setting.value.pluginSetting.titleFontSize = Number(value); - eventBus.trigger("settings-updated"); - }); - } - text.inputEl.reportValidity(); - }); - text.inputEl.setAttribute("type", "number"); - text.inputEl.setAttribute("min", "4"); - return text; - }); - new Setting(containerEl) .setName("Default display graph span") .setDesc("Size of the graph that is displayed at once.") @@ -126,6 +97,37 @@ export class SettingTab extends PluginSettingTab { return text; }); + new Setting(containerEl).setName("Appearance").setHeading(); + + new Setting(containerEl) + .setName("Note title font size") + .setDesc("The font size of the title displayed on a graph node.") + .addText((text) => { + text + .setPlaceholder(`${DEFAULT_TITLE_FONT_SIZE}`) + .setValue(String(pluginSetting.titleFontSize ?? DEFAULT_TITLE_FONT_SIZE)) + .onChange(async (value) => { + // check if value is a number + if (isNaN(Number(value)) || Number(value) <= 3) { + // set the error to the input + text.inputEl.setCustomValidity("Please enter a number higher than 3"); + this.plugin.settingManager.updateSettings((setting) => { + setting.value.pluginSetting.titleFontSize = DEFAULT_TITLE_FONT_SIZE; + }); + } else { + text.inputEl.setCustomValidity(""); + this.plugin.settingManager.updateSettings((setting) => { + setting.value.pluginSetting.titleFontSize = Number(value); + eventBus.trigger("settings-updated"); + }); + } + text.inputEl.reportValidity(); + }); + text.inputEl.setAttribute("type", "number"); + text.inputEl.setAttribute("min", "4"); + return text; + }); + new Setting(containerEl) .setName("Link Color") .setDesc("Choose predefined colors or a custom color for graph links") @@ -157,7 +159,7 @@ export class SettingTab extends PluginSettingTab { setting.value.pluginSetting.linkColorTheme = value; }); } - this.display(); + await this.display(); }) ) .addColorPicker((color) => { diff --git a/src/views/graph/GraphControls.tsx b/src/views/graph/GraphControls.tsx index a31f31f..5aa89af 100644 --- a/src/views/graph/GraphControls.tsx +++ b/src/views/graph/GraphControls.tsx @@ -1,9 +1,15 @@ import React, { useEffect, useState } from "react"; -import { FaArrowDown, FaArrowLeft, FaArrowRight, FaArrowUp } from "react-icons/fa"; +import { + ArrowDown, + ArrowLeft, + ArrowRight, + ArrowUp, + FlipVertical, + FlipVertical2, + Sun, + Sunrise, +} from "lucide-react"; import "@/views/graph/GraphControls.css"; -import { RiFlipVerticalFill, RiFlipVerticalLine } from "react-icons/ri"; -import { PiSunHorizonBold } from "react-icons/pi"; -import { IoSunny } from "react-icons/io5"; import { useAtomValue } from "jotai/react"; import { graphSettingsAtom } from "@/atoms/graphAtoms"; @@ -73,24 +79,24 @@ export const GraphControls: React.FC = ({ )}
{!isHidden && ( <> )} diff --git a/src/views/graph/ReactForceGraph.tsx b/src/views/graph/ReactForceGraph.tsx index 4b2014b..afcf2aa 100644 --- a/src/views/graph/ReactForceGraph.tsx +++ b/src/views/graph/ReactForceGraph.tsx @@ -87,7 +87,7 @@ export const ReactForceGraph: React.FC = ({ // directional forces depending on if a screen is vertical or horizontal useEffect(() => { const fg = fgRef.current; - // TODO optimize: change forces only if dimensions changed a lot + // TODO optimize: change forces only if dimensions changed many times if (fg) { console.debug("Setting Graph forces"); // fg.d3Force("link", d3.forceLink() @@ -262,9 +262,6 @@ export const ReactForceGraph: React.FC = ({ } else { const nodeDuplicates = new Set(); newLinks = links.filter((l) => { - // const exists = graphData.links.find((existingLink) => { - // return Link.compare(l, existingLink); - // }); const exists = graphData.links.find((existingLink) => { return ( (existingLink.source.idx === expandedNode.idx || @@ -303,40 +300,12 @@ export const ReactForceGraph: React.FC = ({ return false; } return !nodeDuplicates.has(n.path); - // const isItParentOfExpanded = n.links.find((l) => l.target.path === expandedNode.path); - // if (isItParentOfExpanded) { - // return !graphData.nodes.find((existingNode) => { - // const exists = Node.compare(n, existingNode); - // // const hasLink = exists && existingNode.links.find((l) => l.tar) - // if (exists) { - // links.forEach((l) => { - // if (l.source.path === existingNode.path) { - // l.source = existingNode; - // } else if (l.target.path === existingNode.path) { - // l.target = existingNode; - // } - // }); - // existingNode.links = [ - // ...existingNode.links, - // ...n.links.filter( - // (l) => - // !existingNode.links.find( - // (existingLink) => - // l.source.path === existingLink.source.path && - // l.target.path === existingLink.target.path - // ) - // ), - // ]; - // } - // return exists; - // }); - // } - // return true; }); } graphData.nodes.sort((a, b) => (a.zIndex || 0) - (b.zIndex || 0)); + //// debug code // const simplifiedNodes = [...graphData.nodes, ...newNodes].map(node => ({ // path: node.path, // links: node.links.map((link) => link.source.path + "->" + link.target.path), @@ -364,7 +333,6 @@ export const ReactForceGraph: React.FC = ({ graphData.nodes.find((n) => n.path === selectedNode.selectedPath) as Node, node ); - // console.info(`HISTORY: ${JSON.stringify(newHistory)}`); setNavHistory(newHistory); if (selectedNode.selectedIndex !== undefined) { const newIndexHistory = stackOnIndexHistory( diff --git a/styles.css b/styles.css index 10f7e20..afc0094 100644 --- a/styles.css +++ b/styles.css @@ -51,4 +51,3 @@ border-radius: 3px; box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } -/*# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsic3JjL3ZpZXdzL2dyYXBoL0dyYXBoQ29udHJvbHMuY3NzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyIucG0tZ3JhcGgtY29udHJvbHMge1xuICBwb3NpdGlvbjogYWJzb2x1dGU7XG4gIHRvcDogNTBweDtcbiAgcmlnaHQ6IDEwcHg7XG4gIGRpc3BsYXk6IGdyaWQ7XG4gIGdyaWQtdGVtcGxhdGUtY29sdW1uczogMWZyIDFmcjsgLyogMiBjb2x1bW5zICovXG4gIGdhcDogNXB4O1xuICBiYWNrZ3JvdW5kOiByZ2JhKDI1NSwgMjU1LCAyNTUsIDAuOCk7IC8qIFNlbWktdHJhbnNwYXJlbnQgYmFja2dyb3VuZCAqL1xuICBwYWRkaW5nOiA1cHg7XG4gIGJvcmRlci1yYWRpdXM6IDVweDtcbiAgYm94LXNoYWRvdzogMCAycHggNHB4IHJnYmEoMCwgMCwgMCwgMC4yKTtcbn1cblxuLnBtLWdyYXBoLWNvbnRyb2xzIGJ1dHRvbiB7XG4gIGRpc3BsYXk6IGZsZXg7XG4gIGFsaWduLWl0ZW1zOiBjZW50ZXI7XG4gIGp1c3RpZnktY29udGVudDogY2VudGVyO1xuICBiYWNrZ3JvdW5kOiB2YXIoLS1jb2xvci1iYXNlLTQwKTtcbiAgYm9yZGVyOiBub25lO1xuICBib3JkZXItcmFkaXVzOiA0cHg7XG4gIGN1cnNvcjogcG9pbnRlcjtcbiAgZm9udC1zaXplOiAxNnB4O1xufVxuXG4ucG0tZ3JhcGgtY29udHJvbHMgYnV0dG9uOmhvdmVyIHtcbiAgYmFja2dyb3VuZDogdmFyKC0tY29sb3ItYmFzZS02MCk7XG59XG5cbi5zbGlkZXItY29udHJvbCB7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgdG9wOiA1MHB4O1xuICBsZWZ0OiAxMHB4O1xuICBkaXNwbGF5OiBncmlkO1xuICBncmlkLXRlbXBsYXRlLWNvbHVtbnM6IDFmcjtcbiAgZ2FwOiA1cHg7XG4gIGJhY2tncm91bmQ6IHJnYmEoMjU1LCAyNTUsIDI1NSwgMC44KTsgLyogU2VtaS10cmFuc3BhcmVudCBiYWNrZ3JvdW5kICovXG4gIHBhZGRpbmc6IDVweDtcbiAgYm9yZGVyLXJhZGl1czogNXB4O1xuICBib3gtc2hhZG93OiAwIDJweCA0cHggcmdiYSgwLCAwLCAwLCAwLjIpO1xuICBjb2xvcjogdmFyKC0tY29sb3ItYmFzZS0wMCk7XG59XG5cbi5zbGlkZXItY29udHJvbCBpbnB1dFt0eXBlPVwicmFuZ2VcIl0ge1xuICB3aWR0aDogMTAwcHg7XG4gIGN1cnNvcjogcG9pbnRlcjtcbn1cblxuLnNsaWRlci12YWx1ZSB7XG4gIHBvc2l0aW9uOiBhYnNvbHV0ZTtcbiAgdG9wOiAtMjBweDsgLyogQWJvdmUgdGhlIHNsaWRlciAqL1xuICBsZWZ0OiA1MCU7XG4gIHRyYW5zZm9ybTogdHJhbnNsYXRlWCgtNTAlKTtcbiAgZm9udC1zaXplOiAxMnB4O1xuICBwYWRkaW5nOiAycHggNXB4O1xuICBib3JkZXItcmFkaXVzOiAzcHg7XG4gIGJveC1zaGFkb3c6IDAgMXB4IDJweCByZ2JhKDAsIDAsIDAsIDAuMSk7XG59XG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQUE7QUFDRTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUFBO0FBR0Y7QUFDRTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQUE7QUFHRjtBQUNFO0FBQUE7QUFHRjtBQUNFO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFBQTtBQUdGO0FBQ0U7QUFDQTtBQUFBO0FBR0Y7QUFDRTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQUE7IiwKICAibmFtZXMiOiBbXQp9Cg== */