@macrostrat/feedback-components 1.0.1 → 1.1.1
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/CHANGELOG.md +10 -0
- package/dist/esm/feedback-components.3b3a5357.js +137 -0
- package/dist/esm/feedback-components.3b3a5357.js.map +1 -0
- package/dist/esm/feedback-components.46a7a347.js +269 -0
- package/dist/esm/feedback-components.46a7a347.js.map +1 -0
- package/dist/esm/{node.28634e40.js → feedback-components.5509fab3.js} +11 -6
- package/dist/esm/feedback-components.5509fab3.js.map +1 -0
- package/dist/esm/feedback-components.586103e8.js +578 -0
- package/dist/esm/feedback-components.586103e8.js.map +1 -0
- package/dist/esm/{extractions.65bb73cc.js → feedback-components.5df2a926.js} +46 -18
- package/dist/esm/feedback-components.5df2a926.js.map +1 -0
- package/dist/esm/{main.module.cd706d67.js → feedback-components.6d32ee91.js} +1 -1
- package/dist/esm/{main.module.cd706d67.js.map → feedback-components.6d32ee91.js.map} +1 -1
- package/dist/esm/feedback-components.95dbe7d7.js +82 -0
- package/dist/esm/feedback-components.95dbe7d7.js.map +1 -0
- package/dist/esm/{type-selector.6e8952d6.js → feedback-components.ad9f284e.js} +6 -5
- package/dist/esm/feedback-components.ad9f284e.js.map +1 -0
- package/dist/esm/{main.module.8d366b6e.css → feedback-components.bf93773c.css} +1 -1
- package/dist/esm/{main.module.8d366b6e.css.map → feedback-components.bf93773c.css.map} +1 -1
- package/dist/esm/{main.module.2f2972c8.css → feedback-components.e273ed5b.css} +1 -1
- package/dist/esm/{main.module.2f2972c8.css.map → feedback-components.e273ed5b.css.map} +1 -1
- package/dist/esm/{main.module.d2fbdf09.js → feedback-components.f9850d85.js} +1 -1
- package/dist/esm/{main.module.d2fbdf09.js.map → feedback-components.f9850d85.js.map} +1 -1
- package/dist/esm/{edit-state.c39d8466.js → feedback-components.fa1d3641.js} +125 -7
- package/dist/esm/feedback-components.fa1d3641.js.map +1 -0
- package/dist/esm/feedback-components.fb60c70d.css +180 -0
- package/dist/esm/feedback-components.fb60c70d.css.map +1 -0
- package/dist/esm/index.d.ts +10 -64
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/node/feedback-components.2f391fa4.js +2 -0
- package/dist/node/feedback-components.2f391fa4.js.map +1 -0
- package/dist/node/feedback-components.561466ac.js +2 -0
- package/dist/node/feedback-components.561466ac.js.map +1 -0
- package/dist/node/feedback-components.571ee23c.js +2 -0
- package/dist/node/feedback-components.571ee23c.js.map +1 -0
- package/dist/node/{main.module.ebdf985b.js → feedback-components.794f429b.js} +2 -2
- package/dist/node/feedback-components.794f429b.js.map +1 -0
- package/dist/node/{main.module.1fdfe813.css → feedback-components.83c21466.css} +1 -1
- package/dist/node/feedback-components.83c21466.css.map +1 -0
- package/dist/node/feedback-components.8b03e8be.js +2 -0
- package/dist/node/feedback-components.8b03e8be.js.map +1 -0
- package/dist/node/{main.module.6bc7d51b.css → feedback-components.9eb1d41a.css} +1 -1
- package/dist/node/feedback-components.9eb1d41a.css.map +1 -0
- package/dist/node/feedback-components.a39f7653.js +2 -0
- package/dist/node/feedback-components.a39f7653.js.map +1 -0
- package/dist/node/feedback-components.acac789b.js +2 -0
- package/dist/node/feedback-components.acac789b.js.map +1 -0
- package/dist/node/feedback-components.b7946db4.js +2 -0
- package/dist/node/feedback-components.b7946db4.js.map +1 -0
- package/dist/node/feedback-components.c459cc27.js +2 -0
- package/dist/node/feedback-components.c459cc27.js.map +1 -0
- package/dist/node/feedback-components.c88cb37f.css +2 -0
- package/dist/node/feedback-components.c88cb37f.css.map +1 -0
- package/dist/node/feedback-components.ec54a1e7.js +2 -0
- package/dist/node/feedback-components.ec54a1e7.js.map +1 -0
- package/dist/node/index.js +1 -1
- package/dist/node/index.js.map +1 -1
- package/package.json +7 -6
- package/src/extractions/index.ts +76 -21
- package/src/extractions/types.ts +6 -1
- package/src/feedback/edit-state.ts +184 -16
- package/src/feedback/feedback.module.sass +121 -9
- package/src/feedback/graph.ts +90 -32
- package/src/feedback/index.ts +553 -146
- package/src/feedback/node.ts +7 -1
- package/src/feedback/text-visualizer.ts +286 -49
- package/src/feedback/type-selector/index.ts +4 -2
- package/dist/esm/edit-state.c39d8466.js.map +0 -1
- package/dist/esm/extractions.65bb73cc.js.map +0 -1
- package/dist/esm/feedback.5c86878e.js +0 -252
- package/dist/esm/feedback.5c86878e.js.map +0 -1
- package/dist/esm/feedback.module.55921afe.css +0 -44
- package/dist/esm/feedback.module.55921afe.css.map +0 -1
- package/dist/esm/feedback.module.765b1e58.js +0 -28
- package/dist/esm/feedback.module.765b1e58.js.map +0 -1
- package/dist/esm/graph.f4f65d79.js +0 -83
- package/dist/esm/graph.f4f65d79.js.map +0 -1
- package/dist/esm/node.28634e40.js.map +0 -1
- package/dist/esm/text-visualizer.198e27ff.js +0 -101
- package/dist/esm/text-visualizer.198e27ff.js.map +0 -1
- package/dist/esm/type-selector.6e8952d6.js.map +0 -1
- package/dist/node/edit-state.f50ca728.js +0 -2
- package/dist/node/edit-state.f50ca728.js.map +0 -1
- package/dist/node/extractions.e6ea2eb9.js +0 -2
- package/dist/node/extractions.e6ea2eb9.js.map +0 -1
- package/dist/node/feedback.8d3d1219.js +0 -2
- package/dist/node/feedback.8d3d1219.js.map +0 -1
- package/dist/node/feedback.module.a8744203.js +0 -2
- package/dist/node/feedback.module.a8744203.js.map +0 -1
- package/dist/node/feedback.module.c4eab97d.css +0 -2
- package/dist/node/feedback.module.c4eab97d.css.map +0 -1
- package/dist/node/graph.ca5b649f.js +0 -2
- package/dist/node/graph.ca5b649f.js.map +0 -1
- package/dist/node/main.module.1857be22.js +0 -2
- package/dist/node/main.module.1857be22.js.map +0 -1
- package/dist/node/main.module.1fdfe813.css.map +0 -1
- package/dist/node/main.module.6bc7d51b.css.map +0 -1
- package/dist/node/main.module.ebdf985b.js.map +0 -1
- package/dist/node/node.33108ccc.js +0 -2
- package/dist/node/node.33108ccc.js.map +0 -1
- package/dist/node/text-visualizer.1e770afa.js +0 -2
- package/dist/node/text-visualizer.1e770afa.js.map +0 -1
- package/dist/node/type-selector.0035ef7d.js +0 -2
- package/dist/node/type-selector.0035ef7d.js.map +0 -1
package/src/extractions/index.ts
CHANGED
|
@@ -5,6 +5,7 @@ import type { Entity, EntityExt, Highlight, EntityType } from "./types";
|
|
|
5
5
|
import { CSSProperties } from "react";
|
|
6
6
|
import { asChromaColor } from "@macrostrat/color-utils";
|
|
7
7
|
import hyper from "@macrostrat/hyper";
|
|
8
|
+
import { useDarkMode } from "@macrostrat/ui-components";
|
|
8
9
|
|
|
9
10
|
export type { Entity, EntityExt };
|
|
10
11
|
|
|
@@ -12,7 +13,7 @@ const h = hyper.styled(styles);
|
|
|
12
13
|
|
|
13
14
|
export function buildHighlights(
|
|
14
15
|
entities: EntityExt[],
|
|
15
|
-
parent: EntityExt | null
|
|
16
|
+
parent: EntityExt | null,
|
|
16
17
|
): Highlight[] {
|
|
17
18
|
let highlights = [];
|
|
18
19
|
let parents = [];
|
|
@@ -25,8 +26,8 @@ export function buildHighlights(
|
|
|
25
26
|
start: entity.indices[0],
|
|
26
27
|
end: entity.indices[1],
|
|
27
28
|
text: entity.name,
|
|
28
|
-
backgroundColor: entity.type
|
|
29
|
-
tag: entity.type
|
|
29
|
+
backgroundColor: entity.type?.color,
|
|
30
|
+
tag: entity.type?.name ?? "lith",
|
|
30
31
|
id: entity.id,
|
|
31
32
|
parents,
|
|
32
33
|
});
|
|
@@ -40,17 +41,21 @@ export function enhanceData(extractionData, models, entityTypes) {
|
|
|
40
41
|
...extractionData,
|
|
41
42
|
model: models.get(extractionData.model_id),
|
|
42
43
|
entities: extractionData.entities?.map((d) =>
|
|
43
|
-
enhanceEntity(d, entityTypes)
|
|
44
|
+
enhanceEntity(d, entityTypes),
|
|
44
45
|
),
|
|
45
46
|
};
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
export function getTagStyle(
|
|
49
50
|
baseColor: string,
|
|
50
|
-
options: { highlighted?: boolean; inDarkMode?: boolean; active?: boolean }
|
|
51
|
+
options: { highlighted?: boolean; inDarkMode?: boolean; active?: boolean },
|
|
51
52
|
): CSSProperties {
|
|
52
|
-
const _baseColor = asChromaColor(baseColor ?? "#
|
|
53
|
-
const {
|
|
53
|
+
const _baseColor = asChromaColor(baseColor ?? "#fff");
|
|
54
|
+
const {
|
|
55
|
+
highlighted = true,
|
|
56
|
+
inDarkMode = useDarkMode().isEnabled,
|
|
57
|
+
active = false,
|
|
58
|
+
} = options;
|
|
54
59
|
|
|
55
60
|
let mixAmount = highlighted ? 0.8 : 0.5;
|
|
56
61
|
let backgroundAlpha = highlighted ? 0.8 : 0.2;
|
|
@@ -60,27 +65,38 @@ export function getTagStyle(
|
|
|
60
65
|
backgroundAlpha = 1;
|
|
61
66
|
}
|
|
62
67
|
|
|
63
|
-
const mixTarget =
|
|
68
|
+
const mixTarget = "black";
|
|
64
69
|
|
|
65
|
-
const color = _baseColor.mix(mixTarget, mixAmount).
|
|
70
|
+
const color = active ? "#000" : _baseColor.mix(mixTarget, mixAmount).hex();
|
|
66
71
|
const borderColor = highlighted
|
|
67
|
-
? _baseColor.mix(mixTarget, mixAmount /
|
|
72
|
+
? _baseColor.mix(mixTarget, mixAmount / 1.1).hex()
|
|
68
73
|
: "transparent";
|
|
69
74
|
|
|
75
|
+
let backgroundColor = active
|
|
76
|
+
? _baseColor.alpha(backgroundAlpha).hex()
|
|
77
|
+
: normalizeColor(_baseColor.alpha(backgroundAlpha).hex());
|
|
78
|
+
|
|
79
|
+
// handle white backgrounds in light mode
|
|
80
|
+
if (!inDarkMode && backgroundColor === "#ffffff") {
|
|
81
|
+
console.log("Adjusting background color for light mode:", backgroundColor);
|
|
82
|
+
backgroundColor = "#f0f0f0";
|
|
83
|
+
}
|
|
84
|
+
|
|
70
85
|
return {
|
|
71
86
|
color,
|
|
72
|
-
backgroundColor
|
|
87
|
+
backgroundColor,
|
|
73
88
|
boxSizing: "border-box",
|
|
74
89
|
borderStyle: "solid",
|
|
75
90
|
borderColor,
|
|
76
|
-
borderWidth: "
|
|
91
|
+
borderWidth: "1.5px",
|
|
77
92
|
fontWeight: active ? "bold" : "normal",
|
|
93
|
+
fontSize: "0.9em",
|
|
78
94
|
};
|
|
79
95
|
}
|
|
80
96
|
|
|
81
97
|
function enhanceEntity(
|
|
82
98
|
entity: Entity,
|
|
83
|
-
entityTypes: Map<number, EntityType
|
|
99
|
+
entityTypes: Map<number, EntityType>,
|
|
84
100
|
): EntityExt {
|
|
85
101
|
return {
|
|
86
102
|
...entity,
|
|
@@ -90,8 +106,8 @@ function enhanceEntity(
|
|
|
90
106
|
}
|
|
91
107
|
|
|
92
108
|
function addColor(entityType: EntityType, match = false) {
|
|
93
|
-
const color = asChromaColor(entityType.color ?? "#
|
|
94
|
-
match ? 1 : 2
|
|
109
|
+
const color = asChromaColor(entityType.color ?? "#fff").brighten(
|
|
110
|
+
match ? 1 : 2,
|
|
95
111
|
);
|
|
96
112
|
|
|
97
113
|
return { ...entityType, color: color.css() };
|
|
@@ -113,7 +129,7 @@ export function ExtractionContext({
|
|
|
113
129
|
h(ModelInfo, { data: data.model }),
|
|
114
130
|
h(
|
|
115
131
|
"ul.entities",
|
|
116
|
-
data.entities.map((d) => h(ExtractionInfo, { data: d, matchComponent }))
|
|
132
|
+
data.entities.map((d) => h(ExtractionInfo, { data: d, matchComponent })),
|
|
117
133
|
),
|
|
118
134
|
]);
|
|
119
135
|
}
|
|
@@ -140,15 +156,16 @@ export function EntityTag({
|
|
|
140
156
|
matchComponent = null,
|
|
141
157
|
}: EntityTagProps) {
|
|
142
158
|
const { name, type, match } = data;
|
|
159
|
+
|
|
143
160
|
const className = classNames(
|
|
144
161
|
{
|
|
145
162
|
matched: match != null,
|
|
146
|
-
type: data.type
|
|
163
|
+
type: data.type?.name ?? "lith",
|
|
147
164
|
},
|
|
148
|
-
"entity"
|
|
165
|
+
"entity",
|
|
149
166
|
);
|
|
150
167
|
|
|
151
|
-
const style = getTagStyle(type
|
|
168
|
+
const style = getTagStyle(type?.color, { highlighted, active });
|
|
152
169
|
|
|
153
170
|
let _matchLink = null;
|
|
154
171
|
if (match != null && matchComponent != null) {
|
|
@@ -168,7 +185,7 @@ export function EntityTag({
|
|
|
168
185
|
}
|
|
169
186
|
},
|
|
170
187
|
},
|
|
171
|
-
[type
|
|
188
|
+
[type?.name, _matchLink],
|
|
172
189
|
),
|
|
173
190
|
]);
|
|
174
191
|
}
|
|
@@ -187,7 +204,7 @@ function ExtractionInfo({
|
|
|
187
204
|
h.if(children.length > 0)([
|
|
188
205
|
h(
|
|
189
206
|
"ul.children",
|
|
190
|
-
children.map((d) => h(ExtractionInfo, { data: d, matchComponent }))
|
|
207
|
+
children.map((d) => h(ExtractionInfo, { data: d, matchComponent })),
|
|
191
208
|
),
|
|
192
209
|
]),
|
|
193
210
|
]);
|
|
@@ -217,3 +234,41 @@ function HighlightedText(props: { text: string; highlights: Highlight[] }) {
|
|
|
217
234
|
parts.push(text.slice(start));
|
|
218
235
|
return h("span", parts);
|
|
219
236
|
}
|
|
237
|
+
|
|
238
|
+
function normalizeColor(hex8) {
|
|
239
|
+
const background = useDarkMode().isEnabled ? "#000000" : "#ffffff";
|
|
240
|
+
|
|
241
|
+
const r = parseInt(hex8.slice(1, 3), 16);
|
|
242
|
+
const g = parseInt(hex8.slice(3, 5), 16);
|
|
243
|
+
const b = parseInt(hex8.slice(5, 7), 16);
|
|
244
|
+
const a = parseInt(hex8.slice(7, 9), 16) / 255;
|
|
245
|
+
|
|
246
|
+
const bgR = parseInt(background.slice(1, 3), 16);
|
|
247
|
+
const bgG = parseInt(background.slice(3, 5), 16);
|
|
248
|
+
const bgB = parseInt(background.slice(5, 7), 16);
|
|
249
|
+
|
|
250
|
+
const blend = (fg, bg) => Math.round((1 - a) * bg + a * fg);
|
|
251
|
+
|
|
252
|
+
const blendedR = blend(r, bgR);
|
|
253
|
+
const blendedG = blend(g, bgG);
|
|
254
|
+
const blendedB = blend(b, bgB);
|
|
255
|
+
|
|
256
|
+
return (
|
|
257
|
+
"#" +
|
|
258
|
+
blendedR.toString(16).padStart(2, "0") +
|
|
259
|
+
blendedG.toString(16).padStart(2, "0") +
|
|
260
|
+
blendedB.toString(16).padStart(2, "0")
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function isHighlighted(id: number, selectedNodes: number[], nodes: any[]) {
|
|
265
|
+
if (selectedNodes?.length === 0) return true;
|
|
266
|
+
return (
|
|
267
|
+
selectedNodes?.includes(id) ||
|
|
268
|
+
nodes?.some(
|
|
269
|
+
(node) =>
|
|
270
|
+
selectedNodes?.includes(node.id) &&
|
|
271
|
+
node.children.some((child) => child.id === id),
|
|
272
|
+
)
|
|
273
|
+
);
|
|
274
|
+
}
|
package/src/extractions/types.ts
CHANGED
|
@@ -38,15 +38,26 @@ type TreeAction =
|
|
|
38
38
|
| { type: "select-entity-type"; payload: EntityType }
|
|
39
39
|
| { type: "toggle-entity-type-selector"; payload?: boolean | null }
|
|
40
40
|
| { type: "deselect" }
|
|
41
|
-
| { type: "reset" }
|
|
41
|
+
| { type: "reset" }
|
|
42
|
+
| { type: "delete-entity-type"; payload: { id: number } }
|
|
43
|
+
| {
|
|
44
|
+
type: "add-entity-type";
|
|
45
|
+
payload: { name: string; description: string; color: string };
|
|
46
|
+
}
|
|
47
|
+
| {
|
|
48
|
+
type: "update-entity-type";
|
|
49
|
+
payload: { id: number; name: string; description: string; color: string };
|
|
50
|
+
}
|
|
51
|
+
| { type: "select-range"; payload: { ids: number[] } };
|
|
42
52
|
|
|
43
53
|
export type TreeDispatch = Dispatch<TreeAction>;
|
|
44
54
|
|
|
45
55
|
export function useUpdatableTree(
|
|
46
56
|
initialTree: TreeData[],
|
|
47
|
-
entityTypes: Map<number, EntityType
|
|
57
|
+
entityTypes: Map<number, EntityType>,
|
|
48
58
|
): [TreeState, TreeDispatch] {
|
|
49
59
|
// Get the first entity type
|
|
60
|
+
// issue: grabs second entity instead of selected one
|
|
50
61
|
const type = entityTypes.values().next().value;
|
|
51
62
|
|
|
52
63
|
return useReducer(treeReducer, {
|
|
@@ -72,13 +83,84 @@ export function useTreeDispatch() {
|
|
|
72
83
|
}
|
|
73
84
|
|
|
74
85
|
function treeReducer(state: TreeState, action: TreeAction) {
|
|
75
|
-
console.log(action);
|
|
76
86
|
switch (action.type) {
|
|
87
|
+
case "add-entity-type": {
|
|
88
|
+
// Add a new entity type to the map
|
|
89
|
+
const { name, description, color } = action.payload;
|
|
90
|
+
const newId = state.lastInternalId - 1;
|
|
91
|
+
const newType: EntityType = {
|
|
92
|
+
id: newId,
|
|
93
|
+
name,
|
|
94
|
+
description: description === "" ? null : description,
|
|
95
|
+
color,
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const newEntityTypesMap = new Map(state.entityTypesMap);
|
|
99
|
+
newEntityTypesMap.set(newId, newType);
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
...state,
|
|
103
|
+
entityTypesMap: newEntityTypesMap,
|
|
104
|
+
selectedEntityType: newType,
|
|
105
|
+
lastInternalId: newId,
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
case "update-entity-type": {
|
|
109
|
+
// Update an existing entity type in the map
|
|
110
|
+
const { id, name, description, color } = action.payload;
|
|
111
|
+
const newEntityTypesMap = new Map(state.entityTypesMap);
|
|
112
|
+
const oldType = newEntityTypesMap.get(id);
|
|
113
|
+
|
|
114
|
+
if (!oldType) {
|
|
115
|
+
console.warn(`Entity type with id ${id} not found`);
|
|
116
|
+
return state;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const updatedType: EntityType = {
|
|
120
|
+
...oldType,
|
|
121
|
+
name,
|
|
122
|
+
description: description === "" ? null : description,
|
|
123
|
+
color,
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
newEntityTypesMap.set(id, updatedType);
|
|
127
|
+
|
|
128
|
+
// Update the tree to reflect the new type
|
|
129
|
+
const newTree = updateTreeTypes(state.tree, oldType, updatedType);
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
...state,
|
|
133
|
+
tree: newTree,
|
|
134
|
+
entityTypesMap: newEntityTypesMap,
|
|
135
|
+
selectedEntityType: updatedType,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
case "select-range":
|
|
139
|
+
// Select a range of nodes by their IDs
|
|
140
|
+
const payloadIds = action.payload.ids;
|
|
141
|
+
const node1 = payloadIds[0];
|
|
142
|
+
const node2 = payloadIds[1];
|
|
143
|
+
|
|
144
|
+
// make list of nodes in order
|
|
145
|
+
const allNodes = flattenAndSort(state.tree);
|
|
146
|
+
|
|
147
|
+
// select all nodes between node1 and node2
|
|
148
|
+
const startIndex = allNodes.findIndex((node) => node.id === node1);
|
|
149
|
+
const endIndex = allNodes.findIndex((node) => node.id === node2);
|
|
150
|
+
|
|
151
|
+
const selectedNodes = allNodes.slice(startIndex, endIndex + 1);
|
|
152
|
+
|
|
153
|
+
console.log("Selecting range:", selectedNodes);
|
|
154
|
+
return {
|
|
155
|
+
...state,
|
|
156
|
+
selectedNodes: selectedNodes.map((node) => node.id),
|
|
157
|
+
};
|
|
158
|
+
|
|
77
159
|
case "move-node":
|
|
78
160
|
// For each node in the tree, if the node is in the dragIds, remove it from the tree and collect it
|
|
79
161
|
const [newTree, removedNodes] = removeNodes(
|
|
80
162
|
state.tree,
|
|
81
|
-
action.payload.dragIds
|
|
163
|
+
action.payload.dragIds,
|
|
82
164
|
);
|
|
83
165
|
|
|
84
166
|
let keyPath: (number | "children")[] = [];
|
|
@@ -97,7 +179,7 @@ function treeReducer(state: TreeState, action: TreeAction) {
|
|
|
97
179
|
// For each node in the tree, if the node is in the ids, remove it from the tree
|
|
98
180
|
const [newTree2, _removedNodes] = removeNodes(
|
|
99
181
|
state.tree,
|
|
100
|
-
action.payload.ids
|
|
182
|
+
action.payload.ids,
|
|
101
183
|
);
|
|
102
184
|
// Get children of the removed nodes
|
|
103
185
|
// If children are not present elsewhere in the tree, insert them
|
|
@@ -112,21 +194,37 @@ function treeReducer(state: TreeState, action: TreeAction) {
|
|
|
112
194
|
...state,
|
|
113
195
|
tree: [...newTree2, ...children],
|
|
114
196
|
selectedNodes: state.selectedNodes.filter(
|
|
115
|
-
(id) => !action.payload.ids.includes(id)
|
|
197
|
+
(id) => !action.payload.ids.includes(id),
|
|
116
198
|
),
|
|
117
199
|
};
|
|
118
200
|
case "select-node":
|
|
119
201
|
const { ids } = action.payload;
|
|
120
|
-
|
|
202
|
+
|
|
203
|
+
const type =
|
|
204
|
+
action.payload.ids.length > 0
|
|
205
|
+
? findNodeById(state.tree, ids[0])?.type
|
|
206
|
+
: null;
|
|
207
|
+
|
|
208
|
+
return { ...state, selectedNodes: ids, selectedEntityType: type };
|
|
121
209
|
// otherwise fall through to toggle-node-selected for a single ID
|
|
122
210
|
case "toggle-node-selected":
|
|
123
211
|
const nodesToAdd = action.payload.ids.filter(
|
|
124
|
-
(id) => !state.selectedNodes.includes(id)
|
|
212
|
+
(id) => !state.selectedNodes.includes(id),
|
|
125
213
|
);
|
|
126
214
|
const nodesToKeep = state.selectedNodes.filter(
|
|
127
|
-
(id) => !action.payload.ids.includes(id)
|
|
215
|
+
(id) => !action.payload.ids.includes(id),
|
|
128
216
|
);
|
|
129
|
-
|
|
217
|
+
|
|
218
|
+
const newType =
|
|
219
|
+
action.payload.ids.length > 0
|
|
220
|
+
? findNodeById(state.tree, action.payload.ids[0])?.type
|
|
221
|
+
: null;
|
|
222
|
+
|
|
223
|
+
return {
|
|
224
|
+
...state,
|
|
225
|
+
selectedNodes: [...nodesToKeep, ...nodesToAdd],
|
|
226
|
+
selectedEntityType: newType,
|
|
227
|
+
};
|
|
130
228
|
|
|
131
229
|
case "create-node":
|
|
132
230
|
const newId = state.lastInternalId - 1;
|
|
@@ -146,6 +244,25 @@ function treeReducer(state: TreeState, action: TreeAction) {
|
|
|
146
244
|
lastInternalId: newId,
|
|
147
245
|
};
|
|
148
246
|
|
|
247
|
+
case "delete-entity-type": {
|
|
248
|
+
// Remove the entity type from the map
|
|
249
|
+
console.log("Deleting entity type:", action.payload.id);
|
|
250
|
+
const { id } = action.payload;
|
|
251
|
+
const newEntityTypesMap = new Map(state.entityTypesMap);
|
|
252
|
+
const oldType = newEntityTypesMap.get(id);
|
|
253
|
+
newEntityTypesMap.delete(id);
|
|
254
|
+
|
|
255
|
+
const defaultType = newEntityTypesMap.values().next().value;
|
|
256
|
+
const newTree = updateTreeTypes(state.tree, oldType, defaultType);
|
|
257
|
+
|
|
258
|
+
return {
|
|
259
|
+
...state,
|
|
260
|
+
tree: newTree,
|
|
261
|
+
entityTypesMap: newEntityTypesMap,
|
|
262
|
+
selectedNodes: [],
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
149
266
|
/** Entity type selection */
|
|
150
267
|
case "toggle-entity-type-selector":
|
|
151
268
|
return {
|
|
@@ -197,7 +314,7 @@ function nodeIsInTree(tree: TreeData[], id: number): boolean {
|
|
|
197
314
|
|
|
198
315
|
function buildNestedSpec(
|
|
199
316
|
keyPath: (number | "children")[],
|
|
200
|
-
innerSpec: Spec<any
|
|
317
|
+
innerSpec: Spec<any>,
|
|
201
318
|
): Spec<TreeData[]> {
|
|
202
319
|
// Build a nested object from a key path
|
|
203
320
|
|
|
@@ -211,7 +328,7 @@ function buildNestedSpec(
|
|
|
211
328
|
|
|
212
329
|
function findNode(
|
|
213
330
|
tree: TreeData[],
|
|
214
|
-
id: number
|
|
331
|
+
id: number,
|
|
215
332
|
): (number | "children")[] | null {
|
|
216
333
|
// Find the index of the node with the given id in the tree, returning the key path
|
|
217
334
|
for (let i = 0; i < tree.length; i++) {
|
|
@@ -229,7 +346,7 @@ function findNode(
|
|
|
229
346
|
|
|
230
347
|
function removeNodes(
|
|
231
348
|
tree: TreeData[],
|
|
232
|
-
ids: number[]
|
|
349
|
+
ids: number[],
|
|
233
350
|
): [TreeData[], TreeData[]] {
|
|
234
351
|
/** Remove nodes with the given ids from the tree and return the new tree and the removed nodes */
|
|
235
352
|
let newTree: TreeData[] = [];
|
|
@@ -259,6 +376,8 @@ export interface EntityOutput {
|
|
|
259
376
|
name: string;
|
|
260
377
|
match: any | null;
|
|
261
378
|
reasoning: string | null;
|
|
379
|
+
color: string | null;
|
|
380
|
+
children: any[] | null;
|
|
262
381
|
}
|
|
263
382
|
|
|
264
383
|
export interface GraphData {
|
|
@@ -279,15 +398,17 @@ export function treeToGraph(tree: TreeData[]): GraphData {
|
|
|
279
398
|
continue;
|
|
280
399
|
}
|
|
281
400
|
|
|
282
|
-
const { indices, id, name } = node;
|
|
401
|
+
const { indices, id, name, type, children } = node;
|
|
283
402
|
|
|
284
403
|
const nodeData: EntityOutput = {
|
|
285
404
|
id,
|
|
286
|
-
type:
|
|
405
|
+
type: type.id,
|
|
406
|
+
color: type.color,
|
|
287
407
|
name,
|
|
288
408
|
txt_range: [indices],
|
|
289
409
|
reasoning: null,
|
|
290
410
|
match: node.match,
|
|
411
|
+
children,
|
|
291
412
|
};
|
|
292
413
|
|
|
293
414
|
nodeMap.set(node.id, node);
|
|
@@ -300,7 +421,7 @@ export function treeToGraph(tree: TreeData[]): GraphData {
|
|
|
300
421
|
|
|
301
422
|
// Now process the children
|
|
302
423
|
const { nodes: childNodes, edges: childEdges } = treeToGraph(
|
|
303
|
-
node.children
|
|
424
|
+
node.children,
|
|
304
425
|
);
|
|
305
426
|
nodes.push(...childNodes);
|
|
306
427
|
edges.push(...childEdges);
|
|
@@ -309,3 +430,50 @@ export function treeToGraph(tree: TreeData[]): GraphData {
|
|
|
309
430
|
|
|
310
431
|
return { nodes, edges };
|
|
311
432
|
}
|
|
433
|
+
|
|
434
|
+
function findNodeById(tree, id) {
|
|
435
|
+
for (const node of tree) {
|
|
436
|
+
if (node.id === id) {
|
|
437
|
+
return node;
|
|
438
|
+
}
|
|
439
|
+
if (node.children) {
|
|
440
|
+
const found = findNodeById(node.children, id);
|
|
441
|
+
if (found) return found;
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
return null;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function updateTreeTypes(tree, oldType, defaultType) {
|
|
448
|
+
return tree.map((node) => updateNodeType(node, oldType, defaultType));
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function updateNodeType(node, oldType, defaultType) {
|
|
452
|
+
const type = node.type.id === oldType.id ? defaultType : node.type;
|
|
453
|
+
|
|
454
|
+
return {
|
|
455
|
+
...node,
|
|
456
|
+
type,
|
|
457
|
+
children: node.children
|
|
458
|
+
? updateTreeTypes(node.children, oldType, defaultType)
|
|
459
|
+
: [],
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function flattenAndSort(nodes) {
|
|
464
|
+
const result = [];
|
|
465
|
+
|
|
466
|
+
function traverse(nodeList) {
|
|
467
|
+
for (const node of nodeList) {
|
|
468
|
+
result.push(node);
|
|
469
|
+
if (Array.isArray(node.children) && node.children.length > 0) {
|
|
470
|
+
traverse(node.children);
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
traverse(nodes);
|
|
476
|
+
|
|
477
|
+
// sort by start
|
|
478
|
+
return result.sort((a, b) => a.indices[0] - b.indices[0]);
|
|
479
|
+
}
|
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
:root
|
|
2
|
+
--text-line-height: 3em
|
|
3
|
+
--main-extra-width: 200px
|
|
4
|
+
|
|
5
|
+
.page-wrapper
|
|
6
|
+
display: flex
|
|
7
|
+
flex-direction: row
|
|
8
|
+
position: relative
|
|
9
|
+
gap: 2em
|
|
10
|
+
align-items: flex-start // makes control-content lose stick
|
|
11
|
+
|
|
12
|
+
.feedback-container
|
|
13
|
+
flex: 4
|
|
14
|
+
|
|
15
|
+
.control-panel
|
|
16
|
+
flex: 1
|
|
17
|
+
height: auto
|
|
18
|
+
|
|
19
|
+
.control-content
|
|
20
|
+
position: sticky
|
|
21
|
+
top: 0
|
|
22
|
+
|
|
1
23
|
.feedback-component
|
|
2
24
|
position: relative
|
|
3
25
|
width: 800px
|
|
@@ -8,22 +30,20 @@
|
|
|
8
30
|
.node
|
|
9
31
|
cursor: pointer
|
|
10
32
|
|
|
33
|
+
circle
|
|
34
|
+
cursor: pointer
|
|
35
|
+
border: 1px solid black
|
|
11
36
|
|
|
37
|
+
.selected
|
|
38
|
+
border: 1px solid white
|
|
39
|
+
|
|
12
40
|
.feedback-text
|
|
13
41
|
margin-bottom: 2em
|
|
14
42
|
|
|
15
43
|
.entity-panel
|
|
16
44
|
position: relative
|
|
17
45
|
max-height: 600px
|
|
18
|
-
|
|
19
|
-
.control-panel
|
|
20
|
-
max-width: 15em
|
|
21
|
-
position: absolute
|
|
22
|
-
top: 1em
|
|
23
|
-
right: 1em
|
|
24
|
-
padding: 0.2em 0.5em
|
|
25
|
-
|
|
26
|
-
.entity-panel
|
|
46
|
+
width: calc(100% - 2em)
|
|
27
47
|
flex: 1
|
|
28
48
|
min-height: 100px
|
|
29
49
|
padding: 1em
|
|
@@ -35,3 +55,95 @@
|
|
|
35
55
|
.selection-tree
|
|
36
56
|
margin: -1em 0
|
|
37
57
|
padding: 1em 0
|
|
58
|
+
|
|
59
|
+
.type-list
|
|
60
|
+
display: grid
|
|
61
|
+
grid-auto-flow: column
|
|
62
|
+
grid-template-rows: repeat(10, auto)
|
|
63
|
+
gap: 0.2em
|
|
64
|
+
|
|
65
|
+
.type-tag
|
|
66
|
+
padding: .2em .5em
|
|
67
|
+
border-radius: .2em
|
|
68
|
+
|
|
69
|
+
.description
|
|
70
|
+
max-width: 300px
|
|
71
|
+
padding: .5em
|
|
72
|
+
|
|
73
|
+
mark
|
|
74
|
+
border-radius: .2em
|
|
75
|
+
cursor: pointer
|
|
76
|
+
color: black !important
|
|
77
|
+
|
|
78
|
+
[role="treeitem"]
|
|
79
|
+
width: auto !important
|
|
80
|
+
|
|
81
|
+
.highlight
|
|
82
|
+
cursor: pointer
|
|
83
|
+
padding: .2em 0
|
|
84
|
+
border-radius: .2em
|
|
85
|
+
position: relative
|
|
86
|
+
zIndex: 10
|
|
87
|
+
|
|
88
|
+
.feedback-text-wrapper
|
|
89
|
+
position: relative
|
|
90
|
+
z-index: 0
|
|
91
|
+
overflow: visible
|
|
92
|
+
line-height: var(--text-line-height)
|
|
93
|
+
|
|
94
|
+
.type-container
|
|
95
|
+
display: flex
|
|
96
|
+
justify-content: space-between
|
|
97
|
+
align-items: center
|
|
98
|
+
column-gap: 1em
|
|
99
|
+
|
|
100
|
+
.add-type
|
|
101
|
+
cursor: pointer
|
|
102
|
+
display: flex
|
|
103
|
+
justify-content: space-between
|
|
104
|
+
padding: 0 .5em
|
|
105
|
+
align-items: center
|
|
106
|
+
|
|
107
|
+
p
|
|
108
|
+
margin: 0
|
|
109
|
+
|
|
110
|
+
.overlay-container
|
|
111
|
+
height: 80vh
|
|
112
|
+
width: 100vw
|
|
113
|
+
display: flex
|
|
114
|
+
justify-content: center
|
|
115
|
+
align-items: center
|
|
116
|
+
|
|
117
|
+
.add-type-overlay
|
|
118
|
+
background-color: var(--secondary-color)
|
|
119
|
+
padding: .5em 1em
|
|
120
|
+
display: flex
|
|
121
|
+
flex-direction: column
|
|
122
|
+
gap: 1em
|
|
123
|
+
border-radius: .2em
|
|
124
|
+
|
|
125
|
+
.title
|
|
126
|
+
display: flex
|
|
127
|
+
justify-content: space-between
|
|
128
|
+
align-items: center
|
|
129
|
+
|
|
130
|
+
h2
|
|
131
|
+
margin: 0
|
|
132
|
+
|
|
133
|
+
.form-group
|
|
134
|
+
display: flex
|
|
135
|
+
gap: 3em
|
|
136
|
+
|
|
137
|
+
.text-inputs
|
|
138
|
+
display: flex
|
|
139
|
+
flex-direction: column
|
|
140
|
+
gap: 1em
|
|
141
|
+
|
|
142
|
+
.icons
|
|
143
|
+
display: flex
|
|
144
|
+
gap: .25em
|
|
145
|
+
|
|
146
|
+
.node-label
|
|
147
|
+
fill: var(--text-emphasized-color)
|
|
148
|
+
fontSize: 10px
|
|
149
|
+
pointerEvents: none
|