Skip to content

Commit

Permalink
Merge pull request #6 from saberzero1/custom-filepath
Browse files Browse the repository at this point in the history
Custom File Path Feature Addition
  • Loading branch information
helloworldkr authored Feb 12, 2024
2 parents e0f589c + 3313fe4 commit e76742e
Show file tree
Hide file tree
Showing 7 changed files with 318 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
node_modules
package-lock.json
main.js
48 changes: 48 additions & 0 deletions esbuild.config.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import esbuild from "esbuild";
import process from "process";
import builtins from "builtin-modules";

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 context = await esbuild.context({
banner: {
js: banner,
},
entryPoints: ["main.ts"],
bundle: true,
external: [
"obsidian",
"electron",
"@codemirror/autocomplete",
"@codemirror/collab",
"@codemirror/commands",
"@codemirror/language",
"@codemirror/lint",
"@codemirror/search",
"@codemirror/state",
"@codemirror/view",
"@lezer/common",
"@lezer/highlight",
"@lezer/lr",
...builtins],
format: "cjs",
target: "es2018",
logLevel: "info",
sourcemap: prod ? false : "inline",
treeShaking: true,
outfile: "main.js",
});

if (prod) {
await context.rebuild();
process.exit(0);
} else {
await context.watch();
}
28 changes: 24 additions & 4 deletions main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,20 @@ import {
TAbstractFile
} from 'obsidian';

import { FolderSuggest } from "./src/FolderSuggester";

import AdmZip from 'adm-zip';

// Remember to rename these classes and interfaces!

interface AskifyPluginSettings {
AskifySyncKeySetting: string;
AskifyLocalFilePathSetting: string;
}

const ASKIFY_DEFAULT_SETTINGS: AskifyPluginSettings = {
AskifySyncKeySetting: 'default'
AskifySyncKeySetting: 'default',
AskifyLocalFilePathSetting: 'Askify'
}

async function unzipFile(filePath, destPath) {
Expand Down Expand Up @@ -84,14 +88,14 @@ export default class AskifyPlugin extends Plugin {

// Step 3: create a folder of Askify
try {
if (!(this.app.vault.getAbstractFileByPath('Askify') instanceof TFolder)) {
await vault.createFolder('Askify')
if (!(this.app.vault.getAbstractFileByPath(askifySyncVal.AskifyLocalFilePathSetting) instanceof TFolder)) {
await vault.createFolder(askifySyncVal.AskifyLocalFilePathSetting)
}
} catch (e) {
console.log("error in creating the folder")
console.log(e);
}
let unzip_folder = folderPath + '/Askify/'
let unzip_folder = folderPath + '/' + askifySyncVal.AskifyLocalFilePathSetting + '/'

// Step 4: unzip file in the Askify folder
await unzipFile(zipFilePath, unzip_folder);
Expand Down Expand Up @@ -209,5 +213,21 @@ class AskifySettingTab extends PluginSettingTab {
this.plugin.settings.AskifySyncKeySetting = value;
await this.plugin.saveSettings();
}));

new Setting(containerEl)
.setName("Askify Local Folder Path")
.setDesc("Enter the folder path where you want to sync the notes")

.addSearch((text) => {
new FolderSuggest(text.inputEl);
text.setPlaceholder("Example: Inbox/Askify")
.setValue(this.plugin.settings.AskifyLocalFilePathSetting)

.onChange(async (value) => {

this.plugin.settings.AskifyLocalFilePathSetting = value;
await this.plugin.saveSettings();
});
});
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
"typescript": "^4.7.4"
},
"dependencies": {
"@popperjs/core": "^2.11.8",
"adm-zip": "^0.5.10",
"build": "^0.1.4"
}
Expand Down
33 changes: 33 additions & 0 deletions src/FolderSuggester.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes

import { TAbstractFile, TFolder } from "obsidian";
import { TextInputSuggest } from "./suggest";

export class FolderSuggest extends TextInputSuggest<TFolder> {
getSuggestions(inputStr: string): TFolder[] {
const abstractFiles = app.vault.getAllLoadedFiles();
const folders: TFolder[] = [];
const lowerCaseInputStr = inputStr.toLowerCase();

abstractFiles.forEach((folder: TAbstractFile) => {
if (
folder instanceof TFolder &&
folder.path.toLowerCase().contains(lowerCaseInputStr)
) {
folders.push(folder);
}
});

return folders;
}

renderSuggestion(file: TFolder, el: HTMLElement): void {
el.setText(file.path);
}

selectSuggestion(file: TFolder): void {
this.inputEl.value = file.path;
this.inputEl.trigger("input");
this.close();
}
}
200 changes: 200 additions & 0 deletions src/suggest.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
// Credits go to Liam's Periodic Notes Plugin: https://github.com/liamcain/obsidian-periodic-notes

import { ISuggestOwner, Scope } from "obsidian";
import { createPopper, Instance as PopperInstance } from "@popperjs/core";

const wrapAround = (value: number, size: number): number => {
return ((value % size) + size) % size;
};

