@dxos/plugin-registry 0.8.3-staging.0fa589b → 0.8.4-main.03d5cd7b56

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.
Files changed (207) hide show
  1. package/dist/lib/neutral/RegistryPlugin.mjs +27 -0
  2. package/dist/lib/neutral/RegistryPlugin.mjs.map +7 -0
  3. package/dist/lib/neutral/RegistryPlugin.node.mjs +153 -0
  4. package/dist/lib/neutral/RegistryPlugin.node.mjs.map +7 -0
  5. package/dist/lib/neutral/chunk-XK2JMH3H.mjs +33 -0
  6. package/dist/lib/neutral/chunk-XK2JMH3H.mjs.map +7 -0
  7. package/dist/lib/neutral/chunk-YV7A523U.mjs +29 -0
  8. package/dist/lib/neutral/chunk-YV7A523U.mjs.map +7 -0
  9. package/dist/lib/neutral/index.mjs +21 -0
  10. package/dist/lib/neutral/index.mjs.map +7 -0
  11. package/dist/lib/neutral/meta.json +1 -0
  12. package/dist/lib/neutral/open-plugin-registry-II735N76.mjs +15 -0
  13. package/dist/lib/neutral/open-plugin-registry-II735N76.mjs.map +7 -0
  14. package/dist/lib/neutral/plugin.mjs +18 -0
  15. package/dist/lib/neutral/plugin.mjs.map +7 -0
  16. package/dist/lib/neutral/translations.mjs +61 -0
  17. package/dist/lib/neutral/translations.mjs.map +7 -0
  18. package/dist/types/src/RegistryPlugin.d.ts +3 -1
  19. package/dist/types/src/RegistryPlugin.d.ts.map +1 -1
  20. package/dist/types/src/RegistryPlugin.node.d.ts +4 -0
  21. package/dist/types/src/RegistryPlugin.node.d.ts.map +1 -0
  22. package/dist/types/src/RegistryPlugin.test.d.ts +2 -0
  23. package/dist/types/src/RegistryPlugin.test.d.ts.map +1 -0
  24. package/dist/types/src/capabilities/app-graph-builder.d.ts +4 -2
  25. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  26. package/dist/types/src/capabilities/dev-plugin-loader.d.ts +11 -0
  27. package/dist/types/src/capabilities/dev-plugin-loader.d.ts.map +1 -0
  28. package/dist/types/src/capabilities/index.d.ts +15 -3
  29. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  30. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  31. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  32. package/dist/types/src/capabilities/react-surface.d.ts +3 -2
  33. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  34. package/dist/types/src/capabilities/settings.d.ts +14 -0
  35. package/dist/types/src/capabilities/settings.d.ts.map +1 -0
  36. package/dist/types/src/commands/disable.d.ts +11 -0
  37. package/dist/types/src/commands/disable.d.ts.map +1 -0
  38. package/dist/types/src/commands/enable.d.ts +11 -0
  39. package/dist/types/src/commands/enable.d.ts.map +1 -0
  40. package/dist/types/src/commands/index.d.ts +3 -0
  41. package/dist/types/src/commands/index.d.ts.map +1 -0
  42. package/dist/types/src/commands/list.d.ts +7 -0
  43. package/dist/types/src/commands/list.d.ts.map +1 -0
  44. package/dist/types/src/commands/util.d.ts +11 -0
  45. package/dist/types/src/commands/util.d.ts.map +1 -0
  46. package/dist/types/src/components/PluginDetail/PluginDetail.d.ts +46 -0
  47. package/dist/types/src/components/PluginDetail/PluginDetail.d.ts.map +1 -0
  48. package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts +68 -0
  49. package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts.map +1 -0
  50. package/dist/types/src/components/PluginDetail/index.d.ts +2 -0
  51. package/dist/types/src/components/PluginDetail/index.d.ts.map +1 -0
  52. package/dist/types/src/components/PluginFailureBadge/PluginFailureBadge.d.ts +20 -0
  53. package/dist/types/src/components/PluginFailureBadge/PluginFailureBadge.d.ts.map +1 -0
  54. package/dist/types/src/components/PluginFailureBadge/index.d.ts +2 -0
  55. package/dist/types/src/components/PluginFailureBadge/index.d.ts.map +1 -0
  56. package/dist/types/src/components/PluginList/PluginItem.d.ts +40 -0
  57. package/dist/types/src/components/PluginList/PluginItem.d.ts.map +1 -0
  58. package/dist/types/src/components/PluginList/PluginList.d.ts +23 -0
  59. package/dist/types/src/components/PluginList/PluginList.d.ts.map +1 -0
  60. package/dist/types/src/components/PluginList/PluginList.stories.d.ts +67 -0
  61. package/dist/types/src/components/PluginList/PluginList.stories.d.ts.map +1 -0
  62. package/dist/types/src/components/PluginList/index.d.ts +2 -0
  63. package/dist/types/src/components/PluginList/index.d.ts.map +1 -0
  64. package/dist/types/src/components/RegistrySettings/RegistrySettings.d.ts +18 -0
  65. package/dist/types/src/components/RegistrySettings/RegistrySettings.d.ts.map +1 -0
  66. package/dist/types/src/components/RegistrySettings/index.d.ts +2 -0
  67. package/dist/types/src/components/RegistrySettings/index.d.ts.map +1 -0
  68. package/dist/types/src/components/index.d.ts +2 -1
  69. package/dist/types/src/components/index.d.ts.map +1 -1
  70. package/dist/types/src/containers/BaseRegistryArticle/BaseRegistryArticle.d.ts +20 -0
  71. package/dist/types/src/containers/BaseRegistryArticle/BaseRegistryArticle.d.ts.map +1 -0
  72. package/dist/types/src/containers/BaseRegistryArticle/index.d.ts +2 -0
  73. package/dist/types/src/containers/BaseRegistryArticle/index.d.ts.map +1 -0
  74. package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts +4 -0
  75. package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts.map +1 -0
  76. package/dist/types/src/containers/LoadPluginDialog/index.d.ts +2 -0
  77. package/dist/types/src/containers/LoadPluginDialog/index.d.ts.map +1 -0
  78. package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts +7 -0
  79. package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts.map +1 -0
  80. package/dist/types/src/containers/PluginArticle/index.d.ts +2 -0
  81. package/dist/types/src/containers/PluginArticle/index.d.ts.map +1 -0
  82. package/dist/types/src/containers/PublicRegistryArticle/PublicRegistryArticle.d.ts +8 -0
  83. package/dist/types/src/containers/PublicRegistryArticle/PublicRegistryArticle.d.ts.map +1 -0
  84. package/dist/types/src/containers/PublicRegistryArticle/index.d.ts +2 -0
  85. package/dist/types/src/containers/PublicRegistryArticle/index.d.ts.map +1 -0
  86. package/dist/types/src/containers/RegistryArticle/RegistryArticle.d.ts +14 -0
  87. package/dist/types/src/containers/RegistryArticle/RegistryArticle.d.ts.map +1 -0
  88. package/dist/types/src/containers/RegistryArticle/index.d.ts +2 -0
  89. package/dist/types/src/containers/RegistryArticle/index.d.ts.map +1 -0
  90. package/dist/types/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.d.ts +13 -0
  91. package/dist/types/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.d.ts.map +1 -0
  92. package/dist/types/src/containers/RegistrySettingsContainer/index.d.ts +2 -0
  93. package/dist/types/src/containers/RegistrySettingsContainer/index.d.ts.map +1 -0
  94. package/dist/types/src/containers/index.d.ts +8 -0
  95. package/dist/types/src/containers/index.d.ts.map +1 -0
  96. package/dist/types/src/hooks/index.d.ts +4 -0
  97. package/dist/types/src/hooks/index.d.ts.map +1 -0
  98. package/dist/types/src/hooks/use-auto-tags.d.ts +20 -0
  99. package/dist/types/src/hooks/use-auto-tags.d.ts.map +1 -0
  100. package/dist/types/src/hooks/use-registry-plugins.d.ts +15 -0
  101. package/dist/types/src/hooks/use-registry-plugins.d.ts.map +1 -0
  102. package/dist/types/src/hooks/use-update-available.d.ts +18 -0
  103. package/dist/types/src/hooks/use-update-available.d.ts.map +1 -0
  104. package/dist/types/src/index.d.ts +1 -1
  105. package/dist/types/src/index.d.ts.map +1 -1
  106. package/dist/types/src/meta.d.ts +7 -4
  107. package/dist/types/src/meta.d.ts.map +1 -1
  108. package/dist/types/src/operations/index.d.ts +3 -0
  109. package/dist/types/src/operations/index.d.ts.map +1 -0
  110. package/dist/types/src/operations/open-plugin-registry.d.ts +5 -0
  111. package/dist/types/src/operations/open-plugin-registry.d.ts.map +1 -0
  112. package/dist/types/src/plugin.d.ts +4 -0
  113. package/dist/types/src/plugin.d.ts.map +1 -0
  114. package/dist/types/src/storage.d.ts +18 -0
  115. package/dist/types/src/storage.d.ts.map +1 -0
  116. package/dist/types/src/translations.d.ts +51 -19
  117. package/dist/types/src/translations.d.ts.map +1 -1
  118. package/dist/types/src/types.d.ts +32 -1
  119. package/dist/types/src/types.d.ts.map +1 -1
  120. package/dist/types/tsconfig.tsbuildinfo +1 -1
  121. package/package.json +74 -37
  122. package/src/RegistryPlugin.node.ts +19 -0
  123. package/src/RegistryPlugin.test.ts +24 -0
  124. package/src/RegistryPlugin.tsx +20 -27
  125. package/src/capabilities/app-graph-builder.ts +180 -135
  126. package/src/capabilities/dev-plugin-loader.ts +39 -0
  127. package/src/capabilities/index.ts +10 -4
  128. package/src/capabilities/operation-handler.ts +16 -0
  129. package/src/capabilities/react-surface.tsx +124 -78
  130. package/src/capabilities/settings.ts +34 -0
  131. package/src/commands/disable.ts +47 -0
  132. package/src/commands/enable.ts +44 -0
  133. package/src/commands/index.ts +14 -0
  134. package/src/commands/list.ts +41 -0
  135. package/src/commands/util.ts +24 -0
  136. package/src/components/PluginDetail/PluginDetail.stories.tsx +44 -0
  137. package/src/components/PluginDetail/PluginDetail.tsx +196 -0
  138. package/src/components/PluginDetail/index.ts +5 -0
  139. package/src/components/PluginFailureBadge/PluginFailureBadge.tsx +68 -0
  140. package/src/components/PluginFailureBadge/index.ts +5 -0
  141. package/src/components/PluginList/PluginItem.tsx +226 -0
  142. package/src/components/PluginList/PluginList.stories.tsx +87 -0
  143. package/src/components/PluginList/PluginList.tsx +52 -0
  144. package/src/components/PluginList/index.ts +5 -0
  145. package/src/components/RegistrySettings/RegistrySettings.tsx +120 -0
  146. package/src/components/RegistrySettings/index.ts +5 -0
  147. package/src/components/index.ts +2 -1
  148. package/src/containers/BaseRegistryArticle/BaseRegistryArticle.tsx +163 -0
  149. package/src/containers/BaseRegistryArticle/index.ts +5 -0
  150. package/src/containers/LoadPluginDialog/LoadPluginDialog.tsx +87 -0
  151. package/src/containers/LoadPluginDialog/index.ts +5 -0
  152. package/src/containers/PluginArticle/PluginArticle.tsx +336 -0
  153. package/src/containers/PluginArticle/index.ts +5 -0
  154. package/src/containers/PublicRegistryArticle/PublicRegistryArticle.tsx +192 -0
  155. package/src/containers/PublicRegistryArticle/index.ts +5 -0
  156. package/src/containers/RegistryArticle/RegistryArticle.tsx +53 -0
  157. package/src/containers/RegistryArticle/index.ts +5 -0
  158. package/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.tsx +59 -0
  159. package/src/containers/RegistrySettingsContainer/index.ts +5 -0
  160. package/src/containers/index.ts +12 -0
  161. package/src/hooks/index.ts +7 -0
  162. package/src/hooks/use-auto-tags.ts +51 -0
  163. package/src/hooks/use-registry-plugins.ts +29 -0
  164. package/src/hooks/use-update-available.ts +31 -0
  165. package/src/index.ts +1 -1
  166. package/src/meta.ts +21 -9
  167. package/src/operations/index.ts +7 -0
  168. package/src/operations/open-plugin-registry.ts +21 -0
  169. package/src/plugin.ts +11 -0
  170. package/src/storage.ts +50 -0
  171. package/src/translations.ts +57 -18
  172. package/src/types.ts +37 -1
  173. package/dist/lib/browser/app-graph-builder-J5YHAGNI.mjs +0 -161
  174. package/dist/lib/browser/app-graph-builder-J5YHAGNI.mjs.map +0 -7
  175. package/dist/lib/browser/chunk-ISC56E5F.mjs +0 -16
  176. package/dist/lib/browser/chunk-ISC56E5F.mjs.map +0 -7
  177. package/dist/lib/browser/index.mjs +0 -71
  178. package/dist/lib/browser/index.mjs.map +0 -7
  179. package/dist/lib/browser/intent-resolver-AQJNBUT3.mjs +0 -23
  180. package/dist/lib/browser/intent-resolver-AQJNBUT3.mjs.map +0 -7
  181. package/dist/lib/browser/meta.json +0 -1
  182. package/dist/lib/browser/react-surface-W5RNDLXV.mjs +0 -335
  183. package/dist/lib/browser/react-surface-W5RNDLXV.mjs.map +0 -7
  184. package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
  185. package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
  186. package/dist/types/src/components/Icon.stories.d.ts +0 -6
  187. package/dist/types/src/components/Icon.stories.d.ts.map +0 -1
  188. package/dist/types/src/components/PluginDetail.d.ts +0 -9
  189. package/dist/types/src/components/PluginDetail.d.ts.map +0 -1
  190. package/dist/types/src/components/PluginDetail.stories.d.ts +0 -8
  191. package/dist/types/src/components/PluginDetail.stories.d.ts.map +0 -1
  192. package/dist/types/src/components/PluginItem.d.ts +0 -13
  193. package/dist/types/src/components/PluginItem.d.ts.map +0 -1
  194. package/dist/types/src/components/PluginList.d.ts +0 -8
  195. package/dist/types/src/components/PluginList.d.ts.map +0 -1
  196. package/dist/types/src/components/PluginList.stories.d.ts +0 -11
  197. package/dist/types/src/components/PluginList.stories.d.ts.map +0 -1
  198. package/dist/types/src/components/RegistryContainer.d.ts +0 -7
  199. package/dist/types/src/components/RegistryContainer.d.ts.map +0 -1
  200. package/src/capabilities/intent-resolver.ts +0 -27
  201. package/src/components/Icon.stories.tsx +0 -107
  202. package/src/components/PluginDetail.stories.tsx +0 -45
  203. package/src/components/PluginDetail.tsx +0 -63
  204. package/src/components/PluginItem.tsx +0 -119
  205. package/src/components/PluginList.stories.tsx +0 -82
  206. package/src/components/PluginList.tsx +0 -26
  207. package/src/components/RegistryContainer.tsx +0 -84
