@dxos/plugin-explorer 0.11.0 → 0.12.0-next-bbabc02363f18d7643ea4160e3a7a7809870fd0d

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 (80) hide show
  1. package/dist/lib/ExplorerAction.mjs +16 -0
  2. package/dist/lib/ExplorerAction.mjs.map +1 -0
  3. package/dist/lib/ExplorerEvents.mjs +15 -0
  4. package/dist/lib/ExplorerEvents.mjs.map +1 -0
  5. package/dist/lib/Graph.mjs +38 -0
  6. package/dist/lib/Graph.mjs.map +1 -0
  7. package/dist/lib/capabilities.mjs +5 -4
  8. package/dist/lib/capabilities.mjs.map +1 -1
  9. package/dist/lib/chunk-ExplorerPlugin.mjs +6 -6
  10. package/dist/lib/chunk-ExplorerPlugin.mjs.map +1 -1
  11. package/dist/lib/chunk-NeighborhoodCompanion.mjs +1 -1
  12. package/dist/lib/chunk-app-graph-builder.mjs +4 -3
  13. package/dist/lib/chunk-app-graph-builder.mjs.map +1 -1
  14. package/dist/lib/chunk-create-object.mjs +10 -8
  15. package/dist/lib/chunk-create-object.mjs.map +1 -1
  16. package/dist/lib/chunk-meta.mjs +2 -2
  17. package/dist/lib/chunk-meta.mjs.map +1 -1
  18. package/dist/lib/chunk-react-surface.mjs +5 -4
  19. package/dist/lib/chunk-react-surface.mjs.map +1 -1
  20. package/dist/lib/chunk-rolldown-runtime.mjs +13 -0
  21. package/dist/lib/chunk-types.mjs +224 -53
  22. package/dist/lib/chunk-types.mjs.map +1 -1
  23. package/dist/lib/components.mjs +1 -1
  24. package/dist/lib/hooks.mjs +1 -1
  25. package/dist/lib/hooks.mjs.map +1 -1
  26. package/dist/lib/index.mjs +4 -2
  27. package/dist/lib/plugin.mjs +1 -1
  28. package/dist/lib/plugin.mjs.map +1 -1
  29. package/dist/lib/testing.mjs +1 -1
  30. package/dist/lib/translations.mjs +3 -3
  31. package/dist/lib/translations.mjs.map +1 -1
  32. package/dist/types/src/ExplorerPlugin.d.ts +1 -1
  33. package/dist/types/src/ExplorerPlugin.d.ts.map +1 -1
  34. package/dist/types/src/capabilities/app-graph-builder.d.ts +2 -2
  35. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  36. package/dist/types/src/capabilities/create-object.d.ts +2 -2
  37. package/dist/types/src/capabilities/create-object.d.ts.map +1 -1
  38. package/dist/types/src/capabilities/index.d.ts +3 -4
  39. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  40. package/dist/types/src/capabilities/react-surface.d.ts +2 -2
  41. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  42. package/dist/types/src/components/Chart/Chart.stories.d.ts +1 -1
  43. package/dist/types/src/components/Globe/Globe.stories.d.ts +1 -1
  44. package/dist/types/src/components/Graph/CanvasForceGraph.stories.d.ts +1 -1
  45. package/dist/types/src/components/Graph/ForceGraph.stories.d.ts +1 -1
  46. package/dist/types/src/components/Tree/EdgeBundling.stories.d.ts +1 -1
  47. package/dist/types/src/index.d.ts +3 -1
  48. package/dist/types/src/index.d.ts.map +1 -1
  49. package/dist/types/src/plugin.d.ts +1 -1
  50. package/dist/types/src/plugin.d.ts.map +1 -1
  51. package/dist/types/src/translations.d.ts.map +1 -1
  52. package/dist/types/src/types/ExplorerEvents.d.ts +8 -0
  53. package/dist/types/src/types/ExplorerEvents.d.ts.map +1 -0
  54. package/dist/types/tsconfig.tsbuildinfo +1 -1
  55. package/dx.config.ts +1 -1
  56. package/package.json +59 -43
  57. package/src/ExplorerPlugin.test.ts +6 -2
  58. package/src/ExplorerPlugin.tsx +16 -11
  59. package/src/capabilities/app-graph-builder.ts +4 -3
  60. package/src/capabilities/create-object.ts +6 -5
  61. package/src/capabilities/index.ts +7 -4
  62. package/src/capabilities/react-surface.ts +5 -3
  63. package/src/components/Graph/CanvasForceGraph.stories.tsx +7 -7
  64. package/src/components/Graph/ForceGraph.stories.tsx +7 -7
  65. package/src/components/Lattice/Lattice.stories.tsx +7 -7
  66. package/src/components/Tree/EdgeBundling.stories.tsx +3 -3
  67. package/src/containers/ExplorerArticle/ExplorerArticle.stories.tsx +7 -7
  68. package/src/hooks/useGraphModel.ts +1 -1
  69. package/src/index.ts +3 -1
  70. package/src/meta.ts +1 -1
  71. package/src/plugin.ts +1 -1
  72. package/src/translations.ts +2 -1
  73. package/src/types/ExplorerAction.ts +2 -2
  74. package/src/types/ExplorerEvents.ts +16 -0
  75. package/dist/lib/chunk-types2.mjs +0 -231
  76. package/dist/lib/chunk-types2.mjs.map +0 -1
  77. package/dist/lib/types.mjs +0 -2
  78. package/dist/types/src/types/index.d.ts +0 -3
  79. package/dist/types/src/types/index.d.ts.map +0 -1
  80. package/src/types/index.ts +0 -6
