Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
233100c
create a `useAsync` hook and a companion `Suspend` component
GamerGirlandCo Apr 22, 2025
a47564a
add `loader` parameter to `useEffect` dependencies
GamerGirlandCo Apr 27, 2025
3e435f7
make `Suspend` fallback prettier
GamerGirlandCo Apr 27, 2025
7f533ed
update `useAsync` to be more resilient to race conditions
GamerGirlandCo May 10, 2025
8225887
rewrite `useAsync` to use suspense boundaries properly
GamerGirlandCo Jun 29, 2025
e4d81d5
expose `SETTINGS_CONTEXT`, `COMPONENT_CONTEXT`, `DATACORE_CONTEXT` an…
GamerGirlandCo Feb 17, 2025
b93b13d
add ability to create views for queries that are treated like any oth…
GamerGirlandCo Apr 19, 2025
cd06d0d
upgrade react-select to silence typescript errors
GamerGirlandCo May 8, 2025
f8f59f4
re-add vim codemirror plugin
GamerGirlandCo May 8, 2025
76b846f
re-add missing autocomplete and javascript language codemirror plugins
GamerGirlandCo May 8, 2025
a9ddc69
add more dev deps to appease typescript
GamerGirlandCo May 8, 2025
2feb14f
bring `@codemirror/view` up to date
GamerGirlandCo May 8, 2025
1bd8301
silence more bogus errors
GamerGirlandCo May 10, 2025
565d5a2
move select augmentation to proper folder, re-export some things
GamerGirlandCo May 10, 2025
4f809c9
re-add `src/ui/fields` folder (for now?)
GamerGirlandCo Aug 4, 2025
0204e8a
re-add missing autocomplete and javascript language codemirror plugins
GamerGirlandCo May 8, 2025
7a0df49
add more dev deps to appease typescript
GamerGirlandCo May 8, 2025
91e80bd
bring `@codemirror/view` up to date
GamerGirlandCo May 8, 2025
69b264b
silence more bogus errors
GamerGirlandCo May 10, 2025
93673c9
move select augmentation to proper folder, re-export some things
GamerGirlandCo May 10, 2025
7eb13e3
add "controlled" editable component
GamerGirlandCo Apr 19, 2025
a945be1
add controlled editable component to local api
GamerGirlandCo Apr 21, 2025
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
30 changes: 27 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 All @@ -28,6 +28,7 @@ import { ScriptCache } from "./script-cache";
import { Expression } from "expression/expression";
import { Card } from "./ui/views/cards";
import { ListView } from "./ui/views/list";
import { ControlledEditable } from "ui/fields/editable";

/**
* Local API provided to specific codeblocks when they are executing.
Expand Down Expand Up @@ -184,6 +185,25 @@ export class DatacoreLocalApi {
/** Execute a textual or typed index query, returning results plus performance metadata. */
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 {
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 @@ -214,6 +234,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 @@ -267,6 +288,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 Expand Up @@ -395,6 +418,7 @@ export class DatacoreLocalApi {
// Interative elements //
/////////////////////////

public ControlledEditable = ControlledEditable;
public Button = Button;
public Textbox = Textbox;
public Callout = Callout;
Expand Down
16 changes: 16 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 @@ -47,6 +48,21 @@ export default class DatacorePlugin extends Plugin {
-100
);

// Views: DatacoreJS view.
// @ts-ignore be quiet
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 });
},
});

// Register JS highlighting for codeblocks.
this.register(this.registerCodeblockHighlighting());

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;
}
32 changes: 32 additions & 0 deletions src/ui/fields/boolean-field.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* @module ui
*/
import { useEditableDispatch } from "ui/fields/editable";
import { FieldControlProps } from "./common-props";
import { TargetedEvent } from "preact/compat";
import { useStableCallback } from "ui/hooks";

/** Editable field for a boolean (true/false) value.
* @group Editable Components
*/
export function BooleanEditable(props: FieldControlProps<boolean>) {
const [state, dispatch] = useEditableDispatch<boolean>({
content: props.value ?? props.defaultValue,
updater: props.updater!,
});

const onChange = useStableCallback(
(evt: TargetedEvent<HTMLInputElement> & MouseEvent) => {
let newValue = !evt.currentTarget.hasClass("is-enabled");
dispatch({ type: "content-changed", newValue });
dispatch({ type: "commit", newValue });
},
[state.content, state, props.value]
);

return (
<div onClick={onChange} className={`checkbox-container${state.content ? " is-enabled" : ""}`}>
<input type="checkbox" />
</div>
);
}
29 changes: 29 additions & 0 deletions src/ui/fields/common-props.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/**
* @module ui
*/
import { LiteralType } from "expression/literal";
import { InlineField } from "index/import/inline-field";

/** Props for any kind of editable field.
* @group Props
* @typeParam T - the type of field being edited
*/
export interface FieldControlProps<T> extends BaseFieldProps<T> {
field: InlineField;
value: T;
file: string;
updater?: (val: T) => unknown;
}

/**
* @group Props
*
*/

export interface BaseFieldProps<T> {
type: LiteralType;
defaultValue?: T | (() => T);
renderAs?: "progress" | "rating" | "select" | "raw";
// TODO: type this better
config?: Record<string, any>;
}
Loading