@dxos/plugin-pipeline 0.8.4-main.fcfe5033a5 → 0.9.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.
Files changed (113) hide show
  1. package/LICENSE +102 -5
  2. package/PLUGIN.mdl +345 -0
  3. package/README.md +1 -1
  4. package/dist/lib/neutral/PipelineArticle-6JIVXMYF.mjs +69 -0
  5. package/dist/lib/neutral/PipelineArticle-6JIVXMYF.mjs.map +7 -0
  6. package/dist/lib/neutral/PipelinePlugin.mjs +38 -0
  7. package/dist/lib/neutral/PipelinePlugin.mjs.map +7 -0
  8. package/dist/lib/neutral/PipelinePlugin.node.mjs +21 -0
  9. package/dist/lib/neutral/PipelinePlugin.node.mjs.map +7 -0
  10. package/dist/lib/neutral/PipelinePlugin.workerd.mjs +16 -0
  11. package/dist/lib/neutral/PipelinePlugin.workerd.mjs.map +7 -0
  12. package/dist/lib/neutral/PipelineProperties-JZQYG2CN.mjs +209 -0
  13. package/dist/lib/neutral/PipelineProperties-JZQYG2CN.mjs.map +7 -0
  14. package/dist/lib/neutral/app-graph-builder-ZLGRWRJU.mjs +44 -0
  15. package/dist/lib/neutral/app-graph-builder-ZLGRWRJU.mjs.map +7 -0
  16. package/dist/lib/neutral/capabilities/index.mjs +11 -0
  17. package/dist/lib/neutral/capabilities/index.mjs.map +7 -0
  18. package/dist/lib/neutral/chunk-CYXDAAKN.mjs +35 -0
  19. package/dist/lib/neutral/chunk-CYXDAAKN.mjs.map +7 -0
  20. package/dist/lib/neutral/components/index.mjs +203 -0
  21. package/dist/lib/neutral/components/index.mjs.map +7 -0
  22. package/dist/lib/neutral/containers/index.mjs +9 -0
  23. package/dist/lib/neutral/containers/index.mjs.map +7 -0
  24. package/dist/lib/neutral/create-object-6Z4MMGX4.mjs +26 -0
  25. package/dist/lib/neutral/create-object-6Z4MMGX4.mjs.map +7 -0
  26. package/dist/lib/neutral/hooks/index.mjs +69 -0
  27. package/dist/lib/neutral/hooks/index.mjs.map +7 -0
  28. package/dist/lib/neutral/index.mjs +7 -0
  29. package/dist/lib/neutral/meta.json +1 -0
  30. package/dist/lib/neutral/meta.mjs +7 -0
  31. package/dist/lib/neutral/meta.mjs.map +7 -0
  32. package/dist/lib/neutral/plugin.mjs +11 -0
  33. package/dist/lib/neutral/plugin.mjs.map +7 -0
  34. package/dist/lib/neutral/react-surface-QG7QSZSS.mjs +49 -0
  35. package/dist/lib/neutral/react-surface-QG7QSZSS.mjs.map +7 -0
  36. package/dist/lib/neutral/translations.mjs +39 -0
  37. package/dist/lib/neutral/translations.mjs.map +7 -0
  38. package/dist/lib/neutral/types/index.mjs.map +7 -0
  39. package/dist/types/src/PipelinePlugin.d.ts +1 -0
  40. package/dist/types/src/PipelinePlugin.d.ts.map +1 -1
  41. package/dist/types/src/PipelinePlugin.node.d.ts +4 -0
  42. package/dist/types/src/PipelinePlugin.node.d.ts.map +1 -0
  43. package/dist/types/src/PipelinePlugin.test.d.ts +2 -0
  44. package/dist/types/src/PipelinePlugin.test.d.ts.map +1 -0
  45. package/dist/types/src/PipelinePlugin.workerd.d.ts +4 -0
  46. package/dist/types/src/PipelinePlugin.workerd.d.ts.map +1 -0
  47. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  48. package/dist/types/src/capabilities/create-object.d.ts +11 -0
  49. package/dist/types/src/capabilities/create-object.d.ts.map +1 -0
  50. package/dist/types/src/capabilities/index.d.ts +6 -0
  51. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  52. package/dist/types/src/capabilities/react-surface.d.ts.map +1 -1
  53. package/dist/types/src/components/PipelineComponent/PipelineColumn.d.ts +4 -4
  54. package/dist/types/src/components/PipelineComponent/PipelineColumn.d.ts.map +1 -1
  55. package/dist/types/src/components/PipelineComponent/PipelineComponent.d.ts +4 -0
  56. package/dist/types/src/components/PipelineComponent/PipelineComponent.d.ts.map +1 -1
  57. package/dist/types/src/components/PipelineComponent/PipelineComponent.stories.d.ts +44 -25
  58. package/dist/types/src/components/PipelineComponent/PipelineComponent.stories.d.ts.map +1 -1
  59. package/dist/types/src/containers/PipelineArticle/PipelineArticle.d.ts +6 -0
  60. package/dist/types/src/containers/PipelineArticle/PipelineArticle.d.ts.map +1 -0
  61. package/dist/types/src/containers/PipelineArticle/PipelineArticle.stories.d.ts +76 -0
  62. package/dist/types/src/containers/PipelineArticle/PipelineArticle.stories.d.ts.map +1 -0
  63. package/dist/types/src/containers/PipelineArticle/index.d.ts +2 -0
  64. package/dist/types/src/containers/PipelineArticle/index.d.ts.map +1 -0
  65. package/dist/types/src/containers/PipelineProperties/PipelineProperties.d.ts.map +1 -1
  66. package/dist/types/src/containers/index.d.ts +1 -1
  67. package/dist/types/src/containers/index.d.ts.map +1 -1
  68. package/dist/types/src/hooks/usePipelineBoardModel.d.ts.map +1 -1
  69. package/dist/types/src/index.d.ts +0 -1
  70. package/dist/types/src/index.d.ts.map +1 -1
  71. package/dist/types/src/meta.d.ts +1 -1
  72. package/dist/types/src/meta.d.ts.map +1 -1
  73. package/dist/types/src/plugin.d.ts +3 -0
  74. package/dist/types/src/plugin.d.ts.map +1 -0
  75. package/dist/types/src/templates/research.d.ts.map +1 -1
  76. package/dist/types/src/translations.d.ts +44 -26
  77. package/dist/types/src/translations.d.ts.map +1 -1
  78. package/dist/types/tsconfig.tsbuildinfo +1 -1
  79. package/package.json +100 -64
  80. package/src/PipelinePlugin.node.ts +19 -0
  81. package/src/PipelinePlugin.test.ts +27 -0
  82. package/src/PipelinePlugin.tsx +10 -29
  83. package/src/PipelinePlugin.workerd.ts +16 -0
  84. package/src/capabilities/create-object.ts +30 -0
  85. package/src/capabilities/index.ts +1 -0
  86. package/src/capabilities/react-surface.tsx +3 -3
  87. package/src/components/PipelineComponent/PipelineColumn.tsx +23 -33
  88. package/src/components/PipelineComponent/PipelineComponent.stories.tsx +6 -8
  89. package/src/components/PipelineComponent/PipelineComponent.tsx +2 -2
  90. package/src/containers/{PipelineContainer/PipelineContainer.stories.tsx → PipelineArticle/PipelineArticle.stories.tsx} +18 -22
  91. package/src/containers/{PipelineContainer/PipelineContainer.tsx → PipelineArticle/PipelineArticle.tsx} +6 -8
  92. package/src/containers/PipelineArticle/index.ts +5 -0
  93. package/src/containers/PipelineProperties/PipelineProperties.tsx +45 -53
  94. package/src/containers/index.ts +1 -1
  95. package/src/hooks/usePipelineBoardModel.ts +8 -9
  96. package/src/index.ts +0 -1
  97. package/src/meta.ts +19 -6
  98. package/src/plugin.ts +9 -0
  99. package/src/templates/research.ts +2 -2
  100. package/src/translations.ts +2 -1
  101. package/src/vite-env.d.ts +10 -0
  102. package/dist/lib/browser/index.mjs +0 -98
  103. package/dist/lib/browser/index.mjs.map +0 -7
  104. package/dist/lib/browser/meta.json +0 -1
  105. package/dist/types/src/containers/PipelineContainer/PipelineContainer.d.ts +0 -6
  106. package/dist/types/src/containers/PipelineContainer/PipelineContainer.d.ts.map +0 -1
  107. package/dist/types/src/containers/PipelineContainer/PipelineContainer.stories.d.ts +0 -57
  108. package/dist/types/src/containers/PipelineContainer/PipelineContainer.stories.d.ts.map +0 -1
  109. package/dist/types/src/containers/PipelineContainer/index.d.ts +0 -2
  110. package/dist/types/src/containers/PipelineContainer/index.d.ts.map +0 -1
  111. package/src/containers/PipelineContainer/index.ts +0 -5
  112. /package/dist/lib/{browser/types → neutral}/index.mjs.map +0 -0
  113. /package/dist/lib/{browser → neutral}/types/index.mjs +0 -0
