@dxos/plugin-pipeline 0.8.3-main.ef1bc66f44 → 0.8.4-main.2244d791bb

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 (48) hide show
  1. package/dist/lib/browser/{PipelineContainer-TRO66W6L.mjs → PipelineContainer-2BRZPECS.mjs} +126 -53
  2. package/dist/lib/browser/PipelineContainer-2BRZPECS.mjs.map +7 -0
  3. package/dist/lib/browser/{PipelineSettings-EBR3UU5C.mjs → PipelineSettings-R54RTVWZ.mjs} +41 -32
  4. package/dist/lib/browser/PipelineSettings-R54RTVWZ.mjs.map +7 -0
  5. package/dist/lib/browser/{app-graph-builder-NKUZQ6A7.mjs → app-graph-builder-NUJB33F7.mjs} +8 -7
  6. package/dist/lib/browser/app-graph-builder-NUJB33F7.mjs.map +7 -0
  7. package/dist/lib/browser/{chunk-XDHJKK7D.mjs → chunk-7GVESVYR.mjs} +2 -2
  8. package/dist/lib/browser/{chunk-J5YNPKHT.mjs → chunk-QWNKLHBY.mjs} +3 -3
  9. package/dist/lib/browser/{chunk-J5YNPKHT.mjs.map → chunk-QWNKLHBY.mjs.map} +1 -1
  10. package/dist/lib/browser/index.mjs +30 -29
  11. package/dist/lib/browser/index.mjs.map +3 -3
  12. package/dist/lib/browser/meta.json +1 -1
  13. package/dist/lib/browser/{react-surface-CH2SUJTQ.mjs → react-surface-CMTEEE6F.mjs} +11 -10
  14. package/dist/lib/browser/react-surface-CMTEEE6F.mjs.map +7 -0
  15. package/dist/lib/browser/types/index.mjs +2 -2
  16. package/dist/types/src/PipelinePlugin.d.ts.map +1 -1
  17. package/dist/types/src/capabilities/app-graph-builder/app-graph-builder.d.ts.map +1 -1
  18. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts +2 -2
  19. package/dist/types/src/capabilities/operation-resolver/operation-resolver.d.ts.map +1 -1
  20. package/dist/types/src/capabilities/react-surface/index.d.ts +1 -1
  21. package/dist/types/src/capabilities/react-surface/index.d.ts.map +1 -1
  22. package/dist/types/src/capabilities/react-surface/react-surface.d.ts +2 -2
  23. package/dist/types/src/capabilities/react-surface/react-surface.d.ts.map +1 -1
  24. package/dist/types/src/components/PipelineColumn.d.ts +3 -4
  25. package/dist/types/src/components/PipelineColumn.d.ts.map +1 -1
  26. package/dist/types/src/components/PipelineComponent.d.ts +13 -7
  27. package/dist/types/src/components/PipelineComponent.d.ts.map +1 -1
  28. package/dist/types/src/components/PipelineComponent.stories.d.ts +11 -11
  29. package/dist/types/src/components/PipelineComponent.stories.d.ts.map +1 -1
  30. package/dist/types/src/components/PipelineContainer.d.ts +1 -1
  31. package/dist/types/src/components/PipelineContainer.d.ts.map +1 -1
  32. package/dist/types/src/components/PipelineContainer.stories.d.ts +11 -11
  33. package/dist/types/src/components/PipelineContainer.stories.d.ts.map +1 -1
  34. package/dist/types/src/components/PipelineSettings.d.ts.map +1 -1
  35. package/dist/types/src/translations.d.ts +11 -11
  36. package/dist/types/tsconfig.tsbuildinfo +1 -1
  37. package/package.json +39 -38
  38. package/src/components/PipelineColumn.tsx +32 -32
  39. package/src/components/PipelineComponent.stories.tsx +11 -4
  40. package/src/components/PipelineComponent.tsx +100 -17
  41. package/src/components/PipelineContainer.stories.tsx +5 -5
  42. package/src/components/PipelineContainer.tsx +6 -2
  43. package/src/components/PipelineSettings.tsx +35 -30
  44. package/dist/lib/browser/PipelineContainer-TRO66W6L.mjs.map +0 -7
  45. package/dist/lib/browser/PipelineSettings-EBR3UU5C.mjs.map +0 -7
  46. package/dist/lib/browser/app-graph-builder-NKUZQ6A7.mjs.map +0 -7
  47. package/dist/lib/browser/react-surface-CH2SUJTQ.mjs.map +0 -7
  48. /package/dist/lib/browser/{chunk-XDHJKK7D.mjs.map → chunk-7GVESVYR.mjs.map} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dxos/plugin-pipeline",
