Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,12 @@
"author": "Michael Brenan",
"license": "MIT",
"devDependencies": {
"@codemirror/autocomplete": "^6.18.6",
"@codemirror/commands": "^6.8.1",
"@codemirror/language": "https://github.com/lishid/cm-language",
"@codemirror/search": "^6.5.10",
"@codemirror/state": "^6.0.1",
"@codemirror/view": "^6.0.1",
"@codemirror/view": "^6.36.7",
"@microsoft/api-extractor": "^7.52.7",
"@types/jest": "^27.0.1",
"@types/luxon": "^2.3.2",
Expand All @@ -45,17 +48,19 @@
"typescript": "^5.4.2"
},
"dependencies": {
"@codemirror/lang-javascript": "^6.2.3",
"@datastructures-js/queue": "^4.2.3",
"@fortawesome/fontawesome-svg-core": "^6.4.0",
"@fortawesome/free-solid-svg-icons": "^6.4.0",
"@fortawesome/react-fontawesome": "^0.2.0",
"@replit/codemirror-vim": "^6.3.0",
"emoji-regex": "^10.2.1",
"flatqueue": "^2.0.3",
"localforage": "1.10.0",
"luxon": "^2.4.0",
"parsimmon": "^1.18.0",
"preact": "^10.17.1",
"react-select": "^5.8.0",
"preact": "^10.26.6",
"react-select": "^5.10.1",
"sorted-btree": "^1.8.1",
"sucrase": "3.35.0",
"yaml": "^2.3.3"
Expand Down
28 changes: 25 additions & 3 deletions src/api/local-api.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import { IndexQuery } from "index/types/index-query";
import { Indexable } from "index/types/indexable";
import { MarkdownPage } from "index/types/markdown";
import { App } from "obsidian";
import { useFileMetadata, useFullQuery, useIndexUpdates, useInterning, useQuery } from "ui/hooks";
import { useAsync, useFileMetadata, useFullQuery, useIndexUpdates, useInterning, useQuery } from "ui/hooks";
import * as luxon from "luxon";
import * as preact from "preact";
import * as hooks from "preact/hooks";
import { Result } from "./result";
import { Group, Stack } from "./ui/layout";
import { Embed, LineSpanEmbed } from "api/ui/embed";
import { CURRENT_FILE_CONTEXT, ErrorMessage, Lit, Markdown, ObsidianLink } from "ui/markdown";
import { CSSProperties } from "preact/compat";
import { APP_CONTEXT, COMPONENT_CONTEXT, CURRENT_FILE_CONTEXT, DATACORE_CONTEXT, ErrorMessage, Lit, Markdown, ObsidianLink, SETTINGS_CONTEXT } from "ui/markdown";
import { CSSProperties, Suspense } from "preact/compat";
import { Literal, Literals } from "expression/literal";
import { Button, Checkbox, Icon, Slider, Switch, Textbox, VanillaSelect } from "./ui/basics";
import { TableView } from "./ui/views/table";
Expand Down Expand Up @@ -189,6 +189,25 @@ export class DatacoreLocalApi {
public tryFullQuery<T extends Indexable = Indexable>(query: string | IndexQuery): Result<SearchResult<T>, string>;
public tryFullQuery(query: string | IndexQuery): Result<SearchResult<Indexable>, string> {
return this.api.tryFullQuery(query);
}
//////////////
// Contexts //
//////////////

// export the necessary contexts to enable rendering
// datacore components outside the datacore plugin
// itself
get SETTINGS_CONTEXT(): typeof SETTINGS_CONTEXT {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think I'd do it like this - it's better to expose hook functions which load this data instead (like dc.useSettings()).

return SETTINGS_CONTEXT;
}
get COMPONENT_CONTEXT(): typeof COMPONENT_CONTEXT {
return COMPONENT_CONTEXT;
}
get DATACORE_CONTEXT(): typeof DATACORE_CONTEXT {
return DATACORE_CONTEXT;
}
get APP_CONTEXT(): typeof APP_CONTEXT {
return APP_CONTEXT;
}

/////////////
Expand Down Expand Up @@ -219,6 +238,7 @@ export class DatacoreLocalApi {
* React's reference-equality-based caching.
*/
public useInterning = useInterning;
public useAsync = useAsync;

/** Memoize the input automatically and process it using a DataArray; returns a vanilla array back. */
public useArray<T, U>(
Expand Down Expand Up @@ -279,6 +299,8 @@ export class DatacoreLocalApi {
/** Horizontal flexbox container; good for putting items together in a row. */
public Group = Group;

public Suspense = Suspense;

/** Renders a literal value in a pretty way that respects settings. */
public Literal = (({ value, sourcePath, inline }: { value: Literal; sourcePath?: string; inline?: boolean }) => {
const implicitSourcePath = hooks.useContext(CURRENT_FILE_CONTEXT);
Expand Down
15 changes: 15 additions & 0 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Datacore } from "index/datacore";
import { DateTime } from "luxon";
import { App, Plugin, PluginSettingTab, Setting } from "obsidian";
import { DEFAULT_SETTINGS, Settings } from "settings";
import { DatacoreQueryView as DatacoreJSView, VIEW_TYPE_DATACOREJS } from "ui/view-page";

/** @internal Reactive data engine for your Obsidian.md vault. */
export default class DatacorePlugin extends Plugin {
Expand Down Expand Up @@ -55,6 +56,20 @@ export default class DatacorePlugin extends Plugin {
callback: async () => {
console.log("Datacore: dropping the datastore and reindexing all items.");
await this.core.reindex();
},
});
// Views: DatacoreJS view.
// @ts-ignore be quiet
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why can this not be typed?

this.registerView(VIEW_TYPE_DATACOREJS, (leaf) => new DatacoreJSView(leaf, this.api));

// Add a command for creating a new view page.
this.addCommand({
id: "datacore-add-view-page",
name: "Create View Page",
callback: () => {
const newLeaf = this.app.workspace.getLeaf("tab");
newLeaf.setViewState({ type: VIEW_TYPE_DATACOREJS, active: true });
this.app.workspace.setActiveLeaf(newLeaf, { focus: true });
},
});

Expand Down
62 changes: 58 additions & 4 deletions src/typings/obsidian-ex.d.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,49 @@
import type { DatacorePlugin } from "main";
import type { CanvasMetadataIndex } from "index/types/json/canvas";

import { Extension } from "@codemirror/state";
import type { DatacoreApi } from "api/api";
import { CanvasMetadataIndex } from "index/types/json/canvas";
import "obsidian";
import { App } from "obsidian";
import * as hooks from "preact/hooks";

/** Provides extensions used by datacore or provider to other plugins via datacore. */
declare module "obsidian" {
interface WorkspaceLeaf {
serialize(): {
id: string;
type: "leaf";
state: {
type: string;
state: any;
};
};
tabHeaderEl: HTMLElement;
tabHeaderInnerTitleEl: HTMLElement;
}
interface View {
getState(): any;
}
interface ItemView {
titleEl: HTMLElement;
getState(): any;
}

interface InternalPlugin<T> {
id: string;
name: string;
description: string;
instance: T;
}
export interface PagePreviewPlugin {
onLinkHover: (
view: View,
hovered: HTMLElement,
hoveredPath: string,
sourcePath: string,
_unknown: unknown
) => void;
}

interface FileManager {
linkUpdaters: {
canvas: {
Expand All @@ -16,16 +55,26 @@ declare module "obsidian" {
};
};
}

interface Vault {
getConfig: (conf: string) => any;
}
interface App {
appId?: string;

plugins: {
enabledPlugins: Set<string>;
plugins: {
datacore?: DatacorePlugin;
datacore?: {
api: DatacoreApi;
};
"datacore-addon-autocomplete"?: {
readonly extensions: Extension[];
};
};
};
internalPlugins: {
getPluginById: <T>(id: string) => InternalPlugin<T>;
};

embedRegistry: {
embedByExtension: {
Expand Down Expand Up @@ -54,5 +103,10 @@ declare module "obsidian" {
declare global {
interface Window {
datacore?: DatacoreApi;
app: App;
CodeMirror: {
defineMode: (mode: string, conf: (config: any) => any) => unknown;
[key: string]: any;
};
}
}
20 changes: 20 additions & 0 deletions src/typings/select.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@

declare module "react-select" {
import { RefAttributes, ReactElement, JSX } from "preact/compat";
import { StateManagerAdditionalProps } from "react-select/dist/declarations/src/useStateManager";
import { Props } from "react-select/dist/declarations/src/Select";
import Select from "react-select/dist/declarations/src/Select";
export * from "react-select/dist/declarations/src/types";
declare type StateManagedPropKeys = 'inputValue' | 'menuIsOpen' | 'onChange' | 'onInputChange' | 'onMenuClose' | 'onMenuOpen' | 'value';
declare type PublicBaseSelectProps<Option, IsMulti extends boolean, Group extends GroupBase<Option>> = JSX.LibraryManagedAttributes<typeof Select, Props<Option, IsMulti, Group>>;
declare type SelectPropsWithOptionalStateManagedProps<Option, IsMulti extends boolean, Group extends GroupBase<Option>> = Omit<PublicBaseSelectProps<Option, IsMulti, Group>, StateManagedPropKeys> & Partial<PublicBaseSelectProps<Option, IsMulti, Group>>;
export declare type StateManagerProps<Option = unknown, IsMulti extends boolean = boolean, Group extends GroupBase<Option> = GroupBase<Option>> = SelectPropsWithOptionalStateManagedProps<Option, IsMulti, Group> & StateManagerAdditionalProps<Option>;
declare const StateManagedSelect: <
Option = unknown,
IsMulti extends boolean = false,
Group extends GroupBase<Option> = GroupBase<Option>
>(
props: StateManagerProps<Option, IsMulti, Group>
) => ReactElement;
export default StateManagedSelect;
}
67 changes: 67 additions & 0 deletions src/ui/hooks.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -214,3 +214,70 @@ export function useAsElement(element: ReactNode | Literal): ReactNode {
}
}, [element]);
}

type FulfilledPromise<T> = {
status: "success";
value: T;
};
type PendingPromise<T> = {
status: "pending";
};
type RejectedPromise<T> = {
status: "error";
reason: any;
};

type AsyncResult<T> = FulfilledPromise<T> | PendingPromise<T> | RejectedPromise<T>;

/**
* a simple hook that leverages `useEffect` and `useState` to
* return some async data and its fulfillment status.
*
* @group Hooks
* @param loader a parameterless function that returns a promise
* @param deps optional deps to pass to useEffect
* @returns a tuple in the form of [resolvedPromise, hasResolved, hasError]
*/
export function useAsync<T>(loader: () => Promise<T>, deps: any[] = []): T {
const [state, set] = useState<AsyncResult<T>>({
status: "pending"
});
useEffect(() => {
set({
status: "pending"
});
let suspender = loader().then(
(v) => {
set({
status: "success",
value: v,
});
},
(e) => {
set({
status: "error",
reason: e,
});
}
);
if(state.status == "pending") throw suspender;
if(state.status == "error") throw state.reason;
/* if (state.done) {
set((prevState) => ({ ...prevState, done: false }));
}

loader().then(
(value) => {
cid === callId.current && set({ value, done: true, error: false });

return value;
},
(error) => {
cid === callId.current && set({ value: undefined!, done: true, error: true });

return error;
}
); */
}, deps);
return (state as FulfilledPromise<T>).value;
}
24 changes: 24 additions & 0 deletions src/ui/view-page.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
.dc-cm-editor .cm-gutters {
flex: 0 0 auto;
background-color: transparent;
color: var(--text-faint) !important;
border-right: none !important;
margin-inline-end: var(--file-folding-offset);
font-size: var(--font-ui-smaller);
z-index: 1;
font-variant: tabular-nums;
}
.cm-typeName {
color: var(--headers);
}
.dc-cm-editor {
padding: 1em;
border-radius: 0.5em;
border: 1px solid var(--h5-color);
}
.cm-tooltip.cm-tooltip-autocomplete {
background: var(--embed-bg);
}
.dc-cm-editor img.cm-widgetBuffer[aria-hidden="true"] {
display: none;
}
Loading