nexus-shell 0.2.3 → 0.2.5
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/README.md +7 -1
- package/dist/components/graph/GraphCanvas.d.ts +95 -0
- package/dist/components/graph/GraphEdge.d.ts +71 -0
- package/dist/components/graph/GraphNode.d.ts +68 -0
- package/dist/components/graph/NodePalette.d.ts +76 -0
- package/dist/components/graph/index.d.ts +16 -0
- package/dist/components/graph/useGraphKeyboard.d.ts +115 -0
- package/dist/components/widgets/SettingsPanel.d.ts +6 -1
- package/dist/components/widgets/ThemeSwitcher.d.ts +7 -2
- package/dist/core/Boot.d.ts +1 -1
- package/dist/core/services/ThemeService.d.ts +10 -1
- package/dist/index.d.ts +3 -0
- package/dist/lib/graph.d.ts +144 -0
- package/dist/lib/themes.d.ts +57 -0
- package/dist/nexus-shell.es.js +1941 -1217
- package/dist/nexus-shell.umd.js +1 -1
- package/dist/style.css +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -129,11 +129,17 @@ shell's own frame and keep every registration working.
|
|
|
129
129
|
| `TerminalPane` | Bottom-docked terminal log and input |
|
|
130
130
|
| `ThemeSwitcher` | Segmented theme control |
|
|
131
131
|
| `TreeWidget` | Virtualised tree for any hierarchy, with drag-to-move and a data-driven menu |
|
|
132
|
+
| `GraphCanvas` | Infinite pannable, zoomable field for node-and-edge editing |
|
|
133
|
+
| `GraphNode` | Positioned, focusable node with edge ports |
|
|
134
|
+
| `GraphEdge` | Directed edge with bezier / smoothstep / straight routing |
|
|
135
|
+
| `NodePalette` | Drag-to-create and keyboard-to-create node types |
|
|
136
|
+
| `useGraphKeyboard` | Spatial keyboard navigation and editing for a graph |
|
|
132
137
|
| `UserProfile` | Avatar and identity widget |
|
|
133
138
|
|
|
134
139
|
## Theming
|
|
135
140
|
|
|
136
|
-
|
|
141
|
+
Four themes ship in the stylesheet: `theme-light`, `theme-dark`, `theme-gt`
|
|
142
|
+
(Georgia Tech) and `theme-tamu` (Texas A&M).
|
|
137
143
|
Colors are CSS custom properties scoped to those classes, so switching themes is
|
|
138
144
|
a class on an ancestor — no React context, no provider.
|
|
139
145
|
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { default as React, ReactNode } from 'react';
|
|
2
|
+
import { IGraphNode, IPoint, IViewport } from '../../lib/graph';
|
|
3
|
+
|
|
4
|
+
export interface GraphCanvasHandle {
|
|
5
|
+
/** Current pan and zoom. */
|
|
6
|
+
getViewport: () => IViewport;
|
|
7
|
+
/** Jump to a viewport. */
|
|
8
|
+
setViewport: (viewport: IViewport) => void;
|
|
9
|
+
/** Frame the given nodes, or clear the viewport when the list is empty. */
|
|
10
|
+
fitTo: (nodes: IGraphNode[], padding?: number) => void;
|
|
11
|
+
/** Multiply the zoom about the centre of the element. */
|
|
12
|
+
zoomBy: (factor: number) => void;
|
|
13
|
+
/** Convert a client (page) point into graph space. */
|
|
14
|
+
clientToGraph: (client: IPoint) => IPoint;
|
|
15
|
+
/** Move keyboard focus to the canvas. */
|
|
16
|
+
focus: () => void;
|
|
17
|
+
}
|
|
18
|
+
export interface GraphCanvasProps {
|
|
19
|
+
/**
|
|
20
|
+
* Content rendered in graph space. Position children with `GraphNode`, or
|
|
21
|
+
* with your own absolutely-positioned elements in graph coordinates.
|
|
22
|
+
*/
|
|
23
|
+
children?: ReactNode;
|
|
24
|
+
/**
|
|
25
|
+
* Content rendered in screen space, above the graph and unaffected by pan or
|
|
26
|
+
* zoom — toolbars, a minimap, a legend.
|
|
27
|
+
*/
|
|
28
|
+
overlay?: ReactNode;
|
|
29
|
+
/**
|
|
30
|
+
* Take control of pan and zoom. Omit it and the canvas manages its own,
|
|
31
|
+
* which is what most callers want: you should not have to own a viewport
|
|
32
|
+
* just to draw a graph.
|
|
33
|
+
*/
|
|
34
|
+
viewport?: IViewport;
|
|
35
|
+
/** Starting viewport when uncontrolled. Defaults to origin at 1× zoom. */
|
|
36
|
+
defaultViewport?: IViewport;
|
|
37
|
+
/** Called whenever pan or zoom changes, controlled or not. */
|
|
38
|
+
onViewportChange?: (viewport: IViewport) => void;
|
|
39
|
+
/**
|
|
40
|
+
* Called with a graph-space point when empty canvas is clicked. Use it to
|
|
41
|
+
* clear selection, or to place a node at the click.
|
|
42
|
+
*/
|
|
43
|
+
onCanvasClick?: (point: IPoint, event: React.MouseEvent) => void;
|
|
44
|
+
/** Called with a graph-space point when empty canvas is double-clicked. */
|
|
45
|
+
onCanvasDoubleClick?: (point: IPoint, event: React.MouseEvent) => void;
|
|
46
|
+
/** Called with a graph-space point on right-click of empty canvas. */
|
|
47
|
+
onCanvasContextMenu?: (point: IPoint, event: React.MouseEvent) => void;
|
|
48
|
+
/**
|
|
49
|
+
* Called when something is dropped on the canvas, with the graph-space point
|
|
50
|
+
* under the cursor. Pair with `NodePalette` for drag-to-create.
|
|
51
|
+
*/
|
|
52
|
+
onDrop?: (point: IPoint, event: React.DragEvent) => void;
|
|
53
|
+
/** Show the dot grid. Defaults to `true`. */
|
|
54
|
+
grid?: boolean;
|
|
55
|
+
/** Grid spacing in graph units. Defaults to `24`. */
|
|
56
|
+
gridSize?: number;
|
|
57
|
+
/** Smallest allowed zoom. Defaults to `0.1`. */
|
|
58
|
+
minScale?: number;
|
|
59
|
+
/** Largest allowed zoom. Defaults to `4`. */
|
|
60
|
+
maxScale?: number;
|
|
61
|
+
/** Allow panning by dragging empty canvas. Defaults to `true`. */
|
|
62
|
+
pannable?: boolean;
|
|
63
|
+
/** Allow zooming with the wheel or trackpad pinch. Defaults to `true`. */
|
|
64
|
+
zoomable?: boolean;
|
|
65
|
+
/** Accessible label for the canvas region. Defaults to `"Graph canvas"`. */
|
|
66
|
+
'aria-label'?: string;
|
|
67
|
+
/** Extra classes merged onto the root element. */
|
|
68
|
+
className?: string;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* An infinite, pannable, zoomable field to draw a graph on.
|
|
72
|
+
*
|
|
73
|
+
* The canvas owns exactly one thing: the mapping between screen pixels and
|
|
74
|
+
* graph space. It knows nothing about nodes, edges, or what any of it means —
|
|
75
|
+
* children are rendered inside a transformed layer, so anything positioned in
|
|
76
|
+
* graph coordinates lands in the right place.
|
|
77
|
+
*
|
|
78
|
+
* Pan by dragging empty space or with the middle mouse button; zoom with the
|
|
79
|
+
* wheel or a trackpad pinch, always anchored to the cursor. Space-drag pans
|
|
80
|
+
* from anywhere, including over a node.
|
|
81
|
+
*
|
|
82
|
+
* Viewport state is internal by default. Pass `viewport` to take control.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```tsx
|
|
86
|
+
* <GraphCanvas onDrop={(point) => addNodeAt(point)}>
|
|
87
|
+
* {nodes.map((node) => (
|
|
88
|
+
* <GraphNode key={node.id} node={node} onMove={move}>
|
|
89
|
+
* {node.label}
|
|
90
|
+
* </GraphNode>
|
|
91
|
+
* ))}
|
|
92
|
+
* </GraphCanvas>
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export declare const GraphCanvas: React.ForwardRefExoticComponent<GraphCanvasProps & React.RefAttributes<GraphCanvasHandle>>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { default as React } from 'react';
|
|
2
|
+
import { GraphPort, IGraphEdge, IGraphNode, IPoint } from '../../lib/graph';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* How an edge gets from one node to the other.
|
|
6
|
+
*
|
|
7
|
+
* - `bezier` — a smooth curve. The default; reads well for dense graphs.
|
|
8
|
+
* - `smoothstep` — right angles with rounded corners, for flowcharts.
|
|
9
|
+
* - `straight` — a direct line.
|
|
10
|
+
*/
|
|
11
|
+
export type EdgeRouting = 'bezier' | 'smoothstep' | 'straight';
|
|
12
|
+
export interface GraphEdgeProps {
|
|
13
|
+
/** The edge to render. */
|
|
14
|
+
edge: IGraphEdge;
|
|
15
|
+
/** The node the edge leaves. */
|
|
16
|
+
source: IGraphNode;
|
|
17
|
+
/** The node the edge enters. */
|
|
18
|
+
target: IGraphNode;
|
|
19
|
+
/** Path shape. Defaults to `"bezier"`. */
|
|
20
|
+
routing?: EdgeRouting;
|
|
21
|
+
/** Draw as selected. */
|
|
22
|
+
selected?: boolean;
|
|
23
|
+
/** Draw an arrowhead at the target end. Defaults to `true`. */
|
|
24
|
+
arrow?: boolean;
|
|
25
|
+
/** Called on click. The wide invisible hit area makes this practical. */
|
|
26
|
+
onSelect?: (id: string, event: React.MouseEvent) => void;
|
|
27
|
+
/** Called on right-click, with the client point for a context menu. */
|
|
28
|
+
onContextMenu?: (id: string, point: IPoint, event: React.MouseEvent) => void;
|
|
29
|
+
/** Extra classes merged onto the visible path. */
|
|
30
|
+
className?: string;
|
|
31
|
+
}
|
|
32
|
+
/** The SVG path for a pair of points and the sides they leave from. */
|
|
33
|
+
export declare const edgePath: (from: IPoint, to: IPoint, fromPort: GraphPort, routing: EdgeRouting) => string;
|
|
34
|
+
/**
|
|
35
|
+
* A directed edge between two nodes, rendered as an SVG path.
|
|
36
|
+
*
|
|
37
|
+
* Which sides the edge leaves and enters is worked out from the nodes'
|
|
38
|
+
* relative positions, so a node needs no handle configuration to be
|
|
39
|
+
* connectable — move a node and the attachment points follow.
|
|
40
|
+
*
|
|
41
|
+
* Render inside {@link GraphEdgeLayer}, which provides the SVG surface and the
|
|
42
|
+
* arrowhead marker.
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```tsx
|
|
46
|
+
* <GraphEdgeLayer>
|
|
47
|
+
* {edges.map((edge) => (
|
|
48
|
+
* <GraphEdge
|
|
49
|
+
* key={edge.id}
|
|
50
|
+
* edge={edge}
|
|
51
|
+
* source={byId[edge.source]}
|
|
52
|
+
* target={byId[edge.target]}
|
|
53
|
+
* onSelect={setSelectedEdge}
|
|
54
|
+
* />
|
|
55
|
+
* ))}
|
|
56
|
+
* </GraphEdgeLayer>
|
|
57
|
+
* ```
|
|
58
|
+
*/
|
|
59
|
+
export declare const GraphEdge: ({ edge, source, target, routing, selected, arrow, onSelect, onContextMenu, className, }: GraphEdgeProps) => import("react/jsx-runtime").JSX.Element;
|
|
60
|
+
export interface GraphEdgeLayerProps {
|
|
61
|
+
children?: React.ReactNode;
|
|
62
|
+
className?: string;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The SVG surface edges are drawn on.
|
|
66
|
+
*
|
|
67
|
+
* Sits beneath the nodes inside a {@link GraphCanvas}, spanning an effectively
|
|
68
|
+
* unbounded area so edges remain visible wherever the graph is panned. It also
|
|
69
|
+
* defines the shared arrowhead marker.
|
|
70
|
+
*/
|
|
71
|
+
export declare const GraphEdgeLayer: ({ children, className }: GraphEdgeLayerProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { default as React, ReactNode } from 'react';
|
|
2
|
+
import { GraphPort, IGraphNode, IPoint } from '../../lib/graph';
|
|
3
|
+
|
|
4
|
+
export interface GraphNodeProps {
|
|
5
|
+
/** The node to render. Position and size come from here. */
|
|
6
|
+
node: IGraphNode;
|
|
7
|
+
/** Node body. Anything — a label, a form, a chart. */
|
|
8
|
+
children?: ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* Called with the new top-left position while dragging, and again on
|
|
11
|
+
* release. The component never mutates the node — you apply the move, which
|
|
12
|
+
* is what lets you snap, constrain or reject it.
|
|
13
|
+
*/
|
|
14
|
+
onMove?: (id: string, position: IPoint) => void;
|
|
15
|
+
/** Called when the drag ends, if the node actually moved. */
|
|
16
|
+
onMoveEnd?: (id: string, position: IPoint) => void;
|
|
17
|
+
/** Called on click. */
|
|
18
|
+
onSelect?: (id: string, event: React.MouseEvent) => void;
|
|
19
|
+
/** Called on double-click. Conventionally enters edit mode. */
|
|
20
|
+
onActivate?: (id: string) => void;
|
|
21
|
+
/** Called on right-click, with the client point for a context menu. */
|
|
22
|
+
onContextMenu?: (id: string, point: IPoint, event: React.MouseEvent) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Called when one of the node's ports is dragged, to begin a connection.
|
|
25
|
+
* Providing it is what makes the ports appear.
|
|
26
|
+
*/
|
|
27
|
+
onConnectStart?: (id: string, port: GraphPort) => void;
|
|
28
|
+
/**
|
|
29
|
+
* Called when a pointer is released over this node, with its id — the target
|
|
30
|
+
* end of a connection started elsewhere.
|
|
31
|
+
*/
|
|
32
|
+
onConnectEnd?: (targetId: string) => void;
|
|
33
|
+
/** Draw as selected. */
|
|
34
|
+
selected?: boolean;
|
|
35
|
+
/**
|
|
36
|
+
* Draw as focused — the keyboard cursor. Distinct from `selected`: focus is
|
|
37
|
+
* where the next keystroke lands, selection is what an action applies to.
|
|
38
|
+
*/
|
|
39
|
+
focused?: boolean;
|
|
40
|
+
/** Show connection ports on hover and while focused. Defaults to `true`. */
|
|
41
|
+
ports?: boolean;
|
|
42
|
+
/** Allow dragging. Defaults to `true`. */
|
|
43
|
+
draggable?: boolean;
|
|
44
|
+
/** Extra classes merged onto the node element. */
|
|
45
|
+
className?: string;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* A positioned, focusable node on a {@link GraphCanvas}.
|
|
49
|
+
*
|
|
50
|
+
* Provides placement, dragging, selection and focus affordances, and the four
|
|
51
|
+
* edge ports. What the node *contains* is entirely yours — pass any children.
|
|
52
|
+
*
|
|
53
|
+
* Fully controlled: dragging reports positions through `onMove` but never moves
|
|
54
|
+
* the node itself, so snapping, constraints and undo stay in your hands.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```tsx
|
|
58
|
+
* <GraphNode
|
|
59
|
+
* node={node}
|
|
60
|
+
* focused={node.id === focusedId}
|
|
61
|
+
* onMove={(id, position) => setNodes(move(nodes, id, position))}
|
|
62
|
+
* onActivate={(id) => setEditing(id)}
|
|
63
|
+
* >
|
|
64
|
+
* <p className="text-xs font-medium">{node.data.label}</p>
|
|
65
|
+
* </GraphNode>
|
|
66
|
+
* ```
|
|
67
|
+
*/
|
|
68
|
+
export declare const GraphNode: ({ node, children, onMove, onMoveEnd, onSelect, onActivate, onContextMenu, onConnectStart, onConnectEnd, selected, focused, ports, draggable, className, }: GraphNodeProps) => import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { default as React, ReactNode } from 'react';
|
|
2
|
+
|
|
3
|
+
/** The MIME type palette entries put their kind on during a drag. */
|
|
4
|
+
export declare const GRAPH_NODE_MIME = "application/x-nexus-graph-node";
|
|
5
|
+
/** An entry in a {@link NodePalette}. */
|
|
6
|
+
export interface INodePaletteItem {
|
|
7
|
+
/**
|
|
8
|
+
* The `kind` given to the node this creates. Also the payload carried on the
|
|
9
|
+
* drag, so the drop handler knows what was dragged.
|
|
10
|
+
*/
|
|
11
|
+
kind: string;
|
|
12
|
+
/** Label shown in the palette. */
|
|
13
|
+
label: string;
|
|
14
|
+
/** Optional icon or swatch rendered before the label. */
|
|
15
|
+
icon?: ReactNode;
|
|
16
|
+
/** One line explaining what the node is, shown as a tooltip. */
|
|
17
|
+
description?: string;
|
|
18
|
+
}
|
|
19
|
+
export interface NodePaletteProps {
|
|
20
|
+
/** The node types on offer. */
|
|
21
|
+
items: INodePaletteItem[];
|
|
22
|
+
/**
|
|
23
|
+
* Called when an item is activated by click or Enter, for creating without a
|
|
24
|
+
* drag. Keyboard users need this — a drag-only palette is unreachable.
|
|
25
|
+
*/
|
|
26
|
+
onSelect?: (item: INodePaletteItem) => void;
|
|
27
|
+
/** Called when a drag starts, if you want to show a drop hint. */
|
|
28
|
+
onDragStart?: (item: INodePaletteItem) => void;
|
|
29
|
+
/** Stack vertically instead of wrapping horizontally. Defaults to `false`. */
|
|
30
|
+
vertical?: boolean;
|
|
31
|
+
/** Extra classes merged onto the root element. */
|
|
32
|
+
className?: string;
|
|
33
|
+
/** Accessible label for the group. Defaults to `"Node palette"`. */
|
|
34
|
+
'aria-label'?: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* A palette of node types that can be dragged onto a {@link GraphCanvas}, or
|
|
38
|
+
* activated with the keyboard.
|
|
39
|
+
*
|
|
40
|
+
* The palette carries only the item's `kind` on the drag; what a node of that
|
|
41
|
+
* kind actually *is* is decided by your drop handler. Pair it with
|
|
42
|
+
* {@link readPaletteDrag} on the canvas.
|
|
43
|
+
*
|
|
44
|
+
* Every item is a real button, so the palette is fully usable without a
|
|
45
|
+
* pointer — `onSelect` fires on click or Enter.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```tsx
|
|
49
|
+
* <GraphCanvas
|
|
50
|
+
* onDrop={(point, event) => {
|
|
51
|
+
* const kind = readPaletteDrag(event);
|
|
52
|
+
* if (kind) addNode({ kind, position: point });
|
|
53
|
+
* }}
|
|
54
|
+
* overlay={
|
|
55
|
+
* <NodePalette
|
|
56
|
+
* items={[{ kind: 'task', label: 'Task' }, { kind: 'gate', label: 'Gate' }]}
|
|
57
|
+
* onSelect={(item) => addNodeAtCentre(item.kind)}
|
|
58
|
+
* />
|
|
59
|
+
* }
|
|
60
|
+
* />
|
|
61
|
+
* ```
|
|
62
|
+
*/
|
|
63
|
+
export declare const NodePalette: ({ items, onSelect, onDragStart, vertical, className, "aria-label": ariaLabel, }: NodePaletteProps) => import("react/jsx-runtime").JSX.Element;
|
|
64
|
+
/**
|
|
65
|
+
* Read the node kind from a drop on the canvas, or `null` if the drag did not
|
|
66
|
+
* come from a {@link NodePalette}.
|
|
67
|
+
*
|
|
68
|
+
* @example
|
|
69
|
+
* ```tsx
|
|
70
|
+
* <GraphCanvas onDrop={(point, event) => {
|
|
71
|
+
* const kind = readPaletteDrag(event);
|
|
72
|
+
* if (kind) createNode(kind, point);
|
|
73
|
+
* }} />
|
|
74
|
+
* ```
|
|
75
|
+
*/
|
|
76
|
+
export declare const readPaletteDrag: (event: React.DragEvent) => string | null;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The graph layer.
|
|
3
|
+
*
|
|
4
|
+
* A decomposed set of primitives for building node-and-edge editors, in the
|
|
5
|
+
* same spirit as the rest of the library: the canvas knows about coordinates,
|
|
6
|
+
* the node knows about placement and focus, the edge knows about geometry, and
|
|
7
|
+
* the keyboard hook knows about traversal. None of them know what a node
|
|
8
|
+
* *means* — that stays in your application.
|
|
9
|
+
*
|
|
10
|
+
* Compose them yourself, or reach for whichever piece you need on its own.
|
|
11
|
+
*/
|
|
12
|
+
export { GraphCanvas, type GraphCanvasProps, type GraphCanvasHandle } from './GraphCanvas';
|
|
13
|
+
export { GraphNode, type GraphNodeProps } from './GraphNode';
|
|
14
|
+
export { GraphEdge, GraphEdgeLayer, edgePath, type GraphEdgeProps, type GraphEdgeLayerProps, type EdgeRouting, } from './GraphEdge';
|
|
15
|
+
export { useGraphKeyboard, DEFAULT_GRAPH_KEYS, type UseGraphKeyboardOptions, type UseGraphKeyboardResult, type GraphKeyboardActions, } from './useGraphKeyboard';
|
|
16
|
+
export { NodePalette, readPaletteDrag, GRAPH_NODE_MIME, type NodePaletteProps, type INodePaletteItem, } from './NodePalette';
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { Direction, IGraphEdge, IGraphNode, IPoint } from '../../lib/graph';
|
|
2
|
+
|
|
3
|
+
/** The default keyboard map. Every binding is overridable. */
|
|
4
|
+
export declare const DEFAULT_GRAPH_KEYS: {
|
|
5
|
+
/** Move focus to the nearest node in a direction. */
|
|
6
|
+
readonly moveFocus: readonly ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"];
|
|
7
|
+
/** Create a connected node in a direction. */
|
|
8
|
+
readonly createConnected: "Tab";
|
|
9
|
+
/** Edit the focused node. */
|
|
10
|
+
readonly edit: "Enter";
|
|
11
|
+
/** Leave edit mode, cancel a pending connection, or clear focus. */
|
|
12
|
+
readonly cancel: "Escape";
|
|
13
|
+
/** Delete the focused node. */
|
|
14
|
+
readonly delete: readonly ["Delete", "Backspace"];
|
|
15
|
+
/** Begin connecting from the focused node. */
|
|
16
|
+
readonly connect: "c";
|
|
17
|
+
/** Nudge the focused node. Held with the arrow keys. */
|
|
18
|
+
readonly nudgeModifier: "shiftKey";
|
|
19
|
+
};
|
|
20
|
+
export interface GraphKeyboardActions {
|
|
21
|
+
/**
|
|
22
|
+
* Create a node at a position, returning its id so focus can follow it. The
|
|
23
|
+
* library never invents node content — you decide what a new node is.
|
|
24
|
+
*/
|
|
25
|
+
onCreateNode?: (position: IPoint, context: {
|
|
26
|
+
from?: string;
|
|
27
|
+
direction?: Direction;
|
|
28
|
+
}) => string | undefined;
|
|
29
|
+
/** Connect two nodes. */
|
|
30
|
+
onConnect?: (sourceId: string, targetId: string) => void;
|
|
31
|
+
/** Delete a node and its edges. */
|
|
32
|
+
onDeleteNode?: (id: string) => void;
|
|
33
|
+
/** Move a node, used by nudging. */
|
|
34
|
+
onMoveNode?: (id: string, position: IPoint) => void;
|
|
35
|
+
/** Enter edit mode for a node. */
|
|
36
|
+
onEditNode?: (id: string) => void;
|
|
37
|
+
}
|
|
38
|
+
export interface UseGraphKeyboardOptions extends GraphKeyboardActions {
|
|
39
|
+
/** The current nodes. Traversal and placement are computed from these. */
|
|
40
|
+
nodes: IGraphNode[];
|
|
41
|
+
/** The current edges. Used to reason about existing connections. */
|
|
42
|
+
edges: IGraphEdge[];
|
|
43
|
+
/** Take control of which node has keyboard focus. */
|
|
44
|
+
focusedId?: string | null;
|
|
45
|
+
/** Starting focus when uncontrolled. */
|
|
46
|
+
defaultFocusedId?: string | null;
|
|
47
|
+
/** Called whenever focus moves. */
|
|
48
|
+
onFocusChange?: (id: string | null) => void;
|
|
49
|
+
/**
|
|
50
|
+
* Element the listener attaches to. Bindings only fire while focus is inside
|
|
51
|
+
* it, so several graphs can coexist on one page.
|
|
52
|
+
*/
|
|
53
|
+
targetRef: React.RefObject<HTMLElement | null>;
|
|
54
|
+
/** How far a nudge moves a node, in graph units. Defaults to `16`. */
|
|
55
|
+
nudgeDistance?: number;
|
|
56
|
+
/** Turn the whole thing off. */
|
|
57
|
+
disabled?: boolean;
|
|
58
|
+
}
|
|
59
|
+
export interface UseGraphKeyboardResult {
|
|
60
|
+
/** The node the next keystroke applies to. */
|
|
61
|
+
focusedId: string | null;
|
|
62
|
+
setFocusedId: (id: string | null) => void;
|
|
63
|
+
/** Node currently being edited, if any. */
|
|
64
|
+
editingId: string | null;
|
|
65
|
+
setEditingId: (id: string | null) => void;
|
|
66
|
+
/** Source of a connection awaiting a target, if any. */
|
|
67
|
+
connectingFrom: string | null;
|
|
68
|
+
setConnectingFrom: (id: string | null) => void;
|
|
69
|
+
/** Move focus as an arrow key would. Exposed for toolbar buttons. */
|
|
70
|
+
moveFocus: (direction: Direction) => void;
|
|
71
|
+
/** Create a connected node as Tab would. */
|
|
72
|
+
createConnected: (direction?: Direction) => void;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Keyboard-driven graph navigation and editing.
|
|
76
|
+
*
|
|
77
|
+
* Headless: it owns the focus, editing and connection cursors and reports what
|
|
78
|
+
* should happen, but renders nothing and mutates nothing. That means it works
|
|
79
|
+
* with `GraphNode` or with your own node rendering, and every mutation stays
|
|
80
|
+
* subject to your validation and undo.
|
|
81
|
+
*
|
|
82
|
+
* The default map, all overridable:
|
|
83
|
+
*
|
|
84
|
+
* | Key | Action |
|
|
85
|
+
* | :-- | :-- |
|
|
86
|
+
* | Arrows | Move focus to the nearest node in that direction |
|
|
87
|
+
* | Shift + Arrows | Nudge the focused node |
|
|
88
|
+
* | Tab / Shift+Tab | Create a node connected to the focused one |
|
|
89
|
+
* | Enter | Edit the focused node |
|
|
90
|
+
* | `c` then a node | Connect from the focused node |
|
|
91
|
+
* | Delete / Backspace | Delete the focused node |
|
|
92
|
+
* | Escape | Leave edit mode, cancel a connection, or clear focus |
|
|
93
|
+
*
|
|
94
|
+
* Arrow traversal is spatial rather than by insertion order — it picks the
|
|
95
|
+
* nearest node in the direction travelled, penalising lateral offset, which is
|
|
96
|
+
* what makes a hand-arranged diagram navigable without a mouse.
|
|
97
|
+
*
|
|
98
|
+
* @example
|
|
99
|
+
* ```tsx
|
|
100
|
+
* const canvasRef = useRef<HTMLDivElement>(null);
|
|
101
|
+
*
|
|
102
|
+
* const keyboard = useGraphKeyboard({
|
|
103
|
+
* nodes, edges, targetRef: canvasRef,
|
|
104
|
+
* onCreateNode: (position) => {
|
|
105
|
+
* const id = nextId('node', nodes);
|
|
106
|
+
* setNodes([...nodes, { id, position, data: { label: 'New' } }]);
|
|
107
|
+
* return id;
|
|
108
|
+
* },
|
|
109
|
+
* onConnect: (source, target) =>
|
|
110
|
+
* setEdges([...edges, { id: nextId('edge', edges), source, target }]),
|
|
111
|
+
* onDeleteNode: (id) => { … },
|
|
112
|
+
* });
|
|
113
|
+
* ```
|
|
114
|
+
*/
|
|
115
|
+
export declare const useGraphKeyboard: ({ nodes, edges, focusedId: controlledFocusedId, defaultFocusedId, onFocusChange, targetRef, nudgeDistance, disabled, onCreateNode, onConnect, onDeleteNode, onMoveNode, onEditNode, }: UseGraphKeyboardOptions) => UseGraphKeyboardResult;
|
|
@@ -6,13 +6,18 @@ export interface ISettingsThemeOption<T extends string = string> {
|
|
|
6
6
|
label: string;
|
|
7
7
|
icon: LucideIcon;
|
|
8
8
|
}
|
|
9
|
+
/**
|
|
10
|
+
* The themes bundled in the library stylesheet, with their full labels.
|
|
11
|
+
* Derived from the theme registry, so a new bundled theme appears here without
|
|
12
|
+
* a code change.
|
|
13
|
+
*/
|
|
9
14
|
export declare const DEFAULT_SETTINGS_THEMES: ISettingsThemeOption[];
|
|
10
15
|
export interface SettingsPanelProps<T extends string = string> {
|
|
11
16
|
/** Currently selected theme id. */
|
|
12
17
|
theme: T;
|
|
13
18
|
/** Called with the newly selected theme id. */
|
|
14
19
|
onThemeChange: (theme: T) => void;
|
|
15
|
-
/** Theme choices to offer. Defaults to
|
|
20
|
+
/** Theme choices to offer. Defaults to every bundled theme. */
|
|
16
21
|
themes?: ISettingsThemeOption<T>[];
|
|
17
22
|
/** Extra classes merged onto the root element. */
|
|
18
23
|
className?: string;
|
|
@@ -5,6 +5,11 @@ export interface IThemeOption<T extends string = string> {
|
|
|
5
5
|
/** Short label shown on the segment. Kept terse — segments are compact. */
|
|
6
6
|
label: string;
|
|
7
7
|
}
|
|
8
|
+
/**
|
|
9
|
+
* The themes bundled in the library stylesheet, using their abbreviated labels
|
|
10
|
+
* because the segments are narrow. Derived from the theme registry, so a new
|
|
11
|
+
* bundled theme appears here without a code change.
|
|
12
|
+
*/
|
|
8
13
|
export declare const DEFAULT_THEME_OPTIONS: IThemeOption[];
|
|
9
14
|
export interface ThemeSwitcherProps<T extends string = string> {
|
|
10
15
|
/** Currently selected theme id. */
|
|
@@ -12,8 +17,8 @@ export interface ThemeSwitcherProps<T extends string = string> {
|
|
|
12
17
|
/** Called with the newly selected theme id. */
|
|
13
18
|
onChange: (theme: T) => void;
|
|
14
19
|
/**
|
|
15
|
-
* Selectable themes. Defaults to
|
|
16
|
-
*
|
|
20
|
+
* Selectable themes. Defaults to every theme bundled in the library's
|
|
21
|
+
* stylesheet. Supply your own to offer a different set.
|
|
17
22
|
*/
|
|
18
23
|
options?: IThemeOption<T>[];
|
|
19
24
|
/** Extra classes merged onto the root element. */
|
package/dist/core/Boot.d.ts
CHANGED
|
@@ -31,7 +31,7 @@ export interface InitializeShellOptions {
|
|
|
31
31
|
defaultMenus?: boolean;
|
|
32
32
|
}
|
|
33
33
|
/** The command ids `initializeShell` registers unless told not to. */
|
|
34
|
-
export declare const DEFAULT_COMMAND_IDS: readonly ["view.toggleTerminal", "view.toggleChat", "view.toggleSidebar",
|
|
34
|
+
export declare const DEFAULT_COMMAND_IDS: readonly ["view.toggleTerminal", "view.toggleChat", "view.toggleSidebar", ...string[]];
|
|
35
35
|
/**
|
|
36
36
|
* Boot the shell: register commands, menus, panels and status bar items into
|
|
37
37
|
* the shell registries and stores.
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
|
|
1
|
+
import { BundledThemeId } from '../../lib/themes';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A theme id.
|
|
5
|
+
*
|
|
6
|
+
* The bundled ids are suggested by autocomplete, but any string is accepted:
|
|
7
|
+
* define a `.theme-yourname` block with the same custom properties and it works
|
|
8
|
+
* exactly like the built-ins.
|
|
9
|
+
*/
|
|
10
|
+
export type ThemeType = BundledThemeId | (string & {});
|
|
2
11
|
interface ThemeState {
|
|
3
12
|
theme: ThemeType;
|
|
4
13
|
setTheme: (theme: ThemeType) => void;
|
package/dist/index.d.ts
CHANGED
|
@@ -56,4 +56,7 @@ export { TerminalPane, type TerminalPaneProps } from './components/widgets/Termi
|
|
|
56
56
|
export { ThemeSwitcher, type ThemeSwitcherProps, type IThemeOption, DEFAULT_THEME_OPTIONS } from './components/widgets/ThemeSwitcher';
|
|
57
57
|
export { TreeWidget, type TreeWidgetProps, type ITreeNode, type ITreeAction, type ITreeContext } from './components/widgets/TreeWidget';
|
|
58
58
|
export { UserProfile, type UserProfileProps, type IUserProfile, type IUserProfileAction } from './components/widgets/UserProfile';
|
|
59
|
+
export * from './components/graph';
|
|
60
|
+
export { DEFAULT_NODE_SIZE, IDENTITY_VIEWPORT, clampScale, edgesOf, findNeighbour, fitViewport, graphBounds, isConnected, nextId, nodeRect, placeRelativeTo, portPoint, rectCenter, removeNode, resolvePorts, toGraphSpace, toScreenSpace, zoomAt, type Direction, type GraphPort, type IGraphEdge, type IGraphNode, type IPoint, type IRect, type IViewport, } from './lib/graph';
|
|
59
61
|
export { cn } from './lib/cn';
|
|
62
|
+
export { BUNDLED_THEMES, BUNDLED_THEME_CLASSES, themeClass, type IBundledTheme, type BundledThemeId, } from './lib/themes';
|