3
- "version": "0.8.3-main.ef1bc66f44",
3
+ "version": "0.8.4-main.2244d791bb",
4
4
  "description": "Pipeline plugin for Composer",
5
5
  "homepage": "https://dxos.org",
6
6
  "bugs": "https://github.com/dxos/dxos/issues",
@@ -40,32 +40,33 @@
40
40
  "@radix-ui/react-compose-refs": "1.1.1",
41
41
  "@radix-ui/react-context": "1.1.1",
42
42
  "effect": "3.19.16",
43
- "@dxos/app-toolkit": "0.8.3-main.ef1bc66f44",
44
- "@dxos/devtools": "0.8.3-main.ef1bc66f44",
45
- "@dxos/async": "0.8.3-main.ef1bc66f44",
46
- "@dxos/assistant-toolkit": "0.8.3-main.ef1bc66f44",
47
- "@dxos/echo": "0.8.3-main.ef1bc66f44",
48
- "@dxos/operation": "0.8.3-main.ef1bc66f44",
49
- "@dxos/functions": "0.8.3-main.ef1bc66f44",
50
- "@dxos/plugin-deck": "0.8.3-main.ef1bc66f44",
51
- "@dxos/plugin-client": "0.8.3-main.ef1bc66f44",
52
- "@dxos/app-framework": "0.8.3-main.ef1bc66f44",
53
- "@dxos/plugin-graph": "0.8.3-main.ef1bc66f44",
54
- "@dxos/plugin-inbox": "0.8.3-main.ef1bc66f44",
55
- "@dxos/plugin-markdown": "0.8.3-main.ef1bc66f44",
56
- "@dxos/log": "0.8.3-main.ef1bc66f44",
57
- "@dxos/react-client": "0.8.3-main.ef1bc66f44",
58
- "@dxos/react-ui-list": "0.8.3-main.ef1bc66f44",
59
- "@dxos/react-ui-components": "0.8.3-main.ef1bc66f44",
60
- "@dxos/react-ui-attention": "0.8.3-main.ef1bc66f44",
61
- "@dxos/react-ui-stack": "0.8.3-main.ef1bc66f44",
62
- "@dxos/react-ui-mosaic": "0.8.3-main.ef1bc66f44",
63
- "@dxos/echo-query": "0.8.3-main.ef1bc66f44",
64
- "@dxos/schema": "0.8.3-main.ef1bc66f44",
65
- "@dxos/react-ui-form": "0.8.3-main.ef1bc66f44",
66
- "@dxos/util": "0.8.3-main.ef1bc66f44",
67
- "@dxos/plugin-space": "0.8.3-main.ef1bc66f44",
68
- "@dxos/types": "0.8.3-main.ef1bc66f44"
43
+ "@dxos/app-toolkit": "0.8.4-main.2244d791bb",
44
+ "@dxos/app-framework": "0.8.4-main.2244d791bb",
45
+ "@dxos/assistant-toolkit": "0.8.4-main.2244d791bb",
46
+ "@dxos/async": "0.8.4-main.2244d791bb",
47
+ "@dxos/echo-atom": "0.8.4-main.2244d791bb",
48
+ "@dxos/echo": "0.8.4-main.2244d791bb",
49
+ "@dxos/echo-query": "0.8.4-main.2244d791bb",
50
+ "@dxos/devtools": "0.8.4-main.2244d791bb",
51
+ "@dxos/log": "0.8.4-main.2244d791bb",
52
+ "@dxos/operation": "0.8.4-main.2244d791bb",
53
+ "@dxos/plugin-client": "0.8.4-main.2244d791bb",
54
+ "@dxos/plugin-deck": "0.8.4-main.2244d791bb",
55
+ "@dxos/plugin-graph": "0.8.4-main.2244d791bb",
56
+ "@dxos/plugin-inbox": "0.8.4-main.2244d791bb",
57
+ "@dxos/plugin-markdown": "0.8.4-main.2244d791bb",
58
+ "@dxos/functions": "0.8.4-main.2244d791bb",
59
+ "@dxos/react-ui-attention": "0.8.4-main.2244d791bb",
60
+ "@dxos/react-client": "0.8.4-main.2244d791bb",
61
+ "@dxos/react-ui-components": "0.8.4-main.2244d791bb",
62
+ "@dxos/plugin-space": "0.8.4-main.2244d791bb",
63
+ "@dxos/react-ui-form": "0.8.4-main.2244d791bb",
64
+ "@dxos/react-ui-list": "0.8.4-main.2244d791bb",
65
+ "@dxos/react-ui-stack": "0.8.4-main.2244d791bb",
66
+ "@dxos/react-ui-mosaic": "0.8.4-main.2244d791bb",
67
+ "@dxos/schema": "0.8.4-main.2244d791bb",
68
+ "@dxos/types": "0.8.4-main.2244d791bb",
69
+ "@dxos/util": "0.8.4-main.2244d791bb"
69
70
  },