@@ -8,26 +8,25 @@ import * as Layer from 'effect/Layer';
8
8
  import React from 'react';
9
9
 
10
10
  import { withPluginManager } from '@dxos/app-framework/testing';
11
- import { Filter, Ref } from '@dxos/client/echo';
12
- import { Database, Feed, JsonSchema, Obj, Query, Tag } from '@dxos/echo';
13
- import { Collection, View } from '@dxos/echo';
14
- import { createFeedServiceLayer } from '@dxos/echo-db';
15
- import { ClientPlugin } from '@dxos/plugin-client';
11
+ import { Collection, Database, Feed, Filter, JsonSchema, Obj, Query, Ref, Scope, Tag, View } from '@dxos/echo';
12
+ import { createFeedServiceLayer } from '@dxos/echo-client';
13
+ import { ClientPlugin } from '@dxos/plugin-client/plugin';
16
14
  import { initializeIdentity } from '@dxos/plugin-client/testing';
17
- import { InboxPlugin } from '@dxos/plugin-inbox';
18
- import { PreviewPlugin } from '@dxos/plugin-preview';
15
+ import { InboxPlugin } from '@dxos/plugin-inbox/plugin';
16
+ import { PreviewPlugin } from '@dxos/plugin-preview/testing';
19
17
  import { StorybookPlugin, corePlugins } from '@dxos/plugin-testing';
