@dxos/plugin-registry 0.8.4-main.bc674ce → 0.8.4-main.bcb3aa67d6

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 (136) hide show
  1. package/dist/lib/browser/cli/index.mjs +15 -19
  2. package/dist/lib/browser/cli/index.mjs.map +3 -3
  3. package/dist/lib/browser/index.mjs +50 -41
  4. package/dist/lib/browser/index.mjs.map +4 -4
  5. package/dist/lib/browser/meta.json +1 -1
  6. package/dist/lib/node-esm/cli/index.mjs +15 -18
  7. package/dist/lib/node-esm/cli/index.mjs.map +3 -3
  8. package/dist/lib/node-esm/index.mjs +51 -41
  9. package/dist/lib/node-esm/index.mjs.map +4 -4
  10. package/dist/lib/node-esm/meta.json +1 -1
  11. package/dist/types/src/RegistryPlugin.d.ts.map +1 -1
  12. package/dist/types/src/capabilities/app-graph-builder.d.ts +6 -0
  13. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -0
  14. package/dist/types/src/capabilities/index.d.ts +5 -3
  15. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  16. package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
  17. package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
  18. package/dist/types/src/capabilities/react-surface.d.ts +5 -0
  19. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -0
  20. package/dist/types/src/cli/commands/disable.d.ts +2 -2
  21. package/dist/types/src/cli/commands/enable.d.ts +2 -2
  22. package/dist/types/src/cli/plugin.d.ts.map +1 -1
  23. package/dist/types/src/cli/storage.d.ts +4 -4
  24. package/dist/types/src/cli/storage.d.ts.map +1 -1
  25. package/dist/types/src/components/PluginDetail/PluginDetail.d.ts +15 -0
  26. package/dist/types/src/components/PluginDetail/PluginDetail.d.ts.map +1 -0
  27. package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts +43 -0
  28. package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts.map +1 -0
  29. package/dist/types/src/components/PluginDetail/index.d.ts +2 -0
  30. package/dist/types/src/components/PluginDetail/index.d.ts.map +1 -0
  31. package/dist/types/src/components/PluginList/PluginItem.d.ts.map +1 -0
  32. package/dist/types/src/components/PluginList/PluginList.d.ts.map +1 -0
  33. package/dist/types/src/components/PluginList/PluginList.stories.d.ts +38 -0
  34. package/dist/types/src/components/PluginList/PluginList.stories.d.ts.map +1 -0
  35. package/dist/types/src/components/PluginList/index.d.ts +2 -0
  36. package/dist/types/src/components/PluginList/index.d.ts.map +1 -0
  37. package/dist/types/src/components/index.d.ts +0 -1
  38. package/dist/types/src/components/index.d.ts.map +1 -1
  39. package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts +4 -0
  40. package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts.map +1 -0
  41. package/dist/types/src/containers/LoadPluginDialog/index.d.ts +3 -0
  42. package/dist/types/src/containers/LoadPluginDialog/index.d.ts.map +1 -0
  43. package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts +7 -0
  44. package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts.map +1 -0
  45. package/dist/types/src/containers/PluginArticle/index.d.ts +3 -0
  46. package/dist/types/src/containers/PluginArticle/index.d.ts.map +1 -0
  47. package/dist/types/src/containers/PluginRegistry/PluginRegistry.d.ts +14 -0
  48. package/dist/types/src/containers/PluginRegistry/PluginRegistry.d.ts.map +1 -0
  49. package/dist/types/src/containers/PluginRegistry/index.d.ts +3 -0
  50. package/dist/types/src/containers/PluginRegistry/index.d.ts.map +1 -0
  51. package/dist/types/src/containers/index.d.ts +6 -0
  52. package/dist/types/src/containers/index.d.ts.map +1 -0
  53. package/dist/types/src/meta.d.ts +5 -1
  54. package/dist/types/src/meta.d.ts.map +1 -1
  55. package/dist/types/src/operations/index.d.ts +3 -0
  56. package/dist/types/src/operations/index.d.ts.map +1 -0
  57. package/dist/types/src/operations/open-plugin-registry.d.ts +5 -0
  58. package/dist/types/src/operations/open-plugin-registry.d.ts.map +1 -0
  59. package/dist/types/src/translations.d.ts +19 -15
  60. package/dist/types/src/translations.d.ts.map +1 -1
  61. package/dist/types/tsconfig.tsbuildinfo +1 -1
  62. package/package.json +41 -31
  63. package/src/RegistryPlugin.tsx +9 -7
  64. package/src/capabilities/app-graph-builder.ts +148 -0
  65. package/src/capabilities/index.ts +9 -3
  66. package/src/capabilities/operation-handler.ts +16 -0
  67. package/src/capabilities/react-surface.tsx +94 -0
  68. package/src/cli/plugin.ts +4 -3
  69. package/src/cli/storage.ts +14 -21
  70. package/src/components/{PluginDetail.stories.tsx → PluginDetail/PluginDetail.stories.tsx} +4 -3
  71. package/src/components/PluginDetail/PluginDetail.tsx +84 -0
  72. package/src/components/PluginDetail/index.ts +5 -0
  73. package/src/components/{PluginItem.tsx → PluginList/PluginItem.tsx} +16 -12
  74. package/src/components/{PluginList.stories.tsx → PluginList/PluginList.stories.tsx} +17 -7
  75. package/src/components/PluginList/index.ts +5 -0
  76. package/src/components/index.ts +0 -1
  77. package/src/containers/LoadPluginDialog/LoadPluginDialog.tsx +84 -0
  78. package/src/containers/LoadPluginDialog/index.ts +7 -0
  79. package/src/containers/PluginArticle/PluginArticle.tsx +28 -0
  80. package/src/containers/PluginArticle/index.ts +7 -0
  81. package/src/containers/PluginRegistry/PluginRegistry.tsx +94 -0
  82. package/src/containers/PluginRegistry/index.ts +7 -0
  83. package/src/containers/index.ts +10 -0
  84. package/src/meta.ts +12 -5
  85. package/src/operations/index.ts +7 -0
  86. package/src/operations/open-plugin-registry.ts +21 -0
  87. package/src/translations.ts +19 -15
  88. package/dist/lib/browser/app-graph-builder-P3ZGGFAR.mjs +0 -166
  89. package/dist/lib/browser/app-graph-builder-P3ZGGFAR.mjs.map +0 -7
  90. package/dist/lib/browser/chunk-VOAOTNGS.mjs +0 -19
  91. package/dist/lib/browser/chunk-VOAOTNGS.mjs.map +0 -7
  92. package/dist/lib/browser/operation-resolver-P5PKLPRT.mjs +0 -24
  93. package/dist/lib/browser/operation-resolver-P5PKLPRT.mjs.map +0 -7
  94. package/dist/lib/browser/react-surface-IIM6T7IA.mjs +0 -337
  95. package/dist/lib/browser/react-surface-IIM6T7IA.mjs.map +0 -7
  96. package/dist/lib/node-esm/app-graph-builder-56JXLOGZ.mjs +0 -167
  97. package/dist/lib/node-esm/app-graph-builder-56JXLOGZ.mjs.map +0 -7
  98. package/dist/lib/node-esm/chunk-SALMIZ2W.mjs +0 -21
  99. package/dist/lib/node-esm/chunk-SALMIZ2W.mjs.map +0 -7
  100. package/dist/lib/node-esm/operation-resolver-3ZB5W3T3.mjs +0 -25
  101. package/dist/lib/node-esm/operation-resolver-3ZB5W3T3.mjs.map +0 -7
  102. package/dist/lib/node-esm/react-surface-F67P7RC3.mjs +0 -338
  103. package/dist/lib/node-esm/react-surface-F67P7RC3.mjs.map +0 -7
  104. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts +0 -6
  105. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +0 -1
  106. package/dist/types/src/capabilities/app-graph-builder/index.d.ts +0 -3
  107. package/dist/types/src/capabilities/app-graph-builder/index.d.ts.map +0 -1
  108. package/dist/types/src/capabilities/operation-resolver/index.d.ts +0 -3
  109. package/dist/types/src/capabilities/operation-resolver/index.d.ts.map +0 -1
  110. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +0 -5
  111. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +0 -1
  112. package/dist/types/src/capabilities/react-surface/index.d.ts +0 -3
  113. package/dist/types/src/capabilities/react-surface/index.d.ts.map +0 -1
  114. package/dist/types/src/capabilities/react-surface/react-surface.d.ts +0 -5
  115. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +0 -1
  116. package/dist/types/src/components/PluginDetail.d.ts +0 -9
  117. package/dist/types/src/components/PluginDetail.d.ts.map +0 -1
  118. package/dist/types/src/components/PluginDetail.stories.d.ts +0 -32
  119. package/dist/types/src/components/PluginDetail.stories.d.ts.map +0 -1
  120. package/dist/types/src/components/PluginItem.d.ts.map +0 -1
  121. package/dist/types/src/components/PluginList.d.ts.map +0 -1
  122. package/dist/types/src/components/PluginList.stories.d.ts +0 -34
  123. package/dist/types/src/components/PluginList.stories.d.ts.map +0 -1
  124. package/dist/types/src/components/RegistryContainer.d.ts +0 -8
  125. package/dist/types/src/components/RegistryContainer.d.ts.map +0 -1
  126. package/src/capabilities/app-graph-builder/app-graph-builder.ts +0 -136
  127. package/src/capabilities/app-graph-builder/index.ts +0 -7
  128. package/src/capabilities/operation-resolver/index.ts +0 -7
  129. package/src/capabilities/operation-resolver/operation-resolver.ts +0 -23
  130. package/src/capabilities/react-surface/index.ts +0 -7
  131. package/src/capabilities/react-surface/react-surface.tsx +0 -99
  132. package/src/components/PluginDetail.tsx +0 -79
  133. package/src/components/RegistryContainer.tsx +0 -83
  134. /package/dist/types/src/components/{PluginItem.d.ts → PluginList/PluginItem.d.ts} +0 -0
  135. /package/dist/types/src/components/{PluginList.d.ts → PluginList/PluginList.d.ts} +0 -0
  136. /package/src/components/{PluginList.tsx → PluginList/PluginList.tsx} +0 -0
