mirror of
https://github.com/mnaoumov/obsidian-advanced-debug-mode.git
synced 2026-07-22 05:44:00 +00:00
Template
This commit is contained in:
commit
5f9423bde2
33 changed files with 12405 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 @@
|
||||||
|
# Advanced Debugger
|
||||||
|
|
||||||
|
This is a plugin for [Obsidian](https://obsidian.md/) that Does something awesome.
|
||||||
|
|
||||||
|
## 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-advanced-debugger) 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('advanced-debugger');
|
||||||
|
```
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
2
eslint.config.mts
Normal file
2
eslint.config.mts
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
import { configs } from 'obsidian-dev-utils/ScriptUtils/ESLint/eslint.config';
|
||||||
|
export default configs;
|
||||||
10
manifest.json
Normal file
10
manifest.json
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"author": "mnaoumov",
|
||||||
|
"authorUrl": "https://github.com/mnaoumov/",
|
||||||
|
"description": "Does something awesome.",
|
||||||
|
"fundingUrl": "https://www.buymeacoffee.com/mnaoumov",
|
||||||
|
"id": "advanced-debugger",
|
||||||
|
"isDesktopOnly": false,
|
||||||
|
"name": "Advanced Debugger",
|
||||||
|
"version": "0.0.0"
|
||||||
|
}
|
||||||
11169
package-lock.json
generated
Normal file
11169
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
43
package.json
Normal file
43
package.json
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
{
|
||||||
|
"name": "advanced-debugger",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "Does something awesome.",
|
||||||
|
"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.11.0",
|
||||||
|
"@codemirror/state": "^6.5.2",
|
||||||
|
"@codemirror/view": "^6.36.4",
|
||||||
|
"@tsconfig/strictest": "^2.0.5",
|
||||||
|
"@types/node": "^22.13.10",
|
||||||
|
"@types/react": "^19.0.12",
|
||||||
|
"@types/react-dom": "^19.0.4",
|
||||||
|
"jiti": "^2.4.2",
|
||||||
|
"moment": "^2.30.1",
|
||||||
|
"obsidian": "^1.8.7",
|
||||||
|
"obsidian-dev-utils": "^19.19.1",
|
||||||
|
"obsidian-typings": "^2.37.0",
|
||||||
|
"react": "^19.0.0",
|
||||||
|
"react-dom": "^19.0.0",
|
||||||
|
"svelte": "^5.23.2"
|
||||||
|
},
|
||||||
|
"overrides": {
|
||||||
|
"boolean": "npm:dry-uninstall"
|
||||||
|
}
|
||||||
|
}
|
||||||
263
src/AdvancedDebuggerPlugin.ts
Normal file
263
src/AdvancedDebuggerPlugin.ts
Normal file
|
|
@ -0,0 +1,263 @@
|
||||||
|
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 { AdvancedDebuggerPluginSettings } from './AdvancedDebuggerPluginSettings.ts';
|
||||||
|
import { AdvancedDebuggerPluginSettingsTab } from './AdvancedDebuggerPluginSettingsTab.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_REACT_VIEW_TYPE,
|
||||||
|
SampleReactView
|
||||||
|
} from './Views/SampleReactView.tsx';
|
||||||
|
import {
|
||||||
|
SAMPLE_SVELTE_VIEW_TYPE,
|
||||||
|
SampleSvelteView
|
||||||
|
} from './Views/SampleSvelteView.ts';
|
||||||
|
import {
|
||||||
|
SAMPLE_VIEW_TYPE,
|
||||||
|
SampleView
|
||||||
|
} from './Views/SampleView.ts';
|
||||||
|
|
||||||
|
export class AdvancedDebuggerPlugin extends PluginBase<AdvancedDebuggerPluginSettings> {
|
||||||
|
protected override createPluginSettings(data: unknown): AdvancedDebuggerPluginSettings {
|
||||||
|
return new AdvancedDebuggerPluginSettings(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override createPluginSettingsTab(): null | PluginSettingTab {
|
||||||
|
return new AdvancedDebuggerPluginSettingsTab(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override async onLayoutReady(): Promise<void> {
|
||||||
|
new Notice('This is executed after all plugins are loaded');
|
||||||
|
await this.openView(SAMPLE_VIEW_TYPE);
|
||||||
|
await this.openView(SAMPLE_SVELTE_VIEW_TYPE);
|
||||||
|
await this.openView(SAMPLE_REACT_VIEW_TYPE);
|
||||||
|
}
|
||||||
|
|
||||||
|
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.registerView(SAMPLE_SVELTE_VIEW_TYPE, (leaf) => new SampleSvelteView(leaf));
|
||||||
|
this.registerView(SAMPLE_REACT_VIEW_TYPE, (leaf) => new SampleReactView(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 openView(viewType: string): Promise<void> {
|
||||||
|
let leaf: null | WorkspaceLeaf;
|
||||||
|
const leaves = this.app.workspace.getLeavesOfType(viewType);
|
||||||
|
|
||||||
|
if (leaves.length > 0) {
|
||||||
|
leaf = leaves[0] ?? null;
|
||||||
|
} else {
|
||||||
|
leaf = this.app.workspace.getRightLeaf(true);
|
||||||
|
await leaf?.setViewState({ active: true, type: viewType });
|
||||||
|
}
|
||||||
|
|
||||||
|
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/AdvancedDebuggerPluginSettings.ts
Normal file
45
src/AdvancedDebuggerPluginSettings.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 AdvancedDebuggerPluginSettings 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/AdvancedDebuggerPluginSettingsTab.ts
Normal file
222
src/AdvancedDebuggerPluginSettingsTab.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 { AdvancedDebuggerPlugin } from './AdvancedDebuggerPlugin.ts';
|
||||||
|
|
||||||
|
export class AdvancedDebuggerPluginSettingsTab extends PluginSettingsTabBase<AdvancedDebuggerPlugin> {
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/ReactComponents/SampleReactComponent.tsx
Normal file
35
src/ReactComponents/SampleReactComponent.tsx
Normal file
|
|
@ -0,0 +1,35 @@
|
||||||
|
import type { JSX } from 'react';
|
||||||
|
|
||||||
|
import { useApp } from 'obsidian-dev-utils/obsidian/React/AppContext';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
export interface SampleReactComponentProps {
|
||||||
|
startCount: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SampleReactComponent(props: SampleReactComponentProps): JSX.Element {
|
||||||
|
const [count, setCount] = useState(props.startCount);
|
||||||
|
const app = useApp();
|
||||||
|
|
||||||
|
function increment(): void {
|
||||||
|
setCount(count + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<h4>Sample React Component!</h4>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span>
|
||||||
|
My number is {count}
|
||||||
|
!
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onClick={increment}>Increment</button>
|
||||||
|
<div>
|
||||||
|
Vault name: {app.vault.getName()}
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
7
src/SvelteComponents/SampleSvelteComponent.d.ts
vendored
Normal file
7
src/SvelteComponents/SampleSvelteComponent.d.ts
vendored
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
export interface SampleSvelteComponentExports {
|
||||||
|
increment(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SampleSvelteComponentProps {
|
||||||
|
startCount: number;
|
||||||
|
}
|
||||||
19
src/SvelteComponents/SampleSvelteComponent.svelte
Normal file
19
src/SvelteComponents/SampleSvelteComponent.svelte
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import type { SampleSvelteComponentProps } from './SampleSvelteComponent.d.ts';
|
||||||
|
|
||||||
|
let { startCount }: SampleSvelteComponentProps = $props();
|
||||||
|
|
||||||
|
let count = $state(startCount);
|
||||||
|
|
||||||
|
export function increment() {
|
||||||
|
count += 1;
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<h4>Sample Svelte Component!</h4>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<span>My number is {count}!</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button onclick={increment}>Increment</button>
|
||||||
47
src/Views/SampleReactView.tsx
Normal file
47
src/Views/SampleReactView.tsx
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import type { Root } from 'react-dom/client';
|
||||||
|
|
||||||
|
import { ItemView } from 'obsidian';
|
||||||
|
import { AppContext } from 'obsidian-dev-utils/obsidian/React/AppContext';
|
||||||
|
import { StrictMode } from 'react';
|
||||||
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
|
import type { SampleReactComponentProps } from '../ReactComponents/SampleReactComponent.tsx';
|
||||||
|
|
||||||
|
import { SampleReactComponent } from '../ReactComponents/SampleReactComponent.tsx';
|
||||||
|
|
||||||
|
export const SAMPLE_REACT_VIEW_TYPE = 'advanced-debugger-SampleReactView';
|
||||||
|
|
||||||
|
export class SampleReactView extends ItemView {
|
||||||
|
private root: null | Root = null;
|
||||||
|
|
||||||
|
public override getDisplayText(): string {
|
||||||
|
return 'SampleReact view';
|
||||||
|
}
|
||||||
|
|
||||||
|
public override getViewType(): string {
|
||||||
|
return SAMPLE_REACT_VIEW_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async onClose(): Promise<void> {
|
||||||
|
this.root?.unmount();
|
||||||
|
await Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async onOpen(): Promise<void> {
|
||||||
|
const START_COUNT = 10;
|
||||||
|
const props: SampleReactComponentProps = {
|
||||||
|
startCount: START_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
this.root = createRoot(this.contentEl);
|
||||||
|
this.root.render(
|
||||||
|
<StrictMode>
|
||||||
|
<AppContext.Provider value={this.app}>
|
||||||
|
<SampleReactComponent {...props} />
|
||||||
|
</AppContext.Provider>
|
||||||
|
</StrictMode>
|
||||||
|
);
|
||||||
|
|
||||||
|
await Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
47
src/Views/SampleSvelteView.ts
Normal file
47
src/Views/SampleSvelteView.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
import { ItemView } from 'obsidian';
|
||||||
|
import {
|
||||||
|
mount,
|
||||||
|
unmount
|
||||||
|
} from 'svelte';
|
||||||
|
|
||||||
|
import type {
|
||||||
|
SampleSvelteComponentExports,
|
||||||
|
SampleSvelteComponentProps
|
||||||
|
} from '../SvelteComponents/SampleSvelteComponent.d.ts';
|
||||||
|
|
||||||
|
import SampleSvelteComponent from '../SvelteComponents/SampleSvelteComponent.svelte';
|
||||||
|
|
||||||
|
export const SAMPLE_SVELTE_VIEW_TYPE = 'advanced-debugger-SampleSvelteView';
|
||||||
|
|
||||||
|
export class SampleSvelteView extends ItemView {
|
||||||
|
private sampleSvelteComponent: null | SampleSvelteComponentExports = null;
|
||||||
|
|
||||||
|
public override getDisplayText(): string {
|
||||||
|
return 'SampleSvelte view';
|
||||||
|
}
|
||||||
|
|
||||||
|
public override getViewType(): string {
|
||||||
|
return SAMPLE_SVELTE_VIEW_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async onClose(): Promise<void> {
|
||||||
|
if (this.sampleSvelteComponent) {
|
||||||
|
await unmount(this.sampleSvelteComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async onOpen(): Promise<void> {
|
||||||
|
const START_COUNT = 10;
|
||||||
|
const props: SampleSvelteComponentProps = {
|
||||||
|
startCount: START_COUNT
|
||||||
|
};
|
||||||
|
|
||||||
|
this.sampleSvelteComponent = mount(SampleSvelteComponent, {
|
||||||
|
props,
|
||||||
|
target: this.contentEl
|
||||||
|
}) as SampleSvelteComponentExports;
|
||||||
|
|
||||||
|
this.sampleSvelteComponent.increment();
|
||||||
|
await Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/Views/SampleView.ts
Normal file
19
src/Views/SampleView.ts
Normal file
|
|
@ -0,0 +1,19 @@
|
||||||
|
import { ItemView } from 'obsidian';
|
||||||
|
|
||||||
|
export const SAMPLE_VIEW_TYPE = 'AdvancedDebugger-SampleView';
|
||||||
|
|
||||||
|
export class SampleView extends ItemView {
|
||||||
|
public override getDisplayText(): string {
|
||||||
|
return 'Sample view';
|
||||||
|
}
|
||||||
|
|
||||||
|
public override getViewType(): string {
|
||||||
|
return SAMPLE_VIEW_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override async onOpen(): Promise<void> {
|
||||||
|
this.contentEl.empty();
|
||||||
|
this.contentEl.createEl('h4', { text: 'Sample view' });
|
||||||
|
await Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/main.ts
Normal file
5
src/main.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
import './styles/main.scss';
|
||||||
|
import { AdvancedDebuggerPlugin } from './AdvancedDebuggerPlugin.ts';
|
||||||
|
|
||||||
|
// eslint-disable-next-line import-x/no-default-export
|
||||||
|
export default AdvancedDebuggerPlugin;
|
||||||
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
|
||||||
|
*/
|
||||||
|
|
||||||
|
.advanced-debugger.obsidian-dev-utils {
|
||||||
|
/* Override library styles */
|
||||||
|
}
|
||||||
34
tsconfig.json
Normal file
34
tsconfig.json
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
{
|
||||||
|
"extends": "@tsconfig/strictest/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowArbitraryExtensions": true,
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"importHelpers": true,
|
||||||
|
"inlineSourceMap": true,
|
||||||
|
"inlineSources": true,
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"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",
|
||||||
|
"./src/**/*.tsx",
|
||||||
|
"./scripts/**/*.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
1
versions.json
Normal file
1
versions.json
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
{}
|
||||||
Loading…
Reference in a new issue