@dxos/plugin-sheet 0.8.2-main.fbd8ed0 → 0.8.2-staging.42af850

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 (136) hide show
  1. package/dist/lib/browser/{SheetContainer-UV7RMPXS.mjs → SheetContainer-GXPG3ZDN.mjs} +141 -162
  2. package/dist/lib/browser/SheetContainer-GXPG3ZDN.mjs.map +7 -0
  3. package/dist/lib/browser/anchor-sort-CUTFYIT4.mjs +24 -0
  4. package/dist/lib/browser/anchor-sort-CUTFYIT4.mjs.map +7 -0
  5. package/dist/lib/browser/{chunk-N2FOQHUH.mjs → chunk-5FLX3UGU.mjs} +8 -17
  6. package/dist/lib/browser/chunk-5FLX3UGU.mjs.map +7 -0
  7. package/dist/lib/browser/chunk-AYMJXZFS.mjs +847 -0
  8. package/dist/lib/browser/chunk-AYMJXZFS.mjs.map +7 -0
  9. package/dist/lib/browser/{chunk-AT5ZK6JD.mjs → chunk-FJRLDX7Z.mjs} +1 -1
  10. package/dist/lib/browser/chunk-FJRLDX7Z.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-EMSCNWEK.mjs → chunk-IR42IS3F.mjs} +2 -2
  12. package/dist/lib/{node-esm/chunk-76T5X4VP.mjs.map → browser/chunk-IR42IS3F.mjs.map} +1 -1
  13. package/dist/lib/browser/{compute-graph-registry-MBJKPAHX.mjs → compute-graph-registry-IXGGJJBU.mjs} +2 -2
  14. package/dist/lib/browser/index.mjs +15 -14
  15. package/dist/lib/browser/index.mjs.map +3 -3
  16. package/dist/lib/browser/{intent-resolver-DN7JXDAV.mjs → intent-resolver-UI4DHURQ.mjs} +2 -2
  17. package/dist/lib/browser/{markdown-57MKY5WB.mjs → markdown-T4TUP4BF.mjs} +3 -3
  18. package/dist/lib/browser/meta.json +1 -1
  19. package/dist/lib/browser/{react-surface-NQU63B63.mjs → react-surface-KI6T5M2X.mjs} +5 -5
  20. package/dist/lib/browser/types/index.mjs +1 -1
  21. package/dist/lib/node/SheetContainer-UUDOHLZR.cjs +351 -0
  22. package/dist/lib/node/SheetContainer-UUDOHLZR.cjs.map +7 -0
  23. package/dist/lib/node/{thread-E7YPGR5T.cjs → anchor-sort-LTLYUTUP.cjs} +17 -10
  24. package/dist/lib/node/anchor-sort-LTLYUTUP.cjs.map +7 -0
  25. package/dist/lib/node/{chunk-HLSQVT3C.cjs → chunk-76NESQLB.cjs} +10 -19
  26. package/dist/lib/node/chunk-76NESQLB.cjs.map +7 -0
  27. package/dist/lib/node/chunk-BXBNSNDK.cjs +855 -0
  28. package/dist/lib/node/chunk-BXBNSNDK.cjs.map +7 -0
  29. package/dist/lib/node/{chunk-LEV7OSTK.cjs → chunk-FIM6EZ6M.cjs} +4 -4
  30. package/dist/lib/node/chunk-FIM6EZ6M.cjs.map +7 -0
  31. package/dist/lib/node/{chunk-O2FOEUYB.cjs → chunk-LJWWS53Z.cjs} +5 -5
  32. package/dist/lib/node/{chunk-O2FOEUYB.cjs.map → chunk-LJWWS53Z.cjs.map} +1 -1
  33. package/dist/lib/node/{compute-graph-registry-EBBDN6ZX.cjs → compute-graph-registry-ARLDHPFW.cjs} +6 -6
  34. package/dist/lib/node/index.cjs +21 -20
  35. package/dist/lib/node/index.cjs.map +3 -3
  36. package/dist/lib/node/{intent-resolver-OPF56TAL.cjs → intent-resolver-EVLGL7VZ.cjs} +9 -9
  37. package/dist/lib/node/{markdown-Q3RXYPHQ.cjs → markdown-DBPOAYI7.cjs} +8 -8
  38. package/dist/lib/node/meta.json +1 -1
  39. package/dist/lib/node/{react-surface-7SAA5DX3.cjs → react-surface-QHAPOAR2.cjs} +14 -14
  40. package/dist/lib/node/types/index.cjs +29 -29
  41. package/dist/lib/node/types/index.cjs.map +1 -1
  42. package/dist/lib/node-esm/{SheetContainer-XW7B2AJI.mjs → SheetContainer-44KHKMPI.mjs} +141 -162
  43. package/dist/lib/node-esm/SheetContainer-44KHKMPI.mjs.map +7 -0
  44. package/dist/lib/node-esm/anchor-sort-3E2VGLO6.mjs +25 -0
  45. package/dist/lib/node-esm/anchor-sort-3E2VGLO6.mjs.map +7 -0
  46. package/dist/lib/node-esm/{chunk-LYZV4Q4C.mjs → chunk-DIF3IOAB.mjs} +8 -17
  47. package/dist/lib/node-esm/chunk-DIF3IOAB.mjs.map +7 -0
  48. package/dist/lib/node-esm/chunk-GCCM7R45.mjs +848 -0
  49. package/dist/lib/node-esm/chunk-GCCM7R45.mjs.map +7 -0
  50. package/dist/lib/node-esm/{chunk-76T5X4VP.mjs → chunk-IQ76YE6M.mjs} +2 -2
  51. package/dist/lib/{browser/chunk-EMSCNWEK.mjs.map → node-esm/chunk-IQ76YE6M.mjs.map} +1 -1
  52. package/dist/lib/node-esm/{chunk-HXBUY5ET.mjs → chunk-NMCVJWDT.mjs} +1 -1
  53. package/dist/lib/node-esm/chunk-NMCVJWDT.mjs.map +7 -0
  54. package/dist/lib/node-esm/{compute-graph-registry-TVG6RN2J.mjs → compute-graph-registry-7PDWXMHF.mjs} +2 -2
  55. package/dist/lib/node-esm/index.mjs +15 -14
  56. package/dist/lib/node-esm/index.mjs.map +3 -3
  57. package/dist/lib/node-esm/{intent-resolver-7AN5CN4R.mjs → intent-resolver-TPOH5JM5.mjs} +2 -2
  58. package/dist/lib/node-esm/{markdown-ACHAWUOE.mjs → markdown-WWUJ3E5F.mjs} +3 -3
  59. package/dist/lib/node-esm/meta.json +1 -1
  60. package/dist/lib/node-esm/{react-surface-BPNN7RSE.mjs → react-surface-XT2J3S67.mjs} +5 -5
  61. package/dist/lib/node-esm/types/index.mjs +1 -1
  62. package/dist/types/src/SheetPlugin.d.ts.map +1 -1
  63. package/dist/types/src/capabilities/anchor-sort.d.ts +6 -0
  64. package/dist/types/src/capabilities/anchor-sort.d.ts.map +1 -0
  65. package/dist/types/src/capabilities/index.d.ts +4 -4
  66. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  67. package/dist/types/src/components/GridSheet/util.d.ts.map +1 -1
  68. package/dist/types/src/components/SheetContainer/SheetContainer.stories.d.ts.map +1 -1
  69. package/dist/types/src/components/SheetToolbar/SheetToolbar.d.ts +2 -2
  70. package/dist/types/src/components/SheetToolbar/SheetToolbar.d.ts.map +1 -1
  71. package/dist/types/src/components/SheetToolbar/useToolbarState.d.ts +2 -3
  72. package/dist/types/src/components/SheetToolbar/useToolbarState.d.ts.map +1 -1
  73. package/dist/types/src/integrations/thread-ranges.d.ts.map +1 -1
  74. package/dist/types/src/model/sheet-model.d.ts.map +1 -1
  75. package/dist/types/src/serializer.d.ts.map +1 -1
  76. package/dist/types/src/testing/playwright/sheet-manager.d.ts.map +1 -1
  77. package/dist/types/src/types/schema.d.ts +0 -207
  78. package/dist/types/src/types/schema.d.ts.map +1 -1
  79. package/dist/types/src/types/util.d.ts.map +1 -1
  80. package/package.json +49 -48
  81. package/src/SheetPlugin.tsx +7 -5
  82. package/src/capabilities/anchor-sort.ts +22 -0
  83. package/src/capabilities/index.ts +1 -1
  84. package/src/components/FunctionEditor/FunctionEditor.tsx +1 -1
  85. package/src/components/GridSheet/util.ts +10 -9
  86. package/src/components/SheetContainer/SheetContainer.stories.tsx +2 -1
  87. package/src/components/SheetContainer/SheetContainer.tsx +1 -1
  88. package/src/components/SheetToolbar/SheetToolbar.stories.tsx +1 -1
  89. package/src/components/SheetToolbar/SheetToolbar.tsx +44 -33
  90. package/src/components/SheetToolbar/useToolbarState.ts +1 -2
  91. package/src/extensions/compute.ts +1 -1
  92. package/src/integrations/thread-ranges.ts +12 -14
  93. package/src/model/sheet-model.ts +17 -17
  94. package/src/serializer.ts +1 -2
  95. package/src/testing/playwright/sheet-manager.ts +9 -9
  96. package/src/types/schema.ts +1 -5
  97. package/src/types/util.ts +0 -1
  98. package/dist/lib/browser/SheetContainer-UV7RMPXS.mjs.map +0 -7
  99. package/dist/lib/browser/chunk-AT5ZK6JD.mjs.map +0 -7
  100. package/dist/lib/browser/chunk-N2FOQHUH.mjs.map +0 -7
  101. package/dist/lib/browser/chunk-TN7LTDHU.mjs +0 -826
  102. package/dist/lib/browser/chunk-TN7LTDHU.mjs.map +0 -7
  103. package/dist/lib/browser/thread-WU64QL2A.mjs +0 -17
  104. package/dist/lib/browser/thread-WU64QL2A.mjs.map +0 -7
  105. package/dist/lib/node/SheetContainer-GR2KDOAE.cjs +0 -368
  106. package/dist/lib/node/SheetContainer-GR2KDOAE.cjs.map +0 -7
  107. package/dist/lib/node/chunk-CN7OBGYV.cjs +0 -833
  108. package/dist/lib/node/chunk-CN7OBGYV.cjs.map +0 -7
  109. package/dist/lib/node/chunk-HLSQVT3C.cjs.map +0 -7
  110. package/dist/lib/node/chunk-LEV7OSTK.cjs.map +0 -7
  111. package/dist/lib/node/thread-E7YPGR5T.cjs.map +0 -7
  112. package/dist/lib/node-esm/SheetContainer-XW7B2AJI.mjs.map +0 -7
  113. package/dist/lib/node-esm/chunk-3JJ7ETTJ.mjs +0 -827
  114. package/dist/lib/node-esm/chunk-3JJ7ETTJ.mjs.map +0 -7
  115. package/dist/lib/node-esm/chunk-HXBUY5ET.mjs.map +0 -7
  116. package/dist/lib/node-esm/chunk-LYZV4Q4C.mjs.map +0 -7
  117. package/dist/lib/node-esm/thread-ULESW6IX.mjs +0 -18
  118. package/dist/lib/node-esm/thread-ULESW6IX.mjs.map +0 -7
  119. package/dist/types/src/capabilities/thread.d.ts +0 -6
  120. package/dist/types/src/capabilities/thread.d.ts.map +0 -1
  121. package/dist/types/src/components/SheetToolbar/comment.d.ts +0 -25
  122. package/dist/types/src/components/SheetToolbar/comment.d.ts.map +0 -1
  123. package/src/capabilities/thread.ts +0 -14
  124. package/src/components/SheetToolbar/comment.ts +0 -84
  125. /package/dist/lib/browser/{compute-graph-registry-MBJKPAHX.mjs.map → compute-graph-registry-IXGGJJBU.mjs.map} +0 -0
  126. /package/dist/lib/browser/{intent-resolver-DN7JXDAV.mjs.map → intent-resolver-UI4DHURQ.mjs.map} +0 -0
  127. /package/dist/lib/browser/{markdown-57MKY5WB.mjs.map → markdown-T4TUP4BF.mjs.map} +0 -0
  128. /package/dist/lib/browser/{react-surface-NQU63B63.mjs.map → react-surface-KI6T5M2X.mjs.map} +0 -0
  129. /package/dist/lib/node/{compute-graph-registry-EBBDN6ZX.cjs.map → compute-graph-registry-ARLDHPFW.cjs.map} +0 -0
  130. /package/dist/lib/node/{intent-resolver-OPF56TAL.cjs.map → intent-resolver-EVLGL7VZ.cjs.map} +0 -0
  131. /package/dist/lib/node/{markdown-Q3RXYPHQ.cjs.map → markdown-DBPOAYI7.cjs.map} +0 -0
  132. /package/dist/lib/node/{react-surface-7SAA5DX3.cjs.map → react-surface-QHAPOAR2.cjs.map} +0 -0
  133. /package/dist/lib/node-esm/{compute-graph-registry-TVG6RN2J.mjs.map → compute-graph-registry-7PDWXMHF.mjs.map} +0 -0
  134. /package/dist/lib/node-esm/{intent-resolver-7AN5CN4R.mjs.map → intent-resolver-TPOH5JM5.mjs.map} +0 -0
  135. /package/dist/lib/node-esm/{markdown-ACHAWUOE.mjs.map → markdown-WWUJ3E5F.mjs.map} +0 -0
  136. /package/dist/lib/node-esm/{react-surface-BPNN7RSE.mjs.map → react-surface-XT2J3S67.mjs.map} +0 -0