@@ -0,0 +1,16 @@
1
+ import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
2
+ import * as Schema from "effect/Schema";
3
+ import * as SpaceForm from "@dxos/plugin-space/SpaceForm";
4
+ //#region src/types/ExplorerAction.ts
5
+ var ExplorerAction_exports = /* @__PURE__ */ __exportAll({ GraphProps: () => GraphProps });
6
+ var GraphProps = Schema.Struct({
7
+ name: Schema.optional(Schema.String),
8
+ typename: Schema.String.pipe(Schema.annotations({ title: "Select type" }), SpaceForm.TypeInputOptionsAnnotation.set({
9
+ location: ["database", "runtime"],
10
+ kind: ["user"]
11
+ }), Schema.optional)
12
+ });
13
+ //#endregion
14
+ export { GraphProps, ExplorerAction_exports as t };
15
+
16
+ //# sourceMappingURL=ExplorerAction.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExplorerAction.mjs","names":[],"sources":["../../src/types/ExplorerAction.ts"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nimport * as SpaceForm from '@dxos/plugin-space/SpaceForm';\n\nexport const GraphProps = Schema.Struct({\n name: Schema.optional(Schema.String),\n // TODO(wittjosiah): This should be a query input instead.\n typename: Schema.String.pipe(\n Schema.annotations({ title: 'Select type' }),\n SpaceForm.TypeInputOptionsAnnotation.set({\n location: ['database', 'runtime'],\n kind: ['user'],\n }),\n Schema.optional,\n ),\n});\n"],"mappings":";;;;;AAQA,IAAa,aAAa,OAAO,OAAO;CACtC,MAAM,OAAO,SAAS,OAAO,MAAM;CAEnC,UAAU,OAAO,OAAO,KACtB,OAAO,YAAY,EAAE,OAAO,cAAc,CAAC,GAC3C,UAAU,2BAA2B,IAAI;EACvC,UAAU,CAAC,YAAY,SAAS;EAChC,MAAM,CAAC,MAAM;CACf,CAAC,GACD,OAAO,QACT;AACF,CAAC"}
@@ -0,0 +1,15 @@
1
+ import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
2
+ import * as ActivationEvent from "@dxos/app-framework/ActivationEvent";
3
+ import { meta } from "#meta";
4
+ //#region src/types/ExplorerEvents.ts
5
+ var ExplorerEvents_exports = /* @__PURE__ */ __exportAll({ Start: () => Start });
6
+ /**
7
+ * The feature's start event: the plugin's start-gated modules (and cross-plugin
8
+ * contributions consumed by this feature) activate here. Fired on demand and by the
9
+ * host's idle trickle.
10
+ */
11
+ var Start = ActivationEvent.pluginStart(meta.profile.key);
12
+ //#endregion
13
+ export { Start, ExplorerEvents_exports as t };
14
+
15
+ //# sourceMappingURL=ExplorerEvents.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ExplorerEvents.mjs","names":[],"sources":["../../src/types/ExplorerEvents.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\n// @import-as-namespace\n\nimport * as ActivationEvent from '@dxos/app-framework/ActivationEvent';\n\nimport { meta } from '#meta';\n\n/**\n * The feature's start event: the plugin's start-gated modules (and cross-plugin\n * contributions consumed by this feature) activate here. Fired on demand and by the\n * host's idle trickle.\n */\nexport const Start = ActivationEvent.pluginStart(meta.profile.key);\n"],"mappings":";;;;;;;;;;AAeA,IAAa,QAAQ,gBAAgB,YAAY,KAAK,QAAQ,GAAG"}
@@ -0,0 +1,38 @@
1
+ import { t as __exportAll } from "./chunk-rolldown-runtime.mjs";
2
+ import * as Schema from "effect/Schema";
3
+ import { Annotation, DXN, Filter, Obj, Query, QueryAST, Ref, Type, View } from "@dxos/echo";
4
+ import { FormInputAnnotation, LabelAnnotation } from "@dxos/echo/Annotation";
5
+ import { ViewAnnotation } from "@dxos/schema";
6
+ //#region src/types/Graph.ts
7
+ var Graph_exports = /* @__PURE__ */ __exportAll({
8
+ Graph: () => Graph,
9
+ make: () => make
10
+ });
11
+ var Graph = class extends Type.makeObject(DXN.make("org.dxos.type.graph", "0.1.0"))(Schema.Struct({
12
+ name: Schema.optional(Schema.String),
13
+ view: Ref.Ref(View.View).pipe(FormInputAnnotation.set(false), Schema.optional),
14
+ query: Schema.Struct({
15
+ raw: Schema.optional(Schema.String),
16
+ ast: QueryAST.Query
17
+ }).pipe(FormInputAnnotation.set(false))
18
+ }).pipe(LabelAnnotation.set(["name"]), ViewAnnotation.set(["view"]), Annotation.IconAnnotation.set({
19
+ icon: "ph--graph--regular",
20
+ hue: "green"
21
+ }))) {};
22
+ /**
23
+ * Make a graph as a view of a data set.
24
+ */
25
+ var make = ({ name, query = {
26
+ raw: "",
27
+ ast: Query.select(Filter.nothing()).ast
28
+ }, view } = {}) => {
29
+ return Obj.make(Graph, {
30
+ name,
31
+ view: view && Ref.make(view),
32
+ query
33
+ });
34
+ };
35
+ //#endregion
36
+ export { Graph, make, Graph_exports as t };
37
+
38
+ //# sourceMappingURL=Graph.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Graph.mjs","names":[],"sources":["../../src/types/Graph.ts"],"sourcesContent":["//\n// Copyright 2024 DXOS.org\n//\n\nimport * as Schema from 'effect/Schema';\n\nimport { Annotation, DXN, Filter, Obj, Query, QueryAST, Ref, Type, View } from '@dxos/echo';\nimport { FormInputAnnotation, LabelAnnotation } from '@dxos/echo/Annotation';\nimport { ViewAnnotation } from '@dxos/schema';\n\nexport class Graph extends Type.makeObject<Graph>(DXN.make('org.dxos.type.graph', '0.1.0'))(\n Schema.Struct({\n name: Schema.optional(Schema.String),\n view: Ref.Ref(View.View).pipe(FormInputAnnotation.set(false), Schema.optional),\n query: Schema.Struct({\n raw: Schema.optional(Schema.String),\n ast: QueryAST.Query,\n }).pipe(FormInputAnnotation.set(false)),\n }).pipe(\n LabelAnnotation.set(['name']),\n ViewAnnotation.set(['view']),\n Annotation.IconAnnotation.set({ icon: 'ph--graph--regular', hue: 'green' }),\n ),\n) {}\n\ntype MakeProps = Omit<Partial<Obj.MakeProps<typeof Graph>>, 'view'> & {\n view?: View.View;\n};\n\n/**\n * Make a graph as a view of a data set.\n */\nexport const make = ({\n name,\n query = { raw: '', ast: Query.select(Filter.nothing()).ast },\n view,\n}: MakeProps = {}): Graph => {\n return Obj.make(Graph, { name, view: view && Ref.make(view), query });\n};\n"],"mappings":";;;;;;;;;;AAUA,IAAa,QAAb,cAA2B,KAAK,WAAkB,IAAI,KAAK,uBAAuB,OAAO,CAAC,CAAC,CACzF,OAAO,OAAO;CACZ,MAAM,OAAO,SAAS,OAAO,MAAM;CACnC,MAAM,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,KAAK,oBAAoB,IAAI,KAAK,GAAG,OAAO,QAAQ;CAC7E,OAAO,OAAO,OAAO;EACnB,KAAK,OAAO,SAAS,OAAO,MAAM;EAClC,KAAK,SAAS;CAChB,CAAC,CAAC,CAAC,KAAK,oBAAoB,IAAI,KAAK,CAAC;AACxC,CAAC,CAAC,CAAC,KACD,gBAAgB,IAAI,CAAC,MAAM,CAAC,GAC5B,eAAe,IAAI,CAAC,MAAM,CAAC,GAC3B,WAAW,eAAe,IAAI;CAAE,MAAM;CAAsB,KAAK;AAAQ,CAAC,CAC5E,CACF,CAAC,CAAC,CAAC;;;;AASH,IAAa,QAAQ,EACnB,MACA,QAAQ;CAAE,KAAK;CAAI,KAAK,MAAM,OAAO,OAAO,QAAQ,CAAC,CAAC,CAAC;AAAI,GAC3D,SACa,CAAC,MAAa;CAC3B,OAAO,IAAI,KAAK,OAAO;EAAE;EAAM,MAAM,QAAQ,IAAI,KAAK,IAAI;EAAG;CAAM,CAAC;AACtE"}
@@ -1,8 +1,9 @@
1
- import { Capability } from "@dxos/app-framework";
1
+ import * as AppCapability from "@dxos/app-toolkit/AppCapability";
2
+ import * as SpaceCapability from "@dxos/plugin-space/SpaceCapability";
2
3
  //#region src/capabilities/index.ts
3
- var AppGraphBuilder = Capability.lazy("AppGraphBuilder", () => import("./chunk-app-graph-builder.mjs"));
4
- var CreateObject = Capability.lazy("CreateObject", () => import("./chunk-create-object.mjs"));
5
- var ReactSurface = Capability.lazy("ReactSurface", () => import("./chunk-react-surface.mjs"));
4
+ var AppGraphBuilder = AppCapability.appGraphBuilder(() => import("./chunk-app-graph-builder.mjs"));
5
+ var CreateObject = SpaceCapability.createObject(() => import("./chunk-create-object.mjs"));
6
+ var ReactSurface = AppCapability.surface(() => import("./chunk-react-surface.mjs"), { roles: ["org.dxos.role.article", "org.dxos.role.section"] });
6
7
  //#endregion
7
8
  export { AppGraphBuilder, CreateObject, ReactSurface };
8
9
 
@@ -1 +1 @@
1
- {"version":3,"file":"capabilities.mjs","names":[],"sources":["../../src/capabilities/index.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport { Capability } from '@dxos/app-framework';\n\nexport const AppGraphBuilder = Capability.lazy('AppGraphBuilder', () => import('./app-graph-builder'));\nexport const CreateObject = Capability.lazy('CreateObject', () => import('./create-object'));\nexport const ReactSurface = Capability.lazy('ReactSurface', () => import('./react-surface'));\n"],"mappings":";;AAMA,IAAa,kBAAkB,WAAW,KAAK,yBAAyB,OAAO,gCAAsB;AACrG,IAAa,eAAe,WAAW,KAAK,sBAAsB,OAAO,4BAAkB;AAC3F,IAAa,eAAe,WAAW,KAAK,sBAAsB,OAAO,4BAAkB"}
1
+ {"version":3,"file":"capabilities.mjs","names":[],"sources":["../../src/capabilities/index.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\nimport * as SpaceCapability from '@dxos/plugin-space/SpaceCapability';\n\nexport const AppGraphBuilder = AppCapability.appGraphBuilder(() => import('./app-graph-builder'));\nexport const CreateObject = SpaceCapability.createObject(() => import('./create-object'));\nexport const ReactSurface = AppCapability.surface(() => import('./react-surface'), {\n roles: ['org.dxos.role.article', 'org.dxos.role.section'],\n});\n"],"mappings":";;;AAOA,IAAa,kBAAkB,cAAc,sBAAsB,OAAO,gCAAsB;AAChG,IAAa,eAAe,gBAAgB,mBAAmB,OAAO,4BAAkB;AACxF,IAAa,eAAe,cAAc,cAAc,OAAO,8BAAoB,EACjF,OAAO,CAAC,yBAAyB,uBAAuB,EAC1D,CAAC"}
@@ -1,17 +1,17 @@
1
- import { Plugin } from "@dxos/app-framework";
2
- import { AppPlugin } from "@dxos/app-toolkit";
3
- import { AppGraphBuilder, CreateObject, ReactSurface } from "#capabilities";
1
+ import { Graph } from "./Graph.mjs";
2
+ import * as Plugin from "@dxos/app-framework/Plugin";
4
3
  import { meta } from "#meta";
