mirror of
https://github.com/mnaoumov/obsidian-backlink-full-path.git
synced 2026-07-22 05:41:39 +00:00
Generate from template
This commit is contained in:
commit
fe6dccadf6
30 changed files with 11982 additions and 0 deletions
11
.editorconfig
Normal file
11
.editorconfig
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
# top-most EditorConfig file
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
insert_final_newline = true
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
tab_width = 2
|
||||
trim_trailing_whitespace = true
|
||||
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
* text=auto eol=lf
|
||||
15
.github/FUNDING.yml
vendored
Normal file
15
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: # Replace with a single Ko-fi username
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
thanks_dev: # Replace with a single thanks.dev username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
71
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
71
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
name: Bug report
|
||||
description: Report a bug and help improve the plugin
|
||||
title: "[BUG] Short description of the bug"
|
||||
labels: bug
|
||||
assignees: mnaoumov
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Bug report
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: A clear and concise description of the bug. Include any relevant details.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps to Reproduce
|
||||
description: Provide a step-by-step description.
|
||||
value: |
|
||||
1. Go to '...'
|
||||
2. Click on '...'
|
||||
3. Notice that '...'
|
||||
...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: What did you expect to happen?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Actual Behavior
|
||||
description: What actually happened? Include error messages if available.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Environment Information
|
||||
description: Environment Information
|
||||
value: |
|
||||
- **Plugin Version**: [e.g., 1.0.0]
|
||||
- **Obsidian Version**: [e.g., v1.3.2]
|
||||
- **Operating System**: [e.g., Windows 10]
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Attachments
|
||||
description: Required for bug reproduction
|
||||
value: |
|
||||
- Please attach a video showing the bug. It is not mandatory, but might be very helpful to speed up the bug fix
|
||||
- Please attach a sample vault where the bug can be reproduced. It is not mandatory, but might be very helpful to speed up the bug fix
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Confirmations
|
||||
description: Ensure the following conditions are met
|
||||
options:
|
||||
- label: I attached a video showing the bug, or it is not necessary
|
||||
required: true
|
||||
- label: I attached a sample vault where the bug can be reproduced, or it is not necessary
|
||||
required: true
|
||||
- label: I have tested the bug with the latest version of the plugin
|
||||
required: true
|
||||
- label: I have checked GitHub for existing bugs
|
||||
required: true
|
||||
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
1
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
blank_issues_enabled: false
|
||||
61
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
61
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
name: Feature request
|
||||
description: Request a feature and help improve the plugin
|
||||
title: "[FR] Short description of the feature"
|
||||
labels: enhancement
|
||||
assignees: mnaoumov
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
## Feature Request
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Description
|
||||
description: A clear and concise description of the feature request. Include any relevant details.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Details
|
||||
description: Provide a step-by-step description.
|
||||
value: |
|
||||
1. Go to '...'
|
||||
2. Click on '...'
|
||||
3. Notice that '...'
|
||||
...
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Desired Behavior
|
||||
description: What do you want to happen?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current Behavior
|
||||
description: What actually happens?
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Attachments
|
||||
description: Required for feature investigation
|
||||
value: |
|
||||
- Please attach a video showing the current behavior. It is not mandatory, but might be very helpful to speed up the feature implementation
|
||||
- Please attach a sample vault where the desired Feature Request could be applied. It is not mandatory, but might be very helpful to speed up the feature implementation
|
||||
validations:
|
||||
required: true
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Confirmations
|
||||
description: Ensure the following conditions are met
|
||||
options:
|
||||
- label: I attached a video showing the current behavior, or it is not necessary
|
||||
required: true
|
||||
- label: I attached a sample vault where the desired Feature Request could be applied, or it is not necessary
|
||||
required: true
|
||||
- label: I have tested the absence of the requested feature with the latest version of the plugin
|
||||
required: true
|
||||
- label: I have checked GitHub for existing Feature Requests
|
||||
required: true
|
||||
26
.gitignore
vendored
Normal file
26
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
# vscode
|
||||
.vscode
|
||||
|
||||
# Intellij
|
||||
*.iml
|
||||
.idea
|
||||
|
||||
# npm
|
||||
node_modules
|
||||
|
||||
# Don't include the compiled main.js file in the repo.
|
||||
# They should be uploaded to GitHub releases instead.
|
||||
main.js
|
||||
|
||||
# Exclude sourcemaps
|
||||
*.map
|
||||
|
||||
# obsidian
|
||||
data.json
|
||||
|
||||
# Exclude macOS Finder (System Explorer) View States
|
||||
.DS_Store
|
||||
|
||||
dist
|
||||
.env
|
||||
/tsconfig.tsbuildinfo
|
||||
1
.npmrc
Normal file
1
.npmrc
Normal file
|
|
@ -0,0 +1 @@
|
|||
tag-version-prefix=""
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025 Michael Naumov
|
||||
|
||||
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.
|
||||
28
README.md
Normal file
28
README.md
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Backlink Full Path
|
||||
|
||||
This is a plugin for [Obsidian](https://obsidian.md/) that Shows the backlink's full path in the backlinks panel.
|
||||
|
||||
## Installation
|
||||
|
||||
- The plugin is not available in [the official Community Plugins repository](https://obsidian.md/plugins) yet.
|
||||
- [Beta releases](obsidian://brat?plugin=https://github.com/mnaoumov/obsidian-backlink-full-path) can be installed via [BRAT](https://obsidian.md/plugins?id=obsidian42-brat).
|
||||
|
||||
## Debugging
|
||||
|
||||
By default, debug messages for this plugin are hidden.
|
||||
|
||||
To show them, run the following command:
|
||||
|
||||
```js
|
||||
window.DEBUG.enable('backlink-full-path');
|
||||
```
|
||||
|
||||
For more details, refer to the [documentation](https://github.com/mnaoumov/obsidian-dev-utils?tab=readme-ov-file#debugging).
|
||||
|
||||
## Support
|
||||
|
||||
<a href="https://www.buymeacoffee.com/mnaoumov" target="_blank">Support this plugin</a>
|
||||
|
||||
## License
|
||||
|
||||
© [Michael Naumov](https://github.com/mnaoumov/)
|
||||
17
cspell.json
Normal file
17
cspell.json
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"version": "0.2",
|
||||
"ignorePaths": [
|
||||
"dist",
|
||||
"node_modules",
|
||||
"tsconfig.tsbuildinfo"
|
||||
],
|
||||
"dictionaryDefinitions": [],
|
||||
"dictionaries": [],
|
||||
"words": [
|
||||
"dblclick",
|
||||
"tsbuildinfo"
|
||||
],
|
||||
"ignoreWords": [],
|
||||
"import": [],
|
||||
"enabled": true
|
||||
}
|
||||
1
eslint.config.mjs
Normal file
1
eslint.config.mjs
Normal file
|
|
@ -0,0 +1 @@
|
|||
export { configs as default } from 'obsidian-dev-utils/ScriptUtils/ESLint/eslint.config';
|
||||
10
manifest.json
Normal file
10
manifest.json
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"author": "mnaoumov",
|
||||
"authorUrl": "https://github.com/mnaoumov/",
|
||||
"description": "Shows the backlink's full path in the backlinks panel.",
|
||||
"fundingUrl": "https://www.buymeacoffee.com/mnaoumov",
|
||||
"id": "backlink-full-path",
|
||||
"isDesktopOnly": false,
|
||||
"name": "Backlink Full Path",
|
||||
"version": "0.0.0"
|
||||
}
|
||||
10864
package-lock.json
generated
Normal file
10864
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
38
package.json
Normal file
38
package.json
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "backlink-full-path",
|
||||
"version": "0.0.0",
|
||||
"description": "Shows the backlink's full path in the backlinks panel.",
|
||||
"keywords": [],
|
||||
"license": "MIT",
|
||||
"author": "mnaoumov",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "obsidian-dev-utils build",
|
||||
"build:clean": "obsidian-dev-utils build:clean",
|
||||
"build:compile": "obsidian-dev-utils build:compile",
|
||||
"build:compile:svelte": "obsidian-dev-utils build:compile:svelte",
|
||||
"build:compile:typescript": "obsidian-dev-utils build:compile:typescript",
|
||||
"dev": "obsidian-dev-utils dev",
|
||||
"format": "obsidian-dev-utils format",
|
||||
"format:check": "obsidian-dev-utils format:check",
|
||||
"lint": "obsidian-dev-utils lint",
|
||||
"lint:fix": "obsidian-dev-utils lint:fix",
|
||||
"spellcheck": "obsidian-dev-utils spellcheck",
|
||||
"version": "obsidian-dev-utils version"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@codemirror/language": "^6.10.8",
|
||||
"@codemirror/state": "^6.5.2",
|
||||
"@codemirror/view": "^6.36.4",
|
||||
"@tsconfig/strictest": "^2.0.5",
|
||||
"@types/node": "^22.13.10",
|
||||
"moment": "^2.30.1",
|
||||
"obsidian": "^1.8.7",
|
||||
"obsidian-dev-utils": "^19.8.2",
|
||||
"obsidian-typings": "^2.35.0",
|
||||
"svelte": "^5.22.6"
|
||||
},
|
||||
"overrides": {
|
||||
"boolean": "npm:dry-uninstall"
|
||||
}
|
||||
}
|
||||
251
src/BacklinkFullPathPlugin.ts
Normal file
251
src/BacklinkFullPathPlugin.ts
Normal file
|
|
@ -0,0 +1,251 @@
|
|||
import type {
|
||||
Editor,
|
||||
MarkdownPostProcessorContext,
|
||||
ObsidianProtocolData,
|
||||
TAbstractFile,
|
||||
WorkspaceLeaf
|
||||
} from 'obsidian';
|
||||
|
||||
import {
|
||||
MarkdownView,
|
||||
Notice,
|
||||
PluginSettingTab
|
||||
} from 'obsidian';
|
||||
import { convertAsyncToSync } from 'obsidian-dev-utils/Async';
|
||||
import { getDebugger } from 'obsidian-dev-utils/Debug';
|
||||
import { alert } from 'obsidian-dev-utils/obsidian/Modals/Alert';
|
||||
import { confirm } from 'obsidian-dev-utils/obsidian/Modals/Confirm';
|
||||
import { prompt } from 'obsidian-dev-utils/obsidian/Modals/Prompt';
|
||||
import { selectItem } from 'obsidian-dev-utils/obsidian/Modals/SelectItem';
|
||||
import { PluginBase } from 'obsidian-dev-utils/obsidian/Plugin/PluginBase';
|
||||
|
||||
import { BacklinkFullPathPluginSettings } from './BacklinkFullPathPluginSettings.ts';
|
||||
import { BacklinkFullPathPluginSettingsTab } from './BacklinkFullPathPluginSettingsTab.ts';
|
||||
import { sampleStateField } from './EditorExtensions/SampleStateField.ts';
|
||||
import { sampleViewPlugin } from './EditorExtensions/SampleViewPlugin.ts';
|
||||
import { SampleEditorSuggest } from './EditorSuggests/SampleEditorSuggest.ts';
|
||||
import { SampleModal } from './Modals/SampleModal.ts';
|
||||
import {
|
||||
SAMPLE_VIEW_TYPE,
|
||||
SampleView
|
||||
} from './Views/SampleView.ts';
|
||||
|
||||
export class BacklinkFullPathPlugin extends PluginBase<BacklinkFullPathPluginSettings> {
|
||||
protected override createPluginSettings(data: unknown): BacklinkFullPathPluginSettings {
|
||||
return new BacklinkFullPathPluginSettings(data);
|
||||
}
|
||||
|
||||
protected override createPluginSettingsTab(): null | PluginSettingTab {
|
||||
return new BacklinkFullPathPluginSettingsTab(this);
|
||||
}
|
||||
|
||||
protected override async onLayoutReady(): Promise<void> {
|
||||
new Notice('This is executed after all plugins are loaded');
|
||||
await this.openSampleView();
|
||||
}
|
||||
|
||||
protected override onloadComplete(): void {
|
||||
this.addCommand({
|
||||
callback: this.runSampleCommand.bind(this),
|
||||
id: 'sample-command',
|
||||
name: 'Sample command'
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
editorCallback: this.runSampleEditorCommand.bind(this),
|
||||
id: 'sample-editor-command',
|
||||
name: 'Sample editor command'
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
checkCallback: this.runSampleCommandWithCheck.bind(this),
|
||||
id: 'sample-command-with-check',
|
||||
name: 'Sample command with check'
|
||||
});
|
||||
|
||||
this.addRibbonIcon('dice', 'Sample ribbon icon', this.runSampleRibbonIconCommand.bind(this));
|
||||
|
||||
this.addStatusBarItem().setText('Sample status bar item');
|
||||
|
||||
this.registerDomEvent(document, 'dblclick', this.handleSampleDomEvent.bind(this));
|
||||
|
||||
this.registerEditorExtension([sampleViewPlugin, sampleStateField]);
|
||||
|
||||
this.registerEditorSuggest(new SampleEditorSuggest(this.app));
|
||||
|
||||
this.registerEvent(this.app.vault.on('create', this.handleSampleEvent.bind(this)));
|
||||
|
||||
this.registerExtensions(['sample-extension-1', 'sample-extension-2'], SAMPLE_VIEW_TYPE);
|
||||
|
||||
this.registerHoverLinkSource(SAMPLE_VIEW_TYPE, {
|
||||
defaultMod: true,
|
||||
display: this.manifest.name
|
||||
});
|
||||
|
||||
const INTERVAL_IN_MILLISECONDS = 60_000;
|
||||
this.registerInterval(window.setInterval(this.handleSampleIntervalTick.bind(this), INTERVAL_IN_MILLISECONDS));
|
||||
|
||||
this.registerMarkdownCodeBlockProcessor('sample-code-block-processor', this.handleSampleCodeBlockProcessor.bind(this));
|
||||
|
||||
this.registerMarkdownPostProcessor(this.handleSampleMarkdownPostProcessor.bind(this));
|
||||
|
||||
this.registerObsidianProtocolHandler('sample-action', this.handleSampleObsidianProtocolHandler.bind(this));
|
||||
|
||||
this.registerView(SAMPLE_VIEW_TYPE, (leaf) => new SampleView(leaf));
|
||||
|
||||
this.registerModalCommands();
|
||||
}
|
||||
|
||||
private handleSampleCodeBlockProcessor(source: string, el: HTMLElement, ctx: MarkdownPostProcessorContext): void {
|
||||
getDebugger('handleSampleCodeBlockProcessor')(source, el, ctx);
|
||||
el.setText('Sample code block processor');
|
||||
}
|
||||
|
||||
private handleSampleDomEvent(evt: MouseEvent): void {
|
||||
const tagName = evt.target instanceof HTMLElement ? evt.target.tagName : '';
|
||||
new Notice(`Sample DOM event: ${tagName}`);
|
||||
}
|
||||
|
||||
private handleSampleEvent(file: TAbstractFile): void {
|
||||
if (!this.app.workspace.layoutReady) {
|
||||
return;
|
||||
}
|
||||
|
||||
new Notice(`Sample event: ${file.name}`);
|
||||
}
|
||||
|
||||
private handleSampleIntervalTick(): void {
|
||||
new Notice('Sample interval tick');
|
||||
}
|
||||
|
||||
private handleSampleMarkdownPostProcessor(el: HTMLElement, ctx: MarkdownPostProcessorContext): void {
|
||||
getDebugger('handleSampleMarkdownPostProcessor')(el, ctx);
|
||||
if (el.hasClass('el-h6')) {
|
||||
el.setText('Sample markdown post processor');
|
||||
}
|
||||
}
|
||||
|
||||
private handleSampleObsidianProtocolHandler(params: ObsidianProtocolData): void {
|
||||
new Notice(`Sample obsidian protocol handler: ${params.action}`);
|
||||
}
|
||||
|
||||
private async openSampleView(): Promise<void> {
|
||||
let leaf: null | WorkspaceLeaf;
|
||||
const leaves = this.app.workspace.getLeavesOfType(SAMPLE_VIEW_TYPE);
|
||||
|
||||
if (leaves.length > 0) {
|
||||
leaf = leaves[0] ?? null;
|
||||
} else {
|
||||
leaf = this.app.workspace.getRightLeaf(false);
|
||||
await leaf?.setViewState({ active: true, type: SAMPLE_VIEW_TYPE });
|
||||
}
|
||||
|
||||
if (leaf) {
|
||||
await this.app.workspace.revealLeaf(leaf);
|
||||
}
|
||||
}
|
||||
|
||||
private registerModalCommands(): void {
|
||||
this.addCommand({
|
||||
callback: this.showSampleModal.bind(this),
|
||||
id: 'show-sample-modal',
|
||||
name: 'Show Sample Modal'
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
callback: convertAsyncToSync(this.showAlert.bind(this)),
|
||||
id: 'show-alert-modal',
|
||||
name: 'Show Alert Modal'
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
callback: convertAsyncToSync(this.showConfirm.bind(this)),
|
||||
id: 'show-confirm-modal',
|
||||
name: 'Show Confirm Modal'
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
callback: convertAsyncToSync(this.showPrompt.bind(this)),
|
||||
id: 'show-prompt-modal',
|
||||
name: 'Show Prompt Modal'
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
callback: convertAsyncToSync(this.showSelectItem.bind(this)),
|
||||
id: 'show-select-item-modal',
|
||||
name: 'Show Select Item Modal'
|
||||
});
|
||||
}
|
||||
|
||||
private runSampleCommand(): void {
|
||||
new Notice('Sample command');
|
||||
}
|
||||
|
||||
private runSampleCommandWithCheck(checking: boolean): boolean {
|
||||
const markdownView = this.app.workspace.getActiveViewOfType(MarkdownView);
|
||||
if (!markdownView) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!checking) {
|
||||
new Notice('Sample command with check');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private runSampleEditorCommand(editor: Editor): void {
|
||||
editor.replaceSelection('Sample Editor Command');
|
||||
}
|
||||
|
||||
private runSampleRibbonIconCommand(): void {
|
||||
new Notice('Sample ribbon icon command');
|
||||
}
|
||||
|
||||
private async showAlert(): Promise<void> {
|
||||
await alert({
|
||||
app: this.app,
|
||||
message: 'Sample alert message',
|
||||
title: 'Sample alert title'
|
||||
});
|
||||
}
|
||||
|
||||
private async showConfirm(): Promise<void> {
|
||||
const result = await confirm({
|
||||
app: this.app,
|
||||
message: 'Sample confirm message',
|
||||
title: 'Sample confirm title'
|
||||
});
|
||||
|
||||
new Notice(`Sample confirm result: ${result.toString()}`);
|
||||
}
|
||||
|
||||
private async showPrompt(): Promise<void> {
|
||||
await prompt({
|
||||
app: this.app,
|
||||
defaultValue: 'Sample prompt default value',
|
||||
placeholder: 'Sample prompt placeholder',
|
||||
title: 'Sample prompt title',
|
||||
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
||||
valueValidator: (value): string | void => {
|
||||
const MIN_LENGTH = 30;
|
||||
if (value.length < MIN_LENGTH) {
|
||||
return `Value must be at least ${MIN_LENGTH.toString()} characters long`;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private showSampleModal(): void {
|
||||
new SampleModal(this.app).open();
|
||||
}
|
||||
|
||||
private async showSelectItem(): Promise<void> {
|
||||
await selectItem({
|
||||
app: this.app,
|
||||
items: ['Item 1', 'Item 2', 'Item 3'],
|
||||
itemTextFunc: (item) => item,
|
||||
placeholder: 'Sample select item placeholder'
|
||||
});
|
||||
}
|
||||
}
|
||||
45
src/BacklinkFullPathPluginSettings.ts
Normal file
45
src/BacklinkFullPathPluginSettings.ts
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
import type { Duration } from 'moment';
|
||||
import type { IsoMonth } from 'obsidian-dev-utils/obsidian/Components/MonthComponent';
|
||||
import type { IsoWeek } from 'obsidian-dev-utils/obsidian/Components/WeekComponent';
|
||||
|
||||
import { duration } from 'moment';
|
||||
import { PluginSettingsBase } from 'obsidian-dev-utils/obsidian/Plugin/PluginSettingsBase';
|
||||
|
||||
export class BacklinkFullPathPluginSettings extends PluginSettingsBase {
|
||||
/* eslint-disable no-magic-numbers */
|
||||
public colorSetting = '#123456';
|
||||
public dateSetting = new Date();
|
||||
public dateTimeSetting = new Date();
|
||||
public dropdownSetting = 'Value2';
|
||||
public emailSetting = 'defaultEmail@example.com';
|
||||
public momentFormatSetting = 'YYYY-MM-DD';
|
||||
public monthSetting: IsoMonth = {
|
||||
month: 2,
|
||||
year: 2025
|
||||
};
|
||||
|
||||
public multipleDropdownSetting: string[] = ['Value2', 'Value5'];
|
||||
public multipleEmailSetting: string[] = ['defaultEmail@example.com', 'defaultEmail2@example.com'];
|
||||
public multipleTextSetting: string[] = ['defaultText1', 'defaultText2', 'defaultText3'];
|
||||
public numberSetting = 123;
|
||||
public progressBarSetting = 50;
|
||||
public searchSetting = 'defaultSearch';
|
||||
public sliderSetting = 50;
|
||||
public textAreaSetting = 'defaultTextArea';
|
||||
public textSetting = 'defaultText';
|
||||
public timeSetting: Duration = duration({ hours: 12, minutes: 34 });
|
||||
public toggleSetting = true;
|
||||
public urlSetting = 'https://example.com';
|
||||
|
||||
public weekSetting: IsoWeek = {
|
||||
weekNumber: 6,
|
||||
year: 2025
|
||||
};
|
||||
|
||||
/* eslint-enable no-magic-numbers */
|
||||
|
||||
public constructor(data: unknown) {
|
||||
super();
|
||||
this.init(data);
|
||||
}
|
||||
}
|
||||
222
src/BacklinkFullPathPluginSettingsTab.ts
Normal file
222
src/BacklinkFullPathPluginSettingsTab.ts
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
import { PluginSettingsTabBase } from 'obsidian-dev-utils/obsidian/Plugin/PluginSettingsTabBase';
|
||||
import { SettingEx } from 'obsidian-dev-utils/obsidian/SettingEx';
|
||||
|
||||
import type { BacklinkFullPathPlugin } from './BacklinkFullPathPlugin.ts';
|
||||
|
||||
export class BacklinkFullPathPluginSettingsTab extends PluginSettingsTabBase<BacklinkFullPathPlugin> {
|
||||
public override display(): void {
|
||||
this.containerEl.empty();
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Button Setting Name')
|
||||
.setDesc('Button Setting Description.')
|
||||
.addButton((button) => {
|
||||
button.setButtonText('Button Text')
|
||||
.onClick(() => {
|
||||
new Notice('Button clicked');
|
||||
});
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Color Setting Name')
|
||||
.setDesc('Color Setting Description.')
|
||||
.addColorPicker((color) => {
|
||||
this.bind(color, 'colorSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Date Setting Name')
|
||||
.setDesc('Date Setting Description.')
|
||||
.addDate((date) => {
|
||||
this.bind(date, 'dateSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('DateTime Setting Name')
|
||||
.setDesc('DateTime Setting Description.')
|
||||
.addDateTime((dateTime) => {
|
||||
this.bind(dateTime, 'dateTimeSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Dropdown Setting Name')
|
||||
.setDesc('Dropdown Setting Description.')
|
||||
.addDropdown((dropdown) => {
|
||||
dropdown.addOptions({
|
||||
Value1: 'Display 1',
|
||||
Value2: 'Display 2',
|
||||
Value3: 'Display 3'
|
||||
});
|
||||
this.bind(dropdown, 'dropdownSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Email Setting Name')
|
||||
.setDesc('Email Setting Description.')
|
||||
.addEmail((email) => {
|
||||
this.bind(email, 'emailSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Extra Button Setting Name')
|
||||
.setDesc('Extra Button Setting Description.')
|
||||
.addExtraButton((extraButton) => {
|
||||
extraButton
|
||||
.onClick(() => {
|
||||
new Notice('Extra Button clicked');
|
||||
});
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('File Setting Name')
|
||||
.setDesc('File Setting Description.')
|
||||
.addFile((file) => {
|
||||
file.onChange((value) => {
|
||||
new Notice(`File selected: ${value?.name ?? '(None)'}`);
|
||||
});
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Moment Format Setting Name')
|
||||
.setDesc('Moment Format Setting Description.')
|
||||
.addMomentFormat((momentFormat) => {
|
||||
this.bind(momentFormat, 'momentFormatSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Month Setting Name')
|
||||
.setDesc('Month Setting Description.')
|
||||
.addMonth((month) => {
|
||||
this.bind(month, 'monthSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Multiple Dropdown Setting Name')
|
||||
.setDesc('Multiple Dropdown Setting Description.')
|
||||
.addMultipleDropdown((multipleDropdown) => {
|
||||
multipleDropdown.addOptions({
|
||||
Value1: 'Display 1',
|
||||
Value2: 'Display 2',
|
||||
Value3: 'Display 3',
|
||||
Value4: 'Display 4',
|
||||
Value5: 'Display 5'
|
||||
});
|
||||
|
||||
this.bind(multipleDropdown, 'multipleDropdownSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Multiple Email Setting Name')
|
||||
.setDesc('Multiple Email Setting Description.')
|
||||
.addMultipleEmail((multipleEmail) => {
|
||||
this.bind(multipleEmail, 'multipleEmailSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Multiple File Setting Name')
|
||||
.setDesc('Multiple File Setting Description.')
|
||||
.addMultipleFile((multipleFile) => {
|
||||
multipleFile.onChange((value) => {
|
||||
const fileNames = value.map((file) => file.name);
|
||||
new Notice(`Files selected: ${fileNames.join(', ')}`);
|
||||
});
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Multiple Text Setting Name')
|
||||
.setDesc('Multiple Text Setting Description.')
|
||||
.addMultipleText((multipleText) => {
|
||||
this.bind(multipleText, 'multipleTextSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Number Setting Name')
|
||||
.setDesc('Number Setting Description.')
|
||||
.addNumber((number) => {
|
||||
this.bind(number, 'numberSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('ProgressBar Setting Name')
|
||||
.setDesc('ProgressBar Setting Description.')
|
||||
.addProgressBar((progressBar) => {
|
||||
progressBar.setValue(this.plugin.settings.progressBarSetting);
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Search Setting Name')
|
||||
.setDesc('Search Setting Description.')
|
||||
.addSearch((search) => {
|
||||
this.bind(search, 'searchSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Slider Setting Name')
|
||||
.setDesc('Slider Setting Description.')
|
||||
.addSlider((slider) => {
|
||||
this.bind(slider, 'sliderSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Text Setting Name')
|
||||
.setDesc('Text Setting Description.')
|
||||
.addText((text) => {
|
||||
this.bind(text, 'textSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Text Area Setting Name')
|
||||
.setDesc('Text Area Setting Description.')
|
||||
.addTextArea((textArea) => {
|
||||
this.bind(textArea, 'textAreaSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Time Setting Name')
|
||||
.setDesc('Time Setting Description.')
|
||||
.addTime((time) => {
|
||||
this.bind(time, 'timeSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Toggle Setting Name')
|
||||
.setDesc('Toggle Setting Description.')
|
||||
.addToggle((toggle) => {
|
||||
this.bind(toggle, 'toggleSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Url Setting Name')
|
||||
.setDesc('Url Setting Description.')
|
||||
.addUrl((url) => {
|
||||
this.bind(url, 'urlSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Week Setting Name')
|
||||
.setDesc('Week Setting Description.')
|
||||
.addWeek((week) => {
|
||||
this.bind(week, 'weekSetting');
|
||||
});
|
||||
|
||||
new SettingEx(this.containerEl)
|
||||
.setName('Advanced Text Setting Name')
|
||||
.setDesc('Advanced Text Setting Description.')
|
||||
.addText((text) => {
|
||||
this.bind(text, 'textSetting', {
|
||||
componentToPluginSettingsValueConverter: (uiValue: string) => uiValue.replace(' (converted)', ''),
|
||||
onChanged: () => {
|
||||
new Notice('Advanced Text Setting changed');
|
||||
},
|
||||
pluginSettingsToComponentValueConverter: (pluginSettingsValue: string) => `${pluginSettingsValue} (converted)`,
|
||||
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
|
||||
valueValidator(uiValue): string | void {
|
||||
if (uiValue === '') {
|
||||
return 'Value must be non-empty';
|
||||
}
|
||||
}
|
||||
})
|
||||
.setPlaceholder('Enter a value');
|
||||
});
|
||||
}
|
||||
}
|
||||
51
src/EditorExtensions/SampleStateField.ts
Normal file
51
src/EditorExtensions/SampleStateField.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import type { Extension } from '@codemirror/state';
|
||||
import type { DecorationSet } from '@codemirror/view';
|
||||
import type { StateFieldSpec } from 'obsidian-dev-utils/codemirror/StateFieldSpec';
|
||||
|
||||
import { syntaxTree } from '@codemirror/language';
|
||||
import {
|
||||
RangeSetBuilder,
|
||||
StateField,
|
||||
Transaction
|
||||
} from '@codemirror/state';
|
||||
import {
|
||||
Decoration,
|
||||
EditorView
|
||||
} from '@codemirror/view';
|
||||
|
||||
import { SampleWidget } from './SampleWidget.ts';
|
||||
|
||||
class SampleStateField implements StateFieldSpec<DecorationSet> {
|
||||
public create(): DecorationSet {
|
||||
return Decoration.none;
|
||||
}
|
||||
|
||||
public provide(field: StateField<DecorationSet>): Extension {
|
||||
return EditorView.decorations.from(field);
|
||||
}
|
||||
|
||||
public update(_oldState: DecorationSet, transaction: Transaction): DecorationSet {
|
||||
const OFFSET = 2;
|
||||
const builder = new RangeSetBuilder<Decoration>();
|
||||
|
||||
syntaxTree(transaction.state).iterate({
|
||||
enter(node) {
|
||||
if (node.type.name.startsWith('list')) {
|
||||
const listCharFrom = node.from - OFFSET;
|
||||
|
||||
builder.add(
|
||||
listCharFrom,
|
||||
listCharFrom + 1,
|
||||
Decoration.replace({
|
||||
widget: new SampleWidget()
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return builder.finish();
|
||||
}
|
||||
}
|
||||
|
||||
export const sampleStateField = StateField.define<DecorationSet>(new SampleStateField());
|
||||
66
src/EditorExtensions/SampleViewPlugin.ts
Normal file
66
src/EditorExtensions/SampleViewPlugin.ts
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
import type {
|
||||
DecorationSet,
|
||||
PluginSpec,
|
||||
PluginValue
|
||||
} from '@codemirror/view';
|
||||
|
||||
import { syntaxTree } from '@codemirror/language';
|
||||
import { RangeSetBuilder } from '@codemirror/state';
|
||||
import {
|
||||
Decoration,
|
||||
EditorView,
|
||||
ViewPlugin,
|
||||
ViewUpdate
|
||||
} from '@codemirror/view';
|
||||
|
||||
import { SampleWidget } from './SampleWidget.ts';
|
||||
|
||||
class SampleViewPlugin implements PluginValue {
|
||||
public decorations: DecorationSet;
|
||||
|
||||
public constructor(view: EditorView) {
|
||||
this.decorations = this.buildDecorations(view);
|
||||
}
|
||||
|
||||
public buildDecorations(view: EditorView): DecorationSet {
|
||||
const OFFSET = 2;
|
||||
const builder = new RangeSetBuilder<Decoration>();
|
||||
|
||||
for (const { from, to } of view.visibleRanges) {
|
||||
syntaxTree(view.state).iterate({
|
||||
enter(node) {
|
||||
if (node.type.name.startsWith('list')) {
|
||||
const listCharFrom = node.from - OFFSET;
|
||||
|
||||
builder.add(
|
||||
listCharFrom,
|
||||
listCharFrom + 1,
|
||||
Decoration.replace({
|
||||
widget: new SampleWidget()
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
from,
|
||||
to
|
||||
});
|
||||
}
|
||||
|
||||
return builder.finish();
|
||||
}
|
||||
|
||||
public update(update: ViewUpdate): void {
|
||||
if (update.docChanged || update.viewportChanged) {
|
||||
this.decorations = this.buildDecorations(update.view);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pluginSpec: PluginSpec<SampleViewPlugin> = {
|
||||
decorations: (value: SampleViewPlugin) => value.decorations
|
||||
};
|
||||
|
||||
export const sampleViewPlugin = ViewPlugin.fromClass(
|
||||
SampleViewPlugin,
|
||||
pluginSpec
|
||||
);
|
||||
7
src/EditorExtensions/SampleWidget.ts
Normal file
7
src/EditorExtensions/SampleWidget.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { WidgetType } from '@codemirror/view';
|
||||
|
||||
export class SampleWidget extends WidgetType {
|
||||
public toDOM(): HTMLElement {
|
||||
return createEl('span', { text: '👉' });
|
||||
}
|
||||
}
|
||||
44
src/EditorSuggests/SampleEditorSuggest.ts
Normal file
44
src/EditorSuggests/SampleEditorSuggest.ts
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
import type {
|
||||
Editor,
|
||||
EditorPosition,
|
||||
EditorSuggestContext,
|
||||
EditorSuggestTriggerInfo,
|
||||
TFile
|
||||
} from 'obsidian';
|
||||
|
||||
import { EditorSuggest } from 'obsidian';
|
||||
|
||||
export class SampleEditorSuggest extends EditorSuggest<string> {
|
||||
private cursor: EditorPosition | null = null;
|
||||
private editor: Editor | null = null;
|
||||
|
||||
public override getSuggestions(context: EditorSuggestContext): Promise<string[]> | string[] {
|
||||
return [`${context.query} 1`, `${context.query} 2`, `${context.query} 3`];
|
||||
}
|
||||
|
||||
public override onTrigger(cursor: EditorPosition, editor: Editor, file: null | TFile): EditorSuggestTriggerInfo | null {
|
||||
this.cursor = cursor;
|
||||
this.editor = editor;
|
||||
const line = editor.getLine(cursor.line);
|
||||
if (line !== 'Sample') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
end: cursor,
|
||||
query: `Query ${file?.name ?? ''}`,
|
||||
start: cursor
|
||||
};
|
||||
}
|
||||
|
||||
public override renderSuggestion(value: string, el: HTMLElement): void {
|
||||
el.createEl('strong', { text: value });
|
||||
}
|
||||
|
||||
public override selectSuggestion(value: string, evt: KeyboardEvent | MouseEvent): void {
|
||||
this.close();
|
||||
if (this.editor && this.cursor) {
|
||||
this.editor.replaceRange(`Transformed ${value} ${evt.type}`, this.cursor);
|
||||
}
|
||||
}
|
||||
}
|
||||
7
src/Modals/SampleModal.ts
Normal file
7
src/Modals/SampleModal.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { Modal } from 'obsidian';
|
||||
|
||||
export class SampleModal extends Modal {
|
||||
public override onOpen(): void {
|
||||
this.contentEl.setText('Sample Modal');
|
||||
}
|
||||
}
|
||||
7
src/SvelteComponents/SampleComponent.d.ts
vendored
Normal file
7
src/SvelteComponents/SampleComponent.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
export interface SampleComponentExports {
|
||||
increment(): void;
|
||||
}
|
||||
|
||||
export interface SampleComponentProps {
|
||||
startCount: number;
|
||||
}
|
||||
21
src/SvelteComponents/SampleComponent.svelte
Normal file
21
src/SvelteComponents/SampleComponent.svelte
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
<script lang="ts">
|
||||
import type { SampleComponentProps } from './SampleComponent.d.ts';
|
||||
|
||||
let { startCount }: SampleComponentProps = $props();
|
||||
|
||||
let count = $state(startCount);
|
||||
|
||||
export function increment() {
|
||||
count += 1;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="number">
|
||||
<span>My number is {count}!</span>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.number {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
47
src/Views/SampleView.ts
Normal file
47
src/Views/SampleView.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { ItemView } from 'obsidian';
|
||||
import {
|
||||
mount,
|
||||
unmount
|
||||
} from 'svelte';
|
||||
|
||||
import type {
|
||||
SampleComponentExports,
|
||||
SampleComponentProps
|
||||
} from '../SvelteComponents/SampleComponent.d.ts';
|
||||
|
||||
import SampleComponent from '../SvelteComponents/SampleComponent.svelte';
|
||||
|
||||
export const SAMPLE_VIEW_TYPE = 'backlink-full-path-sample-view';
|
||||
|
||||
export class SampleView extends ItemView {
|
||||
private sampleComponent: null | SampleComponentExports = null;
|
||||
|
||||
public override getDisplayText(): string {
|
||||
return 'Sample view';
|
||||
}
|
||||
|
||||
public override getViewType(): string {
|
||||
return SAMPLE_VIEW_TYPE;
|
||||
}
|
||||
|
||||
public override async onClose(): Promise<void> {
|
||||
if (this.sampleComponent) {
|
||||
await unmount(this.sampleComponent);
|
||||
}
|
||||
}
|
||||
|
||||
public override async onOpen(): Promise<void> {
|
||||
const START_COUNT = 10;
|
||||
const props: SampleComponentProps = {
|
||||
startCount: START_COUNT
|
||||
};
|
||||
|
||||
this.sampleComponent = mount(SampleComponent, {
|
||||
props,
|
||||
target: this.contentEl
|
||||
}) as SampleComponentExports;
|
||||
|
||||
this.sampleComponent.increment();
|
||||
await Promise.resolve();
|
||||
}
|
||||
}
|
||||
5
src/main.ts
Normal file
5
src/main.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import './styles/main.scss';
|
||||
import { BacklinkFullPathPlugin } from './BacklinkFullPathPlugin.ts';
|
||||
|
||||
// eslint-disable-next-line import-x/no-default-export
|
||||
export default BacklinkFullPathPlugin;
|
||||
9
src/styles/main.scss
Normal file
9
src/styles/main.scss
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/**
|
||||
* You can remove this file if you don't need any styles
|
||||
* See default library styles: https://github.com/mnaoumov/obsidian-dev-utils/blob/main/src/styles/main.scss
|
||||
* See available library css classes: https://github.com/mnaoumov/obsidian-dev-utils/blob/main/src/CssClass.ts
|
||||
*/
|
||||
|
||||
.backlink-full-path.obsidian-dev-utils {
|
||||
/* Override library styles */
|
||||
}
|
||||
33
tsconfig.json
Normal file
33
tsconfig.json
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
{
|
||||
"extends": "@tsconfig/strictest/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"allowArbitraryExtensions": true,
|
||||
"allowImportingTsExtensions": true,
|
||||
"allowJs": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"importHelpers": true,
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"lib": [
|
||||
"DOM",
|
||||
"ES2024"
|
||||
],
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"noEmit": true,
|
||||
"skipLibCheck": false,
|
||||
"target": "ES2024",
|
||||
"types": [
|
||||
"node",
|
||||
"obsidian-typings",
|
||||
"svelte"
|
||||
],
|
||||
"verbatimModuleSyntax": true
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*.svelte",
|
||||
"./src/**/*.ts",
|
||||
"./scripts/**/*.ts"
|
||||
]
|
||||
}
|
||||
1
versions.json
Normal file
1
versions.json
Normal file
|
|
@ -0,0 +1 @@
|
|||
{}
|
||||
Loading…
Reference in a new issue