70
71
  "devDependencies": {
71
72
  "@effect-atom/atom-react": "^0.5.0",
@@ -74,23 +75,23 @@
74
75
  "react": "~19.2.3",
75
76
  "react-dom": "~19.2.3",
76
77
  "vite": "7.1.9",
77
- "@dxos/plugin-inbox": "0.8.3-main.ef1bc66f44",
78
- "@dxos/plugin-attention": "0.8.3-main.ef1bc66f44",
79
- "@dxos/plugin-preview": "0.8.3-main.ef1bc66f44",
80
- "@dxos/plugin-testing": "0.8.3-main.ef1bc66f44",
81
- "@dxos/random": "0.8.3-main.ef1bc66f44",
82
- "@dxos/react-ui": "0.8.3-main.ef1bc66f44",
83
- "@dxos/plugin-theme": "0.8.3-main.ef1bc66f44",
84
- "@dxos/ui-theme": "0.8.3-main.ef1bc66f44",
85
- "@dxos/storybook-utils": "0.8.3-main.ef1bc66f44"
78
+ "@dxos/plugin-attention": "0.8.4-main.2244d791bb",
79
+ "@dxos/plugin-preview": "0.8.4-main.2244d791bb",
80
+ "@dxos/plugin-inbox": "0.8.4-main.2244d791bb",
81
+ "@dxos/plugin-testing": "0.8.4-main.2244d791bb",
82
+ "@dxos/plugin-theme": "0.8.4-main.2244d791bb",
83
+ "@dxos/storybook-utils": "0.8.4-main.2244d791bb",
84
+ "@dxos/random": "0.8.4-main.2244d791bb",
85
+ "@dxos/react-ui": "0.8.4-main.2244d791bb",
86
+ "@dxos/ui-theme": "0.8.4-main.2244d791bb"
86
87
  },
87
88
  "peerDependencies": {
88
89
  "@effect-atom/atom-react": "^0.5.0",
89
90
  "effect": "3.19.16",
90
91
  "react": "~19.2.3",
91
92
  "react-dom": "~19.2.3",
92
- "@dxos/react-ui": "0.8.3-main.ef1bc66f44",
93
- "@dxos/ui-theme": "0.8.3-main.ef1bc66f44"
93
+ "@dxos/react-ui": "0.8.4-main.2244d791bb",
94
+ "@dxos/ui-theme": "0.8.4-main.2244d791bb"
94
95
  },