20
18
  import { random } from '@dxos/random';
21
19
  import { useDatabase, useQuery } from '@dxos/react-client/echo';
22
- import { translations as stackTranslations } from '@dxos/react-ui-stack';
20
+ import { translations as stackTranslations } from '@dxos/react-ui-stack/translations';
23
21
  import { withLayout } from '@dxos/react-ui/testing';
24
22
  import { ViewModel } from '@dxos/schema';
25
23
  import { createObjectFactory } from '@dxos/schema/testing';
26
24
  import { Message, Organization, Person, Pipeline, Task } from '@dxos/types';
27
25
 
28
- import { translations } from '../../translations';
26
+ import { translations } from '#translations';
27
+
29
28
  import PipelineProperties from '../PipelineProperties';
30
- import { PipelineContainer } from './PipelineContainer';
29
+ import { PipelineArticle } from './PipelineArticle';
31
30
 
32
31
  random.seed(0);
33
32
 
@@ -42,14 +41,14 @@ const DefaultStory = () => {
42
41
 
43
42
  return (
44
43
  <div className='grow grid grid-cols-[1fr_350px] overflow-hidden h-full w-full'>
45
- <PipelineContainer role='article' subject={pipeline} attendableId='test' />
44
+ <PipelineArticle role='article' subject={pipeline} attendableId='test' />
46
45
  <PipelineProperties pipeline={pipeline} classNames='border-s border-separator' />
47
46
  </div>
48
47
  );
49
48
  };
50
49
 
