├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── Releases.md ├── esbuild.config.mjs ├── global.d.ts ├── main.js ├── manifest.json ├── package-lock.json ├── package.json ├── src ├── adapters │ ├── dataview │ │ ├── dataviewMarkdownAdapter.ts │ │ └── metadata │ │ │ ├── dataviewEditor.ts │ │ │ ├── dataviewReader.ts │ │ │ └── parseDataview.ts │ ├── fonts │ │ └── fontsAdapters.ts │ ├── icons │ │ └── iconsAdapter.ts │ ├── image │ │ └── imageAdapter.ts │ ├── mdb │ │ ├── db │ │ │ ├── db.ts │ │ │ ├── sqljs.js │ │ │ └── sqljs │ │ │ │ ├── sql-wasm.js │ │ │ │ └── sql-wasm.wasm │ │ ├── localCache │ │ │ ├── localCache.ts │ │ │ └── localCacheMobile.ts │ │ ├── mdbAdapter.ts │ │ └── utils │ │ │ ├── mdb.ts │ │ │ ├── property.ts │ │ │ └── schema.ts │ ├── obsidian │ │ ├── SpaceViewContainer.tsx │ │ ├── commands │ │ │ └── obsidianCommands.ts │ │ ├── fileSystemPathFixer.tsx │ │ ├── filesystem │ │ │ ├── filesystem.ts │ │ │ ├── indexCurrentFileTree.ts │ │ │ ├── rebuildIndex.ts │ │ │ ├── schemas │ │ │ │ └── vaultSchema.ts │ │ │ └── spaceFileOps.tsx │ │ ├── filetypes │ │ │ ├── canvasAdapter.ts │ │ │ ├── frontmatter │ │ │ │ ├── fm.ts │ │ │ │ └── frontMatterKeys.ts │ │ │ ├── jsonAdapter.ts │ │ │ └── markdownAdapter.ts │ │ ├── inlineContextLoader.tsx │ │ ├── settings.ts │ │ ├── types │ │ │ └── obsidian.d.ts │ │ ├── ui │ │ │ ├── WindowManager.tsx │ │ │ ├── editors │ │ │ │ ├── EmbedSpaceView.tsx │ │ │ │ ├── HTMLFileViewer.tsx │ │ │ │ ├── MDBFileViewer.tsx │ │ │ │ ├── ReadingModeHeader.tsx │ │ │ │ ├── SpaceFragmentViewComponent.tsx │ │ │ │ ├── markdownView │ │ │ │ │ ├── FileLinkViewComponent.tsx │ │ │ │ │ ├── FileView.tsx │ │ │ │ │ └── RemoteMarkdownHeaderView.tsx │ │ │ │ └── readingMode.tsx │ │ │ ├── explorer │ │ │ │ └── ContextExplorerLeafView.tsx │ │ │ ├── icons.ts │ │ │ ├── kit │ │ │ │ ├── InstallKitModal.tsx │ │ │ │ └── kits.ts │ │ │ ├── modal.tsx │ │ │ ├── navigator │ │ │ │ ├── EverLeafView.tsx │ │ │ │ └── NavigatorView.tsx │ │ │ ├── prompt.tsx │ │ │ ├── showMainMenu.tsx │ │ │ ├── sticker.ts │ │ │ └── ui.tsx │ │ └── utils │ │ │ ├── file.ts │ │ │ ├── internalPluginLoaded.ts │ │ │ ├── leaf.ts │ │ │ ├── markdown.ts │ │ │ ├── markdownPost.tsx │ │ │ ├── modifyTabSticker.ts │ │ │ ├── obsidian.ts │ │ │ ├── patches.ts │ │ │ ├── tags.ts │ │ │ └── task.ts │ └── text │ │ ├── removemd.js │ │ └── textCacher.ts ├── basics │ ├── basics.tsx │ ├── cmExtensions.ts │ ├── codemirror.ts │ ├── codemirror │ │ ├── flowEditor.tsx │ │ ├── flowStateFields.ts │ │ ├── flowViewUpdates.ts │ │ └── placeholder.ts │ ├── enactor │ │ ├── enactor.ts │ │ ├── makemd.tsx │ │ └── obsidian.tsx │ ├── flow │ │ ├── FlowEditorHover.tsx │ │ ├── PortalType.ts │ │ ├── flowCommands.ts │ │ ├── flowEditor.ts │ │ ├── markdownPost.tsx │ │ └── patchWorkspaceForFlow.ts │ ├── makemods │ │ └── replaceMobileMainMenu.tsx │ ├── menus │ │ ├── MakeMenu │ │ │ └── MakeMenu.tsx │ │ ├── StickerMenu.tsx │ │ ├── inlineStylerView │ │ │ ├── InlineMenu.tsx │ │ │ ├── Mark.tsx │ │ │ ├── inlineStyler.tsx │ │ │ ├── marks.ts │ │ │ └── styles │ │ │ │ ├── default.ts │ │ │ │ └── index.ts │ │ ├── obsidianSyntax.ts │ │ └── registerMenus.ts │ ├── schemas │ │ └── settings.ts │ ├── tooltip.ts │ ├── types │ │ ├── TransactionRange.ts │ │ ├── command.ts │ │ └── settings.ts │ ├── ui │ │ ├── SettingsPanel.ts │ │ ├── UICollapse.tsx │ │ └── UINote.tsx │ └── utils │ │ └── utils.ts ├── commands.tsx ├── core │ ├── assets │ │ └── placeholders.ts │ ├── export │ │ ├── fromHtml │ │ │ └── htmlToTree.ts │ │ ├── nav │ │ │ └── generateNav.tsx │ │ ├── styleAst │ │ │ ├── generateStyleAst.ts │ │ │ ├── styleAstToCSS.ts │ │ │ └── treeToStyleAst.ts │ │ ├── toHtml │ │ │ ├── mdToHtml.ts │ │ │ ├── mdToTree.ts │ │ │ ├── spaceToHtml.ts │ │ │ └── wikilink.d.ts │ │ ├── treeHelpers.ts │ │ └── treeToAst │ │ │ └── treeToHast.ts │ ├── middleware │ │ ├── commands.ts │ │ ├── filesystem.ts │ │ ├── filetypes.ts │ │ └── ui.tsx │ ├── react │ │ ├── components │ │ │ ├── Blink │ │ │ │ ├── Blink.tsx │ │ │ │ └── BlinkComponent.tsx │ │ │ ├── Explorer │ │ │ │ ├── Explorer.tsx │ │ │ │ └── PropertiesView.tsx │ │ │ ├── MDBView │ │ │ │ └── MDBViewer.tsx │ │ │ ├── MarkdownEditor │ │ │ │ ├── Backlinks.tsx │ │ │ │ ├── BannerView.tsx │ │ │ │ └── MarkdownHeaderView.tsx │ │ │ ├── Navigator │ │ │ │ ├── EverView.tsx │ │ │ │ ├── Focuses │ │ │ │ │ ├── Focus.tsx │ │ │ │ │ └── FocusSelector.tsx │ │ │ │ ├── MainList.tsx │ │ │ │ ├── MainMenu.tsx │ │ │ │ ├── Navigator.tsx │ │ │ │ ├── SpaceTree │ │ │ │ │ ├── NavigatorFocusEditor.tsx │ │ │ │ │ ├── SpaceTreeItem.tsx │ │ │ │ │ ├── SpaceTreeView.tsx │ │ │ │ │ └── SpaceTreeVirtualized.tsx │ │ │ │ └── SyncWarnings.tsx │ │ │ ├── PathView │ │ │ │ ├── NoteView.tsx │ │ │ │ └── PathView.tsx │ │ │ ├── SpaceEditor │ │ │ │ ├── Actions │ │ │ │ │ ├── APIPropertyEditor.tsx │ │ │ │ │ ├── ActionEditor.tsx │ │ │ │ │ ├── ActionNode.tsx │ │ │ │ │ ├── ActionTester.tsx │ │ │ │ │ ├── BuiltinPropertyEditor.tsx │ │ │ │ │ ├── FormulaEditor.tsx │ │ │ │ │ ├── ScriptEditor.tsx │ │ │ │ │ └── SpaceActions.tsx │ │ │ │ ├── SpaceActionProperty.tsx │ │ │ │ ├── SpaceExport.tsx │ │ │ │ ├── SpaceItemProperty.tsx │ │ │ │ ├── SpaceJoins.tsx │ │ │ │ ├── SpaceListProperty.tsx │ │ │ │ ├── SpaceQuery.tsx │ │ │ │ └── SpaceTemplateProperty.tsx │ │ │ ├── SpaceView │ │ │ │ ├── Contexts │ │ │ │ │ ├── CalendarView │ │ │ │ │ │ ├── CalendarHeaderView.tsx │ │ │ │ │ │ ├── DayView │ │ │ │ │ │ │ ├── DayGutter.tsx │ │ │ │ │ │ │ ├── DayItem.tsx │ │ │ │ │ │ │ └── DayView.tsx │ │ │ │ │ │ ├── MonthView │ │ │ │ │ │ │ ├── MonthDayCell.tsx │ │ │ │ │ │ │ ├── MonthView.tsx │ │ │ │ │ │ │ ├── MonthWeekItem.tsx │ │ │ │ │ │ │ └── MonthWeekRow.tsx │ │ │ │ │ │ └── WeekView │ │ │ │ │ │ │ ├── AllDayCell.tsx │ │ │ │ │ │ │ ├── AllDayItem.tsx │ │ │ │ │ │ │ └── WeekView.tsx │ │ │ │ │ ├── ContextBuilder │ │ │ │ │ │ ├── ContextInfiniteScroll.tsx │ │ │ │ │ │ ├── ContextListEditSelector.tsx │ │ │ │ │ │ ├── ContextListInstance.tsx │ │ │ │ │ │ ├── ContextListView.tsx │ │ │ │ │ │ ├── ContextSelector.tsx │ │ │ │ │ │ ├── FrameContainerView.tsx │ │ │ │ │ │ └── InfiniteScrollTrigger.tsx │ │ │ │ │ ├── ContextListContainer.tsx │ │ │ │ │ ├── DataTypeView │ │ │ │ │ │ ├── AggregateCell.tsx │ │ │ │ │ │ ├── BooleanCell.tsx │ │ │ │ │ │ ├── ColorCell.tsx │ │ │ │ │ │ ├── ContextCell.tsx │ │ │ │ │ │ ├── DataPropertyView.tsx │ │ │ │ │ │ ├── DataTypeView.tsx │ │ │ │ │ │ ├── DateCell.tsx │ │ │ │ │ │ ├── FlexCell.tsx │ │ │ │ │ │ ├── FormulaCell.tsx │ │ │ │ │ │ ├── IconCell.tsx │ │ │ │ │ │ ├── ImageCell.tsx │ │ │ │ │ │ ├── LinkCell.tsx │ │ │ │ │ │ ├── NumberCell.tsx │ │ │ │ │ │ ├── ObjectCell.tsx │ │ │ │ │ │ ├── OptionCell.tsx │ │ │ │ │ │ ├── PropertySelectCell.tsx │ │ │ │ │ │ ├── SpaceCell.tsx │ │ │ │ │ │ ├── SuperCell.tsx │ │ │ │ │ │ ├── SuperCell │ │ │ │ │ │ │ └── ParameterSetter.tsx │ │ │ │ │ │ ├── TagCell.tsx │ │ │ │ │ │ └── TextCell.tsx │ │ │ │ │ ├── FilterBar │ │ │ │ │ │ ├── ContextTitle.tsx │ │ │ │ │ │ ├── CustomVIewOption.tsx │ │ │ │ │ │ ├── FilterBar.tsx │ │ │ │ │ │ ├── ListSelector.tsx │ │ │ │ │ │ └── SearchBar.tsx │ │ │ │ │ ├── SpaceEditor │ │ │ │ │ │ └── HeaderPropertiesView.tsx │ │ │ │ │ ├── SpaceView.tsx │ │ │ │ │ └── TableView │ │ │ │ │ │ ├── ColumnHeader.tsx │ │ │ │ │ │ └── TableView.tsx │ │ │ │ ├── Editor │ │ │ │ │ └── EmbedView │ │ │ │ │ │ ├── SpaceFragmentView.tsx │ │ │ │ │ │ └── SpaceFragmentWrapper.tsx │ │ │ │ ├── Frames │ │ │ │ │ ├── DefaultFrames │ │ │ │ │ │ └── DefaultFrames.ts │ │ │ │ │ ├── EditorNodes │ │ │ │ │ │ ├── AudioNodeView.tsx │ │ │ │ │ │ ├── ContentNodeView.tsx │ │ │ │ │ │ ├── ContextNodeView.tsx │ │ │ │ │ │ ├── FlowNodeView.tsx │ │ │ │ │ │ ├── FrameEditorNodeView.tsx │ │ │ │ │ │ ├── IconNodeView.tsx │ │ │ │ │ │ ├── ImageNodeView.tsx │ │ │ │ │ │ ├── InputNodeView.tsx │ │ │ │ │ │ ├── NewNodeView.tsx │ │ │ │ │ │ └── TextNodeView.tsx │ │ │ │ │ ├── FrameNodeEditor │ │ │ │ │ │ ├── FrameHoverMenu.tsx │ │ │ │ │ │ ├── FrameMultiNodeEditor.tsx │ │ │ │ │ │ ├── FrameNodeEditor.tsx │ │ │ │ │ │ ├── FrameNodeEditorContainer.tsx │ │ │ │ │ │ ├── Overlays │ │ │ │ │ │ │ ├── FrameCorners.tsx │ │ │ │ │ │ │ ├── FrameDraggableHandle.tsx │ │ │ │ │ │ │ ├── FrameDropZone.tsx │ │ │ │ │ │ │ ├── FrameFillOverlay.tsx │ │ │ │ │ │ │ ├── FrameGapHandle.tsx │ │ │ │ │ │ │ ├── FrameMarginHandle.tsx │ │ │ │ │ │ │ ├── FramePaddingHandle.tsx │ │ │ │ │ │ │ └── FrameResizer.tsx │ │ │ │ │ │ ├── Slides │ │ │ │ │ │ │ └── FrameSlidesEditor.tsx │ │ │ │ │ │ └── Submenus │ │ │ │ │ │ │ ├── AlignmentSubmenu.tsx │ │ │ │ │ │ │ ├── BorderSubmenu.tsx │ │ │ │ │ │ │ ├── ContentSubmenu.tsx │ │ │ │ │ │ │ ├── HoverSubmenuProps.tsx │ │ │ │ │ │ │ ├── LayoutSubmenu.tsx │ │ │ │ │ │ │ ├── MarginSubmenu.tsx │ │ │ │ │ │ │ ├── ModeSubmenu.tsx │ │ │ │ │ │ │ ├── PaddingSubmenu.tsx │ │ │ │ │ │ │ ├── PropertiesSubmenu.tsx │ │ │ │ │ │ │ ├── ShadowSubmenu.tsx │ │ │ │ │ │ │ ├── SizeSubmenu.tsx │ │ │ │ │ │ │ ├── SpacingSubmenu.tsx │ │ │ │ │ │ │ ├── StyleSubmenu.tsx │ │ │ │ │ │ │ └── TextSubmenu.tsx │ │ │ │ │ ├── Setters │ │ │ │ │ │ ├── ColorSetter.tsx │ │ │ │ │ │ ├── SliderSetter.tsx │ │ │ │ │ │ ├── StepSetter.tsx │ │ │ │ │ │ └── ToggleSetter.tsx │ │ │ │ │ ├── SpaceCommand │ │ │ │ │ │ └── Suggester.tsx │ │ │ │ │ └── ViewNodes │ │ │ │ │ │ ├── FrameEditorInstance.tsx │ │ │ │ │ │ ├── FrameInstance.tsx │ │ │ │ │ │ └── FrameView.tsx │ │ │ │ ├── SpaceHeader.tsx │ │ │ │ ├── SpaceHeaderBar.tsx │ │ │ │ ├── SpaceInner.tsx │ │ │ │ ├── SpaceOuter.tsx │ │ │ │ └── TitleComponent.tsx │ │ │ ├── System │ │ │ │ ├── IconsSet.tsx │ │ │ │ ├── ImageSet.tsx │ │ │ │ ├── MaterialsSet.tsx │ │ │ │ ├── SystemActions.tsx │ │ │ │ ├── SystemTree.tsx │ │ │ │ └── Templates.tsx │ │ │ └── UI │ │ │ │ ├── Crumbs │ │ │ │ ├── ContextTableCrumb.tsx │ │ │ │ ├── ContextViewCrumb.tsx │ │ │ │ ├── FileTableCrumb.tsx │ │ │ │ └── PathCrumb.tsx │ │ │ │ ├── Cues │ │ │ │ └── EmptyState.tsx │ │ │ │ ├── Drawer.tsx │ │ │ │ ├── Dropdown.tsx │ │ │ │ ├── Menus │ │ │ │ ├── contexts │ │ │ │ │ ├── EditPropertyMenu.tsx │ │ │ │ │ ├── PropertyValue.tsx │ │ │ │ │ ├── newSpacePropertyMenu.tsx │ │ │ │ │ ├── rowContextMenu.tsx │ │ │ │ │ └── spacePropertyMenu.tsx │ │ │ │ ├── frames │ │ │ │ │ ├── newFrameMenu.tsx │ │ │ │ │ └── showFramePropsMenu.tsx │ │ │ │ ├── menu.tsx │ │ │ │ ├── menu │ │ │ │ │ ├── SelectMenu.tsx │ │ │ │ │ ├── SelectMenuComponent.tsx │ │ │ │ │ ├── SelectMenuInput.tsx │ │ │ │ │ ├── SelectMenuPill.tsx │ │ │ │ │ ├── SelectMenuSuggestions.tsx │ │ │ │ │ ├── SelectionMenu.tsx │ │ │ │ │ └── concerns │ │ │ │ │ │ ├── focusNextElement.js │ │ │ │ │ │ └── matchers.js │ │ │ │ ├── navigator │ │ │ │ │ ├── pathContextMenu.tsx │ │ │ │ │ ├── showApplyItemsMenu.tsx │ │ │ │ │ ├── showSpaceAddMenu.tsx │ │ │ │ │ └── spaceContextMenu.tsx │ │ │ │ ├── properties │ │ │ │ │ ├── colorPickerMenu.tsx │ │ │ │ │ ├── datePickerMenu.tsx │ │ │ │ │ ├── linkMenu.tsx │ │ │ │ │ ├── propertiesMenu.tsx │ │ │ │ │ ├── propertyMenu.tsx │ │ │ │ │ └── selectSpaceMenu.tsx │ │ │ │ └── selectMenu.tsx │ │ │ │ ├── Modals │ │ │ │ ├── ConfirmationModal.tsx │ │ │ │ ├── HiddenFiles.tsx │ │ │ │ ├── ImageModal.tsx │ │ │ │ ├── InputModal.tsx │ │ │ │ └── modalWrapper.tsx │ │ │ │ ├── Stickers │ │ │ │ └── Sticker.tsx │ │ │ │ └── Toggles │ │ │ │ ├── CollapseToggle.tsx │ │ │ │ └── CollapseToggleSmall.tsx │ │ ├── context │ │ │ ├── ContextEditorContext.tsx │ │ │ ├── FrameEditorRootContext.tsx │ │ │ ├── FrameInstanceContext.tsx │ │ │ ├── FrameRootContext.tsx │ │ │ ├── FrameSelectionContext.tsx │ │ │ ├── FramesMDBContext.tsx │ │ │ ├── PathContext.tsx │ │ │ ├── SidebarContext.tsx │ │ │ ├── SpaceContext.tsx │ │ │ └── WindowContext.tsx │ │ └── hooks │ │ │ ├── ForceUpdate.tsx │ │ │ ├── useCodemirror.tsx │ │ │ ├── useCombinedRef.tsx │ │ │ ├── useEffectOnce.ts │ │ │ ├── useEventListener.ts │ │ │ ├── useInTreeCreateRoot.tsx │ │ │ ├── useLongPress.tsx │ │ │ └── useTimeout.tsx │ ├── schemas │ │ ├── code.ts │ │ ├── parseFieldValue.ts │ │ ├── settings.ts │ │ └── viewTypes.ts │ ├── spaceManager │ │ ├── filesystemAdapter │ │ │ ├── filesystemAdapter.ts │ │ │ ├── spaceInfo.ts │ │ │ └── spaces.ts │ │ ├── spaceManager.ts │ │ └── webAdapter │ │ │ ├── webAdapter.ts │ │ │ └── webCache.ts │ ├── superstate │ │ ├── api.ts │ │ ├── cacheParsers.ts │ │ ├── commands.ts │ │ ├── superstate.ts │ │ ├── utils │ │ │ ├── label.ts │ │ │ ├── path.ts │ │ │ ├── spaces.ts │ │ │ └── tags.ts │ │ └── workers │ │ │ ├── indexer │ │ │ ├── impl.ts │ │ │ ├── indexer.ts │ │ │ └── indexer.worker.ts │ │ │ └── search │ │ │ └── impl.ts │ ├── types │ │ ├── make.d.ts │ │ ├── space.ts │ │ ├── superstate.ts │ │ ├── types.ts │ │ ├── ui.ts │ │ └── worker.d.ts │ └── utils │ │ ├── color.ts │ │ ├── color │ │ └── gradient.ts │ │ ├── commands │ │ ├── actions.ts │ │ ├── commands.ts │ │ └── filter.ts │ │ ├── contexts │ │ ├── context.ts │ │ ├── fields │ │ │ ├── presets.ts │ │ │ └── units.ts │ │ ├── linkContextRow.ts │ │ ├── links.ts │ │ ├── lookup.ts │ │ ├── optionValuesForColumn.ts │ │ ├── pathUpdates.ts │ │ └── predicate │ │ │ ├── aggregates.ts │ │ │ ├── filter.ts │ │ │ ├── filterFns │ │ │ ├── filterFnLabels.ts │ │ │ └── filterFnTypes.ts │ │ │ ├── predicate.tsx │ │ │ ├── sort.ts │ │ │ └── tableview.ts │ │ ├── date.ts │ │ ├── dnd │ │ ├── dragPath.ts │ │ └── dropPath.ts │ │ ├── emoji.ts │ │ ├── fonts.ts │ │ ├── formula │ │ ├── formulas.ts │ │ ├── formulasInfos.ts │ │ ├── keywords.ts │ │ ├── parser.ts │ │ └── syntax.ts │ │ ├── frames │ │ ├── ast.ts │ │ ├── editor │ │ │ └── dropFrame.ts │ │ ├── executable.ts │ │ ├── frame.ts │ │ ├── frames.ts │ │ ├── linker.ts │ │ ├── nodes.ts │ │ ├── renderer.ts │ │ └── runner.ts │ │ ├── hash.ts │ │ ├── json.ts │ │ ├── keyboard.ts │ │ ├── libs │ │ ├── autosizer.tsx │ │ └── detectElementResize.js │ │ ├── metadata.ts │ │ ├── objects.ts │ │ ├── parser.tsx │ │ ├── properties │ │ └── allProperties.ts │ │ ├── serializer.ts │ │ ├── spaces │ │ ├── query.ts │ │ └── space.ts │ │ ├── strings.ts │ │ ├── superstate │ │ ├── parser.ts │ │ └── serializer.ts │ │ ├── tree.ts │ │ └── ui │ │ ├── menu.ts │ │ ├── screen.ts │ │ └── selection.ts ├── css │ ├── .space │ │ └── views.mdb │ ├── DefaultVibe.css │ ├── Editor │ │ ├── Actions │ │ │ └── Actions.css │ │ ├── Context │ │ │ ├── ContextList.css │ │ │ └── FilterBar.css │ │ ├── Flow │ │ │ ├── FlowEditor.css │ │ │ ├── FlowState.css │ │ │ └── Properties.css │ │ ├── Frames │ │ │ ├── Insert.css │ │ │ ├── Node.css │ │ │ ├── Overlay.css │ │ │ ├── Page.css │ │ │ └── Slides.css │ │ └── Properties │ │ │ └── DatePicker.css │ ├── Menus │ │ ├── ColorPicker.css │ │ ├── InlineMenu.css │ │ ├── MainMenu.css │ │ ├── MakeMenu.css │ │ ├── Menu.css │ │ └── StickerMenu.css │ ├── Modal │ │ └── Modal.css │ ├── Obsidian │ │ └── Mods.css │ ├── Panels │ │ ├── Blink.css │ │ ├── ContextBuilder.css │ │ ├── FileContext.css │ │ ├── Navigator │ │ │ ├── EverView.css │ │ │ ├── FileTree.css │ │ │ ├── Focuses.css │ │ │ └── Navigator.css │ │ └── SpaceEditor.css │ ├── SpaceViewer │ │ ├── Calendar.css │ │ ├── Frame.css │ │ ├── Layout.css │ │ ├── Nodes.css │ │ ├── SpaceView.css │ │ ├── TableView.css │ │ └── Text.css │ └── UI │ │ └── Buttons.css ├── main.ts ├── makemd-core.ts ├── schemas │ ├── cache.ts │ ├── frames.ts │ ├── kits │ │ ├── base.ts │ │ ├── calendar.ts │ │ ├── list.ts │ │ ├── slides.ts │ │ └── ui.ts │ └── mdb.ts ├── shared │ ├── FlowEditor.tsx │ ├── assets │ │ ├── emoji.ts │ │ └── icons.ts │ ├── components │ │ ├── PathSticker.tsx │ │ └── StickerModal.tsx │ ├── i18n.ts │ ├── schemas │ │ ├── builtin.ts │ │ ├── context.ts │ │ ├── fields.ts │ │ └── predicate.tsx │ ├── types │ │ ├── PathState.ts │ │ ├── Pos.ts │ │ ├── Warning.ts │ │ ├── actions.ts │ │ ├── afile.ts │ │ ├── api.ts │ │ ├── blink.tsx │ │ ├── caches.ts │ │ ├── commands.ts │ │ ├── context.ts │ │ ├── emojis.ts │ │ ├── focus.ts │ │ ├── frameExec.ts │ │ ├── indexMap.ts │ │ ├── kits.ts │ │ ├── makemd.ts │ │ ├── mdb.ts │ │ ├── menu.ts │ │ ├── metadata.ts │ │ ├── mframe.ts │ │ ├── path.ts │ │ ├── persister.ts │ │ ├── predicate.ts │ │ ├── settings.ts │ │ ├── spaceDef.ts │ │ ├── spaceFragment.ts │ │ ├── spaceInfo.ts │ │ ├── spaceManager.ts │ │ ├── superstate.ts │ │ ├── ui.ts │ │ └── uiManager.ts │ └── utils │ │ ├── array.ts │ │ ├── codemirror │ │ └── selectiveEditor.ts │ │ ├── color.ts │ │ ├── dispatchers │ │ └── dispatcher.ts │ │ ├── dom.ts │ │ ├── inputManager.ts │ │ ├── json.ts │ │ ├── makemd │ │ ├── embed.ts │ │ ├── fragment.ts │ │ ├── inlineTable.ts │ │ └── schema.ts │ │ ├── obsidian.ts │ │ ├── openPathInElement.ts │ │ ├── paths.ts │ │ ├── sanitizers.ts │ │ ├── sticker.ts │ │ ├── stickers.ts │ │ ├── uri.ts │ │ └── uuid.js └── utils │ ├── hide.ts │ ├── parsers.ts │ ├── path.ts │ ├── properties.ts │ ├── regex.ts │ ├── serializers.ts │ └── tags.ts ├── styles.css ├── tsconfig.json └── versions.json /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "env": { 3 | "browser": true, 4 | "es2021": true 5 | }, 6 | "extends": [ 7 | "eslint:recommended", 8 | "plugin:react/recommended", 9 | "plugin:@typescript-eslint/recommended", 10 | "plugin:react-hooks/recommended" 11 | ], 12 | "overrides": [ 13 | ], 14 | "parser": "@typescript-eslint/parser", 15 | "parserOptions": { 16 | "ecmaVersion": "latest", 17 | "sourceType": "module" 18 | }, 19 | "plugins": [ 20 | "react", 21 | "@typescript-eslint" 22 | ], 23 | "rules": { 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .env 3 | .DS_Store 4 | undefined 5 | .vscode -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 JP Cen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Releases.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Make-md/makemd/1d6ba937c5c0fef6794a4612acc5af72c61cf450/Releases.md -------------------------------------------------------------------------------- /global.d.ts: -------------------------------------------------------------------------------- 1 | declare module "*.css"; 2 | -------------------------------------------------------------------------------- /manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "make-md", 3 | "name": "make.md", 4 | "version": "1.1.6", 5 | "minAppVersion": "0.16.0", 6 | "description": "make.md gives you everything you need to organize and personalize your notes.", 7 | "author": "make.md", 8 | "authorUrl": "https://www.make.md", 9 | "isDesktopOnly": false 10 | } 11 | -------------------------------------------------------------------------------- /src/adapters/mdb/db/sqljs.js: -------------------------------------------------------------------------------- 1 | import initSqlJs from "sql.js"; 2 | import sql_wasm from "sqljs/sql-wasm.wasm"; 3 | 4 | export const loadSQL = async () => { 5 | const sql = await initSqlJs({ 6 | wasmBinary: sql_wasm, 7 | }); 8 | return sql; 9 | }; 10 | -------------------------------------------------------------------------------- /src/adapters/mdb/db/sqljs/sql-wasm.wasm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Make-md/makemd/1d6ba937c5c0fef6794a4612acc5af72c61cf450/src/adapters/mdb/db/sqljs/sql-wasm.wasm -------------------------------------------------------------------------------- /src/adapters/mdb/utils/property.ts: -------------------------------------------------------------------------------- 1 | import { fieldSchema } from "shared/schemas/fields"; 2 | import { DBTables, SpaceProperty } from "shared/types/mdb"; 3 | import { sanitizeColumnName } from "shared/utils/sanitizers"; 4 | 5 | export const savePropertyToDBTables = (newColumn: SpaceProperty, fields: SpaceProperty[], oldColumn?: SpaceProperty): DBTables => { 6 | const column = { 7 | ...newColumn, 8 | name: sanitizeColumnName(newColumn.name), 9 | }; 10 | 11 | 12 | const oldFieldIndex = oldColumn 13 | ? fields.findIndex((f) => f.name == oldColumn.name) 14 | : -1; 15 | const newFields: SpaceProperty[] = 16 | oldFieldIndex == -1 17 | ? [...fields, column] 18 | : fields.map((f, i) => (i == oldFieldIndex ? column : f)); 19 | return { 20 | m_fields: { 21 | uniques: fieldSchema.uniques, 22 | cols: fieldSchema.cols, 23 | rows: newFields, 24 | }, 25 | }; 26 | }; 27 | 28 | export const deletePropertyToDBTables = (column: SpaceProperty, fields: SpaceProperty[]): DBTables => { 29 | const newFields = fields.filter((f) => !(f.name == column.name && f.schemaId == column.schemaId)); 30 | return { 31 | m_fields: { 32 | uniques: fieldSchema.uniques, 33 | cols: fieldSchema.cols, 34 | rows: [...newFields], 35 | }, 36 | }; 37 | } -------------------------------------------------------------------------------- /src/adapters/mdb/utils/schema.ts: -------------------------------------------------------------------------------- 1 | import { DBTable, SpaceTableSchema } from "shared/types/mdb"; 2 | 3 | export const saveSchemaToDBTables = (table: SpaceTableSchema, schemas: SpaceTableSchema[]) => { 4 | const newSchema = schemas.find((f) => f.id == table.id) 5 | ? true 6 | : false; 7 | 8 | const newSchemaTable: DBTable = newSchema 9 | ? { 10 | uniques: [], 11 | cols: ["id", "name", "type", "def", "predicate", "primary"], 12 | rows: schemas.map((f) => (f.id == table.id ? table : f)), 13 | } 14 | : { 15 | uniques: [], 16 | cols: ["id", "name", "type", "def", "predicate", "primary"], 17 | rows: [...schemas, table], 18 | }; 19 | return { 20 | m_schema: newSchemaTable, 21 | } 22 | }; 23 | 24 | export const deleteSchemaToDBTables = (table: SpaceTableSchema, schemas: SpaceTableSchema[]) => { 25 | const newSchemaTable: DBTable = { 26 | uniques: [], 27 | cols: ["id", "name", "type", "def", "predicate", "primary"], 28 | rows: schemas.filter((f) => f.id != table.id), 29 | }; 30 | return { 31 | m_schema: newSchemaTable, 32 | } 33 | } -------------------------------------------------------------------------------- /src/adapters/obsidian/commands/obsidianCommands.ts: -------------------------------------------------------------------------------- 1 | import { CLIManager } from "core/middleware/commands"; 2 | import MakeMDPlugin from "main"; 3 | import { Command as ObsidianCommand } from "obsidian"; 4 | import { CLIAdapter } from "shared/types/actions"; 5 | import { CommandWithPath } from "shared/types/commands"; 6 | import { parseURI } from "shared/utils/uri"; 7 | 8 | const obsidianCommandToCommand = (command: ObsidianCommand) : CommandWithPath => { 9 | if (!command) return null; 10 | return { 11 | scheme: 'obsidian', 12 | schema: { 13 | id: command.id, 14 | name: command.name, 15 | type: 'command', 16 | }, 17 | path: 'obsidian://' + command.id, 18 | fields: [], 19 | code: command.callback, 20 | codeType: "closure" 21 | } 22 | } 23 | 24 | export class ObsidianCommands implements CLIAdapter { 25 | constructor (public plugin: MakeMDPlugin) { 26 | } 27 | 28 | manager: CLIManager; 29 | scheme = "obsidian"; 30 | public allCommands = () => { 31 | return Object.values(this.plugin.app.commands.commands).map(f => obsidianCommandToCommand(f)); 32 | } 33 | 34 | public commandForAction = (action: string) => { 35 | 36 | if (!action) return null; 37 | const uri = parseURI(action); 38 | return obsidianCommandToCommand(this.plugin.app.commands.commands[uri.authority]); 39 | } 40 | public runCommand = async (action: string, parameters?: {[key: string]: any}) => { 41 | if (!action) return; 42 | const uri = parseURI(action); 43 | const command = uri.authority 44 | if (this.plugin.app.commands.commands[command]?.callback) { 45 | this.plugin.app.commands.commands[command].callback() 46 | } else if (this.plugin.app.commands.commands[command].checkCallback) { 47 | this.plugin.app.commands.commands[command].checkCallback(false); 48 | } 49 | } 50 | } -------------------------------------------------------------------------------- /src/adapters/obsidian/fileSystemPathFixer.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | getAbstractFileAtPath, 3 | renameFile, 4 | } from "adapters/obsidian/utils/file"; 5 | import { ConfirmationModal } from "core/react/components/UI/Modals/ConfirmationModal"; 6 | import { updatePrimaryAlias } from "core/superstate/utils/label"; 7 | import MakeMDPlugin from "main"; 8 | import { TFile } from "obsidian"; 9 | import React from "react"; 10 | import { sanitizeFileName } from "shared/utils/sanitizers"; 11 | 12 | export const openPathFixer = (plugin: MakeMDPlugin) => { 13 | const superstate = plugin.superstate; 14 | const currentIssueFiles = [...plugin.obsidianAdapter.fileNameWarnings]; 15 | const message = `The following files have issues with their names. Would you like to fix them? \n ${currentIssueFiles.join( 16 | "\n" 17 | )}`; 18 | superstate.ui.openModal( 19 | "Path Fixer", 20 | { 24 | for (const file of currentIssueFiles) { 25 | const currentFile = getAbstractFileAtPath(plugin.app, file); 26 | const currentName = 27 | currentFile instanceof TFile 28 | ? (currentFile as TFile)?.basename 29 | : currentFile.name; 30 | if (!currentFile) return; 31 | await updatePrimaryAlias( 32 | plugin.superstate, 33 | file, 34 | plugin.superstate.pathsIndex.get(file)?.metadata?.property?.aliases, 35 | currentName 36 | ); 37 | await renameFile(plugin, currentFile, sanitizeFileName(currentName)); 38 | } 39 | plugin.obsidianAdapter.fileNameWarnings = new Set(); 40 | }} 41 | >, 42 | window 43 | ); 44 | }; 45 | -------------------------------------------------------------------------------- /src/adapters/obsidian/filesystem/indexCurrentFileTree.ts: -------------------------------------------------------------------------------- 1 | import { vaultSchema } from "adapters/obsidian/filesystem/schemas/vaultSchema"; 2 | import MakeMDPlugin from "main"; 3 | import { TFile, TFolder } from "obsidian"; 4 | import { DBRows, DBTables } from "shared/types/mdb"; 5 | import { getAllAbstractFilesInVault } from "../utils/file"; 6 | 7 | 8 | export const indexCurrentFileTree = (plugin: MakeMDPlugin, vaultDB: DBRows): DBTables => { 9 | const treeItems: DBRows = getAllAbstractFilesInVault(plugin.app).map(file => { 10 | const currentCache = vaultDB.find(f => f.path == file.path) ?? {}; 11 | return { 12 | ...currentCache, 13 | path: file.path, 14 | parent: file.parent?.path, 15 | created: currentCache?.ctime?.length > 0 ? currentCache.ctime : file instanceof TFile ? file.stat.ctime.toString() : undefined, 16 | folder: file instanceof TFolder ? "true" : "false", 17 | }}); 18 | 19 | return { 20 | vault: { 21 | ...vaultSchema, 22 | rows: treeItems 23 | }, 24 | }; 25 | 26 | }; 27 | -------------------------------------------------------------------------------- /src/adapters/obsidian/filesystem/rebuildIndex.ts: -------------------------------------------------------------------------------- 1 | import { ObsidianFileSystem } from "adapters/obsidian/filesystem/filesystem"; 2 | import _ from "lodash"; 3 | import MakeMDPlugin from "main"; 4 | import { indexCurrentFileTree } from "./indexCurrentFileTree"; 5 | 6 | 7 | export const rebuildIndex = async (filesystem: ObsidianFileSystem, plugin: MakeMDPlugin, save?: boolean) => { 8 | const start = Date.now(); 9 | const newTables = indexCurrentFileTree(plugin, filesystem.vaultDBCache ?? []); 10 | if (save && (!_.isEqual(newTables.vault.rows, filesystem.vaultDBCache))) { 11 | await filesystem.saveSpacesDatabaseToDisk(newTables, save); 12 | } 13 | plugin.superstate.ui.notify(`Make.md - Vault Reindexed in ${(Date.now()-start)/1000} seconds`, "console"); 14 | }; 15 | -------------------------------------------------------------------------------- /src/adapters/obsidian/filesystem/schemas/vaultSchema.ts: -------------------------------------------------------------------------------- 1 | import { DBTable } from "shared/types/mdb"; 2 | 3 | 4 | export const vaultSchema: DBTable = { 5 | uniques: ["path"], 6 | cols: ["path", "parent", "created", "sticker", "color", "folder", "rank", "name"], 7 | rows: [], 8 | }; 9 | -------------------------------------------------------------------------------- /src/adapters/obsidian/filetypes/frontmatter/frontMatterKeys.ts: -------------------------------------------------------------------------------- 1 | 2 | export const frontMatterKeys = (fm: Record) => { 3 | return Object.keys(fm ?? {}) 4 | .filter((f) => f != "position") 5 | }; 6 | -------------------------------------------------------------------------------- /src/adapters/obsidian/ui/editors/ReadingModeHeader.tsx: -------------------------------------------------------------------------------- 1 | import { PathProvider } from "core/react/context/PathContext"; 2 | import { MarkdownHeaderView, Superstate } from "makemd-core"; 3 | import React, { useEffect, useRef, useState } from "react"; 4 | 5 | export const ReadingModeHeader = (props: { 6 | superstate: Superstate; 7 | filePath: string; 8 | }) => { 9 | const [path, setPath] = useState(props.filePath); 10 | const ref = useRef(null); 11 | useEffect(() => { 12 | setPath(props.filePath); 13 | }, [props.filePath]); 14 | const changeActiveFile = (path: string) => { 15 | if (ref.current.closest(".mod-active")) setPath(path); 16 | }; 17 | useEffect(() => { 18 | props.superstate.ui.eventsDispatch.addListener( 19 | "activePathChanged", 20 | changeActiveFile 21 | ); 22 | return () => { 23 | props.superstate.ui.eventsDispatch.removeListener( 24 | "activePathChanged", 25 | changeActiveFile 26 | ); 27 | }; 28 | }, []); 29 | return ( 30 |
31 | 32 | 36 | 37 |
38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /src/adapters/obsidian/ui/editors/readingMode.tsx: -------------------------------------------------------------------------------- 1 | export const replaceMarkdownForReadingMode = ( 2 | el: HTMLElement, 3 | callback: (dom: HTMLElement) => void 4 | ) => { 5 | let dom: HTMLElement = el; 6 | setTimeout(async () => { 7 | //wait for el to be attached to the displayed document 8 | let counter = 0; 9 | while (!el.parentElement && counter++ <= 50) await sleep(50); 10 | if (!el.parentElement) return; 11 | 12 | while ( 13 | !dom.hasClass("markdown-reading-view") && 14 | !dom.hasClass("internal-embed") && 15 | dom.parentElement 16 | ) { 17 | dom = dom.parentElement; 18 | } 19 | if (dom && dom.hasClass("markdown-reading-view")) { 20 | callback(dom); 21 | } 22 | }); 23 | }; 24 | -------------------------------------------------------------------------------- /src/adapters/obsidian/ui/explorer/ContextExplorerLeafView.tsx: -------------------------------------------------------------------------------- 1 | import { FileContextView, Superstate, i18n } from "makemd-core"; 2 | import { ItemView, ViewStateResult, WorkspaceLeaf } from "obsidian"; 3 | import React from "react"; 4 | import { Root } from "react-dom/client"; 5 | import { ObsidianUI } from "../ui"; 6 | 7 | export class ContextExplorerLeafView extends ItemView { 8 | superstate: Superstate; 9 | navigation = false; 10 | root: Root; 11 | ui: ObsidianUI; 12 | constructor(leaf: WorkspaceLeaf, superstate: Superstate, ui: ObsidianUI) { 13 | super(leaf); 14 | this.superstate = superstate; 15 | this.ui = ui; 16 | } 17 | 18 | getViewType(): string { 19 | return FILE_CONTEXT_VIEW_TYPE; 20 | } 21 | 22 | getDisplayText(): string { 23 | return VIEW_DISPLAY_TEXT; 24 | } 25 | 26 | getIcon(): string { 27 | return ICON; 28 | } 29 | 30 | async onClose() { 31 | this.destroy(); 32 | } 33 | 34 | destroy() { 35 | this.root?.unmount(); 36 | } 37 | 38 | async onOpen(): Promise { 39 | this.destroy(); 40 | this.constructFileContext(); 41 | } 42 | 43 | async setState(state: any, result: ViewStateResult): Promise { 44 | this.constructFileContext(); 45 | await super.setState(state, result); 46 | 47 | return; 48 | } 49 | getState(): any { 50 | const state = super.getState(); 51 | 52 | // Store information to the state, whenever the workspace changes (opening a new note,...), the view's `getState` will be called, and the resulting state will be saved in the 'workspace' file 53 | return state; 54 | } 55 | 56 | constructFileContext() { 57 | this.destroy(); 58 | this.root = this.ui.createRoot(this.contentEl); 59 | if (this.root) 60 | this.root.render( 61 | 62 | ); 63 | } 64 | } 65 | export const FILE_CONTEXT_VIEW_TYPE = "make-context-view"; 66 | export const ICON = "component"; 67 | export const VIEW_DISPLAY_TEXT = i18n.views.explorer; 68 | -------------------------------------------------------------------------------- /src/adapters/obsidian/ui/prompt.tsx: -------------------------------------------------------------------------------- 1 | import { App, Modal } from "obsidian"; 2 | import React from "react"; 3 | import { Root } from "react-dom/client"; 4 | import { ObsidianUI } from "./ui"; 5 | 6 | export class ObsidianPalette extends Modal { 7 | ref: any; 8 | root: Root; 9 | constructor( 10 | app: App, 11 | public fc: React.FC<{ hide: () => void; ref: any }>, 12 | public className: string, 13 | public ui: ObsidianUI 14 | ) { 15 | super(app); 16 | this.ref = React.createRef(); 17 | } 18 | 19 | onOpen() { 20 | this.contentEl.remove(); 21 | this.modalEl.classList.add("prompt"); 22 | this.modalEl.classList.remove("modal"); 23 | this.modalEl.classList.add(this.className); 24 | 25 | this.titleEl.remove(); 26 | this.modalEl.querySelector(".modal-close-button").remove(); 27 | 28 | const keys = [...this.scope.keys]; 29 | for (let i = 0; i < keys.length; i++) { 30 | if (keys[i].key == "Escape") { 31 | this.scope.unregister(keys[i]); 32 | this.scope.register([], "Escape", () => { 33 | if (typeof this.ref?.current === "function") { 34 | const blurred = this.ref?.current(); 35 | if (blurred) { 36 | this.close(); 37 | } 38 | } else { 39 | this.close(); 40 | } 41 | }); 42 | } 43 | } 44 | this.root = this.ui.createRoot(this.modalEl); 45 | this.root.render( 46 | this.close()} ref={this.ref}> 47 | ); 48 | } 49 | 50 | onClose() { 51 | if (this.root) this.root.unmount(); 52 | const { contentEl } = this; 53 | contentEl.innerHTML = ""; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/adapters/obsidian/ui/sticker.ts: -------------------------------------------------------------------------------- 1 | import MakeMDPlugin from "main"; 2 | import { uiIconSet } from "shared/assets/icons"; 3 | import { emojiFromString, parseStickerString } from "shared/utils/stickers"; 4 | import { lucideIcon } from "./icons"; 5 | 6 | 7 | export const stickerFromString = (sticker: string, plugin: MakeMDPlugin, options?: { 8 | fontless?: boolean; 9 | }) => { 10 | if (!sticker || typeof sticker != 'string') 11 | return ""; 12 | const [type, value] = parseStickerString(sticker); 13 | if (type == '' || type == 'emoji') { 14 | return ` 15 | 16 | ${emojiFromString(value)} 17 | 18 | `; 19 | } else if (type == 'ui') { 20 | return uiIconSet[value]; 21 | } else if (type == 'lucide') { 22 | return lucideIcon(value); 23 | } else { 24 | let icon = plugin.superstate.iconsCache.get(value); 25 | if (!icon) { 26 | 27 | const alias = plugin.superstate.imagesCache.get(value); 28 | if (alias) { 29 | icon = plugin.superstate.iconsCache.get(alias) 30 | } 31 | } 32 | return icon 33 | } 34 | 35 | }; 36 | -------------------------------------------------------------------------------- /src/adapters/obsidian/utils/internalPluginLoaded.ts: -------------------------------------------------------------------------------- 1 | import { App } from "obsidian"; 2 | 3 | 4 | export const internalPluginLoaded = (pluginName: string, app: App) => { 5 | // @ts-ignore 6 | return app.internalPlugins.plugins[pluginName]?._loaded; 7 | }; 8 | -------------------------------------------------------------------------------- /src/adapters/obsidian/utils/leaf.ts: -------------------------------------------------------------------------------- 1 | import { App, WorkspaceLeaf } from "obsidian"; 2 | 3 | export const workspaceLeafForDom = (app: App, dom: HTMLElement) : WorkspaceLeaf => { 4 | const leafDom = dom.closest('.workspace-leaf'); 5 | let foundLeaf: WorkspaceLeaf; 6 | app.workspace.iterateLeaves((leaf) => { 7 | if (leaf.containerEl == leafDom) { 8 | foundLeaf = leaf; 9 | return true; 10 | } 11 | }, app.workspace["rootSplit"]!); 12 | return foundLeaf; 13 | } -------------------------------------------------------------------------------- /src/adapters/obsidian/utils/markdown.ts: -------------------------------------------------------------------------------- 1 | import { defaultContextDBSchema } from "shared/schemas/context"; 2 | import { SpaceTable } from "shared/types/mdb"; 3 | 4 | type Page = { 5 | title: string; 6 | contexts: string[]; 7 | subpages: Page[]; 8 | } 9 | 10 | type Line = { 11 | isTask: boolean; 12 | value: string; 13 | } 14 | 15 | export const markdownToTable = (markdown: string) => { 16 | const lines : Line[] = [] 17 | const table : SpaceTable = { 18 | schema: defaultContextDBSchema, 19 | cols: [], 20 | rows: [] 21 | } 22 | return lines; 23 | } 24 | 25 | export const markdownToPage = (markdown: string) : Page => { 26 | const lineIsTodo = true; 27 | if (lineIsTodo) { 28 | 29 | } 30 | return { 31 | title: '', 32 | contexts: [], 33 | subpages: [] 34 | } 35 | } -------------------------------------------------------------------------------- /src/adapters/obsidian/utils/obsidian.ts: -------------------------------------------------------------------------------- 1 | import { App } from "obsidian"; 2 | 3 | export const corePluginEnabled = (app: App, plugin: string) => app.internalPlugins.getPluginById(plugin) ? true : false; -------------------------------------------------------------------------------- /src/adapters/obsidian/utils/task.ts: -------------------------------------------------------------------------------- 1 | 2 | export type Task = { 3 | text: string; 4 | tags: string[]; 5 | date?: string; 6 | completed: boolean; 7 | }; 8 | export const parseTask = (task: string) : Task => { 9 | let completed = false; 10 | let text = ''; 11 | if (task.startsWith("- [ ] ")) { 12 | completed = false; 13 | text = task.slice(6); 14 | } else if (task.startsWith("- [x] ")) { 15 | completed = true; 16 | text = task.slice(6); 17 | } 18 | const tags = text.match(/#(\w+)/g) ?? []; 19 | const date = text.match(/\^(\d{4}-\d{2}-\d{2})/) ?? []; 20 | return { 21 | text: text.replace(/#(\w+)/g, "").replace(/\^(\d{4}-\d{2}-\d{2})/, ""), 22 | tags: tags.map(f => f.replace("#", "")), 23 | date: date[1], 24 | completed 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /src/basics/cmExtensions.ts: -------------------------------------------------------------------------------- 1 | import { editBlockExtensions } from "../shared/utils/codemirror/selectiveEditor"; 2 | import { 3 | flowEditorInfo, 4 | internalLinkHover, 5 | internalLinkToggle, 6 | preloadFlowEditor 7 | } from "./codemirror/flowEditor"; 8 | import { flowIDStateField, flowTypeStateField } from "./codemirror/flowStateFields"; 9 | import { flowViewUpdates } from "./codemirror/flowViewUpdates"; 10 | import { placeholderExtension } from "./codemirror/placeholder"; 11 | import { cursorTooltip } from "./menus/inlineStylerView/inlineStyler"; 12 | import { toggleMarkExtension } from "./menus/inlineStylerView/marks"; 13 | 14 | import { Extension } from '@codemirror/state'; 15 | 16 | import MakeBasicsPlugin from "./basics"; 17 | import { tooltips } from "./tooltip"; 18 | 19 | export const cmExtensions = (plugin: MakeBasicsPlugin, mobile: boolean) => { 20 | const extensions : Extension[] = [...editBlockExtensions()]; 21 | 22 | extensions.push( 23 | ...[toggleMarkExtension, tooltips({ parent: document.body })] 24 | ); 25 | if (!mobile && plugin.settings.inlineStyler) { 26 | extensions.push(cursorTooltip(plugin)); 27 | } 28 | 29 | if (plugin.settings.flowMenuEnabled && plugin.settings.makeMenuPlaceholder) extensions.push(placeholderExtension(plugin)); 30 | if (plugin.settings.editorFlow) { 31 | extensions.push( 32 | flowTypeStateField, 33 | 34 | preloadFlowEditor, 35 | ); 36 | 37 | extensions.push( 38 | 39 | flowEditorInfo, 40 | flowIDStateField, 41 | flowViewUpdates(plugin) 42 | ); 43 | if (plugin.settings.internalLinkClickFlow) { 44 | extensions.push(internalLinkToggle); 45 | } else { 46 | extensions.push(internalLinkHover(plugin)); 47 | } 48 | } 49 | 50 | 51 | 52 | return extensions; 53 | }; 54 | -------------------------------------------------------------------------------- /src/basics/codemirror/flowStateFields.ts: -------------------------------------------------------------------------------- 1 | import { Annotation, StateField } from "@codemirror/state"; 2 | import { PortalType } from "../flow/PortalType"; 3 | 4 | export const portalTypeAnnotation = Annotation.define(); 5 | export const flowIDAnnotation = Annotation.define(); 6 | export const flowIDStateField = StateField.define({ 7 | create: () => undefined, 8 | update(value, tr) { 9 | if (tr.annotation(flowIDAnnotation)) return tr.annotation(flowIDAnnotation); 10 | return value; 11 | }, 12 | }); 13 | 14 | export const flowTypeStateField = StateField.define({ 15 | create: (state) => "none", 16 | update(value, tr) { 17 | if (tr.annotation(portalTypeAnnotation)) 18 | return tr.annotation(portalTypeAnnotation); 19 | return value; 20 | }, 21 | }); 22 | -------------------------------------------------------------------------------- /src/basics/codemirror/flowViewUpdates.ts: -------------------------------------------------------------------------------- 1 | import { EditorView, ViewUpdate } from "@codemirror/view"; 2 | import MakeBasicsPlugin from "basics/basics"; 3 | import { 4 | flowIDStateField, 5 | flowTypeStateField, 6 | portalTypeAnnotation, 7 | } from "basics/codemirror/flowStateFields"; 8 | import { MarkdownView } from "obsidian"; 9 | import { cacheFlowEditorHeight, flowEditorInfo } from "./flowEditor"; 10 | 11 | //flow view editor viewupdates 12 | 13 | export const flowViewUpdates = (plugin: MakeBasicsPlugin) => EditorView.updateListener.of((v: ViewUpdate) => { 14 | if (v.heightChanged) { 15 | plugin.app.workspace.iterateRootLeaves((leaf) => { 16 | const cm = (leaf.view as MarkdownView).editor?.cm as EditorView; 17 | 18 | if ( 19 | cm && 20 | v.view.dom == cm.dom && 21 | cm.state.field(flowTypeStateField, false) 22 | ) { 23 | if ( 24 | leaf.containerEl.parentElement?.hasClass("workspace-tab-container") 25 | ) { 26 | if (cm.state.field(flowTypeStateField, false) != "doc") { 27 | cm.dispatch({ 28 | annotations: portalTypeAnnotation.of("doc"), 29 | }); 30 | } 31 | } 32 | } 33 | }); 34 | } 35 | if (v.heightChanged) { 36 | const flowID = v.state.field(flowIDStateField, false); 37 | if (flowID) { 38 | plugin.app.workspace.iterateLeaves((leaf) => { 39 | const cm = (leaf.view as MarkdownView).editor?.cm as EditorView; 40 | if (cm) { 41 | const stateField = cm.state.field(flowEditorInfo, false); 42 | if (stateField) { 43 | if (stateField.find((f) => f.id == flowID)) { 44 | cm.dispatch({ 45 | annotations: cacheFlowEditorHeight.of([ 46 | flowID, 47 | v.view.contentHeight, 48 | ]), 49 | }); 50 | } 51 | } 52 | } 53 | }, plugin.app.workspace["rootSplit"]!); 54 | } 55 | } 56 | }); 57 | -------------------------------------------------------------------------------- /src/basics/codemirror/placeholder.ts: -------------------------------------------------------------------------------- 1 | import { RangeSetBuilder, StateField } from "@codemirror/state"; 2 | import { Decoration, DecorationSet, EditorView } from "@codemirror/view"; 3 | import MakeBasicsPlugin from "basics/basics"; 4 | import i18n from "shared/i18n"; 5 | const placeholderLine = (plugin: MakeBasicsPlugin) => Decoration.line({ 6 | attributes: { "data-ph": i18n.labels.placeholder.replace('${1}', plugin.settings.menuTriggerChar) }, 7 | class: "mk-placeholder", 8 | }); 9 | 10 | export const placeholderExtension = (plugin: MakeBasicsPlugin) => StateField.define({ 11 | create() { 12 | return Decoration.none; 13 | }, 14 | update(value, tr) { 15 | const builder = new RangeSetBuilder(); 16 | const currentLine = tr.state.doc.lineAt(tr.state.selection.main.head); 17 | 18 | if (currentLine?.length == 0) 19 | builder.add(currentLine.from, currentLine.from, placeholderLine(plugin)); 20 | const dec = builder.finish(); 21 | return dec; 22 | }, 23 | provide: (f) => EditorView.decorations.from(f), 24 | }); 25 | -------------------------------------------------------------------------------- /src/basics/enactor/enactor.ts: -------------------------------------------------------------------------------- 1 | import { Root } from "react-dom/client"; 2 | import { SelectOption } from "shared/types/menu"; 3 | import { URI } from "shared/types/path"; 4 | import { SpaceFragmentSchema } from "shared/types/spaceFragment"; 5 | 6 | export interface Enactor { 7 | name: string; 8 | load(): void; 9 | convertSpaceFragmentToMarkdown( 10 | spaceFragment: SpaceFragmentSchema, 11 | onReturn: (markdown: string) => void 12 | ): void; 13 | selectLink(e: React.MouseEvent, onSelect: (path: string) => void): void; 14 | selectSpace(e: React.MouseEvent, onSelect: (path: string) => void): void; 15 | pathExists(path: string): Promise; 16 | selectImage(e: React.MouseEvent, onSelect: (path: string) => void): void; 17 | isSpace(path: string): boolean; 18 | loadExtensions(firstLoad: boolean): void; 19 | spaceNotePath(path: string): string | null; 20 | parentPath(path: string): string; 21 | spaceFolderPath(path: string): string; 22 | createNote(parent: string, name: string, content?: string) : Promise; 23 | createRoot(el: Element | DocumentFragment) : Root; 24 | notify(message: string) : void; 25 | uriByString(uri: string, source?: string) : URI; 26 | spaceFragmentSchema(uri: string) : Promise; 27 | resolvePath(path: string, source?: string) : string; 28 | saveSettings() : void; 29 | openMenu(ev: React.MouseEvent, options: SelectOption[]) : void; 30 | openPath(path: string, source?: HTMLElement) : void; 31 | addActiveStateListener(listener: () => void) : void; 32 | removeActiveStateListener(listener: () => void) : void; 33 | } 34 | 35 | -------------------------------------------------------------------------------- /src/basics/flow/PortalType.ts: -------------------------------------------------------------------------------- 1 | export type PortalType = "none" | 2 | "doc" | 3 | "block" | 4 | "foldernote" | 5 | "flow" | 6 | "context"; 7 | -------------------------------------------------------------------------------- /src/basics/flow/flowCommands.ts: -------------------------------------------------------------------------------- 1 | import i18n from "shared/i18n"; 2 | import MakeBasicsPlugin from "../basics"; 3 | 4 | export const loadFlowCommands = (plugin: MakeBasicsPlugin) => { 5 | // this.addCommand({ 6 | // id: 'mk-prev', 7 | // name: "Mod Up", 8 | // callback: () => { 9 | // const cm = getActiveCM(this); 10 | // if (cm) { 11 | // const value = cm.state.field(flowEditorInfo, false); 12 | // const currPosition = cm.state.selection.main; 13 | // const sod = cursorDocStart({state: cm.state, dispatch: cm.dispatch}); 14 | // } 15 | // }, 16 | // hotkeys: [{ 17 | // modifiers: ["Mod"], 18 | // key: "ArrowUp", 19 | // },] 20 | // }) 21 | // this.addCommand({ 22 | // id: 'mk-next', 23 | // name: "Mod Down", 24 | // callback: () => { 25 | // const cm = getActiveCM(this); 26 | // if (cm) { 27 | // const value = cm.state.field(flowEditorInfo, false); 28 | // const currPosition = cm.state.selection.main; 29 | // if (cm.state.selection.main.to == cm.state.doc.length) { 30 | // alert('hello') 31 | // } else { 32 | // cursorDocEnd({state: cm.state, dispatch: cm.dispatch}); 33 | // } 34 | 35 | // } 36 | // }, 37 | // hotkeys: [{ 38 | // modifiers: ["Mod"], 39 | // key: "ArrowDown", 40 | // },] 41 | // }) 42 | 43 | 44 | plugin.plugin.addCommand({ 45 | id: "mk-open-flow", 46 | name: i18n.commandPalette.openFlow, 47 | callback: () => plugin.openFlow(), 48 | }); 49 | 50 | plugin.plugin.addCommand({ 51 | id: "mk-close-flow", 52 | name: i18n.commandPalette.closeFlow, 53 | callback: () => plugin.closeFlow(), 54 | }); 55 | } -------------------------------------------------------------------------------- /src/basics/menus/inlineStylerView/Mark.tsx: -------------------------------------------------------------------------------- 1 | import MakeBasicsPlugin from "basics/basics"; 2 | import React from "react"; 3 | import { uiIconSet } from "shared/assets/icons"; 4 | import i18n from "shared/i18n"; 5 | import { InlineStyle } from "./styles"; 6 | export const Mark = (props: { 7 | plugin: MakeBasicsPlugin; 8 | i: number; 9 | style: InlineStyle; 10 | active: boolean; 11 | toggleMarkAction: (e: React.MouseEvent, s: InlineStyle) => void; 12 | }) => { 13 | const { i, style, active, toggleMarkAction } = props; 14 | 15 | return ( 16 |
)[style.label] 21 | : undefined 22 | } 23 | className={`mk-mark ${style.mark && active ? "mk-mark-active" : ""}`} 24 | dangerouslySetInnerHTML={{ 25 | __html: uiIconSet[`${style.icon}`], 26 | }} 27 | onMouseDown={(e) => toggleMarkAction(e, style)} 28 | >
29 | ); 30 | }; 31 | -------------------------------------------------------------------------------- /src/basics/menus/inlineStylerView/styles/default.ts: -------------------------------------------------------------------------------- 1 | export default [ 2 | { 3 | label: "bold", 4 | value: `****`, 5 | insertOffset: 2, 6 | icon: "mk-mark-strong", 7 | mark: "strong", 8 | }, 9 | { 10 | label: "italics", 11 | value: "**", 12 | insertOffset: 1, 13 | icon: "mk-mark-em", 14 | mark: "em", 15 | }, 16 | { 17 | label: "strikethrough", 18 | value: "~~~~", 19 | insertOffset: 2, 20 | icon: "mk-mark-strikethrough", 21 | mark: "strikethrough", 22 | }, 23 | { 24 | label: "code", 25 | value: "``", 26 | insertOffset: 1, 27 | icon: "mk-mark-code", 28 | mark: "inline-code", 29 | }, 30 | { 31 | label: "link", 32 | value: "[]()", 33 | insertOffset: 1, 34 | cursorOffset: 2, 35 | icon: "mk-mark-link", 36 | }, 37 | 38 | ]; 39 | -------------------------------------------------------------------------------- /src/basics/menus/inlineStylerView/styles/index.ts: -------------------------------------------------------------------------------- 1 | import defaultStyles from "./default"; 2 | 3 | export type InlineStyle = { 4 | label: string; 5 | value: string; 6 | insertOffset: number; 7 | cursorOffset?: number; 8 | icon: string; 9 | mark?: string; 10 | }; 11 | 12 | export function resolveStyles() { 13 | return defaultStyles; 14 | } 15 | -------------------------------------------------------------------------------- /src/basics/menus/obsidianSyntax.ts: -------------------------------------------------------------------------------- 1 | export type oMark = { 2 | mark: string; 3 | formatting: string; 4 | formatChar: string; 5 | altFormatting?: string; 6 | }; 7 | export const oMarks: oMark[] = [ 8 | { 9 | mark: "em", 10 | formatting: "formatting-em", 11 | altFormatting: "em_formatting_formatting-strong", 12 | formatChar: "*", 13 | }, 14 | { 15 | mark: "strong", 16 | formatting: "formatting-strong", 17 | formatChar: "**", 18 | }, 19 | { 20 | mark: "strikethrough", 21 | formatting: "formatting-strikethrough", 22 | formatChar: "~~", 23 | }, 24 | { 25 | mark: "inline-code", 26 | formatting: "formatting-code", 27 | formatChar: "`", 28 | }, 29 | ]; 30 | 31 | export type oBlock = { 32 | block: string; 33 | formatting: string; 34 | blockChar: string; 35 | }; 36 | -------------------------------------------------------------------------------- /src/basics/menus/registerMenus.ts: -------------------------------------------------------------------------------- 1 | import MakeBasicsPlugin from "basics/basics"; 2 | import MakeMenu from "./MakeMenu/MakeMenu"; 3 | import StickerMenu from "./StickerMenu"; 4 | import { loadStylerIntoContainer } from "./inlineStylerView/InlineMenu"; 5 | 6 | export const registerEditorMenus = (plugin: MakeBasicsPlugin) => { 7 | if (plugin.settings.flowMenuEnabled) 8 | { 9 | plugin.plugin.registerEditorSuggest(new MakeMenu(plugin.app, plugin)); 10 | } 11 | if (plugin.settings.inlineStickerMenu) 12 | {plugin.plugin.registerEditorSuggest(new StickerMenu(plugin.app, plugin));} 13 | if (plugin.isTouchScreen() && plugin.settings.mobileMakeBar && plugin.settings.inlineStyler) 14 | loadStylerIntoContainer(plugin.app.mobileToolbar.containerEl, plugin); 15 | } -------------------------------------------------------------------------------- /src/basics/schemas/settings.ts: -------------------------------------------------------------------------------- 1 | import { MakeBasicsSettings } from "basics/types/settings"; 2 | 3 | export const BasicDefaultSettings: MakeBasicsSettings = { 4 | flowMenuEnabled: true, 5 | markSans: false, 6 | makeMenuPlaceholder: true, 7 | mobileMakeBar: false, 8 | mobileSidepanel: false, 9 | inlineStyler: true, 10 | inlineStylerColors: false, 11 | editorFlow: true, 12 | internalLinkClickFlow: false, 13 | internalLinkSticker: false, 14 | editorFlowStyle: "minimal", 15 | menuTriggerChar: "/", 16 | inlineStickerMenu: true, 17 | emojiTriggerChar: ":", 18 | flowState: false, 19 | 20 | }; 21 | -------------------------------------------------------------------------------- /src/basics/types/TransactionRange.ts: -------------------------------------------------------------------------------- 1 | 2 | export type TransactionRange = { 3 | from: number; 4 | to: number; 5 | }; 6 | -------------------------------------------------------------------------------- /src/basics/types/command.ts: -------------------------------------------------------------------------------- 1 | import MakeBasicsPlugin from "basics/basics"; 2 | 3 | import { Editor, TFile } from "obsidian"; 4 | 5 | export enum CommandType { 6 | None, 7 | Command, 8 | Section 9 | } 10 | 11 | export type Command = { 12 | label: string; 13 | value: string; 14 | offset?: [number, number]; 15 | icon: string; 16 | type?: CommandType, 17 | onSelect?: ( 18 | _evt: any, 19 | plugin: MakeBasicsPlugin, 20 | file: TFile, 21 | editor: Editor, 22 | start: { line: number; ch: number }, 23 | startCh: number, 24 | end: { line: number; ch: number }, 25 | onComplete: () => void 26 | ) => void 27 | } 28 | -------------------------------------------------------------------------------- /src/basics/types/settings.ts: -------------------------------------------------------------------------------- 1 | 2 | export type DeleteFileOption = "trash" | "permanent" | "system-trash"; 3 | export type InlineContextLayout = "horizontal" | "vertical"; 4 | 5 | export interface MakeBasicsSettings { 6 | 7 | markSans: boolean; 8 | flowMenuEnabled: boolean; 9 | makeMenuPlaceholder: boolean; 10 | flowState: boolean; 11 | inlineStyler: boolean; 12 | mobileMakeBar: boolean; 13 | mobileSidepanel: boolean; 14 | inlineStylerColors: boolean; 15 | editorFlow: boolean; 16 | internalLinkClickFlow: boolean; 17 | internalLinkSticker: boolean; 18 | editorFlowStyle: string; 19 | menuTriggerChar: string; 20 | inlineStickerMenu: boolean; 21 | emojiTriggerChar: string; 22 | 23 | } 24 | -------------------------------------------------------------------------------- /src/basics/ui/UICollapse.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { uiIconSet } from "shared/assets/icons"; 3 | export const UICollapse = (props: { 4 | collapsed: boolean; 5 | onToggle?: (collapsed: boolean, e: React.MouseEvent) => void; 6 | }) => { 7 | return ( 8 | 19 | ); 20 | }; 21 | -------------------------------------------------------------------------------- /src/basics/utils/utils.ts: -------------------------------------------------------------------------------- 1 | export const compareByField = 2 | (field: string, dir: boolean) => 3 | (_a: Record, _b: Record) => { 4 | const a = dir ? _a : _b; 5 | const b = dir ? _b : _a; 6 | if (a[field] < b[field]) { 7 | return -1; 8 | } 9 | if (a[field] > b[field]) { 10 | return 1; 11 | } 12 | return 0; 13 | }; 14 | -------------------------------------------------------------------------------- /src/core/assets/placeholders.ts: -------------------------------------------------------------------------------- 1 | export const uiPlaceholders : Record = { 2 | "image-select": ` 3 | 4 | 5 | 6 | 7 | 8 | 9 | ` 10 | } -------------------------------------------------------------------------------- /src/core/export/styleAst/treeToStyleAst.ts: -------------------------------------------------------------------------------- 1 | import { Superstate } from "makemd-core"; 2 | import { FrameRunInstance, FrameTreeNode, StyleAst } from "shared/types/frameExec"; 3 | import { FrameTreeProp } from "shared/types/mframe"; 4 | import { getFrameInstanceFromPath } from "../treeHelpers"; 5 | 6 | export const treeNodetoStyleAst = (tree: FrameTreeNode, instance: FrameRunInstance): StyleAst => { 7 | 8 | const walk = (treeNode: FrameTreeNode, instance: FrameRunInstance): StyleAst => { 9 | const children: StyleAst[] = []; 10 | const styles: FrameTreeProp = instance.state[treeNode.id].styles; 11 | const sem: string = instance.state[treeNode.id].styles.sem; 12 | const variant: string = instance.state[treeNode.id].styles.variant; 13 | const node = treeNode.node; 14 | const type = node.type; 15 | 16 | treeNode.children.forEach((child, index) => { 17 | children.push(walk(child, instance)); 18 | }); 19 | return { 20 | sem, 21 | type, 22 | selector: variant, 23 | styles, 24 | children 25 | } 26 | } 27 | return walk(tree, instance); 28 | } 29 | 30 | export const styleFrameToStyleAst = (superstate: Superstate, path: string, schema: string): Promise => { 31 | return getFrameInstanceFromPath(superstate, path, schema, {}, {}).then((instance) => { 32 | return treeNodetoStyleAst(instance.root, instance); 33 | }) 34 | } 35 | -------------------------------------------------------------------------------- /src/core/export/toHtml/mdToHtml.ts: -------------------------------------------------------------------------------- 1 | 2 | import { buildExecutable } from 'core/utils/frames/executable'; 3 | import { executeTreeNode } from 'core/utils/frames/runner'; 4 | import { RootContent } from 'hast'; 5 | import { toHtml } from 'hast-util-to-html'; 6 | import { Superstate } from 'makemd-core'; 7 | import { treeNodeToHast } from '../treeToAst/treeToHast'; 8 | import { mdToTree } from './mdToTree'; 9 | import { HTMLExportOptions } from './spaceToHtml'; 10 | export const markdownToHtml = 11 | async (superstate: Superstate, markdown: string, path: string, options?: HTMLExportOptions) => { 12 | const tree = await mdToTree(superstate, markdown, path); 13 | const exec = await buildExecutable(tree); 14 | const _instance = await executeTreeNode( 15 | exec, 16 | { 17 | prevState: {}, 18 | state: {}, 19 | newState: {}, 20 | slides: {}, 21 | }, 22 | { 23 | api: superstate.api, 24 | contexts: {}, 25 | saveState: () => null, 26 | root: exec, 27 | exec: exec, 28 | runID: '', 29 | selectedSlide: '', 30 | styleAst: options?.styleAst, 31 | } 32 | ); 33 | 34 | const hast = await treeNodeToHast(superstate, tree, _instance, [], path, options); 35 | return toHtml({ 36 | type: 'root', 37 | children: hast as RootContent[], 38 | }); 39 | 40 | }; 41 | -------------------------------------------------------------------------------- /src/core/export/toHtml/wikilink.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'micromark-extension-wiki-link' { 2 | export function syntax(): any; 3 | } -------------------------------------------------------------------------------- /src/core/middleware/filetypes.ts: -------------------------------------------------------------------------------- 1 | import { AFile } from "shared/types/afile"; 2 | import { EventTypeToPayload } from "../../shared/utils/dispatchers/dispatcher"; 3 | import { FilesystemMiddleware } from "./filesystem"; 4 | 5 | export interface FileTypeEventTypes extends EventTypeToPayload { 6 | "onFileMetadataChanged": { file: AFile }, 7 | "onFileFragmentChanged": { file: AFile }, 8 | } 9 | 10 | export interface FileTypeCache { 11 | [fragmentType: string]: any 12 | } 13 | 14 | export interface FileTypeContent { 15 | [fragmentType: string]: any 16 | } 17 | 18 | 19 | export abstract class FileTypeAdapter { 20 | 21 | public supportedFileTypes: string[]; 22 | public id: string; 23 | public initiate: (middleware: FilesystemMiddleware) => void; 24 | public middleware: FilesystemMiddleware; 25 | public loadFile?: (file: AFile) => Promise; 26 | public parseCache: (file: AFile, refresh: boolean) => Promise; 27 | public cache: Map; 28 | public cacheTypes: (file: AFile) => (keyof T)[]; 29 | public contentTypes: (file: AFile) => (keyof C)[]; 30 | 31 | public newFile: (parent: string, name: string, type: string, content?: any) => Promise; 32 | 33 | public getCacheTypeByRefString: (file: AFile, refString: string) => any; 34 | public getCache: (file: AFile, cacheType: keyof T, query?: string) => T[typeof cacheType]; 35 | 36 | public readContent: (file: AFile, contentType: keyof C, contentId: any) => Promise; 37 | public newContent: (file: AFile, contentType: keyof C, name: string, content: C[typeof contentType], options: {[key: string]: any}) => Promise; 38 | public saveContent: (file: AFile, contentType: keyof C, contentId: any, content: (prev: C[typeof contentType]) => any) => Promise; 39 | public deleteContent: (file: AFile, contentType: keyof C, contentId: any) =>void; 40 | 41 | } 42 | 43 | -------------------------------------------------------------------------------- /src/core/react/components/Blink/Blink.tsx: -------------------------------------------------------------------------------- 1 | import { Superstate } from "makemd-core"; 2 | import React from "react"; 3 | import { BlinkMode } from "../../../../shared/types/blink"; 4 | import { BlinkComponent } from "./BlinkComponent"; 5 | 6 | export const openBlinkModal = ( 7 | superstate: Superstate, 8 | mode: BlinkMode, 9 | win: Window, 10 | onSelect?: (link: string) => void, 11 | parentSpace?: string 12 | ) => { 13 | superstate.ui.openPalette( 14 | , 20 | win, 21 | "mk-blink-modal" 22 | ); 23 | }; 24 | -------------------------------------------------------------------------------- /src/core/react/components/MDBView/MDBViewer.tsx: -------------------------------------------------------------------------------- 1 | import { PathProvider } from "core/react/context/PathContext"; 2 | import { Superstate } from "makemd-core"; 3 | import React from "react"; 4 | import { SpaceInfo } from "shared/types/spaceInfo"; 5 | import { ContextEditorProvider } from "../../context/ContextEditorContext"; 6 | import { FramesMDBProvider } from "../../context/FramesMDBContext"; 7 | import { SpaceProvider } from "../../context/SpaceContext"; 8 | import { ContextListContainer } from "../SpaceView/Contexts/ContextListContainer"; 9 | export const MDBViewer = (props: { 10 | superstate: Superstate; 11 | space: SpaceInfo; 12 | schema: string; 13 | }) => { 14 | return ( 15 | 20 | 21 | 22 | 23 | 27 | 28 | 29 | 30 | 31 | ); 32 | }; 33 | -------------------------------------------------------------------------------- /src/core/react/components/Navigator/Navigator.tsx: -------------------------------------------------------------------------------- 1 | import { SidebarProvider } from "core/react/context/SidebarContext"; 2 | import { Superstate } from "makemd-core"; 3 | import React from "react"; 4 | import { MainList } from "./MainList"; 5 | export const Navigator = (props: { superstate: Superstate }) => { 6 | return ( 7 |
8 | 9 | 10 | 11 |
12 | ); 13 | }; 14 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceEditor/Actions/APIPropertyEditor.tsx: -------------------------------------------------------------------------------- 1 | import { Superstate } from "makemd-core"; 2 | import React from "react"; 3 | import { ActionTree } from "shared/types/actions"; 4 | import { Command } from "shared/types/commands"; 5 | import { Metadata } from "shared/types/metadata"; 6 | import { FilterGroupDef } from "shared/types/spaceDef"; 7 | import { SpaceQuery } from "../SpaceQuery"; 8 | export const APIPropertyEditor = (props: { 9 | command: Command; 10 | superstate: Superstate; 11 | actionTree: ActionTree; 12 | saveTree: (actionTree: ActionTree) => void; 13 | fields: Metadata[]; 14 | }) => { 15 | return props.command.schema.id == "filter" ? ( 16 | { 20 | props.saveTree({ 21 | ...props.actionTree, 22 | props: { 23 | ...props.actionTree.props, 24 | $function: filters, 25 | }, 26 | }); 27 | }} 28 | fields={props.fields} 29 | sections={[]} 30 | removeable={true} 31 | > 32 | ) : ( 33 | <> 34 | ); 35 | }; 36 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceEditor/Actions/ActionEditor.tsx: -------------------------------------------------------------------------------- 1 | import { Superstate } from "makemd-core"; 2 | import React from "react"; 3 | import { SpaceProperty } from "shared/types/mdb"; 4 | import { ActionTree } from "../../../../../shared/types/actions"; 5 | import { parseActionString } from "../../../../utils/commands/actions"; 6 | import { ActionNode } from "./ActionNode"; 7 | export const ActionEditor = (props: { 8 | superstate: Superstate; 9 | formula: string; 10 | path: string; 11 | saveFormula: (formula: string) => void; 12 | fields: SpaceProperty[]; 13 | saveOutputType: (outputType: string) => void; 14 | value: { [key: string]: string }; 15 | }) => { 16 | const [actionTree, setActionTree] = React.useState( 17 | parseActionString(props.formula) ?? { 18 | action: "", 19 | props: {}, 20 | propsValue: {}, 21 | children: [], 22 | } 23 | ); 24 | return ( 25 |
26 | { 35 | props.saveFormula(JSON.stringify(tree)); 36 | setActionTree(tree); 37 | }} 38 | > 39 |
40 | ); 41 | }; 42 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceEditor/Actions/ScriptEditor.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | 3 | import { javascript } from "@codemirror/lang-javascript"; 4 | import { githubDark } from "@uiw/codemirror-theme-github"; 5 | import ReactCodeMirror from "@uiw/react-codemirror"; 6 | import { Superstate } from "makemd-core"; 7 | import { Command } from "shared/types/commands"; 8 | import { ActionTester } from "./ActionTester"; 9 | 10 | export const ScriptEditor = (props: { 11 | superstate: Superstate; 12 | command: Command; 13 | saveCommand: (command: Command) => void; 14 | values: { [key: string]: any }; 15 | path: string; 16 | }) => { 17 | const { command } = props; 18 | const value = useMemo(() => { 19 | if (!command) return ""; 20 | return `const ${command.schema.id} = (${command.fields 21 | .map((f) => `${f.name}: ${f.type}`) 22 | .join(", ")}, $api: API, $contexts) => {\n${command.code}\n}`; 23 | }, [command]); 24 | const saveCommand = (value: string) => { 25 | props.saveCommand({ 26 | ...command, 27 | code: value.split("\n").slice(1, -1).join("\n"), 28 | codeType: "script", 29 | }); 30 | }; 31 | 32 | return ( 33 |
34 | 42 | 50 |
51 | ); 52 | }; 53 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/CalendarView/DayView/DayGutter.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | export const DayGutter = (props: { 3 | hourHeight: number; 4 | startHour: number; 5 | endHour: number; 6 | allDay?: boolean; 7 | }) => { 8 | return ( 9 |
10 | {props.allDay &&
all day
} 11 | {Array.from({ length: props.endHour - props.startHour + 1 }).map( 12 | (_, index) => { 13 | const hour = index + props.startHour; 14 | return ( 15 |
16 | {hour % 12 === 0 ? 12 : hour % 12}{" "} 17 | {hour < 12 ? "AM" : "PM"} 18 |
19 | ); 20 | } 21 | )} 22 |
23 | ); 24 | }; 25 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/CalendarView/MonthView/MonthDayCell.tsx: -------------------------------------------------------------------------------- 1 | import { useDraggable, useDroppable } from "@dnd-kit/core"; 2 | import { useCombinedRefs } from "core/react/hooks/useCombinedRef"; 3 | import { Superstate } from "makemd-core"; 4 | import React, { PropsWithChildren } from "react"; 5 | 6 | export const MonthDayCell = ( 7 | props: PropsWithChildren<{ 8 | superstate: Superstate; 9 | active: boolean; 10 | weekStart: Date; 11 | insertItem: (e: React.MouseEvent) => void; 12 | date: Date; 13 | }> 14 | ) => { 15 | const { 16 | attributes, 17 | listeners, 18 | setNodeRef: setDraggableNodeRef, 19 | transform, 20 | } = useDraggable({ 21 | id: "day-" + props.date.getTime(), 22 | data: { 23 | type: "day", 24 | date: props.date.getTime(), 25 | }, 26 | }); 27 | const { setNodeRef: setDroppableNodeRef } = useDroppable({ 28 | id: "day-" + props.date.getTime(), 29 | data: { 30 | type: "day", 31 | weekStart: props.weekStart.getTime(), 32 | date: props.date.getTime(), 33 | }, 34 | }); 35 | const isToday = props.date.toDateString() === new Date().toDateString(); 36 | const setNodeRef = useCombinedRefs(setDroppableNodeRef, setDraggableNodeRef); 37 | 38 | return ( 39 |
{ 47 | if (e.detail == 2) { 48 | props.insertItem(e); 49 | } 50 | }} 51 | style={{ 52 | opacity: "1 !important", 53 | }} 54 | > 55 |
{props.date.getDate()}
56 | {props.children} 57 |
58 | ); 59 | }; 60 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/CalendarView/WeekView/AllDayCell.tsx: -------------------------------------------------------------------------------- 1 | import { useDroppable } from "@dnd-kit/core"; 2 | import { ContextEditorContext } from "core/react/context/ContextEditorContext"; 3 | import { applySat } from "core/utils/color"; 4 | import { Superstate } from "makemd-core"; 5 | import React, { PropsWithChildren, useContext } from "react"; 6 | import { BlinkMode } from "shared/types/blink"; 7 | import { windowFromDocument } from "shared/utils/dom"; 8 | 9 | export const AllDayCell = ( 10 | props: PropsWithChildren<{ 11 | superstate: Superstate; 12 | date: Date; 13 | height: number; 14 | insertItem: (path: string) => void; 15 | }> 16 | ) => { 17 | const { source } = useContext(ContextEditorContext); 18 | const { setNodeRef } = useDroppable({ 19 | id: "allday-" + props.date.toISOString(), 20 | }); 21 | const onClick = (e: React.MouseEvent) => { 22 | e.stopPropagation(); 23 | if (e.detail === 2) { 24 | const rect = e.currentTarget.getBoundingClientRect(); 25 | props.superstate.ui.quickOpen( 26 | BlinkMode.Open, 27 | rect, 28 | windowFromDocument(e.currentTarget.ownerDocument), 29 | (link: string) => { 30 | props.insertItem(link); 31 | }, 32 | source 33 | ); 34 | } 35 | }; 36 | return ( 37 |
50 | {props.children} 51 |
52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/CalendarView/WeekView/AllDayItem.tsx: -------------------------------------------------------------------------------- 1 | import { useDraggable } from "@dnd-kit/core"; 2 | import { PathCrumb } from "core/react/components/UI/Crumbs/PathCrumb"; 3 | import { Superstate } from "makemd-core"; 4 | import React from "react"; 5 | import { PathPropertyName } from "shared/types/context"; 6 | import { DBRow } from "shared/types/mdb"; 7 | 8 | export const AllDayItem = (props: { 9 | superstate: Superstate; 10 | data: DBRow; 11 | index: number; 12 | startDay: number; 13 | endDay: number; 14 | topOffset: number; 15 | style?: React.CSSProperties; 16 | }) => { 17 | const { attributes, listeners, setNodeRef, transform } = useDraggable({ 18 | id: "event-" + props.index, 19 | data: { 20 | type: "event", 21 | index: props.index, 22 | }, 23 | }); 24 | return ( 25 |
37 | 41 |
42 | ); 43 | }; 44 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/ContextBuilder/ContextInfiniteScroll.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from "react"; 2 | export const ContextInfiniteScroll = (props: { onScroll: () => void }) => { 3 | const observerTarget = useRef(null); 4 | 5 | useEffect(() => { 6 | const observer = new IntersectionObserver( 7 | (entries) => { 8 | if (entries[0].isIntersecting) { 9 | props.onScroll(); 10 | } 11 | }, 12 | { threshold: 1 } 13 | ); 14 | 15 | if (observerTarget.current) { 16 | observer.observe(observerTarget.current); 17 | } 18 | 19 | return () => { 20 | if (observerTarget.current) { 21 | observer.unobserve(observerTarget.current); 22 | } 23 | }; 24 | }, [observerTarget]); 25 | return
; 26 | }; 27 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/ContextBuilder/FrameContainerView.tsx: -------------------------------------------------------------------------------- 1 | import { FrameEditorProvider } from "core/react/context/FrameEditorRootContext"; 2 | import { FrameRootProvider } from "core/react/context/FrameRootContext"; 3 | import { FrameSelectionContext } from "core/react/context/FrameSelectionContext"; 4 | import { FramesMDBProvider } from "core/react/context/FramesMDBContext"; 5 | import { Superstate } from "makemd-core"; 6 | import React, { useContext } from "react"; 7 | import { FrameEditorMode } from "shared/types/frameExec"; 8 | import { SpaceProperty } from "shared/types/mdb"; 9 | import { URI } from "shared/types/path"; 10 | 11 | export const FrameContainerView = (props: { 12 | superstate: Superstate; 13 | uri: URI; 14 | cols: SpaceProperty[]; 15 | children?: React.ReactNode; 16 | editMode: FrameEditorMode; 17 | }) => { 18 | const { selected: _selected } = useContext(FrameSelectionContext); 19 | return props.editMode >= FrameEditorMode.Page && 20 | props.uri.authority != "$kit" ? ( 21 | 22 | 27 | {props.children} 28 | 29 | 30 | ) : ( 31 | 36 | {props.children} 37 | 38 | ); 39 | }; 40 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/ContextBuilder/InfiniteScrollTrigger.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from "react"; 2 | 3 | export const InfiniteScrolLTrigger = (props: { loadNextPage: () => void }) => { 4 | const observerTarget = useRef(null); 5 | useEffect(() => { 6 | const observer = new IntersectionObserver( 7 | (entries) => { 8 | if (entries[0].isIntersecting) { 9 | props.loadNextPage(); 10 | } 11 | }, 12 | { threshold: 1 } 13 | ); 14 | 15 | if (observerTarget.current) { 16 | observer.observe(observerTarget.current); 17 | } 18 | 19 | return () => { 20 | if (observerTarget.current) { 21 | observer.unobserve(observerTarget.current); 22 | } 23 | }; 24 | }, [observerTarget]); 25 | return
; 26 | }; 27 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/DataTypeView/BooleanCell.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from "react"; 2 | import { CellEditMode, TableCellProp } from "../TableView/TableView"; 3 | 4 | export const BooleanCell = (props: TableCellProp) => { 5 | const { initialValue, saveValue } = props; 6 | const [value, setValue] = React.useState(initialValue == "true"); 7 | 8 | // When the input is blurred, we'll call our table meta's updateData function 9 | const onChange = () => { 10 | if (props.editMode == CellEditMode.EditModeReadOnly) { 11 | return; 12 | } 13 | setValue(!value); 14 | saveValue(!value ? "true" : "false"); 15 | }; 16 | 17 | useEffect(() => { 18 | if (props.editMode == CellEditMode.EditModeActive) { 19 | setValue(!value); 20 | saveValue(!value ? "true" : "false"); 21 | props.setEditMode(null); 22 | } 23 | }, [props.editMode]); 24 | 25 | // If the initialValue is changed external, sync it up with our state 26 | React.useEffect(() => { 27 | setValue(initialValue == "true"); 28 | }, [initialValue]); 29 | 30 | if (props.editMode < CellEditMode.EditModeView) { 31 | return ( 32 |
33 | 34 |
35 | ); 36 | } 37 | return ( 38 |
39 | 40 |
41 | ); 42 | }; 43 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/DataTypeView/ColorCell.tsx: -------------------------------------------------------------------------------- 1 | import { showColorPickerMenu } from "core/react/components/UI/Menus/properties/colorPickerMenu"; 2 | import React from "react"; 3 | import { windowFromDocument } from "shared/utils/dom"; 4 | import { TableCellProp } from "../TableView/TableView"; 5 | 6 | export const ColorCell = (props: TableCellProp) => { 7 | const showMenu = (e: React.MouseEvent) => { 8 | const handleChangeComplete = (color: string) => { 9 | props.saveValue(color); 10 | }; 11 | const offset = (e.target as HTMLElement).getBoundingClientRect(); 12 | showColorPickerMenu( 13 | props.superstate, 14 | offset, 15 | windowFromDocument(e.view.document), 16 | props.initialValue, 17 | handleChangeComplete 18 | ); 19 | }; 20 | return ( 21 |
22 |
showMenu(e)} 25 | style={{ 26 | backgroundColor: props.initialValue, 27 | width: 30, 28 | height: 30, 29 | }} 30 | >
31 |
32 | ); 33 | }; 34 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/DataTypeView/IconCell.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useMemo, useRef } from "react"; 2 | import StickerModal from "shared/components/StickerModal"; 3 | import i18n from "shared/i18n"; 4 | import { windowFromDocument } from "shared/utils/dom"; 5 | import { parseMultiString } from "utils/parsers"; 6 | import { CellEditMode, TableCellMultiProp } from "../TableView/TableView"; 7 | 8 | export const IconCell = (props: TableCellMultiProp) => { 9 | const value = useMemo( 10 | () => 11 | props.multi 12 | ? parseMultiString(props.initialValue) ?? [] 13 | : [props.initialValue], 14 | [props.initialValue] 15 | ); 16 | 17 | const ref = useRef(null); 18 | 19 | useEffect(() => { 20 | if (props.editMode == CellEditMode.EditModeActive) { 21 | ref?.current?.focus(); 22 | } 23 | }, [props.editMode]); 24 | 25 | const triggerStickerMenu = (e: React.MouseEvent) => { 26 | props.superstate.ui.openPalette( 27 | props.saveValue(emoji)} 30 | />, 31 | windowFromDocument(e.view.document) 32 | ); 33 | }; 34 | 35 | return ( 36 |
37 | {value.map((v, i) => 38 | v?.length > 0 ? ( 39 |
triggerStickerMenu(e)} 47 | >
48 | ) : ( 49 |
triggerStickerMenu(e)} 53 | > 54 | {i18n.labels.selectIcon} 55 |
56 | ) 57 | )} 58 |
59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/DataTypeView/NumberCell.tsx: -------------------------------------------------------------------------------- 1 | import { format as formatNumber } from "numfmt"; 2 | import React, { useEffect, useMemo, useRef } from "react"; 3 | import { safelyParseJSON } from "shared/utils/json"; 4 | import { CellEditMode, TableCellProp } from "../TableView/TableView"; 5 | 6 | export const NumberCell = (props: TableCellProp) => { 7 | const { initialValue, saveValue } = props; 8 | const [value, setValue] = React.useState(initialValue); 9 | 10 | const ref = useRef(null); 11 | // When the input is blurred, we'll call our table meta's updateData function 12 | const onBlur = () => { 13 | if (initialValue != value) saveValue(value); 14 | }; 15 | 16 | const onKeyDown = (e: React.KeyboardEvent) => { 17 | e.stopPropagation(); 18 | if (e.key == "Enter") { 19 | (e.target as HTMLInputElement).blur(); 20 | props.setEditMode(null); 21 | } 22 | if (e.key == "Escape") { 23 | setValue(initialValue); 24 | (e.target as HTMLInputElement).blur(); 25 | props.setEditMode(null); 26 | } 27 | }; 28 | 29 | // If the initialValue is changed external, sync it up with our state 30 | React.useEffect(() => { 31 | setValue(initialValue); 32 | }, [initialValue]); 33 | 34 | useEffect(() => { 35 | if (props.editMode == CellEditMode.EditModeActive) { 36 | ref?.current?.focus(); 37 | } 38 | }, [props.editMode]); 39 | const format = useMemo( 40 | () => safelyParseJSON(props.propertyValue)?.format, 41 | [props.propertyValue] 42 | ); 43 | return props.editMode > CellEditMode.EditModeView ? ( 44 | setValue(e.target.value)} 50 | onKeyDown={onKeyDown} 51 | onBlur={onBlur} 52 | /> 53 | ) : ( 54 |
55 | {format?.length > 0 && value 56 | ? formatNumber(format, parseFloat(value)) 57 | : value ?? ""} 58 |
59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/DataTypeView/PropertySelectCell.tsx: -------------------------------------------------------------------------------- 1 | import { parseLinkedPropertyToValue } from "core/utils/frames/frame"; 2 | import { SelectMenuProps } from "makemd-core"; 3 | import React, { useState } from "react"; 4 | import i18n from "shared/i18n"; 5 | import { SpaceTableColumn } from "shared/types/mdb"; 6 | import { TableCellProp } from "../TableView/TableView"; 7 | import { OptionCellBase } from "./OptionCell"; 8 | 9 | export const PropertySelectCell = ( 10 | props: TableCellProp & { columns: SpaceTableColumn[] } 11 | ) => { 12 | const [value, setValue] = useState( 13 | parseLinkedPropertyToValue(props.initialValue) 14 | ); 15 | 16 | const saveOptions = (_: string[], _value: string[]) => { 17 | setValue(parseLinkedPropertyToValue(_value[0])); 18 | props.saveValue(_value[0]); 19 | }; 20 | const menuProps = (): SelectMenuProps => { 21 | const options = (props.columns ?? []).map((f) => ({ 22 | name: f.name, 23 | description: f.table, 24 | value: 25 | f.table == "" 26 | ? `$root['props']['${f.name}']` 27 | : `$contexts['${f.table}']['${f.name}']`, 28 | })); 29 | 30 | return { 31 | ui: props.superstate.ui, 32 | multi: false, 33 | editable: true, 34 | value: value ? [value] : [], 35 | options: options, 36 | saveOptions, 37 | placeholder: i18n.labels.linkItemSelectPlaceholder, 38 | detail: true, 39 | searchable: true, 40 | // onHide: () => props.setEditMode(null), 41 | }; 42 | }; 43 | return ( 44 |
{_props.value}
} 51 | value={value ? [value] : []} 52 | multi={false} 53 | editMode={props.editMode} 54 | >
55 | ); 56 | }; 57 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/DataTypeView/TextCell.tsx: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from "react"; 2 | import { CellEditMode, TableCellProp } from "../TableView/TableView"; 3 | 4 | export const TextCell = (props: TableCellProp) => { 5 | const { initialValue, saveValue } = props; 6 | 7 | const ref = useRef(null); 8 | // When the input is blurred, we'll call our table meta's updateData function 9 | const onBlur = (e: React.FocusEvent) => { 10 | const value = e.currentTarget.innerText; 11 | if (initialValue != value) saveValue(value); 12 | }; 13 | 14 | const onKeyDown = (e: React.KeyboardEvent) => { 15 | e.stopPropagation(); 16 | if (e.key == "Enter") { 17 | if (!e.shiftKey) { 18 | (e.target as HTMLInputElement).blur(); 19 | props.setEditMode(null); 20 | } 21 | } 22 | if (e.key == "Escape") { 23 | ref.current.innerText = initialValue; 24 | (e.target as HTMLInputElement).blur(); 25 | props.setEditMode(null); 26 | } 27 | }; 28 | 29 | useEffect(() => { 30 | if (props.editMode == CellEditMode.EditModeActive) { 31 | if (ref?.current) { 32 | const sel = window.getSelection(); 33 | sel.selectAllChildren(ref.current); 34 | sel.collapseToEnd(); 35 | } 36 | } 37 | }, [props.editMode]); 38 | 39 | return props.editMode > CellEditMode.EditModeView ? ( 40 |
e.stopPropagation()} 42 | className="mk-cell-text" 43 | ref={ref} 44 | data-ph={props.compactMode ? props.property.name : "Empty"} 45 | onKeyDown={onKeyDown} 46 | onBlur={onBlur} 47 | contentEditable={true} 48 | dangerouslySetInnerHTML={{ __html: initialValue }} 49 | /> 50 | ) : ( 51 |
{initialValue}
52 | ); 53 | }; 54 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/FilterBar/CustomVIewOption.tsx: -------------------------------------------------------------------------------- 1 | import { Sticker } from "core/react/components/UI/Stickers/Sticker"; 2 | import { UIManager } from "makemd-core"; 3 | import React from "react"; 4 | export const CustomViewOption = (props: { 5 | ui: UIManager; 6 | type: string; 7 | icon: string; 8 | selected: boolean; 9 | onSelect: () => void; 10 | onCustomize?: () => void; 11 | hide: () => void; 12 | }) => { 13 | return ( 14 |
{ 17 | props.onSelect(); 18 | props.hide(); 19 | }} 20 | > 21 | 22 |
23 | {props.type} 24 |
25 | {props.selected && ( 26 |
31 | )} 32 | {props.onCustomize && } 44 |
45 | ); 46 | }; 47 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Contexts/FilterBar/SearchBar.tsx: -------------------------------------------------------------------------------- 1 | import { Superstate } from "makemd-core"; 2 | import React, { useEffect } from "react"; 3 | import i18n from "shared/i18n"; 4 | 5 | export const SearchBar = (props: { 6 | superstate: Superstate; 7 | setSearchString: (str: string) => void; 8 | closeSearch?: () => void; 9 | }) => { 10 | const [searchActive, setSearchActive] = React.useState(false); 11 | const clearSearch = () => { 12 | setSearchActive(false); 13 | props.setSearchString(""); 14 | }; 15 | const ref = React.useRef(null); 16 | useEffect(() => { 17 | if (searchActive) { 18 | ref.current?.focus(); 19 | } 20 | }, [searchActive]); 21 | return ( 22 |
23 | 29 | <> 30 | props.setSearchString(e.target.value)} 32 | placeholder={i18n.labels.searchPlaceholder} 33 | className="mk-search-bar" 34 | ref={ref} 35 | > 36 | {props.closeSearch && ( 37 | 48 | )} 49 | 50 |
51 | ); 52 | }; 53 | -------------------------------------------------------------------------------- /src/core/react/components/SpaceView/Frames/EditorNodes/AudioNodeView.tsx: -------------------------------------------------------------------------------- 1 | import React, { useMemo } from "react"; 2 | import { FrameNodeViewProps } from "../ViewNodes/FrameView"; 3 | 4 | export const AudioNodeView = (props: FrameNodeViewProps) => { 5 | const value = props.state.props.value; 6 | const sourcePath = useMemo(() => { 7 | return props.superstate.ui.getUIPath(value); 8 | }, [value]); 9 | 10 | return props.state?.props.value?.length > 0 ? ( 11 |