Skip to content

Commit 26c41c9

Browse files
committed
Merge branch 'rc' into sy-3556-use-high-performance-codecs-for-iterators
2 parents 8b2f3e4 + c6358eb commit 26c41c9

639 files changed

Lines changed: 26331 additions & 13747 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ work
7272
# |||| DATA ||||
7373

7474
**/data
75+
!pluto/src/schematic/edge/data
76+
!pluto/src/schematic/edge/data/**
7577
synnax-data
7678
synnax-data/**
7779

console/src/arc/editor/Editor.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ export const useLoadRemote = createLoadRemote<arc.Arc>({
2929
useSelectVersion,
3030
actionCreator: (v) =>
3131
internalCreate({
32-
version: "0.0.0",
32+
version: "1.0.0",
3333
key: v.key,
3434
remoteCreated: true,
3535
graph: translateGraphToConsole(v.graph),
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright 2026 Synnax Labs, Inc.
2+
//
3+
// Use of this software is governed by the Business Source License included in the file
4+
// licenses/BSL.txt.
5+
//
6+
// As of the Change Date specified in that file, in accordance with the Business Source
7+
// License, use of this software will be governed by the Apache License, Version 2.0,
8+
// included in the file licenses/APL.txt.
9+
10+
import { type Dispatch, type UnknownAction } from "@reduxjs/toolkit";
11+
import { context } from "@synnaxlabs/pluto";
12+
13+
export interface ArcEditorContext {
14+
layoutKey: string;
15+
dispatch: Dispatch<UnknownAction>;
16+
}
17+
18+
// This is a temporary type that is used for drilling the arc program key and
19+
// the UNDOABLE dispatch down into the arc stage renderer. This function will
20+
// be removed when arc no longer gets stored in redux.
21+
export const [Provider, useArcEditorContext] = context.create<ArcEditorContext>({
22+
providerName: "ArcEditor.Provider",
23+
displayName: "ArcEditor.Context",
24+
});

console/src/arc/editor/graph/Editor.tsx

Lines changed: 53 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,12 @@
77
// License, use of this software will be governed by the Apache License, Version 2.0,
88
// included in the file licenses/APL.txt.
99

10-
import { type Dispatch, type UnknownAction } from "@reduxjs/toolkit";
1110
import { arc } from "@synnaxlabs/client";
1211
import { useSelectWindowKey } from "@synnaxlabs/drift/react";
1312
import {
1413
Access,
1514
Arc as Base,
15+
Component,
1616
Diagram,
1717
Haul,
1818
Theming,
@@ -24,24 +24,26 @@ import { type ReactElement, useCallback, useMemo, useRef } from "react";
2424
import { useDispatch } from "react-redux";
2525

2626
import { Controls } from "@/arc/editor/Controls";
27+
import { Provider, useArcEditorContext } from "@/arc/editor/graph/Context";
2728
import {
2829
select,
2930
useSelect,
3031
useSelectNodeProps,
32+
useSelectSelected,
3133
useSelectViewportMode,
3234
} from "@/arc/selectors";
3335
import {
3436
addElement,
35-
clearSelection,
37+
applyEdgeChanges,
38+
applyNodeChanges,
3639
copySelection,
3740
internalCreate,
3841
pasteSelection,
3942
selectAll,
40-
setEdges,
4143
setEditable,
4244
setElementProps,
4345
setFitViewOnResize,
44-
setNodes,
46+
setSelected,
4547
setViewport,
4648
setViewportMode,
4749
type State,
@@ -65,42 +67,35 @@ export const filterHaulItems = (items: Haul.Item[]): HaulItem[] =>
6567

6668
export const canDropHaulItem = Haul.canDropOfType<HaulItem>(HAUL_TYPE);
6769

68-
interface SymbolRendererProps extends Diagram.SymbolProps {
69-
layoutKey: string;
70-
dispatch: Dispatch<UnknownAction>;
71-
}
72-
73-
const StageRenderer = ({
74-
symbolKey,
70+
const NodeRenderer = ({
71+
nodeKey,
7572
position,
7673
selected,
7774
draggable,
78-
dispatch,
79-
layoutKey,
80-
}: SymbolRendererProps): ReactElement | null => {
81-
const props = useSelectNodeProps(layoutKey, symbolKey);
75+
}: Diagram.NodeProps): ReactElement | null => {
76+
const { layoutKey, dispatch } = useArcEditorContext("ArcEditor.NodeRenderer");
77+
const props = useSelectNodeProps(layoutKey, nodeKey);
8278
const { key = "", ...rest } = props ?? {};
8379
const handleChange = useCallback(
8480
(props: object) => {
8581
if (key == null) return;
8682
dispatch(
8783
setElementProps({
8884
layoutKey,
89-
key: symbolKey,
85+
key: nodeKey,
9086
props: { key, ...props },
9187
}),
9288
);
9389
},
94-
[symbolKey, layoutKey, key, dispatch],
90+
[nodeKey, layoutKey, key, dispatch],
9591
);
9692
if (props == null) return null;
9793
const C = Base.Stage.REGISTRY[key];
9894
if (C == null) throw new Error(`Symbol ${key} not found`);
9995
return (
10096
<C.Symbol
10197
key={key}
102-
id={symbolKey}
103-
symbolKey={symbolKey}
98+
nodeKey={nodeKey}
10499
position={position}
105100
selected={selected}
106101
draggable={draggable}
@@ -110,6 +105,10 @@ const StageRenderer = ({
110105
);
111106
};
112107

108+
const ArcDiagram = Base.create({
109+
node: Component.renderProp(NodeRenderer),
110+
});
111+
113112
export const ContextMenu: Layout.ContextMenuRenderer = ({ layoutKey }) => (
114113
<CMenu.Menu>
115114
<Layout.MenuItems layoutKey={layoutKey} />
@@ -128,33 +127,39 @@ export const Editor: Layout.Renderer = ({ layoutKey, visible }) => {
128127
const [undoableDispatch, undo, redo] = useUndoableDispatch<RootState, State>(
129128
selector,
130129
internalCreate,
131-
30, // roughly the right time needed to prevent actions that get dispatch automatically by Diagram.tsx, like setNodes immediately following addElement
130+
30,
132131
);
133132

134133
const theme = Theming.use();
135134
const viewportRef = useSyncedRef(state.graph.viewport);
136135
const hasUpdatePermission = Access.useUpdateGranted(arc.ontologyID(layoutKey));
137136
const canEdit = hasUpdatePermission && state.graph.editable;
138137

139-
const handleEdgesChange: Diagram.DiagramProps["onEdgesChange"] = useCallback(
140-
(edges) => undoableDispatch(setEdges({ key: layoutKey, edges })),
141-
[layoutKey, undoableDispatch],
138+
const selected = useSelectSelected(layoutKey);
139+
140+
const handleSelectionChange = useCallback(
141+
(selected: string[]) => dispatch(setSelected({ key: layoutKey, selected })),
142+
[layoutKey, dispatch],
142143
);
143144

144-
const handleNodesChange: Diagram.DiagramProps["onNodesChange"] = useCallback(
145-
(nodes, changes) => {
146-
if (
147-
// @ts-expect-error - Sometimes, the nodes do have dragging
148-
nodes.some((n) => n.dragging) ||
149-
changes.some((c) => c.type === "select")
150-
)
151-
// don't remember dragging a node or selecting an element
152-
dispatch(setNodes({ key: layoutKey, nodes }));
153-
else undoableDispatch(setNodes({ key: layoutKey, nodes }));
145+
const handleNodesChange = useCallback(
146+
(changes: Diagram.NodeChange[]) => {
147+
const dragging = changes.some(
148+
(c) => c.type === "position" && c.dragging === true,
149+
);
150+
const action = applyNodeChanges({ key: layoutKey, changes });
151+
if (dragging) dispatch(action);
152+
else undoableDispatch(action);
154153
},
155154
[layoutKey, dispatch, undoableDispatch],
156155
);
157156

157+
const handleEdgesChange = useCallback(
158+
(changes: Diagram.EdgeChange[]) =>
159+
undoableDispatch(applyEdgeChanges({ key: layoutKey, changes })),
160+
[layoutKey, undoableDispatch],
161+
);
162+
158163
const handleViewportChange: Diagram.DiagramProps["onViewportChange"] = useCallback(
159164
(vp) => dispatch(setViewport({ key: layoutKey, viewport: vp })),
160165
[layoutKey, dispatch],
@@ -171,13 +176,6 @@ export const Editor: Layout.Renderer = ({ layoutKey, visible }) => {
171176
[layoutKey, dispatch],
172177
);
173178

174-
const elRenderer = useCallback(
175-
(props: Diagram.SymbolProps) => (
176-
<StageRenderer layoutKey={layoutKey} dispatch={undoableDispatch} {...props} />
177-
),
178-
[layoutKey, undoableDispatch],
179-
);
180-
181179
const ref = useRef<HTMLDivElement>(null);
182180

183181
const calculateCursorPosition = useCallback(
@@ -264,7 +262,7 @@ export const Editor: Layout.Renderer = ({ layoutKey, visible }) => {
264262
);
265263

266264
const handleClearSelection = useCallback(
267-
() => dispatch(clearSelection({ key: layoutKey })),
265+
() => dispatch(setSelected({ key: layoutKey, selected: [] })),
268266
[dispatch, layoutKey],
269267
);
270268

@@ -278,13 +276,15 @@ export const Editor: Layout.Renderer = ({ layoutKey, visible }) => {
278276
region: ref,
279277
});
280278

279+
const ctxValue = useMemo(
280+
() => ({ layoutKey, dispatch: undoableDispatch }),
281+
[layoutKey, undoableDispatch],
282+
);
283+
281284
return (
282-
<div
283-
ref={ref}
284-
onDoubleClick={handleDoubleClick}
285-
style={{ width: "inherit", height: "inherit", position: "relative" }}
286-
>
287-
<Base.Arc
285+
<Provider value={ctxValue}>
286+
<ArcDiagram
287+
ref={ref}
288288
viewportMode={viewportMode}
289289
onViewportModeChange={handleViewportModeChange}
290290
onViewportChange={handleViewportChange}
@@ -294,6 +294,8 @@ export const Editor: Layout.Renderer = ({ layoutKey, visible }) => {
294294
// effects on the arc sizing and ensures that we position all the lines
295295
// in the correct place.
296296
viewport={{ ...state.graph.viewport, zoom: 1 }}
297+
selected={selected}
298+
onSelectionChange={handleSelectionChange}
297299
onEdgesChange={handleEdgesChange}
298300
onNodesChange={handleNodesChange}
299301
onEditableChange={handleEditableChange}
@@ -305,14 +307,13 @@ export const Editor: Layout.Renderer = ({ layoutKey, visible }) => {
305307
visible={visible}
306308
{...dropProps}
307309
>
308-
<Diagram.NodeRenderer>{elRenderer}</Diagram.NodeRenderer>
309310
<Diagram.Background />
310311
<BaseControls x>
311-
<Diagram.FitViewControl />
312-
{hasUpdatePermission && <Diagram.ToggleEditControl />}
312+
<Diagram.Controls.FitView />
313+
{hasUpdatePermission && <Diagram.Controls.ToggleEdit />}
313314
</BaseControls>
314-
</Base.Arc>
315+
</ArcDiagram>
315316
<Controls state={state} />
316-
</div>
317+
</Provider>
317318
);
318319
};

console/src/arc/selectors.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,11 @@ export const selectSelectedElementDigests = (
5252
layoutKey: string,
5353
): ElementDigest[] => {
5454
const arc = select(state, layoutKey);
55-
return arc.graph.nodes
56-
.filter((node) => node.selected)
57-
.map((node) => ({ key: node.key, type: "node" }));
55+
const nodeKeys = new Set(arc.graph.nodes.map((n) => n.key));
56+
return arc.graph.selected.map((key) => ({
57+
key,
58+
type: nodeKeys.has(key) ? "node" : "edge",
59+
}));
5860
};
5961

6062
export const useSelectSelectedElementDigests = (layoutKey: string): ElementDigest[] =>
@@ -84,20 +86,27 @@ export const selectSelectedElementsProps = (
8486
): ElementInfo[] => {
8587
const arc = select(state, layoutKey);
8688
if (arc == null) return [];
89+
const selectedSet = new Set(arc.graph.selected);
8790
const nodes: ElementInfo[] = arc.graph.nodes
88-
.filter((node) => node.selected)
91+
.filter((node) => selectedSet.has(node.key))
8992
.map((node) => ({
9093
key: node.key,
9194
type: "node",
9295
node,
9396
props: arc.graph.props[node.key],
9497
}));
9598
const edges: ElementInfo[] = arc.graph.edges
96-
.filter((edge) => edge.selected)
99+
.filter((edge) => selectedSet.has(edge.key))
97100
.map((edge) => ({ key: edge.key, type: "edge", edge }));
98101
return [...nodes, ...edges];
99102
};
100103

104+
export const selectSelected = (state: StoreState, layoutKey: string): string[] =>
105+
select(state, layoutKey).graph.selected;
106+
107+
export const useSelectSelected = (layoutKey: string): string[] =>
108+
useMemoSelect((state: StoreState) => selectSelected(state, layoutKey), [layoutKey]);
109+
101110
export const selectEdge = (
102111
state: StoreState,
103112
layoutKey: string,

console/src/arc/services/link.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ export const handleLink: Link.Handler = async ({ client, key, placeLayout }) =>
1818
placeLayout(
1919
Arc.Editor.create({
2020
name,
21-
version: "0.0.0",
21+
version: "1.0.0",
2222
key,
2323
type: "arc",
2424
remoteCreated: true,

console/src/arc/services/ontology.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ const load = async (client: Synnax, id: ontology.ID, placeLayout: Layout.Placer)
3838
placeLayout(
3939
Arc.Editor.create({
4040
name,
41-
version: "0.0.0",
41+
version: "1.0.0",
4242
key,
4343
type: "arc",
4444
remoteCreated: true,

0 commit comments

Comments
 (0)