4
+ import * as AppCapability from "@dxos/app-toolkit/AppCapability";
5
+ import { AppGraphBuilder, CreateObject, ReactSurface } from "#capabilities";
5
6
  import { translations } from "#translations";
6
- import { Graph } from "#types";
7
7
  //#endregion
8
8
  //#region src/ExplorerPlugin.tsx
9
- var ExplorerPlugin = Plugin.define(meta).pipe(AppPlugin.addCreateObjectModule({ activate: CreateObject }), AppPlugin.addAppGraphModule({ activate: AppGraphBuilder }), AppPlugin.addSchemaModule({ schema: [Graph.Graph] }), AppPlugin.addSurfaceModule({ activate: ReactSurface }), AppPlugin.addTranslationsModule({ translations }), AppPlugin.addPluginAssetModule({ asset: {
9
+ var ExplorerPlugin = Plugin.define(meta).pipe(Plugin.addModule(CreateObject), Plugin.addModule(AppGraphBuilder), Plugin.addModule(AppCapability.schema([Graph])), Plugin.addModule(ReactSurface), Plugin.addModule(AppCapability.translations(translations)), Plugin.addModule(AppCapability.pluginAsset({
10
10
  pluginId: meta.profile.key,
11
11
  path: "PLUGIN.mdl",
12
12
  content: "---\nid: org.dxos.plugin.explorer\nname: ExplorerPlugin\nversion: 0.1.0\n---\n\nAn interactive hypergraph visualization plugin for `DXOS` Composer that reveals relationships between objects in a workspace.\nThe plugin renders a live `Graph` object — backed by a `View` and a `QueryAST` query — through four switchable layout\nvariants: force-directed, radial cluster, edge bundling, and lattice.\nNode colours are derived from ECHO object types; hovering a node triggers an anchor-card preview.\n\n## Extensions\n\nThe following extension dialects are used in this document.\nEach extension is defined in the Appendix or resolved via its URI.\n\n| Term | URI |\n|-------------|--------------------------------|\n| `type` | `org.dxos.mdl.type@1.0` |\n| `feat` | `org.dxos.mdl.feat@1.0` |\n| `test` | `org.dxos.mdl.test@1.0` |\n| `component` | `org.dxos.mdl.component@1.0` |\n| `op` | `org.dxos.mdl.op@1.0` |\n\n## Types\n\n```mdl\ntype QueryInput\n fields:\n raw?: string # raw query string entered by the user\n ast: QueryAST.Query # compiled AST produced by QueryBuilder\n```\n\n```mdl\ntype GraphObject\n desc: ECHO-persistent object that describes a hypergraph visualisation session.\n fields:\n name?: string\n view: Ref<View> # schema view that defines visible fields\n query: QueryInput\n```\n\n```mdl\ntype ExplorerVariant\n literals: force | cluster | bundle | lattice\n```\n\n## Components\n\n```mdl\ncomponent ExplorerArticle\n desc: |\n Top-level article / section surface for a Graph object.\n Renders one of four SVG layout variants and a toolbar for switching between them.\n props:\n role: article | section\n subject: Ref<View>\n attendableId?: string\n state:\n variant: ExplorerVariant # active layout; default force\n filter?: Filter.Any # compiled from toolbar query input\n model?: SpaceGraphModel # live reactive graph built from db + filter\n slots:\n toolbar?: ReactNode # shown only when role === article\n actions:\n setVariant(v: ExplorerVariant)\n setFilter(raw: string)\n layout: |\n ┌────────────────────────────────┐\n │ [toolbar slot — article only] │\n │ [QueryEditor] [variant tabs] │\n ├────────────────────────────────┤\n │ │\n │ SVG canvas (force / cluster │\n │ / bundle / lattice) │\n │ │\n └────────────────────────────────┘\n```\n\n```mdl\ncomponent Visualization\n desc: |\n Internal component that owns a single persistent SVG.Graph mount.\n Swaps the active GraphProjector when the variant changes, seeding the\n new projector with the previous layout so node positions tween smoothly.\n props:\n variant: ExplorerVariant\n model: SpaceGraphModel\n state:\n projector?: GraphProjector<SpaceGraphNode>\n actions:\n createProjector(variant: ExplorerVariant, prev?: GraphLayout) → GraphProjector\n createRenderNode(variant: ExplorerVariant) → RenderNode\n```\n\n## Operations\n\n```mdl\nop createGraph\n desc: |\n Creates a new Graph object scoped to a target space or collection.\n Derives a ViewModel from the database for the selected typename, then\n writes the object via SpaceOperation.AddObject.\n input:\n name?: string\n typename?: string # ECHO typename used to seed the initial view\n target: Space | Collection\n targetNodeId?: string\n output: GraphObject\n effects: [echo:write]\n note: |\n The typename is resolved through ViewModel.makeFromDatabase; the resulting\n View is stored as a Ref inside the Graph object.\n```\n\n```mdl\nop buildFilter\n desc: |\n Compiles a raw query string into a Filter via QueryBuilder.\n Pure — no side effects; result is passed to SpaceGraphModel.setFilter.\n input:\n raw: string\n output: Filter.Any\n errors:\n ParseError: raw string is not valid query syntax\n```\n\n## Features\n\n```mdl\nfeat F-1: Graph Object\n\n req F-1.1: A Graph object stores a name, a View ref, and a compiled QueryAST.\n req F-1.2:\n when: user creates a new graph\n then: op:createGraph derives a ViewModel from the chosen typename and writes the Graph to ECHO\n\n req F-1.3:\n when: Graph is persisted\n then: it appears in the space object list and can be opened as an article\n```\n\n```mdl\nfeat F-2: Live Graph Model\n\n req F-2.1:\n when: ExplorerArticle mounts with a valid Graph subject\n then: SpaceGraphModel is opened against the subject's database\n\n req F-2.2:\n when: user types a query in the toolbar\n then: op:buildFilter compiles the raw string; model.setFilter applied; SVG re-renders\n\n req F-2.3:\n when: Graph is unmounted\n then: SpaceGraphModel is closed and all subscriptions released\n\n req F-2.4:\n when: an ECHO object in the database changes\n then: SpaceGraphModel emits an update and SVG.Graph re-renders without full remount\n tags: [reactive, collaborative]\n```\n\n```mdl\nfeat F-3: Visualization Variants\n\n req F-3.1:\n when: variant tab is selected\n then: Visualization swaps projector; previous node x/y seed the new projector so positions tween\n\n req F-3.2:\n when: variant is force\n then: SVG.Zoom wrapper is mounted enabling pan/zoom drag interaction\n\n req F-3.3:\n when: variant is cluster or bundle\n then: no SVG.Zoom wrapper; leaves grouped by typename; radial labels fade in after tween\n\n req F-3.4:\n when: variant is lattice\n then: nodes rendered as small rounded rectangles sorted by typename then label\n\n req F-3.5:\n when: variant is cluster and user clicks a root or group node\n then: subtree toggles collapsed/expanded via GraphClusterProjector.toggleCollapsed\n```\n\n```mdl\nfeat F-4: Node Interaction\n\n req F-4.1:\n when: pointer enters a node\n then: DxAnchorActivate event dispatched with the object's DXN and label, kind: card\n\n req F-4.2:\n when: pointer leaves a node\n then: DxAnchorActivate cleared (onNodeHover called with null)\n\n req F-4.3:\n when: node has no underlying ECHO object (synthetic root / group)\n then: no preview dispatched; node rendered in neutral colour\n```\n\n## Acceptance\n\n```mdl\ntest T-1: Graph creation\n given: user opens the object creation dialog and selects Graph\n when: a typename is selected and the form submitted\n then:\n - op:createGraph runs without error\n - new Graph object visible in space object list\n - Graph.view references a valid View object\n```\n\n```mdl\ntest T-2: Article mounts model\n given: a Graph with a populated query\n when: ExplorerArticle is rendered with role === article\n then:\n - SpaceGraphModel is opened\n - SVG canvas renders nodes derived from the database\n - toolbar shows QueryEditor and variant tabs\n```\n\n```mdl\ntest T-3: Variant switch preserves positions\n given: force variant is active with nodes positioned\n when: user switches to cluster variant\n then:\n - projector is recreated from previous layout\n - nodes tween from old positions to cluster target positions\n - SVG.Zoom wrapper is absent in the new mount\n```\n\n```mdl\ntest T-4: Query filter updates graph\n given: ExplorerArticle is open with all objects visible\n when: user types a typename filter in QueryEditor\n then:\n - op:buildFilter returns a Filter without error\n - SpaceGraphModel.setFilter applied\n - only matching nodes remain visible in the SVG\n```\n\n```mdl\ntest T-5: Node hover preview\n given: force-directed graph with at least one ECHO object node\n when: pointer enters that node\n then:\n - DxAnchorActivate event fired with correct DXN\n - kind === card\n```\n\n```mdl\ntest T-6: Lattice sort order\n given: database contains objects of two typenames A and B\n when: lattice variant is active\n then:\n - all typename-A nodes appear before typename-B nodes (or vice-versa, consistent order)\n - within each group nodes are ordered alphabetically by label\n```\n\n```mdl\ntest T-7: Model closed on unmount\n given: ExplorerArticle is mounted and SpaceGraphModel is open\n when: component unmounts\n then:\n - SpaceGraphModel.close() is called\n - no lingering ECHO subscriptions remain\n```\n\n---\n\n## Appendix: Extension Definitions\n\nExtension block types used in this document are defined below using\nthe core `ext` primitive — the only construct the base language provides.\n\n```mdl\next type\n uri: org.dxos.mdl.type@1.0\n desc: A named data structure with typed fields and optional literals.\n fields:\n desc?: Prose\n fields?: FieldMap # name[?]: TypeExpr (# inline comment)\n literals?: UnionList # a | b | c\n extends?: TypeRef[]\n```\n\n```mdl\next feat\n uri: org.dxos.mdl.feat@1.0\n desc: A named feature grouping one or more requirements.\n fields:\n desc?: Prose\n req: RequirementList\n nesting: self # feat blocks may contain feat blocks\n```\n\n```mdl\next test\n uri: org.dxos.mdl.test@1.0\n desc: An acceptance scenario expressed as given / when / then steps.\n fields:\n given?: Step | Step[]\n when?: Step | Step[]\n then: Step | Step[]\n tags?: TagList\n```\n\n```mdl\next component\n uri: org.dxos.mdl.component@1.0\n desc: A UI component with props, internal state, slots, actions, and events.\n fields:\n desc?: Prose\n props?: FieldMap # external inputs (immutable inside component)\n state?: FieldMap # internal reactive state\n slots?: FieldMap # named ReactNode injection points\n actions?: ActionMap # methods the component exposes or handles\n emits?: EventMap # events the component raises to its parent\n layout?: CodeBlock # ASCII sketch of visual structure (non-normative)\n```\n\n```mdl\next op\n uri: org.dxos.mdl.op@1.0\n desc: |\n A named operation with typed inputs, outputs, and declared errors.\n Pure ops have no effects or requires. Effectful ops declare both.\n fields:\n desc?: Prose\n input?: FieldMap # named input parameters\n output?: TypeExpr # return type\n errors?: ErrorMap # name: Prose (when this error occurs)\n effects?: EffectList # echo:read | echo:write | http | fs | ...\n requires?: ServiceList # injected service dependencies\n note?: Prose # implementation guidance (non-normative)\n```\n",
13
13
  mimeType: "application/x-mdl"
14
- } }), Plugin.make);
14
+ })), Plugin.make);
15
15
  //#endregion