@@ -2,16 +2,24 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import React, { type PropsWithChildren, useCallback } from 'react';
5
+ import { Rx } from '@effect-rx/rx-react';
6
+ import React, { type PropsWithChildren, useMemo } from 'react';
6
7
 
7
- import { createIntent, useIntentDispatcher } from '@dxos/app-framework';
8
+ import { useAppGraph } from '@dxos/app-framework';
8
9
  import { type CompleteCellRange } from '@dxos/compute';
9
- import { ThreadAction } from '@dxos/plugin-thread/types';
10
10
  import { type ThemedClassName } from '@dxos/react-ui';
11
- import { createGapSeparator, MenuProvider, ToolbarMenu, useMenuActions } from '@dxos/react-ui-menu';
11
+ import {
12
+ type ActionGraphEdges,
13
+ type ActionGraphNodes,
14
+ type ActionGraphProps,
15
+ createGapSeparator,
16
+ MenuProvider,
17
+ rxFromSignal,
18
+ ToolbarMenu,
19
+ useMenuActions,
20
+ } from '@dxos/react-ui-menu';
12
21
 
13
22
  import { createAlign, useAlignState } from './align';
14
- import { createComment, useCommentState } from './comment';
15
23
  import { createStyle, useStyleState } from './style';
16
24
  import { type ToolbarState, useToolbarState } from './useToolbarState';
17
25
  import { type SheetModel } from '../../model';
@@ -21,52 +29,55 @@ import { useSheetContext } from '../SheetContext';
21
29
  // Root
22
30
  //
23
31
 
24
- export type SheetToolbarProps = ThemedClassName<PropsWithChildren<{ attendableId?: string }>>;
32
+ export type SheetToolbarProps = ThemedClassName<PropsWithChildren<{ id: string }>>;
25
33
 
26
34
  const createToolbarActions = (
27
35
  model: SheetModel,
28
36
  state: ToolbarState,
29
- onComment: (cellContent: string, cursor: string) => void,
30
37
  cursorFallbackRange?: CompleteCellRange,
38
+ customActions?: Rx.Rx<ActionGraphProps>,
31
39
  ) => {
32
- const align = createAlign(model, state, cursorFallbackRange);
33
- const style = createStyle(model, state, cursorFallbackRange);
34
- const gap = createGapSeparator();
35
- const comment = createComment(model, state, onComment, cursorFallbackRange);
36
- return {
37
- nodes: [...align.nodes, ...style.nodes, ...gap.nodes, ...comment.nodes],
38
- edges: [...align.edges, ...style.edges, ...gap.edges, ...comment.edges],
39
- };
40
+ return Rx.make((get) => {
41
+ const align = get(rxFromSignal(() => createAlign(model, state, cursorFallbackRange)));
42
+ const style = get(rxFromSignal(() => createStyle(model, state, cursorFallbackRange)));
43
+ const gap = createGapSeparator();
44
+ const nodes: ActionGraphNodes = [...align.nodes, ...style.nodes, ...gap.nodes];
45
+ const edges: ActionGraphEdges = [...align.edges, ...style.edges, ...gap.edges];
46
+ if (customActions) {
47
+ const custom = get(customActions);
48
+ nodes.push(...custom.nodes);
49
+ edges.push(...custom.edges);
50
+ }
51
+ return {
52
+ nodes,
53
+ edges,
54
+ };
55
+ });
40
56
  };
41
57
 