class Suggest<T> {
private owner: ISuggestOwner<T>;
private values: T[];
private suggestions: HTMLDivElement[];
private selectedItem: number;
private containerEl: HTMLElement;

constructor(
owner: ISuggestOwner<T>,
containerEl: HTMLElement,
scope: Scope
) {
this.owner = owner;
this.containerEl = containerEl;

containerEl.on(
"click",
".suggestion-item",
this.onSuggestionClick.bind(this)
);
containerEl.on(
"mousemove",
".suggestion-item",
this.onSuggestionMouseover.bind(this)
);

scope.register([], "ArrowUp", (event) => {
if (!event.isComposing) {
this.setSelectedItem(this.selectedItem - 1, true);
return false;
}
});

scope.register([], "ArrowDown", (event) => {
if (!event.isComposing) {
this.setSelectedItem(this.selectedItem + 1, true);
return false;
}
});

scope.register([], "Enter", (event) => {
if (!event.isComposing) {
this.useSelectedItem(event);
return false;
}
});
}

onSuggestionClick(event: MouseEvent, el: HTMLDivElement): void {
event.preventDefault();

const item = this.suggestions.indexOf(el);
this.setSelectedItem(item, false);
this.useSelectedItem(event);
}

onSuggestionMouseover(_event: MouseEvent, el: HTMLDivElement): void {
const item = this.suggestions.indexOf(el);
this.setSelectedItem(item, false);
}

setSuggestions(values: T[]) {
this.containerEl.empty();
const suggestionEls: HTMLDivElement[] = [];

values.forEach((value) => {
const suggestionEl = this.containerEl.createDiv("suggestion-item");
this.owner.renderSuggestion(value, suggestionEl);
suggestionEls.push(suggestionEl);
});

this.values = values;
this.suggestions = suggestionEls;
this.setSelectedItem(0, false);
}

useSelectedItem(event: MouseEvent | KeyboardEvent) {
const currentValue = this.values[this.selectedItem];
if (currentValue) {
this.owner.selectSuggestion(currentValue, event);
}
}

setSelectedItem(selectedIndex: number, scrollIntoView: boolean) {
const normalizedIndex = wrapAround(
selectedIndex,
this.suggestions.length
);
const prevSelectedSuggestion = this.suggestions[this.selectedItem];
const selectedSuggestion = this.suggestions[normalizedIndex];

prevSelectedSuggestion?.removeClass("is-selected");
selectedSuggestion?.addClass("is-selected");

this.selectedItem = normalizedIndex;

if (scrollIntoView) {
selectedSuggestion.scrollIntoView(false);
}
}
}

export abstract class TextInputSuggest<T> implements ISuggestOwner<T> {
protected inputEl: HTMLInputElement | HTMLTextAreaElement;

private popper: PopperInstance;
private scope: Scope;
private suggestEl: HTMLElement;
private suggest: Suggest<T>;

constructor(inputEl: HTMLInputElement | HTMLTextAreaElement) {
this.inputEl = inputEl;
this.scope = new Scope();

this.suggestEl = createDiv("suggestion-container");
const suggestion = this.suggestEl.createDiv("suggestion");
this.suggest = new Suggest(this, suggestion, this.scope);

this.scope.register([], "Escape", this.close.bind(this));

this.inputEl.addEventListener("input", this.onInputChanged.bind(this));
this.inputEl.addEventListener("focus", this.onInputChanged.bind(this));
this.inputEl.addEventListener("blur", this.close.bind(this));
this.suggestEl.on(
"mousedown",
".suggestion-container",
(event: MouseEvent) => {
event.preventDefault();
}
);
}

onInputChanged(): void {
const inputStr = this.inputEl.value;
const suggestions = this.getSuggestions(inputStr);

if (!suggestions) {
this.close();
return;
}

if (suggestions.length > 0) {
this.suggest.setSuggestions(suggestions);
this.open(app.dom.appContainerEl, this.inputEl);
} else {
this.close();
}
}

open(container: HTMLElement, inputEl: HTMLElement): void {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
app.keymap.pushScope(this.scope);

container.appendChild(this.suggestEl);
this.popper = createPopper(inputEl, this.suggestEl, {
placement: "bottom-start",
modifiers: [
{
name: "sameWidth",
enabled: true,
fn: ({ state, instance }) => {
// Note: positioning needs to be calculated twice -
// first pass - positioning it according to the width of the popper
// second pass - position it with the width bound to the reference element
// we need to early exit to avoid an infinite loop
const targetWidth = `${state.rects.reference.width}px`;
if (state.styles.popper.width === targetWidth) {
return;
}
state.styles.popper.width = targetWidth;
instance.update();
},
phase: "beforeWrite",
requires: ["computeStyles"],
},
],
});
}

close(): void {
app.keymap.popScope(this.scope);

this.suggest.setSuggestions([]);
if (this.popper) this.popper.destroy();
this.suggestEl.detach();
}

abstract getSuggestions(inputStr: string): T[];
abstract renderSuggestion(item: T, el: HTMLElement): void;
abstract selectSuggestion(item: T): void;
}
9 changes: 9 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
declare module "obsidian" {
interface App {
dom: {
appContainerEl: HTMLElement;
};
}
}

export {};

0 comments on commit e76742e

Please sign in to comment.