@dxos/plugin-registry 0.8.4-main.e8ec1fe → 0.8.4-main.effb148878
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +102 -5
- package/dist/lib/neutral/RegistryPlugin.mjs +27 -0
- package/dist/lib/neutral/RegistryPlugin.mjs.map +7 -0
- package/dist/lib/neutral/RegistryPlugin.node.mjs +153 -0
- package/dist/lib/neutral/RegistryPlugin.node.mjs.map +7 -0
- package/dist/lib/neutral/RegistryPlugin.workerd.mjs +10 -0
- package/dist/lib/neutral/RegistryPlugin.workerd.mjs.map +7 -0
- package/dist/lib/neutral/chunk-MYLRMFLY.mjs +34 -0
- package/dist/lib/neutral/chunk-MYLRMFLY.mjs.map +7 -0
- package/dist/lib/neutral/chunk-XK2JMH3H.mjs +33 -0
- package/dist/lib/neutral/chunk-XK2JMH3H.mjs.map +7 -0
- package/dist/lib/neutral/index.mjs +25 -0
- package/dist/lib/neutral/index.mjs.map +7 -0
- package/dist/lib/neutral/meta.json +1 -0
- package/dist/lib/neutral/open-plugin-registry-II735N76.mjs +15 -0
- package/dist/lib/neutral/open-plugin-registry-II735N76.mjs.map +7 -0
- package/dist/lib/neutral/plugin.mjs +18 -0
- package/dist/lib/neutral/plugin.mjs.map +7 -0
- package/dist/lib/neutral/translations.mjs +70 -0
- package/dist/lib/neutral/translations.mjs.map +7 -0
- package/dist/types/src/RegistryPlugin.d.ts +3 -1
- package/dist/types/src/RegistryPlugin.d.ts.map +1 -1
- package/dist/types/src/RegistryPlugin.node.d.ts +4 -0
- package/dist/types/src/RegistryPlugin.node.d.ts.map +1 -0
- package/dist/types/src/RegistryPlugin.test.d.ts +2 -0
- package/dist/types/src/RegistryPlugin.test.d.ts.map +1 -0
- package/dist/types/src/RegistryPlugin.workerd.d.ts +4 -0
- package/dist/types/src/RegistryPlugin.workerd.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts +4 -2
- package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
- package/dist/types/src/capabilities/dev-plugin-loader.d.ts +11 -0
- package/dist/types/src/capabilities/dev-plugin-loader.d.ts.map +1 -0
- package/dist/types/src/capabilities/index.d.ts +15 -3
- package/dist/types/src/capabilities/index.d.ts.map +1 -1
- package/dist/types/src/capabilities/operation-handler.d.ts +6 -0
- package/dist/types/src/capabilities/operation-handler.d.ts.map +1 -0
- package/dist/types/src/capabilities/react-surface.d.ts +3 -2
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/settings.d.ts +14 -0
- package/dist/types/src/capabilities/settings.d.ts.map +1 -0
- package/dist/types/src/commands/disable.d.ts +11 -0
- package/dist/types/src/commands/disable.d.ts.map +1 -0
- package/dist/types/src/commands/enable.d.ts +11 -0
- package/dist/types/src/commands/enable.d.ts.map +1 -0
- package/dist/types/src/commands/index.d.ts +3 -0
- package/dist/types/src/commands/index.d.ts.map +1 -0
- package/dist/types/src/commands/list.d.ts +7 -0
- package/dist/types/src/commands/list.d.ts.map +1 -0
- package/dist/types/src/commands/util.d.ts +11 -0
- package/dist/types/src/commands/util.d.ts.map +1 -0
- package/dist/types/src/components/DisableDependentsAlert/DisableDependentsAlert.d.ts +24 -0
- package/dist/types/src/components/DisableDependentsAlert/DisableDependentsAlert.d.ts.map +1 -0
- package/dist/types/src/components/DisableDependentsAlert/index.d.ts +2 -0
- package/dist/types/src/components/DisableDependentsAlert/index.d.ts.map +1 -0
- package/dist/types/src/components/PluginDetail/PluginDetail.d.ts +82 -0
- package/dist/types/src/components/PluginDetail/PluginDetail.d.ts.map +1 -0
- package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts +77 -0
- package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts.map +1 -0
- package/dist/types/src/components/PluginDetail/index.d.ts +2 -0
- package/dist/types/src/components/PluginDetail/index.d.ts.map +1 -0
- package/dist/types/src/components/PluginFailureBadge/PluginFailureBadge.d.ts +20 -0
- package/dist/types/src/components/PluginFailureBadge/PluginFailureBadge.d.ts.map +1 -0
- package/dist/types/src/components/PluginFailureBadge/index.d.ts +2 -0
- package/dist/types/src/components/PluginFailureBadge/index.d.ts.map +1 -0
- package/dist/types/src/components/PluginList/PluginItem.d.ts +40 -0
- package/dist/types/src/components/PluginList/PluginItem.d.ts.map +1 -0
- package/dist/types/src/components/PluginList/PluginList.d.ts +23 -0
- package/dist/types/src/components/PluginList/PluginList.d.ts.map +1 -0
- package/dist/types/src/components/PluginList/PluginList.stories.d.ts +76 -0
- package/dist/types/src/components/PluginList/PluginList.stories.d.ts.map +1 -0
- package/dist/types/src/components/PluginList/index.d.ts +2 -0
- package/dist/types/src/components/PluginList/index.d.ts.map +1 -0
- package/dist/types/src/components/RegistrySettings/RegistrySettings.d.ts +18 -0
- package/dist/types/src/components/RegistrySettings/RegistrySettings.d.ts.map +1 -0
- package/dist/types/src/components/RegistrySettings/index.d.ts +2 -0
- package/dist/types/src/components/RegistrySettings/index.d.ts.map +1 -0
- package/dist/types/src/components/index.d.ts +3 -1
- package/dist/types/src/components/index.d.ts.map +1 -1
- package/dist/types/src/containers/BaseRegistryArticle/BaseRegistryArticle.d.ts +20 -0
- package/dist/types/src/containers/BaseRegistryArticle/BaseRegistryArticle.d.ts.map +1 -0
- package/dist/types/src/containers/BaseRegistryArticle/index.d.ts +2 -0
- package/dist/types/src/containers/BaseRegistryArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts +4 -0
- package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts.map +1 -0
- package/dist/types/src/containers/LoadPluginDialog/index.d.ts +2 -0
- package/dist/types/src/containers/LoadPluginDialog/index.d.ts.map +1 -0
- package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts +7 -0
- package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts.map +1 -0
- package/dist/types/src/containers/PluginArticle/index.d.ts +2 -0
- package/dist/types/src/containers/PluginArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/PublicRegistryArticle/PublicRegistryArticle.d.ts +8 -0
- package/dist/types/src/containers/PublicRegistryArticle/PublicRegistryArticle.d.ts.map +1 -0
- package/dist/types/src/containers/PublicRegistryArticle/index.d.ts +2 -0
- package/dist/types/src/containers/PublicRegistryArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/RegistryArticle/RegistryArticle.d.ts +14 -0
- package/dist/types/src/containers/RegistryArticle/RegistryArticle.d.ts.map +1 -0
- package/dist/types/src/containers/RegistryArticle/index.d.ts +2 -0
- package/dist/types/src/containers/RegistryArticle/index.d.ts.map +1 -0
- package/dist/types/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.d.ts +13 -0
- package/dist/types/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.d.ts.map +1 -0
- package/dist/types/src/containers/RegistrySettingsContainer/index.d.ts +2 -0
- package/dist/types/src/containers/RegistrySettingsContainer/index.d.ts.map +1 -0
- package/dist/types/src/containers/index.d.ts +8 -0
- package/dist/types/src/containers/index.d.ts.map +1 -0
- package/dist/types/src/hooks/index.d.ts +9 -0
- package/dist/types/src/hooks/index.d.ts.map +1 -0
- package/dist/types/src/hooks/useAutoTags.d.ts +20 -0
- package/dist/types/src/hooks/useAutoTags.d.ts.map +1 -0
- package/dist/types/src/hooks/useCatalogEntry.d.ts +11 -0
- package/dist/types/src/hooks/useCatalogEntry.d.ts.map +1 -0
- package/dist/types/src/hooks/useDisableConfirmation.d.ts +19 -0
- package/dist/types/src/hooks/useDisableConfirmation.d.ts.map +1 -0
- package/dist/types/src/hooks/useInstalledVersionTag.d.ts +18 -0
- package/dist/types/src/hooks/useInstalledVersionTag.d.ts.map +1 -0
- package/dist/types/src/hooks/usePluginActions.d.ts +36 -0
- package/dist/types/src/hooks/usePluginActions.d.ts.map +1 -0
- package/dist/types/src/hooks/useRegistryPlugins.d.ts +15 -0
- package/dist/types/src/hooks/useRegistryPlugins.d.ts.map +1 -0
- package/dist/types/src/hooks/useUpdateAvailable.d.ts +18 -0
- package/dist/types/src/hooks/useUpdateAvailable.d.ts.map +1 -0
- package/dist/types/src/hooks/useVersionPicker.d.ts +21 -0
- package/dist/types/src/hooks/useVersionPicker.d.ts.map +1 -0
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +20 -3
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/operations/index.d.ts +3 -0
- package/dist/types/src/operations/index.d.ts.map +1 -0
- package/dist/types/src/operations/open-plugin-registry.d.ts +5 -0
- package/dist/types/src/operations/open-plugin-registry.d.ts.map +1 -0
- package/dist/types/src/plugin.d.ts +4 -0
- package/dist/types/src/plugin.d.ts.map +1 -0
- package/dist/types/src/storage.d.ts +18 -0
- package/dist/types/src/storage.d.ts.map +1 -0
- package/dist/types/src/translations.d.ts +57 -16
- package/dist/types/src/translations.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +30 -1
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +76 -35
- package/src/RegistryPlugin.node.ts +19 -0
- package/src/RegistryPlugin.test.ts +24 -0
- package/src/RegistryPlugin.tsx +19 -25
- package/src/RegistryPlugin.workerd.ts +11 -0
- package/src/capabilities/app-graph-builder.ts +180 -136
- package/src/capabilities/dev-plugin-loader.ts +39 -0
- package/src/capabilities/index.ts +10 -4
- package/src/capabilities/operation-handler.ts +16 -0
- package/src/capabilities/react-surface.tsx +133 -74
- package/src/capabilities/settings.ts +34 -0
- package/src/commands/disable.ts +47 -0
- package/src/commands/enable.ts +44 -0
- package/src/commands/index.ts +14 -0
- package/src/commands/list.ts +41 -0
- package/src/commands/util.ts +24 -0
- package/src/components/DisableDependentsAlert/DisableDependentsAlert.tsx +69 -0
- package/src/components/DisableDependentsAlert/index.ts +5 -0
- package/src/components/PluginDetail/PluginDetail.stories.tsx +45 -0
- package/src/components/PluginDetail/PluginDetail.tsx +340 -0
- package/src/components/PluginDetail/index.ts +5 -0
- package/src/components/PluginFailureBadge/PluginFailureBadge.tsx +68 -0
- package/src/components/PluginFailureBadge/index.ts +5 -0
- package/src/components/PluginList/PluginItem.tsx +226 -0
- package/src/components/PluginList/PluginList.stories.tsx +87 -0
- package/src/components/PluginList/PluginList.tsx +52 -0
- package/src/components/PluginList/index.ts +5 -0
- package/src/components/RegistrySettings/RegistrySettings.tsx +120 -0
- package/src/components/RegistrySettings/index.ts +5 -0
- package/src/components/index.ts +3 -1
- package/src/containers/BaseRegistryArticle/BaseRegistryArticle.tsx +178 -0
- package/src/containers/BaseRegistryArticle/index.ts +5 -0
- package/src/containers/LoadPluginDialog/LoadPluginDialog.tsx +87 -0
- package/src/containers/LoadPluginDialog/index.ts +5 -0
- package/src/containers/PluginArticle/PluginArticle.tsx +117 -0
- package/src/containers/PluginArticle/index.ts +5 -0
- package/src/containers/PublicRegistryArticle/PublicRegistryArticle.tsx +192 -0
- package/src/containers/PublicRegistryArticle/index.ts +5 -0
- package/src/containers/RegistryArticle/RegistryArticle.tsx +53 -0
- package/src/containers/RegistryArticle/index.ts +5 -0
- package/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.tsx +59 -0
- package/src/containers/RegistrySettingsContainer/index.ts +5 -0
- package/src/containers/index.ts +12 -0
- package/src/hooks/index.ts +12 -0
- package/src/hooks/useAutoTags.ts +51 -0
- package/src/hooks/useCatalogEntry.ts +25 -0
- package/src/hooks/useDisableConfirmation.ts +58 -0
- package/src/hooks/useInstalledVersionTag.ts +41 -0
- package/src/hooks/usePluginActions.ts +174 -0
- package/src/hooks/useRegistryPlugins.ts +29 -0
- package/src/hooks/useUpdateAvailable.ts +31 -0
- package/src/hooks/useVersionPicker.ts +112 -0
- package/src/index.ts +1 -1
- package/src/meta.ts +32 -7
- package/src/operations/index.ts +7 -0
- package/src/operations/open-plugin-registry.ts +21 -0
- package/src/plugin.ts +11 -0
- package/src/storage.ts +50 -0
- package/src/translations.ts +62 -15
- package/src/types.ts +33 -1
- package/dist/lib/browser/app-graph-builder-6CNBP3A4.mjs +0 -162
- package/dist/lib/browser/app-graph-builder-6CNBP3A4.mjs.map +0 -7
- package/dist/lib/browser/chunk-XPEFEID2.mjs +0 -19
- package/dist/lib/browser/chunk-XPEFEID2.mjs.map +0 -7
- package/dist/lib/browser/index.mjs +0 -69
- package/dist/lib/browser/index.mjs.map +0 -7
- package/dist/lib/browser/intent-resolver-LKNUYBTN.mjs +0 -23
- package/dist/lib/browser/intent-resolver-LKNUYBTN.mjs.map +0 -7
- package/dist/lib/browser/meta.json +0 -1
- package/dist/lib/browser/react-surface-IGCXOVE2.mjs +0 -359
- package/dist/lib/browser/react-surface-IGCXOVE2.mjs.map +0 -7
- package/dist/types/src/capabilities/intent-resolver.d.ts +0 -4
- package/dist/types/src/capabilities/intent-resolver.d.ts.map +0 -1
- package/dist/types/src/components/PluginDetail.d.ts +0 -9
- package/dist/types/src/components/PluginDetail.d.ts.map +0 -1
- package/dist/types/src/components/PluginDetail.stories.d.ts +0 -32
- package/dist/types/src/components/PluginDetail.stories.d.ts.map +0 -1
- package/dist/types/src/components/PluginItem.d.ts +0 -13
- package/dist/types/src/components/PluginItem.d.ts.map +0 -1
- package/dist/types/src/components/PluginList.d.ts +0 -8
- package/dist/types/src/components/PluginList.d.ts.map +0 -1
- package/dist/types/src/components/PluginList.stories.d.ts +0 -34
- package/dist/types/src/components/PluginList.stories.d.ts.map +0 -1
- package/dist/types/src/components/RegistryContainer.d.ts +0 -7
- package/dist/types/src/components/RegistryContainer.d.ts.map +0 -1
- package/src/capabilities/intent-resolver.ts +0 -27
- package/src/components/PluginDetail.stories.tsx +0 -45
- package/src/components/PluginDetail.tsx +0 -82
- package/src/components/PluginItem.tsx +0 -120
- package/src/components/PluginList.stories.tsx +0 -80
- package/src/components/PluginList.tsx +0 -24
- package/src/components/RegistryContainer.tsx +0 -76
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { type PropsWithChildren } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type Plugin, type PluginManager, type Registry } from '@dxos/app-framework';
|
|
8
|
+
import {
|
|
9
|
+
Button,
|
|
10
|
+
Carousel,
|
|
11
|
+
Icon,
|
|
12
|
+
Input,
|
|
13
|
+
Link,
|
|
14
|
+
ScrollArea,
|
|
15
|
+
Select,
|
|
16
|
+
Tag,
|
|
17
|
+
ThemedClassName,
|
|
18
|
+
useTranslation,
|
|
19
|
+
} from '@dxos/react-ui';
|
|
20
|
+
import { composable, composableProps } from '@dxos/react-ui';
|
|
21
|
+
import { getStyles, mx } from '@dxos/ui-theme';
|
|
22
|
+
|
|
23
|
+
import { meta } from '#meta';
|
|
24
|
+
|
|
25
|
+
import { PluginFailureBadge } from '../PluginFailureBadge';
|
|
26
|
+
|
|
27
|
+
export type PluginDetailProps = {
|
|
28
|
+
plugin: Plugin.Plugin;
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
/** True while an in-flight install is running. Disables the install button. */
|
|
31
|
+
installing?: boolean;
|
|
32
|
+
/** True while an in-flight update is running. Disables the update button. */
|
|
33
|
+
updating?: boolean;
|
|
34
|
+
/** True when the catalog has a newer version than the one installed. */
|
|
35
|
+
hasUpdate?: boolean;
|
|
36
|
+
/** Currently installed version tag (used to mark the matching select option). */
|
|
37
|
+
installedVersionTag?: string;
|
|
38
|
+
/** Currently selected version tag in the picker. */
|
|
39
|
+
selectedVersionTag?: string;
|
|
40
|
+
/** Available versions of this plugin from the catalog. When non-empty, a version picker is shown. */
|
|
41
|
+
versions?: readonly Registry.PluginVersion[];
|
|
42
|
+
/**
|
|
43
|
+
* Ids of plugins this plugin declares as dependencies (direct only). Rendered
|
|
44
|
+
* under a "Requires" heading so the user can see what enabling this plugin
|
|
45
|
+
* will auto-enable.
|
|
46
|
+
*/
|
|
47
|
+
dependencies?: readonly string[];
|
|
48
|
+
/**
|
|
49
|
+
* Ids of plugins that declare this plugin as a dependency (direct only).
|
|
50
|
+
* Rendered under a "Required by" heading so the user understands the
|
|
51
|
+
* downstream impact of disabling this plugin.
|
|
52
|
+
*/
|
|
53
|
+
dependents?: readonly string[];
|
|
54
|
+
/**
|
|
55
|
+
* Failure record for this plugin, if any. When present a warning badge is
|
|
56
|
+
* rendered next to the plugin name in the header.
|
|
57
|
+
*/
|
|
58
|
+
failure?: PluginManager.PluginFailure;
|
|
59
|
+
onEnabledChange?: (enabled: boolean) => void;
|
|
60
|
+
/**
|
|
61
|
+
* When provided, the plugin is not installed and an Install button is shown
|
|
62
|
+
* in place of the enable Switch.
|
|
63
|
+
*/
|
|
64
|
+
onInstall?: () => void;
|
|
65
|
+
/**
|
|
66
|
+
* Called when the user clicks Install on the version picker.
|
|
67
|
+
*/
|
|
68
|
+
onInstallVersion?: () => void;
|
|
69
|
+
/**
|
|
70
|
+
* Called when the user activates a dependency / dependent chip. When
|
|
71
|
+
* provided, chips are rendered as buttons that fire this handler with the
|
|
72
|
+
* target plugin id; when omitted, chips are non-interactive labels.
|
|
73
|
+
*/
|
|
74
|
+
onNavigateToPlugin?: (pluginId: string) => void;
|
|
75
|
+
/**
|
|
76
|
+
* Called when the user activates the in-app spec viewer link. When provided
|
|
77
|
+
* (i.e. the plugin bundles its MDL via `Plugin.Meta.specContent`), a second
|
|
78
|
+
* "View specification" link is rendered next to the external spec link.
|
|
79
|
+
*/
|
|
80
|
+
onOpenSpec?: () => void;
|
|
81
|
+
/**
|
|
82
|
+
* Resolves a plugin id to its display name for dependency / dependent
|
|
83
|
+
* chip labels. The component delegates the lookup to the parent so each
|
|
84
|
+
* surface can decide how to source names (e.g. `Plugin.Meta.name` from the
|
|
85
|
+
* registered plugin set). When omitted, chips render the raw id.
|
|
86
|
+
*/
|
|
87
|
+
onResolvePluginName?: (pluginId: string) => string;
|
|
88
|
+
/**
|
|
89
|
+
* When provided, an Uninstall button is rendered. Leave undefined for core
|
|
90
|
+
* or non-removable plugins.
|
|
91
|
+
*/
|
|
92
|
+
onUninstall?: () => void;
|
|
93
|
+
/**
|
|
94
|
+
* When provided and the plugin is installed with a newer catalog version available,
|
|
95
|
+
* an Update button is shown in place of the enable Switch.
|
|
96
|
+
*/
|
|
97
|
+
onUpdate?: () => void;
|
|
98
|
+
/**
|
|
99
|
+
* Called when the user selects a different version in the picker.
|
|
100
|
+
*/
|
|
101
|
+
onVersionChange?: (tag: string) => void;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export const PluginDetail = composable<HTMLDivElement, PluginDetailProps>(
|
|
105
|
+
(
|
|
106
|
+
{
|
|
107
|
+
plugin,
|
|
108
|
+
enabled,
|
|
109
|
+
installing,
|
|
110
|
+
updating,
|
|
111
|
+
hasUpdate,
|
|
112
|
+
installedVersionTag,
|
|
113
|
+
selectedVersionTag,
|
|
114
|
+
versions,
|
|
115
|
+
dependencies,
|
|
116
|
+
dependents,
|
|
117
|
+
failure,
|
|
118
|
+
onEnabledChange,
|
|
119
|
+
onInstall,
|
|
120
|
+
onInstallVersion,
|
|
121
|
+
onNavigateToPlugin,
|
|
122
|
+
onOpenSpec,
|
|
123
|
+
onResolvePluginName,
|
|
124
|
+
onUninstall,
|
|
125
|
+
onUpdate,
|
|
126
|
+
onVersionChange,
|
|
127
|
+
...props
|
|
128
|
+
},
|
|
129
|
+
forwardedRef,
|
|
130
|
+
) => {
|
|
131
|
+
const { t } = useTranslation(meta.id);
|
|
132
|
+
const {
|
|
133
|
+
id,
|
|
134
|
+
name,
|
|
135
|
+
author,
|
|
136
|
+
description,
|
|
137
|
+
homePage,
|
|
138
|
+
source,
|
|
139
|
+
screenshots,
|
|
140
|
+
icon = 'ph--circle--regular',
|
|
141
|
+
iconHue = 'neutral',
|
|
142
|
+
} = plugin.meta;
|
|
143
|
+
const styles = getStyles(iconHue);
|
|
144
|
+
|
|
145
|
+
return (
|
|
146
|
+
<ScrollArea.Root {...composableProps(props)} orientation='vertical' ref={forwardedRef}>
|
|
147
|
+
<ScrollArea.Viewport>
|
|
148
|
+
{/*
|
|
149
|
+
* 3-column grid: [icon/prev | content/viewport | next].
|
|
150
|
+
* Most sections occupy `col-start-2 col-span-2` so they stretch from
|
|
151
|
+
* the centre column out to the trailing edge. The carousel uses
|
|
152
|
+
* `display: contents` to flatten its children into this grid so
|
|
153
|
+
* `Carousel.Previous` lands in col 1, `Carousel.Viewport` aligns
|
|
154
|
+
* with the rest of the content in col 2, and `Carousel.Next` sits
|
|
155
|
+
* in col 3.
|
|
156
|
+
*/}
|
|
157
|
+
<div className='dx-document grid grid-cols-[4rem_minmax(0,1fr)_4rem] gap-x-4 p-4 items-start'>
|
|
158
|
+
<Icon classNames={mx('row-start-1 p-1 rounded-md', styles.fill, styles.foreground)} icon={icon} size={14} />
|
|
159
|
+
|
|
160
|
+
<div className='row-start-1 col-start-2 col-span-2 grid grid-cols-[1fr_min-content] gap-x-3 w-full pt-1'>
|
|
161
|
+
<div className='flex items-center gap-2'>
|
|
162
|
+
<h2 className='text-xl'>{name}</h2>
|
|
163
|
+
{failure && <PluginFailureBadge failure={failure} size={5} />}
|
|
164
|
+
</div>
|
|
165
|
+
{onInstall ? (
|
|
166
|
+
<Button density='md' variant='primary' disabled={installing} onClick={onInstall}>
|
|
167
|
+
{installing ? t('installing.label') : t('install.label')}
|
|
168
|
+
</Button>
|
|
169
|
+
) : (
|
|
170
|
+
<Input.Root>
|
|
171
|
+
<Input.Switch classNames='self-center' checked={enabled} onCheckedChange={onEnabledChange} />
|
|
172
|
+
</Input.Root>
|
|
173
|
+
)}
|
|
174
|
+
<div className='flex items-center gap-1 pt-0.5 text-sm text-description'>
|
|
175
|
+
{id}
|
|
176
|
+
{author && <span className='dx-tag dx-tag--info'>{author}</span>}
|
|
177
|
+
</div>
|
|
178
|
+
</div>
|
|
179
|
+
|
|
180
|
+
<Section.Root>
|
|
181
|
+
<Section.Heading title='Description' />
|
|
182
|
+
<Section.Body>
|
|
183
|
+
<p className='text-description'>{description}</p>
|
|
184
|
+
</Section.Body>
|
|
185
|
+
</Section.Root>
|
|
186
|
+
|
|
187
|
+
{screenshots && screenshots.length > 0 && (
|
|
188
|
+
<Section.Root>
|
|
189
|
+
<Section.Heading title='Preview' />
|
|
190
|
+
<Section.Body>
|
|
191
|
+
<Carousel.Root classNames='contents' count={screenshots.length}>
|
|
192
|
+
<Carousel.Viewport>
|
|
193
|
+
{screenshots.map((src, index) => (
|
|
194
|
+
<Carousel.Slide key={src} index={index} src={src} alt={name} />
|
|
195
|
+
))}
|
|
196
|
+
</Carousel.Viewport>
|
|
197
|
+
<Carousel.Indicators />
|
|
198
|
+
</Carousel.Root>
|
|
199
|
+
</Section.Body>
|
|
200
|
+
</Section.Root>
|
|
201
|
+
)}
|
|
202
|
+
|
|
203
|
+
<Section.Root>
|
|
204
|
+
<Section.Heading title={t('resources.label')} />
|
|
205
|
+
<Section.Body>
|
|
206
|
+
<div className='flex gap-3 items-center'>
|
|
207
|
+
{homePage && (
|
|
208
|
+
<Link href={homePage} classNames='text-sm text-description'>
|
|
209
|
+
{t('home-page.label')}
|
|
210
|
+
<Icon icon='ph--arrow-square-out--regular' size={3} classNames='ml-1 dx-icon-inline' />
|
|
211
|
+
</Link>
|
|
212
|
+
)}
|
|
213
|
+
|
|
214
|
+
{source && (
|
|
215
|
+
<Link href={source} classNames='text-sm text-description'>
|
|
216
|
+
{t('source.label')}
|
|
217
|
+
<Icon icon='ph--arrow-square-out--regular' size={3} classNames='ml-1 dx-icon-inline' />
|
|
218
|
+
</Link>
|
|
219
|
+
)}
|
|
220
|
+
|
|
221
|
+
{onOpenSpec && <Chip id={id} name={t('open-spec.label')} onClick={onOpenSpec} />}
|
|
222
|
+
</div>
|
|
223
|
+
</Section.Body>
|
|
224
|
+
</Section.Root>
|
|
225
|
+
|
|
226
|
+
{dependencies && dependencies.length > 0 && (
|
|
227
|
+
<Section.Root>
|
|
228
|
+
<Section.Heading title={t('dependencies.label')} />
|
|
229
|
+
<Section.Body>
|
|
230
|
+
<div className='flex flex-wrap gap-1'>
|
|
231
|
+
{dependencies.map((depId) => (
|
|
232
|
+
<Chip
|
|
233
|
+
key={depId}
|
|
234
|
+
id={depId}
|
|
235
|
+
name={onResolvePluginName?.(depId) ?? depId}
|
|
236
|
+
onClick={onNavigateToPlugin}
|
|
237
|
+
/>
|
|
238
|
+
))}
|
|
239
|
+
</div>
|
|
240
|
+
</Section.Body>
|
|
241
|
+
</Section.Root>
|
|
242
|
+
)}
|
|
243
|
+
|
|
244
|
+
{dependents && dependents.length > 0 && (
|
|
245
|
+
<Section.Root>
|
|
246
|
+
<Section.Heading title={t('dependents.label')} />
|
|
247
|
+
<Section.Body>
|
|
248
|
+
<div className='flex flex-wrap gap-1'>
|
|
249
|
+
{dependents.map((dependentId) => (
|
|
250
|
+
<Chip
|
|
251
|
+
key={dependentId}
|
|
252
|
+
id={dependentId}
|
|
253
|
+
name={onResolvePluginName?.(dependentId) ?? dependentId}
|
|
254
|
+
onClick={onNavigateToPlugin}
|
|
255
|
+
/>
|
|
256
|
+
))}
|
|
257
|
+
</div>
|
|
258
|
+
</Section.Body>
|
|
259
|
+
</Section.Root>
|
|
260
|
+
)}
|
|
261
|
+
|
|
262
|
+
{versions && versions.length > 0 && (
|
|
263
|
+
<Section.Root>
|
|
264
|
+
<Section.Heading title={t('versions.label')} />
|
|
265
|
+
<Section.Body>
|
|
266
|
+
<div className='flex gap-2 items-center'>
|
|
267
|
+
<Select.Root value={selectedVersionTag} onValueChange={onVersionChange}>
|
|
268
|
+
<Select.TriggerButton classNames='min-w-32' />
|
|
269
|
+
<Select.Portal>
|
|
270
|
+
<Select.Content>
|
|
271
|
+
<Select.Viewport>
|
|
272
|
+
{versions.map((versionEntry) => (
|
|
273
|
+
<Select.Option key={versionEntry.tag} value={versionEntry.tag}>
|
|
274
|
+
{versionEntry.tag}
|
|
275
|
+
{installedVersionTag === versionEntry.tag ? ` (${t('installed.label')})` : ''}
|
|
276
|
+
</Select.Option>
|
|
277
|
+
))}
|
|
278
|
+
</Select.Viewport>
|
|
279
|
+
</Select.Content>
|
|
280
|
+
</Select.Portal>
|
|
281
|
+
</Select.Root>
|
|
282
|
+
{onInstallVersion && (
|
|
283
|
+
<Button
|
|
284
|
+
density='md'
|
|
285
|
+
variant='primary'
|
|
286
|
+
disabled={installing || selectedVersionTag === installedVersionTag}
|
|
287
|
+
onClick={onInstallVersion}
|
|
288
|
+
>
|
|
289
|
+
{installing ? t('installing.label') : t('install-version.label')}
|
|
290
|
+
</Button>
|
|
291
|
+
)}
|
|
292
|
+
</div>
|
|
293
|
+
</Section.Body>
|
|
294
|
+
</Section.Root>
|
|
295
|
+
)}
|
|
296
|
+
|
|
297
|
+
{(onUninstall || (hasUpdate && onUpdate) || updating) && (
|
|
298
|
+
<div className='col-start-2 col-span-2 flex gap-2'>
|
|
299
|
+
{updating ? (
|
|
300
|
+
<Button variant='primary' disabled>
|
|
301
|
+
{t('updating.label')}
|
|
302
|
+
</Button>
|
|
303
|
+
) : hasUpdate && onUpdate ? (
|
|
304
|
+
<Button variant='primary' onClick={onUpdate}>
|
|
305
|
+
{t('update.label')}
|
|
306
|
+
</Button>
|
|
307
|
+
) : null}
|
|
308
|
+
{onUninstall && <Button onClick={onUninstall}>{t('uninstall.label')}</Button>}
|
|
309
|
+
</div>
|
|
310
|
+
)}
|
|
311
|
+
</div>
|
|
312
|
+
</ScrollArea.Viewport>
|
|
313
|
+
</ScrollArea.Root>
|
|
314
|
+
);
|
|
315
|
+
},
|
|
316
|
+
);
|
|
317
|
+
|
|
318
|
+
PluginDetail.displayName = 'PluginDetail';
|
|
319
|
+
|
|
320
|
+
const SectionRoot = ({ children }: PropsWithChildren<{}>) => <>{children}</>;
|
|
321
|
+
|
|
322
|
+
const SectionHeading = ({ title }: { title: string }) => (
|
|
323
|
+
<h2 className='col-start-2 col-span-2 pt-6 pb-2 uppercase text-sm font-medium text-subdued'>{title}</h2>
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
const SectionBody = ({ classNames, children }: ThemedClassName<PropsWithChildren>) => (
|
|
327
|
+
<div className={mx('col-start-2 flex flex-col gap-2', classNames)}>{children}</div>
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
const Section = {
|
|
331
|
+
Root: SectionRoot,
|
|
332
|
+
Heading: SectionHeading,
|
|
333
|
+
Body: SectionBody,
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
const Chip = ({ id, name, onClick }: { id: string; name: string; onClick?: (pluginId: string) => void }) => (
|
|
337
|
+
<Tag title={id} onClick={onClick ? () => onClick(id) : undefined} classNames='dx-hover'>
|
|
338
|
+
{name}
|
|
339
|
+
</Tag>
|
|
340
|
+
);
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2026 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React from 'react';
|
|
6
|
+
|
|
7
|
+
import { type PluginManager } from '@dxos/app-framework';
|
|
8
|
+
import { IconButton, Popover, useTranslation } from '@dxos/react-ui';
|
|
9
|
+
|
|
10
|
+
import { meta } from '#meta';
|
|
11
|
+
|
|
12
|
+
// Mirrors react-ui `Icon`'s `size` prop literal subset; kept inline so we don't
|
|
13
|
+
// have to add `@dxos/ui-types` as a dep just for one type alias.
|
|
14
|
+
type IconSize = 4 | 5 | 6 | 8;
|
|
15
|
+
|
|
16
|
+
export type PluginFailureBadgeProps = {
|
|
17
|
+
failure: PluginManager.PluginFailure;
|
|
18
|
+
/** Optional override for the trigger icon's tailwind classes. */
|
|
19
|
+
iconClassNames?: string;
|
|
20
|
+
/** Visual size of the warning icon (passed through to react-ui `<Icon />`). */
|
|
21
|
+
size?: IconSize;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Compact warning glyph rendered next to a plugin's name when it has failed
|
|
26
|
+
* to load or activate. Clicking the icon opens a popover that names the phase
|
|
27
|
+
* (load / activation), the reason (timeout / error), and the underlying error
|
|
28
|
+
* message — enough for an operator to tell "remote host is offline" apart
|
|
29
|
+
* from "the plugin crashed".
|
|
30
|
+
*/
|
|
31
|
+
export const PluginFailureBadge = ({ failure, iconClassNames, size = 4 }: PluginFailureBadgeProps) => {
|
|
32
|
+
const { t } = useTranslation(meta.id);
|
|
33
|
+
|
|
34
|
+
const phaseLabel = failure.phase === 'load' ? t('failure-phase-load.label') : t('failure-phase-activation.label');
|
|
35
|
+
const reasonLabel =
|
|
36
|
+
failure.reason === 'timeout' ? t('failure-reason-timeout.label') : t('failure-reason-error.label');
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<Popover.Root>
|
|
40
|
+
<Popover.Trigger asChild>
|
|
41
|
+
<IconButton
|
|
42
|
+
variant='ghost'
|
|
43
|
+
iconOnly
|
|
44
|
+
noTooltip
|
|
45
|
+
icon='ph--warning--bold'
|
|
46
|
+
size={size}
|
|
47
|
+
iconClassNames={iconClassNames}
|
|
48
|
+
label={t('failure-badge.label')}
|
|
49
|
+
data-testid={`pluginFailureBadge.${failure.id}`}
|
|
50
|
+
// Don't bubble up to row click handlers (e.g. the card's `onClick`
|
|
51
|
+
// that opens the details pane). The popover is the action here.
|
|
52
|
+
onClick={(event) => event.stopPropagation()}
|
|
53
|
+
/>
|
|
54
|
+
</Popover.Trigger>
|
|
55
|
+
<Popover.Content>
|
|
56
|
+
<Popover.Viewport>
|
|
57
|
+
<div className='px-3 py-2 min-w-[18rem] max-w-[28rem] flex flex-col gap-1'>
|
|
58
|
+
<p className='font-medium text-sm'>
|
|
59
|
+
{t('failure-title.label', { phase: phaseLabel, reason: reasonLabel })}
|
|
60
|
+
</p>
|
|
61
|
+
<p className='text-description text-sm break-words'>{failure.error.message}</p>
|
|
62
|
+
</div>
|
|
63
|
+
</Popover.Viewport>
|
|
64
|
+
<Popover.Arrow />
|
|
65
|
+
</Popover.Content>
|
|
66
|
+
</Popover.Root>
|
|
67
|
+
);
|
|
68
|
+
};
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import React, { type MouseEvent, useCallback, useMemo } from 'react';
|
|
6
|
+
|
|
7
|
+
import { type Plugin, type PluginManager } from '@dxos/app-framework';
|
|
8
|
+
import {
|
|
9
|
+
Button,
|
|
10
|
+
type ChromaticPalette,
|
|
11
|
+
Icon,
|
|
12
|
+
IconButton,
|
|
13
|
+
Input,
|
|
14
|
+
Link,
|
|
15
|
+
ListItem,
|
|
16
|
+
type NeutralPalette,
|
|
17
|
+
Tag,
|
|
18
|
+
useTranslation,
|
|
19
|
+
} from '@dxos/react-ui';
|
|
20
|
+
import { mx } from '@dxos/ui-theme';
|
|
21
|
+
import { getStyles } from '@dxos/ui-theme';
|
|
22
|
+
|
|
23
|
+
import { meta } from '#meta';
|
|
24
|
+
import { type RegistryTagType } from '#types';
|
|
25
|
+
|
|
26
|
+
import { PluginFailureBadge } from '../PluginFailureBadge';
|
|
27
|
+
|
|
28
|
+
export type PluginItemProps = {
|
|
29
|
+
plugin: Plugin.Plugin;
|
|
30
|
+
/** Ids of plugins currently installed (loaded into the manager). */
|
|
31
|
+
installed?: readonly string[];
|
|
32
|
+
/** Ids of plugins whose install is in flight. */
|
|
33
|
+
installing?: readonly string[];
|
|
34
|
+
enabled?: readonly string[];
|
|
35
|
+
/**
|
|
36
|
+
* Derived tags (e.g. `registry`, `local`) to display alongside the plugin's own meta.tags.
|
|
37
|
+
* Not persisted to plugin meta; computed per-render by the container.
|
|
38
|
+
*/
|
|
39
|
+
extraTags?: readonly string[];
|
|
40
|
+
onClick?: (id: string) => void;
|
|
41
|
+
onChange?: (id: string, enabled: boolean) => void;
|
|
42
|
+
/**
|
|
43
|
+
* Install handler. When provided and the plugin is not installed, an Install button
|
|
44
|
+
* is rendered in place of the enable switch.
|
|
45
|
+
*/
|
|
46
|
+
onInstall?: (id: string) => void;
|
|
47
|
+
/**
|
|
48
|
+
* When true and plugin is installed, shows an Update button instead of the enable switch.
|
|
49
|
+
*/
|
|
50
|
+
hasUpdate?: boolean;
|
|
51
|
+
/** Called when the user clicks the Update button. */
|
|
52
|
+
onUpdate?: (id: string) => void;
|
|
53
|
+
/** Ids of plugins whose update is currently in flight. */
|
|
54
|
+
updating?: readonly string[];
|
|
55
|
+
hasSettings?: (id: string) => boolean;
|
|
56
|
+
onSettings?: (id: string) => void;
|
|
57
|
+
/**
|
|
58
|
+
* Failure record for this plugin, if any. When present a warning badge is
|
|
59
|
+
* rendered next to the plugin name; clicking it opens a popover with the
|
|
60
|
+
* phase, reason, and error message.
|
|
61
|
+
*/
|
|
62
|
+
failure?: PluginManager.PluginFailure;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
export const PluginItem = ({
|
|
66
|
+
plugin,
|
|
67
|
+
installed,
|
|
68
|
+
installing,
|
|
69
|
+
enabled = [],
|
|
70
|
+
extraTags,
|
|
71
|
+
onClick,
|
|
72
|
+
onChange,
|
|
73
|
+
onInstall,
|
|
74
|
+
hasUpdate,
|
|
75
|
+
onUpdate,
|
|
76
|
+
updating,
|
|
77
|
+
hasSettings: hasSettingsProp,
|
|
78
|
+
onSettings,
|
|
79
|
+
failure,
|
|
80
|
+
}: PluginItemProps) => {
|
|
81
|
+
const { t } = useTranslation(meta.id);
|
|
82
|
+
const { id, name, description, tags, icon = 'ph--circle--regular', iconHue = 'neutral' } = plugin.meta;
|
|
83
|
+
const displayTags = useMemo(() => {
|
|
84
|
+
if (!extraTags || extraTags.length === 0) {
|
|
85
|
+
return tags ?? [];
|
|
86
|
+
}
|
|
87
|
+
const set = new Set<string>(tags ?? []);
|
|
88
|
+
for (const tag of extraTags) {
|
|
89
|
+
set.add(tag);
|
|
90
|
+
}
|
|
91
|
+
return Array.from(set);
|
|
92
|
+
}, [tags, extraTags]);
|
|
93
|
+
const isEnabled = enabled.includes(id);
|
|
94
|
+
const isInstalled = installed ? installed.includes(id) : true;
|
|
95
|
+
const isInstalling = installing?.includes(id) ?? false;
|
|
96
|
+
const isUpdating = updating?.includes(id) ?? false;
|
|
97
|
+
const showInstallButton = !!onInstall && !isInstalled;
|
|
98
|
+
const showUpdateButton = !!onUpdate && isInstalled && !!hasUpdate;
|
|
99
|
+
const inputId = `${id}-input`;
|
|
100
|
+
const labelId = `${id}-label`;
|
|
101
|
+
const descriptionId = `${id}-description`;
|
|
102
|
+
const handleClick = useCallback(() => onClick?.(id), [id, onClick]);
|
|
103
|
+
|
|
104
|
+
const handleChange = useCallback(
|
|
105
|
+
(event: MouseEvent) => {
|
|
106
|
+
event.stopPropagation();
|
|
107
|
+
onChange?.(id, !isEnabled);
|
|
108
|
+
},
|
|
109
|
+
[id, isEnabled, onChange],
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const handleInstall = useCallback(
|
|
113
|
+
(event: MouseEvent) => {
|
|
114
|
+
event.stopPropagation();
|
|
115
|
+
onInstall?.(id);
|
|
116
|
+
},
|
|
117
|
+
[id, onInstall],
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const handleUpdate = useCallback(
|
|
121
|
+
(event: MouseEvent) => {
|
|
122
|
+
event.stopPropagation();
|
|
123
|
+
onUpdate?.(id);
|
|
124
|
+
},
|
|
125
|
+
[id, onUpdate],
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
const hasSettings = hasSettingsProp?.(id) ?? false;
|
|
129
|
+
const handleSettings = useCallback(() => onSettings?.(id), [id, onSettings]);
|
|
130
|
+
const styles = getStyles(iconHue);
|
|
131
|
+
const gridCols = 'grid grid-cols-[5rem_1fr]';
|
|
132
|
+
const gridRows = 'grid grid-rows-[40px_1fr_min-content_40px]';
|
|
133
|
+
|
|
134
|
+
return (
|
|
135
|
+
<ListItem.Root
|
|
136
|
+
key={id}
|
|
137
|
+
labelId={labelId}
|
|
138
|
+
data-testid={`pluginList.${id}`}
|
|
139
|
+
aria-describedby={descriptionId}
|
|
140
|
+
classNames={mx(gridCols, 'h-[14rem] w-full gap-3 pe-2 bg-modal-surface rounded-md overflow-hidden')}
|
|
141
|
+
>
|
|
142
|
+
<div className={mx(gridRows, 'justify-center rounded-l-md', styles.surface)}>
|
|
143
|
+
<Icon
|
|
144
|
+
classNames={mx('row-start-2 cursor-pointer', styles.foreground)}
|
|
145
|
+
icon={icon}
|
|
146
|
+
size={14}
|
|
147
|
+
onClick={handleClick}
|
|
148
|
+
/>
|
|
149
|
+
</div>
|
|
150
|
+
|
|
151
|
+
<div className={mx(gridRows)}>
|
|
152
|
+
<div className='flex items-center gap-2 overflow-hidden cursor-pointer' onClick={handleClick}>
|
|
153
|
+
<span className='text-lg truncate'>{name ?? id}</span>
|
|
154
|
+
{failure && <PluginFailureBadge failure={failure} />}
|
|
155
|
+
</div>
|
|
156
|
+
|
|
157
|
+
<div>
|
|
158
|
+
<p className={mx('text-description', 'line-clamp-4 min-w-0')}>{description}</p>
|
|
159
|
+
</div>
|
|
160
|
+
|
|
161
|
+
<div className='flex -ms-0.5 overflow-x-auto scrollbar-none'>
|
|
162
|
+
{displayTags.map((tag) => (
|
|
163
|
+
<Tag key={tag} palette={tagColors[tag as RegistryTagType]} classNames='text-xs uppercase font-thin'>
|
|
164
|
+
{tag}
|
|
165
|
+
</Tag>
|
|
166
|
+
))}
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div className='flex gap-2 items-center text-sm'>
|
|
170
|
+
<IconButton
|
|
171
|
+
aria-describedby={descriptionId}
|
|
172
|
+
classNames='cursor-pointer'
|
|
173
|
+
icon='ph--gear--regular'
|
|
174
|
+
label={t('plugin-settings.label')}
|
|
175
|
+
iconOnly
|
|
176
|
+
size={4}
|
|
177
|
+
onClick={handleSettings}
|
|
178
|
+
disabled={!hasSettings}
|
|
179
|
+
/>
|
|
180
|
+
|
|
181
|
+
<Link aria-describedby={descriptionId} classNames='text-description cursor-pointer' onClick={handleClick}>
|
|
182
|
+
{t('details.label')}
|
|
183
|
+
</Link>
|
|
184
|
+
|
|
185
|
+
<div className='grow' />
|
|
186
|
+
<div className='pe-1'>
|
|
187
|
+
{isUpdating ? (
|
|
188
|
+
<Button aria-describedby={descriptionId} density='md' variant='primary' disabled>
|
|
189
|
+
{t('updating.label')}
|
|
190
|
+
</Button>
|
|
191
|
+
) : showUpdateButton ? (
|
|
192
|
+
<Button aria-describedby={descriptionId} density='md' variant='primary' onClick={handleUpdate}>
|
|
193
|
+
{t('update.label')}
|
|
194
|
+
</Button>
|
|
195
|
+
) : showInstallButton ? (
|
|
196
|
+
<Button
|
|
197
|
+
aria-describedby={descriptionId}
|
|
198
|
+
density='md'
|
|
199
|
+
variant='primary'
|
|
200
|
+
disabled={isInstalling}
|
|
201
|
+
onClick={handleInstall}
|
|
202
|
+
>
|
|
203
|
+
{isInstalling ? t('installing.label') : t('install.label')}
|
|
204
|
+
</Button>
|
|
205
|
+
) : (
|
|
206
|
+
<Input.Root id={inputId}>
|
|
207
|
+
<Input.Switch classNames='self-center' checked={isEnabled} onClick={handleChange} />
|
|
208
|
+
</Input.Root>
|
|
209
|
+
)}
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
</div>
|
|
213
|
+
</ListItem.Root>
|
|
214
|
+
);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const tagColors: Record<RegistryTagType, ChromaticPalette | NeutralPalette> = {
|
|
218
|
+
new: 'rose',
|
|
219
|
+
beta: 'teal',
|
|
220
|
+
labs: 'blue',
|
|
221
|
+
popular: 'green',
|
|
222
|
+
featured: 'pink',
|
|
223
|
+
experimental: 'amber',
|
|
224
|
+
registry: 'indigo',
|
|
225
|
+
local: 'neutral',
|
|
226
|
+
};
|