@luxonis/depthai-pipeline-lib 1.5.0 → 1.7.0

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.
@@ -6,16 +6,20 @@ import { css } from '../styled-system/css/css.mjs';
6
6
  import { DOCS_BASE_URL, NodesWithLinks, } from '../services/pipeline.js';
7
7
  const NodeHandles = props => {
8
8
  const { handles, type } = props;
9
- return (_jsx(Flex, { full: true, direction: "column", align: type === 'input' ? 'start' : 'end', children: handles.map(({ type: handleType, id, blocking, queueSize, name }) => (_jsxs(Flex, { position: "relative", align: "end", direction: handleType === 'input' ? 'row' : 'row-reverse', children: [_jsx(Handle, { type: handleType === 'input' ? 'target' : 'source', position: handleType === 'input' ? Position.Left : Position.Right, id: name, isConnectable: false, className: css({
9
+ return (_jsx(Flex, { full: true, direction: "column", align: type === 'input' ? 'start' : 'end', children: handles.map(({ type: handleType, id, blocking, queueSize, name, maxQueueSize, fps }) => (_jsxs(Flex, { position: "relative", align: "end", direction: handleType === 'input' ? 'row' : 'row-reverse', children: [_jsx(Handle, { type: handleType === 'input' ? 'target' : 'source', position: handleType === 'input' ? Position.Left : Position.Right, id: name, isConnectable: false, className: css({
10
10
  width: 'custom.handle.dot!',
11
11
  height: 'custom.handle.dot!',
12
12
  backgroundColor: blocking ? 'dark.warning!' : 'dark.success!',
13
13
  border: 'none!',
14
- }) }), _jsx(SubLabel, { className: css({
15
- ...(type === 'input' //
16
- ? { 'marginLeft': 'xs' }
17
- : { 'marginRight': 'xs' }),
18
- }), text: type === 'input' ? `[${queueSize}] ${name}` : name, break: "none" })] }, id))) }));
14
+ }) }), _jsx(NodeHandlesSubLabel, { type: type, name: name, queueSize: queueSize, maxQueueSize: maxQueueSize, fps: fps })] }, id))) }));
15
+ };
16
+ const NodeHandlesSubLabel = props => {
17
+ const { type, name, queueSize, maxQueueSize, fps } = props;
18
+ return (_jsx(SubLabel, { className: css({
19
+ ...(type === 'input' //
20
+ ? { marginLeft: 'xs' }
21
+ : { marginRight: 'xs' }),
22
+ }), text: `[${fps ? `${fps.toFixed(1)} | ` : ''}${maxQueueSize ? `${maxQueueSize}/${queueSize}` : queueSize}] ${name}`, break: "none" }));
19
23
  };
20
24
  export const PipelineNode = props => {
21
25
  const { data: node } = props;
@@ -38,9 +42,21 @@ export const PipelineNode = props => {
38
42
  }), children: [_jsx("span", { className: css({
39
43
  width: 'icon.sm',
40
44
  height: 'icon.sm',
41
- }) }), _jsx(Label, { text: node.name, color: "unset" }), _jsx(Button, { variant: "ghost", color: "transparent", icon: HelpIcon, onClick: () => window.open(link, '_blank'), className: clsx('node-help-icon', css({
45
+ }) }), _jsx(Label, { text: node.id ? `${node.name} (${node.id})` : node.name, color: "unset" }), _jsx(Button, { variant: "ghost", color: "transparent", icon: HelpIcon, onClick: () => window.open(link, '_blank'), className: clsx('node-help-icon', css({
42
46
  width: 'auto',
43
47
  height: 'auto',
44
48
  right: 'xs',
45
- })) })] }), _jsxs(Flex, { gap: "sm", paddingY: "xs", children: [_jsx(NodeHandles, { type: "input", handles: node.handles.input }), _jsx(NodeHandles, { type: "output", handles: node.handles.output })] })] }));
49
+ })) })] }), _jsx(Handle, { type: "target", position: Position.Top, id: "top", isConnectable: false, className: css({
50
+ width: 'custom.handle.dot!',
51
+ height: 'custom.handle.dot!',
52
+ }), style: {
53
+ border: 'none',
54
+ background: 'transparent',
55
+ } }), _jsxs(Flex, { gap: "sm", paddingY: "xs", children: [_jsx(NodeHandles, { type: "input", handles: node.handles.input }), _jsx(NodeHandles, { type: "output", handles: node.handles.output })] }), _jsx(Handle, { type: "source", position: Position.Bottom, id: "bottom", isConnectable: false, className: css({
56
+ width: 'custom.handle.dot!',
57
+ height: 'custom.handle.dot!',
58
+ }), style: {
59
+ border: 'none',
60
+ background: 'transparent',
61
+ } })] }));
46
62
  };