@@ -0,0 +1,16 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Effect from 'effect/Effect';
6
+
7
+ import { Capabilities, Capability } from '@dxos/app-framework';
8
+ import type { OperationHandlerSet } from '@dxos/compute';
9
+
10
+ import { RegistryOperationHandlerSet } from '#operations';
11
+
12
+ export default Capability.makeModule<OperationHandlerSet.OperationHandlerSet>(
13
+ Effect.fnUntraced(function* () {
14
+ return Capability.contributes(Capabilities.OperationHandler, RegistryOperationHandlerSet);
15
+ }),
16
+ );
@@ -2,89 +2,135 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import React, { useCallback, useMemo } from 'react';
5
+ import * as Effect from 'effect/Effect';
6
+ import React, { useMemo } from 'react';
6
7
 
7
- import { Capabilities, contributes, createSurface, Plugin, usePluginManager } from '@dxos/app-framework';
8
- import { StackItem } from '@dxos/react-ui-stack';
8
+ import { Capabilities, Capability, Plugin } from '@dxos/app-framework';
9
+ import { Surface, usePluginManager } from '@dxos/app-framework/ui';
10
+ import { AppCapabilities } from '@dxos/app-toolkit';
11
+ import { AppSurface } from '@dxos/app-toolkit/ui';
9
12
 