95
96
  "publishConfig": {
96
97
  "access": "public"
@@ -2,17 +2,19 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { useAtomValue } from '@effect-atom/atom-react';
5
6
  import { useComposedRefs } from '@radix-ui/react-compose-refs';
6
7
  import type * as Schema from 'effect/Schema';
7
8
  import React, { forwardRef, useMemo, useRef, useState } from 'react';
8
9
 
9
10
  import { Obj, Query, Type } from '@dxos/echo';
10
- import { getQueryTarget, resolveSchemaWithRegistry } from '@dxos/plugin-space';
11
- import { Filter, getSpace, isSpace, useObject, useQuery } from '@dxos/react-client/echo';
12
- import { ScrollArea, useAsyncEffect, useTranslation } from '@dxos/react-ui';
13
- import { Card, Focus, Mosaic, type MosaicTileProps } from '@dxos/react-ui-mosaic';
11
+ import { resolveSchemaWithRegistry } from '@dxos/plugin-space';
12
+ import { Filter, getSpace, useObject } from '@dxos/react-client/echo';
13
+ import { useAsyncEffect, useTranslation } from '@dxos/react-ui';
14
+ import { Board, Card, Focus, Mosaic, type MosaicTileProps, useBoard } from '@dxos/react-ui-mosaic';
14
15
  import { ProjectionModel, createEchoChangeCallback } from '@dxos/schema';
15
16
  import { type Pipeline } from '@dxos/types';
17
+ import { mx } from '@dxos/ui-theme';
16
18
 
17
19
  import { meta } from '../meta';
18
20
 
@@ -24,15 +26,13 @@ import { type ItemProps, usePipeline } from './PipelineComponent';
24
26
 
25
27
  const PIPELINE_COLUMN_NAME = 'PipelineColumn';
26
28
 
27
- export type PipelineColumnProps = {
28
- column: Pipeline.Column;
29
- };
29
+ export type PipelineColumnProps = Pick<MosaicTileProps<Pipeline.Column>, 'classNames' | 'location' | 'data' | 'debug'>;
30
30
 
31
- // TODO(thure): Duplicates a lot of the same boilerplate as Kanban columns; is there an opportunity to DRY these out?
32
- // TODO(wittjosiah): Support column DnD reordering.
33
31
  // TODO(wittjosiah): Support item DnD reordering (ordering needs to be stored on the view presentation collection).
34
- export const PipelineColumn = ({ column }: PipelineColumnProps) => {
32
+ export const PipelineColumn = ({ data: column, location, classNames, debug }: PipelineColumnProps) => {
35
33
  const { t } = useTranslation(meta.id);
34
+ const { model } = useBoard<Pipeline.Column, Obj.Unknown>(PIPELINE_COLUMN_NAME);
35
+ const items = useAtomValue(model.items(column));
36
36
  // Subscribe to the view target for reactivity.
37
37
  const [viewSnapshot] = useObject(column.view);
38
38
  const view = column.view.target;
@@ -50,7 +50,7 @@ export const PipelineColumn = ({ column }: PipelineColumnProps) => {
50
50
  }, [JSON.stringify(viewSnapshot?.query.ast)]);
51
51
 
52
52
  useAsyncEffect(async () => {
53
- if (!query || !space) {
53
+ if (!query || !space?.db) {
54
54
  return;
55
55
  }
56
56
 
@@ -58,12 +58,6 @@ export const PipelineColumn = ({ column }: PipelineColumnProps) => {
58
58
  setSchema(() => schema);
59
59
  }, [space, query]);
60
60
 
61
- const queryTarget = getQueryTarget(query.ast, space);
62
- const items = useQuery(queryTarget, query);
63
- const sortedItems = useMemo(() => {
64
- // TODO(burdon): Hack to reverse queue.
65
- return isSpace(queryTarget) ? items : [...items.reverse()];
66
- }, [queryTarget, items]);
67
61
  const projectionModel = useMemo(() => {
68
62
  if (!schema || !view) {
69
63
  return undefined;
@@ -85,23 +79,29 @@ export const PipelineColumn = ({ column }: PipelineColumnProps) => {
85
79
  return null;
86
80
  }
87
81
 
82
+ const dragHandleRef = useRef<HTMLButtonElement>(null);
83
+
88
84
  return (
89
- <Focus.Group asChild>
90
- <div className='grid bs-full card-default-width overflow-hidden grid-rows-[min-content_1fr] density-fine border border-separator rounded-md bg-deckSurface'>
91
- <Card.Toolbar>
92
- <Card.DragHandle />
93
- <Card.Title>{column.name ?? t('untitled view title')}</Card.Title>
94
- <Card.Menu />
95
- </Card.Toolbar>
96
- <Mosaic.Container asChild orientation='vertical' withFocus>
97
- <ScrollArea.Root orientation='vertical'>
98
- <ScrollArea.Viewport classNames='p-2'>
99
- <Mosaic.Stack orientation='vertical' items={sortedItems} getId={(item) => item.id} Tile={Tile} />
100
- </ScrollArea.Viewport>
101
- </ScrollArea.Root>
102
- </Mosaic.Container>
85
+ <Board.Column.Root
86
+ data={column}
87
+ location={location}
88
+ classNames={classNames}
89
+ debug={debug}
90
+ dragHandleRef={dragHandleRef}
91
+ >
92
+ <div
93
+ role='none'
94
+ data-testid='board-column'
95
+ className={mx('group/column grid bs-full overflow-hidden grid-rows-[var(--rail-action)_1fr]', classNames)}
96
+ >
97
+ <Board.Column.Header
98
+ classNames='border-be border-separator'
99
+ label={column.name ?? t('untitled view title')}
100
+ dragHandleRef={dragHandleRef}
101
+ />
102
+ <Board.Column.Body data={column} Tile={Tile} />
103
103
  </div>
104
- </Focus.Group>
104
+ </Board.Column.Root>
105
105
  );
106
106
  };
107
107
 
@@ -2,8 +2,9 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { RegistryContext } from '@effect-atom/atom-react';
5
6
  import { type Meta, type StoryObj } from '@storybook/react-vite';
6
- import React, { useCallback, useEffect } from 'react';
7
+ import React, { useCallback, useContext, useEffect } from 'react';
7
8
 
8
9
  import { Filter, Ref } from '@dxos/client/echo';
9
10
  import { Obj, Query, Type } from '@dxos/echo';
@@ -15,11 +16,12 @@ import { Form, omitId } from '@dxos/react-ui-form';
15
16
  import { withMosaic } from '@dxos/react-ui-mosaic/testing';
16
17
  import { Collection, View } from '@dxos/schema';
17
18
  import { createObjectFactory } from '@dxos/schema/testing';
19
+ import { withRegistry } from '@dxos/storybook-utils';
18
20
  import { Person, Pipeline } from '@dxos/types';
19
21
 
20
22
  import { translations } from '../translations';
21
23
 
22
- import { type ItemProps, PipelineComponent as PipelineComponent } from './PipelineComponent';
24
+ import { type ItemProps, PipelineComponent, usePipelineBoardModel } from './PipelineComponent';
23
25
 
24
26
  const StorybookProjectItem = ({ item, projectionModel }: ItemProps) => {
25
27
  if (Obj.instanceOf(Person.Person, item)) {
@@ -40,9 +42,11 @@ const StorybookProjectItem = ({ item, projectionModel }: ItemProps) => {
40
42
  };
41
43
 
42
44
  const DefaultStory = () => {
45
+ const registry = useContext(RegistryContext);
43
46
  const { space } = useClientStory();
44
47
  const pipelines = useQuery(space?.db, Filter.type(Pipeline.Pipeline));
45
48
  const pipeline = pipelines[0];
49
+ const model = usePipelineBoardModel(pipeline, registry);
46
50
 
47
51
  const handleAddColumn = useCallback(() => {
48
52
  if (!space || !pipeline) {
@@ -72,17 +76,19 @@ const DefaultStory = () => {
72
76
  }
73
77
 
74
78
  return (
75
- <PipelineComponent.Root Item={StorybookProjectItem} onAddColumn={handleAddColumn}>
79
+ <PipelineComponent.Root Item={StorybookProjectItem} model={model} onAddColumn={handleAddColumn}>
76
80
  <PipelineComponent.Content pipeline={pipeline} />
77
81
  </PipelineComponent.Root>
78
82
  );
79
83
  };
80
84
 
81
85
  const MutationsStory = () => {
86
+ const registry = useContext(RegistryContext);
82
87
  const { space } = useClientStory();
83
88
  const pipelines = useQuery(space?.db, Filter.type(Pipeline.Pipeline));
84
89
  const contacts = useQuery(space?.db, Filter.type(Person.Person));
85
90
  const pipeline = pipelines[0];
91
+ const model = usePipelineBoardModel(pipeline, registry);
86
92
 
87
93
  const handleAddColumn = useCallback(() => {
88
94
  if (!space || !pipeline) {
@@ -141,7 +147,7 @@ const MutationsStory = () => {
141
147
  }
142
148
 
143
149
  return (
144
- <PipelineComponent.Root Item={StorybookProjectItem} onAddColumn={handleAddColumn}>
150
+ <PipelineComponent.Root Item={StorybookProjectItem} model={model} onAddColumn={handleAddColumn}>
145
151
  <PipelineComponent.Content pipeline={pipeline} />
146
152
  </PipelineComponent.Root>
147
153
  );
@@ -152,6 +158,7 @@ const meta = {
152
158
  decorators: [
153
159
  withTheme(),
154
160
  withLayout({ layout: 'column' }),
161
+ withRegistry,
155
162
  withMosaic(),
156
163
  withClientProvider({
157
164
  types: [Pipeline.Pipeline, View.View, Collection.Collection, Person.Person],
@@ -2,15 +2,19 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
+ import { Atom, type Registry, useAtomValue } from '@effect-atom/atom-react';
5
6
  import { createContext } from '@radix-ui/react-context';
6
- import React, { type FC } from 'react';
7
+ import * as Schema from 'effect/Schema';
8
+ import React, { type FC, type PropsWithChildren, useMemo } from 'react';
7
9
 
8
- import { type Obj } from '@dxos/echo';
9
- import { useObject } from '@dxos/react-client/echo';
10
+ import { Obj, Query } from '@dxos/echo';
11
+ import { AtomObj, AtomQuery } from '@dxos/echo-atom';
12
+ import { getQueryTarget } from '@dxos/plugin-space';
13
+ import { getSpace, isSpace } from '@dxos/react-client/echo';
10
14
  import { Toolbar, type ToolbarRootProps, useTranslation } from '@dxos/react-ui';
11
- import { Stack } from '@dxos/react-ui-stack';
15
+ import { Board, type BoardModel, useBoard, useEventHandlerAdapter } from '@dxos/react-ui-mosaic';
12
16
  import { type ProjectionModel } from '@dxos/schema';
13
- import { type Pipeline as PipelineType } from '@dxos/types';
17
+ import { Pipeline, type Pipeline as PipelineType } from '@dxos/types';
14
18
 
15
19
  import { meta } from '../meta';
16
20
 
@@ -35,16 +39,90 @@ type PipelineContextValue = {
35
39
  // Root
36
40
  //
37
41
 
38
- type PipelineRootProps = PipelineContextValue;
42
+ type PipelineRootProps = PropsWithChildren<
43
+ PipelineContextValue & {
44
+ model: BoardModel<PipelineType.Column, Obj.Unknown>;
45
+ }
46
+ >;
39
47
 
40
48
  const PIPELINE_ROOT = 'Pipeline.Root';
41
49
 
42
- const [PipelineRoot, usePipeline] = createContext<PipelineContextValue>(PIPELINE_ROOT, {
50
+ const [PipelineRootContext, usePipeline] = createContext<PipelineContextValue>(PIPELINE_ROOT, {
43
51
  Item: itemNoOp,
44
52
  });
45
53
 
54
+ const PipelineRoot = ({ model, children, ...contextValue }: PipelineRootProps) => (
55
+ <PipelineRootContext {...contextValue}>
56
+ <Board.Root model={model}>{children}</Board.Root>
57
+ </PipelineRootContext>
58
+ );
59
+
46
60
  PipelineRoot.displayName = PIPELINE_ROOT;
47
61
 
62
+ //
63
+ // usePipelineBoardModel
64
+ //
65
+
66
+ const emptyColumnsAtom = Atom.make(() => [] as PipelineType.Column[]);
67
+ const emptyItemsAtom = Atom.make(() => [] as Obj.Unknown[]);
68
+ const emptyPipelineModel: BoardModel<PipelineType.Column, Obj.Unknown> = {
69
+ getColumnId: (data) => (data as PipelineType.Column).view.dxn.toString(),
70
+ getItemId: (data) => (data as Obj.Unknown).id,
71
+ isColumn: (obj: unknown): obj is PipelineType.Column => Schema.is(Pipeline.Column)(obj),
72
+ isItem: (obj): obj is Obj.Unknown => Obj.isObject(obj),
73
+ columns: emptyColumnsAtom,
74
+ items: () => emptyItemsAtom,
75
+ getColumns: () => [],
76
+ getItems: () => [],
77
+ };
78
+
79
+ const usePipelineBoardModel = (
80
+ pipeline: PipelineType.Pipeline | undefined,
81
+ registry: Registry.Registry,
82
+ ): BoardModel<PipelineType.Column, Obj.Unknown> =>
83
+ useMemo<BoardModel<PipelineType.Column, Obj.Unknown>>(() => {
84
+ if (pipeline == null) {
85
+ return emptyPipelineModel;
86
+ }
87
+ const space = getSpace(pipeline);
88
+ const columnsAtom = AtomObj.makeProperty(pipeline, 'columns');
89
+ const columnAtomFamily = Atom.family<string, Atom.Atom<PipelineType.Column | undefined>>((viewKey: string) =>
90
+ Atom.make((get) => {
91
+ const columns = get(columnsAtom);
92
+ return columns.find((c) => c.view.dxn.toString() === viewKey);
93
+ }),
94
+ );
95
+ const itemsAtomFamily = Atom.family<string, Atom.Atom<Obj.Unknown[]>>((viewKey: string) =>
96
+ Atom.make((get) => {
97
+ const column = get(columnAtomFamily(viewKey));
98
+ if (column == null) {
99
+ return [];
100
+ }
101
+ const viewSnapshot = get(AtomObj.make(column.view));
102
+ if (!viewSnapshot?.query?.ast) {
103
+ return [];
104
+ }
105
+ const query = Query.fromAst(JSON.parse(JSON.stringify(viewSnapshot.query.ast)));
106
+ const queryTarget = space ? getQueryTarget(query.ast, space) : undefined;
107
+ if (!queryTarget) {
108
+ return [];
109
+ }
110
+ const raw = get(AtomQuery.make(queryTarget, query));
111
+ return isSpace(queryTarget) ? raw : [...raw].reverse();
112
+ }),
113
+ );
114
+ return {
115
+ getColumnId: (data) => (data as PipelineType.Column).view.dxn.toString(),
116
+ getItemId: (data) => (data as Obj.Unknown).id,
117
+ isColumn: (obj: unknown): obj is PipelineType.Column => Schema.is(Pipeline.Column)(obj),
118
+ isItem: (obj: unknown): obj is Obj.Unknown => Obj.isObject(obj),
119
+ columns: columnsAtom,
120
+ items: (column) => itemsAtomFamily(column.view.dxn.toString()),
121
+ getColumns: () => [...registry.get(columnsAtom)],
122
+ getItems: (column) => registry.get(itemsAtomFamily(column.view.dxn.toString())) ?? [],
123
+ };
124
+ }, [pipeline, registry]);
125
+
48
126
  //
49
127
  // Content
50
128
  //
@@ -56,15 +134,20 @@ type PipelineContentProps = {
56
134
  };
57
135
 
58
136
  const PipelineContent = ({ pipeline }: PipelineContentProps) => {
59
- const [columns] = useObject(pipeline, 'columns');
60
-
61
- return (
62
- <Stack orientation='horizontal' size='contain' rail={false}>
63
- {columns.map((column) => {
64
- return <PipelineColumn key={column.view.dxn.toString()} column={column} />;
65
- })}
66
- </Stack>
67
- );
137
+ const { model } = useBoard(PIPELINE_CONTENT_NAME);
138
+ const columns = useAtomValue(model.columns);
139
+ const eventHandler = useEventHandlerAdapter<PipelineType.Column, Obj.Unknown>({
140
+ id: pipeline.id,
141
+ // TODO(wittjosiah): Cast because columns array is readonly.
142
+ items: columns as any,
143
+ getId: model.getColumnId,
144
+ get: (data) => data as unknown as Obj.Unknown,
145
+ make: (object) => object as unknown as PipelineType.Column,
146
+ canDrop: ({ source }) => model.isColumn(source.data),
147
+ onChange: (mutate) => Obj.change(pipeline, (p) => mutate(p.columns)),
148
+ });
149
+
150
+ return <Board.Content id='pipeline' eventHandler={eventHandler} Tile={PipelineColumn} />;
68
151
  };
69
152
 
70
153
  PipelineContent.displayName = PIPELINE_CONTENT_NAME;
@@ -98,6 +181,6 @@ export const PipelineComponent = {
98
181
  Toolbar: PipelineToolbar,
99
182
  };
100
183
 
101
- export { usePipeline };
184
+ export { usePipeline, usePipelineBoardModel };
102
185
 
103
186
  export type { ItemProps, PipelineContextValue, PipelineRootProps };
@@ -39,7 +39,7 @@ const DefaultStory = () => {
39
39
  }
40
40
 
41
41
  return (
42
- <Stack orientation='horizontal' size='split' rail={false} classNames='pli-0'>
42
+ <Stack orientation='horizontal' size='split' rail={false} classNames='pli-0 bs-full'>
43
43
  <PipelineContainer role='article' subject={pipeline} />
44
44
  <PipelineObjectSettings pipeline={pipeline} classNames='border-is border-separator' />
45
45
  </Stack>
@@ -144,7 +144,7 @@ const meta = {
144
144
  space.db.add(pipeline);
145
145
 
146
146
  // Generate sample Organizations
147
- Array.from({ length: 5 }).forEach(() => {
147
+ Array.from({ length: 20 }).forEach(() => {
148
148
  const org = Obj.make(
149
149
  Organization.Organization,
150
150
  {
@@ -161,7 +161,7 @@ const meta = {
161
161
  });
162
162
 
163
163
  // Generate sample Tasks
164
- Array.from({ length: 8 }).forEach(() => {
164
+ Array.from({ length: 20 }).forEach(() => {
165
165
  const task = Obj.make(
166
166
  Task.Task,
167
167
  {
@@ -181,7 +181,7 @@ const meta = {
181
181
  yield* Effect.promise(() => factory([{ type: Person.Person, count: 12 }]));
182
182
 
183
183
  // Generate sample Projects
184
- Array.from({ length: 3 }).forEach(() => {
184
+ Array.from({ length: 20 }).forEach(() => {
185
185
  const nestedProject = Pipeline.make({
186
186
  name: faker.commerce.productName(),
187
187
  description: faker.lorem.sentence(),
@@ -190,7 +190,7 @@ const meta = {
190
190
  });
191
191
 
192
192
  // Generate sample Messages
193
- const messages = Array.from({ length: 6 }).map(() => {
193
+ const messages = Array.from({ length: 20 }).map(() => {
194
194
  const message = Obj.make(Message.Message, {
195
195
  created: faker.date.recent().toISOString(),
196
196
  sender: { role: 'user' },
@@ -4,6 +4,8 @@
4
4
 
5
5
  import React, { useCallback } from 'react';
6
6
 
7
+ import { Capabilities } from '@dxos/app-framework';
8
+ import { useCapability } from '@dxos/app-framework/ui';
7
9
  import { useOperationInvoker } from '@dxos/app-framework/ui';
8
10
  import { Surface } from '@dxos/app-framework/ui';
9
11
  import { type SurfaceComponentProps } from '@dxos/app-toolkit/ui';
@@ -13,11 +15,13 @@ import { Layout } from '@dxos/react-ui';
13
15
  import { useAttention } from '@dxos/react-ui-attention';
14
16
  import { type Pipeline as PipelineType } from '@dxos/types';
15
17
 
16
- import { type ItemProps, PipelineComponent } from './PipelineComponent';
18
+ import { type ItemProps, PipelineComponent, usePipelineBoardModel } from './PipelineComponent';
17
19
 
18
20
  export type PipelineContainerProps = SurfaceComponentProps<PipelineType.Pipeline>;
19
21
 
20
22
  export const PipelineContainer = ({ role, subject: pipeline }: PipelineContainerProps) => {
23
+ const registry = useCapability(Capabilities.AtomRegistry);
24
+ const model = usePipelineBoardModel(pipeline, registry);
21
25
  const { invokePromise } = useOperationInvoker();
22
26
  const attendableId = Obj.getDXN(pipeline).toString();
23
27
  const { hasAttention } = useAttention(attendableId);
@@ -33,7 +37,7 @@ export const PipelineContainer = ({ role, subject: pipeline }: PipelineContainer
33
37
 
34
38
  return (
35
39
  <Layout.Main role={role} toolbar>
36
- <PipelineComponent.Root Item={PipelineItem} onAddColumn={handleColumnAdd}>
40
+ <PipelineComponent.Root Item={PipelineItem} model={model} onAddColumn={handleColumnAdd}>
37
41
  <PipelineComponent.Toolbar disabled={!hasAttention} />
38
42
  <PipelineComponent.Content pipeline={pipeline} />
39
43
  </PipelineComponent.Root>