16
16
  export { ExplorerPlugin as t };
17
17
 
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-ExplorerPlugin.mjs","names":[],"sources":["../../src/ExplorerPlugin.tsx"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\nimport { Plugin } from '@dxos/app-framework';\nimport { AppPlugin } from '@dxos/app-toolkit';\n\nimport { AppGraphBuilder, CreateObject, ReactSurface } from '#capabilities';\nimport { meta } from '#meta';\nimport { translations } from '#translations';\nimport { Graph } from '#types';\n\n// eslint-disable-next-line import/no-relative-packages\nimport pluginSpec from '../PLUGIN.mdl?raw';\n\nexport const ExplorerPlugin = Plugin.define(meta).pipe(\n AppPlugin.addCreateObjectModule({ activate: CreateObject }),\n AppPlugin.addAppGraphModule({ activate: AppGraphBuilder }),\n AppPlugin.addSchemaModule({ schema: [Graph.Graph] }),\n AppPlugin.addSurfaceModule({ activate: ReactSurface }),\n AppPlugin.addTranslationsModule({ translations }),\n AppPlugin.addPluginAssetModule({\n asset: { pluginId: meta.profile.key, path: 'PLUGIN.mdl', content: pluginSpec, mimeType: 'application/x-mdl' },\n }),\n Plugin.make,\n);\n\nexport default ExplorerPlugin;\n"],"mappings":";;;;;;;;AAeA,IAAa,iBAAiB,OAAO,OAAO,IAAI,CAAC,CAAC,KAChD,UAAU,sBAAsB,EAAE,UAAU,aAAa,CAAC,GAC1D,UAAU,kBAAkB,EAAE,UAAU,gBAAgB,CAAC,GACzD,UAAU,gBAAgB,EAAE,QAAQ,CAAC,MAAM,KAAK,EAAE,CAAC,GACnD,UAAU,iBAAiB,EAAE,UAAU,aAAa,CAAC,GACrD,UAAU,sBAAsB,EAAE,aAAa,CAAC,GAChD,UAAU,qBAAqB,EAC7B,OAAO;CAAE,UAAU,KAAK,QAAQ;CAAK,MAAM;CAAc,SAAS;CAAY,UAAU;AAAoB,EAC9G,CAAC,GACD,OAAO,IACT"}
1
+ {"version":3,"file":"chunk-ExplorerPlugin.mjs","names":[],"sources":["../../src/ExplorerPlugin.tsx"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\nimport * as Plugin from '@dxos/app-framework/Plugin';\nimport * as AppCapability from '@dxos/app-toolkit/AppCapability';\n\nimport { AppGraphBuilder, CreateObject, ReactSurface } from '#capabilities';\nimport { meta } from '#meta';\nimport { translations } from '#translations';\n\n// eslint-disable-next-line import/no-relative-packages\nimport pluginSpec from '../PLUGIN.mdl?raw';\nimport * as Graph from './types/Graph';\n\nexport const ExplorerPlugin = Plugin.define(meta).pipe(\n Plugin.addModule(CreateObject),\n Plugin.addModule(AppGraphBuilder),\n Plugin.addModule(AppCapability.schema([Graph.Graph])),\n Plugin.addModule(ReactSurface),\n Plugin.addModule(AppCapability.translations(translations)),\n Plugin.addModule(\n AppCapability.pluginAsset({\n pluginId: meta.profile.key,\n path: 'PLUGIN.mdl',\n content: pluginSpec,\n mimeType: 'application/x-mdl',\n }),\n ),\n Plugin.make,\n);\n\nexport default ExplorerPlugin;\n"],"mappings":";;;;;;;;AAeA,IAAa,iBAAiB,OAAO,OAAO,IAAI,CAAC,CAAC,KAChD,OAAO,UAAU,YAAY,GAC7B,OAAO,UAAU,eAAe,GAChC,OAAO,UAAU,cAAc,OAAO,CAAC,KAAW,CAAC,CAAC,GACpD,OAAO,UAAU,YAAY,GAC7B,OAAO,UAAU,cAAc,aAAa,YAAY,CAAC,GACzD,OAAO,UACL,cAAc,YAAY;CACxB,UAAU,KAAK,QAAQ;CACvB,MAAM;CACN,SAAS;CACT,UAAU;AACZ,CAAC,CACH,GACA,OAAO,IACT"}
@@ -1,5 +1,5 @@
1
- import { Obj } from "@dxos/echo";
2
1
  import { meta } from "#meta";
2
+ import { Obj } from "@dxos/echo";
3
3
  import { useCallback, useState } from "react";
4
4
  import { jsx, jsxs } from "react/jsx-runtime";
5
5
  import { DxAnchorActivate, Panel, Toolbar, useTranslation } from "@dxos/react-ui";
@@ -1,8 +1,9 @@
1
- import { Capability } from "@dxos/app-framework";
2
1
  import { Position } from "@dxos/util";
3
- import { AppCapabilities, AppNode } from "@dxos/app-toolkit";
4
2
  import { meta } from "#meta";
5
3
  import * as Effect from "effect/Effect";
4
+ import * as Capability from "@dxos/app-framework/Capability";
5
+ import * as AppCapabilities from "@dxos/app-toolkit/AppCapabilities";
6
+ import * as AppNode from "@dxos/app-toolkit/AppNode";
6
7
  import { GraphBuilder, NodeMatcher } from "@dxos/plugin-graph";
7
8
  //#region src/capabilities/app-graph-builder.ts
8
9
  /**
@@ -21,7 +22,7 @@ var app_graph_builder_default = Capability.makeModule(Effect.fnUntraced(function
21
22
  position: Position.last
22
23
  })])
23
24
  })]);
24
- return Capability.contributes(AppCapabilities.AppGraphBuilder, extensions);
25
+ return Capability.contribute(AppCapabilities.AppGraphBuilder, extensions);
25
26
  }));
26
27
  //#endregion
27
28
  export { app_graph_builder_default as default };
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-app-graph-builder.mjs","names":[],"sources":["../../src/capabilities/app-graph-builder.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Capability } from '@dxos/app-framework';\nimport { AppCapabilities, AppNode } from '@dxos/app-toolkit';\nimport { GraphBuilder, NodeMatcher } from '@dxos/plugin-graph';\nimport { Position } from '@dxos/util';\n\nimport { meta } from '#meta';\n\n/**\n * Offers a \"Neighborhood\" companion on any ECHO object. The matched node is passed through as the\n * companion's `companionTo` (the active node), which the surface renders as an ego-centric graph.\n */\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n const extensions = yield* Effect.all([\n GraphBuilder.createExtension({\n id: 'neighborhoodCompanion',\n match: NodeMatcher.whenEchoObjectMatches,\n connector: () =>\n Effect.succeed([\n AppNode.makeCompanion({\n variant: 'neighborhood',\n label: ['neighborhood-companion.label', { ns: meta.profile.key }],\n icon: 'ph--share-network--regular',\n data: 'neighborhood',\n position: Position.last,\n }),\n ]),\n }),\n ]);\n\n return Capability.contributes(AppCapabilities.AppGraphBuilder, extensions);\n }),\n);\n"],"mappings":";;;;;;;;;;;AAiBA,IAAA,4BAAe,WAAW,WACxB,OAAO,WAAW,aAAa;CAC7B,MAAM,aAAa,OAAO,OAAO,IAAI,CACnC,aAAa,gBAAgB;EAC3B,IAAI;EACJ,OAAO,YAAY;EACnB,iBACE,OAAO,QAAQ,CACb,QAAQ,cAAc;GACpB,SAAS;GACT,OAAO,CAAC,gCAAgC,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC;GAChE,MAAM;GACN,MAAM;GACN,UAAU,SAAS;EACrB,CAAC,CACH,CAAC;CACL,CAAC,CACH,CAAC;CAED,OAAO,WAAW,YAAY,gBAAgB,iBAAiB,UAAU;AAC3E,CAAC,CACH"}
1
+ {"version":3,"file":"chunk-app-graph-builder.mjs","names":[],"sources":["../../src/capabilities/app-graph-builder.ts"],"sourcesContent":["//\n// Copyright 2026 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as AppCapabilities from '@dxos/app-toolkit/AppCapabilities';\nimport * as AppNode from '@dxos/app-toolkit/AppNode';\nimport { GraphBuilder, NodeMatcher } from '@dxos/plugin-graph';\nimport { Position } from '@dxos/util';\n\nimport { meta } from '#meta';\n\n/**\n * Offers a \"Neighborhood\" companion on any ECHO object. The matched node is passed through as the\n * companion's `companionTo` (the active node), which the surface renders as an ego-centric graph.\n */\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n const extensions = yield* Effect.all([\n GraphBuilder.createExtension({\n id: 'neighborhoodCompanion',\n match: NodeMatcher.whenEchoObjectMatches,\n connector: () =>\n Effect.succeed([\n AppNode.makeCompanion({\n variant: 'neighborhood',\n label: ['neighborhood-companion.label', { ns: meta.profile.key }],\n icon: 'ph--share-network--regular',\n data: 'neighborhood',\n position: Position.last,\n }),\n ]),\n }),\n ]);\n\n return Capability.contribute(AppCapabilities.AppGraphBuilder, extensions);\n }),\n);\n"],"mappings":";;;;;;;;;;;;AAkBA,IAAA,4BAAe,WAAW,WACxB,OAAO,WAAW,aAAa;CAC7B,MAAM,aAAa,OAAO,OAAO,IAAI,CACnC,aAAa,gBAAgB;EAC3B,IAAI;EACJ,OAAO,YAAY;EACnB,iBACE,OAAO,QAAQ,CACb,QAAQ,cAAc;GACpB,SAAS;GACT,OAAO,CAAC,gCAAgC,EAAE,IAAI,KAAK,QAAQ,IAAI,CAAC;GAChE,MAAM;GACN,MAAM;GACN,UAAU,SAAS;EACrB,CAAC,CACH,CAAC;CACL,CAAC,CACH,CAAC;CAED,OAAO,WAAW,WAAW,gBAAgB,iBAAiB,UAAU;AAC1E,CAAC,CACH"}
@@ -1,22 +1,24 @@
1
- import { Capability } from "@dxos/app-framework";
2
- import { SpaceCapabilities, SpaceOperation } from "@dxos/plugin-space";
1
+ import { GraphProps } from "./ExplorerAction.mjs";
2
+ import { Graph, make } from "./Graph.mjs";
3
3
  import { Type } from "@dxos/echo";
