generated from obsidianmd/obsidian-sample-plugin
-
-
Notifications
You must be signed in to change notification settings - Fork 30
/
ObsidianAPI.ts
152 lines (135 loc) · 5.36 KB
/
ObsidianAPI.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
import { Component } from 'obsidian';
import type MetaBindPlugin from 'packages/obsidian/src/main';
import { API, type LifecycleHook } from 'packages/core/src/api/API.js';
import { type BindTargetDeclaration } from 'packages/core/src/parsers/bindTargetParser/BindTargetDeclaration';
import { getUUID } from 'packages/core/src/utils/Utils';
import { validateAPIArgs } from 'packages/core/src/utils/ZodUtils';
import { ErrorLevel, MetaBindInternalError } from 'packages/core/src/utils/errors/MetaBindErrors';
import { MarkdownRenderChildWidget } from 'packages/obsidian/src/cm6/Cm6_Widgets';
import { MountableMDRC } from 'packages/obsidian/src/MountableMDRC';
import { z } from 'zod';
import { V_BindTargetDeclaration, V_HTMLElement, V_Mountable } from 'packages/core/src/api/Validators';
import { Signal } from 'packages/core/src/utils/Signal';
import { getJsEnginePluginAPI } from 'packages/obsidian/src/ObsUtils';
import { type ReactiveComponent } from 'jsEngine/api/reactive/ReactiveComponent';
import { type Mountable } from 'packages/core/src/utils/Mountable';
import { type InlineFieldType, isFieldTypeAllowedInline } from 'packages/core/src/config/APIConfigs';
/**
* Either a [Component](https://docs.obsidian.md/Reference/TypeScript+API/Component) or a [MarkdownPostProcessorContext](https://docs.obsidian.md/Reference/TypeScript+API/MarkdownPostProcessorContext).
*/
export interface ComponentLike {
addChild(child: Component): void;
}
/**
* @internal
*/
export const V_ComponentLike = z.object({
addChild: z.function().args(z.instanceof(Component)).returns(z.void()),
});
/**
* Meta Bind API for Obsidian.
* @extends API
*/
export class ObsidianAPI extends API<MetaBindPlugin> {
constructor(plugin: MetaBindPlugin) {
super(plugin);
}
/**
* Wraps any mountable in a [MarkdownRenderChild](https://docs.obsidian.md/Reference/TypeScript+API/MarkdownRenderChild)
* and adds it as a child to the passed in {@link ComponentLike}.
*
* A {@link ComponentLike} is either a [Component](https://docs.obsidian.md/Reference/TypeScript+API/Component) or a [MarkdownPostProcessorContext](https://docs.obsidian.md/Reference/TypeScript+API/MarkdownPostProcessorContext)
*
* @param mountable the mountable to wrap in a [MarkdownRenderChild](https://docs.obsidian.md/Reference/TypeScript+API/MarkdownRenderChild)
* @param containerEl the element to mount the [MarkdownRenderChild](https://docs.obsidian.md/Reference/TypeScript+API/MarkdownRenderChild) to
* @param component the {@link ComponentLike} to register the [MarkdownRenderChild](https://docs.obsidian.md/Reference/TypeScript+API/MarkdownRenderChild) to
*/
public wrapInMDRC(mountable: Mountable, containerEl: HTMLElement, component: ComponentLike): MountableMDRC {
validateAPIArgs(
z.object({
field: V_Mountable,
containerEl: V_HTMLElement,
component: V_ComponentLike,
}),
{
field: mountable,
containerEl: containerEl,
component: component,
},
);
const mdrc = new MountableMDRC(this.plugin, mountable, containerEl);
component.addChild(mdrc);
return mdrc;
}
/**
* Creates a CM6 widget from a given widget type.
*
* This is only useful fur use in a CodeMirror plugin.
*
* @param inlineFieldType
* @param content
* @param filePath
* @param component
*/
public constructMDRCWidget(
inlineFieldType: InlineFieldType,
content: string,
filePath: string,
component: Component,
): MarkdownRenderChildWidget {
if (isFieldTypeAllowedInline(inlineFieldType)) {
return new MarkdownRenderChildWidget(inlineFieldType, content, filePath, component, this.plugin);
}
throw new MetaBindInternalError({
errorLevel: ErrorLevel.CRITICAL,
effect: 'failed to construct mdrc',
cause: `Invalid inline field type "${inlineFieldType}"`,
});
}
/**
* Creates a JS Engine reactive component that will re-render when the given bind targets change.
*
* This requires JS Engine to be installed and enabled!
*
* @param bindTargets the bind targets to listen to
* @param lifecycleHook a [Component](https://docs.obsidian.md/Reference/TypeScript+API/Component)
* @param callback the callback to call with all the values of the bind targets when one of them changes. What ever this callback returns will be rendered by the reactive component.
*/
public reactiveMetadata(
bindTargets: BindTargetDeclaration[],
lifecycleHook: LifecycleHook,
callback: (...values: unknown[]) => Promise<unknown>,
): ReactiveComponent {
validateAPIArgs(
z.object({
bindTargets: V_BindTargetDeclaration.array(),
lifecycleHook: this.plugin.internal.getLifecycleHookValidator(),
callback: z.function(),
}),
{
bindTargets: bindTargets,
lifecycleHook: lifecycleHook,
callback: callback,
},
);
const jsEngine = getJsEnginePluginAPI(this.plugin);
const uuid = getUUID();
const signal = new Signal<unknown>(undefined);
const dependencies = bindTargets.map(bindTarget => ({
bindTarget: bindTarget,
callbackSignal: new Signal<unknown>(undefined),
}));
let reactive: ReactiveComponent | undefined = undefined;
const subscription = this.plugin.metadataManager.subscribeComputed(
uuid,
signal,
undefined,
dependencies,
(values: unknown[]) => reactive?.refresh(...values),
() => {},
);
lifecycleHook.register(() => subscription.unsubscribe());
reactive = jsEngine.reactive(callback, ...dependencies.map(x => x.callbackSignal.get()));
return reactive;
}
}