Copy disabled (too large)
Download .txt
Showing preview only (19,616K chars total). Download the full file to get everything.
Repository: NotePlan/plugins
Branch: main
Commit: ef025ab7eb3a
Files: 1354
Total size: 18.4 MB
Directory structure:
gitextract_lyr50gqo/
├── .cursor/
│ ├── commands/
│ │ └── np-plugin-commands.md
│ └── rules/
│ ├── html-react-rules.mdc
│ └── np-programming-general.mdc
├── .cursorignore
├── .eslintrc
├── .flowconfig
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows/
│ └── node.js.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .watchmanconfig
├── CHANGELOG.md
├── Flow_Guide.md
├── GithubFlow.md
├── KimMachineGun.Raindrop/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── NPPluginMain.js
│ ├── Raindrop.js
│ └── index.js
├── LICENSE
├── README.md
├── TasksModule_docs.md
├── __mocks__/
│ ├── Backlink.mock.js
│ ├── Calendar.mock.js
│ ├── CalendarItem.mock.js
│ ├── Clipboard.mock.js
│ ├── CommandBar.mock.js
│ ├── DataStore.mock.js
│ ├── Editor.mock.js
│ ├── Fetch.mock.js
│ ├── NP_THEME.mock.js
│ ├── Note.mock.js
│ ├── NotePlan.mock.js
│ ├── Paragraph.mock.js
│ ├── PluginCommandObject.mock.js
│ ├── PluginObject.mock.js
│ ├── Range.mock.js
│ ├── _README-Mocks.md
│ ├── __tests__/
│ │ └── fetch.mock.test.js
│ ├── index.js
│ ├── jestHelpers.js
│ └── support/
│ └── pluginSample.json
├── aaronpoweruser.ReadwiseUnofficial/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── NPReadwise.js
│ ├── NPReadwiseHelpers.js
│ ├── NPReadwiseNotes.js
│ ├── NPReadwiseSyncLog.js
│ ├── NPTriggers-Hooks.js
│ └── index.js
├── babel.config.js
├── buildDashboard.sh
├── buildShared.sh
├── codedungeon.Toolbox/
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── __tests__/
│ │ ├── convertSelectionToHtml.test.js
│ │ ├── convertToHtml.test.js
│ │ └── reorderList.test.js
│ ├── plugin.json
│ └── src/
│ ├── convertSelectionToHtml.js
│ ├── convertToHtml.js
│ ├── convertToRtf.js
│ ├── index.js
│ ├── reorderList.js
│ └── support/
│ └── CodedungeonToolbox.js
├── community-plugins.json
├── dbludeau.TodoistNoteplanSync/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── NPPluginMain.NOTACTIVE.js
│ ├── plugin.json
│ ├── requiredFiles/
│ │ └── html-plugin-comms.js
│ └── src/
│ ├── NPPluginMain.js
│ ├── NPTriggers-Hooks.js
│ ├── index.js
│ └── support/
│ ├── fetchOverrides.js
│ ├── fetchResponses/
│ │ └── google.search-for-something.json
│ └── helpers.js
├── deleteme.testPluginDownload/
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ └── index.js
├── docs/
│ ├── custom_theme/
│ │ ├── README.md
│ │ ├── assets/
│ │ │ ├── anchor.js
│ │ │ ├── bass-addons.css
│ │ │ ├── bass.css
│ │ │ ├── fonts/
│ │ │ │ ├── LICENSE.txt
│ │ │ │ ├── OTF/
│ │ │ │ │ ├── SourceCodePro-Bold.otf
│ │ │ │ │ └── SourceCodePro-Regular.otf
│ │ │ │ └── source-code-pro.css
│ │ │ ├── github.css
│ │ │ ├── site.js
│ │ │ ├── split.css
│ │ │ ├── split.js
│ │ │ └── style.css
│ │ ├── index._
│ │ ├── index.js
│ │ ├── note._
│ │ ├── paramProperty._
│ │ ├── section._
│ │ └── section_list._
│ └── documentation.cfg.json
├── dwertheimer.DateAutomations/
│ ├── README.md
│ ├── changelog.md
│ ├── plugin.json
│ └── src/
│ ├── dateFunctions.js
│ └── index.js
├── dwertheimer.EventAutomations/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── NPEventBlocks.test.js
│ │ ├── NPTimeblocking.Integration.test.js
│ │ ├── NPTimeblocking.test.js
│ │ ├── by_timeblock_tag.test.js
│ │ ├── config.test.js
│ │ ├── presets.test.js
│ │ ├── timeblocking-helpers.test.js
│ │ ├── timeblocking-shared.test.js
│ │ └── timeblocking-taskSorting.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPEventBlocks.js
│ ├── NPTimeblocking.js
│ ├── byTagMode.js
│ ├── config.js
│ ├── events.js
│ ├── index.js
│ ├── presets.js
│ ├── timeblocking-flow-types.js
│ ├── timeblocking-helpers.js
│ ├── timeblocking-shared.js
│ └── triggers.js
├── dwertheimer.Favorites/
│ ├── CHANGELOG.md
│ ├── DEBUGGING_REQUEST_TIMEOUTS.md
│ ├── IMPLEMENTATION_PLAN.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── NPFavorites.test.js
│ │ └── favorites.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPFavoritePresets.js
│ ├── NPFavorites.js
│ ├── components/
│ │ ├── AppContext.jsx
│ │ ├── FavoritesView.css
│ │ └── FavoritesView.jsx
│ ├── favorites.js
│ ├── favoritesRouter.js
│ ├── index.js
│ ├── requestHandlers.js
│ ├── shared/
│ │ └── types.js
│ ├── support/
│ │ ├── performRollup.node.js
│ │ └── rollup.FavoritesView.entry.js
│ └── windowManagement.js
├── dwertheimer.Forms/
│ ├── CHANGELOG.md
│ ├── DEBUGGING_PLAN_TEMPLATEJS_HANG.md
│ ├── README.md
│ ├── TEMPLATEJS_FREEZE_DIAG_THROWS.md
│ ├── plugin.json
│ └── src/
│ ├── FormFieldRenderTest.js
│ ├── NPTemplateForm.js
│ ├── NPTriggers-Hooks.js
│ ├── ProcessingTemplate.js
│ ├── components/
│ │ ├── AppContext.jsx
│ │ ├── ConditionsEditor.jsx
│ │ ├── FieldEditor.jsx
│ │ ├── FieldTypeSelector.jsx
│ │ ├── FormBrowserView.css
│ │ ├── FormBrowserView.jsx
│ │ ├── FormBuilder.css
│ │ ├── FormBuilder.jsx
│ │ ├── FormBuilderView.jsx
│ │ ├── FormErrorBanner.jsx
│ │ ├── FormFieldsList.jsx
│ │ ├── FormPreview.jsx
│ │ ├── FormSettings.jsx
│ │ ├── FormView.css
│ │ ├── FormView.jsx
│ │ ├── OptionsEditor.jsx
│ │ ├── PositionInput.jsx
│ │ ├── ProcessingMethodSection.jsx
│ │ ├── TemplateTagEditor.css
│ │ ├── TemplateTagEditor.jsx
│ │ ├── TemplateTagInserter.css
│ │ ├── TemplateTagInserter.jsx
│ │ ├── ValueInsertButtons.jsx
│ │ └── fieldTypes.js
│ ├── dataHandlers.js
│ ├── formBrowserHandlers.js
│ ├── formBrowserRouter.js
│ ├── formBuilderHandlers.js
│ ├── formBuilderRouter.js
│ ├── formSubmission.js
│ ├── formSubmitHandlers.js
│ ├── formSubmitRouter.js
│ ├── index.js
│ ├── noteHelpers.js
│ ├── requestHandlers.js
│ ├── shared/
│ │ ├── constants.js
│ │ └── types.js
│ ├── support/
│ │ ├── fetchOverrides.js
│ │ ├── fetchResponses/
│ │ │ └── google.search-for-something.json
│ │ ├── performRollup.node.js
│ │ ├── rollup.FormBrowserView.entry.js
│ │ ├── rollup.FormBuilderView.entry.js
│ │ └── rollup.FormView.entry.js
│ ├── templateIO.js
│ ├── utils/
│ │ └── encodingFix.js
│ └── windowManagement.js
├── dwertheimer.JestHelpers/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── NPMocks.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPPluginMain.js
│ ├── index.js
│ └── support/
│ └── helpers.js
├── dwertheimer.MathSolver/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── NPMathBlocks.test.js
│ │ ├── date-time-math.test.js
│ │ └── solver.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPMathBlocks.js
│ ├── index.js
│ └── support/
│ ├── date-time-math.js
│ ├── helpers.js
│ └── solver.js
├── dwertheimer.ReactSkeleton/
│ ├── REACT_COMMUNICATION_PATTERNS.md
│ ├── REACT_UTILITIES.md
│ ├── REQUEST_COMMUNICATIONS_AND_ROUTING.md
│ ├── changelog.md
│ ├── plugin.json
│ ├── readme.md
│ ├── requiredFiles/
│ │ └── css.plugin.css
│ └── src/
│ ├── index.js
│ ├── react/
│ │ ├── components/
│ │ │ ├── AppContext.jsx
│ │ │ ├── Button.jsx
│ │ │ ├── Checkbox.jsx
│ │ │ ├── CompositeLineExample.jsx
│ │ │ └── WebView.jsx
│ │ └── support/
│ │ ├── performRollup.node.js
│ │ └── rollup.WebView.entry.js
│ ├── reactMain.js
│ ├── requestHandlers.js
│ └── router.js
├── dwertheimer.TaskAutomations/
│ ├── __tests__/
│ │ ├── NPOverdueReact.test.js
│ │ ├── NPTaskScanAndProcess.test.js
│ │ └── lastUsedChoices.test.js
│ ├── changelog.md
│ ├── plugin.json
│ ├── readme.md
│ ├── requiredFiles/
│ │ ├── css.plugin.css
│ │ └── css.w3.css
│ └── src/
│ ├── NPFollowUp.js
│ ├── NPOverdue.js
│ ├── NPOverdueReact.js
│ ├── NPTaskScanAndProcess.js
│ ├── index.js
│ ├── lastUsedChoices.js
│ ├── react/
│ │ ├── Button.jsx
│ │ ├── EditableElement.jsx
│ │ ├── MultiActionBar.jsx
│ │ ├── StatusButton.jsx
│ │ ├── ThemedSelect.jsx
│ │ ├── TypeFilter.jsx
│ │ ├── WebView.jsx
│ │ ├── dataTableFormatting.jsx
│ │ └── support/
│ │ ├── performRollup.node.js
│ │ └── rollup.WebView.entry.js
│ └── taskSync.js
├── dwertheimer.TaskSorting/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── factories/
│ │ │ ├── taskDocument.json
│ │ │ ├── taskDocument.notes.txt
│ │ │ └── taskDocumentAfterSortByTitle.json
│ │ ├── sortTasks.test.js
│ │ └── tagTasks.test.js
│ ├── plugin.json
│ ├── requiredFiles/
│ │ └── html-plugin-comms.js
│ └── src/
│ ├── NPTriggers-Hooks.js
│ ├── index.js
│ ├── markTasks.js
│ ├── sortTasks.js
│ ├── support/
│ │ ├── fetchOverrides.js
│ │ ├── fetchResponses/
│ │ │ └── google.search-for-something.json
│ │ └── helpers.js
│ └── tagTasks.js
├── emetzger.Calendar/
│ └── plugin.json
├── emetzger.LinearCalendar/
│ └── plugin.json
├── flow-typed/
│ ├── Noteplan.js
│ └── npm/
│ ├── @babel/
│ │ ├── cli_vx.x.x.js
│ │ ├── core_vx.x.x.js
│ │ ├── eslint-parser_vx.x.x.js
│ │ ├── generator_vx.x.x.js
│ │ ├── parser_vx.x.x.js
│ │ ├── preset-env_vx.x.x.js
│ │ ├── preset-flow_vx.x.x.js
│ │ └── preset-react_vx.x.x.js
│ ├── @codedungeon/
│ │ └── gunner_vx.x.x.js
│ ├── @rollup/
│ │ ├── plugin-alias_vx.x.x.js
│ │ ├── plugin-babel_vx.x.x.js
│ │ ├── plugin-commonjs_vx.x.x.js
│ │ ├── plugin-json_vx.x.x.js
│ │ └── plugin-node-resolve_vx.x.x.js
│ ├── @samverschueren/
│ │ └── stream-to-observable_vx.x.x.js
│ ├── axios_v0.21.x.js
│ ├── babel-cli_vx.x.x.js
│ ├── babel_vx.x.x.js
│ ├── babelify_vx.x.x.js
│ ├── bcrypt_v5.x.x.js
│ ├── bqpjs_vx.x.x.js
│ ├── browserify_vx.x.x.js
│ ├── bump-regex_vx.x.x.js
│ ├── chroma-js_v2.x.x.js
│ ├── chrono-node_vx.x.x.js
│ ├── clipboardy_vx.x.x.js
│ ├── columnify_vx.x.x.js
│ ├── commander_vx.x.x.js
│ ├── concurrently_vx.x.x.js
│ ├── contentful-html-rich-text-converter_vx.x.x.js
│ ├── dayjs_v1.x.x.js
│ ├── documentation_vx.x.x.js
│ ├── enquirer_vx.x.x.js
│ ├── eslint-config-prettier_vx.x.x.js
│ ├── eslint-import-resolver-alias_vx.x.x.js
│ ├── eslint-plugin-flowtype_vx.x.x.js
│ ├── eslint-plugin-import_vx.x.x.js
│ ├── eslint-plugin-no-floating-promise_vx.x.x.js
│ ├── eslint-plugin-react_vx.x.x.js
│ ├── eslint-plugin-unused-imports_vx.x.x.js
│ ├── eslint_vx.x.x.js
│ ├── fast-glob_vx.x.x.js
│ ├── findup-sync_vx.x.x.js
│ ├── flow-bin_v0.x.x.js
│ ├── front-matter_vx.x.x.js
│ ├── fsevents_vx.x.x.js
│ ├── fuse.js_v6.x.x.js
│ ├── fuse.js_vx.x.x.js
│ ├── git-state_vx.x.x.js
│ ├── html-minifier_vx.x.x.js
│ ├── inquirer_vx.x.x.js
│ ├── jest-silent-reporter_vx.x.x.js
│ ├── jest-spec-reporter_vx.x.x.js
│ ├── jest_v27.x.x.js
│ ├── js-yaml_v4.x.x.js
│ ├── js-yaml_vx.x.x.js
│ ├── json5_vx.x.x.js
│ ├── listr_vx.x.x.js
│ ├── lodash-es_v4.x.x.js
│ ├── luxon-business-days_vx.x.x.js
│ ├── luxon_vx.x.x.js
│ ├── mathjs_vx.x.x.js
│ ├── mermaid_vx.x.x.js
│ ├── mkdirp_v1.x.x.js
│ ├── moment-business-days_vx.x.x.js
│ ├── moment_v2.x.x.js
│ ├── node-fetch_v1.x.x.js
│ ├── node-gyp_vx.x.x.js
│ ├── node-libcurl_vx.x.x.js
│ ├── node-notifier_vx.x.x.js
│ ├── prettier_vx.x.x.js
│ ├── progress_vx.x.x.js
│ ├── react-data-table-component_vx.x.x.js
│ ├── react-dom_v18.x.x.js
│ ├── react-error-boundary_vx.x.x.js
│ ├── react-loader-spinner_vx.x.x.js
│ ├── react-select_vx.x.x.js
│ ├── rimraf_v2.x.x.js
│ ├── rollup-plugin-replace_vx.x.x.js
│ ├── rollup-plugin-terser_vx.x.x.js
│ ├── rollup-plugin-visualizer_vx.x.x.js
│ ├── rollup_vx.x.x.js
│ ├── rxjs_v6.x.x.js
│ ├── showdown_vx.x.x.js
│ ├── simple-input_vx.x.x.js
│ ├── sinon_v7.x.x.js
│ ├── split_vx.x.x.js
│ ├── sprintf-js_vx.x.x.js
│ ├── strftime_vx.x.x.js
│ ├── toml_vx.x.x.js
│ └── webpack_v4.x.x.js
├── github-actions-reporter.js
├── grdn.TagTracker/
│ ├── .claude/
│ │ └── settings.local.json
│ └── plugin.json
├── helpers/
│ ├── HTMLView.js
│ ├── NPAddItems.js
│ ├── NPCalendar.js
│ ├── NPConfiguration.js
│ ├── NPEditor.js
│ ├── NPEditorBasics.js
│ ├── NPExtendedRepeat.js
│ ├── NPFrontMatter.js
│ ├── NPMoveItems.js
│ ├── NPParagraph.js
│ ├── NPPresets.js
│ ├── NPRequiredFiles.js
│ ├── NPScheduleItems.js
│ ├── NPSettings.js
│ ├── NPSyncedCopies.js
│ ├── NPTeamspace.js
│ ├── NPThemeToCSS.js
│ ├── NPVersions.js
│ ├── NPWindows.js
│ ├── NPdateTime.js
│ ├── NPdev.js
│ ├── NPnote.js
│ ├── README.md
│ ├── __tests__/
│ │ ├── HTMLView.test.js
│ │ ├── NPConfiguration.test.js
│ │ ├── NPConfiguration.updateSettingData.test.js
│ │ ├── NPDateTime.test.js
│ │ ├── NPExtendedRepeat.doneMarker.test.js
│ │ ├── NPExtendedRepeat.generateRepeatForPara.test.js
│ │ ├── NPFrontMatter/
│ │ │ ├── NPFrontMatter.analyzeTemplateStructure.test.js
│ │ │ ├── NPFrontMatterAttributes.test.js
│ │ │ ├── NPFrontMatterDetection.test.js
│ │ │ ├── NPFrontMatterFormatting.test.js
│ │ │ ├── NPFrontMatterManipulation.test.js
│ │ │ ├── NPFrontMatterMisc.test.js
│ │ │ ├── NPFrontMatterNotes.test.js
│ │ │ └── NPFrontMatterTriggers.test.js
│ │ ├── NPFrontMatter.detectInlineTitleRobust.test.js
│ │ ├── NPNote.test.js
│ │ ├── NPParagraph.test.js
│ │ ├── NPPresets.test.js
│ │ ├── NPSettings.test.js
│ │ ├── NPSyncedCopies.test.js
│ │ ├── NPThemeToCSS.test.js
│ │ ├── blocks.test.js
│ │ ├── calendar.test.js
│ │ ├── config.test.js
│ │ ├── dataManipulation.test.js
│ │ ├── dateTime.test.js
│ │ ├── dev.test.js
│ │ ├── folders.test.js
│ │ ├── general.test.js
│ │ ├── headings.test.js
│ │ ├── note.test.js
│ │ ├── notePlanWeekFormatter.test.js
│ │ ├── paragraph.test.js
│ │ ├── parentsAndChildren.test.js
│ │ ├── regex.test.js
│ │ ├── regexEscape.test.js
│ │ ├── search.test.js
│ │ ├── sorting.test.js
│ │ ├── stringTransforms.test.js
│ │ ├── syncedCopies.test.js
│ │ ├── teamspace.test.js
│ │ ├── timeblocks.test.js
│ │ ├── urls.test.js
│ │ └── utils.test.js
│ ├── blocks.js
│ ├── calendar.js
│ ├── checkType.js
│ ├── codeBlocks.js
│ ├── colors.js
│ ├── config.js
│ ├── content.js
│ ├── dataManipulation.js
│ ├── dateTime.js
│ ├── dev.js
│ ├── folders.js
│ ├── general.js
│ ├── headings.js
│ ├── markdown-regex.js
│ ├── note.js
│ ├── noteChooserFilenameResolve.js
│ ├── notePlanWeekFormatter.js
│ ├── openAI.js
│ ├── paragraph.js
│ ├── parentsAndChildren.js
│ ├── promisePolyfill.js
│ ├── react/
│ │ ├── CollapsibleObjectViewer.css
│ │ ├── CollapsibleObjectViewer.jsx
│ │ ├── ConsoleLogView.css
│ │ ├── ConsoleLogView.jsx
│ │ ├── DebugPanel.css
│ │ ├── DebugPanel.jsx
│ │ ├── DynamicDialog/
│ │ │ ├── AutosaveField.jsx
│ │ │ ├── ButtonComponents.jsx
│ │ │ ├── CREATING_NEW_DYNAMICDIALOG_FIELD_TYPES.md
│ │ │ ├── CalendarPicker.css
│ │ │ ├── CalendarPicker.jsx
│ │ │ ├── ColorChooser.css
│ │ │ ├── ColorChooser.jsx
│ │ │ ├── ConditionalValues.css
│ │ │ ├── ConditionalValues.jsx
│ │ │ ├── ContainedMultiSelectChooser.css
│ │ │ ├── ContainedMultiSelectChooser.jsx
│ │ │ ├── DD_NEW_FEATURE_CHECKLIST.md
│ │ │ ├── DropdownSelect.css
│ │ │ ├── DropdownSelect.jsx
│ │ │ ├── DropdownSelectChooser.css
│ │ │ ├── DropdownSelectChooser.jsx
│ │ │ ├── DynamicDialog.css
│ │ │ ├── DynamicDialog.jsx
│ │ │ ├── EventChooser.css
│ │ │ ├── EventChooser.jsx
│ │ │ ├── ExpandableTextarea.css
│ │ │ ├── ExpandableTextarea.jsx
│ │ │ ├── FolderChooser.css
│ │ │ ├── FolderChooser.jsx
│ │ │ ├── FrontmatterKeyChooser.css
│ │ │ ├── FrontmatterKeyChooser.jsx
│ │ │ ├── GenericDatePicker.css
│ │ │ ├── GenericDatePicker.jsx
│ │ │ ├── HeadingChooser.css
│ │ │ ├── HeadingChooser.jsx
│ │ │ ├── IconChooser.jsx
│ │ │ ├── IconStyleChooser.jsx
│ │ │ ├── InputBox.jsx
│ │ │ ├── MarkdownPreview.css
│ │ │ ├── MarkdownPreview.jsx
│ │ │ ├── MentionChooser.css
│ │ │ ├── MentionChooser.jsx
│ │ │ ├── MultiSelectChooser.css
│ │ │ ├── MultiSelectChooser.jsx
│ │ │ ├── NoteChooser.css
│ │ │ ├── NoteChooser.jsx
│ │ │ ├── OrderingPanel.css
│ │ │ ├── OrderingPanel.jsx
│ │ │ ├── PatternChooser.jsx
│ │ │ ├── SearchableChooser.css
│ │ │ ├── SearchableChooser.jsx
│ │ │ ├── SpaceChooser.css
│ │ │ ├── SpaceChooser.jsx
│ │ │ ├── Switch.jsx
│ │ │ ├── TableOfContents.css
│ │ │ ├── TableOfContents.jsx
│ │ │ ├── TagChooser.css
│ │ │ ├── TagChooser.jsx
│ │ │ ├── TemplateJSBlock.css
│ │ │ ├── TemplateJSBlock.jsx
│ │ │ ├── TextComponent.jsx
│ │ │ ├── ThemedSelect.jsx
│ │ │ ├── _README.md
│ │ │ ├── __tests__/
│ │ │ │ └── DropdownSelect.test.jsx
│ │ │ ├── dateFormatOptions.js
│ │ │ ├── dialogElementRenderer.js
│ │ │ ├── index.js
│ │ │ ├── useRequestWithRetry.js
│ │ │ └── valueInsertData.js
│ │ ├── EditableInput.jsx
│ │ ├── FilterableList.css
│ │ ├── FilterableList.jsx
│ │ ├── IdleTimer.jsx
│ │ ├── InfoIcon.css
│ │ ├── InfoIcon.jsx
│ │ ├── List.css
│ │ ├── List.jsx
│ │ ├── Modal/
│ │ │ ├── Modal.css
│ │ │ ├── Modal.jsx
│ │ │ ├── ModalWithTooltip.jsx
│ │ │ └── index.js
│ │ ├── ModalSpinner.jsx
│ │ ├── ModifierHints.css
│ │ ├── ModifierHints.jsx
│ │ ├── NonModalSpinner.jsx
│ │ ├── SearchBox.jsx
│ │ ├── SimpleDialog.css
│ │ ├── SimpleDialog.jsx
│ │ ├── TestingPane.css
│ │ ├── TestingPane.jsx
│ │ ├── ThemedSelect.jsx
│ │ ├── dateStrings.js
│ │ ├── pluginRequestEnvelope.js
│ │ ├── reactDev.js
│ │ ├── reactMouseKeyboard.js
│ │ ├── reactUtils.js
│ │ ├── routerUtils.js
│ │ ├── testSimpleDialog.js
│ │ ├── useEffectGuard.js
│ │ └── userInput.jsx
│ ├── regex.js
│ ├── regexEscape.js
│ ├── search.js
│ ├── sorting.js
│ ├── stringTransforms.js
│ ├── syncedCopies.js
│ ├── teamspace.js
│ ├── testing/
│ │ ├── CustomError.js
│ │ ├── expect.js
│ │ └── testingUtils.js
│ ├── timeblocks.js
│ ├── urls.js
│ ├── userInput.js
│ └── utils.js
├── index.js
├── jest.config.js
├── jest.customSummaryReporter.js
├── jest.noteplan-globals.js
├── jgclark.DailyJournal/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── index.js
│ ├── journal.js
│ ├── journalHelpers.js
│ └── templatesStartEnd.js
├── jgclark.Dashboard/
│ ├── ADDING_A_DASHBOARD_SETTING.md
│ ├── ARCHITECTURE-How_Stuff_Works.md
│ ├── CHANGELOG.md
│ ├── DASHBOARD_HELPERS_REVIEW.md
│ ├── README.md
│ ├── plugin.json
│ ├── plugin.json.orig
│ ├── readme-react.md
│ ├── requiredFiles/
│ │ └── css.plugin.css
│ └── src/
│ ├── NPHooks.js
│ ├── __tests__/
│ │ ├── dashboardHelpers.test.js
│ │ └── dashboardLineToNPDisplayHTML.test.js
│ ├── bannerClickHandlers.js
│ ├── clickHandlers.js
│ ├── constants.js
│ ├── countDoneTasks.js
│ ├── dashboardHelpers.js
│ ├── dashboardHooks.js
│ ├── dashboardSettings.js
│ ├── dataGeneration.js
│ ├── dataGenerationDays.js
│ ├── dataGenerationOverdue.js
│ ├── dataGenerationPriority.js
│ ├── dataGenerationProjects.js
│ ├── dataGenerationSearch.js
│ ├── dataGenerationTags.js
│ ├── dataGenerationWeeks.js
│ ├── demoData.js
│ ├── diagnosticGenerator.js
│ ├── index.js
│ ├── moveClickHandlers.js
│ ├── moveDayClickHandlers.js
│ ├── moveWeekClickHandlers.js
│ ├── perspectiveClickHandlers.js
│ ├── perspectiveHelpers.js
│ ├── perspectivesShared.js
│ ├── pluginToHTMLBridge.js
│ ├── projectClickHandlers.js
│ ├── react/
│ │ ├── components/
│ │ │ ├── AddButtons.jsx
│ │ │ ├── AppContext.jsx
│ │ │ ├── Button.jsx
│ │ │ ├── CalendarPicker.jsx
│ │ │ ├── CircularProgressBar.jsx
│ │ │ ├── ComboBox.jsx
│ │ │ ├── CommandButton.jsx
│ │ │ ├── Dashboard.jsx
│ │ │ ├── Dialog.jsx
│ │ │ ├── DialogForProjectItems.jsx
│ │ │ ├── DialogForTaskItems.jsx
│ │ │ ├── DropdownMenu.jsx
│ │ │ ├── Header/
│ │ │ │ ├── AddToAnyNote.css
│ │ │ │ ├── AddToAnyNote.jsx
│ │ │ │ ├── DoneCounts.jsx
│ │ │ │ ├── Header.css
│ │ │ │ ├── Header.jsx
│ │ │ │ ├── PerspectiveSelector.jsx
│ │ │ │ ├── SearchBar.css
│ │ │ │ ├── SearchBar.jsx
│ │ │ │ ├── SearchPanel.css
│ │ │ │ ├── SearchPanel.jsx
│ │ │ │ ├── __tests__/
│ │ │ │ │ └── PerspectiveSelector.test.jsx
│ │ │ │ ├── featureFlagItems.js
│ │ │ │ ├── filterDropdownItems.js
│ │ │ │ ├── headerDropdownHandlers.js
│ │ │ │ ├── index.js
│ │ │ │ └── useLastFullRefresh.js
│ │ │ ├── IdleTimer.jsx
│ │ │ ├── InputBox.jsx
│ │ │ ├── ItemContent.jsx
│ │ │ ├── ItemGrid.jsx
│ │ │ ├── ItemNoteLink.jsx
│ │ │ ├── ItemRow.css
│ │ │ ├── ItemRow.jsx
│ │ │ ├── MessageOnlyItem.jsx
│ │ │ ├── Modal/
│ │ │ │ ├── Modal.css
│ │ │ │ ├── Modal.jsx
│ │ │ │ └── index.js
│ │ │ ├── MultiSelectSpaces.jsx
│ │ │ ├── NoTasks.jsx
│ │ │ ├── NoteTitleLink.jsx
│ │ │ ├── PerspectiveSettings.jsx
│ │ │ ├── PerspectivesTable.jsx
│ │ │ ├── ProjectItem.jsx
│ │ │ ├── RefreshControl.jsx
│ │ │ ├── Section/
│ │ │ │ ├── Section.css
│ │ │ │ ├── Section.jsx
│ │ │ │ ├── index.js
│ │ │ │ ├── sectionHelpers.js
│ │ │ │ └── useSectionSortAndFilter.jsx
│ │ │ ├── SettingsDialog.jsx
│ │ │ ├── SmallCircularProgressIndicator.jsx
│ │ │ ├── StatusIcon.jsx
│ │ │ ├── Switch.jsx
│ │ │ ├── TaskItem.css
│ │ │ ├── TaskItem.jsx
│ │ │ ├── TasksFiltered.css
│ │ │ ├── TasksFiltered.jsx
│ │ │ ├── TextComponent.jsx
│ │ │ ├── ThemedComboBox.jsx
│ │ │ ├── ToolTipOnModifierPress.jsx
│ │ │ ├── Tooltip.jsx
│ │ │ ├── WebView.jsx
│ │ │ ├── __tests__/
│ │ │ │ └── DropdownMenu.test.jsx
│ │ │ └── testing/
│ │ │ ├── dashboardSettings.tests.js
│ │ │ ├── general.tests.js
│ │ │ ├── perspectives.tests.js
│ │ │ ├── sectionHelpers.test.js
│ │ │ ├── testingHelpers.js
│ │ │ ├── tests.js
│ │ │ └── useSectionSortAndFilter.test.js
│ │ ├── css/
│ │ │ ├── CalendarPicker.css
│ │ │ ├── Dashboard.css
│ │ │ ├── DashboardDialog.css
│ │ │ ├── DropdownMenu.css
│ │ │ ├── MultiSelectSpaces.css
│ │ │ ├── PerspectiveSettings.css
│ │ │ ├── PerspectivesTable.css
│ │ │ ├── ProgressBar.css
│ │ │ ├── SettingsDialog.css
│ │ │ ├── Tooltip.css
│ │ │ └── animation.css
│ │ ├── customHooks/
│ │ │ ├── useMidnightRollover.jsx
│ │ │ ├── useRefreshTimer.jsx
│ │ │ ├── useSettingsDialogHandler.jsx
│ │ │ ├── useSyncDashboardSettingsWithPlugin.js
│ │ │ ├── useSyncPerspectivesWithPlugin.js
│ │ │ └── useWatchForResizes.jsx
│ │ ├── dashboardLineToNPDisplayHTML.js
│ │ ├── reducers/
│ │ │ ├── actionTypes.js
│ │ │ ├── dashboardSettingsReducer.js
│ │ │ └── perspectiveSettingsReducer.js
│ │ └── support/
│ │ ├── performRollup.node.js
│ │ ├── rollup.WebView.entry.js
│ │ ├── settingsHelpers.js
│ │ └── uiElementRenderHelpers.js
│ ├── reactMain.js
│ ├── refreshClickHandlers.js
│ ├── requestHandlers/
│ │ └── addTaskToNote.js
│ ├── routeRequestsFromReact.js
│ ├── shared.js
│ ├── tagMentionCache.js
│ └── types.js
├── jgclark.EventHelpers/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── eventsToNotes.test.js
│ ├── plugin.json
│ └── src/
│ ├── eventsHelpers.js
│ ├── eventsToNotes.js
│ ├── index.js
│ ├── offsets.js
│ └── timeblocks.js
├── jgclark.Filer/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── moveCompletedToDone.test.js
│ ├── plugin.json
│ └── src/
│ ├── IDs.js
│ ├── archive.js
│ ├── filerHelpers.js
│ ├── index.js
│ ├── moveCompletedToDone.js
│ ├── moveItems.js
│ └── noteLinks.js
├── jgclark.Journalling/
│ └── CHANGELOG.md
├── jgclark.MOCs/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── MOCs.js
│ └── index.js
├── jgclark.NoteHelpers/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── duplicateNote.test.js
│ │ ├── newNote.test.js
│ │ └── unLinkedNoteFinder.test.js
│ ├── plugin.json
│ └── src/
│ ├── countDays.js
│ ├── duplicateNote.js
│ ├── helpers/
│ │ ├── findInconsistentNames.js
│ │ ├── makeNoteTitleMatchFilename.js
│ │ └── renameNotes.js
│ ├── index.js
│ ├── indexFolders.js
│ ├── lib/
│ │ └── commands/
│ │ ├── filenameToTitle.js
│ │ ├── listInconsistentNames.js
│ │ ├── renameInconsistentNames.js
│ │ └── titleToFilename.js
│ ├── listPublishedNotes.js
│ ├── newNote.js
│ ├── noteHelpers.js
│ ├── noteNavigation.js
│ ├── unlinkedNoteFinder.js
│ └── writeModified.js
├── jgclark.PeriodicReviews/
│ └── src/
│ └── reviewHTMLViewGenerator.js
├── jgclark.QuickCapture/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── inbox.js
│ ├── index.js
│ ├── quickCapture.js
│ └── quickCaptureHelpers.js
├── jgclark.RepeatExtensions/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── repeatHelpers.test.js
│ │ └── repeatTrigger.test.js
│ ├── plugin.json
│ └── src/
│ ├── index.js
│ ├── repeatHelpers.js
│ ├── repeatMain.js
│ ├── repeatPara.js
│ └── repeatTrigger.js
├── jgclark.Reviews/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── css/
│ │ ├── fontawesome.css
│ │ ├── regular.css
│ │ └── solid.css
│ ├── experiments/
│ │ ├── CSS-circle-test.html
│ │ ├── SVG-circle-test-attempt1.html
│ │ ├── SVG-circle-test-attempt2.html
│ │ ├── SVG-circle-test-attempt3.html
│ │ ├── chart-experiments.js
│ │ ├── font-tests.html
│ │ └── fontTests.js
│ ├── plugin.json
│ ├── remove_combined_fm_metadata_8323fd97.plan.md
│ ├── requiredFiles/
│ │ ├── HTMLWinCommsSwitchboard.js
│ │ ├── projectList.css
│ │ ├── projectListDialog.css
│ │ ├── projectListEvents.js
│ │ ├── shortcut.js
│ │ └── showTimeAgo.js
│ └── src/
│ ├── __tests__/
│ │ ├── allProjectsListHelpers.sorting.test.js
│ │ ├── filterProjectNotesByFolders.test.js
│ │ ├── getMetadataLineIndexFromBody.test.js
│ │ ├── noteChangeCache.test.js
│ │ ├── projectClass.defaultReviewInterval.test.js
│ │ ├── projectClass.embeddedCombinedMentions.test.js
│ │ ├── projectClass.frontmatterParsing.test.js
│ │ └── reviewHelpers.clearNextReviewFrontmatterField.test.js
│ ├── allProjectsListHelpers.js
│ ├── convertNote.js
│ ├── index.js
│ ├── migration.js
│ ├── migrationLog.js
│ ├── pluginToHTMLBridge.js
│ ├── projectClass.js
│ ├── projectClassCalculations.js
│ ├── projectClassHelpers.js
│ ├── projects.js
│ ├── projectsHTMLGenerator.js
│ ├── projectsHTMLTemplates.js
│ ├── projectsWeeklyProgress.js
│ ├── reviewHelpers.js
│ └── reviews.js
├── jgclark.SearchExtensions/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── searchHelpers.test.js
│ ├── plugin.json
│ ├── requiredFiles/
│ │ └── flexiSearch.css
│ └── src/
│ ├── externalSearch.js
│ ├── flexiSearch.js
│ ├── index.js
│ ├── replace.js
│ ├── saveSearch.js
│ ├── searchHelpers.js
│ └── searchTriggers.js
├── jgclark.Summaries/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ ├── requiredFiles/
│ │ ├── README-chart.md
│ │ ├── chartStats.css
│ │ └── chartStatsScripts.js
│ └── src/
│ ├── TMOccurrences.js
│ ├── __tests__/
│ │ └── chartStatsDisplayStats.test.js
│ ├── chartStats.js
│ ├── configHelpers.js
│ ├── dateHelpers.js
│ ├── forCharts.js
│ ├── forHeatmaps.js
│ ├── gatherOccurrencesHelpers.js
│ ├── index.js
│ ├── progress.js
│ ├── stats.js
│ ├── summaryHelpers.js
│ ├── summarySettings.js
│ ├── testCharting.js
│ └── todayProgress.js
├── jgclark.WindowTools/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── WTHelpers.js
│ ├── index.js
│ ├── openers.js
│ ├── otherWindowTools.js
│ └── windowSets.js
├── jgclark.tests/
│ ├── plugin.json
│ └── src/
│ └── index.js
├── m1well.Expenses/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── expensesChecks.test.js
│ │ └── expensesHelper.test.js
│ ├── plugin.json
│ └── src/
│ ├── expenses.js
│ ├── expensesChecks.js
│ ├── expensesHelper.js
│ ├── expensesModels.js
│ └── index.js
├── nmn.DataQuery/
│ ├── plugin.json
│ ├── readme.md
│ └── src/
│ └── index.js
├── nmn.TimeTracking/
│ ├── plugin.json
│ ├── readme.md
│ └── src/
│ └── index.js
├── np.CallbackURLs/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── NPXCallbackWizard.test.js
│ │ └── utils.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPOpenFolders.js
│ ├── NPOpenLinks.js
│ ├── NPTemplateRunner.js
│ ├── NPXCallbackWizard.js
│ ├── index.js
│ └── support/
│ └── utils.js
├── np.Globals/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── globals.test.js
│ ├── lib/
│ │ └── NPGlobals.js
│ ├── plugin.json
│ └── src/
│ ├── Globals.js
│ └── index.js
├── np.MeetingNotes/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── hello-world.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPMeetingNotes.js
│ ├── index.js
│ └── support/
│ └── hello-world.js
├── np.Preview/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ ├── requiredFiles/
│ │ ├── mermaid@10.1.0.min.mjs
│ │ └── tex-chtml.js
│ └── src/
│ ├── bundling/
│ │ └── performMermaidRollup.node.js
│ ├── index.js
│ ├── mathTests.js
│ ├── mermaidTests.js
│ ├── previewMain.js
│ ├── previewTriggers.js
│ └── testCheckboxes.js
├── np.Shared/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ ├── requiredFiles/
│ │ ├── css.w3.css
│ │ ├── duotone.min.flat4NP.css
│ │ ├── encodeDecode.js
│ │ ├── fontawesome.css
│ │ ├── light.min.flat4NP.css
│ │ ├── noteplanstate-edited.otf
│ │ ├── pluginToHTMLCommsBridge.js
│ │ ├── pluginToHTMLErrorBridge.js
│ │ ├── regular.min.flat4NP.css
│ │ ├── shortcut.js
│ │ └── solid.min.flat4NP.css
│ └── src/
│ ├── NPReactLocal.js
│ ├── chooserHandlers.js
│ ├── index.js
│ ├── react/
│ │ ├── ErrorFallback.jsx
│ │ ├── MessageBanner.css
│ │ ├── MessageBanner.jsx
│ │ ├── Root.css
│ │ ├── Root.jsx
│ │ ├── Toast.css
│ │ ├── Toast.jsx
│ │ └── support/
│ │ ├── performRollup.node.js
│ │ ├── rollup.react.entry.js
│ │ └── rollup.root.entry.js
│ ├── requestHandlers/
│ │ ├── getAvailableCalendars.js
│ │ ├── getAvailableReminderLists.js
│ │ ├── getEvents.js
│ │ ├── getFolders.js
│ │ ├── getFrontmatterKeyValues.js
│ │ ├── getHashtags.js
│ │ ├── getHeadings.js
│ │ ├── getMentions.js
│ │ ├── getNotes.js
│ │ ├── getTeamspaces.js
│ │ └── noteHelpers.js
│ └── sharedRequestRouter.js
├── np.TOC/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── NPTriggers-Hooks.js
│ ├── index.js
│ ├── insertTOC.js
│ └── support/
│ ├── TOC-Heading.md
│ ├── TOC-Template.md
│ └── helpers.js
├── np.Templating/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── TEMP_DOCS_TemplateRunner_getNoteTitled_calendar_targets.md
│ ├── __tests__/
│ │ ├── BasePromptHandler.test.js
│ │ ├── NPTemplateNoteHelpers.test.js
│ │ ├── NPTemplateRunner.test.js
│ │ ├── ai-error-analysis.test.js
│ │ ├── ai-override.test.js
│ │ ├── awaitVariableAssignment.test.js
│ │ ├── date-module-now-fix.test.js
│ │ ├── date-module-timezone-debug.test.js
│ │ ├── date-module-timezone-simple.test.js
│ │ ├── date-module-timezone-working.test.js
│ │ ├── date-module.test.js
│ │ ├── ejs-error-handling.test.js
│ │ ├── error-handling.test.js
│ │ ├── factories/
│ │ │ ├── async.ejs
│ │ │ ├── complex-json-template.ejs
│ │ │ ├── custom-tags.ejs
│ │ │ ├── date-reference.ejs
│ │ │ ├── dates-various.ejs
│ │ │ ├── dates.ejs
│ │ │ ├── day-header-template.ejs
│ │ │ ├── double-dashes-in-body.ejs
│ │ │ ├── error-sample.ejs
│ │ │ ├── extended.ejs
│ │ │ ├── frontmatter-convert-project-note.md
│ │ │ ├── frontmatter-convert-success.md
│ │ │ ├── frontmatter-extended.ejs
│ │ │ ├── frontmatter-illegal-attribute.ejs
│ │ │ ├── frontmatter-indented.ejs
│ │ │ ├── frontmatter-minimal.ejs
│ │ │ ├── frontmatter-practical.ejs
│ │ │ ├── frontmatter-quick-note.ejs
│ │ │ ├── frontmatter-with-asterick-separators.ejs
│ │ │ ├── frontmatter-with-double-dashes.ejs
│ │ │ ├── frontmatter-with-multiple-fm-like-lines1.ejs
│ │ │ ├── frontmatter-with-multiple-fm-like-lines2.ejs
│ │ │ ├── frontmatter-with-separators.ejs
│ │ │ ├── frontmatter-yml.ejs
│ │ │ ├── invalid-json-test.ejs
│ │ │ ├── invalid-line-error.ejs
│ │ │ ├── invalid-syntax.ejs
│ │ │ ├── missing-object.ejs
│ │ │ ├── multiple-imports-one-line-return.ejs
│ │ │ ├── multiple-imports.ejs
│ │ │ ├── nested-templates.ejs
│ │ │ ├── simple-function.ejs
│ │ │ ├── simple.ejs
│ │ │ ├── simulate-tasks.ejs
│ │ │ ├── single-quoted-json-template.ejs
│ │ │ ├── stop-on-json-error.ejs
│ │ │ ├── syntax-error-template.ejs
│ │ │ ├── tags-extended.ejs
│ │ │ ├── tags-function.ejs
│ │ │ ├── tags.ejs
│ │ │ ├── template-logic.ejs
│ │ │ ├── ternary.ejs
│ │ │ ├── times.ejs
│ │ │ └── web-await-tests.ejs
│ │ ├── frontmatter-error-handling.test.js
│ │ ├── frontmatter-module.test.js
│ │ ├── full-pipeline-integration.test.js
│ │ ├── getRenderContext.test.js
│ │ ├── getTemplate.test.js
│ │ ├── import-tag-processor.test.js
│ │ ├── include-tag-processor.test.js
│ │ ├── isCode.test.js
│ │ ├── merge-statements.test.js
│ │ ├── preprocess-functions.test.js
│ │ ├── prompt-cancellation.test.js
│ │ ├── promptAwaitIssue.test.js
│ │ ├── promptDate.test.js
│ │ ├── promptDateInterval.test.js
│ │ ├── promptEdgeCases.test.js
│ │ ├── promptFormBatch.test.js
│ │ ├── promptFormTag.test.js
│ │ ├── promptIntegration.test.js
│ │ ├── promptKey.test.js
│ │ ├── promptRegistry.test.js
│ │ ├── promptSafetyChecks.test.js
│ │ ├── promptTagAndMention.test.js
│ │ ├── promptTagOutputBehavior.test.js
│ │ ├── promptTagSingleParameter.test.js
│ │ ├── promptVariableAssignment.test.js
│ │ ├── render-pipeline.test.js
│ │ ├── renderTemplate.test.js
│ │ ├── setup.js
│ │ ├── sharedPromptFunctions.test.js
│ │ ├── smart-quotes.test.js
│ │ ├── standardPrompt.test.js
│ │ ├── stringUtils.test.js
│ │ ├── tagUtils.test.js
│ │ ├── template-error-handling.test.js
│ │ ├── template-preprocessing.test.js
│ │ ├── template-preprocessor-regression.test.js
│ │ ├── template-preprocessor.test.js
│ │ ├── template-render-preprocessor.test.js
│ │ ├── templateManager.test.js
│ │ ├── templateRenderer.test.js
│ │ ├── templateUtils.test.js
│ │ ├── templateVariableAssignment.test.js
│ │ ├── templateVariableValidation.test.js
│ │ ├── templating.test.js
│ │ ├── testUtils.js
│ │ ├── time-module.test.js
│ │ ├── unquotedParameterTest.test.js
│ │ ├── variableAssignmentQuotesBug.test.js
│ │ ├── web-api-tests.test.js
│ │ ├── web-await-tests.test.js
│ │ └── web-module.test.js
│ ├── docs/
│ │ ├── AddingNewPromptCommands.md
│ │ ├── PromptCommandBarForms.md
│ │ ├── PromptCommands.md
│ │ ├── PromptTagsList.md
│ │ └── PromptTestingSummary.md
│ ├── lib/
│ │ ├── NPTemplateNoteHelpers.js
│ │ ├── NPTemplating.js
│ │ ├── REFACTORING_PLAN.md
│ │ ├── REFACTORING_SUMMARY.md
│ │ ├── TemplatingEngine.js
│ │ ├── config/
│ │ │ ├── configManager.js
│ │ │ └── index.js
│ │ ├── core/
│ │ │ ├── index.js
│ │ │ ├── tagUtils.js
│ │ │ └── templateManager.js
│ │ ├── engine/
│ │ │ ├── aiAnalyzer.js
│ │ │ ├── errorProcessor.js
│ │ │ ├── frontmatterProcessor.js
│ │ │ ├── pluginIntegrator.js
│ │ │ ├── renderOrchestrator.js
│ │ │ └── templateRenderer.js
│ │ ├── globals.js
│ │ ├── handlers/
│ │ │ └── index.js
│ │ ├── helpers.js
│ │ ├── rendering/
│ │ │ ├── __tests__/
│ │ │ │ └── templateProcessor.test.js
│ │ │ ├── errorHandler.js
│ │ │ ├── index.js
│ │ │ ├── templateProcessor.js
│ │ │ └── templateValidator.js
│ │ ├── shared/
│ │ │ └── templateUtils.js
│ │ ├── support/
│ │ │ ├── ejs.js
│ │ │ └── modules/
│ │ │ ├── DateModule.js
│ │ │ ├── FrontmatterModule.js
│ │ │ ├── NoteModule.js
│ │ │ ├── SystemModule.js
│ │ │ ├── TasksModule.js
│ │ │ ├── TimeModule.js
│ │ │ ├── UtilityModule.js
│ │ │ ├── WebModule.js
│ │ │ ├── advice.js
│ │ │ ├── affirmation.js
│ │ │ ├── data/
│ │ │ │ ├── adviceData.js
│ │ │ │ ├── affirmations.js
│ │ │ │ ├── service.js
│ │ │ │ └── stoicQuotes.js
│ │ │ ├── helpers-example.md
│ │ │ ├── helpersModule.js
│ │ │ ├── journal.js
│ │ │ ├── notePlanWeather.js
│ │ │ ├── prompts/
│ │ │ │ ├── AddingNewPromptCommands.md
│ │ │ │ ├── BasePromptHandler.js
│ │ │ │ ├── PromptDateHandler.js
│ │ │ │ ├── PromptDateIntervalHandler.js
│ │ │ │ ├── PromptFormHandler.js
│ │ │ │ ├── PromptKeyHandler.js
│ │ │ │ ├── PromptManager.js
│ │ │ │ ├── PromptMentionHandler.js
│ │ │ │ ├── PromptRegistry.js
│ │ │ │ ├── PromptTagHandler.js
│ │ │ │ ├── README.md
│ │ │ │ ├── StandardPromptHandler.js
│ │ │ │ ├── handlers/
│ │ │ │ │ └── index.js
│ │ │ │ ├── index.js
│ │ │ │ ├── promptFormBatch.js
│ │ │ │ ├── promptTagParse.js
│ │ │ │ ├── promptTypesRegistry.js
│ │ │ │ └── sharedPromptFunctions.js
│ │ │ ├── quote.js
│ │ │ ├── stoicQuotes.js
│ │ │ ├── verse.js
│ │ │ ├── weather.js
│ │ │ ├── weatherSummary.js
│ │ │ └── wotd.js
│ │ ├── toolbox_old.js
│ │ └── utils/
│ │ ├── codeProcessing.js
│ │ ├── dateHelpers.js
│ │ ├── errorHandling.js
│ │ ├── index.js
│ │ ├── pluginIntegration.js
│ │ └── stringUtils.js
│ ├── plugin.json
│ ├── plugins/
│ │ ├── BiblePlugin.js
│ │ └── WeatherPlugin.js
│ ├── samples/
│ │ ├── Sample Template.md
│ │ ├── Test (Execute Quick).md
│ │ ├── Test (Execute).md
│ │ ├── Test (Include).md
│ │ ├── Test (Snippets).md
│ │ ├── Test Note Included.md
│ │ ├── miscellaneous/
│ │ │ └── Restaurants.md
│ │ ├── prompt-edge-cases/
│ │ │ └── README.md
│ │ ├── section1.md
│ │ └── snippets/
│ │ ├── README.md
│ │ ├── strings-obj.md
│ │ └── strings.md
│ └── src/
│ ├── NPTemplateRunner.js
│ ├── Templating.js
│ ├── commands.js
│ └── index.js
├── np.ThemeChooser/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── themeHelpers.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPThemeChooser.js
│ ├── NPThemeCustomizer.js
│ ├── NPThemeHTML.js
│ ├── NPThemeHooks.js
│ ├── NPThemePresets.js
│ ├── NPThemeShared.js
│ ├── chooseColor.js
│ ├── index.js
│ └── support/
│ ├── masterTheme.json
│ └── themeHelpers.js
├── np.Tidy/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── cancelIncompleteTasks.test.js
│ │ ├── emptyBlocks.test.js
│ │ └── topLevelTasks.test.js
│ ├── plugin.json
│ └── src/
│ ├── cancelIncompleteTasks.js
│ ├── cleanFilenames.js
│ ├── conflicts.js
│ ├── doubledNotes.js
│ ├── duplicates.js
│ ├── emptyElements.js
│ ├── fileRoot.js
│ ├── index.js
│ ├── lineLinks.js
│ ├── missingDailyNotes.js
│ ├── tidyHelpers.js
│ ├── tidyMain.js
│ ├── tidyRemoveSections.js
│ ├── tidyRepeats.js
│ ├── tidyStubs.js
│ ├── topLevelTasks.js
│ └── triggersHooks.js
├── np.WeatherLookup/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── utils.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPWeatherLookup.js
│ ├── index.js
│ └── support/
│ ├── old-weather-for-reference.txt
│ └── weather-utils.js
├── np.installer/
│ └── plugin.json
├── np.plugin-test/
│ ├── README.md
│ ├── changelog.md
│ ├── plugin.json
│ ├── requiredFiles/
│ │ └── css.plugin.css
│ └── src/
│ ├── commandListGenerator.js
│ ├── index.js
│ ├── pluginCommandsPopup.js
│ ├── pluginTester.js
│ └── react/
│ ├── Button.jsx
│ ├── Checkbox.jsx
│ ├── CompositeLineExample.jsx
│ ├── PluginListingPage.jsx
│ ├── WebView.jsx
│ ├── __test__/
│ │ └── filterFunctions.test.js
│ └── support/
│ ├── filterFunctions.jsx
│ ├── performRollup.node.js
│ └── rollup.WebView.entry.js
├── np.statistics/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── index.js
│ ├── showNoteCount.js
│ ├── showWordCount.js
│ └── taskNoteStats.js
├── package.json
├── plugins.config.js
├── scripts/
│ ├── __tests__/
│ │ ├── releases.test.js
│ │ └── rollup.generic.tes.js
│ ├── generateDocs.js
│ ├── releases.js
│ ├── rollup.generic.js
│ ├── rollup.js
│ └── shared.js
├── shared.AI/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── NPBulletsAI-Main.test.js
│ │ ├── externalFileInteractions.test.js
│ │ └── helpers.test.js
│ ├── non-implemented_functions.js
│ ├── plugin.json
│ └── src/
│ ├── BulletsAI-Main.js
│ ├── NPAI.js
│ ├── chat.js
│ ├── imageAI.js
│ ├── index.js
│ ├── summarize.js
│ └── support/
│ ├── .readme_text/
│ │ ├── commands.md
│ │ ├── gettingstarted.md
│ │ └── preferences.md
│ ├── AIFlowTypes.js
│ ├── externalFileInteractions.js
│ ├── fetchOverrides.js
│ ├── fetchResponses/
│ │ ├── completions.heatTransfer.json
│ │ ├── completions.heatTransferKeyTopics.json
│ │ ├── completions.mercury.json
│ │ ├── completions.mercuryKeyTopics.json
│ │ ├── completions.thermalProtection.json
│ │ ├── completions.thermalProtectionKeyTopics.json
│ │ └── summarize_3 Little Pigs.1.json
│ ├── formatters.js
│ ├── helpers.js
│ ├── introwizard.js
│ ├── networking.js
│ ├── onboarding.js
│ ├── onboardingText.js
│ ├── prompts.js
│ └── settingsAdjustments.js
├── src/
│ ├── __tests__/
│ │ └── README.md
│ ├── commands/
│ │ ├── PluginCreate.js
│ │ ├── PluginDevelop.js
│ │ ├── PluginPullRequest.js
│ │ ├── PluginRelease.js
│ │ ├── PluginTest.js
│ │ └── support/
│ │ ├── github.js
│ │ ├── plugin-create.js
│ │ ├── plugin-info.js
│ │ ├── plugin-pull-request.js
│ │ ├── plugin-release/
│ │ │ ├── git-tasks.js
│ │ │ ├── prerequisite-tasks.js
│ │ │ ├── release-prompts.js
│ │ │ ├── release-tasks.js
│ │ │ ├── script-grep.js
│ │ │ └── update-version-tasks.js
│ │ ├── plugin-release.js
│ │ ├── plugin-test.js
│ │ ├── plugin-utils.js
│ │ └── release-management.js
│ ├── templates/
│ │ └── np.plugin.starter/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── __tests__/
│ │ │ └── NPPluginMain.NOTACTIVE.js
│ │ ├── plugin.json
│ │ ├── requiredFiles/
│ │ │ └── html-plugin-comms.js
│ │ └── src/
│ │ ├── NPMessagesFromHTMLWindow.js
│ │ ├── NPPluginMain.js
│ │ ├── NPTriggers-Hooks.js
│ │ ├── index.js
│ │ └── support/
│ │ ├── fetchOverrides.js
│ │ ├── fetchResponses/
│ │ │ └── google.search-for-something.json
│ │ └── helpers.js
│ └── utils/
│ ├── app.js
│ ├── general.js
│ └── security.lib.js
└── tasks/
├── bumpBuild.js
├── init.js
└── start.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .cursor/commands/np-plugin-commands.md
================================================
# np-plugin-commands
Write your command content here.
This command will be available in chat with /np-plugin-commands
- Build plugin: `npc plugin:dev <plugin-id> -nc`
- Build react runtime: `node ./<plugin-id>/src/react/support/performRollup.node.js`
================================================
FILE: .cursor/rules/html-react-rules.mdc
================================================
---
alwaysApply: true
---
- For NotePlan code, never use require statements. Use import statements at the top of the file -- never inline/dynamically import modules. Rollup will not process them correctly.
- You should not ever try to build the code with "npm run build" -- this will not work. The programmer will have to use Rollup to build the code. There is separate tooling for this described in @README.md. Leave the building to the programmer.
- NotePlan has global variables that are available to all plugins such as DataStore, CommandBar, Editor, and NotePlan. You can use these variables to access the NotePlan API. You do not need to import them.
# Do not commit or push any changes until I tell you to.
# Any time you create or are working on a DIV, make sure that DIV has a human-understandable class name so we can debug more easily
## Promise Polyfills
**Do not use raw `Promise.resolve`, `Promise.all`, or `Promise.race` in plugin code—NotePlan's JSContext may not have them.** Use polyfills from `@helpers/promisePolyfill.js`: `initPromisePolyfills()`, `promiseResolve()`, `promiseAll()`, `promiseRace()`, `waitForCondition()`, `setTimeoutPolyfill()`. After creating/modifying notes, call `DataStore.updateCache(note, true)`.
## React Function Memoization (CRITICAL)
**ALWAYS wrap functions passed to React Context or child components in `useCallback`.** This prevents infinite render loops that crash the app.
```javascript
// ❌ WRONG - Causes infinite loops
const requestFromPlugin = (command: string, dataToSend: any = {}) => { ... }
// ✅ CORRECT - Stable function reference
const requestFromPlugin = useCallback((command: string, dataToSend: any = {}) => {
// ... implementation
}, [dispatch]) // Minimal dependencies
```
**Functions that MUST be memoized:**
- `requestFromPlugin`, `sendActionToPlugin`, `sendToPlugin`
- Any function passed to `AppProvider` props
- Any function used in `useEffect` dependency arrays
**AppContext MUST use `useMemo`:**
```javascript
const contextValue = useMemo(() => ({
sendActionToPlugin, sendToPlugin, requestFromPlugin, dispatch, pluginData
}), [sendActionToPlugin, sendToPlugin, requestFromPlugin, dispatch, pluginData])
```
**This has caused infinite loops 5+ times. Always verify function memoization before considering code complete.**
📚 For detailed examples, see `@dwertheimer.ReactSkeleton/REACT_COMMUNICATION_PATTERNS.md`
## DynamicDialog Component
Use `DynamicDialog` instead of custom dialog components. Handlers MUST be wrapped in `useCallback`. See `@helpers/react/DynamicDialog/CREATING_NEW_DYNAMICDIALOG_FIELD_TYPES.md` for full guide.
## NotePlan Theme Colors
Use these CSS variables with default fallback values. **DO NOT INVENT NEW CSS VARIABLES.**
```css
--bg-main-color: #eff1f5;
--fg-sidebar-color: #242E32;
--bg-sidebar-color: #ECECEC;
--divider-color: #CDCFD0;
--block-id-color: #79A0B5;
--fg-main-color: #4c4f69;
--h1-color: #5c5f77;
--h2-color: #5c5f77;
--h3-color: #5c5f77;
--bg-alt-color: #e6e9ef;
--tint-color: #dc8a78;
--bg-mid-color: #ebedf2;
--bg-apple-input-color: #fbfbfb;
--bg-apple-switch-color: #dadada;
--fg-apple-switch-color: #ffffff;
--bg-apple-button-color: #fcfcfc;
--item-icon-color: #1e66f5;
--fg-done-color: #04a5e5;
--fg-canceled-color: #4F57A0E0;
--hashtag-color: inherit;
--attag-color: inherit;
--code-color: #0091f8;
--fg-placeholder-color: rgba(76, 79, 105, 0.7);
--fg-error-color: #b85450;
--bg-error-color: #f5e6e6;
--fg-disabled-color: #999999;
--bg-disabled-color: #f5f5f5;
```
Example: `background: var(--bg-main-color, #eff1f5);`
================================================
FILE: .cursor/rules/np-programming-general.mdc
================================================
---
description:
alwaysApply: true
---
## Critical Rules
CRITICAL: Never use dynamic imports. Rollup will not process them correctly. Always use static imports at the top of files.
## Code Style Guidelines
- Use Flow for static typing
- No semicolons (enforced by ESLint/Prettier)
- Single quotes for strings
- Max line length: 180 characters
- Use template literals instead of string concatenation
- Use ES6+ features (const/let, arrow functions)
- Use async/await and handle promises properly (no floating promises)
- Follow existing naming patterns in the codebase
- Proper error handling with try/catch blocks
- Follow import order: external libs -> internal libs -> local files. Within that put in alphabetical order of source filename.
- Keep code DRY and modular with clear function responsibilities
- Add JSDoc comments for all functions
- Always research the `helpers/` folder before writing new code
- Prefer writing explicit functions over constant declarations
- When moving code, keep existing comments and commented-out code
- Please provide Flow types (as appropriate) and JSDOC with all responses.
- If you are rewriting a full file, make sure to include all import statements also.
- Do not remove any console.logs or comments (or logDebug, clo, logInfo, logError statements).
## Common Helper Functions
The most frequently used functions in the codebase are:
- Logging: logDebug, logError, clo, logInfo, logWarn
- Note utilities: findNote, getParagraphs, getSelectedParagraphs, getTasksFromNote
- Date handling: getTodaysDateHyphenated, getDateStringFromCalendarFilename
- Configuration: getSettings, updateSettingsForPlugin
- UI interaction: showMessage, showMessageYesNo, displayTitle
## Testing
- Do not prompt the user to run tests. Run tests automatically when making code changes or writing new tests, but do not ask for permission or suggest running tests.
- Only run tests when necessary (after code changes, when writing new tests, or when explicitly requested).
- Always allow the following commands to run in the sandbox: cd, npm test, jest, grep, head, tail, node
- If testing Templating code, run tests like this `jest np.Templating/__tests__/**/*.test.js --no-watch`
- When you run jest tests, always include the flag --no-watch at the end because my jest config by default watches
- **DO NOT EVER HARD-CODE JEST WORKAROUNDS INTO FUNCTIONS WE ARE TESTING**
## Developer Documentation
- Add updates to the first H2 section in the plugin's CHANGELOG.md file -- this should match the release number as found in the relevant plugin.json file
================================================
FILE: .cursorignore
================================================
# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)
node_modules/
.history/
.git/
================================================
FILE: .eslintrc
================================================
{
"extends": [
"eslint:recommended",
"plugin:import/recommended",
// "plugin:prettier/recommended",
"plugin:import/errors",
"plugin:import/warnings",
"plugin:flowtype/recommended",
"prettier",
"plugin:react/recommended"
],
"plugins": [
// "prettier",
"import",
"flowtype",
"unused-imports",
"no-floating-promise",
"react"
],
"parser": "@babel/eslint-parser",
"parserOptions": {
"ecmaVersion": 2020,
"sourceType": "module",
"requireConfigFile": false,
"ecmaFeatures": {
"jsx": true
},
"babelOptions": {
"presets": [
"@babel/flow",
"@babel/preset-react"
]
}
},
"rules": {
"react/prop-types": "off",
"no-unused-vars": [
1,
{
"args": "all",
"varsIgnorePattern": "^_|^clo$|^clvt$|^timer$|^log|^JSP$|^clof$|^res$|^describe$|^test$|^expect$|^beforeAll$|customConsole$|simpleFormatter$|Note$|^Paragraph$|^NotePlan$|^Editor$|^DataStore$|^CommandBar$|^Calendar$|^CalendarItem$|^Clipboard$|^HTMLView$|^globalThis$",
"argsIgnorePattern": "^_"
}
],
"prefer-template": "warn",
"eqeqeq": [
"error",
"smart"
],
"semi": [
"error",
"never"
],
"curly": [
"error",
"multi-line"
],
"linebreak-style": [
"error",
"unix"
],
"max-len": [
"error",
{
"code": 180,
"ignoreComments": true,
"ignoreStrings": true,
"ignoreTemplateLiterals": true
}
],
"new-cap": "off",
"no-case-declarations": "error",
"no-console": "off",
"no-floating-promise/no-floating-promise": 2,
"no-prototype-builtins": "off",
// "no-return-in-foreach": "error",
"no-useless-escape": "off",
"no-var": "error",
"prefer-const": "warn",
"import/order": "warn",
"react/react-in-jsx-scope": 0,
"require-await": "error",
"unused-imports/no-unused-imports": "off",
"unused-imports/no-unused-vars": "off"
},
"env": {
"node": true,
"es6": true,
"browser": true
},
"globals": {
"Paragraph": true,
"Note": true,
"ParagaraphBridge": true,
"Editor": true,
"DataStore": true,
"CommandBar": true,
"Calendar": true,
"CalendarItem": true,
"Clipboard": true,
"NotePlan": true,
"HTMLView": true,
"globalThis": true,
"fetch": true,
"$ReadOnlyArray": true,
"$ReadOnly": true
},
"ignorePatterns": [
".history",
"node_modules",
"flow-typed",
"*/script.js",
"np.Templating/lib/support/ejs.js",
"**/*.min.js"
],
"settings": {
"import/resolver": {
"alias": {
"map": [
["@plugins", "./"],
["@helpers", "./helpers"],
["@mocks", "./__mocks__"],
["NPTemplating", "./np.Templating/lib/NPTemplating"],
["TemplatingEngine", "./np.Templating"],
["@templating", "./np.Templating/lib"],
["@templatingModules", "./np.Templating/lib/support/modules"],
["NPGlobals", "./np.Globals/lib/NPGlobals"]
],
"extensions": [".js", ".jsx", ".json"]
}
}
}
}
================================================
FILE: .flowconfig
================================================
[ignore]
# removed to fix issue with flow reporting errors with non-typed modules
# details here - https://github.com/facebook/flow/issues/6646#issuecomment-447272772
# <PROJECT_ROOT>/node_modules/.*
# This particular sub-folder should be ignored because it includes malformed JSON
<PROJECT_ROOT>/node_modules/resolve/test/.*
<PROJECT_ROOT>/flow-typed/**/*.*
<PROJECT_ROOT>/flow-typed/Noteplan.js
<PROJECT_ROOT>/private/**/*.*
<PROJECT_ROOT>/**/.history/.*
[include]
[libs]
flow-typed
[lints]
# FIXME: all of the below seem to crash flow in VSCode
#all=warn # warn on everything, except those specified. I don't see why this isn't working.
#sketchy-null=warn
#sketchy-number=warn
#invalid-computed-prop=off
#unnecessary-optional-chain=warn
#unused-promise=warn # was not working for @jgclark at v0.202
[options]
autoimports=false
emoji=true
exact_by_default=true
experimental.const_params=true
module.use_strict=true
suppress_type=$FlowIgnore
suppress_type=$FlowFixMe
# suppress_type=$FlowTODO
module.name_mapper='^@plugins' ->'<PROJECT_ROOT>'
module.name_mapper='^@helpers' ->'<PROJECT_ROOT>/helpers'
module.name_mapper='^@mocks' ->'<PROJECT_ROOT>/__mocks__'
module.name_mapper='^NPTemplating' ->'<PROJECT_ROOT>/np.Templating/lib/NPTemplating'
module.name_mapper='^TemplatingEngine' ->'<PROJECT_ROOT>/np.Templating/lib/TemplatingEngine'
module.name_mapper='^@templating' ->'<PROJECT_ROOT>/np.Templating/lib'
module.name_mapper='^@templatingModules' ->'<PROJECT_ROOT>/np.Templating/lib/support/modules'
module.name_mapper='^NPGlobals' ->'<PROJECT_ROOT>/np.Globals/lib/NPGlobals'
[strict]
================================================
FILE: .github/ISSUE_TEMPLATE/bug_report.md
================================================
---
name: Bug report
about: Create a report to help us improve a Plugin
title: '<plugin name>: <title>'
labels: 'bug'
assignees: ''
---
**Checks**
- [ ] I confirm I have restarted NotePlan and this problem still persists.
- [ ] I confirm I have reinstalled the Plugin and this problem still persists.
- [ ] Is this a repeatable problem?
**System**
- Device OS: [e.g. iOS 15.1, macOS 15.0.1]
- NotePlan Version [e.g. 3.14.2]
- Plugin Name & Version [e.g. "Templates" v0.2]
**Describe the bug**
A concise description of what the bug is.
What do you expect it to do instead?
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Additional context**
Add any other context about the problem here.
**Plugin Console Log**
To provide us with more clues about where the bug/error is occurring...
1. Open the Plugin's Preferences by going to `NotePlan's menu > Preferences > Plugins` and clicking the settings "cog" icon next to the plugin in question. Scroll to the bottom and set the logging level to "DEBUG" and click "Save & Close"
3. Now open the Plugin Console by going to `Noteplan > Help > Plugin Console` (_not the macOS Console app_).
4. Run the plugin command you're reporting
5. Copy the output from the Plugin Console and paste it below
6. Delete any output that has personal information you don't want in there
```javascript
PASTE_PLUGIN_CONSOLE_OUTPUT_HERE
```
================================================
FILE: .github/ISSUE_TEMPLATE/feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for this project
title: '<plugin name>: <title>'
labels: 'feature request'
assignees: ''
---
**What Plugin is this about?**
**What is your feature request designed to achieve? (the 'why' not the 'how')**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like (the 'how')**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
================================================
FILE: .github/workflows/node.js.yml
================================================
# Disable/enable this script at: https://github.com/NotePlan/plugins/actions/workflows/node.js.yml
# Run tests on push or pull_request
# per: https://joelhooks.com/jest-and-github-actions/
# also uses github-actions-reporter.js for reporting
#
# LOCAL ACTIONS TESTING NOTES:
# (this doesn't work because there is no local macos docker image i have found yet)
# test github actions locally using act https://github.com/nektos/act
# first time install: brew install act
# then just run `act` from the command line to test a push action
#
# REMOTE NOTES (e.g. for after a push has been done and failed):
# to see the results of the last github actions run (e.g. on push)
# gh run view
# ... and select the latest run (and at the end it tells you how to see the detailed errors)
name: Node.js CI
on:
workflow_dispatch:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
Run-all-Jest-Tests:
runs-on: macos-latest
strategy:
matrix:
# node-version: [14.x, 16.x, 18.x, 19.x, 20.x]
node-version: [24.x,25.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Disable optional dependencies # work around git bug on macos arm64 dependencies
run: export NPM_CONFIG_OPTIONAL=false
# shoulde eventually be a clean install using: - run: npm ci --legacy-peer-deps
# - run: npm i -g node-gyp@latest && npm config set node_gyp "/usr/local/lib/node_modules/node-gyp/bin/node-gyp.js"
- run: npm ci --legacy-peer-deps # clean install (deletes node_modules)
- run: npm link # necessary for some reason specific to NP dev setup
# note: when upgrade to node 16+, add this to the following --max-old-space-size=8192
- run: NODE_OPTIONS=--max-old-space-size=8192 npm run test:ci # run Jest CI version and stop if errors
- name: Log Passing Tests ✅
if: ${{ success() }}
run: |
curl --request POST \
--url https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }} \
--header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
--header 'content-type: application/json' \
--data '{
"context": "tests",
"state": "success",
"description": "Jest Tests passed",
"target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}'
- name: Log Failed Tests 🚨
if: ${{ failure() }}
run: |
curl --request POST \
--url https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }} \
--header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
--header 'content-type: application/json' \
--data '{
"context": "tests",
"state": "failure",
"description": "Jest Tests failed",
"target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}'
Build-All-Plugins:
runs-on: macos-latest
strategy:
matrix:
node-version: [24.x,25.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
- uses: actions/checkout@v4
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
# shoulde eventually be a clean install using: - run: npm ci --legacy-peer-deps
# - run: npm i -g node-gyp@latest && npm config set node_gyp "/usr/local/lib/node_modules/node-gyp/bin/node-gyp.js"
- run: npm ci --legacy-peer-deps # clean install (deletes node_modules)
- run: npm link # necessary for some reason specific to NP dev setup
# note: when upgrade to node 16+, add this to the following --max-old-space-size=8192
- run: NODE_OPTIONS=--max-old-space-size=8192 node scripts/rollup.js -b -ci # build all plugins and stop if errors
- name: Log Plugin Build Success ✅
if: ${{ success() }}
run: |
curl --request POST \
--url https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }} \
--header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
--header 'content-type: application/json' \
--data '{
"context": "tests",
"state": "success",
"description": "Plugins Build successful",
"target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}'
- name: Log Plugin Build Failure 🚨
if: ${{ failure() }}
run: |
curl --request POST \
--url https://api.github.com/repos/${{ github.repository }}/statuses/${{ github.sha }} \
--header 'authorization: Bearer ${{ secrets.GITHUB_TOKEN }}' \
--header 'content-type: application/json' \
--data '{
"context": "tests",
"state": "failure",
"description": "Plugins Build failed",
"target_url": "https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
}'
================================================
FILE: .gitignore
================================================
.DS_Store
node_modules*
dist/
.cache/
*/ts-template/*.*
.pluginpath
*test*.yaml
*.code-workspace
*.dict
.vscode/launch.json
*/script.js
.vscode/
.pluginsrc
.idea/
lcov*
coverage/
.history/**/*
**/.history/**/*
.hyperlayout
.eslintignore
docs/assets/
docs/index.html
Documentation-Helpers
**/react.*.dev.js
**/react.*.min.js
.windsurfrules
CLAUDE.md
================================================
FILE: .prettierignore
================================================
================================================
FILE: .prettierrc
================================================
{
"semi": false,
"quoteProps": "as-needed",
"templateCurlySpacing": false,
"experimentalOperatorPosition": "start",
"bracketSameLine": false,
"arrowParens": "always",
"objectWrap": "preserve"
}
================================================
FILE: .watchmanconfig
================================================
{
}
================================================
FILE: CHANGELOG.md
================================================
# Package.json Changelog
## About Plugins/package.json / package-lock.json
## [Unreleased]
### Helpers
- **helpers/NPnote.js** — `getNoteFromIdentifier` improvements:
- Resolve by filename first when the identifier ends with `DataStore.defaultFileExtension` (e.g. `Note.md` or `folder/Note.md`) via `DataStore.noteByFilename(identifier, 'Notes')`.
- Fallback chain for project note by title: exact title, then quoted title (e.g. `"Title"`), then case-insensitive + search all folders.
- Clearer error message when the identifier is neither a project note nor a valid date/interval (no longer reports title strings as "not a valid date string").
## [3.20.0] - 2024-02-15 (@dwertheimer)
- added clof to eslint and flow rc
## [3.19.0] - 2024-01-13 (@dwertheimer)
- removed an errant import of libcurl that was crashing builds with node-gyp errors
## [3.18.0] - 2023-01-08 (@dwertheimer)
- edits to rollup & releases to help with "plugin.requiredFiles" and the requiredFiles folder (files to be copied to the Plugins folder and to releases for React, etc.)
- add fast-glob module for rollup (to watch for changes in files outside the build tree of index.js)
## [3.17.0] - 2022-12-17 (@dwertheimer)
### Additions to the plugin template for new plugins:
- add hooks and stubs for: onOpen, onEditorWillSave
- add fetch mocking import and instructions on how to use it
- moved pre-existing hooks to the new NPTriggers-Hooks.js file
## [3.16.0] - 2022-09-13 (@dwertheimer)
- added fsevents to try to reduce CPU usage of node on the autowatch per [article](https://medium.com/@manusajith/fix-for-100-cpu-usage-by-node-js-529916100aa6)
## [3.15.0] - 2022-08-30 (@akrabat)
- updated node-libcurl to 2.3.4 for Apple Silicon compatibility
## [3.14.0] - 2022-08-19 (@dwertheimer)
- added columnify for columnar output
## [3.13.0] - 2022-08-19 (@dwertheimer)
- added mathjs module for math calculations
- added -m | --minify option to build process to minify/mangle output for large plugins
## [3.12.0] - 2022-07-24 (@codedungeon)
- fixed `maximum stack call` error with new log level implementation
- all tests are currently passing, with the exception of 3 (@jgclark has been notified)
- fixed all core linting errors (individual plugins from @dwertheimer or @jgbclark still need some more at time of this release)
## [3.11.0] - 2022-07-24 (@codedungeon)
- Added `logInfo`, `logError`, `logWarn`, `logDebug` helpers, support plugin `Debugging` section (@dwertheimer)
- Updated plugin starter (`npc plugin:create`) to include `Debugging` section in settings (@codedungeon)
## [3.10.2] - 2022-07-10 (@codedungeon)
- added `np.Templating/lib/support/ejs.js` to `.eslintrc :: ignorePatterns`
## [3.10.1] - 2022-07-10 (@codedungeon)
- Fixed linting errors
- Updated `.eslintrc` to define max-len to match prettier setting in `package.json`
> I am hoping you all will be cool with the updated max-len value
> I work on a 30" most of the time so a longer line length is easier to read, if this becomes an issue I will try and get used to someting smaller (we used to have 120)
- Updated `test:dev` and `test:watch` npm scripts
> Run `test:dev` to perform a single test run of all specs in `__tests__` directories
> Run `test:watch` to perform a run test for all specs in `__tests__` using watch mode
- Updated `test` npm script to also call `test:dev`
- small refactor to `np.plugin-flow-skeleton`
## [3.10.0] - 2022-07-07 (@codedungeon)
- added `--force` option to `npc plugin:create` which will skip all network lookups (when retrieving github user information) (@dwertheimer)
- added task to `npc plugin:release` which remove previous releases for same pluginId (@jgclark)
> you can use the `--noDelete` flag to skip delete tasks (this will rarely be used, but added for completeness)
## [3.9.0] - 2022-06-17 (@jgclark)
- removed luxon
- (unmentioned here but I believe @nmn remove luxon-business-days about 2022-06-12)
## [3.8.0] - 2022-06-15 (@nmn)
- added eslint-plugin-no-floating-promise to package.json
- added package-lock back into git
- added .watchmanconfig
## [3.6.0] - 2022-06-08 (@codedungeon)
- added `documentation` module back in and edited the `npm run docs` command
- Updated @codedungeon/gunner CLI library
- added example of new "arguments: {}" fields in `plugin.json` when there are arguments that can be passed in when calling a plugin command from x-callback
## [3.5.0] - 2022-06-01 (@codedungeon)
- updated [#205](https://github.com/NotePlan/plugins/issues/205) `npc plugin:release` to include `CHANGELOG.md` if exists ()
- updated `npc plugin:create` to include extended plugin skeleton (@dwertheimer)
> Added more skeleton tests (thank you @dwertheimer)
- restored 180 character width in `prettier` settings
> If we continue to toggle this setting, my suggestion would be to remove it as a base setting and integrate personal `prettier.config.js` configuration
## [3.4.1] - 2022-05-24 (@codedungeon)
- fixed `npc plugin:release`
## [3.4.1] - 2022-05-15 (@codedungeon)
- removed `dayjs` dependency
- You can remove as you see fit, but it was not being used anywhere so it should not be causing any issues
## [3.4.0] - 2022-05-15 (@jgclark)
- add `luxon` depedency
- luxon-business-days
## [3.3.0] - 2022-05-06 (@dwertheimer)
- added support for searching notes using packages:
- fuse.js
- bqpjs
## [3.2.6] - 2022-04-23 (@mikeerickson)
- added support for using aliases in jest tests
- configured in `jest.config.js` which matches configuration in `plugins.config.js`
## [3.2.5] - 2022-04-16 (@mikeerickson)
- fix `npc plugin:relase` command to properly include `plugin.name` (@jgclark)
## [3.2.4] - 2022-03-20 (@mikeerickson)
- Fixed `helpers/NPConfiguration.js :: getSetting` completed implementation
- Fixed `helpers/NPConfiguration.js :: getSettings` completed implementation
## [3.2.3] - 2022-03-20 (@mikeerickson)
- Extended `helpers/dev :: clo` to output raw value if not object (allow passing non-object without having to change method call)
## [3.2.2] - 2022-03-18 (@mikeerickson)
- Removed `fetchWithTimeout` helper that was added in 3.2.1
## [3.2.1] - 2022-03-18 (@mikeerickson)
- removed `--verbose false` flag from `test:dev` and `test:watch` scripts
## [3.2.0] - 2022-03-16 (@mikeerickson)
- added `fetchWithTimeout` helper to `./helpers/dev`
## [3.1.2] - 2022-02-27 (@mikeerickson)
- fixed issue with `npc plugin:release` build test command
- removed test execution when running test build (addresses item test imports)
## [3.1.1] - 2022-02-23 (@mikeerickson)
- updated `date-fns` dependency to `^2.23.0` (requested by @m1well)
- added `eslin-plugin-unused-imports: 1.1.5` (requested by @m1well)
- updated CLI command description and examples
## [3.1.0] - 2022-02-19 (@mikeerickson)
- fixed issue with release script
- refactored release validation in CLI `npc plugin:release`
- add guard to make sure releasing from plugins directory
## [3.0.2] - 2022-02-17 (@mikeerickson)
- restored `docs` command
## [3.0.1] - 2022-02-17 (@mikeerickson)
- updated Plugins v3.0
## [2.2.0] - 2021-09-06 (@mikeerickson)
### Fixed
- fixed `plugin:create` command to use latest `@codedungeon/gunner`
- fixed `plugin:info` command to use latest `@codedungeon/gunner`
## [2.1.0] - 2021-08-26 (@mikeerickson)
### Added
- added `showdown` node dependency
- added `codedungeon.Toolbox` v1.0.0
## [2.0.1] - 2021-08-26 (@mikeerickson)
### Changed
- modified `.flowconfig` configuration, address error messsage for node_modules which do not contain type definitions
## [2.0.0] - 2021-08-16 (@mikeerickson)
Initial Release
## Changelog
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
### Plugin Versioning Uses Semver
All NotePlan plugins follow `semver` versioning. For details, please refer to [semver website](https://semver.org/)
================================================
FILE: Flow_Guide.md
================================================
# Flow Guide
This section is for those looking to understand how to use Flow types for more reliable Javascript.
You can read a [full guide for Flow](https://flow.org/en/docs/) but here are some quick tips if you're familiar with Swift:
1. Type annotations work very similarly by putting `: type` after variables and arguments
2. Function return types also use `:` and not `=>` like Swift
3. For all the primitive types in Javascript, you must use lowercase for the tyeps:
- `string` not `String`
- `number` not `Number` or `Int` or `Float` (Javascript only has a single number type)
- `boolean` not `Boolean`
- `null` for null types
- `void` for undefined
4. All other types are generally capitalized - `TParagraph`, `Date` etc.
5. Union Types:
- Think about how Swift allows you to create tagged values with Enums.
- Flow just lets you make a union of multiple types: `string | number` is a type that takes both types
- When dealing with a union type, you can use a `typeof` as the condition of an `if` statement and
flow will know the more accurate type within the if statement.
- Flow also support regular enums `enum SomeEnum {A, B, C}` but without attached data
- If you need attached data you can use a union of object types, but that's an advanced topic.
6. Flow also has support for optional types, but the `?` comes before the type.
- `String?` in Swift would translate to `?string` in Flow
- This is just an alias for `string | null | void`
7. The return type of `async` function must always be a `Promise<SOMETYPE>`. The type argument for the `Promise<>` depends on you.
## Optional Types
One of the *BIG* benefits of typeschecking Javascript is to catch mistakes where we forget to handle
the case where a value might not exist. These are very common in the Noteplan API.
* A `Note` may not have an actual `title`.
* When given a choce of options, a user may select nothing!
* etc.
So the *correct* type for cases like this would be `string | void | null` (Strings or Unefined or Null),
but since this is such a common pattern, Flow gives a simple syntax to handle these case:
`?string`. This is called an "optional string". If there is a value, it's of type string, but the value may not exist.
## Object Types
There are few different ways to define Object types.
[Read the docs](https://flow.org/en/docs/types/objects/)
### Plain Object Types
Plain objects are defined just with `{}` brackets.
e.g.
```typescript
type Person = { name: string, age: number }
```
Flow object types are exact by default. This means that a `Person` object type *cannot* have any keys
other than `name` and `string`. If you want to allow extra keys, you can add `...` at the type end of the object type:
```typescript
type Personish = { name: string, age: number, ... }
```
The `Personish` object type **must** contain `name` and `age`, but it can also contain additional
arbitrary keys that won't have any types.
#### Combining Objects
Flow supports the Object-spread syntax to combine objects:
```typescript
type Named = {name: string}
type Aged = {age: number}
type Person = {...Named, ...Aged}
```
Object spread syntax works exact like it does for actual objects and order matters.
When there are duplicate keys, the last key wins.
Spread syntax behaves somewhat similar to a logical `AND`.
For a logical `OR`, you create a `UNION` of object types too:
```typescript
type NamedOrAged = Named | Aged
```
`NamedOrAged` may either have a `name` key, *OR* an `age` key, but NOT both.
Generally when creating unions of objects, it's useful to have at least one key that has a static string value:
```typescript
type NamedOrAged = {
type: 'Named',
...Named,
} | {
type: 'Aged',
...Aged,
}
```
This pattern makes code that uses such a type easier to work with:
```js
switch (namedOrAged.type) {
case 'Named':
// Now we know that `namedOrAged` has a `name` key
console.log(namedOrAged.name);
break;
case 'Aged':
// Now we know that `namedOrAged` has an `age` key
console.log(namedOrAged.age);
break;
default:
// We still need a default case to make Flow happy unless you use an enum
}
```
Generally, it's best to use plain Object types where possible, but sometimes it makes sense to use Classes.
### Classes
Javascript has `class` keyword support. And classes are slightly special in Flow as they're both a value *and* a type.
An object may be of type `Person`, but `new Person()` is also a function call.
Class types in Flow have one *big* difference when compared to plain object types. They are checked by NAME and not by STRUCTURE.
Consider this example:
```typescript
declare class PersonClass {
name: string;
age: number;
}
const bob = {
name: "Bob",
age: 30
}
```
Here, `bob` is **NOT** of the type `PersonClass` even though it has the same keys within. This is because only
an object that is created with the `PersonClass` constructor can of be of its type.
```typescript
const actualPersonBob = new Person("Bob", 30);
```
Classes have their place, but should be used sparingly.
### Interfaces
Interfaces are the compromise between a `Class` and a plain Object Type. It's fairly similar to a Plain Object type,
but it can also be used to check class objects.
Further, classes can `implement` one or more `interfaces`:
```typescript
interface Named {name: string}
interface Aged {age: number}
class Person implements Named, Aged {
name: string;
age: number;
}
```
[Read More Here](https://medium.com/flow-type/sound-typing-for-this-in-flow-d62db2af969e)
### `this` type
The `this` type is only valid for `class` and `interface` types.
## When Types are REQUIRED
When you're using typechecking and value or function that is being "exported" needs explicit type annotations.
### Flow for Swift Developers
Dealing with an optional string:
```swift
let str: String?
let err: String = str // type error
if let str = str {
let val: String = str // no type error
}
```
translates to:
```typescript
let str: ?string;
let err: string = str; // type error
if (str != null) {
const val: string = str; // no type error
}
```
As you can see, a simple if check to verify that str is not `null` or `undefined` is enough for Flow to refine it's type.
No special syntax needed.
## Type definitions of Noteplan
Please look under `flow-typed/Noteplan.js` for all the type definitions you can use. Note that this file defines the object/values
available in Javascript, but the types of those objects are usually prefixed with a T.
For example, the `Paragraph` object is of the type `TParagraph`
================================================
FILE: GithubFlow.md
================================================
# Contributing to Noteplan/plugins
The process of contributing to Noteplan/plugins is the same as contributing to any Github open source projects. But it can be a little daunting if you have never done it before. Here's a brief walk through.
## Creating a Fork
Just head over to the NotePlan Plugins [GitHub page](https://github.com/NotePlan/plugins) and click the "Fork" button.
<img width="500" alt="Screen Cap 2023-04-02 at 09 29 36@2x" src="https://user-images.githubusercontent.com/8949588/229366802-404fcaa2-523d-4c27-9803-9e0ba913d01d.png">
You can use all the default settings, which will fork it into your Github account under the name "plugins":
<img width="500" alt="Screen Cap 2023-04-02 at 09 54 12@2x" src="https://user-images.githubusercontent.com/8949588/229367303-bf504ca1-15fe-4b9f-b8c9-e8b3afcc93fd.png">
This will create a fork (copy) of the noteplan/plugins repository in your **personal github account** (e.g. YOUR_GITHUB_USERNAME/plugins). Now we need to get the link to that repository, so Click the green `Code` button and click the overlapping squares icon to copy the link to this repository on your github account.
<img width="494" alt="Screen Cap 2023-04-02 at 10 10 00@2x" src="https://user-images.githubusercontent.com/8949588/229368157-a02bc0e9-8f82-4c84-8a1e-1556bd2165d8.png">
## Decide where you want to work on the code
You will be working on your code in a directory outside of the NotePlan file sandbox, so you can put the plugin development code anywhere you want on your computer. You will then use the command line interface tool (`noteplan-cli`) in the plugin repository to automatically build and copy the plugin code from your development folder to your NotePlan Plugins folder so you can test/use your plugin. So now, find or create a directory where you want to start development (anywhere on your computer).
## Cloning the Repo to your Desktop
Once you have your own fork (on Github.com) and a directory where you want to develop, you'll need to create a **clone** of that code on your local computer so you can work on it. To do that, you can use any git client app (e.g. the [free Github Desktop app](https://desktop.github.com/) to clone your repo, or if you prefer, skip the app and just head straight to the command line in your terminal:
```shell
# Change directory to where you want to install the plugins project
cd DIR_PATH
# Clone the fork we just created to your local machine
git clone https://github.com/YOUR_GITHUB_USERNAME/plugins.git
```
> **Note**
> The URL above ^^^ is the one you copied in the previous step.
This will create a clone (aka "working copy") of the repository on your local computer
## Keeping Your Fork Up to Date
Over time, you'll want to make sure you keep your fork up to date by tracking the original "upstream" repo that you forked. To do this, you'll need to add a remote:
```shell
# Change directory so you're in the local working copy of the plugins
cd plugins
# Add 'upstream' repo to list of remotes
git remote add upstream https://github.com/NotePlan/plugins.git
# Verify the new remote named 'upstream'
git remote -v
```
This should show you two sets of "remotes":
- push/pull to your repository on github
- push/pull to the main NotePlan/plugins (upstream) repository
### Keeping your fork up-to-date
To keep your fork/working copy updated with the latest upstream changes (changes in the main NotePlan repository), you'll need to first fetch the upstream repo's branches and latest commits to bring them into your repository:
```shell
# Fetch from upstream remote
git fetch upstream
```
Now, checkout your own main (master) branch and merge the upstream repo's main branch:
> ***NOTE:*** NotePlan's master branch is "main". So if you see instructions on the Internet for git-related things that tell you to do something to "master", just replace that with "main"
```shell
# Checkout your main branch and merge the upstream changes into your local copy
git checkout main
# Note: You will already be on the main branch by default unless you have created/switched to a branch
git merge upstream/main
```
If there are no conflicting commits on your local master/main branch, git will simply perform a "fast-forward" (it will bring all the latest NotePlan/plugins commits into your working copy). However, if you have been making changes on your master/main (in the vast majority of cases you probably shouldn't be - [see the next section](#doing-your-work), you may have to deal with conflicts. When doing so, be careful to respect the changes made upstream.
Now, your local master/main branch is up-to-date with everything modified upstream.
## Doing Your Work
### Create a Branch
Whenever you begin work on a new feature or bugfix, it's important that you create a new branch. Not only is it proper git workflow, but it also keeps your changes organized and separated from the master branch so that you can easily submit and manage multiple pull requests for every task you complete.
To create a new branch and start working on it:
```shell
# Checkout the master/main branch - you always want your new branch to always be based on main
git checkout main
# Create a new branch named newfeature (give your branch its own simple informative name)
git branch newfeature
# Switch to your new branch
git checkout newfeature
```
Now, go to town hacking away and making whatever changes you want to. You should add files and make local commits to your repository as you work. [Read this](https://support.atlassian.com/bitbucket-cloud/docs/add-edit-and-commit-to-source-files/) for more information. A good rule of thumb is to do a commit each time you add a new file or get some meaningful piece of code working. Committing along the way gives you a "save point" that you could roll back to if things go wrong along the way. This will cause you to have lots of commits, but we will clean that up later before issuing a pull request.
When you want to submit your changes to be potentially included in the main/public NotePlan plugins repository, you will want to create a [Pull Request](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests).
## Submitting a Pull Request to NotePlan
### Cleaning Up Your Work
Prior to submitting your pull request, you should do a few things to clean up your branch and make it as simple as possible for the NotePlan repo's maintainers to test, accept, and merge your work.
The first step is to make sure there are no changes on the main NotePlan repository that you haven't merged locally.
```shell
# Fetch upstream master/main and merge with your repo's master/main branch
git fetch upstream
git checkout main
git merge upstream/main
# If you see any new commits on main mentioned, you will need to rebase your development branch
git checkout newfeature
git rebase main
```
Now, it may be desirable to squash some of your smaller commits down into a small number of larger more cohesive commits. You can do this with an interactive rebase:
```shell
# Rebase all commits on your development branch
git checkout newfeature
git rebase -i main
```
This will open up a text editor where you can specify which commits to squash. [Read this](https://medium.com/@slamflipstrom/a-beginners-guide-to-squashing-commits-with-git-rebase-8185cf6e62ec) for more information.
### Pushing your changes to Github
Now that you have changes committed locally on your computer, you need to push them up to your Github forked repository.
```shell
# push local changes to your github repository as a feature branch
git push origin newfeature
```
### Submitting
Once you've committed and pushed all of your changes to GitHub, go to the page for your fork on GitHub.com, select your development branch, and click the pull request button. Fill out the description and submit the request for consideration.
### Making Changes
If you need to make any adjustments to code in your pull request (either thoughts you had or requests from the NotePlan repo maintainer), you can just just `git push` new changes/updates to the branch you submitted for a pull request. Github will automatically update the pull request with your new code.
### Clean-up
After your PR is accepted and you're done with the development branch, you're free to delete it.
```shell
git branch -d newfeature
```
**Copyright**
Credits: Instructions based on [this gist](https://gist.githubusercontent.com/Chaser324/ce0505fbed06b947d962/raw/23b18d33a8e1a512c53155aabdf97042d8c63768/GitHub-Forking.md)
Copyright 2017, Chase Pettit
MIT License, http://www.opensource.org/licenses/mit-license.php
**Additional Reading**
* [Atlassian - Merging vs. Rebasing](https://www.atlassian.com/git/tutorials/merging-vs-rebasing)
**Sources**
* [GitHub - Fork a Repo](https://help.github.com/articles/fork-a-repo)
* [GitHub - Syncing a Fork](https://help.github.com/articles/syncing-a-fork)
* [GitHub - Checking Out a Pull Request](https://help.github.com/articles/checking-out-pull-requests-locally)
================================================
FILE: KimMachineGun.Raindrop/CHANGELOG.md
================================================
# KimMachineGun.Raindrop Plugin Changelog
## About KimMachineGun.Raindrop Plugin
See Plugin [README](https://github.com/NotePlan/plugins/blob/main/KimMachineGun.Raindrop/README.md) for details on
available commands and use case.
## [0.1.0] - 2022-08-03 (KimMachineGun)
### Added
- added `/rd` command to search raindrop and create note.
- added `/rd:insert` command to search raindrop and insert (or copy) link.
================================================
FILE: KimMachineGun.Raindrop/README.md
================================================
# KimMachineGun.Raindrop Plugin
This plugin provides commands for working with Raindrop.io.
## Settings
These commands require configuration.
### Access Token (*required)
1. Go to the [Raindrop's integrations setting](https://app.raindrop.io/settings/integrations).
2. Create new app.
3. Create test token.
4. Copy and paste that token to plugin setting.
### Note Folder
This setting specifies the folder to which this plugin will create a note.
### Tag Prefix
This setting specifies the prefix of the tag imported from Raindrop.io.
## Latest Updates
See [CHANGELOG](https://github.com/NotePlan/plugins/blob/main/KimMachineGun.Raindrop/CHANGELOG.md) for latest
updates/changes to this plugin.
## API Documentation
https://developer.raindrop.io/
================================================
FILE: KimMachineGun.Raindrop/plugin.json
================================================
{
"COMMENT": "Details on these fields: https://help.noteplan.co/article/67-create-command-bar-plugins",
"macOS.minVersion": "10.13.0",
"noteplan.minAppVersion": "3.4.0",
"plugin.id": "KimMachineGun.Raindrop",
"plugin.name": "🧩 Raindrop.io",
"plugin.version": "0.1.0",
"plugin.lastUpdateInfo": "Initial Release",
"plugin.description": "Raindrop.io integration plugin.",
"plugin.author": "KimMachineGun",
"plugin.dependencies": [],
"plugin.script": "script.js",
"plugin.url": "https://github.com/NotePlan/plugins/blob/main/KimMachineGun.Raindrop/README.md",
"plugin.changelog": "https://github.com/NotePlan/plugins/blob/main/KimMachineGun.Raindrop/CHANGELOG.md",
"plugin.commands": [
{
"name": "rd",
"description": "Search and create note.",
"jsFunction": "searchAndCreateNote",
"alias": [
"rd"
]
},
{
"name": "rd:insert",
"description": "Search and insert (or copy) link.",
"jsFunction": "searchAndInsertOrCopy",
"alias": [
"rdi"
]
}
],
"plugin.settings": [
{
"COMMENT": "Plugin settings documentation: https://help.noteplan.co/article/123-plugin-configuration",
"type": "heading",
"title": "Raindrop Settings"
},
{
"title": "Access Token",
"key": "accessToken",
"type": "string",
"description": "Your Raindrop.io test access token"
},
{
"title": "Note Folder",
"key": "noteFolder",
"type": "string",
"description": "Folder to store Raindrop notes",
"default": "/Raindrop.io/Tags"
},
{
"title": "Tag Prefix",
"key": "tagPrefix",
"type": "string",
"description": "Prefix of Raindrop tags",
"default": "rd/"
},
{
"NOTE": "DO NOT CHANGE THE FOLLOWING SETTINGS; ADD YOUR SETTINGS ABOVE ^^^",
"type": "separator"
},
{
"type": "heading",
"title": "Debugging"
},
{
"key": "_logLevel",
"type": "string",
"title": "Log Level",
"choices": [
"DEBUG",
"INFO",
"WARN",
"ERROR",
"none"
],
"description": "Set how much logging output will be displayed when executing Raindrop commands in NotePlan Plugin Console Logs (NotePlan -> Help -> Plugin Console)\n\n - DEBUG: Show All Logs\n - INFO: Only Show Info, Warnings, and Errors\n - WARN: Only Show Errors or Warnings\n - ERROR: Only Show Errors\n - none: Don't show any logs",
"default": "INFO",
"required": true
}
]
}
================================================
FILE: KimMachineGun.Raindrop/src/NPPluginMain.js
================================================
// @flow
// Plugin code goes in files like this. Can be one per command, or several in a file.
// `export async function [name of jsFunction called by Noteplan]`
// then include that function name as an export in the index.js file also
// About Flow: https://flow.org/en/docs/usage/#toc-write-flow-code
// Getting started with Flow in NotePlan: https://github.com/NotePlan/plugins/blob/main/Flow_Guide.md
// NOTE: This file is named NPPluginMain.js (you could change that name and change the reference to it in index.js)
// As a matter of convention, we use NP at the beginning of files which contain calls to NotePlan APIs (Editor, DataStore, etc.)
// Because you cannot easily write tests for code that calls NotePlan APIs, we try to keep the code in the NP files as lean as possible
// and put the majority of the work in the /support folder files which have Jest tests for each function
// support/helpers is an example of a testable file that is used by the plugin command
// REMINDER, to build this plugin as you work on it:
// From the command line:
// `noteplan-cli plugin:dev KimMachineGun.Raindrop --test --watch --coverage`
/**
* LOGGING
* A user will be able to set their logging level in the plugin's settings (if you used the plugin:create command)
* As a general rule, you should use logDebug (see below) for messages while you're developing. As developer,
* you will set your log level in your plugin preferences to DEBUG and you will see these messages but
* an ordinary user will not. When you want to output a message,you can use the following
* logging level commands for different levels of messages:
*
* logDebug(pluginJson,"Only developers or people helping debug will see these messages")
* log(pluginJson,"Ordinary users will see these informational messages")
* logWarn(pluginJson,"All users will see these warning/non-fatal messages")
* logError(pluginJson,"All users will see these fatal/error messages")
*/
import pluginJson from '../plugin.json'
import {JSP, logError} from '@helpers/dev'
export async function searchAndInsertOrCopy(): Promise<void> {
await searchInRaindrop(insertOrCopyRaindropTitle)
}
export async function searchAndCreateNote(): Promise<void> {
await searchInRaindrop(createRaindropNote)
}
async function searchInRaindrop(cb: (raindrop: Raindrop) => Promise<void>): Promise<void> {
const settings = DataStore.settings
const accessToken = settings.accessToken ?? ''
if (accessToken === '') {
logError(pluginJson, `Please configure your access token first.`)
return
}
// every command/plugin entry point should always be wrapped in a try/catch block
try {
const search = await CommandBar.showInput(`Search`, `Search in Raindrop.io with '%@'`) ?? ''
if (search === '') {
logError(pluginJson, `Too short search term.`)
return
}
let raindrops = []
for (let i = 0; ; i++) {
const raw = await requestToRaindrop('GET', `https://api.raindrop.io/rest/v1/raindrops/0?search=${encodeURIComponent(search)}&page=${encodeURIComponent(i)}&perPage=50`)
const response = JSON.parse(raw)
if (!response.result) {
logError(pluginJson, `An error occurred during searching.`)
return
}
const raindropsInPage: Array<Raindrop> = response.items ?? []
raindrops = raindrops.concat(raindropsInPage)
if (raindrops.length === 0) {
await CommandBar.prompt(
'Not Found',
`Nothing found in all raindrops with '${search}'`,
)
logError(pluginJson, `Nothing found in all raindrops.`)
return
}
const titles: string[] = raindrops.map((x) => {
if (x.tags.length === 0) {
return x.title
}
const tags = x.tags.map(x => `#${x}`).join(',')
return `${x.title} / ${tags}`
})
titles.push('Load More...')
const selected = await CommandBar.showOptions(titles, `Found ${raindrops.length} raindrops`)
if (selected.index === titles.length - 1) {
continue
}
await cb(raindrops[selected.index])
break
}
} catch (error) {
logError(pluginJson, JSP(error))
}
}
async function insertOrCopyRaindropTitle(rd: Raindrop) {
let linkedTitle = `[${rd.title}](${rd.link})`
if (rd.tags.length > 0) {
linkedTitle = `${linkedTitle} ${rd.tags.map(formatTag).join(' ')}`
}
if (Editor.note == null) {
Clipboard.string = linkedTitle
await CommandBar.prompt(
'Note Not Opened',
`Copy '${linkedTitle}' to your clipboard.`,
)
} else {
Editor.insertTextAtCursor(linkedTitle)
}
}
async function createRaindropNote(rd: Raindrop) {
const settings = DataStore.settings
const noteFolder = settings.noteFolder ?? ''
const title = `[${rd.title}](${rd.link})`
let body = ''
const collection = await fetchCollection(rd.collection.$id)
if (collection) {
body = `${body}**Collection:** \`${collection._id === -1 ? 'Unsorted' : collection.title}\`\n`
}
if (rd.excerpt !== '' || rd.highlight.body !== '') {
body = `${body}**Description:**\n> ${rd.excerpt || rd.highlight.body}\n`
}
if (rd.tags.length !== 0) {
body = `${body}**Tags:**\n${rd.tags.map(formatTag).map(x => `- ${x}`).join('\n')}\n`
}
body = `${body}---\n`
const filename = await createNoteIfNotExists(title, noteFolder, body)
await Editor.openNoteByFilename(filename)
}
async function createNoteIfNotExists(title: string, folder: string, content?: string): string {
const existingNotes = DataStore.projectNoteByTitle(title, true, false) ?? []
if (existingNotes.length === 0) {
if (content) {
content = `# ${title}\n${content}`
return await DataStore.newNoteWithContent(content, folder)
} else {
return await DataStore.newNote(title, folder)
}
}
}
function formatTag(tag: string): string {
const prefix = DataStore.settings.tagPrefix ?? ''
return `#${prefix}${tag.replaceAll(' ', '_').toLowerCase()}`
}
async function fetchCollection(id: number): ?Collection {
const raw = await requestToRaindrop('GET', `https://api.raindrop.io/rest/v1/collection/${id}`)
const response = JSON.parse(raw)
if (!response.result) {
logError(pluginJson, `An error occurred during fetching collection.`)
return null
}
return response.item
}
async function requestToRaindrop(method: string, url: string, init?: RequestInit): Promise<Response> {
const settings = DataStore.settings
const accessToken = settings.accessToken ?? ''
if (accessToken === '') {
logError(pluginJson, `Please configure your access token first.`)
}
return await fetch(url, {
method: method,
headers: {
'Authorization': `Bearer ${accessToken}`,
},
...init
})
}
================================================
FILE: KimMachineGun.Raindrop/src/Raindrop.js
================================================
// @flow
declare type RaindropType = 'link' | 'article' | 'image' | 'video' | 'document' | 'audio';
declare type RaindropCacheStatus = 'ready' | 'retry' | 'failed' | 'invalid-origin' | 'invalid-timeout' | 'invalid-size';
declare type RaindropHighlightColor =
'blue'
| 'brown'
| 'cyan'
| 'gray'
| 'green'
| 'indigo'
| 'orange'
| 'pink'
| 'purple'
| 'red'
| 'teal'
| 'yellow';
declare class Raindrop {
// Impossible to create Paragraphs manually
constructor(_: empty): empty;
/**
* Unique identifier of raindrop
*/
_id: string;
/**
* Collection that the raindrop resides in
*/
collection: {
/**
* Unique identifier of collection
*/
$id: number;
};
/**
* Raindrop cover URL
*/
cover: string;
/**
* Creation date
*/
created: string;
/**
* Hostname of a link.
* Files always have `raindrop.io` hostname
*/
domain: string;
/**
* Description; max length: 10000
*/
excerpt: string;
/**
* Update date
*/
lastUpdate: string;
/**
* URL
*/
link: string;
/**
* Covers list in format
*/
media: Array<{
/**
* URL of cover
*/
link: string;
}>;
/**
* Tags list
*/
tags: Array<string>;
/**
* Title; max length: 1000
*/
title: string;
/**
* `link` `article` `image` `video` `document` or `audio`
*/
type: RaindropType;
/**
* Raindrop owner
*/
user: {
/**
* Unique Identifier of raindrop owner
*/
$id: number;
};
/**
* Marked as broken (original `link` is not reachable anymore)
*/
borken: boolean;
/**
* Permanent copy (cached version) details
*/
cache: {
/**
* `ready` `retry` `failed` `invalid-origin` `invalid-timeout` or `invalid-size`
*/
status: RaindropCacheStatus;
/**
* Full size in bytes
*/
size: number;
/**
* Date when copy is successfully made
*/
created: number;
};
/**
* Sometime raindrop may belong to other user, not to the one who create it.
* For example when this raindrop is created in shared collection by other user.
* This object contains info about original author.
*/
creatorRef: {
/**
* Original author (user ID) of a raindrop
*/
_id: number;
/**
* Original author name of a raindrop
*/
fullName: number;
};
/**
* This raindrop uploaded from desktop
*/
file: {
/**
* File name
*/
name: string;
/**
* File size in bytes
*/
size: number;
/**
* Mime type
*/
type: string;
};
/**
* Marked as "favorite"
*/
important: boolean;
/**
* Highlights in this raindrop
*/
highlights: Array<{
/**
* Unique id of highlight
*/
_id: string;
/**
* Text of highlight (required)
*/
text: string;
/**
* Color of highlight.
* Default `yellow`
*
* Can be `blue`, `brown`, `cyan`, `gray`, `green`, `indigo`, `orange`, `pink`, `purple`, `red`, `teal`, `yellow`
*/
color: RaindropHighlightColor;
/**
* Optional note for highlight
*/
note: string;
/**
* Creation date of highlight
*/
created: string;
}>;
/**
* Highlight of this raindrop
* NOT DOCUMENTED IN RAINDROP.IO
*/
highlight: {
/**
* Highlight of this raindrop
*/
body: string
}
}
// TODO: type all fields in [Collections](https://developer.raindrop.io/v1/collections)
declare class Collection {
// Impossible to create Paragraphs manually
constructor(_: empty): empty;
/**
* The id of the collection
*/
_id: string;
/**
* Name of the collection
*/
title: string;
}
================================================
FILE: KimMachineGun.Raindrop/src/index.js
================================================
// @flow
// Flow typing is important for reducing errors and improving the quality of the code.
// About Flow: https://flow.org/en/docs/usage/#toc-write-flow-code
// Getting started with Flow in NotePlan: https://github.com/NotePlan/plugins/blob/main/Flow_Guide.md
// Note: As you will see in this plugin folder, you can have multiple files -- e.g. one file per command or logical group of commands
// ...and separate files for helper/support functions that can be tested in isolation
// The `autowatch` packager combines them all into one script.js file for NotePlan to read
// From the command line:
// `noteplan-cli plugin:dev {{pluginId}} --test --watch --coverage`
// ...will watch for changes and will compile the Plugin script code
// and copy it to your plugins directory where NotePlan can find it
// Since NP reloads the Javascript every time you CMD-J to insert a plugin,
// you can immediately test the new code without restarting NotePlan
// This index.js file is where the packager starts looking for files to combine into one script.js file
// So you need to add a line below for each function that you want NP to have access to.
// Typically, listed below are only the top-level plug-in functions listed in plugin.json
export { searchAndInsertOrCopy, searchAndCreateNote } from './NPPluginMain' // add one of these for every command specifified in plugin.json (the function could be in any file as long as it's exported)
// Do not change this line. This is here so your plugin will get recompiled every time you change your plugin.json file
import pluginJson from '../plugin.json'
/*
* NOTEPLAN HOOKS
* The rest of these functions are called by NotePlan automatically under certain conditions
* It is unlikely you will need to edit/add anything below this line
*/
// eslint-disable-next-line import/order
import { updateSettingData, pluginUpdated } from '@helpers/NPConfiguration'
import { logError, JSP } from '@helpers/dev'
/**
* NotePlan calls this function after the plugin is installed or updated.
* The `updateSettingData` function looks through the new plugin settings in plugin.json and updates
* the user preferences to include any new fields
*/
export async function onUpdateOrInstall(): Promise<void> {
await updateSettingData(pluginJson)
}
/**
* NotePlan calls this function every time the plugin is run (any command in this plugin)
* You should not need to edit this function. All work should be done in the commands themselves
*/
// eslint-disable-next-line require-await
export async function init(): Promise<void> {
try {
// Check for the latest version of this plugin, and if a minor update is available, install it and show a message
DataStore.installOrUpdatePluginsByID([pluginJson['plugin.id']], false, false, false).then((r) =>
pluginUpdated(pluginJson, r),
)
} catch (error) {
logError(pluginJson, JSP(error))
}
}
/**
* NotePlan calls this function settings are updated in the Preferences panel
* You should not need to edit this function
*/
export async function onSettingsUpdated(): Promise<void> {}
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2021-2022 Eduard Metzger
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================
# NotePlan Plugins
[](https://github.com/NotePlan/plugins/actions/workflows/node.js.yml)
## Overview
NotePlan Plugins provides an extensive API for extending default editing and task management and work across all platforms (macOS and iOS).
Each plugin command can be invoked using the [NotePlan Command Bar](https://help.noteplan.co/article/65-commandbar-plugins), or by entering any of available commands directly in the editor by entering `/<command_name>` (NotePlan will auto update the list of possible commands as you type)

## Anatomy of a Plugin
If you want to develop plugins, Step 1 is to read the [NotePlan Knowledgebase Document](https://help.noteplan.co/article/67-create-command-bar-plugins) describing how plugins work in NotePlan and the basic plugin anatomy. Once you have read that carefully and understand the basics, you should return here to acquire and start using the NotePlan Plugin tooling described below.
## Prerequisite
The following items are required for NotePlan Plugin Development
- Node 14 or 16 -- **Do Not Use any Node version <14 or >16** (see "Switching Node Versions")
- NotePlan 3.4 or greater
- macOS Catalina 10.15.2 or greater (strongly recommend macOS Big Sur 11.x or Monterey 12.x)
- github CLI `gh` is strongly recommended - [how to install gh](https://cli.github.com/)
## Switching Node Versions
The NotePlan plugin code has not yet been migrated to Node versions >16. If you are developing elsewhere using Node v17+, you will want to switch to Node v16 when you are doing NotePlan Plugin development. The fast/easy way to do that is with a Node version manager [like "n"](https://www.npmjs.com/package/n). This way you can flip in and out of Node versions at will.
## Plugin Information
If you have an idea for a plugin, [submit them here](https://feedback.noteplan.co/plugins-scripting) or inquire in the [NotePlan Discord community](https://discord.gg/D4268MT)'s `#plugin` channel.
If you are a developer and want to contribute and build your plugins, see the [plugin writing documentation](https://help.noteplan.co/article/67-create-command-bar-plugins) and discuss this with other developers on [Discord](https://discord.gg/D4268MT) `#plugin-dev` channel. You might want to consult this [good modern JavaScript tutorial](https://javascript.info/).
### Getting Started with Plugin Development
**Step 1: Forking/Cloning NotePlan Plugin Repository**
Read [these instructions](GithubFlow.md) for how to fork and clone this code
**Step 1.5 Have a look at the code**
When you have cloned this repository, you will not only have the tooling, but you will have the actual source code for every publicly-available NotePlan plugin. This will give you a wealth of material to learn from and borrow from. Speaking of which, there is a `/helpers` directory at the root of the repository that contains a lot of useful functions built upon the NotePlan APIs and will speed up your development. It would be good to familiarize yourself with that code by browsing it. There is a searchable index of the helper code that can be accessed by running this command in a terminal:
`npm run docs`
**Step 2: Install Node (if not installed)**
Make sure you have the proper version of `node` installed (if you need to install node, `brew install node@16` is the quickest method, or you can follow instructions on [node website](https://nodejs.org/en/download/)).
**Step 3: Initialize Local Development Environment**
Run the following 3 commands from the root of your local GitHub repository for `NotePlan/plugins`.
1) Update node-gyp
```shell
npm i -g node-gyp@latest && npm config set node_gyp "/usr/local/lib/node_modules/node-gyp/bin/node-gyp.js"
```
> **Note**: Don't be surprised if this command fails. It is only necessary in certain cases. If it fails, it probably means you didn't need it. Just continue on.
2) Install the node_modules
```shell
npm install
```
> **NOTE**: if you are running node >= 16 and you get failure messages on the vanilla install command above, you will need to use this command instead: `npm install --legacy-peer-deps`
3) Link the files to make them run properly from the command line (especially the `noteplan-cli`)
```shell
npm run init
```
This will install the necessary npm dependencies and initialize your plugin working directory, including:
- Configuring `eslint` [eslint](https://eslint.org/) (for checking code conventions)
- Configuring `flow` [flow](https://flow.org/) (for type checking)
- Configuring `babel` [babel](https://babeljs.io/) (a JS compiler)
- Configuring `rollup` [rollup](https://rollupjs.org/guide/en/) (for bundling multiple source files into a single release).
Each of these tools have their own configuration files at the root directory (e.g., `.flowconfig` or `.eslintrc`)
_Note: Each of these configuration files can be overridden if needed by placing a project specific configuration file in you project plugin, however, for consistency with other NotePlan plugins, we encourage to use the defaults wherever possible._
### Creating your first NotePlan Plugin
Using the NotePlan CLI, perform the following actions:
**Step 1: Create your plugin using NotePlan CLI**
Answer the prompt questions (or supply all the necessary options from command line (see `noteplan-cli plugin:create --help` for details)
`noteplan-cli plugin:create`
**Step 2: Startup Auto Watch Process**
Open up a Terminal shell, `cd` to the repository root directory, and issue the command:
`npc plugin:dev <your_plugin_folder> --watch` from the root directory to build your plugin as you develop so it can be tested in NotePlan. This will compile your code and put it into your NotePlan app directory so you can test your plugin. The `--watch` flag keeps the process looking for changes to your files and will automatically rebuild the plugin for you. (more on that below)
**Step 3: Start your plugin command develop and test locally**
You can now develop and test your plugin locally,
**Step 4: Create Pull Request (if you wish to make your plugin public)**
At this point, if you would like to make your plugin available publicly, you can proceed to [creating a Pull Request](https://github.com/NotePlan/plugins/blob/main/GithubFlow.md#submitting-a-pull-request-to-noteplan) to have your code included in the NotePlan Plugin Repository
### Common Development Actions
These are the most common commands you will use while developing:
#### File Watcher
The default watch command `npc plugin:dev <your_plugin_folder> --watch`:
`npc plugin:dev` from the root of your local `NotePlan/plugins` repository which will bundle all the files in your `/src` directory into single file `script.js` and will be copied from your repository directory to your Plugins folder in the running NotePlan data directory for testing.
The `init` script should have detected whether you are using the SetApp or App Store version of NotePlan and set the correct path to your Plugins folder. If it did not, you can manually change it in `.pluginpath`.
*Note: The watcher will remain running, _watching_ the NotePlan directory and re-compile whenever changes have been made to your `<your_plugin>/src` JavaScript files.*
**npc plugin:dev <your_plugin_directory> --watch**
For example, running `npc plugin:dev dwertheimer.TaskAutomations --watch` will perform the same watching operations for the `dwertheimer.TaskAutomations` plugin only.
### NotePlan CLI Commands
NotePlan includes a suite of CLI commands which you can use during development.
```shell
noteplan-cli <command>
or
npc <command>
```
For all CLI commands, you can pass the `--help` for available flags
#### npc plugin:dev
The most common CLI command, this can be used to build plugin, test plugins (wrapper for `npc plugin:test`)
```shell
npc plugin:dev <plugin> [options]
# run watcher, compact mode and display notification with build result
npc plugin:dev codedungeon.Toolbox --watch --compact --notify
# same as above, using CLI shorthand
npc plugin:dev codedungeon.Toolbox -wcn
# run NotePlan test suite in watch mode
# this is a wrapper for npc plugin:test
npc plugin:dev codedungeon.Toolbox -tw
```
#### npc plugin:test
The `test` command can be used in addition to the `npc plugin:dev <plugin> --test` which will only execute the NotePlan Test Runner
```shell
npc plugin:test <plugin> [options]
# execute test running in watch mode, with silent enabled
npc plugin:test codedungeon.Toolbox --watch --silent
# as with other plugin commands, youc an use CLI shorthand
# this will perform the same as above
npc plugin:test codedungeon.Toolbox -ws
```
#### npc plugin:create
Create new NotePlan Plugin
```shell
npc plugin:create [options]
```
#### npc plugin:pr
Create NotePlan Plugin Pull Request
```shell
npc plugin:pr [options]
```
#### npc plugin:test
Run test suite for NotePlan Plugin
```shell
npc plugin:test <plugin> [options]
# run plugin:test watch
npc plugin:test codedungeon.Toolbox --watch
# run plugin:test watch, silent mode
npc plugin:test codedungeon.Toolbox --watch --silent
# run plugin:test with CLI shorthand
npc plugin:test codedungeon.Toolbox -ws
# run plugin:test with coverage report
npc plugin:test codedungeon.Toolbox --coverage
```
#### Create Pull Request
Once you are finished editing and testing your plugin, you can [submit a Pull Request](https://github.com/NotePlan/plugins/blob/main/GithubFlow.md#submitting-a-pull-request-to-noteplan) to the NotePlan/plugins repository and it will be reviewed for inclusion. Once it has been approved, it will be available from **NotePlan > Preferences > Plugins** section, enabling it to be installed by other NotePlan users.
### Frequently Used Commands
The common script you will run `npc plugin:dev <plugin>` however, you may need to use any of the following
- `npc plugin:dev <plugin> --watch --compact --notify` a less verbose version of `autowatch` that might suit more experienced developers
- `npc plugin:dev <plugin> -wcn` watcher, compact mode, notify using CLI shorthand
- `npc plugin:dev <plugin> -tw` test mode, watcher using CLI shorthand
- `npc plugin:test <plugin> -w` test mode, using `test` command
- `npm run typecheck`: typecheck all javascript files with `Flow`. Only files with a `// @flow` comment are checked.
- `npm run fix`: lint and auto-format
- `npm run docs`: build documentation for javascript files
- `npm run lint`: run ESlint on the entire repo
- `npm run lint-fix`: run ESlint on the entire repo and fix whatever it can automatically fix
- `npm run format`: auto-format all Javascript files using `prettier`
- `gh release delete <release name>`: Will delete the release from the repository, so making it unavailable in NotePlan as well. (Though it won't remove it from anyone who has already downloaded it.)
## Editor Setup
Use the setup guide for your preferred editor (we prefer Visual Studio Code), and then read the section on Working with Multiple Files.
### Visual Studio Code (recommended)
**Install VSCode Extensions**
1. Install the following extensions for the following tools:
- `flow` "Flow Language Support" by flowtype
- `eslint` "ESLint" by Dirk Baeumer
- `prettier` "Prettier - Code formatter" by Prettier
- (optional) "TODO Highlight V2" by wayou/jgclark
**Update Settings**
1. Set `prettier` to be the default formatter for js files.
- You can open the Command Bar using `CMD+SHIFT+P` and then search for `Format Document`.
- When you do this, you may get asked for a formatter of choice. Choose "Prettier"
- If it asks you if this should be your default for all JS files, choose Yes.
2. Restart the editor to ensure the plugins are working.
- You should see type errors when you make those
- You should see lint errors when you format code wrong
- You should see your code get auto formatted when you save
3. Make sure to open this folder directly in VSCode and not the entire repo as the ESLint plugin can be annoying about that
### Sublime Text 3 and 4
1. Install the following extensions using Package Control
- `SublimeLinter` This allows various linters to work
- `SublimeLinter-eslint`
- `SublimeLinter-flow`
- `jsPrettier`
- `Babel` Syntax definitions for ES6 Javascript and React JSX extensions
2. Configure your packages:
- Open a `.js` file
- From the View menu, select Syntax → Open all with current extension as… → Babel → JavaScript (Babel)
- Open the package settings for `jsPrettier` and add `"auto_format_on_save": true,`
### Linting Code
If you don't have an editor set up to lint as you code, you can run `npm run test` and it will give a list of problems to fix.
### Using Flow
By practice, NotePlan plugins use [flow](https://flow.org/) for static type checking. You can get more information by referencing [NotePlan Flow Guide](https://github.com/NotePlan/plugins/blob/main/Flow_Guide.md)
## NotePlan Plugin Support
Should you need support for anything related to NotePlan Plugins, you can reach us at the following:
### Email
If you would prefer email, you can reach us at:
- [NotePlan Info](hello@noteplan.co)
### Discord
Perhaps the fastest method would be at our Discord channel, where you will have access to the widest amount of resources:
- [Discord Plugins Channel](https://discord.com/channels/763107030223290449/784376250771832843)
### Github Issues
This is a great resource to request assistance, either in the form of a bug report, or feature request for a current or future NotePlan Plugin
- [GitHub Issues](https://github.com/NotePlan/plugins/issues/new/choose)
## Contributing
If you would like to contribute to the NotePlan Plugin repository, feel free to submit a [Pull Request](https://github.com/NotePlan/plugins/blob/main/GithubFlow.md#submitting-a-pull-request-to-noteplan) for any existing NotePlan Plugin, or any of the support materials.
================================================
FILE: TasksModule_docs.md
================================================
import Link from 'next/link'
import Callout from '@/components/Callout'
# Tasks Module
## Overview
The Tasks Module provides methods for interacting with tasks within NotePlan.
<Callout
type="info"
description={`
This module allows you to retrieve and manipulate tasks from your notes, ensuring they are synchronized with block IDs for reliable referencing.
`}
/>
## Methods
> namespace: `tasks`
The following are the methods available in the Tasks Module. They can be used in any `Templating` template; no additional configuration is required.
---
### getSyncedOpenTasksFrom
> #### async getSyncedOpenTasksFrom(sourceIdentifier : string) : Promise<string>
>
> Retrieves open tasks (including their sub-tasks/children) from a specified note (daily, weekly, monthly, quarterly, yearly calendar note, or a project note). It ensures each open task paragraph and its children have a block ID and returns a string with each task on a new line.
- `sourceIdentifier` - (string) Specifies the note to retrieve tasks from. This can be:
- `'<today>'`: Fetches tasks from today's daily note.
- `'<yesterday>'`: Fetches tasks from yesterday's daily note.
- An ISO 8601 date string for a specific calendar note:
- Daily: `"YYYYMMDD"` (e.g., `"20230410"`) or `"YYYY-MM-DD"` (e.g., `"2023-04-10"`)
- Weekly: `"YYYY-Www"` (e.g., `"2023-W24"`)
- Monthly: `"YYYY-MM"` (e.g., `"2023-10"`)
- Quarterly: `"YYYY-Qq"` (e.g., `"2023-Q4"`)
- Yearly: `"YYYY"` (e.g., `"2023"`)
- The title of a project note (string).
- `-> result` - (Promise<string>) Returns a promise that resolves to a string containing all open tasks and their sub-tasks from the specified note, each on a new line. If the note is not found or contains no open tasks, it resolves to an empty string.
**Behavior Notes:**
* The method uses `getOpenTasksAndChildren` to identify open tasks and their hierarchical children.
* It automatically adds block IDs to any open task or child task paragraph that doesn't already have one. This modification happens directly in the NotePlan data store.
* If multiple project notes match a given title, the method will use the first one found and log a debug message.
**Examples**
The following example retrieves open tasks from today's daily note:
```javascript
<%- await tasks.getSyncedOpenTasksFrom('<today>') %>
```
The following example retrieves open tasks from a specific weekly note:
```javascript
<%- await tasks.getSyncedOpenTasksFrom('2023-W42') %>
```
The following example retrieves open tasks from a project note titled "My Project Q4":
```javascript
<%- await tasks.getSyncedOpenTasksFrom('My Project Q4') %>
```
================================================
FILE: __mocks__/Backlink.mock.js
================================================
/* eslint-disable */
/* Backlink mock class
*
* Usage: const myBacklink = new Backlink({ param changes here })
*
*/
export class Backlink {
// Properties
blockId = 'PLACEHOLDER' // TODO: add value
content = 'PLACEHOLDER' // TODO: add value
contentRange = 'PLACEHOLDER' // TODO: add value
date = {} /* new Date("Tue Jun 14 2022 00:00:00 GMT-0700 (PDT)"), */
filename = 'PLACEHOLDER' // TODO: add value
heading = 'PLACEHOLDER' // TODO: add value
headingLevel = 'PLACEHOLDER' // TODO: add value
headingRange = 'PLACEHOLDER' // TODO: add value
indents = 'PLACEHOLDER' // TODO: add value
isRecurring = 'PLACEHOLDER' // TODO: add value
lineIndex = 'PLACEHOLDER' // TODO: add value
linkedNoteTitles = []
note = {} /* {
"filename": "20220614.md",
"type": "Calendar",
"title": "2022-06-14",
"date": "2022-06-14T07:00:00.000Z",
"changedDate": "2022-06-15T23:34:12.000Z",
"createdDate": "2022-06-15T23:34:12.000Z",
"hashtags": [],
"mentions": [],
"linkedItems": [
"{\"type\":\"open\",\"content\":\" >today add filler photo ^mmw6w5\",\"blockId\":\"^mmw6w5\",\"rawContent\":\"* >today add filler photo ^mmw6w5\",\"prefix\":\"* \",\"contentRange\":{},\"lineIndex\":0,\"date\":\"2022-06-15T07:00:00.000Z\",\"heading\":\"\",\"headingLevel\":-1,\"isRecurring\":false,\"indents\":0,\"filename\":\"20220614.md\",\"noteType\":\"Calendar\",\"linkedNoteTitles\":[],\"subItems\":[],\"referencedBlocks\":[{}],\"note\":{}}"
],
"datedTodos": [
"{\"type\":\"open\",\"content\":\" >today add filler photo ^mmw6w5\",\"blockId\":\"^mmw6w5\",\"rawContent\":\"* >today add filler photo ^mmw6w5\",\"prefix\":\"* \",\"contentRange\":{},\"lineIndex\":0,\"date\":\"2022-06-15T07:00:00.000Z\",\"heading\":\"\",\"headingLevel\":-1,\"isRecurring\":false,\"indents\":0,\"filename\":\"20220614.md\",\"noteType\":\"Calendar\",\"linkedNoteTitles\":[],\"subItems\":[],\"referencedBlocks\":[{}],\"note\":{}}"
],
"backlinks": [],
"frontmatterTypes": [],
"content": "* >today add filler photo ^mmw6w5\n* ",
"paragraphs": [
"{\"type\":\"open\",\"content\":\" >today add filler photo ^mmw6w5\",\"blockId\":\"^mmw6w5\",\"rawContent\":\"* >today add filler photo ^mmw6w5\",\"prefix\":\"* \",\"contentRange\":{},\"lineIndex\":0,\"date\":\"2022-06-15T07:00:00.000Z\",\"heading\":\"\",\"headingLevel\":-1,\"isRecurring\":false,\"indents\":0,\"filename\":\"20220614.md\",\"noteType\":\"Calendar\",\"linkedNoteTitles\":[],\"subItems\":[],\"referencedBlocks\":[{}],\"note\":{}}",
"{\"type\":\"open\",\"content\":\"\",\"rawContent\":\"* \",\"prefix\":\"* \",\"contentRange\":{},\"lineIndex\":1,\"heading\":\"\",\"headingLevel\":-1,\"isRecurring\":false,\"indents\":0,\"filename\":\"20220614.md\",\"noteType\":\"Calendar\",\"linkedNoteTitles\":[],\"subItems\":[],\"referencedBlocks\":[],\"note\":{}}"
]
} , */
noteType = 'PLACEHOLDER' // TODO: add value
prefix = 'PLACEHOLDER' // TODO: add value
rawContent = 'PLACEHOLDER' // TODO: add value
referencedBlocks = []
subItems = [] /* sample: [{
"type": "open",
"content": " >today add filler photo ^mmw6w5",
"blockId": "^mmw6w5",
"rawContent": "* >today add filler photo ^mmw6w5",
"prefix": "* ",
"contentRange": {},
"lineIndex": 0,
"date": "2022-06-15T07:00:00.000Z",
"heading": "",
"headingLevel": -1,
"isRecurring": false,
"indents": 0,
"filename": "20220614.md",
"noteType": "Calendar",
"linkedNoteTitles": [],
"subItems": [],
"referencedBlocks": [
{}
],
"note": {}
} ] */
type = 'PLACEHOLDER' // TODO: add value
// Methods
async children() {
throw 'Backlink :: children Not implemented yet'
}
async duplicate() {
throw 'Backlink :: duplicate Not implemented yet'
}
async init() {
throw 'Backlink :: init Not implemented yet'
}
constructor(data?: any = {}) {
this.__update(data)
}
__update(data?: any = {}) {
Object.keys(data).forEach((key) => {
this[key] = data[key]
})
return this
}
}
================================================
FILE: __mocks__/Calendar.mock.js
================================================
/* eslint-disable */
/*
* Calendar mocks
*
* Note: nested object example data are there for reference only -- will need to be deleted or cleaned up before use (consider using a factory)
* For functions: check whether async or not & add params & return value
*
*/
import moment from 'moment/min/moment-with-locales'
import * as chrono from 'chrono-node'
export const Calendar = {
// async add() { return null },
// async addUnitToDate() { return null },
availableCalendarTitles(writeOnly: boolean) {
if (writeOnly) {
return ['cal1']
} else {
return ['cal1', 'cal2']
}
},
// async availableReminderListTitles() { return null },
// async dateFrom() { return null },
/* dateUnits: [{ return second }], */
// async eventByID() { return null },
// async eventsBetween() { return null },
// async eventsToday() { return null },
parseDateText(str) {
if (str && str.length) {
const chronoGuesses = chrono.parse(str)
if (chronoGuesses && chronoGuesses.length) {
const retObj = {
start: chronoGuesses[0].start.date(),
end: chronoGuesses[0].end?.date() || chronoGuesses[0].start.date(),
text: chronoGuesses[0].text || '',
index: 2,
}
return [retObj]
}
throw `Calendar.parseDateText() date string (${str}) not recognized`
}
return { start: new Date('2022-01-01 00:00'), end: new Date('2022-01-01 03:00'), text: str, index: 2 }
},
// async reminderByID() { return null },
// async remindersBetween() { return null },
// async remindersByLists() { return null },
// async remindersToday() { return null },
// async remove() { return null },
// async timeAgoSinceNow() { return null },
// async unitOf() { return null },
// async unitsAgoFromNow() { return null },
// async unitsBetween() { return null },
// async unitsUntilNow() { return null },
// async update() { return null },
endOfWeek(date) {
return moment(date).endOf('week').toDate() // new Date('2022-01-07 23:59')
},
startOfWeek(date) {
return moment(date).startOf('week').toDate() // new Date('2022-01-01 00:00')
},
weekNumber(date) {
return moment(date).week()
},
}
// module.exports = Calendar
================================================
FILE: __mocks__/CalendarItem.mock.js
================================================
/* eslint-disable */
/*
* CalendarItem mock class
*
* Usage: const myCalendarItem = new CalendarItem({ param changes here })
*
*/
export class CalendarItem {
// Properties
attendeeNames = [] /* sample: [Storey Wertheimer Wertheimer ] */
attendees = [] /* sample: [[Storey Wertheimer Wertheimer](mailto:storey@wertheimer.com) ] */
availability = 'PLACEHOLDER' // TODO: add value
calendar = 'PLACEHOLDER' // TODO: add value
calendarItemLink = 'PLACEHOLDER' // TODO: add value
date = {} /* new Date("Sun May 22 2022 00:00:00 GMT-0700 (PDT)"), */
endDate = {} /* new Date("Mon Jun 20 2022 00:00:00 GMT-0700 (PDT)"), */
id = 'PLACEHOLDER' // TODO: add value
isAllDay = 'PLACEHOLDER' // TODO: add value
isCalendarWritable = 'PLACEHOLDER' // TODO: add value
isCompleted = 'PLACEHOLDER' // TODO: add value
isRecurring = 'PLACEHOLDER' // TODO: add value
location = 'PLACEHOLDER' // TODO: add value
notes = 'PLACEHOLDER' // TODO: add value
occurences = [] /* sample: [Sun May 22 2022 00:00:00 GMT-0700 (PDT) ] */
title = 'PLACEHOLDER' // TODO: add value
type = 'PLACEHOLDER' // TODO: add value
url = 'PLACEHOLDER' // TODO: add value
// Methods
constructor(data?: any = {}) {
this.__update(data)
}
__update(data?: any = {}) {
Object.keys(data).forEach((key) => {
this[key] = data[key]
})
return this
}
}
================================================
FILE: __mocks__/Clipboard.mock.js
================================================
/* eslint-disable */
/*
* Clipboard mocks
*
* Note: nested object example data are there for reference only -- will need to be deleted or cleaned up before use (consider using a factory)
* For functions: check whether async or not & add params & return value
*
*/
const Clipboard = {
// async availableType() { return null },
// async base64DataStringForType() { return null },
// async clearContents() { return null },
// async dataForType() { return null },
// async setBase64DataStringForType() { return null },
// async setDataForType() { return null },
// async setStringForType() { return null },
string: 'clipString',
// async stringForType() { return null },
/* types: [{ return public.utf8-plain-text }], */
}
module.exports = Clipboard
================================================
FILE: __mocks__/CommandBar.mock.js
================================================
/* eslint-disable */
/*
* CommandBar mocks
*
* Note: nested object example data are there for reference only -- will need to be deleted or cleaned up before use (consider using a factory)
* For functions: check whether async or not & add params & return value
*
*/
const CommandBar = {
async hide() {
return
},
async onAsyncThread() {
return
},
async onMainThread() {
return
},
async openURL() {
return
},
placeholder: 'CommandBar placeholder',
async prompt(title = '', message = '') {
console.log(`CommandBar prompt: ${title}: ${message}`)
return `CommandBar.prompt ${title} ${message}`
},
searchText: 'some text',
async showInput(placeholder, submitText) {
return placeholder //return the placeholder string as input
},
async showLoading(visible, text, progress) {
return
},
async showOptions(options, placeholder) {
return { index: 0, value: options[0], keyModifiers: ['cmd', 'opt', 'shift', 'ctrl'] }
},
async prompt(title, message, buttons) {
return message //return the message string as input
},
async textPrompt(title, message, defaultText) {
return message //return the message string as input
},
}
module.exports = CommandBar
================================================
FILE: __mocks__/DataStore.mock.js
================================================
/* eslint-disable */
/*
* DataStore mocks
*
* Note: nested object example data are there for reference only -- will need to be deleted or cleaned up before use (consider using a factory)
* For functions: check whether async or not & add params & return value
*
*/
import * as samplePlugin from './support/pluginSample.json'
import { logDebug } from '@helpers/dev'
let __json = samplePlugin //variable used for saving/getting json
export const DataStore = {
// async calendarNoteByDate() { return null },
// async calendarNoteByDateString() { return null },
/* calendarNotes: [{ return {
"filename": "20200202.md",
"type": "Calendar",
"title": "2020-02-02",
"date": "2020-02-02T08:00:00.000Z",
"changedDate": "2022-05-11T14:09:19.432Z",
"createdDate": "2022-05-11T14:09:19.432Z",
"hashtags": [],
"mentions": [],
"linkedItems": [],
"datedTodos": [],
"backlinks": [
"{"type":"note","content":"testt","rawContent":"testt","prefix":"","lineIndex":0,"heading":"","headingLevel":0,"isRecurring":false,"indents":0,"filename":"Summaries/testt.md","noteType":"Notes","linkedNoteTitles":[],"subItems":["{"type":"title","content":"foo","rawContent":"# foo","prefix":"# ","contentRange":{},"lineIndex":0,"date":"2020-02-02T08:00:00.000Z","heading":"testt","headingLevel":0,"isRecurring":false,"indents":0,"filename":"Summaries/testt.md","noteType":"Notes","linkedNoteTitles":[],"subItems":[],"referencedBlocks":[],"note":{}}","{"type":"list","content":"==foo== >2020-02-02","rawContent":"- ==foo== >2020-02-02","prefix":"- ","contentRange":{},"lineIndex":3,"date":"2020-02-02T08:00:00.000Z","heading":"foo","headingRange":{},"headingLevel":3,"isRecurring":false,"indents":0,"filename":"Summaries/testt.md","noteType":"Notes","linkedNoteTitles":[],"subItems":[],"referencedBlocks":[],"note":{}}"],"referencedBlocks":[],"note":{}}"
],
"frontmatterTypes": [],
"content": "
* foo",
"paragraphs": [
"{"type":"empty","content":"","rawContent":"","prefix":"","contentRange":{},"lineIndex":0,"heading":"","headingLevel":-1,"isRecurring":false,"indents":0,"filename":"20200202.md","noteType":"Calendar","linkedNoteTitles":[],"subItems":[],"referencedBlocks":[],"note":{}}",
"{"type":"open","content":"foo","rawContent":"* foo","prefix":"* ","contentRange":{},"lineIndex":1,"heading":"","headingLevel":-1,"isRecurring":false,"indents":0,"filename":"20200202.md","noteType":"Calendar","linkedNoteTitles":[],"subItems":[],"referencedBlocks":[],"note":{}}"
]
} }], */
defaultFileExtension: 'md',
/* folders: [{ return / }], */
// async installOrUpdatePluginsByID() { return null },
async installPlugin(pluginObject, showLoading = false) {
logDebug('DataStore.installPlugin (mock)', `requested install of plugin: ${pluginObject.id}; showLoading: ${showLoading}; returning null`)
return null
},
// async installedPlugins() { return null },
// async invokePluginCommand() { return null },
// async invokePluginCommandByName() { return null },
// async isPluginInstalledByID() { return null },
// async listPlugins() { return null },
// async loadData() { return null },
async loadJSON(str) {
return __json
},
// async moveNote() { return null },
async newNote(title = '', folder = '') {
return `# ${title}`
},
// async newNoteWithContent() { return null },
// async noteByFilename() { return null },
preference(key: string = ''): string {
// let deliberatelyUndefined
switch (key) {
case 'timeblockTextMustContainString':
// return 'at' // to test use of 'must contain string'
// return deliberatelyUndefined // to test an error case
return '' // set to blank to mimic no additional NP checking of text strings
break
case 'isAsteriskTodo':
return true
break
default:
return ''
break
}
},
// async projectNoteByFilename() { return null },
// async projectNoteByTitle() { return null },
// async projectNoteByTitleCaseInsensitive() { return null },
/* projectNotes: [{ return {
"filename": "Migrated/Marlita Hours.md",
"type": "Notes",
"title": "",
"changedDate": "2021-09-07T13:49:41.000Z",
"createdDate": "2021-04-29T20:30:00.000Z",
"hashtags": [],
"mentions": [],
"linkedItems": [],
"datedTodos": [],
"backlinks": [],
"frontmatterTypes": [],
"content": "# ",
"paragraphs": [
"{"type":"title","content":"","rawContent":"# ","prefix":"# ","contentRange":{},"lineIndex":0,"heading":"","headingLevel":1,"isRecurring":false,"indents":0,"filename":"Migrated/Marlita Hours.md","noteType":"Notes","linkedNoteTitles":[],"subItems":[],"referencedBlocks":[],"note":{}}"
]
} }], */
// async referencedBlocks() { return null },
// async saveData() { return null },
async saveJSON(object, filename) {
__json = object
return true
},
// async search() { return null },
// async searchCalendarNotes() { return null },
// async searchProjectNotes() { return null },
// async setPreference() { return null },
settings: {
settingsFieldName: 'Settings field value',
_logLevel: 'none',
},
}
// module.exports = DataStore
================================================
FILE: __mocks__/Editor.mock.js
================================================
/* eslint-disable */
/**
* Editor mocks with Proxy
*
* Editor and Note share many of the same properties+methods (CoreNoteFields), so most of them are defined in Note.mock.js and can apply to both.
*
* This module uses a JavaScript Proxy to redirect all function calls to the underlying `note` object unless specifically overridden.
* The `get` trap in the Proxy checks if a property exists on the `Editor` object. If it does, it returns that property.
* If not, it delegates the call to the `note` object. If the property is not found in either, it throws an error.
*
* To override a function that is not in the underlying `note`, simply define it in the `editorOverrides` object.
*
* Note: All `open*` functions are specifically overridden to return `this.note`.
*/
import { Note } from './Note.mock'
const noteObject = new Note() // NOTE: try to reference the code in the Note mock wherever possible!
// NOTE: noteObject is spread into Editor below, so any properties that exist in Note will overwrite the ones in Editor
const editorOverrides = {
...{
async openNoteByDate(date: Date, newWindow?: boolean, highlightStart?: number, highlightEnd?: number, splitView?: boolean, timeframe?: string): Promise<TNote> {
return noteObject
},
async openNoteByDateString() {
return noteObject
},
async openNoteByFilename() {
return noteObject
},
async openNoteByTitle() {
return noteObject
},
async openNoteByTitleCaseInsensitive() {
return noteObject
},
note: noteObject,
},
...noteObject,
}
export const Editor = new Proxy(editorOverrides, {
get(target, prop) {
if (prop in target) {
return target[prop]
}
if (prop && target.note && prop in target.note) {
return target.note[prop]
}
// Handle known built-in Symbol properties with sensible defaults
const symbolProperties = [Symbol.iterator, Symbol.toPrimitive, Symbol.asyncIterator, Symbol.hasInstance, Symbol.toStringTag]
if (symbolProperties.includes(prop)) {
if (prop === Symbol.iterator) return undefined
if (prop === Symbol.toPrimitive) return (hint) => (hint === 'number' ? NaN : String(target.note))
if (prop === Symbol.asyncIterator) return undefined
if (prop === Symbol.hasInstance) return undefined
if (prop === Symbol.toStringTag) return 'Editor'
}
// Handle Jest specific methods that are not defined on Note and which should not cause errors
if (['asymmetricMatch'].includes(prop)) {
return undefined
}
// Throw detailed error if property is not found
throw new Error(
`Editor.mock.js: Property "${String(prop)}" not found. Editor.${String(prop)} or Note.${String(prop)} does not exist.\n` +
`- Check if this property/method should be implemented in Note.mock.js.\n` +
`- If it's Editor-specific, consider adding it to Editor.mock.js overrides in editorOverrides.\n` +
`- If this is a Jest-specific method (such as 'asymmetricMatch') or a built-in Symbol (e.g., Symbol.iterator, Symbol.toPrimitive), ` +
`return a sensible default instead.\n`,
)
},
set(target, prop, value) {
if (prop === 'note') {
target.note = value
// Reinitialize the proxy with the new note
Object.assign(target, value)
return true
}
target[prop] = value
return true
},
})
================================================
FILE: __mocks__/Fetch.mock.js
================================================
// @flow
/**
* Mock the fetch() function to return a specific response for a given URL and options.body
* You pass in text to the match object and if the url or options.body contain that text, the response is returned
* Note: match.url is required but match.optionsBody is optional
* The matches are case-insensitive and are turned into RegExps so you can include regular expressions in the match strings
* So you could match for "foo" (a plain string) or you could match for "foo.*bar" (a regular expression) that would be true for "foo bar" or "foo xxx bar"
* The matches are done in order of the mockResponses array, so the first match is returned that matches the URL and/or options.body
* So it's a good idea to put the more complex rules first and the simpler rules later in the array
* For instance, if two requests are going to have the word "Mercury" in them, but one request will have "concept of Mercury", you might want to put the more specific rule first
* It's generally a good idea to put as many words as possible in the match string to avoid false matches
* If no match is found, the defaultResponse text is returned
* @param {Array<FetchMockResponse>} mockResponses - Array of mock responses in the form of FetchMockResponse
* @example
import response1 from './mockResponses/response1.json' // a JSON file with a sample server response (you will probably have several of these)
import { FetchMock, type FetchMockResponse } from '@mocks/Fetch.mock'
const OVERRIDE_FETCH = true // set to true to override the global fetch() function with fake responses passed below
if (OVERRIDE_FETCH) {
const fm = new FetchMock([
{ match: { url: 'xxx', optionsBody: "foo.*bar" }, response: JSON.stringify(response1) }
]) // add one object to array for each mock response
fetch = async (url, opts) => fm.fetch(url, opts) //override the global fetch
}
* ...then wherever the code is using fetch, it will use the mock
* const result = await fetch('http://xxx.com/api', { body: 'has foo and also has the word bar in it' }) // returns 'fake server response here' (the response text)
*/
export class FetchMock {
responses: Array<FetchMockResponse> = [defaultResponse]
constructor(mockResponses: Array<FetchMockResponse>) {
if (mockResponses && !Array.isArray(mockResponses)) throw new Error('Fetch constructor requires an array of mock responses')
this.responses = [...(mockResponses?.length ? mockResponses : []), ...this.responses]
}
fetch(url: string, options: FetchOptions): string {
const match = this.responses.find((r) => {
const urlTest = r.match?.url ? new RegExp(r.match.url, 'ig').test(url) : false
// body options will return true if it's a match or if it's not defined
const optionsBodyTest = r.match?.optionsBody && options.body ? new RegExp(r.match.optionsBody, 'ig').test(options?.body || '') : r.match?.optionsBody ? false : true
return urlTest && optionsBodyTest
})
// return match ? Promise.resolve(match.response) : Promise.resolve(defaultResponse.response)
return match ? match.response : defaultResponse.response
}
}
export type FetchMockResponse = {
match: { url: string /* a string to look for in the URL passed to fetch */, optionsBody?: string /* an (optional) string to look for in the options.body passed to fetch */ },
response: string /* the response to return if the match is found */,
}
const defaultResponse = {
match: { url: '', optionsBody: '' },
response:
'Fetch call mocking is turned on and received a request, but the request did not match any of the supplied mock responses -- check the spelling and/or RegEx patterns in the URL and optionsBody matchers.',
}
================================================
FILE: __mocks__/NP_THEME.mock.js
================================================
export const mockNP_THEME = {
base: {
backgroundColor: '#ffffff', // Example color
textColor: '#000000', // Example color
altColor: '#f0f0f0', // Example color
tintColor: '#ff0000', // Example color
},
}
================================================
FILE: __mocks__/Note.mock.js
================================================
// @flow
import { logDebug } from '../helpers/dev'
import { hasFrontMatter, getAttributes } from '@helpers/NPFrontMatter'
/*
* Note mock class
*
* Usage: const myNote = new Note({ param changes here })
*
*/
import { textWithoutSyncedCopyTag } from '@helpers/syncedCopies'
export class Note {
// Explicitly define properties that are dynamically assigned
content: string
/** Full note markdown when tests construct notes with `{ rawContent }` only */
rawContent: string = ''
// Properties
backlinks: any[] = [] /* sample: [ SOMETHING ], */
changedDate: any = {} /* new Date("Tue Sep 07 2021 06:49:41 GMT-0700 (PDT)"), */
/**
* @private
* @type {string}
*/
_content: string = 'CONTENT_PLACEHOLDER_FROM_NOTE_MOCK' // see setter and getter at the bottom of the file
createdDate: any = {} /* new Date("Thu Apr 29 2021 13:30:00 GMT-0700 (PDT)"), */
date: string = 'DATE_PLACEHOLDER_FROM_NOTE_MOCK' // TODO: add value
datedTodos: any[] = [] /* sample: [ SOMETHING ], */
filename: string = 'FILENAME_PLACEHOLDER_FROM_NOTE_MOCK' // TODO: add value
frontmatterTypes: string[] = [] /* sample: [ SOMETHING ], */
frontmatterAttributes: any = {}
hashtags: any[] = [] /* sample: [ SOMETHING ], */
linkedItems: any[] = [] /* sample: [ SOMETHING ], */
mentions: any[] = [] /* sample: [ SOMETHING ], */
paragraphs: any[] = [] /* sample: [{
"type": "Notes",
"content": "",
"rawContent": "# ",
"prefix": "# ",
"contentRange": {},
"lineIndex": 0,
"heading": "",
"headingLevel": 1,
"isRecurring": false,
"indents": 0,
"filename": "Migrated/Marlita Hours.md",
"noteType": "Notes",
"linkedNoteTitles": [],
"subItems": [],
"referencedBlocks": [],
"note": {}
} ], */
title: string = 'TITLE_PLACEHOLDER_FROM_NOTE_MOCK' // TODO: add value
type: string = 'Notes'
// Methods
async addBlockID(p: any) {
if (!/\^[a-zA-Z0-9]{6}/.test(p.content)) {
p.content = `${textWithoutSyncedCopyTag(p.content)} ^123456`
p.rawContent = `${textWithoutSyncedCopyTag(p.rawContent || p.content)} ^123456`
p.blockId = '^123456'
}
}
async addParagraphBelowHeadingTitle(content: string, paragraphType: string, headingTitle: string, shouldAppend: boolean, shouldCreate: boolean) {
// TODO: may need to create actual rawContent for certain tests
const paras = makeParagraphsFromContent(content)
const paragraphs = this.paragraphs
// find paragraph with content === headingTitle
const headingIndex = paragraphs.findIndex((p) => p.content === headingTitle)
this.paragraphs.splice(headingIndex + 1, 0, ...paras)
this.paragraphs.forEach((p, i) => (this.paragraphs[i].lineIndex = i))
this.resetLineIndexesAndContent()
}
async addTodoBelowHeadingTitle(): Promise<void> {
throw 'Note :: addTodoBelowHeadingTitle Not implemented yet'
}
appendParagraph(title: string, type: ParagraphType): void {
this.paragraphs.push({ content: title, type: type, lineIndex: this.paragraphs.length })
return
}
async appendParagraphBelowHeadingLineIndex(): Promise<void> {
throw 'Note :: appendParagraphBelowHeadingLineIndex Not implemented yet'
}
async appendTodo(): Promise<void> {
throw 'Note :: appendTodo Not implemented yet'
}
async appendTodoBelowHeadingLineIndex(): Promise<void> {
throw 'Note :: appendTodoBelowHeadingLineIndex Not implemented yet'
}
async insertCancelledTodo(): Promise<void> {
throw 'Note :: insertCancelledTodo Not implemented yet'
}
async insertCompletedTodo(): Promise<void> {
await Promise.resolve()
throw 'Note :: insertCompletedTodo Not implemented yet'
}
insertHeading(content: string, lineIndex: number, headingLevel: number): void {
// .insertHeading(content, lineIndex, headingLevel)
const headingMark = '#'.repeat(headingLevel)
const heading = `${headingMark} ${content}`
const paras = makeParagraphsFromContent(heading)
this.paragraphs.splice(lineIndex, 0, ...paras)
this.paragraphs.forEach((p, i) => (this.paragraphs[i].lineIndex = i))
this.resetLineIndexesAndContent()
return
}
async insertList(): Promise<void> {
await Promise.resolve()
throw 'Note :: insertList Not implemented yet'
}
insertParagraph(content: string, lineIndex: number, type: ParagraphType): void {
//TODO: deal with the lineIndex?
// .insertParagraph(content, lineIndex, type)
// if string contains "\n" then split into multiple paragraphs
const paras = makeParagraphsFromContent(content)
// if (paras[paras.length - 1].content === '') paras.pop()
// splice at lineIndex, do not remove any existing paragraphs
this.paragraphs.splice(lineIndex, 0, ...paras)
this.paragraphs.forEach((p, i) => (this.paragraphs[i].lineIndex = i))
this.resetLineIndexesAndContent()
return
}
async insertParagraphAfterParagraph(content: string, otherParagraph: any, paragraphType: string) {
// .insertParagraphAfterParagraph(content, otherParagraph, paragraphType)
// TODO: may need to create actual rawContent for certain tests
const paras = makeParagraphsFromContent(content)
this.paragraphs.splice(otherParagraph.lineIndex + 1, 0, ...paras)
this.paragraphs.forEach((p, i) => (this.paragraphs[i].lineIndex = i))
this.resetLineIndexesAndContent()
}
async insertParagraphBeforeParagraph(content: string, otherParagraph: any, type: string) {
// .insertParagraphBeforeParagraph(content, otherParagraph, paragraphType)
// TODO: may need to create actual rawContent for certain tests
const paras = makeParagraphsFromContent(content)
this.paragraphs.splice(otherParagraph.lineIndex, 0, ...paras)
this.paragraphs.forEach((p, i) => (this.paragraphs[i].lineIndex = i))
this.resetLineIndexesAndContent()
}
async insertQuote(): Promise<void> {
throw 'Note :: insertQuote Not implemented yet'
}
async insertScheduledTodo(): Promise<void> {
throw 'Note :: insertScheduledTodo Not implemented yet'
}
async insertTextAtCharacterIndex(): Promise<void> {
throw 'Note :: insertTextAtCharacterIndex Not implemented yet'
}
async insertTodo(): Promise<void> {
throw 'Note :: insertTodo Not implemented yet'
}
async insertTodoAfterParagraph(): Promise<void> {
throw 'Note :: insertTodoAfterParagraph Not implemented yet'
}
async insertTodoBeforeParagraph(): Promise<void> {
throw 'Note :: insertTodoBeforeParagraph Not implemented yet'
}
async paragraphRangeAtCharacterIndex(): Promise<void> {
throw 'Note :: paragraphRangeAtCharacterIndex Not implemented yet'
}
async prependParagraph(content: string, type: ParagraphType) {
this.paragraphs = [{ content, type }, ...this.paragraphs]
logDebug(`JEST Note: note.prependParagraph() called. but .content is approximated but not exactly correct, because it does not add markdown.`) // TODO(@dwertheimer): Is this now correct for .rawContent? And isn't .content set here?
this.resetLineIndexesAndContent()
}
async prependTodo(): Promise<void> {
throw 'Note :: prependTodo Not implemented yet'
}
async printNote(): Promise<void> {
throw 'Note :: printNote Not implemented yet'
}
async removeBlockID(p: any) {
p.content = textWithoutSyncedCopyTag(p.content)
p.rawContent = textWithoutSyncedCopyTag(p.rawContent)
if (p.blockId) delete p.blockId
}
async removeParagraph(para: any) {
this.paragraphs = this.paragraphs.filter((p) => p.lineIndex !== para.lineIndex)
this.resetLineIndexesAndContent()
}
async removeParagraphAtIndex(): Promise<void> {
throw 'Note :: removeParagraphAtIndex Not implemented yet'
}
async removeParagraphs(paras: any[]) {
// filter this.paragraphs to remove paragraphs with lineIndex in paras
this.paragraphs = this.paragraphs.filter((p) => !paras.find((para) => para.lineIndex === p.lineIndex))
this.resetLineIndexesAndContent()
}
async replaceTextInCharacterRange(): Promise<void> {
throw 'Note :: replaceTextInCharacterRange Not implemented yet'
}
async updateParagraph(para: any) {
this.paragraphs[para.lineIndex] = para
}
async updateParagraphs(paras: any[]) {
paras.forEach((para) => {
this.paragraphs[para.lineIndex] = para
this.resetLineIndexesAndContent()
})
}
/**
* HELPERS TO SET UP THE NOTE AFTER PARAGRAPH CHANGES
*/
resetLineIndexesAndContent() {
this.paragraphs.forEach((p, i) => (this.paragraphs[i].lineIndex = i))
this._content = this.paragraphs.map((p) => p.content).join('\n')
this.setFrontmatterAttributes()
}
/**
* Sets the frontmatter attributes of the note after the note content has been updated
*/
setFrontmatterAttributes() {
if (hasFrontMatter(this._content)) {
this.frontmatterAttributes = getAttributes(this._content) || {}
this.frontmatterTypes =
Object.keys(this.frontmatterAttributes).length > 0 ? (this.frontmatterAttributes.type ? this.frontmatterAttributes.type.split(',').map((t) => t.trim()) : []) : []
}
}
/**
* Gets the content of the note.
* @returns {string} The current content of the note.
*/
get content(): string {
return this._content
}
/**
* Sets the content of the note and performs additional actions.
* @param {string} value - The new content value.
*/
set content(value: string) {
this._content = value
this.paragraphs = makeParagraphsFromContent(value)
this.setFrontmatterAttributes()
}
__update(data?: any = {}): this {
Object.keys(data).forEach((key) => {
const value = data[key]
if (key === 'content') {
if (value !== '') {
this._content = value
this.paragraphs = makeParagraphsFromContent(this._content)
}
} else if (key === 'rawContent') {
this.rawContent = value
if (typeof value === 'string' && value !== '' && (data.content === undefined || data.content === '')) {
this._content = value
this.paragraphs = makeParagraphsFromContent(this._content)
}
} else {
this[key] = data[key]
}
})
return this
}
constructor(data?: any = {}) {
this.__update(data)
if (!this.paragraphs) this.paragraphs = []
this.resetLineIndexesAndContent()
}
}
// Helper function to determine the type of a line based on its content
function getLineTypeAndContent(content: string, lastHeadingLevel: number = 0): { content: string, type: string, headingLevel: number } {
const trimmedContent = content.trim()
let type = 'unknown'
let lineContent = trimmedContent.replace(/^\t*/, '')
let headingLevel = lastHeadingLevel
if (lineContent === '---') {
type = 'separator'
} else if (/^\s*#{1,} /.test(lineContent)) {
type = 'title'
// Extract heading level BEFORE removing the # characters
const hashMatch = lineContent.match(/^\s*(#{1,6})/)
headingLevel = hashMatch ? hashMatch[1].length : 1
lineContent = lineContent.replace(/^\s*#{1,} /, '')
} else if (lineContent.startsWith('- [x]') || lineContent.startsWith('* [x]')) {
type = 'done'
lineContent = lineContent.slice(5)
} else if (lineContent.startsWith('- [-]') || lineContent.startsWith('* [-]')) {
type = 'cancelled'
lineContent = lineContent.slice(5)
} else if (lineContent.startsWith('- [>]') || lineContent.startsWith('* [>]')) {
type = 'scheduled'
lineContent = lineContent.slice(5)
} else if (lineContent.startsWith('- [ ]') || lineContent.startsWith('* [ ]')) {
type = 'open'
lineContent = lineContent.slice(5)
} else if (lineContent.startsWith('- ') && !lineContent.startsWith('- [')) {
type = 'list'
lineContent = lineContent.slice(2)
} else if (lineContent.startsWith('+ [x]')) {
type = 'checklistDone'
lineContent = lineContent.slice(5)
} else if (lineContent.startsWith('+ [-]')) {
type = 'checklistCancelled'
lineContent = lineContent.slice(5)
} else if (lineContent.startsWith('+ [>]')) {
type = 'checklistScheduled'
lineContent = lineContent.slice(5)
} else if (lineContent.startsWith('+ [ ]')) {
type = 'checklist'
lineContent = lineContent.slice(5)
} else if (lineContent.startsWith('+ ') && !lineContent.startsWith('+ [')) {
type = 'checklist'
lineContent = lineContent.slice(2)
} else if (lineContent.startsWith('* ')) {
type = 'open'
lineContent = lineContent.slice(2)
} else if (/^\s*\d|\w/.test(lineContent)) {
type = 'text'
}
return { content: lineContent.trim(), type, headingLevel }
}
// Helper function to count leading tabs in a line
function countLeadingTabs(content: string): number {
const match = content.match(/^\t*/)
return match ? match[0].length : 0
}
// Helper function to create paragraphs from content
function makeParagraphsFromContent(content: string): any[] {
const lines = content.split('\n')
if (lines[lines.length - 1] === '') {
lines.pop() // Remove the last line if it's empty
}
return lines.map((c, i) => {
let lastHeadingLevel = 0
const { content: lineContent, type, headingLevel } = getLineTypeAndContent(c, lastHeadingLevel)
lastHeadingLevel = headingLevel
return {
content: lineContent,
type,
rawContent: c,
lineIndex: i,
indents: countLeadingTabs(c),
headingLevel,
}
})
}
================================================
FILE: __mocks__/NotePlan.mock.js
================================================
/*
* NotePlan mocks
*
* NOTE: Unlike the other mocks, this is a class and not an object. So you should use `new NotePlan()` to create an instance.
* .e.g.
* beforeAll(() => {
* global.NotePlan = new NotePlan()
* })
*
* Note: nested object example data are there for reference only -- will need to be deleted or cleaned up before use (consider using a factory)
* For functions: check whether async or not & add params & return value
*
*/
export class NotePlan {
environment = {
languageCode: 'en',
regionCode: 'US',
is12hFormat: true,
preferredLanguages: ['en-US'],
secondsFromGMT: -25200,
localTimeZoneAbbreviation: 'PDT',
localTimeZoneIdentifier: 'America/Los_Angeles',
isDaylightSavingTime: true,
daylightSavingTimeOffset: 3600,
nextDaylightSavingTimeTransition: '2022-11-06T09:00:00.000Z',
platform: 'macOS',
hasSettings: true,
version: '3.18.0',
versionNumber: 3180,
buildVersion: 1417, // = 3.18.0
templateFolder: '@Templates',
}
// async openURL() { return null },
// async resetCaches() { return null },
selectedSidebarFolder = `SelectedFolder`
// async showConfigurationView() { return null },
/**
* Mock AI function for testing
*/
static ai(prompt, filenames = [], useStrictFilenames = false, model = 'gpt-4') {
// Return a mock AI response for testing
return Promise.resolve(`Mock AI Analysis: This appears to be a template error. Please check your variable definitions and syntax.`)
}
constructor(data = {}) {
this.__update(data)
}
__update(data = {}) {
Object.keys(data).forEach((key) => {
this[key] = data[key]
})
return this
}
}
================================================
FILE: __mocks__/Paragraph.mock.js
================================================
/* eslint-disable */
/*
* Paragraph mock class
*
* Usage: const myParagraph = new Paragraph({ param changes here })
*
*/
export class Paragraph {
// Properties
blockId = null
content = 'SET_ME_IN_TEST'
contentRange = {} /* {
"start": 0,
"end": 2,
"length": 2
} , */
date = new Date()
filename = 'testFile.md'
heading = ''
headingLevel = 1
headingRange = { start: 0, end: 0, length: 0 }
indents = 0
isRecurring = false
lineIndex = 0
linkedNoteTitles = []
note = {} /* {
"filename": "Migrated/Marlita Hours.md",
"type": "Notes",
"title": "",
"changedDate": "2021-09-07T13:49:41.000Z",
"createdDate": "2021-04-29T20:30:00.000Z",
"hashtags": [],
"mentions": [],
"linkedItems": [],
"datedTodos": [],
"backlinks": [],
"frontmatterTypes": [],
"content": "# ",
"paragraphs": [
"{\"type\":\"title\",\"content\":\"\",\"rawContent\":\"# \",\"prefix\":\"# \",\"contentRange\":{},\"lineIndex\":0,\"heading\":\"\",\"headingLevel\":1,\"isRecurring\":false,\"indents\":0,\"filename\":\"Migrated/Marlita Hours.md\",\"noteType\":\"Notes\",\"linkedNoteTitles\":[],\"subItems\":[],\"referencedBlocks\":[],\"note\":{}}"
]
} , */
noteType = 'Notes'
prefix = ''
rawContent = 'SET_ME_IN_TEST'
referencedBlocks = []
subItems = []
type = 'text'
// Methods
async children() {
throw 'Paragraph :: children called, but children was not set. You should pass a children function with the paragraph'
}
// async duplicate() {
// return this
// }
async init() {
throw 'Paragraph :: init Not implemented yet'
}
constructor(data?: any = {}) {
this.__update(data)
if (!data.rawContent) {
// set rawContent from content
switch (this.type) {
case 'open':
this.rawContent = `- [ ] ${this.content}`
break
case 'cancelled':
this.rawContent = `- [-] ${this.content}`
break
case 'done':
this.rawContent = `- [x] ${this.content}`
break
case 'scheduled':
this.rawContent = `- [>] ${this.content}`
break
case 'checklist':
this.rawContent = `+ [ ] ${this.content}`
break
case 'checklistCancelled':
this.rawContent = `+ [-] ${this.content}`
break
case 'checklistDone':
this.rawContent = `+ [x] ${this.content}`
break
case 'checklistScheduled':
this.rawContent = `+ [>] ${this.content}`
break
case 'separator':
this.rawContent = `---`
break
case 'empty':
this.rawContent = ``
break
}
}
// TODO: is there a way of incrementing lineIndex here?
}
__update(data?: any = {}) {
Object.keys(data).forEach((key) => {
this[key] = data[key]
})
return this
}
}
================================================
FILE: __mocks__/PluginCommandObject.mock.js
================================================
/* eslint-disable */
/*
* PluginCommandObjectMock mock class
*
* Usage: const myPluginCommandObject = new PluginCommandObject({ param changes here })
*
*/
export class PluginCommandObject {
// Properties
desc = 'PLACEHOLDER' // TODO: add value
name = 'PLACEHOLDER' // TODO: add value
pluginID = 'PLACEHOLDER' // TODO: add value
pluginName = 'PLACEHOLDER' // TODO: add value
// Methods
constructor(data?: any = {}) {
this.__update(data)
}
__update(data?: any = {}) {
Object.keys(data).forEach((key) => {
this[key] = data[key]
})
return this
}
}
================================================
FILE: __mocks__/PluginObject.mock.js
================================================
/* eslint-disable */
/*
* PluginObject mock class
*
* Usage: const myPluginObject = new PluginObject({ param changes here })
*
*/
export class PluginObject {
// Properties
author = 'PLACEHOLDER' // TODO: add value
availableUpdate = 'PLACEHOLDER' // TODO: add value
commands = [] /* sample: [{
"name": "atb - Create AutoTimeBlocks for >today's Tasks",
"desc": "Read >today todos and insert them into today's calendar note as timeblocks",
"pluginID": "dwertheimer.EventAutomations",
"pluginName": "🗓 AutoTimeBlocking & Synced Today Todos"
} ] */
desc = 'PLACEHOLDER' // TODO: add value
id = 'PLACEHOLDER' // TODO: add value
isOnline = 'PLACEHOLDER' // TODO: add value
name = 'PLACEHOLDER' // TODO: add value
releaseUrl = 'PLACEHOLDER' // TODO: add value
repoUrl = 'PLACEHOLDER' // TODO: add value
script = 'PLACEHOLDER' // TODO: add value
version = 'PLACEHOLDER' // TODO: add value
// Methods
constructor(data?: any = {}) {
this.__update(data)
}
__update(data?: any = {}) {
Object.keys(data).forEach((key) => {
this[key] = data[key]
})
return this
}
}
================================================
FILE: __mocks__/Range.mock.js
================================================
/* eslint-disable */
/*
* Range mock class
*
* Usage: const myRange = new Range({ param changes here })
*
*/
export class Range {
// Properties
start = 0
end = 1
length = 1
// Methods
constructor(data?: any = {}) {
this.__update(data)
}
__update(data?: any = {}) {
Object.keys(data).forEach((key) => {
this[key] = data[key]
})
return this
}
}
================================================
FILE: __mocks__/_README-Mocks.md
================================================
# Mocking NotePlan objects in your Jest testing files:
The best/fastest/easiest/most-reliable thing to do when writing plugins is to minimize the amount of code in the plugin entrypoint functions and rely on pure-JS support functions to do the heavy lifting. This is preferable, because those pure functions can be easily tested using Jest without relying on NotePlan's APIs.
That said, we have a goal of fully mocking the NotePlan APIs so that plugins can not only can have functional unit tests, but can also have integration tests which confirm that your plugin code is working end-to-end (and remains working as the codebase changes).
> **The API-mocking is a work in progress and will take some time to get fully fleshed out. Many of the API functions are stubbed or commented out. You may need to implement a function in the mock you're using along the way (PRs are welcome!).**
That said, here's how the testing of NotePlan APIs in your plugin works:
## Steps:
**In your test file:**
1. Import the mocks you need
2. Hoist the relevant mocks up to global scope in the beforeAll() method of your test file
3. Create sub-object content mocks (if necessary) to populate top level objects with Notes, Paragraphs, etc.
## Testing using mocked-out data
The most basic example is: you have a function with a call to a NotePlan API that retrieves a piece of data. You want to test the function, but the test will fail without that data from NotePlan. In this case, we can use a Mock simply to return a fake piece of data from a simulated NotePlan API.
The following example is an actual example from the code base: tests whether the `isTimeBlockLine()` function returns expected values, using one field of mocked out data from the API (the `DataStore.preference(...)` function). This function cannot be tested without a mock because it contains this one line:
```js
const mustContainString = checkString(DataStore.preference("timeblockTextMustContainString"))
```
That call to DataStore will make a typical Jest test die. So we can just mock that DataStore function to return a value to our test:
```js
/* globals describe, expect, it, test, beforeAll */
import * as tb from '../timeblocks'
import DataStore from '@mocks/index'
beforeAll(() => {
global.DataStore = DataStore
})
describe('helpers/timeblocks.js', () => { // file
describe('isTimeBlockLine SHOULD MATCH', () => { // function
test('should match: - @done(2021-12-12) 2:30-3:45', () => {
expect(tb.isTimeBlockLine('- @done(2021-12-12) 2:30-3:45')).toEqual(true)
})
})
})
```
This now works because the DataStore mock returns '' for `DataStore.preference("timeblockTextMustContainString")` and thus, our tests can continue.
## Testing a call from your plugin to an NP API
A slightly more complex example: We want to make sure that our plugin is writing the proper value to NotePlan editor. In this case, we need to listen in to writes to the Editor api to ensure the correct value is being passed.
The following example tests whether `Editor.insertTextAtCursor()` is called from the 'JestHelpers' plugin's `sayHello` function:
```js
/* global describe, test, it, jest, expect, beforeAll */
import * as NPfile from '../src/NPPluginMain' // import everything for this plugin
import { DataStore } from '@mocks/index' // import mock(s)
beforeAll(() => {
global.Editor = Editor
})
describe('dwertheimer.JestHelpers' /*my plugin id*/, () => {
describe('NPPluginMain' /* file */, () => {
describe('sayHello' /* function */, () => {
test('should insert text if called with a string param', async () => {
const spy = jest.spyOn(Editor, 'insertTextAtCursor') // assuming my plugin calls this one NP command
const result = await NPfile.sayHello('myText')
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenNthCalledWith(
1,
`myText`,
)
// So we know that Editor.insertTextAtCursor was called with `myText` which was passed to the plugin entry point (e.g. from an xcallbackurl)
spy.mockRestore()
})
})
})
})
```
## Mocking sub-objects
The top-level NP objects: `Calendar, Clipboard, CommandBar, DataStore, Editor, NotePlan` all have sub-objects that will likely need to be mocked as well,depending on what your plugin is doing. For example, `Editor` has a property called `note`. You can create that mock note using the `Note` factory:
```js
Editor.note = new Note({ filename: 'testingFile' })
```
Editor.note now has some basic properties, but to look like a real NotePlan `Note` object, a Note needs to have some paragraphs, and those paragraphs have some properties/methods also. You can mock the paragraphs with the `Paragraph` mock factory:
```js
Editor.note.paragraphs = [new Paragraph({ content: 'paraContent1' }),new Paragraph({ content: 'paraContent2' })]
```
**Note: as you can see, when you instantiate a new factory instance, you can pass through any variables you want to override (e.g. `.content` in the above example)**
## A Full Example (from the "plugin:create" skeleton)
```js
// Jest testing docs: https://jestjs.io/docs/using-matchers
/* global describe, test, jest, expect */
import * as mainFile from '../src/NPPluginMain'
import { copyObject } from '@helpers/dev'
import { Calendar, Clipboard, CommandBar, DataStore, Editor, NotePlan, Note, Paragraph, Backlink, Range, CalendarItem, PluginObject, PluginCommandObject } from '@mocks/index'
beforeAll(() => {
global.Calendar = Calendar
global.Clipboard = Clipboard
global.CommandBar = CommandBar
global.DataStore = DataStore
global.Editor = Editor
global.NotePlan = NotePlan
})
describe('{{pluginId}}' /* pluginID */, () => {
describe('NPPluginMain' /* file */, () => {
describe('sayHello' /* function */, () => {
test('should insert text if called with a string param', async () => {
// tests start with "should" to describe the expected behavior
const spy = jest.spyOn(Editor, 'insertTextAtCursor')
const result = await mainFile.sayHello('Testing...')
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenNthCalledWith(
1,
`***You clicked the link!*** The message at the end of the link is "Testing...". Now the rest of the plugin will run just as before...\n\n`,
)
spy.mockRestore()
})
test('should write result to console', async () => {
// tests start with "should" to describe the expected behavior
const spy = jest.spyOn(console, 'log')
const result = await mainFile.sayHello()
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenNthCalledWith(1, expect.stringMatching(/The plugin says: HELLO WORLD FROM TEST PLUGIN!/))
spy.mockRestore()
})
test('should call DataStore.settings', async () => {
// tests start with "should" to describe the expected behavior
const oldValue = DataStore.settings
DataStore.settings = { settingsString: 'settingTest' }
const spy = jest.spyOn(Editor, 'insertTextAtCursor')
const _ = await mainFile.sayHello()
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenNthCalledWith(1, expect.stringMatching(/settingTest/))
DataStore.settings = oldValue
spy.mockRestore()
})
test('should call DataStore.settings if no value set', async () => {
// tests start with "should" to describe the expected behavior
const oldValue = DataStore.settings
DataStore.settings = { settingsString: undefined }
const spy = jest.spyOn(Editor, 'insertTextAtCursor')
const _ = await mainFile.sayHello()
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenNthCalledWith(1, expect.stringMatching(/\*\*\"\"\*\*/))
DataStore.settings = oldValue
spy.mockRestore()
})
test('should CLO write note.paragraphs to console', async () => {
// tests start with "should" to describe the expected behavior
const prevEditorNoteValue = copyObject(Editor.note || {})
Editor.note = new Note({ filename: 'testingFile' })
Editor.note.paragraphs = [new Paragraph({ content: 'testingParagraph' })]
const spy = jest.spyOn(console, 'log')
const result = await mainFile.sayHello()
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenNthCalledWith(2, expect.stringMatching(/\"content\": \"testingParagraph\"/))
Editor.note = prevEditorNoteValue
spy.mockRestore()
})
test('should insert a link if not called with a string param', async () => {
// tests start with "should" to describe the expected behavior
const spy = jest.spyOn(Editor, 'insertTextAtCursor')
const result = await mainFile.sayHello('')
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenLastCalledWith(expect.stringMatching(/noteplan:\/\/x-callback-url\/runPlugin/))
spy.mockRestore()
})
test('should write an error to console on throw', async () => {
// tests start with "should" to describe the expected behavior
const spy = jest.spyOn(console, 'log')
const oldValue = Editor.insertTextAtCursor
delete Editor.insertTextAtCursor
const result = await mainFile.sayHello()
expect(spy).toHaveBeenCalled()
expect(spy).toHaveBeenNthCalledWith(2, expect.stringMatching(/ERROR/))
Editor.insertTextAtCursor = oldValue
spy.mockRestore()
})
})
})
})
```
================================================
FILE: __mocks__/__tests__/fetch.mock.test.js
================================================
/* global jest, describe, test, expect, beforeAll */
import { CustomConsole } from '@jest/console' // see note below
import { DataStore, Editor, CommandBar, NotePlan } from '@mocks/index'
import { FetchMock } from '../Fetch.mock'
import { simpleFormatter } from '@mocks/index'
// Make DataStore and Editor available globally for the source code
global.DataStore = DataStore
global.Editor = Editor
global.CommandBar = CommandBar
global.NotePlan = NotePlan
const PLUGIN_NAME = `Fetch.mock`
const FILENAME = ``
beforeAll(() => {
global.console = new CustomConsole(process.stdout, process.stderr, simpleFormatter) // minimize log footprint
})
/* Samples:
expect(result).toMatch(/someString/)
expect(result).not.toMatch(/someString/)
expect(result).toEqual([])
import { mockWasCalledWith } from '@mocks/mockHelpers'
const spy = jest.spyOn(console, 'log')
const result = mainFile.getConfig()
expect(mockWasCalledWith(spy, /config was empty/)).toBe(true)
spy.mockRestore()
test('should return the command object', () => {
const result = f.getPluginCommands({ 'plugin.commands': [{ a: 'foo' }] })
expect(result).toEqual([{ a: 'foo' }])
})
*/
describe(`${PLUGIN_NAME}`, () => {
describe(`${FILENAME}`, () => {
//functions go here using jfunc command
/*
* constructor()
*/
describe('constructor()' /* function */, () => {
test('should create fetch with default response', () => {
const f = new FetchMock()
expect(f.responses.length).toEqual(1)
expect(f.responses[0].response).toMatch(/did not match/)
})
test('should create fetch with supplied response', () => {
const f = new FetchMock([{ match: { url: 'foo', optionsBody: 'bar' }, response: 'baz' }])
expect(f.responses.length).toEqual(2)
expect(f.responses[0].response).toMatch(/baz/)
expect(f.responses[1].response).toMatch(/did not match/)
})
test('should return default response with no match', async () => {
const f = new FetchMock()
const result = await f.fetch()
expect(result).toMatch(/did not match/)
})
test('should return match if both url and body match', async () => {
const f = new FetchMock([{ match: { url: 'foo', optionsBody: 'bar' }, response: 'baz' }])
const result = await f.fetch('http://foo', { body: 'does it include bar' })
expect(result).toMatch(/baz/)
})
test('should return match if both url and body match (case insensitive)', async () => {
const f = new FetchMock([{ match: { url: 'FOO', optionsBody: 'Bar' }, response: 'baz' }])
const result = await f.fetch('http://foo', { body: 'does it include bar' })
expect(result).toMatch(/baz/)
})
test('should return default if url matches but not body', async () => {
const f = new FetchMock([{ match: { url: 'foo', optionsBody: 'bar' }, response: 'baz' }])
const result = await f.fetch('http://foo', { body: 'does it include xxx' })
expect(result).toMatch(/did not match/)
})
test('should return default if body matches but not url', async () => {
const f = new FetchMock([{ match: { url: 'fun', optionsBody: 'bar' }, response: 'baz' }])
const result = await f.fetch('http://foo', { body: 'does it include bar' })
expect(result).toMatch(/did not match/)
})
test('should return match if url matches and body is blank', async () => {
const f = new FetchMock([{ match: { url: 'foo', optionsBody: '' }, response: 'baz' }])
const result = await f.fetch('http://foo', { body: 'does it include bar' })
expect(result).toMatch(/baz/)
})
test('should return match if url matches and body is not passed', async () => {
const f = new FetchMock([{ match: { url: 'foo' }, response: 'baz' }])
const result = await f.fetch('http://foo', { body: 'does it include bar' })
expect(result).toMatch(/baz/)
})
})
})
})
================================================
FILE: __mocks__/index.js
================================================
export { Calendar } from './Calendar.mock'
export { default as Clipboard } from './Clipboard.mock'
export { default as CommandBar } from './CommandBar.mock'
export { DataStore } from './DataStore.mock'
export { Editor } from './Editor.mock'
export { NotePlan } from './NotePlan.mock'
export { Note } from './Note.mock'
export { Paragraph } from './Paragraph.mock'
export { CalendarItem } from './CalendarItem.mock'
export { Backlink } from './Backlink.mock'
export { Range } from './Range.mock'
export { PluginObject } from './PluginObject.mock'
export { PluginCommandObject } from './PluginCommandObject.mock'
export { simpleFormatter, mockWasCalledWithString, loadFactoryFile } from './jestHelpers'
================================================
FILE: __mocks__/jestHelpers.js
================================================
// @flow
import path from 'path'
import { existsSync, promises as fs } from 'fs'
/**
* Check if a spy was called (at any point) with a string parameter matching the given string/regex
* Mostly used for checking a console.log call when you don't know when in the sequence that log may have been called
* Assumes that the parameter to the mock was a string (e.g. console.log(xxx))
* @param { JestSpyType } spy
* @param {regexp|string} testStrRegex - a string or regex to match the spy call's arguments
* @returns {boolean} was called or not
* @example usage:
const spy = jest.spyOn(console, 'log')
const result = mainFile.getConfig()
expect(mockWasCalledWithString(spy, /config was empty/)).toBe(true)
*/
export const mockWasCalledWithString = (spy: any, testStrRegex: RegExp | string): boolean => {
let found: any = []
const regex = typeof testStrRegex === 'string' ? new RegExp(testStrRegex) : testStrRegex
if (spy?.mock?.calls?.length) {
const calls = spy.mock.calls
found = calls.filter((call) => call.find((arg) => regex.test(arg)))
}
return found.length > 0
}
/**
* Minimize console.log output during test runs
* (way to much wasted whitespace in the jest default output)
* per: https://stackoverflow.com/questions/51555568/remove-logging-the-origin-line-in-jest/57443150#57443150
* @param {*} type
* @param {*} message
* @returns
*/
export function simpleFormatter(_type: string, message: string): string {
const TITLE_INDENT = ' '
const CONSOLE_INDENT = `${TITLE_INDENT} `
return message
.split(/\n/)
.map((line) => CONSOLE_INDENT + line)
.join('\n')
}
/**
* Load a factory file for use in Jest debugging
* Factory files are in the /factories subfolder of the calling function
* @param {string} factoryName
* @returns
*/
export async function loadFactoryFile(factoryName: string = ''): Promise<string> {
const factoryFilename = path.join(__dirname, 'factories', factoryName)
if (existsSync(factoryFilename)) {
return await fs.readFile(factoryFilename, 'utf-8')
}
return `FACTORY_NOT_FOUND - ${factoryFilename}`
}
================================================
FILE: __mocks__/support/pluginSample.json
================================================
{
"noteplan.minAppVersion": "3.0.21",
"macOS.minVersion": "10.15.7",
"iOS.minVersion": "14",
"plugin.id": "test.plugin.id",
"plugin.name": "Move selected note to another folder",
"plugin.description": "Move a note from the current folder into another.",
"plugin.author": "Eduard Metzger",
"plugin.url": "https://…",
"plugin.version": "0.0.2",
"plugin.dependencies": [],
"plugin.script": "script.js",
"plugin.commands": [
{
"name": "move",
"alias": [
"m",
"mv"
],
"description": "Moves note to another folder",
"jsFunction": "moveNote",
"arguments": [
"note title",
"heading"
]
},
{
"name": "command1Name",
"jsFunction": "command1",
"description": "",
"isPreset": true
},
{
"name": "command2Name",
"jsFunction": "command2",
"description": "",
"data": "foos",
"isPreset": true
},
{
"name": "run preset sample",
"jsFunction": "runPreset01",
"description": "run preset desc",
"isPreset": true
}
],
"plugin.settings": [
{
"type": "hidden",
"key": "hiddenKey",
"title": "String Setting",
"description": "An example of a string setting.",
"default": "hello world"
},
{
"type": "string",
"key": "someString",
"title": "String Setting",
"description": "An example of a string setting.",
"default": "What a value",
"required": true
},
{
"type": "number",
"key": "someNumber",
"title": "Number Setting",
"description": "An example of a number setting."
},
{
"type": "separator"
},
{
"type": "heading",
"title": "Heading"
},
{
"type": "bool",
"key": "someBool",
"title": "Boolean Setting",
"description": "An example of a boolean setting."
},
{
"type": "date",
"key": "someDate",
"title": "Date Setting",
"description": "An example of a date setting.",
"format": "YYYY-MM-dd"
},
{
"type": "separator"
},
{
"type": "heading",
"title": "Heading"
},
{
"type": "[string]",
"key": "someStringArray",
"title": "String Array Setting",
"description": "An example of a string array setting.",
"default": [
"one",
"two",
"three"
]
},
{
"type": "separator"
},
{
"type": "string",
"key": "someStringEnum",
"title": "String Setting With Choices",
"description": "An example of a string setting with choices.",
"choices": [
"one",
"two",
"three"
]
},
{
"type": "json",
"key": "shortcutExpenses",
"title": "Shortcut Expenses",
"description": "Just some example shortcut expenses - please adapt to your needs. You can also add an amount, then you can insert the shortcut without any question.",
"default": "[\n\t{\n\thello: \"world\"\n\t}\n]",
"required": true,
"boxHeight": 300
}
]
}
================================================
FILE: aaronpoweruser.ReadwiseUnofficial/CHANGELOG.md
================================================
# aaronpoweruser.ReadwiseUnofficial Changelog
## About aaronpoweruser.ReadwiseUnofficial Plugin
See Plugin [README](https://github.com/NotePlan/plugins/blob/main/aaronpoweruser.ReadwiseUnofficial/README.md) for details on available commands and use case.
## [1.1.4] Readwise 2024-05-28 (@aaronpoweruser)
- Add a setting for paragraph type.
## [1.1.3] Readwise 2024-05-22 (@aaronpoweruser)
- Ignore leading escaped quotes
- Code cleanup
- Remove illegal characters in daily review titles.
- Switch book highlight links to Kindle web reader as deep links are [deprecated](https://help.readwise.io/article/40-can-i-jump-to-a-highlight-directly-in-the-kindle-app).
## [1.1.2] Readwise 2024-04-22 (aaronpoweruser)
- Fix Sync log dates not respecting time zones.
## [1.1.1] Readwise 2024-04-20 (aaronpoweruser)
- Fix internal links in daily reviews.
## [1.1.0] Readwise 2024-04-19 (aaronpoweruser)
- Refactor
- Added a sync log see [README](https://github.com/NotePlan/plugins/blob/main/aaronpoweruser.ReadwiseUnofficial/README.md).
- Fixed alert dialog count being wrong sometimes.
- Fixed illegal characters and newlines in titles.
- Add support for daily reviews via template.
## [1.0.1] Readwise 2023-08-20 (aaronpoweruser)
- [#452] Fix pagination issue for users with large libraries thanks @TobiasMende
## [1.0.0] Readwise 2023-02-07 (aaronpoweruser)
- public release
## [0.1.8] Readwise 2023-02-07 (aaronpoweruser)
- Show user notes
- Add support for highlight headings
## [0.1.7] Readwise 2023-01-25 (aaronpoweruser)
- Settings description clean up
- Fix null highlight links
- Fix unneeded long titles
## [0.1.6] - 2023-01-12 (aaronpoweruser)
- Fix access token dialog
- Use readable titles to make linking to notes easier
- Add long title field
- Allow Supplemental(readwise generated) notes to be grouped with main notes
- Change highlights from list to quotes
## [0.1.5] - 2023-01-12 (aaronpoweruser)
- Fix metadata not being created
- Fixed double metadata when not using group by type
- Allow empty tag prefixes
Known issues:
- "Don’t Set Goals… Do This Instead" causes a new note to be created
- Using heading as metadata does not support tag updates
- Images are not handled gracefully
## [0.1.4] - 2023-01-09 (aaronpoweruser)
- Clean up new lines in notes
- add success message
- Fix rebuild
## [0.1.3] - 2023-01-03 (aaronpoweruser)
- Add front matter support
- Add optional tag prefix
- Clean up settings
- Code cleanup
## [0.1.2] - 2023-01-02 (aaronpoweruser)
- Add missing highlight header
- Increase timeout when checking for new note
## [0.1.1] - 2022-12-29 (aaronpoweruser)
- Enabled only syncing new highlights (added a hidden setting to override)
- Clean up plugin settings description
- Only add metadata once (need to handle tag updates)
- Add link to kindle for books (fixes links being null)
## [0.1.0] - 2022-12-28 (aaronpoweruser)
First release
## Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Plugin Versioning Uses Semver
All NotePlan plugins follow `semver` versioning. For details, please refer to [semver website](https://semver.org/)
================================================
FILE: aaronpoweruser.ReadwiseUnofficial/README.md
================================================
# Readwise Unofficial Noteplan Plugin
## About This Plugin
A sync engine for readwise
### Features
- Daily review's via templates
- Full highlight download
- Highlight updates
- Tags
- Group be content type
- Set download folder
- Front matter support
- NotePlan tags

### Todo
- Index note
- Image support (needs Noteplan API update)
### Known issues
- Using heading as metadata does not support tag updates
- Images are not handled gracefully
## Commands
### //Readwise sync
Downloads new highlights since last sync
### //Readwise rebuild
**Only needs to be used if highlights are deleted**
Downloads all highlights (if they already exist they will be duplicated)
### //Readwise Daily review
Downloads daily reviews (does not mark as read) with links to note and author.
Can be added to a note with templates using
``` javascript
<%- await DataStore.invokePluginCommandByName("Readwise Daily Review","aaronpoweruser.ReadwiseUnofficial") %>
```

## Settings
These commands require configuration.
### Access Token (*required*)
1. Go to the [Readwise](readwise.io/access_token).
2. Copy and paste that token to plugin settings.
### Note Folder
This setting specifies the folder to which this plugin will create a note.
### Tag Prefix (optional)
This setting specifies the prefix of the tag imported from Readwise.
### Metadata format
FrontMatter will store the metadata in the front matter of the note. Heading will store the metadata in a heading at the start of the note.
### Group by type
Group all highlights in under the readwise folder or having them separated by content type ie readwise/books, readwise/articles.
### Group supplemental highlights separately
**Ignored if group by type is disabled**
Group all highlights together or keep supplemental (readwise generated) highlights separate.
### Sync log
A note that has all highlights synced during a sync with dated references.

## Latest Updates
See [CHANGELOG](https://github.com/NotePlan/plugins/blob/main/aaronpoweruser.ReadwiseUnofficial/CHANGELOG.md) for latest updates/changes to this plugin.
================================================
FILE: aaronpoweruser.ReadwiseUnofficial/plugin.json
================================================
{
"COMMENT": "Details on these fields: https://help.noteplan.co/article/67-create-command-bar-plugins",
"macOS.minVersion": "10.13.0",
"noteplan.minAppVersion": "3.4.0",
"plugin.id": "aaronpoweruser.ReadwiseUnofficial",
"plugin.name": "📚 Readwise Unofficial",
"plugin.version": "1.1.4",
"plugin.lastUpdateInfo": "Added paragraph type setting. Fixed links to highlights on web.\nFix sync log dates being wrong.\nAdd a sync log note. \nAdd support for daily reviews via templates",
"plugin.description": "A sync plugin for Readwise",
"plugin.author": "aaronpoweruser",
"plugin.dependencies": [],
"plugin.script": "script.js",
"plugin.url": "https://github.com/NotePlan/plugins/blob/main/aaronpoweruser.ReadwiseUnofficial/README.md",
"plugin.changelog": "https://github.com/NotePlan/plugins/blob/main/aaronpoweruser.ReadwiseUnofficial/CHANGELOG.md",
"plugin.commands": [
{
"name": "Readwise sync",
"description": "Sync Readwise highlights",
"jsFunction": "readwiseSync"
},
{
"name": "Readwise Daily Review",
"description": "Sync Readwise highlights",
"jsFunction": "readwiseDailyReview"
},
{
"name": "Readwise rebuild",
"description": "Force sync Readwise highlights",
"jsFunction": "readwiseRebuild",
"alias": [
"rwfs"
],
"hidden": false
},
{
"name": "onOpen",
"description": "DO NOT EDIT THIS COMMAND/TRIGGER",
"jsFunction": "onOpen",
"hidden": true
},
{
"name": "onEditorWillSave",
"description": "DO NOT EDIT THIS COMMAND/TRIGGER",
"jsFunction": "onEditorWillSave",
"hidden": true
}
],
"plugin.settings": [
{
"COMMENT": "Plugin settings documentation: https://help.noteplan.co/article/123-plugin-configuration",
"type": "heading",
"title": "Readwise Unofficial Settings"
},
{
"title": "Access token",
"key": "accessToken",
"required": true,
"type": "string",
"description": "Enter your readwise access token from readwise.io/access_token",
"default": ""
},
{
"title": "Note folder",
"key": "baseFolder",
"type": "string",
"description": "The folder to store all highlights in",
"default": "Readwise"
},
{
"title": "Tag Prefix",
"key": "tagPrefix",
"type": "string",
"description": "Prefix for Readwise tags",
"default": "Readwise"
},
{
"title": "Metadata format",
"key": "useFrontMatter",
"type": "string",
"description": "FrontMatter will store the metadata in the front matter of the note. Heading will store the metadata in a heading in the note.",
"choices": [
"FrontMatter",
"Heading"
],
"default": "FrontMatter",
"required": true
},
{
"title": "Highlight paragraph type",
"key": "paragraphType",
"type": "string",
"description:": "The type of paragraph to use for the highlight. quote: Use a blockquote for the highlight, list: Use a bullet for the highlight",
"choices": [
"quote",
"list"
],
"default": "quote",
"required": true
},
{
"title": "Group by type",
"key": "groupByType",
"type": "bool",
"description": "Group all highlights in under the readwise folder or having them separated by content type ie readwise/books, readwise/articles.",
"default": true,
"required": true
},
{
"title": "Group supplemental highlights separately",
"key": "ignoreSupplementals",
"type": "bool",
"description": "** Ignored if group by type is disabled **\nGroup all highlights together or keep supplemental (readwise generated) highlights seperate.",
"default": true,
"required": true
},
{
"title": "Add a link to highlight",
"key": "showLinkToHighlight",
"type": "bool",
"description": "Include a link to the source of the highlight in the note.",
"default": false,
"required": true
},
{
"title": "Create a sync log",
"key": "writeSyncLog",
"type": "bool",
"description": "Include a log of the synced highlights.",
"default": false,
"required": true
},
{
"COMMENT": "Enable to force a resync of all highlights",
"title": "Force sync all highlights",
"key": "forceSync",
"type": "hidden",
"description": "Debug setting to always recreate all highlights",
"default": "false",
"required": false
},
{
"NOTE": "DO NOT CHANGE THE FOLLOWING SETTINGS; ADD YOUR SETTINGS ABOVE ^^^",
"type": "separator"
},
{
"type": "heading",
"title": "Debugging"
},
{
"key": "_logLevel",
"type": "string",
"title": "Log Level",
"choices": [
"DEBUG",
"INFO",
"WARN",
"ERROR",
"none"
],
"description": "Set how much logging output will be displayed when executing Readwise Unofficial commands in NotePlan Plugin Console Logs (NotePlan -> Help -> Plugin Console)\n\n - DEBUG: Show All Logs\n - INFO: Only Show Info, Warnings, and Errors\n - WARN: Only Show Errors or Warnings\n - ERROR: Only Show Errors\n - none: Don't show any logs",
"default": "INFO",
"required": true
}
]
}
================================================
FILE: aaronpoweruser.ReadwiseUnofficial/src/NPReadwise.js
================================================
// @flow
import { showMessage } from '../../helpers/userInput'
import pluginJson from '../plugin.json'
import { checkAccessToken, escapeTwitterHandle, getParagraphTypeChar, removeInvalidChars, removeNewlines } from './NPReadwiseHelpers'
import { parseHighlightsAndWriteToNote } from './NPReadwiseNotes'
import { startReadwiseSyncLog, finishReadwiseSyncLog } from './NPReadwiseSyncLog'
import { log, logDebug, logError } from '@helpers/dev'
const LAST_SYNC_TIME = 'last_sync_time'
/**
* Syncs new readwise highlights
*/
export async function readwiseSync(): Promise<void> {
checkAccessToken()
const response = await getReadwise(false)
await handleReadwiseSync(response)
}
/**
* Rebuilds all readwise highlights
*/
export async function readwiseRebuild(): Promise<void> {
checkAccessToken()
const response = await getReadwise(true)
await handleReadwiseSync(response)
}
/**
* Gets the daily review highlights from Readwise
* @returns {string} - the highlights as a string
*/
export async function readwiseDailyReview(): Promise<string> {
checkAccessToken()
return await getReadwiseDailyReview()
}
async function handleReadwiseSync(response: any): Promise<void> {
let downloadHighlightCount = 0,
updatedSourceCount = 0
await startReadwiseSyncLog()
response.forEach((highlightSource) => {
updatedSourceCount++
downloadHighlightCount += highlightSource.highlights.length
parseHighlightsAndWriteToNote(highlightSource)
})
log(pluginJson, `Downloaded ${downloadHighlightCount} highlights from Readwise. Updated ${updatedSourceCount} notes.`)
await showMessage(`Downloaded ${downloadHighlightCount} highlights from Readwise. Updated ${updatedSourceCount} notes.`)
await finishReadwiseSyncLog(downloadHighlightCount, updatedSourceCount)
}
/**
* Gets the readwise data from the API
* @param {boolean} force - if true, will ignore the last sync time and get all data
* @returns {*} - the readwise data as a JSON object
* @see https://readwise.io/api_deets
*/
async function getReadwise(force: boolean): Promise<any> {
const accessToken = DataStore.settings.accessToken ?? ''
let lastFetchTime = DataStore.loadData(LAST_SYNC_TIME, true) ?? ''
if (DataStore.settings.forceSync === 'true' || force === true) {
lastFetchTime = ''
}
log(pluginJson, `last fetch time is : ${lastFetchTime}`)
logDebug(pluginJson, `base folder is : ${DataStore.settings.baseFolder}`)
return await doReadWiseFetch(accessToken, lastFetchTime, 0, '')
}
/*
* Recursively fetches readwise data
* @param {string} accessToken - the readwise access token
* @param {string} lastFetchTime - the last time the data was fetched
* @param {int} downloadCount - the number of highlights downloaded
* @param {string} nextPageCursor - the cursor for the next page of data
* @returns {*} - the readwise data as a JSON object
* @see https://readwise.io/api_deets
*/
async function doReadWiseFetch(accessToken: string, lastFetchTime: string, downloadCount: number, nextPageCursor: string): Promise<any> {
try {
const url = `https://readwise.io/api/v2/export/?updatedAfter=${lastFetchTime}&pageCursor=${nextPageCursor}`
const options = {
method: 'GET',
headers: {
Authorization: `token ${accessToken}`,
},
}
const response = await fetch(url, options)
DataStore.saveData(new Date().toISOString(), LAST_SYNC_TIME, true)
const parsedJson = JSON.parse(response)
const pageCursor = parsedJson.nextPageCursor
logDebug(pluginJson, `page cursor is : ${pageCursor}`)
let data: any = []
const count = parsedJson.count + downloadCount
if (pageCursor !== null && pageCursor !== '') {
data = await doReadWiseFetch(accessToken, lastFetchTime, count, pageCursor)
}
const result = parsedJson.results.concat(data)
// DataStore.saveData(JSON.stringify(result), 'readwise_data.json', true)
return result
} catch (error) {
logError(pluginJson, error)
}
}
/*
* Gets the users Daily review from the readwise api
* @returns {string} - the daily review highlights
*/
async function getReadwiseDailyReview(): Promise<string> {
const accessToken = DataStore.settings.accessToken ?? ''
let highlightString = ''
try {
const url = `https://readwise.io/api/v2/review/`
const options = {
method: 'GET',
headers: {
Authorization: `token ${accessToken}`,
},
}
const response = await fetch(url, options)
const highlights = JSON.parse(response).highlights
await highlights.map((highlight) => {
const formattedHighlight = `${removeNewlines(highlight.text)} [ [[${removeInvalidChars(highlight.title)}]], [[${escapeTwitterHandle(highlight.author)}]] ]`
highlightString += `${getParagraphTypeChar()} ${formattedHighlight}\n`
})
if (highlightString.endsWith('\n')) {
// remove the last newline
highlightString = highlightString.substring(0, highlightString.length - 1)
}
logDebug(pluginJson, `Daily review highlights are\n\n ${highlightString}`)
} catch (error) {
logError(pluginJson, error)
}
return highlightString
}
================================================
FILE: aaronpoweruser.ReadwiseUnofficial/src/NPReadwiseHelpers.js
================================================
// @flow
import { showMessage } from '../../helpers/userInput'
import pluginJson from '../plugin.json'
import { logDebug } from '@helpers/dev'
const READWISE_API_KEY_LENGTH = 50
/**
* Checks if the readwise access token is valid
*/
export async function checkAccessToken(): Promise<void> {
const accessToken = DataStore.settings.accessToken ?? ''
logDebug(pluginJson, `access token is : ${accessToken}`)
if (accessToken === '') {
await showMessage('No access token found. Please add your Readwise access token in the plugin settings.')
} else if (accessToken.length !== READWISE_API_KEY_LENGTH) {
await showMessage('Invalid access token. Please check your Readwise access token in the plugin settings.')
}
}
/**
* @param {*} source Readwise data as a JSON object
* @returns Note title
*/
export function buildReadwiseNoteTitle(source: any): string {
if (source.readable_title !== '') {
return removeInvalidChars(source.readable_title)
} else if (source.title !== '') {
return removeInvalidChars(source.title)
} else {
return removeInvalidChars(source.author)
}
}
/**
* Sanitize the string by removing invalid characters
* @param {string} string - the string to sanitize
* @returns {string} - the sanitized string
*/
export function removeInvalidChars(string: string): string {
return removeNewlines(
string
.replace(/^"/, '') // remove leading double quote
.trim(),
)
}
/**
* Parse readwise data and generate front matter
* @param {*} source - the readwise data as a JSON object
* @returns
*/
export function buildReadwiseFrontMatter(source: any): any {
const frontMatter = {}
// $FlowIgnore[prop-missing] - intentionally setting properties dynamically as frontMatter keys are dynamic
frontMatter.author = `[[${escapeTwitterHandle(source.author)}]]`
if (source.readable_title.toLowerCase().trim() !== source.title.toLowerCase().trim()) {
// $FlowIgnore[prop-missing] - intentionally setting properties dynamically as frontMatter keys are dynamic
frontMatter.long_title = removeInvalidChars(source.title)
}
if (source.book_tags !== null && source.book_tags.length > 0) {
// $FlowIgnore[prop-missing] - intentionally setting properties dynamically as frontMatter keys are dynamic
frontMatter.tags = source.book_tags.map((tag) => `${formatTag(tag.name)}`).join(', ')
}
if (source.unique_url !== null) {
// $FlowIgnore[prop-missing] - we are intentionally setting properties dynamically
frontMatter.url = source.unique_url
}
return frontMatter
}
/**
* Creates the metadata heading for the note
* @param {*} source - the readwise data as a JSON object
* @returns {string} - the formatted heading
*/
export function buildReadwiseMetadataHeading(source: any): string {
let metadata = `author: [[${escapeTwitterHandle(source.author)}]]\n`
if (source.book_tags !== null && source.book_tags.length > 0) {
metadata += `tags: ${source.book_tags.map((tag) => `${formatTag(tag.name)}`).join(', ')}\n`
}
if (source.unique_url !== null) {
metadata += `url: ${source.unique_url}`
}
if (source.readable_title.toLowerCase().trim() !== source.title.toLowerCase().trim()) {
metadata += `long_title: ${removeInvalidChars(source.title)}`
}
return metadata
}
/**
* Formats the note tag using the prefix from plugin settings
* @param {string} tag - the tag to format
* @returns {string} - the formatted tag
*/
function formatTag(tag: string): string {
const prefix = DataStore.settings.tagPrefix ?? ''
if (prefix === '') {
return `#${tag}`
} else {
return `#${prefix}/${tag}`
}
}
/**
* Remove all newline characters from a string
* @param {string} text - the text to remove newline characters from
* @returns {string} - the text with newline characters removed
*/
export function removeNewlines(text: string): string {
return text.replaceAll(/\n/g, ' ')
}
/**
* Escapes Twitter handles by adding 'Twitter/' before the '@' symbol
* to avoid creating a mention in Noteplan
* and removing 'on Twitter' from the handle
* @param {string} handle - the Twitter handle to escape
* @returns {string} - the escaped Twitter handle
*/
export function escapeTwitterHandle(handle: string): string {
if (handle.startsWith('@') && handle.endsWith(' on Twitter')) {
return handle.replace('@', 'Twitter/@').replace(' on Twitter', '')
}
return handle
}
/**
* Gets the date in iso format with the local timezone
* @returns {string} - the local date
*/
export function getLocalDate(): string {
const local_dateTime_in_mills = new Date().setHours(new Date().getHours() - new Date().getTimezoneOffset() / 60)
const local_dateTime = new Date(local_dateTime_in_mills).toISOString()
return local_dateTime.split('T')[0]
}
/**
* Get the paragraph type character based on settings
* @returns {string} - the paragraph type character
*/
export function getParagraphTypeChar(): string {
const paragraphType = DataStore.settings.paragraphType ?? 'quote'
let paragraphChar = '>'
if (paragraphType === 'quote') {
paragraphChar = '>'
} else if (paragraphType === 'list') {
paragraphChar = '-'
}
return paragraphChar
}
================================================
FILE: aaronpoweruser.ReadwiseUnofficial/src/NPReadwiseNotes.js
================================================
// @flow
import pluginJson from '../plugin.json'
import { updateFrontMatterVars } from '../../helpers/NPFrontMatter'
import { findEndOfActivePartOfNote } from '../../helpers/paragraph'
import { buildReadwiseFrontMatter, buildReadwiseMetadataHeading, buildReadwiseNoteTitle, removeNewlines } from './NPReadwiseHelpers'
import { writeReadwiseSyncLogLine } from './NPReadwiseSyncLog'
import { logDebug, logError } from '@helpers/dev'
import { getOrMakeRegularNoteInFolder } from '@helpers/NPnote'
/**
* Gets or creates the note for the readwise data
* @param {string} title - the title of the note
* @param {string} category - the category of the note
* @returns {TNote} - the note
*/
async function getOrCreateReadwiseNote(title: string, category: string): Promise<?TNote> {
const rootFolder = DataStore.settings.baseFolder ?? 'Readwise'
let baseFolder = rootFolder
let outputNote: ?TNote
if (DataStore.settings.groupByType === true) {
// Note: supplementals are not guaranteed to have user generated highlights
if (DataStore.settings.ignoreSupplementals === true && category === 'supplementals') {
baseFolder = `${rootFolder}/books`
} else {
baseFolder = `${rootFolder}/${category}`
}
}
try {
outputNote = await getOrMakeRegularNoteInFolder(title, baseFolder, '')
} catch (error) {
logError(pluginJson, error)
}
return outputNote
}
/**
* Parses the readwise data and writes it to a note
* @param {*} source - the readwise data as a JSON object
*/
export async function parseHighlightsAndWriteToNote(highlightSource: any): Promise<any> {
try {
const noteTitle: string = buildReadwiseNoteTitle(highlightSource)
const outputNote: ?TNote = await getOrCreateReadwiseNote(noteTitle, highlightSource.category)
const useFrontMatter = DataStore.settings.useFrontMatter === 'FrontMatter'
if (outputNote) {
if (!useFrontMatter) {
//TODO: Support updating metadata (tags)
if (!outputNote?.content?.includes('## Metadata')) {
outputNote?.addParagraphBelowHeadingTitle(buildReadwiseMetadataHeading(highlightSource), 'text', 'Metadata', true, true)
}
} else {
updateFrontMatterVars(outputNote, buildReadwiseFrontMatter(highlightSource))
}
if (!outputNote?.content?.includes('# Highlights')) {
outputNote.insertHeading('Highlights', findEndOfActivePartOfNote(outputNote) + 1, 1)
}
}
if (outputNote) {
await writeReadwiseSyncLogLine(noteTitle, highlightSource.highlights.length)
await highlightSource.highlights.map((highlight) => appendHighlightToNote(outputNote, highlight, highlightSource.source, highlightSource.asin))
}
} catch (error) {
logError(pluginJson, error)
}
}
/**
* Appends the highlight with a link to the note
* @param {TNote} outputNote - the note to append to
* @param {*} highlight - the readwise highlight as a JSON object
* @param {string} category - the source of the highlight
* @param {string} asin - the asin of the book
*/
function appendHighlightToNote(outputNote: TNote, highlight: any, category: string, asin: string): void {
let linkToHighlightOnWeb = ''
let userNote = ''
if (highlight.tags !== null && highlight.tags !== '') {
for (const tag of highlight.tags) {
if (tag.name !== null && tag.name !== '' && tag.name.toLowerCase().startsWith('h') && tag.name.length === 2) {
const headingLevel = parseInt(tag.name.substring(1)) + 1
if (headingLevel <= 8) {
outputNote.insertHeading(removeNewlines(highlight.text), findEndOfActivePartOfNote(outputNote) + 1, headingLevel)
}
}
}
}
if (highlight.note !== null && highlight.note !== '') {
userNote = `(${highlight.note})`
}
if (DataStore.settings.showLinkToHighlight === true) {
if (category === 'supplemental') {
linkToHighlightOnWeb = ` [View highlight](${highlight.readwise_url})`
} else if (asin !== null && highlight.location !== null) {
linkToHighlightOnWeb = ` [Location ${highlight.location}](https://read.amazon.com/?asin=${asin})`
} else if (highlight.url !== null) {
linkToHighlightOnWeb = ` [View highlight](${highlight.url})`
}
}
const paragraphType = DataStore.settings.paragraphType ?? 'quote'
outputNote.appendParagraph(removeNewlines(highlight.text) + userNote + linkToHighlightOnWeb, paragraphType)
}
================================================
FILE: aaronpoweruser.ReadwiseUnofficial/src/NPReadwiseSyncLog.js
================================================
// @flow
import { getLocalDate } from './NPReadwiseHelpers'
import { getOrMakeRegularNoteInFolder } from '@helpers/NPnote'
const SYNC_LOG_TOKEN = 'readWiseToken'
const SYNC_NOTE_TITLE = 'Readwise Syncs'
export async function startReadwiseSyncLog(): Promise<void> {
if (DataStore.settings.writeSyncLog === true) {
const outputNote = await getOrMakeRegularNoteInFolder(SYNC_NOTE_TITLE, DataStore.settings.baseFolder)
await outputNote?.insertHeading(SYNC_LOG_TOKEN, 1, 2)
}
}
export async function writeReadwiseSyncLogLine(title: string, count: number): Promise<void> {
if (DataStore.settings.writeSyncLog === true) {
const outputNote = await getOrMakeRegularNoteInFolder(SYNC_NOTE_TITLE, DataStore.settings.baseFolder)
await outputNote?.addParagraphBelowHeadingTitle(`${count} highlight${count !== 1 ? 's' : ''} from [[${title}]]`, 'list', SYNC_LOG_TOKEN, true, false)
}
}
export async function finishReadwiseSyncLog(downloadHighlightCount: number, updatedSourceCount: number): Promise<void> {
if (DataStore.settings.writeSyncLog === true) {
const outputNote = await getOrMakeRegularNoteInFolder(SYNC_NOTE_TITLE, DataStore.settings.baseFolder, '')
const dateString =
`[[${getLocalDate()}]] ${new Date().toLocaleTimeString([], { timeStyle: 'short' })} ` +
`— synced ${downloadHighlightCount} highlight${downloadHighlightCount !== 1 ? 's' : ''} from ${updatedSourceCount} documents.`
if (outputNote) {
outputNote.content = outputNote.content?.replace(SYNC_LOG_TOKEN, dateString)
}
}
}
================================================
FILE: aaronpoweruser.ReadwiseUnofficial/src/NPTriggers-Hooks.js
================================================
/* eslint-disable require-await */
// @flow
import pluginJson from '../plugin.json' // gives you access to the contents of plugin.json
import { log, logError, logDebug, timer, clo, JSP } from '@helpers/dev'
import { updateSettingData, pluginUpdated } from '@helpers/NPConfiguration'
/**
* NOTEPLAN PER-NOTE TRIGGERS
*
* The following functions are called by NotePlan automatically
* if a note has a triggers: section in its frontmatter
* See the documentation: https://help.noteplan.co/article/173-plugin-note-triggers
*/
/**
* onOpen
* Plugin entrypoint for command: "/onOpen"
* Called when a note is opened and that note
* has a triggers: onOpen in its frontmatter
* @param {TNote} note - current note in Editor
*/
export async function onOpen(note: TNote): Promise<void> {
try {
logDebug(pluginJson, `onOpen running for note:"${String(note.filename)}"`)
// Try to guard against infinite loops of opens/refreshing
// You can delete this code if you are sure that your onOpen trigger will not cause an infinite loop
// But the safest thing to do is put your code inside the if loop below to ensure it runs no more than once every 15s
const now = new Date()
if (Editor?.note?.changedDate) {
const lastEdit = new Date(Editor?.note?.changedDate)
if (now - lastEdit > 15000) {
logDebug(pluginJson, `onOpen ${timer(lastEdit)} since last edit`)
// Put your code here or call a function that does the work
} else {
logDebug(pluginJson, `onOpen: Only ${timer(lastEdit)} since last edit (hasn't been 15s)`)
}
}
} catch (error) {
logError(pluginJson, `onOpen: ${JSP(error)}`)
}
}
/**
* onEditorWillSave
* Plugin entrypoint for command: "/onEditorWillSave"
*/
export async function onEditorWillSave() {
try {
logDebug(pluginJson, `onEditorWillSave running with note in Editor:"${String(Editor.filename)}"`)
// Put your code here or call a function that does the work
// Note: as stated in the documentation, if you want to change any content in the Editor
// before the file is written, you should NOT use the *note* variable here to change content
// Instead, use Editor.* commands (e.g. Editor.insertTextAtCursor()) or Editor.updateParagraphs()
} catch (error) {
logError(pluginJson, `onEditorWillSave: ${JSP(error)}`)
}
}
/*
* NOTEPLAN GLOBAL PLUGIN HOOKS
*
* The rest of these functions are called by NotePlan automatically under certain conditions
* It is unlikely you will need to edit/add anything below this line
*
*/
/**
* NotePlan calls this function after the plugin is installed or updated.
* The `updateSettingData` function looks through the new plugin settings in plugin.json and updates
* the user preferences to include any new fields
*/
export async function onUpdateOrInstall(): Promise<void> {
log(pluginJson, 'NPThemeChooser::onUpdateOrInstall running')
await updateSettingData(pluginJson)
}
/**
* NotePlan calls this function every time the plugin is run (any command in this plugin, including triggers)
* You should not need to edit this function. All work should be done in the commands themselves
*/
export function init(): void {
logDebug(pluginJson, `${pluginJson['plugin.id']} :: init running`)
// clo(DataStore.settings, `${pluginJson['plugin.id']} Plugin Settings`)
DataStore.installOrUpdatePluginsByID([pluginJson['plugin.id']], true, false, false).then((r) => pluginUpdated(pluginJson, r))
}
/**
* NotePlan calls this function settings are updated in the Preferences panel
* You should not need to edit this function
*/
export async function onSettingsUpdated(): Promise<void> {
logDebug(pluginJson, `${pluginJson['plugin.id']} :: onSettingsUpdated running`)
}
================================================
FILE: aaronpoweruser.ReadwiseUnofficial/src/index.js
================================================
// @flow
export { readwiseSync, readwiseRebuild, readwiseDailyReview } from './NPReadwise'
// FETCH mocking for offline testing
// If you want to use external server calls in your plugin, it can be useful to mock the server responses
// while you are developing the plugin. This allows you to test the plugin without having to
// have a server running or having to have a network connection (or wait/pay for the server calls)
// Comment the following import line out if you want to use live fetch/server endpoints (normal operation)
// Uncomment it for using server mocks (fake/canned responses) you define in support/fetchOverrides.js
// import './support/fetchOverrides'
/**
* Other imports/exports - you will normally not need to edit these
*/
// eslint-disable-next-line import/order
export { onUpdateOrInstall, init, onSettingsUpdated } from './NPTriggers-Hooks'
export { onOpen, onEditorWillSave } from './NPTriggers-Hooks'
================================================
FILE: babel.config.js
================================================
// babel.config.js
module.exports = (api) => {
const isTest = api.env('test')
return {
presets: [
'@babel/preset-flow',
'@babel/preset-react',
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
modules: isTest ? 'commonjs' : false, // Use CommonJS for Jest, ES modules for Rollup
},
],
],
}
}
================================================
FILE: buildDashboard.sh
================================================
#!/bin/sh
# Build Dashboard, and then run it. Needs to run from local GH root directory.
node ./jgclark.Dashboard/src/react/support/performRollup.node.js
npc plugin:dev jgclark.Dashboard -nc
echo "Running Dashboard..."
open "noteplan://x-callback-url/runPlugin?pluginID=jgclark.Dashboard&command=showDashboard"
================================================
FILE: buildShared.sh
================================================
#!/bin/sh
# Build Dashboard, and then run it. Needs to run from local GH root directory.
node ./np.Shared/src/react/support/performRollup.node.js
npc plugin:dev np.Shared -nc
================================================
FILE: codedungeon.Toolbox/CHANGELOG.md
================================================
# codedungeon.Toolbox Changelog
## About codedungeon.Toolbox Plugin
See Plugin [README](https://github.com/NotePlan/plugins/blob/main/codedungeon.Toolbox/README.md) for details on available commands and use case.
## [1.5.0] - 2022-07-29 (@mikeerickson)
- Added Plugin Console Setting
## [1.4.1] - 2022-07-07 (@mikeerickson)
- No changes, forcing version update
## [1.4.0] - 2022-07-07 (@mikeerickson)
- Removed dead code
- Fixed linting errors
## [1.3.1] - 2022-05-24 (@mikeerickson)
- Removed `convertToRtf` as it has been added to NotePlan
## [1.3.0] - 2021-09-26 (@mikeerickson)
- Modified `convertToHtml` to remove attributes by default
- Modified `convertSelectionToHtml` to remove attributes by default
## [1.2.1] - 2021-09-04 (@mikeerickson)
### Added
- Updated to use new NotePlan helper modules
## [1.1.0] - 2021-08-29 (@mikeerickson)
### Added
**convertToRtf** - Converts current note to RTF and copies to clipboard
**reorderList** - Reorders an `ordered list` (select list items which you wish to reorder)
## [1.0.1..1.0.2] - 2021-08-28 (@mikeerickson)
### Fixed
- fixed link to project readme
- https://github.com/NotePlan/plugins/blob/main/codedungeon.Toolbox/README.md
## [1.0.0] - 2021-08-26 (@mikeerickson)
### **Initial Release**
### Added
**covertToHtml** - Convert current note to html and copies to clipboard
**convertSelectionToHtml** - Convert current selection to html and copies to clipboard
## Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## Plugin Versioning Uses Semver
All NotePlan plugins follow `semver` versioning. For details, please refer to [semver website](https://semver.org/)
================================================
FILE: codedungeon.Toolbox/LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2021 Mike Erickson
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: codedungeon.Toolbox/README.md
================================================
# 🧩 Codedungeon Toolbox for Noteplan
## Overview
**Codedungeon Toolbox for NotePlan** is a plugin for NotePlan that provides a suite of utility methods, which can be used across all platforms (unless specifically noted in each command)
<h1 align="center">
<img src="docs/images/toolbox-logo.png" width="15%" height="15%" alt="codedungeon.Toolbox">
</h1>
## Commands
All commands can be invoked using the _NotePlan Command Bar_ (`Command-J` then ` / `) which NotePlan has reserved for plugin activation. Or by selecting `🧩 Codedungeon Toolbox` from the **Plugins** menu)
<h1 align="center">
<img src="docs/images/command-bar.png" alt="codedungeon.Toolbox">
</h1>
Once the command bar is displayed, you can continue typing any of the following commands to invoke the appropriate plugin command. In some case where specifically noted, you can alternately invoke the plugin command at the current insertion pointer within your document.
| Command | Available Inline | Description |
| ----------------------- | ---------------- | ------------------------------------------------------------------------------------------------- |
| convertToHtml | Yes [^1] | Converts the current note to HTML and places on clipboard |
| convertSelectionToHtml | No | Converts the current selection to HTML and places on clipboard |
| convertToRtf | Yes [^1] | Converts the current note to RTF and places on clipboard |
| reorderList | No | Reorders selected unordered list items (starting at **1** and increase each line at same level) |
[^1]: Can be triggered inline but contents will appear in converted HTML document
## License
Copyright © 2021-2022 Mike Erickson
Released under the MIT license
## Credits
**Codedugeon Toolbox for NotePlan** written by **Mike Erickson**
E-Mail: [codedungeon@gmail.com](mailto:codedungeon@gmail.com)
Support: [https://github.com/NotePlan/plugins/issues](https://github.com/NotePlan/plugins/issues)
Twitter: [@codedungeon](http://twitter.com/codedungeon)
Website: [codedungeon.io](http://codedungeon.io)
================================================
FILE: codedungeon.Toolbox/__tests__/convertSelectionToHtml.test.js
================================================
/* eslint-disable */
/*-------------------------------------------------------------------------------------------
* Copyright (c) 2021 Mike Erickson / Codedungeon. All rights reserved.
* Licensed under the MIT license. See LICENSE in the project root for license information.
* -----------------------------------------------------------------------------------------*/
import { DataStore, Editor, CommandBar, NotePlan } from '@mocks/index'
import CodedungeonToolbox from '../src/support/CodedungeonToolbox'
// Make DataStore and Editor available globally for the source code
global.DataStore = DataStore
global.Editor = Editor
global.CommandBar = CommandBar
global.NotePlan = NotePlan
let toolbox
beforeEach(() => {
toolbox = new CodedungeonToolbox()
})
test('codedungeon.Toolbox convertToHtml - headings', () => {
const markdown = `#Heading1\n##Heading2\n###Heading3\n####Heading4\n**TODO Items:**\n* Item 1\n*Item 2`
const html = toolbox.markdownToHtml(markdown, { removeAttributes: false })
expect(html).toContain('<h1 id="heading1">Heading1</h1>')
expect(html).toContain('<h2 id="heading2">Heading2</h2>')
expect(html).toContain('<h3 id="heading3">Heading3</h3>')
expect(html).toContain('<h4 id="heading4">Heading4</h4>')
expect(html).toContain('<h4 id="heading4">Heading4</h4>')
})
test('codedungeon.Toolbox convertToHtml - bold, italic', () => {
const markdown = `**Bold Item**
*Italic Item*
~~Striketrough~~`
const html = toolbox.markdownToHtml(markdown)
expect(html).toContain('<strong>Bold Item</strong>')
expect(html).toContain('<em>Italic Item</em>')
})
test('codedungeon.Toolbox convertToHtml - tasks', () => {
const markdown = `**TODO Items:**
* Item 1
* Item 2`
const html = toolbox.markdownToHtml(markdown)
expect(html).toContain('<strong>TODO Items:</strong>')
expect(html).toContain('<li>Item 1</li>')
expect(html).toContain('<li>Item 2</li>')
})
test('codedungeon.Toolbox convertToHtml - unordered lists', () => {
const toolbox = new CodedungeonToolbox()
const markdown = `**Lists:**
- Item 1
- Item 2
- Item 3`
const html = toolbox.markdownToHtml(markdown)
expect(html).toContain('<strong>Lists:</strong>')
expect(html).toContain('<ul>')
expect(html).toContain('<li>Item 1</li>')
expect(html).toContain('<li>Item 2</li>')
expect(html).toContain('</ul>')
})
================================================
FILE: codedungeon.Toolbox/__tests__/convertToHtml.test.js
================================================
/* eslint-disable */
/*-------------------------------------------------------------------------------------------
* Copyright (c) 2021 Mike Erickson / Codedungeon. All rights reserved.
* Licensed under the MIT license. See LICENSE in the project root for license information.
* -----------------------------------------------------------------------------------------*/
import { DataStore, Editor, CommandBar, NotePlan } from '@mocks/index'
import CodedungeonToolbox from '../src/support/CodedungeonToolbox'
// Make DataStore and Editor available globally for the source code
global.DataStore = DataStore
global.Editor = Editor
global.CommandBar = CommandBar
global.NotePlan = NotePlan
let toolbox
beforeEach(() => {
toolbox = new CodedungeonToolbox()
})
test('codedungeon.Toolbox convertToHtml - headings dont remove attributes', () => {
const markdown = `#Heading1\n##Heading2\n###Heading3\n####Heading4\n**TODO Items:**\n* Item 1\n*Item 2`
const html = toolbox.markdownToHtml(markdown, { removeAttributes: false })
expect(html).toContain('<h1 id="heading1">Heading1</h1>')
expect(html).toContain('<h2 id="heading2">Heading2</h2>')
expect(html).toContain('<h3 id="heading3">Heading3</h3>')
expect(html).toContain('<h4 id="heading4">Heading4</h4>')
expect(html).toContain('<h4 id="heading4">Heading4</h4>')
})
test('codedungeon.Toolbox convertToHtml - bold, italic', () => {
const markdown = `**Bold Item**
*Italic Item*
~~Striketrough~~`
const html = toolbox.markdownToHtml(markdown)
expect(html).toContain('<strong>Bold Item</strong>')
expect(html).toContain('<em>Italic Item</em>')
})
test('codedungeon.Toolbox convertToHtml - tasks', () => {
const markdown = `**TODO Items:**
* Item 1
* Item 2`
const html = toolbox.markdownToHtml(markdown)
expect(html).toContain('<strong>TODO Items:</strong>')
expect(html).toContain('<li>Item 1</li>')
expect(html).toContain('<li>Item 2</li>')
})
test('codedungeon.Toolbox convertToHtml - unordered lists', () => {
const markdown = `**Lists:**
- Item 1
- Item 2
- Item 3`
const html = toolbox.markdownToHtml(markdown)
expect(html).toContain('<strong>Lists:</strong>')
expect(html).toContain('<ul>')
expect(html).toContain('<li>Item 1</li>')
expect(html).toContain('<li>Item 2</li>')
expect(html).toContain('</ul>')
})
test('codedungeon.Toolbox convertToHtml - remove attributes', () => {
const markdown = `# Heading 1\n\n## Heading 2\n### Heading 3\n#### Heading 4`
const html = toolbox.markdownToHtml(markdown, { removeAttributes: true })
expect(html).toEqual('<h1>Heading 1</h1>\n<h2>Heading 2</h2>\n<h3>Heading 3</h3>\n<h4>Heading 4</h4>')
})
================================================
FILE: codedungeon.Toolbox/__tests__/reorderList.test.js
================================================
/* eslint-disable */
/*-------------------------------------------------------------------------------------------
* Copyright (c) 2021 Mike Erickson / Codedungeon. All rights reserved.
* Licensed under the MIT license. See LICENSE in the project root for license information.
* -----------------------------------------------------------------------------------------*/
import { DataStore, Editor, CommandBar, NotePlan } from '@mocks/index'
import CodedungeonToolbox from '../src/support/CodedungeonToolbox'
// Make DataStore and Editor available globally for the source code
global.DataStore = DataStore
global.Editor = Editor
global.CommandBar = CommandBar
global.NotePlan = NotePlan
let toolbox
beforeEach(() => {
toolbox = new CodedungeonToolbox()
})
test('codedungeon.Toolbox reorderList', async () => {
let list = ['1. item 1', '\t1. subitem 1', '\t2. subitem 1', '2. item 2', '3. item 3', '3. item 4', '4. item 5']
let result = await toolbox.reorderList(list)
expect(result[0]).toBe('1. item 1')
expect(result[3]).toBe('2. item 2')
expect(result[4]).toBe('3. item 3')
expect(result[5]).toBe('4. item 4')
expect(result[6]).toBe('5. item 5')
})
================================================
FILE: codedungeon.Toolbox/plugin.json
================================================
{
"macOS.minVersion": "10.13.0",
"noteplan.minAppVersion": "3.0.21",
"plugin.id": "codedungeon.Toolbox",
"plugin.name": "🧩 Codedungeon Toolbox",
"plugin.description": "General Purpose Utility Commands",
"plugin.author": "codedungeon",
"plugin.version": "1.5.0",
"plugin.dependencies": [],
"plugin.script": "script.js",
"plugin.url": "https://github.com/NotePlan/plugins/blob/main/codedungeon.Toolbox/README.md",
"plugin.commands": [
{
"name": "convertToHtml",
"description": "Convert current note to HTML",
"jsFunction": "convertToHtml"
},
{
"name": "convertSelectionToHtml",
"description": "Convert current selection to HTML",
"jsFunction": "convertSelectionToHtml"
},
{
"name": "reorderList",
"description": "Reorder current ordered list",
"jsFunction": "reorderList"
}
],
"plugin.settings": [
{
"type": "heading",
"title": "codedungeon.Toolbox Settings"
},
{
"key": "version",
"type": "hidden",
"title": "codedungeon.Toolbox Settings Version"
},
{
"type": "heading",
"title": "Debugging"
},
{
"key": "_logLevel",
"type": "string",
"title": "Log Level",
"choices": [
"DEBUG",
"INFO",
"WARN",
"ERROR",
"none"
],
"description": "Set how much loggin output will be displayed when executing codedungeon.Toolbox commands in NotePlan Plugin Console Logs (NotePlan -> Help -> Plugin Console)\n\n - DEBUG: Show All Logs\n - INFO: Only Show Info, Warnings, and Errors\n - WARN: Only Show Errors or Warnings\n - ERROR: Only Show Errors\n - none: Silence Logs",
"default": "INFO",
"required": true
}
]
}
================================================
FILE: codedungeon.Toolbox/src/convertSelectionToHtml.js
================================================
// @flow
/*-------------------------------------------------------------------------------------------
* Copyright (c) 2021 Mike Erickson / Codedungeon. All rights reserved.
* Licensed under the MIT license. See LICENSE in the project root for license information.
* -----------------------------------------------------------------------------------------*/
import { showMessage } from '../../helpers/userInput'
import CodedungeonToolbox from './support/CodedungeonToolbox'
export async function convertSelectionToHtml(): Promise<void> {
const toolbox = new CodedungeonToolbox()
const note = Editor.selectedLinesText.join('\n') || ''
const html = toolbox.markdownToHtml(note)
Clipboard.string = html
if (html.length > 0) {
await showMessage('Content Copied To Clipboard')
} else {
await showMessage('An error occured converting content to HTML')
}
}
================================================
FILE: codedungeon.Toolbox/src/convertToHtml.js
================================================
// @flow
/*-------------------------------------------------------------------------------------------
* Copyright (c) 2021-2022 Mike Erickson / Codedungeon. All rights reserved.
* Licensed under the MIT license. See LICENSE in the project root for license information.
* -----------------------------------------------------------------------------------------*/
import { showMessage } from '../../helpers/userInput'
import CodedungeonToolbox from './support/CodedungeonToolbox'
export async function convertToHtml(): Promise<void> {
const toolbox = new CodedungeonToolbox()
const note = Editor.content || ''
const html = toolbox.markdownToHtml(note)
Clipboard.string = html
if (html.length > 0) {
await showMessage('Content Copied To Clipboard')
} else {
await showMessage('An error occured converting content to HTML')
}
}
================================================
FILE: codedungeon.Toolbox/src/convertToRtf.js
================================================
// @flow
/*-------------------------------------------------------------------------------------------
* Copyright (c) 2021-2022 Mike Erickson / Codedungeon. All rights reserved.
* Licensed under the MIT license. See LICENSE in the project root for license information.
* -----------------------------------------------------------------------------------------*/
import { showMessage } from '../../helpers/userInput'
import CodedungeonToolbox from './support/CodedungeonToolbox'
export async function convertToRtf(): Promise<void> {
const toolbox = new CodedungeonToolbox()
const note = Editor.content || ''
const rtf = await toolbox.markdownToRtf(note)
Clipboard.string = rtf
if (rtf.length > 0) {
await showMessage('Content Copied To Clipboard')
} else {
await showMessage('An error occured converting content to RTF')
}
}
================================================
FILE: codedungeon.Toolbox/src/index.js
================================================
/*-------------------------------------------------------------------------------------------
* Copyright (c) 2021-2022 Mike Erickson / Codedungeon. All rights reserved.
* Licensed under the MIT license. See LICENSE in the project root for license information.
* -----------------------------------------------------------------------------------------*/
export { convertToHtml } from './con
gitextract_lyr50gqo/
├── .cursor/
│ ├── commands/
│ │ └── np-plugin-commands.md
│ └── rules/
│ ├── html-react-rules.mdc
│ └── np-programming-general.mdc
├── .cursorignore
├── .eslintrc
├── .flowconfig
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── bug_report.md
│ │ └── feature_request.md
│ └── workflows/
│ └── node.js.yml
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .watchmanconfig
├── CHANGELOG.md
├── Flow_Guide.md
├── GithubFlow.md
├── KimMachineGun.Raindrop/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── NPPluginMain.js
│ ├── Raindrop.js
│ └── index.js
├── LICENSE
├── README.md
├── TasksModule_docs.md
├── __mocks__/
│ ├── Backlink.mock.js
│ ├── Calendar.mock.js
│ ├── CalendarItem.mock.js
│ ├── Clipboard.mock.js
│ ├── CommandBar.mock.js
│ ├── DataStore.mock.js
│ ├── Editor.mock.js
│ ├── Fetch.mock.js
│ ├── NP_THEME.mock.js
│ ├── Note.mock.js
│ ├── NotePlan.mock.js
│ ├── Paragraph.mock.js
│ ├── PluginCommandObject.mock.js
│ ├── PluginObject.mock.js
│ ├── Range.mock.js
│ ├── _README-Mocks.md
│ ├── __tests__/
│ │ └── fetch.mock.test.js
│ ├── index.js
│ ├── jestHelpers.js
│ └── support/
│ └── pluginSample.json
├── aaronpoweruser.ReadwiseUnofficial/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── NPReadwise.js
│ ├── NPReadwiseHelpers.js
│ ├── NPReadwiseNotes.js
│ ├── NPReadwiseSyncLog.js
│ ├── NPTriggers-Hooks.js
│ └── index.js
├── babel.config.js
├── buildDashboard.sh
├── buildShared.sh
├── codedungeon.Toolbox/
│ ├── CHANGELOG.md
│ ├── LICENSE
│ ├── README.md
│ ├── __tests__/
│ │ ├── convertSelectionToHtml.test.js
│ │ ├── convertToHtml.test.js
│ │ └── reorderList.test.js
│ ├── plugin.json
│ └── src/
│ ├── convertSelectionToHtml.js
│ ├── convertToHtml.js
│ ├── convertToRtf.js
│ ├── index.js
│ ├── reorderList.js
│ └── support/
│ └── CodedungeonToolbox.js
├── community-plugins.json
├── dbludeau.TodoistNoteplanSync/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── NPPluginMain.NOTACTIVE.js
│ ├── plugin.json
│ ├── requiredFiles/
│ │ └── html-plugin-comms.js
│ └── src/
│ ├── NPPluginMain.js
│ ├── NPTriggers-Hooks.js
│ ├── index.js
│ └── support/
│ ├── fetchOverrides.js
│ ├── fetchResponses/
│ │ └── google.search-for-something.json
│ └── helpers.js
├── deleteme.testPluginDownload/
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ └── index.js
├── docs/
│ ├── custom_theme/
│ │ ├── README.md
│ │ ├── assets/
│ │ │ ├── anchor.js
│ │ │ ├── bass-addons.css
│ │ │ ├── bass.css
│ │ │ ├── fonts/
│ │ │ │ ├── LICENSE.txt
│ │ │ │ ├── OTF/
│ │ │ │ │ ├── SourceCodePro-Bold.otf
│ │ │ │ │ └── SourceCodePro-Regular.otf
│ │ │ │ └── source-code-pro.css
│ │ │ ├── github.css
│ │ │ ├── site.js
│ │ │ ├── split.css
│ │ │ ├── split.js
│ │ │ └── style.css
│ │ ├── index._
│ │ ├── index.js
│ │ ├── note._
│ │ ├── paramProperty._
│ │ ├── section._
│ │ └── section_list._
│ └── documentation.cfg.json
├── dwertheimer.DateAutomations/
│ ├── README.md
│ ├── changelog.md
│ ├── plugin.json
│ └── src/
│ ├── dateFunctions.js
│ └── index.js
├── dwertheimer.EventAutomations/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── NPEventBlocks.test.js
│ │ ├── NPTimeblocking.Integration.test.js
│ │ ├── NPTimeblocking.test.js
│ │ ├── by_timeblock_tag.test.js
│ │ ├── config.test.js
│ │ ├── presets.test.js
│ │ ├── timeblocking-helpers.test.js
│ │ ├── timeblocking-shared.test.js
│ │ └── timeblocking-taskSorting.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPEventBlocks.js
│ ├── NPTimeblocking.js
│ ├── byTagMode.js
│ ├── config.js
│ ├── events.js
│ ├── index.js
│ ├── presets.js
│ ├── timeblocking-flow-types.js
│ ├── timeblocking-helpers.js
│ ├── timeblocking-shared.js
│ └── triggers.js
├── dwertheimer.Favorites/
│ ├── CHANGELOG.md
│ ├── DEBUGGING_REQUEST_TIMEOUTS.md
│ ├── IMPLEMENTATION_PLAN.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── NPFavorites.test.js
│ │ └── favorites.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPFavoritePresets.js
│ ├── NPFavorites.js
│ ├── components/
│ │ ├── AppContext.jsx
│ │ ├── FavoritesView.css
│ │ └── FavoritesView.jsx
│ ├── favorites.js
│ ├── favoritesRouter.js
│ ├── index.js
│ ├── requestHandlers.js
│ ├── shared/
│ │ └── types.js
│ ├── support/
│ │ ├── performRollup.node.js
│ │ └── rollup.FavoritesView.entry.js
│ └── windowManagement.js
├── dwertheimer.Forms/
│ ├── CHANGELOG.md
│ ├── DEBUGGING_PLAN_TEMPLATEJS_HANG.md
│ ├── README.md
│ ├── TEMPLATEJS_FREEZE_DIAG_THROWS.md
│ ├── plugin.json
│ └── src/
│ ├── FormFieldRenderTest.js
│ ├── NPTemplateForm.js
│ ├── NPTriggers-Hooks.js
│ ├── ProcessingTemplate.js
│ ├── components/
│ │ ├── AppContext.jsx
│ │ ├── ConditionsEditor.jsx
│ │ ├── FieldEditor.jsx
│ │ ├── FieldTypeSelector.jsx
│ │ ├── FormBrowserView.css
│ │ ├── FormBrowserView.jsx
│ │ ├── FormBuilder.css
│ │ ├── FormBuilder.jsx
│ │ ├── FormBuilderView.jsx
│ │ ├── FormErrorBanner.jsx
│ │ ├── FormFieldsList.jsx
│ │ ├── FormPreview.jsx
│ │ ├── FormSettings.jsx
│ │ ├── FormView.css
│ │ ├── FormView.jsx
│ │ ├── OptionsEditor.jsx
│ │ ├── PositionInput.jsx
│ │ ├── ProcessingMethodSection.jsx
│ │ ├── TemplateTagEditor.css
│ │ ├── TemplateTagEditor.jsx
│ │ ├── TemplateTagInserter.css
│ │ ├── TemplateTagInserter.jsx
│ │ ├── ValueInsertButtons.jsx
│ │ └── fieldTypes.js
│ ├── dataHandlers.js
│ ├── formBrowserHandlers.js
│ ├── formBrowserRouter.js
│ ├── formBuilderHandlers.js
│ ├── formBuilderRouter.js
│ ├── formSubmission.js
│ ├── formSubmitHandlers.js
│ ├── formSubmitRouter.js
│ ├── index.js
│ ├── noteHelpers.js
│ ├── requestHandlers.js
│ ├── shared/
│ │ ├── constants.js
│ │ └── types.js
│ ├── support/
│ │ ├── fetchOverrides.js
│ │ ├── fetchResponses/
│ │ │ └── google.search-for-something.json
│ │ ├── performRollup.node.js
│ │ ├── rollup.FormBrowserView.entry.js
│ │ ├── rollup.FormBuilderView.entry.js
│ │ └── rollup.FormView.entry.js
│ ├── templateIO.js
│ ├── utils/
│ │ └── encodingFix.js
│ └── windowManagement.js
├── dwertheimer.JestHelpers/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── NPMocks.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPPluginMain.js
│ ├── index.js
│ └── support/
│ └── helpers.js
├── dwertheimer.MathSolver/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── NPMathBlocks.test.js
│ │ ├── date-time-math.test.js
│ │ └── solver.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPMathBlocks.js
│ ├── index.js
│ └── support/
│ ├── date-time-math.js
│ ├── helpers.js
│ └── solver.js
├── dwertheimer.ReactSkeleton/
│ ├── REACT_COMMUNICATION_PATTERNS.md
│ ├── REACT_UTILITIES.md
│ ├── REQUEST_COMMUNICATIONS_AND_ROUTING.md
│ ├── changelog.md
│ ├── plugin.json
│ ├── readme.md
│ ├── requiredFiles/
│ │ └── css.plugin.css
│ └── src/
│ ├── index.js
│ ├── react/
│ │ ├── components/
│ │ │ ├── AppContext.jsx
│ │ │ ├── Button.jsx
│ │ │ ├── Checkbox.jsx
│ │ │ ├── CompositeLineExample.jsx
│ │ │ └── WebView.jsx
│ │ └── support/
│ │ ├── performRollup.node.js
│ │ └── rollup.WebView.entry.js
│ ├── reactMain.js
│ ├── requestHandlers.js
│ └── router.js
├── dwertheimer.TaskAutomations/
│ ├── __tests__/
│ │ ├── NPOverdueReact.test.js
│ │ ├── NPTaskScanAndProcess.test.js
│ │ └── lastUsedChoices.test.js
│ ├── changelog.md
│ ├── plugin.json
│ ├── readme.md
│ ├── requiredFiles/
│ │ ├── css.plugin.css
│ │ └── css.w3.css
│ └── src/
│ ├── NPFollowUp.js
│ ├── NPOverdue.js
│ ├── NPOverdueReact.js
│ ├── NPTaskScanAndProcess.js
│ ├── index.js
│ ├── lastUsedChoices.js
│ ├── react/
│ │ ├── Button.jsx
│ │ ├── EditableElement.jsx
│ │ ├── MultiActionBar.jsx
│ │ ├── StatusButton.jsx
│ │ ├── ThemedSelect.jsx
│ │ ├── TypeFilter.jsx
│ │ ├── WebView.jsx
│ │ ├── dataTableFormatting.jsx
│ │ └── support/
│ │ ├── performRollup.node.js
│ │ └── rollup.WebView.entry.js
│ └── taskSync.js
├── dwertheimer.TaskSorting/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── factories/
│ │ │ ├── taskDocument.json
│ │ │ ├── taskDocument.notes.txt
│ │ │ └── taskDocumentAfterSortByTitle.json
│ │ ├── sortTasks.test.js
│ │ └── tagTasks.test.js
│ ├── plugin.json
│ ├── requiredFiles/
│ │ └── html-plugin-comms.js
│ └── src/
│ ├── NPTriggers-Hooks.js
│ ├── index.js
│ ├── markTasks.js
│ ├── sortTasks.js
│ ├── support/
│ │ ├── fetchOverrides.js
│ │ ├── fetchResponses/
│ │ │ └── google.search-for-something.json
│ │ └── helpers.js
│ └── tagTasks.js
├── emetzger.Calendar/
│ └── plugin.json
├── emetzger.LinearCalendar/
│ └── plugin.json
├── flow-typed/
│ ├── Noteplan.js
│ └── npm/
│ ├── @babel/
│ │ ├── cli_vx.x.x.js
│ │ ├── core_vx.x.x.js
│ │ ├── eslint-parser_vx.x.x.js
│ │ ├── generator_vx.x.x.js
│ │ ├── parser_vx.x.x.js
│ │ ├── preset-env_vx.x.x.js
│ │ ├── preset-flow_vx.x.x.js
│ │ └── preset-react_vx.x.x.js
│ ├── @codedungeon/
│ │ └── gunner_vx.x.x.js
│ ├── @rollup/
│ │ ├── plugin-alias_vx.x.x.js
│ │ ├── plugin-babel_vx.x.x.js
│ │ ├── plugin-commonjs_vx.x.x.js
│ │ ├── plugin-json_vx.x.x.js
│ │ └── plugin-node-resolve_vx.x.x.js
│ ├── @samverschueren/
│ │ └── stream-to-observable_vx.x.x.js
│ ├── axios_v0.21.x.js
│ ├── babel-cli_vx.x.x.js
│ ├── babel_vx.x.x.js
│ ├── babelify_vx.x.x.js
│ ├── bcrypt_v5.x.x.js
│ ├── bqpjs_vx.x.x.js
│ ├── browserify_vx.x.x.js
│ ├── bump-regex_vx.x.x.js
│ ├── chroma-js_v2.x.x.js
│ ├── chrono-node_vx.x.x.js
│ ├── clipboardy_vx.x.x.js
│ ├── columnify_vx.x.x.js
│ ├── commander_vx.x.x.js
│ ├── concurrently_vx.x.x.js
│ ├── contentful-html-rich-text-converter_vx.x.x.js
│ ├── dayjs_v1.x.x.js
│ ├── documentation_vx.x.x.js
│ ├── enquirer_vx.x.x.js
│ ├── eslint-config-prettier_vx.x.x.js
│ ├── eslint-import-resolver-alias_vx.x.x.js
│ ├── eslint-plugin-flowtype_vx.x.x.js
│ ├── eslint-plugin-import_vx.x.x.js
│ ├── eslint-plugin-no-floating-promise_vx.x.x.js
│ ├── eslint-plugin-react_vx.x.x.js
│ ├── eslint-plugin-unused-imports_vx.x.x.js
│ ├── eslint_vx.x.x.js
│ ├── fast-glob_vx.x.x.js
│ ├── findup-sync_vx.x.x.js
│ ├── flow-bin_v0.x.x.js
│ ├── front-matter_vx.x.x.js
│ ├── fsevents_vx.x.x.js
│ ├── fuse.js_v6.x.x.js
│ ├── fuse.js_vx.x.x.js
│ ├── git-state_vx.x.x.js
│ ├── html-minifier_vx.x.x.js
│ ├── inquirer_vx.x.x.js
│ ├── jest-silent-reporter_vx.x.x.js
│ ├── jest-spec-reporter_vx.x.x.js
│ ├── jest_v27.x.x.js
│ ├── js-yaml_v4.x.x.js
│ ├── js-yaml_vx.x.x.js
│ ├── json5_vx.x.x.js
│ ├── listr_vx.x.x.js
│ ├── lodash-es_v4.x.x.js
│ ├── luxon-business-days_vx.x.x.js
│ ├── luxon_vx.x.x.js
│ ├── mathjs_vx.x.x.js
│ ├── mermaid_vx.x.x.js
│ ├── mkdirp_v1.x.x.js
│ ├── moment-business-days_vx.x.x.js
│ ├── moment_v2.x.x.js
│ ├── node-fetch_v1.x.x.js
│ ├── node-gyp_vx.x.x.js
│ ├── node-libcurl_vx.x.x.js
│ ├── node-notifier_vx.x.x.js
│ ├── prettier_vx.x.x.js
│ ├── progress_vx.x.x.js
│ ├── react-data-table-component_vx.x.x.js
│ ├── react-dom_v18.x.x.js
│ ├── react-error-boundary_vx.x.x.js
│ ├── react-loader-spinner_vx.x.x.js
│ ├── react-select_vx.x.x.js
│ ├── rimraf_v2.x.x.js
│ ├── rollup-plugin-replace_vx.x.x.js
│ ├── rollup-plugin-terser_vx.x.x.js
│ ├── rollup-plugin-visualizer_vx.x.x.js
│ ├── rollup_vx.x.x.js
│ ├── rxjs_v6.x.x.js
│ ├── showdown_vx.x.x.js
│ ├── simple-input_vx.x.x.js
│ ├── sinon_v7.x.x.js
│ ├── split_vx.x.x.js
│ ├── sprintf-js_vx.x.x.js
│ ├── strftime_vx.x.x.js
│ ├── toml_vx.x.x.js
│ └── webpack_v4.x.x.js
├── github-actions-reporter.js
├── grdn.TagTracker/
│ ├── .claude/
│ │ └── settings.local.json
│ └── plugin.json
├── helpers/
│ ├── HTMLView.js
│ ├── NPAddItems.js
│ ├── NPCalendar.js
│ ├── NPConfiguration.js
│ ├── NPEditor.js
│ ├── NPEditorBasics.js
│ ├── NPExtendedRepeat.js
│ ├── NPFrontMatter.js
│ ├── NPMoveItems.js
│ ├── NPParagraph.js
│ ├── NPPresets.js
│ ├── NPRequiredFiles.js
│ ├── NPScheduleItems.js
│ ├── NPSettings.js
│ ├── NPSyncedCopies.js
│ ├── NPTeamspace.js
│ ├── NPThemeToCSS.js
│ ├── NPVersions.js
│ ├── NPWindows.js
│ ├── NPdateTime.js
│ ├── NPdev.js
│ ├── NPnote.js
│ ├── README.md
│ ├── __tests__/
│ │ ├── HTMLView.test.js
│ │ ├── NPConfiguration.test.js
│ │ ├── NPConfiguration.updateSettingData.test.js
│ │ ├── NPDateTime.test.js
│ │ ├── NPExtendedRepeat.doneMarker.test.js
│ │ ├── NPExtendedRepeat.generateRepeatForPara.test.js
│ │ ├── NPFrontMatter/
│ │ │ ├── NPFrontMatter.analyzeTemplateStructure.test.js
│ │ │ ├── NPFrontMatterAttributes.test.js
│ │ │ ├── NPFrontMatterDetection.test.js
│ │ │ ├── NPFrontMatterFormatting.test.js
│ │ │ ├── NPFrontMatterManipulation.test.js
│ │ │ ├── NPFrontMatterMisc.test.js
│ │ │ ├── NPFrontMatterNotes.test.js
│ │ │ └── NPFrontMatterTriggers.test.js
│ │ ├── NPFrontMatter.detectInlineTitleRobust.test.js
│ │ ├── NPNote.test.js
│ │ ├── NPParagraph.test.js
│ │ ├── NPPresets.test.js
│ │ ├── NPSettings.test.js
│ │ ├── NPSyncedCopies.test.js
│ │ ├── NPThemeToCSS.test.js
│ │ ├── blocks.test.js
│ │ ├── calendar.test.js
│ │ ├── config.test.js
│ │ ├── dataManipulation.test.js
│ │ ├── dateTime.test.js
│ │ ├── dev.test.js
│ │ ├── folders.test.js
│ │ ├── general.test.js
│ │ ├── headings.test.js
│ │ ├── note.test.js
│ │ ├── notePlanWeekFormatter.test.js
│ │ ├── paragraph.test.js
│ │ ├── parentsAndChildren.test.js
│ │ ├── regex.test.js
│ │ ├── regexEscape.test.js
│ │ ├── search.test.js
│ │ ├── sorting.test.js
│ │ ├── stringTransforms.test.js
│ │ ├── syncedCopies.test.js
│ │ ├── teamspace.test.js
│ │ ├── timeblocks.test.js
│ │ ├── urls.test.js
│ │ └── utils.test.js
│ ├── blocks.js
│ ├── calendar.js
│ ├── checkType.js
│ ├── codeBlocks.js
│ ├── colors.js
│ ├── config.js
│ ├── content.js
│ ├── dataManipulation.js
│ ├── dateTime.js
│ ├── dev.js
│ ├── folders.js
│ ├── general.js
│ ├── headings.js
│ ├── markdown-regex.js
│ ├── note.js
│ ├── noteChooserFilenameResolve.js
│ ├── notePlanWeekFormatter.js
│ ├── openAI.js
│ ├── paragraph.js
│ ├── parentsAndChildren.js
│ ├── promisePolyfill.js
│ ├── react/
│ │ ├── CollapsibleObjectViewer.css
│ │ ├── CollapsibleObjectViewer.jsx
│ │ ├── ConsoleLogView.css
│ │ ├── ConsoleLogView.jsx
│ │ ├── DebugPanel.css
│ │ ├── DebugPanel.jsx
│ │ ├── DynamicDialog/
│ │ │ ├── AutosaveField.jsx
│ │ │ ├── ButtonComponents.jsx
│ │ │ ├── CREATING_NEW_DYNAMICDIALOG_FIELD_TYPES.md
│ │ │ ├── CalendarPicker.css
│ │ │ ├── CalendarPicker.jsx
│ │ │ ├── ColorChooser.css
│ │ │ ├── ColorChooser.jsx
│ │ │ ├── ConditionalValues.css
│ │ │ ├── ConditionalValues.jsx
│ │ │ ├── ContainedMultiSelectChooser.css
│ │ │ ├── ContainedMultiSelectChooser.jsx
│ │ │ ├── DD_NEW_FEATURE_CHECKLIST.md
│ │ │ ├── DropdownSelect.css
│ │ │ ├── DropdownSelect.jsx
│ │ │ ├── DropdownSelectChooser.css
│ │ │ ├── DropdownSelectChooser.jsx
│ │ │ ├── DynamicDialog.css
│ │ │ ├── DynamicDialog.jsx
│ │ │ ├── EventChooser.css
│ │ │ ├── EventChooser.jsx
│ │ │ ├── ExpandableTextarea.css
│ │ │ ├── ExpandableTextarea.jsx
│ │ │ ├── FolderChooser.css
│ │ │ ├── FolderChooser.jsx
│ │ │ ├── FrontmatterKeyChooser.css
│ │ │ ├── FrontmatterKeyChooser.jsx
│ │ │ ├── GenericDatePicker.css
│ │ │ ├── GenericDatePicker.jsx
│ │ │ ├── HeadingChooser.css
│ │ │ ├── HeadingChooser.jsx
│ │ │ ├── IconChooser.jsx
│ │ │ ├── IconStyleChooser.jsx
│ │ │ ├── InputBox.jsx
│ │ │ ├── MarkdownPreview.css
│ │ │ ├── MarkdownPreview.jsx
│ │ │ ├── MentionChooser.css
│ │ │ ├── MentionChooser.jsx
│ │ │ ├── MultiSelectChooser.css
│ │ │ ├── MultiSelectChooser.jsx
│ │ │ ├── NoteChooser.css
│ │ │ ├── NoteChooser.jsx
│ │ │ ├── OrderingPanel.css
│ │ │ ├── OrderingPanel.jsx
│ │ │ ├── PatternChooser.jsx
│ │ │ ├── SearchableChooser.css
│ │ │ ├── SearchableChooser.jsx
│ │ │ ├── SpaceChooser.css
│ │ │ ├── SpaceChooser.jsx
│ │ │ ├── Switch.jsx
│ │ │ ├── TableOfContents.css
│ │ │ ├── TableOfContents.jsx
│ │ │ ├── TagChooser.css
│ │ │ ├── TagChooser.jsx
│ │ │ ├── TemplateJSBlock.css
│ │ │ ├── TemplateJSBlock.jsx
│ │ │ ├── TextComponent.jsx
│ │ │ ├── ThemedSelect.jsx
│ │ │ ├── _README.md
│ │ │ ├── __tests__/
│ │ │ │ └── DropdownSelect.test.jsx
│ │ │ ├── dateFormatOptions.js
│ │ │ ├── dialogElementRenderer.js
│ │ │ ├── index.js
│ │ │ ├── useRequestWithRetry.js
│ │ │ └── valueInsertData.js
│ │ ├── EditableInput.jsx
│ │ ├── FilterableList.css
│ │ ├── FilterableList.jsx
│ │ ├── IdleTimer.jsx
│ │ ├── InfoIcon.css
│ │ ├── InfoIcon.jsx
│ │ ├── List.css
│ │ ├── List.jsx
│ │ ├── Modal/
│ │ │ ├── Modal.css
│ │ │ ├── Modal.jsx
│ │ │ ├── ModalWithTooltip.jsx
│ │ │ └── index.js
│ │ ├── ModalSpinner.jsx
│ │ ├── ModifierHints.css
│ │ ├── ModifierHints.jsx
│ │ ├── NonModalSpinner.jsx
│ │ ├── SearchBox.jsx
│ │ ├── SimpleDialog.css
│ │ ├── SimpleDialog.jsx
│ │ ├── TestingPane.css
│ │ ├── TestingPane.jsx
│ │ ├── ThemedSelect.jsx
│ │ ├── dateStrings.js
│ │ ├── pluginRequestEnvelope.js
│ │ ├── reactDev.js
│ │ ├── reactMouseKeyboard.js
│ │ ├── reactUtils.js
│ │ ├── routerUtils.js
│ │ ├── testSimpleDialog.js
│ │ ├── useEffectGuard.js
│ │ └── userInput.jsx
│ ├── regex.js
│ ├── regexEscape.js
│ ├── search.js
│ ├── sorting.js
│ ├── stringTransforms.js
│ ├── syncedCopies.js
│ ├── teamspace.js
│ ├── testing/
│ │ ├── CustomError.js
│ │ ├── expect.js
│ │ └── testingUtils.js
│ ├── timeblocks.js
│ ├── urls.js
│ ├── userInput.js
│ └── utils.js
├── index.js
├── jest.config.js
├── jest.customSummaryReporter.js
├── jest.noteplan-globals.js
├── jgclark.DailyJournal/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── index.js
│ ├── journal.js
│ ├── journalHelpers.js
│ └── templatesStartEnd.js
├── jgclark.Dashboard/
│ ├── ADDING_A_DASHBOARD_SETTING.md
│ ├── ARCHITECTURE-How_Stuff_Works.md
│ ├── CHANGELOG.md
│ ├── DASHBOARD_HELPERS_REVIEW.md
│ ├── README.md
│ ├── plugin.json
│ ├── plugin.json.orig
│ ├── readme-react.md
│ ├── requiredFiles/
│ │ └── css.plugin.css
│ └── src/
│ ├── NPHooks.js
│ ├── __tests__/
│ │ ├── dashboardHelpers.test.js
│ │ └── dashboardLineToNPDisplayHTML.test.js
│ ├── bannerClickHandlers.js
│ ├── clickHandlers.js
│ ├── constants.js
│ ├── countDoneTasks.js
│ ├── dashboardHelpers.js
│ ├── dashboardHooks.js
│ ├── dashboardSettings.js
│ ├── dataGeneration.js
│ ├── dataGenerationDays.js
│ ├── dataGenerationOverdue.js
│ ├── dataGenerationPriority.js
│ ├── dataGenerationProjects.js
│ ├── dataGenerationSearch.js
│ ├── dataGenerationTags.js
│ ├── dataGenerationWeeks.js
│ ├── demoData.js
│ ├── diagnosticGenerator.js
│ ├── index.js
│ ├── moveClickHandlers.js
│ ├── moveDayClickHandlers.js
│ ├── moveWeekClickHandlers.js
│ ├── perspectiveClickHandlers.js
│ ├── perspectiveHelpers.js
│ ├── perspectivesShared.js
│ ├── pluginToHTMLBridge.js
│ ├── projectClickHandlers.js
│ ├── react/
│ │ ├── components/
│ │ │ ├── AddButtons.jsx
│ │ │ ├── AppContext.jsx
│ │ │ ├── Button.jsx
│ │ │ ├── CalendarPicker.jsx
│ │ │ ├── CircularProgressBar.jsx
│ │ │ ├── ComboBox.jsx
│ │ │ ├── CommandButton.jsx
│ │ │ ├── Dashboard.jsx
│ │ │ ├── Dialog.jsx
│ │ │ ├── DialogForProjectItems.jsx
│ │ │ ├── DialogForTaskItems.jsx
│ │ │ ├── DropdownMenu.jsx
│ │ │ ├── Header/
│ │ │ │ ├── AddToAnyNote.css
│ │ │ │ ├── AddToAnyNote.jsx
│ │ │ │ ├── DoneCounts.jsx
│ │ │ │ ├── Header.css
│ │ │ │ ├── Header.jsx
│ │ │ │ ├── PerspectiveSelector.jsx
│ │ │ │ ├── SearchBar.css
│ │ │ │ ├── SearchBar.jsx
│ │ │ │ ├── SearchPanel.css
│ │ │ │ ├── SearchPanel.jsx
│ │ │ │ ├── __tests__/
│ │ │ │ │ └── PerspectiveSelector.test.jsx
│ │ │ │ ├── featureFlagItems.js
│ │ │ │ ├── filterDropdownItems.js
│ │ │ │ ├── headerDropdownHandlers.js
│ │ │ │ ├── index.js
│ │ │ │ └── useLastFullRefresh.js
│ │ │ ├── IdleTimer.jsx
│ │ │ ├── InputBox.jsx
│ │ │ ├── ItemContent.jsx
│ │ │ ├── ItemGrid.jsx
│ │ │ ├── ItemNoteLink.jsx
│ │ │ ├── ItemRow.css
│ │ │ ├── ItemRow.jsx
│ │ │ ├── MessageOnlyItem.jsx
│ │ │ ├── Modal/
│ │ │ │ ├── Modal.css
│ │ │ │ ├── Modal.jsx
│ │ │ │ └── index.js
│ │ │ ├── MultiSelectSpaces.jsx
│ │ │ ├── NoTasks.jsx
│ │ │ ├── NoteTitleLink.jsx
│ │ │ ├── PerspectiveSettings.jsx
│ │ │ ├── PerspectivesTable.jsx
│ │ │ ├── ProjectItem.jsx
│ │ │ ├── RefreshControl.jsx
│ │ │ ├── Section/
│ │ │ │ ├── Section.css
│ │ │ │ ├── Section.jsx
│ │ │ │ ├── index.js
│ │ │ │ ├── sectionHelpers.js
│ │ │ │ └── useSectionSortAndFilter.jsx
│ │ │ ├── SettingsDialog.jsx
│ │ │ ├── SmallCircularProgressIndicator.jsx
│ │ │ ├── StatusIcon.jsx
│ │ │ ├── Switch.jsx
│ │ │ ├── TaskItem.css
│ │ │ ├── TaskItem.jsx
│ │ │ ├── TasksFiltered.css
│ │ │ ├── TasksFiltered.jsx
│ │ │ ├── TextComponent.jsx
│ │ │ ├── ThemedComboBox.jsx
│ │ │ ├── ToolTipOnModifierPress.jsx
│ │ │ ├── Tooltip.jsx
│ │ │ ├── WebView.jsx
│ │ │ ├── __tests__/
│ │ │ │ └── DropdownMenu.test.jsx
│ │ │ └── testing/
│ │ │ ├── dashboardSettings.tests.js
│ │ │ ├── general.tests.js
│ │ │ ├── perspectives.tests.js
│ │ │ ├── sectionHelpers.test.js
│ │ │ ├── testingHelpers.js
│ │ │ ├── tests.js
│ │ │ └── useSectionSortAndFilter.test.js
│ │ ├── css/
│ │ │ ├── CalendarPicker.css
│ │ │ ├── Dashboard.css
│ │ │ ├── DashboardDialog.css
│ │ │ ├── DropdownMenu.css
│ │ │ ├── MultiSelectSpaces.css
│ │ │ ├── PerspectiveSettings.css
│ │ │ ├── PerspectivesTable.css
│ │ │ ├── ProgressBar.css
│ │ │ ├── SettingsDialog.css
│ │ │ ├── Tooltip.css
│ │ │ └── animation.css
│ │ ├── customHooks/
│ │ │ ├── useMidnightRollover.jsx
│ │ │ ├── useRefreshTimer.jsx
│ │ │ ├── useSettingsDialogHandler.jsx
│ │ │ ├── useSyncDashboardSettingsWithPlugin.js
│ │ │ ├── useSyncPerspectivesWithPlugin.js
│ │ │ └── useWatchForResizes.jsx
│ │ ├── dashboardLineToNPDisplayHTML.js
│ │ ├── reducers/
│ │ │ ├── actionTypes.js
│ │ │ ├── dashboardSettingsReducer.js
│ │ │ └── perspectiveSettingsReducer.js
│ │ └── support/
│ │ ├── performRollup.node.js
│ │ ├── rollup.WebView.entry.js
│ │ ├── settingsHelpers.js
│ │ └── uiElementRenderHelpers.js
│ ├── reactMain.js
│ ├── refreshClickHandlers.js
│ ├── requestHandlers/
│ │ └── addTaskToNote.js
│ ├── routeRequestsFromReact.js
│ ├── shared.js
│ ├── tagMentionCache.js
│ └── types.js
├── jgclark.EventHelpers/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── eventsToNotes.test.js
│ ├── plugin.json
│ └── src/
│ ├── eventsHelpers.js
│ ├── eventsToNotes.js
│ ├── index.js
│ ├── offsets.js
│ └── timeblocks.js
├── jgclark.Filer/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── moveCompletedToDone.test.js
│ ├── plugin.json
│ └── src/
│ ├── IDs.js
│ ├── archive.js
│ ├── filerHelpers.js
│ ├── index.js
│ ├── moveCompletedToDone.js
│ ├── moveItems.js
│ └── noteLinks.js
├── jgclark.Journalling/
│ └── CHANGELOG.md
├── jgclark.MOCs/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── MOCs.js
│ └── index.js
├── jgclark.NoteHelpers/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── duplicateNote.test.js
│ │ ├── newNote.test.js
│ │ └── unLinkedNoteFinder.test.js
│ ├── plugin.json
│ └── src/
│ ├── countDays.js
│ ├── duplicateNote.js
│ ├── helpers/
│ │ ├── findInconsistentNames.js
│ │ ├── makeNoteTitleMatchFilename.js
│ │ └── renameNotes.js
│ ├── index.js
│ ├── indexFolders.js
│ ├── lib/
│ │ └── commands/
│ │ ├── filenameToTitle.js
│ │ ├── listInconsistentNames.js
│ │ ├── renameInconsistentNames.js
│ │ └── titleToFilename.js
│ ├── listPublishedNotes.js
│ ├── newNote.js
│ ├── noteHelpers.js
│ ├── noteNavigation.js
│ ├── unlinkedNoteFinder.js
│ └── writeModified.js
├── jgclark.PeriodicReviews/
│ └── src/
│ └── reviewHTMLViewGenerator.js
├── jgclark.QuickCapture/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── inbox.js
│ ├── index.js
│ ├── quickCapture.js
│ └── quickCaptureHelpers.js
├── jgclark.RepeatExtensions/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── repeatHelpers.test.js
│ │ └── repeatTrigger.test.js
│ ├── plugin.json
│ └── src/
│ ├── index.js
│ ├── repeatHelpers.js
│ ├── repeatMain.js
│ ├── repeatPara.js
│ └── repeatTrigger.js
├── jgclark.Reviews/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── css/
│ │ ├── fontawesome.css
│ │ ├── regular.css
│ │ └── solid.css
│ ├── experiments/
│ │ ├── CSS-circle-test.html
│ │ ├── SVG-circle-test-attempt1.html
│ │ ├── SVG-circle-test-attempt2.html
│ │ ├── SVG-circle-test-attempt3.html
│ │ ├── chart-experiments.js
│ │ ├── font-tests.html
│ │ └── fontTests.js
│ ├── plugin.json
│ ├── remove_combined_fm_metadata_8323fd97.plan.md
│ ├── requiredFiles/
│ │ ├── HTMLWinCommsSwitchboard.js
│ │ ├── projectList.css
│ │ ├── projectListDialog.css
│ │ ├── projectListEvents.js
│ │ ├── shortcut.js
│ │ └── showTimeAgo.js
│ └── src/
│ ├── __tests__/
│ │ ├── allProjectsListHelpers.sorting.test.js
│ │ ├── filterProjectNotesByFolders.test.js
│ │ ├── getMetadataLineIndexFromBody.test.js
│ │ ├── noteChangeCache.test.js
│ │ ├── projectClass.defaultReviewInterval.test.js
│ │ ├── projectClass.embeddedCombinedMentions.test.js
│ │ ├── projectClass.frontmatterParsing.test.js
│ │ └── reviewHelpers.clearNextReviewFrontmatterField.test.js
│ ├── allProjectsListHelpers.js
│ ├── convertNote.js
│ ├── index.js
│ ├── migration.js
│ ├── migrationLog.js
│ ├── pluginToHTMLBridge.js
│ ├── projectClass.js
│ ├── projectClassCalculations.js
│ ├── projectClassHelpers.js
│ ├── projects.js
│ ├── projectsHTMLGenerator.js
│ ├── projectsHTMLTemplates.js
│ ├── projectsWeeklyProgress.js
│ ├── reviewHelpers.js
│ └── reviews.js
├── jgclark.SearchExtensions/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── searchHelpers.test.js
│ ├── plugin.json
│ ├── requiredFiles/
│ │ └── flexiSearch.css
│ └── src/
│ ├── externalSearch.js
│ ├── flexiSearch.js
│ ├── index.js
│ ├── replace.js
│ ├── saveSearch.js
│ ├── searchHelpers.js
│ └── searchTriggers.js
├── jgclark.Summaries/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ ├── requiredFiles/
│ │ ├── README-chart.md
│ │ ├── chartStats.css
│ │ └── chartStatsScripts.js
│ └── src/
│ ├── TMOccurrences.js
│ ├── __tests__/
│ │ └── chartStatsDisplayStats.test.js
│ ├── chartStats.js
│ ├── configHelpers.js
│ ├── dateHelpers.js
│ ├── forCharts.js
│ ├── forHeatmaps.js
│ ├── gatherOccurrencesHelpers.js
│ ├── index.js
│ ├── progress.js
│ ├── stats.js
│ ├── summaryHelpers.js
│ ├── summarySettings.js
│ ├── testCharting.js
│ └── todayProgress.js
├── jgclark.WindowTools/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── WTHelpers.js
│ ├── index.js
│ ├── openers.js
│ ├── otherWindowTools.js
│ └── windowSets.js
├── jgclark.tests/
│ ├── plugin.json
│ └── src/
│ └── index.js
├── m1well.Expenses/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── expensesChecks.test.js
│ │ └── expensesHelper.test.js
│ ├── plugin.json
│ └── src/
│ ├── expenses.js
│ ├── expensesChecks.js
│ ├── expensesHelper.js
│ ├── expensesModels.js
│ └── index.js
├── nmn.DataQuery/
│ ├── plugin.json
│ ├── readme.md
│ └── src/
│ └── index.js
├── nmn.TimeTracking/
│ ├── plugin.json
│ ├── readme.md
│ └── src/
│ └── index.js
├── np.CallbackURLs/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── NPXCallbackWizard.test.js
│ │ └── utils.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPOpenFolders.js
│ ├── NPOpenLinks.js
│ ├── NPTemplateRunner.js
│ ├── NPXCallbackWizard.js
│ ├── index.js
│ └── support/
│ └── utils.js
├── np.Globals/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── globals.test.js
│ ├── lib/
│ │ └── NPGlobals.js
│ ├── plugin.json
│ └── src/
│ ├── Globals.js
│ └── index.js
├── np.MeetingNotes/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── hello-world.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPMeetingNotes.js
│ ├── index.js
│ └── support/
│ └── hello-world.js
├── np.Preview/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ ├── requiredFiles/
│ │ ├── mermaid@10.1.0.min.mjs
│ │ └── tex-chtml.js
│ └── src/
│ ├── bundling/
│ │ └── performMermaidRollup.node.js
│ ├── index.js
│ ├── mathTests.js
│ ├── mermaidTests.js
│ ├── previewMain.js
│ ├── previewTriggers.js
│ └── testCheckboxes.js
├── np.Shared/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ ├── requiredFiles/
│ │ ├── css.w3.css
│ │ ├── duotone.min.flat4NP.css
│ │ ├── encodeDecode.js
│ │ ├── fontawesome.css
│ │ ├── light.min.flat4NP.css
│ │ ├── noteplanstate-edited.otf
│ │ ├── pluginToHTMLCommsBridge.js
│ │ ├── pluginToHTMLErrorBridge.js
│ │ ├── regular.min.flat4NP.css
│ │ ├── shortcut.js
│ │ └── solid.min.flat4NP.css
│ └── src/
│ ├── NPReactLocal.js
│ ├── chooserHandlers.js
│ ├── index.js
│ ├── react/
│ │ ├── ErrorFallback.jsx
│ │ ├── MessageBanner.css
│ │ ├── MessageBanner.jsx
│ │ ├── Root.css
│ │ ├── Root.jsx
│ │ ├── Toast.css
│ │ ├── Toast.jsx
│ │ └── support/
│ │ ├── performRollup.node.js
│ │ ├── rollup.react.entry.js
│ │ └── rollup.root.entry.js
│ ├── requestHandlers/
│ │ ├── getAvailableCalendars.js
│ │ ├── getAvailableReminderLists.js
│ │ ├── getEvents.js
│ │ ├── getFolders.js
│ │ ├── getFrontmatterKeyValues.js
│ │ ├── getHashtags.js
│ │ ├── getHeadings.js
│ │ ├── getMentions.js
│ │ ├── getNotes.js
│ │ ├── getTeamspaces.js
│ │ └── noteHelpers.js
│ └── sharedRequestRouter.js
├── np.TOC/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── NPTriggers-Hooks.js
│ ├── index.js
│ ├── insertTOC.js
│ └── support/
│ ├── TOC-Heading.md
│ ├── TOC-Template.md
│ └── helpers.js
├── np.Templating/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── TEMP_DOCS_TemplateRunner_getNoteTitled_calendar_targets.md
│ ├── __tests__/
│ │ ├── BasePromptHandler.test.js
│ │ ├── NPTemplateNoteHelpers.test.js
│ │ ├── NPTemplateRunner.test.js
│ │ ├── ai-error-analysis.test.js
│ │ ├── ai-override.test.js
│ │ ├── awaitVariableAssignment.test.js
│ │ ├── date-module-now-fix.test.js
│ │ ├── date-module-timezone-debug.test.js
│ │ ├── date-module-timezone-simple.test.js
│ │ ├── date-module-timezone-working.test.js
│ │ ├── date-module.test.js
│ │ ├── ejs-error-handling.test.js
│ │ ├── error-handling.test.js
│ │ ├── factories/
│ │ │ ├── async.ejs
│ │ │ ├── complex-json-template.ejs
│ │ │ ├── custom-tags.ejs
│ │ │ ├── date-reference.ejs
│ │ │ ├── dates-various.ejs
│ │ │ ├── dates.ejs
│ │ │ ├── day-header-template.ejs
│ │ │ ├── double-dashes-in-body.ejs
│ │ │ ├── error-sample.ejs
│ │ │ ├── extended.ejs
│ │ │ ├── frontmatter-convert-project-note.md
│ │ │ ├── frontmatter-convert-success.md
│ │ │ ├── frontmatter-extended.ejs
│ │ │ ├── frontmatter-illegal-attribute.ejs
│ │ │ ├── frontmatter-indented.ejs
│ │ │ ├── frontmatter-minimal.ejs
│ │ │ ├── frontmatter-practical.ejs
│ │ │ ├── frontmatter-quick-note.ejs
│ │ │ ├── frontmatter-with-asterick-separators.ejs
│ │ │ ├── frontmatter-with-double-dashes.ejs
│ │ │ ├── frontmatter-with-multiple-fm-like-lines1.ejs
│ │ │ ├── frontmatter-with-multiple-fm-like-lines2.ejs
│ │ │ ├── frontmatter-with-separators.ejs
│ │ │ ├── frontmatter-yml.ejs
│ │ │ ├── invalid-json-test.ejs
│ │ │ ├── invalid-line-error.ejs
│ │ │ ├── invalid-syntax.ejs
│ │ │ ├── missing-object.ejs
│ │ │ ├── multiple-imports-one-line-return.ejs
│ │ │ ├── multiple-imports.ejs
│ │ │ ├── nested-templates.ejs
│ │ │ ├── simple-function.ejs
│ │ │ ├── simple.ejs
│ │ │ ├── simulate-tasks.ejs
│ │ │ ├── single-quoted-json-template.ejs
│ │ │ ├── stop-on-json-error.ejs
│ │ │ ├── syntax-error-template.ejs
│ │ │ ├── tags-extended.ejs
│ │ │ ├── tags-function.ejs
│ │ │ ├── tags.ejs
│ │ │ ├── template-logic.ejs
│ │ │ ├── ternary.ejs
│ │ │ ├── times.ejs
│ │ │ └── web-await-tests.ejs
│ │ ├── frontmatter-error-handling.test.js
│ │ ├── frontmatter-module.test.js
│ │ ├── full-pipeline-integration.test.js
│ │ ├── getRenderContext.test.js
│ │ ├── getTemplate.test.js
│ │ ├── import-tag-processor.test.js
│ │ ├── include-tag-processor.test.js
│ │ ├── isCode.test.js
│ │ ├── merge-statements.test.js
│ │ ├── preprocess-functions.test.js
│ │ ├── prompt-cancellation.test.js
│ │ ├── promptAwaitIssue.test.js
│ │ ├── promptDate.test.js
│ │ ├── promptDateInterval.test.js
│ │ ├── promptEdgeCases.test.js
│ │ ├── promptFormBatch.test.js
│ │ ├── promptFormTag.test.js
│ │ ├── promptIntegration.test.js
│ │ ├── promptKey.test.js
│ │ ├── promptRegistry.test.js
│ │ ├── promptSafetyChecks.test.js
│ │ ├── promptTagAndMention.test.js
│ │ ├── promptTagOutputBehavior.test.js
│ │ ├── promptTagSingleParameter.test.js
│ │ ├── promptVariableAssignment.test.js
│ │ ├── render-pipeline.test.js
│ │ ├── renderTemplate.test.js
│ │ ├── setup.js
│ │ ├── sharedPromptFunctions.test.js
│ │ ├── smart-quotes.test.js
│ │ ├── standardPrompt.test.js
│ │ ├── stringUtils.test.js
│ │ ├── tagUtils.test.js
│ │ ├── template-error-handling.test.js
│ │ ├── template-preprocessing.test.js
│ │ ├── template-preprocessor-regression.test.js
│ │ ├── template-preprocessor.test.js
│ │ ├── template-render-preprocessor.test.js
│ │ ├── templateManager.test.js
│ │ ├── templateRenderer.test.js
│ │ ├── templateUtils.test.js
│ │ ├── templateVariableAssignment.test.js
│ │ ├── templateVariableValidation.test.js
│ │ ├── templating.test.js
│ │ ├── testUtils.js
│ │ ├── time-module.test.js
│ │ ├── unquotedParameterTest.test.js
│ │ ├── variableAssignmentQuotesBug.test.js
│ │ ├── web-api-tests.test.js
│ │ ├── web-await-tests.test.js
│ │ └── web-module.test.js
│ ├── docs/
│ │ ├── AddingNewPromptCommands.md
│ │ ├── PromptCommandBarForms.md
│ │ ├── PromptCommands.md
│ │ ├── PromptTagsList.md
│ │ └── PromptTestingSummary.md
│ ├── lib/
│ │ ├── NPTemplateNoteHelpers.js
│ │ ├── NPTemplating.js
│ │ ├── REFACTORING_PLAN.md
│ │ ├── REFACTORING_SUMMARY.md
│ │ ├── TemplatingEngine.js
│ │ ├── config/
│ │ │ ├── configManager.js
│ │ │ └── index.js
│ │ ├── core/
│ │ │ ├── index.js
│ │ │ ├── tagUtils.js
│ │ │ └── templateManager.js
│ │ ├── engine/
│ │ │ ├── aiAnalyzer.js
│ │ │ ├── errorProcessor.js
│ │ │ ├── frontmatterProcessor.js
│ │ │ ├── pluginIntegrator.js
│ │ │ ├── renderOrchestrator.js
│ │ │ └── templateRenderer.js
│ │ ├── globals.js
│ │ ├── handlers/
│ │ │ └── index.js
│ │ ├── helpers.js
│ │ ├── rendering/
│ │ │ ├── __tests__/
│ │ │ │ └── templateProcessor.test.js
│ │ │ ├── errorHandler.js
│ │ │ ├── index.js
│ │ │ ├── templateProcessor.js
│ │ │ └── templateValidator.js
│ │ ├── shared/
│ │ │ └── templateUtils.js
│ │ ├── support/
│ │ │ ├── ejs.js
│ │ │ └── modules/
│ │ │ ├── DateModule.js
│ │ │ ├── FrontmatterModule.js
│ │ │ ├── NoteModule.js
│ │ │ ├── SystemModule.js
│ │ │ ├── TasksModule.js
│ │ │ ├── TimeModule.js
│ │ │ ├── UtilityModule.js
│ │ │ ├── WebModule.js
│ │ │ ├── advice.js
│ │ │ ├── affirmation.js
│ │ │ ├── data/
│ │ │ │ ├── adviceData.js
│ │ │ │ ├── affirmations.js
│ │ │ │ ├── service.js
│ │ │ │ └── stoicQuotes.js
│ │ │ ├── helpers-example.md
│ │ │ ├── helpersModule.js
│ │ │ ├── journal.js
│ │ │ ├── notePlanWeather.js
│ │ │ ├── prompts/
│ │ │ │ ├── AddingNewPromptCommands.md
│ │ │ │ ├── BasePromptHandler.js
│ │ │ │ ├── PromptDateHandler.js
│ │ │ │ ├── PromptDateIntervalHandler.js
│ │ │ │ ├── PromptFormHandler.js
│ │ │ │ ├── PromptKeyHandler.js
│ │ │ │ ├── PromptManager.js
│ │ │ │ ├── PromptMentionHandler.js
│ │ │ │ ├── PromptRegistry.js
│ │ │ │ ├── PromptTagHandler.js
│ │ │ │ ├── README.md
│ │ │ │ ├── StandardPromptHandler.js
│ │ │ │ ├── handlers/
│ │ │ │ │ └── index.js
│ │ │ │ ├── index.js
│ │ │ │ ├── promptFormBatch.js
│ │ │ │ ├── promptTagParse.js
│ │ │ │ ├── promptTypesRegistry.js
│ │ │ │ └── sharedPromptFunctions.js
│ │ │ ├── quote.js
│ │ │ ├── stoicQuotes.js
│ │ │ ├── verse.js
│ │ │ ├── weather.js
│ │ │ ├── weatherSummary.js
│ │ │ └── wotd.js
│ │ ├── toolbox_old.js
│ │ └── utils/
│ │ ├── codeProcessing.js
│ │ ├── dateHelpers.js
│ │ ├── errorHandling.js
│ │ ├── index.js
│ │ ├── pluginIntegration.js
│ │ └── stringUtils.js
│ ├── plugin.json
│ ├── plugins/
│ │ ├── BiblePlugin.js
│ │ └── WeatherPlugin.js
│ ├── samples/
│ │ ├── Sample Template.md
│ │ ├── Test (Execute Quick).md
│ │ ├── Test (Execute).md
│ │ ├── Test (Include).md
│ │ ├── Test (Snippets).md
│ │ ├── Test Note Included.md
│ │ ├── miscellaneous/
│ │ │ └── Restaurants.md
│ │ ├── prompt-edge-cases/
│ │ │ └── README.md
│ │ ├── section1.md
│ │ └── snippets/
│ │ ├── README.md
│ │ ├── strings-obj.md
│ │ └── strings.md
│ └── src/
│ ├── NPTemplateRunner.js
│ ├── Templating.js
│ ├── commands.js
│ └── index.js
├── np.ThemeChooser/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── themeHelpers.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPThemeChooser.js
│ ├── NPThemeCustomizer.js
│ ├── NPThemeHTML.js
│ ├── NPThemeHooks.js
│ ├── NPThemePresets.js
│ ├── NPThemeShared.js
│ ├── chooseColor.js
│ ├── index.js
│ └── support/
│ ├── masterTheme.json
│ └── themeHelpers.js
├── np.Tidy/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── cancelIncompleteTasks.test.js
│ │ ├── emptyBlocks.test.js
│ │ └── topLevelTasks.test.js
│ ├── plugin.json
│ └── src/
│ ├── cancelIncompleteTasks.js
│ ├── cleanFilenames.js
│ ├── conflicts.js
│ ├── doubledNotes.js
│ ├── duplicates.js
│ ├── emptyElements.js
│ ├── fileRoot.js
│ ├── index.js
│ ├── lineLinks.js
│ ├── missingDailyNotes.js
│ ├── tidyHelpers.js
│ ├── tidyMain.js
│ ├── tidyRemoveSections.js
│ ├── tidyRepeats.js
│ ├── tidyStubs.js
│ ├── topLevelTasks.js
│ └── triggersHooks.js
├── np.WeatherLookup/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ └── utils.test.js
│ ├── plugin.json
│ └── src/
│ ├── NPWeatherLookup.js
│ ├── index.js
│ └── support/
│ ├── old-weather-for-reference.txt
│ └── weather-utils.js
├── np.installer/
│ └── plugin.json
├── np.plugin-test/
│ ├── README.md
│ ├── changelog.md
│ ├── plugin.json
│ ├── requiredFiles/
│ │ └── css.plugin.css
│ └── src/
│ ├── commandListGenerator.js
│ ├── index.js
│ ├── pluginCommandsPopup.js
│ ├── pluginTester.js
│ └── react/
│ ├── Button.jsx
│ ├── Checkbox.jsx
│ ├── CompositeLineExample.jsx
│ ├── PluginListingPage.jsx
│ ├── WebView.jsx
│ ├── __test__/
│ │ └── filterFunctions.test.js
│ └── support/
│ ├── filterFunctions.jsx
│ ├── performRollup.node.js
│ └── rollup.WebView.entry.js
├── np.statistics/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── plugin.json
│ └── src/
│ ├── index.js
│ ├── showNoteCount.js
│ ├── showWordCount.js
│ └── taskNoteStats.js
├── package.json
├── plugins.config.js
├── scripts/
│ ├── __tests__/
│ │ ├── releases.test.js
│ │ └── rollup.generic.tes.js
│ ├── generateDocs.js
│ ├── releases.js
│ ├── rollup.generic.js
│ ├── rollup.js
│ └── shared.js
├── shared.AI/
│ ├── CHANGELOG.md
│ ├── README.md
│ ├── __tests__/
│ │ ├── NPBulletsAI-Main.test.js
│ │ ├── externalFileInteractions.test.js
│ │ └── helpers.test.js
│ ├── non-implemented_functions.js
│ ├── plugin.json
│ └── src/
│ ├── BulletsAI-Main.js
│ ├── NPAI.js
│ ├── chat.js
│ ├── imageAI.js
│ ├── index.js
│ ├── summarize.js
│ └── support/
│ ├── .readme_text/
│ │ ├── commands.md
│ │ ├── gettingstarted.md
│ │ └── preferences.md
│ ├── AIFlowTypes.js
│ ├── externalFileInteractions.js
│ ├── fetchOverrides.js
│ ├── fetchResponses/
│ │ ├── completions.heatTransfer.json
│ │ ├── completions.heatTransferKeyTopics.json
│ │ ├── completions.mercury.json
│ │ ├── completions.mercuryKeyTopics.json
│ │ ├── completions.thermalProtection.json
│ │ ├── completions.thermalProtectionKeyTopics.json
│ │ └── summarize_3 Little Pigs.1.json
│ ├── formatters.js
│ ├── helpers.js
│ ├── introwizard.js
│ ├── networking.js
│ ├── onboarding.js
│ ├── onboardingText.js
│ ├── prompts.js
│ └── settingsAdjustments.js
├── src/
│ ├── __tests__/
│ │ └── README.md
│ ├── commands/
│ │ ├── PluginCreate.js
│ │ ├── PluginDevelop.js
│ │ ├── PluginPullRequest.js
│ │ ├── PluginRelease.js
│ │ ├── PluginTest.js
│ │ └── support/
│ │ ├── github.js
│ │ ├── plugin-create.js
│ │ ├── plugin-info.js
│ │ ├── plugin-pull-request.js
│ │ ├── plugin-release/
│ │ │ ├── git-tasks.js
│ │ │ ├── prerequisite-tasks.js
│ │ │ ├── release-prompts.js
│ │ │ ├── release-tasks.js
│ │ │ ├── script-grep.js
│ │ │ └── update-version-tasks.js
│ │ ├── plugin-release.js
│ │ ├── plugin-test.js
│ │ ├── plugin-utils.js
│ │ └── release-management.js
│ ├── templates/
│ │ └── np.plugin.starter/
│ │ ├── CHANGELOG.md
│ │ ├── README.md
│ │ ├── __tests__/
│ │ │ └── NPPluginMain.NOTACTIVE.js
│ │ ├── plugin.json
│ │ ├── requiredFiles/
│ │ │ └── html-plugin-comms.js
│ │ └── src/
│ │ ├── NPMessagesFromHTMLWindow.js
│ │ ├── NPPluginMain.js
│ │ ├── NPTriggers-Hooks.js
│ │ ├── index.js
│ │ └── support/
│ │ ├── fetchOverrides.js
│ │ ├── fetchResponses/
│ │ │ └── google.search-for-something.json
│ │ └── helpers.js
│ └── utils/
│ ├── app.js
│ ├── general.js
│ └── security.lib.js
└── tasks/
├── bumpBuild.js
├── init.js
└── start.js
Showing preview only (2,841K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (11412 symbols across 571 files)
FILE: KimMachineGun.Raindrop/src/NPPluginMain.js
function searchAndInsertOrCopy (line 34) | async function searchAndInsertOrCopy(): Promise<void> {
function searchAndCreateNote (line 38) | async function searchAndCreateNote(): Promise<void> {
method if (line 144) | if (content) {
FILE: KimMachineGun.Raindrop/src/Raindrop.js
class Raindrop (line 21) | class Raindrop {
class Collection (line 220) | class Collection {
FILE: KimMachineGun.Raindrop/src/index.js
function onUpdateOrInstall (line 37) | async function onUpdateOrInstall(): Promise<void> {
function init (line 46) | async function init(): Promise<void> {
function onSettingsUpdated (line 61) | async function onSettingsUpdated(): Promise<void> {}
FILE: __mocks__/Backlink.mock.js
class Backlink (line 9) | class Backlink {
method children (line 75) | async children() {
method duplicate (line 78) | async duplicate() {
method init (line 81) | async init() {
FILE: __mocks__/Calendar.mock.js
method availableCalendarTitles (line 15) | availableCalendarTitles(writeOnly: boolean) {
method parseDateText (line 28) | parseDateText(str) {
method endOfWeek (line 55) | endOfWeek(date) {
method startOfWeek (line 58) | startOfWeek(date) {
method weekNumber (line 61) | weekNumber(date) {
FILE: __mocks__/CalendarItem.mock.js
class CalendarItem (line 9) | class CalendarItem {
FILE: __mocks__/CommandBar.mock.js
method hide (line 11) | async hide() {
method onAsyncThread (line 14) | async onAsyncThread() {
method onMainThread (line 17) | async onMainThread() {
method openURL (line 20) | async openURL() {
method prompt (line 24) | async prompt(title = '', message = '') {
method showInput (line 29) | async showInput(placeholder, submitText) {
method showLoading (line 33) | async showLoading(visible, text, progress) {
method showOptions (line 36) | async showOptions(options, placeholder) {
method prompt (line 39) | async prompt(title, message, buttons) {
method textPrompt (line 42) | async textPrompt(title, message, defaultText) {
FILE: __mocks__/DataStore.mock.js
method installPlugin (line 41) | async installPlugin(pluginObject, showLoading = false) {
method loadJSON (line 51) | async loadJSON(str) {
method newNote (line 55) | async newNote(title = '', folder = '') {
method switch (line 63) | switch (key) {
method saveJSON (line 101) | async saveJSON(object, filename) {
FILE: __mocks__/Editor.mock.js
method get (line 43) | get(target, prop) {
method set (line 72) | set(target, prop, value) {
FILE: __mocks__/Note.mock.js
method addBlockID (line 57) | async addBlockID(p: any) {
method addParagraphBelowHeadingTitle (line 64) | async addParagraphBelowHeadingTitle(content: string, paragraphType: stri...
method addTodoBelowHeadingTitle (line 74) | async addTodoBelowHeadingTitle(): Promise<void> {
method appendParagraph (line 77) | appendParagraph(title: string, type: ParagraphType): void {
method appendParagraphBelowHeadingLineIndex (line 81) | async appendParagraphBelowHeadingLineIndex(): Promise<void> {
method appendTodo (line 84) | async appendTodo(): Promise<void> {
method appendTodoBelowHeadingLineIndex (line 87) | async appendTodoBelowHeadingLineIndex(): Promise<void> {
method insertCancelledTodo (line 90) | async insertCancelledTodo(): Promise<void> {
method insertCompletedTodo (line 93) | async insertCompletedTodo(): Promise<void> {
method insertHeading (line 97) | insertHeading(content: string, lineIndex: number, headingLevel: number):...
method insertList (line 107) | async insertList(): Promise<void> {
method insertParagraph (line 111) | insertParagraph(content: string, lineIndex: number, type: ParagraphType)...
method insertParagraphAfterParagraph (line 123) | async insertParagraphAfterParagraph(content: string, otherParagraph: any...
method insertParagraphBeforeParagraph (line 131) | async insertParagraphBeforeParagraph(content: string, otherParagraph: an...
method insertQuote (line 139) | async insertQuote(): Promise<void> {
method insertScheduledTodo (line 142) | async insertScheduledTodo(): Promise<void> {
method insertTextAtCharacterIndex (line 145) | async insertTextAtCharacterIndex(): Promise<void> {
method insertTodo (line 148) | async insertTodo(): Promise<void> {
method insertTodoAfterParagraph (line 151) | async insertTodoAfterParagraph(): Promise<void> {
method insertTodoBeforeParagraph (line 154) | async insertTodoBeforeParagraph(): Promise<void> {
method paragraphRangeAtCharacterIndex (line 157) | async paragraphRangeAtCharacterIndex(): Promise<void> {
method prependParagraph (line 160) | async prependParagraph(content: string, type: ParagraphType) {
method prependTodo (line 165) | async prependTodo(): Promise<void> {
method printNote (line 168) | async printNote(): Promise<void> {
method removeBlockID (line 171) | async removeBlockID(p: any) {
method removeParagraph (line 176) | async removeParagraph(para: any) {
method removeParagraphAtIndex (line 180) | async removeParagraphAtIndex(): Promise<void> {
method removeParagraphs (line 183) | async removeParagraphs(paras: any[]) {
method replaceTextInCharacterRange (line 188) | async replaceTextInCharacterRange(): Promise<void> {
method updateParagraph (line 191) | async updateParagraph(para: any) {
method updateParagraphs (line 194) | async updateParagraphs(paras: any[]) {
method resetLineIndexesAndContent (line 204) | resetLineIndexesAndContent() {
method setFrontmatterAttributes (line 213) | setFrontmatterAttributes() {
method content (line 225) | get content(): string {
method content (line 233) | set content(value: string) {
FILE: __mocks__/NotePlan.mock.js
class NotePlan (line 15) | class NotePlan {
method ai (line 42) | static ai(prompt, filenames = [], useStrictFilenames = false, model = ...
method constructor (line 47) | constructor(data = {}) {
method __update (line 51) | __update(data = {}) {
FILE: __mocks__/Paragraph.mock.js
class Paragraph (line 9) | class Paragraph {
method children (line 52) | async children() {
method init (line 58) | async init() {
method switch (line 66) | switch (this.type) {
FILE: __mocks__/PluginCommandObject.mock.js
class PluginCommandObject (line 9) | class PluginCommandObject {
FILE: __mocks__/PluginObject.mock.js
class PluginObject (line 9) | class PluginObject {
FILE: __mocks__/Range.mock.js
class Range (line 9) | class Range {
FILE: __mocks__/__tests__/fetch.mock.test.js
constant PLUGIN_NAME (line 13) | const PLUGIN_NAME = `Fetch.mock`
constant FILENAME (line 14) | const FILENAME = ``
FILE: __mocks__/jestHelpers.js
function simpleFormatter (line 35) | function simpleFormatter(_type: string, message: string): string {
FILE: aaronpoweruser.ReadwiseUnofficial/src/NPReadwise.js
constant LAST_SYNC_TIME (line 9) | const LAST_SYNC_TIME = 'last_sync_time'
function readwiseSync (line 14) | async function readwiseSync(): Promise<void> {
function readwiseRebuild (line 23) | async function readwiseRebuild(): Promise<void> {
FILE: aaronpoweruser.ReadwiseUnofficial/src/NPReadwiseHelpers.js
constant READWISE_API_KEY_LENGTH (line 6) | const READWISE_API_KEY_LENGTH = 50
function checkAccessToken (line 11) | async function checkAccessToken(): Promise<void> {
function buildReadwiseNoteTitle (line 26) | function buildReadwiseNoteTitle(source: any): string {
function removeInvalidChars (line 41) | function removeInvalidChars(string: string): string {
function buildReadwiseFrontMatter (line 54) | function buildReadwiseFrontMatter(source: any): any {
function buildReadwiseMetadataHeading (line 78) | function buildReadwiseMetadataHeading(source: any): string {
function formatTag (line 97) | function formatTag(tag: string): string {
function removeNewlines (line 111) | function removeNewlines(text: string): string {
function escapeTwitterHandle (line 122) | function escapeTwitterHandle(handle: string): string {
function getLocalDate (line 133) | function getLocalDate(): string {
function getParagraphTypeChar (line 143) | function getParagraphTypeChar(): string {
FILE: aaronpoweruser.ReadwiseUnofficial/src/NPReadwiseNotes.js
function appendHighlightToNote (line 74) | function appendHighlightToNote(outputNote: TNote, highlight: any, catego...
FILE: aaronpoweruser.ReadwiseUnofficial/src/NPReadwiseSyncLog.js
constant SYNC_LOG_TOKEN (line 5) | const SYNC_LOG_TOKEN = 'readWiseToken'
constant SYNC_NOTE_TITLE (line 6) | const SYNC_NOTE_TITLE = 'Readwise Syncs'
function startReadwiseSyncLog (line 8) | async function startReadwiseSyncLog(): Promise<void> {
function writeReadwiseSyncLogLine (line 15) | async function writeReadwiseSyncLogLine(title: string, count: number): P...
function finishReadwiseSyncLog (line 22) | async function finishReadwiseSyncLog(downloadHighlightCount: number, upd...
FILE: aaronpoweruser.ReadwiseUnofficial/src/NPTriggers-Hooks.js
function onOpen (line 23) | async function onOpen(note: TNote): Promise<void> {
function onEditorWillSave (line 48) | async function onEditorWillSave() {
function onUpdateOrInstall (line 73) | async function onUpdateOrInstall(): Promise<void> {
function init (line 82) | function init(): void {
function onSettingsUpdated (line 92) | async function onSettingsUpdated(): Promise<void> {
FILE: codedungeon.Toolbox/src/convertSelectionToHtml.js
function convertSelectionToHtml (line 10) | async function convertSelectionToHtml(): Promise<void> {
FILE: codedungeon.Toolbox/src/convertToHtml.js
function convertToHtml (line 10) | async function convertToHtml(): Promise<void> {
FILE: codedungeon.Toolbox/src/convertToRtf.js
function convertToRtf (line 10) | async function convertToRtf(): Promise<void> {
FILE: codedungeon.Toolbox/src/reorderList.js
function reorderList (line 9) | async function reorderList(): Promise<void> {
FILE: codedungeon.Toolbox/src/support/CodedungeonToolbox.js
class CodedungeonToolbox (line 29) | class CodedungeonToolbox {
method markdownToHtml (line 30) | markdownToHtml(text = '', options = { removeAttributes: true }) {
method markdownToRtf (line 40) | async markdownToRtf(markdownText = '') {
method reorderList (line 93) | reorderList(listData = '') {
FILE: dbludeau.TodoistNoteplanSync/requiredFiles/html-plugin-comms.js
function onMessageFromPlugin (line 30) | function onMessageFromPlugin(type, data) {
function onUpdateDivReceived (line 51) | function onUpdateDivReceived(data) {
function onClickStatus (line 73) | function onClickStatus(filename, lineIndex, statusWas, lineID) {
function replaceHTML (line 89) | function replaceHTML(divID, html, innerText) {
function showError (line 101) | function showError(message) {
FILE: dbludeau.TodoistNoteplanSync/src/NPPluginMain.js
method newToken (line 70) | set newToken(passedToken: string) {
method newFolder (line 76) | set newFolder(passedFolder: string) {
method syncDates (line 85) | set syncDates(passedSyncDates: boolean) {
method syncPriorities (line 91) | set syncPriorities(passedSyncPriorities: true) {
method syncTags (line 97) | set syncTags(passedSyncTags: boolean) {
method teamAccount (line 103) | set teamAccount(passedTeamAccount: boolean) {
method syncUnassigned (line 109) | set syncUnassigned(passedSyncUnassigned: boolean) {
method newHeader (line 115) | set newHeader(passedHeader: string) {
function syncEverything (line 142) | async function syncEverything() {
method if (line 392) | if (setup.addUnassigned) {
method if (line 482) | if (task.priority === 4) {
function setSettings (line 518) | function setSettings() {
function writeOutTask (line 569) | async function writeOutTask(note: TNote, task: Object) {
function getRequestObject (line 630) | function getRequestObject() {
function postRequestObject (line 646) | function postRequestObject() {
function getExistingNote (line 662) | function getExistingNote(project_name: string): Object {
function reviewExistingNoteplanTasks (line 685) | function reviewExistingNoteplanTasks(note: TNote) {
function closeTodoistTask (line 719) | async function closeTodoistTask(task_id: string) {
FILE: dbludeau.TodoistNoteplanSync/src/NPTriggers-Hooks.js
function onOpen (line 23) | async function onOpen(note: TNote): Promise<void> {
function onEditorWillSave (line 48) | async function onEditorWillSave() {
function onUpdateOrInstall (line 73) | async function onUpdateOrInstall(): Promise<void> {
function init (line 82) | function init(): void {
function onSettingsUpdated (line 92) | async function onSettingsUpdated(): Promise<void> {
FILE: dbludeau.TodoistNoteplanSync/src/support/helpers.js
function uppercase (line 9) | function uppercase(str: string = ''): string {
FILE: deleteme.testPluginDownload/src/index.js
function onUpdateOrInstall (line 11) | async function onUpdateOrInstall(): Promise<void> {
function runOnInstallOrUpdate (line 20) | async function runOnInstallOrUpdate(): Promise<void> {
FILE: docs/custom_theme/assets/anchor.js
function AnchorJS (line 26) | function AnchorJS(options) {
FILE: docs/custom_theme/assets/site.js
function toggleSibling (line 54) | function toggleSibling() {
function showHashTarget (line 67) | function showHashTarget(targetId) {
function scrollIntoView (line 81) | function scrollIntoView(targetId) {
function gotoCurrentTarget (line 91) | function gotoCurrentTarget() {
function preOpen (line 104) | function preOpen() {
function updateState (line 134) | function updateState() {
function loadState (line 144) | function loadState(ev) {
FILE: docs/custom_theme/assets/split.js
function setElementSize (line 228) | function setElementSize(el, size, gutSize, i) {
function setGutterSize (line 241) | function setGutterSize(gutterElement, gutSize, i) {
function getSizes (line 250) | function getSizes() {
function getMousePosition (line 256) | function getMousePosition(e) {
function adjust (line 267) | function adjust(offset) {
function drag (line 293) | function drag(e) {
function calculateSizes (line 345) | function calculateSizes() {
function innerSize (line 362) | function innerSize(element) {
function trimToMin (line 392) | function trimToMin(sizesToTrim) {
function stopDragging (line 460) | function stopDragging() {
function startDragging (line 505) | function startDragging(e) {
function adjustToMin (line 687) | function adjustToMin(element) {
function setSizes (line 713) | function setSizes(newSizes) {
function destroy (line 731) | function destroy(preserveStyles, preserveGutter) {
FILE: docs/custom_theme/index.js
function copyDir (line 14) | async function copyDir(sorce, dest) {
function isFunction (line 26) | function isFunction(section) {
function getSlug (line 33) | function getSlug(str) {
method slug (line 51) | slug(str) {
method shortSignature (line 54) | shortSignature(section) {
method signature (line 63) | signature(section) {
method md (line 76) | md(ast, inline) {
method highlight (line 87) | highlight(example) {
FILE: dwertheimer.DateAutomations/src/dateFunctions.js
function getDateConfig (line 37) | function getDateConfig(): DateConfig {
function getFormattedDateTime (line 65) | function getFormattedDateTime() {
function insertISODate (line 137) | function insertISODate() {
function insertDate (line 143) | function insertDate() {
function insertDateTime (line 151) | function insertDateTime() {
function get8601String (line 162) | function get8601String(): string {
function insertDateTime8601 (line 167) | function insertDateTime8601() {
function insertTime (line 172) | function insertTime() {
function insertCalendarNoteLink (line 183) | function insertCalendarNoteLink() {
function dateFormatPicker (line 188) | async function dateFormatPicker() {
function insertStrftime (line 199) | function insertStrftime() {
function insertWeekDates (line 237) | async function insertWeekDates() {
FILE: dwertheimer.DateAutomations/src/index.js
constant PLUGIN_ID (line 17) | const PLUGIN_ID = 'date' // the key that's used in _configuration note
function onUpdateOrInstall (line 18) | async function onUpdateOrInstall(config: any = { silent: false }): Promi...
FILE: dwertheimer.EventAutomations/__tests__/by_timeblock_tag.test.js
constant PLUGIN_NAME (line 10) | const PLUGIN_NAME = `dwertheimer.EventAutomations`
constant FILENAME (line 11) | const FILENAME = `timeblocking-helpers.js`
FILE: dwertheimer.EventAutomations/__tests__/timeblocking-helpers.test.js
constant PLUGIN_NAME (line 22) | const PLUGIN_NAME = `dwertheimer.EventAutomations`
FILE: dwertheimer.EventAutomations/__tests__/timeblocking-shared.test.js
constant PLUGIN_NAME (line 9) | const PLUGIN_NAME = `dwertheimer.EventAutomations`
constant FILENAME (line 10) | const FILENAME = `timeblocking-shared.js`
FILE: dwertheimer.EventAutomations/src/NPEventBlocks.js
function parseDateTextChecker (line 54) | function parseDateTextChecker() {
function getPluginSettings (line 97) | function getPluginSettings(): EventBlocksConfig {
FILE: dwertheimer.EventAutomations/src/NPTimeblocking.js
function getExistingTimeBlocksFromNoteAsEvents (line 65) | function getExistingTimeBlocksFromNoteAsEvents(note: CoreNoteFields, def...
function deleteCalendarEventsWithTag (line 117) | async function deleteCalendarEventsWithTag(tag: string, dateStr: string)...
method logDebug (line 192) | logDebug(pluginJson, `timeBlockTag: ("${timeBlockTag}"), Editor.paras=${...
method if (line 279) | if (!passBackResults) {
method for (line 418) | for (let index = startIndex; index <= endIndex; index++) {
method if (line 477) | if (todosWithLinks.length === 0) {
FILE: dwertheimer.EventAutomations/src/config.js
function getTimeBlockingDefaults (line 6) | function getTimeBlockingDefaults(): AutoTimeBlockingConfig {
function validateAutoTimeBlockingConfig (line 41) | function validateAutoTimeBlockingConfig(config: AutoTimeBlockingConfig):...
FILE: dwertheimer.EventAutomations/src/events.js
function getTimeOffset (line 8) | function getTimeOffset(offset: HourMinObj = { h: 0, m: 0 }) {
function createNoteForCalendarItemWithQuickTemplate (line 23) | async function createNoteForCalendarItemWithQuickTemplate(): Promise<voi...
function createNoteForCalendarItemWithoutQuickTemplate (line 27) | async function createNoteForCalendarItemWithoutQuickTemplate(): Promise<...
function createNoteForCalendarItem (line 31) | async function createNoteForCalendarItem(useQuickTemplate: boolean = tru...
FILE: dwertheimer.EventAutomations/src/index.js
constant PLUGIN_ID (line 21) | const PLUGIN_ID = 'autoTimeBlocking' // the key that's used in _configur...
function onUpdateOrInstall (line 23) | async function onUpdateOrInstall(): Promise<void> {
function onSettingsUpdated (line 35) | function onSettingsUpdated() {}
function init (line 37) | function init(): void {
FILE: dwertheimer.EventAutomations/src/presets.js
function setConfigForPreset (line 12) | function setConfigForPreset(config, preset) {
function getPreset (line 29) | function getPreset(config) {
FILE: dwertheimer.EventAutomations/src/timeblocking-helpers.js
function getBlankDayMap (line 53) | function getBlankDayMap(intervalMins: number): IntervalMap {
function blockTimeFor (line 57) | function blockTimeFor(timeMap: IntervalMap, blockdata: BlockData, config...
method catch (line 254) | catch (error) {
method catch (line 333) | catch (error) {
method if (line 731) | if (Array.isArray(pattern)) {
method if (line 752) | if (sortList && paragraphs) {
FILE: dwertheimer.EventAutomations/src/timeblocking-shared.js
function createSyncedCopies (line 50) | async function createSyncedCopies(todos: Array<SortableParagraphSubset>,...
FILE: dwertheimer.Favorites/__tests__/NPFavorites.test.js
constant PLUGIN_NAME (line 26) | const PLUGIN_NAME = `Favorites`
constant FILENAME (line 27) | const FILENAME = `NPFavorites`
FILE: dwertheimer.Favorites/src/NPFavoritePresets.js
constant COMMAND_NAME_TEMPLATE (line 9) | const COMMAND_NAME_TEMPLATE = 'Favorites: Set Preset '
function favoritePresetChosen (line 45) | async function favoritePresetChosen(commandDetails: PresetCommand | null...
function changePreset (line 115) | async function changePreset(incoming: string) {
FILE: dwertheimer.Favorites/src/NPFavorites.js
method if (line 21) | if (!config.favoriteKey || [' ', '\t', '#', '@'].some((char) => config.f...
function setFavorite (line 38) | async function setFavorite(): Promise<void> {
function openFavorite (line 71) | async function openFavorite(): Promise<void> {
function removeFavorite (line 94) | async function removeFavorite(): Promise<void> {
FILE: dwertheimer.Favorites/src/components/FavoritesView.jsx
constant IDLE_TIMEOUT_MS (line 23) | const IDLE_TIMEOUT_MS = 60000 // 1 minute
FILE: dwertheimer.Favorites/src/favoritesRouter.js
constant FAVORITES_BROWSER_WINDOW_ID (line 24) | const FAVORITES_BROWSER_WINDOW_ID = 'favorites-browser-window'
method switch (line 33) | switch (actionType) {
FILE: dwertheimer.Favorites/src/index.js
function onUpdateOrInstall (line 43) | async function onUpdateOrInstall(): Promise<void> {
function init (line 53) | function init(): void {
function onSettingsUpdated (line 58) | function onSettingsUpdated(): void {
FILE: dwertheimer.Favorites/src/windowManagement.js
constant REACT_WINDOW_TITLE (line 11) | const REACT_WINDOW_TITLE = 'Favorites'
constant FAVORITES_BROWSER_WINDOW_ID (line 12) | const FAVORITES_BROWSER_WINDOW_ID = 'favorites-browser-window'
function createWindowInitData (line 30) | function createWindowInitData(showFloating: boolean, windowId: string): ...
function getPluginData (line 59) | function getPluginData(showFloating: boolean, windowId: string): { [stri...
FILE: dwertheimer.Forms/src/FormFieldRenderTest.js
function testFormFieldRender (line 22) | async function testFormFieldRender(): Promise<void> {
FILE: dwertheimer.Forms/src/NPTemplateForm.js
method if (line 302) | if (templateNote) {
FILE: dwertheimer.Forms/src/NPTriggers-Hooks.js
function onOpen (line 24) | async function onOpen(note: TNote): Promise<void> {
function onEditorWillSave (line 49) | async function onEditorWillSave() {
function onUpdateOrInstall (line 74) | async function onUpdateOrInstall(): Promise<void> {
function init (line 88) | function init(): void {
function onSettingsUpdated (line 102) | async function onSettingsUpdated(): Promise<void> {
function versionCheck (line 113) | async function versionCheck(): Promise<void> {
FILE: dwertheimer.Forms/src/components/FieldEditor.jsx
function isValidCSSWidth (line 25) | function isValidCSSWidth(value: string): boolean {
FILE: dwertheimer.Forms/src/components/FieldTypeSelector.jsx
function FieldTypeSelector (line 16) | function FieldTypeSelector({ isOpen, onSelect, onClose }: FieldTypeSelec...
FILE: dwertheimer.Forms/src/components/FormFieldsList.jsx
function FormFieldsList (line 27) | function FormFieldsList({
FILE: dwertheimer.Forms/src/components/FormPreview.jsx
function getScreenDimensions (line 38) | function getScreenDimensions(): { width: number, height: number }
method if (line 58) | if (typeof value === 'number') {
method if (line 96) | if (intendedSize === null || intendedSize === undefined) {
FILE: dwertheimer.Forms/src/components/FormSettings.jsx
function FormSettings (line 35) | function FormSettings({
FILE: dwertheimer.Forms/src/components/FormView.jsx
constant DATA_LOAD_COMMANDS (line 46) | const DATA_LOAD_COMMANDS = ['getFolders', 'getTeamspaces', 'getNotes', '...
function FormView (line 57) | function FormView({ data, dispatch, reactSettings, setReactSettings, onS...
function decodeHTMLEntities (line 878) | function decodeHTMLEntities(text: string): string {
FILE: dwertheimer.Forms/src/components/PositionInput.jsx
function PositionInput (line 27) | function PositionInput({
FILE: dwertheimer.Forms/src/components/ProcessingMethodSection.jsx
function ProcessingMethodSection (line 46) | function ProcessingMethodSection({
FILE: dwertheimer.Forms/src/components/TemplateTagEditor.jsx
method if (line 459) | if (i === clickedIndex) {
FILE: dwertheimer.Forms/src/components/TemplateTagInserter.jsx
function TemplateTagInserter (line 38) | function TemplateTagInserter({
FILE: dwertheimer.Forms/src/components/ValueInsertButtons.jsx
function ValueInsertButtons (line 26) | function ValueInsertButtons({
FILE: dwertheimer.Forms/src/dataHandlers.js
method if (line 158) | if (spaceId !== '') {
method if (line 193) | if (spaceId !== '') {
FILE: dwertheimer.Forms/src/formBrowserHandlers.js
method openFormBuilder (line 562) | openFormBuilder(templateTitle)
FILE: dwertheimer.Forms/src/formBrowserRouter.js
constant FORM_BROWSER_WINDOW_ID (line 13) | const FORM_BROWSER_WINDOW_ID = 'forms-chooser-window'
method switch (line 23) | switch (actionType) {
FILE: dwertheimer.Forms/src/formBuilderHandlers.js
constant FORM_FRONTMATTER_ALLOWLIST (line 57) | const FORM_FRONTMATTER_ALLOWLIST = [
method if (line 145) | if (filename) {
FILE: dwertheimer.Forms/src/formSubmission.js
function resolveShouldOpenInEditor (line 21) | function resolveShouldOpenInEditor(value: any): boolean {
function isValidIdentifier (line 31) | function isValidIdentifier(key: string): boolean {
function generateKeyFromLabel (line 42) | function generateKeyFromLabel(label: string, index: number): string {
function deepSanitizeNulls (line 68) | function deepSanitizeNulls(obj: any): any {
method catch (line 153) | catch (e) {
method if (line 176) | if (!formFields || formFields.length === 0) {
method if (line 1136) | if (!Array.isArray(formFields) || formFields.length === 0) {
FILE: dwertheimer.Forms/src/formSubmitHandlers.js
method if (line 80) | if (windowId !== WEBVIEW_WINDOW_ID) {
method if (line 116) | if (!formTemplateFilename || !formTemplateFilename.trim()) {
method logDebug (line 268) | logDebug(pluginJson, `[BACK-END] handleFormSubmitAction: Called from fro...
FILE: dwertheimer.Forms/src/formSubmitRouter.js
method if (line 91) | if (actionType === 'submitForm') {
FILE: dwertheimer.Forms/src/noteHelpers.js
method if (line 89) | if (!Array.isArray(notes) || notes.length === 0) {
method if (line 175) | if (!rd.relName || !rd.dateStr) {
FILE: dwertheimer.Forms/src/requestHandlers.js
function testRequestHandlers (line 57) | async function testRequestHandlers(): Promise<void> {
function getAvailableReminderLists (line 147) | function getAvailableReminderLists(_params: Object = {}): RequestResponse {
function removeEmptyLinesFromNote (line 724) | function removeEmptyLinesFromNote(note: any): void {
function updateFormLinksInNote (line 752) | async function updateFormLinksInNote(
FILE: dwertheimer.Forms/src/shared/constants.js
constant FORMBUILDER_WINDOW_ID (line 13) | const FORMBUILDER_WINDOW_ID = `${pluginJson['plugin.id']} Form Builder R...
constant WEBVIEW_WINDOW_ID (line 18) | const WEBVIEW_WINDOW_ID = `${pluginJson['plugin.id']} Form Entry React W...
FILE: dwertheimer.Forms/src/templateIO.js
method if (line 51) | if (!templateTitle || !templateTitle.trim()) {
method catch (line 275) | catch (error) {
method catch (line 336) | catch (error) {
FILE: dwertheimer.Forms/src/utils/encodingFix.js
function isDoubleEncoded (line 13) | function isDoubleEncoded(str: string): boolean {
function fixDoubleEncoded (line 79) | function fixDoubleEncoded(str: string): string {
FILE: dwertheimer.Forms/src/windowManagement.js
constant REACT_WINDOW_TITLE (line 23) | const REACT_WINDOW_TITLE = 'Form View'
method if (line 68) | if (formTitle) {
method if (line 88) | if (win.customId === WEBVIEW_WINDOW_ID) {
method if (line 104) | if (typeof value === 'number') {
method if (line 116) | if (trimmedValue === 'left') {
method if (line 122) | if (trimmedValue === 'top') {
method if (line 416) | if (!needsEvents) {
method if (line 458) | if (!frontmatterKeys || frontmatterKeys.length === 0) {
method if (line 743) | if (templateNote.filename?.startsWith('%%NotePlanCloud%%')) {
method if (line 752) | if (argObj.initialReceivingTemplateTitle) {
FILE: dwertheimer.JestHelpers/src/NPPluginMain.js
function createMockOutput (line 29) | function createMockOutput(object: any, name: string): void {
method catch (line 156) | catch (error) {
FILE: dwertheimer.JestHelpers/src/index.js
function onUpdateOrInstall (line 37) | async function onUpdateOrInstall(): Promise<void> {
function init (line 45) | async function init(): Promise<void> {
function onSettingsUpdated (line 54) | async function onSettingsUpdated(): Promise<void> {}
FILE: dwertheimer.JestHelpers/src/support/helpers.js
function uppercase (line 10) | function uppercase(str: string = ''): string {
FILE: dwertheimer.MathSolver/src/NPMathBlocks.js
function getFrontmatterVariables (line 48) | function getFrontmatterVariables(noteContent: string): any {
function annotateResults (line 149) | function annotateResults(note: CoreNoteFields, blockData: $ReadOnly<Code...
function showResultsInPopup (line 227) | async function showResultsInPopup(results: Array<LineInfo>, template: st...
method if (line 246) | if (Editor) {
FILE: dwertheimer.MathSolver/src/index.js
function onUpdateOrInstall (line 33) | async function onUpdateOrInstall(): Promise<void> {
function init (line 42) | async function init(): Promise<void> {
function onSettingsUpdated (line 56) | async function onSettingsUpdated(): Promise<void> {}
FILE: dwertheimer.MathSolver/src/support/date-time-math.js
function checkForTime (line 4) | function checkForTime(strToBeParsed, currentData) {
FILE: dwertheimer.MathSolver/src/support/helpers.js
function uppercase (line 10) | function uppercase(str: string = ''): string {
FILE: dwertheimer.MathSolver/src/support/solver.js
method if (line 238) | if (info[i].typeOfResult === 'N') {
method if (line 249) | if (info[i].typeOfResult === 'N') {
FILE: dwertheimer.ReactSkeleton/src/index.js
function init (line 24) | function init(): void {
function onSettingsUpdated (line 30) | async function onSettingsUpdated(): Promise<void> {
function onUpdateOrInstall (line 34) | function onUpdateOrInstall(): void {
FILE: dwertheimer.ReactSkeleton/src/react/components/Button.jsx
function Button (line 18) | function Button(props: Props): any {
FILE: dwertheimer.ReactSkeleton/src/react/components/WebView.jsx
method if (line 282) | if (!newData) {
method if (line 285) | if (Object.keys(newData).length < Object.keys(pluginData).length) {
FILE: dwertheimer.ReactSkeleton/src/reactMain.js
constant WEBVIEW_WINDOW_ID (line 9) | const WEBVIEW_WINDOW_ID = `${pluginJson['plugin.id']} React Window` // w...
constant REACT_WINDOW_TITLE (line 11) | const REACT_WINDOW_TITLE = 'React View Skeleton Test' // change this to ...
function getInitialDataForReactWindow (line 29) | function getInitialDataForReactWindow(): PassedData {
method if (line 76) | if (!getWindowFromId(WEBVIEW_WINDOW_ID)) {
function openReactWindow (line 88) | async function openReactWindow(): Promise<void> {
FILE: dwertheimer.ReactSkeleton/src/requestHandlers.js
method catch (line 139) | catch (error) {
FILE: dwertheimer.ReactSkeleton/src/router.js
constant WEBVIEW_WINDOW_ID (line 15) | const WEBVIEW_WINDOW_ID = `${pluginJson['plugin.id']} React Window`
FILE: dwertheimer.TaskAutomations/__tests__/NPOverdueReact.test.js
constant PLUGIN_NAME (line 7) | const PLUGIN_NAME = `dwertheimer.React`
constant FILENAME (line 8) | const FILENAME = `NPOverdueReact`
FILE: dwertheimer.TaskAutomations/__tests__/NPTaskScanAndProcess.test.js
constant PLUGIN_NAME (line 20) | const PLUGIN_NAME = `dwertheimer.TaskAutomations`
constant FILENAME (line 21) | const FILENAME = `NPTaskScanAndProcess`
FILE: dwertheimer.TaskAutomations/__tests__/lastUsedChoices.test.js
constant PLUGIN_NAME (line 17) | const PLUGIN_NAME = `dwertheimer.TaskAutomations`
constant FILENAME (line 18) | const FILENAME = `lastUsedChoices`
FILE: dwertheimer.TaskAutomations/src/NPFollowUp.js
method if (line 147) | if (saveHere) {
FILE: dwertheimer.TaskAutomations/src/NPOverdue.js
function askToReviewWeeklyTasks (line 20) | async function askToReviewWeeklyTasks(byTask: boolean = false, forDateSt...
function askToReviewTodaysTasks (line 42) | async function askToReviewTodaysTasks(byTask?: boolean = false, forDateS...
function askToReviewForgottenTasks (line 65) | async function askToReviewForgottenTasks(byTask: boolean = false, ending...
function updateDatePlusTags (line 95) | async function updateDatePlusTags(incoming: string): Promise<void> {
method catch (line 194) | catch (error) {
method switch (line 208) | switch (_dateString) {
method logDebug (line 338) | logDebug(pluginJson, `reviewOverdueTasksInFolder: incoming="${incoming}"...
FILE: dwertheimer.TaskAutomations/src/NPOverdueReact.js
constant DEBUG (line 31) | const DEBUG = true
constant WEBVIEW_WINDOW_ID (line 32) | const WEBVIEW_WINDOW_ID = 'TaskAutomations.Overdue'
function createOptionChoice (line 39) | function createOptionChoice(value: string): CommandBarChoice {
method logDebug (line 52) | logDebug(pluginJson, `finalizeChanges ${JSON.stringify(result)}`)
function updateRowDataAndSend (line 214) | async function updateRowDataAndSend(updateInfo: any, updateText: string ...
method if (line 309) | if (!returnValue?.updatedRows?.length) {
method if (line 411) | if (!flatParaList || flatParaList.length === 0) {
FILE: dwertheimer.TaskAutomations/src/NPTaskScanAndProcess.js
constant CONTINUE (line 59) | const CONTINUE = 1
constant CANCEL (line 60) | const CANCEL = -2
constant SEE_TASK_AGAIN (line 61) | const SEE_TASK_AGAIN = 0
function updateParagraph (line 68) | function updateParagraph(origPara): void {
method if (line 84) | if (userChoice.length && typeof userChoice === 'string' && userChoice[0]...
method if (line 514) | if (foldersToIgnore.includes(noteFolder)) {
method for (line 536) | for (const n of notesWithDates) {
FILE: dwertheimer.TaskAutomations/src/index.js
function init (line 32) | function init(): void {
function onSettingsUpdated (line 38) | async function onSettingsUpdated(): Promise<void> {
function onUpdateOrInstall (line 42) | function onUpdateOrInstall(): void {
function testOnUpdateOrInstall (line 47) | async function testOnUpdateOrInstall(): Promise<void> {
FILE: dwertheimer.TaskAutomations/src/lastUsedChoices.js
function updateLastUsedChoices (line 102) | function updateLastUsedChoices(commandBarSelection: CommandBarSelection)...
FILE: dwertheimer.TaskAutomations/src/react/Button.jsx
function Button (line 6) | function Button(props) {
FILE: dwertheimer.TaskAutomations/src/react/ThemedSelect.jsx
function ThemedSelect (line 230) | function ThemedSelect(props: Props): any {
FILE: dwertheimer.TaskAutomations/src/react/TypeFilter.jsx
function TypeFilter (line 11) | function TypeFilter(props: Props): any {
FILE: dwertheimer.TaskAutomations/src/react/WebView.jsx
method logDebug (line 235) | logDebug(`Webview: highlightAndSend rowIds:${rowIDs.toString()} action:$...
FILE: dwertheimer.TaskSorting/__tests__/sortTasks.test.js
constant PLUGIN_NAME (line 21) | const PLUGIN_NAME = `dwertheimer.TaskAutomations`
constant FILENAME (line 22) | const FILENAME = `sortTasks`
FILE: dwertheimer.TaskSorting/__tests__/tagTasks.test.js
constant PLUGIN_NAME (line 23) | const PLUGIN_NAME = `${colors.yellow('dwertheimer.TaskAutomations')}`
FILE: dwertheimer.TaskSorting/requiredFiles/html-plugin-comms.js
function onMessageFromPlugin (line 30) | function onMessageFromPlugin(type, data) {
function onUpdateDivReceived (line 51) | function onUpdateDivReceived(data) {
function onClickStatus (line 73) | function onClickStatus(filename, lineIndex, statusWas, lineID) {
function replaceHTML (line 89) | function replaceHTML(divID, html, innerText) {
function showError (line 101) | function showError(message) {
FILE: dwertheimer.TaskSorting/src/NPTriggers-Hooks.js
function onOpen (line 25) | async function onOpen(note: TNote): Promise<void> {
function onEditorWillSave (line 50) | async function onEditorWillSave() {
function onUpdateOrInstall (line 75) | async function onUpdateOrInstall(): Promise<void> {
function init (line 88) | function init(): void {
function onSettingsUpdated (line 101) | async function onSettingsUpdated(): Promise<void> {
function versionCheck (line 112) | async function versionCheck(): Promise<void> {
function triggerCopyNoteTags (line 122) | async function triggerCopyNoteTags(): Promise<void> {
FILE: dwertheimer.TaskSorting/src/markTasks.js
function setTasks (line 7) | function setTasks(dir) {
function markTasks (line 33) | async function markTasks(mark: Direction, withConfirmation: boolean = tr...
FILE: dwertheimer.TaskSorting/src/sortTasks.js
constant DEFAULT_HEADINGS (line 13) | const DEFAULT_HEADINGS = {
function getTaskTypeHeadings (line 28) | function getTaskTypeHeadings() {
constant ROOT (line 42) | const ROOT = '__'
constant SORT_ORDERS (line 48) | const SORT_ORDERS = [
method removeHeadingFromNote (line 339) | removeHeadingFromNote(note, heading)
method if (line 689) | if (tasks[taskType] && tasks[taskType].length > 0) {
method if (line 701) | if (tasks[taskType] && tasks[taskType].length > 0) {
method if (line 713) | if (tasks[taskType] && tasks[taskType].length > 0) {
method if (line 725) | if (tasks[taskType] && tasks[taskType].length > 0) {
method if (line 751) | if (tasks[taskType] && tasks[taskType].length > 0) {
method if (line 759) | if (tasks[taskType] && tasks[taskType].length > 0) {
method if (line 767) | if (tasks[taskType] && tasks[taskType].length > 0) {
method if (line 775) | if (tasks[taskType] && tasks[taskType].length > 0) {
method if (line 900) | if ((nextPara && nextPara.type === 'empty') || !nextPara) {
method if (line 1152) | if (sortOrder) {
FILE: dwertheimer.TaskSorting/src/support/helpers.js
function uppercase (line 9) | function uppercase(str: string = ''): string {
FILE: dwertheimer.TaskSorting/src/tagTasks.js
method if (line 70) | if (tagsToRemove?.length) {
method if (line 98) | if (i > 0) {
method if (line 175) | if (Editor) {
FILE: flow-typed/Noteplan.js
method showInMainWindow (line 2808) | static showInMainWindow(html: string, title: string, options: Object): P...
FILE: flow-typed/npm/node-fetch_v1.x.x.js
class Request (line 8) | class Request mixins Body {
method buffer (line 71) | buffer(): Promise<Buffer >
FILE: flow-typed/npm/webpack_v4.x.x.js
class $WebpackError (line 8) | class $WebpackError extends Error {
class WebpackCompilation (line 44) | class WebpackCompilation {
class WebpackStats (line 49) | class WebpackStats {
FILE: github-actions-reporter.js
class GithubActionsReporter (line 1) | class GithubActionsReporter {
method constructor (line 2) | constructor(globalConfig, options) {
method onRunComplete (line 7) | onRunComplete(contexts, results) {
FILE: helpers/HTMLView.js
function getCallbackCodeString (line 78) | function getCallbackCodeString(jsFunctionName: string, commandName: stri...
function pruneTheme (line 277) | function pruneTheme(themeObj: any): any {
function getThemeJS (line 340) | function getThemeJS(cleanIt: boolean = true, includeSpecificStyles: bool...
method catch (line 777) | catch (error) {
FILE: helpers/NPCalendar.js
method if (line 99) | if (writableCalendars.includes(calendarName)) {
FILE: helpers/NPConfiguration.js
function getDefaultValueForNewSetting (line 64) | function getDefaultValueForNewSetting(setting: any): any {
function updateSettingData (line 75) | function updateSettingData(pluginJsonData: any): number {
function copySpecificSettings (line 158) | async function copySpecificSettings(oldPluginID: string, newPluginID: st...
method if (line 203) | if (triggerUpdateMechanism) {
method if (line 417) | if (!pluginInfo || !pluginInfo.id) {
method for (line 457) | for (let i = 0; i < pluginsToInstall.length; i++) {
FILE: helpers/NPEditor.js
method if (line 172) | if (selection == null) {
method if (line 178) | if (usersVersionHas('settableLineIndex')) {
FILE: helpers/NPEditorBasics.js
function saveEditorIfNecessary (line 18) | async function saveEditorIfNecessary(): Promise<void> {
FILE: helpers/NPExtendedRepeat.js
constant REPEAT_EXTENSIONS_PLUGIN_ID (line 37) | const REPEAT_EXTENSIONS_PLUGIN_ID: string = 'jgclark.RepeatExtensions'
constant LOG_CONTEXT (line 39) | const LOG_CONTEXT = 'extendedRepeat'
constant EXTENDED_REPEAT_STR (line 44) | const EXTENDED_REPEAT_STR: string = `@repeat\\(${RE_DATE_INTERVAL}\\)` /...
constant RE_EXTENDED_REPEAT (line 45) | const RE_EXTENDED_REPEAT: RegExp = new RegExp(EXTENDED_REPEAT_STR) // fi...
constant EXTENDED_REPEAT_CAPTURE_STR (line 46) | const EXTENDED_REPEAT_CAPTURE_STR: string = `@repeat\\((.*?)\\)` // find...
constant RE_EXTENDED_REPEAT_CAPTURE (line 47) | const RE_EXTENDED_REPEAT_CAPTURE: RegExp = new RegExp(EXTENDED_REPEAT_CA...
constant RE_CANCELLED_TASK (line 48) | const RE_CANCELLED_TASK: RegExp = new RegExp(`[\\^\\n]\\s*?[\\*\\+\\-]\\...
function generateNewRepeatDate (line 87) | function generateNewRepeatDate(noteToUse: CoreNoteFields, currentContent...
function invokeExtendedRepeatIfNeededAfterMarkComplete (line 362) | async function invokeExtendedRepeatIfNeededAfterMarkComplete(para: TPara...
FILE: helpers/NPFrontMatter.js
constant TRIGGER_LIST (line 23) | const TRIGGER_LIST = ['onEditorWillSave', 'onOpen']
function quoteTextIfNeededForFM (line 42) | function quoteTextIfNeededForFM(_text: string | number | boolean, quoteS...
function noteHasFrontMatter (line 102) | function noteHasFrontMatter(note: CoreNoteFields): boolean {
method catch (line 200) | catch (err) {
method catch (line 231) | catch (error) {
function removeFrontMatterField (line 293) | function removeFrontMatterField(note: CoreNoteFields, fieldToRemove: str...
function _objectToYaml (line 357) | function _objectToYaml(obj: any, indent: string = ' '): string {
function writeFrontMatter (line 387) | function writeFrontMatter(note: CoreNoteFields, attributes: { [string]: ...
function setFrontMatterVars (line 420) | function setFrontMatterVars(note: CoreNoteFields, varObj: { [string]: st...
method if (line 897) | if (_getFMText(note.content || '') !== `---\n${fmData.frontmatter}\n---\...
method for (line 1823) | for (let i = 0; i < lines.length; i++) {
method logDebug (line 1857) | logDebug('detectInlineTitle', 'Invalid frontmatter detected, not looking...
FILE: helpers/NPMoveItems.js
function logDebug (line 35) | async function moveItemToRegularNote(
function moveParagraphToNote (line 309) | function moveParagraphToNote(para: TParagraph, destinationNote: TNote): ...
function moveGivenParaAndBlock (line 340) | function moveGivenParaAndBlock(para: TParagraph, destFilename: string, d...
FILE: helpers/NPParagraph.js
constant CONFIRM_YES (line 28) | const CONFIRM_YES = 'Yes'
constant RUN_SILENTLY_YES (line 29) | const RUN_SILENTLY_YES = 'yes'
function getNoteFromFilename (line 40) | function getNoteFromFilename(filenameIn: string): TNote | null {
function removeHeadingFromNote (line 92) | function removeHeadingFromNote(note: CoreNoteFields, headingStr: string,...
function deleteEntireBlock (line 109) | function deleteEntireBlock(note: CoreNoteFields, para: TParagraph, inclu...
function removeContentUnderHeading (line 132) | function removeContentUnderHeading(note: CoreNoteFields, heading: string...
function insertContentUnderHeading (line 156) | function insertContentUnderHeading(destNote: CoreNoteFields, headingToFi...
function replaceContentUnderHeading (line 185) | async function replaceContentUnderHeading(
method if (line 990) | if (returnDetails) {
method if (line 1423) | if (thisNote.paragraphs.length > 0) {
method if (line 1513) | if (para) {
FILE: helpers/NPPresets.js
method chooseOption (line 92) | chooseOption(msg, opts)
FILE: helpers/NPRequiredFiles.js
function checkForRequiredSharedFiles (line 7) | async function checkForRequiredSharedFiles(pluginJson: any): Promise<voi...
FILE: helpers/NPScheduleItems.js
function unscheduleItem (line 27) | function unscheduleItem(filename: string, content: string): string {
function scheduleItemLiteMethod (line 68) | function scheduleItemLiteMethod(thisPara: TParagraph, dateStrToAdd: stri...
function scheduleItem (line 110) | function scheduleItem(origPara: TParagraph, dateStrToAdd: string, newTas...
FILE: helpers/NPSettings.js
function getSettingsFromPluginJson (line 11) | function getSettingsFromPluginJson(pluginJson: any) {
function getSettingsOptions (line 21) | function getSettingsOptions(settingsArray: any, includeHidden: boolean =...
function updateSetting (line 174) | async function updateSetting(key: string, pluginJson: any): any {
FILE: helpers/NPTeamspace.js
function getTeamspaceRootIdentifier (line 17) | function getTeamspaceRootIdentifier(): string {
function getTeamspaceNoteFilenameRegex (line 26) | function getTeamspaceNoteFilenameRegex(): RegExp {
function getTeamspaceTitleFromNote (line 51) | function getTeamspaceTitleFromNote(note: TNote): string {
FILE: helpers/NPThemeToCSS.js
function loadThemeData (line 24) | function loadThemeData(themeNameIn: string = ''): { themeName: string, t...
function generateCSSFromTheme (line 87) | function generateCSSFromTheme(themeNameIn: string = ''): string {
function getTintColor (line 477) | function getTintColor(): string {
function convertStyleObjectBlock (line 495) | function convertStyleObjectBlock(styleObject: any, includeFontDetails: b...
function textDecorationFromNP (line 607) | function textDecorationFromNP(selector: string, value: number): string {
function pxToRem (line 660) | function pxToRem(thisFontSize: number, baseFontSize: number): string {
function RGBColourConvert (line 674) | function RGBColourConvert(RGBIn: string): string {
function mixHexColors (line 700) | function mixHexColors(color1: string, color2: string): string {
FILE: helpers/NPVersions.js
function usersVersionHas (line 14) | function usersVersionHas(feature: string): boolean {
FILE: helpers/NPWindows.js
constant MIN_WINDOW_WIDTH (line 22) | const MIN_WINDOW_WIDTH = 300
constant MIN_WINDOW_HEIGHT (line 23) | const MIN_WINDOW_HEIGHT = 430
function rectToString (line 33) | function rectToString(rect: Rect): string {
function logWindowsList (line 43) | function logWindowsList(): void {
method if (line 209) | if (NotePlan.environment.platform !== 'macOS') {
method if (line 221) | if (caseInsensitiveMatch(customId, thisWin.customId) /* || caseInsensiti...
method if (line 238) | if (!doCheckIsVisible || (foundWin.isVisible ?? false)) {
method if (line 310) | if (thisEditorWindow.filename === filenameToFind) {
method if (line 327) | if (thisWindow.customId === customId) {
method for (line 539) | for (let y = 0; y <= screenHeight - minHeight; y += stepSize) {
method if (line 701) | if (thisWindow.customId === windowCustomId) {
method if (line 708) | if (thisWindow.customId === windowCustomId) {
method if (line 725) | if (thisWindow.customId === windowCustomId) {
method if (line 732) | if (thisWindow.customId === windowCustomId) {
method if (line 753) | if (thisWindow.id === windowId) {
method if (line 760) | if (thisWindow.id === windowId) {
method if (line 844) | if (win) {
method if (line 1001) | if (usersVersionHas('mainSidebarControl')) {
FILE: helpers/NPdateTime.js
method if (line 40) | if (typeof CommandBar.textPrompt === 'function') {
function setMomentLocaleFromEnvironment (line 87) | function setMomentLocaleFromEnvironment(): void {
function nowLocaleShortDateTime (line 97) | function nowLocaleShortDateTime(): string {
function nowLocaleDate (line 101) | function nowLocaleDate(): string {
function nowLocaleShortTime (line 105) | function nowLocaleShortTime(): string {
function nowDoneDateTimeString (line 114) | function nowDoneDateTimeString(): string {
function toLocaleDateTimeString (line 120) | function toLocaleDateTimeString(dateObj: Date, locale: string | Array<st...
function localeDateStr (line 135) | function localeDateStr(dateIn: Date): string {
function toNPLocaleDateString (line 142) | function toNPLocaleDateString(dateObj: Date, dateStyle: string = 'short'...
function toLocaleTime (line 159) | function toLocaleTime(dateObj: Date, locale: string | Array<string> = []...
function printDateRange (line 172) | function printDateRange(dr: DateRange) {
function getUsersFirstDayOfWeekUTC (line 232) | function getUsersFirstDayOfWeekUTC(): number {
function getPeriodStartEndDates (line 316) | async function getPeriodStartEndDates(
function getDateStrForEndofPeriodFromCalendarFilename (line 758) | function getDateStrForEndofPeriodFromCalendarFilename(filename: string):...
function pad (line 793) | function pad(n: number): string {
function getFirstDateInPeriod (line 989) | function getFirstDateInPeriod(NPDateStringIn: string): string {
function getLastDateInPeriod (line 1036) | function getLastDateInPeriod(NPDateStringIn: string): string {
function getDateStrFromRelativeDateString (line 1368) | function getDateStrFromRelativeDateString(relDateStr: string): string {
function displayTitleWithRelDate (line 1386) | function displayTitleWithRelDate(noteIn: CoreNoteFields, showRelativeDat...
function getDisplayTitleAndPathForRegularNote (line 1409) | function getDisplayTitleAndPathForRegularNote(noteIn: CoreNoteFields): s...
function getCalendarFilenameFromDateString (line 1441) | function getCalendarFilenameFromDateString(dateStr: string): string {
method if (line 1498) | if (!p.note) {
method if (line 1615) | if (baseDateIn === '') {
FILE: helpers/NPdev.js
function logAllEnvironmentSettings (line 11) | function logAllEnvironmentSettings(): void {
function logPreference (line 126) | function logPreference(key: string): void {
function logPreferenceAskUser (line 151) | async function logPreferenceAskUser(): Promise<void> {
function unsetPreference (line 166) | function unsetPreference(prefName: string): void {
function unsetPreferenceAskUser (line 179) | async function unsetPreferenceAskUser(): Promise<void> {
FILE: helpers/NPnote.js
constant TEAMSPACE_ICON_COLOR (line 50) | const TEAMSPACE_ICON_COLOR = 'green-700'
function chooseNoteV2 (line 86) | async function chooseNoteV2(
function getNoteDecoration (line 288) | function getNoteDecoration(note: TNote): TCommandBarOptionObject {
function convertNoteToFrontmatter (line 655) | function convertNoteToFrontmatter(note: TNote, defaultFMText: string = '...
function selectFirstNonTitleLineInEditor (line 688) | function selectFirstNonTitleLineInEditor(): void {
method for (line 738) | for (const subItem of noteBacklink.subItems) {
method if (line 1070) | if (item === '') {
method if (line 1090) | if (includeSubfolders) {
method if (line 1224) | if (item === '') {
method if (line 1239) | if (includeSubfolders) {
method if (line 1335) | if (headingParas[0].content === note.title) {
method if (line 1350) | if (note.type === 'Calendar') {
method if (line 1604) | if (notesIn) {
method logError (line 1654) | logError('note / getOrMakeRegularNoteInFolder', `can't read new ${noteTi...
FILE: helpers/__tests__/HTMLView.test.js
constant FILE (line 20) | const FILE = `${colors.yellow('helpers/NPSyncedCopies')}`
FILE: helpers/__tests__/NPConfiguration.test.js
constant PLUGIN_NAME (line 11) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 12) | const FILENAME = `NPConfiguration`
FILE: helpers/__tests__/NPConfiguration.updateSettingData.test.js
constant PLUGIN_ID (line 19) | const PLUGIN_ID = 'test.plugin'
function makePluginJson (line 25) | function makePluginJson(settingsEntries) {
method get (line 152) | get() {
method set (line 155) | set() {
FILE: helpers/__tests__/NPDateTime.test.js
constant FILENAME (line 9) | const FILENAME = `NPDateTime`
function isValidDate (line 11) | function isValidDate(date) {
FILE: helpers/__tests__/NPFrontMatter/NPFrontMatterAttributes.test.js
constant PLUGIN_NAME (line 7) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 8) | const FILENAME = `NPFrontMatterAttributes`
FILE: helpers/__tests__/NPFrontMatter/NPFrontMatterDetection.test.js
constant PLUGIN_NAME (line 7) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 8) | const FILENAME = `NPFrontMatterDetection`
FILE: helpers/__tests__/NPFrontMatter/NPFrontMatterFormatting.test.js
constant PLUGIN_NAME (line 7) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 8) | const FILENAME = `NPFrontMatterFormatting`
FILE: helpers/__tests__/NPFrontMatter/NPFrontMatterManipulation.test.js
constant PLUGIN_NAME (line 7) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 8) | const FILENAME = `NPFrontMatterManipulation`
FILE: helpers/__tests__/NPFrontMatter/NPFrontMatterMisc.test.js
constant PLUGIN_NAME (line 7) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 8) | const FILENAME = `NPFrontMatterMisc`
FILE: helpers/__tests__/NPFrontMatter/NPFrontMatterNotes.test.js
constant PLUGIN_NAME (line 7) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 8) | const FILENAME = `NPFrontMatterNotes`
FILE: helpers/__tests__/NPFrontMatter/NPFrontMatterTriggers.test.js
constant PLUGIN_NAME (line 7) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 8) | const FILENAME = `NPFrontMatterTriggers`
FILE: helpers/__tests__/NPNote.test.js
constant PLUGIN_NAME (line 14) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 15) | const FILENAME = `NPNote`
FILE: helpers/__tests__/NPPresets.test.js
constant PLUGIN_NAME (line 6) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 7) | const FILENAME = `NPPresets`
FILE: helpers/__tests__/NPSettings.test.js
constant PLUGIN_NAME (line 9) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 10) | const FILENAME = `NPSettings`
FILE: helpers/__tests__/NPSyncedCopies.test.js
constant FILE (line 16) | const FILE = `${colors.yellow('helpers/NPSyncedCopies')}`
FILE: helpers/__tests__/NPThemeToCSS.test.js
constant FILE (line 25) | const FILE = `${colors.yellow('helpers/NPThemeToCSS')}`
FILE: helpers/__tests__/blocks.test.js
constant PLUGIN_NAME (line 7) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 8) | const FILENAME = `blocks`
FILE: helpers/__tests__/calendar.test.js
constant PLUGIN_NAME (line 12) | const PLUGIN_NAME = `📙 ${colors.yellow('helpers/calendar')}`
FILE: helpers/__tests__/config.test.js
constant FILE (line 17) | const FILE = `${colors.yellow('helpers/config')}`
FILE: helpers/__tests__/dataManipulation.test.js
constant FILE (line 8) | const FILE = `${colors.yellow('helpers/dataManipulation')}`
FILE: helpers/__tests__/dateTime.test.js
constant PLUGIN_NAME (line 26) | const PLUGIN_NAME = `📙 ${colors.yellow('helpers/dateTime')}`
FILE: helpers/__tests__/general.test.js
constant FILE (line 12) | const FILE = `${colors.yellow('helpers/general')}`
FILE: helpers/__tests__/note.test.js
constant PLUGIN_NAME (line 7) | const PLUGIN_NAME = `helpers/note`
FILE: helpers/__tests__/sorting.test.js
constant PLUGIN_NAME (line 7) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 8) | const FILENAME = `NPNote`
FILE: helpers/__tests__/stringTransforms.test.js
constant PLUGIN_NAME (line 19) | const PLUGIN_NAME = `📙 ${colors.yellow('helpers/stringTransforms')}`
FILE: helpers/__tests__/teamspace.test.js
constant PLUGIN_NAME (line 21) | const PLUGIN_NAME = `📙 ${colors.yellow('helpers/teamspace')}`
FILE: helpers/__tests__/timeblocks.test.js
constant HELPER_NAME (line 18) | const HELPER_NAME = `📙 ${colors.yellow('helpers/timeblocks')}`
FILE: helpers/__tests__/urls.test.js
constant PLUGIN_NAME (line 8) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 9) | const FILENAME = `urls.js`
FILE: helpers/__tests__/utils.test.js
constant PLUGIN_NAME (line 8) | const PLUGIN_NAME = `helpers`
constant FILENAME (line 9) | const FILENAME = `utils.js`
FILE: helpers/blocks.js
function getOrCreateDoneSection (line 211) | function getOrCreateDoneSection(note: TNote): number {
FILE: helpers/calendar.js
constant RE_EVENT_ID (line 15) | const RE_EVENT_ID = `event:[A-F0-9-]{36,37}`
method if (line 95) | if ((returnType === 'email' && result[2]) || (returnType === 'name' && r...
FILE: helpers/checkType.js
method return (line 86) | return checkA(value)
method if (line 106) | if (Array.isArray(value)) {
method for (line 130) | for (const key in checkerObj) {
method checker (line 148) | checker(defaultValue)
FILE: helpers/codeBlocks.js
function addCodeBlock (line 18) | function addCodeBlock(destNote: CoreNoteFields, textToAdd: string, codeB...
method if (line 48) | if (paragraph.type === 'code') {
function replaceCodeBlockContent (line 106) | function replaceCodeBlockContent(note: CoreNoteFields, codeBlockType: st...
function saveCodeBlockToNote (line 224) | async function saveCodeBlockToNote(
method catch (line 318) | catch (parseError) {
FILE: helpers/colors.js
constant TAILWIND_COLORS (line 53) | const TAILWIND_COLORS = {
constant TAILWIND_COLOR_NAMES (line 316) | const TAILWIND_COLOR_NAMES = Object.keys(TAILWIND_COLORS)
function tailwindToRgbWithOpacity (line 365) | function tailwindToRgbWithOpacity(color, opacity = 1) {
function tailwindToHsl (line 404) | function tailwindToHsl(color, includeAlpha = false) {
function colorToModernSpecWithOpacity (line 455) | function colorToModernSpecWithOpacity(colorIn, opacity = 1) {
FILE: helpers/content.js
function noteHasFileLinks (line 9) | function noteHasFileLinks(note: CoreNoteFields): boolean {
method if (line 28) | if (!note) {
FILE: helpers/dataManipulation.js
function trimAnyQuotes (line 12) | function trimAnyQuotes(inStr: string): string {
function trimString (line 23) | function trimString(inStr: string, maxLen: number): string {
function compareObjects (line 119) | function compareObjects(o1: Object, o2: Object): boolean {
method return (line 163) | return arr.filter((a: T) => !exclude.find((b: T) => b[propertyName] === ...
method if (line 269) | if (typeof input === 'boolean') {
method if (line 296) | if (Array.isArray(input)) {
method if (line 300) | if (typeof input === 'string') {
FILE: helpers/dateTime.js
constant TEAMSPACE_INDICATOR (line 13) | const TEAMSPACE_INDICATOR = '%%NotePlanCloud%%'
constant RE_TEAMSPACE_INDICATOR_AND_ID (line 14) | const RE_TEAMSPACE_INDICATOR_AND_ID = new RegExp(`^${TEAMSPACE_INDICATOR...
constant MOMENT_FORMAT_NP_ISO (line 18) | const MOMENT_FORMAT_NP_ISO = 'YYYY-MM-DD'
constant MOMENT_FORMAT_NP_DAY (line 19) | const MOMENT_FORMAT_NP_DAY = 'YYYYMMDD'
constant MOMENT_FORMAT_NP_WEEK (line 20) | const MOMENT_FORMAT_NP_WEEK = 'GGGG-[W]WW' // note: GGGG is the week yea...
constant MOMENT_FORMAT_NP_MONTH (line 21) | const MOMENT_FORMAT_NP_MONTH = 'YYYY-MM'
constant MOMENT_FORMAT_NP_QUARTER (line 22) | const MOMENT_FORMAT_NP_QUARTER = 'YYYY-[Q]Q'
constant MOMENT_FORMAT_NP_YEAR (line 23) | const MOMENT_FORMAT_NP_YEAR = 'YYYY'
constant RE_TIME (line 30) | const RE_TIME = '[0-2]\\d{1}:[0-5]\\d{1}\\s?(?:AM|PM|am|pm)?' // find '1...
constant RE_DATE (line 31) | const RE_DATE = '\\d{4}-[01]\\d-[0123]\\d' // find ISO dates of form YYY...
constant RE_YYYYMMDD_DATE (line 32) | const RE_YYYYMMDD_DATE = '\\d{4}[01]\\d[0123]\\d' // version of above th...
constant RE_DATE_CAPTURE (line 33) | const RE_DATE_CAPTURE = `(\\d{4}[01]\\d{1}\\d{2})` // capture date of fo...
constant RE_ISO_DATE (line 34) | const RE_ISO_DATE = RE_DATE // now earlier RE_DATE made the same as this...
constant RE_PLUS_DATE_G (line 35) | const RE_PLUS_DATE_G: RegExp = />(\d{4}-\d{2}-\d{2})(\+)*/g
constant RE_PLUS_DATE (line 36) | const RE_PLUS_DATE: RegExp = />(\d{4}-\d{2}-\d{2})(\+)*/
constant RE_SCHEDULED_ISO_DATE (line 37) | const RE_SCHEDULED_ISO_DATE = '>\\d{4}-[01]\\d-[0123]\\d' // find schedu...
constant RE_DATE_TIME (line 38) | const RE_DATE_TIME = `${RE_DATE} ${RE_TIME}` // YYYY-MM-DD HH:MM[AM|PM]
constant RE_BARE_DATE (line 39) | const RE_BARE_DATE = `[^\d(<\/-]${RE_DATE}` // an ISO date without a dig...
constant RE_BARE_DATE_CAPTURE (line 40) | const RE_BARE_DATE_CAPTURE = `[^\d(<\/-](${RE_DATE})` // capturing date ...
constant RE_FILE_EXTENSIONS_GROUP (line 41) | const RE_FILE_EXTENSIONS_GROUP = `\\.(md|txt)$` // and tie to end of string
constant RE_NP_DAY_SPEC (line 42) | const RE_NP_DAY_SPEC = RE_YYYYMMDD_DATE
constant RE_DAILY_NOTE_FILENAME (line 43) | const RE_DAILY_NOTE_FILENAME = `(^|\\/)${RE_YYYYMMDD_DATE}${RE_FILE_EXTE...
constant DAILY_NOTE_LINK (line 44) | const DAILY_NOTE_LINK = `[\<\>]${RE_DATE}(?!<)` // don't match >YYYY-MM-...
constant RE_SCHEDULED_DAILY_NOTE_LINK (line 45) | const RE_SCHEDULED_DAILY_NOTE_LINK: RegExp = />\d{4}-[01]\d-[0123]\d/ //...
constant RE_NP_WEEK_SPEC (line 48) | const RE_NP_WEEK_SPEC = '\\d{4}\\-W[0-5]\\d' // find dates of form YYYY-Wnn
constant WEEK_NOTE_LINK (line 49) | const WEEK_NOTE_LINK = `[\<\>]${RE_NP_WEEK_SPEC}`
constant SCHEDULED_WEEK_NOTE_LINK (line 50) | const SCHEDULED_WEEK_NOTE_LINK = '\\s+>\\d{4}\\-W[0-5]\\d'
constant RE_SCHEDULED_WEEK_NOTE_LINK (line 51) | const RE_SCHEDULED_WEEK_NOTE_LINK: RegExp = />\d{4}\-W[0-5]\d/ // Note: ...
constant RE_WEEKLY_NOTE_FILENAME (line 52) | const RE_WEEKLY_NOTE_FILENAME = `(^|\\/)${RE_NP_WEEK_SPEC}${RE_FILE_EXTE...
constant RE_BARE_WEEKLY_DATE (line 53) | const RE_BARE_WEEKLY_DATE = `[^\d(<\/-]${RE_NP_WEEK_SPEC}` // a YYYY-Www...
constant RE_BARE_WEEKLY_DATE_CAPTURE (line 54) | const RE_BARE_WEEKLY_DATE_CAPTURE = `[^\d(<\/-](${RE_NP_WEEK_SPEC})` // ...
constant RE_NP_MONTH_SPEC (line 58) | const RE_NP_MONTH_SPEC = '\\d{4}-[01]\\d(?![\\d-])' // find dates of for...
constant MONTH_NOTE_LINK (line 59) | const MONTH_NOTE_LINK = `[\<\>]${RE_NP_MONTH_SPEC}`
constant SCHEDULED_MONTH_NOTE_LINK (line 60) | const SCHEDULED_MONTH_NOTE_LINK = `>${RE_NP_MONTH_SPEC}`
constant RE_SCHEDULED_MONTH_NOTE_LINK (line 61) | const RE_SCHEDULED_MONTH_NOTE_LINK: RegExp = new RegExp(`>${RE_NP_MONTH_...
constant RE_MONTHLY_NOTE_FILENAME (line 62) | const RE_MONTHLY_NOTE_FILENAME = `(^|\\/)${RE_NP_MONTH_SPEC}${RE_FILE_EX...
constant RE_NP_QUARTER_SPEC (line 65) | const RE_NP_QUARTER_SPEC = '\\d{4}\\-Q[1-4](?!\\d)' // find dates of for...
constant QUARTER_NOTE_LINK (line 66) | const QUARTER_NOTE_LINK = `[\<\>]${RE_NP_QUARTER_SPEC}`
constant SCHEDULED_QUARTERLY_NOTE_LINK (line 67) | const SCHEDULED_QUARTERLY_NOTE_LINK = `>${RE_NP_QUARTER_SPEC}`
constant RE_SCHEDULED_QUARTERLY_NOTE_LINK (line 68) | const RE_SCHEDULED_QUARTERLY_NOTE_LINK: RegExp = new RegExp(`>${RE_NP_QU...
constant RE_QUARTERLY_NOTE_FILENAME (line 69) | const RE_QUARTERLY_NOTE_FILENAME = `(^|\\/)${RE_NP_QUARTER_SPEC}${RE_FIL...
constant RE_NP_YEAR_SPEC (line 72) | const RE_NP_YEAR_SPEC = '\\d{4}(?![\\d-])' // find years of form YYYY wi...
constant RE_BARE_YEAR_SPEC (line 73) | const RE_BARE_YEAR_SPEC = '^\\d{4}$' // find years of form YYYY without ...
constant YEAR_NOTE_LINK (line 74) | const YEAR_NOTE_LINK = `[\<\>]${RE_NP_YEAR_SPEC}`
constant SCHEDULED_YEARLY_NOTE_LINK (line 75) | const SCHEDULED_YEARLY_NOTE_LINK = `>${RE_NP_YEAR_SPEC}`
constant RE_SCHEDULED_YEARLY_NOTE_LINK (line 76) | const RE_SCHEDULED_YEARLY_NOTE_LINK: RegExp = new RegExp(`>${RE_NP_YEAR_...
constant RE_YEARLY_NOTE_FILENAME (line 77) | const RE_YEARLY_NOTE_FILENAME = `(^|\\/)${RE_NP_YEAR_SPEC}${RE_FILE_EXTE...
constant RE_ANY_DUE_DATE_TYPE (line 80) | const RE_ANY_DUE_DATE_TYPE: RegExp = new RegExp(`\\s+>(${RE_DATE}|${RE_N...
constant RE_IS_SCHEDULED (line 81) | const RE_IS_SCHEDULED: RegExp = new RegExp(`>(${RE_DATE}|${RE_NP_WEEK_SP...
constant RE_SCHEDULED_DATES_G (line 82) | const RE_SCHEDULED_DATES_G: RegExp = new RegExp(`>(${RE_DATE}|${RE_NP_WE...
constant RE_IS_SCHEDULED_CAPTURES (line 83) | const RE_IS_SCHEDULED_CAPTURES: RegExp = new RegExp(`>((${RE_DATE}|${RE_...
constant RE_DONE_DATE_TIME (line 86) | const RE_DONE_DATE_TIME: RegExp = new RegExp(`@done\\(${RE_DATE_TIME}\\)...
constant RE_DONE_DATE_TIME_CAPTURES (line 87) | const RE_DONE_DATE_TIME_CAPTURES: RegExp = new RegExp(`@done\\((${RE_DAT...
constant RE_DONE_DATE_OR_DATE_TIME_DATE_CAPTURE (line 88) | const RE_DONE_DATE_OR_DATE_TIME_DATE_CAPTURE: RegExp = new RegExp(`@done...
constant RE_DONE_DATE_OPT_TIME (line 89) | const RE_DONE_DATE_OPT_TIME: RegExp = new RegExp(`@done\\(${RE_ISO_DATE}...
constant RE_DATE_INTERVAL (line 92) | const RE_DATE_INTERVAL = `[+\\-]?\\d+[BbDdWwMmQqYy]`
constant RE_OFFSET_DATE (line 93) | const RE_OFFSET_DATE = `{\\^?${RE_DATE_INTERVAL}}`
constant RE_OFFSET_DATE_CAPTURE (line 94) | const RE_OFFSET_DATE_CAPTURE = `{(\\^?${RE_DATE_INTERVAL})}`
function getTodaysDateHyphenated (line 103) | function getTodaysDateHyphenated(): string {
function getTodaysDateAsArrowDate (line 119) | function getTodaysDateAsArrowDate(): string {
function getTodaysDateUnhyphenated (line 129) | function getTodaysDateUnhyphenated(): string {
function getJSDateStartOfToday (line 137) | function getJSDateStartOfToday(): Date {
function isDailyNote (line 166) | function isDailyNote(note: CoreNoteFields): boolean {
function isWeeklyNote (line 173) | function isWeeklyNote(note: CoreNoteFields): boolean {
function isMonthlyNote (line 179) | function isMonthlyNote(note: CoreNoteFields): boolean {
function isQuarterlyNote (line 185) | function isQuarterlyNote(note: CoreNoteFields): boolean {
function isYearlyNote (line 191) | function isYearlyNote(note: CoreNoteFields): boolean {
function convertISODateFilenameToNPDayFilename (line 323) | function convertISODateFilenameToNPDayFilename(dailyNoteFilename: string...
function toISODateString (line 334) | function toISODateString(dateObj: Date): string {
function hyphenatedDate (line 351) | function hyphenatedDate(date: Date): string {
function toISOShortDateTimeString (line 360) | function toISOShortDateTimeString(dateObj: Date): string {
function toLocaleDateTimeString (line 364) | function toLocaleDateTimeString(dateObj: Date, locale: string | Array<st...
function toLocaleDateString (line 368) | function toLocaleDateString(dateObj: Date, locale: string | Array<string...
function toLocaleTime (line 372) | function toLocaleTime(dateObj: Date, locale: string | Array<string> = []...
function printDateRange (line 376) | function printDateRange(dr: DateRange) {
function YYYYMMDDDateStringFromDate (line 387) | function YYYYMMDDDateStringFromDate(dateObj: Date): string {
function filenameDateString (line 399) | function filenameDateString(dateObj: Date): string {
function hyphenatedDateString (line 411) | function hyphenatedDateString(dateObj: Date): string {
function formatNoteDate (line 427) | function formatNoteDate(inputDate: Date, style: string): string {
function formatNoteDateFromNPDateStr (line 464) | function formatNoteDateFromNPDateStr(inputDateStr: string, style: string...
function getTimeStringFromDate (line 500) | function getTimeStringFromDate(date: Date): string {
function getTimeStringFromHM (line 507) | function getTimeStringFromHM(hours: number, minutes: number): string {
function getDisplayDateStrFromFilenameDateStr (line 517) | function getDisplayDateStrFromFilenameDateStr(dateStrIn: string): string {
function getFilenameDateStrFromDisplayDateStr (line 531) | function getFilenameDateStrFromDisplayDateStr(dateStrIn: string): string {
function getAPIDateStrFromDisplayDateStr (line 544) | function getAPIDateStrFromDisplayDateStr(dateStrIn: string): string {
function getFilenameWithoutTeamspaceID (line 559) | function getFilenameWithoutTeamspaceID(filenameIn: string): string {
function getDateStringFromCalendarFilename (line 578) | function getDateStringFromCalendarFilename(filenameIn: string, returnISO...
function getISODateStringFromYYYYMMDD (line 622) | function getISODateStringFromYYYYMMDD(dateStrWithoutHyphens: string): st...
function removeRepeats (line 676) | function removeRepeats(content: string): string {
method if (line 723) | if (fromDate == null || toDate == null) {
method if (line 726) | if (fromDate > toDate) {
function validateDateRangeAndConvertToISODateStrings (line 739) | function validateDateRangeAndConvertToISODateStrings(fromDate: Date, toD...
function withinDateRange (line 758) | function withinDateRange(testDate: string, fromDate: string, toDate: str...
function relativeDateFromNumber (line 792) | function relativeDateFromNumber(diffIn: number, useShortStyle: boolean =...
function getWeek (line 956) | function getWeek(inDate: Date): number {
function getNPWeekStr (line 996) | function getNPWeekStr(inDate: Date): string {
function getNPMonthStr (line 1006) | function getNPMonthStr(inDate: Date): string {
function getNPQuarterStr (line 1016) | function getNPQuarterStr(inDate: Date): string {
function getNPYearStr (line 1026) | function getNPYearStr(inDate: Date): string {
function weekStartDateStr (line 1062) | function weekStartDateStr(inStr: string): string {
function calcWeekOffset (line 1093) | function calcWeekOffset(startWeek: number, startYear: number, offset: nu...
function convertOffsetUnitToMomentUnit (line 1114) | function convertOffsetUnitToMomentUnit(unit: string): string {
function getNPDateFormatForFilenameFromOffsetUnit (line 1135) | function getNPDateFormatForFilenameFromOffsetUnit(unit: string): string {
function getNPDateFormatForDisplayFromOffsetUnit (line 1157) | function getNPDateFormatForDisplayFromOffsetUnit(unit: string): string {
function getPeriodOfNPDateStr (line 1178) | function getPeriodOfNPDateStr(dateStr: string): string {
function splitIntervalToParts (line 1259) | function splitIntervalToParts(intervalStr: string): { number: number, ty...
function calcOffsetDateStrUsingCalendarType (line 1279) | function calcOffsetDateStrUsingCalendarType(offsetInterval: string, base...
FILE: helpers/dev.js
constant PARAM_BLACKLIST (line 14) | const PARAM_BLACKLIST = ['note', 'referencedBlocks', 'availableThemes', ...
method if (line 49) | if (obj === null || obj === undefined) {
method String (line 53) | String(obj)
method if (line 189) | if (oldObj === newObj) {
method if (line 199) | if (!Array.isArray(oldObj)) {
method if (line 219) | if (typeof oldObj !== 'object' || oldObj === null) {
method if (line 271) | if (isObject(value1) && isObject(value2)) {
method if (line 690) | if (message.length > 0) {
FILE: helpers/folders.js
function getFoldersMatching (line 37) | function getFoldersMatching(
method catch (line 202) | catch (error) {
method if (line 258) | if (e === '/') {
method if (line 496) | if (alsoSubFolders) {
method if (line 639) | if (caseInsensitiveMatch(file.filename, filepath)) {
method if (line 662) | if (typeof folderData === 'object' && folderData !== null) {
method catch (line 767) | catch (error) {
method catch (line 783) | catch (error) {
method catch (line 800) | catch (error) {
FILE: helpers/general.js
method if (line 34) | if (iterable) {
method for (line 83) | for (const [keyLowerCase, value] of super.entries()) {
method if (line 102) | if (typeof value === 'string') {
method boolean (line 110) | boolean {
method return (line 114) | return super.has(value)
method catch (line 140) | catch (err) {
method if (line 326) | if (heading?.length) {
method if (line 523) | if (wantedParam === '') {
FILE: helpers/headings.js
function isParaAMatchForHeading (line 18) | function isParaAMatchForHeading(para: TParagraph, headingName: string, h...
function isTitleWithEqualOrLowerHeadingLevel (line 106) | function isTitleWithEqualOrLowerHeadingLevel(item: TParagraph, prevLowes...
FILE: helpers/note.js
function setTitle (line 39) | function setTitle(note: CoreNoteFields, title: string): void {
function noteType (line 81) | function noteType(filename: string): NoteType {
function getNoteLinkForDisplay (line 120) | function getNoteLinkForDisplay(filename: string, dateStyle: string): str...
method if (line 179) | if (!name) {
method if (line 495) | if (excludeSpecialFolders) {
method if (line 504) | if (thisFolder.startsWith(ef)) {
method if (line 629) | if (paras[i].type === 'title' && paras[i].headingLevel <= sectionHeading...
method if (line 742) | if (!excludedFolders) {
method if (line 782) | if (note.type === 'Calendar') {
FILE: helpers/noteChooserFilenameResolve.js
function calendarBasenameForCompare (line 44) | function calendarBasenameForCompare(filenameIn: string): string {
function calendarNoteFilenamesEquivalent (line 56) | function calendarNoteFilenamesEquivalent(a: string, b: string): boolean {
function resolveNoteChooserFilenameForLookup (line 86) | function resolveNoteChooserFilenameForLookup(filenameIn: string): string {
FILE: helpers/openAI.js
constant BASE_URL (line 14) | const BASE_URL = 'https://api.openai.com/v1'
constant TOKEN_LIMIT (line 15) | const TOKEN_LIMIT = 3000 // tokens per request (actually 3072)
constant MAX_RETRIES (line 16) | const MAX_RETRIES = 5 // number of times to retry a request if it fails
constant CHAT_COMPONENT (line 17) | const CHAT_COMPONENT = 'chat/completions'
constant MODEL_COST (line 18) | const MODEL_COST = { 'gpt-4': { inputCost: 0.03 / 1000, outputCost: 0.06...
function getFetchRequestObj (line 90) | async function getFetchRequestObj(method: string = 'GET', body: any = nu...
function getErrorStringToDisplay (line 119) | function getErrorStringToDisplay(resultJSON: any): string {
function countTokens (line 145) | function countTokens(inputString: string): number {
method if (line 165) | if (shouldShowMessage) {
function createInitialChatObject (line 198) | function createInitialChatObject(model: string = 'gpt-3.5-turbo', system...
FILE: helpers/paragraph.js
function caseInsensitiveSubstringMatch (line 30) | function caseInsensitiveSubstringMatch(searchTerm: string, textToSearch:...
function stripAllURIsAndNoteLinks (line 50) | function stripAllURIsAndNoteLinks(input: string): string {
function stripDoneDateTimeMentions (line 75) | function stripDoneDateTimeMentions(input: string): string {
function isTermInURL (line 90) | function isTermInURL(term: string, searchString: string): boolean {
function isTermInNotelinkOrURI (line 107) | function isTermInNotelinkOrURI(term: string, input: string): boolean {
function isTermInEventLinkHiddenPart (line 151) | function isTermInEventLinkHiddenPart(term: string, input: string): boole...
function isTermInMarkdownPath (line 195) | function isTermInMarkdownPath(term: string, searchString: string): boole...
function rangeToString (line 226) | function rangeToString(r: TRange): string {
function contentRangeToString (line 238) | function contentRangeToString(content: string, r: TRange): string {
function displayTitle (line 255) | function displayTitle(note: CoreNoteFields, addTeamspaceIconAndName: boo...
method catch (line 305) | catch (error) {
method if (line 578) | if (!note) {
method if (line 582) | if (insertionIndex < 0) {
method if (line 586) | if (insertionIndex > note.paragraphs.length) {
method for (line 592) | for (let i = 0; i < paraTextArr.length; i++) {
method if (line 624) | if (paras[0].type === 'title' && paras[0].headingLevel === 1) {
method if (line 754) | if (p.content === contentToFind) {
method if (line 775) | if (headingToFind && headingToFind !== '') {
method if (line 1078) | if (p.type === 'done') {
FILE: helpers/parentsAndChildren.js
method if (line 53) | if (p.children.length) {
method if (line 141) | if (p.lineIndex > para.lineIndex && p.indents > thisIndentLevel && lastL...
FILE: helpers/promisePolyfill.js
method if (line 14) | if (typeof Promise !== 'undefined' && typeof Promise.resolve === 'functi...
method if (line 30) | if (typeof Promise !== 'undefined' && typeof Promise.race === 'function') {
method if (line 68) | if (typeof Promise !== 'undefined' && typeof Promise.all === 'function') {
method if (line 120) | if (delayMs < 10) {
method if (line 142) | if (typeof setTimeout !== 'undefined') {
method if (line 178) | if (typeof Promise.resolve !== 'function') {
FILE: helpers/react/CollapsibleObjectViewer.jsx
function renderObject (line 54) | function renderObject(
method if (line 379) | if (onToggle) {
FILE: helpers/react/DynamicDialog/AutosaveField.jsx
method if (line 172) | if (!requestFromPlugin || disabled) {
FILE: helpers/react/DynamicDialog/ColorChooser.jsx
function ColorChooser (line 31) | function ColorChooser({
FILE: helpers/react/DynamicDialog/DropdownSelect.jsx
method if (line 201) | if (isEditable) {
method if (line 218) | if (option.value === '' && placeholder) {
FILE: helpers/react/DynamicDialog/DropdownSelectChooser.jsx
function DropdownSelectChooser (line 51) | function DropdownSelectChooser({
FILE: helpers/react/DynamicDialog/DynamicDialog.jsx
constant FOCUS_FIRST_FIELD (line 53) | const FOCUS_FIRST_FIELD = false
function stateOfControllingSetting (line 353) | function stateOfControllingSetting(item: TSettingItem): boolean {
function shouldRenderItem (line 370) | function shouldRenderItem(item: TSettingItem): boolean {
method if (line 950) | if (autosaveTriggerRegisteredRef.current) {
FILE: helpers/react/DynamicDialog/EventChooser.jsx
function formatTime (line 63) | function formatTime(date: Date): string {
function formatEventDisplay (line 94) | function formatEventDisplay(event: EventOption): string {
function isEventHappeningNow (line 109) | function isEventHappeningNow(event: EventOption, now: Date): boolean {
function isEventWithin15Minutes (line 131) | function isEventWithin15Minutes(event: EventOption, now: Date): boolean {
function loadEvents (line 308) | async function loadEvents() {
FILE: helpers/react/DynamicDialog/ExpandableTextarea.jsx
function ExpandableTextarea (line 34) | function ExpandableTextarea({
FILE: helpers/react/DynamicDialog/FolderChooser.jsx
method if (line 437) | if (isStaticOption(item)) {
method 'New Folder' (line 448) | 'New Folder'
method if (line 468) | if (item === '__NEW_FOLDER__') {
method if (line 471) | if (isStaticOption(item)) {
method if (line 479) | if (item === '__NEW_FOLDER__') {
method if (line 532) | if (item === '<select>' || item === '<SELECT>') {
FILE: helpers/react/DynamicDialog/GenericDatePicker.jsx
method if (line 180) | if (inputValue !== '') {
FILE: helpers/react/DynamicDialog/HeadingChooser.jsx
function HeadingChooser (line 47) | function HeadingChooser({
FILE: helpers/react/DynamicDialog/IconChooser.jsx
function IconChooser (line 30) | function IconChooser({
FILE: helpers/react/DynamicDialog/IconStyleChooser.jsx
function IconStyleChooser (line 28) | function IconStyleChooser({
FILE: helpers/react/DynamicDialog/InputBox.jsx
method if (line 64) | if (required && value.trim() === '') {
method if (line 67) | if (validationType) {
FILE: helpers/react/DynamicDialog/MarkdownPreview.jsx
function MarkdownPreview (line 32) | function MarkdownPreview({
FILE: helpers/react/DynamicDialog/MultiSelectChooser.jsx
function MultiSelectChooser (line 43) | function MultiSelectChooser({
FILE: helpers/react/DynamicDialog/NoteChooser.jsx
constant TEAMSPACES_INCLUDE_REGEX (line 23) | const TEAMSPACES_INCLUDE_REGEX = /today|week/
function NoteChooser (line 90) | function NoteChooser({
method if (line 771) | if (note.filename === '__NEW_NOTE__') {
FILE: helpers/react/DynamicDialog/PatternChooser.jsx
function PatternChooser (line 28) | function PatternChooser({
FILE: helpers/react/DynamicDialog/SearchableChooser.jsx
method if (line 589) | if (event && (event.altKey || event.metaKey) && onOptionClick) {
method if (line 621) | if (items && items.length > 0) {
FILE: helpers/react/DynamicDialog/SpaceChooser.jsx
function SpaceChooser (line 43) | function SpaceChooser({
method if (line 284) | if (space.id === '__all__') {
method if (line 298) | if (space.id === '__all__') {
FILE: helpers/react/DynamicDialog/TableOfContents.jsx
function generateHeadingId (line 20) | function generateHeadingId(label: string): string {
function scrollToHeading (line 32) | function scrollToHeading(headingId: string): void {
function scrollToHeadingElement (line 116) | function scrollToHeadingElement(element: HTMLElement, headingId: string)...
method if (line 186) | if (containerRef && containerRef.current) {
method if (line 255) | if (parent instanceof HTMLElement && parent.classList.contains('ui-headi...
FILE: helpers/react/DynamicDialog/TemplateJSBlock.jsx
function TemplateJSBlock (line 25) | function TemplateJSBlock({
FILE: helpers/react/DynamicDialog/dateFormatOptions.js
method if (line 23) | if (typeof NotePlan !== 'undefined' && NotePlan.environment) {
FILE: helpers/react/DynamicDialog/dialogElementRenderer.js
function ColorChooserField (line 46) | function ColorChooserField({ item, value, handleFieldChange, ...rest }: ...
function IconChooserField (line 50) | function IconChooserField({ item, value, handleFieldChange, ...rest }: a...
function PatternChooserField (line 54) | function PatternChooserField({ item, value, handleFieldChange, ...rest }...
function IconStyleChooserField (line 58) | function IconStyleChooserField({ item, value, handleFieldChange, ...rest...
function renderItem (line 107) | function renderItem({
method if (line 624) | if (item.key) {
method if (line 1173) | if (item.key) {
method if (line 1225) | if (item.key) {
method if (line 1298) | if (item.key) {
FILE: helpers/react/DynamicDialog/useRequestWithRetry.js
method if (line 90) | if (!requestFromPlugin || !enabled) {
method onError (line 113) | onError(maxRetriesError)
method if (line 122) | if (hasAbortController) {
method onSuccess (line 229) | onSuccess(normalizedResponse)
method onError (line 273) | onError(error)
FILE: helpers/react/DynamicDialog/valueInsertData.js
constant PATTERNS (line 9) | const PATTERNS = ['lined', 'squared', 'mini-squared', 'dotted']
constant ICON_STYLES (line 12) | const ICON_STYLES = ['solid', 'light', 'regular']
constant FA_ICON_NAMES (line 15) | const FA_ICON_NAMES = [
FILE: helpers/react/FilterableList.jsx
function FilterableList (line 50) | function FilterableList({
FILE: helpers/react/InfoIcon.jsx
function InfoIcon (line 32) | function InfoIcon({
FILE: helpers/react/List.jsx
function List (line 46) | function List({
FILE: helpers/react/Modal/ModalWithTooltip.jsx
function ModalWithTooltip (line 28) | function ModalWithTooltip({
FILE: helpers/react/ModifierHints.jsx
function ModifierHints (line 42) | function ModifierHints({
FILE: helpers/react/SimpleDialog.jsx
function SimpleDialog (line 41) | function SimpleDialog({ isOpen, title, message, buttons, buttonLabels, o...
FILE: helpers/react/TestingPane.jsx
method if (line 179) | if (!test.skip) {
FILE: helpers/react/ThemedSelect.jsx
function ThemedSelect (line 245) | function ThemedSelect(props: Props): any {
FILE: helpers/react/pluginRequestEnvelope.js
method if (line 84) | if (envelope.success) {
FILE: helpers/react/reactDev.js
function decodeHTMLEntities (line 12) | function decodeHTMLEntities(text: string): string {
function stringToColor (line 29) | function stringToColor(input: string): string {
function hexToRgb (line 49) | function hexToRgb(hex: string): { r: number, g: number, b: number }
function adjustBrightness (line 63) | function adjustBrightness(_r: number, _g: number, _b: number): { r: numb...
FILE: helpers/react/reactUtils.js
function splitUnicode (line 12) | function splitUnicode(text: string): Array<string> {
function unicodeLength (line 23) | function unicodeLength(text: string): number {
method if (line 95) | if (!path || path.length <= maxLength) {
method for (line 147) | for (let i = 1; i < parts.length - 1; i++) {
FILE: helpers/react/routerUtils.js
function normalizeSharedInvokeResult (line 19) | function normalizeSharedInvokeResult(raw: any, pluginJson: any): any {
function getWindowIdFromRequest (line 99) | function getWindowIdFromRequest(data: any, defaultWindowId: string): str...
FILE: helpers/react/testSimpleDialog.js
function testSimpleDialog (line 20) | async function testSimpleDialog(): Promise<void> {
FILE: helpers/react/useEffectGuard.js
function useEffectGuard (line 30) | function useEffectGuard(
function useEffectTracker (line 91) | function useEffectTracker(componentName: string, effectName: string, opt...
FILE: helpers/react/userInput.jsx
method if (line 152) | if (event.key === 'Enter') {
method if (line 231) | if (value === 'ok') {
method if (line 237) | if (event.key === 'Enter') {
FILE: helpers/regex.js
constant RE_SCHEDULED_DATES_G (line 26) | const RE_SCHEDULED_DATES_G: RegExp = />(today|tomorrow|yesterday|(([0-9]...
constant RE_FIRST_SCHEDULED_DATE_CAPTURE (line 27) | const RE_FIRST_SCHEDULED_DATE_CAPTURE: RegExp =
constant RE_ARROW_DATES_G (line 29) | const RE_ARROW_DATES_G: RegExp = />(today|tomorrow|yesterday|(([0-9]{4})...
constant RE_EVENT_LINK (line 83) | const RE_EVENT_LINK: RegExp = /!\[.*\]\(\d{4}-[01]\d-[0123]\d\s[0-2]\d:[...
constant RE_NOTELINK_G (line 109) | const RE_NOTELINK_G: RegExp = /\[\[[^\[]+\]\]/g
constant RE_NOTELINK_CAPTURE_TITLE_G (line 110) | const RE_NOTELINK_CAPTURE_TITLE_G: RegExp = /\[\[([^\[]+)\]\]/g
constant RE_MARKDOWN_LINKS_CAPTURE_G (line 113) | const RE_MARKDOWN_LINKS_CAPTURE_G: RegExp = /\[([^\]]+)\]\(([^\)]+)\)/g
constant RE_MARKDOWN_LINK_PATH_CAPTURE (line 114) | const RE_MARKDOWN_LINK_PATH_CAPTURE: RegExp = /\[.+?\]\(([^\)]*?)\)/
constant RE_MARKDOWN_LINK_PATH_CAPTURE_G (line 115) | const RE_MARKDOWN_LINK_PATH_CAPTURE_G: RegExp = /\[(.+?)\]\([^\)]*?\)/g
constant RE_SIMPLE_URI_MATCH (line 116) | const RE_SIMPLE_URI_MATCH: RegExp = /([\w-]+:\/\/[\w\.\/\?\#\&\d\-\=%*~,...
constant RE_SIMPLE_URI_MATCH_G (line 117) | const RE_SIMPLE_URI_MATCH_G: RegExp = /([\w-]+:\/\/[\w\.\/\?\#\&\d\-\=%*...
constant RE_SIMPLE_BARE_URI_MATCH_G (line 119) | const RE_SIMPLE_BARE_URI_MATCH_G: RegExp = /((?!([\("'])).|^)([\b\w-]+:\...
constant RE_BARE_URI_MATCH_G (line 128) | const RE_BARE_URI_MATCH_G: RegExp = /(?:^|[\s\.,;!?:])((www\.[^\s\[\](),...
constant RE_SYNC_MARKER (line 131) | const RE_SYNC_MARKER: RegExp = /\^[A-Za-z0-9]{6}(?![A-Za-z0-9])/
constant UUID_PATTERN (line 135) | const UUID_PATTERN = '([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[...
constant TEAMSPACE_INDICATOR (line 137) | const TEAMSPACE_INDICATOR = '%%NotePlanCloud%%'
constant RE_TEAMSPACE_INDICATOR_AND_ID (line 138) | const RE_TEAMSPACE_INDICATOR_AND_ID: RegExp = new RegExp(`^${TEAMSPACE_I...
constant RE_TEAMSPACE_NOTE_UUID (line 139) | const RE_TEAMSPACE_NOTE_UUID: RegExp = new RegExp(`[^%]/(${UUID_PATTERN}...
constant RE_DONE_MENTION (line 142) | const RE_DONE_MENTION: RegExp = new RegExp(`@done\\(${RE_DATE_TIME}\\)`,...
constant PUNCT_CLASS_STR (line 143) | const PUNCT_CLASS_STR = `[\[\]!"#\$%&'\(\)\*\+,\-\.\/:;<=>\?@\\\^_\`\{\|...
constant PUNCT_CLASS_STR_QUOTED (line 144) | const PUNCT_CLASS_STR_QUOTED = '[\\[\\]!"#\\$%&\'\\(\\)\\*\\+,\\-\\.\\/:...
constant RE_NP_HASHTAG (line 194) | const RE_NP_HASHTAG: RegExp = /(?:^|[^A-Za-z0-9_])(#(?:[\w\d]+(?:[\/\-][...
constant RE_NP_HASHTAG_G (line 195) | const RE_NP_HASHTAG_G: RegExp = /(?:^|[^A-Za-z0-9_])(#(?:[\w\d]+(?:[\/\-...
constant RE_HASHTAG_G (line 198) | const RE_HASHTAG_G: RegExp = new RegExp(/(?:\s|^|\"|\(|\)|\')(#[A-Za-z][...
constant RE_NP_MENTION (line 204) | const RE_NP_MENTION: RegExp = /(?:^|[^A-Za-z0-9_])(@(?:[\w\d]+(?:[\/\-][...
constant RE_NP_MENTION_G (line 205) | const RE_NP_MENTION_G: RegExp = /(?:^|[^A-Za-z0-9_])(@(?:[\w\d]+(?:[\/\-...
constant RE_NOTE_TITLE_CAPTURE (line 208) | const RE_NOTE_TITLE_CAPTURE: RegExp = /\[\[(.*?)(?:#(.*?))?\]*\]\]/ // t...
constant ANY_TYPE_OF_INITIAL_TASK_INDICATOR (line 211) | const ANY_TYPE_OF_INITIAL_TASK_INDICATOR = `^\s*(\*|\-|\+|\d+\.])`
constant RE_ANY_TYPE_OF_OPEN_TASK_OR_CHECKLIST_MARKER (line 212) | const RE_ANY_TYPE_OF_OPEN_TASK_OR_CHECKLIST_MARKER: RegExp = /^\s*(\[[ \...
constant RE_ANY_TYPE_OF_OPEN_TASK_OR_CHECKLIST_MARKER_MULTI_LINE (line 213) | const RE_ANY_TYPE_OF_OPEN_TASK_OR_CHECKLIST_MARKER_MULTI_LINE: RegExp = ...
constant RE_ANY_TYPE_OF_CLOSED_TASK_OR_CHECKLIST_MARKER (line 214) | const RE_ANY_TYPE_OF_CLOSED_TASK_OR_CHECKLIST_MARKER: RegExp = /^\s*[\*\...
constant RE_ANY_TYPE_OF_CLOSED_TASK_OR_CHECKLIST_MARKER_MULTI_LINE (line 215) | const RE_ANY_TYPE_OF_CLOSED_TASK_OR_CHECKLIST_MARKER_MULTI_LINE: RegExp ...
function formRegExForUsersOpenTasks (line 224) | function formRegExForUsersOpenTasks(multiLine: boolean): RegExp {
function isCalendarNoteFilename (line 247) | function isCalendarNoteFilename(stringToTest: string): boolean {
FILE: helpers/regexEscape.js
function escapeRegExp (line 18) | function escapeRegExp(str: string): string {
FILE: helpers/search.js
function caseInsensitiveArrayIncludes (line 19) | function caseInsensitiveArrayIncludes(searchTerm: string, arrayToSearch:...
method if (line 360) | if (wantedHashtags.length > 0) {
method if (line 386) | if (wantedMentions.length > 0) {
FILE: helpers/sorting.js
constant RE_HASHTAGS (line 57) | const RE_HASHTAGS: RegExp = /\B#([a-zA-Z0-9\/]+\b)/g
constant RE_MENTIONS (line 58) | const RE_MENTIONS: RegExp = /\B@([a-zA-Z0-9\/]+\b)/g
constant RE_LEADING_EXCLAMATIONS (line 59) | const RE_LEADING_EXCLAMATIONS: RegExp = /^\s*(!+)/g // at start of conte...
constant RE_LEADING_PARENS_PRIORITY (line 60) | const RE_LEADING_PARENS_PRIORITY: RegExp = /^\s*\(([a-zA-z])\)\B/g // mu...
function getNumericPriority (line 177) | function getNumericPriority(item: SortableParagraphSubset): number {
function getNumericPriorityFromPara (line 204) | function getNumericPriorityFromPara(para: TParagraph): number {
method for (line 211) | for (let c = 0; c < paras.length; c++) {
FILE: helpers/stringTransforms.js
function truncateHTML (line 39) | function truncateHTML(htmlIn: string, maxLength: number, dots: boolean =...
function convertAllLinksToHTMLLinks (line 95) | function convertAllLinksToHTMLLinks(original: string): string {
function changeBareLinksToHTMLLink (line 112) | function changeBareLinksToHTMLLink(original: string, addWebIcon: boolean...
function getLinkDisplayTextFromBareURL (line 145) | function getLinkDisplayTextFromBareURL(linkURL: string): string {
function changeMarkdownLinksToHTMLLink (line 172) | function changeMarkdownLinksToHTMLLink(original: string, addWebIcon: boo...
function stripLinksFromString (line 200) | function stripLinksFromString(original: string, leaveLinkText: boolean =...
function stripDateRefsFromString (line 222) | function stripDateRefsFromString(original: string): string {
function stripTodaysDateRefsFromString (line 245) | function stripTodaysDateRefsFromString(original: string): string {
function stripThisWeeksDateRefsFromString (line 268) | function stripThisWeeksDateRefsFromString(original: string): string {
function stripBackwardsDateRefsFromString (line 292) | function stripBackwardsDateRefsFromString(original: string): string {
function stripWikiLinksFromString (line 315) | function stripWikiLinksFromString(original: string): string {
function stripHashtagsFromString (line 334) | function stripHashtagsFromString(original: string): string {
function stripBlockIDsFromString (line 430) | function stripBlockIDsFromString(original: string): string {
function stripAllTagssFromString (line 451) | function stripAllTagssFromString(original: string): string {
function stripAllMarkersFromString (line 468) | function stripAllMarkersFromString(original: string, stripTags: false, s...
function stripAllInternalReferencesFromString (line 486) | function stripAllInternalReferencesFromString(original: string, stripTag...
function stripTaskMarkersFromString (line 503) | function stripTaskMarkersFromString(original: string): string {
function stripMailtoLinks (line 520) | function stripMailtoLinks(email: string): string {
function convertMarkdownLinks (line 529) | function convertMarkdownLinks(text: string): string {
function encodeRFC3986URIComponent (line 542) | function encodeRFC3986URIComponent(input: string): string {
function decodeRFC3986URIComponent (line 567) | function decodeRFC3986URIComponent(input: string): string {
function removeDateTags (line 580) | function removeDateTags(content: string): string {
function removeDateTagsAndToday (line 595) | function removeDateTagsAndToday(tag: string, removeAllCalendarPeriodNote...
function removeRepeats (line 620) | function removeRepeats(content: string): string {
function stripDoubleQuotes (line 659) | function stripDoubleQuotes(text: string): string {
function prepAndTruncateMarkdownForDisplay (line 734) | function prepAndTruncateMarkdownForDisplay(markdownIn: string, maxLength...
FILE: helpers/teamspace.js
constant TEAMSPACE_FA_ICON (line 19) | const TEAMSPACE_FA_ICON = 'fa-regular fa-cube' // used from v3.19
constant PRIVATE_FA_ICON (line 20) | const PRIVATE_FA_ICON = 'fa-solid fa-user'
function isTeamspaceNoteFromFilename (line 30) | function isTeamspaceNoteFromFilename(filenameIn: string): boolean {
function getFilenameWithoutTeamspaceID (line 44) | function getFilenameWithoutTeamspaceID(filenameIn: string): string {
function getTeamspaceIDFromFilename (line 67) | function getTeamspaceIDFromFilename(filenameIn: string): string {
function parseTeamspaceFilename (line 93) | function parseTeamspaceFilename(filenameIn: string): { filename: string,...
FILE: helpers/testing/CustomError.js
class AssertionError (line 4) | class AssertionError extends Error {
method constructor (line 8) | constructor(message: string, expected: any, received: any) {
FILE: helpers/testing/expect.js
method if (line 37) | if (actual !== expected) {
method if (line 63) | if (actual !== undefined) {
method if (line 75) | if (actual !== null) {
method if (line 87) | if (!actual) {
method if (line 99) | if (actual) {
method if (line 112) | if (!Array.isArray(actual)) {
method if (line 159) | if (typeof actual !== 'number' || actual >= value) {
FILE: helpers/timeblocks.js
constant RE_ISO_DATE (line 15) | const RE_ISO_DATE = '\\d{4}-[01]\\d{1}-\\d{2}' // this is now a near dup...
constant RE_HOURS (line 16) | const RE_HOURS = '[0-2]?\\d'
constant RE_HOURS_EXT (line 17) | const RE_HOURS_EXT = `(${RE_HOURS}|NOON|noon|MIDNIGHT|midnight)`
constant RE_MINUTES (line 18) | const RE_MINUTES = '[0-5]\\d'
constant RE_TIME (line 19) | const RE_TIME = `${RE_HOURS}:${RE_MINUTES}`
constant RE_TIME_EXT (line 20) | const RE_TIME_EXT = `${RE_HOURS_EXT}:${RE_MINUTES}`
constant RE_AMPM (line 22) | const RE_AMPM = `\\s?(AM|am|PM|pm)` // logic changed in v3.4
constant RE_AMPM_OPT (line 23) | const RE_AMPM_OPT = `${RE_AMPM}?`
constant RE_TIME_TO (line 24) | const RE_TIME_TO = `\\s?(\\-|\\–|\\~)\\s?`
constant RE_START_OF_LINE (line 27) | const RE_START_OF_LINE = `(?:^|\\s)`
constant TIMEBLOCK_PARA_TYPES (line 48) | const TIMEBLOCK_PARA_TYPES = ['title', 'open', 'done', 'list', 'checklis...
constant TIMEBLOCK_ACTIVE_PARA_TYPES (line 49) | const TIMEBLOCK_ACTIVE_PARA_TYPES = ['title', 'open', 'list', 'checklist']
constant RE_TIMEBLOCK (line 145) | const RE_TIMEBLOCK = `${RE_TIME}${RE_AMPM_OPT}(${RE_TIME_TO}${RE_TIME}${...
constant RE_TIMEBLOCK_IN_LINE (line 146) | const RE_TIMEBLOCK_IN_LINE = `${RE_START_OF_LINE}${RE_TIMEBLOCK}`
function isTimeBlockLine (line 167) | function isTimeBlockLine(contentString: string, mustContainStringArg: st...
function isTypeThatCanHaveATimeBlock (line 221) | function isTypeThatCanHaveATimeBlock(para: TParagraph): boolean {
function isTypeThatCanHaveAnActiveTimeBlock (line 232) | function isTypeThatCanHaveAnActiveTimeBlock(para: TParagraph): boolean {
function isTimeBlockPara (line 246) | function isTimeBlockPara(para: TParagraph, timeblockTextMustContainStrin...
function isActiveOrFutureTimeBlockPara (line 261) | function isActiveOrFutureTimeBlockPara(para: TParagraph, mustContainStri...
function getStartTimeStrFromParaContent (line 317) | function getStartTimeStrFromParaContent(content: string): string {
function getEndTimeStrFromParaContent (line 334) | function getEndTimeStrFromParaContent(content: string): string {
function getEndTimeObjFromParaContent (line 391) | function getEndTimeObjFromParaContent(content: string): { hours: number,...
FILE: helpers/urls.js
method catch (line 105) | catch (err) {
function findProjectNoteUrlInText (line 117) | function findProjectNoteUrlInText(text: string, allowCalendarNotes: bool...
FILE: helpers/userInput.js
constant TEAMSPACE_ICON_COLOR (line 22) | const TEAMSPACE_ICON_COLOR = 'green-700'
function parseJSON5 (line 27) | function parseJSON5(contents: string): ?{ [string]: ?mixed }
function chooseDecoratedOptionWithModifiers (line 125) | async function chooseDecoratedOptionWithModifiers(
method if (line 180) | if (typeof CommandBar.textPrompt === 'function') {
method if (line 200) | if (typeof CommandBar.textPrompt === 'function') {
function showMessageWithList (line 240) | async function showMessageWithList(message: string, list: Array<string>,...
function showMessageYesNo (line 263) | async function showMessageYesNo(message: string, choicesArray: Array<str...
function showMessageYesNoCancel (line 285) | async function showMessageYesNoCancel(message: string, choicesArray: Arr...
method if (line 513) | if (folder === newFolderText) {
method if (line 691) | if (includeFolderPath) {
method catch (line 819) | catch (error) {
method if (line 1098) | if (!allSettings.canBeEmpty) {
method Number (line 1125) | Number(reply)
FILE: helpers/utils.js
function isOpenNotScheduled (line 52) | function isOpenNotScheduled(t: TParagraph): boolean {
function isOpenAndScheduled (line 61) | function isOpenAndScheduled(t: TParagraph): boolean {
function isOpenOrScheduled (line 70) | function isOpenOrScheduled(t: TParagraph): boolean {
function isOpenTaskNotScheduled (line 79) | function isOpenTaskNotScheduled(t: TParagraph): boolean {
FILE: jest.customSummaryReporter.js
class CustomReporter (line 9) | class CustomReporter {
method constructor (line 10) | constructor(globalConfig, reporterOptions, reporterContext) {
method onRunComplete (line 17) | onRunComplete(testContexts, results) {
FILE: jgclark.DailyJournal/src/index.js
function init (line 36) | function init(): void {
function onSettingsUpdated (line 45) | async function onSettingsUpdated(): Promise<void> {
function onUpdateOrInstall (line 49) | async function onUpdateOrInstall(): Promise<void> {
function updateSettings (line 89) | async function updateSettings() {
FILE: jgclark.DailyJournal/src/journal.js
function dailyJournalQuestions (line 24) | async function dailyJournalQuestions(): Promise<void> {
function weeklyJournalQuestions (line 39) | async function weeklyJournalQuestions(): Promise<void> {
function monthlyJournalQuestions (line 55) | async function monthlyJournalQuestions(): Promise<void> {
function quarterlyJournalQuestions (line 70) | async function quarterlyJournalQuestions(): Promise<void> {
function yearlyJournalQuestions (line 88) | async function yearlyJournalQuestions(): Promise<void> {
method if (line 234) | if (originalLine.startsWith('-')) {
FILE: jgclark.DailyJournal/src/templatesStartEnd.js
constant NOTE_TYPE_CONFIG (line 19) | const NOTE_TYPE_CONFIG = {
method if (line 55) | if (Editor.note && isNoteType(Editor.note) && !workToday) {
function renderAndInsertTemplate (line 72) | async function renderAndInsertTemplate(
function applyTemplateToNote (line 116) | async function applyTemplateToNote(
function dayStart (line 189) | async function dayStart(workToday: boolean = false): Promise<void> {
function todayStart (line 194) | async function todayStart(): Promise<void> {
function weekStart (line 199) | async function weekStart(): Promise<void> {
function monthStart (line 204) | async function monthStart(): Promise<void> {
function dayEnd (line 209) | async function dayEnd(workToday: boolean = false): Promise<void> {
function todayEnd (line 214) | async function todayEnd(): Promise<void> {
function weekEnd (line 219) | async function weekEnd(): Promise<void> {
function monthEnd (line 224) | async function monthEnd(): Promise<void> {
FILE: jgclark.Dashboard/src/NPHooks.js
function init (line 23) | function init(): void {
function onSettingsUpdated (line 35) | async function onSettingsUpdated(): Promise<void> {
function versionCheck (line 44) | async function versionCheck(): Promise<void> {
FILE: jgclark.Dashboard/src/__tests__/dashboardHelpers.test.js
constant PLUGIN_NAME (line 23) | const PLUGIN_NAME = `jgclark.Dashboard`
constant FILENAME (line 24) | const FILENAME = `dashboardHelpers`
FILE: jgclark.Dashboard/src/bannerClickHandlers.js
method catch (line 53) | catch (error) {
FILE: jgclark.Dashboard/src/constants.js
constant WEBVIEW_WINDOW_ID (line 13) | const WEBVIEW_WINDOW_ID = `${pluginJson['plugin.id']}.main` // will be u...
FILE: jgclark.Dashboard/src/countDoneTasks.js
constant CHANGED_NOTE_FILE (line 17) | const CHANGED_NOTE_FILE = '../../data/jgclark.Dashboard/todaysChangedNot...
constant LAST_TIME_THIS_WAS_RUN_PREF (line 18) | const LAST_TIME_THIS_WAS_RUN_PREF = 'jgclark.Dashboard.todayDoneCountsLi...
function getNumCompletedTasksFromNote (line 60) | function getNumCompletedTasksFromNote(
FILE: jgclark.Dashboard/src/dashboardHelpers.js
method for (line 323) | for (const teamspace of DataStore.teamspaces) {
method if (line 749) | if (!p) {
method if (line 873) | if (!allowedTeamspaceIDs || allowedTeamspaceIDs.length === 0) {
method includes (line 880) | includes(note.teamspaceID)
method if (line 1234) | if (!p) {
FILE: jgclark.Dashboard/src/dashboardHooks.js
function haveOpenItemsChanged (line 48) | function haveOpenItemsChanged(note: TNote): boolean {
FILE: jgclark.Dashboard/src/dashboardSettings.js
function normaliseDashboardNumberSettings (line 491) | function normaliseDashboardNumberSettings(settingsIn: TAnyObject): TAnyO...
FILE: jgclark.Dashboard/src/dataGeneration.js
method if (line 245) | if (currentMonthlyNote) {
method if (line 379) | if (sortedRefParas.length > 0) {
method if (line 439) | if (currentQuarterlyNote) {
FILE: jgclark.Dashboard/src/dataGenerationDays.js
method if (line 80) | if (currentDailyNote) {
method if (line 218) | if (sortedRefParas.length > 0) {
method if (line 269) | if (!config.showTimeBlockSection) {
method if (line 295) | if (item.para && isActiveOrFutureTimeBlockPara(item.para, mustContainStr...
method if (line 388) | if (yesterdaysNote) {
method if (line 453) | if (sortedRefParas.length > 0) {
method if (line 526) | if (tomorrowsNote) {
method if (line 618) | if (sortedRefParas.length > 0) {
FILE: jgclark.Dashboard/src/dataGenerationOverdue.js
method for (line 40) | for (let c = 0; c < 13; c++) {
FILE: jgclark.Dashboard/src/dataGenerationPriority.js
method for (line 48) | for (let c = 0; c < 30; c++) {
method if (line 225) | if (isOpenNotScheduled(paragraph) && getNumericPriorityFromPara(paragrap...
FILE: jgclark.Dashboard/src/dataGenerationProjects.js
function makeProjectRowItem (line 22) | function makeProjectRowItem(sectionCode: TSectionCode, project: any, ite...
function makeNextActionTaskItems (line 57) | function makeNextActionTaskItems(
method if (line 120) | if (!(await pluginIsInstalled('jgclark.Reviews'))) {
FILE: jgclark.Dashboard/src/dataGenerationSearch.js
function externallyStartSearch (line 39) | async function externallyStartSearch(
function getParagraphFromSearchResult (line 406) | function getParagraphFromSearchResult(rnal: noteAndLine): TParagraph {
FILE: jgclark.Dashboard/src/dataGenerationWeeks.js
method if (line 56) | if (currentWeeklyNote) {
method if (line 204) | if (sortedRefParas.length > 0) {
method if (line 324) | if (sortedRefParas.length > 0) {
FILE: jgclark.Dashboard/src/diagnosticGenerator.js
function generateDiagnosticsFile (line 31) | async function generateDiagnosticsFile() {
FILE: jgclark.Dashboard/src/index.js
function backupSettings (line 80) | async function backupSettings(): Promise<void> {
function onUpdateOrInstall (line 90) | async function onUpdateOrInstall(): Promise<void> {
FILE: jgclark.Dashboard/src/moveClickHandlers.js
function calculateNewDateStr (line 43) | function calculateNewDateStr(
method catch (line 101) | catch (error) {
FILE: jgclark.Dashboard/src/moveDayClickHandlers.js
function filterParasByPriority (line 42) | function filterParasByPriority(
function removeChildItems (line 72) | function removeChildItems(paras: Array<TParagraphForDashboard>): Array<T...
function scheduleAllOverdueOpenToToday (line 466) | async function scheduleAllOverdueOpenToToday(
FILE: jgclark.Dashboard/src/perspectiveHelpers.js
constant PROJECT_LIST_WINDOW_ID (line 71) | const PROJECT_LIST_WINDOW_ID = `jgclark.Reviews.rich-review-list` // not...
method for (line 138) | for (const thisP of perspectivesArray) {
function logPerspectives (line 148) | function logPerspectives(perspectivesArray: Array<TPerspectiveDef>, logA...
method if (line 172) | if (perspectiveSettings.find((s) => s.name === '-') === undefined) {
method if (line 235) | if (!perspectiveSettings) {
method if (line 247) | if (!perspectiveSettings) {
method if (line 371) | if (!allDefs || allDefs.length === 0) {
method logDebug (line 629) | logDebug('cleanDashboardSettingsInAPerspective', `- Removing key '${key}'`)
method if (line 743) | if (p.name !== '-') {
FILE: jgclark.Dashboard/src/pluginToHTMLBridge.js
function removeEmptySearchOrSavedSections (line 72) | function removeEmptySearchOrSavedSections(sections: Array<any>, _handler...
function runPluginCommand (line 93) | async function runPluginCommand(data: TPluginCommandSimplified) {
function bridgeClickDashboardItem (line 108) | async function bridgeClickDashboardItem(data: MessageDataObject) {
function processActionOnReturn (line 440) | async function processActionOnReturn(handlerResultIn: TBridgeClickHandle...
FILE: jgclark.Dashboard/src/react/components/Button.jsx
function Button (line 14) | function Button(props: ButtonProps): React$Node {
FILE: jgclark.Dashboard/src/react/components/CircularProgressBar.jsx
function CircularProgressBar (line 28) | function CircularProgressBar(props: ProgressBarProps): Node {
FILE: jgclark.Dashboard/src/react/components/CommandButton.jsx
function CommandButton (line 21) | function CommandButton(inputObj: ButtonProps): React$Node {
FILE: jgclark.Dashboard/src/react/components/Dialog.jsx
function onDialogClose (line 34) | function onDialogClose(xWasClicked: boolean) {
method if (line 40) | if (isOpen && dialogRef.current) {
function handleKeyDown (line 102) | function handleKeyDown(event: KeyboardEvent) {
FILE: jgclark.Dashboard/src/react/components/DialogForProjectItems.jsx
function handleTitleClick (line 109) | function handleTitleClick(e: MouseEvent) { // MouseEvent will contain th...
function handleButtonClick (line 144) | function handleButtonClick(_event: MouseEvent, controlStr: string, type:...
FILE: jgclark.Dashboard/src/react/components/DialogForTaskItems.jsx
method if (line 251) | if (!visibleItems[i].processed) {
method if (line 398) | if (forceClose) {
FILE: jgclark.Dashboard/src/react/components/DropdownMenu.jsx
function DropdownMenu (line 55) | function DropdownMenu({
FILE: jgclark.Dashboard/src/react/components/Header/AddToAnyNote.jsx
method if (line 228) | if (space !== null && space !== undefined) {
FILE: jgclark.Dashboard/src/react/components/Header/Header.jsx
function handleButtonClick (line 234) | function handleButtonClick(_event: MouseEvent, controlStr: string, handl...
FILE: jgclark.Dashboard/src/react/components/Header/headerDropdownHandlers.js
method if (line 159) | if (openDropdownMenu && openDropdownMenu !== menu && dropdownMenuChanges...
method if (line 174) | if (!openDropdownMenu && dropdownMenuChangesMade) {
FILE: jgclark.Dashboard/src/react/components/IdleTimer.jsx
constant TICK_INTERVAL (line 19) | const TICK_INTERVAL = 15000 // 15 seconds
constant LEGAL_DRIFT_THRESHHOLD (line 23) | const LEGAL_DRIFT_THRESHHOLD = 10000
function IdleTimer (line 48) | function IdleTimer({ idleTime, onIdleTimeout, userIsInteracting = false ...
FILE: jgclark.Dashboard/src/react/components/ItemContent.jsx
function ItemContent (line 24) | function ItemContent({ item /*, children */, thisSection }: Props): Reac...
FILE: jgclark.Dashboard/src/react/components/ItemGrid.jsx
function ItemGrid (line 22) | function ItemGrid({ items, thisSection, onToggleShowAll }: Props): React...
FILE: jgclark.Dashboard/src/react/components/ItemNoteLink.jsx
function ItemNoteLink (line 31) | function ItemNoteLink({ item, thisSection, alwaysShowNoteTitle = false, ...
FILE: jgclark.Dashboard/src/react/components/ItemRow.jsx
function ItemRow (line 30) | function ItemRow({ item, thisSection, onToggleShowAll }: Props): Node {
FILE: jgclark.Dashboard/src/react/components/MultiSelectSpaces.jsx
method if (line 50) | if (newSelected.length > 1) {
FILE: jgclark.Dashboard/src/react/components/NoTasks.jsx
constant DECAY (line 47) | const DECAY = 4 // confetti decay in seconds
constant SPREAD (line 48) | const SPREAD = 60 // degrees to spread from the angle of the cannon
constant GRAVITY (line 49) | const GRAVITY = 1200
function getLength (line 52) | function getLength(x0: number, y0: number, x1: number, y1: number) {
function getDegAngle (line 59) | function getDegAngle(x0: number, y0: number, x1: number, y1: number) {
FILE: jgclark.Dashboard/src/react/components/NoteTitleLink.jsx
function NoteTitleLink (line 35) | function NoteTitleLink({
FILE: jgclark.Dashboard/src/react/components/ProjectItem.jsx
function ProjectItem (line 24) | function ProjectItem({ item, thisSection }: Props): Node {
FILE: jgclark.Dashboard/src/react/components/Section/Section.jsx
method if (line 60) | if (previousValue === currentValue || previousValue == null || currentVa...
function getTaskOrItemDisplayString (line 532) | function getTaskOrItemDisplayString(count: number, type: string) {
FILE: jgclark.Dashboard/src/react/components/Section/sectionHelpers.js
method if (line 34) | if (section) {
method if (line 360) | if (!dashboardSettings || dashboardSettings.showWinsSection === false) {
FILE: jgclark.Dashboard/src/react/components/Section/useSectionSortAndFilter.jsx
constant DEFAULT_MAX_ITEMS_TO_SHOW (line 30) | const DEFAULT_MAX_ITEMS_TO_SHOW = 20
constant DEFAULT_FILTER_BY_PRIORITY (line 31) | const DEFAULT_FILTER_BY_PRIORITY = false
method if (line 298) | if (items.length === 0) {
method if (line 305) | if (treatSingleItemTypesAsZeroItems.includes(i.itemType)) {
FILE: jgclark.Dashboard/src/react/components/SettingsDialog.jsx
function stateOfControllingSetting (line 88) | function stateOfControllingSetting(item: TSettingItem): boolean {
FILE: jgclark.Dashboard/src/react/components/SmallCircularProgressIndicator.jsx
function SmallCircularProgressIndicator (line 18) | function SmallCircularProgressIndicator({ item }: Props): Node {
FILE: jgclark.Dashboard/src/react/components/StatusIcon.jsx
function getClassNameFromType (line 36) | function getClassNameFromType(itemType: string): string {
function handleIconClick (line 68) | function handleIconClick(event: MouseEvent) {
FILE: jgclark.Dashboard/src/react/components/TaskItem.jsx
function TaskItem (line 21) | function TaskItem({ item, thisSection }: Props): Node {
FILE: jgclark.Dashboard/src/react/components/TasksFiltered.jsx
function handleLineClick (line 26) | function handleLineClick(_e: MouseEvent) {
FILE: jgclark.Dashboard/src/react/components/WebView.jsx
function WebView (line 67) | function WebView({ data, dispatch, reactSettings, setReactSettings }: Pr...
FILE: jgclark.Dashboard/src/react/customHooks/useMidnightRollover.jsx
constant TICK_INTERVAL_MS (line 20) | const TICK_INTERVAL_MS = 15000
function useMidnightRollover (line 30) | function useMidnightRollover(options: UseMidnightRolloverOptions): void {
FILE: jgclark.Dashboard/src/react/customHooks/useRefreshTimer.jsx
function useRefreshTimer (line 42) | function useRefreshTimer(options: RefreshTimerOptions): RefreshTimerRetu...
FILE: jgclark.Dashboard/src/react/customHooks/useWatchForResizes.jsx
function handleResize (line 22) | function handleResize() {
FILE: jgclark.Dashboard/src/react/dashboardLineToNPDisplayHTML.js
function applyDashboardSettingsToDisplayedItemHtml (line 49) | function applyDashboardSettingsToDisplayedItemHtml(mainContent: string, ...
function makeNoteTitleWithOpenActionFromTitle (line 70) | function makeNoteTitleWithOpenActionFromTitle(noteTitle: string, folderN...
method for (line 128) | for (const capture of captures) {
method for (line 139) | for (const capturedTitle of captures) {
FILE: jgclark.Dashboard/src/react/reducers/actionTypes.js
constant DASHBOARD_ACTIONS (line 2) | const DASHBOARD_ACTIONS = {
constant PERSPECTIVE_ACTIONS (line 7) | const PERSPECTIVE_ACTIONS = {
FILE: jgclark.Dashboard/src/react/reducers/dashboardSettingsReducer.js
function dashboardSettingsReducer (line 29) | function dashboardSettingsReducer(state: TDashboardSettings, action: TDa...
FILE: jgclark.Dashboard/src/react/reducers/perspectiveSettingsReducer.js
function perspectiveSettingsReducer (line 26) | function perspectiveSettingsReducer(state: TPerspectiveSettings, action:...
FILE: jgclark.Dashboard/src/react/support/settingsHelpers.js
method item (line 13) | item) => {
FILE: jgclark.Dashboard/src/react/support/uiElementRenderHelpers.js
function renderItem (line 47) | function renderItem({
FILE: jgclark.Dashboard/src/reactMain.js
constant RESOURCE_LINKS_FOR_HEADER (line 32) | const RESOURCE_LINKS_FOR_HEADER = `
function showDemoDashboard (line 70) | async function showDemoDashboard(): Promise<void> {
function setSetting (line 83) | async function setSetting(key: string, value: string): Promise<void> {
function setSettings (line 129) | async function setSettings(paramsIn: string): Promise<void> {
function makeSettingsAsCallback (line 177) | async function makeSettingsAsCallback(): Promise<void> {
function showPerspective (line 222) | async function showPerspective(name: string = ''): Promise<void> {
function showSections (line 236) | async function showSections(sectionCodeList: string = ''): Promise<void> {
function showDashboardReact (line 301) | async function showDashboardReact(callMode: string = 'full', perspective...
function reactWindowInitialisedSoStartGeneratingData (line 371) | async function reactWindowInitialisedSoStartGeneratingData(): Promise<vo...
FILE: jgclark.Dashboard/src/refreshClickHandlers.js
function refreshDashboard (line 35) | async function refreshDashboard(): Promise<void> {
function incrementallyRefreshSomeSections (line 98) | async function incrementallyRefreshSomeSections(
FILE: jgclark.Dashboard/src/routeRequestsFromReact.js
method addTaskToNote (line 37) | addTaskToNote(data, pluginJson)
FILE: jgclark.Dashboard/src/shared.js
function parseSettings (line 23) | function parseSettings(settingsStr: string): any {
method if (line 92) | if (allKeys.has(key)) {
method if (line 138) | if (keysToEliminate.includes(key)) {
FILE: jgclark.Dashboard/src/tagMentionCache.js
constant TAG_CACHE_UPDATE_INTERVAL_HOURS (line 34) | const TAG_CACHE_UPDATE_INTERVAL_HOURS = 1 // how often to update the cache
constant TAG_CACHE_GENERATE_INTERVAL_HOURS (line 35) | const TAG_CACHE_GENERATE_INTERVAL_HOURS = 24 // how often to re-generate...
constant TAG_CACHE_ONLY_FOR_OPEN_ITEMS (line 38) | const TAG_CACHE_ONLY_FOR_OPEN_ITEMS = true // Note: if false, then for J...
constant TAG_CACHE_FOR_ALL_TAGS (line 39) | const TAG_CACHE_FOR_ALL_TAGS = false // if true, then will cache all tag...
constant EXCLUDED_TAGS_OR_MENTIONS (line 41) | const EXCLUDED_TAGS_OR_MENTIONS = ['@done', '@start', '@review', '@revie...
function clearTagMentionCacheGenerationPref (line 48) | function clearTagMentionCacheGenerationPref(): void {
function isTagMentionCacheAvailable (line 56) | function isTagMentionCacheAvailable(): boolean {
function isTagMentionCacheAvailableForItem (line 60) | function isTagMentionCacheAvailableForItem(item: string): boolean {
function isTagMentionCacheGenerationScheduled (line 73) | function isTagMentionCacheGenerationScheduled(): boolean {
function scheduleTagMentionCacheGenerationIfTooOld (line 81) | function scheduleTagMentionCacheGenerationIfTooOld(generatedAtStr: strin...
method if (line 123) | if (DataStore.fileExists(wantedTagMentionsListFile)) {
method if (line 482) | if (
method if (line 561) | if (TAG_CACHE_FOR_ALL_TAGS) {
FILE: jgclark.EventHelpers/src/eventsHelpers.js
function getDefaultConfig (line 22) | function getDefaultConfig(): EventsConfig {
FILE: jgclark.EventHelpers/src/eventsToNotes.js
constant SORT_ORDER_CALENDAR (line 30) | const SORT_ORDER_CALENDAR = 'calendar'
constant DEFAULT_HEADING_LEVEL (line 31) | const DEFAULT_HEADING_LEVEL = 2
constant DEFAULT_FORMAT_EVENTS (line 32) | const DEFAULT_FORMAT_EVENTS = '- *|CAL|*: *|TITLE|* (*|START|*)*| with A...
constant DEFAULT_FORMAT_ALL_DAY (line 33) | const DEFAULT_FORMAT_ALL_DAY = '- *|CAL|*: *|TITLE|**| with ATTENDEENAME...
method if (line 131) | if (paramStringIn == null) {
method if (line 277) | if (!includeAllDayEvents && e.isAllDay) {
method catch (line 629) | catch (error) {
FILE: jgclark.EventHelpers/src/index.js
function init (line 20) | function init(): void {
function onSettingsUpdated (line 27) | async function onSettingsUpdated(): Promise<void> {
function onUpdateOrInstall (line 32) | async function onUpdateOrInstall(): Promise<void> {
function updateSettings (line 51) | async function updateSettings() {
FILE: jgclark.EventHelpers/src/offsets.js
function shiftDates (line 39) | async function shiftDates(): Promise<void> {
function maybeRemoveDoneDatePart (line 132) | function maybeRemoveDoneDatePart(content: string, config: any): string {
function maybeRemoveProcessedTagName (line 142) | function maybeRemoveProcessedTagName(content: string, config: any): stri...
function shiftIsoDatesInContent (line 150) | function shiftIsoDatesInContent(content: string, interval: string): { co...
function shiftWeekDatesInContent (line 170) | function shiftWeekDatesInContent(
function isSectionBoundary (line 198) | function isSectionBoundary(levelNow: number, prevLevel: number, content:...
function appendComputedFinalDateIfWanted (line 204) | function appendComputedFinalDateIfWanted(
function setCurrentTargetDateIfBareDate (line 221) | function setCurrentTargetDateIfBareDate(
function ensureBaseDate (line 244) | async function ensureBaseDate(
function applyOffsetInLine (line 266) | function applyOffsetInLine(
function processDateOffsets (line 302) | async function processDateOffsets(): Promise<void> {
FILE: jgclark.EventHelpers/src/timeblocks.js
function timeBlocksToCalendar (line 17) | async function timeBlocksToCalendar(): Promise<void> {
FILE: jgclark.Filer/src/IDs.js
function addIDAndAddToOtherNote (line 25) | async function addIDAndAddToOtherNote(): Promise<void> {
FILE: jgclark.Filer/src/index.js
function init (line 36) | function init(): void {
function onSettingsUpdated (line 45) | async function onSettingsUpdated(): Promise<void> {
function onUpdateOrInstall (line 49) | async function onUpdateOrInstall(_testUpdate: boolean = false): Promise<...
function updateSettings (line 65) | async function updateSettings() {
FILE: jgclark.Filer/src/moveCompletedToDone.js
constant PLUGIN_ID (line 32) | const PLUGIN_ID = "jgclark.Filer"
constant MAKE_HEADINGS_ONE_DEEPER (line 33) | const MAKE_HEADINGS_ONE_DEEPER: boolean = false
constant WHEN_TO_MOVE_ASK_EACH_TIME (line 34) | const WHEN_TO_MOVE_ASK_EACH_TIME = 'ask each time'
constant WHEN_TO_MOVE_SECTION_COMPLETE (line 35) | const WHEN_TO_MOVE_SECTION_COMPLETE = 'move when whole section complete'
constant WHEN_TO_MOVE_ANY_COMPLETE (line 36) | const WHEN_TO_MOVE_ANY_COMPLETE = 'move when any are complete'
method if (line 82) | if (!isClosed(candidate)) {
function appendParasUnderHeading (line 198) | function appendParasUnderHeading(
function getOrCreateNamedDoneSection (line 262) | function getOrCreateNamedDoneSection(note: TNote, doneSectionHeadingName...
function getNamedDoneSectionBlock (line 300) | function getNamedDoneSectionBlock(note: TNote, doneSectionHeadingName: s...
FILE: jgclark.Filer/src/moveItems.js
function getParagraphsToMove (line 34) | function getParagraphsToMove(
function addDateBacklinkIfNeeded (line 74) | function addDateBacklinkIfNeeded(parasInBlock: Array<TParagraph>, note: ...
function moveParagraphsToNote (line 93) | function moveParagraphsToNote(
function moveParaBlock (line 129) | async function moveParaBlock(): Promise<void> {
function moveParas (line 144) | async function moveParas(withBlockContext: boolean = false): Promise<voi...
function moveParasToThisWeekly (line 241) | async function moveParasToThisWeekly(): Promise<void> {
function moveParasToNextWeekly (line 250) | async function moveParasToNextWeekly(): Promise<void> {
function moveParasToCalendarWeekly (line 262) | async function moveParasToCalendarWeekly(destDate: Date, withBlockContex...
function moveParasToToday (line 330) | async function moveParasToToday(): Promise<void> {
function moveParasToTomorrow (line 339) | async function moveParasToTomorrow(): Promise<void> {
function moveParasToCalendarDate (line 351) | async function moveParasToCalendarDate(destDate: Date, withBlockContext:...
FILE: jgclark.MOCs/src/MOCs.js
method logWarn (line 107) | logWarn('makeMOC', `User has cancelled operation.`)
method for (line 227) | for (let i = 0; i < uniqTitles.length; i++) {
method if (line 243) | if (config.showEmptyOccurrences) {
FILE: jgclark.MOCs/src/index.js
function init (line 18) | function init(): void {
function onSettingsUpdated (line 23) | function onSettingsUpdated(): void {
function onUpdateOrInstall (line 29) | async function onUpdateOrInstall(): Promise<void> {
function updateSettings (line 48) | async function updateSettings() {
FILE: jgclark.NoteHelpers/__tests__/duplicateNote.test.js
constant PLUGIN_NAME (line 9) | const PLUGIN_NAME = `{{pluginID}}`
constant FILENAME (line 10) | const FILENAME = `NPPluginMain`
FILE: jgclark.NoteHelpers/__tests__/unLinkedNoteFinder.test.js
constant PLUGIN_NAME (line 9) | const PLUGIN_NAME = `{{pluginID}}`
constant FILENAME (line 10) | const FILENAME = `NPPluginMain`
FILE: jgclark.NoteHelpers/src/countDays.js
function countAndAddDays (line 8) | async function countAndAddDays(): Promise<void> {
function getMsSinceEpoch (line 47) | function getMsSinceEpoch(year: number, month: number, date: number): num...
function daysUntil (line 59) | function daysUntil(year: number, month: number, date: number): number {
FILE: jgclark.NoteHelpers/src/duplicateNote.js
function generateCandidateTitleForDuplicate (line 22) | function generateCandidateTitleForDuplicate(originalTitle: string): stri...
function updateTitleInContentArray (line 96) | function updateTitleInContentArray(
function duplicateNote (line 137) | async function duplicateNote(): Promise<void> {
FILE: jgclark.NoteHelpers/src/helpers/renameNotes.js
function renameNoteToTitle (line 24) | async function renameNoteToTitle(
FILE: jgclark.NoteHelpers/src/index.js
function resetCaches (line 44) | function resetCaches() {
function init (line 50) | function init(): void {
function onSettingsUpdated (line 55) | async function onSettingsUpdated(): Promise<void> {
function onUpdateOrInstall (line 62) | async function onUpdateOrInstall(): Promise<void> {
function updateSettings (line 81) | async function updateSettings(): Promise<void> {
function testChooseFolder (line 97) | async function testChooseFolder(): Promise<void> {
FILE: jgclark.NoteHelpers/src/indexFolders.js
constant INDEX_NOTE_FRONTMATTER (line 41) | const INDEX_NOTE_FRONTMATTER = `---
FILE: jgclark.NoteHelpers/src/lib/commands/filenameToTitle.js
function filenameToTitle (line 10) | async function filenameToTitle(): Promise<void> {
FILE: jgclark.NoteHelpers/src/lib/commands/listInconsistentNames.js
function listInconsistentNames (line 23) | async function listInconsistentNames(folderIn: string = ''): Promise<voi...
FILE: jgclark.NoteHelpers/src/lib/commands/renameInconsistentNames.js
function renameInconsistentNames (line 19) | async function renameInconsistentNames(folderIn: string = ''): Promise<v...
FILE: jgclark.NoteHelpers/src/lib/commands/titleToFilename.js
function titleToFilename (line 10) | async function titleToFilename(): Promise<void> {
FILE: jgclark.NoteHelpers/src/listPublishedNotes.js
function listPublishedNotes (line 18) | async function listPublishedNotes(): Promise<void> {
FILE: jgclark.NoteHelpers/src/newNote.js
function getSuggestedTitleFromContent (line 26) | function getSuggestedTitleFromContent(content: string): string {
function newNote (line 59) | async function newNote(): Promise<void> {
function newNoteFromClipboard (line 100) | async function newNoteFromClipboard(): Promise<void> {
function newNoteFromSelection (line 143) | async function newNoteFromSelection(): Promise<void> {
FILE: jgclark.NoteHelpers/src/noteHelpers.js
method if (line 83) | if (type === 'Calendar') {
method if (line 174) | if (
method if (line 345) | if (Editor == null) {
FILE: jgclark.NoteHelpers/src/noteNavigation.js
method "Couldn't find a '## Done' section. Stopping." (line 119) | "Couldn't find a '## Done' section. Stopping.")
FILE: jgclark.NoteHelpers/src/unlinkedNoteFinder.js
constant CODE_BLOCK_PLACEHOLDER (line 8) | const CODE_BLOCK_PLACEHOLDER = '8ce08058-d387-4d3a-8043-4f3b7ef63eb7'
constant MARKDOWN_LINK_PLACEHOLDER (line 9) | const MARKDOWN_LINK_PLACEHOLDER = '975b7115-5568-4bc6-b6c8-6603350572ea'
function triggerFindUnlinkedNotes (line 14) | async function triggerFindUnlinkedNotes() {
function findUnlinkedNotesInCurrentNote (line 21) | async function findUnlinkedNotesInCurrentNote() {
function findUnlinkedNotesInAllNotes (line 31) | async function findUnlinkedNotesInAllNotes() {
function findUnlinkedNotes (line 45) | async function findUnlinkedNotes(notes: Array<TNote>): Promise<number> {
FILE: jgclark.NoteHelpers/src/writeModified.js
function writeModified (line 28) | async function writeModified(): Promise<void> {
FILE: jgclark.PeriodicReviews/src/reviewHTMLViewGenerator.js
constant RE_DONE_MENTION_STRIP_FOR_SUMMARY_G (line 56) | const RE_DONE_MENTION_STRIP_FOR_SUMMARY_G = new RegExp(RE_DONE_DATE_OPT_...
function escapeHTML (line 82) | function escapeHTML(input: string): string {
function stripPresentationDelimiters (line 96) | function stripPresentationDelimiters(input: string): string {
function formatTaskAsHTML (line 105) | function formatTaskAsHTML(taskContent: string): string {
function splitSegmentAtTypeMarker (line 141) | function splitSegmentAtTypeMarker(segment: string, questionType: string)...
function makeReviewInlineControl (line 163) | function makeReviewInlineControl(
function makeQuestionLineDiv (line 213) | function makeQuestionLineDiv(
function formatWinsSummaryHeading (line 295) | function formatWinsSummaryHeading(count: number): string {
method if (line 344) | if (eventsForPeriod.length === 0) {
FILE: jgclark.QuickCapture/src/index.js
function init (line 38) | function init(): void {
function onSettingsUpdated (line 50) | async function onSettingsUpdated(): Promise<void> {
function onUpdateOrInstall (line 54) | async function onUpdateOrInstall(): Promise<void> {
function updateSettings (line 70) | async function updateSettings() {
function smartCreateTest (line 83) | async function smartCreateTest(): Promise<void> {
function smartAppendParasTest (line 100) | async function smartAppendParasTest(): Promise<void> {
function smartPrependParasTest (line 114) | async function smartPrependParasTest(): Promise<void> {
function insertParasTest (line 128) | async function insertParasTest(): Promise<void> {
FILE: jgclark.QuickCapture/src/quickCapture.js
method logDebug (line 531) | logDebug(pluginJson, `starting /qajw with arg0='${textArg}'`)
method logDebug (line 564) | logDebug(pluginJson, `starting /qajm with arg0='${textArg}'`)
method logDebug (line 597) | logDebug(pluginJson, `starting /qajy with arg0='${textArg}'`)
FILE: jgclark.QuickCapture/src/quickCaptureHelpers.js
method if (line 55) | if (useDefaultsIfNecessary) {
FILE: jgclark.RepeatExtensions/src/index.js
function init (line 19) | function init(): void {
function onSettingsUpdated (line 28) | async function onSettingsUpdated(): Promise<void> {
function onUpdateOrInstall (line 32) | async function onUpdateOrInstall(testUpdate: boolean = false): Promise<v...
function updateSettings (line 56) | async function updateSettings() {
FILE: jgclark.RepeatExtensions/src/repeatMain.js
method if (line 98) | if (origPara.heading !== lastHeading) {
method if (line 110) | if (!runSilently) {
method if (line 120) | if (DataStore.isPluginInstalledByID('dwertheimer.TaskSorting')) {
FILE: jgclark.RepeatExtensions/src/repeatTrigger.js
function onEditorWillSave (line 22) | async function onEditorWillSave(): Promise<void> {
FILE: jgclark.Reviews/experiments/chart-experiments.js
function basicChartTest (line 7) | function basicChartTest() {
FILE: jgclark.Reviews/experiments/fontTests.js
function testFonts (line 7) | function testFonts(): void {
FILE: jgclark.Reviews/requiredFiles/HTMLWinCommsSwitchboard.js
function delay (line 20) | async function delay(time) {
function getScrollPos (line 28) | function getScrollPos() {
function onMessageFromPlugin (line 46) | function onMessageFromPlugin(type, data) {
function updateDivReceived (line 74) | function updateDivReceived(data) {
function deleteItemRow (line 84) | function deleteItemRow(data) {
function setReviewingProject (line 94) | function setReviewingProject(data) {
function clearReviewingProject (line 127) | function clearReviewingProject(data) {
function completeTaskInDisplay (line 153) | async function completeTaskInDisplay(data) {
function completeChecklistInDisplay (line 200) | async function completeChecklistInDisplay(data) {
function cancelTaskInDisplay (line 245) | async function cancelTaskInDisplay(data) {
function cancelChecklistInDisplay (line 274) | async function cancelChecklistInDisplay(data) {
function toggleTypeInDisplay (line 303) | function toggleTypeInDisplay(data) {
function updateItemFilename (line 322) | function updateItemFilename(data) {
function updateItemContent (line 346) | function updateItemContent(data) {
function unscheduleItem (line 371) | async function unscheduleItem(data) {
function setPriorityInDisplay (line 389) | function setPriorityInDisplay(data) {
function onClickProjectListItem (line 433) | function onClickProjectListItem(data) {
function onChangeCheckbox (line 442) | function onChangeCheckbox(settingName, state) {
function findAncestor (line 459) | function findAncestor(startElement, tagName) {
function findDescendantByTagName (line 477) | function findDescendantByTagName(startElement, tagName) {
function findDescendantByClassName (line 498) | function findDescendantByClassName(startElement, className) {
function deleteHTMLItem (line 512) | function deleteHTMLItem(ID) {
function addClassToID (line 524) | function addClassToID(ID, newerClass) {
function replaceClassInID (line 536) | function replaceClassInID(ID, replacementClass) {
function replaceHTMLinID (line 546) | function replaceHTMLinID(ID, html, innerText) {
function replaceHTMLinElement (line 560) | function replaceHTMLinElement(elem, html, innerText) {
function setCounter (line 573) | function setCounter(counterID, value) {
function incrementItemCount (line 578) | function incrementItemCount(counterID) {
function decrementItemCount (line 589) | function decrementItemCount(counterID) {
function getNumItemsInSection (line 607) | function getNumItemsInSection(sectionID, tagName) {
function getNumItemsInSectionByClass (line 635) | function getNumItemsInSectionByClass(sectionID, className) {
function doesIDExist (line 656) | function doesIDExist(itemID) {
function showError (line 661) | function showError(message) {
FILE: jgclark.Reviews/requiredFiles/projectListEvents.js
function showProjectControlDialog (line 51) | function showProjectControlDialog(dataObject) {
function getFolderFromFilename (line 217) | function getFolderFromFilename(fullFilename) {
function setPositionForDialog (line 239) | function setPositionForDialog(approxDialogWidth, approxDialogHeight, dia...
function addIconClickEventListeners (line 289) | function addIconClickEventListeners() {
function addContentEventListeners (line 328) | function addContentEventListeners() {
function addReviewProjectEventListeners (line 360) | function addReviewProjectEventListeners() {
function addCommandButtonEventListeners (line 381) | function addCommandButtonEventListeners() {
function handleIconClick (line 416) | function handleIconClick(id, itemType, encodedfilename, encodedcontent, ...
function handleContentClick (line 448) | function handleContentClick(event, id, encodedfilename, encodedcontent) {
FILE: jgclark.Reviews/requiredFiles/showTimeAgo.js
function showTimeAgo (line 13) | function showTimeAgo() {
FILE: jgclark.Reviews/src/__tests__/allProjectsListHelpers.sorting.test.js
function sortConfig (line 11) | function sortConfig(partial: Object): ReviewConfig {
FILE: jgclark.Reviews/src/allProjectsListHelpers.js
constant MS_PER_HOUR (line 34) | const MS_PER_HOUR = 1000 * 60 * 60
constant ERROR_FILENAME_PLACEHOLDER (line 35) | const ERROR_FILENAME_PLACEHOLDER = 'error'
constant ERROR_READING_PLACEHOLDER (line 36) | const ERROR_READING_PLACEHOLDER = '<error reading'
constant SEQUENTIAL_TAG_DEFAULT (line 37) | const SEQUENTIAL_TAG_DEFAULT = '#sequential'
function makeProjectListCacheKey (line 45) | function makeProjectListCacheKey(filename: string, tag: string): string {
function loadRawAllProjectsListSnapshot (line 53) | function loadRawAllProjectsListSnapshot(): Array<any> {
method if (line 81) | if (typeof project.isReadyForReview === 'boolean') {
method if (line 453) | if (row != null && typeof row.filename === 'string' && row.filename !== ...
FILE: jgclark.Reviews/src/convertNote.js
constant RE_REVIEW_INTERVAL (line 22) | const RE_REVIEW_INTERVAL = /^[+\-]?\d+[BbDdWwMmQqYy]$/
function parseBoolFromForm (line 39) | function parseBoolFromForm(value: mixed): boolean {
function buildFrontmatterAttrs (line 102) | function buildFrontmatterAttrs(inputs: ConvertToProjectInputs, config: R...
function isValidNoteForConvert (line 131) | function isValidNoteForConvert(note: CoreNoteFields): boolean {
FILE: jgclark.Reviews/src/index.js
function init (line 67) | function init(): void {
function testSettingsUpdated (line 81) | async function testSettingsUpdated(): Promise<void> {
function onSettingsUpdated (line 85) | async function onSettingsUpdated(): Promise<void> {
function onUpdateOrInstall (line 101) | async function onUpdateOrInstall(): Promise<void> {
FILE: jgclark.Reviews/src/migration.js
constant SEQUENTIAL_TAG_DEFAULT (line 20) | const SEQUENTIAL_TAG_DEFAULT = '#sequential'
FILE: jgclark.Reviews/src/migrationLog.js
constant MIGRATION_TSV_HEADER (line 13) | const MIGRATION_TSV_HEADER = 'filename\ttitle\tdate\tdetail'
function beginSuppressMigrationLogForBatchConstruction (line 22) | function beginSuppressMigrationLogForBatchConstruction(): void {
function endSuppressMigrationLogForBatchConstruction (line 30) | function endSuppressMigrationLogForBatchConstruction(): void {
function sanitizeTsvCell (line 39) | function sanitizeTsvCell(value: string): string {
method if (line 58) | if (migrationLogSuppressDepth > 0 && options?.force !== true) {
FILE: jgclark.Reviews/src/pluginToHTMLBridge.js
function onMessageFromHTMLView (line 62) | async function onMessageFromHTMLView(actionType: string, data: any): any {
function runPluginCommand (line 99) | async function runPluginCommand(data: any) {
function bridgeChangeCheckbox (line 143) | async function bridgeChangeCheckbox(data: SettingDataObject) {
method catch (line 192) | catch (error) {
function bridgeClickProjectListItem (line 201) | async function bridgeClickProjectListItem(data: MessageDataObject) {
FILE: jgclark.Reviews/src/projectClass.js
constant DEFAULT_REVIEW_INTERVAL (line 57) | const DEFAULT_REVIEW_INTERVAL = '1w'
function mentionStrForMarkdownOutput (line 79) | function mentionStrForMarkdownOutput(prefKey: string, defaultMention: st...
method if (line 420) | if (reISODate.test(parsed)) {
method if (line 424) | if (reISODate.test(parsed)) {
method if (line 428) | if (reISODate.test(parsed)) {
method if (line 432) | if (reISODate.test(parsed)) {
method if (line 436) | if (reISODate.test(parsed)) {
method if (line 443) | if (reInterval.test(parsed)) {
method if (line 791) | if (key !== '' && !keysToRemove.includes(key)) keysToRemove.push(key)
method migrateProjectMetadataLineInEditor (line 1441) | migrateProjectMetadataLineInEditor(possibleThisEditorForMigration)
FILE: jgclark.Reviews/src/projectClassCalculations.js
function createImmutableProjectCopy (line 33) | function createImmutableProjectCopy(project: Project, updates: ProjectUp...
function calcDurationsForProject (line 110) | function calcDurationsForProject(thisProjectIn: Project): Project {
function calcReviewFieldsForProject (line 145) | function calcReviewFieldsForProject(thisProjectIn: Project): Project {
FILE: jgclark.Reviews/src/projectClassHelpers.js
constant RE_ISO_DATE_LINE (line 23) | const RE_ISO_DATE_LINE: RegExp = new RegExp(`^${RE_DATE}$`)
function parseProjectFrontmatterValue (line 68) | function parseProjectFrontmatterValue(rawValue: string): string {
function readRawFrontmatterField (line 93) | function readRawFrontmatterField(note: CoreNoteFields, fieldName: string...
method if (line 119) | if (startDate != null) {
function normalizeProgressDateFromForm (line 173) | function normalizeProgressDateFromForm(value: mixed): string {
function promptAddProgressLineInputs (line 239) | async function promptAddProgressLineInputs(
function getMetadataPresenceState (line 295) | function getMetadataPresenceState(note: TNote, combinedKey: string): {
FILE: jgclark.Reviews/src/projects.js
constant ERROR_FILENAME_PLACEHOLDER (line 26) | const ERROR_FILENAME_PLACEHOLDER = '<error>'
constant ARCHIVE_PROMPT_YES (line 27) | const ARCHIVE_PROMPT_YES = 'Yes'
constant ARCHIVE_PROMPT_NO (line 28) | const ARCHIVE_PROMPT_NO = 'No'
constant DEFAULT_PROJECT_CLOSEOUT_INPUTS (line 37) | const DEFAULT_PROJECT_CLOSEOUT_INPUTS: ProjectCloseoutInputs = {
method if (line 268) | if (!willArchive) {
FILE: jgclark.Reviews/src/projectsHTMLGenerator.js
function projectFolderDisplayLabel (line 31) | function projectFolderDisplayLabel(thisProject: Project, config: ReviewC...
function buildProjectFolderMetadataRowDiv (line 60) | function buildProjectFolderMetadataRowDiv(thisProject: Project, config: ...
method if (line 425) | if (thisProject.isCompleted) {
method if (line 435) | if (thisProject.lastProgressComment !== '') {
method if (line 443) | if (thisProject.dueDays != null && !isNaN(thisProject.dueDays)) {
FILE: jgclark.Reviews/src/projectsWeeklyProgress.js
constant DEFAULT_NUM_WEEKS (line 32) | const DEFAULT_NUM_WEEKS: number = 26
method if (line 320) | if (rows.length < 2) {
FILE: jgclark.Reviews/src/reviewHelpers.js
function getFrontmatterFieldKeyFromMentionPreference (line 97) | function getFrontmatterFieldKeyFromMentionPreference(prefName: string, d...
function getDateMentionNameToFrontmatterKeyMap (line 105) | function getDateMentionNameToFrontmatterKeyMap(): { [string]: string }
function extractTagsOnly (line 123) | function extractTagsOnly(text: string): string {
function populateSeparateDateKeysFromCombinedValue (line 146) | function populateSeparateDateKeysFromCombinedValue(
method if (line 495) | if (lines.length < 3) {
method if (line 502) | if (String(lines[i]).trim() === '---') {
method catch (line 593) | catch (error) {
method if (line 950) | if (mentionParam !== '') {
method if (line 1000) | if (thisEditor.note == null || thisEditor.note.type === 'Calendar' || th...
FILE: jgclark.Reviews/src/reviews.js
constant RICH_PROJECT_LIST_WIN_ID (line 88) | const RICH_PROJECT_LIST_WIN_ID = `${pluginID}.rich-review-list`
method if (line 151) | if (folderDisplayName.includes(']')) {
method catch (line 255) | catch (error) {
method runProjectListRenderers (line 350) | runProjectListRenderers(config, shouldOpen, scrollPos)
method if (line 927) | if (offerConfirm && config.confirmNextReview) {
FILE: jgclark.SearchExtensions/src/externalSearch.js
function extendedSearch (line 18) | async function extendedSearch(
FILE: jgclark.SearchExtensions/src/flexiSearch.js
function showFlexiSearchDialog (line 365) | async function showFlexiSearchDialog(
function flexiSearchHandler (line 417) | async function flexiSearchHandler(
function closeDialogWindow (line 463) | function closeDialogWindow(customId: string): any {
function savePluginPreference (line 482) | function savePluginPreference(key: string, value: string): any {
function getPluginPreference (line 502) | function getPluginPreference(key: string): any {
FILE: jgclark.SearchExtensions/src/index.js
function init (line 42) | function init(): void {
function onSettingsUpdated (line 54) | async function onSettingsUpdated(): Promise<void> {
function onUpdateOrInstall (line 63) | async function onUpdateOrInstall(): Promise<void> {
function updateSettings (line 83) | async function updateSettings() {
FILE: jgclark.SearchExtensions/src/replace.js
function buildReplaceRegex (line 27) | function buildReplaceRegex(searchTerm: typedSearchTerm, searchStr: strin...
method if (line 54) | if (searchTermArg) {
method if (line 82) | if (replaceExpressionArg) {
FILE: jgclark.SearchExtensions/src/saveSearch.js
method logDebug (line 178) | logDebug('quickSearch', `starting with searchTermsArg=${searchTermsArg ?...
method logDebug (line 210) | logDebug('searchPeriod', `starting with searchTermsArg=${searchTermsArg ...
method if (line 342) | if (calledNonInteractively) {
method false (line 371) | false)
method if (line 508) | if (noteOpenInEditor(noteFilename)) {
FILE: jgclark.SearchExtensions/src/searchHelpers.js
constant OPEN_PARA_TYPES (line 91) | const OPEN_PARA_TYPES = ['open', 'scheduled', 'checklist', 'checklistSch...
constant SYNCABLE_PARA_TYPES (line 92) | const SYNCABLE_PARA_TYPES = ['open', 'scheduled', 'checklist', 'checklis...
method if (line 497) | if (j === 0) {
method if (line 539) | if (mustResultObjects.length === 0) {
method if (line 1061) | if (justReplaceSection && outputNote.paragraphs.length > 1) {
method if (line 1119) | if (config.groupResultsByNote) {
FILE: jgclark.SearchExtensions/src/searchTriggers.js
function getUrlParams (line 28) | function getUrlParams(query: string): { [key: string]: string }
function refreshSavedSearch (line 66) | async function refreshSavedSearch(): Promise<void> {
FILE: jgclark.Summaries/requiredFiles/chartStatsScripts.js
function formatTime (line 90) | function formatTime(decimalHours) {
function isTimeTag (line 96) | function isTimeTag(tag) {
function isTotalTag (line 100) | function isTotalTag(tag) {
function isAverageTag (line 104) | function isAverageTag(tag) {
function isCountTag (line 108) | function isCountTag(tag) {
function calculateMovingAverage (line 135) | function calculateMovingAverage(data, windowSize) {
function getMondayOfWeek (line 157) | function getMondayOfWeek(dateStr) {
function calculatePeriodAverageSegments (line 173) | function calculatePeriodAverageSegments(data, dates, windowSize) {
function hasAnyDataPoints (line 242) | function hasAnyDataPoints(data) {
function calculateCompletionRate (line 429) | function calculateCompletionRate(data) {
function calculateStreak (line 435) | function calculateStreak(data) {
function createYesNoHeatmapSection (line 446) | function createYesNoHeatmapSection() {
FILE: jgclark.Summaries/src/TMOccurrences.js
class TMOccurrences (line 54) | class TMOccurrences {
method constructor (line 79) | constructor(term: string, type: string, fromISODateStr: string, toISOD...
method addOccurrence (line 117) | addOccurrence(occurrenceStr: string, dateStrArg: string): void {
method summaryTextForInterval (line 196) | summaryTextForInterval(fromDateISOStr: string, toDateISOStr: string, i...
method getTerm (line 235) | getTerm(paddingSize?: number): string {
method getSparklineForPeriod (line 277) | getSparklineForPeriod(style: string = 'ascii', config: any): string {
method getSummaryForPeriod (line 316) | getSummaryForPeriod(style: string): string {
function makeSparkline (line 417) | function makeSparkline(data: Array<number>, options: Object = {}): string {
function makeYesNoLine (line 470) | function makeYesNoLine(data: Array<number>, options: Object = {}): string {
FILE: jgclark.Summaries/src/chartStats.js
constant CHART_GRID_COLOR_LIGHT_MODE (line 64) | const CHART_GRID_COLOR_LIGHT_MODE = '#33333322'
constant CHART_GRID_COLOR_DARK_MODE (line 65) | const CHART_GRID_COLOR_DARK_MODE = '#CCCCCC22'
constant CHART_AXIS_TEXT_COLOR_LIGHT_MODE (line 67) | const CHART_AXIS_TEXT_COLOR_LIGHT_MODE = '#333333'
constant CHART_AXIS_TEXT_COLOR_DARK_MODE (line 68) | const CHART_AXIS_TEXT_COLOR_DARK_MODE = '#CCCCCC'
function getChartGridColor (line 74) | function getChartGridColor(): string {
function getChartAxisTextColor (line 79) | function getChartAxisTextColor(): string {
method if (line 151) | if (periodOrDays.startsWith('customRange|')) {
function formatDateForDisplay (line 413) | function formatDateForDisplay(dateStr: string): string {
constant DEMO_DATA_FILENAME (line 423) | const DEMO_DATA_FILENAME = '../jgclark.Summaries/demoData.json'
function isValidDemoPayload (line 448) | function isValidDemoPayload(payload: any): boolean {
method if (line 784) | if (!jsonStr || typeof jsonStr !== 'string' || jsonStr.trim() === '') {
FILE: jgclark.Summaries/src/forCharts.js
constant MONTHS_TO_LOOK_BACK_FOR_TASKS (line 38) | const MONTHS_TO_LOOK_BACK_FOR_TASKS = 6
function testTaskGenStats (line 46) | async function testTaskGenStats(): Promise<void> {
method if (line 305) | if (!firstKey) {
method if (line 346) | if (!firstKey) {
method for (line 424) | for (const occ of occs) {
FILE: jgclark.Summaries/src/forHeatmaps.js
constant DEFAULT_HEATMAP_INTERVALS (line 44) | const DEFAULT_HEATMAP_INTERVALS = 180
constant DAYS_PER_WEEK (line 45) | const DAYS_PER_WEEK = 7
constant HEATMAP_ZOOM_POINTS (line 46) | const HEATMAP_ZOOM_POINTS = 36
FILE: jgclark.Summaries/src/gatherOccurrencesHelpers.js
method for (line 38) | for (let i = 1; i < combinedTerms.length; i++) {
method if (line 147) | if (caseInsensitiveTagMatch(normalizedWanted, mention)) {
FILE: jgclark.Summaries/src/index.js
function init (line 42) | function init(): void {
function onSettingsUpdated (line 53) | async function onSettingsUpdated(): Promise<void> {
function onUpdateOrInstall (line 57) | async function onUpdateOrInstall(): Promise<void> {
function updateSettings (line 107) | async function updateSettings() {
FILE: jgclark.Summaries/src/stats.js
function determineCallbackParameters (line 165) | function determineCallbackParameters(periodShortCode: string, periodNumb...
function generateStatsOutput (line 241) | async function generateStatsOutput(
FILE: jgclark.Summaries/src/summaryHelpers.js
constant MAX_SPARKLINE_DAYS (line 59) | const MAX_SPARKLINE_DAYS = 31
function gatherOccurrences (line 100) | function gatherOccurrences(
method if (line 357) | if (showSparklines) {
method if (line 372) | if (showSparklines) {
FILE: jgclark.Summaries/src/testCharting.js
function testHeatMapGeneration1 (line 25) | function testHeatMapGeneration1(): void {
function testHeatMapGeneration2 (line 134) | async function testHeatMapGeneration2(): Promise<void> {
function testHeatMapGeneration3 (line 216) | async function testHeatMapGeneration3(): Promise<void> {
FILE: jgclark.Summaries/src/todayProgress.js
method if (line 166) | if (heading !== '') {
FILE: jgclark.WindowTools/src/WTHelpers.js
constant WINDOW_SET_PREF_KEY (line 29) | const WINDOW_SET_PREF_KEY = 'windowSets'
method for (line 400) | for (const hw of windowSet.htmlWindows) {
method logWindowSet (line 434) | logWindowSet(set, thisMachineName)
method if (line 459) | if (set.name === name) {
method if (line 479) | if (usersVersionHas('mainSidebarControl')) {
method if (line 605) | if (ew.noteType) {
FILE: jgclark.WindowTools/src/index.js
function init (line 68) | function init(): void {
function onSettingsUpdated (line 79) | async function onSettingsUpdated(): Promise<void> {
function onUpdateOrInstall (line 83) | async function onUpdateOrInstall(testUpdate: boolean = false): Promise<v...
FILE: jgclark.WindowTools/src/openers.js
function openNoteNewWindow (line 23) | async function openNoteNewWindow(encodedNoteIdentifier: string = ''): Pr...
function openNoteNewSplit (line 72) | async function openNoteNewSplit(encodedNoteIdentifier: string = ''): Pro...
function openCurrentNoteNewSplit (line 107) | async function openCurrentNoteNewSplit(): Promise<void> {
function openCurrentNoteNewWindow (line 133) | async function openCurrentNoteNewWindow(): Promise<void> {
FILE: jgclark.WindowTools/src/otherWindowTools.js
function constrainMainWindow (line 29) | function constrainMainWindow(): void {
function resetMainWindow (line 50) | async function resetMainWindow(): Promise<void> {
function moveCurrentSplitToMain (line 89) | async function moveCurrentSplitToMain(): Promise<void> {
function swapSplitWindows (line 178) | async function swapSplitWindows(): Promise<void> {
FILE: jgclark.WindowTools/src/windowSets.js
function saveWindowSet (line 68) | async function saveWindowSet(): Promise<void> {
FILE: jgclark.tests/src/index.js
function init (line 16) | function init(): void {
function onSettingsUpdated (line 20) | function onSettingsUpdated(): void {
function onUpdateOrInstall (line 24) | async function onUpdateOrInstall(_config: any = { silent: false }): Prom...
function invokePluginCommandByName (line 28) | async function invokePluginCommandByName() {
function showStartActive (line 33) | function showStartActive(): void {
function logCurrentNoteInfo (line 44) | async function logCurrentNoteInfo(): Promise<void> {
function logNoteInfo (line 51) | async function logNoteInfo(): Promise<void> {
FILE: m1well.Expenses/__tests__/expensesChecks.test.js
constant SIMPLE_CONFIG (line 16) | const SIMPLE_CONFIG = {
constant ALLOWED_DELIMTER (line 46) | const ALLOWED_DELIMTER = [';', '%', 'TAB']
constant CONSOLE_SPY (line 48) | const CONSOLE_SPY = jest.spyOn(console, 'log')
FILE: m1well.Expenses/src/expenses.js
constant CONFIG_KEYS (line 21) | const CONFIG_KEYS = {
constant TRACKING_MODE (line 32) | const TRACKING_MODE = ['Individual', 'Shortcuts', 'Fixed']
constant EXAMPLE_CONFIG (line 35) | const EXAMPLE_CONFIG = `
method if (line 399) | if (createNote) {
FILE: m1well.Expenses/src/expensesChecks.js
constant DEFAULT_DELIMITER (line 8) | const DEFAULT_DELIMITER = ';'
constant ALLOWED_DELIMTER (line 9) | const ALLOWED_DELIMTER = [';', '%', 'TAB']
constant MINIMAL_COLUMNS (line 10) | const MINIMAL_COLUMNS = ['date', 'category', 'amount']
constant ALLOWED_AMOUNT_FORMATS (line 11) | const ALLOWED_AMOUNT_FORMATS = ['full', 'short']
FILE: m1well.Expenses/src/index.js
constant PLUGIN_ID (line 14) | const PLUGIN_ID = 'expenses'
function onUpdateOrInstall (line 17) | async function onUpdateOrInstall(config: any = { silent: false }): Promi...
FILE: nmn.DataQuery/src/index.js
function openTestHTML (line 5) | async function openTestHTML() {
FILE: nmn.TimeTracking/src/index.js
function getOrMadeDataFile (line 19) | function getOrMadeDataFile() {
function setConfig (line 44) | function setConfig(data: TData): null | void {
FILE: np.CallbackURLs/src/NPOpenFolders.js
function getValidatedFolderData (line 12) | function getValidatedFolderData(): Object | null {
function createFolderOptions (line 29) | function createFolderOptions(allFolders: $ReadOnlyArray<string>, folderD...
FILE: np.CallbackURLs/src/NPOpenLinks.js
function openLinksInParagraphs (line 13) | function openLinksInParagraphs(paras: Array<TParagraph>) {
function openIncompleteLinksInNote (line 27) | async function openIncompleteLinksInNote() {
FILE: np.CallbackURLs/src/NPTemplateRunner.js
method templateTitle (line 118) | templateTitle
method if (line 126) | if (!filename) {
function getTemplateArgs (line 142) | async function getTemplateArgs(): Promise<> {
FILE: np.CallbackURLs/src/NPXCallbackWizard.js
method if (line 66) | if (fields.openNote === 'yes') {
method if (line 79) | if (command === 'addText') {
method if (line 438) | if (selectedPara.type === 'title') {
FILE: np.CallbackURLs/src/index.js
function onUpdateOrInstall (line 25) | function onUpdateOrInstall(): void {
function init (line 30) | function init(): void {
function onSettingsUpdated (line 35) | async function onSettingsUpdated(): Promise<void> {
FILE: np.CallbackURLs/src/support/utils.js
method uppercase (line 2) | uppercase(str = null) {
FILE: np.Globals/lib/NPGlobals.js
constant DEFAULT_GLOBALS_CONFIG (line 24) | const DEFAULT_GLOBALS_CONFIG = {
class NPGlobals (line 35) | class NPGlobals {
method constructor (line 37) | constructor() {
method setup (line 45) | static async setup() {
method getSettings (line 54) | static async getSettings(): any {
method updateOrInstall (line 66) | static async updateOrInstall(currentSettings: any, currentVersion: str...
FILE: np.Globals/src/Globals.js
function onUpdateOrInstall (line 10) | async function onUpdateOrInstall(config: any = { silent: false }): Promi...
FILE: np.Globals/src/index.js
function onUpdateOrInstall (line 11) | async function onUpdateOrInstall(): Promise<void> {
FILE: np.MeetingNotes/src/NPMeetingNotes.js
function insertNoteTemplate (line 25) | async function insertNoteTemplate(origFileName: string, dailyNoteDate: D...
method getNoteFromSelection (line 393) | getNoteFromSelection(folder)
method if (line 468) | if (DataStore.projectNoteByFilename(filename)) {
method if (line 664) | if ((onlyMeetingNotes && attributes.type && attributes.type.includes('me...
method catch (line 668) | catch (error) {
FILE: np.MeetingNotes/src/index.js
function init (line 12) | function init(): void {
function onUpdateOrInstall (line 20) | async function onUpdateOrInstall(): Promise<void> {
function onSettingsUpdated (line 24) | async function onSettingsUpdated(): Promise<void> {
FILE: np.MeetingNotes/src/support/hello-world.js
method uppercase (line 2) | uppercase(str = null) {
FILE: np.Preview/requiredFiles/mermaid@10.1.0.min.mjs
function dedent (line 1) | function dedent(templ){var values=[];for(var _i=1;_i<arguments.length;_i...
function getDefaultExportFromCjs (line 1) | function getDefaultExportFromCjs(x){return x&&x.__esModule&&Object.proto...
function commonjsRequire (line 1) | function commonjsRequire(path){throw new Error('Could not dynamically re...
function _typeof (line 1) | function _typeof(obj){"@babel/helpers - typeof";return _typeof="function...
function _setPrototypeOf (line 1) | function _setPrototypeOf(o,p){_setPrototypeOf=Object.setPrototypeOf||fun...
function _isNativeReflectConstruct (line 1) | function _isNativeReflectConstruct(){if(typeof Reflect==="undefined"||!R...
function _construct (line 1) | function _construct(Parent,args,Class){if(_isNativeReflectConstruct()){_...
function _toConsumableArray (line 1) | function _toConsumableArray(arr){return _arrayWithoutHoles(arr)||_iterab...
function _arrayWithoutHoles (line 1) | function _arrayWithoutHoles(arr){if(Array.isArray(arr))return _arrayLike...
function _iterableToArray (line 1) | function _iterableToArray(iter){if(typeof Symbol!=="undefined"&&iter[Sym...
function _unsupportedIterableToArray (line 1) | function _unsupportedIterableToArray(o,minLen){if(!o)return;if(typeof o=...
function _arrayLikeToArray (line 1) | function _arrayLikeToArray(arr,len){if(len==null||len>arr.length)len=arr...
function _nonIterableSpread (line 1) | function _nonIterableSpread(){throw new TypeError("Invalid attempt to sp...
function unapply (line 1) | function unapply(func){return function(thisArg){for(var _len=arguments.l...
function unconstruct (line 1) | function unconstruct(func){return function(){for(var _len2=arguments.len...
function addToSet (line 1) | function addToSet(set,array,transformCaseFunc){transformCaseFunc=transfo...
function clone (line 1) | function clone(object){var newObject=create(null);var property;for(prope...
function lookupGetter (line 1) | function lookupGetter(object,prop){while(object!==null){var desc=getOwnP...
function createDOMPurify (line 1) | function createDOMPurify(){var window=arguments.length>0&&arguments[0]!=...
function M (line 1) | function M(t){this.$L=S(t.locale,null,!0),this.parse(t)}
class Type (line 1) | class Type{constructor(){this.type=TYPE.ALL}get(){return this.type}set(t...
method constructor (line 1) | constructor(){this.type=TYPE.ALL}
method get (line 1) | get(){return this.type}
method set (line 1) | set(type){if(this.type&&this.type!==type)throw new Error("Cannot chang...
method reset (line 1) | reset(){this.type=TYPE.ALL}
method is (line 1) | is(type){return this.type===type}
class Channels (line 1) | class Channels{constructor(data,color){this.color=color;this.changed=fal...
method constructor (line 1) | constructor(data,color){this.color=color;this.changed=false;this.data=...
method set (line 1) | set(data,color){this.color=color;this.changed=false;this.data=data;thi...
method _ensureHSL (line 1) | _ensureHSL(){const data=this.data;const{h:h,s:s,l:l}=data;if(h===undef...
method _ensureRGB (line 1) | _ensureRGB(){const data=this.data;const{r:r,g:g,b:b}=data;if(r===undef...
method r (line 1) | get r(){const data=this.data;const r=data.r;if(!this.type.is(TYPE.HSL)...
method g (line 1) | get g(){const data=this.data;const g=data.g;if(!this.type.is(TYPE.HSL)...
method b (line 1) | get b(){const data=this.data;const b=data.b;if(!this.type.is(TYPE.HSL)...
method h (line 1) | get h(){const data=this.data;const h=data.h;if(!this.type.is(TYPE.RGB)...
method s (line 1) | get s(){const data=this.data;const s=data.s;if(!this.type.is(TYPE.RGB)...
method l (line 1) | get l(){const data=this.data;const l=data.l;if(!this.type.is(TYPE.RGB)...
method a (line 1) | get a(){return this.data.a}
method r (line 1) | set r(r){this.type.set(TYPE.RGB);this.changed=true;this.data.r=r}
method g (line 1) | set g(g){this.type.set(TYPE.RGB);this.changed=true;this.data.g=g}
method b (line 1) | set b(b){this.type.set(TYPE.RGB);this.changed=true;this.data.b=b}
method h (line 1) | set h(h){this.type.set(TYPE.HSL);this.changed=true;this.data.h=h}
method s (line 1) | set s(s){this.type.set(TYPE.HSL);this.changed=true;this.data.s=s}
method l (line 1) | set l(l){this.type.set(TYPE.HSL);this.changed=true;this.data.l=l}
method a (line 1) | set a(a){this.changed=true;this.data.a=a}
method constructor (line 1) | constructor(){this.background="#f4f4f4";this.primaryColor="#fff4dd";this...
method updateColors (line 1) | updateColors(){this.primaryTextColor=this.primaryTextColor||(this.darkMo...
method calculate (line 1) | calculate(overrides){if(typeof overrides!=="object"){this.updateColors()...
method constructor (line 1) | constructor(){this.background="#333";this.primaryColor="#1f2020";this.se...
method updateColors (line 1) | updateColors(){this.secondBkg=lighten$1(this.mainBkg,16);this.lineColor=...
method calculate (line 1) | calculate(overrides){if(typeof overrides!=="object"){this.updateColors()...
method constructor (line 1) | constructor(){this.background="#f4f4f4";this.primaryColor="#ECECFF";this...
method updateColors (line 1) | updateColors(){this.cScale0=this.cScale0||this.primaryColor;this.cScale1...
method calculate (line 1) | calculate(overrides){if(typeof overrides!=="object"){this.updateColors()...
method constructor (line 1) | constructor(){this.background="#f4f4f4";this.primaryColor="#cde498";this...
method updateColors (line 1) | updateColors(){this.cScale0=this.cScale0||this.primaryColor;this.cScale1...
method calculate (line 1) | calculate(overrides){if(typeof overrides!=="object"){this.updateColors()...
class Theme5 (line 1) | class Theme5{constructor(){this.primaryColor="#eee";this.contrast="#7070...
method constructor (line 1) | constructor(){this.primaryColor="#eee";this.contrast="#707070";this.se...
method updateColors (line 1) | updateColors(){this.secondBkg=lighten$1(this.contrast,55);this.border2...
method calculate (line 1) | calculate(overrides){if(typeof overrides!=="object"){this.updateColors...
function isRelativeUrlWithoutProtocol (line 1) | function isRelativeUrlWithoutProtocol(url){return relativeFirstCharacter...
function decodeHtmlCharacters (line 1) | function decodeHtmlCharacters(str){return str.replace(htmlEntitiesRegex,...
function sanitizeUrl$1 (line 1) | function sanitizeUrl$1(url){var sanitizedUrl=decodeHtmlCharacters(url||"...
function ascending$1 (line 1) | function ascending$1(a,b){return a==null||b==null?NaN:a<b?-1:a>b?1:a>=b?...
function descending$1 (line 1) | function descending$1(a,b){return a==null||b==null?NaN:b<a?-1:b>a?1:b>=a...
function bisector (line 1) | function bisector(f){let compare1,compare2,delta;if(f.length!==2){compar...
function zero$1 (line 1) | function zero$1(){return 0}
function number$3 (line 1) | function number$3(x){return x===null?NaN:+x}
class InternMap (line 1) | class InternMap extends Map{constructor(entries,key=keyof){super();Objec...
method constructor (line 1) | constructor(entries,key=keyof){super();Object.defineProperties(this,{_...
method get (line 1) | get(key){return super.get(intern_get(this,key))}
method has (line 1) | has(key){return super.has(intern_get(this,key))}
method set (line 1) | set(key,value){return super.set(intern_set(this,key),value)}
method delete (line 1) | delete(key){return super.delete(intern_delete(this,key))}
function intern_get (line 1) | function intern_get({_intern:_intern,_key:_key},value){const key=_key(va...
function intern_set (line 1) | function intern_set({_intern:_intern,_key:_key},value){const key=_key(va...
function intern_delete (line 1) | function intern_delete({_intern:_intern,_key:_key},value){const key=_key...
function keyof (line 1) | function keyof(value){return value!==null&&typeof value==="object"?value...
function tickSpec (line 1) | function tickSpec(start,stop,count){const step=(stop-start)/Math.max(0,c...
function ticks (line 1) | function ticks(start,stop,count){stop=+stop,start=+start,count=+count;if...
function tickIncrement (line 1) | function tickIncrement(start,stop,count){stop=+stop,start=+start,count=+...
function tickStep (line 1) | function tickStep(start,stop,count){stop=+stop,start=+start,count=+count...
function max$2 (line 1) | function max$2(values,valueof){let max;if(valueof===undefined){for(const...
function min$2 (line 1) | function min$2(values,valueof){let min;if(valueof===undefined){for(const...
function identity$5 (line 1) | function identity$5(x){return x}
function translateX (line 1) | function translateX(x){return"translate("+x+",0)"}
function translateY (line 1) | function translateY(y){return"translate(0,"+y+")"}
function number$2 (line 1) | function number$2(scale){return d=>+scale(d)}
function center (line 1) | function center(scale,offset){offset=Math.max(0,scale.bandwidth()-offset...
function entering (line 1) | function entering(){return!this.__axis}
function axis (line 1) | function axis(orient,scale){var tickArguments=[],tickValues=null,tickFor...
function axisTop (line 1) | function axisTop(scale){return axis(top,scale)}
function axisBottom (line 1) | function axisBottom(scale){return axis(bottom,scale)}
function dispatch (line 1) | function dispatch(){for(var i=0,n=arguments.length,_={},t;i<n;++i){if(!(...
function Dispatch (line 1) | function Dispatch(_){this._=_}
function parseTypenames$1 (line 1) | function parseTypenames$1(typenames,types){return typenames.trim().split...
function get$2 (line 1) | function get$2(type,name){for(var i=0,n=type.length,c;i<n;++i){if((c=typ...
function set$2 (line 1) | function set$2(type,name,callback){for(var i=0,n=type.length;i<n;++i){if...
function namespace (line 1) | function namespace(name){var prefix=name+="",i=prefix.indexOf(":");if(i>...
function creatorInherit (line 1) | function creatorInherit(name){return function(){var document=this.ownerD...
function creatorFixed (line 1) | function creatorFixed(fullname){return function(){return this.ownerDocum...
function creator (line 1) | function creator(name){var fullname=namespace(name);return(fullname.loca...
function none (line 1) | function none(){}
function selector (line 1) | function selector(selector){return selector==null?none:function(){return...
function selection_select (line 1) | function selection_select(select){if(typeof select!=="function")select=s...
function array$1 (line 1) | function array$1(x){return x==null?[]:Array.isArray(x)?x:Array.from(x)}
function empty (line 1) | function empty(){return[]}
function selectorAll (line 1) | function selectorAll(selector){return selector==null?empty:function(){re...
function arrayAll (line 1) | function arrayAll(select){return function(){return array$1(select.apply(...
function selection_selectAll (line 1) | function selection_selectAll(select){if(typeof select==="function")selec...
function matcher (line 1) | function matcher(selector){return function(){return this.matches(selecto...
function childMatcher (line 1) | function childMatcher(selector){return function(node){return node.matche...
function childFind (line 1) | function childFind(match){return function(){return find$2.call(this.chil...
function childFirst (line 1) | function childFirst(){return this.firstElementChild}
function selection_selectChild (line 1) | function selection_selectChild(match){return this.select(match==null?chi...
function children (line 1) | function children(){return Array.from(this.children)}
function childrenFilter (line 1) | function childrenFilter(match){return function(){return filter$1.call(th...
function selection_selectChildren (line 1) | function selection_selectChildren(match){return this.selectAll(match==nu...
function selection_filter (line 1) | function selection_filter(match){if(typeof match!=="function")match=matc...
function sparse (line 1) | function sparse(update){return new Array(update.length)}
function selection_enter (line 1) | function selection_enter(){return new Selection$1(this._enter||this._gro...
function EnterNode (line 1) | function EnterNode(parent,datum){this.ownerDocument=parent.ownerDocument...
function constant$3 (line 1) | function constant$3(x){return function(){return x}}
function bindIndex (line 1) | function bindIndex(parent,group,enter,update,exit,data){var i=0,node,gro...
function bindKey (line 1) | function bindKey(parent,group,enter,update,exit,data,key){var i,node,nod...
function datum (line 1) | function datum(node){return node.__data__}
function selection_data (line 1) | function selection_data(value,key){if(!arguments.length)return Array.fro...
function arraylike (line 1) | function arraylike(data){return typeof data==="object"&&"length"in data?...
function selection_exit (line 1) | function selection_exit(){return new Selection$1(this._exit||this._group...
function selection_join (line 1) | function selection_join(onenter,onupdate,onexit){var enter=this.enter(),...
function selection_merge (line 1) | function selection_merge(context){var selection=context.selection?contex...
function selection_order (line 1) | function selection_order(){for(var groups=this._groups,j=-1,m=groups.len...
function selection_sort (line 1) | function selection_sort(compare){if(!compare)compare=ascending;function ...
function ascending (line 1) | function ascending(a,b){return a<b?-1:a>b?1:a>=b?0:NaN}
function selection_call (line 1) | function selection_call(){var callback=arguments[0];arguments[0]=this;ca...
function selection_nodes (line 1) | function selection_nodes(){return Array.from(this)}
function selection_node (line 1) | function selection_node(){for(var groups=this._groups,j=0,m=groups.lengt...
function selection_size (line 1) | function selection_size(){let size=0;for(const node of this)++size;retur...
function selection_empty (line 1) | function selection_empty(){return!this.node()}
function selection_each (line 1) | function selection_each(callback){for(var groups=this._groups,j=0,m=grou...
function attrRemove$1 (line 1) | function attrRemove$1(name){return function(){this.removeAttribute(name)}}
function attrRemoveNS$1 (line 1) | function attrRemoveNS$1(fullname){return function(){this.removeAttribute...
function attrConstant$1 (line 1) | function attrConstant$1(name,value){return function(){this.setAttribute(...
function attrConstantNS$1 (line 1) | function attrConstantNS$1(fullname,value){return function(){this.setAttr...
function attrFunction$1 (line 1) | function attrFunction$1(name,value){return function(){var v=value.apply(...
function attrFunctionNS$1 (line 1) | function attrFunctionNS$1(fullname,value){return function(){var v=value....
function selection_attr (line 1) | function selection_attr(name,value){var fullname=namespace(name);if(argu...
function defaultView (line 1) | function defaultView(node){return node.ownerDocument&&node.ownerDocument...
function styleRemove$1 (line 1) | function styleRemove$1(name){return function(){this.style.removeProperty...
function styleConstant$1 (line 1) | function styleConstant$1(name,value,priority){return function(){this.sty...
function styleFunction$1 (line 1) | function styleFunction$1(name,value,priority){return function(){var v=va...
function selection_style (line 1) | function selection_style(name,value,priority){return arguments.length>1?...
function styleValue (line 1) | function styleValue(node,name){return node.style.getPropertyValue(name)|...
function propertyRemove (line 1) | function propertyRemove(name){return function(){delete this[name]}}
function propertyConstant (line 1) | function propertyConstant(name,value){return function(){this[name]=value}}
function propertyFunction (line 1) | function propertyFunction(name,value){return function(){var v=value.appl...
function selection_property (line 1) | function selection_property(name,value){return arguments.length>1?this.e...
function classArray (line 1) | function classArray(string){return string.trim().split(/^|\s+/)}
function classList (line 1) | function classList(node){return node.classList||new ClassList(node)}
function ClassList (line 1) | function ClassList(node){this._node=node;this._names=classArray(node.get...
function classedAdd (line 1) | function classedAdd(node,names){var list=classList(node),i=-1,n=names.le...
function classedRemove (line 1) | function classedRemove(node,names){var list=classList(node),i=-1,n=names...
function classedTrue (line 1) | function classedTrue(names){return function(){classedAdd(this,names)}}
function classedFalse (line 1) | function classedFalse(names){return function(){classedRemove(this,names)}}
function classedFunction (line 1) | function classedFunction(names,value){return function(){(value.apply(thi...
function selection_classed (line 1) | function selection_classed(name,value){var names=classArray(name+"");if(...
function textRemove (line 1) | function textRemove(){this.textContent=""}
function textConstant$1 (line 1) | function textConstant$1(value){return function(){this.textContent=value}}
function textFunction$1 (line 1) | function textFunction$1(value){return function(){var v=value.apply(this,...
function selection_text (line 1) | function selection_text(value){return arguments.length?this.each(value==...
function htmlRemove (line 1) | function htmlRemove(){this.innerHTML=""}
function htmlConstant (line 1) | function htmlConstant(value){return function(){this.innerHTML=value}}
function htmlFunction (line 1) | function htmlFunction(value){return function(){var v=value.apply(this,ar...
function selection_html (line 1) | function selection_html(value){return arguments.length?this.each(value==...
function raise (line 1) | function raise(){if(this.nextSibling)this.parentNode.appendChild(this)}
function selection_raise (line 1) | function selection_raise(){return this.each(raise)}
function lower (line 1) | function lower(){if(this.previousSibling)this.parentNode.insertBefore(th...
function selection_lower (line 1) | function selection_lower(){return this.each(lower)}
function selection_append (line 1) | function selection_append(name){var create=typeof name==="function"?name...
function constantNull (line 1) | function constantNull(){return null}
function selection_insert (line 1) | function selection_insert(name,before){var create=typeof name==="functio...
function remove (line 1) | function remove(){var parent=this.parentNode;if(parent)parent.removeChil...
function selection_remove (line 1) | function selection_remove(){return this.each(remove)}
function selection_cloneShallow (line 1) | function selection_cloneShallow(){var clone=this.cloneNode(false),parent...
function selection_cloneDeep (line 1) | function selection_cloneDeep(){var clone=this.cloneNode(true),parent=thi...
function selection_clone (line 1) | function selection_clone(deep){return this.select(deep?selection_cloneDe...
function selection_datum (line 1) | function selection_datum(value){return arguments.length?this.property("_...
function contextListener (line 1) | function contextListener(listener){return function(event){listener.call(...
function parseTypenames (line 1) | function parseTypenames(typenames){return typenames.trim().split(/^|\s+/...
function onRemove (line 1) | function onRemove(typename){return function(){var on=this.__on;if(!on)re...
function onAdd (line 1) | function onAdd(typename,value,options){return function(){var on=this.__o...
function selection_on (line 1) | function selection_on(typename,value,options){var typenames=parseTypenam...
function dispatchEvent (line 1) | function dispatchEvent(node,type,params){var window=defaultView(node),ev...
function dispatchConstant (line 1) | function dispatchConstant(type,params){return function(){return dispatch...
function dispatchFunction (line 1) | function dispatchFunction(type,params){return function(){return dispatch...
function selection_dispatch (line 1) | function selection_dispatch(type,params){return this.each((typeof params...
function Selection$1 (line 1) | function Selection$1(groups,parents){this._groups=groups;this._parents=p...
function selection (line 1) | function selection(){return new Selection$1([[document.documentElement]]...
function selection_selection (line 1) | function selection_selection(){return this}
function select (line 1) | function select(selector){return typeof selector==="string"?new Selectio...
function selectAll (line 1) | function selectAll(selector){return typeof selector==="string"?new Selec...
function define (line 1) | function define(constructor,factory,prototype){constructor.prototype=fac...
function extend$1 (line 1) | function extend$1(parent,definition){var prototype=Object.create(parent....
function Color (line 1) | function Color(){}
method copy (line 1) | copy(channels){return Object.assign(new this.constructor,this,channels)}
method displayable (line 1) | displayable(){return this.rgb().displayable()}
function color_formatHex (line 1) | function color_formatHex(){return this.rgb().formatHex()}
function color_formatHex8 (line 1) | function color_formatHex8(){return this.rgb().formatHex8()}
function color_formatHsl (line 1) | function color_formatHsl(){return hslConvert(this).formatHsl()}
function color_formatRgb (line 1) | function color_formatRgb(){return this.rgb().formatRgb()}
function color (line 1) | function color(format){var m,l;format=(format+"").trim().toLowerCase();r...
function rgbn (line 1) | function rgbn(n){return new Rgb(n>>16&255,n>>8&255,n&255,1)}
function rgba (line 1) | function rgba(r,g,b,a){if(a<=0)r=g=b=NaN;return new Rgb(r,g,b,a)}
function rgbConvert (line 1) | function rgbConvert(o){if(!(o instanceof Color))o=color(o);if(!o)return ...
function rgb (line 1) | function rgb(r,g,b,opacity){return arguments.length===1?rgbConvert(r):ne...
function Rgb (line 1) | function Rgb(r,g,b,opacity){this.r=+r;this.g=+g;this.b=+b;this.opacity=+...
method brighter (line 1) | brighter(k){k=k==null?brighter:Math.pow(brighter,k);return new Rgb(this....
method darker (line 1) | darker(k){k=k==null?darker:Math.pow(darker,k);return new Rgb(this.r*k,th...
method rgb (line 1) | rgb(){return this}
method clamp (line 1) | clamp(){return new Rgb(clampi(this.r),clampi(this.g),clampi(this.b),clam...
method displayable (line 1) | displayable(){return-.5<=this.r&&this.r<255.5&&(-.5<=this.g&&this.g<255....
function rgb_formatHex (line 1) | function rgb_formatHex(){return`#${hex(this.r)}${hex(this.g)}${hex(this....
function rgb_formatHex8 (line 1) | function rgb_formatHex8(){return`#${hex(this.r)}${hex(this.g)}${hex(this...
function rgb_formatRgb (line 1) | function rgb_formatRgb(){const a=clampa(this.opacity);return`${a===1?"rg...
function clampa (line 1) | function clampa(opacity){return isNaN(opacity)?1:Math.max(0,Math.min(1,o...
function clampi (line 1) | function clampi(value){return Math.max(0,Math.min(255,Math.round(value)|...
function hex (line 1) | function hex(value){value=clampi(value);return(value<16?"0":"")+value.to...
function hsla (line 1) | function hsla(h,s,l,a){if(a<=0)h=s=l=NaN;else if(l<=0||l>=1)h=s=NaN;else...
function hslConvert (line 1) | function hslConvert(o){if(o instanceof Hsl)return new Hsl(o.h,o.s,o.l,o....
function hsl (line 1) | function hsl(h,s,l,opacity){return arguments.length===1?hslConvert(h):ne...
function Hsl (line 1) | function Hsl(h,s,l,opacity){this.h=+h;this.s=+s;this.l=+l;this.opacity=+...
method brighter (line 1) | brighter(k){k=k==null?brighter:Math.pow(brighter,k);return new Hsl(this....
method darker (line 1) | darker(k){k=k==null?darker:Math.pow(darker,k);return new Hsl(this.h,this...
method rgb (line 1) | rgb(){var h=this.h%360+(this.h<0)*360,s=isNaN(h)||isNaN(this.s)?0:this.s...
method clamp (line 1) | clamp(){return new Hsl(clamph(this.h),clampt(this.s),clampt(this.l),clam...
method displayable (line 1) | displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&(0<=this.l&&t...
method formatHsl (line 1) | formatHsl(){const a=clampa(this.opacity);return`${a===1?"hsl(":"hsla("}$...
function clamph (line 1) | function clamph(value){value=(value||0)%360;return value<0?value+360:value}
function clampt (line 1) | function clampt(value){return Math.max(0,Math.min(1,value||0))}
function hsl2rgb (line 1) | function hsl2rgb(h,m1,m2){return(h<60?m1+(m2-m1)*h/60:h<180?m2:h<240?m1+...
function labConvert (line 1) | function labConvert(o){if(o instanceof Lab)return new Lab(o.l,o.a,o.b,o....
function lab (line 1) | function lab(l,a,b,opacity){return arguments.length===1?labConvert(l):ne...
function Lab (line 1) | function Lab(l,a,b,opacity){this.l=+l;this.a=+a;this.b=+b;this.opacity=+...
method brighter (line 1) | brighter(k){return new Lab(this.l+K*(k==null?1:k),this.a,this.b,this.opa...
method darker (line 1) | darker(k){return new Lab(this.l-K*(k==null?1:k),this.a,this.b,this.opaci...
method rgb (line 1) | rgb(){var y=(this.l+16)/116,x=isNaN(this.a)?y:y+this.a/500,z=isNaN(this....
function xyz2lab (line 1) | function xyz2lab(t){return t>t3?Math.pow(t,1/3):t/t2+t0$1}
function lab2xyz (line 1) | function lab2xyz(t){return t>t1$1?t*t*t:t2*(t-t0$1)}
function lrgb2rgb (line 1) | function lrgb2rgb(x){return 255*(x<=.0031308?12.92*x:1.055*Math.pow(x,1/...
function rgb2lrgb (line 1) | function rgb2lrgb(x){return(x/=255)<=.04045?x/12.92:Math.pow((x+.055)/1....
function hclConvert (line 1) | function hclConvert(o){if(o instanceof Hcl)return new Hcl(o.h,o.c,o.l,o....
function hcl$1 (line 1) | function hcl$1(h,c,l,opacity){return arguments.length===1?hclConvert(h):...
function Hcl (line 1) | function Hcl(h,c,l,opacity){this.h=+h;this.c=+c;this.l=+l;this.opacity=+...
function hcl2lab (line 1) | function hcl2lab(o){if(isNaN(o.h))return new Lab(o.l,0,0,o.opacity);var ...
method brighter (line 1) | brighter(k){return new Hcl(this.h,this.c,this.l+K*(k==null?1:k),this.opa...
method darker (line 1) | darker(k){return new Hcl(this.h,this.c,this.l-K*(k==null?1:k),this.opaci...
method rgb (line 1) | rgb(){return hcl2lab(this).rgb()}
function linear$1 (line 1) | function linear$1(a,d){return function(t){return a+t*d}}
function exponential (line 1) | function exponential(a,b,y){return a=Math.pow(a,y),b=Math.pow(b,y)-a,y=1...
function hue (line 1) | function hue(a,b){var d=b-a;return d?linear$1(a,d>180||d<-180?d-360*Math...
function gamma (line 1) | function gamma(y){return(y=+y)===1?nogamma:function(a,b){return b-a?expo...
function nogamma (line 1) | function nogamma(a,b){var d=b-a;return d?linear$1(a,d):constant$2(isNaN(...
function rgb$1 (line 1) | function rgb$1(start,end){var r=color((start=rgb(start)).r,(end=rgb(end)...
function numberArray (line 1) | function numberArray(a,b){if(!b)b=[];var n=a?Math.min(b.length,a.length)...
function isNumberArray (line 1) | function isNumberArray(x){return ArrayBuffer.isView(x)&&!(x instanceof D...
function genericArray (line 1) | function genericArray(a,b){var nb=b?b.length:0,na=a?Math.min(nb,a.length...
function date$1 (line 1) | function date$1(a,b){var d=new Date;return a=+a,b=+b,function(t){return ...
function interpolateNumber (line 1) | function interpolateNumber(a,b){return a=+a,b=+b,function(t){return a*(1...
function object (line 1) | function object(a,b){var i={},c={},k;if(a===null||typeof a!=="object")a=...
function zero (line 1) | function zero(b){return function(){return b}}
function one (line 1) | function one(b){return function(t){return b(t)+""}}
function interpolateString (line 1) | function interpolateString(a,b){var bi=reA.lastIndex=reB.lastIndex=0,am,...
function interpolate$1 (line 1) | function interpolate$1(a,b){var t=typeof b,c;return b==null||t==="boolea...
function interpolateRound (line 1) | function interpolateRound(a,b){return a=+a,b=+b,function(t){return Math....
function decompose (line 1) | function decompose(a,b,c,d,e,f){var scaleX,scaleY,skewX;if(scaleX=Math.s...
function parseCss (line 1) | function parseCss(value){const m=new(typeof DOMMatrix==="function"?DOMMa...
function parseSvg (line 1) | function parseSvg(value){if(value==null)return identity$4;if(!svgNode)sv...
function interpolateTransform (line 1) | function interpolateTransform(parse,pxComma,pxParen,degParen){function p...
function hcl (line 1) | function hcl(hue){return function(start,end){var h=hue((start=hcl$1(star...
function now$2 (line 1) | function now$2(){return clockNow||(setFrame(clearNow),clockNow=clock.now...
function clearNow (line 1) | function clearNow(){clockNow=0}
function Timer (line 1) | function Timer(){this._call=this._time=this._next=null}
function timer (line 1) | function timer(callback,delay,time){var t=new Timer;t.restart(callback,d...
function timerFlush (line 1) | function timerFlush(){now$2();++frame;var t=taskHead,e;while(t){if((e=cl...
function wake (line 1) | function wake(){clockNow=(clockLast=clock.now())+clockSkew;frame=timeout...
function poke (line 1) | function poke(){var now=clock.now(),delay=now-clockLast;if(delay>pokeDel...
function nap (line 1) | function nap(){var t0,t1=taskHead,t2,time=Infinity;while(t1){if(t1._call...
function sleep (line 1) | function sleep(time){if(frame)return;if(timeout$1)timeout$1=clearTimeout...
function timeout (line 1) | function timeout(callback,delay,time){var t=new Timer;delay=delay==null?...
function schedule (line 1) | function schedule(node,name,id,index,group,timing){var schedules=node.__...
function init$1 (line 1) | function init$1(node,id){var schedule=get$1(node,id);if(schedule.state>C...
function set$1 (line 1) | function set$1(node,id){var schedule=get$1(node,id);if(schedule.state>ST...
function get$1 (line 1) | function get$1(node,id){var schedule=node.__transition;if(!schedule||!(s...
function create (line 1) | function create(node,id,self){var schedules=node.__transition,tween;sche...
function interrupt (line 1) | function interrupt(node,name){var schedules=node.__transition,schedule,a...
function selection_interrupt (line 1) | function selection_interrupt(name){return this.each((function(){interrup...
function tweenRemove (line 1) | function tweenRemove(id,name){var tween0,tween1;return function(){var sc...
function tweenFunction (line 1) | function tweenFunction(id,name,value){var tween0,tween1;if(typeof value!...
function transition_tween (line 1) | function transition_tween(name,value){var id=this._id;name+="";if(argume...
function tweenValue (line 1) | function tweenValue(transition,name,value){var id=transition._id;transit...
function interpolate (line 1) | function interpolate(a,b){var c;return(typeof b==="number"?interpolateNu...
function attrRemove (line 1) | function attrRemove(name){return function(){this.removeAttribute(name)}}
function attrRemoveNS (line 1) | function attrRemoveNS(fullname){return function(){this.removeAttributeNS...
function attrConstant (line 1) | function attrConstant(name,interpolate,value1){var string00,string1=valu...
function attrConstantNS (line 1) | function attrConstantNS(fullname,interpolate,value1){var string00,string...
function attrFunction (line 1) | function attrFunction(name,interpolate,value){var string00,string10,inte...
function attrFunctionNS (line 1) | function attrFunctionNS(fullname,interpolate,value){var string00,string1...
function transition_attr (line 1) | function transition_attr(name,value){var fullname=namespace(name),i=full...
function attrInterpolate (line 1) | function attrInterpolate(name,i){return function(t){this.setAttribute(na...
function attrInterpolateNS (line 1) | function attrInterpolateNS(fullname,i){return function(t){this.setAttrib...
function attrTweenNS (line 1) | function attrTweenNS(fullname,value){var t0,i0;function tween(){var i=va...
function attrTween (line 1) | function attrTween(name,value){var t0,i0;function tween(){var i=value.ap...
function transition_attrTween (line 1) | function transition_attrTween(name,value){var key="attr."+name;if(argume...
function delayFunction (line 1) | function delayFunction(id,value){return function(){init$1(this,id).delay...
function delayConstant (line 1) | function delayConstant(id,value){return value=+value,function(){init$1(t...
function transition_delay (line 1) | function transition_delay(value){var id=this._id;return arguments.length...
function durationFunction (line 1) | function durationFunction(id,value){return function(){set$1(this,id).dur...
function durationConstant (line 1) | function durationConstant(id,value){return value=+value,function(){set$1...
function transition_duration (line 1) | function transition_duration(value){var id=this._id;return arguments.len...
function easeConstant (line 1) | function easeConstant(id,value){if(typeof value!=="function")throw new E...
function transition_ease (line 1) | function transition_ease(value){var id=this._id;return arguments.length?...
function easeVarying (line 1) | function easeVarying(id,value){return function(){var v=value.apply(this,...
function transition_easeVarying (line 1) | function transition_easeVarying(value){if(typeof value!=="function")thro...
function transition_filter (line 1) | function transition_filter(match){if(typeof match!=="function")match=mat...
function transition_merge (line 1) | function transition_merge(transition){if(transition._id!==this._id)throw...
function start$1 (line 1) | function start$1(name){return(name+"").trim().split(/^|\s+/).every((func...
function onFunction (line 1) | function onFunction(id,name,listener){var on0,on1,sit=start$1(name)?init...
function transition_on (line 1) | function transition_on(name,listener){var id=this._id;return arguments.l...
function removeFunction (line 1) | function removeFunction(id){return function(){var parent=this.parentNode...
function transition_remove (line 1) | function transition_remove(){return this.on("end.remove",removeFunction(...
function transition_select (line 1) | function transition_select(select){var name=this._name,id=this._id;if(ty...
function transition_selectAll (line 1) | function transition_selectAll(select){var name=this._name,id=this._id;if...
function transition_selection (line 1) | function transition_selection(){return new Selection(this._groups,this._...
function styleNull (line 1) | function styleNull(name,interpolate){var string00,string10,interpolate0;...
function styleRemove (line 1) | function styleRemove(name){return function(){this.style.removeProperty(n...
function styleConstant (line 1) | function styleConstant(name,interpolate,value1){var string00,string1=val...
function styleFunction (line 1) | function styleFunction(name,interpolate,value){var string00,string10,int...
function styleMaybeRemove (line 1) | function styleMaybeRemove(id,name){var on0,on1,listener0,key="style."+na...
function transition_style (line 1) | function transition_style(name,value,priority){var i=(name+="")==="trans...
function styleInterpolate (line 1) | function styleInterpolate(name,i,priority){return function(t){this.style...
function styleTween (line 1) | function styleTween(name,value,priority){var t,i0;function tween(){var i...
function transition_styleTween (line 1) | function transition_styleTween(name,value,priority){var key="style."+(na...
function textConstant (line 1) | function textConstant(value){return function(){this.textContent=value}}
function textFunction (line 1) | function textFunction(value){return function(){var value1=value(this);th...
function transition_text (line 1) | function transition_text(value){return this.tween("text",typeof value===...
function textInterpolate (line 1) | function textInterpolate(i){return function(t){this.textContent=i.call(t...
function textTween (line 1) | function textTween(value){var t0,i0;function tween(){var i=value.apply(t...
function transition_textTween (line 1) | function transition_textTween(value){var key="text";if(arguments.length<...
function transition_transition (line 1) | function transition_transition(){var name=this._name,id0=this._id,id1=ne...
function transition_end (line 1) | function transition_end(){var on0,on1,that=this,id=that._id,size=that.si...
function Transition (line 1) | function Transition(groups,parents,name,id){this._groups=groups;this._pa...
function newId (line 1) | function newId(){return++id$i}
function cubicInOut (line 1) | function cubicInOut(t){return((t*=2)<=1?t*t*t:(t-=2)*t*t+2)/2}
function inherit (line 1) | function inherit(node,id){var timing;while(!(timing=node.__transition)||...
function selection_transition (line 1) | function selection_transition(name){var id,timing;if(name instanceof Tra...
function append$1 (line 1) | function append$1(strings){this._+=strings[0];for(let i=1,n=strings.leng...
function appendRound (line 1) | function appendRound(digits){let d=Math.floor(digits);if(!(d>=0))throw n...
class Path (line 1) | class Path{constructor(digits){this._x0=this._y0=this._x1=this._y1=null;...
method constructor (line 1) | constructor(digits){this._x0=this._y0=this._x1=this._y1=null;this._=""...
method moveTo (line 1) | moveTo(x,y){this._append`M${this._x0=this._x1=+x},${this._y0=this._y1=...
method closePath (line 1) | closePath(){if(this._x1!==null){this._x1=this._x0,this._y1=this._y0;th...
method lineTo (line 1) | lineTo(x,y){this._append`L${this._x1=+x},${this._y1=+y}`}
method quadraticCurveTo (line 1) | quadraticCurveTo(x1,y1,x,y){this._append`Q${+x1},${+y1},${this._x1=+x}...
method bezierCurveTo (line 1) | bezierCurveTo(x1,y1,x2,y2,x,y){this._append`C${+x1},${+y1},${+x2},${+y...
method arcTo (line 1) | arcTo(x1,y1,x2,y2,r){x1=+x1,y1=+y1,x2=+x2,y2=+y2,r=+r;if(r<0)throw new...
method arc (line 1) | arc(x,y,r,a0,a1,ccw){x=+x,y=+y,r=+r,ccw=!!ccw;if(r<0)throw new Error(`...
method rect (line 1) | rect(x,y,w,h){this._append`M${this._x0=this._x1=+x},${this._y0=this._y...
method toString (line 1) | toString(){return this._}
function responseText (line 1) | function responseText(response){if(!response.ok)throw new Error(response...
function text (line 1) | function text(input,init){return fetch(input,init).then(responseText)}
function parser$e (line 1) | function parser$e(type){return(input,init)=>text(input,init).then((text=...
function formatDecimal (line 1) | function formatDecimal(x){return Math.abs(x=Math.round(x))>=1e21?x.toLoc...
function formatDecimalParts (line 1) | function formatDecimalParts(x,p){if((i=(x=p?x.toExponential(p-1):x.toExp...
function exponent (line 1) | function exponent(x){return x=formatDecimalParts(Math.abs(x)),x?x[1]:NaN}
function formatGroup (line 1) | function formatGroup(grouping,thousands){return function(value,width){va...
function formatNumerals (line 1) | function formatNumerals(numerals){return function(value){return value.re...
function formatSpecifier (line 1) | function formatSpecifier(specifier){if(!(match=re.exec(specifier)))throw...
function FormatSpecifier (line 1) | function FormatSpecifier(specifier){this.fill=specifier.fill===undefined...
function formatTrim (line 1) | function formatTrim(s){out:for(var n=s.length,i=1,i0=-1,i1;i<n;++i){swit...
function formatPrefixAuto (line 1) | function formatPrefixAuto(x,p){var d=formatDecimalParts(x,p);if(!d)retur...
function formatRounded (line 1) | function formatRounded(x,p){var d=formatDecimalParts(x,p);if(!d)return x...
function identity$3 (line 1) | function identity$3(x){return x}
function formatLocale$1 (line 1) | function formatLocale$1(locale){var group=locale.grouping===undefined||l...
function defaultLocale$1 (line 1) | function defaultLocale$1(definition){locale$1=formatLocale$1(definition)...
function precisionFixed (line 1) | function precisionFixed(step){return Math.max(0,-exponent(Math.abs(step)))}
function precisionPrefix (line 1) | function precisionPrefix(step,value){return Math.max(0,Math.max(-8,Math....
function precisionRound (line 1) | function precisionRound(step,max){step=Math.abs(step),max=Math.abs(max)-...
function initRange (line 1) | function initRange(domain,range){switch(arguments.length){case 0:break;c...
function ordinal (line 1) | function ordinal(){var index=new InternMap,domain=[],range=[],unknown=im...
function constants (line 1) | function constants(x){return function(){return x}}
function number$1 (line 1) | function number$1(x){return+x}
function identity$2 (line 1) | function identity$2(x){return x}
function normalize (line 1) | function normalize(a,b){return(b-=a=+a)?function(x){return(x-a)/b}:const...
function clamper (line 1) | function clamper(a,b){var t;if(a>b)t=a,a=b,b=t;return function(x){return...
function bimap (line 1) | function bimap(domain,range,interpolate){var d0=domain[0],d1=domain[1],r...
function polymap (line 1) | function polymap(domain,range,interpolate){var j=Math.min(domain.length,...
function copy$1 (line 1) | function copy$1(source,target){return target.domain(source.domain()).ran...
function transformer (line 1) | function transformer(){var domain=unit,range=unit,interpolate=interpolat...
function continuous (line 1) | function continuous(){return transformer()(identity$2,identity$2)}
function tickFormat (line 1) | function tickFormat(start,stop,count,specifier){var step=tickStep(start,...
function linearish (line 1) | function linearish(scale){var domain=scale.domain;scale.ticks=function(c...
function linear (line 1) | function linear(){var scale=continuous();scale.copy=function(){return co...
function nice (line 1) | function nice(domain,interval){domain=domain.slice();var i0=0,i1=domain....
function timeInterval (line 1) | function timeInterval(floori,offseti,count,field){function interval(date...
function timeWeekday (line 1) | function timeWeekday(i){return timeInterval((date=>{date.setDate(date.ge...
function utcWeekday (line 1) | function utcWeekday(i){return timeInterval((date=>{date.setUTCDate(date....
function ticker (line 1) | function ticker(year,month,week,day,hour,minute){const tickIntervals=[[s...
function localDate (line 1) | function localDate(d){if(0<=d.y&&d.y<100){var date=new Date(-1,d.m,d.d,d...
function utcDate (line 1) | function utcDate(d){if(0<=d.y&&d.y<100){var date=new Date(Date.UTC(-1,d....
function newDate (line 1) | function newDate(y,m,d){return{y:y,m:m,d:d,H:0,M:0,S:0,L:0}}
function formatLocale (line 1) | function formatLocale(locale){var locale_dateTime=locale.dateTime,locale...
function pad (line 1) | function pad(value,fill,width){var sign=value<0?"-":"",string=(sign?-val...
function requote (line 1) | function requote(s){return s.replace(requoteRe,"\\$&")}
function formatRe (line 1) | function formatRe(names){return new RegExp("^(?:"+names.map(requote).joi...
function formatLookup (line 1) | function formatLookup(names){return new Map(names.map(((name,i)=>[name.t...
function parseWeekdayNumberSunday (line 1) | function parseWeekdayNumberSunday(d,string,i){var n=numberRe.exec(string...
function parseWeekdayNumberMonday (line 1) | function parseWeekdayNumberMonday(d,string,i){var n=numberRe.exec(string...
function parseWeekNumberSunday (line 1) | function parseWeekNumberSunday(d,string,i){var n=numberRe.exec(string.sl...
function parseWeekNumberISO (line 1) | function parseWeekNumberISO(d,string,i){var n=numberRe.exec
Copy disabled (too large)
Download .json
Condensed preview — 1354 files, each showing path, character count, and a content snippet. Download the .json file for the full structured content (20,001K chars).
[
{
"path": ".cursor/commands/np-plugin-commands.md",
"chars": 254,
"preview": "# np-plugin-commands\n\nWrite your command content here.\n\nThis command will be available in chat with /np-plugin-commands\n"
},
{
"path": ".cursor/rules/html-react-rules.mdc",
"chars": 3567,
"preview": "---\nalwaysApply: true\n---\n- For NotePlan code, never use require statements. Use import statements at the top of the fil"
},
{
"path": ".cursor/rules/np-programming-general.mdc",
"chars": 2578,
"preview": "---\ndescription: \nalwaysApply: true\n---\n\n## Critical Rules\nCRITICAL: Never use dynamic imports. Rollup will not process "
},
{
"path": ".cursorignore",
"chars": 112,
"preview": "# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv)\nnode_modules/\n.history/\n.git/\n"
},
{
"path": ".eslintrc",
"chars": 3177,
"preview": "{\n \"extends\": [\n \"eslint:recommended\",\n \"plugin:import/recommended\",\n // \"plugin:prettier/recommended\",\n \"p"
},
{
"path": ".flowconfig",
"chars": 1595,
"preview": "[ignore]\n# removed to fix issue with flow reporting errors with non-typed modules\n# details here - https://github.com/fa"
},
{
"path": ".github/ISSUE_TEMPLATE/bug_report.md",
"chars": 1530,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve a Plugin\ntitle: '<plugin name>: <title>'\nlabels: 'bug'\nas"
},
{
"path": ".github/ISSUE_TEMPLATE/feature_request.md",
"chars": 688,
"preview": "---\nname: Feature request\nabout: Suggest an idea for this project\ntitle: '<plugin name>: <title>'\nlabels: 'feature reque"
},
{
"path": ".github/workflows/node.js.yml",
"chars": 5457,
"preview": "# Disable/enable this script at: https://github.com/NotePlan/plugins/actions/workflows/node.js.yml\n\n# Run tests on push "
},
{
"path": ".gitignore",
"chars": 348,
"preview": ".DS_Store\nnode_modules*\ndist/\n.cache/\n*/ts-template/*.*\n.pluginpath\n*test*.yaml\n*.code-workspace\n*.dict\n.vscode/launch.j"
},
{
"path": ".prettierignore",
"chars": 0,
"preview": ""
},
{
"path": ".prettierrc",
"chars": 221,
"preview": "{\n \"semi\": false,\n \"quoteProps\": \"as-needed\",\n \"templateCurlySpacing\": false,\n \"experimentalOperatorPosition"
},
{
"path": ".watchmanconfig",
"chars": 4,
"preview": "{\n}\n"
},
{
"path": "CHANGELOG.md",
"chars": 7965,
"preview": "# Package.json Changelog\n\n## About Plugins/package.json / package-lock.json\n\n## [Unreleased]\n\n### Helpers\n\n- **helpers/N"
},
{
"path": "Flow_Guide.md",
"chars": 6652,
"preview": "# Flow Guide\n\nThis section is for those looking to understand how to use Flow types for more reliable Javascript.\n\nYou c"
},
{
"path": "GithubFlow.md",
"chars": 9100,
"preview": "# Contributing to Noteplan/plugins\n\nThe process of contributing to Noteplan/plugins is the same as contributing to any G"
},
{
"path": "KimMachineGun.Raindrop/CHANGELOG.md",
"chars": 417,
"preview": "# KimMachineGun.Raindrop Plugin Changelog\n\n## About KimMachineGun.Raindrop Plugin\n\nSee Plugin [README](https://github.co"
},
{
"path": "KimMachineGun.Raindrop/README.md",
"chars": 758,
"preview": "# KimMachineGun.Raindrop Plugin\n\nThis plugin provides commands for working with Raindrop.io.\n\n## Settings\n\nThese command"
},
{
"path": "KimMachineGun.Raindrop/plugin.json",
"chars": 2549,
"preview": "{\n \"COMMENT\": \"Details on these fields: https://help.noteplan.co/article/67-create-command-bar-plugins\",\n \"macOS.minVe"
},
{
"path": "KimMachineGun.Raindrop/src/NPPluginMain.js",
"chars": 7193,
"preview": "// @flow\n// Plugin code goes in files like this. Can be one per command, or several in a file.\n// `export async function"
},
{
"path": "KimMachineGun.Raindrop/src/Raindrop.js",
"chars": 4257,
"preview": "// @flow\n\ndeclare type RaindropType = 'link' | 'article' | 'image' | 'video' | 'document' | 'audio';\n\ndeclare type Raind"
},
{
"path": "KimMachineGun.Raindrop/src/index.js",
"chars": 3090,
"preview": "// @flow\n// Flow typing is important for reducing errors and improving the quality of the code.\n// About Flow: https://f"
},
{
"path": "LICENSE",
"chars": 1086,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2021-2022 Eduard Metzger\n\nPermission is hereby granted, free of charge, to any pers"
},
{
"path": "README.md",
"chars": 14111,
"preview": "# NotePlan Plugins\n\n[](https:/"
},
{
"path": "TasksModule_docs.md",
"chars": 2696,
"preview": "import Link from 'next/link'\nimport Callout from '@/components/Callout'\n\n# Tasks Module\n\n## Overview\n\nThe Tasks Module p"
},
{
"path": "__mocks__/Backlink.mock.js",
"chars": 3963,
"preview": "/* eslint-disable */\n\n/* Backlink mock class\n *\n * Usage: const myBacklink = new Backlink({ param changes here })\n *\n */"
},
{
"path": "__mocks__/Calendar.mock.js",
"chars": 2246,
"preview": "/* eslint-disable */\n/*\n * Calendar mocks\n *\n * Note: nested object example data are there for reference only -- will ne"
},
{
"path": "__mocks__/CalendarItem.mock.js",
"chars": 1377,
"preview": "/* eslint-disable */\n/*\n * CalendarItem mock class\n *\n * Usage: const myCalendarItem = new CalendarItem({ param changes "
},
{
"path": "__mocks__/Clipboard.mock.js",
"chars": 774,
"preview": "/* eslint-disable */\n/*\n * Clipboard mocks\n *\n * Note: nested object example data are there for reference only -- will n"
},
{
"path": "__mocks__/CommandBar.mock.js",
"chars": 1233,
"preview": "/* eslint-disable */\n/*\n * CommandBar mocks\n *\n * Note: nested object example data are there for reference only -- will "
},
{
"path": "__mocks__/DataStore.mock.js",
"chars": 5160,
"preview": "/* eslint-disable */\n/*\n * DataStore mocks\n *\n * Note: nested object example data are there for reference only -- will n"
},
{
"path": "__mocks__/Editor.mock.js",
"chars": 3389,
"preview": "/* eslint-disable */\n/**\n * Editor mocks with Proxy\n *\n * Editor and Note share many of the same properties+methods (Cor"
},
{
"path": "__mocks__/Fetch.mock.js",
"chars": 3693,
"preview": "// @flow\n\n/**\n * Mock the fetch() function to return a specific response for a given URL and options.body\n * You pass in"
},
{
"path": "__mocks__/NP_THEME.mock.js",
"chars": 224,
"preview": "export const mockNP_THEME = {\n base: {\n backgroundColor: '#ffffff', // Example color\n textColor: '#000000', // Ex"
},
{
"path": "__mocks__/Note.mock.js",
"chars": 13302,
"preview": "// @flow\n\nimport { logDebug } from '../helpers/dev'\nimport { hasFrontMatter, getAttributes } from '@helpers/NPFrontMatte"
},
{
"path": "__mocks__/NotePlan.mock.js",
"chars": 1687,
"preview": "/*\n * NotePlan mocks\n *\n * NOTE: Unlike the other mocks, this is a class and not an object. So you should use `new NoteP"
},
{
"path": "__mocks__/Paragraph.mock.js",
"chars": 2855,
"preview": "/* eslint-disable */\n/*\n * Paragraph mock class\n *\n * Usage: const myParagraph = new Paragraph({ param changes here })\n "
},
{
"path": "__mocks__/PluginCommandObject.mock.js",
"chars": 595,
"preview": "/* eslint-disable */\n/*\n * PluginCommandObjectMock mock class\n *\n * Usage: const myPluginCommandObject = new PluginComma"
},
{
"path": "__mocks__/PluginObject.mock.js",
"chars": 1120,
"preview": "/* eslint-disable */\n/*\n * PluginObject mock class\n *\n * Usage: const myPluginObject = new PluginObject({ param changes "
},
{
"path": "__mocks__/Range.mock.js",
"chars": 392,
"preview": "/* eslint-disable */\n/*\n * Range mock class\n *\n * Usage: const myRange = new Range({ param changes here })\n *\n */\n\nexpor"
},
{
"path": "__mocks__/_README-Mocks.md",
"chars": 9547,
"preview": "# Mocking NotePlan objects in your Jest testing files:\n\nThe best/fastest/easiest/most-reliable thing to do when writing "
},
{
"path": "__mocks__/__tests__/fetch.mock.test.js",
"chars": 4033,
"preview": "/* global jest, describe, test, expect, beforeAll */\nimport { CustomConsole } from '@jest/console' // see note below\nimp"
},
{
"path": "__mocks__/index.js",
"chars": 701,
"preview": "export { Calendar } from './Calendar.mock'\nexport { default as Clipboard } from './Clipboard.mock'\nexport { default as C"
},
{
"path": "__mocks__/jestHelpers.js",
"chars": 2113,
"preview": "// @flow\nimport path from 'path'\nimport { existsSync, promises as fs } from 'fs'\n\n/**\n * Check if a spy was called (at a"
},
{
"path": "__mocks__/support/pluginSample.json",
"chars": 3795,
"preview": "{\n \"noteplan.minAppVersion\": \"3.0.21\",\n \"macOS.minVersion\": \"10.15.7\",\n \"iOS.minVersion\": \"14\",\n \"plugin.id\""
},
{
"path": "aaronpoweruser.ReadwiseUnofficial/CHANGELOG.md",
"chars": 3314,
"preview": "# aaronpoweruser.ReadwiseUnofficial Changelog\n\n## About aaronpoweruser.ReadwiseUnofficial Plugin\n\nSee Plugin [README](ht"
},
{
"path": "aaronpoweruser.ReadwiseUnofficial/README.md",
"chars": 2204,
"preview": "# Readwise Unofficial Noteplan Plugin\n\n## About This Plugin\n\nA sync engine for readwise\n\n### Features\n\n- Daily review's "
},
{
"path": "aaronpoweruser.ReadwiseUnofficial/plugin.json",
"chars": 5400,
"preview": "{\n \"COMMENT\": \"Details on these fields: https://help.noteplan.co/article/67-create-command-bar-plugins\",\n \"macOS.minVe"
},
{
"path": "aaronpoweruser.ReadwiseUnofficial/src/NPReadwise.js",
"chars": 5129,
"preview": "// @flow\nimport { showMessage } from '../../helpers/userInput'\nimport pluginJson from '../plugin.json'\nimport { checkAcc"
},
{
"path": "aaronpoweruser.ReadwiseUnofficial/src/NPReadwiseHelpers.js",
"chars": 5190,
"preview": "// @flow\nimport { showMessage } from '../../helpers/userInput'\nimport pluginJson from '../plugin.json'\nimport { logDebug"
},
{
"path": "aaronpoweruser.ReadwiseUnofficial/src/NPReadwiseNotes.js",
"chars": 4395,
"preview": "// @flow\nimport pluginJson from '../plugin.json'\nimport { updateFrontMatterVars } from '../../helpers/NPFrontMatter'\nimp"
},
{
"path": "aaronpoweruser.ReadwiseUnofficial/src/NPReadwiseSyncLog.js",
"chars": 1547,
"preview": "// @flow\nimport { getLocalDate } from './NPReadwiseHelpers'\nimport { getOrMakeRegularNoteInFolder } from '@helpers/NPnot"
},
{
"path": "aaronpoweruser.ReadwiseUnofficial/src/NPTriggers-Hooks.js",
"chars": 3752,
"preview": "/* eslint-disable require-await */\n// @flow\n\nimport pluginJson from '../plugin.json' // gives you access to the contents"
},
{
"path": "aaronpoweruser.ReadwiseUnofficial/src/index.js",
"chars": 935,
"preview": "// @flow\n\nexport { readwiseSync, readwiseRebuild, readwiseDailyReview } from './NPReadwise'\n\n// FETCH mocking for offlin"
},
{
"path": "babel.config.js",
"chars": 402,
"preview": "// babel.config.js\nmodule.exports = (api) => {\n const isTest = api.env('test')\n return {\n presets: [\n '@babel/"
},
{
"path": "buildDashboard.sh",
"chars": 311,
"preview": "#!/bin/sh\n# Build Dashboard, and then run it. Needs to run from local GH root directory.\nnode ./jgclark.Dashboard/src/re"
},
{
"path": "buildShared.sh",
"chars": 175,
"preview": "#!/bin/sh\n# Build Dashboard, and then run it. Needs to run from local GH root directory.\nnode ./np.Shared/src/react/supp"
},
{
"path": "codedungeon.Toolbox/CHANGELOG.md",
"chars": 1842,
"preview": "# codedungeon.Toolbox Changelog\n\n## About codedungeon.Toolbox Plugin\n\nSee Plugin [README](https://github.com/NotePlan/pl"
},
{
"path": "codedungeon.Toolbox/LICENSE",
"chars": 1080,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2021 Mike Erickson\n\nPermission is hereby granted, free of charge, to any person obt"
},
{
"path": "codedungeon.Toolbox/README.md",
"chars": 2343,
"preview": "# 🧩 Codedungeon Toolbox for Noteplan\n\n## Overview\n**Codedungeon Toolbox for NotePlan** is a plugin for NotePlan that pro"
},
{
"path": "codedungeon.Toolbox/__tests__/convertSelectionToHtml.test.js",
"chars": 2351,
"preview": "/* eslint-disable */\n\n/*-------------------------------------------------------------------------------------------\n * C"
},
{
"path": "codedungeon.Toolbox/__tests__/convertToHtml.test.js",
"chars": 2667,
"preview": "/* eslint-disable */\n\n/*-------------------------------------------------------------------------------------------\n * C"
},
{
"path": "codedungeon.Toolbox/__tests__/reorderList.test.js",
"chars": 1179,
"preview": "/* eslint-disable */\n\n/*-------------------------------------------------------------------------------------------\n * C"
},
{
"path": "codedungeon.Toolbox/plugin.json",
"chars": 1828,
"preview": "{\n \"macOS.minVersion\": \"10.13.0\",\n \"noteplan.minAppVersion\": \"3.0.21\",\n \"plugin.id\": \"codedungeon.Toolbox\",\n \"plugin"
},
{
"path": "codedungeon.Toolbox/src/convertSelectionToHtml.js",
"chars": 884,
"preview": "// @flow\n/*-------------------------------------------------------------------------------------------\n * Copyright (c) "
},
{
"path": "codedungeon.Toolbox/src/convertToHtml.js",
"chars": 859,
"preview": "// @flow\n/*-------------------------------------------------------------------------------------------\n * Copyright (c) "
},
{
"path": "codedungeon.Toolbox/src/convertToRtf.js",
"chars": 859,
"preview": "// @flow\n/*-------------------------------------------------------------------------------------------\n * Copyright (c) "
},
{
"path": "codedungeon.Toolbox/src/index.js",
"chars": 519,
"preview": "/*-------------------------------------------------------------------------------------------\n * Copyright (c) 2021-2022"
},
{
"path": "codedungeon.Toolbox/src/reorderList.js",
"chars": 681,
"preview": "// @flow\n/*-------------------------------------------------------------------------------------------\n * Copyright (c) "
},
{
"path": "codedungeon.Toolbox/src/support/CodedungeonToolbox.js",
"chars": 4327,
"preview": "/*-------------------------------------------------------------------------------------------\n * Copyright (c) 2021 Mike"
},
{
"path": "community-plugins.json",
"chars": 2027,
"preview": "[\n {\n \"id\": \"np.stockTicker\",\n \"name\": \"Stock Ticker\",\n \"author\": \"EduardMe\",\n \"description\": \"Live stock p"
},
{
"path": "dbludeau.TodoistNoteplanSync/CHANGELOG.md",
"chars": 1016,
"preview": "# Todoist Noteplan Sync Changelog\n\n## About this Plugin\n\nSee Plugin [README](https://github.com/NotePlan/plugins/blob/ma"
},
{
"path": "dbludeau.TodoistNoteplanSync/README.md",
"chars": 4567,
"preview": "# Todoist Noteplan Sync Plugin\n\n## Latest Updates\n\nSee [CHANGELOG](https://github.com/NotePlan/plugins/blob/main/dbludea"
},
{
"path": "dbludeau.TodoistNoteplanSync/__tests__/NPPluginMain.NOTACTIVE.js",
"chars": 5998,
"preview": "/*\n * THIS FILE SHOULD BE RENAMED WITH \".test.js\" AT THE END SO JEST WILL FIND AND RUN IT\n * It is included here as an e"
},
{
"path": "dbludeau.TodoistNoteplanSync/plugin.json",
"chars": 7342,
"preview": "{\n \"COMMENT\": \"Details on these fields: https://help.noteplan.co/article/67-create-command-bar-plugins\",\n \"macOS.minVe"
},
{
"path": "dbludeau.TodoistNoteplanSync/requiredFiles/html-plugin-comms.js",
"chars": 5105,
"preview": "/**\n * html-PluginComms.js - HTML Window: process data to/from the plugin\n * This file is loaded by the browser via <scr"
},
{
"path": "dbludeau.TodoistNoteplanSync/src/NPPluginMain.js",
"chars": 23298,
"preview": "// @flow\n// Plugin code goes in files like this. Can be one per command, or several in a file.\n// `export async function"
},
{
"path": "dbludeau.TodoistNoteplanSync/src/NPTriggers-Hooks.js",
"chars": 3772,
"preview": "/* eslint-disable require-await */\n// @flow\n\nimport pluginJson from '../plugin.json' // gives you access to the contents"
},
{
"path": "dbludeau.TodoistNoteplanSync/src/index.js",
"chars": 2245,
"preview": "// @flow\n// Flow typing is important for reducing errors and improving the quality of the code.\n// About Flow: https://f"
},
{
"path": "dbludeau.TodoistNoteplanSync/src/support/fetchOverrides.js",
"chars": 3234,
"preview": "// @flow\n\n// This file is only loaded and fetch is overridden if the import is enabled in the index file\n\n/**\n * FETCH M"
},
{
"path": "dbludeau.TodoistNoteplanSync/src/support/fetchResponses/google.search-for-something.json",
"chars": 57,
"preview": "{\n \"someKey\": \"Some Value\",\n \"youGet\": \"The Idea\"\n}"
},
{
"path": "dbludeau.TodoistNoteplanSync/src/support/helpers.js",
"chars": 658,
"preview": "// @flow\n// Here's an example function that can be imported and used in the plugin code\n// More importantly, this functi"
},
{
"path": "deleteme.testPluginDownload/README.md",
"chars": 2744,
"preview": "# PLUGIN DOWNLOAD TEST Noteplan Plugin\n\n## Latest Updates\n\nSee [CHANGELOG](https://github.com/NotePlan/plugins/blob/main"
},
{
"path": "deleteme.testPluginDownload/plugin.json",
"chars": 2972,
"preview": "{\n \"plugin.id\": \"deleteme.testPluginDownload\",\n \"plugin.name\": \"🧩 PLUGIN DOWNLOAD TEST\",\n \"plugin.hidden\": true,\n \"C"
},
{
"path": "deleteme.testPluginDownload/src/index.js",
"chars": 1206,
"preview": "// @flow\nimport pluginJson from '../plugin.json' // gives you access to the contents of plugin.json\nimport { updateSetti"
},
{
"path": "docs/custom_theme/README.md",
"chars": 577,
"preview": "# the default theme\n\n\n\nThis is the default theme for [documentationjs](https://github.com/documentati"
},
{
"path": "docs/custom_theme/assets/anchor.js",
"chars": 15308,
"preview": "/*!\n * AnchorJS - v4.0.0 - 2017-06-02\n * https://github.com/bryanbraun/anchorjs\n * Copyright (c) 2017 Bryan Braun; Licen"
},
{
"path": "docs/custom_theme/assets/bass-addons.css",
"chars": 221,
"preview": ".input {\n font-family: inherit;\n display: block;\n width: 100%;\n height: 2rem;\n padding: .5rem;\n margin-bottom: 1re"
},
{
"path": "docs/custom_theme/assets/bass.css",
"chars": 11677,
"preview": "/*! Basscss | http://basscss.com | MIT License */\n\n.h1{ font-size: 2rem }\n.h2{ font-size: 1.5rem }\n.h3{ font-size: 1.25r"
},
{
"path": "docs/custom_theme/assets/fonts/LICENSE.txt",
"chars": 4622,
"preview": "Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Re"
},
{
"path": "docs/custom_theme/assets/fonts/source-code-pro.css",
"chars": 938,
"preview": "@font-face{\n font-family: 'Source Code Pro';\n font-weight: 400;\n font-style: normal;\n font-stretch: normal;\n"
},
{
"path": "docs/custom_theme/assets/github.css",
"chars": 1637,
"preview": "/*\n\ngithub.com style (c) Vasily Polovnyov <vast@whiteants.net>\n\n*/\n\n.hljs {\n display: block;\n overflow-x: auto;\n padd"
},
{
"path": "docs/custom_theme/assets/site.js",
"chars": 4562,
"preview": "/* global anchors */\n\n// add anchor links to headers\nanchors.options.placement = 'left';\nanchors.add('h3');\n\n// Filter U"
},
{
"path": "docs/custom_theme/assets/split.css",
"chars": 585,
"preview": ".gutter {\n background-color: #f5f5f5;\n background-repeat: no-repeat;\n background-position: 50%;\n}\n\n.gutter.gutt"
},
{
"path": "docs/custom_theme/assets/split.js",
"chars": 30356,
"preview": "/*! Split.js - v1.5.11 */\n\n(function (global, factory) {\n typeof exports === 'object' && typeof module !== 'undefined"
},
{
"path": "docs/custom_theme/assets/style.css",
"chars": 1804,
"preview": ".documentation {\n font-family: Helvetica, sans-serif;\n color: #666;\n line-height: 1.5;\n background: #f5f5f5;\n}\n\n.bla"
},
{
"path": "docs/custom_theme/index._",
"chars": 5152,
"preview": "<!doctype html>\n<html lang=\"en\">\n<head>\n <meta charset='utf-8'>\n <title><%- config['project-name'] %> <%- config['proj"
},
{
"path": "docs/custom_theme/index.js",
"chars": 3759,
"preview": "import fs from 'fs/promises'\nimport path from 'path'\nimport { fileURLToPath } from 'url'\nimport template from 'lodash/te"
},
{
"path": "docs/custom_theme/note._",
"chars": 198,
"preview": "<section class='py2 clearfix'>\n\n <h2 id='<%- slug(note.namespace) %>' class='mt0'>\n <%- note.name %>\n </h2>\n\n <% i"
},
{
"path": "docs/custom_theme/paramProperty._",
"chars": 588,
"preview": "<tr>\n <td class='break-word'><span class='code bold'><%- property.name %></span> <code class='quiet'><%= formatType(pro"
},
{
"path": "docs/custom_theme/section._",
"chars": 6638,
"preview": "<section class='p2 mb2 clearfix bg-white minishadow'>\n\n <% if (typeof nested === 'undefined' || (section.context && sec"
},
{
"path": "docs/custom_theme/section_list._",
"chars": 694,
"preview": "<div class=\"clearfix\">\n <% members.forEach(function(member) { %>\n <div class='border-bottom' id='<%- slug(member.nam"
},
{
"path": "docs/documentation.cfg.json",
"chars": 298,
"preview": "{\n \"noPackage\": true,\n \"project-name\":\"NotePlan API+@helpers shared files\",\n \"project-homepage\": \"https://help."
},
{
"path": "dwertheimer.DateAutomations/README.md",
"chars": 2807,
"preview": "# DateAutomations\nDateAutomations includes a number of commands for inserting date/time values into your NotePlan notes\n"
},
{
"path": "dwertheimer.DateAutomations/changelog.md",
"chars": 1408,
"preview": "# dwertheimer.DatAutomations Changelog\n\n## Changelog\n\nAll notable changes to this project will be documented in this fil"
},
{
"path": "dwertheimer.DateAutomations/plugin.json",
"chars": 4426,
"preview": "{\n \"noteplan.minAppVersion\": \"3.3.2\",\n \"macOS.minVersion\": \"10.13.0\",\n \"plugin.id\": \"dwertheimer.DateAutomations\",\n "
},
{
"path": "dwertheimer.DateAutomations/src/dateFunctions.js",
"chars": 7857,
"preview": "// @noflow\n// TODO: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat"
},
{
"path": "dwertheimer.DateAutomations/src/index.js",
"chars": 1112,
"preview": "// @flow\nimport pluginJson from '../plugin.json'\nimport { updateSettingData } from '../../helpers/NPConfiguration'\n\nexpo"
},
{
"path": "dwertheimer.EventAutomations/CHANGELOG.md",
"chars": 9730,
"preview": "# AutoTimeBlocking Change Log\n\nREADME: [How to use AutoTimeBlocking](https://noteplan.co/n/#/1EF12392-B544-4044-AC7A-428"
},
{
"path": "dwertheimer.EventAutomations/README.md",
"chars": 171,
"preview": "# Event Automations\n\n## \"atb - Create AutoTimeBlocks for >today's Tasks\" command\n[How to use AutoTimeBlocking](https://n"
},
{
"path": "dwertheimer.EventAutomations/__tests__/NPEventBlocks.test.js",
"chars": 1338,
"preview": "// Jest testing docs: https://jestjs.io/docs/using-matchers\n/* global describe, test, beforeEach, beforeAll */\n\n// impor"
},
{
"path": "dwertheimer.EventAutomations/__tests__/NPTimeblocking.Integration.test.js",
"chars": 6916,
"preview": "// @flow\n// Jest testing docs: https://jestjs.io/docs/using-matchers\n/* global describe, test, jest, expect, beforeAll *"
},
{
"path": "dwertheimer.EventAutomations/__tests__/NPTimeblocking.test.js",
"chars": 5744,
"preview": "// @flow\n// Jest testing docs: https://jestjs.io/docs/using-matchers\n/* global describe, test, jest, expect, beforeAll *"
},
{
"path": "dwertheimer.EventAutomations/__tests__/by_timeblock_tag.test.js",
"chars": 6347,
"preview": "/* eslint-disable no-unused-vars */\n/* eslint-disable import/order */\n/* global jest, describe, test, expect, beforeAll,"
},
{
"path": "dwertheimer.EventAutomations/__tests__/config.test.js",
"chars": 1653,
"preview": "/* global jest, describe, test, expect, beforeAll, afterAll, beforeEach, afterEach */\n\nimport * as c from '../src/config"
},
{
"path": "dwertheimer.EventAutomations/__tests__/presets.test.js",
"chars": 1017,
"preview": "/* globals describe, expect, test */\nimport { DataStore, Editor, CommandBar, NotePlan } from '@mocks/index'\nimport * as "
},
{
"path": "dwertheimer.EventAutomations/__tests__/timeblocking-helpers.test.js",
"chars": 58812,
"preview": "/* globals describe, expect, test, beforeAll, afterAll */\n// import colors from 'chalk'\n// import /* differenceInCalenda"
},
{
"path": "dwertheimer.EventAutomations/__tests__/timeblocking-shared.test.js",
"chars": 8138,
"preview": "/* eslint-disable no-unused-vars */\n/* eslint-disable import/order */\n/* global jest, describe, test, expect, beforeAll,"
},
{
"path": "dwertheimer.EventAutomations/__tests__/timeblocking-taskSorting.test.js",
"chars": 3700,
"preview": "/* globals describe, expect, it, test, DataStore */\n// import { differenceInCalendarDays, endOfDay, startOfDay, eachMinu"
},
{
"path": "dwertheimer.EventAutomations/plugin.json",
"chars": 19612,
"preview": "{\n \"macOS.minVersion\": \"10.13.0\",\n \"noteplan.minAppVersion\": \"3.6\",\n \"plugin.id\": \"dwertheimer.EventAutomations\",\n \""
},
{
"path": "dwertheimer.EventAutomations/src/NPEventBlocks.js",
"chars": 16893,
"preview": "// @flow\n\nimport { addMinutes } from 'date-fns'\nimport pluginJson from '../plugin.json'\nimport { log, logError, JSP, clo"
},
{
"path": "dwertheimer.EventAutomations/src/NPTimeblocking.js",
"chars": 35041,
"preview": "// @flow\n\n/**\n * WHERE AM I?\n * TODO: update docs for limittotags, presets\n * TODO: timeblocks need the filter DataStore"
},
{
"path": "dwertheimer.EventAutomations/src/byTagMode.js",
"chars": 662,
"preview": "// @flow\n\n// import pluginJson from '../plugin.json'\nimport { sortListBy } from '../../helpers/sorting'\nimport type { Au"
},
{
"path": "dwertheimer.EventAutomations/src/config.js",
"chars": 5685,
"preview": "// @flow\n\nimport { validateConfigProperties } from '../../helpers/config'\nimport { logDebug } from '@helpers/dev'\n\nexpor"
},
{
"path": "dwertheimer.EventAutomations/src/events.js",
"chars": 3726,
"preview": "// @flow\nimport { getEventsForDay } from '../../helpers/NPCalendar'\nimport { getTodaysDateUnhyphenated, type HourMinObj,"
},
{
"path": "dwertheimer.EventAutomations/src/index.js",
"chars": 1522,
"preview": "// @flow\nimport pluginJson from '../plugin.json'\nimport { updateSettingData, pluginUpdated } from '../../helpers/NPConfi"
},
{
"path": "dwertheimer.EventAutomations/src/presets.js",
"chars": 763,
"preview": "import { logDebug } from '@helpers/dev'\n\n/**\n * This is used for the TimeBlocking presets in preferences\n */\n\n/**\n * Pre"
},
{
"path": "dwertheimer.EventAutomations/src/timeblocking-flow-types.js",
"chars": 1393,
"preview": "// @flow\n\nexport type IntervalMap = Array<{ start: string, busy: string | boolean, index: number }>\nexport type OpenBloc"
},
{
"path": "dwertheimer.EventAutomations/src/timeblocking-helpers.js",
"chars": 45350,
"preview": "// @flow\nimport { endOfDay, startOfDay, eachMinuteOfInterval, formatISO9075, addMinutes, differenceInMinutes } from 'dat"
},
{
"path": "dwertheimer.EventAutomations/src/timeblocking-shared.js",
"chars": 12878,
"preview": "// @flow\n\nimport pluginJson from '../plugin.json'\nimport type { SortableParagraphSubset } from '../../helpers/sorting'\ni"
},
{
"path": "dwertheimer.EventAutomations/src/triggers.js",
"chars": 3856,
"preview": "// @flow\n\nimport pluginJson from '../plugin.json'\nimport { shouldRunCheckedItemChecksOriginal, getConfig, getTodaysFilte"
},
{
"path": "dwertheimer.Favorites/CHANGELOG.md",
"chars": 3498,
"preview": "# Favorites Plugin Changelog\n\n## [1.3.6] - 2026-04-13 @dwertheimer\n\n- **PluginRequestEnvelope**: Favorites browser `requ"
},
{
"path": "dwertheimer.Favorites/DEBUGGING_REQUEST_TIMEOUTS.md",
"chars": 9517,
"preview": "# Debugging Request/Response Timeout Issues\n\nThis guide explains how to debug timeout errors in the REQUEST/RESPONSE pat"
},
{
"path": "dwertheimer.Favorites/IMPLEMENTATION_PLAN.md",
"chars": 8538,
"preview": "# Favorites Browser Implementation Plan\n\n## Overview\nThis plan outlines the implementation of a React-based Favorites Br"
},
{
"path": "dwertheimer.Favorites/README.md",
"chars": 6830,
"preview": "# Favorites Plugin\n\n## A temporary substitute for \"pinned notes\"\n\nSomewhere on the Noteplan roadmap is (hopefully) the c"
},
{
"path": "dwertheimer.Favorites/__tests__/NPFavorites.test.js",
"chars": 16469,
"preview": "/* eslint-disable no-unused-vars */\n/* eslint-disable import/order */\n/* global jest, describe, test, expect, beforeAll,"
},
{
"path": "dwertheimer.Favorites/__tests__/favorites.test.js",
"chars": 9683,
"preview": "/* eslint-disable no-unused-vars */\n/* eslint-disable import/order */\n/* globals describe, expect, it, test, beforeAll, "
},
{
"path": "dwertheimer.Favorites/plugin.json",
"chars": 15096,
"preview": "{\n \"COMMENT1\": \"Note If you are not going to use the `npm run autowatch` command to compile, then delete the macOS.minV"
},
{
"path": "dwertheimer.Favorites/src/NPFavoritePresets.js",
"chars": 8583,
"preview": "// @flow\n\nimport pluginJson from '../plugin.json'\nimport { showMessage, chooseOption } from '@helpers/userInput'\nimport "
},
{
"path": "dwertheimer.Favorites/src/NPFavorites.js",
"chars": 6053,
"preview": "// @flow\nimport { chooseOption, showMessage } from '../../helpers/userInput'\nimport { favoriteNotes, getFaveOptionsArray"
},
{
"path": "dwertheimer.Favorites/src/components/AppContext.jsx",
"chars": 4131,
"preview": "// This is a context provider for the app. You should generally not need to edit this file.\n// It provides a way to pass"
},
{
"path": "dwertheimer.Favorites/src/components/FavoritesView.css",
"chars": 4611,
"preview": "/* FavoritesView Component Styles */\n\n.favorites-view-container {\n display: flex;\n flex-direction: column;\n overflow:"
},
{
"path": "dwertheimer.Favorites/src/components/FavoritesView.jsx",
"chars": 37085,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// FavoritesView Component\n// Brow"
},
{
"path": "dwertheimer.Favorites/src/favorites.js",
"chars": 4920,
"preview": "// @flow\n\nimport { noteHasFrontMatter } from '@helpers/NPFrontMatter'\nimport { logDebug, timer } from '@helpers/dev'\nexp"
},
{
"path": "dwertheimer.Favorites/src/favoritesRouter.js",
"chars": 2926,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Favorites Router\n// Routes requ"
},
{
"path": "dwertheimer.Favorites/src/index.js",
"chars": 1962,
"preview": "// @flow\nimport pluginJson from '../plugin.json'\nimport { log, logError, logDebug, timer, clo, JSP } from '@helpers/dev'"
},
{
"path": "dwertheimer.Favorites/src/requestHandlers.js",
"chars": 20215,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Request Handlers - Handle reque"
},
{
"path": "dwertheimer.Favorites/src/shared/types.js",
"chars": 1229,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Shared Types - Used by both Rea"
},
{
"path": "dwertheimer.Favorites/src/support/performRollup.node.js",
"chars": 1444,
"preview": "#!/usr/bin/node\n\n/**\n * FavoritesView Rollup Script\n *\n * Builds development and production modes for:\n * - FavoritesVie"
},
{
"path": "dwertheimer.Favorites/src/support/rollup.FavoritesView.entry.js",
"chars": 252,
"preview": "// use rollup to create the bundle of these included files\n// See directions in the performRollup.node.js file\n\n// Expor"
},
{
"path": "dwertheimer.Favorites/src/windowManagement.js",
"chars": 6666,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Window Management Functions - O"
},
{
"path": "dwertheimer.Forms/CHANGELOG.md",
"chars": 42125,
"preview": "# dwertheimer.Forms Changelog\n\n## About dwertheimer.Forms Plugin\n\nSee Plugin [README](https://github.com/NotePlan/plugin"
},
{
"path": "dwertheimer.Forms/DEBUGGING_PLAN_TEMPLATEJS_HANG.md",
"chars": 16167,
"preview": "# Debugging Plan: `templatejs-block` `fn()` Hang\n\nThis document tracks the investigation into the `dwertheimer.Forms` fr"
},
{
"path": "dwertheimer.Forms/README.md",
"chars": 27234,
"preview": "# 📝 Forms Plugin for NotePlan\n\n## Latest Updates\n\nSee [CHANGELOG](https://github.com/NotePlan/plugins/blob/main/dwerthei"
},
{
"path": "dwertheimer.Forms/TEMPLATEJS_FREEZE_DIAG_THROWS.md",
"chars": 9596,
"preview": "# TemplateJS-Block Freeze — Diagnostic Throw Locations\n\nAdd **one** `throw new Error('DIAG-N: ...')` at a time at these "
},
{
"path": "dwertheimer.Forms/plugin.json",
"chars": 8206,
"preview": "{\n \"COMMENT\": \"Details on these fields: https://help.noteplan.co/article/67-create-command-bar-plugins\",\n \"macOS.minVe"
},
{
"path": "dwertheimer.Forms/src/FormFieldRenderTest.js",
"chars": 54055,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Form Field Render Test\n// Opens"
},
{
"path": "dwertheimer.Forms/src/NPTemplateForm.js",
"chars": 52104,
"preview": "// @flow\n\nimport pluginJson from '../plugin.json'\nimport { type PassedData } from './shared/types.js'\n// Note: getAllNot"
},
{
"path": "dwertheimer.Forms/src/NPTriggers-Hooks.js",
"chars": 4597,
"preview": "/* eslint-disable require-await */\n// @flow\n\nimport pluginJson from '../plugin.json' // gives you access to the contents"
},
{
"path": "dwertheimer.Forms/src/ProcessingTemplate.js",
"chars": 14889,
"preview": "// @flow\nimport pluginJson from '../plugin.json'\nimport { logDebug, logError, JSP, clo } from '@helpers/dev'\nimport { sh"
},
{
"path": "dwertheimer.Forms/src/components/AppContext.jsx",
"chars": 4130,
"preview": "// This is a context provider for the app. You should generally not need to edit this file.\n// It provides a way to pass"
},
{
"path": "dwertheimer.Forms/src/components/ConditionsEditor.jsx",
"chars": 5856,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// ConditionsEditor Component - Ed"
},
{
"path": "dwertheimer.Forms/src/components/FieldEditor.jsx",
"chars": 126465,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// FieldEditor Component - Modal e"
},
{
"path": "dwertheimer.Forms/src/components/FieldTypeSelector.jsx",
"chars": 3654,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// FieldTypeSelector Component - M"
},
{
"path": "dwertheimer.Forms/src/components/FormBrowserView.css",
"chars": 8095,
"preview": "/* FormBrowserView Styles */\n\n.form-browser-container {\n display: flex;\n flex-direction: column;\n height: 100vh;\n wi"
},
{
"path": "dwertheimer.Forms/src/components/FormBrowserView.jsx",
"chars": 42664,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// FormBrowserView Component\n// Br"
},
{
"path": "dwertheimer.Forms/src/components/FormBuilder.css",
"chars": 26389,
"preview": ".form-builder-container {\n display: flex;\n flex-direction: column;\n height: 100vh;\n width: 100%;\n background: var(-"
},
{
"path": "dwertheimer.Forms/src/components/FormBuilder.jsx",
"chars": 36541,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// FormBuilder Component\n// Visual"
},
{
"path": "dwertheimer.Forms/src/components/FormBuilderView.jsx",
"chars": 10621,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// FormBuilderView - React WebView"
},
{
"path": "dwertheimer.Forms/src/components/FormErrorBanner.jsx",
"chars": 5411,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// FormErrorBanner Component - Reu"
},
{
"path": "dwertheimer.Forms/src/components/FormFieldsList.jsx",
"chars": 8892,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// FormFieldsList Component - Midd"
},
{
"path": "dwertheimer.Forms/src/components/FormPreview.jsx",
"chars": 18448,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// FormPreview Component - Right c"
},
{
"path": "dwertheimer.Forms/src/components/FormSettings.jsx",
"chars": 22227,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// FormSettings Component - Left c"
},
{
"path": "dwertheimer.Forms/src/components/FormView.css",
"chars": 19840,
"preview": "/* Dynamic Dialog Styles */\n.dynamic-dialog.form-view {\n width: 90vw !important;\n height: 90vh !important;\n}\n\n/* Templ"
},
{
"path": "dwertheimer.Forms/src/components/FormView.jsx",
"chars": 54950,
"preview": "/***********************************************************************************************************************"
},
{
"path": "dwertheimer.Forms/src/components/OptionsEditor.jsx",
"chars": 6737,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// OptionsEditor Component - Reusa"
},
{
"path": "dwertheimer.Forms/src/components/PositionInput.jsx",
"chars": 2653,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// PositionInput Component\n// Inpu"
},
{
"path": "dwertheimer.Forms/src/components/ProcessingMethodSection.jsx",
"chars": 56201,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// ProcessingMethodSection Compone"
},
{
"path": "dwertheimer.Forms/src/components/TemplateTagEditor.css",
"chars": 7564,
"preview": "/* TemplateTagEditor Component Styles */\n\n.template-tag-editor {\n position: relative;\n width: 100%;\n}\n\n/* Toggle Switc"
},
{
"path": "dwertheimer.Forms/src/components/TemplateTagEditor.jsx",
"chars": 34236,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// TemplateTagEditor Component\n// "
},
{
"path": "dwertheimer.Forms/src/components/TemplateTagInserter.css",
"chars": 4237,
"preview": "/* TemplateTagInserter Component Styles */\n\n.template-tag-inserter-overlay {\n position: fixed;\n top: 0;\n left: 0;\n r"
},
{
"path": "dwertheimer.Forms/src/components/TemplateTagInserter.jsx",
"chars": 12455,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// TemplateTagInserter Component\n/"
},
{
"path": "dwertheimer.Forms/src/components/ValueInsertButtons.jsx",
"chars": 5526,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// ValueInsertButtons - Buttons th"
},
{
"path": "dwertheimer.Forms/src/components/fieldTypes.js",
"chars": 4909,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Field Types Constants\n//-------"
},
{
"path": "dwertheimer.Forms/src/dataHandlers.js",
"chars": 29322,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Data Handlers - Data-fetching f"
},
{
"path": "dwertheimer.Forms/src/formBrowserHandlers.js",
"chars": 26420,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Form Browser Request Handlers\n/"
},
{
"path": "dwertheimer.Forms/src/formBrowserRouter.js",
"chars": 2551,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Form Browser Router\n// Routes r"
},
{
"path": "dwertheimer.Forms/src/formBuilderHandlers.js",
"chars": 36040,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Form Builder Request Handlers\n/"
},
{
"path": "dwertheimer.Forms/src/formBuilderRouter.js",
"chars": 6785,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Form Builder Router\n// Routes a"
},
{
"path": "dwertheimer.Forms/src/formSubmission.js",
"chars": 57857,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Form Submission Handling - Proc"
},
{
"path": "dwertheimer.Forms/src/formSubmitHandlers.js",
"chars": 19880,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Form Submit Request Handlers\n//"
},
{
"path": "dwertheimer.Forms/src/formSubmitRouter.js",
"chars": 7199,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Form Submit Router\n// Routes ac"
},
{
"path": "dwertheimer.Forms/src/index.js",
"chars": 2631,
"preview": "// @flow\n// Flow typing is important for reducing errors and improving the quality of the code.\n// About Flow: https://f"
},
{
"path": "dwertheimer.Forms/src/noteHelpers.js",
"chars": 8986,
"preview": "// @flow\n\n/**\n * Helper functions for converting NotePlan notes to React-compatible format\n * Uses native decoration fun"
},
{
"path": "dwertheimer.Forms/src/requestHandlers.js",
"chars": 36540,
"preview": "// @flow\n\n/**\n * Request Handlers for Forms Plugin\n *\n * This file contains handlers for request/response pattern commun"
},
{
"path": "dwertheimer.Forms/src/shared/constants.js",
"chars": 734,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Shared Constants - Used by both"
},
{
"path": "dwertheimer.Forms/src/shared/types.js",
"chars": 1381,
"preview": "// @flow\n//--------------------------------------------------------------------------\n// Shared Types - Used by both Rea"
},
{
"path": "dwertheimer.Forms/src/support/fetchOverrides.js",
"chars": 3256,
"preview": "// @flow\n\n// This file is only loaded and fetch is overridden if the import is enabled in the index file\n\n/**\n * FETCH M"
},
{
"path": "dwertheimer.Forms/src/support/fetchResponses/google.search-for-something.json",
"chars": 57,
"preview": "{\n \"someKey\": \"Some Value\",\n \"youGet\": \"The Idea\"\n}"
},
{
"path": "dwertheimer.Forms/src/support/performRollup.node.js",
"chars": 2799,
"preview": "#!/usr/bin/node\n\n/**\n * FormView Rollup Script\n *\n * Builds development and production modes for:\n * - WebView bundle\n *"
}
]
// ... and 1154 more files (download for full content)
About this extraction
This page contains the full source code of the NotePlan/plugins GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 1354 files (18.4 MB), approximately 4.9M tokens, and a symbol index with 11412 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.