├── .editorconfig ├── .gitattributes ├── .github ├── FUNDING.yml ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ ├── config.yml │ ├── documentation.yml │ └── feature_request.yml └── workflows │ ├── checkPR.yml │ ├── closeIssue.yml │ └── release.yml ├── .gitignore ├── .npmrc ├── .prettierignore ├── .prettierrc.json ├── CHANGELOG.md ├── LICENSE.txt ├── Publish.js ├── PublishLoad.js ├── README.md ├── automation ├── build │ ├── buildBanner.ts │ ├── bun_test.ts │ ├── esbuild.config.ts │ ├── esbuild.dev.config.ts │ ├── esbuild.publish.config.ts │ └── esbuild.publish.dev.config.ts ├── config.json ├── installScript.ts ├── installScriptClean.ts ├── publishServer.ts ├── release.ts ├── stats.ts ├── tsconfig.json └── utils │ ├── shellUtils.ts │ ├── utils.ts │ └── versionUtils.ts ├── bun.lock ├── bunfig.toml ├── eslint.config.mjs ├── exampleVault ├── .md ├── .obsidian │ ├── plugins │ │ └── obsidian-meta-bind-plugin │ │ │ └── .hotreload │ └── snippets │ │ └── meta-bind-customization.css ├── Advanced Examples │ ├── Activity Tracker.md │ ├── DnD 5e Ability Scores and Modifiers.md │ ├── DnD 5e Travel Calculator.md │ ├── Health Tracker.md │ ├── PF2e DC Calcualtor.md │ ├── PF2e Encounter Calculator.md │ ├── PF2e Travel Calculator.md │ └── Using JS Engine for Complex things.md ├── Advanced Use-Cases.md ├── Alternative Metadata Storage │ ├── Alternative Metadata Storage.md │ └── Other Note.md ├── Buttons │ ├── Button Example.md │ ├── In Note Navigation.md │ ├── Replace Buttons.md │ └── Templater Buttons.md ├── Embed Example.md ├── Examples.md ├── Input Fields │ ├── Date and Time.md │ ├── Editor.md │ ├── Image Suggester.md │ ├── Inline Select.md │ ├── List.md │ ├── Number.md │ ├── Progress Bar.md │ ├── Select and Multi Select.md │ ├── Slider.md │ ├── Suggester.md │ ├── Text.md │ └── Toggle.md ├── Meta Bind API.md ├── O_O 2024 Checklist.md ├── Other Note.md ├── Other │ ├── Example Notes │ │ ├── Example Note with Callouts.md │ │ ├── Example Note with Embeds.md │ │ └── Example Note with Image.md │ └── Images │ │ ├── img_butterfly.webp │ │ ├── img_drops.jpg │ │ ├── img_flower.webp │ │ └── subfolder │ │ └── img_frozen_branch.jpg ├── View Fields │ ├── JS View Field.md │ ├── Other Note.md │ └── View Field.md ├── templates │ ├── Other Template.md │ ├── Test Template.md │ └── templater │ │ ├── Say Hello Command.md │ │ └── Templater Template.md └── testJsFile.js ├── images └── meta-bind-plugin-demo-3-gif.gif ├── manifest-beta.json ├── manifest.json ├── package.json ├── packages ├── core │ ├── .gitignore │ ├── README.md │ ├── package.json │ └── src │ │ ├── MountableManager.ts │ │ ├── Settings.ts │ │ ├── api │ │ ├── API.ts │ │ ├── FileAPI.ts │ │ ├── InternalAPI.ts │ │ └── SyntaxHighlightingAPI.ts │ │ ├── config │ │ ├── APIConfigs.ts │ │ ├── ButtonConfig.ts │ │ ├── FieldConfigs.ts │ │ └── validators │ │ │ ├── ButtonConfigValidators.ts │ │ │ └── Validators.ts │ │ ├── fields │ │ ├── FieldMountable.ts │ │ ├── button │ │ │ ├── AbstractButtonActionConfig.ts │ │ │ ├── ButtonActionRunner.ts │ │ │ ├── ButtonField.ts │ │ │ ├── ButtonGroupField.ts │ │ │ ├── ButtonGroupMountable.ts │ │ │ ├── ButtonManager.ts │ │ │ ├── ButtonMountable.ts │ │ │ └── actions │ │ │ │ ├── CommandButtonActionConfig.ts │ │ │ │ ├── CreateNoteButtonActionConfig.ts │ │ │ │ ├── InlineJSButtonActionConfig.ts │ │ │ │ ├── InputButtonActionConfig.ts │ │ │ │ ├── InsertIntoNoteButtonActionConfig.ts │ │ │ │ ├── JSButtonActionConfig.ts │ │ │ │ ├── OpenButtonActionConfig.ts │ │ │ │ ├── RegexpReplaceInNoteButtonActionConfig.ts │ │ │ │ ├── ReplaceInNoteButtonActionConfig.ts │ │ │ │ ├── ReplaceSelfButtonActionConfig.ts │ │ │ │ ├── RunTemplaterFileButtonActionConfig.ts │ │ │ │ ├── SleepButtonActionConfig.ts │ │ │ │ ├── TemplaterCreateNoteButtonActionConfig.ts │ │ │ │ └── UpdateMetadataButtonActionConfig.ts │ │ ├── embed │ │ │ └── EmbedMountable.ts │ │ ├── excluded │ │ │ └── ExcludedMountable.ts │ │ ├── fieldArguments │ │ │ ├── AbstractFieldArgument.ts │ │ │ ├── AbstractFieldArgumentContainer.ts │ │ │ ├── inputFieldArguments │ │ │ │ ├── AbstractInputFieldArgument.ts │ │ │ │ ├── InputFieldArgumentContainer.ts │ │ │ │ ├── InputFieldArgumentFactory.ts │ │ │ │ └── arguments │ │ │ │ │ ├── AddLabelsInputFieldArgument.ts │ │ │ │ │ ├── AllowOtherInputFieldArgument.ts │ │ │ │ │ ├── ClassInputFieldArgument.ts │ │ │ │ │ ├── DefaultValueInputFieldArgument.ts │ │ │ │ │ ├── LimitInputFieldArgument.ts │ │ │ │ │ ├── MaxValueInputFieldArgument.ts │ │ │ │ │ ├── MinValueInputFieldArgument.ts │ │ │ │ │ ├── MultiLineInputFieldArgument.ts │ │ │ │ │ ├── OffValueInputFieldArgument.ts │ │ │ │ │ ├── OnValueInputFieldArgument.ts │ │ │ │ │ ├── OptionInputFieldArgument.ts │ │ │ │ │ ├── OptionQueryInputFieldArgument.ts │ │ │ │ │ ├── PlaceholderInputFieldArgument.ts │ │ │ │ │ ├── ShowcaseInputFieldArgument.ts │ │ │ │ │ ├── StepSizeValueInputFieldArgument.ts │ │ │ │ │ ├── TitleInputFieldArgument.ts │ │ │ │ │ └── UseLinksInputFieldArgument.ts │ │ │ └── viewFieldArguments │ │ │ │ ├── AbstractViewFieldArgument.ts │ │ │ │ ├── ViewFieldArgumentContainer.ts │ │ │ │ ├── ViewFieldArgumentFactory.ts │ │ │ │ └── argumnets │ │ │ │ ├── ClassViewFieldArgument.ts │ │ │ │ ├── HiddenViewFieldArgument.ts │ │ │ │ └── RenderMarkdownViewFieldArgument.ts │ │ ├── inputFields │ │ │ ├── AbstractInputField.ts │ │ │ ├── InputFieldFactory.ts │ │ │ ├── InputFieldMountable.ts │ │ │ ├── InputFieldSvelteWrapper.ts │ │ │ └── fields │ │ │ │ ├── Date │ │ │ │ ├── DateComponent.svelte │ │ │ │ └── DateIPF.ts │ │ │ │ ├── DatePicker │ │ │ │ ├── Calender.svelte │ │ │ │ ├── DatePicker.svelte │ │ │ │ ├── DatePickerComponent.svelte │ │ │ │ └── DatePickerIPF.ts │ │ │ │ ├── DateTime │ │ │ │ ├── DateTimeComponent.svelte │ │ │ │ └── DateTimeIPF.ts │ │ │ │ ├── Editor │ │ │ │ ├── EditorComponent.svelte │ │ │ │ ├── EditorIPF.ts │ │ │ │ └── MarkdownRenderComponent.svelte │ │ │ │ ├── ImageListSuggester │ │ │ │ ├── ImageListSuggesterComponent.svelte │ │ │ │ └── ImageListSuggesterIPF.ts │ │ │ │ ├── ImageSuggester │ │ │ │ ├── ImageSuggesterComponent.svelte │ │ │ │ └── ImageSuggesterIPF.ts │ │ │ │ ├── InlineList │ │ │ │ ├── InlineListComponent.svelte │ │ │ │ └── InlineListIPF.ts │ │ │ │ ├── InlineListSuggester │ │ │ │ ├── InlineListSuggesterComponent.svelte │ │ │ │ └── InlineListSuggesterIPF.ts │ │ │ │ ├── InlineSelect │ │ │ │ ├── InlineSelectComponent.svelte │ │ │ │ └── InlineSelectIPF.ts │ │ │ │ ├── List │ │ │ │ ├── ListComponent.svelte │ │ │ │ └── ListIPF.ts │ │ │ │ ├── ListSuggester │ │ │ │ ├── ListSuggesterComponent.svelte │ │ │ │ └── ListSuggesterIPF.ts │ │ │ │ ├── MultiSelect │ │ │ │ ├── MultiSelectComponent.svelte │ │ │ │ └── MultiSelectIPF.ts │ │ │ │ ├── Number │ │ │ │ ├── NumberComponent.svelte │ │ │ │ └── NumberIPF.ts │ │ │ │ ├── ProgressBar │ │ │ │ ├── ProgressBarComponent.svelte │ │ │ │ └── ProgressBarIPF.ts │ │ │ │ ├── Select │ │ │ │ ├── SelectComponent.svelte │ │ │ │ └── SelectIPF.ts │ │ │ │ ├── Slider │ │ │ │ ├── SliderComponent.svelte │ │ │ │ └── SliderIPF.ts │ │ │ │ ├── Suggester │ │ │ │ ├── SuggesterComponent.svelte │ │ │ │ ├── SuggesterHelper.ts │ │ │ │ └── SuggesterIPF.ts │ │ │ │ ├── Text │ │ │ │ ├── TextComponent.svelte │ │ │ │ └── TextIPF.ts │ │ │ │ ├── TextArea │ │ │ │ ├── TextAreaComponent.svelte │ │ │ │ └── TextAreaIPF.ts │ │ │ │ ├── Time │ │ │ │ ├── TimeComponent.svelte │ │ │ │ └── TimeIPF.ts │ │ │ │ └── Toggle │ │ │ │ ├── ToggleComponent.svelte │ │ │ │ └── ToggleIPF.ts │ │ ├── metaBindTable │ │ │ ├── MetaBindTableComponent.svelte │ │ │ └── TableMountable.ts │ │ └── viewFields │ │ │ ├── AbstractViewField.ts │ │ │ ├── JsViewFieldMountable.ts │ │ │ ├── ViewFieldFactory.ts │ │ │ ├── ViewFieldMountable.ts │ │ │ ├── ViewFieldVariable.ts │ │ │ └── fields │ │ │ ├── ImageVF.ts │ │ │ ├── LinkVF.ts │ │ │ ├── MathVF.ts │ │ │ └── TextVF.ts │ │ ├── index.ts │ │ ├── metadata │ │ ├── BindTargetScope.ts │ │ ├── DerivedMetadataSubscription.ts │ │ ├── EffectMetadataSubscription.ts │ │ ├── IMetadataSubscription.ts │ │ ├── InternalMetadataSources.ts │ │ ├── MetadataCacheItem.ts │ │ ├── MetadataManager.ts │ │ ├── MetadataSource.ts │ │ └── MetadataSubscription.ts │ │ ├── modals │ │ ├── IModal.ts │ │ ├── ModalContent.ts │ │ ├── SelectModalContent.ts │ │ ├── modalContents │ │ │ ├── ImageSuggesterModalCard.svelte │ │ │ ├── ImageSuggesterModalComponent.svelte │ │ │ ├── SvelteModalContent.ts │ │ │ ├── TextPromptModalContent.svelte │ │ │ └── buttonBuilder │ │ │ │ ├── ButtonBuilderModal.ts │ │ │ │ ├── ButtonBuilderModalComponent.svelte │ │ │ │ ├── CommandActionSettings.svelte │ │ │ │ ├── CreateNoteActionSettings.svelte │ │ │ │ ├── InlineJsActionSettings.svelte │ │ │ │ ├── InputActionSettings.svelte │ │ │ │ ├── InsertIntoNoteActionSettings.svelte │ │ │ │ ├── JSActionSettings.svelte │ │ │ │ ├── OpenActionSettings.svelte │ │ │ │ ├── RegexpReplaceInNoteActionSettings.svelte │ │ │ │ ├── ReplaceInNoteActionSettings.svelte │ │ │ │ ├── ReplaceSelfActionSettings.svelte │ │ │ │ ├── RunTemplaterFileActionSettings.svelte │ │ │ │ ├── SleepActionSettings.svelte │ │ │ │ ├── TemplaterCreateNoteActionSettings.svelte │ │ │ │ └── UpdateMetadataActionSettings.svelte │ │ └── selectModalContents │ │ │ ├── CommandSelectModal.ts │ │ │ ├── FileSelectModal.ts │ │ │ ├── FolderSelectModal.ts │ │ │ └── SuggesterSelectModal.ts │ │ ├── parsers │ │ ├── ButtonParser.ts │ │ ├── DateParser.ts │ │ ├── FieldDeclaration.ts │ │ ├── MarkdownLinkParser.ts │ │ ├── ParsingError.ts │ │ ├── TimeParser.ts │ │ ├── bindTargetParser │ │ │ ├── BindTargetDeclaration.ts │ │ │ └── BindTargetParser.ts │ │ ├── inputFieldParser │ │ │ ├── ITemplateSupplier.ts │ │ │ ├── InputFieldDeclaration.ts │ │ │ ├── InputFieldDeclarationValidator.ts │ │ │ └── InputFieldParser.ts │ │ ├── nomParsers │ │ │ ├── BindTargetNomParsers.ts │ │ │ ├── FieldArgumentNomParsers.ts │ │ │ ├── GeneralNomParsers.ts │ │ │ ├── InputFieldNomParsers.ts │ │ │ ├── MiscNomParsers.ts │ │ │ └── ViewFieldNomParsers.ts │ │ ├── syntaxHighlighting │ │ │ ├── HLPUtils.ts │ │ │ ├── HLPs.ts │ │ │ ├── Highlight.ts │ │ │ └── SyntaxHighlighting.ts │ │ └── viewFieldParser │ │ │ ├── JsViewFieldParser.ts │ │ │ ├── ViewFieldDeclaration.ts │ │ │ ├── ViewFieldDeclarationValidator.ts │ │ │ └── ViewFieldParser.ts │ │ └── utils │ │ ├── DatePickerUtils.ts │ │ ├── DocsUtils.ts │ │ ├── IContextMenu.ts │ │ ├── IFuzzySearch.ts │ │ ├── IJsRenderer.ts │ │ ├── InputFieldExamples.ts │ │ ├── LineNumberExpression.ts │ │ ├── Literal.ts │ │ ├── MathJS.ts │ │ ├── Mountable.ts │ │ ├── RefCounter.ts │ │ ├── Signal.ts │ │ ├── Utils.ts │ │ ├── ZodUtils.ts │ │ ├── components │ │ ├── Button.svelte │ │ ├── ButtonComponent.svelte │ │ ├── FlexRow.svelte │ │ ├── Icon.svelte │ │ ├── ImageCard.svelte │ │ ├── ImageGrid.svelte │ │ ├── LinkComponent.svelte │ │ ├── LinkListComponent.svelte │ │ ├── ListWrapper.svelte │ │ ├── LiteralRenderComponent.svelte │ │ ├── ModalButtonGroup.svelte │ │ ├── MountableComponent.svelte │ │ ├── SettingComponent.svelte │ │ └── Toggle.svelte │ │ ├── errors │ │ ├── ErrorCollection.ts │ │ ├── ErrorCollectionComponent.svelte │ │ ├── ErrorIndicatorComponent.svelte │ │ ├── MetaBindErrorComponent.svelte │ │ └── MetaBindErrors.ts │ │ ├── playground │ │ ├── InputFieldExampleComponent.svelte │ │ ├── PlaygroundComponent.svelte │ │ └── ViewFieldExampleComponent.svelte │ │ └── prop │ │ ├── PropAccess.ts │ │ ├── PropParser.ts │ │ ├── PropPath.ts │ │ └── PropUtils.ts ├── obsidian │ ├── .gitignore │ ├── README.md │ ├── bun.lockb │ ├── extraTypes │ │ ├── Dataview.d.ts │ │ ├── JsEngine.d.ts │ │ ├── Templater.d.ts │ │ └── obsidian-ex.d.ts │ ├── package.json │ ├── src │ │ ├── EditorMenu.ts │ │ ├── FuzzySearch.ts │ │ ├── MountableMDRC.ts │ │ ├── ObsAPI.ts │ │ ├── ObsContextMenu.ts │ │ ├── ObsFileAPI.ts │ │ ├── ObsInternalAPI.ts │ │ ├── ObsJsRenderer.ts │ │ ├── ObsMetadataSource.ts │ │ ├── ObsNotePosition.ts │ │ ├── ObsUtils.ts │ │ ├── cm6 │ │ │ ├── Cm5_Modes.ts │ │ │ ├── Cm6_Util.ts │ │ │ ├── Cm6_ViewPlugin.ts │ │ │ └── Cm6_Widgets.ts │ │ ├── dependencies │ │ │ ├── Dependency.ts │ │ │ ├── DependencyManager.ts │ │ │ └── Version.ts │ │ ├── docsExports.ts │ │ ├── main.ts │ │ ├── modals │ │ │ ├── ImageSuggesterModalHelper.ts │ │ │ ├── ObsModal.ts │ │ │ ├── ObsSearchModal.ts │ │ │ └── SuggesterModalHelper.ts │ │ ├── playground │ │ │ └── PlaygroundView.ts │ │ └── settings │ │ │ ├── SettingsTab.ts │ │ │ ├── buttonTemplateSetting │ │ │ ├── ButtonTemplateSettingComponent.svelte │ │ │ ├── ButtonTemplatesSettingComponent.svelte │ │ │ └── ButtonTemplatesSettingModal.ts │ │ │ ├── excludedFoldersSetting │ │ │ ├── ExcludedFoldersSettingComponent.svelte │ │ │ └── ExcludedFoldersSettingModal.ts │ │ │ └── inputFieldTemplateSetting │ │ │ ├── InputFieldTemplateSettingComponent.svelte │ │ │ ├── InputFieldTemplatesSettingComponent.svelte │ │ │ └── InputFieldTemplatesSettingModal.ts │ └── tsconfig.json ├── publish │ ├── .gitignore │ ├── README.md │ ├── bun.lockb │ ├── extraTypes │ │ └── publish.d.ts │ ├── package.json │ ├── src │ │ ├── PublishAPI.ts │ │ ├── PublishFieldMDRC.ts │ │ ├── PublishFileAPI.ts │ │ ├── PublishInternalAPI.ts │ │ ├── PublishMetadataSource.ts │ │ ├── PublishNotePosition.ts │ │ ├── PublishUtils.ts │ │ └── main.ts │ └── tsconfig.json └── type-ex.d.ts ├── styles.css ├── tests ├── __mocks__ │ ├── TestAPI.ts │ ├── TestComponent.ts │ ├── TestFileAPI.ts │ ├── TestFileSystem.ts │ ├── TestInternalAPI.ts │ └── TestPlugin.ts ├── __preload__ │ ├── customMatchers.ts │ ├── happydom.ts │ ├── obsidianMock.ts │ └── svelteLoader.ts ├── api.test.ts ├── fields │ ├── Button.test.ts │ └── IPF.test.ts ├── metadataManagerTests │ ├── MetadataManager.test.ts │ └── Signal.test.ts ├── parserTests │ ├── BindTargetParser.test.ts │ ├── DateParser.test.ts │ ├── InputFieldParser.test.ts │ ├── MarkdownLinkParser.test.ts │ └── TimeParser.test.ts └── utils │ ├── Literal.test.ts │ ├── PropUtils.test.ts │ └── Utils.test.ts ├── tsconfig.json ├── tsconfig.types.json └── versions.json /.editorconfig: -------------------------------------------------------------------------------- 1 | # top-most EditorConfig file 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | end_of_line = lf 7 | insert_final_newline = true 8 | indent_style = tab 9 | indent_size = 4 10 | tab_width = 4 11 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | Publish.js linguist-generated 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: ['mProjectsCode'] 2 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: false 2 | contact_links: 3 | - name: 'Meta Bind thead on the official Obsidian Discord server' 4 | url: 'https://discord.com/channels/686053708261228577/1286803892549713921' 5 | about: 'Please ask your questions on the official Obsidian Discord server in the "Meta Bind ans JS Engine" thread.' 6 | - name: 'Discussions' 7 | url: 'https://github.com/mProjectsCode/obsidian-meta-bind-plugin/discussions' 8 | about: "If you don't want to join the official Obsidian Discord server, you can use the Discussions tab of this repository to ask your questions." 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.yml: -------------------------------------------------------------------------------- 1 | name: Documentation Issue or Enhancement 2 | description: Report an issue or enhancement for the documentation website. 3 | labels: 4 | - documentation 5 | body: 6 | - type: input 7 | id: site 8 | attributes: 9 | label: Page 10 | description: Please provide the link of the page. 11 | placeholder: https://www.moritzjung.dev/obsidian-meta-bind-plugin-docs/... 12 | validations: 13 | required: true 14 | - type: textarea 15 | id: main 16 | attributes: 17 | label: Issue or Enhancement 18 | description: Please describe the issue or suggested enhancement for the page above. 19 | validations: 20 | required: true 21 | - type: markdown 22 | attributes: 23 | value: >- 24 | This template was generated with [Issue Forms 25 | Creator](https://issue-forms-creator.netlify.app) 26 | -------------------------------------------------------------------------------- /.github/workflows/checkPR.yml: -------------------------------------------------------------------------------- 1 | name: Run Checks and Tests on PR 2 | 3 | on: pull_request 4 | 5 | jobs: 6 | check: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - name: Checkout 11 | uses: actions/checkout@v4 12 | 13 | - name: Install Bun 14 | uses: oven-sh/setup-bun@v1 15 | with: 16 | bun-version: latest 17 | 18 | - name: Install Dependencies 19 | id: build 20 | run: | 21 | bun install 22 | bun run pack:i 23 | 24 | - name: Run Checks 25 | run: | 26 | bun run check 27 | -------------------------------------------------------------------------------- /.github/workflows/closeIssue.yml: -------------------------------------------------------------------------------- 1 | name: Close Invalid Issue 2 | on: 3 | issues: 4 | types: 5 | - labeled 6 | jobs: 7 | closeIssue: 8 | if: github.event.label.name == 'invalid' 9 | runs-on: ubuntu-latest 10 | permissions: 11 | issues: write 12 | steps: 13 | - name: Close Issue 14 | uses: peter-evans/close-issue@v2 15 | with: 16 | comment: This issue is invalid. Please conform to the issue templates. 17 | close-reason: not_planned 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # vscode 2 | .vscode 3 | 4 | # Intellij 5 | *.iml 6 | .idea 7 | 8 | # npm 9 | node_modules 10 | 11 | # Don't include the compiled main.js file in the repo. 12 | # They should be uploaded to GitHub releases instead. 13 | main.js 14 | 15 | # Exclude sourcemaps 16 | *.map 17 | 18 | # obsidian 19 | data.json 20 | 21 | # Exclude macOS Finder (System Explorer) View States 22 | .DS_Store 23 | 24 | obsidian.css 25 | tmp.txt 26 | meta.txt 27 | 28 | !exampleVault/.obsidian 29 | 30 | exampleVault/.obsidian/* 31 | !exampleVault/.obsidian/plugins 32 | !exampleVault/.obsidian/snippets 33 | 34 | exampleVault/.obsidian/plugins/* 35 | exampleVault/.obsidian/plugins/obsidian-meta-bind-plugin/* 36 | !exampleVault/.obsidian/plugins/obsidian-meta-bind-plugin/.hotreload 37 | 38 | coverage/ -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | tag-version-prefix="" -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /lib 2 | node_modules 3 | package-lock.json 4 | exampleVault 5 | main.js 6 | Publish.js 7 | LICENSE.txt 8 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": ["prettier-plugin-svelte"], 3 | 4 | "printWidth": 120, 5 | "useTabs": true, 6 | "tabWidth": 4, 7 | "semi": true, 8 | "singleQuote": true, 9 | "arrowParens": "avoid", 10 | 11 | "svelteAllowShorthand": false 12 | } 13 | -------------------------------------------------------------------------------- /PublishLoad.js: -------------------------------------------------------------------------------- 1 | // META-BIND OBSIDIAN PUBLISH SCRIPT 2 | // add this script to the end of you publish.js 3 | // this script loads and executes the latest version of the meta-bind plugin for obsidian publish 4 | 5 | // The plugin settings. Not all settings are used. 6 | let mb_settingsString = `{ 7 | "devMode": false, 8 | "ignoreCodeBlockRestrictions": false, 9 | "preferredDateFormat": "YYYY-MM-DD", 10 | "useUsDateInputOrder": false, 11 | "firstWeekday": { 12 | "index": 1, 13 | "name": "Monday", 14 | "shortName": "Mo" 15 | }, 16 | "syncInterval": 50, 17 | "minSyncInterval": 1000, 18 | "maxSyncInterval": 50, 19 | "enableJs": true, 20 | "inputFieldTemplates": [], 21 | "excludedFolders": [], 22 | "dateFormat": "eu" 23 | }`; 24 | 25 | // if true, the script is pulled from the master branch instead of the release branch 26 | const mb_dev_mode = false; 27 | 28 | const mb_settings = JSON.parse(mb_settingsString); 29 | 30 | async function mb_load() { 31 | const response = mb_dev_mode 32 | ? await fetch('https://raw.githubusercontent.com/mProjectsCode/obsidian-meta-bind-plugin/master/Publish.js') 33 | : await fetch('https://raw.githubusercontent.com/mProjectsCode/obsidian-meta-bind-plugin/release/Publish.js'); 34 | const script = await response.text(); 35 | eval(script); 36 | } 37 | 38 | mb_load(); 39 | -------------------------------------------------------------------------------- /automation/build/buildBanner.ts: -------------------------------------------------------------------------------- 1 | import manifest from '../../manifest.json' assert { type: 'json' }; 2 | 3 | export function getBuildBanner(buildType: string, getVersion: (version: string) => string) { 4 | return `/* 5 | ------------------------------------------- 6 | ${manifest.name} - ${buildType} 7 | ------------------------------------------- 8 | By: ${manifest.author} (${manifest.authorUrl}) 9 | Time: ${new Date().toUTCString()} 10 | Version: ${getVersion(manifest.version)} 11 | ------------------------------------------- 12 | THIS IS A GENERATED/BUNDLED FILE BY ESBUILD 13 | if you want to view the source, please visit the github repository of this plugin 14 | ------------------------------------------- 15 | Copyright (C) ${new Date().getFullYear()} ${manifest.author} 16 | ------------------------------------------- 17 | This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 18 | 19 | This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. 20 | 21 | You should have received a copy of the GNU General Public License along with this program. If not, see . 22 | */ 23 | `; 24 | } 25 | -------------------------------------------------------------------------------- /automation/build/bun_test.ts: -------------------------------------------------------------------------------- 1 | import builtins from 'builtin-modules'; 2 | import esbuild from 'esbuild'; 3 | import esbuildSvelte from 'esbuild-svelte'; 4 | import sveltePreprocess from 'svelte-preprocess'; 5 | import { getBuildBanner } from 'build/buildBanner'; 6 | 7 | // currently this segfaults for me, but i want to consider using bun for the build system in the future 8 | // https://github.com/oven-sh/bun/issues/12456 9 | 10 | const banner = getBuildBanner('Release Build', version => version); 11 | 12 | const build = await Bun.build({ 13 | banner: { 14 | js: banner, 15 | }, 16 | entryPoints: ['packages/obsidian/src/main.ts'], 17 | bundle: true, 18 | external: [ 19 | 'obsidian', 20 | 'electron', 21 | '@codemirror/autocomplete', 22 | '@codemirror/collab', 23 | '@codemirror/commands', 24 | '@codemirror/language', 25 | '@codemirror/lint', 26 | '@codemirror/search', 27 | '@codemirror/state', 28 | '@codemirror/view', 29 | '@lezer/common', 30 | '@lezer/highlight', 31 | '@lezer/lr', 32 | ...builtins, 33 | ], 34 | format: 'esm', 35 | target: 'browser', 36 | logLevel: 'info', 37 | sourcemap: 'none', 38 | treeShaking: true, 39 | outfile: 'main.js', 40 | minify: true, 41 | metafile: true, 42 | define: { 43 | MB_GLOBAL_CONFIG_DEV_BUILD: 'false', 44 | }, 45 | plugins: [ 46 | // @ts-ignore 47 | esbuildSvelte({ 48 | compilerOptions: { css: 'injected', dev: false }, 49 | preprocess: sveltePreprocess(), 50 | filterWarnings: warning => { 51 | // we don't want warnings from node modules that we can do nothing about 52 | return !warning.filename?.includes('node_modules'); 53 | }, 54 | }), 55 | ], 56 | }); 57 | 58 | // const file = Bun.file('meta.txt'); 59 | // await Bun.write(file, JSON.stringify(build.metafile, null, '\t')); 60 | 61 | // process.exit(0); 62 | -------------------------------------------------------------------------------- /automation/build/esbuild.config.ts: -------------------------------------------------------------------------------- 1 | import builtins from 'builtin-modules'; 2 | import esbuild from 'esbuild'; 3 | import esbuildSvelte from 'esbuild-svelte'; 4 | import { getBuildBanner } from 'build/buildBanner'; 5 | import manifest from '../../manifest.json' assert { type: 'json' }; 6 | 7 | const banner = getBuildBanner('Release Build', version => version); 8 | 9 | const build = await esbuild.build({ 10 | banner: { 11 | js: banner, 12 | }, 13 | entryPoints: ['packages/obsidian/src/main.ts'], 14 | bundle: true, 15 | external: [ 16 | 'obsidian', 17 | 'electron', 18 | '@codemirror/autocomplete', 19 | '@codemirror/collab', 20 | '@codemirror/commands', 21 | '@codemirror/language', 22 | '@codemirror/lint', 23 | '@codemirror/search', 24 | '@codemirror/state', 25 | '@codemirror/view', 26 | '@lezer/common', 27 | '@lezer/highlight', 28 | '@lezer/lr', 29 | ...builtins, 30 | ], 31 | format: 'cjs', 32 | target: 'es2022', 33 | logLevel: 'info', 34 | sourcemap: false, 35 | treeShaking: true, 36 | outfile: 'main.js', 37 | minify: true, 38 | metafile: true, 39 | conditions: ['browser', 'production'], 40 | define: { 41 | MB_VERSION: `"${manifest.version}"`, 42 | MB_DEV_BUILD: 'false', 43 | MB_DEBUG: 'false', 44 | }, 45 | plugins: [ 46 | esbuildSvelte({ 47 | compilerOptions: { css: 'injected', dev: false }, 48 | filterWarnings: warning => { 49 | // we don't want warnings from node modules that we can do nothing about 50 | return !warning.filename?.includes('node_modules'); 51 | }, 52 | }), 53 | ], 54 | }); 55 | 56 | const file = Bun.file('meta.txt'); 57 | await Bun.write(file, JSON.stringify(build.metafile, null, '\t')); 58 | 59 | process.exit(0); 60 | -------------------------------------------------------------------------------- /automation/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "devBranch": "master", 3 | "releaseBranch": "release", 4 | "github": "https://github.com/mProjectsCode/obsidian-meta-bind-plugin", 5 | "corePackages": ["core"], 6 | "packages": ["obsidian", "publish"] 7 | } 8 | -------------------------------------------------------------------------------- /automation/installScript.ts: -------------------------------------------------------------------------------- 1 | import { $, Verboseness } from 'utils/shellUtils'; 2 | import config from './config.json'; 3 | 4 | async function installScript() { 5 | for (const corePackage of config.corePackages) { 6 | await $('bun i', `packages/${corePackage}`, Verboseness.VERBOSE); 7 | } 8 | 9 | for (const nonCorePackage of config.packages) { 10 | await $('bun i', `packages/${nonCorePackage}`, Verboseness.NORMAL); 11 | } 12 | } 13 | 14 | await installScript(); 15 | -------------------------------------------------------------------------------- /automation/installScriptClean.ts: -------------------------------------------------------------------------------- 1 | import { $, Verboseness } from 'utils/shellUtils'; 2 | import config from './config.json'; 3 | 4 | async function installScriptClean() { 5 | for (const corePackage of config.corePackages) { 6 | await $('rm -rf "node_modules"', `packages/${corePackage}`, Verboseness.VERBOSE); 7 | await $('rm "bun.lockb"', `packages/${corePackage}`, Verboseness.VERBOSE); 8 | await $('bun i', `packages/${corePackage}`, Verboseness.VERBOSE); 9 | } 10 | 11 | for (const nonCorePackage of config.packages) { 12 | await $('rm -rf "node_modules"', `packages/${nonCorePackage}`, Verboseness.VERBOSE); 13 | await $('rm "bun.lockb"', `packages/${nonCorePackage}`, Verboseness.VERBOSE); 14 | await $('bun i', `packages/${nonCorePackage}`, Verboseness.NORMAL); 15 | } 16 | 17 | await $('rm -rf "node_modules"', '', Verboseness.VERBOSE); 18 | await $('rm "bun.lockb"', '', Verboseness.VERBOSE); 19 | await $('bun i', '', Verboseness.NORMAL); 20 | } 21 | 22 | await installScriptClean(); 23 | -------------------------------------------------------------------------------- /automation/publishServer.ts: -------------------------------------------------------------------------------- 1 | import { Elysia } from 'elysia'; 2 | import { cors } from '@elysiajs/cors'; 3 | 4 | new Elysia().use(cors()).get('/', Bun.file('./Publish.js')).listen(31000); 5 | -------------------------------------------------------------------------------- /automation/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "module": "ESNext", 5 | "target": "ESNext", 6 | "allowJs": true, 7 | "noImplicitAny": true, 8 | "strict": true, 9 | "strictNullChecks": true, 10 | "noImplicitReturns": true, 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "isolatedModules": true, 14 | "lib": ["DOM", "ES5", "ES6", "ES7", "Es2021"], 15 | "types": ["bun-types"], 16 | "allowSyntheticDefaultImports": true, 17 | "resolveJsonModule": true 18 | }, 19 | "include": ["**/*.ts"] 20 | } 21 | -------------------------------------------------------------------------------- /automation/utils/utils.ts: -------------------------------------------------------------------------------- 1 | export class UserError extends Error {} 2 | 3 | export interface ProjectConfig { 4 | corePackages: string[]; 5 | packages: string[]; 6 | } 7 | -------------------------------------------------------------------------------- /bunfig.toml: -------------------------------------------------------------------------------- 1 | [test] 2 | preload = ["./tests/__preload__/happydom.ts", "./tests/__preload__/svelteLoader.ts", "./tests/__preload__/obsidianMock.ts", "./tests/__preload__/customMatchers.ts"] 3 | root = "./tests" 4 | coverage = true 5 | coverageReporter = ["text", "lcov"] -------------------------------------------------------------------------------- /exampleVault/.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mProjectsCode/obsidian-meta-bind-plugin/b4450146c74decb0f5bef80e154a7bdceffe4d59/exampleVault/.md -------------------------------------------------------------------------------- /exampleVault/.obsidian/plugins/obsidian-meta-bind-plugin/.hotreload: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mProjectsCode/obsidian-meta-bind-plugin/b4450146c74decb0f5bef80e154a7bdceffe4d59/exampleVault/.obsidian/plugins/obsidian-meta-bind-plugin/.hotreload -------------------------------------------------------------------------------- /exampleVault/Advanced Examples/Activity Tracker.md: -------------------------------------------------------------------------------- 1 | --- 2 | activities: 3 | - from: 03:00 4 | to: 04:17 5 | activity: sudying 6 | status: 0 7 | - from: 00:03 8 | activity: youtube 9 | to: 03:00 10 | status: + 11 | --- 12 | 13 | ```js-engine 14 | const mb = engine.getPlugin('obsidian-meta-bind-plugin').api; 15 | 16 | const tableOptions = { 17 | bindTarget: mb.createBindTarget('frontmatter', context.file.path, ['activities']), 18 | tableHead: ['From', 'To', 'Activity', 'Status'], 19 | columns: [ 20 | 'INPUT[time:scope^from]', 21 | 'INPUT[time:scope^to]', 22 | 'INPUT[inlineSelect(option(youtube), option(sudying), option(linch)):scope^activity]', 23 | 'INPUT[inlineSelect(option(-, unproductive), option(0, normal), option(+, productive)):scope^status]', 24 | ], 25 | }; 26 | 27 | const mountable = mb.createTableMountable(context.file.path, tableOptions); 28 | 29 | mb.wrapInMDRC(mountable, container, component); 30 | ``` -------------------------------------------------------------------------------- /exampleVault/Advanced Examples/Health Tracker.md: -------------------------------------------------------------------------------- 1 | --- 2 | health: 50 3 | max_health: 50 4 | damage: 5 5 | --- 6 | 7 | ```meta-bind-button 8 | label: "Deal" 9 | style: destructive 10 | hidden: true 11 | id: "deal-damage" 12 | actions: 13 | - type: updateMetadata 14 | bindTarget: health 15 | evaluate: true 16 | value: x - getMetadata('damage') 17 | ``` 18 | 19 | ```meta-bind-button 20 | label: "Reset" 21 | style: primary 22 | hidden: true 23 | id: "reset-health" 24 | actions: 25 | - type: updateMetadata 26 | bindTarget: health 27 | evaluate: true 28 | value: getMetadata('max_health') 29 | ``` 30 | 31 | Health: `VIEW[{health}][text]` `BUTTON[reset-health]` 32 | 33 | Damage: `INPUT[number:damage]` `BUTTON[deal-damage]` -------------------------------------------------------------------------------- /exampleVault/Advanced Use-Cases.md: -------------------------------------------------------------------------------- 1 | --- 2 | list: 3 | - 1 4 | - 2 5 | - 3 6 | index: 0 7 | options: 8 | - "1" 9 | - "2" 10 | - "3" 11 | selected: 1 12 | --- 13 | 14 | ## Changing the Bind Target 15 | 16 | The `index` determines the element of the `list` array that the number input field binds to. 17 | 18 | Index: `INPUT[inlineSelect(option(0), option(1), option(2)):index]` 19 | 20 | ```meta-bind-js-view 21 | {index} as index 22 | --- 23 | const str = `\`INPUT[number:list[${context.bound.index}]]\``; 24 | return engine.markdown.create(str) 25 | ``` 26 | 27 | ## Generating a list of Options from Front-matter 28 | 29 | `INPUT[inlineList:options]` 30 | 31 | ```meta-bind-js-view 32 | {options} as options 33 | --- 34 | const options = context.bound.options.map(x => `option(${x})`).join(", "); 35 | const str = `\`INPUT[inlineSelect(${options}):selected]\``; 36 | return engine.markdown.create(str); 37 | ``` -------------------------------------------------------------------------------- /exampleVault/Alternative Metadata Storage/Alternative Metadata Storage.md: -------------------------------------------------------------------------------- 1 | 2 | These two inputs are bound to a global in memory cache. 3 | 4 | `INPUT[text:globalMemory^test]` 5 | 6 | `INPUT[text:globalMemory^test]` 7 | 8 | `VIEW[{globalMemory^test123}456][text]` 9 | 10 | These two inputs are bound to a per file in memory cache. 11 | 12 | `INPUT[text:memory^test]` 13 | 14 | `INPUT[text:memory^test]` -------------------------------------------------------------------------------- /exampleVault/Alternative Metadata Storage/Other Note.md: -------------------------------------------------------------------------------- 1 | This input is bound to a global in memory cache. 2 | 3 | `INPUT[text:globalMemory^test]` 4 | 5 | `VIEW[{globalMemory^test} 123][text:globalMemory^test123]` 6 | 7 | This input is bound to a per file in memory cache. 8 | 9 | `INPUT[text:memory^test]` 10 | 11 | `VIEW[{memory^test} 123][text:memory^test123]` 12 | 13 | `VIEW[{memory^test123}456][text]` -------------------------------------------------------------------------------- /exampleVault/Buttons/In Note Navigation.md: -------------------------------------------------------------------------------- 1 | ## This is a Heading at the Top 2 | 3 | ```meta-bind-button 4 | label: Scroll to Middle 5 | style: default 6 | actions: 7 | - type: open 8 | link: "[[#This is a Heading in the Middle]]" 9 | newTab: false 10 | 11 | ``` 12 | 13 | ```meta-bind-button 14 | label: Scroll to Bottom 15 | style: default 16 | actions: 17 | - type: open 18 | link: "[[#This is a Heading at the Bottom]]" 19 | newTab: false 20 | 21 | ``` 22 | 23 | ```meta-bind-button 24 | label: Scroll to Labeled Section 25 | style: default 26 | actions: 27 | - type: open 28 | link: "[[#^section-1]]" 29 | newTab: false 30 | 31 | ``` 32 | 33 | some 34 | 35 | very 36 | 37 | long 38 | 39 | text 40 | 41 | some 42 | 43 | very 44 | 45 | long 46 | 47 | text 48 | 49 | ## This is a Heading in the Middle 50 | 51 | some 52 | 53 | very 54 | 55 | long 56 | 57 | text 58 | 59 | some 60 | 61 | very 62 | 63 | long 64 | 65 | text 66 | 67 | some 68 | 69 | very 70 | 71 | long 72 | 73 | text 74 | 75 | some 76 | 77 | very 78 | 79 | long 80 | 81 | text 82 | 83 | ## This is a Heading at the Bottom 84 | 85 | 86 | Some Secion 87 | ^section-1 -------------------------------------------------------------------------------- /exampleVault/Buttons/Templater Buttons.md: -------------------------------------------------------------------------------- 1 | 2 | ```meta-bind-button 3 | label: Insert Text 4 | hidden: false 5 | class: "" 6 | tooltip: "" 7 | id: "" 8 | style: default 9 | action: 10 | type: "replaceSelf" 11 | replacement: "templates/templater/Templater Template.md" 12 | templater: true 13 | ``` 14 | 15 | ```meta-bind-button 16 | label: Insert Text 17 | hidden: false 18 | class: "" 19 | tooltip: "" 20 | id: "" 21 | style: default 22 | action: 23 | type: "replaceSelf" 24 | replacement: "[[Templater Template]]" 25 | templater: true 26 | ``` -------------------------------------------------------------------------------- /exampleVault/Embed Example.md: -------------------------------------------------------------------------------- 1 | --- 2 | completed: false 3 | select: a 4 | --- 5 | 6 | Test Hello 7 | 8 | ```meta-bind-embed 9 | [[Test Template]] 10 | ``` 11 | 12 | test 13 | 14 | ```meta-bind-embed 15 | [[Non Existent Note]] 16 | ``` 17 | 18 | ![[Non Existent Note]] -------------------------------------------------------------------------------- /exampleVault/Input Fields/Date and Time.md: -------------------------------------------------------------------------------- 1 | --- 2 | time: 13:05 3 | date2: 2024-09-29 4 | date1: 2024-03-07 5 | dateTime: 2024-03-13T15:07 6 | --- 7 | 8 | ### Date 9 | ```meta-bind 10 | INPUT[date(showcase):date1] 11 | ``` 12 | 13 | ### Date Picker 14 | 15 | ```meta-bind 16 | INPUT[datePicker(showcase):date2] 17 | ``` 18 | 19 | ```meta-bind 20 | INPUT[datePicker(showcase, defaultValue(null)):date3] 21 | ``` 22 | 23 | ### Time 24 | ```meta-bind 25 | INPUT[time(showcase):time] 26 | ``` 27 | 28 | 29 | ### Date Time 30 | ```meta-bind 31 | INPUT[dateTime(showcase):dateTime] 32 | ``` -------------------------------------------------------------------------------- /exampleVault/Input Fields/Editor.md: -------------------------------------------------------------------------------- 1 | --- 2 | editor: |- 3 | test 4 | 5 | **test** 6 | 7 | # Heading 8 | editor2: |- 9 | asd 10 | 11 | asd 12 | asd 13 | 14 | as 15 | d 16 | asd 17 | as 18 | d 19 | as 20 | da 21 | sd 22 | 23 | asd 24 | 25 | as 26 | d 27 | as 28 | d 29 | as 30 | d 31 | as 32 | d 33 | as 34 | d 35 | a 36 | d 37 | as 38 | d 39 | as 40 | dasda 41 | \\asd 42 | as 43 | d 44 | a 45 | da 46 | s 47 | da 48 | sd 49 | a 50 | d 51 | a 52 | sd 53 | a 54 | sd 55 | 56 | asd 57 | 58 | asd 59 | --- 60 | 61 | ```meta-bind 62 | INPUT[editor(showcase):editor] 63 | ``` 64 | 65 | ```meta-bind 66 | INPUT[editor(showcase):editor2] 67 | ``` 68 | -------------------------------------------------------------------------------- /exampleVault/Input Fields/Image Suggester.md: -------------------------------------------------------------------------------- 1 | --- 2 | image: Other/Images/img_butterfly.webp 3 | --- 4 | 5 | ```meta-bind 6 | INPUT[imageSuggester(optionQuery("Other/Images"), showcase):image] 7 | ``` -------------------------------------------------------------------------------- /exampleVault/Input Fields/Inline Select.md: -------------------------------------------------------------------------------- 1 | --- 2 | select: b 3 | select2: 1 4 | select3: 2 hours 5 | select4: 6 | --- 7 | 8 | ```meta-bind 9 | INPUT[inlineSelect(option(a), option(b), showcase):select] 10 | ``` 11 | 12 | ```meta-bind 13 | INPUT[inlineSelect(option(1, a), option(2, b), showcase):select2] 14 | ``` 15 | 16 | ```meta-bind 17 | INPUT[inlineSelect(option(1 hour, a), option(2 hours, b), showcase):select3] 18 | ``` 19 | 20 | ```meta-bind 21 | INPUT[inlineSelect(option(null, nothing), option(foo, something), showcase):select4] 22 | ``` 23 | -------------------------------------------------------------------------------- /exampleVault/Input Fields/Number.md: -------------------------------------------------------------------------------- 1 | --- 2 | number: 3 | number2: 123 4 | number3: 5 | --- 6 | 7 | ```meta-bind 8 | INPUT[number(showcase):number] 9 | ``` 10 | 11 | ```meta-bind 12 | INPUT[number(showcase):number2] 13 | ``` 14 | 15 | ```meta-bind 16 | INPUT[number(showcase, placeholder(test), defaultValue(-1)):number3] 17 | ``` 18 | 19 | ```meta-bind 20 | INPUT[number(showcase, placeholder(test), defaultValue(null)):number4] 21 | ``` 22 | -------------------------------------------------------------------------------- /exampleVault/Input Fields/Progress Bar.md: -------------------------------------------------------------------------------- 1 | --- 2 | progress1: -6 3 | progress2: 0.7 4 | progress3: 2 5 | progress4: 2.6 6 | progress5: 60 7 | progress6: 75 8 | --- 9 | 10 | ```meta-bind 11 | INPUT[progressBar(showcase, minValue(-10), maxValue(3)):progress1] 12 | ``` 13 | 14 | ```meta-bind 15 | INPUT[progressBar(showcase, minValue(0), maxValue(1), stepSize(0.1)):progress2] 16 | ``` 17 | 18 | ```meta-bind 19 | INPUT[progressBar(showcase, minValue(0), maxValue(10), stepSize(-1)):progress3] 20 | ``` 21 | 22 | ```meta-bind 23 | INPUT[progressBar(showcase, minValue(0), maxValue(10), stepSize(0.1)):progress4] 24 | ``` 25 | 26 | The labels can be hidden if they are not required. 27 | 28 | ```meta-bind 29 | INPUT[progressBar(defaultValue(53), addLabels(false)):progress5] 30 | ``` 31 | With some css-snippets we can change the color of the progress bar. 32 | 33 | ```meta-bind 34 | INPUT[progressBar(defaultValue(53), class(red-progress-bar)):progress6] 35 | ``` 36 | -------------------------------------------------------------------------------- /exampleVault/Input Fields/Select and Multi Select.md: -------------------------------------------------------------------------------- 1 | --- 2 | select: 1 3 | select3: 2 4 | multiSelect: 5 | - option 1 6 | - option 3 7 | multiSelect2: 8 | - option 1 9 | - option 3 10 | multiSelect3: 11 | - 1 12 | - 13 | - false 14 | select2: 15 | --- 16 | 17 | ### Select 18 | 19 | ```meta-bind 20 | INPUT[select( 21 | option(1, option 1), 22 | option(2, option 2), 23 | option(3, option 3), 24 | showcase 25 | ):select] 26 | ``` 27 | 28 | ```meta-bind 29 | INPUT[select( 30 | option(1, option 1), 31 | option(false, option 2), 32 | option(null, option 3), 33 | showcase 34 | ):select2] 35 | ``` 36 | 37 | ```meta-bind 38 | INPUT[select( 39 | option(1, option 1), 40 | option(2, option 2), 41 | option(3, option 3), 42 | option(3, option 3), 43 | option(2, option 2), 44 | showcase 45 | ):select3] 46 | ``` 47 | 48 | ### Multi Select 49 | 50 | ```meta-bind 51 | INPUT[multiSelect( 52 | option(option 1), 53 | option(option 2), 54 | option(option 3), 55 | option(option 3), 56 | option(option 2), 57 | showcase 58 | ):multiSelect2] 59 | ``` 60 | 61 | ```meta-bind 62 | INPUT[multiSelect( 63 | option(1, option 1), 64 | option(false, option 2), 65 | option(null, option 3), 66 | showcase 67 | ):multiSelect3] 68 | ``` -------------------------------------------------------------------------------- /exampleVault/Input Fields/Slider.md: -------------------------------------------------------------------------------- 1 | --- 2 | slider1: 80 3 | slider2: 14 4 | slider3: 227 5 | slider4: 0.1 6 | --- 7 | 8 | ### Simple Slider 9 | 10 | ```meta-bind 11 | INPUT[slider(showcase):slider1] 12 | ``` 13 | 14 | ### Slider with Labels 15 | 16 | ```meta-bind 17 | INPUT[slider(addLabels, showcase):slider1] 18 | ``` 19 | 20 | ### Slider with Custom Min Max Values 21 | 22 | ```meta-bind 23 | INPUT[slider(addLabels, minValue(-20), maxValue(20), showcase):slider2] 24 | ``` 25 | 26 | ```meta-bind 27 | INPUT[slider(addLabels, minValue(0), maxValue(1000), showcase):slider3] 28 | ``` 29 | 30 | ### Slider with Custom Step Size 31 | 32 | ```meta-bind 33 | INPUT[slider(addLabels, minValue(0), maxValue(10), stepSize(0.1), showcase):slider4] 34 | ``` 35 | -------------------------------------------------------------------------------- /exampleVault/Input Fields/Suggester.md: -------------------------------------------------------------------------------- 1 | --- 2 | suggest: option 2 3 | fileSuggest: "[[Other/Example Notes/Example Note with Embeds.md|Example Note with Embeds]]" 4 | fileSuggest2: "[[Example Note with Embeds]]" 5 | fileSuggest3: Example Note with Embeds 6 | --- 7 | 8 | ### Simple Suggester 9 | 10 | ```meta-bind 11 | INPUT[suggester( 12 | option(option 1), 13 | option(option 2), 14 | option(option 3), 15 | showcase 16 | ):suggest] 17 | ``` 18 | 19 | ```meta-bind 20 | INPUT[suggester( 21 | option(option 1), 22 | option(option 2), 23 | option(option 3), 24 | allowOther, 25 | showcase 26 | ):suggest] 27 | ``` 28 | 29 | ### Suggester with Dataview 30 | 31 | Note, that this will error, if dataview is not enabled. 32 | 33 | `INPUT[suggester(optionQuery(#example-note)):fileSuggest]` 34 | 35 | ```meta-bind 36 | INPUT[suggester(optionQuery(#example-note), showcase):fileSuggest] 37 | ``` 38 | 39 | ```meta-bind 40 | INPUT[suggester(optionQuery(#example-note), useLinks(partial), showcase):fileSuggest2] 41 | ``` 42 | 43 | ```meta-bind 44 | INPUT[suggester(optionQuery(#example-note), useLinks(false), showcase):fileSuggest3] 45 | ``` 46 | -------------------------------------------------------------------------------- /exampleVault/Input Fields/Text.md: -------------------------------------------------------------------------------- 1 | --- 2 | text: alnsdksdnfknfnjksdbfhj 3 | textArea: textArea 4 | --- 5 | 6 | ### Text 7 | 8 | ```meta-bind 9 | INPUT[text(showcase):text] 10 | ``` 11 | 12 | ```meta-bind 13 | INPUT[text(showcase, limit(10)):text] 14 | ``` 15 | 16 | ### Text Area 17 | 18 | ```meta-bind 19 | INPUT[textArea(showcase, class(meta-bind-full-width), class(meta-bind-high)):textArea] 20 | ``` 21 | -------------------------------------------------------------------------------- /exampleVault/Input Fields/Toggle.md: -------------------------------------------------------------------------------- 1 | --- 2 | toggle2: 1 3 | toggle1: true 4 | --- 5 | 6 | ```meta-bind 7 | INPUT[toggle(showcase):toggle1] 8 | ``` 9 | 10 | ```meta-bind 11 | INPUT[toggle(showcase, onValue(1), offValue(0), defaultValue(1)):toggle2] 12 | ``` 13 | 14 | -------------------------------------------------------------------------------- /exampleVault/Meta Bind API.md: -------------------------------------------------------------------------------- 1 | --- 2 | select: b 3 | --- 4 | 5 | ## Creating an Input Field with JS Engine 6 | 7 | JS Engine can be found [here](https://github.com/mProjectsCode/obsidian-js-engine-plugin). 8 | 9 | **Code** 10 | ```js 11 | const mb = engine.getPlugin('obsidian-meta-bind-plugin').api; 12 | 13 | const options = ['a', 'b', 'c']; 14 | 15 | const arguments = options.map(x => ({ 16 | name: 'option', 17 | value: [x], 18 | })); 19 | 20 | arguments.push({ 21 | name: 'title', 22 | value: ['I was created using JS Engine and the Meta Bind API'], 23 | }); 24 | 25 | const bindTarget = mb.parseBindTarget('select', context.file.path); 26 | 27 | const mountable = mb.createInputFieldMountable(context.file.path, { 28 | renderChildType: 'block', 29 | declaration: { 30 | inputFieldType: 'select', 31 | bindTarget: bindTarget, 32 | arguments: arguments, 33 | }, 34 | }); 35 | 36 | mb.wrapInMDRC(mountable, container, component); 37 | ``` 38 | 39 | **Resulting Input Field** 40 | ```js-engine 41 | const mb = engine.getPlugin('obsidian-meta-bind-plugin').api; 42 | 43 | const options = ['a', 'b', 'c']; 44 | 45 | const arguments = options.map(x => ({ 46 | name: 'option', 47 | value: [x], 48 | })); 49 | 50 | arguments.push({ 51 | name: 'title', 52 | value: ['I was created using JS Engine and the Meta Bind API'], 53 | }); 54 | 55 | const bindTarget = mb.parseBindTarget('select', context.file.path); 56 | 57 | const mountable = mb.createInputFieldMountable(context.file.path, { 58 | renderChildType: 'block', 59 | declaration: { 60 | inputFieldType: 'select', 61 | bindTarget: bindTarget, 62 | arguments: arguments, 63 | }, 64 | }); 65 | 66 | mb.wrapInMDRC(mountable, container, component); 67 | ``` 68 | -------------------------------------------------------------------------------- /exampleVault/Other Note.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: test 3 | title: test te 4 | select: option a 5 | date: 2023-09-22 6 | time: 19:20 7 | multi-select: 8 | - option a 9 | - option b 10 | text: this is some text 11 | --- 12 | 13 | ## This is another note 14 | This note is to test syncing to another note. 15 | 16 | ### Select 17 | Select 18 | ```meta-bind 19 | INPUT[ 20 | select( 21 | option(option a), 22 | option(option b), 23 | option(option c), 24 | option(option d) 25 | ):select 26 | ] 27 | ``` 28 | 29 | Select with title 30 | ```meta-bind 31 | INPUT[select( 32 | title(select with title), 33 | option(option a), 34 | option(option b), 35 | option(option c), 36 | option(option d) 37 | ):select] 38 | ``` 39 | 40 | -------------------------------------------------------------------------------- /exampleVault/Other/Example Notes/Example Note with Callouts.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: example-note 3 | --- 4 | 5 | This is an example note with callouts. 6 | 7 | >[!INFO] 8 | >This is an info. 9 | 10 | >[!Danger] 11 | >This is danerous. -------------------------------------------------------------------------------- /exampleVault/Other/Example Notes/Example Note with Embeds.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: example-note 3 | --- 4 | 5 | This is an example note with embeds. 6 | 7 | ![[Example Note with Callouts]] 8 | 9 | ![[Example Note with Image]] -------------------------------------------------------------------------------- /exampleVault/Other/Example Notes/Example Note with Image.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: example-note 3 | --- 4 | 5 | This is an example note with an image. 6 | 7 | ![[img_flower.webp]] -------------------------------------------------------------------------------- /exampleVault/Other/Images/img_butterfly.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mProjectsCode/obsidian-meta-bind-plugin/b4450146c74decb0f5bef80e154a7bdceffe4d59/exampleVault/Other/Images/img_butterfly.webp -------------------------------------------------------------------------------- /exampleVault/Other/Images/img_drops.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mProjectsCode/obsidian-meta-bind-plugin/b4450146c74decb0f5bef80e154a7bdceffe4d59/exampleVault/Other/Images/img_drops.jpg -------------------------------------------------------------------------------- /exampleVault/Other/Images/img_flower.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mProjectsCode/obsidian-meta-bind-plugin/b4450146c74decb0f5bef80e154a7bdceffe4d59/exampleVault/Other/Images/img_flower.webp -------------------------------------------------------------------------------- /exampleVault/Other/Images/subfolder/img_frozen_branch.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mProjectsCode/obsidian-meta-bind-plugin/b4450146c74decb0f5bef80e154a7bdceffe4d59/exampleVault/Other/Images/subfolder/img_frozen_branch.jpg -------------------------------------------------------------------------------- /exampleVault/View Fields/JS View Field.md: -------------------------------------------------------------------------------- 1 | --- 2 | number1: 100 3 | number2: 43 4 | result: "**4300** km" 5 | n1clone: 100 6 | cssclasses: 7 | - aa 8 | - test-class 9 | --- 10 | `INPUT[number:number1]` 11 | `INPUT[number:number2]` 12 | 13 | ```meta-bind-js-view 14 | {number1} as n1 15 | {number2} as n2 16 | save to {result} 17 | --- 18 | return engine.markdown.create(`**${context.bound.n1 * context.bound.n2}** km`); 19 | ``` 20 | 21 | ```meta-bind-js-view 22 | {number1} as n1 23 | save to {n1clone} 24 | hidden 25 | --- 26 | return context.bound.n1; 27 | ``` 28 | 29 | You can also dynamically add CSS classes to your note this way. 30 | 31 | ```meta-bind-js-view 32 | {number1} as n1 33 | save to {cssclasses} 34 | --- 35 | const CLASS = 'test-class'; 36 | 37 | let classes = new Set(context.metadata.frontmatter.cssclasses ?? []); 38 | 39 | if (context.bound.n1 >= 100) { 40 | classes.add(CLASS); 41 | } else { 42 | classes.delete(CLASS); 43 | } 44 | 45 | return [...classes.values()]; 46 | ``` 47 | 48 | ## Other Note 49 | 50 | ```meta-bind-js-view 51 | {Other Note#text} as text 52 | --- 53 | return context.bound.text 54 | ``` 55 | -------------------------------------------------------------------------------- /exampleVault/View Fields/Other Note.md: -------------------------------------------------------------------------------- 1 | --- 2 | text: this is a test 3 | --- 4 | -------------------------------------------------------------------------------- /exampleVault/templates/Other Template.md: -------------------------------------------------------------------------------- 1 | This comes from [[Other Template]]. 2 | 3 | ```meta-bind 4 | INPUT[select(option(a), option(b)):select] 5 | ``` -------------------------------------------------------------------------------- /exampleVault/templates/Test Template.md: -------------------------------------------------------------------------------- 1 | --- 2 | foo: barssadad 3 | --- 4 | 5 | This comes from [[Test Template]]. 6 | 7 | Completed: `INPUT[toggle:completed]` 8 | 9 | [[Test Template]] embeds [[Other Template]]. 10 | 11 | ```meta-bind-embed 12 | [[Other Template]] 13 | ``` -------------------------------------------------------------------------------- /exampleVault/templates/templater/Say Hello Command.md: -------------------------------------------------------------------------------- 1 | <%* 2 | new Notice("Hello!"); 3 | -%> -------------------------------------------------------------------------------- /exampleVault/templates/templater/Templater Template.md: -------------------------------------------------------------------------------- 1 | <% tp.file.title %> -------------------------------------------------------------------------------- /exampleVault/testJsFile.js: -------------------------------------------------------------------------------- 1 | new Notice(`Hello ${context.args.greeting} from "${context.jsFile?.path}", ran by "${context.file?.path}"!`); -------------------------------------------------------------------------------- /images/meta-bind-plugin-demo-3-gif.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mProjectsCode/obsidian-meta-bind-plugin/b4450146c74decb0f5bef80e154a7bdceffe4d59/images/meta-bind-plugin-demo-3-gif.gif -------------------------------------------------------------------------------- /manifest-beta.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-meta-bind-plugin", 3 | "name": "Meta Bind", 4 | "version": "1.4.2", 5 | "minAppVersion": "1.4.0", 6 | "description": "Make your notes interactive with inline input fields, metadata displays, and buttons.", 7 | "author": "Moritz Jung", 8 | "authorUrl": "https://www.moritzjung.dev/", 9 | "fundingUrl": "https://github.com/sponsors/mProjectsCode", 10 | "helpUrl": "https://www.moritzjung.dev/obsidian-meta-bind-plugin-docs/", 11 | "isDesktopOnly": false 12 | } 13 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "obsidian-meta-bind-plugin", 3 | "name": "Meta Bind", 4 | "version": "1.4.2", 5 | "minAppVersion": "1.4.0", 6 | "description": "Make your notes interactive with inline input fields, metadata displays, and buttons.", 7 | "author": "Moritz Jung", 8 | "authorUrl": "https://www.moritzjung.dev/", 9 | "fundingUrl": "https://github.com/sponsors/mProjectsCode", 10 | "helpUrl": "https://www.moritzjung.dev/obsidian-meta-bind-plugin-docs/", 11 | "isDesktopOnly": false 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/README.md: -------------------------------------------------------------------------------- 1 | # meta-bind-core 2 | 3 | Core Package for Meta Bind. This package is independent of the Obsidian API. 4 | 5 | ALL COMMANDS SHOULD BE RUN FROM THE ROOT OF THE REPO. 6 | -------------------------------------------------------------------------------- /packages/core/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "meta-bind-core", 3 | "main": "src/index.ts", 4 | "dependencies": {} 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/src/MountableManager.ts: -------------------------------------------------------------------------------- 1 | import type { FieldMountable } from 'packages/core/src/fields/FieldMountable'; 2 | 3 | export class MountableManager { 4 | activeMountables: Map; 5 | 6 | constructor() { 7 | this.activeMountables = new Map(); 8 | } 9 | 10 | unloadFile(filePath: string): void { 11 | for (const mountable of this.activeMountables.values()) { 12 | if (mountable.getFilePath() === filePath) { 13 | MB_DEBUG && 14 | console.debug(`meta-bind | MountableManager >> unregistered Mountable ${mountable.getUuid()}`); 15 | mountable.unmount(); 16 | } 17 | } 18 | } 19 | 20 | unload(): void { 21 | for (const mountable of this.activeMountables.values()) { 22 | MB_DEBUG && console.debug(`meta-bind | MountableManager >> unregistered Mountable ${mountable.getUuid()}`); 23 | mountable.unmount(); 24 | } 25 | } 26 | 27 | registerMountable(mountable: FieldMountable): void { 28 | MB_DEBUG && console.debug(`meta-bind | MountableManager >> registered Mountable ${mountable.getUuid()}`); 29 | this.activeMountables.set(mountable.getUuid(), mountable); 30 | } 31 | 32 | unregisterMountable(mountable: FieldMountable): void { 33 | MB_DEBUG && console.debug(`meta-bind | MountableManager >> unregistered Mountable ${mountable.getUuid()}`); 34 | this.activeMountables.delete(mountable.getUuid()); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/core/src/fields/FieldMountable.ts: -------------------------------------------------------------------------------- 1 | import { Mountable } from 'packages/core/src/utils/Mountable'; 2 | import type { MetaBind } from '..'; 3 | 4 | export abstract class FieldMountable extends Mountable { 5 | readonly mb: MetaBind; 6 | private readonly filePath: string; 7 | private readonly uuid: string; 8 | 9 | constructor(mb: MetaBind, uuid: string, filePath: string) { 10 | super(); 11 | 12 | this.mb = mb; 13 | this.filePath = filePath; 14 | this.uuid = uuid; 15 | } 16 | 17 | getUuid(): string { 18 | return this.uuid; 19 | } 20 | 21 | getFilePath(): string { 22 | return this.filePath; 23 | } 24 | 25 | protected onMount(_targetEl: HTMLElement): void { 26 | this.mb.mountableManager.registerMountable(this); 27 | } 28 | 29 | protected onUnmount(_targetEl: HTMLElement): void { 30 | this.mb.mountableManager.unregisterMountable(this); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /packages/core/src/fields/button/AbstractButtonActionConfig.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from 'packages/core/src'; 2 | import type { 3 | ButtonActionType, 4 | ButtonClickContext, 5 | ButtonConfig, 6 | ButtonContext, 7 | } from 'packages/core/src/config/ButtonConfig'; 8 | 9 | export abstract class AbstractButtonActionConfig { 10 | actionType: ButtonActionType; 11 | mb: MetaBind; 12 | 13 | constructor(actionType: ButtonActionType, mb: MetaBind) { 14 | this.actionType = actionType; 15 | this.mb = mb; 16 | } 17 | 18 | abstract run( 19 | config: ButtonConfig | undefined, 20 | action: T, 21 | filePath: string, 22 | context: ButtonContext, 23 | click: ButtonClickContext, 24 | ): Promise; 25 | 26 | abstract create(): Required; 27 | 28 | abstract getActionLabel(): string; 29 | } 30 | -------------------------------------------------------------------------------- /packages/core/src/fields/button/actions/CommandButtonActionConfig.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from 'packages/core/src'; 2 | import type { 3 | ButtonClickContext, 4 | ButtonConfig, 5 | ButtonContext, 6 | CommandButtonAction, 7 | } from 'packages/core/src/config/ButtonConfig'; 8 | import { ButtonActionType } from 'packages/core/src/config/ButtonConfig'; 9 | import { AbstractButtonActionConfig } from 'packages/core/src/fields/button/AbstractButtonActionConfig'; 10 | 11 | export class CommandButtonActionConfig extends AbstractButtonActionConfig { 12 | constructor(mb: MetaBind) { 13 | super(ButtonActionType.COMMAND, mb); 14 | } 15 | 16 | async run( 17 | _config: ButtonConfig | undefined, 18 | action: CommandButtonAction, 19 | _filePath: string, 20 | _context: ButtonContext, 21 | _click: ButtonClickContext, 22 | ): Promise { 23 | this.mb.internal.executeCommandById(action.command); 24 | } 25 | 26 | create(): Required { 27 | return { type: ButtonActionType.COMMAND, command: '' }; 28 | } 29 | 30 | getActionLabel(): string { 31 | return 'Run a command'; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/core/src/fields/button/actions/CreateNoteButtonActionConfig.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from 'packages/core/src'; 2 | import type { 3 | ButtonClickContext, 4 | ButtonConfig, 5 | ButtonContext, 6 | CreateNoteButtonAction, 7 | } from 'packages/core/src/config/ButtonConfig'; 8 | import { ButtonActionType } from 'packages/core/src/config/ButtonConfig'; 9 | import { AbstractButtonActionConfig } from 'packages/core/src/fields/button/AbstractButtonActionConfig'; 10 | import { ensureFileExtension, joinPath } from 'packages/core/src/utils/Utils'; 11 | 12 | export class CreateNoteButtonActionConfig extends AbstractButtonActionConfig { 13 | constructor(mb: MetaBind) { 14 | super(ButtonActionType.CREATE_NOTE, mb); 15 | } 16 | 17 | async run( 18 | _config: ButtonConfig | undefined, 19 | action: CreateNoteButtonAction, 20 | _filePath: string, 21 | _context: ButtonContext, 22 | click: ButtonClickContext, 23 | ): Promise { 24 | if (action.openIfAlreadyExists) { 25 | const filePath = ensureFileExtension(joinPath(action.folderPath ?? '', action.fileName), 'md'); 26 | // if the file already exists, open it in the same tab 27 | if (await this.mb.file.exists(filePath)) { 28 | await this.mb.file.open(filePath, '', false); 29 | return; 30 | } 31 | } 32 | 33 | await this.mb.file.create( 34 | action.folderPath ?? '', 35 | action.fileName, 36 | 'md', 37 | action.openNote ?? false, 38 | click.openInNewTab(), 39 | ); 40 | } 41 | 42 | create(): Required { 43 | return { 44 | type: ButtonActionType.CREATE_NOTE, 45 | folderPath: '/', 46 | fileName: 'Untitled', 47 | openNote: true, 48 | openIfAlreadyExists: false, 49 | }; 50 | } 51 | 52 | getActionLabel(): string { 53 | return 'Create a new note'; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /packages/core/src/fields/button/actions/InputButtonActionConfig.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from 'packages/core/src'; 2 | import type { 3 | ButtonClickContext, 4 | ButtonConfig, 5 | ButtonContext, 6 | InputButtonAction, 7 | } from 'packages/core/src/config/ButtonConfig'; 8 | import { ButtonActionType } from 'packages/core/src/config/ButtonConfig'; 9 | import { AbstractButtonActionConfig } from 'packages/core/src/fields/button/AbstractButtonActionConfig'; 10 | 11 | export class InputButtonActionConfig extends AbstractButtonActionConfig { 12 | constructor(mb: MetaBind) { 13 | super(ButtonActionType.INPUT, mb); 14 | } 15 | 16 | async run( 17 | _config: ButtonConfig | undefined, 18 | action: InputButtonAction, 19 | _filePath: string, 20 | _context: ButtonContext, 21 | _click: ButtonClickContext, 22 | ): Promise { 23 | const el = document.activeElement; 24 | if (el && el instanceof HTMLInputElement) { 25 | el.setRangeText(action.str, el.selectionStart!, el.selectionEnd!, 'end'); 26 | el.dispatchEvent(new Event('input', { bubbles: true })); 27 | } 28 | } 29 | 30 | create(): Required { 31 | return { type: ButtonActionType.INPUT, str: '' }; 32 | } 33 | 34 | getActionLabel(): string { 35 | return 'Insert text at cursor'; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/core/src/fields/button/actions/JSButtonActionConfig.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from 'packages/core/src'; 2 | import type { 3 | ButtonClickContext, 4 | ButtonConfig, 5 | ButtonContext, 6 | JSButtonAction, 7 | } from 'packages/core/src/config/ButtonConfig'; 8 | import { ButtonActionType } from 'packages/core/src/config/ButtonConfig'; 9 | import { AbstractButtonActionConfig } from 'packages/core/src/fields/button/AbstractButtonActionConfig'; 10 | import { ErrorLevel, MetaBindJsError } from 'packages/core/src/utils/errors/MetaBindErrors'; 11 | 12 | export class JSButtonActionConfig extends AbstractButtonActionConfig { 13 | constructor(mb: MetaBind) { 14 | super(ButtonActionType.JS, mb); 15 | } 16 | 17 | async run( 18 | config: ButtonConfig | undefined, 19 | action: JSButtonAction, 20 | filePath: string, 21 | context: ButtonContext, 22 | click: ButtonClickContext, 23 | ): Promise { 24 | if (!this.mb.getSettings().enableJs) { 25 | throw new MetaBindJsError({ 26 | errorLevel: ErrorLevel.CRITICAL, 27 | effect: "Can't run button action that requires JS evaluation.", 28 | cause: 'JS evaluation is disabled in the plugin settings.', 29 | }); 30 | } 31 | 32 | const configOverrides: Record = { 33 | buttonConfig: structuredClone(config), 34 | args: structuredClone(action.args), 35 | buttonContext: structuredClone(context), 36 | click: structuredClone(click), 37 | }; 38 | const unloadCallback = await this.mb.internal.jsEngineRunFile(action.file, filePath, configOverrides); 39 | unloadCallback(); 40 | } 41 | 42 | create(): Required { 43 | return { type: ButtonActionType.JS, file: '', args: {} }; 44 | } 45 | 46 | getActionLabel(): string { 47 | return 'Run a JavaScript file'; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/core/src/fields/button/actions/OpenButtonActionConfig.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from 'packages/core/src'; 2 | import type { 3 | ButtonClickContext, 4 | ButtonConfig, 5 | ButtonContext, 6 | OpenButtonAction, 7 | } from 'packages/core/src/config/ButtonConfig'; 8 | import { ButtonActionType } from 'packages/core/src/config/ButtonConfig'; 9 | import { AbstractButtonActionConfig } from 'packages/core/src/fields/button/AbstractButtonActionConfig'; 10 | import { MDLinkParser } from 'packages/core/src/parsers/MarkdownLinkParser'; 11 | 12 | export class OpenButtonActionConfig extends AbstractButtonActionConfig { 13 | constructor(mb: MetaBind) { 14 | super(ButtonActionType.OPEN, mb); 15 | } 16 | 17 | async run( 18 | _config: ButtonConfig | undefined, 19 | action: OpenButtonAction, 20 | filePath: string, 21 | _context: ButtonContext, 22 | click: ButtonClickContext, 23 | ): Promise { 24 | const newTab = click.openInNewTab() || (action.newTab ?? false); 25 | const link = MDLinkParser.interpretAsLink(action.link); 26 | if (!link) { 27 | throw new Error('Invalid link'); 28 | } 29 | link.open(this.mb, filePath, newTab); 30 | } 31 | 32 | create(): Required { 33 | return { type: ButtonActionType.OPEN, link: '', newTab: true }; 34 | } 35 | 36 | getActionLabel(): string { 37 | return 'Open a link'; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/core/src/fields/button/actions/RegexpReplaceInNoteButtonActionConfig.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from 'packages/core/src'; 2 | import type { 3 | ButtonClickContext, 4 | ButtonConfig, 5 | ButtonContext, 6 | RegexpReplaceInNoteButtonAction, 7 | } from 'packages/core/src/config/ButtonConfig'; 8 | import { ButtonActionType } from 'packages/core/src/config/ButtonConfig'; 9 | import { AbstractButtonActionConfig } from 'packages/core/src/fields/button/AbstractButtonActionConfig'; 10 | 11 | export class RegexpReplaceInNoteButtonActionConfig extends AbstractButtonActionConfig { 12 | constructor(mb: MetaBind) { 13 | super(ButtonActionType.REGEXP_REPLACE_IN_NOTE, mb); 14 | } 15 | 16 | async run( 17 | _config: ButtonConfig | undefined, 18 | action: RegexpReplaceInNoteButtonAction, 19 | filePath: string, 20 | _context: ButtonContext, 21 | _click: ButtonClickContext, 22 | ): Promise { 23 | if (action.regexp === '') { 24 | throw new Error('Regexp cannot be empty'); 25 | } 26 | 27 | await this.mb.file.atomicModify(filePath, content => { 28 | return content.replace(new RegExp(action.regexp, action.regexpFlags ?? 'g'), action.replacement); 29 | }); 30 | } 31 | 32 | create(): Required { 33 | return { 34 | type: ButtonActionType.REGEXP_REPLACE_IN_NOTE, 35 | regexp: '([A-Z])\\w+', 36 | replacement: 'Replacement text', 37 | regexpFlags: 'g', 38 | }; 39 | } 40 | 41 | getActionLabel(): string { 42 | return 'Replace text in note using regexp'; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /packages/core/src/fields/button/actions/RunTemplaterFileButtonActionConfig.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from 'packages/core/src'; 2 | import type { 3 | ButtonClickContext, 4 | ButtonConfig, 5 | ButtonContext, 6 | RunTemplaterFileButtonAction, 7 | } from 'packages/core/src/config/ButtonConfig'; 8 | import { ButtonActionType } from 'packages/core/src/config/ButtonConfig'; 9 | import { AbstractButtonActionConfig } from 'packages/core/src/fields/button/AbstractButtonActionConfig'; 10 | 11 | export class RunTemplaterFileButtonActionConfig extends AbstractButtonActionConfig { 12 | constructor(mb: MetaBind) { 13 | super(ButtonActionType.RUN_TEMPLATER_FILE, mb); 14 | } 15 | 16 | async run( 17 | _config: ButtonConfig | undefined, 18 | action: RunTemplaterFileButtonAction, 19 | filePath: string, 20 | _context: ButtonContext, 21 | _click: ButtonClickContext, 22 | ): Promise { 23 | const templateFilePath = this.mb.file.resolveFilePathLike(action.templateFile); 24 | void (await this.mb.internal.evaluateTemplaterTemplate(templateFilePath, filePath)); 25 | } 26 | 27 | create(): Required { 28 | return { 29 | type: ButtonActionType.RUN_TEMPLATER_FILE, 30 | templateFile: '', 31 | }; 32 | } 33 | 34 | getActionLabel(): string { 35 | return 'Run a templater file'; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/core/src/fields/button/actions/SleepButtonActionConfig.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from 'packages/core/src'; 2 | import type { 3 | ButtonClickContext, 4 | ButtonConfig, 5 | ButtonContext, 6 | SleepButtonAction, 7 | } from 'packages/core/src/config/ButtonConfig'; 8 | import { ButtonActionType } from 'packages/core/src/config/ButtonConfig'; 9 | import { AbstractButtonActionConfig } from 'packages/core/src/fields/button/AbstractButtonActionConfig'; 10 | 11 | export class SleepButtonActionConfig extends AbstractButtonActionConfig { 12 | constructor(mb: MetaBind) { 13 | super(ButtonActionType.SLEEP, mb); 14 | } 15 | 16 | async run( 17 | _config: ButtonConfig | undefined, 18 | action: SleepButtonAction, 19 | _filePath: string, 20 | _context: ButtonContext, 21 | _click: ButtonClickContext, 22 | ): Promise { 23 | await new Promise(resolve => setTimeout(resolve, action.ms)); 24 | } 25 | 26 | create(): Required { 27 | return { type: ButtonActionType.SLEEP, ms: 0 }; 28 | } 29 | 30 | getActionLabel(): string { 31 | return 'Sleep for some time'; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /packages/core/src/fields/excluded/ExcludedMountable.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from 'packages/core/src'; 2 | import { FieldMountable } from 'packages/core/src/fields/FieldMountable'; 3 | import { DomHelpers, showUnloadedMessage } from 'packages/core/src/utils/Utils'; 4 | 5 | export class ExcludedMountable extends FieldMountable { 6 | constructor(mb: MetaBind, uuid: string, filePath: string) { 7 | super(mb, uuid, filePath); 8 | } 9 | 10 | protected onMount(targetEl: HTMLElement): void { 11 | MB_DEBUG && console.debug('meta-bind | ExcludedMountable >> mount'); 12 | super.onMount(targetEl); 13 | 14 | DomHelpers.empty(targetEl); 15 | 16 | DomHelpers.createElement(targetEl, 'span', { 17 | text: '[META_BIND] This folder has been excluded in the settings', 18 | class: 'mb-error', 19 | }); 20 | } 21 | 22 | protected onUnmount(targetEl: HTMLElement): void { 23 | MB_DEBUG && console.debug('meta-bind | ExcludedMountable >> unmount'); 24 | super.onUnmount(targetEl); 25 | 26 | DomHelpers.empty(targetEl); 27 | 28 | showUnloadedMessage(targetEl, 'Excluded'); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | InputFieldArgumentConfig, 3 | InputFieldArgumentType, 4 | InputFieldType, 5 | } from 'packages/core/src/config/FieldConfigs'; 6 | import { AbstractFieldArgument } from 'packages/core/src/fields/fieldArguments/AbstractFieldArgument'; 7 | 8 | export abstract class AbstractInputFieldArgument extends AbstractFieldArgument< 9 | InputFieldType, 10 | InputFieldArgumentType, 11 | InputFieldArgumentConfig 12 | > {} 13 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/InputFieldArgumentContainer.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | InputFieldArgumentConfig, 3 | InputFieldArgumentType, 4 | InputFieldType, 5 | } from 'packages/core/src/config/FieldConfigs'; 6 | import { AbstractFieldArgumentContainer } from 'packages/core/src/fields/fieldArguments/AbstractFieldArgumentContainer'; 7 | import type { InputFieldArgumentMapType } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/InputFieldArgumentFactory'; 8 | 9 | export class InputFieldArgumentContainer extends AbstractFieldArgumentContainer< 10 | InputFieldType, 11 | InputFieldArgumentType, 12 | InputFieldArgumentConfig 13 | > { 14 | getAll(name: T): NonNullable>[] { 15 | // @ts-ignore 16 | return super.getAll(name); 17 | } 18 | 19 | get(name: T): InputFieldArgumentMapType | undefined { 20 | return this.getAll(name).at(0); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/AddLabelsInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | 6 | export class AddLabelsInputFieldArgument extends AbstractInputFieldArgument { 7 | value: boolean = true; 8 | 9 | override _parseValue(value: ParsingResultNode[]): void { 10 | this.value = value[0] === undefined || value[0]?.value.toLowerCase() === 'true'; 11 | } 12 | 13 | public getConfig(): InputFieldArgumentConfig { 14 | return InputFieldArgumentConfigs.addLabels; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/AllowOtherInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | 6 | export class AllowOtherInputFieldArgument extends AbstractInputFieldArgument { 7 | value: boolean = true; 8 | 9 | override _parseValue(value: ParsingResultNode[]): void { 10 | this.value = value[0] === undefined || value[0]?.value.toLowerCase() === 'true'; 11 | } 12 | 13 | public getConfig(): InputFieldArgumentConfig { 14 | return InputFieldArgumentConfigs.allowOther; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/ClassInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | 6 | export class ClassInputFieldArgument extends AbstractInputFieldArgument { 7 | value: string[] = []; 8 | 9 | _parseValue(value: ParsingResultNode[]): void { 10 | this.value = value[0].value.split(' '); 11 | } 12 | 13 | public getConfig(): InputFieldArgumentConfig { 14 | return InputFieldArgumentConfigs.class; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/DefaultValueInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | import type { MBExtendedLiteral } from 'packages/core/src/utils/Literal'; 6 | import { parseLiteral } from 'packages/core/src/utils/Literal'; 7 | 8 | export class DefaultValueInputFieldArgument extends AbstractInputFieldArgument { 9 | value: MBExtendedLiteral = ''; 10 | 11 | _parseValue(value: ParsingResultNode[]): void { 12 | this.value = parseLiteral(value[0].value); 13 | } 14 | 15 | public getConfig(): InputFieldArgumentConfig { 16 | return InputFieldArgumentConfigs.defaultValue; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/LimitInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | import { DocsUtils } from 'packages/core/src/utils/DocsUtils'; 6 | import { ErrorLevel, MetaBindArgumentError } from 'packages/core/src/utils/errors/MetaBindErrors'; 7 | 8 | export class LimitInputFieldArgument extends AbstractInputFieldArgument { 9 | value: number | undefined = undefined; 10 | 11 | _parseValue(value: ParsingResultNode[]): void { 12 | this.value = Number.parseInt(value[0].value); 13 | if (Number.isNaN(this.value)) { 14 | throw new MetaBindArgumentError({ 15 | errorLevel: ErrorLevel.WARNING, 16 | effect: 'failed to set value for input field argument', 17 | cause: "value of argument 'limit' must be of type number", 18 | docs: [DocsUtils.linkToInputFieldArgument(this.getConfig().type)], 19 | }); 20 | } 21 | if (this.value <= 0) { 22 | throw new MetaBindArgumentError({ 23 | errorLevel: ErrorLevel.WARNING, 24 | effect: 'failed to set value for input field argument', 25 | cause: "value of argument 'limit' must be a positive number", 26 | docs: [DocsUtils.linkToInputFieldArgument(this.getConfig().type)], 27 | }); 28 | } 29 | } 30 | 31 | public getConfig(): InputFieldArgumentConfig { 32 | return InputFieldArgumentConfigs.limit; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/MaxValueInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | import { DocsUtils } from 'packages/core/src/utils/DocsUtils'; 6 | import { ErrorLevel, MetaBindArgumentError } from 'packages/core/src/utils/errors/MetaBindErrors'; 7 | 8 | export class MaxValueInputFieldArgument extends AbstractInputFieldArgument { 9 | value: number = 100; 10 | 11 | _parseValue(value: ParsingResultNode[]): void { 12 | this.value = Number.parseFloat(value[0].value); 13 | if (Number.isNaN(this.value)) { 14 | throw new MetaBindArgumentError({ 15 | errorLevel: ErrorLevel.WARNING, 16 | effect: 'failed to set value for input field argument', 17 | cause: "value of argument 'maxValue' must be of type number", 18 | docs: [DocsUtils.linkToInputFieldArgument(this.getConfig().type)], 19 | }); 20 | } 21 | } 22 | 23 | public getConfig(): InputFieldArgumentConfig { 24 | return InputFieldArgumentConfigs.maxValue; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/MinValueInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | import { DocsUtils } from 'packages/core/src/utils/DocsUtils'; 6 | import { ErrorLevel, MetaBindArgumentError } from 'packages/core/src/utils/errors/MetaBindErrors'; 7 | 8 | export class MinValueInputFieldArgument extends AbstractInputFieldArgument { 9 | value: number = 0; 10 | 11 | _parseValue(value: ParsingResultNode[]): void { 12 | this.value = Number.parseFloat(value[0].value); 13 | if (Number.isNaN(this.value)) { 14 | throw new MetaBindArgumentError({ 15 | errorLevel: ErrorLevel.WARNING, 16 | effect: 'failed to set value for input field argument', 17 | cause: "value of argument 'minValue' must be of type number", 18 | docs: [DocsUtils.linkToInputFieldArgument(this.getConfig().type)], 19 | }); 20 | } 21 | } 22 | 23 | public getConfig(): InputFieldArgumentConfig { 24 | return InputFieldArgumentConfigs.minValue; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/MultiLineInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | 6 | export class MultiLineInputFieldArgument extends AbstractInputFieldArgument { 7 | value: boolean = true; 8 | 9 | override _parseValue(value: ParsingResultNode[]): void { 10 | this.value = value[0] === undefined || value[0]?.value.toLowerCase() === 'true'; 11 | } 12 | 13 | public getConfig(): InputFieldArgumentConfig { 14 | return InputFieldArgumentConfigs.multiLine; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/OffValueInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | import type { MBLiteral } from 'packages/core/src/utils/Literal'; 6 | import { parseLiteral } from 'packages/core/src/utils/Literal'; 7 | 8 | export class OffValueInputFieldArgument extends AbstractInputFieldArgument { 9 | value: MBLiteral = false; 10 | 11 | _parseValue(value: ParsingResultNode[]): void { 12 | this.value = parseLiteral(value[0].value); 13 | } 14 | 15 | public getConfig(): InputFieldArgumentConfig { 16 | return InputFieldArgumentConfigs.offValue; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/OnValueInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | import type { MBLiteral } from 'packages/core/src/utils/Literal'; 6 | import { parseLiteral } from 'packages/core/src/utils/Literal'; 7 | 8 | export class OnValueInputFieldArgument extends AbstractInputFieldArgument { 9 | value: MBLiteral = true; 10 | 11 | _parseValue(value: ParsingResultNode[]): void { 12 | this.value = parseLiteral(value[0].value); 13 | } 14 | 15 | public getConfig(): InputFieldArgumentConfig { 16 | return InputFieldArgumentConfigs.onValue; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/OptionInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | import type { MBLiteral } from 'packages/core/src/utils/Literal'; 6 | import { parseLiteral } from 'packages/core/src/utils/Literal'; 7 | 8 | export class OptionInputFieldArgument extends AbstractInputFieldArgument { 9 | value: MBLiteral = ''; 10 | name: string = ''; 11 | 12 | _parseValue(value: ParsingResultNode[]): void { 13 | if (value.length === 1) { 14 | this.value = parseLiteral(value[0].value); 15 | this.name = value[0].value; 16 | } else if (value.length === 2) { 17 | this.value = parseLiteral(value[0].value); 18 | this.name = value[1].value; 19 | } 20 | } 21 | 22 | public getConfig(): InputFieldArgumentConfig { 23 | return InputFieldArgumentConfigs.option; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/OptionQueryInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | 6 | export class OptionQueryInputFieldArgument extends AbstractInputFieldArgument { 7 | value: string = ''; 8 | 9 | _parseValue(value: ParsingResultNode[]): void { 10 | this.value = value[0].value; 11 | } 12 | 13 | public getConfig(): InputFieldArgumentConfig { 14 | return InputFieldArgumentConfigs.optionQuery; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/PlaceholderInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | 6 | export class PlaceholderInputFieldArgument extends AbstractInputFieldArgument { 7 | value: string = ''; 8 | 9 | _parseValue(value: ParsingResultNode[]): void { 10 | this.value = value[0].value; 11 | } 12 | 13 | public getConfig(): InputFieldArgumentConfig { 14 | return InputFieldArgumentConfigs.placeholder; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/ShowcaseInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | 6 | export class ShowcaseInputFieldArgument extends AbstractInputFieldArgument { 7 | value: boolean = true; 8 | 9 | _parseValue(value: ParsingResultNode[]): void { 10 | this.value = value[0] === undefined || value[0]?.value.toLowerCase() === 'true'; 11 | } 12 | 13 | public getConfig(): InputFieldArgumentConfig { 14 | return InputFieldArgumentConfigs.showcase; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/StepSizeValueInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | import { DocsUtils } from 'packages/core/src/utils/DocsUtils'; 6 | import { ErrorLevel, MetaBindArgumentError } from 'packages/core/src/utils/errors/MetaBindErrors'; 7 | 8 | export class StepSizeValueInputFieldArgument extends AbstractInputFieldArgument { 9 | value: number = 0; 10 | 11 | _parseValue(value: ParsingResultNode[]): void { 12 | this.value = Number.parseFloat(value[0].value); 13 | if (Number.isNaN(this.value)) { 14 | throw new MetaBindArgumentError({ 15 | errorLevel: ErrorLevel.WARNING, 16 | effect: 'failed to set value for input field argument', 17 | cause: "value of argument 'stepSize' must be of type number", 18 | docs: [DocsUtils.linkToInputFieldArgument(this.getConfig().type)], 19 | }); 20 | } 21 | if (this.value <= 0) { 22 | throw new MetaBindArgumentError({ 23 | errorLevel: ErrorLevel.WARNING, 24 | effect: 'failed to set value for input field argument', 25 | cause: "value of argument 'stepSize' must be a positive number", 26 | docs: [DocsUtils.linkToInputFieldArgument(this.getConfig().type)], 27 | }); 28 | } 29 | } 30 | 31 | public getConfig(): InputFieldArgumentConfig { 32 | return InputFieldArgumentConfigs.stepSize; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/TitleInputFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { InputFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/AbstractInputFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | 6 | export class TitleInputFieldArgument extends AbstractInputFieldArgument { 7 | value: string = ''; 8 | 9 | _parseValue(value: ParsingResultNode[]): void { 10 | this.value = value[0].value; 11 | } 12 | 13 | public getConfig(): InputFieldArgumentConfig { 14 | return InputFieldArgumentConfigs.title; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/viewFieldArguments/AbstractViewFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ViewFieldArgumentConfig, 3 | ViewFieldArgumentType, 4 | ViewFieldType, 5 | } from 'packages/core/src/config/FieldConfigs'; 6 | import { AbstractFieldArgument } from 'packages/core/src/fields/fieldArguments/AbstractFieldArgument'; 7 | 8 | export abstract class AbstractViewFieldArgument extends AbstractFieldArgument< 9 | ViewFieldType, 10 | ViewFieldArgumentType, 11 | ViewFieldArgumentConfig 12 | > {} 13 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/viewFieldArguments/ViewFieldArgumentContainer.ts: -------------------------------------------------------------------------------- 1 | import type { 2 | ViewFieldArgumentConfig, 3 | ViewFieldArgumentType, 4 | ViewFieldType, 5 | } from 'packages/core/src/config/FieldConfigs'; 6 | import { AbstractFieldArgumentContainer } from 'packages/core/src/fields/fieldArguments/AbstractFieldArgumentContainer'; 7 | import type { ViewFieldArgumentMapType } from 'packages/core/src/fields/fieldArguments/viewFieldArguments/ViewFieldArgumentFactory'; 8 | 9 | export class ViewFieldArgumentContainer extends AbstractFieldArgumentContainer< 10 | ViewFieldType, 11 | ViewFieldArgumentType, 12 | ViewFieldArgumentConfig 13 | > { 14 | getAll(name: T): NonNullable>[] { 15 | // @ts-ignore 16 | return super.getAll(name); 17 | } 18 | 19 | get(name: T): ViewFieldArgumentMapType | undefined { 20 | return this.getAll(name).at(0); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/viewFieldArguments/argumnets/ClassViewFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { ViewFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { ViewFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractViewFieldArgument } from 'packages/core/src/fields/fieldArguments/viewFieldArguments/AbstractViewFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | 6 | export class ClassViewFieldArgument extends AbstractViewFieldArgument { 7 | value: string[] = []; 8 | 9 | _parseValue(value: ParsingResultNode[]): void { 10 | this.value = value[0].value.split(' '); 11 | } 12 | 13 | public getConfig(): ViewFieldArgumentConfig { 14 | return ViewFieldArgumentConfigs.class; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/viewFieldArguments/argumnets/HiddenViewFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { ViewFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { ViewFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractViewFieldArgument } from 'packages/core/src/fields/fieldArguments/viewFieldArguments/AbstractViewFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | 6 | export class HiddenViewFieldArgument extends AbstractViewFieldArgument { 7 | value: boolean = true; 8 | 9 | _parseValue(value: ParsingResultNode[]): void { 10 | this.value = value[0] === undefined || value[0]?.value.toLowerCase() === 'true'; 11 | } 12 | 13 | public getConfig(): ViewFieldArgumentConfig { 14 | return ViewFieldArgumentConfigs.hidden; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/fields/fieldArguments/viewFieldArguments/argumnets/RenderMarkdownViewFieldArgument.ts: -------------------------------------------------------------------------------- 1 | import type { ViewFieldArgumentConfig } from 'packages/core/src/config/FieldConfigs'; 2 | import { ViewFieldArgumentConfigs } from 'packages/core/src/config/FieldConfigs'; 3 | import { AbstractViewFieldArgument } from 'packages/core/src/fields/fieldArguments/viewFieldArguments/AbstractViewFieldArgument'; 4 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | 6 | export class RenderMarkdownViewFieldArgument extends AbstractViewFieldArgument { 7 | value: boolean = true; 8 | 9 | _parseValue(value: ParsingResultNode[]): void { 10 | this.value = value[0] === undefined || value[0]?.value.toLowerCase() === 'true'; 11 | } 12 | 13 | public getConfig(): ViewFieldArgumentConfig { 14 | return ViewFieldArgumentConfigs.renderMarkdown; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Date/DateComponent.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | props.onValueChange($state.snapshot(value))} /> 14 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Date/DateIPF.ts: -------------------------------------------------------------------------------- 1 | import { AbstractInputField } from 'packages/core/src/fields/inputFields/AbstractInputField'; 2 | import DateComponent from 'packages/core/src/fields/inputFields/fields/Date/DateComponent.svelte'; 3 | import type { InputFieldSvelteComponent } from 'packages/core/src/fields/inputFields/InputFieldSvelteWrapper'; 4 | import { DateParser } from 'packages/core/src/parsers/DateParser'; 5 | import { parseUnknownToString } from 'packages/core/src/utils/Literal'; 6 | 7 | export class DateIPF extends AbstractInputField { 8 | protected filterValue(value: unknown): string | undefined { 9 | return parseUnknownToString(value); 10 | } 11 | 12 | protected getFallbackDefaultValue(): string { 13 | return DateParser.stringify(DateParser.getDefaultDate()); 14 | } 15 | 16 | protected getSvelteComponent(): InputFieldSvelteComponent { 17 | // @ts-ignore 18 | return DateComponent; 19 | } 20 | 21 | protected rawMapValue(value: string): string { 22 | return value; 23 | } 24 | 25 | protected rawReverseMapValue(value: string): string | undefined { 26 | return value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/DatePicker/Calender.svelte: -------------------------------------------------------------------------------- 1 | 33 | 34 |
35 |
36 | {#each getWeekDays() as day} 37 |
38 | {day} 39 |
40 | {/each} 41 |
42 | 43 |
44 | {#each cells as value (genSvelteId())} 45 |
selectCell(value)} 48 | onkeydown={event => selectCellKey(event, value)} 49 | role="button" 50 | tabindex="0" 51 | class:mb-calendar-highlight={value} 52 | class:mb-calendar-content-cell={value} 53 | class:mb-calendar-selected={selectedDate?.year() === year && 54 | selectedDate?.month() === month && 55 | selectedDate?.date() === value} 56 | > 57 | {value || ''} 58 |
59 | {/each} 60 |
61 |
62 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/DatePicker/DatePickerComponent.svelte: -------------------------------------------------------------------------------- 1 | 27 | 28 |
29 | {value ? value.format(props.dateFormat) : 'none'} 30 | 31 |
32 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/DateTime/DateTimeComponent.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | props.onValueChange($state.snapshot(value))} /> 14 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/DateTime/DateTimeIPF.ts: -------------------------------------------------------------------------------- 1 | import { AbstractInputField } from 'packages/core/src/fields/inputFields/AbstractInputField'; 2 | import DateTimeComponent from 'packages/core/src/fields/inputFields/fields/DateTime/DateTimeComponent.svelte'; 3 | import type { InputFieldSvelteComponent } from 'packages/core/src/fields/inputFields/InputFieldSvelteWrapper'; 4 | import { DateParser } from 'packages/core/src/parsers/DateParser'; 5 | import { parseUnknownToString } from 'packages/core/src/utils/Literal'; 6 | 7 | export class DateTimeIPF extends AbstractInputField { 8 | protected filterValue(value: unknown): string | undefined { 9 | return parseUnknownToString(value); 10 | } 11 | 12 | protected getFallbackDefaultValue(): string { 13 | return DateParser.stringify(DateParser.getDefaultDate()); 14 | } 15 | 16 | protected getSvelteComponent(): InputFieldSvelteComponent { 17 | // @ts-ignore 18 | return DateTimeComponent; 19 | } 20 | 21 | protected rawMapValue(value: string): string { 22 | return value; 23 | } 24 | 25 | protected rawReverseMapValue(value: string): string | undefined { 26 | return value; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Editor/EditorComponent.svelte: -------------------------------------------------------------------------------- 1 | 31 | 32 |
focusIn()} 35 | onkeypress={event => focusInOnKey(event)} 36 | role="button" 37 | tabindex="0" 38 | > 39 | {#if editing} 40 | 45 | {:else} 46 | 47 | {/if} 48 |
49 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Editor/EditorIPF.ts: -------------------------------------------------------------------------------- 1 | import { AbstractInputField } from 'packages/core/src/fields/inputFields/AbstractInputField'; 2 | import EditorComponent from 'packages/core/src/fields/inputFields/fields/Editor/EditorComponent.svelte'; 3 | import type { InputFieldSvelteComponent } from 'packages/core/src/fields/inputFields/InputFieldSvelteWrapper'; 4 | import { isLiteral } from 'packages/core/src/utils/Literal'; 5 | 6 | export class EditorIPF extends AbstractInputField { 7 | mdUnloadCallback: (() => void) | undefined; 8 | 9 | protected filterValue(value: unknown): string | undefined { 10 | return isLiteral(value) ? value?.toString() : undefined; 11 | } 12 | 13 | protected getFallbackDefaultValue(): string { 14 | return ''; 15 | } 16 | 17 | protected getSvelteComponent(): InputFieldSvelteComponent { 18 | // @ts-ignore 19 | return EditorComponent; 20 | } 21 | 22 | protected rawReverseMapValue(value: string): string | undefined { 23 | return value; 24 | } 25 | 26 | protected rawMapValue(value: string): string { 27 | return value; 28 | } 29 | 30 | protected getMountArgs(): Record { 31 | return { 32 | render: (el: HTMLElement, value: string) => void this.renderInElement(el, value), 33 | filePath: this.mountable.getFilePath(), 34 | }; 35 | } 36 | 37 | async renderInElement(el: HTMLElement, value: string): Promise { 38 | this.mdUnloadCallback?.(); 39 | el.innerHTML = ''; 40 | this.mdUnloadCallback = await this.mountable.mb.internal.renderMarkdown( 41 | value, 42 | el, 43 | this.mountable.getFilePath(), 44 | ); 45 | } 46 | 47 | protected onUnmount(): void { 48 | super.onUnmount(); 49 | 50 | this.mdUnloadCallback?.(); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Editor/MarkdownRenderComponent.svelte: -------------------------------------------------------------------------------- 1 | 32 | 33 |
34 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/ImageListSuggester/ImageListSuggesterIPF.ts: -------------------------------------------------------------------------------- 1 | import { AbstractInputField } from 'packages/core/src/fields/inputFields/AbstractInputField'; 2 | import ImageListSuggesterComponent from 'packages/core/src/fields/inputFields/fields/ImageListSuggester/ImageListSuggesterComponent.svelte'; 3 | import type { InputFieldSvelteComponent } from 'packages/core/src/fields/inputFields/InputFieldSvelteWrapper'; 4 | import type { MBLiteral } from 'packages/core/src/utils/Literal'; 5 | import { parseUnknownToLiteralArray, stringifyLiteral } from 'packages/core/src/utils/Literal'; 6 | 7 | interface SvelteExports { 8 | pushValue: (value: MBLiteral) => void; 9 | } 10 | 11 | export class ImageListSuggesterIPF extends AbstractInputField { 12 | protected filterValue(value: unknown): MBLiteral[] | undefined { 13 | return parseUnknownToLiteralArray(value); 14 | } 15 | 16 | protected getFallbackDefaultValue(): string[] { 17 | return []; 18 | } 19 | 20 | protected getSvelteComponent(): InputFieldSvelteComponent { 21 | // @ts-ignore 22 | return ImageListSuggesterComponent; 23 | } 24 | 25 | protected rawMapValue(value: string[]): MBLiteral[] { 26 | return value; 27 | } 28 | 29 | protected rawReverseMapValue(value: MBLiteral[]): string[] | undefined { 30 | return value.map(v => stringifyLiteral(v)).filter(v => v !== undefined); 31 | } 32 | 33 | protected getMountArgs(): Record { 34 | return { 35 | showSuggester: () => this.openModal(), 36 | }; 37 | } 38 | 39 | openModal(): void { 40 | this.mountable.mb.internal.openImageSuggesterModal(this, false, (selected: string | undefined) => { 41 | if (selected !== undefined) { 42 | this.svelteWrapper?.getInstance()?.pushValue(selected); 43 | } 44 | }); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/ImageSuggester/ImageSuggesterIPF.ts: -------------------------------------------------------------------------------- 1 | import { AbstractInputField } from 'packages/core/src/fields/inputFields/AbstractInputField'; 2 | import ImageSuggesterComponent from 'packages/core/src/fields/inputFields/fields/ImageSuggester/ImageSuggesterComponent.svelte'; 3 | import type { InputFieldSvelteComponent } from 'packages/core/src/fields/inputFields/InputFieldSvelteWrapper'; 4 | import type { MBLiteral } from 'packages/core/src/utils/Literal'; 5 | import { isLiteral, stringifyLiteral } from 'packages/core/src/utils/Literal'; 6 | 7 | export class ImageSuggesterIPF extends AbstractInputField { 8 | protected filterValue(value: unknown): MBLiteral | undefined { 9 | return isLiteral(value) ? value : undefined; 10 | } 11 | 12 | protected getFallbackDefaultValue(): string | undefined { 13 | return undefined; 14 | } 15 | 16 | protected getSvelteComponent(): InputFieldSvelteComponent { 17 | // @ts-ignore 18 | return ImageSuggesterComponent; 19 | } 20 | 21 | protected rawMapValue(value: string): MBLiteral { 22 | return value; 23 | } 24 | 25 | protected rawReverseMapValue(value: MBLiteral): string | undefined { 26 | return stringifyLiteral(value); 27 | } 28 | 29 | protected getMountArgs(): Record { 30 | return { 31 | showSuggester: () => this.openModal(), 32 | clear: () => this.setInternalValue(undefined), 33 | }; 34 | } 35 | 36 | openModal(): void { 37 | this.mountable.mb.internal.openImageSuggesterModal(this, true, (selected: string | undefined) => 38 | this.setInternalValue(selected), 39 | ); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/InlineList/InlineListIPF.ts: -------------------------------------------------------------------------------- 1 | import { AbstractInputField } from 'packages/core/src/fields/inputFields/AbstractInputField'; 2 | import InlineListComponent from 'packages/core/src/fields/inputFields/fields/InlineList/InlineListComponent.svelte'; 3 | import type { InputFieldSvelteComponent } from 'packages/core/src/fields/inputFields/InputFieldSvelteWrapper'; 4 | import type { MBLiteral } from 'packages/core/src/utils/Literal'; 5 | import { parseUnknownToLiteralArray } from 'packages/core/src/utils/Literal'; 6 | 7 | interface SvelteExports { 8 | pushValue: (value: MBLiteral) => void; 9 | } 10 | 11 | export class InlineListIPF extends AbstractInputField { 12 | protected filterValue(value: unknown): MBLiteral[] | undefined { 13 | return parseUnknownToLiteralArray(value); 14 | } 15 | 16 | protected getFallbackDefaultValue(): MBLiteral[] { 17 | return []; 18 | } 19 | 20 | protected getSvelteComponent(): InputFieldSvelteComponent { 21 | // @ts-ignore 22 | return InlineListComponent; 23 | } 24 | 25 | protected rawMapValue(value: MBLiteral[]): MBLiteral[] { 26 | return value; 27 | } 28 | 29 | protected rawReverseMapValue(value: MBLiteral[]): MBLiteral[] | undefined { 30 | return value; 31 | } 32 | 33 | protected getMountArgs(): Record { 34 | return { 35 | showInput: () => this.openModal(), 36 | }; 37 | } 38 | 39 | openModal(): void { 40 | this.mountable.mb.internal.openTextPromptModal({ 41 | title: 'Meta Bind List', 42 | subTitle: 'Create a new List Element.', 43 | value: '', 44 | multiline: false, 45 | onSubmit: (newElement: MBLiteral) => { 46 | this.svelteWrapper?.getInstance()?.pushValue(newElement); 47 | }, 48 | onCancel: () => {}, 49 | }); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/InlineSelect/InlineSelectComponent.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | 26 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/List/ListIPF.ts: -------------------------------------------------------------------------------- 1 | import { InputFieldArgumentType } from 'packages/core/src/config/FieldConfigs'; 2 | import { AbstractInputField } from 'packages/core/src/fields/inputFields/AbstractInputField'; 3 | import ListComponent from 'packages/core/src/fields/inputFields/fields/List/ListComponent.svelte'; 4 | import type { InputFieldSvelteComponent } from 'packages/core/src/fields/inputFields/InputFieldSvelteWrapper'; 5 | import type { MBLiteral } from 'packages/core/src/utils/Literal'; 6 | import { parseUnknownToLiteralArray } from 'packages/core/src/utils/Literal'; 7 | 8 | interface SvelteExports { 9 | pushValue: (value: MBLiteral) => void; 10 | } 11 | 12 | export class ListIPF extends AbstractInputField { 13 | protected filterValue(value: unknown): MBLiteral[] | undefined { 14 | return parseUnknownToLiteralArray(value); 15 | } 16 | 17 | protected getFallbackDefaultValue(): MBLiteral[] { 18 | return []; 19 | } 20 | 21 | protected getSvelteComponent(): InputFieldSvelteComponent { 22 | // @ts-ignore 23 | return ListComponent; 24 | } 25 | 26 | protected rawMapValue(value: MBLiteral[]): MBLiteral[] { 27 | return value; 28 | } 29 | 30 | protected rawReverseMapValue(value: MBLiteral[]): MBLiteral[] | undefined { 31 | return value; 32 | } 33 | 34 | protected getMountArgs(): Record { 35 | return { 36 | placeholder: this.mountable.getArgument(InputFieldArgumentType.PLACEHOLDER)?.value ?? 'New Entry...', 37 | limit: this.mountable.getArgument(InputFieldArgumentType.LIMIT)?.value, 38 | multiLine: this.mountable.getArgument(InputFieldArgumentType.MULTI_LINE)?.value === true, 39 | }; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Number/NumberComponent.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | props.onValueChange($state.snapshot(value))} 21 | /> 22 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Number/NumberIPF.ts: -------------------------------------------------------------------------------- 1 | import { InputFieldArgumentType } from 'packages/core/src/config/FieldConfigs'; 2 | import { AbstractInputField } from 'packages/core/src/fields/inputFields/AbstractInputField'; 3 | import NumberComponent from 'packages/core/src/fields/inputFields/fields/Number/NumberComponent.svelte'; 4 | import type { InputFieldSvelteComponent } from 'packages/core/src/fields/inputFields/InputFieldSvelteWrapper'; 5 | import { parseUnknownToFloat } from 'packages/core/src/utils/Literal'; 6 | 7 | export class NumberIPF extends AbstractInputField { 8 | protected filterValue(value: unknown): number | null | undefined { 9 | return parseUnknownToFloat(value); 10 | } 11 | 12 | protected getFallbackDefaultValue(): number | null { 13 | return null; 14 | } 15 | 16 | protected getSvelteComponent(): InputFieldSvelteComponent { 17 | // @ts-ignore 18 | return NumberComponent; 19 | } 20 | 21 | protected rawReverseMapValue(value: number | null): number | null | undefined { 22 | return value; 23 | } 24 | 25 | protected rawMapValue(value: number | null): number | null { 26 | return value; 27 | } 28 | 29 | protected getMountArgs(): Record { 30 | return { 31 | placeholder: this.mountable.getArgument(InputFieldArgumentType.PLACEHOLDER)?.value ?? 'Number', 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Select/SelectComponent.svelte: -------------------------------------------------------------------------------- 1 | 32 | 33 | {#each props.options as option} 34 |
{ 40 | if (e.target instanceof HTMLInputElement) { 41 | return; 42 | } 43 | selectOption(option.value); 44 | }} 45 | onkeypress={event => selectOptionOnKey(event, option.value)} 46 | data-value={stringifyLiteral(option.value)} 47 | > 48 | selectOption(option.value)} /> 49 | 50 |
51 | {/each} 52 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Select/SelectIPF.ts: -------------------------------------------------------------------------------- 1 | import { InputFieldArgumentType } from 'packages/core/src/config/FieldConfigs'; 2 | import type { OptionInputFieldArgument } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/arguments/OptionInputFieldArgument'; 3 | import { AbstractInputField } from 'packages/core/src/fields/inputFields/AbstractInputField'; 4 | import SelectComponent from 'packages/core/src/fields/inputFields/fields/Select/SelectComponent.svelte'; 5 | import type { InputFieldMountable } from 'packages/core/src/fields/inputFields/InputFieldMountable'; 6 | import type { InputFieldSvelteComponent } from 'packages/core/src/fields/inputFields/InputFieldSvelteWrapper'; 7 | import type { MBLiteral } from 'packages/core/src/utils/Literal'; 8 | import { parseUnknownToLiteral } from 'packages/core/src/utils/Literal'; 9 | 10 | export class SelectIPF extends AbstractInputField { 11 | options: OptionInputFieldArgument[]; 12 | 13 | constructor(mountable: InputFieldMountable) { 14 | super(mountable); 15 | 16 | this.options = this.mountable.getArguments(InputFieldArgumentType.OPTION); 17 | } 18 | 19 | protected filterValue(value: unknown): MBLiteral | undefined { 20 | return parseUnknownToLiteral(value); 21 | } 22 | 23 | protected getFallbackDefaultValue(): MBLiteral { 24 | return null; 25 | } 26 | 27 | protected getSvelteComponent(): InputFieldSvelteComponent { 28 | // @ts-ignore 29 | return SelectComponent; 30 | } 31 | 32 | protected rawMapValue(value: MBLiteral): MBLiteral { 33 | return value; 34 | } 35 | 36 | protected rawReverseMapValue(value: MBLiteral): MBLiteral | undefined { 37 | return value; 38 | } 39 | 40 | protected getMountArgs(): Record { 41 | return { 42 | options: this.options, 43 | }; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Slider/SliderComponent.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | {#if props.addLabels} 19 | {props.minValue} 20 | {/if} 21 | props.onValueChange($state.snapshot(value))} 31 | /> 32 | {#if props.addLabels} 33 | {props.maxValue} 34 | {/if} 35 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Suggester/SuggesterComponent.svelte: -------------------------------------------------------------------------------- 1 | 35 | 36 |
37 |
38 | {#if mdLink !== undefined} 39 | 40 | {:else} 41 | {str} 42 | {/if} 43 |
44 | 47 | {#if props.allowOther} 48 | 51 | {/if} 52 |
53 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Suggester/SuggesterHelper.ts: -------------------------------------------------------------------------------- 1 | import type { ImageListSuggesterIPF } from 'packages/core/src/fields/inputFields/fields/ImageListSuggester/ImageListSuggesterIPF'; 2 | import type { ImageSuggesterIPF } from 'packages/core/src/fields/inputFields/fields/ImageSuggester/ImageSuggesterIPF'; 3 | import type { InlineListSuggesterIPF } from 'packages/core/src/fields/inputFields/fields/InlineListSuggester/InlineListSuggesterIPF'; 4 | import type { ListSuggesterIPF } from 'packages/core/src/fields/inputFields/fields/ListSuggester/ListSuggesterIPF'; 5 | import type { SuggesterIPF } from 'packages/core/src/fields/inputFields/fields/Suggester/SuggesterIPF'; 6 | 7 | export type SuggesterLikeIFP = SuggesterIPF | ListSuggesterIPF | InlineListSuggesterIPF; 8 | 9 | export type ImageSuggesterLikeIPF = ImageSuggesterIPF | ImageListSuggesterIPF; 10 | 11 | export class SuggesterOption { 12 | value: T; 13 | displayValue: string; 14 | displayDescription?: string; 15 | 16 | constructor(value: T, displayValue: string, displayDescription?: string) { 17 | this.value = value; 18 | this.displayValue = displayValue; 19 | this.displayDescription = displayDescription; 20 | } 21 | 22 | valueAsString(): string { 23 | return this.value?.toString() ?? 'null'; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Text/TextComponent.svelte: -------------------------------------------------------------------------------- 1 | 21 | 22 | props.onValueChange($state.snapshot(value))} 29 | /> 30 | {#if props.limit !== undefined} 31 | props.limit ? 'mb-content-limit-indicator-overflow' : ''}`} 33 | >{getLimitString(value.length, props.limit)} 35 | {/if} 36 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Text/TextIPF.ts: -------------------------------------------------------------------------------- 1 | import { InputFieldArgumentType } from 'packages/core/src/config/FieldConfigs'; 2 | import { AbstractInputField } from 'packages/core/src/fields/inputFields/AbstractInputField'; 3 | import TextComponent from 'packages/core/src/fields/inputFields/fields/Text/TextComponent.svelte'; 4 | import type { InputFieldSvelteComponent } from 'packages/core/src/fields/inputFields/InputFieldSvelteWrapper'; 5 | import { parseUnknownToString } from 'packages/core/src/utils/Literal'; 6 | 7 | export class TextIPF extends AbstractInputField { 8 | protected filterValue(value: unknown): string | undefined { 9 | return parseUnknownToString(value); 10 | } 11 | 12 | protected getFallbackDefaultValue(): string { 13 | return ''; 14 | } 15 | 16 | protected getSvelteComponent(): InputFieldSvelteComponent { 17 | // @ts-ignore 18 | return TextComponent; 19 | } 20 | 21 | protected rawReverseMapValue(value: string): string | undefined { 22 | return value; 23 | } 24 | 25 | protected rawMapValue(value: string): string { 26 | return value; 27 | } 28 | 29 | protected getMountArgs(): Record { 30 | return { 31 | placeholder: this.mountable.getArgument(InputFieldArgumentType.PLACEHOLDER)?.value ?? 'Text', 32 | limit: this.mountable.getArgument(InputFieldArgumentType.LIMIT)?.value, 33 | }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/TextArea/TextAreaComponent.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 24 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/TextArea/TextAreaIPF.ts: -------------------------------------------------------------------------------- 1 | import { InputFieldArgumentType } from 'packages/core/src/config/FieldConfigs'; 2 | import { AbstractInputField } from 'packages/core/src/fields/inputFields/AbstractInputField'; 3 | import TextAreaComponent from 'packages/core/src/fields/inputFields/fields/TextArea/TextAreaComponent.svelte'; 4 | import type { InputFieldSvelteComponent } from 'packages/core/src/fields/inputFields/InputFieldSvelteWrapper'; 5 | import { parseUnknownToString } from 'packages/core/src/utils/Literal'; 6 | 7 | export class TextAreaIPF extends AbstractInputField { 8 | protected filterValue(value: unknown): string | undefined { 9 | return parseUnknownToString(value); 10 | } 11 | 12 | protected getFallbackDefaultValue(): string { 13 | return ''; 14 | } 15 | 16 | protected getSvelteComponent(): InputFieldSvelteComponent { 17 | // @ts-ignore 18 | return TextAreaComponent; 19 | } 20 | 21 | protected rawReverseMapValue(value: string): string | undefined { 22 | return value; 23 | } 24 | 25 | protected rawMapValue(value: string): string { 26 | return value; 27 | } 28 | 29 | protected getMountArgs(): Record { 30 | return { 31 | placeholder: this.mountable.getArgument(InputFieldArgumentType.PLACEHOLDER)?.value ?? 'Text', 32 | limit: this.mountable.getArgument(InputFieldArgumentType.LIMIT)?.value, 33 | }; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Time/TimeComponent.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | props.onValueChange($state.snapshot(value))} /> 14 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Time/TimeIPF.ts: -------------------------------------------------------------------------------- 1 | import { AbstractInputField } from 'packages/core/src/fields/inputFields/AbstractInputField'; 2 | import TimeComponent from 'packages/core/src/fields/inputFields/fields/Time/TimeComponent.svelte'; 3 | import type { InputFieldSvelteComponent } from 'packages/core/src/fields/inputFields/InputFieldSvelteWrapper'; 4 | import { parseUnknownToString } from 'packages/core/src/utils/Literal'; 5 | 6 | export class TimeIPF extends AbstractInputField { 7 | protected filterValue(value: unknown): string | undefined { 8 | return parseUnknownToString(value); 9 | } 10 | 11 | protected getFallbackDefaultValue(): string { 12 | return '00:00'; 13 | } 14 | 15 | protected getSvelteComponent(): InputFieldSvelteComponent { 16 | // @ts-ignore 17 | return TimeComponent; 18 | } 19 | 20 | protected rawMapValue(value: string): string { 21 | return value; 22 | } 23 | 24 | protected rawReverseMapValue(value: string): string | undefined { 25 | return value; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/src/fields/inputFields/fields/Toggle/ToggleComponent.svelte: -------------------------------------------------------------------------------- 1 | 23 | 24 |
toggleValue()} 31 | onkeydown={event => toggleValueOnKey(event)} 32 | > 33 | 34 |
35 | -------------------------------------------------------------------------------- /packages/core/src/fields/metaBindTable/MetaBindTableComponent.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 |
24 | 25 | 26 | 27 | {#each tableHead as headCell} 28 | 29 | {/each} 30 | 31 | 32 | 33 | 34 | {#each tableRows as tableRow (tableRow.index)} 35 | 36 | {#if tableRow.isValid} 37 | {#each tableRow.cells as cell} 38 | 41 | {/each} 42 | {:else} 43 | 44 | {/if} 45 | 46 | 51 | 52 | {/each} 53 | 54 |
{headCell}
39 | 40 | invalid data 47 | 50 |
55 |
56 | 57 | 58 | -------------------------------------------------------------------------------- /packages/core/src/fields/viewFields/ViewFieldFactory.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from 'packages/core/src'; 2 | import { ViewFieldType } from 'packages/core/src/config/FieldConfigs'; 3 | import { ImageVF } from 'packages/core/src/fields/viewFields/fields/ImageVF'; 4 | import { LinkVF } from 'packages/core/src/fields/viewFields/fields/LinkVF'; 5 | import { MathVF } from 'packages/core/src/fields/viewFields/fields/MathVF'; 6 | import { TextVF } from 'packages/core/src/fields/viewFields/fields/TextVF'; 7 | import type { ViewFieldMountable } from 'packages/core/src/fields/viewFields/ViewFieldMountable'; 8 | import { expectType } from 'packages/core/src/utils/Utils'; 9 | 10 | export type ViewField = MathVF | TextVF | LinkVF | ImageVF; 11 | 12 | export class ViewFieldFactory { 13 | mb: MetaBind; 14 | 15 | constructor(mb: MetaBind) { 16 | this.mb = mb; 17 | } 18 | 19 | createViewField(mountable: ViewFieldMountable): ViewField | undefined { 20 | // Skipped: Date, Time, Image Suggester 21 | 22 | const type = mountable.declaration.viewFieldType; 23 | 24 | if (type === ViewFieldType.MATH) { 25 | return new MathVF(mountable); 26 | } else if (type === ViewFieldType.TEXT) { 27 | return new TextVF(mountable); 28 | } else if (type === ViewFieldType.LINK) { 29 | return new LinkVF(mountable); 30 | } else if (type === ViewFieldType.IMAGE) { 31 | return new ImageVF(mountable); 32 | } 33 | 34 | expectType(type); 35 | 36 | return undefined; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /packages/core/src/fields/viewFields/ViewFieldVariable.ts: -------------------------------------------------------------------------------- 1 | import type { BindTargetDeclaration } from 'packages/core/src/parsers/bindTargetParser/BindTargetDeclaration'; 2 | import type { Signal } from 'packages/core/src/utils/Signal'; 3 | 4 | export interface ViewFieldVariable { 5 | bindTargetDeclaration: BindTargetDeclaration; 6 | metadataSignal: Signal; 7 | uuid: string; 8 | contextName: string | undefined; 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/metadata/BindTargetScope.ts: -------------------------------------------------------------------------------- 1 | import type { BindTargetDeclaration } from 'packages/core/src/parsers/bindTargetParser/BindTargetDeclaration'; 2 | 3 | export class BindTargetScope { 4 | scope: BindTargetDeclaration; 5 | 6 | constructor(scope: BindTargetDeclaration) { 7 | this.scope = scope; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/metadata/IMetadataSubscription.ts: -------------------------------------------------------------------------------- 1 | import type { BindTargetDeclaration } from 'packages/core/src/parsers/bindTargetParser/BindTargetDeclaration'; 2 | 3 | /** 4 | * Interface for a metadata subscription. 5 | */ 6 | export interface IMetadataSubscription { 7 | /** 8 | * UUID for identification. 9 | */ 10 | uuid: string; 11 | /** 12 | * Whether the subscription has been deleted. Used as a flag to prevent double deletion. 13 | */ 14 | deleted: boolean; 15 | /** 16 | * The property the subscription is bound to and thus watches. 17 | */ 18 | bindTarget: BindTargetDeclaration | undefined; 19 | /** 20 | * Unsubscribes from the cache. 21 | */ 22 | unsubscribe: () => void; 23 | /** 24 | * Called when the metadata manager wats to delete the subscription. 25 | */ 26 | delete: () => void; 27 | /** 28 | * Called by the metadata manager when the cache receives an update that concerns this subscription. 29 | * This is NOT called with a clone of the value, so the subscription should not modify the value. 30 | * If the subscription can't guarantee that the value won't be modified, it should clone the value. 31 | * 32 | * @param value 33 | * @returns Whether the subscription has updated, i.e. the value was different from the current memorized value. 34 | */ 35 | onUpdate: (value: unknown) => boolean; 36 | /** 37 | * Whether the subscription can be updated by the metadata manager. 38 | */ 39 | updatable(): boolean; 40 | /** 41 | * Returns the dependencies of this subscription or an empty array if there are none. 42 | */ 43 | getDependencies: () => BindTargetDeclaration[]; 44 | } 45 | -------------------------------------------------------------------------------- /packages/core/src/metadata/MetadataCacheItem.ts: -------------------------------------------------------------------------------- 1 | import type { IMetadataSubscription } from 'packages/core/src/metadata/IMetadataSubscription'; 2 | import type { Metadata } from 'packages/core/src/metadata/MetadataSource'; 3 | 4 | export interface IMetadataCacheItem { 5 | subscriptions: IMetadataSubscription[]; 6 | 7 | /** 8 | * The cycles since the last change to the cache by the plugin. 9 | */ 10 | externalWriteLock: number; 11 | /** 12 | * Whether the cache was changed by the plugin. If this is true, the frontmatter should be updated. 13 | */ 14 | dirty: boolean; 15 | /** 16 | * The cycles that the cache has been inactive, meaning no listener registered to it. 17 | */ 18 | cyclesWithoutListeners: number; 19 | } 20 | 21 | export interface FilePathMetadataCacheItem extends IMetadataCacheItem { 22 | storagePath: string; 23 | data: Metadata; 24 | } 25 | 26 | export interface GlobalMetadataCacheItem extends IMetadataCacheItem { 27 | data: Metadata; 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/src/modals/IModal.ts: -------------------------------------------------------------------------------- 1 | export interface IModal { 2 | open(): void; 3 | 4 | close(): void; 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/src/modals/ModalContent.ts: -------------------------------------------------------------------------------- 1 | import type { IModal } from 'packages/core/src/modals/IModal'; 2 | import { ErrorLevel, MetaBindInternalError } from 'packages/core/src/utils/errors/MetaBindErrors'; 3 | import { Mountable } from 'packages/core/src/utils/Mountable'; 4 | 5 | export abstract class ModalContent extends Mountable { 6 | private modal: IModal | undefined; 7 | 8 | /** 9 | * Set the modal reference that this content is displayed in. 10 | * 11 | * @param modal 12 | */ 13 | public setModal(modal: IModal): void { 14 | this.modal = modal; 15 | } 16 | 17 | /** 18 | * Close the modal that this content is displayed in. 19 | */ 20 | public closeModal(): void { 21 | if (this.modal !== undefined) { 22 | this.modal.close(); 23 | } else { 24 | throw new MetaBindInternalError({ 25 | errorLevel: ErrorLevel.CRITICAL, 26 | effect: 'Failed to close modal', 27 | cause: 'Modal reference in ModalContent is undefined', 28 | }); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/core/src/modals/SelectModalContent.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from '..'; 2 | 3 | export abstract class SelectModalContent { 4 | readonly mb: MetaBind; 5 | private readonly selectCallback: (value: T) => void; 6 | 7 | constructor(mb: MetaBind, selectCallback: (value: T) => void) { 8 | this.mb = mb; 9 | this.selectCallback = selectCallback; 10 | } 11 | 12 | /** 13 | * Get the text to display for the given item. 14 | * 15 | * @param item 16 | */ 17 | abstract getItemText(item: T): string; 18 | 19 | /** 20 | * Get the description to display for the given item. 21 | * 22 | * @param item 23 | */ 24 | abstract getItemDescription(item: T): string | undefined; 25 | 26 | /** 27 | * Get the items to display in the select modal. 28 | */ 29 | abstract getItems(): T[]; 30 | 31 | public onSelected(item: T): void { 32 | this.selectCallback(item); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/ImageSuggesterModalCard.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 |
props.onSelect(props.image)} 20 | onkeydown={event => keySelect(event, props.image)} 21 | role="button" 22 | tabindex="0" 23 | > 24 | {props.image} 25 | 28 |
29 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/ImageSuggesterModalComponent.svelte: -------------------------------------------------------------------------------- 1 | 37 | 38 |
39 | 40 |
41 |
42 | {#each filteredOptions as option} 43 | 44 | {/each} 45 |
46 | 47 | 48 | {#if canSelectNone} 49 | 50 | {/if} 51 | 52 | 53 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/SvelteModalContent.ts: -------------------------------------------------------------------------------- 1 | import { ModalContent } from 'packages/core/src/modals/ModalContent'; 2 | import { DomHelpers } from 'packages/core/src/utils/Utils'; 3 | import type { Component as SvelteComponent } from 'svelte'; 4 | import { unmount } from 'svelte'; 5 | 6 | export type SvelteModalComponentFn = ( 7 | modal: SvelteModalContent, 8 | targetEl: HTMLElement, 9 | ) => ReturnType; 10 | 11 | export class SvelteModalContent extends ModalContent { 12 | component: ReturnType | undefined; 13 | createComponent: SvelteModalComponentFn; 14 | 15 | constructor(createComponent: SvelteModalComponentFn) { 16 | super(); 17 | 18 | this.createComponent = createComponent; 19 | } 20 | 21 | protected onMount(targetEl: HTMLElement): void { 22 | DomHelpers.empty(targetEl); 23 | 24 | this.component = this.createComponent(this, targetEl); 25 | } 26 | 27 | protected onUnmount(targetEl: HTMLElement): void { 28 | if (this.component) { 29 | void unmount(this.component); 30 | } 31 | 32 | DomHelpers.empty(targetEl); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/TextPromptModalContent.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 |

17 | {options.subTitle} 18 |

19 | 20 |
21 | {#if options.multiline} 22 | 23 | {:else} 24 | 25 | {/if} 26 |
27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/buttonBuilder/CommandActionSettings.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/buttonBuilder/CreateNoteActionSettings.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/buttonBuilder/InlineJsActionSettings.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 |
19 | 20 |
21 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/buttonBuilder/InputActionSettings.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/buttonBuilder/InsertIntoNoteActionSettings.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 |
24 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/buttonBuilder/JSActionSettings.svelte: -------------------------------------------------------------------------------- 1 | 25 | 26 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/buttonBuilder/OpenActionSettings.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/buttonBuilder/RegexpReplaceInNoteActionSettings.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 |
28 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/buttonBuilder/ReplaceInNoteActionSettings.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 |
28 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/buttonBuilder/ReplaceSelfActionSettings.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 |
18 | 19 |
20 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/buttonBuilder/RunTemplaterFileActionSettings.svelte: -------------------------------------------------------------------------------- 1 | 23 | 24 | 28 | 31 | 32 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/buttonBuilder/SleepActionSettings.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/core/src/modals/modalContents/buttonBuilder/UpdateMetadataActionSettings.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /packages/core/src/modals/selectModalContents/CommandSelectModal.ts: -------------------------------------------------------------------------------- 1 | import type { Command } from 'packages/core/src/api/InternalAPI'; 2 | import { SelectModalContent } from 'packages/core/src/modals/SelectModalContent'; 3 | 4 | export class CommandSelectModal extends SelectModalContent { 5 | public getItemText(item: Command): string { 6 | return item.name; 7 | } 8 | 9 | public getItemDescription(_: Command): string | undefined { 10 | return undefined; 11 | } 12 | 13 | public getItems(): Command[] { 14 | return this.mb.internal.getAllCommands(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/core/src/modals/selectModalContents/FileSelectModal.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from 'packages/core/src'; 2 | import { SelectModalContent } from 'packages/core/src/modals/SelectModalContent'; 3 | 4 | export class FileSelectModal extends SelectModalContent { 5 | readonly filterFunction?: (filePath: string) => boolean; 6 | 7 | constructor(mb: MetaBind, selectCallback: (value: string) => void, filterFunction?: (filePath: string) => boolean) { 8 | super(mb, selectCallback); 9 | this.filterFunction = filterFunction; 10 | } 11 | 12 | public getItemText(item: string): string { 13 | return item; 14 | } 15 | 16 | public getItemDescription(_: string): string | undefined { 17 | return undefined; 18 | } 19 | 20 | public getItems(): string[] { 21 | if (this.filterFunction !== undefined) { 22 | return this.mb.file.getAllFiles().filter(f => this.filterFunction!(f)); 23 | } else { 24 | return this.mb.file.getAllFiles(); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /packages/core/src/modals/selectModalContents/FolderSelectModal.ts: -------------------------------------------------------------------------------- 1 | import { SelectModalContent } from 'packages/core/src/modals/SelectModalContent'; 2 | 3 | export class FolderSelectModal extends SelectModalContent { 4 | public getItemText(item: string): string { 5 | return item; 6 | } 7 | 8 | public getItemDescription(_: string): string | undefined { 9 | return undefined; 10 | } 11 | 12 | public getItems(): string[] { 13 | return this.mb.file.getAllFolders(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /packages/core/src/modals/selectModalContents/SuggesterSelectModal.ts: -------------------------------------------------------------------------------- 1 | import type { MetaBind } from 'packages/core/src'; 2 | import type { 3 | SuggesterLikeIFP, 4 | SuggesterOption, 5 | } from 'packages/core/src/fields/inputFields/fields/Suggester/SuggesterHelper'; 6 | import { SelectModalContent } from 'packages/core/src/modals/SelectModalContent'; 7 | import type { MBLiteral } from 'packages/core/src/utils/Literal'; 8 | 9 | export class SuggesterSelectModal extends SelectModalContent> { 10 | ipf: SuggesterLikeIFP; 11 | 12 | constructor(mb: MetaBind, selectCallback: (value: SuggesterOption) => void, ipf: SuggesterLikeIFP) { 13 | super(mb, selectCallback); 14 | this.ipf = ipf; 15 | } 16 | 17 | public getItemText(item: SuggesterOption): string { 18 | return item.displayValue; 19 | } 20 | 21 | public getItemDescription(item: SuggesterOption): string | undefined { 22 | return item.displayDescription; 23 | } 24 | 25 | public getItems(): SuggesterOption[] { 26 | return this.mb.internal.getSuggesterOptions(this.ipf); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/core/src/parsers/DateParser.ts: -------------------------------------------------------------------------------- 1 | import type { Moment } from 'moment'; 2 | import moment from 'moment'; 3 | 4 | export class DateParser { 5 | public static dateFormat: string; 6 | 7 | public static stringify(date: Moment): string { 8 | return date.format(this.dateFormat); 9 | } 10 | 11 | public static parse(dateString: string): Moment { 12 | return moment(dateString, DateParser.dateFormat); 13 | } 14 | 15 | public static getDefaultDate(): Moment { 16 | return moment(new Date()); 17 | } 18 | 19 | public static getDefaultDay(): number { 20 | return new Date().getDate(); 21 | } 22 | 23 | public static getDefaultMonth(): number { 24 | return 1; 25 | } 26 | 27 | public static getDefaultYear(): number { 28 | return new Date().getFullYear(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/core/src/parsers/FieldDeclaration.ts: -------------------------------------------------------------------------------- 1 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 2 | import type { ErrorCollection } from 'packages/core/src/utils/errors/ErrorCollection'; 3 | 4 | export interface FieldDeclaration { 5 | declarationString?: string | undefined; 6 | errorCollection: ErrorCollection; 7 | } 8 | 9 | export interface SimpleFieldArgument { 10 | name: string; 11 | value: string[]; 12 | } 13 | 14 | export interface UnvalidatedFieldArgument { 15 | name: ParsingResultNode; 16 | value: ParsingResultNode[]; 17 | } 18 | -------------------------------------------------------------------------------- /packages/core/src/parsers/bindTargetParser/BindTargetDeclaration.ts: -------------------------------------------------------------------------------- 1 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 2 | import type { PropAccessType } from 'packages/core/src/utils/prop/PropAccess'; 3 | import type { PropPath } from 'packages/core/src/utils/prop/PropPath'; 4 | 5 | export interface BindTargetDeclaration { 6 | storageType: string; 7 | storagePath: string; 8 | storageProp: PropPath; 9 | listenToChildren: boolean; 10 | } 11 | 12 | export interface UnvalidatedBindTargetDeclaration { 13 | storageType?: ParsingResultNode | undefined; 14 | storagePath?: ParsingResultNode | undefined; 15 | storageProp: UnvalidatedPropAccess[]; 16 | listenToChildren: boolean; 17 | } 18 | 19 | export interface UnvalidatedPropAccess { 20 | type: PropAccessType; 21 | prop: ParsingResultNode; 22 | } 23 | 24 | export interface SimplePropAccess { 25 | type: PropAccessType; 26 | prop: string; 27 | } 28 | 29 | export enum BindTargetStorageType { 30 | FRONTMATTER = 'frontmatter', 31 | MEMORY = 'memory', 32 | GLOBAL_MEMORY = 'globalMemory', 33 | SCOPE = 'scope', 34 | } 35 | -------------------------------------------------------------------------------- /packages/core/src/parsers/inputFieldParser/ITemplateSupplier.ts: -------------------------------------------------------------------------------- 1 | export interface TemplateSupplierTemplate { 2 | readonly name: string; 3 | readonly template: Readonly; 4 | } 5 | 6 | export interface ITemplateSupplier { 7 | getTemplate(templateName: string): Readonly | undefined; 8 | } 9 | -------------------------------------------------------------------------------- /packages/core/src/parsers/inputFieldParser/InputFieldDeclaration.ts: -------------------------------------------------------------------------------- 1 | import type { InputFieldType } from 'packages/core/src/config/FieldConfigs'; 2 | import type { InputFieldArgumentContainer } from 'packages/core/src/fields/fieldArguments/inputFieldArguments/InputFieldArgumentContainer'; 3 | import type { 4 | BindTargetDeclaration, 5 | UnvalidatedBindTargetDeclaration, 6 | } from 'packages/core/src/parsers/bindTargetParser/BindTargetDeclaration'; 7 | import type { 8 | FieldDeclaration, 9 | SimpleFieldArgument, 10 | UnvalidatedFieldArgument, 11 | } from 'packages/core/src/parsers/FieldDeclaration'; 12 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 13 | 14 | export interface InputFieldDeclaration extends FieldDeclaration { 15 | inputFieldType: InputFieldType; 16 | bindTarget: BindTargetDeclaration | undefined; 17 | argumentContainer: InputFieldArgumentContainer; 18 | } 19 | 20 | export interface PartialUnvalidatedInputFieldDeclaration { 21 | inputFieldType?: ParsingResultNode | undefined; 22 | templateName?: ParsingResultNode | undefined; 23 | bindTarget?: UnvalidatedBindTargetDeclaration | undefined; 24 | arguments: UnvalidatedFieldArgument[]; 25 | } 26 | 27 | export interface UnvalidatedInputFieldDeclaration extends PartialUnvalidatedInputFieldDeclaration, FieldDeclaration {} 28 | 29 | export interface SimpleInputFieldDeclaration { 30 | inputFieldType?: InputFieldType | undefined; 31 | templateName?: string | undefined; 32 | bindTarget?: BindTargetDeclaration | undefined; 33 | arguments?: SimpleFieldArgument[] | undefined; 34 | } 35 | -------------------------------------------------------------------------------- /packages/core/src/parsers/nomParsers/FieldArgumentNomParsers.ts: -------------------------------------------------------------------------------- 1 | import type { Parser } from '@lemons_dev/parsinom/lib/Parser'; 2 | import { P_UTILS } from '@lemons_dev/parsinom/lib/ParserUtils'; 3 | import { P } from '@lemons_dev/parsinom/lib/ParsiNOM'; 4 | import type { UnvalidatedFieldArgument } from 'packages/core/src/parsers/FieldDeclaration'; 5 | import type { ParsingResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 6 | import { 7 | createResultNode, 8 | P_Ident, 9 | P_SingleQuotedString, 10 | } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 11 | 12 | export const P_NonStringArgumentValue: Parser = P.regexp(/^[^()',]+/).describe( 13 | 'any character except parentheses, single quotation marks and commas', 14 | ); 15 | export const P_ArgumentValue: Parser = P.or(P_SingleQuotedString, P_NonStringArgumentValue).node( 16 | createResultNode, 17 | ); 18 | export const P_ArgumentValues: Parser = P.separateBy( 19 | P_ArgumentValue, 20 | P.string(',').describe('argument value separator ","').trim(P_UTILS.optionalWhitespace()), 21 | ); 22 | 23 | export const P_FieldArgument: Parser = P.sequenceMap( 24 | (name, value): UnvalidatedFieldArgument => { 25 | return { 26 | name: name, 27 | value: value, 28 | }; 29 | }, 30 | P_Ident.node(createResultNode), 31 | P_ArgumentValues.trim(P_UTILS.optionalWhitespace()) 32 | .wrap(P.string('(').describe('argument value paren "("'), P.string(')').describe('argument value paren ")"')) 33 | .optional([] as ParsingResultNode[]), 34 | ); 35 | export const P_FieldArguments: Parser = P.separateBy( 36 | P_FieldArgument, 37 | P.string(',').describe('argument separator ","').trim(P_UTILS.optionalWhitespace()), 38 | ); 39 | -------------------------------------------------------------------------------- /packages/core/src/parsers/nomParsers/MiscNomParsers.ts: -------------------------------------------------------------------------------- 1 | import type { Parser } from '@lemons_dev/parsinom/lib/Parser'; 2 | import { P_UTILS } from '@lemons_dev/parsinom/lib/ParserUtils'; 3 | import { P } from '@lemons_dev/parsinom/lib/ParsiNOM'; 4 | import { P_Ident } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 5 | import { LineNumberExpression, lineNumberOpFromString } from 'packages/core/src/utils/LineNumberExpression'; 6 | 7 | export const P_float: Parser = P.sequenceMap( 8 | (sign, number) => (sign === undefined ? number : -number), 9 | P.string('-').optional(), 10 | P.or( 11 | P.sequenceMap((a, b, c) => Number(a + b + c), P_UTILS.digits(), P.string('.'), P_UTILS.digits()), 12 | P_UTILS.digits().map(x => Number(x)), 13 | ), 14 | ).thenEof(); 15 | 16 | export const P_int: Parser = P.sequenceMap( 17 | (sign, number) => (sign === undefined ? number : -number), 18 | P.string('-').optional(), 19 | P_UTILS.digits().map(x => Number(x)), 20 | ).thenEof(); 21 | 22 | export const P_lineNumberExpression = P.or( 23 | P.sequenceMap( 24 | (ident, op, number) => new LineNumberExpression(ident, lineNumberOpFromString(op), number), 25 | P_Ident, 26 | P.or(P.string('+'), P.string('-')).trim(P_UTILS.optionalWhitespace()), 27 | P_int, 28 | ), 29 | P_Ident.map(ident => new LineNumberExpression(ident, undefined, undefined)), 30 | P_int.map(number => new LineNumberExpression(undefined, undefined, number)), 31 | ); 32 | -------------------------------------------------------------------------------- /packages/core/src/parsers/syntaxHighlighting/HLPUtils.ts: -------------------------------------------------------------------------------- 1 | import type { Parser } from '@lemons_dev/parsinom/lib/Parser'; 2 | import { P } from '@lemons_dev/parsinom/lib/ParsiNOM'; 3 | import { Highlight } from 'packages/core/src/parsers/syntaxHighlighting/Highlight'; 4 | 5 | export enum MB_TokenClass { 6 | IDENT = 'ident', 7 | CONTROL = 'control', 8 | STRING = 'string', 9 | KEYWORD = 'keyword', 10 | ERROR = 'error', 11 | } 12 | 13 | export class HLPUtils { 14 | static sequence(...parsers: Parser[]): Parser { 15 | return P.sequenceMap( 16 | (...highlights) => { 17 | return highlights.flat(2).filter((x): x is Highlight => x !== undefined); 18 | }, 19 | ...parsers, 20 | ); 21 | } 22 | 23 | static separateBy(parser: Parser, separator: Parser): Parser { 24 | return HLPUtils.sequence(parser, HLPUtils.sequence(separator, parser).many()).optional([]); 25 | } 26 | 27 | static highlight(parser: Parser, tokenClass: MB_TokenClass): Parser { 28 | return parser.node((_node, range) => { 29 | return [new Highlight(range, tokenClass)]; 30 | }); 31 | } 32 | 33 | static highlightStr(str: string, tokenClass: MB_TokenClass): Parser { 34 | return HLPUtils.highlight(P.string(str), tokenClass); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/core/src/parsers/syntaxHighlighting/Highlight.ts: -------------------------------------------------------------------------------- 1 | import type { ParsingRange } from '@lemons_dev/parsinom/lib/HelperTypes'; 2 | import type { MB_TokenClass } from 'packages/core/src/parsers/syntaxHighlighting/HLPUtils'; 3 | 4 | export class Highlight { 5 | readonly range: ParsingRange; 6 | readonly tokenClass: MB_TokenClass; 7 | 8 | constructor(range: ParsingRange, tokenClass: MB_TokenClass) { 9 | this.range = range; 10 | this.tokenClass = tokenClass; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /packages/core/src/parsers/syntaxHighlighting/SyntaxHighlighting.ts: -------------------------------------------------------------------------------- 1 | import type { ParsingPosition } from '@lemons_dev/parsinom/lib/HelperTypes'; 2 | import type { ParsingError } from 'packages/core/src/parsers/ParsingError'; 3 | import { Highlight } from 'packages/core/src/parsers/syntaxHighlighting/Highlight'; 4 | import { MB_TokenClass } from 'packages/core/src/parsers/syntaxHighlighting/HLPUtils'; 5 | 6 | export class SyntaxHighlighting { 7 | str: string; 8 | highlights: Highlight[]; 9 | parsingError?: ParsingError; 10 | 11 | constructor(str: string, highlights: Highlight[], parsingError?: ParsingError) { 12 | this.str = str; 13 | this.highlights = highlights.filter(x => x.range.from.index !== x.range.to.index); 14 | this.parsingError = parsingError; 15 | } 16 | 17 | getHighlights(): Highlight[] { 18 | if (this.parsingError === undefined) { 19 | return this.highlights; 20 | } 21 | 22 | let errorTo: ParsingPosition; 23 | 24 | if (this.str[this.parsingError.parseFailure.furthest.index] === '\n') { 25 | errorTo = { 26 | index: this.parsingError.parseFailure.furthest.index + 1, 27 | column: 1, 28 | line: this.parsingError.parseFailure.furthest.line + 1, 29 | }; 30 | } else { 31 | errorTo = { 32 | index: this.parsingError.parseFailure.furthest.index + 1, 33 | column: this.parsingError.parseFailure.furthest.column + 1, 34 | line: this.parsingError.parseFailure.furthest.line, 35 | }; 36 | } 37 | 38 | return [ 39 | new Highlight( 40 | { 41 | from: this.parsingError.parseFailure.furthest, 42 | to: errorTo, 43 | }, 44 | MB_TokenClass.ERROR, 45 | ), 46 | ]; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /packages/core/src/utils/IContextMenu.ts: -------------------------------------------------------------------------------- 1 | export interface ContextMenuItemDefinition { 2 | name: string; 3 | icon?: string; 4 | warning?: boolean; 5 | onclick: () => void; 6 | } 7 | 8 | export interface IContextMenu { 9 | setItems(items: ContextMenuItemDefinition[]): void; 10 | 11 | show(x: number, y: number): void; 12 | 13 | showWithEvent(event: MouseEvent): void; 14 | } 15 | -------------------------------------------------------------------------------- /packages/core/src/utils/IFuzzySearch.ts: -------------------------------------------------------------------------------- 1 | export interface IFuzzySearch { 2 | setSearch(query: string): void; 3 | 4 | filterItems(items: T[], getItemText: (item: T) => string): T[]; 5 | } 6 | -------------------------------------------------------------------------------- /packages/core/src/utils/IJsRenderer.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * A JS renderer that should be initialized with a container, a file path and a code string. 3 | * Every time that evaluate is called, the code string is executed and the result is returned, as well as rendered into the container. 4 | */ 5 | export interface IJsRenderer { 6 | /** 7 | * The container element that the result of the evaluation should be rendered to. 8 | */ 9 | containerEl: HTMLElement; 10 | /** 11 | * The code string that should be evaluated. 12 | */ 13 | code: string; 14 | 15 | /** 16 | * Evaluate the code string with the given context. 17 | * Should return the result of the evaluation as well as render the result to the container. 18 | * 19 | * @param context 20 | */ 21 | evaluate(context: Record): Promise; 22 | 23 | unload(): void; 24 | } 25 | -------------------------------------------------------------------------------- /packages/core/src/utils/MathJS.ts: -------------------------------------------------------------------------------- 1 | import type { MathJsInstance, ConfigOptions } from 'mathjs'; 2 | import { create as MathJSCreate, all } from 'mathjs'; 3 | 4 | export function createMathJS(): MathJsInstance { 5 | // TODO: we should probably limit the functionality of MathJS 6 | // we don't need full support for matrices, big numbers and all the other high level stuff 7 | const options: ConfigOptions = {}; 8 | return MathJSCreate(all, options); 9 | } 10 | -------------------------------------------------------------------------------- /packages/core/src/utils/RefCounter.ts: -------------------------------------------------------------------------------- 1 | export class RefCounter { 2 | private readonly value: T; 3 | private count: number; 4 | 5 | constructor(value: T) { 6 | this.value = value; 7 | this.count = 1; 8 | } 9 | 10 | getValue(): T { 11 | return this.value; 12 | } 13 | 14 | increment(): number { 15 | this.count += 1; 16 | return this.count; 17 | } 18 | 19 | decrement(): number { 20 | this.count -= 1; 21 | return this.count; 22 | } 23 | 24 | getCount(): number { 25 | return this.count; 26 | } 27 | 28 | isEmpty(): boolean { 29 | return this.count === 0; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/core/src/utils/components/Button.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 23 | 24 | 36 | -------------------------------------------------------------------------------- /packages/core/src/utils/components/FlexRow.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 |
20 | {@render children()} 21 |
22 | -------------------------------------------------------------------------------- /packages/core/src/utils/components/Icon.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 18 | 19 | {#if iconName.length > 0} 20 |
21 | {/if} 22 | -------------------------------------------------------------------------------- /packages/core/src/utils/components/ImageCard.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 |
18 | {image} 19 |
20 | -------------------------------------------------------------------------------- /packages/core/src/utils/components/ImageGrid.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 |
19 | {#each images as image} 20 | 21 | {:else} 22 | No images 23 | {/each} 24 |
25 | -------------------------------------------------------------------------------- /packages/core/src/utils/components/LinkComponent.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | {#if mdLink.alias} 15 | 16 | {mdLink.alias} 17 | 18 | {:else} 19 | 20 | {linkHref} 21 | 22 | {/if} 23 | -------------------------------------------------------------------------------- /packages/core/src/utils/components/LinkListComponent.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | {#if mdLinkList.length === 0} 18 | 19 | {:else if mdLinkList.length === 1} 20 | 21 | {:else} 22 | 23 | {#snippet children(element)} 24 | 25 | {/snippet} 26 | 27 | {/if} 28 | -------------------------------------------------------------------------------- /packages/core/src/utils/components/ListWrapper.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | {#each elements.slice(0, elements.length - 1) as element} 14 | {@render children(element)}, 15 | 16 | {/each} 17 | {@render children(elements[elements.length - 1])} 18 | -------------------------------------------------------------------------------- /packages/core/src/utils/components/LiteralRenderComponent.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | {#if typeof parsedValue === 'string'} 15 | {parsedValue} 16 | {:else if Array.isArray(parsedValue)} 17 | 18 | 19 | {#snippet children(element)} 20 | {#if typeof element === 'string'} 21 | {element} 22 | {:else} 23 | 24 | {/if} 25 | {/snippet} 26 | 27 | 28 | {:else} 29 | 30 | {/if} 31 | -------------------------------------------------------------------------------- /packages/core/src/utils/components/ModalButtonGroup.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 12 | 13 | 16 | -------------------------------------------------------------------------------- /packages/core/src/utils/components/MountableComponent.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 |
26 | -------------------------------------------------------------------------------- /packages/core/src/utils/components/SettingComponent.svelte: -------------------------------------------------------------------------------- 1 | 2 | 3 | 20 | 21 |
22 |
23 |
{name}
24 |
{description}
25 |
26 |
27 | {#if children} 28 | {@render children()} 29 | {/if} 30 |
31 |
32 | -------------------------------------------------------------------------------- /packages/core/src/utils/components/Toggle.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 |
(checked = !boolChecked)} 18 | onkeydown={e => { 19 | if (e.key === ' ') { 20 | checked = !boolChecked; 21 | } 22 | }} 23 | > 24 | 25 |
26 | -------------------------------------------------------------------------------- /packages/core/src/utils/errors/ErrorCollectionComponent.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | {#if settings.text} 13 |

{settings.text}

14 | {/if} 15 | 16 | {#if settings.code} 17 |
{settings.code}
18 | {/if} 19 | 20 | {#if settings.errorCollection.hasErrors()} 21 |
Errors
22 | {#if settings.errorText} 23 |

{settings.errorText}

24 | {/if} 25 | {#each settings.errorCollection.getErrors() as error} 26 | 27 | {/each} 28 | {/if} 29 | {#if settings.errorCollection.hasWarnings()} 30 |
Warnings
31 | {#if settings.warningText} 32 |

{settings.warningText}

33 | {/if} 34 | {#each settings.errorCollection.getWarnings() as warning} 35 | 36 | {/each} 37 | {/if} 38 | -------------------------------------------------------------------------------- /packages/core/src/utils/errors/MetaBindErrorComponent.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | {#if error instanceof MetaBindError} 12 |
13 |
14 | {error.errorLevel} 16 | [{error.getErrorType()}] 18 | - {error.effect} 19 |
20 | {#if error.positionContext} 21 |
{error.positionContext}
22 | {/if} 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | {#if error.tip} 34 | 35 | 36 | 37 | 38 | {/if} 39 | {#if error.docs} 40 | 41 | 42 | 47 | 48 | {/if} 49 | {#if error.context} 50 | 51 | 52 | 57 | 58 | {/if} 59 | 60 |
Cause{error.cause}
Effect{error.effect}
Tip{error.tip}
Docs 43 | {#each error.docs as doc} 44 | {doc}
45 | {/each} 46 |
Context 53 |
{JSON.stringify(error.context, null, 4)}
56 |
61 |
62 | {:else} 63 |
64 |
{error.stack}
65 |
66 | {/if} 67 | -------------------------------------------------------------------------------- /packages/core/src/utils/playground/InputFieldExampleComponent.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/core/src/utils/playground/ViewFieldExampleComponent.svelte: -------------------------------------------------------------------------------- 1 | 29 | 30 |
31 |

{declaration.title}

32 | 33 | 34 | 35 | 36 | {declaration.display} 37 |
38 | -------------------------------------------------------------------------------- /packages/core/src/utils/prop/PropParser.ts: -------------------------------------------------------------------------------- 1 | import type { UnvalidatedPropAccess } from 'packages/core/src/parsers/bindTargetParser/BindTargetDeclaration'; 2 | import { toResultNode } from 'packages/core/src/parsers/nomParsers/GeneralNomParsers'; 3 | import { P_int } from 'packages/core/src/parsers/nomParsers/MiscNomParsers'; 4 | import { PropAccess, PropAccessType } from 'packages/core/src/utils/prop/PropAccess'; 5 | import { PropPath } from 'packages/core/src/utils/prop/PropPath'; 6 | 7 | export function parsePropPath(arr: string[]): PropPath { 8 | return new PropPath( 9 | arr.map(x => { 10 | if (P_int.tryParse(x).success) { 11 | return new PropAccess(PropAccessType.ARRAY, x); 12 | } else { 13 | return new PropAccess(PropAccessType.OBJECT, x); 14 | } 15 | }), 16 | ); 17 | } 18 | 19 | export function parsePropPathUnvalidated(arr: string[]): UnvalidatedPropAccess[] { 20 | return arr.map(x => { 21 | if (P_int.tryParse(x).success) { 22 | return { 23 | prop: toResultNode(x), 24 | type: PropAccessType.ARRAY, 25 | }; 26 | } else { 27 | return { 28 | prop: toResultNode(x), 29 | type: PropAccessType.OBJECT, 30 | }; 31 | } 32 | }); 33 | } 34 | -------------------------------------------------------------------------------- /packages/core/src/utils/prop/PropUtils.ts: -------------------------------------------------------------------------------- 1 | import type { PropAccessResult } from 'packages/core/src/utils/prop/PropAccess'; 2 | import type { PropPath } from 'packages/core/src/utils/prop/PropPath'; 3 | 4 | export class PropUtils { 5 | static get(obj: unknown, path: PropPath): unknown { 6 | return path.get(obj).child; 7 | } 8 | 9 | static tryGet(obj: unknown, path: PropPath): unknown { 10 | return path.tryGet(obj)?.child; 11 | } 12 | 13 | static fullGet(obj: unknown, path: PropPath): PropAccessResult { 14 | return path.get(obj); 15 | } 16 | 17 | static set(obj: unknown, path: PropPath, value: unknown): void { 18 | path.set(obj, value); 19 | } 20 | 21 | static setAndCreate(obj: unknown, path: PropPath, value: unknown): void { 22 | path.setAndCreate(obj, value); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /packages/obsidian/README.md: -------------------------------------------------------------------------------- 1 | # meta-bind-obsidian 2 | 3 | This is the meta bind adapter package for Obsidian. 4 | 5 | ALL COMMANDS SHOULD BE RUN FROM THE ROOT OF THE REPO. 6 | -------------------------------------------------------------------------------- /packages/obsidian/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mProjectsCode/obsidian-meta-bind-plugin/b4450146c74decb0f5bef80e154a7bdceffe4d59/packages/obsidian/bun.lockb -------------------------------------------------------------------------------- /packages/obsidian/extraTypes/Dataview.d.ts: -------------------------------------------------------------------------------- 1 | // custom types for the Dataview plugin 2 | 3 | declare module 'obsidian-dataview' { 4 | export interface DataviewApi { 5 | pages: (query: string, file: string) => DataArray>; 6 | } 7 | 8 | export interface DataArray { 9 | forEach: (callback: (value: T) => void) => void; 10 | } 11 | 12 | export type DataObject = Record; 13 | export type Literal = boolean | number | string | Literal[] | DataObject | Function | null | HTMLElement; 14 | } 15 | -------------------------------------------------------------------------------- /packages/obsidian/extraTypes/Templater.d.ts: -------------------------------------------------------------------------------- 1 | import type { Plugin, TFile, TFolder } from 'obsidian'; 2 | import type { Templater_RunMode } from 'packages/obsidian/src/ObsUtils'; 3 | 4 | export type TemplaterPlugin = Plugin & { 5 | templater: Templater; 6 | }; 7 | 8 | export interface Templater { 9 | create_running_config( 10 | template_file: TFile | undefined, 11 | target_file: TFile, 12 | run_mode: Templater_RunMode, 13 | ): Templater_RunningConfig; 14 | 15 | read_and_parse_template(config: Templater_RunningConfig): Promise; 16 | 17 | create_new_note_from_template( 18 | template: TFile | string, 19 | folder?: TFolder, 20 | filename?: string, 21 | open_new_note?: boolean, 22 | ): Promise; 23 | } 24 | 25 | export interface Templater_RunningConfig { 26 | template_file: TFile | undefined; 27 | target_file: TFile; 28 | run_mode: Templater_RunMode; 29 | active_file?: TFile | null; 30 | } 31 | -------------------------------------------------------------------------------- /packages/obsidian/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "meta-bind-obsidian", 3 | "dependencies": { 4 | "@codemirror/lang-javascript": "^6.2.1", 5 | "@codemirror/language": "6.9.2", 6 | "@codemirror/state": "6.3.1", 7 | "@codemirror/view": "6.22.0", 8 | "obsidian": "latest" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /packages/obsidian/src/FuzzySearch.ts: -------------------------------------------------------------------------------- 1 | import { prepareFuzzySearch } from 'obsidian'; 2 | import type { IFuzzySearch } from 'packages/core/src/utils/IFuzzySearch'; 3 | 4 | export class FuzzySearch implements IFuzzySearch { 5 | preparedSearch: ReturnType | undefined; 6 | 7 | constructor() {} 8 | 9 | public setSearch(query: string): void { 10 | this.preparedSearch = prepareFuzzySearch(query); 11 | } 12 | 13 | public filterItems(items: T[], getItemText: (item: T) => string): T[] { 14 | if (this.preparedSearch) { 15 | return items.filter(item => this.preparedSearch?.(getItemText(item))?.score != null); 16 | } 17 | return items; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /packages/obsidian/src/MountableMDRC.ts: -------------------------------------------------------------------------------- 1 | import { MarkdownRenderChild } from 'obsidian'; 2 | import type { Mountable } from 'packages/core/src/utils/Mountable'; 3 | import type { ObsMetaBind } from 'packages/obsidian/src/main.ts'; 4 | 5 | export class MountableMDRC extends MarkdownRenderChild { 6 | readonly mb: ObsMetaBind; 7 | readonly mountable: Mountable; 8 | 9 | constructor(mb: ObsMetaBind, mountable: Mountable, containerEl: HTMLElement) { 10 | super(containerEl); 11 | 12 | this.mb = mb; 13 | this.mountable = mountable; 14 | 15 | this.mountable.registerUnmountCb(() => { 16 | this.unload(); 17 | }); 18 | } 19 | 20 | onload(): void { 21 | this.mountable.mount(this.containerEl); 22 | 23 | super.onload(); 24 | } 25 | 26 | onunload(): void { 27 | // prevent double unmounting in some cases 28 | if (this.mountable.isMounted()) { 29 | this.mountable.unmount(); 30 | } 31 | 32 | super.onunload(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /packages/obsidian/src/ObsContextMenu.ts: -------------------------------------------------------------------------------- 1 | import { Menu } from 'obsidian'; 2 | import type { ContextMenuItemDefinition, IContextMenu } from 'packages/core/src/utils/IContextMenu'; 3 | 4 | export class ObsContextMenu implements IContextMenu { 5 | menu: Menu; 6 | 7 | constructor() { 8 | this.menu = new Menu(); 9 | } 10 | 11 | public setItems(items: ContextMenuItemDefinition[]): void { 12 | for (const item of items) { 13 | this.menu.addItem(menuItem => { 14 | menuItem.setTitle(item.name); 15 | if (item.icon) { 16 | menuItem.setIcon(item.icon); 17 | } 18 | if (item.warning) { 19 | menuItem.setWarning(item.warning); 20 | } 21 | menuItem.onClick(item.onclick); 22 | }); 23 | } 24 | } 25 | 26 | public show(x: number, y: number): void { 27 | this.menu.showAtPosition({ x, y }, document); 28 | } 29 | 30 | public showWithEvent(event: MouseEvent): void { 31 | this.menu.showAtMouseEvent(event); 32 | event.stopImmediatePropagation(); 33 | event.preventDefault(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/obsidian/src/ObsNotePosition.ts: -------------------------------------------------------------------------------- 1 | import type { MarkdownPostProcessorContext } from 'obsidian'; 2 | import type { LinePosition } from 'packages/core/src/config/APIConfigs'; 3 | import { NotePosition } from 'packages/core/src/config/APIConfigs'; 4 | 5 | export class ObsNotePosition extends NotePosition { 6 | ctx: MarkdownPostProcessorContext; 7 | element: HTMLElement; 8 | 9 | constructor(ctx: MarkdownPostProcessorContext, element: HTMLElement) { 10 | super(undefined); 11 | 12 | this.ctx = ctx; 13 | this.element = element; 14 | } 15 | 16 | public getPosition(): LinePosition | undefined { 17 | const pos = this.ctx.getSectionInfo(this.element); 18 | 19 | if (!pos) { 20 | return undefined; 21 | } 22 | 23 | return { 24 | lineStart: pos.lineStart, 25 | lineEnd: pos.lineEnd, 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/obsidian/src/ObsUtils.ts: -------------------------------------------------------------------------------- 1 | import type { API as JsEngineAPI } from 'jsEngine/api/API'; 2 | import type JsEnginePlugin from 'jsEngine/main'; 3 | import type { Plugin } from 'obsidian'; 4 | import type { DataviewApi } from 'obsidian-dataview'; 5 | import type { Templater, TemplaterPlugin } from 'packages/obsidian/extraTypes/Templater'; 6 | import type { ObsMetaBind } from 'packages/obsidian/src/main'; 7 | 8 | export function getDataViewPluginAPI(mb: ObsMetaBind): DataviewApi { 9 | const dataViewPlugin = mb.dependencyManager.checkDependency('dataview'); 10 | return (dataViewPlugin as Plugin & { api: DataviewApi }).api; 11 | } 12 | 13 | export function getJsEnginePluginAPI(mb: ObsMetaBind): JsEngineAPI { 14 | const jsEnginePlugin = mb.dependencyManager.checkDependency('js-engine'); 15 | return (jsEnginePlugin as JsEnginePlugin).api; 16 | } 17 | 18 | export enum Templater_RunMode { 19 | CreateNewFromTemplate, 20 | AppendActiveFile, 21 | OverwriteFile, 22 | OverwriteActiveFile, 23 | DynamicProcessor, 24 | StartupTemplate, 25 | } 26 | 27 | export function getTemplaterPluginAPI(mb: ObsMetaBind): Templater { 28 | const templaterPlugin = mb.dependencyManager.checkDependency('templater-obsidian'); 29 | return (templaterPlugin as TemplaterPlugin).templater; 30 | } 31 | -------------------------------------------------------------------------------- /packages/obsidian/src/cm6/Cm6_Widgets.ts: -------------------------------------------------------------------------------- 1 | import type { EditorView } from '@codemirror/view'; 2 | import { WidgetType } from '@codemirror/view'; 3 | import type { Component } from 'obsidian'; 4 | import type { InlineFieldType } from 'packages/core/src/config/APIConfigs'; 5 | import type { ObsMetaBind } from 'packages/obsidian/src/main'; 6 | import type { MountableMDRC } from 'packages/obsidian/src/MountableMDRC'; 7 | 8 | export class MarkdownRenderChildWidget extends WidgetType { 9 | mb: ObsMetaBind; 10 | type: InlineFieldType; 11 | content: string; 12 | filePath: string; 13 | parentComponent: Component; 14 | renderChild?: MountableMDRC; 15 | 16 | constructor(type: InlineFieldType, content: string, filePath: string, component: Component, mb: ObsMetaBind) { 17 | super(); 18 | this.type = type; 19 | this.content = content; 20 | this.filePath = filePath; 21 | this.parentComponent = component; 22 | this.mb = mb; 23 | } 24 | 25 | eq(other: MarkdownRenderChildWidget): boolean { 26 | return other.content === this.content; 27 | } 28 | 29 | public toDOM(_: EditorView): HTMLElement { 30 | const span = document.createElement('span'); 31 | span.addClass('cm-inline-code'); 32 | 33 | const mountable = this.mb.api.createInlineFieldOfTypeFromString( 34 | this.type, 35 | this.content, 36 | this.filePath, 37 | undefined, 38 | ); 39 | 40 | this.renderChild = this.mb.api.wrapInMDRC(mountable, span, this.parentComponent); 41 | 42 | return span; 43 | } 44 | 45 | public destroy(dom: HTMLElement): void { 46 | this.renderChild?.unload(); 47 | super.destroy(dom); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /packages/obsidian/src/dependencies/Dependency.ts: -------------------------------------------------------------------------------- 1 | import type { Version } from 'packages/obsidian/src/dependencies/Version'; 2 | 3 | export interface Dependency { 4 | name: string; 5 | pluginId: string; 6 | /** 7 | * The minimum version, inclusive. 8 | */ 9 | minVersion: Version; 10 | /** 11 | * The maximum version, exclusive. 12 | */ 13 | maxVersion?: Version; 14 | } 15 | -------------------------------------------------------------------------------- /packages/obsidian/src/docsExports.ts: -------------------------------------------------------------------------------- 1 | export * from 'packages/obsidian/src/ObsAPI'; 2 | export * from 'packages/core/src/config/APIConfigs'; 3 | export * from 'packages/core/src/config/FieldConfigs'; 4 | export * from 'packages/core/src/config/ButtonConfig'; 5 | 6 | export { Mountable } from 'packages/core/src/utils/Mountable'; 7 | export { FieldMountable } from 'packages/core/src/fields/FieldMountable'; 8 | 9 | export * from 'packages/core/src/parsers/FieldDeclaration'; 10 | export * from 'packages/core/src/parsers/inputFieldParser/InputFieldDeclaration'; 11 | export * from 'packages/core/src/parsers/bindTargetParser/BindTargetDeclaration'; 12 | export * from 'packages/core/src/parsers/viewFieldParser/ViewFieldDeclaration'; 13 | export type { 14 | ButtonGroupDeclaration, 15 | ButtonDeclaration, 16 | SimpleButtonGroupDeclaration, 17 | } from 'packages/core/src/parsers/ButtonParser'; 18 | -------------------------------------------------------------------------------- /packages/obsidian/src/modals/ObsModal.ts: -------------------------------------------------------------------------------- 1 | import { Modal } from 'obsidian'; 2 | import type { ModalOptions } from 'packages/core/src/api/InternalAPI'; 3 | import type { IModal } from 'packages/core/src/modals/IModal'; 4 | import type { ModalContent } from 'packages/core/src/modals/ModalContent'; 5 | import { DomHelpers } from 'packages/core/src/utils/Utils'; 6 | import type { ObsMetaBind } from 'packages/obsidian/src/main'; 7 | 8 | export class ObsModal extends Modal implements IModal { 9 | content: ModalContent; 10 | options: ModalOptions | undefined; 11 | 12 | constructor(mb: ObsMetaBind, content: ModalContent, options?: ModalOptions) { 13 | super(mb.app); 14 | 15 | this.options = options; 16 | this.content = content; 17 | content.setModal(this); 18 | } 19 | 20 | onOpen(): void { 21 | if (this.options?.title) { 22 | this.titleEl.setText(this.options.title); 23 | } 24 | 25 | if (this.options?.classes) { 26 | DomHelpers.addClasses(this.modalEl, this.options.classes); 27 | } 28 | 29 | this.content.mount(this.contentEl); 30 | } 31 | 32 | onClose(): void { 33 | this.content.unmount(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /packages/obsidian/src/modals/ObsSearchModal.ts: -------------------------------------------------------------------------------- 1 | import type { FuzzyMatch } from 'obsidian'; 2 | import { FuzzySuggestModal, renderResults } from 'obsidian'; 3 | import type { IModal } from 'packages/core/src/modals/IModal'; 4 | import type { SelectModalContent } from 'packages/core/src/modals/SelectModalContent'; 5 | import type { ObsMetaBind } from 'packages/obsidian/src/main'; 6 | 7 | export class ObsSearchModal extends FuzzySuggestModal implements IModal { 8 | private content: SelectModalContent; 9 | 10 | constructor(mb: ObsMetaBind, content: SelectModalContent) { 11 | super(mb.app); 12 | 13 | this.content = content; 14 | } 15 | 16 | renderSuggestion(item: FuzzyMatch, el: HTMLElement): void { 17 | renderResults(el.createDiv(), this.content.getItemText(item.item), item.match); 18 | 19 | const description = this.content.getItemDescription(item.item); 20 | if (description) { 21 | renderResults(el.createEl('small', { cls: 'mb-search-modal-element-description' }), description, { 22 | score: 0, 23 | matches: [], 24 | }); 25 | } 26 | } 27 | 28 | getItems(): T[] { 29 | return this.content.getItems(); 30 | } 31 | 32 | getItemText(item: T): string { 33 | return this.content.getItemText(item); 34 | } 35 | 36 | onChooseItem(item: T, _evt: MouseEvent | KeyboardEvent): void { 37 | this.content.onSelected(item); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /packages/obsidian/src/playground/PlaygroundView.ts: -------------------------------------------------------------------------------- 1 | import type { WorkspaceLeaf } from 'obsidian'; 2 | import { ItemView } from 'obsidian'; 3 | import PlaygroundComponent from 'packages/core/src/utils/playground/PlaygroundComponent.svelte'; 4 | import type { ObsMetaBind } from 'packages/obsidian/src/main'; 5 | import type { Component as SvelteComponent } from 'svelte'; 6 | import { mount, unmount } from 'svelte'; 7 | 8 | export const MB_PLAYGROUND_VIEW_TYPE = 'mb-playground-view-type'; 9 | 10 | export class PlaygroundView extends ItemView { 11 | component: ReturnType | undefined; 12 | mb: ObsMetaBind; 13 | 14 | constructor(leaf: WorkspaceLeaf, mb: ObsMetaBind) { 15 | super(leaf); 16 | this.mb = mb; 17 | } 18 | 19 | getViewType(): string { 20 | return MB_PLAYGROUND_VIEW_TYPE; 21 | } 22 | 23 | getDisplayText(): string { 24 | return 'Meta Bind playground'; 25 | } 26 | 27 | async onOpen(): Promise { 28 | this.contentEl.empty(); 29 | 30 | this.component = mount(PlaygroundComponent, { 31 | target: this.contentEl, 32 | props: { 33 | mb: this.mb, 34 | }, 35 | }); 36 | } 37 | 38 | async onClose(): Promise { 39 | if (this.component) { 40 | void unmount(this.component); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /packages/obsidian/src/settings/buttonTemplateSetting/ButtonTemplatesSettingModal.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'obsidian'; 2 | import { Modal } from 'obsidian'; 3 | import type { ButtonConfig } from 'packages/core/src/config/ButtonConfig'; 4 | import type { ErrorCollection } from 'packages/core/src/utils/errors/ErrorCollection'; 5 | import type { ObsMetaBind } from 'packages/obsidian/src/main'; 6 | import ButtonTemplatesSettingComponent from 'packages/obsidian/src/settings/buttonTemplateSetting/ButtonTemplatesSettingComponent.svelte'; 7 | import type { Component as SvelteComponent } from 'svelte'; 8 | import { mount, unmount } from 'svelte'; 9 | 10 | export class ButtonTemplatesSettingModal extends Modal { 11 | readonly mb: ObsMetaBind; 12 | private component: ReturnType | undefined; 13 | 14 | constructor(app: App, mb: ObsMetaBind) { 15 | super(app); 16 | this.mb = mb; 17 | } 18 | 19 | public onOpen(): void { 20 | this.contentEl.empty(); 21 | if (this.component) { 22 | void unmount(this.component); 23 | } 24 | 25 | this.component = mount(ButtonTemplatesSettingComponent, { 26 | target: this.contentEl, 27 | props: { 28 | buttonConfigs: structuredClone(this.mb.getSettings().buttonTemplates), 29 | modal: this, 30 | }, 31 | }); 32 | } 33 | 34 | public onClose(): void { 35 | this.contentEl.empty(); 36 | if (this.component) { 37 | void unmount(this.component); 38 | } 39 | } 40 | 41 | public save(templates: ButtonConfig[]): ErrorCollection | undefined { 42 | const errorCollection = this.mb.buttonManager.setButtonTemplates(templates); 43 | if (errorCollection.hasErrors()) { 44 | return errorCollection; 45 | } 46 | 47 | this.mb.updateSettings(settings => { 48 | settings.buttonTemplates = templates; 49 | }); 50 | 51 | return undefined; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/obsidian/src/settings/inputFieldTemplateSetting/InputFieldTemplateSettingComponent.svelte: -------------------------------------------------------------------------------- 1 | 19 | 20 |
21 | 22 | 23 | 26 | 27 | 28 |
29 | -------------------------------------------------------------------------------- /packages/obsidian/src/settings/inputFieldTemplateSetting/InputFieldTemplatesSettingModal.ts: -------------------------------------------------------------------------------- 1 | import type { App } from 'obsidian'; 2 | import { Modal } from 'obsidian'; 3 | import type { InputFieldTemplate } from 'packages/core/src/Settings'; 4 | import type { ErrorCollection } from 'packages/core/src/utils/errors/ErrorCollection'; 5 | import type { ObsMetaBind } from 'packages/obsidian/src/main'; 6 | import InputFieldTemplatesSettingComponent from 'packages/obsidian/src/settings/inputFieldTemplateSetting/InputFieldTemplatesSettingComponent.svelte'; 7 | import type { Component as SvelteComponent } from 'svelte'; 8 | import { mount, unmount } from 'svelte'; 9 | 10 | export class InputFieldTemplatesSettingModal extends Modal { 11 | readonly mb: ObsMetaBind; 12 | private component: ReturnType | undefined; 13 | 14 | constructor(app: App, mb: ObsMetaBind) { 15 | super(app); 16 | this.mb = mb; 17 | } 18 | 19 | public onOpen(): void { 20 | this.contentEl.empty(); 21 | if (this.component) { 22 | void unmount(this.component); 23 | } 24 | 25 | this.component = mount(InputFieldTemplatesSettingComponent, { 26 | target: this.contentEl, 27 | props: { 28 | inputFieldTemplates: structuredClone(this.mb.getSettings().inputFieldTemplates), 29 | modal: this, 30 | }, 31 | }); 32 | } 33 | 34 | public onClose(): void { 35 | this.contentEl.empty(); 36 | if (this.component) { 37 | void unmount(this.component); 38 | } 39 | } 40 | 41 | public save(templates: InputFieldTemplate[]): ErrorCollection | undefined { 42 | const errorCollection = this.mb.inputFieldParser.parseTemplates(templates); 43 | if (errorCollection.hasErrors()) { 44 | return errorCollection; 45 | } 46 | 47 | this.mb.updateSettings(settings => { 48 | settings.inputFieldTemplates = templates; 49 | }); 50 | 51 | return undefined; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /packages/obsidian/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*.ts", "src/**/*.svelte", "extraTypes/**/*.ts", "../type-ex.d.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/publish/README.md: -------------------------------------------------------------------------------- 1 | # meta-bind-publish 2 | 3 | This is the meta bind adapter package for Obsidian Publish. 4 | 5 | ALL COMMANDS SHOULD BE RUN FROM THE ROOT OF THE REPO. 6 | -------------------------------------------------------------------------------- /packages/publish/bun.lockb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mProjectsCode/obsidian-meta-bind-plugin/b4450146c74decb0f5bef80e154a7bdceffe4d59/packages/publish/bun.lockb -------------------------------------------------------------------------------- /packages/publish/extraTypes/publish.d.ts: -------------------------------------------------------------------------------- 1 | import all from 'obsidian/publish'; 2 | 3 | declare module 'obsidian/publish' { 4 | export class Notice { 5 | /** 6 | * @public 7 | */ 8 | noticeEl: HTMLElement; 9 | /** 10 | * @param message - The message to be displayed, can either be a simple string or a {@link DocumentFragment} 11 | * @param duration - Time in milliseconds to show the notice for. If this is 0, the 12 | * Notice will stay visible until the user manually dismisses it. 13 | * @public 14 | */ 15 | constructor(message: string | DocumentFragment, duration?: number); 16 | /** 17 | * Change the message of this notice. 18 | * @public 19 | */ 20 | setMessage(message: string | DocumentFragment): this; 21 | /** 22 | * @public 23 | */ 24 | hide(): void; 25 | } 26 | 27 | function setIcon(el: HTMLElement, icon: string): void; 28 | 29 | function parseYaml(text: string): unknown; 30 | 31 | function stringifyYaml(obj: unknown): string; 32 | 33 | interface Publish { 34 | site: { 35 | cache: { 36 | cache: Record< 37 | string, 38 | { 39 | frontmatter?: Record; 40 | } 41 | >; 42 | }; 43 | }; 44 | } 45 | } 46 | 47 | export {}; 48 | -------------------------------------------------------------------------------- /packages/publish/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "meta-bind-publish", 3 | "dependencies": { 4 | "obsidian": "latest" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /packages/publish/src/PublishAPI.ts: -------------------------------------------------------------------------------- 1 | import type { Component } from 'obsidian/publish'; 2 | import { API } from 'packages/core/src/api/API'; 3 | import type { Mountable } from 'packages/core/src/utils/Mountable'; 4 | import type { PublishMetaBind, PublishComponents } from 'packages/publish/src/main'; 5 | import { PublishFieldMDRC } from 'packages/publish/src/PublishFieldMDRC'; 6 | 7 | export interface ComponentLike { 8 | addChild(child: Component): void; 9 | } 10 | 11 | export class PublishAPI extends API { 12 | private readonly pmb: PublishMetaBind; 13 | 14 | constructor(mb: PublishMetaBind) { 15 | super(mb); 16 | 17 | this.pmb = mb; 18 | } 19 | 20 | public wrapInMDRC(field: Mountable, containerEl: HTMLElement, component: ComponentLike): PublishFieldMDRC { 21 | const mdrc = new PublishFieldMDRC(this.pmb, field, containerEl); 22 | component.addChild(mdrc); 23 | 24 | return mdrc; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /packages/publish/src/PublishFieldMDRC.ts: -------------------------------------------------------------------------------- 1 | import { MarkdownRenderChild } from 'obsidian/publish'; 2 | import type { Mountable } from 'packages/core/src/utils/Mountable'; 3 | import type { PublishMetaBind } from 'packages/publish/src/main'; 4 | 5 | export class PublishFieldMDRC extends MarkdownRenderChild { 6 | readonly mb: PublishMetaBind; 7 | readonly mountable: Mountable; 8 | 9 | constructor(mb: PublishMetaBind, mountable: Mountable, containerEl: HTMLElement) { 10 | super(containerEl); 11 | 12 | this.mb = mb; 13 | this.mountable = mountable; 14 | 15 | this.mountable.registerUnmountCb(() => { 16 | this.unload(); 17 | }); 18 | } 19 | 20 | onload(): void { 21 | this.mountable.mount(this.containerEl); 22 | 23 | super.onload(); 24 | } 25 | 26 | onunload(): void { 27 | this.mountable.unmount(); 28 | 29 | super.onunload(); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/publish/src/PublishFileAPI.ts: -------------------------------------------------------------------------------- 1 | import { FileAPI } from 'packages/core/src/api/FileAPI'; 2 | import type { PublishComponents } from 'packages/publish/src/main'; 3 | 4 | export class PublishFileAPI extends FileAPI { 5 | public read(_filePath: string): Promise { 6 | throw new Error('Reading files is not supported in Obsidian Publish.'); 7 | } 8 | 9 | public write(_filePath: string, _content: string): Promise { 10 | throw new Error('Writing files is not supported in Obsidian Publish.'); 11 | } 12 | 13 | public exists(filePath: string): Promise { 14 | return Promise.resolve(this.getAllFiles().contains(filePath)); 15 | } 16 | 17 | public atomicModify(_filePath: string, _modify: (content: string) => string): Promise { 18 | throw new Error('Modifying files is not supported in Obsidian Publish.'); 19 | } 20 | 21 | public create(_folderPath: string, _fileName: string, _extension: string, _open?: boolean): Promise { 22 | throw new Error('Creating files is not supported in Obsidian Publish.'); 23 | } 24 | 25 | public getAllFiles(): string[] { 26 | return Object.keys(publish.site.cache); 27 | } 28 | 29 | public getAllFolders(): string[] { 30 | const filePaths = this.getAllFiles(); 31 | 32 | const folders = new Set(); 33 | 34 | for (const filePath of filePaths) { 35 | const parts = filePath.split('/'); 36 | parts.pop(); 37 | folders.add(parts.join('/')); 38 | } 39 | 40 | return Array.from(folders); 41 | } 42 | 43 | public open(_filePath: string, _callingFilePath: string, _newTab: boolean): Promise { 44 | throw new Error('not implemented'); 45 | } 46 | 47 | public getPathByName(name: string, _relativeTo?: string): string | undefined { 48 | return name; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /packages/publish/src/PublishMetadataSource.ts: -------------------------------------------------------------------------------- 1 | import type { FilePathMetadataCacheItem } from 'packages/core/src/metadata/MetadataCacheItem'; 2 | import type { MetadataManager } from 'packages/core/src/metadata/MetadataManager'; 3 | import type { Metadata } from 'packages/core/src/metadata/MetadataSource'; 4 | import { FilePathMetadataSource } from 'packages/core/src/metadata/MetadataSource'; 5 | import type { PublishMetaBind } from 'packages/publish/src/main'; 6 | 7 | export class PublishMetadataSource extends FilePathMetadataSource { 8 | public readonly mb: PublishMetaBind; 9 | 10 | constructor(mb: PublishMetaBind, id: string, manager: MetadataManager) { 11 | super(id, manager); 12 | this.mb = mb; 13 | } 14 | 15 | public readExternal(storagePath: string): Metadata { 16 | const frontmatter = publish.site.cache.cache[storagePath]?.frontmatter; 17 | 18 | return structuredClone(frontmatter) ?? {}; 19 | } 20 | 21 | getDefaultCacheItem(storagePath: string): FilePathMetadataCacheItem { 22 | const frontmatter = publish.site.cache.cache[storagePath]?.frontmatter; 23 | 24 | MB_DEBUG && 25 | console.log('meta-bind | Obs Source >> loaded frontmatter', structuredClone(frontmatter), storagePath); 26 | 27 | return { 28 | data: structuredClone(frontmatter) ?? {}, 29 | storagePath: storagePath, 30 | ...this.manager.getDefaultCacheItem(), 31 | }; 32 | } 33 | 34 | syncExternal(_: FilePathMetadataCacheItem): void { 35 | // no-op 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /packages/publish/src/PublishNotePosition.ts: -------------------------------------------------------------------------------- 1 | import type { MarkdownPostProcessorContext } from 'obsidian/publish'; 2 | import type { LinePosition } from 'packages/core/src/config/APIConfigs'; 3 | import { NotePosition } from 'packages/core/src/config/APIConfigs'; 4 | 5 | export class PublishNotePosition extends NotePosition { 6 | ctx: MarkdownPostProcessorContext; 7 | element: HTMLElement; 8 | 9 | constructor(ctx: MarkdownPostProcessorContext, element: HTMLElement) { 10 | super(undefined); 11 | 12 | this.ctx = ctx; 13 | this.element = element; 14 | } 15 | 16 | public getPosition(): LinePosition | undefined { 17 | const pos = this.ctx.getSectionInfo(this.element); 18 | 19 | if (!pos) { 20 | return undefined; 21 | } 22 | 23 | return { 24 | lineStart: pos.lineStart, 25 | lineEnd: pos.lineEnd, 26 | }; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/publish/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.json", 3 | "include": ["src/**/*.ts", "src/**/*.svelte", "extraTypes/**/*.ts", "../type-ex.d.ts"] 4 | } 5 | -------------------------------------------------------------------------------- /packages/type-ex.d.ts: -------------------------------------------------------------------------------- 1 | declare global { 2 | const MB_VERSION: string; 3 | const MB_DEV_BUILD: boolean; 4 | const MB_DEBUG: boolean; 5 | } 6 | 7 | export {}; 8 | -------------------------------------------------------------------------------- /tests/__mocks__/TestAPI.ts: -------------------------------------------------------------------------------- 1 | import { API } from 'packages/core/src/api/API'; 2 | import { TestMetaBind, type TestComponents } from './TestPlugin'; 3 | 4 | export class TestAPI extends API { 5 | constructor(mb: TestMetaBind) { 6 | super(mb); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/__mocks__/TestComponent.ts: -------------------------------------------------------------------------------- 1 | export class TestComponent { 2 | loaded: boolean; 3 | used: boolean; 4 | callbacks: (() => void)[]; 5 | 6 | constructor() { 7 | this.loaded = false; 8 | this.used = false; 9 | this.callbacks = []; 10 | } 11 | 12 | load() { 13 | if (this.loaded) { 14 | throw new Error('Attempted double load of TestComponent'); 15 | } 16 | if (this.used) { 17 | throw new Error('Attempted reuse of TestComponent'); 18 | } 19 | this.loaded = true; 20 | } 21 | 22 | unload() { 23 | if (!this.loaded && !this.used) { 24 | throw new Error('Attempted unload of TestComponent before it was loaded'); 25 | } 26 | if (!this.loaded && this.used) { 27 | throw new Error('Attempted double unload of TestComponent'); 28 | } 29 | this.loaded = false; 30 | this.used = true; 31 | this.callbacks.forEach(cb => cb()); 32 | } 33 | 34 | register(callback: () => void) { 35 | if (this.used) { 36 | throw new Error( 37 | 'Attempted to register callback after TestComponent was used. The callback will never be called.', 38 | ); 39 | } 40 | 41 | this.callbacks.push(callback); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /tests/__mocks__/TestFileSystem.ts: -------------------------------------------------------------------------------- 1 | export class TestFileSystem { 2 | files: Map; 3 | 4 | constructor() { 5 | this.files = new Map(); 6 | } 7 | 8 | readFile(path: string): string { 9 | this.validateFilePath(path); 10 | 11 | let fileContent = this.files.get(path); 12 | if (fileContent === undefined) { 13 | throw new Error(`File not found: ${path}`); 14 | } 15 | return fileContent; 16 | } 17 | 18 | writeFile(path: string, content: string): void { 19 | this.validateFilePath(path); 20 | 21 | this.files.set(path, content); 22 | } 23 | 24 | deleteFile(path: string): void { 25 | this.validateFilePath(path); 26 | 27 | if (!this.fileExists(path)) { 28 | throw new Error(`File does not exist: ${path}`); 29 | } 30 | 31 | this.files.delete(path); 32 | } 33 | 34 | listFiles(): string[] { 35 | return Array.from(this.files.keys()); 36 | } 37 | 38 | fileExists(path: string): boolean { 39 | this.validateFilePath(path); 40 | 41 | return this.files.has(path); 42 | } 43 | 44 | listDirs(): string[] { 45 | const dirs = new Set(); 46 | 47 | for (const path of this.files.keys()) { 48 | const parts = path.split('/'); 49 | parts.pop(); 50 | dirs.add(parts.join('/')); 51 | } 52 | 53 | return Array.from(dirs); 54 | } 55 | 56 | validateFilePath(path: string): void { 57 | if (path === '') { 58 | throw new Error('Invalid file path'); 59 | } 60 | 61 | const parts = path.split('/'); 62 | 63 | for (const part of parts) { 64 | if (part === '' || part === '.' || part === '..') { 65 | throw new Error('Invalid file path'); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /tests/__preload__/customMatchers.ts: -------------------------------------------------------------------------------- 1 | // import { expect } from 'bun:test'; 2 | 3 | // expect.extend({}); 4 | 5 | let placeholder = 0; 6 | 7 | // @ts-ignore 8 | global.MB_DEBUG = true; 9 | // @ts-ignore 10 | global.MB_DEV_BUILD = true; 11 | -------------------------------------------------------------------------------- /tests/__preload__/happydom.ts: -------------------------------------------------------------------------------- 1 | import { GlobalRegistrator } from '@happy-dom/global-registrator'; 2 | import process from 'process'; 3 | 4 | GlobalRegistrator.register({ 5 | settings: {}, 6 | }); 7 | 8 | if (process.env.LOG_TESTS === 'false') { 9 | console.log = () => {}; 10 | console.debug = () => {}; 11 | } else { 12 | console.debug = () => {}; 13 | } 14 | -------------------------------------------------------------------------------- /tests/__preload__/obsidianMock.ts: -------------------------------------------------------------------------------- 1 | // import { mock } from 'bun:test'; 2 | 3 | // mock.module('obsidian', () => ({})); 4 | -------------------------------------------------------------------------------- /tests/__preload__/svelteLoader.ts: -------------------------------------------------------------------------------- 1 | import { plugin } from 'bun'; 2 | import esbuildSvelte from 'esbuild-svelte'; 3 | import { sveltePreprocess } from 'svelte-preprocess'; 4 | 5 | plugin( 6 | // @ts-ignore 7 | esbuildSvelte({ 8 | compilerOptions: { css: 'injected', dev: true }, 9 | filterWarnings: warning => { 10 | // we don't want warnings from node modules that we can do nothing about 11 | return !warning.filename?.includes('node_modules'); 12 | }, 13 | }), 14 | ); 15 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": ".", 4 | "module": "ESNext", 5 | "target": "ES2020", 6 | "allowJs": true, 7 | "noImplicitAny": true, 8 | "strict": true, 9 | "strictNullChecks": true, 10 | "noImplicitReturns": true, 11 | "moduleResolution": "Node", 12 | "importHelpers": true, 13 | "isolatedModules": true, 14 | "verbatimModuleSyntax": true, 15 | "lib": ["DOM", "ESNext"], 16 | "types": ["svelte"], 17 | "allowSyntheticDefaultImports": true, 18 | "skipLibCheck": true 19 | }, 20 | "include": [ 21 | "packages/core/**/*.ts", 22 | "packages/core/**/*.svelte", 23 | "packages/obsidian/**/*.ts", 24 | "packages/obsidian/**/*.svelte", 25 | "packages/publish/**/*.ts", 26 | "packages/publish/**/*.svelte", 27 | "tests/**/*.ts", 28 | "node_modules/obsidian/publish.d.ts", 29 | "packages/type-ex.d.ts" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /tsconfig.types.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["packages/**/*.ts"], 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "module": "ESNext", 6 | "target": "ES6", 7 | "allowJs": true, 8 | "declaration": true, 9 | "moduleResolution": "node", 10 | "emitDeclarationOnly": true, 11 | "outFile": "meta-bind.d.ts", 12 | "lib": ["DOM", "ES5", "ES6", "ES7", "Es2021"], 13 | "types": ["svelte"] 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /versions.json: -------------------------------------------------------------------------------- 1 | { 2 | "0.3.3": "0.12.0", 3 | "0.4.0": "1.1.0", 4 | "0.5.0": "1.3.5", 5 | "0.7.0": "1.4.0", 6 | "0.7.1": "1.4.0", 7 | "0.7.2": "1.4.0", 8 | "0.8.0": "1.4.0", 9 | "0.9.0": "1.4.0", 10 | "0.10.0": "1.4.0", 11 | "0.10.1": "1.4.0", 12 | "0.10.2": "1.4.0", 13 | "0.11.0": "1.4.0", 14 | "0.12.0": "1.4.0", 15 | "0.12.1": "1.4.0", 16 | "0.12.2": "1.4.0", 17 | "0.12.3": "1.4.0", 18 | "0.12.4": "1.4.0", 19 | "0.12.5": "1.4.0", 20 | "1.0.0": "1.4.0", 21 | "1.0.1": "1.4.0", 22 | "1.0.2": "1.4.0", 23 | "1.0.3": "1.4.0", 24 | "1.0.4": "1.4.0", 25 | "1.1.0": "1.4.0", 26 | "1.1.1": "1.4.0", 27 | "1.1.2": "1.4.0", 28 | "1.1.3": "1.4.0", 29 | "1.2.0": "1.4.0", 30 | "1.2.1": "1.4.0", 31 | "1.2.2": "1.4.0", 32 | "1.2.3": "1.4.0", 33 | "1.2.4": "1.4.0", 34 | "1.2.5": "1.4.0", 35 | "1.3.0": "1.4.0", 36 | "1.3.1": "1.4.0", 37 | "1.3.2": "1.4.0", 38 | "1.3.3": "1.4.0", 39 | "1.3.4": "1.4.0", 40 | "1.4.0": "1.4.0", 41 | "1.4.1": "1.4.0", 42 | "1.4.2": "1.4.0" 43 | } 44 | --------------------------------------------------------------------------------