4
4
  import { ViewModel } from "@dxos/schema";
5
- import { ExplorerAction, Graph } from "#types";
6
5
  import * as Effect from "effect/Effect";
7
- import { Operation } from "@dxos/compute";
6
+ import * as Capability from "@dxos/app-framework/Capability";
7
+ import * as Operation from "@dxos/compute/Operation";
8
+ import { SpaceOperation } from "@dxos/plugin-space";
9
+ import * as SpaceCapabilities from "@dxos/plugin-space/SpaceCapabilities";
8
10
  //#region src/capabilities/create-object.ts
9
11
  var create_object_default = Capability.makeModule(Effect.fnUntraced(function* () {
10
- return Capability.contributes(SpaceCapabilities.CreateObjectEntry, {
11
- id: Type.getTypename(Graph.Graph),
12
- inputSchema: ExplorerAction.GraphProps,
12
+ return Capability.contribute(SpaceCapabilities.CreateObjectEntry, {
13
+ id: Type.getTypename(Graph),
14
+ inputSchema: GraphProps,
13
15
  createObject: (props, options) => Effect.gen(function* () {
14
16
  const object = yield* Effect.promise(async () => {
15
17
  const view = props.typename ? (await ViewModel.makeFromDatabase({
16
18
  db: options.db,
17
19
  typename: props.typename
18
20
  })).view : void 0;
19
- return Graph.make({
21
+ return make({
20
22
  name: props.name,
21
23
  view
22
24
  });
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-create-object.mjs","names":[],"sources":["../../src/capabilities/create-object.ts"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Capability } from '@dxos/app-framework';\nimport { Operation } from '@dxos/compute';\nimport { Type } from '@dxos/echo';\nimport { SpaceOperation } from '@dxos/plugin-space';\nimport { SpaceCapabilities } from '@dxos/plugin-space';\nimport { ViewModel } from '@dxos/schema';\n\nimport { ExplorerAction, Graph } from '#types';\n\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n return Capability.contributes(SpaceCapabilities.CreateObjectEntry, {\n id: Type.getTypename(Graph.Graph),\n inputSchema: ExplorerAction.GraphProps,\n createObject: (props, options) =>\n Effect.gen(function* () {\n const object = yield* Effect.promise(async () => {\n const view = props.typename\n ? (await ViewModel.makeFromDatabase({ db: options.db, typename: props.typename })).view\n : undefined;\n return Graph.make({ name: props.name, view });\n });\n return yield* Operation.invoke(SpaceOperation.AddObject, {\n object,\n target: options.target,\n targetNodeId: options.targetNodeId,\n });\n }),\n });\n }),\n);\n"],"mappings":";;;;;;;;AAeA,IAAA,wBAAe,WAAW,WACxB,OAAO,WAAW,aAAa;CAC7B,OAAO,WAAW,YAAY,kBAAkB,mBAAmB;EACjE,IAAI,KAAK,YAAY,MAAM,KAAK;EAChC,aAAa,eAAe;EAC5B,eAAe,OAAO,YACpB,OAAO,IAAI,aAAa;GACtB,MAAM,SAAS,OAAO,OAAO,QAAQ,YAAY;IAC/C,MAAM,OAAO,MAAM,YACd,MAAM,UAAU,iBAAiB;KAAE,IAAI,QAAQ;KAAI,UAAU,MAAM;IAAS,CAAC,EAAA,CAAG,OACjF,KAAA;IACJ,OAAO,MAAM,KAAK;KAAE,MAAM,MAAM;KAAM;IAAK,CAAC;GAC9C,CAAC;GACD,OAAO,OAAO,UAAU,OAAO,eAAe,WAAW;IACvD;IACA,QAAQ,QAAQ;IAChB,cAAc,QAAQ;GACxB,CAAC;EACH,CAAC;CACL,CAAC;AACH,CAAC,CACH"}
1
+ {"version":3,"file":"chunk-create-object.mjs","names":[],"sources":["../../src/capabilities/create-object.ts"],"sourcesContent":["//\n// Copyright 2023 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capability from '@dxos/app-framework/Capability';\nimport * as Operation from '@dxos/compute/Operation';\nimport { Type } from '@dxos/echo';\nimport { SpaceOperation } from '@dxos/plugin-space';\nimport * as SpaceCapabilities from '@dxos/plugin-space/SpaceCapabilities';\nimport { ViewModel } from '@dxos/schema';\n\nimport * as ExplorerAction from '../types/ExplorerAction';\nimport * as Graph from '../types/Graph';\n\nexport default Capability.makeModule(\n Effect.fnUntraced(function* () {\n return Capability.contribute(SpaceCapabilities.CreateObjectEntry, {\n id: Type.getTypename(Graph.Graph),\n inputSchema: ExplorerAction.GraphProps,\n createObject: (props, options) =>\n Effect.gen(function* () {\n const object = yield* Effect.promise(async () => {\n const view = props.typename\n ? (await ViewModel.makeFromDatabase({ db: options.db, typename: props.typename })).view\n : undefined;\n return Graph.make({ name: props.name, view });\n });\n return yield* Operation.invoke(SpaceOperation.AddObject, {\n object,\n target: options.target,\n targetNodeId: options.targetNodeId,\n });\n }),\n });\n }),\n);\n"],"mappings":";;;;;;;;;;AAgBA,IAAA,wBAAe,WAAW,WACxB,OAAO,WAAW,aAAa;CAC7B,OAAO,WAAW,WAAW,kBAAkB,mBAAmB;EAChE,IAAI,KAAK,YAAY,KAAW;EAChC,aAAa;EACb,eAAe,OAAO,YACpB,OAAO,IAAI,aAAa;GACtB,MAAM,SAAS,OAAO,OAAO,QAAQ,YAAY;IAC/C,MAAM,OAAO,MAAM,YACd,MAAM,UAAU,iBAAiB;KAAE,IAAI,QAAQ;KAAI,UAAU,MAAM;IAAS,CAAC,EAAA,CAAG,OACjF,KAAA;IACJ,OAAO,KAAW;KAAE,MAAM,MAAM;KAAM;IAAK,CAAC;GAC9C,CAAC;GACD,OAAO,OAAO,UAAU,OAAO,eAAe,WAAW;IACvD;IACA,QAAQ,QAAQ;IAChB,cAAc,QAAQ;GACxB,CAAC;EACH,CAAC;CACL,CAAC;AACH,CAAC,CACH"}
@@ -1,4 +1,4 @@
1
- import { Plugin } from "@dxos/app-framework";
1
+ import * as Plugin from "@dxos/app-framework/Plugin";
2
2
  import { Config2 } from "@dxos/app-framework/config";
3
3
  import { trim } from "@dxos/util";
4
4
  //#region dx.config.ts
@@ -33,7 +33,7 @@ var dx_config_default = Config2.make({ plugin: {
33
33
  hue: "green"
34
34
  },
35
35
  spec: "PLUGIN.mdl",
36
- screenshots: [{ dark: "https://dxos.network/plugin-details-explorer-dark.png" }],
36
+ screenshots: [{ dark: "https://media.dxos.network/plugin-details-explorer-dark.png" }],
37
37
  tags: ["alpha"]
38
38
  } });
39
39
  //#endregion
@@ -1 +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.explorer',\n name: 'Explorer',\n author: 'DXOS',\n description: trim`\n Explorer is an interactive hypergraph visualization plugin that reveals the relationships\n between objects stored in your DXOS workspace. Each Graph document is backed by a live\n ECHO query and a View that you configure — Explorer keeps the visualization synchronized\n with the database in real time so every peer immediately sees changes made by collaborators.\n\n The plugin offers four switchable layout algorithms for the same data set: a physics-based\n force-directed graph for freeform exploration, a radial cluster layout that groups objects\n by type around a central root, an edge-bundling layout that tames visual clutter on dense\n graphs, and a lattice grid that arranges objects in a sorted matrix sorted by type and label.\n Switching between layouts smoothly tweens node positions so spatial context is preserved.\n\n Nodes are coloured by their ECHO object type, and hovering any node opens an anchor-card\n preview panel with the object's details without leaving the graph view.\n The toolbar's query editor lets you filter the visible node set on the fly using\n the same query syntax available across Composer.\n\n A new Graph document can be created from the object-creation dialog; Explorer automatically\n derives an initial View from the chosen type and persists both to ECHO so the graph is\n immediately shareable and replicable across the space.\n `,\n source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-explorer',\n icon: { key: 'ph--graph--regular', hue: 'green' },\n spec: 'PLUGIN.mdl',\n screenshots: [{ dark: 'https://dxos.network/plugin-details-explorer-dark.png' }],\n tags: ['alpha'],\n },\n});\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport { Plugin } from '@dxos/app-framework';\n\nimport config from '../dx.config';\n\nexport const meta = Plugin.getMetaFromConfig(config);\n"],"mappings":";;;;AAOA,IAAA,oBAAe,QAAQ,KAAK,EAC1B,QAAQ;CACN,KAAK;CACL,MAAM;CACN,QAAQ;CACR,aAAa,IAAI;;;;;;;;;;;;;;;;;;;;;CAqBjB,QAAQ;CACR,MAAM;EAAE,KAAK;EAAsB,KAAK;CAAQ;CAChD,MAAM;CACN,aAAa,CAAC,EAAE,MAAM,wDAAwD,CAAC;CAC/E,MAAM,CAAC,OAAO;AAChB,EACF,CAAC;;;AC/BD,IAAa,OAAO,OAAO,kBAAkB,iBAAM"}
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.explorer',\n name: 'Explorer',\n author: 'DXOS',\n description: trim`\n Explorer is an interactive hypergraph visualization plugin that reveals the relationships\n between objects stored in your DXOS workspace. Each Graph document is backed by a live\n ECHO query and a View that you configure — Explorer keeps the visualization synchronized\n with the database in real time so every peer immediately sees changes made by collaborators.\n\n The plugin offers four switchable layout algorithms for the same data set: a physics-based\n force-directed graph for freeform exploration, a radial cluster layout that groups objects\n by type around a central root, an edge-bundling layout that tames visual clutter on dense\n graphs, and a lattice grid that arranges objects in a sorted matrix sorted by type and label.\n Switching between layouts smoothly tweens node positions so spatial context is preserved.\n\n Nodes are coloured by their ECHO object type, and hovering any node opens an anchor-card\n preview panel with the object's details without leaving the graph view.\n The toolbar's query editor lets you filter the visible node set on the fly using\n the same query syntax available across Composer.\n\n A new Graph document can be created from the object-creation dialog; Explorer automatically\n derives an initial View from the chosen type and persists both to ECHO so the graph is\n immediately shareable and replicable across the space.\n `,\n source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-explorer',\n icon: { key: 'ph--graph--regular', hue: 'green' },\n spec: 'PLUGIN.mdl',\n screenshots: [{ dark: 'https://media.dxos.network/plugin-details-explorer-dark.png' }],\n tags: ['alpha'],\n },\n});\n","//\n// Copyright 2023 DXOS.org\n//\n\nimport * as Plugin from '@dxos/app-framework/Plugin';\n\nimport config from '../dx.config';\n\nexport const meta = Plugin.getMetaFromConfig(config);\n"],"mappings":";;;;AAOA,IAAA,oBAAe,QAAQ,KAAK,EAC1B,QAAQ;CACN,KAAK;CACL,MAAM;CACN,QAAQ;CACR,aAAa,IAAI;;;;;;;;;;;;;;;;;;;;;CAqBjB,QAAQ;CACR,MAAM;EAAE,KAAK;EAAsB,KAAK;CAAQ;CAChD,MAAM;CACN,aAAa,CAAC,EAAE,MAAM,8DAA8D,CAAC;CACrF,MAAM,CAAC,OAAO;AAChB,EACF,CAAC;;;AC/BD,IAAa,OAAO,OAAO,kBAAkB,iBAAM"}
@@ -1,13 +1,14 @@
1
- import { Capabilities, Capability } from "@dxos/app-framework";
2
- import { Graph } from "#types";
1
+ import { Graph } from "./Graph.mjs";
2
+ import * as Capabilities from "@dxos/app-framework/Capabilities";
3
3
  import { Surface } from "@dxos/app-framework/ui";
4
4
  import * as Effect from "effect/Effect";
5
+ import * as Capability from "@dxos/app-framework/Capability";
5
6
  import { AppSurface } from "@dxos/app-toolkit/ui";
6
7
  import { ExplorerArticle, NeighborhoodCompanion } from "#containers";
7
8
  //#region src/capabilities/react-surface.ts
8
- var react_surface_default = Capability.makeModule(() => Effect.succeed(Capability.contributes(Capabilities.ReactSurface, [Surface.create({
9
+ var react_surface_default = Capability.makeModule(() => Effect.succeed(Capability.contribute(Capabilities.ReactSurface, [Surface.create({
9
10
  id: "article",
10
- filter: AppSurface.oneOf(AppSurface.object(AppSurface.Article, Graph.Graph), AppSurface.object(AppSurface.Section, Graph.Graph)),
11
+ filter: AppSurface.oneOf(AppSurface.object(AppSurface.Article, Graph), AppSurface.object(AppSurface.Section, Graph)),
11
12
  component: ExplorerArticle,
12
13
  props: ({ role, data: { subject, attendableId } }) => ({
13
14
  role,
@@ -1 +1 @@
1
- {"version":3,"file":"chunk-react-surface.mjs","names":[],"sources":["../../src/capabilities/react-surface.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport { Capabilities, Capability } from '@dxos/app-framework';\nimport { Surface } from '@dxos/app-framework/ui';\nimport { AppSurface } from '@dxos/app-toolkit/ui';\n\nimport { ExplorerArticle, NeighborhoodCompanion } from '#containers';\nimport { Graph } from '#types';\n\nexport default Capability.makeModule(() =>\n Effect.succeed(\n Capability.contributes(Capabilities.ReactSurface, [\n Surface.create({\n id: 'article',\n // TODO(wittjosiah): Split into multiple surfaces if this filter proves too strict for non-article roles.\n filter: AppSurface.oneOf(\n AppSurface.object(AppSurface.Article, Graph.Graph),\n AppSurface.object(AppSurface.Section, Graph.Graph),\n ),\n component: ExplorerArticle,\n props: ({ role, data: { subject, attendableId } }) => ({ role, subject, attendableId }),\n }),\n // Neighborhood companion offered on any ECHO object; `companionTo` is the active node.\n Surface.create({\n id: 'neighborhood',\n filter: AppSurface.allOf(\n AppSurface.literal(AppSurface.Article, 'neighborhood'),\n AppSurface.companion(AppSurface.Article),\n ),\n component: NeighborhoodCompanion,\n props: ({ role, data: { companionTo } }) => ({ role, subject: companionTo }),\n }),\n ]),\n ),\n);\n"],"mappings":";;;;;;;AAaA,IAAA,wBAAe,WAAW,iBACxB,OAAO,QACL,WAAW,YAAY,aAAa,cAAc,CAChD,QAAQ,OAAO;CACb,IAAI;CAEJ,QAAQ,WAAW,MACjB,WAAW,OAAO,WAAW,SAAS,MAAM,KAAK,GACjD,WAAW,OAAO,WAAW,SAAS,MAAM,KAAK,CACnD;CACA,WAAW;CACX,QAAQ,EAAE,MAAM,MAAM,EAAE,SAAS,sBAAsB;EAAE;EAAM;EAAS;CAAa;AACvF,CAAC,GAED,QAAQ,OAAO;CACb,IAAI;CACJ,QAAQ,WAAW,MACjB,WAAW,QAAQ,WAAW,SAAS,cAAc,GACrD,WAAW,UAAU,WAAW,OAAO,CACzC;CACA,WAAW;CACX,QAAQ,EAAE,MAAM,MAAM,EAAE,qBAAqB;EAAE;EAAM,SAAS;CAAY;AAC5E,CAAC,CACH,CAAC,CACH,CACF"}
1
+ {"version":3,"file":"chunk-react-surface.mjs","names":[],"sources":["../../src/capabilities/react-surface.ts"],"sourcesContent":["//\n// Copyright 2025 DXOS.org\n//\n\nimport * as Effect from 'effect/Effect';\n\nimport * as Capabilities from '@dxos/app-framework/Capabilities';\nimport * as Capability from '@dxos/app-framework/Capability';\nimport { Surface } from '@dxos/app-framework/ui';\nimport { AppSurface } from '@dxos/app-toolkit/ui';\n\nimport { ExplorerArticle, NeighborhoodCompanion } from '#containers';\n\nimport * as Graph from '../types/Graph';\n\nexport default Capability.makeModule(() =>\n Effect.succeed(\n Capability.contribute(Capabilities.ReactSurface, [\n Surface.create({\n id: 'article',\n // TODO(wittjosiah): Split into multiple surfaces if this filter proves too strict for non-article roles.\n filter: AppSurface.oneOf(\n AppSurface.object(AppSurface.Article, Graph.Graph),\n AppSurface.object(AppSurface.Section, Graph.Graph),\n ),\n component: ExplorerArticle,\n props: ({ role, data: { subject, attendableId } }) => ({ role, subject, attendableId }),\n }),\n // Neighborhood companion offered on any ECHO object; `companionTo` is the active node.\n Surface.create({\n id: 'neighborhood',\n filter: AppSurface.allOf(\n AppSurface.literal(AppSurface.Article, 'neighborhood'),\n AppSurface.companion(AppSurface.Article),\n ),\n component: NeighborhoodCompanion,\n props: ({ role, data: { companionTo } }) => ({ role, subject: companionTo }),\n }),\n ]),\n ),\n);\n"],"mappings":";;;;;;;;AAeA,IAAA,wBAAe,WAAW,iBACxB,OAAO,QACL,WAAW,WAAW,aAAa,cAAc,CAC/C,QAAQ,OAAO;CACb,IAAI;CAEJ,QAAQ,WAAW,MACjB,WAAW,OAAO,WAAW,SAAS,KAAW,GACjD,WAAW,OAAO,WAAW,SAAS,KAAW,CACnD;CACA,WAAW;CACX,QAAQ,EAAE,MAAM,MAAM,EAAE,SAAS,sBAAsB;EAAE;EAAM;EAAS;CAAa;AACvF,CAAC,GAED,QAAQ,OAAO;CACb,IAAI;CACJ,QAAQ,WAAW,MACjB,WAAW,QAAQ,WAAW,SAAS,cAAc,GACrD,WAAW,UAAU,WAAW,OAAO,CACzC;CACA,WAAW;CACX,QAAQ,EAAE,MAAM,MAAM,EAAE,qBAAqB;EAAE;EAAM,SAAS;CAAY;AAC5E,CAAC,CACH,CAAC,CACH,CACF"}
@@ -0,0 +1,13 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __defProp = Object.defineProperty;
3
+ var __exportAll = (all, no_symbols) => {
4
+ let target = {};
5
+ for (var name in all) __defProp(target, name, {
6
+ get: all[name],
7
+ enumerable: true
8
+ });
9
+ if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
10
+ return target;
11
+ };
12
+ //#endregion
13
+ export { __exportAll as t };
@@ -1,60 +1,231 @@
1
1
  import * as Schema from "effect/Schema";
2
- import { TypeInputOptionsAnnotation } from "@dxos/plugin-space";
3
- import { Annotation, DXN, Filter, Obj, Query, QueryAST, Ref, Type, View } from "@dxos/echo";
4
- import { FormInputAnnotation, LabelAnnotation } from "@dxos/echo/Annotation";
5
- import { ViewAnnotation } from "@dxos/schema";
6
- //#region \0rolldown/runtime.js
7
- var __defProp = Object.defineProperty;
8
- var __exportAll = (all, no_symbols) => {
9
- let target = {};
10
- for (var name in all) __defProp(target, name, {
11
- get: all[name],
12
- enumerable: true
13
- });
14
- if (!no_symbols) __defProp(target, Symbol.toStringTag, { value: "Module" });
15
- return target;
16
- };
17
- //#endregion
18
- //#region src/types/ExplorerAction.ts
19
- var ExplorerAction_exports = /* @__PURE__ */ __exportAll({ GraphProps: () => GraphProps });
20
- var GraphProps = Schema.Struct({
21
- name: Schema.optional(Schema.String),
22
- typename: Schema.String.pipe(Schema.annotations({ title: "Select type" }), TypeInputOptionsAnnotation.set({
23
- location: ["database", "runtime"],
24
- kind: ["user"]
25
- }), Schema.optional)
26
- });
27
- //#endregion
28
- //#region src/types/Graph.ts
29
- var Graph_exports = /* @__PURE__ */ __exportAll({
30
- Graph: () => Graph,
31
- make: () => make
32
- });
33
- var Graph = class extends Type.makeObject(DXN.make("org.dxos.type.graph", "0.1.0"))(Schema.Struct({
34
- name: Schema.optional(Schema.String),
35
- view: Ref.Ref(View.View).pipe(FormInputAnnotation.set(false), Schema.optional),
36
- query: Schema.Struct({
37
- raw: Schema.optional(Schema.String),
38
- ast: QueryAST.Query
39
- }).pipe(FormInputAnnotation.set(false))
40
- }).pipe(LabelAnnotation.set(["name"]), ViewAnnotation.set(["view"]), Annotation.IconAnnotation.set({
41
- icon: "ph--graph--regular",
42
- hue: "green"
43
- }))) {};
2
+ import { DXN, Key, Obj, Ref, Type } from "@dxos/echo";
3
+ import { TestSchema } from "@dxos/echo/testing";
4
+ import { invariant } from "@dxos/invariant";
5
+ //#region src/components/Tree/types/tree.ts
6
+ var __dxlog_file = "/__w/dxos/dxos/packages/plugins/plugin-explorer/src/components/Tree/types/tree.ts";
7
+ var TreeNodeType = Schema.Struct({
8
+ id: Key.EntityId,
9
+ children: Schema.mutable(Schema.Array(Key.EntityId)),
10
+ data: Schema.mutable(Schema.Record({
11
+ key: Schema.String,
12
+ value: Schema.Any
13
+ })),
14
+ ref: Schema.optional(Ref.Ref(TestSchema.Expando))
15
+ }).pipe(Schema.mutable);
16
+ var TreeType = class extends Type.makeObject(DXN.make("org.dxos.type.tree", "0.1.0"))(Schema.Struct({
17
+ root: Key.EntityId,
18
+ nodes: Schema.mutable(Schema.Record({
19
+ key: Key.EntityId,
20
+ value: TreeNodeType
21
+ }))
22
+ })) {};
44
23
  /**
45
- * Make a graph as a view of a data set.
24
+ * Wrapper object for tree.
46
25
  */
47
- var make = ({ name, query = {
48
- raw: "",
49
- ast: Query.select(Filter.nothing()).ast
50
- }, view } = {}) => {
51
- return Obj.make(Graph, {
52
- name,
53
- view: view && Ref.make(view),
54
- query
55
- });
26
+ var Tree = class Tree {
27
+ static create = () => {
28
+ const id = Key.EntityId.random();
29
+ return Obj.make(TreeType, {
30
+ root: id,
31
+ nodes: { [id]: {
32
+ id,
33
+ children: [],
34
+ data: { text: "" }
35
+ } }
36
+ });
37
+ };
38
+ _tree;
39
+ constructor(tree) {
40
+ this._tree = tree ?? Tree.create();
41
+ }
42
+ get tree() {
43
+ return this._tree;
44
+ }
45
+ get size() {
46
+ return Object.keys(this._tree.nodes).length;
47
+ }
48
+ get root() {
49
+ return this.getNode(this._tree.root);
50
+ }
51
+ /**
52
+ * Recursively traverse the tree until the callback returns a value.
53
+ */
54
+ tranverse(callback, root = this._tree.root, depth = 0) {
55
+ const node = this._tree.nodes[root];
56
+ const result = callback(node, depth);
57
+ if (result !== void 0) return result;
58
+ for (const childId of node.children) {
59
+ const result = this.tranverse(callback, childId, depth + 1);
60
+ if (result !== void 0) return result;
61
+ }
62
+ }
63
+ getNode(id) {
64
+ const node = this._tree.nodes[id];
65
+ invariant(node, void 0, {
66
+ "~LogMeta": "~LogMeta",
67
+ F: __dxlog_file,
68
+ L: 94,
69
+ S: this,
70
+ A: ["node", ""]
71
+ });
72
+ return node;
73
+ }
74
+ /**
75
+ * Get the children of a node.
76
+ */
77
+ getChildNodes(node) {
78
+ return node.children.map((id) => this.getNode(id));
79
+ }
80
+ /**
81
+ * Get the parent of a node.
82
+ */
83
+ getParent(node) {
84
+ return this.tranverse((n) => {
85
+ if (n.children.includes(node.id)) return n;
86
+ }) ?? null;
87
+ }
88
+ /**
89
+ * Get the next node in the tree.
90
+ */
91
+ getNext(node, hierarchical = true) {
92
+ if (hierarchical && node.children.length) return this.getChildNodes(node)[0];
93
+ else {
94
+ const parent = this.getParent(node);
95
+ if (parent) {
96
+ const idx = this.getChildNodes(parent).findIndex(({ id }) => id === node.id);
97
+ if (idx < parent.children.length - 1) return this.getNode(parent.children[idx + 1]);
98
+ else return this.getNext(parent, false);
99
+ }
100
+ }
101
+ }
102
+ /**
103
+ * Get the previous node in the tree.
104
+ */
105
+ getPrevious(node, hierarchical = true) {
106
+ const parent = this.getParent(node);
107
+ const idx = this.getChildNodes(parent).findIndex(({ id }) => id === node.id);
108
+ if (idx === 0) {
109
+ if (hierarchical) return parent;
110
+ } else {
111
+ const previous = this.getNode(parent.children[idx - 1]);
112
+ if (hierarchical && previous.children.length) return this.getLastDescendent(previous);
113
+ return previous;
114
+ }
115
+ }
116
+ /**
117
+ * Get the last descendent of a node.
118
+ */
119
+ getLastDescendent(node) {
120
+ const children = this.getChildNodes(node);
121
+ const last = children.length ? children[children.length - 1] : void 0;
122
+ if (last) return this.getLastDescendent(last);
123
+ return node;
124
+ }
125
+ /**
126
+ * Clear tree.
127
+ */
128
+ clear() {
129
+ const root = this._tree.nodes[this._tree.root];
130
+ root.children.length = 0;
131
+ Obj.update(this._tree, (obj) => {
132
+ obj.nodes = { [root.id]: root };
133
+ });
134
+ }
135
+ /**
136
+ * Add node.
137
+ */
138
+ addNode(parent, node, index) {
139
+ if (!node) node = {
140
+ id: Key.EntityId.random(),
141
+ children: [],
142
+ data: { text: "" }
143
+ };
144
+ const nodeToAdd = node;
145
+ Obj.update(this._tree, (obj) => {
146
+ obj.nodes[nodeToAdd.id] = nodeToAdd;
147
+ parent.children.splice(index ?? parent.children.length, 0, nodeToAdd.id);
148
+ });
149
+ return node;
150
+ }
151
+ /**
152
+ * Delete node.
153
+ */
154
+ deleteNode(parent, id) {
155
+ const node = this._tree.nodes[id];
156
+ if (!node) return;
157
+ Obj.update(this._tree, (obj) => {
158
+ delete obj.nodes[node.id];
159
+ });
160
+ const idx = parent.children.findIndex((child) => child === id);
161
+ if (idx !== -1) Obj.update(this._tree, () => {
162
+ parent.children.splice(idx, 1);
163
+ });
164
+ return node;
165
+ }
166
+ /**
167
+ * Move child node.
168
+ */
169
+ moveNode(node, from, to) {
170
+ invariant(from >= 0 && from < node.children.length, void 0, {
171
+ "~LogMeta": "~LogMeta",
172
+ F: __dxlog_file,
173
+ L: 233,
174
+ S: this,
175
+ A: ["from >= 0 && from < node.children.length", ""]
176
+ });
177
+ invariant(to >= 0 && to < node.children.length, void 0, {
178
+ "~LogMeta": "~LogMeta",
179
+ F: __dxlog_file,
180
+ L: 234,
181
+ S: this,
182
+ A: ["to >= 0 && to < node.children.length", ""]
183
+ });
184
+ if (from === to) return null;
185
+ const child = node.children[from];
186
+ Obj.update(this._tree, () => {
187
+ node.children.splice(from, 1);
188
+ node.children.splice(to, 0, child);
189
+ });
190
+ return this.getNode(child);
191
+ }
192
+ /**
193
+ * Indent node.
194
+ */
195
+ indentNode(node) {
196
+ const parent = this.getParent(node);
197
+ if (!parent) return;
198
+ const idx = parent.children.findIndex((child) => child === node.id);
199
+ if (idx < 1 || idx >= parent.children.length) return;
200
+ const previous = this.getNode(parent.children[idx - 1]);
201
+ Obj.update(this._tree, () => {
202
+ parent.children.splice(idx, 1);
203
+ previous.children.push(node.id);
204
+ });
205
+ }
206
+ /**
207
+ * Unindent node.
208
+ */
209
+ unindentNode(node) {
210
+ const parent = this.getParent(node);
211
+ if (!parent) return;
212
+ const ancestor = this.getParent(parent);
213
+ if (!ancestor) return;
214
+ const nodeIdx = parent.children.findIndex((id) => id === node.id);
215
+ let rest = [];
216
+ Obj.update(this._tree, () => {
217
+ rest = parent.children.splice(nodeIdx, parent.children.length - nodeIdx).slice(1);
218
+ });
219
+ const parentIdx = this.getChildNodes(ancestor).findIndex((n) => n.id === parent.id);
220
+ Obj.update(this._tree, () => {
221
+ ancestor.children.splice(parentIdx + 1, 0, node.id);
222
+ });
223
+ Obj.update(this._tree, () => {
224
+ node.children.push(...rest);
225
+ });
226
+ }
56
227
  };
57
228
  //#endregion
58
- export { ExplorerAction_exports as n, Graph_exports as t };
229
+ export { TreeNodeType as n, TreeType as r, Tree as t };
59
230
 
60
231
  //# sourceMappingURL=chunk-types.mjs.map