From 3470cd5a682ffa1945bae6a7053bff7e19ebf235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E2=98=99=E2=97=A6=20The=20Tablet=20=E2=9D=80=20GamerGirla?= =?UTF-8?q?ndCo=20=E2=97=A6=E2=9D=A7?= Date: Sun, 11 Aug 2024 16:07:44 -0400 Subject: [PATCH 01/42] add ability to create views for queries that are treated like any other file view (e.g., they can be dragged into the sidebar) --- src/main.ts | 13 ++ src/typings/obsidian-ex.d.ts | 15 +++ src/ui/datacore-view-page.tsx | 241 ++++++++++++++++++++++++++++++++++ 3 files changed, 269 insertions(+) create mode 100644 src/ui/datacore-view-page.tsx diff --git a/src/main.ts b/src/main.ts index 936aabb2..0689a51c 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,6 +4,7 @@ import { DateTime } from "luxon"; import { App, Plugin, PluginSettingTab, Setting } from "obsidian"; import { createElement, render } from "preact"; import { DEFAULT_SETTINGS, Settings } from "settings"; +import { DatacoreQueryView, VIEW_TYPE_DATACORE } from "ui/datacore-view-page"; import { IndexStatusBar } from "ui/index-status"; /** Reactive data engine for your Obsidian.md vault. */ @@ -51,6 +52,7 @@ export default class DatacorePlugin extends Plugin { async (source: string, el, ctx) => this.api.executeTsx(source, el, ctx, ctx.sourcePath), -100 ); + this.registerView(VIEW_TYPE_DATACORE, (leaf) => new DatacoreQueryView(leaf, this.api, this)); // Register JS highlighting for codeblocks. this.register(this.registerCodeblockHighlighting()); @@ -62,6 +64,17 @@ export default class DatacorePlugin extends Plugin { this.core.initialize(); } + this.addCommand({ + id: "datacore-add-view-page", + name: "Create View Page", + callback: () => { + const newLeaf = this.app.workspace.getLeaf("tab"); + newLeaf.setViewState({type: VIEW_TYPE_DATACORE, active: true}); + this.app.workspace.setActiveLeaf(newLeaf, {focus: true}); + (newLeaf.view as DatacoreQueryView).toggleSettings(true); + }, + }); + // Make the API globally accessible from any context. window.datacore = this.api; diff --git a/src/typings/obsidian-ex.d.ts b/src/typings/obsidian-ex.d.ts index 80ce8d32..3e439da6 100644 --- a/src/typings/obsidian-ex.d.ts +++ b/src/typings/obsidian-ex.d.ts @@ -4,6 +4,21 @@ import "obsidian"; /** @hidden */ declare module "obsidian" { + interface WorkspaceLeaf { + serialize(): { + id: string; + type: "leaf", + state: { + type: string; + state: any; + } + } + tabHeaderEl: HTMLElement; + tabHeaderInnerTitleEl: HTMLElement; + } + interface ItemView { + titleEl: HTMLElement; + } interface FileManager { linkUpdaters: { canvas: { diff --git a/src/ui/datacore-view-page.tsx b/src/ui/datacore-view-page.tsx new file mode 100644 index 00000000..cdcb0911 --- /dev/null +++ b/src/ui/datacore-view-page.tsx @@ -0,0 +1,241 @@ +import { debounce, ItemView, Menu, ViewStateResult, WorkspaceLeaf } from "obsidian"; +import { ScriptLanguage } from "utils/javascript"; +import { DatacoreJSRenderer, ReactRenderer } from "./javascript"; +import { DatacoreLocalApi } from "api/local-api"; +import { DatacoreApi } from "api/api"; +import { createContext } from "preact"; +import { Group, Stack } from "api/ui/layout"; +import { useCallback, useContext, useEffect, useState } from "preact/hooks"; +import { Textbox, VanillaSelect } from "api/ui/basics"; +import { useIndexUpdates, useStableCallback } from "./hooks"; +import DatacorePlugin from "main"; +import { DATACORE_CONTEXT } from "./markdown"; + +export const VIEW_TYPE_DATACORE = "datacore-query-view"; + +const CUSTOM_VIEW_CONTEXT = createContext(undefined!); + +const backButton = ( + + + + +); + +function useVaultFiles() { + const core = useContext(DATACORE_CONTEXT); + const { vault } = core; + const [fileList, setFileList] = useState([]); + let revision = useIndexUpdates(core); + useEffect(() => { + setFileList(vault.getFiles().map((f) => f.path)); + }, [revision, setFileList]); + return fileList; +} + +function useViewState(): [T, (prop: keyof T, value: any) => void, () => void] { + const view = useContext(CUSTOM_VIEW_CONTEXT) as ItemView; + const [state, setter] = useState(view.getEphemeralState() as T); + const stateSaver = useCallback(() => { + view.setState(state, { history: false }); + }, [state, setter]); + const setState = (prop: keyof T, value: any) => { + setter((v) => ({ ...v, [prop]: value })); + }; + return [state, setState, stateSaver]; +} + +function DatacoreViewSettings() { + const [internalState, setInternalState, saveState] = useViewState(); + const options = [ + { + label: "Javascript", + value: "js", + }, + { + label: "Typescript", + value: "ts", + }, + { + label: "Javascript (JSX)", + value: "jsx", + }, + { + label: "Typescript JSX", + value: "tsx", + }, + ]; + useEffect(() => { + debounce(saveState, 250, true)(); + }, [internalState]); + const view = useContext(CUSTOM_VIEW_CONTEXT); + const files = useVaultFiles(); + return ( + + + +
View Title
+ setInternalState("title", e.currentTarget.value as string)} + /> +
+ +
View Type
+ setInternalState("sourceType", s)} + /> +
+ +
Script/View source
+