@dxos/plugin-registry 0.10.0 → 0.11.1
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/dist/lib/RegistryPlugin.mjs +15 -0
- package/dist/lib/RegistryPlugin.mjs.map +1 -0
- package/dist/lib/RegistryPlugin.node.mjs +770 -0
- package/dist/lib/RegistryPlugin.node.mjs.map +1 -0
- package/dist/lib/RegistryPlugin.workerd.mjs +8 -0
- package/dist/lib/RegistryPlugin.workerd.mjs.map +1 -0
- package/dist/lib/capabilities.mjs +11 -0
- package/dist/lib/capabilities.mjs.map +1 -0
- package/dist/lib/chunk-BaseRegistryArticle.mjs +103 -0
- package/dist/lib/chunk-BaseRegistryArticle.mjs.map +1 -0
- package/dist/lib/chunk-LoadPluginDialog.mjs +2 -0
- package/dist/lib/chunk-PluginArticle.mjs +87 -0
- package/dist/lib/chunk-PluginArticle.mjs.map +1 -0
- package/dist/lib/chunk-PublicRegistryArticle.mjs +152 -0
- package/dist/lib/chunk-PublicRegistryArticle.mjs.map +1 -0
- package/dist/lib/chunk-RegistryArticle.mjs +29 -0
- package/dist/lib/chunk-RegistryArticle.mjs.map +1 -0
- package/dist/lib/chunk-RegistrySettingsContainer.mjs +37 -0
- package/dist/lib/chunk-RegistrySettingsContainer.mjs.map +1 -0
- package/dist/lib/chunk-app-graph-builder.mjs +202 -0
- package/dist/lib/chunk-app-graph-builder.mjs.map +1 -0
- package/dist/lib/chunk-categories.mjs +23 -0
- package/dist/lib/chunk-categories.mjs.map +1 -0
- package/dist/lib/chunk-components.mjs +537 -0
- package/dist/lib/chunk-components.mjs.map +1 -0
- package/dist/lib/chunk-containers.mjs +80 -0
- package/dist/lib/chunk-containers.mjs.map +1 -0
- package/dist/lib/chunk-dev-plugin-loader.mjs +40 -0
- package/dist/lib/chunk-dev-plugin-loader.mjs.map +1 -0
- package/dist/lib/chunk-hooks.mjs +345 -0
- package/dist/lib/chunk-hooks.mjs.map +1 -0
- package/dist/lib/chunk-meta.mjs +40 -0
- package/dist/lib/chunk-meta.mjs.map +1 -0
- package/dist/lib/chunk-open-plugin-registry.mjs +12 -0
- package/dist/lib/chunk-open-plugin-registry.mjs.map +1 -0
- package/dist/lib/chunk-operation-handler.mjs +11 -0
- package/dist/lib/chunk-operation-handler.mjs.map +1 -0
- package/dist/lib/chunk-react-surface.mjs +109 -0
- package/dist/lib/chunk-react-surface.mjs.map +1 -0
- package/dist/lib/chunk-settings.mjs +24 -0
- package/dist/lib/chunk-settings.mjs.map +1 -0
- package/dist/lib/chunk-storage.mjs +36 -0
- package/dist/lib/chunk-storage.mjs.map +1 -0
- package/dist/lib/components.mjs +2 -0
- package/dist/lib/containers.mjs +2 -0
- package/dist/lib/index.mjs +3 -0
- package/dist/lib/meta.mjs +2 -0
- package/dist/lib/operations.mjs +7 -0
- package/dist/lib/operations.mjs.map +1 -0
- package/dist/lib/plugin.mjs +9 -0
- package/dist/lib/plugin.mjs.map +1 -0
- package/dist/lib/translations.mjs +66 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/lib/types.mjs +29 -0
- package/dist/lib/types.mjs.map +1 -0
- package/dist/types/src/capabilities/RegistryCategoryArticle.d.ts +11 -0
- package/dist/types/src/capabilities/RegistryCategoryArticle.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
- package/dist/types/src/capabilities/dev-plugin-loader.d.ts +1 -1
- package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/settings.d.ts +1 -1
- package/dist/types/src/categories.d.ts.map +1 -1
- package/dist/types/src/categories.test.d.ts +2 -0
- package/dist/types/src/categories.test.d.ts.map +1 -0
- package/dist/types/src/commands/registry/util.d.ts.map +1 -1
- package/dist/types/src/components/PluginDetail/PluginDetail.d.ts +1 -1
- package/dist/types/src/components/PluginDetail/PluginDetail.d.ts.map +1 -1
- package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts +1 -1
- package/dist/types/src/containers/BaseRegistryArticle/BaseRegistryArticle.d.ts +1 -1
- package/dist/types/src/containers/BaseRegistryArticle/BaseRegistryArticle.d.ts.map +1 -1
- package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts +4 -1
- package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts.map +1 -1
- package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts +4 -1
- package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts.map +1 -1
- package/dist/types/src/containers/PublicRegistryArticle/PublicRegistryArticle.d.ts +1 -1
- package/dist/types/src/containers/PublicRegistryArticle/PublicRegistryArticle.d.ts.map +1 -1
- package/dist/types/src/containers/RegistryArticle/RegistryArticle.d.ts +1 -1
- package/dist/types/src/containers/RegistryArticle/RegistryArticle.d.ts.map +1 -1
- package/dist/types/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.d.ts +4 -7
- package/dist/types/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.d.ts.map +1 -1
- package/dist/types/src/hooks/usePluginActions.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +0 -3
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/operations/open-plugin-registry.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +2 -2
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +38 -37
- package/src/capabilities/RegistryCategoryArticle.tsx +46 -0
- package/src/capabilities/app-graph-builder.ts +80 -74
- package/src/capabilities/react-surface.ts +85 -0
- package/src/categories.test.ts +45 -0
- package/src/categories.ts +10 -7
- package/src/commands/registry/util.ts +1 -1
- package/src/components/PluginList/PluginItem.tsx +5 -3
- package/src/components/PluginList/PluginList.stories.tsx +1 -1
- package/src/components/RegistrySettings/RegistrySettings.tsx +2 -2
- package/src/containers/BaseRegistryArticle/BaseRegistryArticle.tsx +2 -2
- package/src/containers/LoadPluginDialog/LoadPluginDialog.tsx +2 -0
- package/src/containers/PluginArticle/PluginArticle.tsx +5 -1
- package/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.tsx +2 -0
- package/src/hooks/usePluginActions.ts +8 -4
- package/src/meta.ts +5 -8
- package/src/operations/open-plugin-registry.ts +2 -2
- package/src/types.ts +2 -1
- package/dist/lib/neutral/RegistryPlugin.mjs +0 -27
- package/dist/lib/neutral/RegistryPlugin.mjs.map +0 -7
- package/dist/lib/neutral/RegistryPlugin.node.mjs +0 -844
- package/dist/lib/neutral/RegistryPlugin.node.mjs.map +0 -7
- package/dist/lib/neutral/RegistryPlugin.workerd.mjs +0 -10
- package/dist/lib/neutral/RegistryPlugin.workerd.mjs.map +0 -7
- package/dist/lib/neutral/chunk-XK2JMH3H.mjs +0 -33
- package/dist/lib/neutral/chunk-XK2JMH3H.mjs.map +0 -7
- package/dist/lib/neutral/chunk-ZY37XLVF.mjs +0 -46
- package/dist/lib/neutral/chunk-ZY37XLVF.mjs.map +0 -7
- package/dist/lib/neutral/index.mjs +0 -25
- package/dist/lib/neutral/index.mjs.map +0 -7
- package/dist/lib/neutral/meta.json +0 -1
- package/dist/lib/neutral/open-plugin-registry-ID7LV3SF.mjs +0 -15
- package/dist/lib/neutral/open-plugin-registry-ID7LV3SF.mjs.map +0 -7
- package/dist/lib/neutral/plugin.mjs +0 -18
- package/dist/lib/neutral/plugin.mjs.map +0 -7
- package/dist/lib/neutral/translations.mjs +0 -72
- package/dist/lib/neutral/translations.mjs.map +0 -7
- package/src/capabilities/react-surface.tsx +0 -123
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dxos/plugin-registry",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.1",
|
|
4
4
|
"description": "Braneframe plugin gallery",
|
|
5
5
|
"homepage": "https://dxos.org",
|
|
6
6
|
"bugs": "https://github.com/dxos/dxos/issues",
|
|
@@ -25,9 +25,9 @@
|
|
|
25
25
|
"default": "./src/RegistryPlugin.tsx"
|
|
26
26
|
},
|
|
27
27
|
"types": "./dist/types/src/RegistryPlugin.d.ts",
|
|
28
|
-
"workerd": "./dist/lib/
|
|
29
|
-
"node": "./dist/lib/
|
|
30
|
-
"default": "./dist/lib/
|
|
28
|
+
"workerd": "./dist/lib/RegistryPlugin.workerd.mjs",
|
|
29
|
+
"node": "./dist/lib/RegistryPlugin.node.mjs",
|
|
30
|
+
"default": "./dist/lib/RegistryPlugin.mjs"
|
|
31
31
|
},
|
|
32
32
|
"#translations": "./src/translations.ts",
|
|
33
33
|
"#types": "./src/types.ts"
|
|
@@ -36,17 +36,17 @@
|
|
|
36
36
|
".": {
|
|
37
37
|
"source": "./src/index.ts",
|
|
38
38
|
"types": "./dist/types/src/index.d.ts",
|
|
39
|
-
"
|
|
39
|
+
"import": "./dist/lib/index.mjs"
|
|
40
40
|
},
|
|
41
41
|
"./plugin": {
|
|
42
42
|
"source": "./src/plugin.ts",
|
|
43
43
|
"types": "./dist/types/src/plugin.d.ts",
|
|
44
|
-
"
|
|
44
|
+
"import": "./dist/lib/plugin.mjs"
|
|
45
45
|
},
|
|
46
46
|
"./translations": {
|
|
47
47
|
"source": "./src/translations.ts",
|
|
48
48
|
"types": "./dist/types/src/translations.d.ts",
|
|
49
|
-
"
|
|
49
|
+
"import": "./dist/lib/translations.mjs"
|
|
50
50
|
}
|
|
51
51
|
},
|
|
52
52
|
"types": "dist/types/src/index.d.ts",
|
|
@@ -56,31 +56,32 @@
|
|
|
56
56
|
"src"
|
|
57
57
|
],
|
|
58
58
|
"dependencies": {
|
|
59
|
+
"@effect-atom/atom": "^0.5.3",
|
|
59
60
|
"@effect-atom/atom-react": "^0.5.0",
|
|
60
61
|
"@effect/cli": "0.75.2",
|
|
61
62
|
"yaml": "^2.8.2",
|
|
62
|
-
"@dxos/app-
|
|
63
|
-
"@dxos/app-
|
|
64
|
-
"@dxos/client": "0.
|
|
65
|
-
"@dxos/client-protocol": "0.
|
|
66
|
-
"@dxos/compute": "0.
|
|
67
|
-
"@dxos/
|
|
68
|
-
"@dxos/
|
|
69
|
-
"@dxos/
|
|
70
|
-
"@dxos/
|
|
71
|
-
"@dxos/
|
|
72
|
-
"@dxos/
|
|
73
|
-
"@dxos/
|
|
74
|
-
"@dxos/
|
|
75
|
-
"@dxos/
|
|
76
|
-
"@dxos/
|
|
77
|
-
"@dxos/
|
|
78
|
-
"@dxos/
|
|
79
|
-
"@dxos/
|
|
80
|
-
"@dxos/react-ui-
|
|
81
|
-
"@dxos/react-ui-
|
|
82
|
-
"@dxos/
|
|
83
|
-
"@dxos/
|
|
63
|
+
"@dxos/app-framework": "0.11.1",
|
|
64
|
+
"@dxos/app-toolkit": "0.11.1",
|
|
65
|
+
"@dxos/client": "0.11.1",
|
|
66
|
+
"@dxos/client-protocol": "0.11.1",
|
|
67
|
+
"@dxos/compute": "0.11.1",
|
|
68
|
+
"@dxos/cli-util": "0.11.1",
|
|
69
|
+
"@dxos/context": "0.11.1",
|
|
70
|
+
"@dxos/echo": "0.11.1",
|
|
71
|
+
"@dxos/edge-client": "0.11.1",
|
|
72
|
+
"@dxos/effect": "0.11.1",
|
|
73
|
+
"@dxos/invariant": "0.11.1",
|
|
74
|
+
"@dxos/keys": "0.11.1",
|
|
75
|
+
"@dxos/link": "0.11.1",
|
|
76
|
+
"@dxos/log": "0.11.1",
|
|
77
|
+
"@dxos/plugin-graph": "0.11.1",
|
|
78
|
+
"@dxos/plugin-observability": "0.11.1",
|
|
79
|
+
"@dxos/protocols": "0.11.1",
|
|
80
|
+
"@dxos/react-ui-form": "0.11.1",
|
|
81
|
+
"@dxos/react-ui-list": "0.11.1",
|
|
82
|
+
"@dxos/react-ui-markdown": "0.11.1",
|
|
83
|
+
"@dxos/react-ui-mosaic": "0.11.1",
|
|
84
|
+
"@dxos/util": "0.11.1"
|
|
84
85
|
},
|
|
85
86
|
"devDependencies": {
|
|
86
87
|
"@effect-atom/atom-react": "^0.5.0",
|
|
@@ -90,12 +91,12 @@
|
|
|
90
91
|
"effect": "3.21.4",
|
|
91
92
|
"react": "~19.2.7",
|
|
92
93
|
"react-dom": "~19.2.7",
|
|
93
|
-
"vite": "^8.
|
|
94
|
-
"@dxos/plugin-testing": "0.
|
|
95
|
-
"@dxos/random": "0.
|
|
96
|
-
"@dxos/react-ui": "0.
|
|
97
|
-
"@dxos/storybook-utils": "0.
|
|
98
|
-
"@dxos/ui-theme": "0.
|
|
94
|
+
"vite": "^8.1.4",
|
|
95
|
+
"@dxos/plugin-testing": "0.11.1",
|
|
96
|
+
"@dxos/random": "0.11.1",
|
|
97
|
+
"@dxos/react-ui": "0.11.1",
|
|
98
|
+
"@dxos/storybook-utils": "0.11.1",
|
|
99
|
+
"@dxos/ui-theme": "0.11.1"
|
|
99
100
|
},
|
|
100
101
|
"peerDependencies": {
|
|
101
102
|
"@effect-atom/atom-react": "^0.5.0",
|
|
@@ -103,8 +104,8 @@
|
|
|
103
104
|
"effect": "3.21.4",
|
|
104
105
|
"react": "~19.2.7",
|
|
105
106
|
"react-dom": "~19.2.7",
|
|
106
|
-
"@dxos/react-ui": "0.
|
|
107
|
-
"@dxos/ui-theme": "0.
|
|
107
|
+
"@dxos/react-ui": "^0.11.1",
|
|
108
|
+
"@dxos/ui-theme": "^0.11.1"
|
|
108
109
|
},
|
|
109
110
|
"publishConfig": {
|
|
110
111
|
"access": "public"
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { usePluginManager } from '@dxos/app-framework/ui';
|
|
8
|
+
|
|
9
|
+
import { RegistryArticle } from '#containers';
|
|
10
|
+
|
|
11
|
+
import { type PluginPredicate, getCategoryPredicate } from '../categories';
|
|
12
|
+
import { useAutoTags, useRegistryPlugins, useRemotePluginIds } from '../hooks';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Resolves the {@link PluginPredicate} for a registry category against the live plugin list.
|
|
16
|
+
* Shared with the graph builder via {@link getCategoryPredicate} so the category lists and their counts agree.
|
|
17
|
+
*/
|
|
18
|
+
const useCategoryPredicate = (category: string): PluginPredicate => {
|
|
19
|
+
const manager = usePluginManager();
|
|
20
|
+
const remoteIds = useRemotePluginIds();
|
|
21
|
+
const core = useMemo(() => manager.getCore(), [manager]);
|
|
22
|
+
const enabled = useMemo(() => manager.getEnabled(), [manager]);
|
|
23
|
+
return useMemo(
|
|
24
|
+
() => getCategoryPredicate(category, { core, enabled, remoteIds }),
|
|
25
|
+
[category, core, enabled, remoteIds],
|
|
26
|
+
);
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export type RegistryCategoryArticleProps = {
|
|
30
|
+
category: string;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Renders the {@link RegistryArticle} surface for one registry category. Centralises the
|
|
35
|
+
* `usePluginManager` + `useRegistryPlugins` + `useAutoTags` wiring shared by every category surface,
|
|
36
|
+
* none of which can live in a surface's `props` mapper.
|
|
37
|
+
*/
|
|
38
|
+
export const RegistryCategoryArticle = ({ category }: RegistryCategoryArticleProps) => {
|
|
39
|
+
const manager = usePluginManager();
|
|
40
|
+
const filter = useCategoryPredicate(category);
|
|
41
|
+
const { entries } = useRegistryPlugins();
|
|
42
|
+
const extraTagsById = useAutoTags(entries);
|
|
43
|
+
const filtered = useMemo(() => manager.getPlugins().filter(filter), [manager, filter]);
|
|
44
|
+
|
|
45
|
+
return <RegistryArticle id={category} plugins={filtered} extraTagsById={extraTagsById} />;
|
|
46
|
+
};
|
|
@@ -11,7 +11,7 @@ import { DXN } from '@dxos/keys';
|
|
|
11
11
|
import { GraphBuilder, Node, NodeMatcher } from '@dxos/plugin-graph';
|
|
12
12
|
import { Position } from '@dxos/util';
|
|
13
13
|
|
|
14
|
-
import { REGISTRY_ID,
|
|
14
|
+
import { REGISTRY_ID, meta } from '#meta';
|
|
15
15
|
|
|
16
16
|
import { getCategoryPredicate, getRemotePluginIds } from '../categories';
|
|
17
17
|
import { LOAD_PLUGIN_DIALOG } from '../containers';
|
|
@@ -63,6 +63,27 @@ export default Capability.makeModule(
|
|
|
63
63
|
GraphBuilder.createExtension({
|
|
64
64
|
id: 'registry',
|
|
65
65
|
match: NodeMatcher.whenRoot,
|
|
66
|
+
// REGISTRY_ID is a pinned workspace (the URL's workspace anchor), so it carries no key of its
|
|
67
|
+
// own; its category and plugin children are the addressable planks (see `categories`/`plugins`).
|
|
68
|
+
connector: () =>
|
|
69
|
+
Effect.succeed([
|
|
70
|
+
Node.make({
|
|
71
|
+
id: REGISTRY_ID,
|
|
72
|
+
type: meta.profile.key,
|
|
73
|
+
properties: {
|
|
74
|
+
label: ['plugin-registry.label', { ns: meta.profile.key }],
|
|
75
|
+
icon: 'ph--squares-four--regular',
|
|
76
|
+
disposition: 'pin-end',
|
|
77
|
+
position: Position.first,
|
|
78
|
+
testId: 'treeView.pluginRegistry',
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
]),
|
|
82
|
+
}),
|
|
83
|
+
GraphBuilder.createExtension({
|
|
84
|
+
id: 'categories',
|
|
85
|
+
url: { key: 'category', kind: 'item', path: [] },
|
|
86
|
+
match: NodeMatcher.whenId(`root/${REGISTRY_ID}`),
|
|
66
87
|
connector: (_node, get) => {
|
|
67
88
|
const manager = capabilities.get(Capabilities.PluginManager);
|
|
68
89
|
const plugins = get(manager.plugins);
|
|
@@ -77,82 +98,64 @@ export default Capability.makeModule(
|
|
|
77
98
|
|
|
78
99
|
return Effect.succeed([
|
|
79
100
|
Node.make({
|
|
80
|
-
id:
|
|
81
|
-
type:
|
|
101
|
+
id: 'bundled',
|
|
102
|
+
type: 'category',
|
|
103
|
+
data: 'bundled',
|
|
82
104
|
properties: {
|
|
83
|
-
label: ['
|
|
105
|
+
label: ['bundled-plugins.label', { ns: meta.profile.key }],
|
|
84
106
|
icon: 'ph--squares-four--regular',
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
107
|
+
testId: 'pluginRegistry.bundled',
|
|
108
|
+
count: categoryCount('bundled'),
|
|
109
|
+
},
|
|
110
|
+
}),
|
|
111
|
+
Node.make({
|
|
112
|
+
id: 'installed',
|
|
113
|
+
type: 'category',
|
|
114
|
+
data: 'installed',
|
|
115
|
+
properties: {
|
|
116
|
+
label: ['installed-plugins.label', { ns: meta.profile.key }],
|
|
117
|
+
icon: 'ph--check--regular',
|
|
118
|
+
testId: 'pluginRegistry.installed',
|
|
119
|
+
count: categoryCount('installed'),
|
|
120
|
+
},
|
|
121
|
+
}),
|
|
122
|
+
Node.make({
|
|
123
|
+
id: 'recommended',
|
|
124
|
+
type: 'category',
|
|
125
|
+
data: 'recommended',
|
|
126
|
+
properties: {
|
|
127
|
+
label: ['recommended-plugins.label', { ns: meta.profile.key }],
|
|
128
|
+
icon: 'ph--star--regular',
|
|
129
|
+
testId: 'pluginRegistry.recommended',
|
|
130
|
+
count: categoryCount('recommended'),
|
|
131
|
+
},
|
|
132
|
+
}),
|
|
133
|
+
Node.make({
|
|
134
|
+
id: 'labs',
|
|
135
|
+
type: 'category',
|
|
136
|
+
data: 'labs',
|
|
137
|
+
properties: {
|
|
138
|
+
label: ['labs-plugins.label', { ns: meta.profile.key }],
|
|
139
|
+
icon: 'ph--flask--regular',
|
|
140
|
+
testId: 'pluginRegistry.labs',
|
|
141
|
+
count: categoryCount('labs'),
|
|
88
142
|
},
|
|
89
|
-
nodes: [
|
|
90
|
-
Node.make({
|
|
91
|
-
id: registryCategoryId('bundled'),
|
|
92
|
-
type: 'category',
|
|
93
|
-
data: registryCategoryId('bundled'),
|
|
94
|
-
properties: {
|
|
95
|
-
label: ['bundled-plugins.label', { ns: meta.profile.key }],
|
|
96
|
-
icon: 'ph--squares-four--regular',
|
|
97
|
-
key: REGISTRY_KEY,
|
|
98
|
-
testId: 'pluginRegistry.bundled',
|
|
99
|
-
count: categoryCount(registryCategoryId('bundled')),
|
|
100
|
-
},
|
|
101
|
-
}),
|
|
102
|
-
Node.make({
|
|
103
|
-
id: registryCategoryId('installed'),
|
|
104
|
-
type: 'category',
|
|
105
|
-
data: registryCategoryId('installed'),
|
|
106
|
-
properties: {
|
|
107
|
-
label: ['installed-plugins.label', { ns: meta.profile.key }],
|
|
108
|
-
icon: 'ph--check--regular',
|
|
109
|
-
key: REGISTRY_KEY,
|
|
110
|
-
testId: 'pluginRegistry.installed',
|
|
111
|
-
count: categoryCount(registryCategoryId('installed')),
|
|
112
|
-
},
|
|
113
|
-
}),
|
|
114
|
-
Node.make({
|
|
115
|
-
id: registryCategoryId('recommended'),
|
|
116
|
-
type: 'category',
|
|
117
|
-
data: registryCategoryId('recommended'),
|
|
118
|
-
properties: {
|
|
119
|
-
label: ['recommended-plugins.label', { ns: meta.profile.key }],
|
|
120
|
-
icon: 'ph--star--regular',
|
|
121
|
-
key: REGISTRY_KEY,
|
|
122
|
-
testId: 'pluginRegistry.recommended',
|
|
123
|
-
count: categoryCount(registryCategoryId('recommended')),
|
|
124
|
-
},
|
|
125
|
-
}),
|
|
126
|
-
Node.make({
|
|
127
|
-
id: registryCategoryId('labs'),
|
|
128
|
-
type: 'category',
|
|
129
|
-
data: registryCategoryId('labs'),
|
|
130
|
-
properties: {
|
|
131
|
-
label: ['labs-plugins.label', { ns: meta.profile.key }],
|
|
132
|
-
icon: 'ph--flask--regular',
|
|
133
|
-
key: REGISTRY_KEY,
|
|
134
|
-
testId: 'pluginRegistry.labs',
|
|
135
|
-
count: categoryCount(registryCategoryId('labs')),
|
|
136
|
-
},
|
|
137
|
-
}),
|
|
138
|
-
...(registryCount > 0
|
|
139
|
-
? [
|
|
140
|
-
Node.make({
|
|
141
|
-
id: registryCategoryId('registry'),
|
|
142
|
-
type: 'category',
|
|
143
|
-
data: registryCategoryId('registry'),
|
|
144
|
-
properties: {
|
|
145
|
-
label: ['registry-plugins.label', { ns: meta.profile.key }],
|
|
146
|
-
icon: 'ph--users-three--regular',
|
|
147
|
-
key: REGISTRY_KEY,
|
|
148
|
-
testId: 'pluginRegistry.registry',
|
|
149
|
-
count: registryCount,
|
|
150
|
-
},
|
|
151
|
-
}),
|
|
152
|
-
]
|
|
153
|
-
: []),
|
|
154
|
-
],
|
|
155
143
|
}),
|
|
144
|
+
...(registryCount > 0
|
|
145
|
+
? [
|
|
146
|
+
Node.make({
|
|
147
|
+
id: 'registry',
|
|
148
|
+
type: 'category',
|
|
149
|
+
data: 'registry',
|
|
150
|
+
properties: {
|
|
151
|
+
label: ['registry-plugins.label', { ns: meta.profile.key }],
|
|
152
|
+
icon: 'ph--users-three--regular',
|
|
153
|
+
testId: 'pluginRegistry.registry',
|
|
154
|
+
count: registryCount,
|
|
155
|
+
},
|
|
156
|
+
}),
|
|
157
|
+
]
|
|
158
|
+
: []),
|
|
156
159
|
]);
|
|
157
160
|
},
|
|
158
161
|
}),
|
|
@@ -179,6 +182,7 @@ export default Capability.makeModule(
|
|
|
179
182
|
}),
|
|
180
183
|
GraphBuilder.createExtension({
|
|
181
184
|
id: 'plugins',
|
|
185
|
+
url: { key: 'registry', kind: 'item', path: [] },
|
|
182
186
|
match: NodeMatcher.whenId(`root/${REGISTRY_ID}`),
|
|
183
187
|
connector: (_node, get) => {
|
|
184
188
|
const manager = capabilities.get(Capabilities.PluginManager);
|
|
@@ -199,7 +203,9 @@ export default Capability.makeModule(
|
|
|
199
203
|
|
|
200
204
|
const registryEntries = get(manager.pluginRegistry.plugins).entries;
|
|
201
205
|
const registryNodes = registryEntries
|
|
202
|
-
|
|
206
|
+
// `profile.key` is the bare NSID on both sides; comparing against a `dxn:`-prefixed
|
|
207
|
+
// URI here would never match and would duplicate every installed plugin's node.
|
|
208
|
+
.filter((entry) => !installedIds.has(entry.profile.key))
|
|
203
209
|
.map((entry) => {
|
|
204
210
|
const plugin = toDisplayPlugin(entry);
|
|
205
211
|
return Node.make({
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import * as Effect from 'effect/Effect';
|
|
6
|
+
import { type ComponentProps } from 'react';
|
|
7
|
+
|
|
8
|
+
import { Capabilities, Capability, Plugin } from '@dxos/app-framework';
|
|
9
|
+
import { Surface } from '@dxos/app-framework/ui';
|
|
10
|
+
import { AppSurface } from '@dxos/app-toolkit/ui';
|
|
11
|
+
|
|
12
|
+
import { DisableDependentsAlert } from '#components';
|
|
13
|
+
import {
|
|
14
|
+
LOAD_PLUGIN_DIALOG,
|
|
15
|
+
LoadPluginDialog,
|
|
16
|
+
PluginArticle,
|
|
17
|
+
PublicRegistryArticle,
|
|
18
|
+
RegistrySettingsContainer,
|
|
19
|
+
} from '#containers';
|
|
20
|
+
import { DISABLE_DEPENDENTS_DIALOG, meta } from '#meta';
|
|
21
|
+
|
|
22
|
+
import { RegistryCategoryArticle } from './RegistryCategoryArticle';
|
|
23
|
+
|
|
24
|
+
export default Capability.makeModule(() =>
|
|
25
|
+
Effect.succeed(
|
|
26
|
+
Capability.contributes(Capabilities.ReactSurface, [
|
|
27
|
+
Surface.create({
|
|
28
|
+
id: 'bundled',
|
|
29
|
+
filter: AppSurface.literal(AppSurface.Article, 'bundled'),
|
|
30
|
+
component: RegistryCategoryArticle,
|
|
31
|
+
props: () => ({ category: 'bundled' }),
|
|
32
|
+
}),
|
|
33
|
+
Surface.create({
|
|
34
|
+
id: 'installed',
|
|
35
|
+
filter: AppSurface.literal(AppSurface.Article, 'installed'),
|
|
36
|
+
component: RegistryCategoryArticle,
|
|
37
|
+
props: () => ({ category: 'installed' }),
|
|
38
|
+
}),
|
|
39
|
+
Surface.create({
|
|
40
|
+
id: 'recommended',
|
|
41
|
+
filter: AppSurface.literal(AppSurface.Article, 'recommended'),
|
|
42
|
+
component: RegistryCategoryArticle,
|
|
43
|
+
props: () => ({ category: 'recommended' }),
|
|
44
|
+
}),
|
|
45
|
+
Surface.create({
|
|
46
|
+
id: 'labs',
|
|
47
|
+
filter: AppSurface.literal(AppSurface.Article, 'labs'),
|
|
48
|
+
component: RegistryCategoryArticle,
|
|
49
|
+
props: () => ({ category: 'labs' }),
|
|
50
|
+
}),
|
|
51
|
+
Surface.create({
|
|
52
|
+
id: 'registry',
|
|
53
|
+
filter: AppSurface.literal(AppSurface.Article, 'registry'),
|
|
54
|
+
component: PublicRegistryArticle,
|
|
55
|
+
props: () => ({ id: 'registry' }),
|
|
56
|
+
}),
|
|
57
|
+
Surface.create({
|
|
58
|
+
id: 'pluginDetails',
|
|
59
|
+
filter: AppSurface.subject(AppSurface.Article, Plugin.isPlugin),
|
|
60
|
+
component: PluginArticle,
|
|
61
|
+
props: ({ data: { subject } }) => ({ subject }),
|
|
62
|
+
}),
|
|
63
|
+
Surface.create({
|
|
64
|
+
id: LOAD_PLUGIN_DIALOG,
|
|
65
|
+
filter: AppSurface.component(AppSurface.Dialog, LOAD_PLUGIN_DIALOG),
|
|
66
|
+
component: LoadPluginDialog,
|
|
67
|
+
}),
|
|
68
|
+
Surface.create({
|
|
69
|
+
id: DISABLE_DEPENDENTS_DIALOG,
|
|
70
|
+
filter: AppSurface.component<ComponentProps<typeof DisableDependentsAlert>>(
|
|
71
|
+
AppSurface.Dialog,
|
|
72
|
+
DISABLE_DEPENDENTS_DIALOG,
|
|
73
|
+
),
|
|
74
|
+
component: DisableDependentsAlert,
|
|
75
|
+
props: ({ data: { props } }) => ({ ...props }),
|
|
76
|
+
}),
|
|
77
|
+
Surface.create({
|
|
78
|
+
id: 'pluginSettings',
|
|
79
|
+
filter: AppSurface.settings(AppSurface.Article, meta.profile.key),
|
|
80
|
+
component: RegistrySettingsContainer,
|
|
81
|
+
props: ({ data: { subject } }) => ({ subject }),
|
|
82
|
+
}),
|
|
83
|
+
]),
|
|
84
|
+
),
|
|
85
|
+
);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { describe, test } from 'vitest';
|
|
6
|
+
|
|
7
|
+
import { Plugin } from '@dxos/app-framework';
|
|
8
|
+
|
|
9
|
+
import { getCategoryPredicate } from './categories';
|
|
10
|
+
|
|
11
|
+
const context = { core: [], enabled: [], remoteIds: new Set<string>() };
|
|
12
|
+
|
|
13
|
+
describe('getCategoryPredicate', () => {
|
|
14
|
+
test('recommended selects the beta and alpha tiers', ({ expect }) => {
|
|
15
|
+
const predicate = getCategoryPredicate('recommended', context);
|
|
16
|
+
expect(predicate(makePlugin('beta-plugin', ['beta']))).toBe(true);
|
|
17
|
+
expect(predicate(makePlugin('alpha-plugin', ['alpha']))).toBe(true);
|
|
18
|
+
expect(predicate(makePlugin('labs-plugin', ['labs']))).toBe(false);
|
|
19
|
+
// An untagged plugin makes no quality claim, so it must not be recommended.
|
|
20
|
+
expect(predicate(makePlugin('untagged-plugin'))).toBe(false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('recommended matches a tier tag in any position', ({ expect }) => {
|
|
24
|
+
const predicate = getCategoryPredicate('recommended', context);
|
|
25
|
+
expect(predicate(makePlugin('alpha-connector', ['alpha', 'connector']))).toBe(true);
|
|
26
|
+
expect(predicate(makePlugin('labs-connector', ['labs', 'connector']))).toBe(false);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
test('recommended excludes core and remote plugins', ({ expect }) => {
|
|
30
|
+
const key = 'org.dxos.plugin.example';
|
|
31
|
+
const plugin = makePlugin(key, ['beta']);
|
|
32
|
+
expect(getCategoryPredicate('recommended', { ...context, core: [key] })(plugin)).toBe(false);
|
|
33
|
+
expect(getCategoryPredicate('recommended', { ...context, remoteIds: new Set([key]) })(plugin)).toBe(false);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('labs selects the labs tier regardless of core membership', ({ expect }) => {
|
|
37
|
+
const key = 'labs-plugin';
|
|
38
|
+
const predicate = getCategoryPredicate('labs', { ...context, core: [key] });
|
|
39
|
+
expect(predicate(makePlugin(key, ['labs']))).toBe(true);
|
|
40
|
+
expect(predicate(makePlugin('alpha-plugin', ['alpha']))).toBe(false);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const makePlugin = (key: string, tags?: string[]) =>
|
|
45
|
+
Plugin.make(Plugin.define({ profile: { key, name: key, tags } }))();
|
package/src/categories.ts
CHANGED
|
@@ -4,10 +4,11 @@
|
|
|
4
4
|
|
|
5
5
|
import { type Plugin, UrlLoader } from '@dxos/app-framework';
|
|
6
6
|
|
|
7
|
-
import { registryCategoryId } from './meta';
|
|
8
|
-
|
|
9
7
|
export type PluginPredicate = (plugin: Plugin.Plugin) => boolean;
|
|
10
8
|
|
|
9
|
+
/** Quality tiers surfaced in `recommended`; an untagged plugin is excluded rather than assumed ready. */
|
|
10
|
+
const RECOMMENDED_TIERS: readonly string[] = ['beta', 'alpha'];
|
|
11
|
+
|
|
11
12
|
export type CategoryFilterContext = {
|
|
12
13
|
/** Core (bundled-by-default) plugin ids. */
|
|
13
14
|
core: readonly string[];
|
|
@@ -26,14 +27,16 @@ export const getCategoryPredicate = (
|
|
|
26
27
|
{ core, enabled, remoteIds }: CategoryFilterContext,
|
|
27
28
|
): PluginPredicate => {
|
|
28
29
|
switch (category) {
|
|
29
|
-
case
|
|
30
|
+
case 'bundled':
|
|
30
31
|
return ({ meta }) => !core.includes(meta.profile.key) && !remoteIds.has(meta.profile.key);
|
|
31
|
-
case
|
|
32
|
+
case 'installed':
|
|
32
33
|
return ({ meta }) => !core.includes(meta.profile.key) && enabled.includes(meta.profile.key);
|
|
33
|
-
case
|
|
34
|
+
case 'recommended':
|
|
34
35
|
return ({ meta }) =>
|
|
35
|
-
!core.includes(meta.profile.key) &&
|
|
36
|
-
|
|
36
|
+
!core.includes(meta.profile.key) &&
|
|
37
|
+
!remoteIds.has(meta.profile.key) &&
|
|
38
|
+
(meta.profile.tags?.some((tag) => RECOMMENDED_TIERS.includes(tag)) ?? false);
|
|
39
|
+
case 'labs':
|
|
37
40
|
return ({ meta }) => meta.profile.tags?.includes('labs') ?? false;
|
|
38
41
|
default:
|
|
39
42
|
return () => false;
|
|
@@ -13,8 +13,8 @@ import * as Schema from 'effect/Schema';
|
|
|
13
13
|
import { AppSpace } from '@dxos/app-toolkit';
|
|
14
14
|
import { type Client } from '@dxos/client';
|
|
15
15
|
import { Filter } from '@dxos/echo';
|
|
16
|
+
import { AccessToken } from '@dxos/link';
|
|
16
17
|
import { ALL_NSIDS, NSID } from '@dxos/protocols';
|
|
17
|
-
import { AccessToken } from '@dxos/types';
|
|
18
18
|
|
|
19
19
|
export { ALL_NSIDS, NSID };
|
|
20
20
|
|
|
@@ -141,7 +141,7 @@ export const PluginItem = ({
|
|
|
141
141
|
gridCols,
|
|
142
142
|
// Override `Listbox.Item`'s default row chrome (flex/items-center/padding/cursor) so the
|
|
143
143
|
// bespoke card grid stretches both columns to full height and controls its own padding.
|
|
144
|
-
'items-stretch p-0 pe-2 cursor-default h-[14rem] w-full gap-3
|
|
144
|
+
'items-stretch p-0 pe-2 cursor-default h-[14rem] w-full gap-3 dx-modal-surface rounded-md overflow-hidden',
|
|
145
145
|
)}
|
|
146
146
|
>
|
|
147
147
|
<div className={mx(gridRows, 'rounded-l-md', styles.surface)}>
|
|
@@ -218,8 +218,10 @@ export const PluginItem = ({
|
|
|
218
218
|
|
|
219
219
|
const tagColors: Record<RegistryTagType, ChromaticPalette | NeutralPalette> = {
|
|
220
220
|
new: 'rose',
|
|
221
|
-
|
|
222
|
-
|
|
221
|
+
// Tier hues ramp green -> blue -> purple so the ordering reads without knowing the labels.
|
|
222
|
+
beta: 'green',
|
|
223
|
+
alpha: 'blue',
|
|
224
|
+
labs: 'purple',
|
|
223
225
|
popular: 'green',
|
|
224
226
|
featured: 'pink',
|
|
225
227
|
experimental: 'amber',
|
|
@@ -75,7 +75,7 @@ export default meta;
|
|
|
75
75
|
type Story = StoryObj<typeof meta>;
|
|
76
76
|
|
|
77
77
|
export const Default: Story = {
|
|
78
|
-
decorators: [withTheme(), withLayout({ layout: 'column', classNames: '
|
|
78
|
+
decorators: [withTheme(), withLayout({ layout: 'column', classNames: 'dx-deck-surface' })],
|
|
79
79
|
parameters: {
|
|
80
80
|
layout: 'fullscreen',
|
|
81
81
|
},
|
|
@@ -96,7 +96,7 @@ export const RegistrySettings = ({
|
|
|
96
96
|
<Form.Content>
|
|
97
97
|
<Form.Section title={t('dev-plugin.section.title')}>
|
|
98
98
|
<Message.Root valence='neutral'>
|
|
99
|
-
<Message.
|
|
99
|
+
<Message.Body>{t('dev-plugin.description')}</Message.Body>
|
|
100
100
|
</Message.Root>
|
|
101
101
|
<Form.Row label={t('dev-plugin.url.label')} description={t('dev-plugin.url.description')}>
|
|
102
102
|
<Input.Root>
|
|
@@ -120,7 +120,7 @@ export const RegistrySettings = ({
|
|
|
120
120
|
</Form.Row>
|
|
121
121
|
{enabled && !loadedDevId && !busy && (
|
|
122
122
|
<Message.Root valence='warning'>
|
|
123
|
-
<Message.
|
|
123
|
+
<Message.Body>{t('dev-plugin.not-loaded.message')}</Message.Body>
|
|
124
124
|
</Message.Root>
|
|
125
125
|
)}
|
|
126
126
|
</Form.Section>
|
|
@@ -114,8 +114,8 @@ export const BaseRegistryArticle = composable<HTMLDivElement, BaseRegistryArticl
|
|
|
114
114
|
(pluginId: string) =>
|
|
115
115
|
invokePromise(LayoutOperation.Open, {
|
|
116
116
|
subject: [getPluginPath(pluginId)],
|
|
117
|
-
pivotId: id,
|
|
118
|
-
|
|
117
|
+
pivotId: getPluginPath(id),
|
|
118
|
+
disposition: 'add',
|
|
119
119
|
}),
|
|
120
120
|
[invokePromise, id],
|
|
121
121
|
);
|
|
@@ -76,9 +76,11 @@ export const PluginArticle = ({ subject: plugin }: PluginArticleProps) => {
|
|
|
76
76
|
(targetId: string) => {
|
|
77
77
|
void invokePromise(LayoutOperation.Open, {
|
|
78
78
|
subject: [getPluginPath(targetId)],
|
|
79
|
+
pivotId: getPluginPath(pluginId),
|
|
80
|
+
disposition: 'add',
|
|
79
81
|
});
|
|
80
82
|
},
|
|
81
|
-
[invokePromise],
|
|
83
|
+
[invokePromise, pluginId],
|
|
82
84
|
);
|
|
83
85
|
|
|
84
86
|
const actions = usePluginActions({
|
|
@@ -117,3 +119,5 @@ export const PluginArticle = ({ subject: plugin }: PluginArticleProps) => {
|
|
|
117
119
|
/>
|
|
118
120
|
);
|
|
119
121
|
};
|
|
122
|
+
|
|
123
|
+
PluginArticle.displayName = 'PluginArticle';
|