42
- export const SheetToolbar = ({ attendableId, classNames }: SheetToolbarProps) => {
43
- const { dispatchPromise: dispatch } = useIntentDispatcher();
58
+ export const SheetToolbar = ({ id, classNames }: SheetToolbarProps) => {
44
59
  const { model, cursorFallbackRange } = useSheetContext();
45
60
  const state = useToolbarState({});
46
61
  useAlignState(state);
47
62
  useStyleState(state);
48
- useCommentState(state);
49
63
 
50
- const handleComment = useCallback(
51
- (name: string, cursor: string) =>
52
- dispatch(
53
- createIntent(ThreadAction.Create, {
54
- cursor,
55
- name,
56
- subject: model.sheet,
57
- }),
58
- ),
59
- [model.sheet, dispatch],
60
- );
64
+ const { graph } = useAppGraph();
65
+ const customActions = useMemo(() => {
66
+ return Rx.make((get) => {
67
+ const actions = get(graph.actions(id));
68
+ const nodes = actions.filter((action) => action.properties.disposition === 'toolbar');
69
+ return { nodes, edges: nodes.map((node) => ({ source: 'root', target: node.id })) };
70
+ });
71
+ }, [graph]);
61
72
 
62
- const actionsCreator = useCallback(
63
- () => createToolbarActions(model, state, handleComment, cursorFallbackRange),
64
- [model, state, handleComment, cursorFallbackRange],
73
+ const actionsCreator = useMemo(
74
+ () => createToolbarActions(model, state, cursorFallbackRange, customActions),
75
+ [model, state, cursorFallbackRange, customActions],
65
76
  );
66
77
  const menu = useMenuActions(actionsCreator);
67
78
 
68
79
  return (
69
- <MenuProvider {...menu} attendableId={attendableId}>
80
+ <MenuProvider {...menu} attendableId={id}>
70
81
  <ToolbarMenu classNames={classNames} />
71
82
  </MenuProvider>
72
83
  );
@@ -7,10 +7,9 @@ import { useMemo } from 'react';
7
7
  import { live } from '@dxos/live-object';
8
8
 
9
9
  import { type AlignState } from './align';
10
- import { type CommentState } from './comment';
11
10
  import { type StyleState } from './style';
12
11
 
13
- export type ToolbarState = Partial<StyleState & AlignState & CommentState>;
12
+ export type ToolbarState = Partial<StyleState & AlignState>;
14
13
 
15
14
  export const useToolbarState = (initialState: ToolbarState = {}) => {
16
15
  return useMemo(() => live<ToolbarState>(initialState), []);
@@ -141,7 +141,7 @@ class ComputeWidget extends WidgetType {
141
141
  super();
142
142
  }
143
143
 
144
- override toDOM(_view: EditorView) {
144
+ override toDOM(_view: EditorView): HTMLDivElement {
145
145
  const div = document.createElement('div');
146
146
  div.setAttribute('title', this.formula);
147
147
  div.innerText = String(this.value);
@@ -15,10 +15,12 @@ import {
15
15
  } from '@dxos/app-framework';
16
16
  import { debounce } from '@dxos/async';
17
17
  import { type CellAddress, type CompleteCellRange, inRange } from '@dxos/compute';
18
+ import { isInstanceOf, RelationSourceId } from '@dxos/echo-schema';
18
19
  import { ATTENDABLE_PATH_SEPARATOR, DeckAction } from '@dxos/plugin-deck/types';
19
- import { ThreadAction } from '@dxos/plugin-thread/types';
20
- import { fullyQualifiedId } from '@dxos/react-client/echo';
20
+ import { ThreadAction, ThreadType } from '@dxos/plugin-thread/types';
21
+ import { Filter, fullyQualifiedId, getSpace, Query, useQuery } from '@dxos/react-client/echo';
21
22
  import { type DxGridElement, type DxGridPosition, type GridContentProps } from '@dxos/react-ui-grid';
23
+ import { AnchoredTo } from '@dxos/schema';
22
24
 
23
25
  import { useSheetContext } from '../components';
24
26
  import { SHEET_PLUGIN } from '../meta';
@@ -77,23 +79,19 @@ export const useSelectThreadOnCellFocus = () => {
77
79
  const { model, cursor } = useSheetContext();
78
80
  const { dispatchPromise: dispatch } = useIntentDispatcher();
79
81
 
80
- const threads = useMemo(
81
- () => model.sheet.threads?.filter((thread): thread is NonNullable<typeof thread> => !!thread) ?? [],
82
- [
83
- // TODO(thure): Surely we can find a better dependency for this…
84
- JSON.stringify(model.sheet.threads),
85
- ],
86
- );
82
+ const space = getSpace(model.sheet);
83
+ const anchors = useQuery(space, Query.select(Filter.ids(model.sheet.id)).targetOf(AnchoredTo));
87
84
 
88
85
  const selectClosestThread = useCallback(
89
86
  (cellAddress: CellAddress) => {
90
- if (!cellAddress || !threads) {
87
+ if (!cellAddress) {
91
88
  return;
92
89
  }
93
90
 
94
- const closestThread = threads?.find((ref) => {
95
- if (ref.target?.anchor) {
96
- const range = parseThreadAnchorAsCellRange(ref.target!.anchor);
91
+ const closestThread = anchors.find((anchor) => {
92
+ const source = anchor[RelationSourceId];
93
+ if (anchor.anchor && isInstanceOf(ThreadType, source)) {
94
+ const range = parseThreadAnchorAsCellRange(anchor.anchor);
97
95
  return range ? inRange(range, cellAddress) : false;
98
96
  } else {
99
97
  return false;
@@ -109,7 +107,7 @@ export const useSelectThreadOnCellFocus = () => {
109
107
  void dispatch(intent);
110
108
  }
111
109
  },
112
- [dispatch, threads],
110
+ [dispatch, anchors],
113
111
  );
114
112
 
115
113
  const debounced = useMemo(() => {
@@ -119,7 +119,7 @@ export class SheetModel extends Resource {
119
119
  /**
120
120
  * Initialize sheet and engine.
121
121
  */
122
- protected override async _open() {
122
+ protected override async _open(): Promise<void> {
123
123
  log('initialize', { id: this.id });
124
124
  initialize(this._sheet);
125
125
 
@@ -145,7 +145,7 @@ export class SheetModel extends Resource {
145
145
  * NOTE: This resets the undo history.
146
146
  * @deprecated
147
147
  */
148
- reset() {
148
+ reset(): void {
149
149
  invariant(this._node);
150
150
  this._node.graph.hf.clearSheet(this._node.sheetId);
151
151
  Object.entries(this._sheet.cells).forEach(([key, { value }]) => {
@@ -173,17 +173,17 @@ export class SheetModel extends Resource {
173
173
  * @deprecated
174
174
  */
175
175
  // TODO(burdon): Remove.
176
- recalculate() {
176
+ recalculate(): void {
177
177
  this._node?.graph.hf.rebuildAndRecalculate();
178
178
  }
179
179
 
180
- insertRows(i: number, n = 1) {
180
+ insertRows(i: number, n = 1): string[] {
181
181
  const idx = insertIndices(this._sheet.rows, i, n, MAX_ROWS);
182
182
  this.reset();
183
183
  return idx;
184
184
  }
185
185
 
186
- insertColumns(i: number, n = 1) {
186
+ insertColumns(i: number, n = 1): string[] {
187
187
  const idx = insertIndices(this._sheet.columns, i, n, MAX_COLS);
188
188
  this.reset();
189
189
  return idx;
@@ -217,7 +217,7 @@ export class SheetModel extends Resource {
217
217
  return { axis: 'col', index, axisIndex: colIndex, axisMeta: this._sheet.rowMeta[colIndex], values };
218
218
  }
219
219
 
220
- restoreRow({ index, axisIndex, axisMeta, values }: SheetAction.RestoreAxis) {
220
+ restoreRow({ index, axisIndex, axisMeta, values }: SheetAction.RestoreAxis): void {
221
221
  this._sheet.rows.splice(index, 0, axisIndex);
222
222
  values.forEach((value, col) => {
223
223
  if (value) {
@@ -230,7 +230,7 @@ export class SheetModel extends Resource {
230
230
  this.reset();
231
231
  }
232
232
 
233
- restoreColumn({ index, axisIndex, axisMeta, values }: SheetAction.RestoreAxis) {
233
+ restoreColumn({ index, axisIndex, axisMeta, values }: SheetAction.RestoreAxis): void {
234
234
  this._sheet.columns.splice(index, 0, axisIndex);
235
235
  values.forEach((value, row) => {
236
236
  if (value) {
@@ -251,7 +251,7 @@ export class SheetModel extends Resource {
251
251
  /**
252
252
  * Clear range of values.
253
253
  */
254
- clear(range: CellRange) {
254
+ clear(range: CellRange): void {
255
255
  invariant(this._node);
256
256
  const topLeft = getTopLeft(range);
257
257
  const values = this._iterRange(range, () => null);
@@ -262,7 +262,7 @@ export class SheetModel extends Resource {
262
262
  });
263
263
  }
264
264
 
265
- cut(range: CellRange) {
265
+ cut(range: CellRange): void {
266
266
  invariant(this._node);
267
267
  this._node.graph.hf.cut(toModelRange(this._node.sheetId, range));
268
268
  this._iterRange(range, (cell) => {
@@ -271,12 +271,12 @@ export class SheetModel extends Resource {
271
271
  });
272
272
  }
273
273
 
274
- copy(range: CellRange) {
274
+ copy(range: CellRange): void {
275
275
  invariant(this._node);
276
276
  this._node.graph.hf.copy(toModelRange(this._node.sheetId, range));
277
277
  }
278
278
 
279
- paste(cell: CellAddress) {
279
+ paste(cell: CellAddress): void {
280
280
  invariant(this._node);
281
281
  if (!this._node.graph.hf.isClipboardEmpty()) {
282
282
  const changes = this._node.graph.hf.paste(toSimpleCellAddress(this._node.sheetId, cell));
@@ -291,7 +291,7 @@ export class SheetModel extends Resource {
291
291
  }
292
292
 
293
293
  // TODO(burdon): Display undo/redo state.
294
- undo() {
294
+ undo(): void {
295
295
  invariant(this._node);
296
296
  if (this._node.graph.hf.isThereSomethingToUndo()) {
297
297
  this._node.graph.hf.undo();
@@ -299,7 +299,7 @@ export class SheetModel extends Resource {
299
299
  }
300
300
  }
301
301
 
302
- redo() {
302
+ redo(): void {
303
303
  invariant(this._node);
304
304
  if (this._node.graph.hf.isThereSomethingToRedo()) {
305
305
  this._node.graph.hf.redo();
@@ -368,7 +368,7 @@ export class SheetModel extends Resource {
368
368
  /**
369
369
  * Sets the value, updating the sheet and engine.
370
370
  */
371
- setValue(cell: CellAddress, value: CellScalarValue) {
371
+ setValue(cell: CellAddress, value: CellScalarValue): void {
372
372
  invariant(this._node);
373
373
  if (this._options.readonly) {
374
374
  throw new ReadonlyException();
@@ -411,7 +411,7 @@ export class SheetModel extends Resource {
411
411
  /**
412
412
  * Sets values from a simple map.
413
413
  */
414
- setValues(values: Record<string, CellValue>) {
414
+ setValues(values: Record<string, CellValue>): void {
415
415
  Object.entries(values).forEach(([key, { value }]) => {
416
416
  this.setValue(addressFromA1Notation(key), value);
417
417
  });
@@ -441,12 +441,12 @@ export class SheetModel extends Resource {
441
441
  }
442
442
 
443
443
  // TODO(burdon): Delete index.
444
- private _deleteIndices(indices: string[], i: number, n: number) {
444
+ private _deleteIndices(indices: string[], i: number, n: number): void {
445
445
  throw new Error('Not implemented');
446
446
  }
447
447
 
448
448
  // TODO(burdon): Move. Cannot use fractional without changing. Switch back to using unique IDs?
449
- private _moveIndices(indices: string[], i: number, j: number, n: number) {
449
+ private _moveIndices(indices: string[], i: number, j: number, n: number): void {
450
450
  throw new Error('Not implemented');
451
451
  }
452
452
 
package/src/serializer.ts CHANGED
@@ -9,8 +9,7 @@ import { SheetType } from './types';
9
9
 
10
10
  export const serializer: TypedObjectSerializer<SheetType> = {
11
11
  serialize: async ({ object }): Promise<string> => {
12
- const { threads: _threads, ...sheet } = object;
13
- return JSON.stringify(sheet, null, 2);
12
+ return JSON.stringify(object, null, 2);
14
13
  },
15
14
 
16
15
  deserialize: async ({ content, newId }) => {
@@ -25,21 +25,21 @@ export class SheetManager {
25
25
  return this.cellByText('Ready').waitFor({ state: 'visible' });
26
26
  }
27
27
 
28
- async fill(text: string) {
28
+ async fill(text: string): Promise<void> {
29
29
  // TODO(thure): Do these timeouts help with test flakiness?
30
30
  await this.page.waitForTimeout(200);
31
31
  await this.cellEditor().fill(text);
32
32
  await this.page.waitForTimeout(200);
33
33
  }
34
34
 
35
- async press(key: string) {
35
+ async press(key: string): Promise<void> {
36
36
  // TODO(thure): Does these timeouts help with test flakiness?
37
37
  await this.page.waitForTimeout(200);
38
38
  await this.page.keyboard.press(key);
39
39
  await this.page.waitForTimeout(200);
40
40
  }
41
41
 
42
- async commit(key: string) {
42
+ async commit(key: string): Promise<void> {
43
43
  // TODO(thure): Why do we need to wait? Enter is ignored otherwise…
44
44
  await this.page.waitForTimeout(500);
45
45
  await this.press(key);
@@ -49,7 +49,7 @@ export class SheetManager {
49
49
  return this.grid.grid.getByText(text);
50
50
  }
51
51
 
52
- async setFocusedCellValue(text: string, commitKey: string) {
52
+ async setFocusedCellValue(text: string, commitKey: string): Promise<void> {
53
53
  const mode = await this.grid.mode();
54
54
  if (mode === 'browse') {
55
55
  await this.commit('Enter');
@@ -58,7 +58,7 @@ export class SheetManager {
58
58
  await this.commit(commitKey);
59
59
  }
60
60
 
61
- async selectRange(start: DxGridPosition, end: DxGridPosition) {
61
+ async selectRange(start: DxGridPosition, end: DxGridPosition): Promise<void> {
62
62
  const startCell = this.grid.cell(start.col, start.row, start.plane);
63
63
  const endCell = this.grid.cell(end.col, end.row, end.plane);
64
64
  const startBox = await startCell.boundingBox();
@@ -69,7 +69,7 @@ export class SheetManager {
69
69
  });
70
70
  }
71
71
 
72
- async deleteAxis(axis: DxGridAxis, position: number) {
72
+ async deleteAxis(axis: DxGridAxis, position: number): Promise<void> {
73
73
  const col = axis === 'row' ? 0 : position;
74
74
  const row = axis === 'row' ? position : 0;
75
75
  const plane = axis === 'row' ? 'frozenColsStart' : 'frozenRowsStart';
@@ -77,15 +77,15 @@ export class SheetManager {
77
77
  await this.page.getByTestId(`grid.${axis}.drop`).click();
78
78
  }
79
79
 
80
- toolbarAction(key: string, value: string) {
80
+ toolbarAction(key: string, value: string): Locator {
81
81
  return this.page.getByTestId(`grid.toolbar.${key}.${value}`);
82
82
  }
83
83
 
84
- cellEditor() {
84
+ cellEditor(): Locator {
85
85
  return this.page.getByTestId('grid.cell-editor').getByRole('textbox');
86
86
  }
87
87
 
88
- rangeInList(a1Coords: string) {
88
+ rangeInList(a1Coords: string): Locator {
89
89
  return this.page.getByTestId('grid.range-list').getByText(a1Coords);
90
90
  }
91
91
  }
@@ -4,8 +4,7 @@
4
4
 
5
5
  import { Schema } from 'effect';
6
6
 
7
- import { Ref, TypedObject } from '@dxos/echo-schema';
8
- import { ThreadType } from '@dxos/plugin-space/types';
7
+ import { TypedObject } from '@dxos/echo-schema';
9
8
 
10
9
  export const CellValue = Schema.Struct({
11
10
  // TODO(burdon): How to store dates (datetime, date, time), percentages, etc.
@@ -52,7 +51,4 @@ export class SheetType extends TypedObject({ typename: 'dxos.org/type/Sheet', ve
52
51
 
53
52
  // Cell formatting referenced by indexed range.
54
53
  ranges: Schema.mutable(Schema.Array(Range)),
55
-
56
- // Threads associated with the sheet
57
- threads: Schema.optional(Schema.mutable(Schema.Array(Ref(ThreadType)))),
58
54
  }) {}
package/src/types/util.ts CHANGED
@@ -76,7 +76,6 @@ export const createSheet = ({ name, cells, ...size }: CreateSheetOptions = {}):
76
76
  rowMeta: {},
77
77
  columnMeta: {},
78
78
  ranges: [],
79
- threads: [],
80
79
  });
81
80
 
82
81
  initialize(sheet, size);
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/components/SheetContainer/SheetContainer.tsx", "../../../src/components/FunctionEditor/FunctionEditor.tsx", "../../../src/components/SheetToolbar/SheetToolbar.tsx", "../../../src/components/SheetToolbar/align.ts", "../../../src/components/SheetToolbar/comment.ts", "../../../src/components/SheetToolbar/style.ts", "../../../src/components/SheetToolbar/useToolbarState.ts", "../../../src/components/SheetContainer/index.ts"],
4
- "sourcesContent": ["//\n// Copyright 2023 DXOS.org\n//\n\nimport React from 'react';\n\nimport { fullyQualifiedId, type Space } from '@dxos/react-client/echo';\nimport { StackItem } from '@dxos/react-ui-stack';\n\nimport { type SheetType } from '../../types';\nimport { useComputeGraph } from '../ComputeGraph';\nimport { FunctionEditor } from '../FunctionEditor';\nimport { GridSheet } from '../GridSheet';\nimport { SheetProvider } from '../SheetContext';\nimport { SheetToolbar } from '../SheetToolbar';\n\nexport type SheetContainerProps = {\n space: Space;\n sheet: SheetType;\n role?: string;\n ignoreAttention?: boolean;\n};\n\nexport const SheetContainer = ({ space, sheet, role, ignoreAttention }: SheetContainerProps) => {\n const graph = useComputeGraph(space);\n\n return graph ? (\n <SheetProvider sheet={sheet} graph={graph} ignoreAttention={ignoreAttention}>\n <StackItem.Content toolbar statusbar {...(role === 'section' && { classNames: 'aspect-video' })}>\n <SheetToolbar attendableId={fullyQualifiedId(sheet)} />\n <GridSheet />\n <FunctionEditor />\n </StackItem.Content>\n </SheetProvider>\n ) : null;\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport React from 'react';\n\nimport { addressToA1Notation, isFormula, rangeToA1Notation } from '@dxos/compute';\nimport { Icon } from '@dxos/react-ui';\n\nimport { mapFormulaIndicesToRefs } from '../../types';\nimport { useSheetContext } from '../SheetContext';\n\nexport const FunctionEditor = () => {\n const { model, cursor, range } = useSheetContext();\n\n let value;\n let formula = false;\n if (cursor) {\n value = model.getCellValue(cursor);\n if (isFormula(value)) {\n value = model.graph.mapFunctionBindingFromId(mapFormulaIndicesToRefs(model.sheet, value));\n formula = true;\n } else if (value != null) {\n value = String(value);\n }\n }\n\n return (\n <div className='flex shrink-0 justify-between items-center px-4 py-1 text-sm bg-toolbarSurface border-bs !border-separator'>\n <div className='flex gap-4 items-center'>\n <div className='flex w-16 items-center font-mono'>\n {(range && rangeToA1Notation(range)) || (cursor && addressToA1Notation(cursor))}\n </div>\n <div className='flex gap-2 items-center'>\n <Icon icon='ph--function--regular' classNames={['text-greenText', formula ? 'visible' : 'invisible']} />\n <span className='font-mono'>{value}</span>\n </div>\n </div>\n </div>\n );\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport React, { type PropsWithChildren, useCallback } from 'react';\n\nimport { createIntent, useIntentDispatcher } from '@dxos/app-framework';\nimport { type CompleteCellRange } from '@dxos/compute';\nimport { ThreadAction } from '@dxos/plugin-thread/types';\nimport { type ThemedClassName } from '@dxos/react-ui';\nimport { createGapSeparator, MenuProvider, ToolbarMenu, useMenuActions } from '@dxos/react-ui-menu';\n\nimport { createAlign, useAlignState } from './align';\nimport { createComment, useCommentState } from './comment';\nimport { createStyle, useStyleState } from './style';\nimport { type ToolbarState, useToolbarState } from './useToolbarState';\nimport { type SheetModel } from '../../model';\nimport { useSheetContext } from '../SheetContext';\n\n//\n// Root\n//\n\nexport type SheetToolbarProps = ThemedClassName<PropsWithChildren<{ attendableId?: string }>>;\n\nconst createToolbarActions = (\n model: SheetModel,\n state: ToolbarState,\n onComment: (cellContent: string, cursor: string) => void,\n cursorFallbackRange?: CompleteCellRange,\n) => {\n const align = createAlign(model, state, cursorFallbackRange);\n const style = createStyle(model, state, cursorFallbackRange);\n const gap = createGapSeparator();\n const comment = createComment(model, state, onComment, cursorFallbackRange);\n return {\n nodes: [...align.nodes, ...style.nodes, ...gap.nodes, ...comment.nodes],\n edges: [...align.edges, ...style.edges, ...gap.edges, ...comment.edges],\n };\n};\n\nexport const SheetToolbar = ({ attendableId, classNames }: SheetToolbarProps) => {\n const { dispatchPromise: dispatch } = useIntentDispatcher();\n const { model, cursorFallbackRange } = useSheetContext();\n const state = useToolbarState({});\n useAlignState(state);\n useStyleState(state);\n useCommentState(state);\n\n const handleComment = useCallback(\n (name: string, cursor: string) =>\n dispatch(\n createIntent(ThreadAction.Create, {\n cursor,\n name,\n subject: model.sheet,\n }),\n ),\n [model.sheet, dispatch],\n );\n\n const actionsCreator = useCallback(\n () => createToolbarActions(model, state, handleComment, cursorFallbackRange),\n [model, state, handleComment, cursorFallbackRange],\n );\n const menu = useMenuActions(actionsCreator);\n\n return (\n <MenuProvider {...menu} attendableId={attendableId}>\n <ToolbarMenu classNames={classNames} />\n </MenuProvider>\n );\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { useEffect } from 'react';\n\nimport { type CompleteCellRange, inRange } from '@dxos/compute';\nimport { createMenuAction, createMenuItemGroup, type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';\n\nimport { type ToolbarState } from './useToolbarState';\nimport { SHEET_PLUGIN } from '../../meta';\nimport { type SheetModel } from '../../model';\nimport { type AlignKey, alignKey, type AlignValue, rangeFromIndex, rangeToIndex } from '../../types';\nimport { useSheetContext } from '../SheetContext';\n\nexport type AlignAction = { key: AlignKey; value: AlignValue };\n\nexport type AlignState = { [alignKey]: AlignValue | undefined };\n\nconst aligns: Record<AlignValue, string> = {\n start: 'ph--text-align-left--regular',\n center: 'ph--text-align-center--regular',\n end: 'ph--text-align-right--regular',\n};\n\nexport const useAlignState = (state: Partial<AlignState>) => {\n const { cursor, model } = useSheetContext();\n useEffect(() => {\n // TODO(thure): Can this O(n) call be memoized?\n state[alignKey] = (\n cursor\n ? model.sheet.ranges?.findLast(\n ({ range, key }) => key === alignKey && inRange(rangeFromIndex(model.sheet, range), cursor),\n )?.value\n : undefined\n ) as AlignValue | undefined;\n }, [cursor, model.sheet]);\n};\n\nconst createAlignGroupAction = (value?: AlignValue) =>\n createMenuItemGroup('align', {\n label: ['align label', { ns: SHEET_PLUGIN }],\n variant: 'toggleGroup',\n selectCardinality: 'single',\n value: `${alignKey}--${value}`,\n } as ToolbarMenuActionGroupProperties);\n\nconst createAlignActions = (model: SheetModel, state: ToolbarState, cursorFallbackRange?: CompleteCellRange) =>\n Object.entries(aligns).map(([alignValue, icon]) => {\n return createMenuAction<AlignAction>(\n `${alignKey}--${alignValue}`,\n () => {\n if (!cursorFallbackRange) {\n return;\n }\n const index =\n model.sheet.ranges?.findIndex(\n (range) =>\n range.key === alignKey && inRange(rangeFromIndex(model.sheet, range.range), cursorFallbackRange.from),\n ) ?? -1;\n const nextRangeEntity = {\n range: rangeToIndex(model.sheet, cursorFallbackRange),\n key: alignKey,\n value: alignValue as AlignValue,\n };\n if (index < 0) {\n model.sheet.ranges?.push(nextRangeEntity);\n state[alignKey] = nextRangeEntity.value;\n } else if (model.sheet.ranges![index].value === nextRangeEntity.value) {\n model.sheet.ranges?.splice(index, 1);\n state[alignKey] = undefined;\n } else {\n model.sheet.ranges?.splice(index, 1, nextRangeEntity);\n state[alignKey] = nextRangeEntity.value;\n }\n },\n {\n key: alignKey,\n value: alignValue as AlignValue,\n checked: state[alignKey] === alignValue,\n label: [`range value ${alignValue} label`, { ns: SHEET_PLUGIN }],\n icon,\n testId: `grid.toolbar.${alignKey}.${alignValue}`,\n },\n );\n });\n\nexport const createAlign = (model: SheetModel, state: ToolbarState, cursorFallbackRange?: CompleteCellRange) => {\n const alignGroup = createAlignGroupAction(state[alignKey]);\n const alignActions = createAlignActions(model, state, cursorFallbackRange);\n return {\n nodes: [alignGroup, ...alignActions],\n edges: [\n { source: 'root', target: 'align' },\n ...alignActions.map(({ id }) => ({ source: alignGroup.id, target: id })),\n ],\n };\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { useEffect, useMemo } from 'react';\n\nimport { type CompleteCellRange } from '@dxos/compute';\nimport { RefArray } from '@dxos/live-object';\nimport { createMenuAction } from '@dxos/react-ui-menu';\n\nimport { completeCellRangeToThreadCursor } from '../../integrations/thread-ranges';\nimport { SHEET_PLUGIN } from '../../meta';\nimport { type SheetModel } from '../../model';\nimport { commentKey, type CommentKey, type CommentValue, rangeToIndex } from '../../types';\nimport { useSheetContext } from '../SheetContext';\n\nexport type CommentAction = { key: CommentKey; value: CommentValue; cellContent?: string };\n\nexport type CommentState = { commentEnabled: 'comment' | 'no cursor' | 'selection overlaps existing comment' };\n\nexport const useCommentState = (state: Partial<CommentState>) => {\n const { cursorFallbackRange, model } = useSheetContext();\n\n // TODO(thure): Can this O(n) call be memoized?\n const overlapsCommentAnchor = useMemo(\n () =>\n RefArray.targets(model.sheet.threads ?? [])\n .filter((thread) => thread.status !== 'resolved')\n .some((thread) => {\n if (!cursorFallbackRange) {\n return false;\n }\n return rangeToIndex(model.sheet, cursorFallbackRange) === thread.anchor;\n }),\n [cursorFallbackRange, model.sheet],\n );\n\n useEffect(() => {\n state.commentEnabled = !cursorFallbackRange\n ? 'no cursor'\n : overlapsCommentAnchor\n ? 'selection overlaps existing comment'\n : 'comment';\n }, [overlapsCommentAnchor, cursorFallbackRange]);\n};\n\nconst createCommentAction = (\n model: SheetModel,\n state: Partial<CommentState>,\n onComment: (cellContent: string, cursor: string) => void,\n cursorFallbackRange?: CompleteCellRange,\n) =>\n createMenuAction<Pick<CommentAction, 'key'>>(\n 'comment',\n () => {\n if (!cursorFallbackRange) {\n return;\n }\n\n const cellContent = model.getCellText(cursorFallbackRange.from);\n if (!cellContent) {\n return;\n }\n\n onComment(cellContent, completeCellRangeToThreadCursor(cursorFallbackRange));\n },\n {\n key: commentKey,\n testId: 'editor.toolbar.comment',\n icon: 'ph--chat-text--regular',\n label: [`${state.commentEnabled} label`, { ns: SHEET_PLUGIN }],\n disabled: state.commentEnabled !== 'comment',\n },\n );\n\nexport const createComment = (\n model: SheetModel,\n state: Partial<CommentState>,\n onComment: (cellContent: string, cursor: string) => void,\n cursorFallbackRange?: CompleteCellRange,\n) => ({\n nodes: [createCommentAction(model, state, onComment, cursorFallbackRange)],\n edges: [{ source: 'root', target: 'comment' }],\n});\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { useEffect } from 'react';\n\nimport { type CompleteCellRange, inRange } from '@dxos/compute';\nimport { createMenuAction, createMenuItemGroup, type ToolbarMenuActionGroupProperties } from '@dxos/react-ui-menu';\n\nimport { SHEET_PLUGIN } from '../../meta';\nimport { type SheetModel } from '../../model';\nimport { rangeFromIndex, rangeToIndex, type StyleKey, type StyleValue } from '../../types';\nimport { useSheetContext } from '../SheetContext';\n\nexport type StyleState = Partial<Record<StyleValue, boolean>>;\n\nexport type StyleAction = { key: StyleKey; value: StyleValue };\n\nconst styles: Record<StyleValue, string> = {\n highlight: 'ph--highlighter--regular',\n softwrap: 'ph--paragraph--regular',\n};\n\nexport const useStyleState = (state: StyleState) => {\n const { cursorFallbackRange, model } = useSheetContext();\n\n useEffect(() => {\n state.highlight = false;\n state.softwrap = false;\n if (cursorFallbackRange && model.sheet.ranges) {\n model.sheet.ranges\n .filter(\n ({ range, key }) => key === 'style' && inRange(rangeFromIndex(model.sheet, range), cursorFallbackRange.from),\n )\n .forEach(({ value }) => {\n state[value as StyleValue] = true;\n });\n }\n }, [cursorFallbackRange, model.sheet]);\n};\n\nconst createStyleGroup = (state: StyleState) => {\n return createMenuItemGroup('style', {\n variant: 'toggleGroup',\n selectCardinality: 'multiple',\n value: Object.keys(styles)\n .filter((key) => !!state[key as StyleValue])\n .map((styleValue) => `style--${styleValue}`),\n } as ToolbarMenuActionGroupProperties);\n};\n\nconst createStyleActions = (model: SheetModel, state: StyleState, cursorFallbackRange?: CompleteCellRange) =>\n Object.entries(styles).map(([styleValue, icon]) => {\n return createMenuAction<StyleAction>(\n `style--${styleValue}`,\n () => {\n if (!cursorFallbackRange) {\n return;\n }\n const index =\n model.sheet.ranges?.findIndex(\n (range) =>\n range.key === 'style' && inRange(rangeFromIndex(model.sheet, range.range), cursorFallbackRange.from),\n ) ?? -1;\n const nextRangeEntity = {\n range: rangeToIndex(model.sheet, cursorFallbackRange),\n key: 'style',\n value: styleValue as StyleValue,\n };\n if (\n model.sheet.ranges\n .filter(\n ({ range, key: rangeKey }) =>\n rangeKey === 'style' && inRange(rangeFromIndex(model.sheet, range), cursorFallbackRange.from),\n )\n .some(({ value: rangeValue }) => rangeValue === styleValue)\n ) {\n // this value should be unset\n if (index >= 0) {\n model.sheet.ranges?.splice(index, 1);\n }\n state[nextRangeEntity.value] = false;\n } else {\n model.sheet.ranges?.push(nextRangeEntity);\n state[nextRangeEntity.value] = true;\n }\n },\n {\n key: 'style',\n value: styleValue as StyleValue,\n icon,\n label: [`range value ${styleValue} label`, { ns: SHEET_PLUGIN }],\n checked: !!state[styleValue as StyleValue],\n },\n );\n });\n\nexport const createStyle = (model: SheetModel, state: StyleState, cursorFallbackRange?: CompleteCellRange) => {\n const styleGroupAction = createStyleGroup(state);\n const styleActions = createStyleActions(model, state, cursorFallbackRange);\n return {\n nodes: [styleGroupAction, ...styleActions],\n edges: [\n { source: 'root', target: 'style' },\n ...styleActions.map(({ id }) => ({ source: styleGroupAction.id, target: id })),\n ],\n };\n};\n", "//\n// Copyright 2025 DXOS.org\n//\n\nimport { useMemo } from 'react';\n\nimport { live } from '@dxos/live-object';\n\nimport { type AlignState } from './align';\nimport { type CommentState } from './comment';\nimport { type StyleState } from './style';\n\nexport type ToolbarState = Partial<StyleState & AlignState & CommentState>;\n\nexport const useToolbarState = (initialState: ToolbarState = {}) => {\n return useMemo(() => live<ToolbarState>(initialState), []);\n};\n", "//\n// Copyright 2024 DXOS.org\n//\n\nimport { SheetContainer } from './SheetContainer';\n\nexport default SheetContainer;\n"],
5
- "mappings": "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",
6
- "names": ["React", "fullyQualifiedId", "StackItem", "React", "addressToA1Notation", "isFormula", "rangeToA1Notation", "Icon", "FunctionEditor", "model", "cursor", "range", "useSheetContext", "value", "formula", "getCellValue", "isFormula", "graph", "mapFunctionBindingFromId", "mapFormulaIndicesToRefs", "sheet", "String", "div", "className", "rangeToA1Notation", "addressToA1Notation", "Icon", "icon", "classNames", "span", "React", "useCallback", "createIntent", "useIntentDispatcher", "ThreadAction", "createGapSeparator", "MenuProvider", "ToolbarMenu", "useMenuActions", "useEffect", "inRange", "createMenuAction", "createMenuItemGroup", "aligns", "start", "center", "end", "useAlignState", "state", "cursor", "model", "useSheetContext", "useEffect", "alignKey", "sheet", "ranges", "findLast", "range", "key", "inRange", "rangeFromIndex", "value", "undefined", "createAlignGroupAction", "createMenuItemGroup", "label", "ns", "SHEET_PLUGIN", "variant", "selectCardinality", "createAlignActions", "cursorFallbackRange", "Object", "entries", "map", "alignValue", "icon", "createMenuAction", "index", "findIndex", "from", "nextRangeEntity", "rangeToIndex", "push", "splice", "checked", "testId", "createAlign", "alignGroup", "alignActions", "nodes", "edges", "source", "target", "id", "useEffect", "useMemo", "RefArray", "createMenuAction", "useCommentState", "state", "cursorFallbackRange", "model", "useSheetContext", "overlapsCommentAnchor", "useMemo", "RefArray", "targets", "sheet", "threads", "filter", "thread", "status", "some", "rangeToIndex", "anchor", "useEffect", "commentEnabled", "createCommentAction", "onComment", "createMenuAction", "cellContent", "getCellText", "from", "completeCellRangeToThreadCursor", "key", "commentKey", "testId", "icon", "label", "ns", "SHEET_PLUGIN", "disabled", "createComment", "nodes", "edges", "source", "target", "useEffect", "inRange", "createMenuAction", "createMenuItemGroup", "styles", "highlight", "softwrap", "useStyleState", "state", "cursorFallbackRange", "model", "useSheetContext", "useEffect", "sheet", "ranges", "filter", "range", "key", "inRange", "rangeFromIndex", "from", "forEach", "value", "createStyleGroup", "createMenuItemGroup", "variant", "selectCardinality", "Object", "keys", "map", "styleValue", "createStyleActions", "entries", "icon", "createMenuAction", "index", "findIndex", "nextRangeEntity", "rangeToIndex", "rangeKey", "some", "rangeValue", "splice", "push", "label", "ns", "SHEET_PLUGIN", "checked", "createStyle", "styleGroupAction", "styleActions", "nodes", "edges", "source", "target", "id", "useMemo", "live", "useToolbarState", "initialState", "useMemo", "live", "createToolbarActions", "model", "state", "onComment", "cursorFallbackRange", "align", "createAlign", "style", "createStyle", "gap", "createGapSeparator", "comment", "createComment", "nodes", "edges", "SheetToolbar", "attendableId", "classNames", "dispatchPromise", "dispatch", "useIntentDispatcher", "useSheetContext", "useToolbarState", "useAlignState", "useStyleState", "useCommentState", "handleComment", "useCallback", "name", "cursor", "createIntent", "ThreadAction", "Create", "subject", "sheet", "actionsCreator", "menu", "useMenuActions", "React", "MenuProvider", "ToolbarMenu", "SheetContainer", "space", "sheet", "role", "ignoreAttention", "graph", "useComputeGraph", "React", "SheetProvider", "StackItem", "Content", "toolbar", "statusbar", "classNames", "SheetToolbar", "attendableId", "fullyQualifiedId", "GridSheet", "FunctionEditor", "SheetContainer"]
7
- }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../../src/extensions/compute.ts", "../../../src/extensions/editor/extension.ts"],
4
- "sourcesContent": ["//\n// Copyright 2024 DXOS.org\n//\n\nimport { syntaxTree } from '@codemirror/language';\nimport {\n type EditorState,\n type Extension,\n type RangeSet,\n RangeSetBuilder,\n StateEffect,\n StateField,\n type Transaction,\n} from '@codemirror/state';\nimport { Decoration, EditorView, ViewPlugin, WidgetType } from '@codemirror/view';\n\nimport { type CleanupFn, debounce } from '@dxos/async';\nimport {\n createSheetName,\n type CellAddress,\n type CellScalarValue,\n type ComputeGraph,\n type ComputeNode,\n} from '@dxos/compute';\nimport { invariant } from '@dxos/invariant';\nimport { documentId, singleValueFacet } from '@dxos/react-ui-editor';\n\nconst LANGUAGE_TAG = 'dx';\n\n// TODO(burdon): Create marker just for our decorator?\nconst updateAllDecorations = StateEffect.define<void>();\n\nexport const computeGraphFacet = singleValueFacet<ComputeGraph>();\n\nexport type ComputeOptions = {};\n\nexport const compute = (options: ComputeOptions = {}): Extension => {\n let computeNode: ComputeNode | undefined;\n\n const update = (state: EditorState, current?: RangeSet<Decoration>) => {\n const builder = new RangeSetBuilder<Decoration>();\n if (computeNode) {\n computeNode.clear();\n syntaxTree(state).iterate({\n enter: (node) => {\n switch (node.name) {\n case 'FencedCode': {\n const cursor = state.selection.main.head;\n if (state.readOnly || cursor < node.from || cursor > node.to) {\n const info = node.node.getChild('CodeInfo');\n if (info) {\n const type = state.sliceDoc(info.from, info.to);\n const text = node.node.getChild('CodeText');\n if (type === LANGUAGE_TAG && text) {\n const formula = state.sliceDoc(text.from, text.to);\n\n const iter = current?.iter(node.node.from);\n if (iter?.value && iter?.value.spec.formula === formula) {\n // Add existing widget.\n builder.add(node.from, node.to, iter.value);\n } else {\n // TODO(burdon): Create ordered list of cells on each decoration run.\n const cell: CellAddress = { col: node.node.from, row: 0 };\n invariant(computeNode);\n // NOTE: This triggers re-render (below).\n computeNode.setValue(cell, formula);\n const value = computeNode.getValue(cell);\n builder.add(\n node.from,\n node.to,\n Decoration.replace({\n widget: new ComputeWidget(formula, value),\n formula,\n }),\n );\n }\n }\n }\n }\n\n break;\n }\n }\n },\n });\n }\n\n return builder.finish();\n };\n\n return [\n ViewPlugin.fromClass(\n class {\n // Graph subscription.\n private _subscription?: CleanupFn;\n constructor(view: EditorView) {\n const id = view.state.facet(documentId);\n const computeGraph = view.state.facet(computeGraphFacet);\n if (id && computeGraph) {\n queueMicrotask(async () => {\n computeNode = computeGraph.getOrCreateNode(createSheetName({ type: '', id }));\n await computeNode.open();\n\n // Trigger re-render if values updated.\n // TODO(burdon): Trigger only if formula value updated (currently triggered during render).\n this._subscription = computeNode.update.on(\n debounce(({ type, ...rest }) => {\n if (type === 'valuesUpdated') {\n view.dispatch({\n effects: updateAllDecorations.of(),\n });\n }\n }, 250),\n );\n });\n }\n }\n\n destroy() {\n this._subscription?.();\n void computeNode?.close();\n computeNode = undefined;\n }\n },\n ),\n\n StateField.define<RangeSet<Decoration>>({\n create: (state) => update(state),\n update: (rangeSet: RangeSet<Decoration>, tr: Transaction) => update(tr.state, rangeSet),\n provide: (field) => EditorView.decorations.from(field),\n }),\n ];\n};\n\n// TODO(burdon): Click to edit.\nclass ComputeWidget extends WidgetType {\n constructor(\n private readonly formula: string,\n private readonly value: CellScalarValue,\n ) {\n super();\n }\n\n override toDOM(_view: EditorView) {\n const div = document.createElement('div');\n div.setAttribute('title', this.formula);\n div.innerText = String(this.value);\n return div;\n }\n}\n", "//\n// Copyright 2023 DXOS.org\n//\n\nimport {\n type Completion,\n type CompletionContext,\n type CompletionResult,\n acceptCompletion,\n autocompletion,\n completionStatus,\n startCompletion,\n} from '@codemirror/autocomplete';\nimport { HighlightStyle, type Language, syntaxHighlighting } from '@codemirror/language';\nimport { type Extension } from '@codemirror/state';\nimport { type EditorView, ViewPlugin, type ViewUpdate, keymap } from '@codemirror/view';\nimport { type SyntaxNode } from '@lezer/common';\nimport { tags } from '@lezer/highlight';\nimport { spreadsheet } from 'codemirror-lang-spreadsheet';\n\nimport { type FunctionDefinition } from '@dxos/compute';\nimport { RANGE_NOTATION } from '@dxos/compute';\nimport { singleValueFacet } from '@dxos/react-ui-editor';\nimport { mx } from '@dxos/react-ui-theme';\n\n/**\n * https://codemirror.net/examples/styling\n * https://lezer.codemirror.net/docs/ref/#highlight\n * https://github.com/luizzappa/codemirror-lang-spreadsheet/blob/main/src/index.ts#L28 (mapping)\n */\n// TODO(burdon): Define light/dark.\nconst highlightStyles = HighlightStyle.define([\n // Function.\n {\n tag: tags.name,\n class: 'text-accentText',\n },\n // Range.\n {\n tag: tags.tagName,\n class: 'text-pinkText',\n },\n // Values.\n {\n tag: tags.number,\n class: 'text-tealText',\n },\n {\n tag: tags.bool,\n class: 'text-tealText',\n },\n {\n tag: tags.string,\n class: 'text-tealText',\n },\n // Error.\n {\n tag: tags.invalid,\n class: 'text-unAccent',\n },\n]);\n\nconst languageFacet = singleValueFacet<Language>();\n\nexport type SheetExtensionOptions = {\n debug?: boolean;\n functions?: FunctionDefinition[];\n};\n\n/**\n * Spreadsheet formula extension and parser.\n * https://github.com/luizzappa/codemirror-lang-spreadsheet\n * https://github.com/luizzappa/codemirror-app-spreadsheet/blob/master/src/editor.ts\n * https://github.com/codemirror/lang-example\n * https://hyperformula.handsontable.com/guide/key-concepts.html#grammar\n */\nexport const sheetExtension = ({ debug, functions = [] }: SheetExtensionOptions): Extension => {\n const { extension, language } = spreadsheet({ idiom: 'en-US', decimalSeparator: '.' });\n\n const createCompletion = (name: string) => {\n const { section = 'Custom', description, syntax } = functions.find((value) => value.name === name) ?? {};\n\n return {\n section,\n label: name,\n info: () => {\n if (!description && !syntax) {\n return null;\n }\n\n // TODO(burdon): Standardize color styles.\n const root = document.createElement('div');\n root.className = 'flex flex-col gap-2 text-sm';\n\n const title = document.createElement('h2');\n title.innerText = name;\n title.className = 'text-lg font-mono text-accentText';\n root.appendChild(title);\n\n if (description) {\n const info = document.createElement('p');\n info.innerText = description;\n info.className = 'text-subdued';\n root.appendChild(info);\n }\n\n if (syntax) {\n const detail = document.createElement('pre');\n detail.innerText = syntax;\n detail.className = 'whitespace-pre-wrap text-greenText';\n root.appendChild(detail);\n }\n\n return root;\n },\n apply: (view, completion, from, to) => {\n const insertParens = to === view.state.doc.toString().length;\n view.dispatch(\n view.state.update({\n changes: {\n from,\n to,\n insert: completion.label + (insertParens ? '()' : ''),\n },\n selection: {\n anchor: from + completion.label.length + 1,\n },\n }),\n );\n },\n } satisfies Completion;\n };\n\n return [\n extension,\n languageFacet.of(language),\n language.data.of({\n autocomplete: (context: CompletionContext): CompletionResult | null => {\n if (context.state.doc.toString()[0] !== '=') {\n return null;\n }\n const match = context.matchBefore(/\\w*/);\n if (!match || match.from === match.to) {\n return null;\n }\n\n const text = match.text.toUpperCase();\n if (!context.explicit && match.text.length < 2) {\n return null;\n }\n\n return {\n from: match.from,\n options:\n functions?.filter(({ name }) => name.startsWith(text)).map(({ name }) => createCompletion(name)) ?? [],\n };\n },\n }),\n\n syntaxHighlighting(highlightStyles),\n autocompletion({\n aboveCursor: false,\n defaultKeymap: true,\n activateOnTyping: true,\n closeOnBlur: !debug,\n icons: false,\n tooltipClass: () =>\n mx(\n '!-left-[1px] !top-[33px] !-m-0 border !border-t-0 [&>ul]:!min-w-[198px]',\n '[&>ul>li[aria-selected]]:!bg-accentSurface',\n 'border-separator',\n ),\n }),\n keymap.of([\n {\n key: 'Tab',\n run: (view) => {\n return completionStatus(view.state) === 'active' ? acceptCompletion(view) : startCompletion(view);\n },\n },\n ]),\n\n // Parsing.\n // StateField.define({\n // create: (state) => {},\n // update: (value, tr) => {\n // log.info('update');\n // syntaxTree(tr.state).iterate({\n // enter: ({ type, from, to }) => {\n // log.info('node', { type: type.name, from, to });\n // },\n // });\n // },\n // }),\n ];\n};\n\nexport type SelectionRange = { from: number; to: number };\n\nexport interface RangeController {\n setRange(range: string): void;\n}\n\nexport type RangeExtensionOptions = {\n /**\n * Provides controller callback when extension is initialized.\n */\n onInit?: (controller: RangeController) => void;\n /**\n * Called when the active range changes.\n * @param state The current state.\n * @param state.activeRange undefined if no range is active, otherwise a possibly partially defined range.\n */\n onStateChange?: (state: { activeRange: string | undefined }) => void;\n};\n\n/**\n * Tracks the currently active cell within a formula and provides a callback to modify it.\n */\nexport const rangeExtension = ({ onInit, onStateChange }: RangeExtensionOptions): Extension => {\n let view: EditorView;\n let activeRange: SelectionRange | undefined;\n\n // Called externally to provide current range.\n const notifier: RangeController = {\n setRange: (range: string) => {\n if (activeRange) {\n view.dispatch(\n view.state.update({\n changes: { ...activeRange, insert: range.toString() },\n selection: { anchor: activeRange.from + range.length },\n }),\n );\n }\n\n view.focus();\n },\n };\n\n return ViewPlugin.fromClass(\n class {\n constructor(_view: EditorView) {\n view = _view;\n onInit?.(notifier);\n }\n\n update(view: ViewUpdate) {\n const { anchor } = view.state.selection.ranges[0];\n\n // Find first Range or cell at cursor.\n activeRange = undefined;\n const language = view.state.facet(languageFacet);\n const { topNode } = language.parser.parse(view.state.doc.toString());\n visitTree(topNode, ({ type, from, to }) => {\n if (from <= anchor && to >= anchor) {\n switch (type.name) {\n case 'Function':\n // Mark but keep looking.\n activeRange = { from: to, to };\n break;\n\n case 'CloseParen':\n // Mark but keep looking.\n activeRange = { from, to: from };\n break;\n\n case 'RangeToken':\n case 'CellToken':\n activeRange = { from, to };\n return true;\n }\n }\n\n return false;\n });\n\n // Allow start of formula.\n if (!activeRange && view.state.doc.toString()[0] === '=') {\n const str = view.state.doc.sliceString(1);\n if (RANGE_NOTATION.test(str)) {\n activeRange = { from: 1, to: str.length + 1 };\n } else {\n activeRange = { from: str.length + 1, to: str.length + 1 };\n }\n }\n\n onStateChange?.({\n activeRange: activeRange ? view.state.doc.sliceString(activeRange.from, activeRange.to) : undefined,\n });\n }\n },\n );\n};\n\n/**\n * Lezer parse result visitor.\n */\nconst visitTree = (node: SyntaxNode, callback: (node: SyntaxNode) => boolean): boolean => {\n if (callback(node)) {\n return true;\n }\n\n for (let child = node.firstChild; child !== null; child = child.nextSibling) {\n if (visitTree(child, callback)) {\n return true;\n }\n }\n\n return false;\n};\n"],
5
- "mappings": "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",
6
- "names": ["syntaxTree", "RangeSetBuilder", "StateEffect", "StateField", "Decoration", "EditorView", "ViewPlugin", "WidgetType", "debounce", "createSheetName", "invariant", "documentId", "singleValueFacet", "updateAllDecorations", "StateEffect", "define", "computeGraphFacet", "singleValueFacet", "acceptCompletion", "autocompletion", "completionStatus", "startCompletion", "HighlightStyle", "syntaxHighlighting", "ViewPlugin", "keymap", "tags", "spreadsheet", "RANGE_NOTATION", "singleValueFacet", "mx", "highlightStyles", "HighlightStyle", "define", "tag", "tags", "name", "class", "tagName", "number", "bool", "string", "invalid", "languageFacet", "singleValueFacet", "sheetExtension", "debug", "functions", "extension", "language", "spreadsheet", "idiom", "decimalSeparator", "createCompletion", "section", "description", "syntax", "find", "value", "label", "info", "root", "document", "createElement", "className", "title", "innerText", "appendChild", "detail", "apply", "view", "completion", "from", "to", "insertParens", "state", "doc", "toString", "length", "dispatch", "update", "changes", "insert", "selection", "anchor", "of", "data", "autocomplete", "context", "match", "matchBefore", "text", "toUpperCase", "explicit", "options", "filter", "startsWith", "map", "syntaxHighlighting", "autocompletion", "aboveCursor", "defaultKeymap", "activateOnTyping", "closeOnBlur", "icons", "tooltipClass", "mx", "keymap", "key", "run", "completionStatus", "acceptCompletion", "startCompletion", "rangeExtension", "onInit", "onStateChange", "activeRange", "notifier", "setRange", "range", "focus", "ViewPlugin", "fromClass", "constructor", "_view", "ranges", "undefined", "facet", "topNode", "parser", "parse", "visitTree", "type", "str", "sliceString", "RANGE_NOTATION", "test", "node", "callback", "child", "firstChild", "nextSibling"]
7
- }