@@ -1,7 +1,10 @@
1
1
  import React from 'react';
2
2
  import type { FlexProps } from '@luxonis/common-fe-components';
3
- import type { Pipeline } from '../services/pipeline.js';
3
+ import type { ParsedNode, Pipeline } from '../services/pipeline.js';
4
+ import type { PipelineState } from '../services/pipeline-state.js';
4
5
  export type PipelineCanvasProps = FlexProps & {
5
6
  pipeline: Pipeline | null;
7
+ pipelineState?: PipelineState[] | null;
6
8
  };
9
+ export declare const updateNodesOnPipelineStateChange: (nodes: ParsedNode[], pipelineState: PipelineState[]) => ParsedNode[];
7
10
  export declare const PipelineCanvas: React.FC<PipelineCanvasProps>;
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import React from 'react';
3
- import { ReactFlow, ReactFlowProvider, useEdgesState, useNodesState, useReactFlow, useStore, } from '@xyflow/react';
3
+ import { ReactFlow, ReactFlowProvider, useEdgesState, useNodesInitialized, useNodesState, useReactFlow, useStore, } from '@xyflow/react';
4
4
  import { Flex, Header } from '@luxonis/common-fe-components';
5
5
  import Dagre from '@dagrejs/dagre';
6
6
  import { PipelineNode } from './Node.js';
@@ -29,13 +29,116 @@ const getLayoutedElements = (nodes, edges) => {
29
29
  edges,
30
30
  };
31
31
  };
