@dxos/plugin-registry 0.10.0 → 0.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/lib/RegistryPlugin.mjs +15 -0
- package/dist/lib/RegistryPlugin.mjs.map +1 -0
- package/dist/lib/RegistryPlugin.node.mjs +770 -0
- package/dist/lib/RegistryPlugin.node.mjs.map +1 -0
- package/dist/lib/RegistryPlugin.workerd.mjs +8 -0
- package/dist/lib/RegistryPlugin.workerd.mjs.map +1 -0
- package/dist/lib/capabilities.mjs +11 -0
- package/dist/lib/capabilities.mjs.map +1 -0
- package/dist/lib/chunk-BaseRegistryArticle.mjs +103 -0
- package/dist/lib/chunk-BaseRegistryArticle.mjs.map +1 -0
- package/dist/lib/chunk-LoadPluginDialog.mjs +2 -0
- package/dist/lib/chunk-PluginArticle.mjs +87 -0
- package/dist/lib/chunk-PluginArticle.mjs.map +1 -0
- package/dist/lib/chunk-PublicRegistryArticle.mjs +152 -0
- package/dist/lib/chunk-PublicRegistryArticle.mjs.map +1 -0
- package/dist/lib/chunk-RegistryArticle.mjs +29 -0
- package/dist/lib/chunk-RegistryArticle.mjs.map +1 -0
- package/dist/lib/chunk-RegistrySettingsContainer.mjs +37 -0
- package/dist/lib/chunk-RegistrySettingsContainer.mjs.map +1 -0
- package/dist/lib/chunk-app-graph-builder.mjs +202 -0
- package/dist/lib/chunk-app-graph-builder.mjs.map +1 -0
- package/dist/lib/chunk-categories.mjs +23 -0
- package/dist/lib/chunk-categories.mjs.map +1 -0
- package/dist/lib/chunk-components.mjs +537 -0
- package/dist/lib/chunk-components.mjs.map +1 -0
- package/dist/lib/chunk-containers.mjs +80 -0
- package/dist/lib/chunk-containers.mjs.map +1 -0
- package/dist/lib/chunk-dev-plugin-loader.mjs +40 -0
- package/dist/lib/chunk-dev-plugin-loader.mjs.map +1 -0
- package/dist/lib/chunk-hooks.mjs +345 -0
- package/dist/lib/chunk-hooks.mjs.map +1 -0
- package/dist/lib/chunk-meta.mjs +40 -0
- package/dist/lib/chunk-meta.mjs.map +1 -0
- package/dist/lib/chunk-open-plugin-registry.mjs +12 -0
- package/dist/lib/chunk-open-plugin-registry.mjs.map +1 -0
- package/dist/lib/chunk-operation-handler.mjs +11 -0
- package/dist/lib/chunk-operation-handler.mjs.map +1 -0
- package/dist/lib/chunk-react-surface.mjs +109 -0
- package/dist/lib/chunk-react-surface.mjs.map +1 -0
- package/dist/lib/chunk-settings.mjs +24 -0
- package/dist/lib/chunk-settings.mjs.map +1 -0
- package/dist/lib/chunk-storage.mjs +36 -0
- package/dist/lib/chunk-storage.mjs.map +1 -0
- package/dist/lib/components.mjs +2 -0
- package/dist/lib/containers.mjs +2 -0
- package/dist/lib/index.mjs +3 -0
- package/dist/lib/meta.mjs +2 -0
- package/dist/lib/operations.mjs +7 -0
- package/dist/lib/operations.mjs.map +1 -0
- package/dist/lib/plugin.mjs +9 -0
- package/dist/lib/plugin.mjs.map +1 -0
- package/dist/lib/translations.mjs +66 -0
- package/dist/lib/translations.mjs.map +1 -0
- package/dist/lib/types.mjs +29 -0
- package/dist/lib/types.mjs.map +1 -0
- package/dist/types/src/capabilities/RegistryCategoryArticle.d.ts +11 -0
- package/dist/types/src/capabilities/RegistryCategoryArticle.d.ts.map +1 -0
- package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
- package/dist/types/src/capabilities/dev-plugin-loader.d.ts +1 -1
- package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts +1 -1
- package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
- package/dist/types/src/capabilities/settings.d.ts +1 -1
- package/dist/types/src/categories.d.ts.map +1 -1
- package/dist/types/src/categories.test.d.ts +2 -0
- package/dist/types/src/categories.test.d.ts.map +1 -0
- package/dist/types/src/commands/registry/util.d.ts.map +1 -1
- package/dist/types/src/components/PluginDetail/PluginDetail.d.ts +1 -1
- package/dist/types/src/components/PluginDetail/PluginDetail.d.ts.map +1 -1
- package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts +1 -1
- package/dist/types/src/containers/BaseRegistryArticle/BaseRegistryArticle.d.ts +1 -1
- package/dist/types/src/containers/BaseRegistryArticle/BaseRegistryArticle.d.ts.map +1 -1
- package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts +4 -1
- package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts.map +1 -1
- package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts +4 -1
- package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts.map +1 -1
- package/dist/types/src/containers/PublicRegistryArticle/PublicRegistryArticle.d.ts +1 -1
- package/dist/types/src/containers/PublicRegistryArticle/PublicRegistryArticle.d.ts.map +1 -1
- package/dist/types/src/containers/RegistryArticle/RegistryArticle.d.ts +1 -1
- package/dist/types/src/containers/RegistryArticle/RegistryArticle.d.ts.map +1 -1
- package/dist/types/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.d.ts +4 -7
- package/dist/types/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.d.ts.map +1 -1
- package/dist/types/src/hooks/usePluginActions.d.ts.map +1 -1
- package/dist/types/src/meta.d.ts +0 -3
- package/dist/types/src/meta.d.ts.map +1 -1
- package/dist/types/src/operations/open-plugin-registry.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +2 -2
- package/dist/types/src/types.d.ts.map +1 -1
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +38 -37
- package/src/capabilities/RegistryCategoryArticle.tsx +46 -0
- package/src/capabilities/app-graph-builder.ts +80 -74
- package/src/capabilities/react-surface.ts +85 -0
- package/src/categories.test.ts +45 -0
- package/src/categories.ts +10 -7
- package/src/commands/registry/util.ts +1 -1
- package/src/components/PluginList/PluginItem.tsx +5 -3
- package/src/components/PluginList/PluginList.stories.tsx +1 -1
- package/src/components/RegistrySettings/RegistrySettings.tsx +2 -2
- package/src/containers/BaseRegistryArticle/BaseRegistryArticle.tsx +2 -2
- package/src/containers/LoadPluginDialog/LoadPluginDialog.tsx +2 -0
- package/src/containers/PluginArticle/PluginArticle.tsx +5 -1
- package/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.tsx +2 -0
- package/src/hooks/usePluginActions.ts +8 -4
- package/src/meta.ts +5 -8
- package/src/operations/open-plugin-registry.ts +2 -2
- package/src/types.ts +2 -1
- package/dist/lib/neutral/RegistryPlugin.mjs +0 -27
- package/dist/lib/neutral/RegistryPlugin.mjs.map +0 -7
- package/dist/lib/neutral/RegistryPlugin.node.mjs +0 -844
- package/dist/lib/neutral/RegistryPlugin.node.mjs.map +0 -7
- package/dist/lib/neutral/RegistryPlugin.workerd.mjs +0 -10
- package/dist/lib/neutral/RegistryPlugin.workerd.mjs.map +0 -7
- package/dist/lib/neutral/chunk-XK2JMH3H.mjs +0 -33
- package/dist/lib/neutral/chunk-XK2JMH3H.mjs.map +0 -7
- package/dist/lib/neutral/chunk-ZY37XLVF.mjs +0 -46
- package/dist/lib/neutral/chunk-ZY37XLVF.mjs.map +0 -7
- package/dist/lib/neutral/index.mjs +0 -25
- package/dist/lib/neutral/index.mjs.map +0 -7
- package/dist/lib/neutral/meta.json +0 -1
- package/dist/lib/neutral/open-plugin-registry-ID7LV3SF.mjs +0 -15
- package/dist/lib/neutral/open-plugin-registry-ID7LV3SF.mjs.map +0 -7
- package/dist/lib/neutral/plugin.mjs +0 -18
- package/dist/lib/neutral/plugin.mjs.map +0 -7
- package/dist/lib/neutral/translations.mjs +0 -72
- package/dist/lib/neutral/translations.mjs.map +0 -7
- package/src/capabilities/react-surface.tsx +0 -123
|
@@ -0,0 +1,537 @@
|
|
|
1
|
+
import { RegistrySettingsSchema } from "./types.mjs";
|
|
2
|
+
import { meta } from "#meta";
|
|
3
|
+
import { useCallback, useMemo, useState } from "react";
|
|
4
|
+
import { AlertDialog, Button, Carousel, Icon, IconButton, Input, Link, Message, Popover, ScrollArea, Select, Tag, composable, composableProps, useThemeContext, useTranslation } from "@dxos/react-ui";
|
|
5
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
6
|
+
import { MarkdownView } from "@dxos/react-ui-markdown";
|
|
7
|
+
import { getStyles, mx } from "@dxos/ui-theme";
|
|
8
|
+
import { Listbox } from "@dxos/react-ui-list";
|
|
9
|
+
import { log } from "@dxos/log";
|
|
10
|
+
import { Form } from "@dxos/react-ui-form";
|
|
11
|
+
//#region src/components/DisableDependentsAlert/DisableDependentsAlert.tsx
|
|
12
|
+
/**
|
|
13
|
+
* Confirmation prompt shown when a user toggles off a plugin that has
|
|
14
|
+
* currently-enabled dependents. Rendered inside the layout's dialog surface
|
|
15
|
+
* (via `LayoutOperation.UpdateDialog` with `type: 'alert'`), which provides
|
|
16
|
+
* the `AlertDialog.Root` / `Overlay` wrappers — this component renders only
|
|
17
|
+
* the content. `onConfirm` runs the cascade disable and closes the dialog;
|
|
18
|
+
* cancel is handled by the layout's open-change wiring.
|
|
19
|
+
*/
|
|
20
|
+
var DisableDependentsAlert = ({ pluginId, dependents, onResolvePluginName, onConfirm }) => {
|
|
21
|
+
const { t } = useTranslation(meta.profile.key);
|
|
22
|
+
const resolveName = onResolvePluginName ?? ((id) => id);
|
|
23
|
+
return /* @__PURE__ */ jsxs(AlertDialog.Content, { children: [/* @__PURE__ */ jsxs(AlertDialog.Body, { children: [
|
|
24
|
+
/* @__PURE__ */ jsx(AlertDialog.Title, { children: t("disable-dependents-dialog.title") }),
|
|
25
|
+
/* @__PURE__ */ jsx(AlertDialog.Description, { children: t("disable-dependents-dialog.description", { plugin: resolveName(pluginId) }) }),
|
|
26
|
+
/* @__PURE__ */ jsx("ul", {
|
|
27
|
+
className: "mt-2 list-disc pl-6 text-sm text-description",
|
|
28
|
+
children: dependents.map((dependentId) => /* @__PURE__ */ jsx("li", {
|
|
29
|
+
title: dependentId,
|
|
30
|
+
children: resolveName(dependentId)
|
|
31
|
+
}, dependentId))
|
|
32
|
+
})
|
|
33
|
+
] }), /* @__PURE__ */ jsxs(AlertDialog.ActionBar, { children: [
|
|
34
|
+
/* @__PURE__ */ jsx("div", { className: "grow" }),
|
|
35
|
+
/* @__PURE__ */ jsx(AlertDialog.Cancel, {
|
|
36
|
+
asChild: true,
|
|
37
|
+
children: /* @__PURE__ */ jsx(Button, { children: t("cancel.label") })
|
|
38
|
+
}),
|
|
39
|
+
/* @__PURE__ */ jsx(AlertDialog.Action, {
|
|
40
|
+
asChild: true,
|
|
41
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
42
|
+
variant: "primary",
|
|
43
|
+
onClick: onConfirm,
|
|
44
|
+
children: t("disable-dependents-dialog.confirm.label")
|
|
45
|
+
})
|
|
46
|
+
})
|
|
47
|
+
] })] });
|
|
48
|
+
};
|
|
49
|
+
//#endregion
|
|
50
|
+
//#region src/components/PluginFailureBadge/PluginFailureBadge.tsx
|
|
51
|
+
/**
|
|
52
|
+
* Compact warning glyph rendered next to a plugin's name when it has failed
|
|
53
|
+
* to load or activate. Clicking the icon opens a popover that names the phase
|
|
54
|
+
* (load / activation), the reason (timeout / error), and the underlying error
|
|
55
|
+
* message — enough for an operator to tell "remote host is offline" apart
|
|
56
|
+
* from "the plugin crashed".
|
|
57
|
+
*/
|
|
58
|
+
var PluginFailureBadge = ({ failure, size }) => {
|
|
59
|
+
const { t } = useTranslation(meta.profile.key);
|
|
60
|
+
return /* @__PURE__ */ jsxs(Popover.Root, { children: [/* @__PURE__ */ jsx(Popover.Trigger, {
|
|
61
|
+
asChild: true,
|
|
62
|
+
children: /* @__PURE__ */ jsx(IconButton, {
|
|
63
|
+
variant: "destructive",
|
|
64
|
+
icon: "ph--warning--bold",
|
|
65
|
+
iconOnly: true,
|
|
66
|
+
noTooltip: true,
|
|
67
|
+
size,
|
|
68
|
+
label: t("failure-badge.label"),
|
|
69
|
+
"data-testid": `pluginFailureBadge.${failure.id}`,
|
|
70
|
+
onClick: (event) => event.stopPropagation()
|
|
71
|
+
})
|
|
72
|
+
}), /* @__PURE__ */ jsx(Popover.Portal, { children: /* @__PURE__ */ jsxs(Popover.Content, { children: [/* @__PURE__ */ jsx(Popover.Viewport, { children: /* @__PURE__ */ jsxs("div", {
|
|
73
|
+
className: "px-3 py-2 min-w-[18rem] max-w-[28rem] flex flex-col gap-1",
|
|
74
|
+
children: [/* @__PURE__ */ jsx("p", {
|
|
75
|
+
className: "font-medium text-sm",
|
|
76
|
+
children: t("failure-title.label", {
|
|
77
|
+
phase: failure.phase === "load" ? t("failure-phase-load.label") : t("failure-phase-activation.label"),
|
|
78
|
+
reason: failure.reason === "timeout" ? t("failure-reason-timeout.label") : t("failure-reason-error.label")
|
|
79
|
+
})
|
|
80
|
+
}), /* @__PURE__ */ jsx("p", {
|
|
81
|
+
className: "text-description text-sm break-words",
|
|
82
|
+
children: failure.error.message
|
|
83
|
+
})]
|
|
84
|
+
}) }), /* @__PURE__ */ jsx(Popover.Arrow, {})] }) })] });
|
|
85
|
+
};
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/components/PluginDetail/PluginDetail.tsx
|
|
88
|
+
var PluginDetail = composable(({ plugin, enabled, installing, updating, hasUpdate, installedVersionTag, selectedVersionTag, versions, dependencies, dependents, failure, onEnabledChange, onInstall, onInstallVersion, onNavigateToPlugin, onOpenSpec, onResolvePluginName, onUninstall, onUpdate, onVersionChange, ...props }, forwardedRef) => {
|
|
89
|
+
const { t } = useTranslation(meta.profile.key);
|
|
90
|
+
const { themeMode } = useThemeContext();
|
|
91
|
+
const { key: slug, name, author, description, homePage, source, screenshots, icon: rawIcon } = plugin.meta.profile;
|
|
92
|
+
const iconKey = rawIcon?.key ?? "ph--circle--regular";
|
|
93
|
+
const styles = getStyles(rawIcon?.hue ?? "neutral");
|
|
94
|
+
const resolvedScreenshots = (screenshots ?? []).map((entry) => themeMode === "dark" ? entry.dark ?? entry.light : entry.light ?? entry.dark).filter((url) => typeof url === "string" && url.length > 0);
|
|
95
|
+
return /* @__PURE__ */ jsx(ScrollArea.Root, {
|
|
96
|
+
...composableProps(props),
|
|
97
|
+
orientation: "vertical",
|
|
98
|
+
ref: forwardedRef,
|
|
99
|
+
children: /* @__PURE__ */ jsx(ScrollArea.Viewport, { children: /* @__PURE__ */ jsxs("div", {
|
|
100
|
+
className: "dx-document grid grid-cols-[4rem_minmax(0,1fr)_4rem] gap-x-4 p-4 items-start",
|
|
101
|
+
children: [
|
|
102
|
+
/* @__PURE__ */ jsx(Icon, {
|
|
103
|
+
classNames: mx("row-start-1 p-1 rounded-md", styles.bg, styles.fg),
|
|
104
|
+
icon: iconKey,
|
|
105
|
+
size: 14
|
|
106
|
+
}),
|
|
107
|
+
/* @__PURE__ */ jsxs("div", {
|
|
108
|
+
className: "row-start-1 col-start-2 col-span-2 grid grid-cols-[1fr_min-content] gap-x-3 w-full pt-1",
|
|
109
|
+
children: [
|
|
110
|
+
/* @__PURE__ */ jsxs("div", {
|
|
111
|
+
className: "flex items-center gap-2",
|
|
112
|
+
children: [/* @__PURE__ */ jsx("h2", {
|
|
113
|
+
className: "text-xl",
|
|
114
|
+
children: name
|
|
115
|
+
}), failure && /* @__PURE__ */ jsx(PluginFailureBadge, {
|
|
116
|
+
failure,
|
|
117
|
+
size: 5
|
|
118
|
+
})]
|
|
119
|
+
}),
|
|
120
|
+
onInstall ? /* @__PURE__ */ jsx(Button, {
|
|
121
|
+
density: "md",
|
|
122
|
+
variant: "primary",
|
|
123
|
+
disabled: installing,
|
|
124
|
+
onClick: onInstall,
|
|
125
|
+
children: installing ? t("installing.label") : t("install.label")
|
|
126
|
+
}) : /* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsx(Input.Switch, {
|
|
127
|
+
classNames: "self-center",
|
|
128
|
+
checked: enabled,
|
|
129
|
+
onCheckedChange: onEnabledChange
|
|
130
|
+
}) }),
|
|
131
|
+
/* @__PURE__ */ jsxs("div", {
|
|
132
|
+
className: "flex items-center gap-1 pt-0.5 text-sm text-description",
|
|
133
|
+
children: [slug, author && /* @__PURE__ */ jsx("span", {
|
|
134
|
+
className: "dx-tag dx-tag--info",
|
|
135
|
+
children: author
|
|
136
|
+
})]
|
|
137
|
+
})
|
|
138
|
+
]
|
|
139
|
+
}),
|
|
140
|
+
description && /* @__PURE__ */ jsxs(Section.Root, { children: [/* @__PURE__ */ jsx(Section.Heading, { title: t("description.label") }), /* @__PURE__ */ jsx(Section.Body, { children: /* @__PURE__ */ jsx(MarkdownView, {
|
|
141
|
+
classNames: "text-description",
|
|
142
|
+
content: description
|
|
143
|
+
}) })] }),
|
|
144
|
+
resolvedScreenshots.length > 0 && /* @__PURE__ */ jsxs(Section.Root, { children: [/* @__PURE__ */ jsx(Section.Heading, { title: t("preview.label") }), /* @__PURE__ */ jsx(Section.Body, { children: /* @__PURE__ */ jsx(Carousel.Root, {
|
|
145
|
+
count: resolvedScreenshots.length,
|
|
146
|
+
children: /* @__PURE__ */ jsxs(Carousel.Content, {
|
|
147
|
+
classNames: "contents",
|
|
148
|
+
children: [/* @__PURE__ */ jsx(Carousel.Viewport, { children: resolvedScreenshots.map((src, index) => /* @__PURE__ */ jsx(Carousel.Slide, {
|
|
149
|
+
index,
|
|
150
|
+
src,
|
|
151
|
+
alt: name
|
|
152
|
+
}, src)) }), /* @__PURE__ */ jsx(Carousel.Indicators, {})]
|
|
153
|
+
})
|
|
154
|
+
}) })] }),
|
|
155
|
+
/* @__PURE__ */ jsxs(Section.Root, { children: [/* @__PURE__ */ jsx(Section.Heading, { title: t("resources.label") }), /* @__PURE__ */ jsx(Section.Body, { children: /* @__PURE__ */ jsxs("div", {
|
|
156
|
+
className: "flex gap-3 items-center",
|
|
157
|
+
children: [
|
|
158
|
+
homePage && /* @__PURE__ */ jsxs(Link, {
|
|
159
|
+
href: homePage,
|
|
160
|
+
classNames: "text-sm text-description",
|
|
161
|
+
children: [t("home-page.label"), /* @__PURE__ */ jsx(Icon, {
|
|
162
|
+
icon: "ph--arrow-square-out--regular",
|
|
163
|
+
size: 3,
|
|
164
|
+
classNames: "ml-1 dx-icon-inline"
|
|
165
|
+
})]
|
|
166
|
+
}),
|
|
167
|
+
source && /* @__PURE__ */ jsxs(Link, {
|
|
168
|
+
href: source,
|
|
169
|
+
classNames: "text-sm text-description",
|
|
170
|
+
children: [t("source.label"), /* @__PURE__ */ jsx(Icon, {
|
|
171
|
+
icon: "ph--arrow-square-out--regular",
|
|
172
|
+
size: 3,
|
|
173
|
+
classNames: "ml-1 dx-icon-inline"
|
|
174
|
+
})]
|
|
175
|
+
}),
|
|
176
|
+
onOpenSpec && /* @__PURE__ */ jsx(Chip, {
|
|
177
|
+
id: slug,
|
|
178
|
+
name: t("open-spec.label"),
|
|
179
|
+
onClick: onOpenSpec
|
|
180
|
+
})
|
|
181
|
+
]
|
|
182
|
+
}) })] }),
|
|
183
|
+
dependencies && dependencies.length > 0 && /* @__PURE__ */ jsxs(Section.Root, { children: [/* @__PURE__ */ jsx(Section.Heading, { title: t("dependencies.label") }), /* @__PURE__ */ jsx(Section.Body, { children: /* @__PURE__ */ jsx("div", {
|
|
184
|
+
className: "flex flex-wrap gap-1",
|
|
185
|
+
children: dependencies.map((depId) => /* @__PURE__ */ jsx(Chip, {
|
|
186
|
+
id: depId,
|
|
187
|
+
name: onResolvePluginName?.(depId) ?? depId,
|
|
188
|
+
onClick: onNavigateToPlugin
|
|
189
|
+
}, depId))
|
|
190
|
+
}) })] }),
|
|
191
|
+
dependents && dependents.length > 0 && /* @__PURE__ */ jsxs(Section.Root, { children: [/* @__PURE__ */ jsx(Section.Heading, { title: t("dependents.label") }), /* @__PURE__ */ jsx(Section.Body, { children: /* @__PURE__ */ jsx("div", {
|
|
192
|
+
className: "flex flex-wrap gap-1",
|
|
193
|
+
children: dependents.map((dependentId) => /* @__PURE__ */ jsx(Chip, {
|
|
194
|
+
id: dependentId,
|
|
195
|
+
name: onResolvePluginName?.(dependentId) ?? dependentId,
|
|
196
|
+
onClick: onNavigateToPlugin
|
|
197
|
+
}, dependentId))
|
|
198
|
+
}) })] }),
|
|
199
|
+
versions && versions.length > 0 && /* @__PURE__ */ jsxs(Section.Root, { children: [/* @__PURE__ */ jsx(Section.Heading, { title: t("versions.label") }), /* @__PURE__ */ jsx(Section.Body, { children: /* @__PURE__ */ jsxs("div", {
|
|
200
|
+
className: "flex gap-2 items-center",
|
|
201
|
+
children: [/* @__PURE__ */ jsxs(Select.Root, {
|
|
202
|
+
value: selectedVersionTag,
|
|
203
|
+
onValueChange: onVersionChange,
|
|
204
|
+
children: [/* @__PURE__ */ jsx(Select.TriggerButton, { classNames: "min-w-32" }), /* @__PURE__ */ jsx(Select.Portal, { children: /* @__PURE__ */ jsx(Select.Content, { children: /* @__PURE__ */ jsx(Select.Viewport, { children: versions.map((versionEntry) => /* @__PURE__ */ jsxs(Select.Option, {
|
|
205
|
+
value: versionEntry.version,
|
|
206
|
+
children: [versionEntry.version, installedVersionTag === versionEntry.version ? ` (${t("installed.label")})` : ""]
|
|
207
|
+
}, versionEntry.version)) }) }) })]
|
|
208
|
+
}), onInstallVersion && /* @__PURE__ */ jsx(Button, {
|
|
209
|
+
density: "md",
|
|
210
|
+
variant: "primary",
|
|
211
|
+
disabled: installing || selectedVersionTag === installedVersionTag,
|
|
212
|
+
onClick: onInstallVersion,
|
|
213
|
+
children: installing ? t("installing.label") : t("install-version.label")
|
|
214
|
+
})]
|
|
215
|
+
}) })] }),
|
|
216
|
+
(onUninstall || hasUpdate && onUpdate || updating) && /* @__PURE__ */ jsxs("div", {
|
|
217
|
+
className: "col-start-2 col-span-2 flex gap-2",
|
|
218
|
+
children: [updating ? /* @__PURE__ */ jsx(Button, {
|
|
219
|
+
variant: "primary",
|
|
220
|
+
disabled: true,
|
|
221
|
+
children: t("updating.label")
|
|
222
|
+
}) : hasUpdate && onUpdate ? /* @__PURE__ */ jsx(Button, {
|
|
223
|
+
variant: "primary",
|
|
224
|
+
onClick: onUpdate,
|
|
225
|
+
children: t("update.label")
|
|
226
|
+
}) : null, onUninstall && /* @__PURE__ */ jsx(Button, {
|
|
227
|
+
onClick: onUninstall,
|
|
228
|
+
children: t("uninstall.label")
|
|
229
|
+
})]
|
|
230
|
+
})
|
|
231
|
+
]
|
|
232
|
+
}) })
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
PluginDetail.displayName = "PluginDetail";
|
|
236
|
+
var SectionRoot = ({ children }) => /* @__PURE__ */ jsx(Fragment, { children });
|
|
237
|
+
var SectionHeading = ({ title }) => /* @__PURE__ */ jsx("h2", {
|
|
238
|
+
className: "col-start-2 col-span-2 pt-6 pb-2 uppercase text-sm font-medium text-subdued",
|
|
239
|
+
children: title
|
|
240
|
+
});
|
|
241
|
+
var SectionBody = ({ classNames, children }) => /* @__PURE__ */ jsx("div", {
|
|
242
|
+
className: mx("col-start-2 flex flex-col gap-2", classNames),
|
|
243
|
+
children
|
|
244
|
+
});
|
|
245
|
+
var Section = {
|
|
246
|
+
Root: SectionRoot,
|
|
247
|
+
Heading: SectionHeading,
|
|
248
|
+
Body: SectionBody
|
|
249
|
+
};
|
|
250
|
+
var Chip = ({ id, name, onClick }) => /* @__PURE__ */ jsx(Tag, {
|
|
251
|
+
title: id,
|
|
252
|
+
onClick: onClick ? () => onClick(id) : void 0,
|
|
253
|
+
classNames: "dx-hover",
|
|
254
|
+
children: name
|
|
255
|
+
});
|
|
256
|
+
//#endregion
|
|
257
|
+
//#region src/components/PluginList/PluginItem.tsx
|
|
258
|
+
var PluginItem = ({ plugin, installed, installing, enabled = [], extraTags, onClick, onChange, onInstall, hasUpdate, onUpdate, updating, hasSettings: hasSettingsProp, onSettings, failure }) => {
|
|
259
|
+
const { t } = useTranslation(meta.profile.key);
|
|
260
|
+
const { key: id, name, description, tags, icon: rawIcon } = plugin.meta.profile;
|
|
261
|
+
const icon = rawIcon?.key ?? "ph--circle--regular";
|
|
262
|
+
const iconHue = rawIcon?.hue ?? "neutral";
|
|
263
|
+
const displayTags = useMemo(() => {
|
|
264
|
+
if (!extraTags || extraTags.length === 0) return tags ?? [];
|
|
265
|
+
const set = new Set(tags ?? []);
|
|
266
|
+
for (const tag of extraTags) set.add(tag);
|
|
267
|
+
return Array.from(set);
|
|
268
|
+
}, [tags, extraTags]);
|
|
269
|
+
const isEnabled = enabled.includes(id);
|
|
270
|
+
const isInstalled = installed ? installed.includes(id) : true;
|
|
271
|
+
const isInstalling = installing?.includes(id) ?? false;
|
|
272
|
+
const isUpdating = updating?.includes(id) ?? false;
|
|
273
|
+
const showInstallButton = !!onInstall && !isInstalled;
|
|
274
|
+
const showUpdateButton = !!onUpdate && isInstalled && !!hasUpdate;
|
|
275
|
+
const inputId = `${id}-input`;
|
|
276
|
+
const descriptionId = `${id}-description`;
|
|
277
|
+
const handleClick = useCallback(() => onClick?.(id), [id, onClick]);
|
|
278
|
+
const handleChange = useCallback((event) => {
|
|
279
|
+
event.stopPropagation();
|
|
280
|
+
onChange?.(id, !isEnabled);
|
|
281
|
+
}, [
|
|
282
|
+
id,
|
|
283
|
+
isEnabled,
|
|
284
|
+
onChange
|
|
285
|
+
]);
|
|
286
|
+
const handleInstall = useCallback((event) => {
|
|
287
|
+
event.stopPropagation();
|
|
288
|
+
onInstall?.(id);
|
|
289
|
+
}, [id, onInstall]);
|
|
290
|
+
const handleUpdate = useCallback((event) => {
|
|
291
|
+
event.stopPropagation();
|
|
292
|
+
onUpdate?.(id);
|
|
293
|
+
}, [id, onUpdate]);
|
|
294
|
+
const hasSettings = hasSettingsProp?.(id) ?? false;
|
|
295
|
+
const handleSettings = useCallback(() => onSettings?.(id), [id, onSettings]);
|
|
296
|
+
const styles = getStyles(iconHue);
|
|
297
|
+
const gridCols = "grid grid-cols-[5rem_1fr]";
|
|
298
|
+
const gridRows = "grid grid-cols-1 grid-rows-[40px_1fr_min-content_40px]";
|
|
299
|
+
return /* @__PURE__ */ jsxs(Listbox.Item, {
|
|
300
|
+
id,
|
|
301
|
+
"data-testid": `pluginList.${id}`,
|
|
302
|
+
"aria-describedby": descriptionId,
|
|
303
|
+
classNames: mx(gridCols, "items-stretch p-0 pe-2 cursor-default h-[14rem] w-full gap-3 dx-modal-surface rounded-md overflow-hidden"),
|
|
304
|
+
children: [/* @__PURE__ */ jsx("div", {
|
|
305
|
+
className: mx(gridRows, "rounded-l-md", styles.surface),
|
|
306
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
307
|
+
className: "flex justify-center row-start-2 cursor-pointer",
|
|
308
|
+
onClick: handleClick,
|
|
309
|
+
children: /* @__PURE__ */ jsx(Icon, {
|
|
310
|
+
classNames: styles.fg,
|
|
311
|
+
icon,
|
|
312
|
+
size: 14
|
|
313
|
+
})
|
|
314
|
+
})
|
|
315
|
+
}), /* @__PURE__ */ jsxs("div", {
|
|
316
|
+
className: mx(gridRows, "min-w-0"),
|
|
317
|
+
children: [
|
|
318
|
+
/* @__PURE__ */ jsxs("div", {
|
|
319
|
+
className: "flex items-center gap-2 overflow-hidden cursor-pointer",
|
|
320
|
+
onClick: handleClick,
|
|
321
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
322
|
+
className: "text-lg truncate",
|
|
323
|
+
children: name ?? id
|
|
324
|
+
}), failure && /* @__PURE__ */ jsx(PluginFailureBadge, { failure })]
|
|
325
|
+
}),
|
|
326
|
+
/* @__PURE__ */ jsx("div", { children: /* @__PURE__ */ jsx("p", {
|
|
327
|
+
className: "text-description line-clamp-4 min-w-0",
|
|
328
|
+
children: description
|
|
329
|
+
}) }),
|
|
330
|
+
/* @__PURE__ */ jsx("div", {
|
|
331
|
+
className: "flex -ms-0.5 overflow-x-auto scrollbar-none",
|
|
332
|
+
children: displayTags.map((tag) => /* @__PURE__ */ jsx(Tag, {
|
|
333
|
+
hue: tagColors[tag],
|
|
334
|
+
classNames: "text-xs uppercase",
|
|
335
|
+
children: tag
|
|
336
|
+
}, tag))
|
|
337
|
+
}),
|
|
338
|
+
/* @__PURE__ */ jsxs("div", {
|
|
339
|
+
className: "flex gap-2 items-center text-sm",
|
|
340
|
+
children: [
|
|
341
|
+
/* @__PURE__ */ jsx(IconButton, {
|
|
342
|
+
"aria-describedby": descriptionId,
|
|
343
|
+
classNames: "cursor-pointer",
|
|
344
|
+
icon: "ph--gear--regular",
|
|
345
|
+
label: t("plugin-settings.label"),
|
|
346
|
+
iconOnly: true,
|
|
347
|
+
size: 4,
|
|
348
|
+
onClick: handleSettings,
|
|
349
|
+
disabled: !hasSettings
|
|
350
|
+
}),
|
|
351
|
+
/* @__PURE__ */ jsx(Link, {
|
|
352
|
+
"aria-describedby": descriptionId,
|
|
353
|
+
classNames: "text-description cursor-pointer",
|
|
354
|
+
onClick: handleClick,
|
|
355
|
+
children: t("details.label")
|
|
356
|
+
}),
|
|
357
|
+
/* @__PURE__ */ jsx("div", { className: "grow" }),
|
|
358
|
+
/* @__PURE__ */ jsx("div", {
|
|
359
|
+
className: "pe-1",
|
|
360
|
+
children: isUpdating ? /* @__PURE__ */ jsx(Button, {
|
|
361
|
+
"aria-describedby": descriptionId,
|
|
362
|
+
density: "md",
|
|
363
|
+
variant: "primary",
|
|
364
|
+
disabled: true,
|
|
365
|
+
children: t("updating.label")
|
|
366
|
+
}) : showUpdateButton ? /* @__PURE__ */ jsx(Button, {
|
|
367
|
+
"aria-describedby": descriptionId,
|
|
368
|
+
density: "md",
|
|
369
|
+
variant: "primary",
|
|
370
|
+
onClick: handleUpdate,
|
|
371
|
+
children: t("update.label")
|
|
372
|
+
}) : showInstallButton ? /* @__PURE__ */ jsx(Button, {
|
|
373
|
+
"aria-describedby": descriptionId,
|
|
374
|
+
density: "md",
|
|
375
|
+
variant: "primary",
|
|
376
|
+
disabled: isInstalling,
|
|
377
|
+
onClick: handleInstall,
|
|
378
|
+
children: isInstalling ? t("installing.label") : t("install.label")
|
|
379
|
+
}) : /* @__PURE__ */ jsx(Input.Root, {
|
|
380
|
+
id: inputId,
|
|
381
|
+
children: /* @__PURE__ */ jsx(Input.Switch, {
|
|
382
|
+
classNames: "self-center",
|
|
383
|
+
checked: isEnabled,
|
|
384
|
+
onClick: handleChange
|
|
385
|
+
})
|
|
386
|
+
})
|
|
387
|
+
})
|
|
388
|
+
]
|
|
389
|
+
})
|
|
390
|
+
]
|
|
391
|
+
})]
|
|
392
|
+
});
|
|
393
|
+
};
|
|
394
|
+
var tagColors = {
|
|
395
|
+
new: "rose",
|
|
396
|
+
beta: "green",
|
|
397
|
+
alpha: "blue",
|
|
398
|
+
labs: "purple",
|
|
399
|
+
popular: "green",
|
|
400
|
+
featured: "pink",
|
|
401
|
+
experimental: "amber",
|
|
402
|
+
registry: "indigo",
|
|
403
|
+
local: "neutral"
|
|
404
|
+
};
|
|
405
|
+
//#endregion
|
|
406
|
+
//#region src/components/PluginList/PluginList.tsx
|
|
407
|
+
var PluginList = ({ plugins = [], extraTagsById, updateAvailableIds, failuresById, ...props }) => {
|
|
408
|
+
return /* @__PURE__ */ jsx(Listbox.Root, { children: /* @__PURE__ */ jsx(Listbox.Content, {
|
|
409
|
+
"aria-label": "plugins",
|
|
410
|
+
classNames: "grid grid-cols-[repeat(auto-fill,minmax(18rem,1fr))] auto-rows-[max-content] gap-4 p-4",
|
|
411
|
+
children: plugins.map((plugin) => /* @__PURE__ */ jsx(PluginItem, {
|
|
412
|
+
plugin,
|
|
413
|
+
extraTags: extraTagsById?.[plugin.meta.profile.key],
|
|
414
|
+
hasUpdate: updateAvailableIds?.has(plugin.meta.profile.key),
|
|
415
|
+
failure: failuresById?.[plugin.meta.profile.key],
|
|
416
|
+
...props
|
|
417
|
+
}, plugin.meta.profile.key))
|
|
418
|
+
}) });
|
|
419
|
+
};
|
|
420
|
+
//#endregion
|
|
421
|
+
//#region src/components/RegistrySettings/RegistrySettings.tsx
|
|
422
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-registry/src/components/RegistrySettings/RegistrySettings.tsx";
|
|
423
|
+
/**
|
|
424
|
+
* Settings panel for `@dxos/plugin-registry`. The dev-plugin section lets a
|
|
425
|
+
* plugin author point Composer at a local Vite dev server (defaults to
|
|
426
|
+
* `localhost:3967`) and toggle the dev plugin on/off persistently — boot will
|
|
427
|
+
* re-attach it on the next reload, surviving HMR-triggered reloads. If the
|
|
428
|
+
* dev server is offline at boot, the toggle stays on and a warning is logged
|
|
429
|
+
* (the manager's `failed` atom also surfaces a badge on the plugin list).
|
|
430
|
+
*
|
|
431
|
+
* The URL input and toggle are rendered as `Form.Row` action rows (not schema
|
|
432
|
+
* fields): the input needs a dynamic disabled state and the toggle runs async
|
|
433
|
+
* enable/disable side effects, neither of which a plain schema field expresses.
|
|
434
|
+
*/
|
|
435
|
+
var RegistrySettings = ({ settings, onSettingsChange, activeDevPluginIds, onEnableDev, onDisableDev }) => {
|
|
436
|
+
const { t } = useTranslation(meta.profile.key);
|
|
437
|
+
const [busy, setBusy] = useState(false);
|
|
438
|
+
const enabled = !!settings.devPluginEnabled;
|
|
439
|
+
const url = settings.devPluginUrl ?? "";
|
|
440
|
+
const trimmedUrl = url.trim();
|
|
441
|
+
const loadedDevId = activeDevPluginIds[0];
|
|
442
|
+
const handleToggle = useCallback(async () => {
|
|
443
|
+
if (busy || !onSettingsChange) return;
|
|
444
|
+
setBusy(true);
|
|
445
|
+
try {
|
|
446
|
+
if (enabled && loadedDevId) {
|
|
447
|
+
await onDisableDev(loadedDevId);
|
|
448
|
+
onSettingsChange((current) => ({
|
|
449
|
+
...current,
|
|
450
|
+
devPluginEnabled: false
|
|
451
|
+
}));
|
|
452
|
+
} else if (enabled) onSettingsChange((current) => ({
|
|
453
|
+
...current,
|
|
454
|
+
devPluginEnabled: false
|
|
455
|
+
}));
|
|
456
|
+
else {
|
|
457
|
+
if (!trimmedUrl) return;
|
|
458
|
+
onSettingsChange((current) => ({
|
|
459
|
+
...current,
|
|
460
|
+
devPluginEnabled: true
|
|
461
|
+
}));
|
|
462
|
+
try {
|
|
463
|
+
await onEnableDev(trimmedUrl);
|
|
464
|
+
} catch (error) {
|
|
465
|
+
log.warn("dev plugin enable failed", {
|
|
466
|
+
url: trimmedUrl,
|
|
467
|
+
error
|
|
468
|
+
}, {
|
|
469
|
+
"~LogMeta": "~LogMeta",
|
|
470
|
+
F: __dxlog_file,
|
|
471
|
+
L: 76,
|
|
472
|
+
S: void 0
|
|
473
|
+
});
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
} finally {
|
|
477
|
+
setBusy(false);
|
|
478
|
+
}
|
|
479
|
+
}, [
|
|
480
|
+
busy,
|
|
481
|
+
enabled,
|
|
482
|
+
loadedDevId,
|
|
483
|
+
onSettingsChange,
|
|
484
|
+
onDisableDev,
|
|
485
|
+
onEnableDev,
|
|
486
|
+
trimmedUrl
|
|
487
|
+
]);
|
|
488
|
+
const buttonLabel = busy ? t("dev-plugin.busy.label") : enabled ? t("dev-plugin.disable.label") : t("dev-plugin.enable.label");
|
|
489
|
+
return /* @__PURE__ */ jsx(Form.Root, {
|
|
490
|
+
variant: "settings",
|
|
491
|
+
readonly: !onSettingsChange,
|
|
492
|
+
schema: RegistrySettingsSchema,
|
|
493
|
+
values: settings,
|
|
494
|
+
children: /* @__PURE__ */ jsx(Form.Viewport, {
|
|
495
|
+
scroll: true,
|
|
496
|
+
children: /* @__PURE__ */ jsx(Form.Content, { children: /* @__PURE__ */ jsxs(Form.Section, {
|
|
497
|
+
title: t("dev-plugin.section.title"),
|
|
498
|
+
children: [
|
|
499
|
+
/* @__PURE__ */ jsx(Message.Root, {
|
|
500
|
+
valence: "neutral",
|
|
501
|
+
children: /* @__PURE__ */ jsx(Message.Body, { children: t("dev-plugin.description") })
|
|
502
|
+
}),
|
|
503
|
+
/* @__PURE__ */ jsx(Form.Row, {
|
|
504
|
+
label: t("dev-plugin.url.label"),
|
|
505
|
+
description: t("dev-plugin.url.description"),
|
|
506
|
+
children: /* @__PURE__ */ jsx(Input.Root, { children: /* @__PURE__ */ jsx(Input.TextInput, {
|
|
507
|
+
disabled: !onSettingsChange || enabled || busy,
|
|
508
|
+
value: url,
|
|
509
|
+
onChange: (event) => onSettingsChange?.((current) => ({
|
|
510
|
+
...current,
|
|
511
|
+
devPluginUrl: event.target.value
|
|
512
|
+
}))
|
|
513
|
+
}) })
|
|
514
|
+
}),
|
|
515
|
+
/* @__PURE__ */ jsx(Form.Row, {
|
|
516
|
+
label: t("dev-plugin.toggle.label"),
|
|
517
|
+
description: t("dev-plugin.toggle.description"),
|
|
518
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
519
|
+
variant: enabled ? void 0 : "primary",
|
|
520
|
+
disabled: !onSettingsChange || busy || !enabled && !trimmedUrl,
|
|
521
|
+
onClick: () => void handleToggle(),
|
|
522
|
+
children: buttonLabel
|
|
523
|
+
})
|
|
524
|
+
}),
|
|
525
|
+
enabled && !loadedDevId && !busy && /* @__PURE__ */ jsx(Message.Root, {
|
|
526
|
+
valence: "warning",
|
|
527
|
+
children: /* @__PURE__ */ jsx(Message.Body, { children: t("dev-plugin.not-loaded.message") })
|
|
528
|
+
})
|
|
529
|
+
]
|
|
530
|
+
}) })
|
|
531
|
+
})
|
|
532
|
+
});
|
|
533
|
+
};
|
|
534
|
+
//#endregion
|
|
535
|
+
export { DisableDependentsAlert as a, PluginFailureBadge as i, PluginList as n, PluginDetail as r, RegistrySettings as t };
|
|
536
|
+
|
|
537
|
+
//# sourceMappingURL=chunk-components.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-components.mjs","names":[],"sources":["../../src/components/DisableDependentsAlert/DisableDependentsAlert.tsx","../../src/components/PluginFailureBadge/PluginFailureBadge.tsx","../../src/components/PluginDetail/PluginDetail.tsx","../../src/components/PluginList/PluginItem.tsx","../../src/components/PluginList/PluginList.tsx","../../src/components/RegistrySettings/RegistrySettings.tsx"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport React from 'react';\n\nimport { AlertDialog, Button, useTranslation } from '@dxos/react-ui';\n\nimport { meta } from '#meta';\n\nexport type DisableDependentsAlertProps = {\n /** Id of the plugin the user requested to disable. */\n pluginId: string;\n /** Ids of the currently-enabled dependents that would also be disabled. */\n dependents: readonly string[];\n /**\n * Resolves a plugin id to its display name. The component delegates this so\n * the parent can choose how to source names (typically the registered\n * `Plugin.Meta.name`). Falls back to the id when omitted.\n */\n onResolvePluginName?: (pluginId: string) => string;\n onConfirm: () => void;\n};\n\n/**\n * Confirmation prompt shown when a user toggles off a plugin that has\n * currently-enabled dependents. Rendered inside the layout's dialog surface\n * (via `LayoutOperation.UpdateDialog` with `type: 'alert'`), which provides\n * the `AlertDialog.Root` / `Overlay` wrappers — this component renders only\n * the content. `onConfirm` runs the cascade disable and closes the dialog;\n * cancel is handled by the layout's open-change wiring.\n */\nexport const DisableDependentsAlert = ({\n pluginId,\n dependents,\n onResolvePluginName,\n onConfirm,\n}: DisableDependentsAlertProps) => {\n const { t } = useTranslation(meta.profile.key);\n const resolveName = onResolvePluginName ?? ((id: string) => id);\n return (\n <AlertDialog.Content>\n <AlertDialog.Body>\n <AlertDialog.Title>{t('disable-dependents-dialog.title')}</AlertDialog.Title>\n <AlertDialog.Description>\n {t('disable-dependents-dialog.description', { plugin: resolveName(pluginId) })}\n </AlertDialog.Description>\n <ul className='mt-2 list-disc pl-6 text-sm text-description'>\n {dependents.map((dependentId) => (\n <li key={dependentId} title={dependentId}>\n {resolveName(dependentId)}\n </li>\n ))}\n </ul>\n </AlertDialog.Body>\n <AlertDialog.ActionBar>\n <div className='grow' />\n <AlertDialog.Cancel asChild>\n <Button>{t('cancel.label')}</Button>\n </AlertDialog.Cancel>\n <AlertDialog.Action asChild>\n <Button variant='primary' onClick={onConfirm}>\n {t('disable-dependents-dialog.confirm.label')}\n </Button>\n </AlertDialog.Action>\n </AlertDialog.ActionBar>\n </AlertDialog.Content>\n );\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport React from 'react';\n\nimport { type PluginManager } from '@dxos/app-framework';\nimport { IconButton, Popover, useTranslation } from '@dxos/react-ui';\n\nimport { meta } from '#meta';\n\n// Mirrors react-ui `Icon`'s `size` prop literal subset; kept inline so we don't\n// have to add `@dxos/ui-types` as a dep just for one type alias.\ntype IconSize = 4 | 5 | 6 | 8;\n\nexport type PluginFailureBadgeProps = {\n failure: PluginManager.PluginFailure;\n /** Visual size of the warning icon (passed through to react-ui `<Icon />`). */\n size?: IconSize;\n};\n\n/**\n * Compact warning glyph rendered next to a plugin's name when it has failed\n * to load or activate. Clicking the icon opens a popover that names the phase\n * (load / activation), the reason (timeout / error), and the underlying error\n * message — enough for an operator to tell \"remote host is offline\" apart\n * from \"the plugin crashed\".\n */\nexport const PluginFailureBadge = ({ failure, size }: PluginFailureBadgeProps) => {\n const { t } = useTranslation(meta.profile.key);\n\n return (\n <Popover.Root>\n <Popover.Trigger asChild>\n <IconButton\n variant='destructive'\n icon='ph--warning--bold'\n iconOnly\n noTooltip\n size={size}\n label={t('failure-badge.label')}\n data-testid={`pluginFailureBadge.${failure.id}`}\n onClick={(event) => event.stopPropagation()}\n />\n </Popover.Trigger>\n <Popover.Portal>\n <Popover.Content>\n <Popover.Viewport>\n <div className='px-3 py-2 min-w-[18rem] max-w-[28rem] flex flex-col gap-1'>\n <p className='font-medium text-sm'>\n {t('failure-title.label', {\n phase: failure.phase === 'load' ? t('failure-phase-load.label') : t('failure-phase-activation.label'),\n reason:\n failure.reason === 'timeout' ? t('failure-reason-timeout.label') : t('failure-reason-error.label'),\n })}\n </p>\n <p className='text-description text-sm break-words'>{failure.error.message}</p>\n </div>\n </Popover.Viewport>\n <Popover.Arrow />\n </Popover.Content>\n </Popover.Portal>\n </Popover.Root>\n );\n};\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { type PropsWithChildren } from 'react';\n\nimport { type Plugin, type PluginManager } from '@dxos/app-framework';\nimport {\n Button,\n Carousel,\n Icon,\n Input,\n Link,\n ScrollArea,\n Select,\n Tag,\n ThemedClassName,\n useThemeContext,\n useTranslation,\n} from '@dxos/react-ui';\nimport { composable, composableProps } from '@dxos/react-ui';\nimport { MarkdownView } from '@dxos/react-ui-markdown';\nimport { getStyles, mx } from '@dxos/ui-theme';\n\nimport { meta } from '#meta';\n\nimport { PluginFailureBadge } from '../PluginFailureBadge';\n\nexport type PluginDetailProps = {\n plugin: Plugin.Plugin;\n enabled?: boolean;\n /** True while an in-flight install is running. Disables the install button. */\n installing?: boolean;\n /** True while an in-flight update is running. Disables the update button. */\n updating?: boolean;\n /** True when the catalog has a newer version than the one installed. */\n hasUpdate?: boolean;\n /** Currently installed version tag (used to mark the matching select option). */\n installedVersionTag?: string;\n /** Currently selected version tag in the picker. */\n selectedVersionTag?: string;\n /** Available versions of this plugin from the catalog. When non-empty, a version picker is shown. */\n versions?: readonly Plugin.Release[];\n /**\n * Ids of plugins this plugin declares as dependencies (direct only). Rendered\n * under a \"Requires\" heading so the user can see what enabling this plugin\n * will auto-enable.\n */\n dependencies?: readonly string[];\n /**\n * Ids of plugins that declare this plugin as a dependency (direct only).\n * Rendered under a \"Required by\" heading so the user understands the\n * downstream impact of disabling this plugin.\n */\n dependents?: readonly string[];\n /**\n * Failure record for this plugin, if any. When present a warning badge is\n * rendered next to the plugin name in the header.\n */\n failure?: PluginManager.PluginFailure;\n onEnabledChange?: (enabled: boolean) => void;\n /**\n * When provided, the plugin is not installed and an Install button is shown\n * in place of the enable Switch.\n */\n onInstall?: () => void;\n /**\n * Called when the user clicks Install on the version picker.\n */\n onInstallVersion?: () => void;\n /**\n * Called when the user activates a dependency / dependent chip. When\n * provided, chips are rendered as buttons that fire this handler with the\n * target plugin id; when omitted, chips are non-interactive labels.\n */\n onNavigateToPlugin?: (pluginId: string) => void;\n /**\n * Called when the user activates the in-app spec viewer link. When provided\n * (i.e. the plugin bundles its MDL via `Plugin.Meta.specContent`), a second\n * \"View specification\" link is rendered next to the external spec link.\n */\n onOpenSpec?: () => void;\n /**\n * Resolves a plugin id to its display name for dependency / dependent\n * chip labels. The component delegates the lookup to the parent so each\n * surface can decide how to source names (e.g. `Plugin.Meta.name` from the\n * registered plugin set). When omitted, chips render the raw id.\n */\n onResolvePluginName?: (pluginId: string) => string;\n /**\n * When provided, an Uninstall button is rendered. Leave undefined for core\n * or non-removable plugins.\n */\n onUninstall?: () => void;\n /**\n * When provided and the plugin is installed with a newer catalog version available,\n * an Update button is shown in place of the enable Switch.\n */\n onUpdate?: () => void;\n /**\n * Called when the user selects a different version in the picker.\n */\n onVersionChange?: (tag: string) => void;\n};\n\nexport const PluginDetail = composable<HTMLDivElement, PluginDetailProps>(\n (\n {\n plugin,\n enabled,\n installing,\n updating,\n hasUpdate,\n installedVersionTag,\n selectedVersionTag,\n versions,\n dependencies,\n dependents,\n failure,\n onEnabledChange,\n onInstall,\n onInstallVersion,\n onNavigateToPlugin,\n onOpenSpec,\n onResolvePluginName,\n onUninstall,\n onUpdate,\n onVersionChange,\n ...props\n },\n forwardedRef,\n ) => {\n const { t } = useTranslation(meta.profile.key);\n const { themeMode } = useThemeContext();\n const { key: slug, name, author, description, homePage, source, screenshots, icon: rawIcon } = plugin.meta.profile;\n const iconKey = rawIcon?.key ?? 'ph--circle--regular';\n const iconHue = rawIcon?.hue ?? 'neutral';\n const styles = getStyles(iconHue);\n\n const resolvedScreenshots = (screenshots ?? [])\n .map((entry) => (themeMode === 'dark' ? (entry.dark ?? entry.light) : (entry.light ?? entry.dark)))\n .filter((url): url is string => typeof url === 'string' && url.length > 0);\n\n return (\n <ScrollArea.Root {...composableProps(props)} orientation='vertical' ref={forwardedRef}>\n <ScrollArea.Viewport>\n {/*\n * 3-column grid: [icon/prev | content/viewport | next].\n * Most sections occupy `col-start-2 col-span-2` so they stretch from\n * the centre column out to the trailing edge. The carousel uses\n * `display: contents` to flatten its children into this grid so\n * `Carousel.Previous` lands in col 1, `Carousel.Viewport` aligns\n * with the rest of the content in col 2, and `Carousel.Next` sits\n * in col 3.\n */}\n <div className='dx-document grid grid-cols-[4rem_minmax(0,1fr)_4rem] gap-x-4 p-4 items-start'>\n <Icon classNames={mx('row-start-1 p-1 rounded-md', styles.bg, styles.fg)} icon={iconKey} size={14} />\n\n <div className='row-start-1 col-start-2 col-span-2 grid grid-cols-[1fr_min-content] gap-x-3 w-full pt-1'>\n <div className='flex items-center gap-2'>\n <h2 className='text-xl'>{name}</h2>\n {failure && <PluginFailureBadge failure={failure} size={5} />}\n </div>\n {onInstall ? (\n <Button density='md' variant='primary' disabled={installing} onClick={onInstall}>\n {installing ? t('installing.label') : t('install.label')}\n </Button>\n ) : (\n <Input.Root>\n <Input.Switch classNames='self-center' checked={enabled} onCheckedChange={onEnabledChange} />\n </Input.Root>\n )}\n <div className='flex items-center gap-1 pt-0.5 text-sm text-description'>\n {slug}\n {author && <span className='dx-tag dx-tag--info'>{author}</span>}\n </div>\n </div>\n\n {description && (\n <Section.Root>\n <Section.Heading title={t('description.label')} />\n <Section.Body>\n <MarkdownView classNames='text-description' content={description} />\n </Section.Body>\n </Section.Root>\n )}\n\n {resolvedScreenshots.length > 0 && (\n <Section.Root>\n <Section.Heading title={t('preview.label')} />\n <Section.Body>\n <Carousel.Root count={resolvedScreenshots.length}>\n <Carousel.Content classNames='contents'>\n <Carousel.Viewport>\n {resolvedScreenshots.map((src, index) => (\n <Carousel.Slide key={src} index={index} src={src} alt={name} />\n ))}\n </Carousel.Viewport>\n <Carousel.Indicators />\n </Carousel.Content>\n </Carousel.Root>\n </Section.Body>\n </Section.Root>\n )}\n\n <Section.Root>\n <Section.Heading title={t('resources.label')} />\n <Section.Body>\n <div className='flex gap-3 items-center'>\n {homePage && (\n <Link href={homePage} classNames='text-sm text-description'>\n {t('home-page.label')}\n <Icon icon='ph--arrow-square-out--regular' size={3} classNames='ml-1 dx-icon-inline' />\n </Link>\n )}\n\n {source && (\n <Link href={source} classNames='text-sm text-description'>\n {t('source.label')}\n <Icon icon='ph--arrow-square-out--regular' size={3} classNames='ml-1 dx-icon-inline' />\n </Link>\n )}\n\n {onOpenSpec && <Chip id={slug} name={t('open-spec.label')} onClick={onOpenSpec} />}\n </div>\n </Section.Body>\n </Section.Root>\n\n {dependencies && dependencies.length > 0 && (\n <Section.Root>\n <Section.Heading title={t('dependencies.label')} />\n <Section.Body>\n <div className='flex flex-wrap gap-1'>\n {dependencies.map((depId) => (\n <Chip\n key={depId}\n id={depId}\n name={onResolvePluginName?.(depId) ?? depId}\n onClick={onNavigateToPlugin}\n />\n ))}\n </div>\n </Section.Body>\n </Section.Root>\n )}\n\n {dependents && dependents.length > 0 && (\n <Section.Root>\n <Section.Heading title={t('dependents.label')} />\n <Section.Body>\n <div className='flex flex-wrap gap-1'>\n {dependents.map((dependentId) => (\n <Chip\n key={dependentId}\n id={dependentId}\n name={onResolvePluginName?.(dependentId) ?? dependentId}\n onClick={onNavigateToPlugin}\n />\n ))}\n </div>\n </Section.Body>\n </Section.Root>\n )}\n\n {versions && versions.length > 0 && (\n <Section.Root>\n <Section.Heading title={t('versions.label')} />\n <Section.Body>\n <div className='flex gap-2 items-center'>\n <Select.Root value={selectedVersionTag} onValueChange={onVersionChange}>\n <Select.TriggerButton classNames='min-w-32' />\n <Select.Portal>\n <Select.Content>\n <Select.Viewport>\n {versions.map((versionEntry) => (\n <Select.Option key={versionEntry.version} value={versionEntry.version}>\n {versionEntry.version}\n {installedVersionTag === versionEntry.version ? ` (${t('installed.label')})` : ''}\n </Select.Option>\n ))}\n </Select.Viewport>\n </Select.Content>\n </Select.Portal>\n </Select.Root>\n {onInstallVersion && (\n <Button\n density='md'\n variant='primary'\n disabled={installing || selectedVersionTag === installedVersionTag}\n onClick={onInstallVersion}\n >\n {installing ? t('installing.label') : t('install-version.label')}\n </Button>\n )}\n </div>\n </Section.Body>\n </Section.Root>\n )}\n\n {(onUninstall || (hasUpdate && onUpdate) || updating) && (\n <div className='col-start-2 col-span-2 flex gap-2'>\n {updating ? (\n <Button variant='primary' disabled>\n {t('updating.label')}\n </Button>\n ) : hasUpdate && onUpdate ? (\n <Button variant='primary' onClick={onUpdate}>\n {t('update.label')}\n </Button>\n ) : null}\n {onUninstall && <Button onClick={onUninstall}>{t('uninstall.label')}</Button>}\n </div>\n )}\n </div>\n </ScrollArea.Viewport>\n </ScrollArea.Root>\n );\n },\n);\n\nPluginDetail.displayName = 'PluginDetail';\n\nconst SectionRoot = ({ children }: PropsWithChildren<{}>) => <>{children}</>;\n\nconst SectionHeading = ({ title }: { title: string }) => (\n <h2 className='col-start-2 col-span-2 pt-6 pb-2 uppercase text-sm font-medium text-subdued'>{title}</h2>\n);\n\nconst SectionBody = ({ classNames, children }: ThemedClassName<PropsWithChildren>) => (\n <div className={mx('col-start-2 flex flex-col gap-2', classNames)}>{children}</div>\n);\n\nconst Section = {\n Root: SectionRoot,\n Heading: SectionHeading,\n Body: SectionBody,\n};\n\nconst Chip = ({ id, name, onClick }: { id: string; name: string; onClick?: (pluginId: string) => void }) => (\n <Tag title={id} onClick={onClick ? () => onClick(id) : undefined} classNames='dx-hover'>\n {name}\n </Tag>\n);\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport React, { type MouseEvent, useCallback, useMemo } from 'react';\n\nimport { type Plugin, type PluginManager } from '@dxos/app-framework';\nimport {\n Button,\n type ChromaticPalette,\n Icon,\n IconButton,\n Input,\n Link,\n type NeutralPalette,\n Tag,\n useTranslation,\n} from '@dxos/react-ui';\nimport { Listbox } from '@dxos/react-ui-list';\nimport { mx } from '@dxos/ui-theme';\nimport { getStyles } from '@dxos/ui-theme';\n\nimport { meta } from '#meta';\nimport { type RegistryTagType } from '#types';\n\nimport { PluginFailureBadge } from '../PluginFailureBadge';\n\nexport type PluginItemProps = {\n plugin: Plugin.Plugin;\n /** Ids of plugins currently installed (loaded into the manager). */\n installed?: readonly string[];\n /** Ids of plugins whose install is in flight. */\n installing?: readonly string[];\n enabled?: readonly string[];\n /**\n * Derived tags (e.g. `registry`, `local`) to display alongside the plugin's own meta.tags.\n * Not persisted to plugin meta; computed per-render by the container.\n */\n extraTags?: readonly string[];\n onClick?: (id: string) => void;\n onChange?: (id: string, enabled: boolean) => void;\n /**\n * Install handler. When provided and the plugin is not installed, an Install button\n * is rendered in place of the enable switch.\n */\n onInstall?: (id: string) => void;\n /**\n * When true and plugin is installed, shows an Update button instead of the enable switch.\n */\n hasUpdate?: boolean;\n /** Called when the user clicks the Update button. */\n onUpdate?: (id: string) => void;\n /** Ids of plugins whose update is currently in flight. */\n updating?: readonly string[];\n hasSettings?: (id: string) => boolean;\n onSettings?: (id: string) => void;\n /**\n * Failure record for this plugin, if any. When present a warning badge is\n * rendered next to the plugin name; clicking it opens a popover with the\n * phase, reason, and error message.\n */\n failure?: PluginManager.PluginFailure;\n};\n\nexport const PluginItem = ({\n plugin,\n installed,\n installing,\n enabled = [],\n extraTags,\n onClick,\n onChange,\n onInstall,\n hasUpdate,\n onUpdate,\n updating,\n hasSettings: hasSettingsProp,\n onSettings,\n failure,\n}: PluginItemProps) => {\n const { t } = useTranslation(meta.profile.key);\n const { key: id, name, description, tags, icon: rawIcon } = plugin.meta.profile;\n const icon = rawIcon?.key ?? 'ph--circle--regular';\n const iconHue = rawIcon?.hue ?? 'neutral';\n const displayTags = useMemo(() => {\n if (!extraTags || extraTags.length === 0) {\n return tags ?? [];\n }\n const set = new Set<string>(tags ?? []);\n for (const tag of extraTags) {\n set.add(tag);\n }\n return Array.from(set);\n }, [tags, extraTags]);\n const isEnabled = enabled.includes(id);\n const isInstalled = installed ? installed.includes(id) : true;\n const isInstalling = installing?.includes(id) ?? false;\n const isUpdating = updating?.includes(id) ?? false;\n const showInstallButton = !!onInstall && !isInstalled;\n const showUpdateButton = !!onUpdate && isInstalled && !!hasUpdate;\n const inputId = `${id}-input`;\n const descriptionId = `${id}-description`;\n const handleClick = useCallback(() => onClick?.(id), [id, onClick]);\n\n const handleChange = useCallback(\n (event: MouseEvent) => {\n event.stopPropagation();\n onChange?.(id, !isEnabled);\n },\n [id, isEnabled, onChange],\n );\n\n const handleInstall = useCallback(\n (event: MouseEvent) => {\n event.stopPropagation();\n onInstall?.(id);\n },\n [id, onInstall],\n );\n\n const handleUpdate = useCallback(\n (event: MouseEvent) => {\n event.stopPropagation();\n onUpdate?.(id);\n },\n [id, onUpdate],\n );\n\n const hasSettings = hasSettingsProp?.(id) ?? false;\n const handleSettings = useCallback(() => onSettings?.(id), [id, onSettings]);\n const styles = getStyles(iconHue);\n const gridCols = 'grid grid-cols-[5rem_1fr]';\n const gridRows = 'grid grid-cols-1 grid-rows-[40px_1fr_min-content_40px]';\n\n return (\n <Listbox.Item\n id={id}\n data-testid={`pluginList.${id}`}\n aria-describedby={descriptionId}\n classNames={mx(\n gridCols,\n // Override `Listbox.Item`'s default row chrome (flex/items-center/padding/cursor) so the\n // bespoke card grid stretches both columns to full height and controls its own padding.\n 'items-stretch p-0 pe-2 cursor-default h-[14rem] w-full gap-3 dx-modal-surface rounded-md overflow-hidden',\n )}\n >\n <div className={mx(gridRows, 'rounded-l-md', styles.surface)}>\n <div className='flex justify-center row-start-2 cursor-pointer' onClick={handleClick}>\n <Icon classNames={styles.fg} icon={icon} size={14} />\n </div>\n </div>\n\n <div className={mx(gridRows, 'min-w-0')}>\n <div className='flex items-center gap-2 overflow-hidden cursor-pointer' onClick={handleClick}>\n <span className='text-lg truncate'>{name ?? id}</span>\n {failure && <PluginFailureBadge failure={failure} />}\n </div>\n\n <div>\n <p className='text-description line-clamp-4 min-w-0'>{description}</p>\n </div>\n\n <div className='flex -ms-0.5 overflow-x-auto scrollbar-none'>\n {displayTags.map((tag: string) => (\n <Tag key={tag} hue={tagColors[tag as RegistryTagType]} classNames='text-xs uppercase'>\n {tag}\n </Tag>\n ))}\n </div>\n\n <div className='flex gap-2 items-center text-sm'>\n <IconButton\n aria-describedby={descriptionId}\n classNames='cursor-pointer'\n icon='ph--gear--regular'\n label={t('plugin-settings.label')}\n iconOnly\n size={4}\n onClick={handleSettings}\n disabled={!hasSettings}\n />\n\n <Link aria-describedby={descriptionId} classNames='text-description cursor-pointer' onClick={handleClick}>\n {t('details.label')}\n </Link>\n\n <div className='grow' />\n <div className='pe-1'>\n {isUpdating ? (\n <Button aria-describedby={descriptionId} density='md' variant='primary' disabled>\n {t('updating.label')}\n </Button>\n ) : showUpdateButton ? (\n <Button aria-describedby={descriptionId} density='md' variant='primary' onClick={handleUpdate}>\n {t('update.label')}\n </Button>\n ) : showInstallButton ? (\n <Button\n aria-describedby={descriptionId}\n density='md'\n variant='primary'\n disabled={isInstalling}\n onClick={handleInstall}\n >\n {isInstalling ? t('installing.label') : t('install.label')}\n </Button>\n ) : (\n <Input.Root id={inputId}>\n <Input.Switch classNames='self-center' checked={isEnabled} onClick={handleChange} />\n </Input.Root>\n )}\n </div>\n </div>\n </div>\n </Listbox.Item>\n );\n};\n\nconst tagColors: Record<RegistryTagType, ChromaticPalette | NeutralPalette> = {\n new: 'rose',\n // Tier hues ramp green -> blue -> purple so the ordering reads without knowing the labels.\n beta: 'green',\n alpha: 'blue',\n labs: 'purple',\n popular: 'green',\n featured: 'pink',\n experimental: 'amber',\n registry: 'indigo',\n local: 'neutral',\n};\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport React from 'react';\n\nimport { type Plugin, type PluginManager } from '@dxos/app-framework';\nimport { Listbox } from '@dxos/react-ui-list';\n\nimport { PluginItem, type PluginItemProps } from './PluginItem';\n\nexport type PluginListProps = Omit<PluginItemProps, 'plugin' | 'extraTags' | 'hasUpdate' | 'failure'> & {\n plugins?: readonly Plugin.Plugin[];\n /**\n * Map from plugin id → extra tags to display (e.g. `registry`, `local`).\n * Computed by the container; not persisted to plugin meta.\n */\n extraTagsById?: Record<string, readonly string[]>;\n /**\n * Set of plugin ids for which a newer version is available in the catalog.\n * Causes each matching item to render an Update button instead of the enable switch.\n */\n updateAvailableIds?: ReadonlySet<string>;\n /**\n * Map from plugin id → most recent failure record. Used to render a warning\n * badge next to the plugin name. Sourced from `PluginManager.failed`.\n */\n failuresById?: Record<string, PluginManager.PluginFailure>;\n};\n\nexport const PluginList = ({\n plugins = [],\n extraTagsById,\n updateAvailableIds,\n failuresById,\n ...props\n}: PluginListProps) => {\n return (\n <Listbox.Root>\n <Listbox.Content\n aria-label='plugins'\n classNames='grid grid-cols-[repeat(auto-fill,minmax(18rem,1fr))] auto-rows-[max-content] gap-4 p-4'\n >\n {plugins.map((plugin) => (\n <PluginItem\n key={plugin.meta.profile.key}\n plugin={plugin}\n extraTags={extraTagsById?.[plugin.meta.profile.key]}\n hasUpdate={updateAvailableIds?.has(plugin.meta.profile.key)}\n failure={failuresById?.[plugin.meta.profile.key]}\n {...props}\n />\n ))}\n </Listbox.Content>\n </Listbox.Root>\n );\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport React, { useCallback, useState } from 'react';\n\nimport { type AppSurface } from '@dxos/app-toolkit/ui';\nimport { log } from '@dxos/log';\nimport { Button, Input, Message, useTranslation } from '@dxos/react-ui';\nimport { Form } from '@dxos/react-ui-form';\n\nimport { meta } from '#meta';\n\nimport { RegistrySettingsSchema, type RegistrySettings as RegistrySettingsType } from '../../types';\n\nexport type RegistrySettingsProps = AppSurface.SettingsProps<\n RegistrySettingsType,\n {\n activeDevPluginIds: readonly string[];\n onEnableDev: (url: string) => Promise<void>;\n onDisableDev: (id: string) => Promise<void>;\n }\n>;\n\n/**\n * Settings panel for `@dxos/plugin-registry`. The dev-plugin section lets a\n * plugin author point Composer at a local Vite dev server (defaults to\n * `localhost:3967`) and toggle the dev plugin on/off persistently — boot will\n * re-attach it on the next reload, surviving HMR-triggered reloads. If the\n * dev server is offline at boot, the toggle stays on and a warning is logged\n * (the manager's `failed` atom also surfaces a badge on the plugin list).\n *\n * The URL input and toggle are rendered as `Form.Row` action rows (not schema\n * fields): the input needs a dynamic disabled state and the toggle runs async\n * enable/disable side effects, neither of which a plain schema field expresses.\n */\nexport const RegistrySettings = ({\n settings,\n onSettingsChange,\n activeDevPluginIds,\n onEnableDev,\n onDisableDev,\n}: RegistrySettingsProps) => {\n const { t } = useTranslation(meta.profile.key);\n const [busy, setBusy] = useState(false);\n const enabled = !!settings.devPluginEnabled;\n const url = settings.devPluginUrl ?? '';\n const trimmedUrl = url.trim();\n const loadedDevId = activeDevPluginIds[0];\n\n const handleToggle = useCallback(async () => {\n if (busy || !onSettingsChange) {\n return;\n }\n setBusy(true);\n try {\n if (enabled && loadedDevId) {\n await onDisableDev(loadedDevId);\n onSettingsChange((current) => ({ ...current, devPluginEnabled: false }));\n } else if (enabled) {\n // Toggle is on but no dev plugin is currently loaded (boot-time load\n // failed or hasn't finished). Treat the click as \"turn it off.\"\n onSettingsChange((current) => ({ ...current, devPluginEnabled: false }));\n } else {\n if (!trimmedUrl) {\n return;\n }\n // Optimistically persist intent so the next reload retries even if\n // the page closes mid-load.\n onSettingsChange((current) => ({ ...current, devPluginEnabled: true }));\n try {\n await onEnableDev(trimmedUrl);\n } catch (error) {\n // Per product direction: leave the intent on, log the failure. The\n // user explicitly turns it off if they're done iterating.\n log.warn('dev plugin enable failed', { url: trimmedUrl, error });\n }\n }\n } finally {\n setBusy(false);\n }\n }, [busy, enabled, loadedDevId, onSettingsChange, onDisableDev, onEnableDev, trimmedUrl]);\n\n // Button label tracks the persisted intent, not the current load state, so\n // a transient \"intent on but not loaded\" combination still reads as\n // \"Disable Dev Plugin\" — clicking it turns the intent off cleanly.\n const buttonLabel = busy\n ? t('dev-plugin.busy.label')\n : enabled\n ? t('dev-plugin.disable.label')\n : t('dev-plugin.enable.label');\n\n return (\n <Form.Root variant='settings' readonly={!onSettingsChange} schema={RegistrySettingsSchema} values={settings}>\n <Form.Viewport scroll>\n <Form.Content>\n <Form.Section title={t('dev-plugin.section.title')}>\n <Message.Root valence='neutral'>\n <Message.Body>{t('dev-plugin.description')}</Message.Body>\n </Message.Root>\n <Form.Row label={t('dev-plugin.url.label')} description={t('dev-plugin.url.description')}>\n <Input.Root>\n <Input.TextInput\n disabled={!onSettingsChange || enabled || busy}\n value={url}\n onChange={(event) =>\n onSettingsChange?.((current) => ({ ...current, devPluginUrl: event.target.value }))\n }\n />\n </Input.Root>\n </Form.Row>\n <Form.Row label={t('dev-plugin.toggle.label')} description={t('dev-plugin.toggle.description')}>\n <Button\n variant={enabled ? undefined : 'primary'}\n disabled={!onSettingsChange || busy || (!enabled && !trimmedUrl)}\n onClick={() => void handleToggle()}\n >\n {buttonLabel}\n </Button>\n </Form.Row>\n {enabled && !loadedDevId && !busy && (\n <Message.Root valence='warning'>\n <Message.Body>{t('dev-plugin.not-loaded.message')}</Message.Body>\n </Message.Root>\n )}\n </Form.Section>\n </Form.Content>\n </Form.Viewport>\n </Form.Root>\n );\n};\n"],"mappings":"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"}
|