@lapis-notes/workspace 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/LICENSE.md +4 -0
- package/README.md +36 -0
- package/dist/WorkspaceShell.svelte +59 -0
- package/dist/WorkspaceShell.svelte.d.ts +14 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +5 -0
- package/dist/plugin-management/CommunityPluginsSettingsPage.svelte +182 -0
- package/dist/plugin-management/CommunityPluginsSettingsPage.svelte.d.ts +5 -0
- package/dist/plugin-management/CorePluginsSettingsPage.svelte +111 -0
- package/dist/plugin-management/CorePluginsSettingsPage.svelte.d.ts +5 -0
- package/dist/plugin-management/PluginReadmeRenderer.svelte +47 -0
- package/dist/plugin-management/PluginReadmeRenderer.svelte.d.ts +9 -0
- package/dist/plugin-management/PluginRegistrySettingsPage.svelte +751 -0
- package/dist/plugin-management/PluginRegistrySettingsPage.svelte.d.ts +5 -0
- package/dist/plugin-management/PluginSettingsRow.svelte +152 -0
- package/dist/plugin-management/PluginSettingsRow.svelte.d.ts +24 -0
- package/dist/plugin-management/plugin-management-context.svelte.d.ts +15 -0
- package/dist/plugin-management/plugin-management-context.svelte.js +24 -0
- package/dist/plugin-management/plugin-management.css +477 -0
- package/dist/plugin-management/plugin-readme.d.ts +26 -0
- package/dist/plugin-management/plugin-readme.js +34 -0
- package/dist/plugin-management/plugin-settings-navigation.d.ts +8 -0
- package/dist/plugin-management/plugin-settings-navigation.js +37 -0
- package/dist/plugin-management/register-plugin-management-settings.d.ts +3 -0
- package/dist/plugin-management/register-plugin-management-settings.js +95 -0
- package/dist/workspace-shell.css +18 -0
- package/package.json +69 -0
package/CHANGELOG.md
ADDED
package/LICENSE.md
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# @lapis-notes/workspace
|
|
2
|
+
|
|
3
|
+
Workspace shell adapter for Lapis Notes package consumers.
|
|
4
|
+
|
|
5
|
+
This package exposes the public Svelte workspace shell that connects a host-owned
|
|
6
|
+
`App` to Design Core layout presentation. It stays shell-only: hosts own vault
|
|
7
|
+
selection, plugin boot, persistence, routing, profile management, and production
|
|
8
|
+
backend setup.
|
|
9
|
+
|
|
10
|
+
## Public surface
|
|
11
|
+
|
|
12
|
+
- `WorkspaceShell`, the Svelte component used by hosts and demos.
|
|
13
|
+
- `WorkspaceShellProps`, the public prop contract for typed Svelte consumers.
|
|
14
|
+
|
|
15
|
+
## Usage
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { WorkspaceShell } from "@lapis-notes/workspace";
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Install `@lapis-notes/api`, `@lapismd/design-core`, and Svelte from npm with
|
|
22
|
+
compatible versions. `@lapis-notes/workspace@0.1.0` expects
|
|
23
|
+
`@lapis-notes/api@^0.1.0`.
|
|
24
|
+
|
|
25
|
+
## Common Scripts
|
|
26
|
+
|
|
27
|
+
```sh
|
|
28
|
+
pnpm --filter @lapis-notes/workspace build
|
|
29
|
+
pnpm --filter @lapis-notes/workspace check
|
|
30
|
+
pnpm --filter @lapis-notes/workspace test
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## References
|
|
34
|
+
|
|
35
|
+
- [Package spec](spec.md)
|
|
36
|
+
- [Canonical spec](../../spec/src/20-packages/workspace/index.md)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<script lang="ts" module>
|
|
2
|
+
import type { App } from "@lapis-notes/api";
|
|
3
|
+
import type { WorkspaceNavigation } from "@lapismd/design-core/workspace/app-shell";
|
|
4
|
+
import type { WorkspaceRequestedDisplayMode } from "@lapismd/design-core/workspace/core";
|
|
5
|
+
|
|
6
|
+
export interface WorkspaceShellProps {
|
|
7
|
+
app: App;
|
|
8
|
+
displayMode?: WorkspaceRequestedDisplayMode;
|
|
9
|
+
workspaceLabel?: string;
|
|
10
|
+
workspaceNavigation?: WorkspaceNavigation;
|
|
11
|
+
class?: string;
|
|
12
|
+
}
|
|
13
|
+
</script>
|
|
14
|
+
|
|
15
|
+
<script lang="ts">
|
|
16
|
+
import { onMount, untrack } from "svelte";
|
|
17
|
+
import { provideApplicationState } from "@lapis-notes/api";
|
|
18
|
+
import { getWorkspaceHostBinding } from "@lapis-notes/api/workspace-host";
|
|
19
|
+
import { AppShell } from "@lapismd/design-core/workspace/app-shell";
|
|
20
|
+
import "./workspace-shell.css";
|
|
21
|
+
|
|
22
|
+
let {
|
|
23
|
+
app,
|
|
24
|
+
displayMode,
|
|
25
|
+
workspaceLabel = "Lapis Notes",
|
|
26
|
+
workspaceNavigation,
|
|
27
|
+
class: className = "",
|
|
28
|
+
}: WorkspaceShellProps = $props();
|
|
29
|
+
|
|
30
|
+
provideApplicationState(untrack(() => app));
|
|
31
|
+
const { controller } = untrack(() => getWorkspaceHostBinding(app.workspace));
|
|
32
|
+
let activateNewTabs = $derived(
|
|
33
|
+
app.configuration
|
|
34
|
+
.getConfiguration()
|
|
35
|
+
.get("editor.alwaysFocusNewTabs", false),
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
onMount(() => {
|
|
39
|
+
// AppShell.Root does not own startup. By the time this shell is mounted,
|
|
40
|
+
// the consumer has loaded the vault and restored the Lapis workspace.
|
|
41
|
+
void controller.start();
|
|
42
|
+
});
|
|
43
|
+
</script>
|
|
44
|
+
|
|
45
|
+
<div
|
|
46
|
+
class={`lapis-workspace-shell ${className}`}
|
|
47
|
+
data-ui-component="lapis-workspace-shell"
|
|
48
|
+
data-ui-part="root"
|
|
49
|
+
>
|
|
50
|
+
<AppShell.Root
|
|
51
|
+
{controller}
|
|
52
|
+
autoStart={false}
|
|
53
|
+
{activateNewTabs}
|
|
54
|
+
disposeOnDestroy={false}
|
|
55
|
+
theme="inherit"
|
|
56
|
+
>
|
|
57
|
+
<AppShell.Surface {displayMode} {workspaceLabel} {workspaceNavigation} />
|
|
58
|
+
</AppShell.Root>
|
|
59
|
+
</div>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { App } from "@lapis-notes/api";
|
|
2
|
+
import type { WorkspaceNavigation } from "@lapismd/design-core/workspace/app-shell";
|
|
3
|
+
import type { WorkspaceRequestedDisplayMode } from "@lapismd/design-core/workspace/core";
|
|
4
|
+
export interface WorkspaceShellProps {
|
|
5
|
+
app: App;
|
|
6
|
+
displayMode?: WorkspaceRequestedDisplayMode;
|
|
7
|
+
workspaceLabel?: string;
|
|
8
|
+
workspaceNavigation?: WorkspaceNavigation;
|
|
9
|
+
class?: string;
|
|
10
|
+
}
|
|
11
|
+
import "./workspace-shell.css";
|
|
12
|
+
declare const WorkspaceShell: import("svelte").Component<WorkspaceShellProps, {}, "">;
|
|
13
|
+
type WorkspaceShell = ReturnType<typeof WorkspaceShell>;
|
|
14
|
+
export default WorkspaceShell;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as WorkspaceShell } from "./WorkspaceShell.svelte";
|
|
2
|
+
export type { WorkspaceShellProps } from "./WorkspaceShell.svelte";
|
|
3
|
+
export { registerPluginManagementSettings } from "./plugin-management/register-plugin-management-settings";
|
|
4
|
+
export { default as PluginRegistrySettingsPage } from "./plugin-management/PluginRegistrySettingsPage.svelte";
|
|
5
|
+
export { default as CorePluginsSettingsPage } from "./plugin-management/CorePluginsSettingsPage.svelte";
|
|
6
|
+
export { default as CommunityPluginsSettingsPage } from "./plugin-management/CommunityPluginsSettingsPage.svelte";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { default as WorkspaceShell } from "./WorkspaceShell.svelte";
|
|
2
|
+
export { registerPluginManagementSettings } from "./plugin-management/register-plugin-management-settings";
|
|
3
|
+
export { default as PluginRegistrySettingsPage } from "./plugin-management/PluginRegistrySettingsPage.svelte";
|
|
4
|
+
export { default as CorePluginsSettingsPage } from "./plugin-management/CorePluginsSettingsPage.svelte";
|
|
5
|
+
export { default as CommunityPluginsSettingsPage } from "./plugin-management/CommunityPluginsSettingsPage.svelte";
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import * as Button from "@lapismd/design-core/shadcn/button";
|
|
3
|
+
import type { WorkspaceSettingsPageProps } from "@lapismd/design-core/workspace/settings";
|
|
4
|
+
import { onMount, tick, untrack } from "svelte";
|
|
5
|
+
import { getPluginManagementContext } from "./plugin-management-context.svelte";
|
|
6
|
+
import "./plugin-management.css";
|
|
7
|
+
import PluginSettingsRow from "./PluginSettingsRow.svelte";
|
|
8
|
+
import {
|
|
9
|
+
openPluginSettings,
|
|
10
|
+
pluginDiagnosticRows,
|
|
11
|
+
pluginSettingsSectionId,
|
|
12
|
+
} from "./plugin-settings-navigation";
|
|
13
|
+
|
|
14
|
+
let { controller }: WorkspaceSettingsPageProps = $props();
|
|
15
|
+
const context = untrack(() => getPluginManagementContext(controller));
|
|
16
|
+
const app = context.app;
|
|
17
|
+
let revision = $state(0);
|
|
18
|
+
let trustBusy = $state(false);
|
|
19
|
+
let trustState = $derived.by(() => {
|
|
20
|
+
revision;
|
|
21
|
+
return app.workspaceTrust.getState();
|
|
22
|
+
});
|
|
23
|
+
let expandedPluginIds = $state(new Set<string>());
|
|
24
|
+
let plugins = $derived.by(() => {
|
|
25
|
+
revision;
|
|
26
|
+
return app.plugins.communityPlugins;
|
|
27
|
+
});
|
|
28
|
+
let indexedDiagnostics = $derived.by(() => {
|
|
29
|
+
revision;
|
|
30
|
+
const loaded = new Set(plugins.map((plugin) => plugin.manifest.id));
|
|
31
|
+
const core = new Set(
|
|
32
|
+
app.plugins.corePluginEntries.map((entry) => entry.manifest.id),
|
|
33
|
+
);
|
|
34
|
+
return app.plugins.communityPluginDiagnostics.filter(
|
|
35
|
+
(entry) => !loaded.has(entry.pluginId) && !core.has(entry.pluginId),
|
|
36
|
+
);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
$effect(() => {
|
|
40
|
+
const revealRevision = context.reveal.revision;
|
|
41
|
+
if (
|
|
42
|
+
!revealRevision ||
|
|
43
|
+
context.reveal.sectionId !== "community-plugins" ||
|
|
44
|
+
!context.reveal.entryId.startsWith("community:")
|
|
45
|
+
) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
const pluginId = context.reveal.entryId.slice("community:".length);
|
|
49
|
+
expandedPluginIds = new Set([...expandedPluginIds, pluginId]);
|
|
50
|
+
void tick().then(() => {
|
|
51
|
+
document
|
|
52
|
+
.querySelector(`[data-settings-plugin-id="${CSS.escape(pluginId)}"]`)
|
|
53
|
+
?.scrollIntoView({ block: "center" });
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
onMount(() => {
|
|
58
|
+
const refresh = () => (revision += 1);
|
|
59
|
+
const loaded = app.plugins.on("plugins-loaded", refresh);
|
|
60
|
+
const enabled = app.plugins.on("plugin-enabled", refresh);
|
|
61
|
+
const disabled = app.plugins.on("plugin-disabled", refresh);
|
|
62
|
+
const error = app.plugins.on("plugin-error", refresh);
|
|
63
|
+
void app.workspaceTrust.ready().then(refresh);
|
|
64
|
+
return () => {
|
|
65
|
+
app.plugins.offref(loaded);
|
|
66
|
+
app.plugins.offref(enabled);
|
|
67
|
+
app.plugins.offref(disabled);
|
|
68
|
+
app.plugins.offref(error);
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
async function updateTrust(trusted: boolean): Promise<void> {
|
|
73
|
+
if (trustBusy) return;
|
|
74
|
+
trustBusy = true;
|
|
75
|
+
try {
|
|
76
|
+
if (trusted) await app.workspaceTrust.grant();
|
|
77
|
+
else await app.workspaceTrust.revoke();
|
|
78
|
+
revision += 1;
|
|
79
|
+
} finally {
|
|
80
|
+
trustBusy = false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
</script>
|
|
84
|
+
|
|
85
|
+
<section
|
|
86
|
+
class="lapis-plugin-management"
|
|
87
|
+
data-ui-component="lapis-plugin-management"
|
|
88
|
+
data-ui-part="community-plugins"
|
|
89
|
+
>
|
|
90
|
+
<div class="lapis-plugin-management__trust">
|
|
91
|
+
<div>
|
|
92
|
+
<h1>Workspace Trust</h1>
|
|
93
|
+
<p>
|
|
94
|
+
{trustState.trusted
|
|
95
|
+
? "This vault is trusted. Trusted desktop plugins and brokered capabilities may run for this workspace identity."
|
|
96
|
+
: "This vault is untrusted. Trusted desktop plugins, brokered capabilities, and community plugin settings surfaces stay blocked until you trust this workspace."}
|
|
97
|
+
</p>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="lapis-plugin-management__actions">
|
|
100
|
+
<Button.Root
|
|
101
|
+
disabled={trustState.trusted || trustBusy}
|
|
102
|
+
onclick={() => void updateTrust(true)}
|
|
103
|
+
>Trust workspace</Button.Root>
|
|
104
|
+
<Button.Root
|
|
105
|
+
variant="destructive"
|
|
106
|
+
disabled={!trustState.trusted || trustBusy}
|
|
107
|
+
onclick={() => void updateTrust(false)}
|
|
108
|
+
>Revoke trust</Button.Root>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
|
|
112
|
+
<header class="lapis-plugin-management__header">
|
|
113
|
+
<div>
|
|
114
|
+
<h1>Installed Plugins</h1>
|
|
115
|
+
<p>Manage installed community and registry plugins for this vault.</p>
|
|
116
|
+
</div>
|
|
117
|
+
<Button.Root
|
|
118
|
+
variant="outline"
|
|
119
|
+
onclick={() => globalThis.location?.reload()}
|
|
120
|
+
>Reload plugins</Button.Root>
|
|
121
|
+
</header>
|
|
122
|
+
|
|
123
|
+
<div class="lapis-plugin-management__rows">
|
|
124
|
+
{#if plugins.length === 0 && indexedDiagnostics.length === 0}
|
|
125
|
+
<p class="lapis-plugin-management__empty">
|
|
126
|
+
No community plugins found in .obsidian/plugins.
|
|
127
|
+
</p>
|
|
128
|
+
{/if}
|
|
129
|
+
|
|
130
|
+
{#each plugins as plugin (plugin.manifest.id)}
|
|
131
|
+
<PluginSettingsRow
|
|
132
|
+
pluginId={plugin.manifest.id}
|
|
133
|
+
name={plugin.manifest.name}
|
|
134
|
+
description={plugin.manifest.description}
|
|
135
|
+
version={plugin.manifest.version}
|
|
136
|
+
provenance={plugin.provenance}
|
|
137
|
+
status={plugin.state === "failed"
|
|
138
|
+
? "Failed"
|
|
139
|
+
: plugin.enabled
|
|
140
|
+
? "Enabled"
|
|
141
|
+
: "Disabled"}
|
|
142
|
+
error={plugin.lastFailureMessage ?? plugin.errorMessage ?? undefined}
|
|
143
|
+
enabled={plugin.enabled}
|
|
144
|
+
expanded={expandedPluginIds.has(plugin.manifest.id)}
|
|
145
|
+
hasOptions={Boolean(
|
|
146
|
+
pluginSettingsSectionId(controller, plugin.manifest.id),
|
|
147
|
+
)}
|
|
148
|
+
diagnostics={pluginDiagnosticRows(app, plugin.manifest.id)}
|
|
149
|
+
onOptions={() => openPluginSettings(controller, plugin.manifest.id)}
|
|
150
|
+
onRestart={async () => {
|
|
151
|
+
await app.plugins.restartPlugin(plugin.manifest.id);
|
|
152
|
+
}}
|
|
153
|
+
onToggle={async (enabled) => {
|
|
154
|
+
if (enabled) await app.plugins.enablePlugin(plugin.manifest.id);
|
|
155
|
+
else await app.plugins.disablePlugin(plugin.manifest.id);
|
|
156
|
+
}}
|
|
157
|
+
/>
|
|
158
|
+
{/each}
|
|
159
|
+
|
|
160
|
+
{#each indexedDiagnostics as diagnostics (diagnostics.pluginId)}
|
|
161
|
+
<PluginSettingsRow
|
|
162
|
+
pluginId={diagnostics.pluginId}
|
|
163
|
+
name={diagnostics.name ?? diagnostics.pluginId}
|
|
164
|
+
description={diagnostics.description ?? undefined}
|
|
165
|
+
version={diagnostics.version ?? undefined}
|
|
166
|
+
provenance={diagnostics.provenance}
|
|
167
|
+
status={diagnostics.state}
|
|
168
|
+
error={diagnostics.lastFailureMessage ?? undefined}
|
|
169
|
+
enabled={false}
|
|
170
|
+
expanded={expandedPluginIds.has(diagnostics.pluginId)}
|
|
171
|
+
hasOptions={Boolean(
|
|
172
|
+
pluginSettingsSectionId(controller, diagnostics.pluginId),
|
|
173
|
+
)}
|
|
174
|
+
diagnostics={pluginDiagnosticRows(app, diagnostics.pluginId)}
|
|
175
|
+
onOptions={() => openPluginSettings(controller, diagnostics.pluginId)}
|
|
176
|
+
onRestart={async () => {
|
|
177
|
+
await app.plugins.restartPlugin(diagnostics.pluginId);
|
|
178
|
+
}}
|
|
179
|
+
/>
|
|
180
|
+
{/each}
|
|
181
|
+
</div>
|
|
182
|
+
</section>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { WorkspaceSettingsPageProps } from "@lapismd/design-core/workspace/settings";
|
|
2
|
+
import "./plugin-management.css";
|
|
3
|
+
declare const CommunityPluginsSettingsPage: import("svelte").Component<WorkspaceSettingsPageProps, {}, "">;
|
|
4
|
+
type CommunityPluginsSettingsPage = ReturnType<typeof CommunityPluginsSettingsPage>;
|
|
5
|
+
export default CommunityPluginsSettingsPage;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { WorkspaceSettingsPageProps } from "@lapismd/design-core/workspace/settings";
|
|
3
|
+
import { onMount, tick, untrack } from "svelte";
|
|
4
|
+
import { getPluginManagementContext } from "./plugin-management-context.svelte";
|
|
5
|
+
import "./plugin-management.css";
|
|
6
|
+
import PluginSettingsRow from "./PluginSettingsRow.svelte";
|
|
7
|
+
import {
|
|
8
|
+
openPluginSettings,
|
|
9
|
+
pluginDiagnosticRows,
|
|
10
|
+
pluginSettingsSectionId,
|
|
11
|
+
} from "./plugin-settings-navigation";
|
|
12
|
+
|
|
13
|
+
let { controller }: WorkspaceSettingsPageProps = $props();
|
|
14
|
+
const context = untrack(() => getPluginManagementContext(controller));
|
|
15
|
+
const app = context.app;
|
|
16
|
+
let revision = $state(0);
|
|
17
|
+
let expandedPluginIds = $state(new Set<string>());
|
|
18
|
+
let entries = $derived.by(() => {
|
|
19
|
+
revision;
|
|
20
|
+
const seen = new Set<string>();
|
|
21
|
+
return app.plugins.corePluginEntries.filter((entry) => {
|
|
22
|
+
if (seen.has(entry.manifest.id)) return false;
|
|
23
|
+
seen.add(entry.manifest.id);
|
|
24
|
+
return true;
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
$effect(() => {
|
|
29
|
+
const revealRevision = context.reveal.revision;
|
|
30
|
+
if (
|
|
31
|
+
!revealRevision ||
|
|
32
|
+
context.reveal.sectionId !== "core-plugins" ||
|
|
33
|
+
!context.reveal.entryId.startsWith("core:")
|
|
34
|
+
) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const pluginId = context.reveal.entryId.slice("core:".length);
|
|
38
|
+
expandedPluginIds = new Set([...expandedPluginIds, pluginId]);
|
|
39
|
+
void tick().then(() => {
|
|
40
|
+
document
|
|
41
|
+
.querySelector(`[data-settings-plugin-id="${CSS.escape(pluginId)}"]`)
|
|
42
|
+
?.scrollIntoView({ block: "center" });
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
onMount(() => {
|
|
47
|
+
const refresh = () => (revision += 1);
|
|
48
|
+
const loaded = app.plugins.on("plugins-loaded", refresh);
|
|
49
|
+
const enabled = app.plugins.on("plugin-enabled", refresh);
|
|
50
|
+
const disabled = app.plugins.on("plugin-disabled", refresh);
|
|
51
|
+
const error = app.plugins.on("plugin-error", refresh);
|
|
52
|
+
return () => {
|
|
53
|
+
app.plugins.offref(loaded);
|
|
54
|
+
app.plugins.offref(enabled);
|
|
55
|
+
app.plugins.offref(disabled);
|
|
56
|
+
app.plugins.offref(error);
|
|
57
|
+
};
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
</script>
|
|
61
|
+
|
|
62
|
+
<section
|
|
63
|
+
class="lapis-plugin-management"
|
|
64
|
+
data-ui-component="lapis-plugin-management"
|
|
65
|
+
data-ui-part="core-plugins"
|
|
66
|
+
>
|
|
67
|
+
<header class="lapis-plugin-management__header">
|
|
68
|
+
<div>
|
|
69
|
+
<h1>Installed Core Plugins</h1>
|
|
70
|
+
<p>Enable, inspect, and configure this app's static plugins.</p>
|
|
71
|
+
</div>
|
|
72
|
+
</header>
|
|
73
|
+
|
|
74
|
+
<div class="lapis-plugin-management__rows">
|
|
75
|
+
{#if entries.length === 0}
|
|
76
|
+
<p class="lapis-plugin-management__empty">
|
|
77
|
+
No core plugins are registered.
|
|
78
|
+
</p>
|
|
79
|
+
{/if}
|
|
80
|
+
{#each entries as entry (entry.manifest.id)}
|
|
81
|
+
<PluginSettingsRow
|
|
82
|
+
pluginId={entry.manifest.id}
|
|
83
|
+
name={entry.manifest.name}
|
|
84
|
+
description={entry.manifest.description}
|
|
85
|
+
version={entry.manifest.version}
|
|
86
|
+
provenance={entry.distribution}
|
|
87
|
+
status={entry.errorMessage
|
|
88
|
+
? "Failed"
|
|
89
|
+
: entry.enabled
|
|
90
|
+
? "Enabled"
|
|
91
|
+
: "Disabled"}
|
|
92
|
+
error={entry.errorMessage ?? undefined}
|
|
93
|
+
enabled={entry.enabled}
|
|
94
|
+
required={entry.required}
|
|
95
|
+
expanded={expandedPluginIds.has(entry.manifest.id)}
|
|
96
|
+
hasOptions={Boolean(
|
|
97
|
+
pluginSettingsSectionId(controller, entry.manifest.id),
|
|
98
|
+
)}
|
|
99
|
+
diagnostics={pluginDiagnosticRows(app, entry.manifest.id)}
|
|
100
|
+
onOptions={() => openPluginSettings(controller, entry.manifest.id)}
|
|
101
|
+
onRestart={async () => {
|
|
102
|
+
await app.plugins.restartPlugin(entry.manifest.id);
|
|
103
|
+
}}
|
|
104
|
+
onToggle={async (enabled) => {
|
|
105
|
+
if (enabled) await app.plugins.enablePlugin(entry.manifest.id);
|
|
106
|
+
else await app.plugins.disablePlugin(entry.manifest.id);
|
|
107
|
+
}}
|
|
108
|
+
/>
|
|
109
|
+
{/each}
|
|
110
|
+
</div>
|
|
111
|
+
</section>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { WorkspaceSettingsPageProps } from "@lapismd/design-core/workspace/settings";
|
|
2
|
+
import "./plugin-management.css";
|
|
3
|
+
declare const CorePluginsSettingsPage: import("svelte").Component<WorkspaceSettingsPageProps, {}, "">;
|
|
4
|
+
type CorePluginsSettingsPage = ReturnType<typeof CorePluginsSettingsPage>;
|
|
5
|
+
export default CorePluginsSettingsPage;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Component, MarkdownRenderer, type App } from "@lapis-notes/api";
|
|
3
|
+
import { onDestroy } from "svelte";
|
|
4
|
+
|
|
5
|
+
class PluginReadmeLifecycle extends Component {}
|
|
6
|
+
|
|
7
|
+
let {
|
|
8
|
+
app,
|
|
9
|
+
markdown,
|
|
10
|
+
sourcePath,
|
|
11
|
+
}: { app: App; markdown: string; sourcePath: string } = $props();
|
|
12
|
+
|
|
13
|
+
let host = $state<HTMLElement | null>(null);
|
|
14
|
+
let renderRevision = 0;
|
|
15
|
+
let lifecycle: Component | null = null;
|
|
16
|
+
|
|
17
|
+
$effect(() => {
|
|
18
|
+
const target = host;
|
|
19
|
+
const value = markdown;
|
|
20
|
+
const path = sourcePath;
|
|
21
|
+
if (!target) return;
|
|
22
|
+
const revision = ++renderRevision;
|
|
23
|
+
lifecycle?.unload();
|
|
24
|
+
lifecycle = new PluginReadmeLifecycle();
|
|
25
|
+
lifecycle.load();
|
|
26
|
+
target.replaceChildren();
|
|
27
|
+
void MarkdownRenderer.render(app, value, target, path, lifecycle).catch(
|
|
28
|
+
(error) => {
|
|
29
|
+
if (revision !== renderRevision) return;
|
|
30
|
+
target.textContent =
|
|
31
|
+
error instanceof Error ? error.message : String(error);
|
|
32
|
+
},
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
onDestroy(() => {
|
|
37
|
+
renderRevision += 1;
|
|
38
|
+
lifecycle?.unload();
|
|
39
|
+
lifecycle = null;
|
|
40
|
+
});
|
|
41
|
+
</script>
|
|
42
|
+
|
|
43
|
+
<div
|
|
44
|
+
bind:this={host}
|
|
45
|
+
class="lapis-plugin-readme"
|
|
46
|
+
data-ui-component="lapis-plugin-readme"
|
|
47
|
+
></div>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type App } from "@lapis-notes/api";
|
|
2
|
+
type $$ComponentProps = {
|
|
3
|
+
app: App;
|
|
4
|
+
markdown: string;
|
|
5
|
+
sourcePath: string;
|
|
6
|
+
};
|
|
7
|
+
declare const PluginReadmeRenderer: import("svelte").Component<$$ComponentProps, {}, "">;
|
|
8
|
+
type PluginReadmeRenderer = ReturnType<typeof PluginReadmeRenderer>;
|
|
9
|
+
export default PluginReadmeRenderer;
|