@dxos/plugin-registry 0.8.4-main.b97322e → 0.8.4-main.bc2380dfbc
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/LICENSE +102 -5
- package/dist/lib/neutral/RegistryPlugin.mjs +27 -0
- package/dist/lib/neutral/RegistryPlugin.mjs.map +7 -0
- package/dist/lib/neutral/RegistryPlugin.node.mjs +153 -0
- package/dist/lib/neutral/RegistryPlugin.node.mjs.map +7 -0
- package/dist/lib/neutral/RegistryPlugin.workerd.mjs +10 -0
- package/dist/lib/neutral/RegistryPlugin.workerd.mjs.map +7 -0
- package/dist/lib/neutral/chunk-A6F6X6M4.mjs +31 -0
- package/dist/lib/neutral/chunk-A6F6X6M4.mjs.map +7 -0
- package/dist/lib/neutral/chunk-XK2JMH3H.mjs +33 -0
- package/dist/lib/neutral/chunk-XK2JMH3H.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +23 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/open-plugin-registry-II735N76.mjs +15 -0
- package/dist/lib/neutral/open-plugin-registry-II735N76.mjs.map +7 -0
- package/dist/lib/neutral/plugin.mjs +18 -0
- package/dist/lib/neutral/plugin.mjs.map +7 -0
- package/dist/lib/neutral/translations.mjs +67 -0
- package/dist/lib/neutral/translations.mjs.map +7 -0
- package/dist/types/src/RegistryPlugin.d.ts +3 -1
- package/dist/types/src/RegistryPlugin.d.ts.map +1 -1
- package/dist/types/src/RegistryPlugin.node.d.ts +4 -0
- package/dist/types/src/RegistryPlugin.node.d.ts.map +1 -0
- package/dist/types/src/RegistryPlugin.test.d.ts +2 -0
- package/dist/types/src/RegistryPlugin.test.d.ts.map +1 -0
- package/dist/types/src/RegistryPlugin.workerd.d.ts +4 -0
- package/dist/types/src/RegistryPlugin.workerd.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts +4 -2
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/dev-plugin-loader.d.ts +11 -0
- package/dist/types/src/capabilities/dev-plugin-loader.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +15 -3
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +3 -2
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/settings.d.ts +14 -0
- package/dist/types/src/capabilities/settings.d.ts.map +1 -0
- package/dist/types/src/commands/disable.d.ts +11 -0
- package/dist/types/src/commands/disable.d.ts.map +1 -0
- package/dist/types/src/commands/enable.d.ts +11 -0
- package/dist/types/src/commands/enable.d.ts.map +1 -0
- package/dist/types/src/commands/index.d.ts +3 -0
- package/dist/types/src/commands/index.d.ts.map +1 -0
- package/dist/types/src/commands/list.d.ts +7 -0
- package/dist/types/src/commands/list.d.ts.map +1 -0
- package/dist/types/src/commands/util.d.ts +11 -0
- package/dist/types/src/commands/util.d.ts.map +1 -0
- package/dist/types/src/components/DisableDependentsAlert/DisableDependentsAlert.d.ts +24 -0
- package/dist/types/src/components/DisableDependentsAlert/DisableDependentsAlert.d.ts.map +1 -0
- package/dist/types/src/components/DisableDependentsAlert/index.d.ts +2 -0
- package/dist/types/src/components/DisableDependentsAlert/index.d.ts.map +1 -0
- package/dist/types/src/components/PluginDetail/PluginDetail.d.ts +72 -0
- package/dist/types/src/components/PluginDetail/PluginDetail.d.ts.map +1 -0
- package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts +74 -0
- package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts.map +1 -0
- package/dist/types/src/components/PluginDetail/index.d.ts +2 -0
- package/dist/types/src/components/PluginDetail/index.d.ts.map +1 -0
- package/dist/types/src/components/PluginFailureBadge/PluginFailureBadge.d.ts +20 -0
- package/dist/types/src/components/PluginFailureBadge/PluginFailureBadge.d.ts.map +1 -0
- package/dist/types/src/components/PluginFailureBadge/index.d.ts +2 -0
- package/dist/types/src/components/PluginFailureBadge/index.d.ts.map +1 -0
- package/dist/types/src/components/PluginList/PluginItem.d.ts +40 -0
- package/dist/types/src/components/PluginList/PluginItem.d.ts.map +1 -0
- package/dist/types/src/components/PluginList/PluginList.d.ts +23 -0
- package/dist/types/src/components/PluginList/PluginList.d.ts.map +1 -0
- package/dist/types/src/components/PluginList/PluginList.stories.d.ts +73 -0
- package/dist/types/src/components/PluginList/PluginList.stories.d.ts.map +1 -0
- package/dist/types/src/components/PluginList/index.d.ts +2 -0
- package/dist/types/src/components/PluginList/index.d.ts.map +1 -0
- package/dist/types/src/components/RegistrySettings/RegistrySettings.d.ts +18 -0
- package/dist/types/src/components/RegistrySettings/RegistrySettings.d.ts.map +1 -0
- package/dist/types/src/components/RegistrySettings/index.d.ts +2 -0
- package/dist/types/src/components/RegistrySettings/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +3 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/containers/BaseRegistryArticle/BaseRegistryArticle.d.ts +20 -0
- package/dist/types/src/containers/BaseRegistryArticle/BaseRegistryArticle.d.ts.map +1 -0
- package/dist/types/src/containers/BaseRegistryArticle/index.d.ts +2 -0
- package/dist/types/src/containers/BaseRegistryArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts +4 -0
- package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts.map +1 -0
- package/dist/types/src/containers/LoadPluginDialog/index.d.ts +2 -0
- package/dist/types/src/containers/LoadPluginDialog/index.d.ts.map +1 -0
- package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts +7 -0
- package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts.map +1 -0
- package/dist/types/src/containers/PluginArticle/index.d.ts +2 -0
- package/dist/types/src/containers/PluginArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/PublicRegistryArticle/PublicRegistryArticle.d.ts +8 -0
- package/dist/types/src/containers/PublicRegistryArticle/PublicRegistryArticle.d.ts.map +1 -0
- package/dist/types/src/containers/PublicRegistryArticle/index.d.ts +2 -0
- package/dist/types/src/containers/PublicRegistryArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/RegistryArticle/RegistryArticle.d.ts +14 -0
- package/dist/types/src/containers/RegistryArticle/RegistryArticle.d.ts.map +1 -0
- package/dist/types/src/containers/RegistryArticle/index.d.ts +2 -0
- package/dist/types/src/containers/RegistryArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.d.ts +13 -0
- package/dist/types/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.d.ts.map +1 -0
- package/dist/types/src/containers/RegistrySettingsContainer/index.d.ts +2 -0
- package/dist/types/src/containers/RegistrySettingsContainer/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +8 -0
- package/dist/types/src/containers/index.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +5 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/use-auto-tags.d.ts +20 -0
- package/dist/types/src/hooks/use-auto-tags.d.ts.map +1 -0
- package/dist/types/src/hooks/use-disable-confirmation.d.ts +19 -0
- package/dist/types/src/hooks/use-disable-confirmation.d.ts.map +1 -0
- package/dist/types/src/hooks/use-registry-plugins.d.ts +15 -0
- package/dist/types/src/hooks/use-registry-plugins.d.ts.map +1 -0
- package/dist/types/src/hooks/use-update-available.d.ts +18 -0
- package/dist/types/src/hooks/use-update-available.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +9 -4
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/operations/open-plugin-registry.d.ts +5 -0
- package/dist/types/src/operations/open-plugin-registry.d.ts.map +1 -0
- package/dist/types/src/plugin.d.ts +4 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- package/dist/types/src/storage.d.ts +18 -0
- package/dist/types/src/storage.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +54 -16
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +32 -1
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +77 -37
- package/src/RegistryPlugin.node.ts +19 -0
- package/src/RegistryPlugin.test.ts +24 -0
- package/src/RegistryPlugin.tsx +20 -27
- package/src/RegistryPlugin.workerd.ts +11 -0
- package/src/capabilities/app-graph-builder.ts +180 -135
- package/src/capabilities/dev-plugin-loader.ts +39 -0
- package/src/capabilities/index.ts +10 -4
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/react-surface.tsx +133 -78
- package/src/capabilities/settings.ts +34 -0
- package/src/commands/disable.ts +47 -0
- package/src/commands/enable.ts +44 -0
- package/src/commands/index.ts +14 -0
- package/src/commands/list.ts +41 -0
- package/src/commands/util.ts +24 -0
- package/src/components/DisableDependentsAlert/DisableDependentsAlert.tsx +69 -0
- package/src/components/DisableDependentsAlert/index.ts +5 -0
- package/src/components/PluginDetail/PluginDetail.stories.tsx +44 -0
- package/src/components/PluginDetail/PluginDetail.tsx +272 -0
- package/src/components/PluginDetail/index.ts +5 -0
- package/src/components/PluginFailureBadge/PluginFailureBadge.tsx +68 -0
- package/src/components/PluginFailureBadge/index.ts +5 -0
- package/src/components/PluginList/PluginItem.tsx +226 -0
- package/src/components/PluginList/PluginList.stories.tsx +87 -0
- package/src/components/PluginList/PluginList.tsx +52 -0
- package/src/components/PluginList/index.ts +5 -0
- package/src/components/RegistrySettings/RegistrySettings.tsx +120 -0
- package/src/components/RegistrySettings/index.ts +5 -0
- package/src/components/index.ts +3 -1
- package/src/containers/BaseRegistryArticle/BaseRegistryArticle.tsx +178 -0
- package/src/containers/BaseRegistryArticle/index.ts +5 -0
- package/src/containers/LoadPluginDialog/LoadPluginDialog.tsx +87 -0
- package/src/containers/LoadPluginDialog/index.ts +5 -0
- package/src/containers/PluginArticle/PluginArticle.tsx +378 -0
- package/src/containers/PluginArticle/index.ts +5 -0
- package/src/containers/PublicRegistryArticle/PublicRegistryArticle.tsx +192 -0
- package/src/containers/PublicRegistryArticle/index.ts +5 -0
- package/src/containers/RegistryArticle/RegistryArticle.tsx +53 -0
- package/src/containers/RegistryArticle/index.ts +5 -0
- package/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.tsx +59 -0
- package/src/containers/RegistrySettingsContainer/index.ts +5 -0
- package/src/containers/index.ts +12 -0
- package/src/hooks/index.ts +8 -0
- package/src/hooks/use-auto-tags.ts +51 -0
- package/src/hooks/use-disable-confirmation.ts +58 -0
- package/src/hooks/use-registry-plugins.ts +29 -0
- package/src/hooks/use-update-available.ts +31 -0
- package/src/index.ts +1 -1
- package/src/meta.ts +24 -9
- package/src/operations/index.ts +7 -0
- package/src/operations/open-plugin-registry.ts +21 -0
- package/src/plugin.ts +11 -0
- package/src/storage.ts +50 -0
- package/src/translations.ts +59 -15
- package/src/types.ts +37 -1
- package/dist/lib/browser/app-graph-builder-LKQBQDUA.mjs +0 -161
- package/dist/lib/browser/app-graph-builder-LKQBQDUA.mjs.map +0 -7
- package/dist/lib/browser/chunk-AEBASNOI.mjs +0 -16
- package/dist/lib/browser/chunk-AEBASNOI.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -71
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-W67CIJI3.mjs +0 -23
- package/dist/lib/browser/intent-resolver-W67CIJI3.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/react-surface-3GNB7AOY.mjs +0 -333
- package/dist/lib/browser/react-surface-3GNB7AOY.mjs.map +0 -7
- package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
- package/dist/types/src/components/Icon.stories.d.ts +0 -6
- package/dist/types/src/components/Icon.stories.d.ts.map +0 -1
- package/dist/types/src/components/PluginDetail.d.ts +0 -9
- package/dist/types/src/components/PluginDetail.d.ts.map +0 -1
- package/dist/types/src/components/PluginDetail.stories.d.ts +0 -8
- package/dist/types/src/components/PluginDetail.stories.d.ts.map +0 -1
- package/dist/types/src/components/PluginItem.d.ts +0 -13
- package/dist/types/src/components/PluginItem.d.ts.map +0 -1
- package/dist/types/src/components/PluginList.d.ts +0 -8
- package/dist/types/src/components/PluginList.d.ts.map +0 -1
- package/dist/types/src/components/PluginList.stories.d.ts +0 -11
- package/dist/types/src/components/PluginList.stories.d.ts.map +0 -1
- package/dist/types/src/components/RegistryContainer.d.ts +0 -7
- package/dist/types/src/components/RegistryContainer.d.ts.map +0 -1
- package/src/capabilities/intent-resolver.ts +0 -27
- package/src/components/Icon.stories.tsx +0 -108
- package/src/components/PluginDetail.stories.tsx +0 -45
- package/src/components/PluginDetail.tsx +0 -63
- package/src/components/PluginItem.tsx +0 -119
- package/src/components/PluginList.stories.tsx +0 -82
- package/src/components/PluginList.tsx +0 -26
- package/src/components/RegistryContainer.tsx +0 -84
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useAtomValue } from '@effect-atom/atom-react';
|
|
6
|
+
import * as Effect from 'effect/Effect';
|
|
7
|
+
import React, { useCallback } from 'react';
|
|
8
|
+
|
|
9
|
+
import { useSettingsState, usePluginManager } from '@dxos/app-framework/ui';
|
|
10
|
+
import { type AppCapabilities } from '@dxos/app-toolkit';
|
|
11
|
+
import { runAndForwardErrors } from '@dxos/effect';
|
|
12
|
+
|
|
13
|
+
import { RegistrySettings } from '../../components';
|
|
14
|
+
import { type RegistrySettings as RegistrySettingsType } from '../../types';
|
|
15
|
+
|
|
16
|
+
export type RegistrySettingsContainerProps = {
|
|
17
|
+
subject: AppCapabilities.Settings;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Wires the {@link RegistrySettings} presentational component to the plugin
|
|
22
|
+
* manager and the settings atom: subscribes to `manager.devPluginIds` for the
|
|
23
|
+
* "currently loaded" indicator and exposes enable/disable callbacks that drive
|
|
24
|
+
* the manager's add/enable/remove flow.
|
|
25
|
+
*/
|
|
26
|
+
export const RegistrySettingsContainer = ({ subject }: RegistrySettingsContainerProps) => {
|
|
27
|
+
const manager = usePluginManager();
|
|
28
|
+
const { settings, updateSettings } = useSettingsState<RegistrySettingsType>(subject.atom);
|
|
29
|
+
const activeDevPluginIds = useAtomValue(manager.devPluginIds);
|
|
30
|
+
|
|
31
|
+
const onEnableDev = useCallback(
|
|
32
|
+
async (url: string) => {
|
|
33
|
+
await runAndForwardErrors(
|
|
34
|
+
Effect.gen(function* () {
|
|
35
|
+
const plugin = yield* manager.add(url);
|
|
36
|
+
yield* manager.enable(plugin.meta.id);
|
|
37
|
+
}),
|
|
38
|
+
);
|
|
39
|
+
},
|
|
40
|
+
[manager],
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const onDisableDev = useCallback(
|
|
44
|
+
async (id: string) => {
|
|
45
|
+
await runAndForwardErrors(manager.remove(id));
|
|
46
|
+
},
|
|
47
|
+
[manager],
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
<RegistrySettings
|
|
52
|
+
settings={settings}
|
|
53
|
+
onSettingsChange={updateSettings}
|
|
54
|
+
activeDevPluginIds={activeDevPluginIds}
|
|
55
|
+
onEnableDev={onEnableDev}
|
|
56
|
+
onDisableDev={onDisableDev}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type ComponentType, lazy } from 'react';
|
|
6
|
+
|
|
7
|
+
export { LOAD_PLUGIN_DIALOG } from './LoadPluginDialog/LoadPluginDialog';
|
|
8
|
+
export const LoadPluginDialog: ComponentType<any> = lazy(() => import('./LoadPluginDialog'));
|
|
9
|
+
export const PluginArticle: ComponentType<any> = lazy(() => import('./PluginArticle'));
|
|
10
|
+
export const PublicRegistryArticle: ComponentType<any> = lazy(() => import('./PublicRegistryArticle'));
|
|
11
|
+
export const RegistryArticle: ComponentType<any> = lazy(() => import('./RegistryArticle'));
|
|
12
|
+
export const RegistrySettingsContainer: ComponentType<any> = lazy(() => import('./RegistrySettingsContainer'));
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type Registry, UrlLoader } from '@dxos/app-framework';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Auto-tags that are derived at display time rather than persisted to `Plugin.Meta`.
|
|
11
|
+
* - `registry`: plugin id appears in the registry catalog manifest.
|
|
12
|
+
* - `local`: plugin was loaded from a `localhost` / `127.0.0.1` / `::1` URL.
|
|
13
|
+
*/
|
|
14
|
+
export type AutoTagsMap = Record<string, readonly string[]>;
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Computes the per-plugin-id extra tags the UI should display.
|
|
18
|
+
*
|
|
19
|
+
* Reads the persisted remote-plugin entries synchronously to derive `local`, and
|
|
20
|
+
* adds `registry` for any plugin id found in the supplied registry manifest entries.
|
|
21
|
+
*/
|
|
22
|
+
export const useAutoTags = (registryEntries: readonly Registry.Plugin[]): AutoTagsMap =>
|
|
23
|
+
useMemo(() => {
|
|
24
|
+
const byId: Record<string, string[]> = {};
|
|
25
|
+
const addTag = (id: string, tag: string) => {
|
|
26
|
+
const existing = byId[id];
|
|
27
|
+
if (!existing) {
|
|
28
|
+
byId[id] = [tag];
|
|
29
|
+
} else if (!existing.includes(tag)) {
|
|
30
|
+
existing.push(tag);
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
for (const entry of UrlLoader.getRemoteEntries()) {
|
|
35
|
+
if (UrlLoader.isLocalUrl(entry.url)) {
|
|
36
|
+
addTag(entry.id, 'local');
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
for (const entry of registryEntries) {
|
|
40
|
+
addTag(entry.id, 'registry');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return byId;
|
|
44
|
+
}, [registryEntries]);
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Returns the set of plugin ids known to originate from a remote URL (not bundled).
|
|
48
|
+
* Used to filter the Official/Recommended sections so third-party plugins are excluded.
|
|
49
|
+
*/
|
|
50
|
+
export const useRemotePluginIds = (): ReadonlySet<string> =>
|
|
51
|
+
useMemo(() => new Set(UrlLoader.getRemoteEntries().map((entry) => entry.id)), []);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useCallback } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type PluginManager } from '@dxos/app-framework';
|
|
8
|
+
import { useOperationInvoker } from '@dxos/app-framework/ui';
|
|
9
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
10
|
+
|
|
11
|
+
import { DISABLE_DEPENDENTS_DIALOG } from '#meta';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Returns `requestDisable(pluginId)` that gates `manager.disable` on a
|
|
15
|
+
* confirmation prompt when the target has currently-enabled dependents.
|
|
16
|
+
*
|
|
17
|
+
* `PluginManager.disable` cascades by default; this hook is the UX safeguard
|
|
18
|
+
* around that. The caller supplies a `dispatch` callback that performs the
|
|
19
|
+
* actual disable (wrapping it with observability or any other side effects).
|
|
20
|
+
* When there are no enabled dependents, dispatch runs immediately; otherwise
|
|
21
|
+
* we open the shared {@link DISABLE_DEPENDENTS_DIALOG} surface — its
|
|
22
|
+
* `onConfirm` runs the dispatch and closes the dialog.
|
|
23
|
+
*
|
|
24
|
+
* Display-name resolution for the dialog is delegated to the registered
|
|
25
|
+
* plugin set (`Plugin.Meta.name`). Per-plugin i18n translations only load
|
|
26
|
+
* when a plugin is activated, so `meta.name` is the only label always
|
|
27
|
+
* available regardless of enabled state.
|
|
28
|
+
*/
|
|
29
|
+
export const useDisableConfirmation = (manager: PluginManager.PluginManager, dispatch: (id: string) => void) => {
|
|
30
|
+
const { invokePromise } = useOperationInvoker();
|
|
31
|
+
|
|
32
|
+
return useCallback(
|
|
33
|
+
(pluginId: string): void => {
|
|
34
|
+
const enabledDependents = manager.getDependents(pluginId, { transitive: true, enabledOnly: true });
|
|
35
|
+
if (enabledDependents.length === 0) {
|
|
36
|
+
dispatch(pluginId);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const resolveName = (id: string): string =>
|
|
40
|
+
manager.getPlugins().find((plugin) => plugin.meta.id === id)?.meta.name ?? id;
|
|
41
|
+
void invokePromise(LayoutOperation.UpdateDialog, {
|
|
42
|
+
subject: DISABLE_DEPENDENTS_DIALOG,
|
|
43
|
+
state: true,
|
|
44
|
+
type: 'alert',
|
|
45
|
+
props: {
|
|
46
|
+
pluginId,
|
|
47
|
+
dependents: enabledDependents,
|
|
48
|
+
onResolvePluginName: resolveName,
|
|
49
|
+
onConfirm: () => {
|
|
50
|
+
dispatch(pluginId);
|
|
51
|
+
void invokePromise(LayoutOperation.UpdateDialog, { state: false });
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
});
|
|
55
|
+
},
|
|
56
|
+
[manager, dispatch, invokePromise],
|
|
57
|
+
);
|
|
58
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useAtomValue } from '@effect-atom/atom-react';
|
|
6
|
+
|
|
7
|
+
import { type Registry } from '@dxos/app-framework';
|
|
8
|
+
import { usePluginManager } from '@dxos/app-framework/ui';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Reads the registry plugin catalog from `manager.pluginRegistry.plugins`.
|
|
12
|
+
* The catalog is loaded once when the plugin manager is constructed (with a
|
|
13
|
+
* provider) so this hook is purely reactive — subscribers re-render as the
|
|
14
|
+
* `{entries, loading, error}` state settles.
|
|
15
|
+
*/
|
|
16
|
+
export const useRegistryPlugins = (): Registry.PluginsState => {
|
|
17
|
+
const manager = usePluginManager();
|
|
18
|
+
return useAtomValue(manager.pluginRegistry.plugins);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Returns the live `Registry.Manager` from the plugin manager so callers can
|
|
23
|
+
* issue `listVersions` / `getPlugin` calls directly. The catalog state itself
|
|
24
|
+
* is best read through {@link useRegistryPlugins} above.
|
|
25
|
+
*/
|
|
26
|
+
export const useRegistryPluginProvider = (): Registry.Manager => {
|
|
27
|
+
const manager = usePluginManager();
|
|
28
|
+
return manager.pluginRegistry;
|
|
29
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { type Registry, UrlLoader } from '@dxos/app-framework';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Returns a set of plugin ids for which the registry has a newer version
|
|
9
|
+
* than what was recorded at install time.
|
|
10
|
+
*
|
|
11
|
+
* Only flags plugins where a version was recorded at install time AND it differs
|
|
12
|
+
* from the catalog's current version. Legacy installs (no version stored) are ignored.
|
|
13
|
+
*
|
|
14
|
+
* NOTE: this is intentionally NOT memoised. The installed-version side of the
|
|
15
|
+
* comparison lives in localStorage and is mutated by `manager.add` /
|
|
16
|
+
* `manager.remove` / `UrlLoader.setInstalledVersion` outside React's view, so a
|
|
17
|
+
* memo keyed on `entries` would happily return stale data for the rest of the
|
|
18
|
+
* session after an update completes (the bug that produced "Update button
|
|
19
|
+
* reappears after Updating…"). The loop is O(catalog size) sync reads — cheap
|
|
20
|
+
* enough to do every render.
|
|
21
|
+
*/
|
|
22
|
+
export const useUpdateAvailableIds = (entries: readonly Registry.Plugin[]): ReadonlySet<string> => {
|
|
23
|
+
const ids = new Set<string>();
|
|
24
|
+
for (const entry of entries) {
|
|
25
|
+
const installedVersion = UrlLoader.getInstalledVersion(entry.id);
|
|
26
|
+
if (installedVersion !== undefined && installedVersion !== entry.version) {
|
|
27
|
+
ids.add(entry.id);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return ids;
|
|
31
|
+
};
|
package/src/index.ts
CHANGED
package/src/meta.ts
CHANGED
|
@@ -2,17 +2,32 @@
|
|
|
2
2
|
// Copyright 2023 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import { type
|
|
5
|
+
import { type Plugin } from '@dxos/app-framework';
|
|
6
|
+
import { pinnedWorkspaceId } from '@dxos/app-toolkit';
|
|
7
|
+
import { trim } from '@dxos/util';
|
|
6
8
|
|
|
7
|
-
export const
|
|
8
|
-
|
|
9
|
-
// TODO(wittjosiah): Deck does not currently support `/` in ids.
|
|
10
|
-
// TODO(wittjosiah): This is a hack to prevent the previous deck from being set for pinned items.
|
|
11
|
-
// Ideally this should be worked into the data model in a generic way.
|
|
12
|
-
export const REGISTRY_ID = '!dxos:plugin-registry';
|
|
9
|
+
export const REGISTRY_ID = pinnedWorkspaceId('dxos:plugin-registry');
|
|
13
10
|
export const REGISTRY_KEY = 'plugin-registry';
|
|
14
11
|
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
// TODO(wittjosiah): Should this be a special separator or use the standard path separator?
|
|
13
|
+
const CATEGORY_SEPARATOR = '>';
|
|
14
|
+
|
|
15
|
+
/** Build a registry category node ID. */
|
|
16
|
+
export const registryCategoryId = (category: string): string => `${REGISTRY_KEY}${CATEGORY_SEPARATOR}${category}`;
|
|
17
|
+
|
|
18
|
+
/** Qualified graph path to a specific plugin node. */
|
|
19
|
+
export const getPluginPath = (pluginId: string): string => `root/${REGISTRY_ID}/${pluginId}`;
|
|
20
|
+
|
|
21
|
+
export const meta: Plugin.Meta = {
|
|
22
|
+
id: 'org.dxos.plugin.registry',
|
|
17
23
|
name: 'Plugins',
|
|
24
|
+
description: trim`
|
|
25
|
+
Plugin management system for discovering, installing, and configuring workspace extensions.
|
|
26
|
+
Browse available plugins and customize your workspace capabilities.
|
|
27
|
+
`,
|
|
28
|
+
icon: 'ph--squares-four--regular',
|
|
29
|
+
tags: ['system'],
|
|
18
30
|
};
|
|
31
|
+
|
|
32
|
+
/** Cascade-disable confirmation dialog surface id. */
|
|
33
|
+
export const DISABLE_DEPENDENTS_DIALOG = `${meta.id}.disable-dependents-dialog`;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
|
|
7
|
+
import { LayoutOperation, SettingsOperation, getSpacePath } from '@dxos/app-toolkit';
|
|
8
|
+
import { Operation } from '@dxos/compute';
|
|
9
|
+
|
|
10
|
+
import { REGISTRY_ID } from '#meta';
|
|
11
|
+
|
|
12
|
+
const handler: Operation.WithHandler<typeof SettingsOperation.OpenPluginRegistry> =
|
|
13
|
+
SettingsOperation.OpenPluginRegistry.pipe(
|
|
14
|
+
Operation.withHandler(
|
|
15
|
+
Effect.fnUntraced(function* () {
|
|
16
|
+
yield* Operation.invoke(LayoutOperation.SwitchWorkspace, { subject: getSpacePath(REGISTRY_ID) });
|
|
17
|
+
}),
|
|
18
|
+
),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
export default handler;
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Plugin } from '@dxos/app-framework';
|
|
6
|
+
|
|
7
|
+
import { meta } from './meta';
|
|
8
|
+
|
|
9
|
+
export const RegistryPlugin = Plugin.lazy(meta, () => import('#plugin'));
|
|
10
|
+
|
|
11
|
+
export { RegistryOperationHandlerSet } from './operations';
|
package/src/storage.ts
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as FileSystem from '@effect/platform/FileSystem';
|
|
6
|
+
import * as Path from '@effect/platform/Path';
|
|
7
|
+
import * as Effect from 'effect/Effect';
|
|
8
|
+
import * as Schema from 'effect/Schema';
|
|
9
|
+
import * as Yaml from 'yaml';
|
|
10
|
+
|
|
11
|
+
import { DX_CONFIG } from '@dxos/client-protocol';
|
|
12
|
+
|
|
13
|
+
// TODO(wittjosiah): Factor out to app-framework?
|
|
14
|
+
|
|
15
|
+
const PluginsSchema = Schema.Array(Schema.String);
|
|
16
|
+
|
|
17
|
+
/** CLI-only: path to plugins/<profile>.yml (sibling to profile/). */
|
|
18
|
+
const getPluginsConfigPath = (profile: string) => `${DX_CONFIG}/plugins/${profile}.yml`;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Load enabled plugins from plugins/<profile>.yml (sibling to profile/).
|
|
22
|
+
* Returns the list of enabled plugin IDs, or an empty array if the file doesn't exist.
|
|
23
|
+
*/
|
|
24
|
+
export const loadEnabledPlugins = Effect.fn(function* ({ profile }: { profile: string }) {
|
|
25
|
+
const fs = yield* FileSystem.FileSystem;
|
|
26
|
+
const pluginsPath = getPluginsConfigPath(profile);
|
|
27
|
+
|
|
28
|
+
const content = yield* fs
|
|
29
|
+
.readFileString(pluginsPath)
|
|
30
|
+
.pipe(Effect.catchTag('SystemError', () => Effect.succeed('[]')));
|
|
31
|
+
|
|
32
|
+
const raw = Yaml.parse(content);
|
|
33
|
+
const parsed = yield* Schema.decodeUnknown(PluginsSchema)(raw ?? []).pipe(
|
|
34
|
+
Effect.catchAll(() => Effect.succeed([] as string[])),
|
|
35
|
+
);
|
|
36
|
+
|
|
37
|
+
return parsed;
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Save enabled plugins to plugins/<profile>.yml.
|
|
42
|
+
*/
|
|
43
|
+
export const saveEnabledPlugins = Effect.fn(function* ({ profile, enabled }: { profile: string; enabled: string[] }) {
|
|
44
|
+
const fs = yield* FileSystem.FileSystem;
|
|
45
|
+
const path = yield* Path.Path;
|
|
46
|
+
const pluginsPath = getPluginsConfigPath(profile);
|
|
47
|
+
yield* fs.makeDirectory(path.dirname(pluginsPath), { recursive: true });
|
|
48
|
+
const encoded = Yaml.stringify(enabled);
|
|
49
|
+
yield* fs.writeFileString(pluginsPath, encoded);
|
|
50
|
+
});
|
package/src/translations.ts
CHANGED
|
@@ -4,26 +4,70 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Resource } from '@dxos/react-ui';
|
|
6
6
|
|
|
7
|
-
import { meta } from '
|
|
7
|
+
import { meta } from '#meta';
|
|
8
8
|
|
|
9
9
|
export const translations = [
|
|
10
10
|
{
|
|
11
11
|
'en-US': {
|
|
12
12
|
[meta.id]: {
|
|
13
|
-
'plugin
|
|
14
|
-
'details
|
|
15
|
-
'home
|
|
16
|
-
'source
|
|
17
|
-
'plugin
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
21
|
-
'
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'
|
|
13
|
+
'plugin.name': 'Registry',
|
|
14
|
+
'details.label': 'Details',
|
|
15
|
+
'home-page.label': 'Website',
|
|
16
|
+
'source.label': 'Source',
|
|
17
|
+
'plugin-registry.label': 'Plugins',
|
|
18
|
+
'plugin-settings.label': 'Plugin Settings',
|
|
19
|
+
'official-plugins.label': 'Official',
|
|
20
|
+
'installed-plugins.label': 'Enabled',
|
|
21
|
+
'recommended-plugins.label': 'Recommended',
|
|
22
|
+
'labs-plugins.label': 'Labs',
|
|
23
|
+
'registry-plugins.label': 'Registry',
|
|
24
|
+
'load-by-url.label': 'Load from URL',
|
|
25
|
+
'load-by-url-dialog.title': 'Load Plugin from URL',
|
|
26
|
+
'plugin-url.label': 'Plugin URL',
|
|
27
|
+
'load-plugin.label': 'Load Plugin',
|
|
28
|
+
'loading.label': 'Loading...',
|
|
29
|
+
'filter.label': 'Filter plugins',
|
|
30
|
+
'filter.placeholder': 'Filter…',
|
|
31
|
+
'registry.loading.label': 'Loading registry plugins…',
|
|
32
|
+
'registry.error.label': 'Failed to load registry plugins: {{message}}',
|
|
33
|
+
'registry.empty.label': 'No registry plugins available.',
|
|
34
|
+
'dev-plugin.section.title': 'Dev plugin',
|
|
35
|
+
'dev-plugin.description':
|
|
36
|
+
'Load a plugin from a local Vite dev server (the `composerPlugin` defaults to port 3967). Composer reattaches it on every reload, surviving HMR — useful when iterating on a community plugin against a live registry version.',
|
|
37
|
+
'dev-plugin.url.label': 'Manifest URL',
|
|
38
|
+
'dev-plugin.url.description':
|
|
39
|
+
'Defaults to the local dev server. Edit if your dev server runs on a different port.',
|
|
40
|
+
'dev-plugin.toggle.label': 'Dev plugin',
|
|
41
|
+
'dev-plugin.toggle.description':
|
|
42
|
+
'Stays on across reloads. If the dev server is offline at boot, a warning is logged and the next reload retries.',
|
|
43
|
+
'dev-plugin.enable.label': 'Enable',
|
|
44
|
+
'dev-plugin.disable.label': 'Disable',
|
|
45
|
+
'dev-plugin.busy.label': 'Loading…',
|
|
46
|
+
'dev-plugin.not-loaded.message':
|
|
47
|
+
'Dev plugin is enabled but not currently loaded. Check that the dev server is running, then reload.',
|
|
48
|
+
'coming-soon.label': 'Coming soon!',
|
|
49
|
+
'open-plugin-registry.label': 'Open plugin registry',
|
|
50
|
+
'install.label': 'Install',
|
|
51
|
+
'installing.label': 'Installing…',
|
|
52
|
+
'uninstall.label': 'Uninstall',
|
|
53
|
+
'update.label': 'Update',
|
|
54
|
+
'updating.label': 'Updating…',
|
|
55
|
+
'versions.label': 'Versions',
|
|
56
|
+
'installed.label': 'installed',
|
|
57
|
+
'install-version.label': 'Install version',
|
|
58
|
+
'failure-badge.label': 'Plugin failure details',
|
|
59
|
+
'failure-title.label': 'Plugin {{phase}} failed: {{reason}}',
|
|
60
|
+
'failure-phase-load.label': 'load',
|
|
61
|
+
'failure-phase-activation.label': 'activation',
|
|
62
|
+
'failure-reason-timeout.label': 'timed out',
|
|
63
|
+
'failure-reason-error.label': 'error',
|
|
64
|
+
'dependencies.label': 'Requires',
|
|
65
|
+
'dependents.label': 'Required by',
|
|
66
|
+
'disable-dependents-dialog.title': 'Disable dependent plugins?',
|
|
67
|
+
'disable-dependents-dialog.description':
|
|
68
|
+
'Disabling {{plugin}} will also disable the following plugins that depend on it:',
|
|
69
|
+
'disable-dependents-dialog.confirm.label': 'Disable',
|
|
70
|
+
'cancel.label': 'Cancel',
|
|
27
71
|
},
|
|
28
72
|
},
|
|
29
73
|
},
|
package/src/types.ts
CHANGED
|
@@ -2,12 +2,48 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { type Atom } from '@effect-atom/atom-react';
|
|
6
|
+
import * as Schema from 'effect/Schema';
|
|
7
|
+
|
|
8
|
+
import { Capability } from '@dxos/app-framework';
|
|
6
9
|
|
|
7
10
|
export const RegistrySettingsSchema = Schema.mutable(
|
|
8
11
|
Schema.Struct({
|
|
9
12
|
experimental: Schema.optional(Schema.Boolean),
|
|
13
|
+
/**
|
|
14
|
+
* Manifest URL for a plugin served by a local Vite dev server. The
|
|
15
|
+
* registry settings panel pre-fills this with `localhost:3967`; authors
|
|
16
|
+
* iterating on a different port can edit it.
|
|
17
|
+
*/
|
|
18
|
+
devPluginUrl: Schema.optional(Schema.String),
|
|
19
|
+
/**
|
|
20
|
+
* When true, the registry plugin attempts to load `devPluginUrl` on every
|
|
21
|
+
* app boot. Failures (dev server offline, manifest 404) are logged and
|
|
22
|
+
* the flag stays on so the next boot retries — the user explicitly turns
|
|
23
|
+
* it off when they're done iterating.
|
|
24
|
+
*/
|
|
25
|
+
devPluginEnabled: Schema.optional(Schema.Boolean),
|
|
10
26
|
}),
|
|
11
27
|
);
|
|
12
28
|
|
|
13
29
|
export type RegistrySettings = Schema.Schema.Type<typeof RegistrySettingsSchema>;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Per-plugin capabilities exposed by `@dxos/plugin-registry`.
|
|
33
|
+
*/
|
|
34
|
+
export namespace RegistryCapabilities {
|
|
35
|
+
export const Settings = Capability.make<Atom.Writable<RegistrySettings>>('@dxos/plugin-registry/settings');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export const RegistryTagType = Schema.Literal(
|
|
39
|
+
'new',
|
|
40
|
+
'beta',
|
|
41
|
+
'labs',
|
|
42
|
+
'popular',
|
|
43
|
+
'featured',
|
|
44
|
+
'experimental',
|
|
45
|
+
'registry',
|
|
46
|
+
'local',
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
export type RegistryTagType = Schema.Schema.Type<typeof RegistryTagType>;
|