51
50
  const meta = {
52
- title: 'plugins/plugin-pipeline/containers/PipelineContainer',
51
+ title: 'plugins/plugin-pipeline/containers/PipelineArticle',
53
52
  render: DefaultStory,
54
53
  decorators: [
55
54
  withLayout({ layout: 'fullscreen' }),
@@ -74,7 +73,7 @@ const meta = {
74
73
 
75
74
  yield* Effect.gen(function* () {
76
75
  const tag = yield* Database.add(Tag.make({ label: 'important', hue: 'green' }));
77
- const tagDxn = Obj.getDXN(tag).toString();
76
+ const tagUri = Obj.getURI(tag);
78
77
 
79
78
  // Create a view for Contacts.
80
79
  const personView = ViewModel.make({
@@ -84,13 +83,13 @@ const meta = {
84
83
 
85
84
  // Create a view for Organizations.
86
85
  const organizationView = ViewModel.make({
87
- query: Query.select(Filter.type(Organization.Organization)).select(Filter.tag(tagDxn)),
86
+ query: Query.select(Filter.type(Organization.Organization)).select(Filter.tag(tagUri)),
88
87
  jsonSchema: JsonSchema.toJsonSchema(Organization.Organization),
89
88
  });
90
89
 
91
90
  // Create a view for Tasks.
92
91
  const taskView = ViewModel.make({
93
- query: Query.select(Filter.type(Task.Task)).select(Filter.tag(tagDxn)),
92
+ query: Query.select(Filter.type(Task.Task)).select(Filter.tag(tagUri)),
94
93
  jsonSchema: JsonSchema.toJsonSchema(Task.Task),
95
94
  });
96
95
 
@@ -111,11 +110,8 @@ const meta = {
111
110
  );
112
111
  yield* Feed.append(messageFeed, messages);
113
112
 
114
- const messageQueueDxn = Feed.getQueueDxn(messageFeed)!.toString();
115
113
  const messageView = ViewModel.make({
116
- query: Query.select(Filter.type(Message.Message)).from({
117
- queues: [messageQueueDxn],
118
- }),
114
+ query: Query.select(Filter.type(Message.Message)).from(Scope.feed(Obj.getURI(messageFeed))),
119
115
  jsonSchema: JsonSchema.toJsonSchema(Message.Message),
120
116
  });
121
117
 
@@ -167,7 +163,7 @@ const meta = {
167
163
  yield* Database.add(
168
164
  Obj.make(Organization.Organization, {
169
165
  [Obj.Meta]: {
170
- tags: random.datatype.boolean() ? [tagDxn] : [],
166
+ tags: random.datatype.boolean() ? [tagUri] : [],
171
167
  },
172
168
  name: random.company.name(),
173
169
  website: random.internet.url(),
@@ -182,7 +178,7 @@ const meta = {
182
178
  yield* Database.add(
183
179
  Obj.make(Task.Task, {
184
180
  [Obj.Meta]: {
185
- tags: random.datatype.boolean() ? [tagDxn] : [],
181
+ tags: random.datatype.boolean() ? [tagUri] : [],
186
182
  },
187
183
  title: random.lorem.sentence(),
188
184
  status: random.helpers.arrayElement(['todo', 'in-progress', 'done']) as any,
@@ -220,7 +216,7 @@ const meta = {
220
216
  layout: 'fullscreen',
221
217
  translations: [...translations, ...stackTranslations],
222
218
  },
223
- } satisfies Meta<typeof PipelineContainer>;
219
+ } satisfies Meta<typeof PipelineArticle>;
224
220
 
225
221
  export default meta;
226
222
 
@@ -5,16 +5,14 @@
5
5
  import React, { useCallback, useEffect } from 'react';
6
6
 
7
7
  import { Capabilities } from '@dxos/app-framework';
8
- import { useCapability } from '@dxos/app-framework/ui';
9
- import { useOperationInvoker } from '@dxos/app-framework/ui';
10
- import { Surface } from '@dxos/app-framework/ui';
8
+ import { Surface, useCapability, useOperationInvoker } from '@dxos/app-framework/ui';
9
+ import { LayoutOperation } from '@dxos/app-toolkit';
11
10
  import {
12
11
  AppSurface,
13
12
  OBJECT_ACTIONS_CONTRIBUTION_ID,
14
13
  OBJECT_ACTIONS_CONTRIBUTION_PRIORITY,
15
14
  useObjectMenuItems,
16
15
  } from '@dxos/app-toolkit/ui';
17
- import { DeckOperation } from '@dxos/plugin-deck/operations';
18
16
  import { Panel } from '@dxos/react-ui';
19
17
  import { linkedSegment } from '@dxos/react-ui-attention';
20
18
  import { useAttention } from '@dxos/react-ui-attention';
@@ -26,9 +24,9 @@ import { usePipelineBoardModel } from '#hooks';
26
24
 
27
25
  const PIPELINE_ITEM = 'PipelineItem';
28
26
 
29
- export type PipelineContainerProps = AppSurface.ObjectArticleProps<Pipeline.Pipeline>;
27
+ export type PipelineArticleProps = AppSurface.ObjectArticleProps<Pipeline.Pipeline>;
30
28
 
31
- export const PipelineContainer = ({ role, subject: pipeline, attendableId }: PipelineContainerProps) => {
29
+ export const PipelineArticle = ({ role, subject: pipeline, attendableId }: PipelineArticleProps) => {
32
30
  const registry = useCapability(Capabilities.AtomRegistry);
33
31
  const model = usePipelineBoardModel(pipeline, registry);
34
32
  const { invokePromise } = useOperationInvoker();
@@ -36,8 +34,8 @@ export const PipelineContainer = ({ role, subject: pipeline, attendableId }: Pip
36
34
 
37
35
  const handleColumnAdd = useCallback(
38
36
  () =>
39
- invokePromise(DeckOperation.ChangeCompanion, {
40
- companion: linkedSegment('settings'),
37
+ invokePromise(LayoutOperation.UpdateCompanion, {
38
+ subject: linkedSegment('settings'),
41
39
  }),
42
40
  [invokePromise],
43
41
  );
@@ -0,0 +1,5 @@
1
+ //
2
+ // Copyright 2023 DXOS.org
3
+ //
4
+
5
+ export { PipelineArticle as default } from './PipelineArticle';
@@ -7,15 +7,16 @@ import React, { useCallback, useMemo, useRef, useState } from 'react';
7
7
 
8
8
  import { resolveSchemaWithRegistry } from '@dxos/app-toolkit/query';
9
9
  import { useTypeOptions } from '@dxos/app-toolkit/ui';
10
- import { DXN, Filter, JsonSchema, Obj, Query, type QueryAST, Ref, Tag } from '@dxos/echo';
11
- import { type JsonPath, type Mutable } from '@dxos/echo/internal';
12
- import { getSpace, useObject, useQuery } from '@dxos/react-client/echo';
10
+ import { EID, Filter, JsonSchema, Obj, Query, type QueryAST, Ref, Scope, Tag, type Type } from '@dxos/echo';
11
+ import { type Mutable } from '@dxos/echo/Obj';
12
+ import { SchemaEx } from '@dxos/effect';
13
+ import { useObject, useQuery } from '@dxos/react-client/echo';
13
14
  import { IconButton, type ThemedClassName, useAsyncEffect, useTranslation } from '@dxos/react-ui';
14
15
  import { Form, ViewEditor } from '@dxos/react-ui-form';
15
16
  import { List } from '@dxos/react-ui-list';
16
17
  import { type ProjectionModel, ViewModel } from '@dxos/schema';
17
18
  import { Pipeline } from '@dxos/types';
18
- import { mx, osTranslations, subtleHover } from '@dxos/ui-theme';
19
+ import { mx, osTranslations } from '@dxos/ui-theme';
19
20
  import { arrayMove } from '@dxos/util';
20
21
 
21
22
  import { meta } from '#meta';
@@ -34,19 +35,16 @@ export type PipelinePropertiesProps = ThemedClassName<{
34
35
  */
35
36
  export const PipelineProperties = ({ classNames, pipeline }: PipelinePropertiesProps) => {
36
37
  const { t } = useTranslation(meta.id);
37
- const space = getSpace(pipeline);
38
+ const db = Obj.getDatabase(pipeline);
38
39
  const [expandedId, setExpandedId] = useState<string>();
39
40
  const [columns, updateColumns] = useObject(pipeline, 'columns');
40
- const column = useMemo(
41
- () => columns.find((column) => column.view.dxn.toString() === expandedId),
42
- [columns, expandedId],
43
- );
41
+ const column = useMemo(() => columns.find((column) => column.view.uri === expandedId), [columns, expandedId]);
44
42
  const [view, updateView] = useObject(column?.view);
45
- const [schema, setSchema] = useState<Schema.Schema.AnyNoContext>(() => Schema.Struct({}));
43
+ const [type, setType] = useState<Type.AnyEntity>();
46
44
  const projectionRef = useRef<ProjectionModel>(null);
47
- const tags = useQuery(space?.db, Filter.type(Tag.Tag));
45
+ const tags = useQuery(db, Filter.type(Tag.Tag));
48
46
  const types = useTypeOptions({
49
- space,
47
+ db,
50
48
  annotation: {
51
49
  location: ['database', 'runtime'],
52
50
  kind: ['user'],
@@ -54,15 +52,15 @@ export const PipelineProperties = ({ classNames, pipeline }: PipelinePropertiesP
54
52
  });
55
53
 
56
54
  useAsyncEffect(async () => {
57
- if (!view?.query || !space) {
55
+ if (!view?.query || !db) {
58
56
  return;
59
57
  }
60
58
 
61
- const foundSchema = await resolveSchemaWithRegistry(space.db.schemaRegistry, view.query.ast);
62
- if (foundSchema && foundSchema !== schema) {
63
- setSchema(() => foundSchema);
59
+ const foundType = await resolveSchemaWithRegistry(db, view.query.ast);
60
+ if (foundType && foundType !== type) {
61
+ setType(() => foundType);
64
62
  }
65
- }, [space, view, schema]);
63
+ }, [db, view, type]);
66
64
 
67
65
  const handleMove = useCallback(
68
66
  (fromIndex: number, toIndex: number) =>
@@ -73,38 +71,38 @@ export const PipelineProperties = ({ classNames, pipeline }: PipelinePropertiesP
73
71
  );
74
72
 
75
73
  const handleQueryChanged = useCallback(
76
- async (newQuery: QueryAST.Query, target?: string) => {
77
- if (!view || !space) {
74
+ async (newQuery: QueryAST.Query, target?: EID.EID) => {
75
+ if (!view || !db) {
78
76
  return;
79
77
  }
80
78
 
81
- const queue = target && DXN.tryParse(target) ? target : undefined;
82
- const query = queue ? Query.fromAst(newQuery).from({ queues: [queue] }) : Query.fromAst(newQuery);
79
+ const queue = target;
80
+ const query = queue ? Query.fromAst(newQuery).from([Scope.feed(String(queue))]) : Query.fromAst(newQuery);
83
81
  updateView((view) => {
84
82
  view.query.ast = query.ast as Mutable<typeof query.ast>;
85
83
  });
86
- const newSchema = await resolveSchemaWithRegistry(space.db.schemaRegistry, query.ast);
87
- if (!newSchema) {
84
+ const newType = await resolveSchemaWithRegistry(db, query.ast);
85
+ if (!newType) {
88
86
  return;
89
87
  }
90
88
 
91
89
  const newView = ViewModel.make({
92
90
  query,
93
- jsonSchema: JsonSchema.toJsonSchema(newSchema),
91
+ jsonSchema: newType.jsonSchema,
94
92
  });
95
93
  updateView((view) => {
96
94
  view.projection = Obj.getSnapshot(newView).projection as Mutable<typeof view.projection>;
97
95
  });
98
96
 
99
- setSchema(() => newSchema);
97
+ setType(() => newType);
100
98
  },
101
- [view, updateView, schema],
99
+ [db, view, updateView, type],
102
100
  );
103
101
 
104
102
  const handleColumnValuesChanged = useCallback(
105
103
  (column: Pipeline.Column) =>
106
- (newValues: { name?: string }, { changed }: { changed: Record<JsonPath, boolean> }) => {
107
- if (changed['name' as JsonPath]) {
104
+ (newValues: { name?: string }, { changed }: { changed: Record<SchemaEx.JsonPath, boolean> }) => {
105
+ if (changed['name' as SchemaEx.JsonPath]) {
108
106
  const columnIndex = columns.findIndex((c) => c === column);
109
107
  if (columnIndex === -1) {
110
108
  return;
@@ -118,14 +116,12 @@ export const PipelineProperties = ({ classNames, pipeline }: PipelinePropertiesP
118
116
  );
119
117
 
120
118
  const handleToggleField = useCallback((column: Pipeline.Column) => {
121
- setExpandedId((prevExpandedId) =>
122
- prevExpandedId === column.view.dxn.toString() ? undefined : column.view.dxn.toString(),
123
- );
119
+ setExpandedId((prevExpandedId) => (prevExpandedId === column.view.uri ? undefined : column.view.uri));
124
120
  }, []);
125
121
 
126
122
  const handleDelete = useCallback(
127
123
  async (column: Pipeline.Column) => {
128
- if (column.view.dxn.toString() === expandedId) {
124
+ if (column.view.uri === expandedId) {
129
125
  setExpandedId(undefined);
130
126
  }
131
127
 
@@ -134,20 +130,20 @@ export const PipelineProperties = ({ classNames, pipeline }: PipelinePropertiesP
134
130
  updateColumns((columns) => {
135
131
  columns.splice(index, 1);
136
132
  });
137
- space?.db.remove(viewToRemove);
133
+ db?.remove(viewToRemove);
138
134
  },
139
- [expandedId, columns, updateColumns, space],
135
+ [expandedId, columns, updateColumns, db],
140
136
  );
141
137
 
142
138
  const handleAdd = useCallback(() => {
143
- if (!space) {
139
+ if (!db) {
144
140
  return;
145
141
  }
146
142
  const newView = ViewModel.make({
147
143
  query: Query.select(Filter.nothing()),
148
144
  jsonSchema: JsonSchema.toJsonSchema(Schema.Struct({})),
149
145
  });
150
- space.db.add(newView);
146
+ db.add(newView);
151
147
  updateColumns((columns) => {
152
148
  columns.push({
153
149
  name: '',
@@ -157,16 +153,16 @@ export const PipelineProperties = ({ classNames, pipeline }: PipelinePropertiesP
157
153
  });
158
154
  });
159
155
  setExpandedId(newView.id);
160
- }, [space, updateColumns]);
156
+ }, [db, updateColumns]);
161
157
 
162
158
  return (
163
- <div role='none' className={mx('py-form-padding overflow-y-auto', classNames)}>
159
+ <div className={mx('py-form-padding overflow-y-auto', classNames)}>
164
160
  <h2 className='text-sm text-description py-1'>{t('views.label')}</h2>
165
161
 
166
162
  <List.Root<Pipeline.Column>
167
163
  items={columns}
168
164
  isItem={Schema.is(Pipeline.Column)}
169
- getId={(column) => column.view.dxn.toString()}
165
+ getId={(column) => column.view.uri}
170
166
  onMove={handleMove}
171
167
  >
172
168
  {({ items: columns }) => (
@@ -174,12 +170,12 @@ export const PipelineProperties = ({ classNames, pipeline }: PipelinePropertiesP
174
170
  <div role='list' className={mx(listGrid)}>
175
171
  {columns.map((column) => (
176
172
  <List.Item<Pipeline.Column>
177
- key={column.view.dxn.toString()}
173
+ key={column.view.uri}
178
174
  item={column}
179
175
  classNames={listItemGrid}
180
- aria-expanded={expandedId === column.view.dxn.toString()}
176
+ aria-expanded={expandedId === column.view.uri}
181
177
  >
182
- <div role='none' className={mx(subtleHover, listItemGrid, 'rounded-xs cursor-pointer min-h-10')}>
178
+ <div className={mx(listItemGrid, 'dx-hover rounded-xs cursor-pointer min-h-10')}>
183
179
  <List.ItemDragHandle />
184
180
  <List.ItemTitle onClick={() => handleToggleField(column)}>
185
181
  {column.name || t('untitled-view.title')}
@@ -194,16 +190,12 @@ export const PipelineProperties = ({ classNames, pipeline }: PipelinePropertiesP
194
190
  iconOnly
195
191
  variant='ghost'
196
192
  label={t('toggle-expand.label', { ns: osTranslations })}
197
- icon={
198
- expandedId === column.view.dxn.toString()
199
- ? 'ph--caret-down--regular'
200
- : 'ph--caret-right--regular'
201
- }
193
+ icon={expandedId === column.view.uri ? 'ph--caret-down--regular' : 'ph--caret-right--regular'}
202
194
  onClick={() => handleToggleField(column)}
203
195
  />
204
196
  </div>
205
- {expandedId === column.view.dxn.toString() && column?.view.target && (
206
- <div role='none' className='col-span-5 my-2 border border-separator rounded-md'>
197
+ {expandedId === column.view.uri && column?.view.target && (
198
+ <div className='col-span-5 my-2 border border-separator rounded-md'>
207
199
  <Form.Root
208
200
  schema={ColumnFormSchema}
209
201
  values={column}
@@ -217,10 +209,10 @@ export const PipelineProperties = ({ classNames, pipeline }: PipelinePropertiesP
217
209
  ref={projectionRef}
218
210
  mode='tag'
219
211
  readonly
220
- schema={schema}
212
+ type={type}
221
213
  view={column.view.target}
222
- registry={space?.db.schemaRegistry}
223
- db={space?.db}
214
+ registry={db?.graph.registry}
215
+ db={db}
224
216
  tags={tags}
225
217
  types={types}
226
218
  onQueryChanged={handleQueryChanged}
@@ -234,7 +226,7 @@ export const PipelineProperties = ({ classNames, pipeline }: PipelinePropertiesP
234
226
  )}
235
227
  </List.Root>
236
228
 
237
- <div role='none' className='my-form-padding'>
229
+ <div className='my-form-padding'>
238
230
  <IconButton icon='ph--plus--regular' label={t('add-view.label')} onClick={handleAdd} classNames='w-full' />
239
231
  </div>
240
232
  </div>
@@ -4,5 +4,5 @@
4
4
 
5
5
  import { type ComponentType, lazy } from 'react';
6
6
 
7
- export const PipelineContainer: ComponentType<any> = lazy(() => import('./PipelineContainer'));
7
+ export const PipelineArticle: ComponentType<any> = lazy(() => import('./PipelineArticle'));
8
8
  export const PipelineProperties: ComponentType<any> = lazy(() => import('./PipelineProperties'));
@@ -8,7 +8,6 @@ import { useMemo } from 'react';
8
8
 
9
9
  import { getQueryTarget } from '@dxos/app-toolkit/query';
10
10
  import { Obj, Query } from '@dxos/echo';
11
- import { AtomObj, AtomQuery } from '@dxos/echo-atom';
12
11
  import { getSpace, isSpace } from '@dxos/react-client/echo';
13
12
  import { type BoardModel } from '@dxos/react-ui-mosaic';
14
13
  import { Pipeline } from '@dxos/types';
@@ -23,11 +22,11 @@ export const usePipelineBoardModel = (
23
22
  }
24
23
 
25
24
  const space = getSpace(pipeline);
26
- const columnsAtom = AtomObj.makeProperty(pipeline, 'columns');
25
+ const columnsAtom = Obj.atomProperty(pipeline, 'columns');
27
26
  const columnAtomFamily = Atom.family<string, Atom.Atom<Pipeline.Column | undefined>>((viewKey: string) =>
28
27
  Atom.make((get) => {
29
28
  const columns = get(columnsAtom);
30
- return columns.find((c) => c.view.dxn.toString() === viewKey);
29
+ return columns.find((c) => c.view.uri === viewKey);
31
30
  }),
32
31
  );
33
32
 
@@ -37,7 +36,7 @@ export const usePipelineBoardModel = (
37
36
  if (column == null) {
38
37
  return [];
39
38
  }
40
- const viewSnapshot = get(AtomObj.make(column.view));
39
+ const viewSnapshot = get(Obj.atom(column.view));
41
40
  if (!viewSnapshot?.query?.ast) {
42
41
  return [];
43
42
  }
@@ -46,20 +45,20 @@ export const usePipelineBoardModel = (
46
45
  if (!queryTarget) {
47
46
  return [];
48
47
  }
49
- const raw = get(AtomQuery.make(queryTarget, query));
48
+ const raw = get(queryTarget.query(query).atom);
50
49
  return isSpace(queryTarget) ? raw : [...raw].reverse();
51
50
  }),
52
51
  );
53
52
 
54
53
  return {
55
- getColumnId: (data) => (data as Pipeline.Column).view.dxn.toString(),
54
+ getColumnId: (data) => (data as Pipeline.Column).view.uri,
56
55
  getItemId: (data) => (data as Obj.Unknown).id,
57
56
  isColumn: (obj: unknown): obj is Pipeline.Column => Schema.is(Pipeline.Column)(obj),
58
57
  isItem: (obj: unknown): obj is Obj.Unknown => Obj.isObject(obj),
59
58
  columns: columnsAtom,
60
- items: (column) => itemsAtomFamily(column.view.dxn.toString()),
59
+ items: (column) => itemsAtomFamily(column.view.uri),
61
60
  getColumns: () => [...registry.get(columnsAtom)],
62
- getItems: (column) => registry.get(itemsAtomFamily(column.view.dxn.toString())) ?? [],
61
+ getItems: (column) => registry.get(itemsAtomFamily(column.view.uri)) ?? [],
63
62
  };
64
63
  }, [pipeline, registry]);
65
64
 
@@ -68,7 +67,7 @@ const emptyColumnsAtom = Atom.make(() => [] as Pipeline.Column[]);
68
67
  const emptyItemsAtom = Atom.make(() => [] as Obj.Unknown[]);
69
68
 
70
69
  const emptyPipelineModel: BoardModel<Pipeline.Column, Obj.Unknown> = {
71
- getColumnId: (data) => (data as Pipeline.Column).view.dxn.toString(),
70
+ getColumnId: (data) => (data as Pipeline.Column).view.uri,
72
71
  getItemId: (data) => (data as Obj.Unknown).id,
73
72
  isColumn: (obj: unknown): obj is Pipeline.Column => Schema.is(Pipeline.Column)(obj),
74
73
  isItem: (obj): obj is Obj.Unknown => Obj.isObject(obj),
package/src/index.ts CHANGED
@@ -3,4 +3,3 @@
3
3
  //
4
4
 
5
5
  export * from './meta';
6
- export * from './PipelinePlugin';
package/src/meta.ts CHANGED
@@ -2,18 +2,31 @@
2
2
  // Copyright 2023 DXOS.org
3
3
  //
4
4
 
5
- import { type Plugin } from '@dxos/app-framework';
5
+ import { Plugin } from '@dxos/app-framework';
6
+ import { DXN } from '@dxos/keys';
6
7
  import { trim } from '@dxos/util';
7
8
 
8
- export const meta: Plugin.Meta = {
9
- id: 'org.dxos.plugin.pipeline',
9
+ export const meta = Plugin.makeMeta({
10
+ key: DXN.make('org.dxos.plugin.pipeline'),
10
11
  name: 'Pipelines',
12
+ author: 'DXOS',
11
13
  description: trim`
12
- Pipelines for organizing tasks, milestones, and deliverables.
13
- Track progress across multiple pipelines and coordinate team activities in a structured environment.
14
+ A multi-column Kanban-style pipeline plugin for DXOS Composer.
15
+ Each Pipeline holds an ordered set of Columns; each Column is backed by a ViewModel whose query
16
+ can select any ECHO object type — messages, contacts, documents, or custom types — from the active space.
17
+
18
+ Columns are fully configurable through the Settings companion panel, where users can add, rename,
19
+ reorder, and delete columns and edit the underlying query and projection for each column.
20
+ Objects can be dragged between columns and the full pipeline state, including column order and
21
+ per-column item order, persists collaboratively via ECHO.
22
+
23
+ A built-in Research template scaffolds a four-column pipeline wired to the Mailbox, Contacts,
24
+ Organizations, and Notes objects in the space, providing an out-of-the-box CRM-style workflow.
25
+ Pipeline articles also expose Invocations and Automation companion tabs for AI-driven automation.
14
26
  `,
15
27
  icon: 'ph--path--regular',
16
28
  iconHue: 'purple',
17
29
  source: 'https://github.com/dxos/dxos/tree/main/packages/plugins/plugin-pipeline',
30
+ spec: 'PLUGIN.mdl',
18
31
  tags: ['labs'],
19
- };
32
+ });
package/src/plugin.ts ADDED
@@ -0,0 +1,9 @@
1
+ //
2
+ // Copyright 2025 DXOS.org
3
+ //
4
+
5
+ import { Plugin } from '@dxos/app-framework';
6
+
7
+ import { meta } from './meta';
8
+
9
+ export const PipelinePlugin = Plugin.lazy(meta, () => import('#plugin'));
@@ -3,8 +3,8 @@
3
3
  //
4
4
 
5
5
  import { type Database, Filter, JsonSchema, Query, Ref } from '@dxos/echo';
6
- import { Mailbox } from '@dxos/plugin-inbox/types';
7
- import { Markdown } from '@dxos/plugin-markdown/types';
6
+ import { Mailbox } from '@dxos/plugin-inbox';
7
+ import { Markdown } from '@dxos/plugin-markdown';
8
8
  import { ViewModel } from '@dxos/schema';
9
9
  import { Message, Organization, Person, Pipeline } from '@dxos/types';
10
10
 
@@ -1,3 +1,4 @@
1
+ import { Type } from '@dxos/echo';
1
2
  import { type Resource } from '@dxos/react-ui';
2
3
  import { Pipeline } from '@dxos/types';
3
4
  //
@@ -9,7 +10,7 @@ import { meta } from '#meta';
9
10
  export const translations = [
10
11
  {
11
12
  'en-US': {
12
- [Pipeline.Pipeline.typename]: {
13
+ [Type.getTypename(Pipeline.Pipeline)]: {
13
14
  'typename.label': 'Pipeline',
14
15
  'typename.label_zero': 'Pipelines',
15
16
  'typename.label_one': 'Pipeline',
@@ -0,0 +1,10 @@
1
+ /// <reference types="vite/client" />
2
+
3
+ //
4
+ // Copyright 2026 DXOS.org
5
+ //
6
+
7
+ declare module '*.mdl?raw' {
8
+ const content: string;
9
+ export default content;
10
+ }