@@ -6,11 +6,11 @@ import React, { type MouseEvent, useCallback } from 'react';
6
6
 
7
7
  import { type Plugin } from '@dxos/app-framework';
8
8
  import { type ChromaticPalette, Icon, IconButton, Input, Link, ListItem, Tag, useTranslation } from '@dxos/react-ui';
9
- import { descriptionText, mx } from '@dxos/ui-theme';
9
+ import { mx } from '@dxos/ui-theme';
10
10
  import { getStyles } from '@dxos/ui-theme';
11
11
 
12
- import { meta } from '../meta';
13
- import { type RegistryTagType } from '../types';
12
+ import { meta } from '#meta';
13
+ import { type RegistryTagType } from '#types';
14
14
 
15
15
  export type PluginItemProps = {
16
16
  plugin: Plugin.Plugin;
@@ -58,11 +58,15 @@ export const PluginItem = ({
58
58
  labelId={labelId}
59
59
  data-testid={`pluginList.${id}`}
60
60
  aria-describedby={descriptionId}
61
- classNames={mx(gridCols, 'bs-[12rem] is-full gap-3 pie-2 border border-separator rounded-md overflow-hidden')}
61
+ classNames={mx(gridCols, 'h-[14rem] w-full gap-3 pe-2 bg-modal-surface rounded-md overflow-hidden')}
62
62
  >
63
- <div className={mx(gridRows, 'justify-center rounded-l-md', styles.bg)}>
64
- <div />
65
- <Icon classNames={mx('cursor-pointer', styles.icon)} icon={icon} size={14} onClick={handleClick} />
63
+ <div className={mx(gridRows, 'justify-center rounded-l-md', styles.surface)}>
64
+ <Icon
65
+ classNames={mx('row-start-2 cursor-pointer', styles.surfaceText)}
66
+ icon={icon}
67
+ size={14}
68
+ onClick={handleClick}
69
+ />
66
70
  </div>
67
71
 
68
72
  <div className={mx(gridRows)}>
@@ -71,10 +75,10 @@ export const PluginItem = ({
71
75
  </div>
72
76
 
73
77
  <div>
74
- <p className={mx(descriptionText, 'line-clamp-4 min-is-0')}>{description}</p>
78
+ <p className={mx('text-description', 'line-clamp-4 min-w-0')}>{description}</p>
75
79
  </div>
76
80
 
77
- <div className='flex -mis-0.5 overflow-x-auto scrollbar-none'>
81
+ <div className='flex -ms-0.5 overflow-x-auto scrollbar-none'>
78
82
  {tags?.map((tag) => (
79
83
  <Tag key={tag} palette={tagColors[tag as RegistryTagType]} classNames='text-xs uppercase font-thin'>
80
84
  {tag}
@@ -87,7 +91,7 @@ export const PluginItem = ({
87
91
  aria-describedby={descriptionId}
88
92
  classNames='cursor-pointer'
89
93
  icon='ph--gear--regular'
90
- label={t('settings label')}
94
+ label={t('settings.label')}
91
95
  iconOnly
92
96
  size={4}
93
97
  onClick={handleSettings}
@@ -95,11 +99,11 @@ export const PluginItem = ({
95
99
  />
96
100
 
97
101
  <Link aria-describedby={descriptionId} classNames='text-description cursor-pointer' onClick={handleClick}>
98
- {t('details label')}
102
+ {t('details.label')}
99
103
  </Link>
100
104
 
101
105
  <div className='grow' />
102
- <div className='pie-1'>
106
+ <div className='pe-1'>
103
107
  <Input.Root id={inputId}>
104
108
  <Input.Switch classNames='self-center' checked={isEnabled} onClick={handleChange} />
105
109
  </Input.Root>
@@ -7,11 +7,12 @@ import React, { useState } from 'react';
7
7
 
8
8
  import { type Plugin, Plugin as PluginNS } from '@dxos/app-framework';
9
9
  import { faker } from '@dxos/random';
10
+ import { ScrollArea } from '@dxos/react-ui';
10
11
  import { withLayout, withTheme } from '@dxos/react-ui/testing';
11
12
  import { getHashHue } from '@dxos/ui-theme';
12
13
 
13
- import { translations } from '../translations';
14
- import { RegistryTagType } from '../types';
14
+ import { translations } from '../../translations';
15
+ import { RegistryTagType } from '#types';
15
16
 
16
17
  import { PluginList } from './PluginList';
17
18
 
@@ -31,7 +32,7 @@ const DefaultStory = () => {
31
32
  faker.helpers.multiple(
32
33
  () =>
33
34
  PluginNS.define({
34
- id: `dxos.org/plugin/plugin-${faker.string.uuid()}`,
35
+ id: `org.dxos.plugin.plugin-${faker.string.uuid()}`,
35
36
  name: `${faker.commerce.productName()}`,
36
37
  description: faker.lorem.sentences(Math.ceil(Math.random() * 3)),
37
38
  tags: faker.helpers.uniqueArray(RegistryTagType.literals as any, Math.floor(Math.random() * 3)),
@@ -49,11 +50,17 @@ const DefaultStory = () => {
49
50
  setEnabled((plugins) => (enabled ? [...plugins, id] : plugins.filter((plugin) => plugin === id)));
50
51
  };
51
52
 
52
- return <PluginList plugins={plugins} enabled={enabled} onChange={handleChange} hasSettings={() => true} />;
53
+ return (
54
+ <ScrollArea.Root orientation='vertical'>
55
+ <ScrollArea.Viewport>
56
+ <PluginList plugins={plugins} enabled={enabled} onChange={handleChange} hasSettings={() => true} />
57
+ </ScrollArea.Viewport>
58
+ </ScrollArea.Root>
59
+ );
53
60
  };
54
61
 
55
62
  const meta = {
56
- title: 'plugins/plugin-registry/PluginList',
63
+ title: 'plugins/plugin-registry/components/PluginList',
57
64
  component: PluginList,
58
65
  render: DefaultStory,
59
66
  parameters: {
@@ -66,11 +73,14 @@ export default meta;
66
73
  type Story = StoryObj<typeof meta>;
67
74
 
68
75
  export const Default: Story = {
69
- decorators: [withTheme, withLayout({ layout: 'column', scroll: true })],
76
+ decorators: [withTheme(), withLayout({ layout: 'column', classNames: 'bg-deck-surface' })],
77
+ parameters: {
78
+ layout: 'fullscreen',
79
+ },
70
80
  };
71
81
 
72
82
  export const FullScreen: Story = {
73
- decorators: [withTheme, withLayout({ scroll: true })],
83
+ decorators: [withTheme(), withLayout({ scroll: true })],
74
84
  parameters: {
75
85
  layout: 'fullscreen',
76
86
  },
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export * from './PluginList';
@@ -4,4 +4,3 @@
4
4
 
5
5
  export * from './PluginDetail';
6
6
  export * from './PluginList';
7
- export * from './RegistryContainer';
@@ -0,0 +1,84 @@
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
+ if (!url.trim()) {
26
+ return;
27
+ }
28
+
29
+ setLoading(true);
30
+ setError(null);
31
+
32
+ void Effect.gen(function* () {
33
+ yield* manager.add(url.trim());
34
+ closeRef.current?.click();
35
+ }).pipe(
36
+ Effect.catchAll((err) =>
37
+ Effect.sync(() => {
38
+ setError(String(err));
39
+ }),
40
+ ),
41
+ Effect.tap(() => Effect.sync(() => setLoading(false))),
42
+ runAndForwardErrors,
43
+ );
44
+ }, [url, manager]);
45
+
46
+ return (
47
+ <Dialog.Content>
48
+ <Dialog.Header>
49
+ <Dialog.Title>{t('load-by-url-dialog.title')}</Dialog.Title>
50
+ <Dialog.Close asChild>
51
+ <Dialog.CloseIconButton ref={closeRef} />
52
+ </Dialog.Close>
53
+ </Dialog.Header>
54
+ <Dialog.Body>
55
+ <div className='flex flex-col gap-4'>
56
+ <Input.Root validationValence={error ? 'error' : undefined}>
57
+ <Input.Label>{t('plugin-url.label')}</Input.Label>
58
+ <Input.TextInput
59
+ placeholder='https://example.com/plugin.mjs'
60
+ value={url}
61
+ onChange={(event) => {
62
+ setUrl(event.target.value);
63
+ setError(null);
64
+ }}
65
+ onKeyDown={(event) => {
66
+ if (event.key === 'Enter') {
67
+ handleLoad();
68
+ }
69
+ }}
70
+ disabled={loading}
71
+ autoFocus
72
+ />
73
+ {error && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}
74
+ </Input.Root>
75
+ <div className='flex justify-end'>
76
+ <Button variant='primary' disabled={!url.trim() || loading} onClick={handleLoad}>
77
+ {loading ? t('loading.label') : t('load-plugin.label')}
78
+ </Button>
79
+ </div>
80
+ </div>
81
+ </Dialog.Body>
82
+ </Dialog.Content>
83
+ );
84
+ };
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { LoadPluginDialog } from './LoadPluginDialog';
6
+
7
+ export default LoadPluginDialog;
@@ -0,0 +1,28 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import React, { useCallback } from 'react';
6
+
7
+ import { type Plugin } from '@dxos/app-framework';
8
+ import { usePluginManager } from '@dxos/app-framework/ui';
9
+ import { runAndForwardErrors } from '@dxos/effect';
10
+
11
+ import { PluginDetail } from '#components';
12
+
13
+ // TODO(burdon): Convert to ECHO type.
14
+ export type PluginArticleProps = { subject: Plugin.Plugin };
15
+
16
+ export const PluginArticle = ({ subject: plugin }: PluginArticleProps) => {
17
+ const manager = usePluginManager();
18
+ const enabled = manager.getEnabled().includes(plugin.meta.id);
19
+ const handleEnableChange = useCallback(
20
+ (enabled: boolean) =>
21
+ enabled
22
+ ? runAndForwardErrors(manager.enable(plugin.meta.id))
23
+ : runAndForwardErrors(manager.disable(plugin.meta.id)),
24
+ [manager, plugin.meta.id],
25
+ );
26
+
27
+ return <PluginDetail plugin={plugin} enabled={enabled} onEnabledChange={handleEnableChange} />;
28
+ };
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { PluginArticle } from './PluginArticle';
6
+
7
+ export default PluginArticle;
@@ -0,0 +1,94 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { useAtomValue } from '@effect-atom/atom-react';
6
+ import * as Effect from 'effect/Effect';
7
+ import React, { useCallback, useMemo } from 'react';
8
+
9
+ import { type Plugin } from '@dxos/app-framework';
10
+ import { useCapabilities, useOperationInvoker, usePluginManager } from '@dxos/app-framework/ui';
11
+ import { AppCapabilities, LayoutOperation, SettingsOperation } from '@dxos/app-toolkit';
12
+ import { runAndForwardErrors } from '@dxos/effect';
13
+ import { ObservabilityOperation } from '@dxos/plugin-observability/operations';
14
+ import { ScrollArea } from '@dxos/react-ui';
15
+ import { composable, composableProps } from '@dxos/ui-theme';
16
+
17
+ import { PluginList } from '#components';
18
+ import { getPluginPath } from '#meta';
19
+
20
+ const sortByPluginMeta = ({ meta: { name: a = '' } }: Plugin.Plugin, { meta: { name: b = '' } }: Plugin.Plugin) =>
21
+ a.localeCompare(b);
22
+
23
+ export type PluginRegistryProps = {
24
+ id: string;
25
+ plugins: Plugin.Plugin[];
26
+ };
27
+
28
+ export const PluginRegistry = composable<HTMLDivElement, PluginRegistryProps>(
29
+ ({ id, plugins: pluginsProp, ...props }, forwardedRef) => {
30
+ const manager = usePluginManager();
31
+ const { invoke, invokePromise } = useOperationInvoker();
32
+ const plugins = useMemo(() => pluginsProp.sort(sortByPluginMeta), [pluginsProp]);
33
+ const enabled = useAtomValue(manager.enabled);
34
+ const allSettings = useCapabilities(AppCapabilities.Settings);
35
+
36
+ // TODO(wittjosiah): Factor out to an intent?
37
+ const handleChange = useCallback(
38
+ (id: string, enabled: boolean) =>
39
+ Effect.gen(function* () {
40
+ if (enabled) {
41
+ yield* manager.enable(id);
42
+ } else {
43
+ yield* manager.disable(id);
44
+ }
45
+
46
+ yield* invoke(ObservabilityOperation.SendEvent, {
47
+ name: 'plugins.toggle',
48
+ properties: {
49
+ plugin: id,
50
+ enabled,
51
+ },
52
+ });
53
+ }).pipe(runAndForwardErrors),
54
+ [invoke, manager],
55
+ );
56
+
57
+ const handleClick = useCallback(
58
+ (pluginId: string) =>
59
+ invokePromise(LayoutOperation.Open, {
60
+ subject: [getPluginPath(pluginId)],
61
+ pivotId: id,
62
+ positioning: 'end',
63
+ }),
64
+ [invokePromise, id],
65
+ );
66
+
67
+ const hasSettings = useCallback(
68
+ (pluginId: string) => allSettings.some((s) => s.prefix === pluginId),
69
+ [allSettings],
70
+ );
71
+
72
+ const handleSettings = useCallback(
73
+ (pluginId: string) => invokePromise(SettingsOperation.Open, { plugin: pluginId }),
74
+ [invokePromise],
75
+ );
76
+
77
+ return (
78
+ <ScrollArea.Root {...composableProps(props)} orientation='vertical' ref={forwardedRef}>
79
+ <ScrollArea.Viewport>
80
+ <PluginList
81
+ plugins={plugins}
82
+ enabled={enabled}
83
+ onClick={handleClick}
84
+ onChange={handleChange}
85
+ hasSettings={hasSettings}
86
+ onSettings={handleSettings}
87
+ />
88
+ </ScrollArea.Viewport>
89
+ </ScrollArea.Root>
90
+ );
91
+ },
92
+ );
93
+
94
+ PluginRegistry.displayName = 'PluginRegistry';
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ import { PluginRegistry } from './PluginRegistry';
6
+
7
+ export default PluginRegistry;
@@ -0,0 +1,10 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { type ComponentType, lazy } from 'react';
6
+
7
+ export { LOAD_PLUGIN_DIALOG } from './LoadPluginDialog/LoadPluginDialog';
8
+ export const LoadPluginDialog: ComponentType<any> = lazy(() => import('./LoadPluginDialog'));
9
+ export const PluginArticle: ComponentType<any> = lazy(() => import('./PluginArticle'));
10
+ export const PluginRegistry: ComponentType<any> = lazy(() => import('./PluginRegistry'));
package/src/meta.ts CHANGED
@@ -3,16 +3,23 @@
3
3
  //
4
4
 
5
5
  import { type Plugin } from '@dxos/app-framework';
6
+ import { pinnedWorkspaceId } from '@dxos/app-toolkit';
6
7
  import { trim } from '@dxos/util';
7
8
 
8
- // TODO(wittjosiah): Deck does not currently support `/` in ids.
9
- // TODO(wittjosiah): This is a hack to prevent the previous deck from being set for pinned items.
10
- // Ideally this should be worked into the data model in a generic way.
11
- export const REGISTRY_ID = '!dxos:plugin-registry';
9
+ export const REGISTRY_ID = pinnedWorkspaceId('dxos:plugin-registry');
12
10
  export const REGISTRY_KEY = 'plugin-registry';
13
11
 
12
+ // TODO(wittjosiah): Should this be a special separator or use the standard path separator?
13
+ const CATEGORY_SEPARATOR = '>';
14
+
15
+ /** Build a registry category node ID. */
16
+ export const registryCategoryId = (category: string): string => `${REGISTRY_KEY}${CATEGORY_SEPARATOR}${category}`;
17
+
18
+ /** Qualified graph path to a specific plugin node. */
19
+ export const getPluginPath = (pluginId: string): string => `root/${REGISTRY_ID}/${pluginId}`;
20
+
14
21
  export const meta: Plugin.Meta = {
15
- id: 'dxos.org/plugin/registry',
22
+ id: 'org.dxos.plugin.registry',
16
23
  name: 'Plugins',
17
24
  description: trim`
18
25
  Plugin management system for discovering, installing, and configuring workspace extensions.
@@ -0,0 +1,7 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { OperationHandlerSet } from '@dxos/operation';
6
+
7
+ export const RegistryOperationHandlerSet = OperationHandlerSet.lazy(() => import('./open-plugin-registry'));
@@ -0,0 +1,21 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import * as Effect from 'effect/Effect';
6
+
7
+ import { LayoutOperation, SettingsOperation, getSpacePath } from '@dxos/app-toolkit';
8
+ import { Operation } from '@dxos/operation';
9
+
10
+ import { REGISTRY_ID } from '#meta';
11
+
12
+ const handler: Operation.WithHandler<typeof SettingsOperation.OpenPluginRegistry> =
13
+ SettingsOperation.OpenPluginRegistry.pipe(
14
+ Operation.withHandler(
15
+ Effect.fnUntraced(function* () {
16
+ yield* Operation.invoke(LayoutOperation.SwitchWorkspace, { subject: getSpacePath(REGISTRY_ID) });
17
+ }),
18
+ ),
19
+ );
20
+
21
+ export default handler;
@@ -4,26 +4,30 @@
4
4
 
5
5
  import { type Resource } from '@dxos/react-ui';
6
6
 
7
- import { meta } from './meta';
7
+ import { meta } from '#meta';
8
8
 
9
9
  export const translations = [
10
10
  {
11
11
  'en-US': {
12
12
  [meta.id]: {
13
- 'plugin name': 'Registry',
14
- 'details label': 'Details',
15
- 'home page label': 'Website',
16
- 'source label': 'Source',
17
- 'plugin registry label': 'Plugin Registry',
18
- 'all plugins label': 'All',
19
- 'installed plugins label': 'Enabled',
20
- 'recommended plugins label': 'Recommended',
21
- 'labs plugins label': 'Labs',
22
- 'community plugins label': 'Community',
23
- 'load by url label': 'Load by URL (soon)',
24
- 'coming soon label': 'Coming soon!',
25
- 'open plugin registry label': 'Open plugin registry',
26
- 'settings label': 'Settings',
13
+ 'plugin.name': 'Registry',
14
+ 'details.label': 'Details',
15
+ 'home-page.label': 'Website',
16
+ 'source.label': 'Source',
17
+ 'plugin-registry.label': 'Plugin Registry',
18
+ 'all-plugins.label': 'All',
19
+ 'installed-plugins.label': 'Enabled',
20
+ 'recommended-plugins.label': 'Recommended',
21
+ 'labs-plugins.label': 'Labs',
22
+ 'community-plugins.label': 'Community',
23
+ 'load-by-url.label': 'Load from URL',
24
+ 'load-by-url-dialog.title': 'Load Plugin from URL',
25
+ 'plugin-url.label': 'Plugin URL',
26
+ 'load-plugin.label': 'Load Plugin',
27
+ 'loading.label': 'Loading...',
28
+ 'coming-soon.label': 'Coming soon!',
29
+ 'open-plugin-registry.label': 'Open plugin registry',
30
+ 'settings.label': 'Settings',
27
31
  },
28
32
  },
29
33
  },
@@ -1,166 +0,0 @@
1
- import {
2
- REGISTRY_ID,
3
- REGISTRY_KEY,
4
- meta
5
- } from "./chunk-VOAOTNGS.mjs";
6
-
7
- // src/capabilities/app-graph-builder/app-graph-builder.ts
8
- import * as Effect from "effect/Effect";
9
- import { Capability, Common, SettingsOperation } from "@dxos/app-framework";
10
- import { Operation } from "@dxos/operation";
11
- import { GraphBuilder, NodeMatcher } from "@dxos/plugin-graph";
12
- var app_graph_builder_default = Capability.makeModule(Effect.fnUntraced(function* () {
13
- const capabilities = yield* Capability.Service;
14
- const extensions = yield* Effect.all([
15
- GraphBuilder.createExtension({
16
- id: meta.id,
17
- match: NodeMatcher.whenRoot,
18
- actions: () => Effect.succeed([
19
- {
20
- id: meta.id,
21
- data: () => Operation.invoke(SettingsOperation.OpenPluginRegistry),
22
- properties: {
23
- label: [
24
- "open plugin registry label",
25
- {
26
- ns: meta.id
27
- }
28
- ],
29
- icon: "ph--squares-four--regular",
30
- disposition: "menu"
31
- }
32
- }
33
- ])
34
- }),
35
- GraphBuilder.createExtension({
36
- id: meta.id,
37
- match: NodeMatcher.whenRoot,
38
- connector: () => Effect.succeed([
39
- {
40
- id: REGISTRY_ID,
41
- type: meta.id,
42
- properties: {
43
- label: [
44
- "plugin registry label",
45
- {
46
- ns: meta.id
47
- }
48
- ],
49
- icon: "ph--squares-four--regular",
50
- disposition: "pin-end",
51
- testId: "treeView.pluginRegistry"
52
- },
53
- nodes: [
54
- {
55
- id: `${REGISTRY_KEY}+all`,
56
- type: "category",
57
- data: `${REGISTRY_KEY}+all`,
58
- properties: {
59
- label: [
60
- "all plugins label",
61
- {
62
- ns: meta.id
63
- }
64
- ],
65
- icon: "ph--squares-four--regular",
66
- key: REGISTRY_KEY,
67
- testId: "pluginRegistry.all"
68
- }
69
- },
70
- {
71
- id: `${REGISTRY_KEY}+installed`,
72
- type: "category",
73
- data: `${REGISTRY_KEY}+installed`,
74
- properties: {
75
- label: [
76
- "installed plugins label",
77
- {
78
- ns: meta.id
79
- }
80
- ],
81
- icon: "ph--check--regular",
82
- key: REGISTRY_KEY,
83
- testId: "pluginRegistry.installed"
84
- }
85
- },
86
- {
87
- id: `${REGISTRY_KEY}+recommended`,
88
- type: "category",
89
- data: `${REGISTRY_KEY}+recommended`,
90
- properties: {
91
- label: [
92
- "recommended plugins label",
93
- {
94
- ns: meta.id
95
- }
96
- ],
97
- icon: "ph--star--regular",
98
- key: REGISTRY_KEY,
99
- testId: "pluginRegistry.recommended"
100
- }
101
- },
102
- {
103
- id: `${REGISTRY_KEY}+labs`,
104
- type: "category",
105
- data: `${REGISTRY_KEY}+labs`,
106
- properties: {
107
- label: [
108
- "labs plugins label",
109
- {
110
- ns: meta.id
111
- }
112
- ],
113
- icon: "ph--flask--regular",
114
- key: REGISTRY_KEY,
115
- testId: "pluginRegistry.labs"
116
- }
117
- }
118
- ]
119
- }
120
- ])
121
- }),
122
- GraphBuilder.createExtension({
123
- id: `${meta.id}/actions`,
124
- match: NodeMatcher.whenId(REGISTRY_ID),
125
- actions: () => Effect.succeed([
126
- {
127
- id: `${meta.id}/load-by-url`,
128
- data: Effect.fnUntraced(function* () {
129
- }),
130
- properties: {
131
- label: [
132
- "load by url label",
133
- {
134
- ns: meta.id
135
- }
136
- ],
137
- icon: "ph--cloud-arrow-down--regular",
138
- disabled: true
139
- }
140
- }
141
- ])
142
- }),
143
- GraphBuilder.createExtension({
144
- id: `${meta.id}/plugins`,
145
- match: NodeMatcher.whenId(REGISTRY_ID),
146
- connector: () => {
147
- const manager = capabilities.get(Common.Capability.PluginManager);
148
- return Effect.succeed(manager.getPlugins().map((plugin) => ({
149
- id: plugin.meta.id.replaceAll("/", ":"),
150
- type: "dxos.org/plugin",
151
- data: plugin,
152
- properties: {
153
- label: plugin.meta.name ?? plugin.meta.id,
154
- icon: plugin.meta.icon ?? "ph--circle--regular",
155
- disposition: "hidden"
156
- }
157
- })));
158
- }
159
- })
160
- ]);
161
- return Capability.contributes(Common.Capability.AppGraphBuilder, extensions);
162
- }));
163
- export {
164
- app_graph_builder_default as default
165
- };
166
- //# sourceMappingURL=app-graph-builder-P3ZGGFAR.mjs.map