@dxos/plugin-registry 0.9.1-staging.ee54ba693a → 0.11.0
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,80 @@
|
|
|
1
|
+
import { a as meta } from "./chunk-meta.mjs";
|
|
2
|
+
import { DXN } from "@dxos/keys";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import { lazy, useCallback, useRef, useState } from "react";
|
|
5
|
+
import { Button, Dialog, Input, useTranslation } from "@dxos/react-ui";
|
|
6
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { usePluginManager } from "@dxos/app-framework/ui";
|
|
8
|
+
import { EffectEx } from "@dxos/effect";
|
|
9
|
+
//#region src/containers/LoadPluginDialog/LoadPluginDialog.tsx
|
|
10
|
+
var LOAD_PLUGIN_DIALOG = DXN.make(`${meta.profile.key}.loadPluginDialog`);
|
|
11
|
+
var LoadPluginDialog$1 = () => {
|
|
12
|
+
const manager = usePluginManager();
|
|
13
|
+
const { t } = useTranslation(meta.profile.key);
|
|
14
|
+
const [url, setUrl] = useState("");
|
|
15
|
+
const [loading, setLoading] = useState(false);
|
|
16
|
+
const [error, setError] = useState(null);
|
|
17
|
+
const closeRef = useRef(null);
|
|
18
|
+
const handleLoad = useCallback(() => {
|
|
19
|
+
const trimmed = url.trim();
|
|
20
|
+
if (!trimmed) return;
|
|
21
|
+
setLoading(true);
|
|
22
|
+
setError(null);
|
|
23
|
+
Effect.gen(function* () {
|
|
24
|
+
const plugin = yield* manager.add(trimmed);
|
|
25
|
+
yield* manager.enable(plugin.meta.profile.key);
|
|
26
|
+
closeRef.current?.click();
|
|
27
|
+
}).pipe(Effect.catchAll((err) => Effect.sync(() => {
|
|
28
|
+
setError(String(err));
|
|
29
|
+
})), Effect.tap(() => Effect.sync(() => setLoading(false))), EffectEx.runAndForwardErrors);
|
|
30
|
+
}, [url, manager]);
|
|
31
|
+
return /* @__PURE__ */ jsxs(Dialog.Content, { children: [/* @__PURE__ */ jsxs(Dialog.Header, { children: [/* @__PURE__ */ jsx(Dialog.Title, { children: t("load-by-url-dialog.title") }), /* @__PURE__ */ jsx(Dialog.Close, {
|
|
32
|
+
asChild: true,
|
|
33
|
+
children: /* @__PURE__ */ jsx(Dialog.ActionIconButton, {
|
|
34
|
+
action: "close",
|
|
35
|
+
ref: closeRef
|
|
36
|
+
})
|
|
37
|
+
})] }), /* @__PURE__ */ jsx(Dialog.Body, { children: /* @__PURE__ */ jsxs("div", {
|
|
38
|
+
className: "flex flex-col gap-4",
|
|
39
|
+
children: [/* @__PURE__ */ jsxs(Input.Root, {
|
|
40
|
+
validationValence: error ? "error" : void 0,
|
|
41
|
+
children: [
|
|
42
|
+
/* @__PURE__ */ jsx(Input.Label, { children: t("plugin-url.label") }),
|
|
43
|
+
/* @__PURE__ */ jsx(Input.TextInput, {
|
|
44
|
+
placeholder: "https://example.com/manifest.json",
|
|
45
|
+
value: url,
|
|
46
|
+
onChange: (event) => {
|
|
47
|
+
setUrl(event.target.value);
|
|
48
|
+
setError(null);
|
|
49
|
+
},
|
|
50
|
+
onKeyDown: (event) => {
|
|
51
|
+
if (event.key === "Enter") handleLoad();
|
|
52
|
+
},
|
|
53
|
+
disabled: loading,
|
|
54
|
+
autoFocus: true
|
|
55
|
+
}),
|
|
56
|
+
error && /* @__PURE__ */ jsx(Input.DescriptionAndValidation, { children: error })
|
|
57
|
+
]
|
|
58
|
+
}), /* @__PURE__ */ jsx("div", {
|
|
59
|
+
className: "flex justify-end",
|
|
60
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
61
|
+
variant: "primary",
|
|
62
|
+
disabled: !url.trim() || loading,
|
|
63
|
+
onClick: handleLoad,
|
|
64
|
+
children: loading ? t("loading.label") : t("load-plugin.label")
|
|
65
|
+
})
|
|
66
|
+
})]
|
|
67
|
+
}) })] });
|
|
68
|
+
};
|
|
69
|
+
LoadPluginDialog$1.displayName = "LoadPluginDialog";
|
|
70
|
+
//#endregion
|
|
71
|
+
//#region src/containers/index.ts
|
|
72
|
+
var LoadPluginDialog = lazy(() => import("./chunk-LoadPluginDialog.mjs"));
|
|
73
|
+
var PluginArticle = lazy(() => import("./chunk-PluginArticle.mjs"));
|
|
74
|
+
var PublicRegistryArticle = lazy(() => import("./chunk-PublicRegistryArticle.mjs"));
|
|
75
|
+
var RegistryArticle = lazy(() => import("./chunk-RegistryArticle.mjs"));
|
|
76
|
+
var RegistrySettingsContainer = lazy(() => import("./chunk-RegistrySettingsContainer.mjs"));
|
|
77
|
+
//#endregion
|
|
78
|
+
export { RegistrySettingsContainer as a, RegistryArticle as i, PluginArticle as n, LOAD_PLUGIN_DIALOG as o, PublicRegistryArticle as r, LoadPluginDialog$1 as s, LoadPluginDialog as t };
|
|
79
|
+
|
|
80
|
+
//# sourceMappingURL=chunk-containers.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-containers.mjs","names":[],"sources":["../../src/containers/LoadPluginDialog/LoadPluginDialog.tsx","../../src/containers/index.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport React, { useCallback, useRef, useState } from 'react';\n\nimport { usePluginManager } from '@dxos/app-framework/ui';\nimport { EffectEx } from '@dxos/effect';\nimport { DXN } from '@dxos/keys';\nimport { Button, Dialog, Input, useTranslation } from '@dxos/react-ui';\n\nimport { meta } from '../../meta';\n\nexport const LOAD_PLUGIN_DIALOG = DXN.make(`${meta.profile.key}.loadPluginDialog`);\n\nexport const LoadPluginDialog = () => {\n const manager = usePluginManager();\n const { t } = useTranslation(meta.profile.key);\n const [url, setUrl] = useState('');\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const closeRef = useRef<HTMLButtonElement | null>(null);\n\n const handleLoad = useCallback(() => {\n const trimmed = url.trim();\n if (!trimmed) {\n return;\n }\n\n setLoading(true);\n setError(null);\n\n void Effect.gen(function* () {\n const plugin = yield* manager.add(trimmed);\n yield* manager.enable(plugin.meta.profile.key);\n closeRef.current?.click();\n }).pipe(\n Effect.catchAll((err) =>\n Effect.sync(() => {\n setError(String(err));\n }),\n ),\n Effect.tap(() => Effect.sync(() => setLoading(false))),\n EffectEx.runAndForwardErrors,\n );\n }, [url, manager]);\n\n return (\n <Dialog.Content>\n <Dialog.Header>\n <Dialog.Title>{t('load-by-url-dialog.title')}</Dialog.Title>\n <Dialog.Close asChild>\n <Dialog.ActionIconButton action='close' ref={closeRef} />\n </Dialog.Close>\n </Dialog.Header>\n <Dialog.Body>\n {/* TODO(burdon): Form section. */}\n <div className='flex flex-col gap-4'>\n <Input.Root validationValence={error ? 'error' : undefined}>\n <Input.Label>{t('plugin-url.label')}</Input.Label>\n <Input.TextInput\n placeholder='https://example.com/manifest.json'\n value={url}\n onChange={(event) => {\n setUrl(event.target.value);\n setError(null);\n }}\n onKeyDown={(event) => {\n if (event.key === 'Enter') {\n handleLoad();\n }\n }}\n disabled={loading}\n autoFocus\n />\n {error && <Input.DescriptionAndValidation>{error}</Input.DescriptionAndValidation>}\n </Input.Root>\n <div className='flex justify-end'>\n <Button variant='primary' disabled={!url.trim() || loading} onClick={handleLoad}>\n {loading ? t('loading.label') : t('load-plugin.label')}\n </Button>\n </div>\n </div>\n </Dialog.Body>\n </Dialog.Content>\n );\n};\n\nLoadPluginDialog.displayName = 'LoadPluginDialog';\n","//\n// Copyright 2025 DXOS.org\n//\n\nimport { type ComponentType, lazy } from 'react';\n\nexport { LOAD_PLUGIN_DIALOG } from './LoadPluginDialog/LoadPluginDialog';\nexport const LoadPluginDialog: ComponentType<any> = lazy(() => import('./LoadPluginDialog'));\nexport const PluginArticle: ComponentType<any> = lazy(() => import('./PluginArticle'));\nexport const PublicRegistryArticle: ComponentType<any> = lazy(() => import('./PublicRegistryArticle'));\nexport const RegistryArticle: ComponentType<any> = lazy(() => import('./RegistryArticle'));\nexport const RegistrySettingsContainer: ComponentType<any> = lazy(() => import('./RegistrySettingsContainer'));\n"],"mappings":";;;;;;;;;AAcA,IAAa,qBAAqB,IAAI,KAAK,GAAG,KAAK,QAAQ,IAAI,kBAAkB;AAEjF,IAAa,2BAAyB;CACpC,MAAM,UAAU,iBAAiB;CACjC,MAAM,EAAE,MAAM,eAAe,KAAK,QAAQ,GAAG;CAC7C,MAAM,CAAC,KAAK,UAAU,SAAS,EAAE;CACjC,MAAM,CAAC,SAAS,cAAc,SAAS,KAAK;CAC5C,MAAM,CAAC,OAAO,YAAY,SAAwB,IAAI;CACtD,MAAM,WAAW,OAAiC,IAAI;CAEtD,MAAM,aAAa,kBAAkB;EACnC,MAAM,UAAU,IAAI,KAAK;EACzB,IAAI,CAAC,SACH;EAGF,WAAW,IAAI;EACf,SAAS,IAAI;EAEb,OAAY,IAAI,aAAa;GAC3B,MAAM,SAAS,OAAO,QAAQ,IAAI,OAAO;GACzC,OAAO,QAAQ,OAAO,OAAO,KAAK,QAAQ,GAAG;GAC7C,SAAS,SAAS,MAAM;EAC1B,CAAC,CAAC,CAAC,KACD,OAAO,UAAU,QACf,OAAO,WAAW;GAChB,SAAS,OAAO,GAAG,CAAC;EACtB,CAAC,CACH,GACA,OAAO,UAAU,OAAO,WAAW,WAAW,KAAK,CAAC,CAAC,GACrD,SAAS,mBACX;CACF,GAAG,CAAC,KAAK,OAAO,CAAC;CAEjB,OACE,qBAAC,OAAO,SAAR,EAAA,UAAA,CACE,qBAAC,OAAO,QAAR,EAAA,UAAA,CACE,oBAAC,OAAO,OAAR,EAAA,UAAe,EAAE,0BAA0B,EAAgB,CAAA,GAC3D,oBAAC,OAAO,OAAR;EAAc,SAAA;YACZ,oBAAC,OAAO,kBAAR;GAAyB,QAAO;GAAQ,KAAK;EAAW,CAAA;CAC5C,CAAA,CACD,EAAA,CAAA,GACf,oBAAC,OAAO,MAAR,EAAA,UAEE,qBAAC,OAAD;EAAK,WAAU;YAAf,CACE,qBAAC,MAAM,MAAP;GAAY,mBAAmB,QAAQ,UAAU,KAAA;aAAjD;IACE,oBAAC,MAAM,OAAP,EAAA,UAAc,EAAE,kBAAkB,EAAe,CAAA;IACjD,oBAAC,MAAM,WAAP;KACE,aAAY;KACZ,OAAO;KACP,WAAW,UAAU;MACnB,OAAO,MAAM,OAAO,KAAK;MACzB,SAAS,IAAI;KACf;KACA,YAAY,UAAU;MACpB,IAAI,MAAM,QAAQ,SAChB,WAAW;KAEf;KACA,UAAU;KACV,WAAA;IACD,CAAA;IACA,SAAS,oBAAC,MAAM,0BAAP,EAAA,UAAiC,MAAsC,CAAA;GACvE;MACZ,oBAAC,OAAD;GAAK,WAAU;aACb,oBAAC,QAAD;IAAQ,SAAQ;IAAU,UAAU,CAAC,IAAI,KAAK,KAAK;IAAS,SAAS;cAClE,UAAU,EAAE,eAAe,IAAI,EAAE,mBAAmB;GAC/C,CAAA;EACL,CAAA,CACF;IACM,CAAA,CACC,EAAA,CAAA;AAEpB;AAEA,mBAAiB,cAAc;;;AClF/B,IAAa,mBAAuC,WAAW,OAAO,+BAAqB;AAC3F,IAAa,gBAAoC,WAAW,OAAO,4BAAkB;AACrF,IAAa,wBAA4C,WAAW,OAAO,oCAA0B;AACrG,IAAa,kBAAsC,WAAW,OAAO,8BAAoB;AACzF,IAAa,4BAAgD,WAAW,OAAO,wCAA8B"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { RegistryCapabilities } from "./types.mjs";
|
|
2
|
+
import { Capabilities, Capability } from "@dxos/app-framework";
|
|
3
|
+
import * as Effect from "effect/Effect";
|
|
4
|
+
import { log } from "@dxos/log";
|
|
5
|
+
//#region src/capabilities/dev-plugin-loader.ts
|
|
6
|
+
var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-registry/src/capabilities/dev-plugin-loader.ts";
|
|
7
|
+
/**
|
|
8
|
+
* Startup module that auto-loads a locally-served dev plugin when the user has
|
|
9
|
+
* the toggle enabled in registry settings. Failures (dev server offline, 404,
|
|
10
|
+
* timeout) are logged and swallowed so a stale dev URL never blocks app boot —
|
|
11
|
+
* the toggle stays on, the next reload retries.
|
|
12
|
+
*/
|
|
13
|
+
var dev_plugin_loader_default = Capability.makeModule(Effect.fnUntraced(function* () {
|
|
14
|
+
const manager = yield* Capability.get(Capabilities.PluginManager);
|
|
15
|
+
const registry = yield* Capability.get(Capabilities.AtomRegistry);
|
|
16
|
+
const settingsAtom = yield* Capability.get(RegistryCapabilities.Settings);
|
|
17
|
+
const settings = registry.get(settingsAtom);
|
|
18
|
+
const url = settings.devPluginUrl?.trim();
|
|
19
|
+
if (!settings.devPluginEnabled || !url) return [];
|
|
20
|
+
log.info("loading dev plugin from registry settings", { url }, {
|
|
21
|
+
"~LogMeta": "~LogMeta",
|
|
22
|
+
F: __dxlog_file,
|
|
23
|
+
L: 30,
|
|
24
|
+
S: this
|
|
25
|
+
});
|
|
26
|
+
yield* manager.add(url).pipe(Effect.flatMap((plugin) => manager.enable(plugin.meta.profile.key)), Effect.tapError((error) => Effect.sync(() => log.warn("dev plugin auto-load failed", {
|
|
27
|
+
url,
|
|
28
|
+
error
|
|
29
|
+
}, {
|
|
30
|
+
"~LogMeta": "~LogMeta",
|
|
31
|
+
F: __dxlog_file,
|
|
32
|
+
L: 33,
|
|
33
|
+
S: this
|
|
34
|
+
}))), Effect.ignore);
|
|
35
|
+
return [];
|
|
36
|
+
}));
|
|
37
|
+
//#endregion
|
|
38
|
+
export { dev_plugin_loader_default as default };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=chunk-dev-plugin-loader.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-dev-plugin-loader.mjs","names":[],"sources":["../../src/capabilities/dev-plugin-loader.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Capabilities, Capability } from '@dxos/app-framework';\nimport { log } from '@dxos/log';\n\nimport { RegistryCapabilities } from '../types';\n\n/**\n * Startup module that auto-loads a locally-served dev plugin when the user has\n * the toggle enabled in registry settings. Failures (dev server offline, 404,\n * timeout) are logged and swallowed so a stale dev URL never blocks app boot —\n * the toggle stays on, the next reload retries.\n */\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n const manager = yield* Capability.get(Capabilities.PluginManager);\n const registry = yield* Capability.get(Capabilities.AtomRegistry);\n const settingsAtom = yield* Capability.get(RegistryCapabilities.Settings);\n\n const settings = registry.get(settingsAtom);\n const url = settings.devPluginUrl?.trim();\n if (!settings.devPluginEnabled || !url) {\n return [];\n }\n\n log.info('loading dev plugin from registry settings', { url });\n yield* manager.add(url).pipe(\n Effect.flatMap((plugin) => manager.enable(plugin.meta.profile.key)),\n Effect.tapError((error) => Effect.sync(() => log.warn('dev plugin auto-load failed', { url, error }))),\n Effect.ignore,\n );\n\n return [];\n }),\n);\n"],"mappings":";;;;;;;;;;;;AAiBA,IAAA,4BAAe,WAAW,WACxB,OAAO,WAAW,aAAa;CAC7B,MAAM,UAAU,OAAO,WAAW,IAAI,aAAa,aAAa;CAChE,MAAM,WAAW,OAAO,WAAW,IAAI,aAAa,YAAY;CAChE,MAAM,eAAe,OAAO,WAAW,IAAI,qBAAqB,QAAQ;CAExE,MAAM,WAAW,SAAS,IAAI,YAAY;CAC1C,MAAM,MAAM,SAAS,cAAc,KAAK;CACxC,IAAI,CAAC,SAAS,oBAAoB,CAAC,KACjC,OAAO,CAAC;CAGV,IAAI,KAAK,6CAA6C,EAAE,IAAI,GAAA;EAAA,YAAA;EAAA,GAAA;EAAA,GAAA;EAAA,GAAA;CAAA,CAAC;CAC7D,OAAO,QAAQ,IAAI,GAAG,CAAC,CAAC,KACtB,OAAO,SAAS,WAAW,QAAQ,OAAO,OAAO,KAAK,QAAQ,GAAG,CAAC,GAClE,OAAO,UAAU,UAAU,OAAO,WAAW,IAAI,KAAK,+BAA+B;EAAE;EAAK;CAAM,GAAA;EAAA,YAAA;EAAA,GAAA;EAAA,GAAA;EAAA,GAAA;CAAA,CAAC,CAAC,CAAC,GACrG,OAAO,MACT;CAEA,OAAO,CAAC;AACV,CAAC,CACH"}
|
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
import { n as getRemotePluginIds } from "./chunk-categories.mjs";
|
|
2
|
+
import { UrlLoader } from "@dxos/app-framework";
|
|
3
|
+
import { LayoutOperation } from "@dxos/app-toolkit";
|
|
4
|
+
import * as Effect from "effect/Effect";
|
|
5
|
+
import { DISABLE_DEPENDENTS_DIALOG, getPluginPath, getPluginSpecPath } from "#meta";
|
|
6
|
+
import { useCallback, useEffect, useMemo, useState } from "react";
|
|
7
|
+
import { useOperationInvoker, usePluginManager } from "@dxos/app-framework/ui";
|
|
8
|
+
import { EffectEx } from "@dxos/effect";
|
|
9
|
+
import { useAppGraph } from "@dxos/app-toolkit/ui";
|
|
10
|
+
import { useAtomValue } from "@effect-atom/atom-react";
|
|
11
|
+
import { useNode } from "@dxos/plugin-graph/hooks";
|
|
12
|
+
//#region src/hooks/useAutoTags.ts
|
|
13
|
+
/**
|
|
14
|
+
* Computes the per-plugin-id extra tags the UI should display.
|
|
15
|
+
*
|
|
16
|
+
* Reads the persisted remote-plugin entries synchronously to derive `local`, and
|
|
17
|
+
* adds `registry` for any plugin id found in the supplied registry manifest entries.
|
|
18
|
+
*/
|
|
19
|
+
var useAutoTags = (registryEntries) => useMemo(() => {
|
|
20
|
+
const byId = {};
|
|
21
|
+
const addTag = (id, tag) => {
|
|
22
|
+
const existing = byId[id];
|
|
23
|
+
if (!existing) byId[id] = [tag];
|
|
24
|
+
else if (!existing.includes(tag)) existing.push(tag);
|
|
25
|
+
};
|
|
26
|
+
for (const entry of UrlLoader.getRemoteEntries()) if (UrlLoader.isLocalUrl(entry.url)) addTag(entry.id, "local");
|
|
27
|
+
for (const entry of registryEntries) addTag(entry.profile.key, "registry");
|
|
28
|
+
return byId;
|
|
29
|
+
}, [registryEntries]);
|
|
30
|
+
/**
|
|
31
|
+
* Returns the set of plugin ids known to originate from a remote URL (not bundled).
|
|
32
|
+
* Used to filter the Official/Recommended sections so third-party plugins are excluded.
|
|
33
|
+
*/
|
|
34
|
+
var useRemotePluginIds = () => useMemo(() => getRemotePluginIds(), []);
|
|
35
|
+
//#endregion
|
|
36
|
+
//#region src/hooks/useRegistryPlugins.ts
|
|
37
|
+
/**
|
|
38
|
+
* Reads the registry plugin catalog from `manager.pluginRegistry.plugins`.
|
|
39
|
+
* The catalog is loaded once when the plugin manager is constructed (with a
|
|
40
|
+
* provider) so this hook is purely reactive — subscribers re-render as the
|
|
41
|
+
* `{entries, loading, error}` state settles.
|
|
42
|
+
*/
|
|
43
|
+
var useRegistryPlugins = () => {
|
|
44
|
+
return useAtomValue(usePluginManager().pluginRegistry.plugins);
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Returns the live `Registry.Manager` from the plugin manager so callers can
|
|
48
|
+
* issue `listVersions` / `getPlugin` calls directly. The catalog state itself
|
|
49
|
+
* is best read through {@link useRegistryPlugins} above.
|
|
50
|
+
*/
|
|
51
|
+
var useRegistryPluginProvider = () => {
|
|
52
|
+
return usePluginManager().pluginRegistry;
|
|
53
|
+
};
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/hooks/useCatalogEntry.ts
|
|
56
|
+
/**
|
|
57
|
+
* Resolves the registry catalog entry for a plugin by id, plus the `moduleUrl`
|
|
58
|
+
* the install/update actions need.
|
|
59
|
+
*/
|
|
60
|
+
var useCatalogEntry = (pluginId) => {
|
|
61
|
+
const { entries } = useRegistryPlugins();
|
|
62
|
+
const catalogEntry = useMemo(() => entries.find((entry) => entry.profile.key === pluginId), [entries, pluginId]);
|
|
63
|
+
return {
|
|
64
|
+
catalogEntry,
|
|
65
|
+
moduleUrl: catalogEntry?.release?.moduleUrl
|
|
66
|
+
};
|
|
67
|
+
};
|
|
68
|
+
//#endregion
|
|
69
|
+
//#region src/hooks/useDisableConfirmation.ts
|
|
70
|
+
/**
|
|
71
|
+
* Returns `requestDisable(pluginId)` that gates `manager.disable` on a
|
|
72
|
+
* confirmation prompt when the target has currently-enabled dependents.
|
|
73
|
+
*
|
|
74
|
+
* `PluginManager.disable` cascades by default; this hook is the UX safeguard
|
|
75
|
+
* around that. The caller supplies a `dispatch` callback that performs the
|
|
76
|
+
* actual disable (wrapping it with observability or any other side effects).
|
|
77
|
+
* When there are no enabled dependents, dispatch runs immediately; otherwise
|
|
78
|
+
* we open the shared {@link DISABLE_DEPENDENTS_DIALOG} surface — its
|
|
79
|
+
* `onConfirm` runs the dispatch and closes the dialog.
|
|
80
|
+
*
|
|
81
|
+
* Display-name resolution for the dialog is delegated to the registered
|
|
82
|
+
* plugin set (`Plugin.Meta.name`). Per-plugin i18n translations only load
|
|
83
|
+
* when a plugin is activated, so `meta.name` is the only label always
|
|
84
|
+
* available regardless of enabled state.
|
|
85
|
+
*/
|
|
86
|
+
var useDisableConfirmation = (manager, dispatch) => {
|
|
87
|
+
const { invokePromise } = useOperationInvoker();
|
|
88
|
+
return useCallback((pluginId) => {
|
|
89
|
+
const enabledDependents = manager.getDependents(pluginId, {
|
|
90
|
+
transitive: true,
|
|
91
|
+
enabledOnly: true
|
|
92
|
+
});
|
|
93
|
+
if (enabledDependents.length === 0) {
|
|
94
|
+
dispatch(pluginId);
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const resolveName = (id) => manager.getPlugins().find((plugin) => plugin.meta.profile.key === id)?.meta.profile.name ?? id;
|
|
98
|
+
invokePromise(LayoutOperation.UpdateDialog, {
|
|
99
|
+
subject: DISABLE_DEPENDENTS_DIALOG,
|
|
100
|
+
state: true,
|
|
101
|
+
type: "alert",
|
|
102
|
+
props: {
|
|
103
|
+
pluginId,
|
|
104
|
+
dependents: enabledDependents,
|
|
105
|
+
onResolvePluginName: resolveName,
|
|
106
|
+
onConfirm: () => {
|
|
107
|
+
dispatch(pluginId);
|
|
108
|
+
invokePromise(LayoutOperation.UpdateDialog, { state: false });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
}, [
|
|
113
|
+
manager,
|
|
114
|
+
dispatch,
|
|
115
|
+
invokePromise
|
|
116
|
+
]);
|
|
117
|
+
};
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/hooks/useInstalledVersionTag.ts
|
|
120
|
+
/**
|
|
121
|
+
* Tracks the per-plugin installed version (sourced from `UrlLoader` localStorage)
|
|
122
|
+
* as React state so post-install/update re-renders are deterministic — the value
|
|
123
|
+
* lives in component state rather than being read inline on every render.
|
|
124
|
+
*
|
|
125
|
+
* - Initialised from localStorage at mount.
|
|
126
|
+
* - Auto-resyncs when the plugin manager's plugin list changes (catches external
|
|
127
|
+
* installs / removes triggered by the list view).
|
|
128
|
+
* - `syncInstalledVersion()` is the explicit handle install/update operations
|
|
129
|
+
* call inside their `Effect.ensuring` so the post-mutation render sees both
|
|
130
|
+
* the new version and the cleared `installing`/`updating` flag together.
|
|
131
|
+
*/
|
|
132
|
+
var useInstalledVersionTag = (pluginId, plugins) => {
|
|
133
|
+
const [installedVersionTag, setInstalledVersionTag] = useState(() => UrlLoader.getInstalledVersion(pluginId));
|
|
134
|
+
const syncInstalledVersion = useCallback(() => {
|
|
135
|
+
setInstalledVersionTag(UrlLoader.getInstalledVersion(pluginId));
|
|
136
|
+
}, [pluginId]);
|
|
137
|
+
useEffect(() => {
|
|
138
|
+
syncInstalledVersion();
|
|
139
|
+
}, [plugins, syncInstalledVersion]);
|
|
140
|
+
return {
|
|
141
|
+
installedVersionTag,
|
|
142
|
+
syncInstalledVersion
|
|
143
|
+
};
|
|
144
|
+
};
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/hooks/usePluginActions.ts
|
|
147
|
+
/**
|
|
148
|
+
* The user-facing actions the article exposes — enable/disable, uninstall,
|
|
149
|
+
* install (latest), update (to latest catalog version), and install-from-picker —
|
|
150
|
+
* plus the `installing`/`updating` flags they drive. Each handler ends with a
|
|
151
|
+
* `syncInstalledVersion()` call inside its `Effect.ensuring` so the cached
|
|
152
|
+
* installed version snaps to the freshly-written localStorage value in the same
|
|
153
|
+
* render that clears the in-flight flag.
|
|
154
|
+
*
|
|
155
|
+
* `handleOpenSpec` is included only when a `spec` child graph node exists for
|
|
156
|
+
* this plugin — contributed by whichever plugin can render MDL (today
|
|
157
|
+
* `plugin-code`). When that contributor isn't enabled, the action is absent
|
|
158
|
+
* and the detail view hides its "Open Specification" chip.
|
|
159
|
+
*/
|
|
160
|
+
var usePluginActions = ({ manager, pluginId, isInstalled, moduleUrl, catalogEntry, pickerVersions, selectedVersionTag, syncInstalledVersion }) => {
|
|
161
|
+
const { invokePromise } = useOperationInvoker();
|
|
162
|
+
const { graph } = useAppGraph();
|
|
163
|
+
const [installing, setInstalling] = useState(false);
|
|
164
|
+
const [updating, setUpdating] = useState(false);
|
|
165
|
+
const requestDisable = useDisableConfirmation(manager, (id) => void EffectEx.runAndForwardErrors(manager.disable(id)));
|
|
166
|
+
const specPath = getPluginSpecPath(pluginId);
|
|
167
|
+
const hasSpecNode = !!useNode(graph, specPath);
|
|
168
|
+
const handleOpenSpec = useCallback(() => {
|
|
169
|
+
invokePromise(LayoutOperation.Open, {
|
|
170
|
+
subject: [specPath],
|
|
171
|
+
pivotId: getPluginPath(pluginId),
|
|
172
|
+
disposition: "add"
|
|
173
|
+
});
|
|
174
|
+
}, [
|
|
175
|
+
invokePromise,
|
|
176
|
+
specPath,
|
|
177
|
+
pluginId
|
|
178
|
+
]);
|
|
179
|
+
return {
|
|
180
|
+
installing,
|
|
181
|
+
updating,
|
|
182
|
+
handleEnableChange: useCallback((enabled) => {
|
|
183
|
+
if (enabled) {
|
|
184
|
+
EffectEx.runAndForwardErrors(manager.enable(pluginId));
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
requestDisable(pluginId);
|
|
188
|
+
}, [
|
|
189
|
+
manager,
|
|
190
|
+
pluginId,
|
|
191
|
+
requestDisable
|
|
192
|
+
]),
|
|
193
|
+
handleUninstall: useCallback(() => {
|
|
194
|
+
EffectEx.runAndForwardErrors(manager.remove(pluginId));
|
|
195
|
+
}, [manager, pluginId]),
|
|
196
|
+
handleInstall: useCallback(() => {
|
|
197
|
+
if (!moduleUrl) return;
|
|
198
|
+
setInstalling(true);
|
|
199
|
+
Effect.gen(function* () {
|
|
200
|
+
const added = yield* manager.add(moduleUrl);
|
|
201
|
+
yield* manager.enable(added.meta.profile.key);
|
|
202
|
+
if (catalogEntry?.release?.version) UrlLoader.setInstalledVersion(added.meta.profile.key, catalogEntry.release.version);
|
|
203
|
+
}).pipe(Effect.ensuring(Effect.sync(() => {
|
|
204
|
+
setInstalling(false);
|
|
205
|
+
syncInstalledVersion();
|
|
206
|
+
})), EffectEx.runAndForwardErrors);
|
|
207
|
+
}, [
|
|
208
|
+
manager,
|
|
209
|
+
moduleUrl,
|
|
210
|
+
catalogEntry,
|
|
211
|
+
syncInstalledVersion
|
|
212
|
+
]),
|
|
213
|
+
handleUpdate: useCallback(() => {
|
|
214
|
+
if (!moduleUrl || !catalogEntry) return;
|
|
215
|
+
setUpdating(true);
|
|
216
|
+
Effect.gen(function* () {
|
|
217
|
+
yield* manager.remove(pluginId);
|
|
218
|
+
const added = yield* manager.add(moduleUrl);
|
|
219
|
+
yield* manager.enable(added.meta.profile.key);
|
|
220
|
+
if (catalogEntry.release?.version) UrlLoader.setInstalledVersion(added.meta.profile.key, catalogEntry.release.version);
|
|
221
|
+
}).pipe(Effect.ensuring(Effect.sync(() => {
|
|
222
|
+
setUpdating(false);
|
|
223
|
+
syncInstalledVersion();
|
|
224
|
+
})), EffectEx.runAndForwardErrors);
|
|
225
|
+
}, [
|
|
226
|
+
manager,
|
|
227
|
+
pluginId,
|
|
228
|
+
moduleUrl,
|
|
229
|
+
catalogEntry,
|
|
230
|
+
syncInstalledVersion
|
|
231
|
+
]),
|
|
232
|
+
handleInstallVersion: useCallback(() => {
|
|
233
|
+
const version = pickerVersions.find((candidate) => candidate.version === selectedVersionTag);
|
|
234
|
+
if (!version || !version.moduleUrl) return;
|
|
235
|
+
setInstalling(true);
|
|
236
|
+
Effect.gen(function* () {
|
|
237
|
+
if (isInstalled) yield* manager.remove(pluginId);
|
|
238
|
+
const added = yield* manager.add(version.moduleUrl);
|
|
239
|
+
yield* manager.enable(added.meta.profile.key);
|
|
240
|
+
UrlLoader.setInstalledVersion(added.meta.profile.key, version.version);
|
|
241
|
+
}).pipe(Effect.ensuring(Effect.sync(() => {
|
|
242
|
+
setInstalling(false);
|
|
243
|
+
syncInstalledVersion();
|
|
244
|
+
})), EffectEx.runAndForwardErrors);
|
|
245
|
+
}, [
|
|
246
|
+
manager,
|
|
247
|
+
pluginId,
|
|
248
|
+
isInstalled,
|
|
249
|
+
selectedVersionTag,
|
|
250
|
+
pickerVersions,
|
|
251
|
+
syncInstalledVersion
|
|
252
|
+
]),
|
|
253
|
+
handleOpenSpec: hasSpecNode ? handleOpenSpec : void 0
|
|
254
|
+
};
|
|
255
|
+
};
|
|
256
|
+
//#endregion
|
|
257
|
+
//#region src/hooks/useUpdateAvailable.ts
|
|
258
|
+
/**
|
|
259
|
+
* Returns a set of plugin ids for which the registry has a newer version
|
|
260
|
+
* than what was recorded at install time.
|
|
261
|
+
*
|
|
262
|
+
* Only flags plugins where a version was recorded at install time AND it differs
|
|
263
|
+
* from the catalog's current version. Legacy installs (no version stored) are ignored.
|
|
264
|
+
*
|
|
265
|
+
* NOTE: this is intentionally NOT memoised. The installed-version side of the
|
|
266
|
+
* comparison lives in localStorage and is mutated by `manager.add` /
|
|
267
|
+
* `manager.remove` / `UrlLoader.setInstalledVersion` outside React's view, so a
|
|
268
|
+
* memo keyed on `entries` would happily return stale data for the rest of the
|
|
269
|
+
* session after an update completes (the bug that produced "Update button
|
|
270
|
+
* reappears after Updating…"). The loop is O(catalog size) sync reads — cheap
|
|
271
|
+
* enough to do every render.
|
|
272
|
+
*/
|
|
273
|
+
var useUpdateAvailableIds = (entries) => {
|
|
274
|
+
const ids = /* @__PURE__ */ new Set();
|
|
275
|
+
for (const entry of entries) {
|
|
276
|
+
const installedVersion = UrlLoader.getInstalledVersion(entry.profile.key);
|
|
277
|
+
if (installedVersion !== void 0 && installedVersion !== entry.release?.version) ids.add(entry.profile.key);
|
|
278
|
+
}
|
|
279
|
+
return ids;
|
|
280
|
+
};
|
|
281
|
+
//#endregion
|
|
282
|
+
//#region src/hooks/useVersionPicker.ts
|
|
283
|
+
/**
|
|
284
|
+
* Owns the version picker's state machine: fetches the available versions list
|
|
285
|
+
* from the provider (served from the inlined `releases` array on each entry —
|
|
286
|
+
* no separate endpoint needed) and keeps the selection clamped to whatever's
|
|
287
|
+
* currently in the list so an external update doesn't strand the trigger on a
|
|
288
|
+
* tag that's no longer available.
|
|
289
|
+
*/
|
|
290
|
+
var useVersionPicker = ({ provider, pluginId, moduleUrl, installedVersionTag }) => {
|
|
291
|
+
const [versions, setVersions] = useState([]);
|
|
292
|
+
const [selectedVersionTag, setSelectedVersionTag] = useState();
|
|
293
|
+
useEffect(() => {
|
|
294
|
+
setVersions([]);
|
|
295
|
+
setSelectedVersionTag(void 0);
|
|
296
|
+
if (!pluginId) return;
|
|
297
|
+
let cancelled = false;
|
|
298
|
+
provider.listVersions(pluginId).pipe(Effect.match({
|
|
299
|
+
onSuccess: (vs) => {
|
|
300
|
+
if (cancelled) return;
|
|
301
|
+
setVersions(vs);
|
|
302
|
+
const installedVersion = UrlLoader.getInstalledVersion(pluginId);
|
|
303
|
+
setSelectedVersionTag(installedVersion ?? vs[0]?.version);
|
|
304
|
+
},
|
|
305
|
+
onFailure: () => {
|
|
306
|
+
if (cancelled) return;
|
|
307
|
+
setVersions([]);
|
|
308
|
+
setSelectedVersionTag(void 0);
|
|
309
|
+
}
|
|
310
|
+
}), EffectEx.runAndForwardErrors);
|
|
311
|
+
return () => {
|
|
312
|
+
cancelled = true;
|
|
313
|
+
};
|
|
314
|
+
}, [provider, pluginId]);
|
|
315
|
+
const pickerVersions = useMemo(() => {
|
|
316
|
+
if (!installedVersionTag) return versions;
|
|
317
|
+
if (versions.some((entry) => entry.version === installedVersionTag)) return versions;
|
|
318
|
+
return [{
|
|
319
|
+
version: installedVersionTag,
|
|
320
|
+
moduleUrl: moduleUrl ?? ""
|
|
321
|
+
}, ...versions];
|
|
322
|
+
}, [
|
|
323
|
+
versions,
|
|
324
|
+
installedVersionTag,
|
|
325
|
+
moduleUrl
|
|
326
|
+
]);
|
|
327
|
+
useEffect(() => {
|
|
328
|
+
if (pickerVersions.length === 0) return;
|
|
329
|
+
if (selectedVersionTag && pickerVersions.some((entry) => entry.version === selectedVersionTag)) return;
|
|
330
|
+
setSelectedVersionTag(installedVersionTag ?? pickerVersions[0]?.version);
|
|
331
|
+
}, [
|
|
332
|
+
pickerVersions,
|
|
333
|
+
selectedVersionTag,
|
|
334
|
+
installedVersionTag
|
|
335
|
+
]);
|
|
336
|
+
return {
|
|
337
|
+
pickerVersions,
|
|
338
|
+
selectedVersionTag,
|
|
339
|
+
setSelectedVersionTag
|
|
340
|
+
};
|
|
341
|
+
};
|
|
342
|
+
//#endregion
|
|
343
|
+
export { useDisableConfirmation as a, useRegistryPlugins as c, useInstalledVersionTag as i, useAutoTags as l, useUpdateAvailableIds as n, useCatalogEntry as o, usePluginActions as r, useRegistryPluginProvider as s, useVersionPicker as t, useRemotePluginIds as u };
|
|
344
|
+
|
|
345
|
+
//# sourceMappingURL=chunk-hooks.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-hooks.mjs","names":[],"sources":["../../src/hooks/useAutoTags.ts","../../src/hooks/useRegistryPlugins.ts","../../src/hooks/useCatalogEntry.ts","../../src/hooks/useDisableConfirmation.ts","../../src/hooks/useInstalledVersionTag.ts","../../src/hooks/usePluginActions.ts","../../src/hooks/useUpdateAvailable.ts","../../src/hooks/useVersionPicker.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { useMemo } from 'react';\n\nimport { type Plugin, UrlLoader } from '@dxos/app-framework';\n\nimport { getRemotePluginIds } from '../categories';\n\n/**\n * Auto-tags that are derived at display time rather than persisted to `Plugin.Meta`.\n * - `registry`: plugin id appears in the registry catalog manifest.\n * - `local`: plugin was loaded from a `localhost` / `127.0.0.1` / `::1` URL.\n */\nexport type AutoTagsMap = Record<string, readonly string[]>;\n\n/**\n * Computes the per-plugin-id extra tags the UI should display.\n *\n * Reads the persisted remote-plugin entries synchronously to derive `local`, and\n * adds `registry` for any plugin id found in the supplied registry manifest entries.\n */\nexport const useAutoTags = (registryEntries: readonly Plugin.Meta[]): AutoTagsMap =>\n useMemo(() => {\n const byId: Record<string, string[]> = {};\n const addTag = (id: string, tag: string) => {\n const existing = byId[id];\n if (!existing) {\n byId[id] = [tag];\n } else if (!existing.includes(tag)) {\n existing.push(tag);\n }\n };\n\n for (const entry of UrlLoader.getRemoteEntries()) {\n if (UrlLoader.isLocalUrl(entry.url)) {\n addTag(entry.id, 'local');\n }\n }\n for (const entry of registryEntries) {\n addTag(entry.profile.key, 'registry');\n }\n\n return byId;\n }, [registryEntries]);\n\n/**\n * Returns the set of plugin ids known to originate from a remote URL (not bundled).\n * Used to filter the Official/Recommended sections so third-party plugins are excluded.\n */\nexport const useRemotePluginIds = (): ReadonlySet<string> => useMemo(() => getRemotePluginIds(), []);\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { useAtomValue } from '@effect-atom/atom-react';\n\nimport { type Registry } from '@dxos/app-framework';\nimport { usePluginManager } from '@dxos/app-framework/ui';\n\n/**\n * Reads the registry plugin catalog from `manager.pluginRegistry.plugins`.\n * The catalog is loaded once when the plugin manager is constructed (with a\n * provider) so this hook is purely reactive — subscribers re-render as the\n * `{entries, loading, error}` state settles.\n */\nexport const useRegistryPlugins = (): Registry.PluginsState => {\n const manager = usePluginManager();\n return useAtomValue(manager.pluginRegistry.plugins);\n};\n\n/**\n * Returns the live `Registry.Manager` from the plugin manager so callers can\n * issue `listVersions` / `getPlugin` calls directly. The catalog state itself\n * is best read through {@link useRegistryPlugins} above.\n */\nexport const useRegistryPluginProvider = (): Registry.Manager => {\n const manager = usePluginManager();\n return manager.pluginRegistry;\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { useMemo } from 'react';\n\nimport { type Plugin } from '@dxos/app-framework';\n\nimport { useRegistryPlugins } from './useRegistryPlugins';\n\n/**\n * Resolves the registry catalog entry for a plugin by id, plus the `moduleUrl`\n * the install/update actions need.\n */\nexport const useCatalogEntry = (\n pluginId: string,\n): {\n catalogEntry: Plugin.Meta | undefined;\n moduleUrl: string | undefined;\n} => {\n const { entries } = useRegistryPlugins();\n const catalogEntry = useMemo(() => entries.find((entry) => entry.profile.key === pluginId), [entries, pluginId]);\n return { catalogEntry, moduleUrl: catalogEntry?.release?.moduleUrl };\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { useCallback } from 'react';\n\nimport { type PluginManager } from '@dxos/app-framework';\nimport { useOperationInvoker } from '@dxos/app-framework/ui';\nimport { LayoutOperation } from '@dxos/app-toolkit';\n\nimport { DISABLE_DEPENDENTS_DIALOG } from '#meta';\n\n/**\n * Returns `requestDisable(pluginId)` that gates `manager.disable` on a\n * confirmation prompt when the target has currently-enabled dependents.\n *\n * `PluginManager.disable` cascades by default; this hook is the UX safeguard\n * around that. The caller supplies a `dispatch` callback that performs the\n * actual disable (wrapping it with observability or any other side effects).\n * When there are no enabled dependents, dispatch runs immediately; otherwise\n * we open the shared {@link DISABLE_DEPENDENTS_DIALOG} surface — its\n * `onConfirm` runs the dispatch and closes the dialog.\n *\n * Display-name resolution for the dialog is delegated to the registered\n * plugin set (`Plugin.Meta.name`). Per-plugin i18n translations only load\n * when a plugin is activated, so `meta.name` is the only label always\n * available regardless of enabled state.\n */\nexport const useDisableConfirmation = (manager: PluginManager.PluginManager, dispatch: (id: string) => void) => {\n const { invokePromise } = useOperationInvoker();\n\n return useCallback(\n (pluginId: string): void => {\n const enabledDependents = manager.getDependents(pluginId, { transitive: true, enabledOnly: true });\n if (enabledDependents.length === 0) {\n dispatch(pluginId);\n return;\n }\n const resolveName = (id: string): string =>\n manager.getPlugins().find((plugin) => plugin.meta.profile.key === id)?.meta.profile.name ?? id;\n void invokePromise(LayoutOperation.UpdateDialog, {\n subject: DISABLE_DEPENDENTS_DIALOG,\n state: true,\n type: 'alert',\n props: {\n pluginId,\n dependents: enabledDependents,\n onResolvePluginName: resolveName,\n onConfirm: () => {\n dispatch(pluginId);\n void invokePromise(LayoutOperation.UpdateDialog, { state: false });\n },\n },\n });\n },\n [manager, dispatch, invokePromise],\n );\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { useCallback, useEffect, useState } from 'react';\n\nimport { type Plugin, UrlLoader } from '@dxos/app-framework';\n\n/**\n * Tracks the per-plugin installed version (sourced from `UrlLoader` localStorage)\n * as React state so post-install/update re-renders are deterministic — the value\n * lives in component state rather than being read inline on every render.\n *\n * - Initialised from localStorage at mount.\n * - Auto-resyncs when the plugin manager's plugin list changes (catches external\n * installs / removes triggered by the list view).\n * - `syncInstalledVersion()` is the explicit handle install/update operations\n * call inside their `Effect.ensuring` so the post-mutation render sees both\n * the new version and the cleared `installing`/`updating` flag together.\n */\nexport const useInstalledVersionTag = (\n pluginId: string,\n plugins: readonly Plugin.Plugin[],\n): {\n installedVersionTag: string | undefined;\n syncInstalledVersion: () => void;\n} => {\n const [installedVersionTag, setInstalledVersionTag] = useState<string | undefined>(() =>\n UrlLoader.getInstalledVersion(pluginId),\n );\n\n const syncInstalledVersion = useCallback(() => {\n setInstalledVersionTag(UrlLoader.getInstalledVersion(pluginId));\n }, [pluginId]);\n\n useEffect(() => {\n syncInstalledVersion();\n }, [plugins, syncInstalledVersion]);\n\n return { installedVersionTag, syncInstalledVersion };\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport { useCallback, useState } from 'react';\n\nimport { type Plugin, type PluginManager, UrlLoader } from '@dxos/app-framework';\nimport { useOperationInvoker } from '@dxos/app-framework/ui';\nimport { LayoutOperation } from '@dxos/app-toolkit';\nimport { useAppGraph } from '@dxos/app-toolkit/ui';\nimport { EffectEx } from '@dxos/effect';\nimport { useNode } from '@dxos/plugin-graph/hooks';\n\nimport { getPluginPath, getPluginSpecPath } from '#meta';\n\nimport { useDisableConfirmation } from './useDisableConfirmation';\n\nexport type PluginActionsProps = {\n manager: PluginManager.PluginManager;\n pluginId: string;\n isInstalled: boolean;\n moduleUrl: string | undefined;\n catalogEntry: Plugin.Meta | undefined;\n pickerVersions: readonly Plugin.Release[];\n selectedVersionTag: string | undefined;\n syncInstalledVersion: () => void;\n};\n\nexport type PluginActions = {\n installing: boolean;\n updating: boolean;\n handleEnableChange: (enabled: boolean) => void;\n handleUninstall: () => void;\n handleInstall: () => void;\n handleUpdate: () => void;\n handleInstallVersion: () => void;\n handleOpenSpec?: () => void;\n};\n\n/**\n * The user-facing actions the article exposes — enable/disable, uninstall,\n * install (latest), update (to latest catalog version), and install-from-picker —\n * plus the `installing`/`updating` flags they drive. Each handler ends with a\n * `syncInstalledVersion()` call inside its `Effect.ensuring` so the cached\n * installed version snaps to the freshly-written localStorage value in the same\n * render that clears the in-flight flag.\n *\n * `handleOpenSpec` is included only when a `spec` child graph node exists for\n * this plugin — contributed by whichever plugin can render MDL (today\n * `plugin-code`). When that contributor isn't enabled, the action is absent\n * and the detail view hides its \"Open Specification\" chip.\n */\nexport const usePluginActions = ({\n manager,\n pluginId,\n isInstalled,\n moduleUrl,\n catalogEntry,\n pickerVersions,\n selectedVersionTag,\n syncInstalledVersion,\n}: PluginActionsProps): PluginActions => {\n const { invokePromise } = useOperationInvoker();\n const { graph } = useAppGraph();\n\n const [installing, setInstalling] = useState(false);\n const [updating, setUpdating] = useState(false);\n const requestDisable = useDisableConfirmation(\n manager,\n (id) => void EffectEx.runAndForwardErrors(manager.disable(id)),\n );\n\n const specPath = getPluginSpecPath(pluginId);\n const hasSpecNode = !!useNode(graph, specPath);\n const handleOpenSpec = useCallback(() => {\n void invokePromise(LayoutOperation.Open, {\n subject: [specPath],\n pivotId: getPluginPath(pluginId),\n disposition: 'add',\n });\n }, [invokePromise, specPath, pluginId]);\n\n const handleEnableChange = useCallback(\n (enabled: boolean) => {\n if (enabled) {\n void EffectEx.runAndForwardErrors(manager.enable(pluginId));\n return;\n }\n\n requestDisable(pluginId);\n },\n [manager, pluginId, requestDisable],\n );\n\n const handleUninstall = useCallback(() => {\n void EffectEx.runAndForwardErrors(manager.remove(pluginId));\n }, [manager, pluginId]);\n\n const handleInstall = useCallback(() => {\n if (!moduleUrl) {\n return;\n }\n\n setInstalling(true);\n void Effect.gen(function* () {\n const added = yield* manager.add(moduleUrl);\n yield* manager.enable(added.meta.profile.key);\n if (catalogEntry?.release?.version) {\n UrlLoader.setInstalledVersion(added.meta.profile.key, catalogEntry.release.version);\n }\n }).pipe(\n Effect.ensuring(\n Effect.sync(() => {\n setInstalling(false);\n syncInstalledVersion();\n }),\n ),\n EffectEx.runAndForwardErrors,\n );\n }, [manager, moduleUrl, catalogEntry, syncInstalledVersion]);\n\n const handleUpdate = useCallback(() => {\n if (!moduleUrl || !catalogEntry) {\n return;\n }\n\n setUpdating(true);\n void Effect.gen(function* () {\n yield* manager.remove(pluginId);\n const added = yield* manager.add(moduleUrl);\n yield* manager.enable(added.meta.profile.key);\n if (catalogEntry.release?.version) {\n UrlLoader.setInstalledVersion(added.meta.profile.key, catalogEntry.release.version);\n }\n }).pipe(\n Effect.ensuring(\n Effect.sync(() => {\n setUpdating(false);\n syncInstalledVersion();\n }),\n ),\n EffectEx.runAndForwardErrors,\n );\n }, [manager, pluginId, moduleUrl, catalogEntry, syncInstalledVersion]);\n\n const handleInstallVersion = useCallback(() => {\n const version = pickerVersions.find((candidate) => candidate.version === selectedVersionTag);\n if (!version || !version.moduleUrl) {\n return;\n }\n\n setInstalling(true);\n void Effect.gen(function* () {\n // Remove existing version before installing a different one.\n if (isInstalled) {\n yield* manager.remove(pluginId);\n }\n const added = yield* manager.add(version.moduleUrl);\n yield* manager.enable(added.meta.profile.key);\n UrlLoader.setInstalledVersion(added.meta.profile.key, version.version);\n }).pipe(\n Effect.ensuring(\n Effect.sync(() => {\n setInstalling(false);\n syncInstalledVersion();\n }),\n ),\n EffectEx.runAndForwardErrors,\n );\n }, [manager, pluginId, isInstalled, selectedVersionTag, pickerVersions, syncInstalledVersion]);\n\n return {\n installing,\n updating,\n handleEnableChange,\n handleUninstall,\n handleInstall,\n handleUpdate,\n handleInstallVersion,\n handleOpenSpec: hasSpecNode ? handleOpenSpec : undefined,\n };\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Plugin, UrlLoader } from '@dxos/app-framework';\n\n/**\n * Returns a set of plugin ids for which the registry has a newer version\n * than what was recorded at install time.\n *\n * Only flags plugins where a version was recorded at install time AND it differs\n * from the catalog's current version. Legacy installs (no version stored) are ignored.\n *\n * NOTE: this is intentionally NOT memoised. The installed-version side of the\n * comparison lives in localStorage and is mutated by `manager.add` /\n * `manager.remove` / `UrlLoader.setInstalledVersion` outside React's view, so a\n * memo keyed on `entries` would happily return stale data for the rest of the\n * session after an update completes (the bug that produced \"Update button\n * reappears after Updating…\"). The loop is O(catalog size) sync reads — cheap\n * enough to do every render.\n */\nexport const useUpdateAvailableIds = (entries: readonly Plugin.Meta[]): ReadonlySet<string> => {\n const ids = new Set<string>();\n for (const entry of entries) {\n const installedVersion = UrlLoader.getInstalledVersion(entry.profile.key);\n if (installedVersion !== undefined && installedVersion !== entry.release?.version) {\n ids.add(entry.profile.key);\n }\n }\n return ids;\n};\n","//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\nimport { type Dispatch, type SetStateAction, useEffect, useMemo, useState } from 'react';\n\nimport { type Plugin, type Registry, UrlLoader } from '@dxos/app-framework';\nimport { EffectEx } from '@dxos/effect';\n\n/**\n * Owns the version picker's state machine: fetches the available versions list\n * from the provider (served from the inlined `releases` array on each entry —\n * no separate endpoint needed) and keeps the selection clamped to whatever's\n * currently in the list so an external update doesn't strand the trigger on a\n * tag that's no longer available.\n */\nexport const useVersionPicker = ({\n provider,\n pluginId,\n moduleUrl,\n installedVersionTag,\n}: {\n provider: Registry.Manager;\n pluginId: string;\n moduleUrl: string | undefined;\n installedVersionTag: string | undefined;\n}): {\n pickerVersions: readonly Plugin.Release[];\n selectedVersionTag: string | undefined;\n setSelectedVersionTag: Dispatch<SetStateAction<string | undefined>>;\n} => {\n const [versions, setVersions] = useState<readonly Plugin.Release[]>([]);\n const [selectedVersionTag, setSelectedVersionTag] = useState<string | undefined>();\n\n // Load version list whenever pluginId changes. The `cancelled` flag guards\n // against a stale in-flight response overwriting newer state when the user\n // navigates between plugins quickly.\n useEffect(() => {\n setVersions([]);\n setSelectedVersionTag(undefined);\n if (!pluginId) {\n return;\n }\n let cancelled = false;\n void provider.listVersions(pluginId).pipe(\n Effect.match({\n onSuccess: (vs) => {\n if (cancelled) {\n return;\n }\n setVersions(vs);\n // Default selection: the currently installed version, or the latest.\n const installedVersion = UrlLoader.getInstalledVersion(pluginId);\n setSelectedVersionTag(installedVersion ?? vs[0]?.version);\n },\n onFailure: () => {\n if (cancelled) {\n return;\n }\n setVersions([]);\n setSelectedVersionTag(undefined);\n },\n }),\n EffectEx.runAndForwardErrors,\n );\n return () => {\n cancelled = true;\n };\n }, [provider, pluginId]);\n\n // Make sure the picker always lists the installed version, even if the catalog\n // hasn't surfaced it (the current `listVersions` stub only returns latest).\n const pickerVersions = useMemo<readonly Plugin.Release[]>(() => {\n if (!installedVersionTag) {\n return versions;\n }\n if (versions.some((entry) => entry.version === installedVersionTag)) {\n return versions;\n }\n const installedEntry: Plugin.Release = {\n version: installedVersionTag,\n // Picker won't be re-installing this entry unless the user selects + clicks Install,\n // and the catalog moduleUrl is the closest stand-in we have.\n moduleUrl: moduleUrl ?? '',\n };\n return [installedEntry, ...versions];\n }, [versions, installedVersionTag, moduleUrl]);\n\n // Keep `selectedVersionTag` in sync when picker contents change (e.g. the user\n // updated the plugin from the list view — installed version moved, picker\n // shrunk, prior selection is no longer in the list).\n useEffect(() => {\n if (pickerVersions.length === 0) {\n return;\n }\n if (selectedVersionTag && pickerVersions.some((entry) => entry.version === selectedVersionTag)) {\n return;\n }\n setSelectedVersionTag(installedVersionTag ?? pickerVersions[0]?.version);\n }, [pickerVersions, selectedVersionTag, installedVersionTag]);\n\n return { pickerVersions, selectedVersionTag, setSelectedVersionTag };\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Plugin } from "@dxos/app-framework";
|
|
2
|
+
import { GraphPath } from "@dxos/app-toolkit";
|
|
3
|
+
import { DXN } from "@dxos/keys";
|
|
4
|
+
import { Config2 } from "@dxos/app-framework/config";
|
|
5
|
+
import { trim } from "@dxos/util";
|
|
6
|
+
//#region dx.config.ts
|
|
7
|
+
var dx_config_default = Config2.make({ plugin: {
|
|
8
|
+
key: "org.dxos.plugin.registry",
|
|
9
|
+
name: "Plugins",
|
|
10
|
+
author: "DXOS",
|
|
11
|
+
description: trim`
|
|
12
|
+
Plugin management system for discovering, installing, and configuring workspace extensions.
|
|
13
|
+
Browse available plugins and customize your workspace capabilities.
|
|
14
|
+
`,
|
|
15
|
+
icon: { key: "ph--squares-four--regular" },
|
|
16
|
+
tags: ["system"]
|
|
17
|
+
} });
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/meta.ts
|
|
20
|
+
var REGISTRY_ID = GraphPath.pinnedWorkspaceId("dxos:plugin-registry");
|
|
21
|
+
/** Qualified graph path to a specific plugin node. */
|
|
22
|
+
var getPluginPath = (pluginId) => `root/${REGISTRY_ID}/${pluginId}`;
|
|
23
|
+
/**
|
|
24
|
+
* Qualified graph path to the bundled MDL spec child node for a plugin.
|
|
25
|
+
*
|
|
26
|
+
* The child is contributed by whichever plugin can render MDL (today:
|
|
27
|
+
* `plugin-code`). plugin-registry only knows the path convention so it can:
|
|
28
|
+
* - dispatch `LayoutOperation.Open` to open the spec viewer, and
|
|
29
|
+
* - probe the app graph for the child's existence to gate the "View
|
|
30
|
+
* specification" button. If no plugin contributes a renderer for the
|
|
31
|
+
* child, the node is absent and the button stays hidden.
|
|
32
|
+
*/
|
|
33
|
+
var getPluginSpecPath = (pluginId) => `${getPluginPath(pluginId)}/spec`;
|
|
34
|
+
var meta = Plugin.getMetaFromConfig(dx_config_default);
|
|
35
|
+
/** Cascade-disable confirmation dialog surface id. */
|
|
36
|
+
var DISABLE_DEPENDENTS_DIALOG = DXN.make(`${meta.profile.key}.disableDependentsDialog`);
|
|
37
|
+
//#endregion
|
|
38
|
+
export { meta as a, getPluginSpecPath as i, REGISTRY_ID as n, getPluginPath as r, DISABLE_DEPENDENTS_DIALOG as t };
|
|
39
|
+
|
|
40
|
+
//# sourceMappingURL=chunk-meta.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-meta.mjs","names":[],"sources":["../../dx.config.ts","../../src/meta.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { Config2 } from '@dxos/app-framework/config';\nimport { trim } from '@dxos/util';\n\nexport default Config2.make({\n plugin: {\n key: 'org.dxos.plugin.registry',\n name: 'Plugins',\n author: 'DXOS',\n description: trim`\n Plugin management system for discovering, installing, and configuring workspace extensions.\n Browse available plugins and customize your workspace capabilities.\n `,\n icon: { key: 'ph--squares-four--regular' },\n tags: ['system'],\n },\n});\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { Plugin } from '@dxos/app-framework';\nimport { GraphPath } from '@dxos/app-toolkit';\nimport { DXN } from '@dxos/keys';\n\nimport config from '../dx.config';\n\nexport const REGISTRY_ID = GraphPath.pinnedWorkspaceId('dxos:plugin-registry');\n\n// A registry category node id is the bare category name (e.g. `bundled`), which doubles as its URL\n// segment (`category/<name>`); the graph builder, the category surfaces and `getCategoryPredicate` all\n// use the name directly.\n\n/** Qualified graph path to a specific plugin node. */\nexport const getPluginPath = (pluginId: string): string => `root/${REGISTRY_ID}/${pluginId}`;\n\n/**\n * Qualified graph path to the bundled MDL spec child node for a plugin.\n *\n * The child is contributed by whichever plugin can render MDL (today:\n * `plugin-code`). plugin-registry only knows the path convention so it can:\n * - dispatch `LayoutOperation.Open` to open the spec viewer, and\n * - probe the app graph for the child's existence to gate the \"View\n * specification\" button. If no plugin contributes a renderer for the\n * child, the node is absent and the button stays hidden.\n */\nexport const getPluginSpecPath = (pluginId: string): string => `${getPluginPath(pluginId)}/spec`;\n\nexport const meta = Plugin.getMetaFromConfig(config);\n\n/** Cascade-disable confirmation dialog surface id. */\nexport const DISABLE_DEPENDENTS_DIALOG = DXN.make(`${meta.profile.key}.disableDependentsDialog`);\n"],"mappings":";;;;;;AAOA,IAAA,oBAAe,QAAQ,KAAK,EAC1B,QAAQ;CACN,KAAK;CACL,MAAM;CACN,QAAQ;CACR,aAAa,IAAI;;;;CAIjB,MAAM,EAAE,KAAK,4BAA4B;CACzC,MAAM,CAAC,QAAQ;AACjB,EACF,CAAC;;;ACTD,IAAa,cAAc,UAAU,kBAAkB,sBAAsB;;AAO7E,IAAa,iBAAiB,aAA6B,QAAQ,YAAY,GAAG;;;;;;;;;;;AAYlF,IAAa,qBAAqB,aAA6B,GAAG,cAAc,QAAQ,EAAE;AAE1F,IAAa,OAAO,OAAO,kBAAkB,iBAAM;;AAGnD,IAAa,4BAA4B,IAAI,KAAK,GAAG,KAAK,QAAQ,IAAI,yBAAyB"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { GraphPath, LayoutOperation, SettingsOperation } from "@dxos/app-toolkit";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import { REGISTRY_ID } from "#meta";
|
|
4
|
+
import { Operation } from "@dxos/compute";
|
|
5
|
+
//#region src/operations/open-plugin-registry.ts
|
|
6
|
+
var handler = SettingsOperation.OpenPluginRegistry.pipe(Operation.withHandler(Effect.fnUntraced(function* () {
|
|
7
|
+
yield* Operation.invoke(LayoutOperation.SwitchWorkspace, { subject: GraphPath.getSpacePath(REGISTRY_ID) });
|
|
8
|
+
})));
|
|
9
|
+
//#endregion
|
|
10
|
+
export { handler as default };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=chunk-open-plugin-registry.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-open-plugin-registry.mjs","names":[],"sources":["../../src/operations/open-plugin-registry.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { GraphPath, LayoutOperation, SettingsOperation } from '@dxos/app-toolkit';\nimport { Operation } from '@dxos/compute';\n\nimport { REGISTRY_ID } from '#meta';\n\nconst handler: Operation.WithHandler<typeof SettingsOperation.OpenPluginRegistry> =\n SettingsOperation.OpenPluginRegistry.pipe(\n Operation.withHandler(\n Effect.fnUntraced(function* () {\n yield* Operation.invoke(LayoutOperation.SwitchWorkspace, { subject: GraphPath.getSpacePath(REGISTRY_ID) });\n }),\n ),\n );\n\nexport default handler;\n"],"mappings":";;;;;AAWA,IAAM,UACJ,kBAAkB,mBAAmB,KACnC,UAAU,YACR,OAAO,WAAW,aAAa;CAC7B,OAAO,UAAU,OAAO,gBAAgB,iBAAiB,EAAE,SAAS,UAAU,aAAa,WAAW,EAAE,CAAC;AAC3G,CAAC,CACH,CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Capabilities, Capability } from "@dxos/app-framework";
|
|
2
|
+
import * as Effect from "effect/Effect";
|
|
3
|
+
import { RegistryOperationHandlerSet } from "#operations";
|
|
4
|
+
//#region src/capabilities/operation-handler.ts
|
|
5
|
+
var operation_handler_default = Capability.makeModule(Effect.fnUntraced(function* () {
|
|
6
|
+
return Capability.contributes(Capabilities.OperationHandler, RegistryOperationHandlerSet);
|
|
7
|
+
}));
|
|
8
|
+
//#endregion
|
|
9
|
+
export { operation_handler_default as default };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=chunk-operation-handler.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chunk-operation-handler.mjs","names":[],"sources":["../../src/capabilities/operation-handler.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Capabilities, Capability } from '@dxos/app-framework';\nimport type { OperationHandlerSet } from '@dxos/compute';\n\nimport { RegistryOperationHandlerSet } from '#operations';\n\nexport default Capability.makeModule<OperationHandlerSet.OperationHandlerSet>(\n Effect.fnUntraced(function* () {\n return Capability.contributes(Capabilities.OperationHandler, RegistryOperationHandlerSet);\n }),\n);\n"],"mappings":";;;;AAWA,IAAA,4BAAe,WAAW,WACxB,OAAO,WAAW,aAAa;CAC7B,OAAO,WAAW,YAAY,aAAa,kBAAkB,2BAA2B;AAC1F,CAAC,CACH"}
|