@dxos/plugin-registry 0.8.4-main.8360d9e660 → 0.8.4-main.8baae0fced
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/{browser/cli/index.mjs → neutral/RegistryPlugin.node.mjs} +28 -97
- 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-MYLRMFLY.mjs +34 -0
- package/dist/lib/neutral/chunk-MYLRMFLY.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 +25 -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 +70 -0
- package/dist/lib/neutral/translations.mjs.map +7 -0
- package/dist/types/src/RegistryPlugin.d.ts +1 -0
- package/dist/types/src/RegistryPlugin.d.ts.map +1 -1
- package/dist/types/src/{cli/plugin.d.ts → RegistryPlugin.node.d.ts} +2 -1
- 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.map +1 -0
- 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.map +1 -0
- 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/{cli/commands → commands}/disable.d.ts +2 -2
- package/dist/types/src/commands/disable.d.ts.map +1 -0
- package/dist/types/src/{cli/commands → commands}/enable.d.ts +2 -2
- package/dist/types/src/commands/enable.d.ts.map +1 -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.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 +75 -2
- package/dist/types/src/components/PluginDetail/PluginDetail.d.ts.map +1 -1
- package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts +60 -16
- package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts.map +1 -1
- 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 +29 -2
- package/dist/types/src/components/PluginList/PluginItem.d.ts.map +1 -1
- package/dist/types/src/components/PluginList/PluginList.d.ts +18 -3
- package/dist/types/src/components/PluginList/PluginList.d.ts.map +1 -1
- package/dist/types/src/components/PluginList/PluginList.stories.d.ts +58 -16
- package/dist/types/src/components/PluginList/PluginList.stories.d.ts.map +1 -1
- 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 -0
- 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.map +1 -1
- package/dist/types/src/containers/PluginArticle/index.d.ts +1 -2
- package/dist/types/src/containers/PluginArticle/index.d.ts.map +1 -1
- 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 +5 -1
- package/dist/types/src/containers/index.d.ts.map +1 -1
- package/dist/types/src/hooks/index.d.ts +9 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useAutoTags.d.ts +20 -0
- package/dist/types/src/hooks/useAutoTags.d.ts.map +1 -0
- package/dist/types/src/hooks/useCatalogEntry.d.ts +11 -0
- package/dist/types/src/hooks/useCatalogEntry.d.ts.map +1 -0
- package/dist/types/src/hooks/useDisableConfirmation.d.ts +19 -0
- package/dist/types/src/hooks/useDisableConfirmation.d.ts.map +1 -0
- package/dist/types/src/hooks/useInstalledVersionTag.d.ts +18 -0
- package/dist/types/src/hooks/useInstalledVersionTag.d.ts.map +1 -0
- package/dist/types/src/hooks/usePluginActions.d.ts +36 -0
- package/dist/types/src/hooks/usePluginActions.d.ts.map +1 -0
- package/dist/types/src/hooks/useRegistryPlugins.d.ts +15 -0
- package/dist/types/src/hooks/useRegistryPlugins.d.ts.map +1 -0
- package/dist/types/src/hooks/useUpdateAvailable.d.ts +18 -0
- package/dist/types/src/hooks/useUpdateAvailable.d.ts.map +1 -0
- package/dist/types/src/hooks/useVersionPicker.d.ts +21 -0
- package/dist/types/src/hooks/useVersionPicker.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 +16 -1
- 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.map +1 -0
- package/dist/types/src/translations.d.ts +57 -16
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +30 -1
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +60 -37
- package/src/{cli/plugin.ts → RegistryPlugin.node.ts} +3 -1
- package/src/RegistryPlugin.test.ts +24 -0
- package/src/RegistryPlugin.tsx +13 -5
- package/src/RegistryPlugin.workerd.ts +11 -0
- package/src/capabilities/app-graph-builder.ts +200 -0
- package/src/capabilities/dev-plugin-loader.ts +39 -0
- package/src/capabilities/index.ts +11 -3
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/react-surface.tsx +145 -0
- package/src/capabilities/settings.ts +34 -0
- package/src/components/DisableDependentsAlert/DisableDependentsAlert.tsx +69 -0
- package/src/components/DisableDependentsAlert/index.ts +5 -0
- package/src/components/PluginDetail/PluginDetail.stories.tsx +5 -4
- package/src/components/PluginDetail/PluginDetail.tsx +320 -62
- package/src/components/PluginFailureBadge/PluginFailureBadge.tsx +68 -0
- package/src/components/PluginFailureBadge/index.ts +5 -0
- package/src/components/PluginList/PluginItem.tsx +117 -15
- package/src/components/PluginList/PluginList.stories.tsx +17 -14
- package/src/components/PluginList/PluginList.tsx +32 -4
- package/src/components/RegistrySettings/RegistrySettings.tsx +120 -0
- package/src/components/RegistrySettings/index.ts +5 -0
- package/src/components/index.ts +3 -0
- 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 +102 -13
- package/src/containers/PluginArticle/index.ts +1 -3
- 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 +5 -1
- package/src/hooks/index.ts +12 -0
- package/src/hooks/useAutoTags.ts +51 -0
- package/src/hooks/useCatalogEntry.ts +25 -0
- package/src/hooks/useDisableConfirmation.ts +58 -0
- package/src/hooks/useInstalledVersionTag.ts +41 -0
- package/src/hooks/usePluginActions.ts +174 -0
- package/src/hooks/useRegistryPlugins.ts +29 -0
- package/src/hooks/useUpdateAvailable.ts +31 -0
- package/src/hooks/useVersionPicker.ts +112 -0
- package/src/index.ts +1 -1
- package/src/meta.ts +26 -1
- package/src/operations/index.ts +7 -0
- package/src/operations/open-plugin-registry.ts +21 -0
- package/src/plugin.ts +11 -0
- package/src/translations.ts +62 -15
- package/src/types.ts +33 -1
- package/dist/lib/browser/PluginArticle-N2ITBUGD.mjs +0 -29
- package/dist/lib/browser/PluginArticle-N2ITBUGD.mjs.map +0 -7
- package/dist/lib/browser/PluginRegistry-DKO4UEYH.mjs +0 -78
- package/dist/lib/browser/PluginRegistry-DKO4UEYH.mjs.map +0 -7
- package/dist/lib/browser/app-graph-builder-AHS5BI4Y.mjs +0 -167
- package/dist/lib/browser/app-graph-builder-AHS5BI4Y.mjs.map +0 -7
- package/dist/lib/browser/chunk-2YWVCU2W.mjs +0 -191
- package/dist/lib/browser/chunk-2YWVCU2W.mjs.map +0 -7
- package/dist/lib/browser/chunk-KMUDYITM.mjs +0 -21
- package/dist/lib/browser/chunk-KMUDYITM.mjs.map +0 -7
- package/dist/lib/browser/cli/index.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -65
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/operation-resolver-PK4B2BZQ.mjs +0 -25
- package/dist/lib/browser/operation-resolver-PK4B2BZQ.mjs.map +0 -7
- package/dist/lib/browser/react-surface-ZBP7YXKO.mjs +0 -85
- package/dist/lib/browser/react-surface-ZBP7YXKO.mjs.map +0 -7
- package/dist/lib/node-esm/PluginArticle-3NEYDKN5.mjs +0 -30
- package/dist/lib/node-esm/PluginArticle-3NEYDKN5.mjs.map +0 -7
- package/dist/lib/node-esm/PluginRegistry-BYKWC732.mjs +0 -79
- package/dist/lib/node-esm/PluginRegistry-BYKWC732.mjs.map +0 -7
- package/dist/lib/node-esm/app-graph-builder-DBDL5WWF.mjs +0 -168
- package/dist/lib/node-esm/app-graph-builder-DBDL5WWF.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-BOCFM775.mjs +0 -192
- package/dist/lib/node-esm/chunk-BOCFM775.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-ENSNUFQK.mjs +0 -23
- package/dist/lib/node-esm/chunk-ENSNUFQK.mjs.map +0 -7
- package/dist/lib/node-esm/cli/index.mjs +0 -223
- package/dist/lib/node-esm/cli/index.mjs.map +0 -7
- package/dist/lib/node-esm/index.mjs +0 -66
- package/dist/lib/node-esm/index.mjs.map +0 -7
- package/dist/lib/node-esm/meta.json +0 -1
- package/dist/lib/node-esm/operation-resolver-CCHN4NBP.mjs +0 -26
- package/dist/lib/node-esm/operation-resolver-CCHN4NBP.mjs.map +0 -7
- package/dist/lib/node-esm/react-surface-FJHMLGZF.mjs +0 -86
- package/dist/lib/node-esm/react-surface-FJHMLGZF.mjs.map +0 -7
- package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +0 -1
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts +0 -3
- package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/operation-resolver/index.d.ts +0 -3
- package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +0 -5
- package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
- package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
- package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
- package/dist/types/src/cli/commands/disable.d.ts.map +0 -1
- package/dist/types/src/cli/commands/enable.d.ts.map +0 -1
- package/dist/types/src/cli/commands/index.d.ts.map +0 -1
- package/dist/types/src/cli/commands/list.d.ts +0 -7
- package/dist/types/src/cli/commands/list.d.ts.map +0 -1
- package/dist/types/src/cli/commands/util.d.ts.map +0 -1
- package/dist/types/src/cli/index.d.ts +0 -3
- package/dist/types/src/cli/index.d.ts.map +0 -1
- package/dist/types/src/cli/plugin.d.ts.map +0 -1
- package/dist/types/src/cli/storage.d.ts.map +0 -1
- package/dist/types/src/containers/PluginRegistry/PluginRegistry.d.ts +0 -8
- package/dist/types/src/containers/PluginRegistry/PluginRegistry.d.ts.map +0 -1
- package/dist/types/src/containers/PluginRegistry/index.d.ts +0 -3
- package/dist/types/src/containers/PluginRegistry/index.d.ts.map +0 -1
- package/src/capabilities/app-graph-builder/app-graph-builder.ts +0 -137
- package/src/capabilities/app-graph-builder/index.ts +0 -7
- package/src/capabilities/operation-resolver/index.ts +0 -7
- package/src/capabilities/operation-resolver/operation-resolver.ts +0 -24
- package/src/capabilities/react-surface/index.ts +0 -7
- package/src/capabilities/react-surface/react-surface.tsx +0 -88
- package/src/cli/index.ts +0 -6
- package/src/containers/PluginRegistry/PluginRegistry.tsx +0 -86
- package/src/containers/PluginRegistry/index.ts +0 -7
- /package/dist/types/src/capabilities/{app-graph-builder/app-graph-builder.d.ts → app-graph-builder.d.ts} +0 -0
- /package/dist/types/src/capabilities/{react-surface/react-surface.d.ts → react-surface.d.ts} +0 -0
- /package/dist/types/src/{cli/commands → commands}/index.d.ts +0 -0
- /package/dist/types/src/{cli/commands → commands}/util.d.ts +0 -0
- /package/dist/types/src/{cli/storage.d.ts → storage.d.ts} +0 -0
- /package/src/{cli/commands → commands}/disable.ts +0 -0
- /package/src/{cli/commands → commands}/enable.ts +0 -0
- /package/src/{cli/commands → commands}/index.ts +0 -0
- /package/src/{cli/commands → commands}/list.ts +0 -0
- /package/src/{cli/commands → commands}/util.ts +0 -0
- /package/src/{cli/storage.ts → storage.ts} +0 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import React, { useCallback, useRef, useState } from 'react';
|
|
7
|
+
|
|
8
|
+
import { usePluginManager } from '@dxos/app-framework/ui';
|
|
9
|
+
import { runAndForwardErrors } from '@dxos/effect';
|
|
10
|
+
import { Button, Dialog, Input, useTranslation } from '@dxos/react-ui';
|
|
11
|
+
|
|
12
|
+
import { meta } from '../../meta';
|
|
13
|
+
|
|
14
|
+
export const LOAD_PLUGIN_DIALOG = `${meta.id}.LoadPluginDialog`;
|
|
15
|
+
|
|
16
|
+
export const LoadPluginDialog = () => {
|
|
17
|
+
const manager = usePluginManager();
|
|
18
|
+
const { t } = useTranslation(meta.id);
|
|
19
|
+
const [url, setUrl] = useState('');
|
|
20
|
+
const [loading, setLoading] = useState(false);
|
|
21
|
+
const [error, setError] = useState<string | null>(null);
|
|
22
|
+
const closeRef = useRef<HTMLButtonElement | null>(null);
|
|
23
|
+
|
|
24
|
+
const handleLoad = useCallback(() => {
|
|
25
|
+
const trimmed = url.trim();
|
|
26
|
+
if (!trimmed) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
setLoading(true);
|
|
31
|
+
setError(null);
|
|
32
|
+
|
|
33
|
+
void Effect.gen(function* () {
|
|
34
|
+
const plugin = yield* manager.add(trimmed);
|
|
35
|
+
yield* manager.enable(plugin.meta.id);
|
|
36
|
+
closeRef.current?.click();
|
|
37
|
+
}).pipe(
|
|
38
|
+
Effect.catchAll((err) =>
|
|
39
|
+
Effect.sync(() => {
|
|
40
|
+
setError(String(err));
|
|
41
|
+
}),
|
|
42
|
+
),
|
|
43
|
+
Effect.tap(() => Effect.sync(() => setLoading(false))),
|
|
44
|
+
runAndForwardErrors,
|
|
45
|
+
);
|
|
46
|
+
}, [url, manager]);
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<Dialog.Content>
|
|
50
|
+
<Dialog.Header>
|
|
51
|
+
<Dialog.Title>{t('load-by-url-dialog.title')}</Dialog.Title>
|
|
52
|
+
<Dialog.Close asChild>
|
|
53
|
+
<Dialog.CloseIconButton ref={closeRef} />
|
|
54
|
+
</Dialog.Close>
|
|
55
|
+
</Dialog.Header>
|
|
56
|
+
<Dialog.Body>
|
|
57
|
+
{/* TODO(burdon): Form section. */}
|
|
58
|
+
<div className='flex flex-col gap-4'>
|
|
59
|
+
<Input.Root validationValence={error ? 'error' : undefined}>
|
|
60
|
+
<Input.Label>{t('plugin-url.label')}</Input.Label>
|
|
61
|
+
<Input.TextInput
|
|
62
|
+
placeholder='https://example.com/manifest.json'
|
|
63
|
+
value={url}
|
|
64
|
+
onChange={(event) => {
|
|
65
|
+
setUrl(event.target.value);
|
|
66
|
+
setError(null);
|
|
67
|
+
}}
|
|
68
|
+
onKeyDown={(event) => {
|
|
69
|
+
if (event.key === 'Enter') {
|
|
70
|
+
handleLoad();
|
|
71
|
+
}
|
|
72
|
+
}}
|
|
73
|
+
disabled={loading}
|
|
74
|
+
autoFocus
|
|
75
|
+
/>
|
|
76
|
+
{error && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
|
|
77
|
+
</Input.Root>
|
|
78
|
+
<div className='flex justify-end'>
|
|
79
|
+
<Button variant='primary' disabled={!url.trim() || loading} onClick={handleLoad}>
|
|
80
|
+
{loading ? t('loading.label') : t('load-plugin.label')}
|
|
81
|
+
</Button>
|
|
82
|
+
</div>
|
|
83
|
+
</div>
|
|
84
|
+
</Dialog.Body>
|
|
85
|
+
</Dialog.Content>
|
|
86
|
+
);
|
|
87
|
+
};
|
|
@@ -2,27 +2,116 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { useAtomValue } from '@effect-atom/atom-react';
|
|
6
|
+
import React, { useCallback, useMemo } from 'react';
|
|
6
7
|
|
|
7
8
|
import { type Plugin } from '@dxos/app-framework';
|
|
8
|
-
import { usePluginManager } from '@dxos/app-framework/ui';
|
|
9
|
-
import {
|
|
9
|
+
import { useOperationInvoker, usePluginManager } from '@dxos/app-framework/ui';
|
|
10
|
+
import { LayoutOperation } from '@dxos/app-toolkit';
|
|
10
11
|
|
|
11
|
-
import { PluginDetail } from '
|
|
12
|
+
import { PluginDetail } from '#components';
|
|
13
|
+
import { getPluginPath } from '#meta';
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
useCatalogEntry,
|
|
17
|
+
useInstalledVersionTag,
|
|
18
|
+
usePluginActions,
|
|
19
|
+
useRegistryPluginProvider,
|
|
20
|
+
useRemotePluginIds,
|
|
21
|
+
useVersionPicker,
|
|
22
|
+
} from '../../hooks';
|
|
12
23
|
|
|
13
|
-
// TODO(burdon): Convert to ECHO type.
|
|
14
24
|
export type PluginArticleProps = { subject: Plugin.Plugin };
|
|
15
25
|
|
|
16
26
|
export const PluginArticle = ({ subject: plugin }: PluginArticleProps) => {
|
|
27
|
+
const pluginId = plugin.meta.id;
|
|
17
28
|
const manager = usePluginManager();
|
|
18
|
-
const
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
29
|
+
const plugins = useAtomValue(manager.plugins);
|
|
30
|
+
const remotePluginIds = useRemotePluginIds();
|
|
31
|
+
const provider = useRegistryPluginProvider();
|
|
32
|
+
const { invokePromise } = useOperationInvoker();
|
|
33
|
+
|
|
34
|
+
const { catalogEntry, moduleUrl, repo } = useCatalogEntry(pluginId);
|
|
35
|
+
const { installedVersionTag, syncInstalledVersion } = useInstalledVersionTag(pluginId, plugins);
|
|
36
|
+
const { pickerVersions, selectedVersionTag, setSelectedVersionTag } = useVersionPicker({
|
|
37
|
+
provider,
|
|
38
|
+
pluginId,
|
|
39
|
+
repo,
|
|
40
|
+
moduleUrl,
|
|
41
|
+
installedVersionTag,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const enabled = manager.getEnabled().includes(pluginId);
|
|
45
|
+
const failed = useAtomValue(manager.failed);
|
|
46
|
+
const failure = useMemo(() => failed.find((entry) => entry.id === pluginId), [failed, pluginId]);
|
|
47
|
+
const isInstalled = useMemo(() => plugins.some((candidate) => candidate.meta.id === pluginId), [plugins, pluginId]);
|
|
48
|
+
const isCore = manager.getCore().includes(pluginId);
|
|
49
|
+
const canUninstall = isInstalled && !isCore && remotePluginIds.has(pluginId);
|
|
50
|
+
const hasUpdate =
|
|
51
|
+
isInstalled && !!catalogEntry && !!installedVersionTag && installedVersionTag !== catalogEntry.version;
|
|
52
|
+
|
|
53
|
+
// Recompute graph slices whenever the plugin list changes, so installs /
|
|
54
|
+
// removals through other surfaces (or this article's own actions) keep the
|
|
55
|
+
// detail view in sync.
|
|
56
|
+
const dependencies = useMemo(
|
|
57
|
+
() => manager.getDependencies(pluginId, { transitive: false }),
|
|
58
|
+
[manager, pluginId, plugins],
|
|
59
|
+
);
|
|
60
|
+
const dependents = useMemo(
|
|
61
|
+
() => manager.getDependents(pluginId, { transitive: false }),
|
|
62
|
+
[manager, pluginId, plugins],
|
|
25
63
|
);
|
|
26
64
|
|
|
27
|
-
|
|
65
|
+
// Resolves a plugin id to its display name. A plugin's translations are
|
|
66
|
+
// only loaded when it is activated, so the always-available `meta.name`
|
|
67
|
+
// from the registered plugin set is the right source for chip labels.
|
|
68
|
+
const handleResolvePluginName = useCallback(
|
|
69
|
+
(id: string): string => plugins.find((candidate) => candidate.meta.id === id)?.meta.name ?? id,
|
|
70
|
+
[plugins],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const handleNavigateToPlugin = useCallback(
|
|
74
|
+
(targetId: string) => {
|
|
75
|
+
void invokePromise(LayoutOperation.Open, {
|
|
76
|
+
subject: [getPluginPath(targetId)],
|
|
77
|
+
});
|
|
78
|
+
},
|
|
79
|
+
[invokePromise],
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
const actions = usePluginActions({
|
|
83
|
+
manager,
|
|
84
|
+
pluginId,
|
|
85
|
+
isInstalled,
|
|
86
|
+
moduleUrl,
|
|
87
|
+
catalogEntry,
|
|
88
|
+
pickerVersions,
|
|
89
|
+
selectedVersionTag,
|
|
90
|
+
syncInstalledVersion,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<PluginDetail
|
|
95
|
+
plugin={plugin}
|
|
96
|
+
enabled={enabled}
|
|
97
|
+
installing={actions.installing}
|
|
98
|
+
updating={actions.updating}
|
|
99
|
+
hasUpdate={hasUpdate}
|
|
100
|
+
installedVersionTag={installedVersionTag}
|
|
101
|
+
selectedVersionTag={selectedVersionTag}
|
|
102
|
+
versions={pickerVersions}
|
|
103
|
+
dependencies={dependencies}
|
|
104
|
+
dependents={dependents}
|
|
105
|
+
failure={failure}
|
|
106
|
+
onOpenSpec={actions.handleOpenSpec}
|
|
107
|
+
onEnabledChange={actions.handleEnableChange}
|
|
108
|
+
onInstall={!isInstalled && moduleUrl ? actions.handleInstall : undefined}
|
|
109
|
+
onInstallVersion={pickerVersions.length > 0 ? actions.handleInstallVersion : undefined}
|
|
110
|
+
onNavigateToPlugin={handleNavigateToPlugin}
|
|
111
|
+
onResolvePluginName={handleResolvePluginName}
|
|
112
|
+
onUninstall={canUninstall ? actions.handleUninstall : undefined}
|
|
113
|
+
onUpdate={hasUpdate ? actions.handleUpdate : undefined}
|
|
114
|
+
onVersionChange={setSelectedVersionTag}
|
|
115
|
+
/>
|
|
116
|
+
);
|
|
28
117
|
};
|
|
@@ -0,0 +1,192 @@
|
|
|
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, useMemo, useState } from 'react';
|
|
8
|
+
|
|
9
|
+
import { type Registry, Plugin, UrlLoader } from '@dxos/app-framework';
|
|
10
|
+
import { useOperationInvoker, usePluginManager } from '@dxos/app-framework/ui';
|
|
11
|
+
import { runAndForwardErrors } from '@dxos/effect';
|
|
12
|
+
import { ObservabilityOperation } from '@dxos/plugin-observability';
|
|
13
|
+
import { useTranslation } from '@dxos/react-ui';
|
|
14
|
+
import { composable } from '@dxos/ui-theme';
|
|
15
|
+
|
|
16
|
+
import { meta } from '#meta';
|
|
17
|
+
|
|
18
|
+
import { useAutoTags, useRegistryPlugins, useUpdateAvailableIds } from '../../hooks';
|
|
19
|
+
import { BaseRegistryArticle } from '../BaseRegistryArticle';
|
|
20
|
+
|
|
21
|
+
const sortEntries = (a: Registry.Plugin, b: Registry.Plugin) => (a.name ?? a.id).localeCompare(b.name ?? b.id);
|
|
22
|
+
|
|
23
|
+
const sortPlugins = (a: Plugin.Plugin, b: Plugin.Plugin) =>
|
|
24
|
+
(a.meta.name ?? a.meta.id).localeCompare(b.meta.name ?? b.meta.id);
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Turns a registry catalog entry into a minimal Plugin object so we can reuse
|
|
28
|
+
* {@link BaseRegistryArticle} for rendering. The synthesized plugin has no
|
|
29
|
+
* modules — it exists only for display until the user installs it.
|
|
30
|
+
*/
|
|
31
|
+
const toDisplayPlugin = (plugin: Registry.Plugin): Plugin.Plugin =>
|
|
32
|
+
({
|
|
33
|
+
[Plugin.PluginTypeId]: Plugin.PluginTypeId,
|
|
34
|
+
meta: {
|
|
35
|
+
id: plugin.id,
|
|
36
|
+
name: plugin.name,
|
|
37
|
+
description: plugin.description,
|
|
38
|
+
homePage: plugin.homePage,
|
|
39
|
+
source: plugin.source,
|
|
40
|
+
screenshots: plugin.screenshots,
|
|
41
|
+
tags: plugin.tags,
|
|
42
|
+
icon: plugin.icon,
|
|
43
|
+
iconHue: plugin.iconHue,
|
|
44
|
+
},
|
|
45
|
+
modules: [],
|
|
46
|
+
}) as Plugin.Plugin;
|
|
47
|
+
|
|
48
|
+
export type PublicRegistryArticleProps = {
|
|
49
|
+
id: string;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
export const PublicRegistryArticle = composable<HTMLDivElement, PublicRegistryArticleProps>(
|
|
53
|
+
({ id, ...props }, forwardedRef) => {
|
|
54
|
+
const { t } = useTranslation(meta.id);
|
|
55
|
+
const manager = usePluginManager();
|
|
56
|
+
const { invoke } = useOperationInvoker();
|
|
57
|
+
const { entries, loading, error } = useRegistryPlugins();
|
|
58
|
+
const plugins = useAtomValue(manager.plugins);
|
|
59
|
+
const installedIds = useMemo(() => plugins.map((plugin) => plugin.meta.id), [plugins]);
|
|
60
|
+
const extraTagsById = useAutoTags(entries);
|
|
61
|
+
|
|
62
|
+
// Snapshot of installed plugin ids at mount time. Used to sort installed
|
|
63
|
+
// plugins to the top without having newly-installed rows jump up mid-session.
|
|
64
|
+
const [installedSnapshot] = useState<ReadonlySet<string>>(
|
|
65
|
+
() => new Set(manager.getPlugins().map((plugin) => plugin.meta.id)),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
const [installingIds, setInstallingIds] = useState<readonly string[]>([]);
|
|
69
|
+
const [updatingIds, setUpdatingIds] = useState<readonly string[]>([]);
|
|
70
|
+
const updateAvailableIds = useUpdateAvailableIds(entries);
|
|
71
|
+
|
|
72
|
+
const sortedEntries = useMemo(() => [...entries].sort(sortEntries), [entries]);
|
|
73
|
+
const moduleUrlById = useMemo(() => {
|
|
74
|
+
const map: Record<string, string> = {};
|
|
75
|
+
for (const entry of sortedEntries) {
|
|
76
|
+
map[entry.id] = entry.moduleUrl;
|
|
77
|
+
}
|
|
78
|
+
return map;
|
|
79
|
+
}, [sortedEntries]);
|
|
80
|
+
|
|
81
|
+
// Maps plugin id → current registry version (i.e. plugin.version from the catalog).
|
|
82
|
+
const versionById = useMemo(() => {
|
|
83
|
+
const map: Record<string, string> = {};
|
|
84
|
+
for (const entry of sortedEntries) {
|
|
85
|
+
map[entry.id] = entry.version;
|
|
86
|
+
}
|
|
87
|
+
return map;
|
|
88
|
+
}, [sortedEntries]);
|
|
89
|
+
|
|
90
|
+
// Single list with installed-at-mount plugins sorted to the top. Sort order
|
|
91
|
+
// is based on the mount-time snapshot so rows don't jump as the user
|
|
92
|
+
// installs plugins during the session.
|
|
93
|
+
const items = useMemo(() => {
|
|
94
|
+
const catalogIds = new Set(sortedEntries.map((entry) => entry.id));
|
|
95
|
+
const remoteIds = new Set(UrlLoader.getRemoteEntries().map((entry) => entry.id));
|
|
96
|
+
const fromCatalog = sortedEntries.map(toDisplayPlugin);
|
|
97
|
+
const fromUrlOnly = plugins.filter((plugin) => remoteIds.has(plugin.meta.id) && !catalogIds.has(plugin.meta.id));
|
|
98
|
+
const installedFirst = [...fromCatalog, ...fromUrlOnly].sort((a, b) => {
|
|
99
|
+
const aInstalled = installedSnapshot.has(a.meta.id);
|
|
100
|
+
const bInstalled = installedSnapshot.has(b.meta.id);
|
|
101
|
+
if (aInstalled !== bInstalled) {
|
|
102
|
+
return aInstalled ? -1 : 1;
|
|
103
|
+
}
|
|
104
|
+
return sortPlugins(a, b);
|
|
105
|
+
});
|
|
106
|
+
return installedFirst;
|
|
107
|
+
}, [sortedEntries, installedSnapshot, plugins]);
|
|
108
|
+
|
|
109
|
+
const handleInstall = useCallback(
|
|
110
|
+
(pluginId: string) => {
|
|
111
|
+
const moduleUrl = moduleUrlById[pluginId];
|
|
112
|
+
const version = versionById[pluginId];
|
|
113
|
+
if (!moduleUrl) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
setInstallingIds((prev) => (prev.includes(pluginId) ? prev : [...prev, pluginId]));
|
|
117
|
+
void Effect.gen(function* () {
|
|
118
|
+
const plugin = yield* manager.add(moduleUrl);
|
|
119
|
+
yield* manager.enable(plugin.meta.id);
|
|
120
|
+
// Persist the installed version for future update detection.
|
|
121
|
+
if (version) {
|
|
122
|
+
UrlLoader.setInstalledVersion(plugin.meta.id, version);
|
|
123
|
+
}
|
|
124
|
+
yield* invoke(ObservabilityOperation.SendEvent, {
|
|
125
|
+
name: 'plugins.install',
|
|
126
|
+
properties: { plugin: plugin.meta.id, source: 'registry' },
|
|
127
|
+
});
|
|
128
|
+
}).pipe(
|
|
129
|
+
Effect.ensuring(Effect.sync(() => setInstallingIds((prev) => prev.filter((pid) => pid !== pluginId)))),
|
|
130
|
+
runAndForwardErrors,
|
|
131
|
+
);
|
|
132
|
+
},
|
|
133
|
+
[invoke, manager, moduleUrlById, versionById],
|
|
134
|
+
);
|
|
135
|
+
|
|
136
|
+
const handleUpdate = useCallback(
|
|
137
|
+
(pluginId: string) => {
|
|
138
|
+
const moduleUrl = moduleUrlById[pluginId];
|
|
139
|
+
const version = versionById[pluginId];
|
|
140
|
+
if (!moduleUrl) {
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
setUpdatingIds((prev) => (prev.includes(pluginId) ? prev : [...prev, pluginId]));
|
|
144
|
+
void Effect.gen(function* () {
|
|
145
|
+
// Unload the old version then re-install from the new URL.
|
|
146
|
+
yield* manager.remove(pluginId);
|
|
147
|
+
const plugin = yield* manager.add(moduleUrl);
|
|
148
|
+
yield* manager.enable(plugin.meta.id);
|
|
149
|
+
if (version) {
|
|
150
|
+
UrlLoader.setInstalledVersion(plugin.meta.id, version);
|
|
151
|
+
}
|
|
152
|
+
yield* invoke(ObservabilityOperation.SendEvent, {
|
|
153
|
+
name: 'plugins.update',
|
|
154
|
+
properties: { plugin: plugin.meta.id, source: 'registry' },
|
|
155
|
+
});
|
|
156
|
+
}).pipe(
|
|
157
|
+
Effect.ensuring(Effect.sync(() => setUpdatingIds((prev) => prev.filter((pid) => pid !== pluginId)))),
|
|
158
|
+
runAndForwardErrors,
|
|
159
|
+
);
|
|
160
|
+
},
|
|
161
|
+
[invoke, manager, moduleUrlById, versionById],
|
|
162
|
+
);
|
|
163
|
+
|
|
164
|
+
const empty = error ? (
|
|
165
|
+
<div className='p-4 text-description'>{t('registry.error.label', { message: error.message })}</div>
|
|
166
|
+
) : loading ? (
|
|
167
|
+
<div className='p-4 text-description'>{t('registry.loading.label')}</div>
|
|
168
|
+
) : (
|
|
169
|
+
<div className='p-4 text-description'>{t('registry.empty.label')}</div>
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
return (
|
|
173
|
+
<BaseRegistryArticle
|
|
174
|
+
{...props}
|
|
175
|
+
id={id}
|
|
176
|
+
source='registry'
|
|
177
|
+
plugins={items}
|
|
178
|
+
installed={installedIds}
|
|
179
|
+
installing={installingIds}
|
|
180
|
+
updating={updatingIds}
|
|
181
|
+
updateAvailableIds={updateAvailableIds}
|
|
182
|
+
extraTagsById={extraTagsById}
|
|
183
|
+
onInstall={handleInstall}
|
|
184
|
+
onUpdate={handleUpdate}
|
|
185
|
+
empty={empty}
|
|
186
|
+
ref={forwardedRef}
|
|
187
|
+
/>
|
|
188
|
+
);
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
PublicRegistryArticle.displayName = 'PublicRegistryArticle';
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2023 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useAtomValue } from '@effect-atom/atom-react';
|
|
6
|
+
import React, { useMemo } from 'react';
|
|
7
|
+
|
|
8
|
+
import { type Plugin, type PluginManager } from '@dxos/app-framework';
|
|
9
|
+
import { usePluginManager } from '@dxos/app-framework/ui';
|
|
10
|
+
import { composable } from '@dxos/ui-theme';
|
|
11
|
+
|
|
12
|
+
import { BaseRegistryArticle } from '../BaseRegistryArticle';
|
|
13
|
+
|
|
14
|
+
const sortByPluginMeta = (a: Plugin.Plugin, b: Plugin.Plugin) =>
|
|
15
|
+
(a.meta.name ?? a.meta.id).localeCompare(b.meta.name ?? b.meta.id);
|
|
16
|
+
|
|
17
|
+
export type RegistryArticleProps = {
|
|
18
|
+
id: string;
|
|
19
|
+
plugins: Plugin.Plugin[];
|
|
20
|
+
/**
|
|
21
|
+
* Map from plugin id → display-only tags (e.g. `registry`, `local`) computed by the caller.
|
|
22
|
+
*/
|
|
23
|
+
extraTagsById?: Record<string, readonly string[]>;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
export const RegistryArticle = composable<HTMLDivElement, RegistryArticleProps>(
|
|
27
|
+
({ id, plugins: pluginsProp, extraTagsById, ...props }, forwardedRef) => {
|
|
28
|
+
const manager = usePluginManager();
|
|
29
|
+
const failed = useAtomValue(manager.failed);
|
|
30
|
+
const plugins = useMemo(() => [...pluginsProp].sort(sortByPluginMeta), [pluginsProp]);
|
|
31
|
+
const failuresById = useMemo(
|
|
32
|
+
() =>
|
|
33
|
+
failed.reduce<Record<string, PluginManager.PluginFailure>>((acc, failure) => {
|
|
34
|
+
acc[failure.id] = failure;
|
|
35
|
+
return acc;
|
|
36
|
+
}, {}),
|
|
37
|
+
[failed],
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<BaseRegistryArticle
|
|
42
|
+
{...props}
|
|
43
|
+
id={id}
|
|
44
|
+
plugins={plugins}
|
|
45
|
+
extraTagsById={extraTagsById}
|
|
46
|
+
failuresById={failuresById}
|
|
47
|
+
ref={forwardedRef}
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
50
|
+
},
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
RegistryArticle.displayName = 'RegistryArticle';
|
|
@@ -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
|
+
};
|
package/src/containers/index.ts
CHANGED
|
@@ -4,5 +4,9 @@
|
|
|
4
4
|
|
|
5
5
|
import { type ComponentType, lazy } from 'react';
|
|
6
6
|
|
|
7
|
+
export { LOAD_PLUGIN_DIALOG } from './LoadPluginDialog/LoadPluginDialog';
|
|
8
|
+
export const LoadPluginDialog: ComponentType<any> = lazy(() => import('./LoadPluginDialog'));
|
|
7
9
|
export const PluginArticle: ComponentType<any> = lazy(() => import('./PluginArticle'));
|
|
8
|
-
export const
|
|
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,12 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
export * from './useAutoTags';
|
|
6
|
+
export * from './useCatalogEntry';
|
|
7
|
+
export * from './useDisableConfirmation';
|
|
8
|
+
export * from './useInstalledVersionTag';
|
|
9
|
+
export * from './usePluginActions';
|
|
10
|
+
export * from './useRegistryPlugins';
|
|
11
|
+
export * from './useUpdateAvailable';
|
|
12
|
+
export * from './useVersionPicker';
|
|
@@ -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,25 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type Registry } from '@dxos/app-framework';
|
|
8
|
+
|
|
9
|
+
import { useRegistryPlugins } from './useRegistryPlugins';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Resolves the registry catalog entry for a plugin by id, plus the two fields
|
|
13
|
+
* (`moduleUrl`, `repo`) the rest of the article cares about.
|
|
14
|
+
*/
|
|
15
|
+
export const useCatalogEntry = (
|
|
16
|
+
pluginId: string,
|
|
17
|
+
): {
|
|
18
|
+
catalogEntry: Registry.Plugin | undefined;
|
|
19
|
+
moduleUrl: string | undefined;
|
|
20
|
+
repo: string | undefined;
|
|
21
|
+
} => {
|
|
22
|
+
const { entries } = useRegistryPlugins();
|
|
23
|
+
const catalogEntry = useMemo(() => entries.find((entry) => entry.id === pluginId), [entries, pluginId]);
|
|
24
|
+
return { catalogEntry, moduleUrl: catalogEntry?.moduleUrl, repo: catalogEntry?.repo };
|
|
25
|
+
};
|