@dxos/react-ui-attention 0.8.4-main.c4373fc → 0.8.4-main.c85a9c8dae
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.
- package/dist/lib/browser/chunk-PAZBZOO2.mjs +7 -0
- package/dist/lib/browser/chunk-PAZBZOO2.mjs.map +7 -0
- package/dist/lib/browser/chunk-TAZUDNJI.mjs +705 -0
- package/dist/lib/browser/chunk-TAZUDNJI.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +75 -8
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +21 -5
- package/dist/lib/browser/testing/index.mjs.map +3 -3
- package/dist/lib/browser/types.mjs +7 -0
- package/dist/lib/browser/types.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-FT553KKP.mjs +706 -0
- package/dist/lib/node-esm/chunk-FT553KKP.mjs.map +7 -0
- package/dist/lib/node-esm/chunk-PMGNPL4M.mjs +9 -0
- package/dist/lib/node-esm/chunk-PMGNPL4M.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +75 -8
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +21 -5
- package/dist/lib/node-esm/testing/index.mjs.map +3 -3
- package/dist/lib/node-esm/types.mjs +8 -0
- package/dist/lib/node-esm/types.mjs.map +7 -0
- package/dist/types/src/attention.d.ts +22 -17
- package/dist/types/src/attention.d.ts.map +1 -1
- package/dist/types/src/components/AttentionGlyph.d.ts.map +1 -1
- package/dist/types/src/components/AttentionProvider.d.ts +6 -5
- package/dist/types/src/components/AttentionProvider.d.ts.map +1 -1
- package/dist/types/src/components/SelectionProvider.d.ts +5 -4
- package/dist/types/src/components/SelectionProvider.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/selection.d.ts +23 -3
- package/dist/types/src/selection.d.ts.map +1 -1
- package/dist/types/src/testing/decorators/withAttention.d.ts +5 -1
- package/dist/types/src/testing/decorators/withAttention.d.ts.map +1 -1
- package/dist/types/src/types.d.ts +10 -0
- package/dist/types/src/types.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +36 -24
- package/src/attention.test.ts +12 -7
- package/src/attention.ts +64 -42
- package/src/components/AttentionGlyph.stories.tsx +2 -2
- package/src/components/AttentionGlyph.tsx +13 -6
- package/src/components/AttentionProvider.stories.tsx +2 -2
- package/src/components/AttentionProvider.tsx +77 -70
- package/src/components/SelectionProvider.tsx +62 -15
- package/src/index.ts +1 -0
- package/src/selection.ts +111 -71
- package/src/testing/decorators/withAttention.ts +21 -4
- package/src/types.ts +16 -0
- package/dist/lib/browser/chunk-HELLA3BH.mjs +0 -675
- package/dist/lib/browser/chunk-HELLA3BH.mjs.map +0 -7
- package/dist/lib/node-esm/chunk-FBZV7QG7.mjs +0 -677
- package/dist/lib/node-esm/chunk-FBZV7QG7.mjs.map +0 -7
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { RegistryContext } from '@effect-atom/atom-react';
|
|
5
6
|
import { createContext } from '@radix-ui/react-context';
|
|
6
7
|
import * as Match from 'effect/Match';
|
|
7
|
-
import React, { type PropsWithChildren, useCallback, useMemo } from 'react';
|
|
8
|
+
import React, { type PropsWithChildren, useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
8
9
|
|
|
9
|
-
import { useDefaultValue } from '@dxos/react-
|
|
10
|
+
import { useDefaultValue } from '@dxos/react-hooks';
|
|
10
11
|
|
|
11
12
|
import {
|
|
12
13
|
type Selection,
|
|
@@ -23,7 +24,7 @@ type SelectionContextValue = {
|
|
|
23
24
|
};
|
|
24
25
|
|
|
25
26
|
const [SelectionContextProvider, useSelectionContext] = createContext<SelectionContextValue>(SELECTION_NAME, {
|
|
26
|
-
selection:
|
|
27
|
+
selection: undefined as unknown as SelectionManager,
|
|
27
28
|
});
|
|
28
29
|
|
|
29
30
|
/**
|
|
@@ -34,7 +35,8 @@ export const SelectionProvider = ({
|
|
|
34
35
|
children,
|
|
35
36
|
selection: propsSelection,
|
|
36
37
|
}: PropsWithChildren<{ selection?: SelectionManager }>) => {
|
|
37
|
-
const
|
|
38
|
+
const registry = useContext(RegistryContext);
|
|
39
|
+
const selection = useDefaultValue(propsSelection, () => new SelectionManager(registry));
|
|
38
40
|
return <SelectionContextProvider selection={selection}>{children}</SelectionContextProvider>;
|
|
39
41
|
};
|
|
40
42
|
|
|
@@ -46,6 +48,18 @@ export const useSelectionManager = () => {
|
|
|
46
48
|
return selection;
|
|
47
49
|
};
|
|
48
50
|
|
|
51
|
+
const getDefaultResult = <T extends SelectionMode>(mode: T): SelectionResult<T> => {
|
|
52
|
+
return Match.type<Selection>().pipe(
|
|
53
|
+
Match.when({ mode: 'single' }, (selection) => selection.id),
|
|
54
|
+
Match.when({ mode: 'multi' }, (selection) => selection.ids),
|
|
55
|
+
Match.when({ mode: 'range' }, (selection) =>
|
|
56
|
+
selection.from && selection.to ? { from: selection.from, to: selection.to } : undefined,
|
|
57
|
+
),
|
|
58
|
+
Match.when({ mode: 'multi-range' }, (selection) => selection.ranges),
|
|
59
|
+
Match.exhaustive,
|
|
60
|
+
)(defaultSelection(mode)) as SelectionResult<T>;
|
|
61
|
+
};
|
|
62
|
+
|
|
49
63
|
/**
|
|
50
64
|
* Get the selected objects for a given context.
|
|
51
65
|
*/
|
|
@@ -54,28 +68,49 @@ export const useSelected = <T extends SelectionMode>(
|
|
|
54
68
|
mode: T = 'multi' as T,
|
|
55
69
|
): SelectionResult<T> => {
|
|
56
70
|
const { selection } = useSelectionContext(SELECTION_NAME);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
71
|
+
const [state, setState] = useState<SelectionResult<T>>(() =>
|
|
72
|
+
contextId && selection ? selection.getSelected(contextId, mode) : getDefaultResult(mode),
|
|
73
|
+
);
|
|
60
74
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
75
|
+
useEffect(() => {
|
|
76
|
+
if (!contextId || !selection) {
|
|
77
|
+
setState(getDefaultResult(mode));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Set initial state.
|
|
82
|
+
setState(selection.getSelected(contextId, mode));
|
|
83
|
+
|
|
84
|
+
// Subscribe to changes.
|
|
85
|
+
return selection.subscribe(() => {
|
|
86
|
+
setState(selection.getSelected(contextId, mode));
|
|
87
|
+
});
|
|
88
|
+
}, [selection, contextId, mode]);
|
|
89
|
+
|
|
90
|
+
return state;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
export type UseSelectionActions = {
|
|
94
|
+
singleSelect: (id: string) => void;
|
|
95
|
+
multiSelect: (ids: string[]) => void;
|
|
96
|
+
rangeSelect: (from: string, to: string) => void;
|
|
97
|
+
toggle: (id: string) => void;
|
|
98
|
+
clear: () => void;
|
|
68
99
|
};
|
|
69
100
|
|
|
70
101
|
/**
|
|
71
102
|
* Provides functions to manage the selection state for multiple contexts.
|
|
72
103
|
*/
|
|
73
|
-
|
|
104
|
+
// TODO(burdon): Mode not used.
|
|
105
|
+
export const useSelectionActions = (contextIds: string[], mode: SelectionMode = 'multi'): UseSelectionActions => {
|
|
74
106
|
const stableContextIds = useMemo(() => contextIds, [JSON.stringify(contextIds)]); // TODO(burdon): Avoid stringify.
|
|
75
107
|
const { selection } = useSelectionContext(SELECTION_NAME);
|
|
76
108
|
|
|
77
109
|
const singleSelect = useCallback(
|
|
78
110
|
(id: string) => {
|
|
111
|
+
if (!selection) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
79
114
|
for (const contextId of stableContextIds) {
|
|
80
115
|
selection.updateSingle(contextId, id);
|
|
81
116
|
}
|
|
@@ -85,6 +120,9 @@ export const useSelectionActions = (contextIds: string[], mode: SelectionMode =
|
|
|
85
120
|
|
|
86
121
|
const multiSelect = useCallback(
|
|
87
122
|
(ids: string[]) => {
|
|
123
|
+
if (!selection) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
88
126
|
for (const contextId of stableContextIds) {
|
|
89
127
|
selection.updateMulti(contextId, ids);
|
|
90
128
|
}
|
|
@@ -94,6 +132,9 @@ export const useSelectionActions = (contextIds: string[], mode: SelectionMode =
|
|
|
94
132
|
|
|
95
133
|
const rangeSelect = useCallback(
|
|
96
134
|
(from: string, to: string) => {
|
|
135
|
+
if (!selection) {
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
97
138
|
for (const contextId of stableContextIds) {
|
|
98
139
|
selection.updateRange(contextId, from, to);
|
|
99
140
|
}
|
|
@@ -103,6 +144,9 @@ export const useSelectionActions = (contextIds: string[], mode: SelectionMode =
|
|
|
103
144
|
|
|
104
145
|
const toggle = useCallback(
|
|
105
146
|
(id: string) => {
|
|
147
|
+
if (!selection) {
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
106
150
|
for (const contextId of stableContextIds) {
|
|
107
151
|
selection.toggleSelection(contextId, id);
|
|
108
152
|
}
|
|
@@ -111,6 +155,9 @@ export const useSelectionActions = (contextIds: string[], mode: SelectionMode =
|
|
|
111
155
|
);
|
|
112
156
|
|
|
113
157
|
const clear = useCallback(() => {
|
|
158
|
+
if (!selection) {
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
114
161
|
for (const contextId of stableContextIds) {
|
|
115
162
|
selection.clearSelection(contextId);
|
|
116
163
|
}
|
package/src/index.ts
CHANGED
package/src/selection.ts
CHANGED
|
@@ -2,12 +2,11 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
import {
|
|
5
|
+
import { Atom, type Registry } from '@effect-atom/atom-react';
|
|
6
6
|
import * as Match from 'effect/Match';
|
|
7
7
|
import * as Schema from 'effect/Schema';
|
|
8
8
|
|
|
9
9
|
import { invariant } from '@dxos/invariant';
|
|
10
|
-
import { type Live, live } from '@dxos/live-object';
|
|
11
10
|
|
|
12
11
|
// TODO(burdon): Reconcile with @dxos/graph.
|
|
13
12
|
|
|
@@ -70,117 +69,158 @@ export const getSelectionSet = (selectionManager: SelectionManager, contextId?:
|
|
|
70
69
|
* Each context maintains its own selection mode and state.
|
|
71
70
|
*/
|
|
72
71
|
export class SelectionManager {
|
|
73
|
-
private readonly _state
|
|
72
|
+
private readonly _state: Atom.Writable<{ selections: Record<string, Selection> }>;
|
|
73
|
+
|
|
74
|
+
constructor(
|
|
75
|
+
private readonly _registry: Registry.Registry,
|
|
76
|
+
initial: Record<string, Selection> = {},
|
|
77
|
+
) {
|
|
78
|
+
this._state = Atom.make<{ selections: Record<string, Selection> }>({
|
|
79
|
+
selections: { ...initial },
|
|
80
|
+
});
|
|
81
|
+
}
|
|
74
82
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
/**
|
|
84
|
+
* Get the state atom for reactive access in the graph system.
|
|
85
|
+
*/
|
|
86
|
+
get state(): Atom.Atom<{ selections: Record<string, Selection> }> {
|
|
87
|
+
return this._state;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Gets the current state.
|
|
92
|
+
*/
|
|
93
|
+
getState(): { selections: Record<string, Selection> } {
|
|
94
|
+
return this._registry.get(this._state);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Subscribe to changes in the selection state.
|
|
99
|
+
*/
|
|
100
|
+
subscribe(cb: (state: { selections: Record<string, Selection> }) => void): () => void {
|
|
101
|
+
this._registry.get(this._state);
|
|
102
|
+
return this._registry.subscribe(this._state, () => {
|
|
103
|
+
cb(this._registry.get(this._state));
|
|
104
|
+
});
|
|
81
105
|
}
|
|
82
106
|
|
|
83
107
|
getSelectionContexts(): string[] {
|
|
84
|
-
return Object.keys(this._state.selections);
|
|
108
|
+
return Object.keys(this._registry.get(this._state).selections);
|
|
85
109
|
}
|
|
86
110
|
|
|
111
|
+
getSelection(contextId: string): Selection | undefined;
|
|
112
|
+
getSelection<T extends SelectionMode>(contextId: string, mode: T): Selection;
|
|
87
113
|
getSelection<T extends SelectionMode | undefined>(
|
|
88
114
|
contextId: string,
|
|
89
115
|
mode: T = undefined as T,
|
|
90
|
-
):
|
|
91
|
-
const
|
|
116
|
+
): Selection | undefined {
|
|
117
|
+
const state = this._registry.get(this._state);
|
|
118
|
+
const selection = state.selections[contextId];
|
|
92
119
|
if (!mode || selection) {
|
|
93
120
|
return selection;
|
|
94
121
|
}
|
|
95
122
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
123
|
+
// Create new selection for the context.
|
|
124
|
+
const newSelection = defaultSelection(mode);
|
|
125
|
+
this._registry.update(this._state, (state) => ({
|
|
126
|
+
selections: { ...state.selections, [contextId]: newSelection },
|
|
127
|
+
}));
|
|
128
|
+
return newSelection;
|
|
100
129
|
}
|
|
101
130
|
|
|
102
131
|
// TODO(burdon): Disambiguate with getSelection?
|
|
103
132
|
getSelected<T extends SelectionMode>(contextId: string, mode: T = 'multi' as T): SelectionResult<T> {
|
|
104
133
|
const selection = this.getSelection(contextId, mode);
|
|
105
134
|
invariant(selection?.mode === mode, 'Selection mode mismatch');
|
|
135
|
+
// Cast required because TypeScript can't infer the relationship between T and the matched result.
|
|
106
136
|
return Match.type<Selection>().pipe(
|
|
107
137
|
Match.when({ mode: 'single' }, (s) => s.id),
|
|
108
138
|
Match.when({ mode: 'multi' }, (s) => s.ids),
|
|
109
139
|
Match.when({ mode: 'range' }, (s) => (s.from && s.to ? { from: s.from, to: s.to } : undefined)),
|
|
110
140
|
Match.when({ mode: 'multi-range' }, (s) => s.ranges),
|
|
111
141
|
Match.exhaustive,
|
|
112
|
-
)(selection) as
|
|
142
|
+
)(selection) as SelectionResult<T>;
|
|
113
143
|
}
|
|
114
144
|
|
|
115
145
|
updateSingle(contextId: string, id: string): void {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
146
|
+
const selection = this.getSelection(contextId, 'single');
|
|
147
|
+
invariant(selection?.mode === 'single', 'Selection mode is not single');
|
|
148
|
+
this._registry.update(this._state, (state) => ({
|
|
149
|
+
selections: {
|
|
150
|
+
...state.selections,
|
|
151
|
+
[contextId]: { ...selection, id },
|
|
152
|
+
},
|
|
153
|
+
}));
|
|
121
154
|
}
|
|
122
155
|
|
|
123
156
|
updateMulti(contextId: string, ids: string[]) {
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
157
|
+
const selection = this.getSelection(contextId, 'multi');
|
|
158
|
+
invariant(selection?.mode === 'multi', 'Selection mode is not multi');
|
|
159
|
+
this._registry.update(this._state, (state) => ({
|
|
160
|
+
selections: {
|
|
161
|
+
...state.selections,
|
|
162
|
+
[contextId]: { ...selection, ids: [...ids] },
|
|
163
|
+
},
|
|
164
|
+
}));
|
|
129
165
|
}
|
|
130
166
|
|
|
131
167
|
updateRange(contextId: string, from: string, to: string) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
168
|
+
const selection = this.getSelection(contextId, 'range');
|
|
169
|
+
invariant(selection?.mode === 'range', 'Selection mode is not range');
|
|
170
|
+
this._registry.update(this._state, (state) => ({
|
|
171
|
+
selections: {
|
|
172
|
+
...state.selections,
|
|
173
|
+
[contextId]: { ...selection, from, to },
|
|
174
|
+
},
|
|
175
|
+
}));
|
|
138
176
|
}
|
|
139
177
|
|
|
140
178
|
updateMultiRange(contextId: string, ranges: { from: string; to: string }[]) {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
179
|
+
const selection = this.getSelection(contextId, 'multi-range');
|
|
180
|
+
invariant(selection?.mode === 'multi-range', 'Selection mode is not multi-range');
|
|
181
|
+
this._registry.update(this._state, (state) => ({
|
|
182
|
+
selections: {
|
|
183
|
+
...state.selections,
|
|
184
|
+
[contextId]: { ...selection, ranges: [...ranges] },
|
|
185
|
+
},
|
|
186
|
+
}));
|
|
146
187
|
}
|
|
147
188
|
|
|
148
189
|
clearSelection(contextId: string): void {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
Match.exhaustive,
|
|
169
|
-
)(selection);
|
|
170
|
-
});
|
|
190
|
+
const selection = this.getSelection(contextId);
|
|
191
|
+
if (!selection) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const clearedSelection = Match.type<Selection>().pipe(
|
|
196
|
+
Match.when({ mode: 'single' }, (s) => ({ ...s, id: undefined })),
|
|
197
|
+
Match.when({ mode: 'multi' }, (s) => ({ ...s, ids: [] })),
|
|
198
|
+
Match.when({ mode: 'range' }, (s) => ({ ...s, from: undefined, to: undefined })),
|
|
199
|
+
Match.when({ mode: 'multi-range' }, (s) => ({ ...s, ranges: [] })),
|
|
200
|
+
Match.exhaustive,
|
|
201
|
+
)(selection);
|
|
202
|
+
|
|
203
|
+
this._registry.update(this._state, (state) => ({
|
|
204
|
+
selections: {
|
|
205
|
+
...state.selections,
|
|
206
|
+
[contextId]: clearedSelection,
|
|
207
|
+
},
|
|
208
|
+
}));
|
|
171
209
|
}
|
|
172
210
|
|
|
173
211
|
toggleSelection(contextId: string, id: string): void {
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
212
|
+
const selection = this.getSelection(contextId, 'multi');
|
|
213
|
+
invariant(selection?.mode === 'multi', 'Selection mode is not multi');
|
|
214
|
+
|
|
215
|
+
const newIds = selection.ids.includes(id)
|
|
216
|
+
? selection.ids.filter((existingId) => existingId !== id)
|
|
217
|
+
: [...selection.ids, id];
|
|
218
|
+
|
|
219
|
+
this._registry.update(this._state, (state) => ({
|
|
220
|
+
selections: {
|
|
221
|
+
...state.selections,
|
|
222
|
+
[contextId]: { ...selection, ids: newIds },
|
|
223
|
+
},
|
|
224
|
+
}));
|
|
185
225
|
}
|
|
186
226
|
}
|
|
@@ -2,11 +2,28 @@
|
|
|
2
2
|
// Copyright 2024 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
+
import { Registry, RegistryContext } from '@effect-atom/atom-react';
|
|
5
6
|
import { type Decorator } from '@storybook/react';
|
|
6
|
-
import { createElement } from 'react';
|
|
7
|
+
import { createElement, useMemo } from 'react';
|
|
7
8
|
|
|
8
|
-
import {
|
|
9
|
+
import { AttentionManager } from '../../attention';
|
|
10
|
+
import { RootAttentionProvider, SelectionProvider } from '../../components';
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Storybook decorator that provides attention context.
|
|
14
|
+
* @param initialAttendedId Optional ID to set as initially attended.
|
|
15
|
+
*/
|
|
16
|
+
export const withAttention = (initialAttendedId?: string): Decorator => {
|
|
17
|
+
return (Story) => {
|
|
18
|
+
const registry = useMemo(() => Registry.make(), []);
|
|
19
|
+
const attention = useMemo(
|
|
20
|
+
() => (initialAttendedId ? new AttentionManager(registry, [initialAttendedId]) : undefined),
|
|
21
|
+
[registry],
|
|
22
|
+
);
|
|
23
|
+
return createElement(
|
|
24
|
+
RegistryContext.Provider,
|
|
25
|
+
{ value: registry },
|
|
26
|
+
createElement(RootAttentionProvider, { attention }, createElement(SelectionProvider, {}, createElement(Story))),
|
|
27
|
+
);
|
|
28
|
+
};
|
|
12
29
|
};
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
// NOTE: Chosen from RFC 1738's `safe` characters: http://www.faqs.org/rfcs/rfc1738.html
|
|
6
|
+
export const ATTENDABLE_PATH_SEPARATOR = '~';
|
|
7
|
+
|
|
8
|
+
export type Attention = {
|
|
9
|
+
hasAttention: boolean;
|
|
10
|
+
isAncestor: boolean;
|
|
11
|
+
isRelated: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export type CurrentState = {
|
|
15
|
+
current: string[];
|
|
16
|
+
};
|