@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.
Files changed (127) hide show
  1. package/dist/lib/RegistryPlugin.mjs +15 -0
  2. package/dist/lib/RegistryPlugin.mjs.map +1 -0
  3. package/dist/lib/RegistryPlugin.node.mjs +770 -0
  4. package/dist/lib/RegistryPlugin.node.mjs.map +1 -0
  5. package/dist/lib/RegistryPlugin.workerd.mjs +8 -0
  6. package/dist/lib/RegistryPlugin.workerd.mjs.map +1 -0
  7. package/dist/lib/capabilities.mjs +11 -0
  8. package/dist/lib/capabilities.mjs.map +1 -0
  9. package/dist/lib/chunk-BaseRegistryArticle.mjs +103 -0
  10. package/dist/lib/chunk-BaseRegistryArticle.mjs.map +1 -0
  11. package/dist/lib/chunk-LoadPluginDialog.mjs +2 -0
  12. package/dist/lib/chunk-PluginArticle.mjs +87 -0
  13. package/dist/lib/chunk-PluginArticle.mjs.map +1 -0
  14. package/dist/lib/chunk-PublicRegistryArticle.mjs +152 -0
  15. package/dist/lib/chunk-PublicRegistryArticle.mjs.map +1 -0
  16. package/dist/lib/chunk-RegistryArticle.mjs +29 -0
  17. package/dist/lib/chunk-RegistryArticle.mjs.map +1 -0
  18. package/dist/lib/chunk-RegistrySettingsContainer.mjs +37 -0
  19. package/dist/lib/chunk-RegistrySettingsContainer.mjs.map +1 -0
  20. package/dist/lib/chunk-app-graph-builder.mjs +202 -0
  21. package/dist/lib/chunk-app-graph-builder.mjs.map +1 -0
  22. package/dist/lib/chunk-categories.mjs +23 -0
  23. package/dist/lib/chunk-categories.mjs.map +1 -0
  24. package/dist/lib/chunk-components.mjs +537 -0
  25. package/dist/lib/chunk-components.mjs.map +1 -0
  26. package/dist/lib/chunk-containers.mjs +80 -0
  27. package/dist/lib/chunk-containers.mjs.map +1 -0
  28. package/dist/lib/chunk-dev-plugin-loader.mjs +40 -0
  29. package/dist/lib/chunk-dev-plugin-loader.mjs.map +1 -0
  30. package/dist/lib/chunk-hooks.mjs +345 -0
  31. package/dist/lib/chunk-hooks.mjs.map +1 -0
  32. package/dist/lib/chunk-meta.mjs +40 -0
  33. package/dist/lib/chunk-meta.mjs.map +1 -0
  34. package/dist/lib/chunk-open-plugin-registry.mjs +12 -0
  35. package/dist/lib/chunk-open-plugin-registry.mjs.map +1 -0
  36. package/dist/lib/chunk-operation-handler.mjs +11 -0
  37. package/dist/lib/chunk-operation-handler.mjs.map +1 -0
  38. package/dist/lib/chunk-react-surface.mjs +109 -0
  39. package/dist/lib/chunk-react-surface.mjs.map +1 -0
  40. package/dist/lib/chunk-settings.mjs +24 -0
  41. package/dist/lib/chunk-settings.mjs.map +1 -0
  42. package/dist/lib/chunk-storage.mjs +36 -0
  43. package/dist/lib/chunk-storage.mjs.map +1 -0
  44. package/dist/lib/components.mjs +2 -0
  45. package/dist/lib/containers.mjs +2 -0
  46. package/dist/lib/index.mjs +3 -0
  47. package/dist/lib/meta.mjs +2 -0
  48. package/dist/lib/operations.mjs +7 -0
  49. package/dist/lib/operations.mjs.map +1 -0
  50. package/dist/lib/plugin.mjs +9 -0
  51. package/dist/lib/plugin.mjs.map +1 -0
  52. package/dist/lib/translations.mjs +66 -0
  53. package/dist/lib/translations.mjs.map +1 -0
  54. package/dist/lib/types.mjs +29 -0
  55. package/dist/lib/types.mjs.map +1 -0
  56. package/dist/types/src/capabilities/RegistryCategoryArticle.d.ts +11 -0
  57. package/dist/types/src/capabilities/RegistryCategoryArticle.d.ts.map +1 -0
  58. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
  59. package/dist/types/src/capabilities/dev-plugin-loader.d.ts +1 -1
  60. package/dist/types/src/capabilities/operation-handler.d.ts +1 -1
  61. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  62. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  63. package/dist/types/src/capabilities/settings.d.ts +1 -1
  64. package/dist/types/src/categories.d.ts.map +1 -1
  65. package/dist/types/src/categories.test.d.ts +2 -0
  66. package/dist/types/src/categories.test.d.ts.map +1 -0
  67. package/dist/types/src/commands/registry/util.d.ts.map +1 -1
  68. package/dist/types/src/components/PluginDetail/PluginDetail.d.ts +1 -1
  69. package/dist/types/src/components/PluginDetail/PluginDetail.d.ts.map +1 -1
  70. package/dist/types/src/components/PluginDetail/PluginDetail.stories.d.ts +1 -1
  71. package/dist/types/src/containers/BaseRegistryArticle/BaseRegistryArticle.d.ts +1 -1
  72. package/dist/types/src/containers/BaseRegistryArticle/BaseRegistryArticle.d.ts.map +1 -1
  73. package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts +4 -1
  74. package/dist/types/src/containers/LoadPluginDialog/LoadPluginDialog.d.ts.map +1 -1
  75. package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts +4 -1
  76. package/dist/types/src/containers/PluginArticle/PluginArticle.d.ts.map +1 -1
  77. package/dist/types/src/containers/PublicRegistryArticle/PublicRegistryArticle.d.ts +1 -1
  78. package/dist/types/src/containers/PublicRegistryArticle/PublicRegistryArticle.d.ts.map +1 -1
  79. package/dist/types/src/containers/RegistryArticle/RegistryArticle.d.ts +1 -1
  80. package/dist/types/src/containers/RegistryArticle/RegistryArticle.d.ts.map +1 -1
  81. package/dist/types/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.d.ts +4 -7
  82. package/dist/types/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.d.ts.map +1 -1
  83. package/dist/types/src/hooks/usePluginActions.d.ts.map +1 -1
  84. package/dist/types/src/meta.d.ts +0 -3
  85. package/dist/types/src/meta.d.ts.map +1 -1
  86. package/dist/types/src/operations/open-plugin-registry.d.ts.map +1 -1
  87. package/dist/types/src/types.d.ts +2 -2
  88. package/dist/types/src/types.d.ts.map +1 -1
  89. package/dist/types/tsconfig.tsbuildinfo +1 -1
  90. package/package.json +38 -37
  91. package/src/capabilities/RegistryCategoryArticle.tsx +46 -0
  92. package/src/capabilities/app-graph-builder.ts +80 -74
  93. package/src/capabilities/react-surface.ts +85 -0
  94. package/src/categories.test.ts +45 -0
  95. package/src/categories.ts +10 -7
  96. package/src/commands/registry/util.ts +1 -1
  97. package/src/components/PluginList/PluginItem.tsx +5 -3
  98. package/src/components/PluginList/PluginList.stories.tsx +1 -1
  99. package/src/components/RegistrySettings/RegistrySettings.tsx +2 -2
  100. package/src/containers/BaseRegistryArticle/BaseRegistryArticle.tsx +2 -2
  101. package/src/containers/LoadPluginDialog/LoadPluginDialog.tsx +2 -0
  102. package/src/containers/PluginArticle/PluginArticle.tsx +5 -1
  103. package/src/containers/RegistrySettingsContainer/RegistrySettingsContainer.tsx +2 -0
  104. package/src/hooks/usePluginActions.ts +8 -4
  105. package/src/meta.ts +5 -8
  106. package/src/operations/open-plugin-registry.ts +2 -2
  107. package/src/types.ts +2 -1
  108. package/dist/lib/neutral/RegistryPlugin.mjs +0 -27
  109. package/dist/lib/neutral/RegistryPlugin.mjs.map +0 -7
  110. package/dist/lib/neutral/RegistryPlugin.node.mjs +0 -844
  111. package/dist/lib/neutral/RegistryPlugin.node.mjs.map +0 -7
  112. package/dist/lib/neutral/RegistryPlugin.workerd.mjs +0 -10
  113. package/dist/lib/neutral/RegistryPlugin.workerd.mjs.map +0 -7
  114. package/dist/lib/neutral/chunk-XK2JMH3H.mjs +0 -33
  115. package/dist/lib/neutral/chunk-XK2JMH3H.mjs.map +0 -7
  116. package/dist/lib/neutral/chunk-ZY37XLVF.mjs +0 -46
  117. package/dist/lib/neutral/chunk-ZY37XLVF.mjs.map +0 -7
  118. package/dist/lib/neutral/index.mjs +0 -25
  119. package/dist/lib/neutral/index.mjs.map +0 -7
  120. package/dist/lib/neutral/meta.json +0 -1
  121. package/dist/lib/neutral/open-plugin-registry-ID7LV3SF.mjs +0 -15
  122. package/dist/lib/neutral/open-plugin-registry-ID7LV3SF.mjs.map +0 -7
  123. package/dist/lib/neutral/plugin.mjs +0 -18
  124. package/dist/lib/neutral/plugin.mjs.map +0 -7
  125. package/dist/lib/neutral/translations.mjs +0 -72
  126. package/dist/lib/neutral/translations.mjs.map +0 -7
  127. package/src/capabilities/react-surface.tsx +0 -123