10
- import { PluginDetail, RegistryContainer } from '../components';
11
- import { REGISTRY_KEY, REGISTRY_PLUGIN } from '../meta';
13
+ import {
14
+ LOAD_PLUGIN_DIALOG,
15
+ LoadPluginDialog,
16
+ PluginArticle,
17
+ PublicRegistryArticle,
18
+ RegistryArticle,
19
+ RegistrySettingsContainer,
20
+ } from '#containers';
21
+ import { meta, registryCategoryId } from '#meta';
12
22
 
13
- export default () =>
14
- contributes(Capabilities.ReactSurface, [
15
- createSurface({
16
- id: `${REGISTRY_PLUGIN}/all`,
17
- role: 'article',
18
- filter: (data): data is any => data.subject === `${REGISTRY_KEY}+all`,
19
- component: () => {
20
- const manager = usePluginManager();
21
- const filtered = useMemo(() => manager.plugins.filter(({ meta }) => !manager.core.includes(meta.id)), []);
23
+ import { useAutoTags, useRegistryPlugins, useRemotePluginIds } from '../hooks';
22
24
 
23
- return <RegistryContainer id={`${REGISTRY_KEY}+all`} plugins={filtered} />;
24
- },
25
- }),
26
- createSurface({
27
- id: `${REGISTRY_PLUGIN}/installed`,
28
- role: 'article',
29
- filter: (data): data is any => data.subject === `${REGISTRY_KEY}+installed`,
30
- component: () => {
31
- const manager = usePluginManager();
32
- const filtered = useMemo(
33
- () =>
34
- manager.plugins
35
- .filter(({ meta }) => !manager.core.includes(meta.id))
36
- .filter(({ meta }) => manager.enabled.includes(meta.id)),
37
- [],
38
- );
25
+ export default Capability.makeModule(() =>
26
+ Effect.succeed(
27
+ Capability.contributes(Capabilities.ReactSurface, [
28
+ Surface.create({
29
+ id: 'official',
30
+ filter: AppSurface.literal(AppSurface.Article, registryCategoryId('official')),
31
+ component: () => {
32
+ const manager = usePluginManager();
33
+ const remoteIds = useRemotePluginIds();
34
+ const core = useMemo(() => manager.getCore(), [manager]);
35
+ const predicate = useMemo<PluginPredicate>(
36
+ () =>
37
+ ({ meta }) =>
38
+ !core.includes(meta.id) && !remoteIds.has(meta.id),
39
+ [core, remoteIds],
40
+ );
39
41
 
40
- return <RegistryContainer id={`${REGISTRY_KEY}+installed`} plugins={filtered} />;
41
- },
42
- }),
43
- createSurface({
44
- id: `${REGISTRY_PLUGIN}/recommended`,
45
- role: 'article',
46
- filter: (data): data is any => data.subject === `${REGISTRY_KEY}+recommended`,
47
- component: () => {
48
- const manager = usePluginManager();
49
- const filtered = useMemo(
50
- () =>
51
- manager.plugins
52
- .filter(({ meta }) => !manager.core.includes(meta.id))
53
- .filter(({ meta }) => !meta.tags?.includes('labs')),
54
- [],
55
- );
42
+ return <FilteredRegistryArticle id={registryCategoryId('official')} filter={predicate} />;
43
+ },
44
+ }),
45
+ Surface.create({
46
+ id: 'installed',
47
+ filter: AppSurface.literal(AppSurface.Article, registryCategoryId('installed')),
48
+ component: () => {
49
+ const manager = usePluginManager();
50
+ const core = useMemo(() => manager.getCore(), [manager]);
51
+ const enabled = useMemo(() => manager.getEnabled(), [manager]);
52
+ const predicate = useMemo<PluginPredicate>(
53
+ () =>
54
+ ({ meta }) =>
55
+ !core.includes(meta.id) && enabled.includes(meta.id),
56
+ [core, enabled],
57
+ );
56
58
 
57
- return <RegistryContainer id={`${REGISTRY_KEY}+recommended`} plugins={filtered} />;
58
- },
59
- }),
60
- createSurface({
61
- id: `${REGISTRY_PLUGIN}/labs`,
62
- role: 'article',
63
- filter: (data): data is any => data.subject === `${REGISTRY_KEY}+labs`,
64
- component: () => {
65
- const manager = usePluginManager();
66
- const filtered = useMemo(() => manager.plugins.filter(({ meta }) => meta.tags?.includes('labs')), []);
59
+ return <FilteredRegistryArticle id={registryCategoryId('installed')} filter={predicate} />;
60
+ },
61
+ }),
62
+ Surface.create({
63
+ id: 'recommended',
64
+ filter: AppSurface.literal(AppSurface.Article, registryCategoryId('recommended')),
65
+ component: () => {
66
+ const manager = usePluginManager();
67
+ const remoteIds = useRemotePluginIds();
68
+ const core = useMemo(() => manager.getCore(), [manager]);
69
+ const predicate = useMemo<PluginPredicate>(
70
+ () =>
71
+ ({ meta }) =>
72
+ !core.includes(meta.id) && !remoteIds.has(meta.id) && !meta.tags?.includes('labs'),
73
+ [core, remoteIds],
74
+ );
67
75
 
68
- return <RegistryContainer id={`${REGISTRY_KEY}+labs`} plugins={filtered} />;
69
- },
70
- }),
71
- createSurface({
72
- id: `${REGISTRY_PLUGIN}/plugin-details`,
73
- role: 'article',
74
- filter: (data): data is { subject: Plugin } => data.subject instanceof Plugin,
75
- component: ({ data: { subject } }) => {
76
- const manager = usePluginManager();
77
- const enabled = manager.enabled.includes(subject.meta.id);
78
- const handleEnable = useCallback(
79
- () => (enabled ? manager.disable(subject.meta.id) : manager.enable(subject.meta.id)),
80
- [manager, subject.meta.id, enabled],
81
- );
76
+ return <FilteredRegistryArticle id={registryCategoryId('recommended')} filter={predicate} />;
77
+ },
78
+ }),
79
+ Surface.create({
80
+ id: 'labs',
81
+ filter: AppSurface.literal(AppSurface.Article, registryCategoryId('labs')),
82
+ component: () => {
83
+ const predicate = useMemo<PluginPredicate>(
84
+ () =>
85
+ ({ meta }) =>
86
+ meta.tags?.includes('labs') ?? false,
87
+ [],
88
+ );
82
89
 
83
- return (
84
- <StackItem.Content role='article'>
85
- <PluginDetail plugin={subject} enabled={enabled} onEnable={handleEnable} />
86
- </StackItem.Content>
87
- );
88
- },
89
- }),
90
- ]);
90
+ return <FilteredRegistryArticle id={registryCategoryId('labs')} filter={predicate} />;
91
+ },
92
+ }),
93
+ Surface.create({
94
+ id: 'registry',
95
+ filter: AppSurface.literal(AppSurface.Article, registryCategoryId('registry')),
96
+ component: () => <PublicRegistryArticle id={registryCategoryId('registry')} />,
97
+ }),
98
+ Surface.create({
99
+ id: 'plugin-details',
100
+ filter: AppSurface.subject(AppSurface.Article, Plugin.isPlugin),
101
+ component: ({ data: { subject } }) => {
102
+ return <PluginArticle subject={subject} />;
103
+ },
104
+ }),
105
+ Surface.create({
106
+ id: LOAD_PLUGIN_DIALOG,
107
+ filter: AppSurface.component(AppSurface.Dialog, LOAD_PLUGIN_DIALOG),
108
+ component: () => <LoadPluginDialog />,
109
+ }),
110
+ Surface.create({
111
+ id: 'plugin-settings',
112
+ role: 'article',
113
+ filter: (data): data is { subject: AppCapabilities.Settings } =>
114
+ AppCapabilities.isSettings(data.subject) && data.subject.prefix === meta.id,
115
+ component: ({ data: { subject } }) => <RegistrySettingsContainer subject={subject} />,
116
+ }),
117
+ ]),
118
+ ),
119
+ );
120
+
121
+ type PluginPredicate = (plugin: Plugin.Plugin) => boolean;
122
+
123
+ /**
124
+ * Renders the {@link RegistryArticle} surface filtered by an arbitrary
125
+ * predicate computed against the live plugin list. Centralises the
126
+ * `usePluginManager` + `useRegistryPlugins` + `useAutoTags` wiring shared
127
+ * by every category surface.
128
+ */
129
+ const FilteredRegistryArticle = ({ id, filter }: { id: string; filter: PluginPredicate }) => {
130
+ const manager = usePluginManager();
131
+ const { entries } = useRegistryPlugins();
132
+ const extraTagsById = useAutoTags(entries);
133
+ const filtered = useMemo(() => manager.getPlugins().filter(filter), [manager, filter]);
134
+
135
+ return <RegistryArticle id={id} plugins={filtered} extraTagsById={extraTagsById} />;
136
+ };
@@ -0,0 +1,34 @@
1
+ //
2
+ // Copyright 2026 DXOS.org
3
+ //
4
+
5
+ import * as Effect from 'effect/Effect';
6
+
7
+ import { Capability, PLUGIN_DEV_SERVER_PORT } from '@dxos/app-framework';
8
+ import { AppCapabilities } from '@dxos/app-toolkit';
9
+ import { createKvsStore } from '@dxos/effect';
10
+
11
+ import { meta } from '#meta';
12
+
13
+ import { RegistryCapabilities, RegistrySettingsSchema } from '../types';
14
+
15
+ const DEFAULT_DEV_PLUGIN_URL = `http://localhost:${PLUGIN_DEV_SERVER_PORT}/manifest.json`;
16
+
17
+ export default Capability.makeModule(() =>
18
+ Effect.sync(() => {
19
+ const settingsAtom = createKvsStore({
20
+ key: meta.id,
21
+ schema: RegistrySettingsSchema,
22
+ defaultValue: () => ({ devPluginUrl: DEFAULT_DEV_PLUGIN_URL }),
23
+ });
24
+
25
+ return [
26
+ Capability.contributes(RegistryCapabilities.Settings, settingsAtom),
27
+ Capability.contributes(AppCapabilities.Settings, {
28
+ prefix: meta.id,
29
+ schema: RegistrySettingsSchema,
30
+ atom: settingsAtom,
31
+ }),
32
+ ];
33
+ }),
34
+ );
@@ -0,0 +1,47 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Args from '@effect/cli/Args';
6
+ import * as Command from '@effect/cli/Command';
7
+ import * as Console from 'effect/Console';
8
+ import * as Effect from 'effect/Effect';
9
+
10
+ import { Plugin } from '@dxos/app-framework';
11
+ import { CommandConfig } from '@dxos/cli-util';
12
+ import { invariant } from '@dxos/invariant';
13
+
14
+ import { saveEnabledPlugins } from '../storage';
15
+
16
+ export const handler = Effect.fn(function* ({ id }: { id: string }) {
17
+ const { json, profile } = yield* CommandConfig;
18
+ const manager = yield* Plugin.Service;
19
+
20
+ const plugins = manager.getPlugins();
21
+ const plugin = plugins.find((p: Plugin.Plugin) => p.meta.id === id);
22
+ invariant(plugin, `Plugin not found: ${id}`);
23
+
24
+ const core = manager.getCore();
25
+ invariant(!core.includes(id), `Cannot disable core plugin: ${id}`);
26
+
27
+ const disabled = yield* manager.disable(id);
28
+ invariant(disabled, `Failed to disable plugin: ${id}`);
29
+
30
+ // Save enabled plugins to storage
31
+ const enabledPlugins = manager.getEnabled();
32
+ yield* saveEnabledPlugins({ profile, enabled: [...enabledPlugins] });
33
+
34
+ if (json) {
35
+ yield* Console.log(JSON.stringify({ id, enabled: false }, null, 2));
36
+ } else {
37
+ yield* Console.log(`Plugin "${plugin.meta.name ?? id}" disabled.`);
38
+ }
39
+ });
40
+
41
+ export const disable = Command.make(
42
+ 'disable',
43
+ {
44
+ id: Args.text({ name: 'id' }).pipe(Args.withDescription('The ID of the plugin to disable.')),
45
+ },
46
+ handler,
47
+ ).pipe(Command.withDescription('Disable a plugin.'));
@@ -0,0 +1,44 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Args from '@effect/cli/Args';
6
+ import * as Command from '@effect/cli/Command';
7
+ import * as Console from 'effect/Console';
8
+ import * as Effect from 'effect/Effect';
9
+
10
+ import { Plugin } from '@dxos/app-framework';
11
+ import { CommandConfig } from '@dxos/cli-util';
12
+ import { invariant } from '@dxos/invariant';
13
+
14
+ import { saveEnabledPlugins } from '../storage';
15
+
16
+ export const handler = Effect.fn(function* ({ id }: { id: string }) {
17
+ const { json, profile } = yield* CommandConfig;
18
+ const manager = yield* Plugin.Service;
19
+
20
+ const plugins = manager.getPlugins();
21
+ const plugin = plugins.find((p: Plugin.Plugin) => p.meta.id === id);
22
+ invariant(plugin, `Plugin not found: ${id}`);
23
+
24
+ const enabled = yield* manager.enable(id);
25
+ invariant(enabled, `Failed to enable plugin: ${id}`);
26
+
27
+ // Save enabled plugins to storage
28
+ const enabledPlugins = manager.getEnabled();
29
+ yield* saveEnabledPlugins({ profile, enabled: [...enabledPlugins] });
30
+
31
+ if (json) {
32
+ yield* Console.log(JSON.stringify({ id, enabled: true }, null, 2));
33
+ } else {
34
+ yield* Console.log(`Plugin "${plugin.meta.name ?? id}" enabled.`);
35
+ }
36
+ });
37
+
38
+ export const enable = Command.make(
39
+ 'enable',
40
+ {
41
+ id: Args.text({ name: 'id' }).pipe(Args.withDescription('The ID of the plugin to enable.')),
42
+ },
43
+ handler,
44
+ ).pipe(Command.withDescription('Enable a plugin.'));
@@ -0,0 +1,14 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Command from '@effect/cli/Command';
6
+
7
+ import { disable } from './disable';
8
+ import { enable } from './enable';
9
+ import { list } from './list';
10
+
11
+ export const plugin: Command.Command<any, any, any, any> = Command.make('plugin').pipe(
12
+ Command.withDescription('Manage plugins.'),
13
+ Command.withSubcommands([enable, disable, list]),
14
+ );
@@ -0,0 +1,41 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Command from '@effect/cli/Command';
6
+ import * as Console from 'effect/Console';
7
+ import * as Effect from 'effect/Effect';
8
+
9
+ import { Plugin } from '@dxos/app-framework';
10
+ import { CommandConfig, printList } from '@dxos/cli-util';
11
+
12
+ import { type FormattedPlugin, printPlugin } from './util';
13
+
14
+ export const handler = Effect.fn(function* () {
15
+ const { json } = yield* CommandConfig;
16
+ const manager = yield* Plugin.Service;
17
+
18
+ const plugins = manager.getPlugins();
19
+ const enabled = manager.getEnabled();
20
+ const core = manager.getCore();
21
+
22
+ const formattedPlugins: FormattedPlugin[] = plugins.map((plugin: Plugin.Plugin) => {
23
+ const isEnabled = enabled.includes(plugin.meta.id);
24
+ const isCore = core.includes(plugin.meta.id);
25
+ return {
26
+ id: plugin.meta.id,
27
+ name: plugin.meta.name ?? plugin.meta.id,
28
+ enabled: isEnabled,
29
+ core: isCore,
30
+ };
31
+ });
32
+
33
+ if (json) {
34
+ yield* Console.log(JSON.stringify(formattedPlugins, null, 2));
35
+ } else {
36
+ const formatted = formattedPlugins.map(printPlugin);
37
+ yield* Console.log(printList(formatted));
38
+ }
39
+ });
40
+
41
+ export const list = Command.make('list', {}, handler).pipe(Command.withDescription('List all available plugins.'));
@@ -0,0 +1,24 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { FormBuilder } from '@dxos/cli-util';
6
+
7
+ export type FormattedPlugin = {
8
+ id: string;
9
+ name: string;
10
+ enabled: boolean;
11
+ core: boolean;
12
+ };
13
+
14
+ /**
15
+ * Pretty prints a plugin with ANSI colors.
16
+ */
17
+ export const printPlugin = (plugin: FormattedPlugin) => {
18
+ const status = plugin.core ? 'core' : plugin.enabled ? 'enabled' : 'disabled';
19
+ return FormBuilder.make({ title: plugin.name }).pipe(
20
+ FormBuilder.set('id', plugin.id),
21
+ FormBuilder.set('status', status),
22
+ FormBuilder.build,
23
+ );
24
+ };
@@ -0,0 +1,44 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
6
+
7
+ import { Plugin } from '@dxos/app-framework';
8
+ import { random } from '@dxos/random';
9
+ import { withLayout, withTheme } from '@dxos/react-ui/testing';
10
+
11
+ import { translations } from '#translations';
12
+
13
+ import { PluginDetail } from './PluginDetail';
14
+
15
+ const meta = {
16
+ title: 'plugins/plugin-registry/components/PluginDetail',
17
+ component: PluginDetail,
18
+ decorators: [withTheme(), withLayout({ layout: 'column' })],
19
+ parameters: {
20
+ layout: 'fullscreen',
21
+ translations,
22
+ },
23
+ } satisfies Meta<typeof PluginDetail>;
24
+
25
+ export default meta;
26
+
27
+ type Story = StoryObj<typeof meta>;
28
+
29
+ export const Default: Story = {
30
+ args: {
31
+ plugin: Plugin.define({
32
+ id: 'com.example.plugin.test',
33
+ name: 'Test Plugin',
34
+ description: random.lorem.paragraphs(2),
35
+ icon: 'ph--bug--regular',
36
+ iconHue: 'sky',
37
+ homePage: 'https://example.com',
38
+ source: 'https://github.com/example/test-plugin',
39
+ screenshots: [
40
+ 'https://media.gcflearnfree.org/content/55e073de7dd48174331f51b3_01_17_2014/getting_started_interactive2.png',
41
+ ],
42
+ }).pipe(Plugin.make)(),
43
+ },
44
+ };