32
- const PipelineCanvasBody = ({ pipeline, ...flexProps }) => {
32
+ const adjustNodes = (nodes) => {
33
+ const PADDING = 16;
34
+ const positionedNodes = nodes
35
+ .filter(node => node.type !== 'group')
36
+ .map((node) => {
37
+ const position = node.position;
38
+ const x = position.x - (node.measured?.width ?? 200) / 2;
39
+ const y = position.y - (node.measured?.height ?? 100) / 2;
40
+ return {
41
+ ...node,
42
+ position: { x, y },
43
+ };
44
+ });
45
+ const groupNodes = nodes.filter(node => node.type === 'group');
46
+ const positionedGroups = groupNodes.map(parent => {
47
+ const children = positionedNodes.filter(n => n.parentId === parent.id);
48
+ if (children.length === 0) {
49
+ return { ...parent, position: { x: 0, y: 0 } };
50
+ }
51
+ let minX = 9999;
52
+ let minY = 9999;
53
+ let maxX = -9999;
54
+ let maxY = -9999;
55
+ // eslint-disable-next-line github/array-foreach
56
+ children.forEach(child => {
57
+ const width = child.measured?.width ?? 200;
58
+ const height = child.measured?.height ?? 100;
59
+ minX = Math.min(minX, child.position.x);
60
+ minY = Math.min(minY, child.position.y);
61
+ maxX = Math.max(maxX, child.position.x + width);
62
+ maxY = Math.max(maxY, child.position.y + height);
63
+ });
64
+ const parentX = minX - PADDING;
65
+ const parentY = minY - PADDING;
66
+ // eslint-disable-next-line github/array-foreach
67
+ children.forEach(child => {
68
+ child.position.x -= parentX;
69
+ child.position.y -= parentY;
70
+ });
71
+ return {
72
+ ...parent,
73
+ position: { x: parentX, y: parentY },
74
+ measured: {
75
+ width: maxX - minX + PADDING * 2,
76
+ height: maxY - minY + PADDING * 2,
77
+ },
78
+ width: maxX - minX + PADDING * 2,
79
+ height: maxY - minY + PADDING * 2,
80
+ style: {
81
+ ...parent.style,
82
+ width: maxX - minX + PADDING * 2,
83
+ height: maxY - minY + PADDING * 2,
84
+ },
85
+ };
86
+ });
87
+ return [...positionedGroups, ...positionedNodes];
88
+ };
89
+ export const updateNodesOnPipelineStateChange = (nodes, pipelineState) => {
90
+ const parsedNodes = [];
91
+ for (const node of nodes) {
92
+ const nodeState = pipelineState.find(state => state.id.toString() === node.id.toString());
93
+ const inputHandles = node.data.handles.input;
94
+ const outputHandles = node.data.handles.output;
95
+ const newInputHandles = [];
96
+ for (const inputHandle of inputHandles) {
97
+ const obj = { ...inputHandle };
98
+ const inputState = nodeState?.inputs[inputHandle.name];
99
+ if (inputState) {
100
+ obj.maxQueueSize = inputState.numQueued;
101
+ obj.fps = inputState.timing.fps;
102
+ }
103
+ newInputHandles.push(obj);
104
+ }
105
+ const newOutputHandles = [];
106
+ for (const outputHandle of outputHandles) {
107
+ const obj = { ...outputHandle };
108
+ const outputState = nodeState?.outputs[outputHandle.name];
109
+ if (outputState) {
110
+ obj.maxQueueSize = outputState.numQueued;
111
+ obj.fps = outputState.timing.fps;
112
+ }
113
+ newOutputHandles.push(obj);
114
+ }
115
+ parsedNodes.push({
116
+ ...node,
117
+ data: {
118
+ ...node.data,
119
+ handles: {
120
+ input: newInputHandles,
121
+ output: newOutputHandles,
122
+ },
123
+ },
124
+ });
125
+ }
126
+ return parsedNodes;
127
+ };
128
+ const PipelineCanvasBody = ({ pipeline,
129
+ // eslint-disable-next-line no-warning-comments
130
+ // TODO: Remove optional after pipelineState is implemented in Viewer and Visualizer
131
+ pipelineState = null, ...flexProps }) => {
33
132
  const { fitView, setViewport, getViewport } = useReactFlow();
34
133
  const autoArrangeRef = React.useRef(true);
35
134
  const widthSelector = (state) => state.width;
36
135
  const heightSelector = (state) => state.height;
37
136
  const reactFlowWidth = useStore(widthSelector);
38
137
  const reactFlowHeight = useStore(heightSelector);
138
+ const nodesInitialized = useNodesInitialized();
139
+ const [isAutoLayoutingFinished, setIsAutoLayoutingFinished] = React.useState(false);
140
+ const [isNodesUpdatedWithState, setIsNodesUpdatedWithState] = React.useState(false);
141
+ const [shouldFitAndResize, setShouldFitAndResize] = React.useState(false);
39
142
  React.useEffect(() => {
40
143
  void fitView();
41
144
  // eslint-disable-next-line react-hooks/exhaustive-deps
@@ -50,6 +153,24 @@ const PipelineCanvasBody = ({ pipeline, ...flexProps }) => {
50
153
  setNodes([...layouted.nodes]);
51
154
  setEdges([...layouted.edges]);
52
155
  }, [pipeline?.edges, pipeline?.nodes, setEdges, setNodes]);
156
+ React.useEffect(() => {
157
+ if (nodesInitialized && !isAutoLayoutingFinished) {
158
+ const adjustedNodes = adjustNodes(nodes);
159
+ setNodes([...adjustedNodes]);
160
+ setIsAutoLayoutingFinished(true);
161
+ setShouldFitAndResize(true);
162
+ }
163
+ // eslint-disable-next-line react-hooks/exhaustive-deps
164
+ }, [nodesInitialized, isAutoLayoutingFinished]);
165
+ React.useEffect(() => {
166
+ if (pipelineState && nodes.length > 0 && !isNodesUpdatedWithState) {
167
+ const updatedNodes = updateNodesOnPipelineStateChange(nodes, pipelineState);
168
+ setNodes([...updatedNodes]);
169
+ setIsNodesUpdatedWithState(true);
170
+ setShouldFitAndResize(true);
171
+ }
172
+ // eslint-disable-next-line react-hooks/exhaustive-deps
173
+ }, [pipelineState, nodes, isNodesUpdatedWithState]);
53
174
  const setViewportAndFit = async (x, y, zoom, nds) => {
54
175
  await setViewport({ x, y, zoom });
55
176
  await fitView({ nodes: nds });
@@ -64,6 +185,14 @@ const PipelineCanvasBody = ({ pipeline, ...flexProps }) => {
64
185
  }
65
186
  // eslint-disable-next-line react-hooks/exhaustive-deps
66
187
  }, [nodes, edges]);
188
+ React.useEffect(() => {
189
+ if (shouldFitAndResize) {
190
+ const viewport = getViewport();
191
+ void setViewportAndFit(viewport.x, viewport.y, viewport.zoom, nodes);
192
+ setShouldFitAndResize(false);
193
+ }
194
+ // eslint-disable-next-line react-hooks/exhaustive-deps
195
+ }, [shouldFitAndResize]);
67
196
  return (_jsxs(Flex, { align: "center", justify: "center", full: true, height: "container.xs", rounded: "common", border: "base", ...flexProps, children: [!pipeline && _jsx(Header, { text: "Loading pipeline..." }), pipeline && (_jsx(ReactFlow, { nodes: nodes, edges: edges, onNodeDragStart: () => (autoArrangeRef.current = false), onNodesChange: onNodesChange, fitView: true, nodeTypes: { generic: PipelineNode }, minZoom: 0.4 }))] }));
68
197
  };
69
198
  export const PipelineCanvas = props => (_jsx(ReactFlowProvider, { children: _jsx(PipelineCanvasBody, { ...props }) }));
@@ -1,4 +1,6 @@
1
1
  export { parsePipeline } from './services/pipeline.js';
2
2
  export type { Pipeline, RawPipelinePayload } from './services/pipeline.js';
3
+ export { parsePipelineState } from './services/pipeline-state.js';
4
+ export type { PipelineState, RawPipelineStatePayload } from './services/pipeline-state.js';
3
5
  export { PipelineCanvas } from './components/PipelineCanvas.js';
4
6
  export type { PipelineCanvasProps } from './components/PipelineCanvas.js';
package/dist/src/index.js CHANGED
@@ -1,2 +1,3 @@
1
1
  export { parsePipeline } from './services/pipeline.js';
2
+ export { parsePipelineState } from './services/pipeline-state.js';
2
3
  export { PipelineCanvas } from './components/PipelineCanvas.js';
@@ -0,0 +1,42 @@
1
+ export type RawPipelineStatePayload = {
2
+ nodeStates: RawPipelineState[];
3
+ };
4
+ export type IOStates = {
5
+ [key: string]: {
6
+ numQueued: number;
7
+ timing: TimingWithFps;
8
+ };
9
+ };
10
+ type Timing = {
11
+ averageMicrosRecent: number;
12
+ maxMicros: number;
13
+ maxMicrosRecent: number;
14
+ medianMicrosRecent: number;
15
+ minMicros: number;
16
+ minMicrosRecent: number;
17
+ stdDevMicrosRecent: number;
18
+ };
19
+ type TimingWithFps = {
20
+ durationStats: Timing;
21
+ fps: number;
22
+ };
23
+ export type RawPipelineState = [
24
+ number,
25
+ {
26
+ events: any[];
27
+ inputStates: IOStates;
28
+ inputsGetTiming: TimingWithFps;
29
+ mainLoopTiming: TimingWithFps;
30
+ otherTimings: object;
31
+ outputStates: IOStates;
32
+ outputsSendTiming: TimingWithFps;
33
+ state: number;
34
+ }
35
+ ];
36
+ export type PipelineState = {
37
+ id: number;
38
+ inputs: IOStates;
39
+ outputs: IOStates;
40
+ };
41
+ export declare function parsePipelineState(rawPayload: RawPipelineStatePayload): PipelineState[];
42
+ export {};
@@ -0,0 +1,13 @@
1
+ export function parsePipelineState(rawPayload) {
2
+ const { nodeStates } = rawPayload;
3
+ const parsedNodeStates = [];
4
+ for (const [nodeId, nodeState] of nodeStates) {
5
+ const currentNode = {
6
+ id: nodeId,
7
+ inputs: nodeState.inputStates,
8
+ outputs: nodeState.outputStates,
9
+ };
10
+ parsedNodeStates.push(currentNode);
11
+ }
12
+ return parsedNodeStates;
13
+ }
@@ -1,4 +1,4 @@
1
- import type { Edge, Node } from '@xyflow/react';
1
+ import { type Edge, type Node } from '@xyflow/react';
2
2
  export type RawPipelineNodeIO = [
3
3
  [
4
4
  string,
@@ -16,7 +16,9 @@ export type RawPipelineNode = {
16
16
  id: number;
17
17
  ioInfo: RawPipelineNodeIO[];
18
18
  name: string;
19
+ parentId?: number;
19
20
  };
21
+ export type RawPipelineBridge = [number, number];
20
22
  export type RawPipelineEdge = {
21
23
  node1Id: number;
22
24
  node1Output: string;
@@ -29,6 +31,7 @@ export type RawPipelinePayload = {
29
31
  export type RawPipeline = {
30
32
  connections: RawPipelineEdge[];
31
33
  nodes: [number, RawPipelineNode][];
34
+ bridges?: [number, number][];
32
35
  };
33
36
  export type Pipeline = {
34
37
  nodes: ParsedNode[];
@@ -40,8 +43,12 @@ export type ParsedHandle = {
40
43
  type: 'input' | 'output';
41
44
  blocking: boolean;
42
45
  queueSize: number;
46
+ maxQueueSize?: number;
47
+ fps?: number;
43
48
  };
44
49
  export type ParsedNode = Node<{
50
+ id: string;
51
+ parentId?: string;
45
52
  name: string;
46
53
  handles: {
47
54
  input: ParsedHandle[];
@@ -1,3 +1,4 @@
1
+ import { MarkerType } from '@xyflow/react';
1
2
  function parseHandles(handles) {
2
3
  const parsedHandles = {
3
4
  input: [],
@@ -24,19 +25,61 @@ export function parsePipeline(rawPayload) {
24
25
  type: 'generic',
25
26
  id: id.toString(),
26
27
  position: { x: 0, y: 0 },
28
+ parentId: node.parentId?.toString() === '-1' ? undefined : node.parentId?.toString(),
29
+ extent: node.parentId?.toString() === '-1' ? undefined : 'parent',
27
30
  data: {
31
+ id: id.toString(),
28
32
  name: node.name,
29
33
  handles: parseHandles(node.ioInfo),
30
34
  },
31
35
  })) ?? [];
36
+ const mappedParentNodes = nodes.map(node => node.parentId).filter(id => id !== undefined);
37
+ const filteredParentNodes = mappedParentNodes.filter(id => id !== '-1');
38
+ const parentNodes = [...new Set(filteredParentNodes)];
39
+ const parentNodesArr = parentNodes
40
+ .map(id => pipeline.nodes.find(([_, node]) => node.id.toString() === id))
41
+ .filter(node => node !== undefined);
42
+ const formattedParentNodes = parentNodesArr.map(([id, node]) => ({
43
+ type: 'group',
44
+ id: id.toString(),
45
+ position: { x: 0, y: 0 },
46
+ parentId: node.parentId?.toString() === '-1' ? undefined : node.parentId?.toString(),
47
+ data: {
48
+ id: id.toString(),
49
+ name: node.name,
50
+ handles: parseHandles(node.ioInfo),
51
+ },
52
+ style: {
53
+ padding: 0,
54
+ borderStyle: 'dashed',
55
+ background: 'rgba(0,0,0,0.125)',
56
+ },
57
+ })) ?? [];
32
58
  const edges = pipeline.connections.map(connection => ({
33
- id: `${connection.node1Id}-${connection.node2Id}`,
59
+ id: `${connection.node1Id}-${connection.node2Id}-${connection.node1Output}-${connection.node2Input}-edge`,
34
60
  source: connection.node1Id.toString(),
35
61
  target: connection.node2Id.toString(),
36
62
  sourceHandle: connection.node1Output,
37
63
  targetHandle: connection.node2Input,
38
64
  })) ?? [];
39
- return { nodes, edges };
65
+ const bridges = pipeline.bridges?.map(bridge => ({
66
+ id: `${bridge[0]}-${bridge[1]}-bridge`,
67
+ source: bridge[0].toString(),
68
+ target: bridge[1].toString(),
69
+ sourceHandle: 'bottom',
70
+ targetHandle: 'top',
71
+ animated: true,
72
+ markerStart: {
73
+ type: MarkerType.ArrowClosed,
74
+ },
75
+ markerEnd: {
76
+ type: MarkerType.ArrowClosed,
77
+ },
78
+ type: 'step',
79
+ })) ?? [];
80
+ const groupedNodes = [...formattedParentNodes, ...nodes];
81
+ // NOTE: Parent nodes should be rendered before child nodes
82
+ return { nodes: [...groupedNodes], edges: [...edges, ...bridges] };
40
83
  }
41
84
  export const DOCS_BASE_URL = `https://docs.luxonis.com/software-v3/depthai/depthai-components/nodes`;
42
85
  export const NodesWithLinks = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@luxonis/depthai-pipeline-lib",
3
- "version": "1.5.0",
3
+ "version": "1.7.0",
4
4
  "type": "module",
5
5
  "license": "UNLICENSED",
6
6
  "main": "./dist/src/index.js",
@@ -1,196 +0,0 @@
1
- @layer utilities {
2
-
3
- .bg_dark\.warning\! {
4
- background-color: var(--colors-dark-warning) !important
5
- }
6
-
7
- .bg_dark\.success\! {
8
- background-color: var(--colors-dark-success) !important
9
- }
10
-
11
- .w_custom\.handle\.dot\! {
12
- width: var(--sizes-custom-handle-dot) !important
13
- }
14
-
15
- .h_custom\.handle\.dot\! {
16
- height: var(--sizes-custom-handle-dot) !important
17
- }
18
-
19
- .border_none\! {
20
- border: var(--borders-none) !important
21
- }
22
-
23
- .ml_xs {
24
- margin-left: var(--spacing-xs)
25
- }
26
-
27
- .mr_xs {
28
- margin-right: var(--spacing-xs)
29
- }
30
-
31
- .min-w_container\.smaller\.xxs {
32
- min-width: var(--sizes-container-smaller-xxs)
33
- }
34
-
35
- .border_base {
36
- border: var(--borders-base)
37
- }
38
-
39
- .rounded_common {
40
- border-radius: var(--radii-common)
41
- }
42
-
43
- .bg_white {
44
- background-color: var(--colors-white)
45
- }
46
-
47
- .w_full {
48
- width: var(--sizes-full)
49
- }
50
-
51
- .bg_light\.gray {
52
- background-color: var(--colors-light-gray)
53
- }
54
-
55
- .rounded-t_common {
56
- border-top-left-radius: var(--radii-common);
57
- border-top-right-radius: var(--radii-common)
58
- }
59
-
60
- .w_icon\.sm {
61
- width: var(--sizes-icon-sm)
62
- }
63
-
64
- .h_icon\.sm {
65
- height: var(--sizes-icon-sm)
66
- }
67
-
68
- .w_auto {
69
- width: auto
70
- }
71
-
72
- .h_auto {
73
- height: auto
74
- }
75
-
76
- .right_xs {
77
- right: var(--spacing-xs)
78
- }
79
-
80
- .text_unset {
81
- color: unset
82
- }
83
-
84
- .text_transparent {
85
- color: var(--colors-transparent)
86
- }
87
-
88
- .items_start {
89
- align-items: start
90
- }
91
-
92
- .flex_row {
93
- flex-direction: row
94
- }
95
-
96
- .flex_row-reverse {
97
- flex-direction: row-reverse
98
- }
99
-
100
- .items_end {
101
- align-items: end
102
- }
103
-
104
- .pos_relative {
105
- position: relative
106
- }
107
-
108
- .flex_column {
109
- flex-direction: column
110
- }
111
-
112
- .items_center {
113
- align-items: center
114
- }
115
-
116
- .justify_space-between {
117
- justify-content: space-between
118
- }
119
-
120
- .gap_xs {
121
- gap: var(--spacing-xs)
122
- }
123
-
124
- .d_flex {
125
- display: flex
126
- }
127
-
128
- .gap_sm {
129
- gap: var(--spacing-sm)
130
- }
131
-
132
- .py_xs {
133
- padding-block: var(--spacing-xs)
134
- }
135
- .\[\&\:hover_\.node-help-icon\]\:text_text\.normal:hover .node-help-icon {
136
- color: var(--colors-text-normal)
137
- }
138
- }
139
-
140
- @layer recipes {
141
- .button--variant_ghost:is(:hover, [data-hover]) {
142
- background: var(--colors-accent);
143
- color: var(--colors-accent-foreground)
144
- }
145
-
146
- .button--size_default {
147
- height: var(--sizes-10);
148
- padding-inline: var(--spacing-4);
149
- padding-block: var(--spacing-2)
150
- }
151
-
152
- @layer _base {
153
- .label {
154
- font-size: 0.875rem;
155
- line-height: var(--line-heights-none);
156
- font-weight: var(--font-weights-medium);
157
- }
158
- .peer:is(:disabled, [disabled], [data-disabled]) ~ .label {
159
- cursor: not-allowed;
160
- opacity: 0.7
161
- }
162
- }
163
-
164
- @layer _base {
165
- .button {
166
- display: inline-flex;
167
- align-items: center;
168
- justify-content: center;
169
- border-radius: var(--radii-md);
170
- font-size: 0.875rem;
171
- line-height: 1.25rem;
172
- font-weight: var(--font-weights-medium);
173
- transition-property: var(--transition-prop, color, background-color, border-color, outline-color, text-decoration-color, fill, stroke);
174
- transition-timing-function: var(--transition-easing, cubic-bezier(0.4, 0, 0.2, 1));
175
- transition-duration: var(--transition-duration, 150ms);
176
- cursor: pointer;
177
- --shadow-panda-ring-offset-color: var(--colors-background);
178
- gap: var(--spacing-2);
179
- }
180
- .button:is(:disabled, [disabled], [data-disabled]) {
181
- pointer-events: none;
182
- opacity: 50%
183
- }
184
- .button:is(:focus-visible, [data-focus-visible]) {
185
- outline: 2px solid transparent;
186
- outline-offset: 2px;
187
- --shadow-panda-ring-offset-shadow: var(--shadow-panda-ring-inset,) 0 0 0 var(--shadow-panda-ring-offset-width, 0px) var(--shadow-panda-ring-offset-color);
188
- --shadow-panda-ring-shadow: var(--shadow-panda-ring-inset,) 0 0 0 calc(2px + var(--shadow-panda-ring-offset-width, 0px)) var(--shadow-panda-ring-color);
189
- box-shadow: var(--shadow-panda-ring-offset-shadow),var(--shadow-panda-ring-shadow),var(--shadow-panda-base-shadow,0 0 #0000);
190
- --shadow-panda-ring-color: var(--colors-ring)
191
- ;
192
- --shadow-panda-ring-offset-width: 2px
193
-
194
- }
195
- }
196
- }
@@ -1,26 +0,0 @@
1
- @layer utilities {
2
-
3
- .d_flex {
4
- display: flex
5
- }
6
-
7
- .items_center {
8
- align-items: center
9
- }
10
-
11
- .justify_center {
12
- justify-content: center
13
- }
14
-
15
- .h_container\.xs {
16
- height: var(--sizes-container-xs)
17
- }
18
-
19
- .rounded_common {
20
- border-radius: var(--radii-common)
21
- }
22
-
23
- .border_base {
24
- border: var(--borders-base)
25
- }
26
- }