@@ -0,0 +1,29 @@
1
+ import { t as BaseRegistryArticle } from "./chunk-BaseRegistryArticle.mjs";
2
+ import { useMemo } from "react";
3
+ import { composable } from "@dxos/react-ui";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import { usePluginManager } from "@dxos/app-framework/ui";
6
+ import { useAtomValue } from "@effect-atom/atom-react";
7
+ //#region src/containers/RegistryArticle/RegistryArticle.tsx
8
+ var sortByPluginMeta = (a, b) => (a.meta.profile.name ?? a.meta.profile.key).localeCompare(b.meta.profile.name ?? b.meta.profile.key);
9
+ var RegistryArticle = composable(({ id, plugins: pluginsProp, extraTagsById, ...props }, forwardedRef) => {
10
+ const failed = useAtomValue(usePluginManager().failed);
11
+ const plugins = useMemo(() => [...pluginsProp].sort(sortByPluginMeta), [pluginsProp]);
12
+ const failuresById = useMemo(() => failed.reduce((acc, failure) => {
13
+ acc[failure.id] = failure;
14
+ return acc;
15
+ }, {}), [failed]);
16
+ return /* @__PURE__ */ jsx(BaseRegistryArticle, {
17
+ ...props,
18
+ id,
19
+ plugins,
20
+ extraTagsById,
21
+ failuresById,
22
+ ref: forwardedRef
23
+ });
24
+ });
25
+ RegistryArticle.displayName = "RegistryArticle";
26
+ //#endregion
27
+ export { RegistryArticle, RegistryArticle as default };
28
+
29
+ //# sourceMappingURL=chunk-RegistryArticle.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-RegistryArticle.mjs","names":[],"sources":["../../src/containers/RegistryArticle/RegistryArticle.tsx"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\nimport { useAtomValue } from '@effect-atom/atom-react';\nimport React, { useMemo } from 'react';\n\nimport { type Plugin, type PluginManager } from '@dxos/app-framework';\nimport { usePluginManager } from '@dxos/app-framework/ui';\nimport { composable } from '@dxos/react-ui';\n\nimport { BaseRegistryArticle } from '../BaseRegistryArticle';\n\nconst sortByPluginMeta = (a: Plugin.Plugin, b: Plugin.Plugin) =>\n (a.meta.profile.name ?? a.meta.profile.key).localeCompare(b.meta.profile.name ?? b.meta.profile.key);\n\nexport type RegistryArticleProps = {\n id: string;\n plugins: Plugin.Plugin[];\n /**\n * Map from plugin id → display-only tags (e.g. `registry`, `local`) computed by the caller.\n */\n extraTagsById?: Record<string, readonly string[]>;\n};\n\nexport const RegistryArticle = composable<HTMLDivElement, RegistryArticleProps>(\n ({ id, plugins: pluginsProp, extraTagsById, ...props }, forwardedRef) => {\n const manager = usePluginManager();\n const failed = useAtomValue(manager.failed);\n const plugins = useMemo(() => [...pluginsProp].sort(sortByPluginMeta), [pluginsProp]);\n const failuresById = useMemo(\n () =>\n failed.reduce<Record<string, PluginManager.PluginFailure>>((acc, failure) => {\n acc[failure.id] = failure;\n return acc;\n }, {}),\n [failed],\n );\n\n return (\n <BaseRegistryArticle\n {...props}\n id={id}\n plugins={plugins}\n extraTagsById={extraTagsById}\n failuresById={failuresById}\n ref={forwardedRef}\n />\n );\n },\n);\n\nRegistryArticle.displayName = 'RegistryArticle';\n"],"mappings":";;;;;;;AAaA,IAAM,oBAAoB,GAAkB,OACzC,EAAE,KAAK,QAAQ,QAAQ,EAAE,KAAK,QAAQ,IAAA,CAAK,cAAc,EAAE,KAAK,QAAQ,QAAQ,EAAE,KAAK,QAAQ,GAAG;AAWrG,IAAa,kBAAkB,YAC5B,EAAE,IAAI,SAAS,aAAa,eAAe,GAAG,SAAS,iBAAiB;CAEvE,MAAM,SAAS,aADC,iBACY,CAAA,CAAQ,MAAM;CAC1C,MAAM,UAAU,cAAc,CAAC,GAAG,WAAW,CAAC,CAAC,KAAK,gBAAgB,GAAG,CAAC,WAAW,CAAC;CACpF,MAAM,eAAe,cAEjB,OAAO,QAAqD,KAAK,YAAY;EAC3E,IAAI,QAAQ,MAAM;EAClB,OAAO;CACT,GAAG,CAAC,CAAC,GACP,CAAC,MAAM,CACT;CAEA,OACE,oBAAC,qBAAD;EACE,GAAI;EACA;EACK;EACM;EACD;EACd,KAAK;CACN,CAAA;AAEL,CACF;AAEA,gBAAgB,cAAc"}
@@ -0,0 +1,37 @@
1
+ import { t as RegistrySettings } from "./chunk-components.mjs";
2
+ import * as Effect from "effect/Effect";
3
+ import { useCallback } from "react";
4
+ import { jsx } from "react/jsx-runtime";
5
+ import { usePluginManager, useSettingsState } from "@dxos/app-framework/ui";
6
+ import { EffectEx } from "@dxos/effect";
7
+ import { useAtomValue } from "@effect-atom/atom-react";
8
+ //#region src/containers/RegistrySettingsContainer/RegistrySettingsContainer.tsx
9
+ /**
10
+ * Wires the {@link RegistrySettings} presentational component to the plugin
11
+ * manager and the settings atom: subscribes to `manager.devPluginIds` for the
12
+ * "currently loaded" indicator and exposes enable/disable callbacks that drive
13
+ * the manager's add/enable/remove flow.
14
+ */
15
+ var RegistrySettingsContainer = ({ subject }) => {
16
+ const manager = usePluginManager();
17
+ const { settings, updateSettings } = useSettingsState(subject.atom);
18
+ return /* @__PURE__ */ jsx(RegistrySettings, {
19
+ settings,
20
+ onSettingsChange: updateSettings,
21
+ activeDevPluginIds: useAtomValue(manager.devPluginIds),
22
+ onEnableDev: useCallback(async (url) => {
23
+ await EffectEx.runAndForwardErrors(Effect.gen(function* () {
24
+ const plugin = yield* manager.add(url);
25
+ yield* manager.enable(plugin.meta.profile.key);
26
+ }));
27
+ }, [manager]),
28
+ onDisableDev: useCallback(async (id) => {
29
+ await EffectEx.runAndForwardErrors(manager.remove(id));
30
+ }, [manager])
31
+ });
32
+ };
33
+ RegistrySettingsContainer.displayName = "RegistrySettingsContainer";
34
+ //#endregion
35
+ export { RegistrySettingsContainer as default };
36
+
37
+ //# sourceMappingURL=chunk-RegistrySettingsContainer.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-RegistrySettingsContainer.mjs","names":[],"sources":["../../src/containers/RegistrySettingsContainer/RegistrySettingsContainer.tsx"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { useAtomValue } from '@effect-atom/atom-react';\nimport * as Effect from 'effect/Effect';\nimport React, { useCallback } from 'react';\n\nimport { usePluginManager, useSettingsState } from '@dxos/app-framework/ui';\nimport { type AppCapabilities } from '@dxos/app-toolkit';\nimport { EffectEx } from '@dxos/effect';\n\nimport { RegistrySettings } from '../../components';\nimport { type RegistrySettings as RegistrySettingsType } from '../../types';\n\nexport type RegistrySettingsContainerProps = {\n subject: AppCapabilities.Settings;\n};\n\n/**\n * Wires the {@link RegistrySettings} presentational component to the plugin\n * manager and the settings atom: subscribes to `manager.devPluginIds` for the\n * \"currently loaded\" indicator and exposes enable/disable callbacks that drive\n * the manager's add/enable/remove flow.\n */\nexport const RegistrySettingsContainer = ({ subject }: RegistrySettingsContainerProps) => {\n const manager = usePluginManager();\n const { settings, updateSettings } = useSettingsState<RegistrySettingsType>(subject.atom);\n const activeDevPluginIds = useAtomValue(manager.devPluginIds);\n\n const onEnableDev = useCallback(\n async (url: string) => {\n await EffectEx.runAndForwardErrors(\n Effect.gen(function* () {\n const plugin = yield* manager.add(url);\n yield* manager.enable(plugin.meta.profile.key);\n }),\n );\n },\n [manager],\n );\n\n const onDisableDev = useCallback(\n async (id: string) => {\n await EffectEx.runAndForwardErrors(manager.remove(id));\n },\n [manager],\n );\n\n return (\n <RegistrySettings\n settings={settings}\n onSettingsChange={updateSettings}\n activeDevPluginIds={activeDevPluginIds}\n onEnableDev={onEnableDev}\n onDisableDev={onDisableDev}\n />\n );\n};\n\nRegistrySettingsContainer.displayName = 'RegistrySettingsContainer';\n"],"mappings":";;;;;;;;;;;;;;AAyBA,IAAa,6BAA6B,EAAE,cAA8C;CACxF,MAAM,UAAU,iBAAiB;CACjC,MAAM,EAAE,UAAU,mBAAmB,iBAAuC,QAAQ,IAAI;CAsBxF,OACE,oBAAC,kBAAD;EACY;EACV,kBAAkB;EACE,oBAzBG,aAAa,QAAQ,YAyBxB;EACP,aAxBG,YAClB,OAAO,QAAgB;GACrB,MAAM,SAAS,oBACb,OAAO,IAAI,aAAa;IACtB,MAAM,SAAS,OAAO,QAAQ,IAAI,GAAG;IACrC,OAAO,QAAQ,OAAO,OAAO,KAAK,QAAQ,GAAG;GAC/C,CAAC,CACH;EACF,GACA,CAAC,OAAO,CAeO;EACC,cAbG,YACnB,OAAO,OAAe;GACpB,MAAM,SAAS,oBAAoB,QAAQ,OAAO,EAAE,CAAC;EACvD,GACA,CAAC,OAAO,CASQ;CACf,CAAA;AAEL;AAEA,0BAA0B,cAAc"}
@@ -0,0 +1,202 @@
1
+ import { o as LOAD_PLUGIN_DIALOG } from "./chunk-containers.mjs";
2
+ import { n as getRemotePluginIds, t as getCategoryPredicate } from "./chunk-categories.mjs";
3
+ import { Capabilities, Capability, Plugin } from "@dxos/app-framework";
4
+ import { AppCapabilities, LayoutOperation, SettingsOperation } from "@dxos/app-toolkit";
5
+ import { DXN } from "@dxos/keys";
6
+ import { Position } from "@dxos/util";
7
+ import * as Effect from "effect/Effect";
8
+ import { REGISTRY_ID, meta } from "#meta";
9
+ import { Operation } from "@dxos/compute";
10
+ import { GraphBuilder, Node, NodeMatcher } from "@dxos/plugin-graph";
11
+ //#region src/capabilities/app-graph-builder.ts
12
+ /**
13
+ * Turns a registry catalog entry into a minimal {@link Plugin.Plugin} so it
14
+ * can be attached as the graph node's `data`. The synthesized plugin has no
15
+ * modules and only exists so the article surface can render details for
16
+ * registry plugins that haven't been installed yet.
17
+ */
18
+ var toDisplayPlugin = (entry) => ({
19
+ [Plugin.PluginTypeId]: Plugin.PluginTypeId,
20
+ meta: Plugin.makeMeta({
21
+ key: DXN.make(entry.profile.key),
22
+ name: entry.profile.name,
23
+ description: entry.profile.description,
24
+ homePage: entry.profile.homePage,
25
+ source: entry.profile.source,
26
+ screenshots: entry.profile.screenshots,
27
+ tags: entry.profile.tags,
28
+ icon: entry.profile.icon,
29
+ author: entry.profile.author
30
+ }),
31
+ modules: []
32
+ });
33
+ var app_graph_builder_default = Capability.makeModule(Effect.fnUntraced(function* () {
34
+ const capabilities = yield* Capability.Service;
35
+ const extensions = yield* Effect.all([
36
+ GraphBuilder.createExtension({
37
+ id: "openRegistry",
38
+ match: NodeMatcher.whenRoot,
39
+ actions: () => Effect.succeed([{
40
+ id: "openRegistry",
41
+ data: () => Operation.invoke(SettingsOperation.OpenPluginRegistry),
42
+ properties: {
43
+ label: ["open-plugin-registry.label", { ns: meta.profile.key }],
44
+ icon: "ph--squares-four--regular",
45
+ disposition: "menu"
46
+ }
47
+ }])
48
+ }),
49
+ GraphBuilder.createExtension({
50
+ id: "registry",
51
+ match: NodeMatcher.whenRoot,
52
+ connector: () => Effect.succeed([Node.make({
53
+ id: REGISTRY_ID,
54
+ type: meta.profile.key,
55
+ properties: {
56
+ label: ["plugin-registry.label", { ns: meta.profile.key }],
57
+ icon: "ph--squares-four--regular",
58
+ disposition: "pin-end",
59
+ position: Position.first,
60
+ testId: "treeView.pluginRegistry"
61
+ }
62
+ })])
63
+ }),
64
+ GraphBuilder.createExtension({
65
+ id: "categories",
66
+ url: {
67
+ key: "category",
68
+ kind: "item",
69
+ path: []
70
+ },
71
+ match: NodeMatcher.whenId(`root/${REGISTRY_ID}`),
72
+ connector: (_node, get) => {
73
+ const manager = capabilities.get(Capabilities.PluginManager);
74
+ const plugins = get(manager.plugins);
75
+ const filterContext = {
76
+ core: get(manager.core),
77
+ enabled: get(manager.enabled),
78
+ remoteIds: getRemotePluginIds()
79
+ };
80
+ const categoryCount = (category) => plugins.filter(getCategoryPredicate(category, filterContext)).length;
81
+ const registryCount = get(manager.pluginRegistry.plugins).entries.length;
82
+ return Effect.succeed([
83
+ Node.make({
84
+ id: "bundled",
85
+ type: "category",
86
+ data: "bundled",
87
+ properties: {
88
+ label: ["bundled-plugins.label", { ns: meta.profile.key }],
89
+ icon: "ph--squares-four--regular",
90
+ testId: "pluginRegistry.bundled",
91
+ count: categoryCount("bundled")
92
+ }
93
+ }),
94
+ Node.make({
95
+ id: "installed",
96
+ type: "category",
97
+ data: "installed",
98
+ properties: {
99
+ label: ["installed-plugins.label", { ns: meta.profile.key }],
100
+ icon: "ph--check--regular",
101
+ testId: "pluginRegistry.installed",
102
+ count: categoryCount("installed")
103
+ }
104
+ }),
105
+ Node.make({
106
+ id: "recommended",
107
+ type: "category",
108
+ data: "recommended",
109
+ properties: {
110
+ label: ["recommended-plugins.label", { ns: meta.profile.key }],
111
+ icon: "ph--star--regular",
112
+ testId: "pluginRegistry.recommended",
113
+ count: categoryCount("recommended")
114
+ }
115
+ }),
116
+ Node.make({
117
+ id: "labs",
118
+ type: "category",
119
+ data: "labs",
120
+ properties: {
121
+ label: ["labs-plugins.label", { ns: meta.profile.key }],
122
+ icon: "ph--flask--regular",
123
+ testId: "pluginRegistry.labs",
124
+ count: categoryCount("labs")
125
+ }
126
+ }),
127
+ ...registryCount > 0 ? [Node.make({
128
+ id: "registry",
129
+ type: "category",
130
+ data: "registry",
131
+ properties: {
132
+ label: ["registry-plugins.label", { ns: meta.profile.key }],
133
+ icon: "ph--users-three--regular",
134
+ testId: "pluginRegistry.registry",
135
+ count: registryCount
136
+ }
137
+ })] : []
138
+ ]);
139
+ }
140
+ }),
141
+ GraphBuilder.createExtension({
142
+ id: "actions",
143
+ match: NodeMatcher.whenId(`root/${REGISTRY_ID}`),
144
+ actions: () => Effect.succeed([{
145
+ id: "loadByUrl",
146
+ data: Effect.fnUntraced(function* () {
147
+ yield* Operation.invoke(LayoutOperation.UpdateDialog, {
148
+ subject: LOAD_PLUGIN_DIALOG,
149
+ state: true
150
+ });
151
+ }),
152
+ properties: {
153
+ label: ["load-by-url.label", { ns: meta.profile.key }],
154
+ icon: "ph--cloud-arrow-down--regular",
155
+ disposition: "list-item-primary"
156
+ }
157
+ }])
158
+ }),
159
+ GraphBuilder.createExtension({
160
+ id: "plugins",
161
+ url: {
162
+ key: "registry",
163
+ kind: "item",
164
+ path: []
165
+ },
166
+ match: NodeMatcher.whenId(`root/${REGISTRY_ID}`),
167
+ connector: (_node, get) => {
168
+ const manager = capabilities.get(Capabilities.PluginManager);
169
+ const installedIds = new Set(manager.getPlugins().map((plugin) => plugin.meta.profile.key));
170
+ const installedNodes = manager.getPlugins().map((plugin) => Node.make({
171
+ id: plugin.meta.profile.key,
172
+ type: "org.dxos.plugin",
173
+ data: plugin,
174
+ properties: {
175
+ label: plugin.meta.profile.name ?? plugin.meta.profile.key,
176
+ icon: plugin.meta.profile.icon?.key ?? "ph--circle--regular",
177
+ disposition: "hidden"
178
+ }
179
+ }));
180
+ const registryNodes = get(manager.pluginRegistry.plugins).entries.filter((entry) => !installedIds.has(entry.profile.key)).map((entry) => {
181
+ const plugin = toDisplayPlugin(entry);
182
+ return Node.make({
183
+ id: plugin.meta.profile.key,
184
+ type: "org.dxos.plugin",
185
+ data: plugin,
186
+ properties: {
187
+ label: plugin.meta.profile.name ?? plugin.meta.profile.key,
188
+ icon: plugin.meta.profile.icon?.key ?? "ph--circle--regular",
189
+ disposition: "hidden"
190
+ }
191
+ });
192
+ });
193
+ return Effect.succeed([...installedNodes, ...registryNodes]);
194
+ }
195
+ })
196
+ ]);
197
+ return Capability.contributes(AppCapabilities.AppGraphBuilder, extensions);
198
+ }));
199
+ //#endregion
200
+ export { app_graph_builder_default as default };
201
+
202
+ //# sourceMappingURL=chunk-app-graph-builder.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-app-graph-builder.mjs","names":[],"sources":["../../src/capabilities/app-graph-builder.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Capabilities, Capability, Plugin } from '@dxos/app-framework';\nimport { AppCapabilities, LayoutOperation, SettingsOperation } from '@dxos/app-toolkit';\nimport { Operation } from '@dxos/compute';\nimport { DXN } from '@dxos/keys';\nimport { GraphBuilder, Node, NodeMatcher } from '@dxos/plugin-graph';\nimport { Position } from '@dxos/util';\n\nimport { REGISTRY_ID, meta } from '#meta';\n\nimport { getCategoryPredicate, getRemotePluginIds } from '../categories';\nimport { LOAD_PLUGIN_DIALOG } from '../containers';\n\n/**\n * Turns a registry catalog entry into a minimal {@link Plugin.Plugin} so it\n * can be attached as the graph node's `data`. The synthesized plugin has no\n * modules and only exists so the article surface can render details for\n * registry plugins that haven't been installed yet.\n */\nconst toDisplayPlugin = (entry: Plugin.Meta): Plugin.Plugin =>\n ({\n [Plugin.PluginTypeId]: Plugin.PluginTypeId,\n meta: Plugin.makeMeta({\n key: DXN.make(entry.profile.key),\n name: entry.profile.name,\n description: entry.profile.description,\n homePage: entry.profile.homePage,\n source: entry.profile.source,\n screenshots: entry.profile.screenshots,\n tags: entry.profile.tags,\n icon: entry.profile.icon,\n author: entry.profile.author,\n }),\n modules: [],\n }) as Plugin.Plugin;\n\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n const capabilities = yield* Capability.Service;\n\n const extensions = yield* Effect.all([\n GraphBuilder.createExtension({\n id: 'openRegistry',\n match: NodeMatcher.whenRoot,\n actions: () =>\n Effect.succeed([\n {\n id: 'openRegistry',\n data: () => Operation.invoke(SettingsOperation.OpenPluginRegistry),\n properties: {\n label: ['open-plugin-registry.label', { ns: meta.profile.key }],\n icon: 'ph--squares-four--regular',\n disposition: 'menu',\n },\n },\n ]),\n }),\n GraphBuilder.createExtension({\n id: 'registry',\n match: NodeMatcher.whenRoot,\n // REGISTRY_ID is a pinned workspace (the URL's workspace anchor), so it carries no key of its\n // own; its category and plugin children are the addressable planks (see `categories`/`plugins`).\n connector: () =>\n Effect.succeed([\n Node.make({\n id: REGISTRY_ID,\n type: meta.profile.key,\n properties: {\n label: ['plugin-registry.label', { ns: meta.profile.key }],\n icon: 'ph--squares-four--regular',\n disposition: 'pin-end',\n position: Position.first,\n testId: 'treeView.pluginRegistry',\n },\n }),\n ]),\n }),\n GraphBuilder.createExtension({\n id: 'categories',\n url: { key: 'category', kind: 'item', path: [] },\n match: NodeMatcher.whenId(`root/${REGISTRY_ID}`),\n connector: (_node, get) => {\n const manager = capabilities.get(Capabilities.PluginManager);\n const plugins = get(manager.plugins);\n const filterContext = {\n core: get(manager.core),\n enabled: get(manager.enabled),\n remoteIds: getRemotePluginIds(),\n };\n const categoryCount = (category: string) =>\n plugins.filter(getCategoryPredicate(category, filterContext)).length;\n const registryCount = get(manager.pluginRegistry.plugins).entries.length;\n\n return Effect.succeed([\n Node.make({\n id: 'bundled',\n type: 'category',\n data: 'bundled',\n properties: {\n label: ['bundled-plugins.label', { ns: meta.profile.key }],\n icon: 'ph--squares-four--regular',\n testId: 'pluginRegistry.bundled',\n count: categoryCount('bundled'),\n },\n }),\n Node.make({\n id: 'installed',\n type: 'category',\n data: 'installed',\n properties: {\n label: ['installed-plugins.label', { ns: meta.profile.key }],\n icon: 'ph--check--regular',\n testId: 'pluginRegistry.installed',\n count: categoryCount('installed'),\n },\n }),\n Node.make({\n id: 'recommended',\n type: 'category',\n data: 'recommended',\n properties: {\n label: ['recommended-plugins.label', { ns: meta.profile.key }],\n icon: 'ph--star--regular',\n testId: 'pluginRegistry.recommended',\n count: categoryCount('recommended'),\n },\n }),\n Node.make({\n id: 'labs',\n type: 'category',\n data: 'labs',\n properties: {\n label: ['labs-plugins.label', { ns: meta.profile.key }],\n icon: 'ph--flask--regular',\n testId: 'pluginRegistry.labs',\n count: categoryCount('labs'),\n },\n }),\n ...(registryCount > 0\n ? [\n Node.make({\n id: 'registry',\n type: 'category',\n data: 'registry',\n properties: {\n label: ['registry-plugins.label', { ns: meta.profile.key }],\n icon: 'ph--users-three--regular',\n testId: 'pluginRegistry.registry',\n count: registryCount,\n },\n }),\n ]\n : []),\n ]);\n },\n }),\n GraphBuilder.createExtension({\n id: 'actions',\n match: NodeMatcher.whenId(`root/${REGISTRY_ID}`),\n actions: () =>\n Effect.succeed([\n {\n id: 'loadByUrl',\n data: Effect.fnUntraced(function* () {\n yield* Operation.invoke(LayoutOperation.UpdateDialog, {\n subject: LOAD_PLUGIN_DIALOG,\n state: true,\n });\n }),\n properties: {\n label: ['load-by-url.label', { ns: meta.profile.key }],\n icon: 'ph--cloud-arrow-down--regular',\n disposition: 'list-item-primary',\n },\n },\n ]),\n }),\n GraphBuilder.createExtension({\n id: 'plugins',\n url: { key: 'registry', kind: 'item', path: [] },\n match: NodeMatcher.whenId(`root/${REGISTRY_ID}`),\n connector: (_node, get) => {\n const manager = capabilities.get(Capabilities.PluginManager);\n const installedIds = new Set(manager.getPlugins().map((plugin) => plugin.meta.profile.key));\n\n const installedNodes = manager.getPlugins().map((plugin) =>\n Node.make({\n id: plugin.meta.profile.key,\n type: 'org.dxos.plugin',\n data: plugin,\n properties: {\n label: plugin.meta.profile.name ?? plugin.meta.profile.key,\n icon: plugin.meta.profile.icon?.key ?? 'ph--circle--regular',\n disposition: 'hidden',\n },\n }),\n );\n\n const registryEntries = get(manager.pluginRegistry.plugins).entries;\n const registryNodes = registryEntries\n // `profile.key` is the bare NSID on both sides; comparing against a `dxn:`-prefixed\n // URI here would never match and would duplicate every installed plugin's node.\n .filter((entry) => !installedIds.has(entry.profile.key))\n .map((entry) => {\n const plugin = toDisplayPlugin(entry);\n return Node.make({\n id: plugin.meta.profile.key,\n type: 'org.dxos.plugin',\n data: plugin,\n properties: {\n label: plugin.meta.profile.name ?? plugin.meta.profile.key,\n icon: plugin.meta.profile.icon?.key ?? 'ph--circle--regular',\n disposition: 'hidden',\n },\n });\n });\n\n return Effect.succeed([...installedNodes, ...registryNodes]);\n },\n }),\n ]);\n\n return Capability.contributes(AppCapabilities.AppGraphBuilder, extensions);\n }),\n);\n"],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ import { UrlLoader } from "@dxos/app-framework";
2
+ //#region src/categories.ts
3
+ /** Quality tiers surfaced in `recommended`; an untagged plugin is excluded rather than assumed ready. */
4
+ var RECOMMENDED_TIERS = ["beta", "alpha"];
5
+ /**
6
+ * Predicate selecting the plugins shown in a registry category, computed against the live plugin list.
7
+ * The `registry` category is sourced from the catalog rather than the local plugin list and is not handled here.
8
+ */
9
+ var getCategoryPredicate = (category, { core, enabled, remoteIds }) => {
10
+ switch (category) {
11
+ case "bundled": return ({ meta }) => !core.includes(meta.profile.key) && !remoteIds.has(meta.profile.key);
12
+ case "installed": return ({ meta }) => !core.includes(meta.profile.key) && enabled.includes(meta.profile.key);
13
+ case "recommended": return ({ meta }) => !core.includes(meta.profile.key) && !remoteIds.has(meta.profile.key) && (meta.profile.tags?.some((tag) => RECOMMENDED_TIERS.includes(tag)) ?? false);
14
+ case "labs": return ({ meta }) => meta.profile.tags?.includes("labs") ?? false;
15
+ default: return () => false;
16
+ }
17
+ };
18
+ /** Set of plugin ids known to originate from a remote URL (not bundled). */
19
+ var getRemotePluginIds = () => new Set(UrlLoader.getRemoteEntries().map((entry) => entry.id));
20
+ //#endregion
21
+ export { getRemotePluginIds as n, getCategoryPredicate as t };
22
+
23
+ //# sourceMappingURL=chunk-categories.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chunk-categories.mjs","names":[],"sources":["../../src/categories.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport { type Plugin, UrlLoader } from '@dxos/app-framework';\n\nexport type PluginPredicate = (plugin: Plugin.Plugin) => boolean;\n\n/** Quality tiers surfaced in `recommended`; an untagged plugin is excluded rather than assumed ready. */\nconst RECOMMENDED_TIERS: readonly string[] = ['beta', 'alpha'];\n\nexport type CategoryFilterContext = {\n /** Core (bundled-by-default) plugin ids. */\n core: readonly string[];\n /** Enabled plugin ids. */\n enabled: readonly string[];\n /** Plugin ids loaded from a remote URL (not bundled). */\n remoteIds: ReadonlySet<string>;\n};\n\n/**\n * Predicate selecting the plugins shown in a registry category, computed against the live plugin list.\n * The `registry` category is sourced from the catalog rather than the local plugin list and is not handled here.\n */\nexport const getCategoryPredicate = (\n category: string,\n { core, enabled, remoteIds }: CategoryFilterContext,\n): PluginPredicate => {\n switch (category) {\n case 'bundled':\n return ({ meta }) => !core.includes(meta.profile.key) && !remoteIds.has(meta.profile.key);\n case 'installed':\n return ({ meta }) => !core.includes(meta.profile.key) && enabled.includes(meta.profile.key);\n case 'recommended':\n return ({ meta }) =>\n !core.includes(meta.profile.key) &&\n !remoteIds.has(meta.profile.key) &&\n (meta.profile.tags?.some((tag) => RECOMMENDED_TIERS.includes(tag)) ?? false);\n case 'labs':\n return ({ meta }) => meta.profile.tags?.includes('labs') ?? false;\n default:\n return () => false;\n }\n};\n\n/** Set of plugin ids known to originate from a remote URL (not bundled). */\nexport const getRemotePluginIds = (): ReadonlySet<string> =>\n new Set(UrlLoader.getRemoteEntries().map((entry) => entry.id));\n"],"mappings":";;;AASA,IAAM,oBAAuC,CAAC,QAAQ,OAAO;;;;;AAe7D,IAAa,wBACX,UACA,EAAE,MAAM,SAAS,gBACG;CACpB,QAAQ,UAAR;EACE,KAAK,WACH,QAAQ,EAAE,WAAW,CAAC,KAAK,SAAS,KAAK,QAAQ,GAAG,KAAK,CAAC,UAAU,IAAI,KAAK,QAAQ,GAAG;EAC1F,KAAK,aACH,QAAQ,EAAE,WAAW,CAAC,KAAK,SAAS,KAAK,QAAQ,GAAG,KAAK,QAAQ,SAAS,KAAK,QAAQ,GAAG;EAC5F,KAAK,eACH,QAAQ,EAAE,WACR,CAAC,KAAK,SAAS,KAAK,QAAQ,GAAG,KAC/B,CAAC,UAAU,IAAI,KAAK,QAAQ,GAAG,MAC9B,KAAK,QAAQ,MAAM,MAAM,QAAQ,kBAAkB,SAAS,GAAG,CAAC,KAAK;EAC1E,KAAK,QACH,QAAQ,EAAE,WAAW,KAAK,QAAQ,MAAM,SAAS,MAAM,KAAK;EAC9D,SACE,aAAa;CACjB;AACF;;AAGA,IAAa,2BACX,IAAI,IAAI,UAAU,iBAAiB,CAAC,CAAC,KAAK,UAAU,MAAM,EAAE,CAAC"}