Compare commits

..

39 commits

Author SHA1 Message Date
Kritagya Bhattarai
4d5d1538c9
Merge pull request #8 from CalfMoon/dependabot/npm_and_yarn/esbuild-0.28.1
chore(deps-dev): bump esbuild from 0.25.5 to 0.28.1
2026-06-16 17:17:39 +05:45
dependabot[bot]
b3dfbb089b
chore(deps-dev): bump esbuild from 0.25.5 to 0.28.1
Bumps [esbuild](https://github.com/evanw/esbuild) from 0.25.5 to 0.28.1.
- [Release notes](https://github.com/evanw/esbuild/releases)
- [Changelog](https://github.com/evanw/esbuild/blob/main/CHANGELOG-2025.md)
- [Commits](https://github.com/evanw/esbuild/compare/v0.25.5...v0.28.1)

---
updated-dependencies:
- dependency-name: esbuild
  dependency-version: 0.28.1
  dependency-type: direct:development
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-06-16 11:29:59 +00:00
Kritagya Bhattarai (CalfMoon)
99cb882ff3 Merge branch 'modularize' 2026-03-31 11:47:39 +05:45
Kritagya Bhattarai (CalfMoon)
aa5f5cb5e5 fix: recalculate size when manual size is deleted 2026-03-31 11:26:08 +05:45
Kritagya Bhattarai (CalfMoon)
dbdf7d2a90 fix(calc): add assignment of value after calculations 2026-03-31 11:22:04 +05:45
Kritagya Bhattarai (CalfMoon)
8ba7f85b55 update README 2026-03-31 01:10:39 +05:45
Kritagya Bhattarai (CalfMoon)
ca82866158 chore: release 3.0.0 2026-03-31 00:53:39 +05:45
Kritagya Bhattarai (CalfMoon)
a36f1c9ed0 refactor: move calculation functions to new class 2026-03-31 00:42:56 +05:45
Kritagya Bhattarai (CalfMoon)
cd5f4c97c9 refactor: modularize settings file 2026-03-31 00:38:57 +05:45
Kritagya Bhattarai (CalfMoon)
f23e337178 fix: logical error 2026-03-30 22:25:14 +05:45
Kritagya Bhattarai (CalfMoon)
9d2b9769c4 refactor: remove some linting errors 2026-03-29 22:00:24 +05:45
Kritagya Bhattarai (CalfMoon)
d02a58be62 Merge branch 'working' 2026-03-29 21:42:59 +05:45
Kritagya Bhattarai (CalfMoon)
43ea5c276a feat(ui): add option to remove manually added weights 2026-03-29 21:28:05 +05:45
Kritagya Bhattarai (CalfMoon)
3c56ee6e74 feat: don't allow addition of duplicates in manual addition 2026-03-29 21:20:56 +05:45
Kritagya Bhattarai (CalfMoon)
e0279c3845 doc: add Manually Set Weight to Configuration Option in README 2026-03-29 20:19:03 +05:45
Kritagya Bhattarai (CalfMoon)
d212d1f57f feat(calc): read manually added weights 2026-03-29 19:52:52 +05:45
Kritagya Bhattarai (CalfMoon)
fd824ea406 feat(ui): add option to manually add weight 2026-03-29 19:06:55 +05:45
Kritagya Bhattarai (CalfMoon)
a213c8b3c5 refactor: fix linting mistakes 2026-03-28 21:53:47 +05:45
Kritagya Bhattarai (CalfMoon)
aa0987082e chore: update template from obsidianmd/obsidian-sample-plugin 2026-03-28 21:05:58 +05:45
Kritagya Bhattarai (CalfMoon)
b43c8dcafc chore: update dependencies 2026-03-28 20:35:13 +05:45
Kritagya Bhattarai (CalfMoon)
2810b9379c chore: release 2.1.0 2026-03-28 20:07:59 +05:45
Kritagya Bhattarai (CalfMoon)
41636dd29c Merge branch 'working' 2026-03-28 18:32:51 +05:45
Kritagya Bhattarai (CalfMoon)
783607292d perf(update): prevent graph update from running multiple times at once 2026-03-28 18:28:06 +05:45
Kritagya Bhattarai (CalfMoon)
777de23d9a refactor: rename clearSizeCache() -> recalulateSize()
this represents what the function is doing better
2026-03-28 18:15:07 +05:45
Kritagya Bhattarai (CalfMoon)
a9b433ba2a perf(calc): don't run calculations if graph view isn't loaded 2026-03-28 18:14:21 +05:45
Kritagya Bhattarai (CalfMoon)
b319d722fd fix(calc): calculation error associated with treeOptimizeMap 2026-03-28 17:59:23 +05:45
Kritagya Bhattarai (CalfMoon)
7d19749492 refactor: remove looping as it had no practical function 2026-03-28 17:52:32 +05:45
Kritagya Bhattarai (CalfMoon)
bc0f7fdcc0 Add release workflow 2026-03-28 17:05:36 +05:45
Kritagya Bhattarai (CalfMoon)
cdbc614b2f chore: release 2.0.0 2026-03-28 13:19:30 +05:45
Kritagya Bhattarai (CalfMoon)
6db8409e6e refactor: write cleaner code and comments 2026-03-28 13:07:22 +05:45
Kritagya Bhattarai (CalfMoon)
d3663e9de8 fix: size being 0 if graph view is initially open (#2)
this basically fixes the issue by increasing the time it takes for the
update cycle to run. Therefore giving obsidian more time to the link
between nodes.
2026-03-28 01:33:55 +05:45
Kritagya Bhattarai (CalfMoon)
70b51e9a3d doc: make README clearer(#3) 2026-03-27 22:39:05 +05:45
Kritagya Bhattarai (CalfMoon)
90f45a0df2 fix: random size after timelapse
timelapse caused the order in array to be changed which messed up the
cache, no longer a problem with hashmap
2026-03-27 22:31:17 +05:45
Kritagya Bhattarai (CalfMoon)
bd933fbd68 fix(calc): fix looping error with travel forward tree
Previously a fix was only implemented for when A->B->C->A now it also
works for A->B->C->D->B
2026-03-27 22:24:17 +05:45
Kritagya Bhattarai (CalfMoon)
c5739de84c fix: update graph on settings change (#4) 2026-03-27 18:50:09 +05:45
Kritagya Bhattarai
b23deebdf5
Merge pull request #1 from visua1hue/fix-typo
Fixed typos in settings
2025-04-02 17:19:35 +05:45
MikĒ
713d54aeda Fixed typos in settings panel 2025-04-01 20:40:21 +02:00
Kritagya Bhattarai (CalfMoon)
7886168d88 fix: change according to suggestion obsidianmd/obsidian-releases#5678 2025-03-28 17:26:50 +05:45
Kritagya Bhattarai (CalfMoon)
11c2d86cf8 refactor: rename Sample to NodeFactor 2025-03-13 11:56:27 +05:45
22 changed files with 4116 additions and 1006 deletions

View file

@ -1,3 +0,0 @@
node_modules/
main.js

View file

@ -1,23 +0,0 @@
{
"root": true,
"parser": "@typescript-eslint/parser",
"env": { "node": true },
"plugins": [
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended"
],
"parserOptions": {
"sourceType": "module"
},
"rules": {
"no-unused-vars": "off",
"@typescript-eslint/no-unused-vars": ["error", { "args": "none" }],
"@typescript-eslint/ban-ts-comment": "off",
"no-prototype-builtins": "off",
"@typescript-eslint/no-empty-function": "off"
}
}

28
.github/workflows/lint.yml vendored Normal file
View file

@ -0,0 +1,28 @@
name: Node.js build
on:
push:
branches: ["**"]
pull_request:
branches: ["**"]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20.x, 22.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: "npm"
- run: npm ci
- run: npm run build --if-present
- run: npm run lint

35
.github/workflows/release.yaml vendored Normal file
View file

@ -0,0 +1,35 @@
name: Release Obsidian plugin
on:
push:
tags:
- "*"
jobs:
build:
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: "18.x"
- name: Build plugin
run: |
npm install
npm run build
- name: Create release
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
tag="${GITHUB_REF#refs/tags/}"
gh release create "$tag" \
--title="$tag" \
--draft \
main.js manifest.json

22
LICENSE
View file

@ -1,5 +1,21 @@
Copyright (C) 2020-2025 by Dynalist Inc.
MIT License
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
Copyright (c) 2021 CalfMoon
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,8 +1,15 @@
# Obsidian Node Factor
# Node Factor
This plugin helps you customize the factors that effect node size in obsidian graph view.
The factors include, letter count, forward & backward weight, and forward tree weight.
The factors include:
- Letter count in a file.
- Weight of links coming forward and/or backward out of a file.
- Whether we should count every descendant coming forward out of a file or just the children.
- You can manually add size for certain files too.
## Demo
<img src="./screenshots/1.jpeg">
<img src="./screenshots/2.jpeg">
## Installation
1. Search for "Node Factor" in Obsidian's community plugin browser.
2. Install it.
@ -10,13 +17,17 @@ The factors include, letter count, forward & backward weight, and forward tree w
## Configuration Options
| Option | Meaning | Default value |
| ------------------------------- | ------------------------------------------------------------------------------------------------------- | ------------- |
| Forward link weight multiplier | Weight of single link coming out of a node | 1 |
| Forward link weight multiplier | Weight of single link coming forward out of a node | 1 |
| Travel forward tree | Determines if forward weight is decided by just the immediate nodes coming out or everything after it | off |
| Backward link weight multiplier | Weight of single link coming out of a node | 1 |
| Backward link weight multiplier | Weight of single link coming backward out of a node | 1 |
| Character per weight | Given number of letters give 1 weight | 0 |
| Manually Set Weight | Manually set weight of any file, weight set here overrides everything else | empty set |
## Author
CalfMoon: [Email](kritagyabhattarai@proton.me), [Github](https://github.com/CalfMoon)
## Bug Reports
This project still new and might have a few bugs. For bug reports use this repo's Issues section.
This project is still new and might have a few bugs. For bug reports use this repo's Issues section.
## License
OBSD, see [LICENSE](LICENSE).
MIT, see [LICENSE](LICENSE).

View file

@ -1,14 +1,15 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";
import { builtinModules } from 'node:module';
const banner = `/*
const banner =
`/*
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
if you want to view the source, please visit the github repository of this plugin
*/
`;
const prod = process.argv[2] === "production";
const prod = (process.argv[2] === "production");
const context = await esbuild.context({
banner: {
@ -30,8 +31,7 @@ const context = await esbuild.context({
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins,
],
...builtinModules],
format: "cjs",
target: "es2018",
logLevel: "info",

34
eslint.config.mts Normal file
View file

@ -0,0 +1,34 @@
import tseslint from 'typescript-eslint';
import obsidianmd from "eslint-plugin-obsidianmd";
import globals from "globals";
import { globalIgnores } from "eslint/config";
export default tseslint.config(
{
languageOptions: {
globals: {
...globals.browser,
},
parserOptions: {
projectService: {
allowDefaultProject: [
'eslint.config.js',
'manifest.json'
]
},
tsconfigRootDir: import.meta.dirname,
extraFileExtensions: ['.json']
},
},
},
...obsidianmd.configs.recommended,
globalIgnores([
"node_modules",
"dist",
"esbuild.config.mjs",
"eslint.config.js",
"version-bump.mjs",
"versions.json",
"main.js",
]),
);

View file

@ -1,10 +1,10 @@
{
"id": "node-factor",
"name": "Node Factor",
"version": "1.0.0",
"version": "3.0.0",
"minAppVersion": "0.15.0",
"description": "Customize factors effecting node size in graph.",
"author": "CalfMoon",
"authorUrl": "https://github.com/CalfMoon",
"isDesktopOnly": false
}
}

4277
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,24 +1,30 @@
{
"name": "obsidian-node-factor",
"version": "1.0.0",
"name": "node-factor",
"version": "3.0.0",
"description": "Customize factors effecting node size in graph.",
"main": "main.js",
"scripts": {
"dev": "node esbuild.config.mjs",
"build": "tsc -noEmit -skipLibCheck && node esbuild.config.mjs production",
"version": "node version-bump.mjs && git add manifest.json versions.json"
"version": "node version-bump.mjs && git add manifest.json versions.json",
"lint": "eslint ."
},
"keywords": [],
"author": "",
"author": "CalfMoon",
"license": "MIT",
"devDependencies": {
"@eslint/js": "9.30.1",
"@types/node": "^16.11.6",
"@typescript-eslint/eslint-plugin": "5.29.0",
"@typescript-eslint/parser": "5.29.0",
"builtin-modules": "3.3.0",
"esbuild": "0.17.3",
"obsidian": "latest",
"esbuild": "0.28.1",
"eslint": "^9.39.4",
"eslint-plugin-obsidianmd": "0.1.9",
"globals": "^14.0.0",
"jiti": "2.6.1",
"tslib": "2.4.0",
"typescript": "4.7.4"
"typescript": "^5.8.3",
"typescript-eslint": "^8.35.1"
},
"dependencies": {
"obsidian": "latest"
}
}

67
src/calculator.ts Normal file
View file

@ -0,0 +1,67 @@
import { App } from "obsidian";
import { NodeFactorSettings, ObsidianNode } from "types";
export default class Calculations {
private app: App;
private settings: NodeFactorSettings;
constructor(app: App, settings: NodeFactorSettings) {
this.app = app;
this.settings = settings;
}
calcNodeWeight(node: ObsidianNode): number {
const settings = this.settings;
let weight = 0;
const manualFileData = settings.manual.find(
(manualFileData) => manualFileData.id == node.id,
);
if (manualFileData != null) {
return manualFileData.weight;
}
weight += Object.keys(node.reverse).length * settings.bwdMultiplier;
if (settings.fwdTree) {
weight +=
this.fwdNodeTreeSize(node, new Set()) * settings.fwdMultiplier;
} else {
weight += Object.keys(node.forward).length * settings.fwdMultiplier;
}
if (settings.lettersPerWt != 0) {
weight += this.letterCount(node) / settings.lettersPerWt;
}
return Math.round(weight);
}
private letterCount(node: ObsidianNode): number {
const file = this.app.vault.getFileByPath(node.id);
if (file == null || file.extension != "md") return 0;
return file.stat.size;
}
private fwdNodeTreeSize(
node: ObsidianNode,
antiLoopSet: Set<string>,
): number {
let size = 0;
antiLoopSet.add(node.id);
Object.entries(node.forward).forEach(([key, value]) => {
// @ts-ignore
const childNode: ObsidianNode = value.target;
// Prevents looping if A -> B -> C -> D -> B
if (!antiLoopSet.has(key)) {
size++;
const childSize = this.fwdNodeTreeSize(childNode, antiLoopSet);
size += childSize;
}
});
return size;
}
}

View file

@ -1,131 +1,80 @@
import { Plugin } from "obsidian";
import { ObsidianNode, NodeFactorSettings, DEFAULT_SETTINGS } from "./types";
import SampleSettingTab from "./settings";
import { ObsidianNode, NodeFactorSettings, DEFAULT_SETTINGS } from "types";
import NodeFactorSettingTab from "settings";
import Calculator from "calculator";
export default class NodeFactor extends Plugin {
settings: NodeFactorSettings;
// stops loop when graph isn't open
private updateLoop: boolean;
// This is a hash map that helps in specifically optimizing the forward tree
private treeOptimizeMap: Map<string, number> = new Map();
// This array helps in optimizing the whole calculation process
private storedSized: Array<number> = [];
async onload() {
await this.loadSettings();
this.addSettingTab(new SampleSettingTab(this.app, this));
this.addSettingTab(new NodeFactorSettingTab(this.app, this));
this.registerEvent(
this.app.workspace.on("layout-change", () => {
const leaf = this.app.workspace
.getLeavesOfType("graph")
.first();
if (!leaf) {
this.updateLoop = false;
return;
}
this.updateLoop = true;
// @ts-ignore
const nodes: ObsidianNode[] = leaf.view.renderer.nodes;
if (nodes.length === 0) return;
this.calcLoop(nodes);
}),
this.app.workspace.on("active-leaf-change", () =>
this.updateGraph(),
),
);
this.registerEvent(
this.app.workspace.on("layout-change", () => this.updateGraph()),
);
// clear cache when there is change in the vault
this.registerEvent(
this.app.vault.on("create", () => (this.storedSized = [])),
);
this.registerEvent(
this.app.vault.on("modify", () => (this.storedSized = [])),
);
this.registerEvent(
this.app.vault.on("delete", () => (this.storedSized = [])),
);
this.registerEvent(
this.app.vault.on("rename", () => (this.storedSized = [])),
);
}
async onunload() {
this.updateLoop = false;
}
private calcLoop(nodes: ObsidianNode[]) {
setTimeout(() => {
this.updateNodes(nodes);
if (this.updateLoop) this.calcLoop(nodes);
}, 10);
}
private updateNodes(nodes: ObsidianNode[]) {
this.treeOptimizeMap.clear();
nodes.forEach((node, i) => {
let weight: number;
if (this.storedSized[i] != undefined) {
weight = this.storedSized[i];
} else {
weight = this.calcNodeWeight(node);
this.storedSized[i] = weight;
}
node.weight = weight;
this.app.workspace.onLayoutReady(() => {
// layout ready is required because obsidian calls
// create for every existing file when initally loading obsidian
// https://docs.obsidian.md/plugins/guides/load-time#Pitfalls
this.registerEvent(
this.app.vault.on("create", () => this.recalculateSize()),
);
});
this.registerEvent(
this.app.vault.on("modify", () => this.recalculateSize()),
);
this.registerEvent(
this.app.vault.on("delete", () => this.recalculateSize()),
);
this.registerEvent(
this.app.vault.on("rename", () => this.recalculateSize()),
);
}
private calcNodeWeight(node: ObsidianNode): number {
const settings = this.settings;
let weight = 0;
onunload() {}
weight += Object.keys(node.reverse).length * settings.bwdMultiplier;
if (settings.fwdTree) {
weight +=
this.fwdNodeTreeSize(node, node.id) * settings.fwdMultiplier;
} else {
weight += Object.keys(node.forward).length * settings.fwdMultiplier;
}
private sizeCache: Map<string, number> = new Map();
private timeoutId: NodeJS.Timeout;
private updateGraph() {
const leaf = this.app.workspace.getLeavesOfType("graph").first();
// don't run if graph page isn't loaded
if (!leaf) return;
if (settings.lettersPerWt != 0) {
weight += this.letterCount(node) / settings.lettersPerWt;
}
// @ts-ignore
const nodes: Array<ObsidianNode> = leaf.view.renderer.nodes;
if (nodes.length === 0) return;
return Math.round(weight);
// Slight delay in calculations is needed to fix node size
// if graph view is initially opened when opening obsidian
clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(() => {
const calculator = new Calculator(this.app, this.settings);
nodes.forEach((node, _i) => {
let weight = 0;
if (this.sizeCache.get(node.id) != undefined) {
weight = this.sizeCache.get(node.id) as number;
} else {
weight = calculator.calcNodeWeight(node);
this.sizeCache.set(node.id, weight);
}
node.weight = weight;
});
}, 500);
}
private letterCount(node: ObsidianNode): number {
const file = this.app.vault.getFileByPath(node.id);
if (!file || file.extension != "md") return 0;
return file.stat.size;
}
private fwdNodeTreeSize(node: ObsidianNode, id: string): number {
let size = 0;
const sizeMap = this.treeOptimizeMap.get(node.id);
if (sizeMap !== undefined) {
return sizeMap as number;
}
Object.entries(node.forward).forEach(([key, value]) => {
// @ts-ignore
const childNode: ObsidianNode = value.target;
// Prevents looping if A -> B -> C -> A
if (key === id) return size;
size++;
const childSize = this.fwdNodeTreeSize(childNode, id);
size += childSize;
});
this.treeOptimizeMap.set(node.id, size);
return size;
recalculateSize() {
this.sizeCache.clear();
this.updateGraph();
}
async loadSettings() {

View file

@ -1,73 +0,0 @@
import { App, PluginSettingTab, Setting } from "obsidian";
import NodeFactor from "./main";
export default class SampleSettingTab extends PluginSettingTab {
plugin: NodeFactor;
constructor(app: App, plugin: NodeFactor) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const { containerEl } = this;
containerEl.empty();
new Setting(containerEl)
.setName("Forward link weight multiplier")
.setDesc("Multiplier for forward links weight (0 to diable).")
.addSlider((slider) =>
slider
.setLimits(0, 20, 1)
.setDynamicTooltip()
.setValue(this.plugin.settings.fwdMultiplier)
.onChange(async (value) => {
this.plugin.settings.fwdMultiplier = value;
await this.plugin.saveSettings();
}),
);
new Setting(containerEl)
.setName("Travel forward tree")
.setDesc("Travel forward tree to calcuate node size.")
.addToggle((toggle) =>
toggle
.setValue(this.plugin.settings.fwdTree)
.onChange(async (value) => {
this.plugin.settings.fwdTree = value;
await this.plugin.saveSettings();
}),
);
new Setting(containerEl)
.setName("Backward link weight multiplier")
.setDesc("Multiplier for backward links weight (0 to diable).")
.addSlider((slider) =>
slider
.setLimits(0, 20, 1)
.setDynamicTooltip()
.setValue(this.plugin.settings.bwdMultiplier)
.onChange(async (value) => {
this.plugin.settings.bwdMultiplier = value;
await this.plugin.saveSettings();
}),
);
new Setting(containerEl)
.setName("Character per weight")
.setDesc(
"Add 1 weight to node size per no of given Character (0 to disable).",
)
.addSlider((slider) =>
slider
.setLimits(0, 5000, 100)
.setDynamicTooltip()
.setValue(this.plugin.settings.lettersPerWt)
.onChange(async (value) => {
this.plugin.settings.lettersPerWt = value;
await this.plugin.saveSettings();
}),
);
}
}

View file

@ -0,0 +1,32 @@
import { App, AbstractInputSuggest } from "obsidian";
export default class FileSuggest extends AbstractInputSuggest<string> {
private files: Array<string>;
private inputEl: HTMLInputElement;
constructor(app: App, inputEl: HTMLInputElement) {
super(app, inputEl);
this.inputEl = inputEl;
this.files = this.app.vault.getFiles().map((file) => file.path);
}
getSuggestions(inputStr: string): string[] {
const inputLower = inputStr.toLowerCase();
return this.files.filter((file) =>
file.toLowerCase().includes(inputLower),
);
}
renderSuggestion(folder: string, el: HTMLElement): void {
el.setText(folder);
}
selectSuggestion(folder: string): void {
this.inputEl.value = folder;
const event = new Event("input");
this.inputEl.dispatchEvent(event);
this.close();
}
}

26
src/settings/index.ts Normal file
View file

@ -0,0 +1,26 @@
import { App, PluginSettingTab } from "obsidian";
import NodeFactor from "main";
import ManualSetting from "./manual";
import ProgrameticalSetting from "./programetical";
export default class NodeFactorSettingTab extends PluginSettingTab {
private plugin: NodeFactor;
constructor(app: App, plugin: NodeFactor) {
super(app, plugin);
this.plugin = plugin;
}
display(): void {
const { plugin, containerEl, app } = this;
containerEl.empty();
new ProgrameticalSetting(plugin, containerEl).display();
new ManualSetting(app, plugin, containerEl, () =>
this.display(),
).display();
}
}

115
src/settings/manual.ts Normal file
View file

@ -0,0 +1,115 @@
import {
App,
ButtonComponent,
Notice,
SearchComponent,
Setting,
SliderComponent,
} from "obsidian";
import NodeFactor from "main";
import { FileData } from "types";
import FileSuggest from "./file-suggest";
export default class ManualSetting {
private app: App;
private plugin: NodeFactor;
private containerEl: HTMLElement;
private refreshDisplay: () => void;
constructor(
app: App,
plugin: NodeFactor,
containerEl: HTMLElement,
refreshDisplay: () => void,
) {
this.app = app;
this.plugin = plugin;
this.containerEl = containerEl;
this.refreshDisplay = refreshDisplay;
}
display(): void {
new Setting(this.containerEl).setName("Manually set size").setHeading();
let selectedWeight: SliderComponent;
let selectedFile: SearchComponent;
let submitButton: ButtonComponent;
const addWt = new Setting(this.containerEl)
.setName("Add new weight")
.setDesc("Weight added here overrides everything else.");
addWt.addSearch((search) => {
new FileSuggest(this.app, search.inputEl);
selectedFile = search;
search.setPlaceholder("Enter file name");
search.onChange(async (value) => {
submitButton.setDisabled(value.length === 0);
});
});
addWt.addSlider((slider) => {
selectedWeight = slider;
slider.setLimits(0, 100, 5);
slider.setDynamicTooltip();
slider.setValue(0);
});
addWt.addButton((button) => {
submitButton = button;
button.setDisabled(true);
button.setButtonText("Add");
button.setTooltip("Click to add new manual size");
button.onClick(async () => {
const enteredFileData: FileData = {
id: selectedFile.getValue(),
weight: selectedWeight.getValue(),
};
selectedWeight.setValue(0);
selectedFile.setValue("");
const fileExists = this.plugin.settings.manual.find(
(foundFile) => foundFile.id == enteredFileData.id,
);
if (fileExists != undefined) {
new Notice(
"That file's size has already been entered," +
" remove it first to change weight.",
);
return;
}
this.plugin.settings.manual.push(enteredFileData);
await this.plugin.saveSettings();
new Notice("New size added manually");
this.refreshDisplay();
this.plugin.recalculateSize();
});
});
// Display all manually added weights & option to remove them
this.plugin.settings.manual.forEach((value: FileData) => {
const manualDisplay = new Setting(this.containerEl)
.setName(value.id)
.setDesc(`Weight: ${String(value.weight)}`);
manualDisplay.addButton((button) => {
button.setIcon("trash");
button.setTooltip("Remove manually added size");
button.onClick(async () => {
this.plugin.settings.manual.remove(value);
await this.plugin.saveSettings();
this.refreshDisplay();
this.plugin.recalculateSize();
});
});
});
}
}

View file

@ -0,0 +1,85 @@
import { Setting } from "obsidian";
import NodeFactor from "main";
export default class ProgrameticalSetting {
private plugin: NodeFactor;
private containerEl: HTMLElement;
constructor(plugin: NodeFactor, containerEl: HTMLElement) {
this.plugin = plugin;
this.containerEl = containerEl;
}
display(): void {
new Setting(this.containerEl)
.setName("Programatically set size")
.setHeading();
const fwdLinkWt = new Setting(this.containerEl)
.setName("Forward link weight multiplier")
.setDesc("Multiplier for forward links weight (0 to disable).");
fwdLinkWt.addSlider((slider) => {
slider.setLimits(0, 20, 1);
slider.setDynamicTooltip();
slider.setValue(this.plugin.settings.fwdMultiplier);
slider.onChange(async (value) => {
this.plugin.settings.fwdMultiplier = value;
await this.plugin.saveSettings();
this.plugin.recalculateSize();
});
});
const travelFwdTree = new Setting(this.containerEl)
.setName("Travel forward tree")
.setDesc(
"Travel forward and add all other nodes to determine final node size.",
);
travelFwdTree.addToggle((toggle) => {
toggle.setValue(this.plugin.settings.fwdTree);
toggle.onChange(async (value) => {
this.plugin.settings.fwdTree = value;
await this.plugin.saveSettings();
this.plugin.recalculateSize();
});
});
const bwdLinkWt = new Setting(this.containerEl)
.setName("Backward link weight multiplier")
.setDesc("Multiplier for backward links weight (0 to disable).");
bwdLinkWt.addSlider((slider) => {
slider.setLimits(0, 20, 1);
slider.setDynamicTooltip();
slider.setValue(this.plugin.settings.bwdMultiplier);
slider.onChange(async (value) => {
this.plugin.settings.bwdMultiplier = value;
await this.plugin.saveSettings();
this.plugin.recalculateSize();
});
});
const charWt = new Setting(this.containerEl)
.setName("Character per weight")
.setDesc(
"Add 1 weight to node size per no of given character (0 to disable).",
);
charWt.addSlider((slider) => {
slider.setLimits(0, 5000, 100);
slider.setDynamicTooltip();
slider.setValue(this.plugin.settings.lettersPerWt);
slider.onChange(async (value) => {
this.plugin.settings.lettersPerWt = value;
await this.plugin.saveSettings();
this.plugin.recalculateSize();
});
});
}
}

View file

@ -12,6 +12,13 @@ export interface NodeFactorSettings {
bwdMultiplier: number;
lettersPerWt: number;
manual: Array<FileData>;
}
export interface FileData {
id: string;
weight: number;
}
export const DEFAULT_SETTINGS: NodeFactorSettings = {
@ -21,4 +28,6 @@ export const DEFAULT_SETTINGS: NodeFactorSettings = {
bwdMultiplier: 1,
lettersPerWt: 0,
manual: new Array<FileData>(),
};

View file

@ -1,24 +1,30 @@
{
"compilerOptions": {
"baseUrl": ".",
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"target": "ES6",
"allowJs": true,
"noImplicitAny": true,
"moduleResolution": "node",
"importHelpers": true,
"isolatedModules": true,
"strictNullChecks": true,
"lib": [
"DOM",
"ES5",
"ES6",
"ES7"
]
},
"include": [
"**/*.ts"
]
"compilerOptions": {
"baseUrl": "src",
"inlineSourceMap": true,
"inlineSources": true,
"module": "ESNext",
"target": "ES6",
"allowJs": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noImplicitReturns": true,
"moduleResolution": "node",
"importHelpers": true,
"noUncheckedIndexedAccess": true,
"isolatedModules": true,
"strictNullChecks": true,
"strictBindCallApply": true,
"allowSyntheticDefaultImports": true,
"useUnknownInCatchVariables": true,
"lib": [
"DOM",
"ES5",
"ES6",
"ES7"
]
},
"include": [
"src/**/*.ts"
]
}

View file

@ -3,12 +3,15 @@ import { readFileSync, writeFileSync } from "fs";
const targetVersion = process.env.npm_package_version;
// read minAppVersion from manifest.json and bump version to target version
let manifest = JSON.parse(readFileSync("manifest.json", "utf8"));
const manifest = JSON.parse(readFileSync("manifest.json", "utf8"));
const { minAppVersion } = manifest;
manifest.version = targetVersion;
writeFileSync("manifest.json", JSON.stringify(manifest, null, "\t"));
// update versions.json with target version and minAppVersion from manifest.json
let versions = JSON.parse(readFileSync("versions.json", "utf8"));
versions[targetVersion] = minAppVersion;
writeFileSync("versions.json", JSON.stringify(versions, null, "\t"));
// but only if the target version is not already in versions.json
const versions = JSON.parse(readFileSync('versions.json', 'utf8'));
if (!Object.values(versions).includes(minAppVersion)) {
versions[targetVersion] = minAppVersion;
writeFileSync('versions.json', JSON.stringify(versions, null, '\t'));
}

View file

@ -1,3 +1,5 @@
{
"1.0.0": "0.15.0"
}
"1.0.0": "0.15.0",
"2.0.0": "0.15.0",
"2.1.0": "0.15.0"
}