diff --git a/package.json b/package.json index 3bf6b927..496694d3 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "dev:ui:live": "turbo run dev --filter=@tokens-studio/graph-engine-ui...", "docs": "turbo run docs", "format": "turbo run format --parallel --continue", + "postinstall": "npx patch-package", "lint": "turbo run lint", "lint-staged": "npx lint-staged", "prepare": "husky install", @@ -41,7 +42,9 @@ "**/typescript-transform-paths" ] }, - "dependencies": {}, + "dependencies": { + "patch-package": "^8.0.0" + }, "devDependencies": { "husky": "^8.0.3", "lint-staged": "^13.2.1", @@ -50,4 +53,4 @@ "turbo": "^1.9.8" }, "packageManager": "yarn@1.22.19" -} \ No newline at end of file +} diff --git a/packages/documentation/docs/guides/developers/engine/creating-a-node.md b/packages/documentation/docs/guides/developers/engine/creating-a-node.md index 923793da..98e94800 100644 --- a/packages/documentation/docs/guides/developers/engine/creating-a-node.md +++ b/packages/documentation/docs/guides/developers/engine/creating-a-node.md @@ -74,7 +74,7 @@ describe("math/add", () => { node.inputs.b.setValue(1); //For more complicated nodes you will likely want to interact with the graph object instead of executing the node directly - await node.execute(); + await node.run(); expect(node.outputs.value.value).toStrictEqual(3); }); }); diff --git a/packages/graph-editor/package.json b/packages/graph-editor/package.json index 78d4ebe5..653da364 100644 --- a/packages/graph-editor/package.json +++ b/packages/graph-editor/package.json @@ -52,7 +52,7 @@ "@tokens-studio/tokens": "^0.0.24", "@tokens-studio/types": "0.2.3", "@tokens-studio/ui": "^0.5.5", - "animation-timeline-js": "^2.3.2", + "@xzdarcy/react-timeline-editor": "^0.1.9", "array-move": "^4.0.0", "better-react-mathjax": "^2.0.3", "classnames": "2.3.2", diff --git a/packages/graph-editor/src/components/debugger/data.ts b/packages/graph-editor/src/components/debugger/data.ts new file mode 100644 index 00000000..3e3d0931 --- /dev/null +++ b/packages/graph-editor/src/components/debugger/data.ts @@ -0,0 +1,61 @@ +import { CustomTimeLineAction, CustomTimelineRow } from "@/components/debugger" +import { makeObservable, observable, computed, action, flow } from "mobx" + + + +export class DebugInfo { + + _rows: CustomTimelineRow[] = [] + offset = 0; + first = true + + constructor() { + makeObservable(this, { + _rows: observable, + rows: computed, + addRow: action, + addAction: action + }) + + } + + get rows() { + return this._rows + } + + addRow(row: CustomTimelineRow) { + this._rows = [...this._rows, row] + } + + addAction(rowId: string, action: CustomTimeLineAction) { + + if (this.first) { + this.first = false; + this.offset = action.start; + } + + this._rows = this._rows.map(row => { + if (row.id === rowId) { + return { + ...row, + actions: [...row.actions, { + ...action, + start: (action.start - this.offset)/1000, + end: (action.end - this.offset)/1000 + }] + } + } + return row + }); + + } + + clear() { + this.first = true; + this._rows = [] + } + + +} + +export const debugInfo = new DebugInfo() \ No newline at end of file diff --git a/packages/graph-editor/src/components/debugger/debugger.css b/packages/graph-editor/src/components/debugger/debugger.css deleted file mode 100644 index a15a7309..00000000 --- a/packages/graph-editor/src/components/debugger/debugger.css +++ /dev/null @@ -1,48 +0,0 @@ - .toolbar { - background-color: #383838; - padding-left: 44px; - max-height: 36px; - height: 36px; - position: relative; - overflow: hidden; - display: flex; - height: 36px; - background-color: #3c3c3c; - } -.outline { - width: 250px; - min-width: 150px; - overflow: hidden; - display: flex; - flex-direction: column; - height: 100%; - align-items: stretch; - align-content: stretch; -} - .outline-header { - height: 30px; - } - - .outline-scroll-container { - overflow: hidden; - } - - .outline-node { - padding-left: 20px; - display: flex; - align-items: center; - width: 100%; - color: white; - -webkit-user-select: none; - user-select: none; - height: 24px; - } - - .outline-node:hover { - background-color: #3399ff; - } - - .links { - display: flex; - align-items: center; - } \ No newline at end of file diff --git a/packages/graph-editor/src/components/debugger/debugger.scss b/packages/graph-editor/src/components/debugger/debugger.scss new file mode 100644 index 00000000..eb27779c --- /dev/null +++ b/packages/graph-editor/src/components/debugger/debugger.scss @@ -0,0 +1,92 @@ +.timeline-editor-time-unit-scale { + color: var(--colors-fgDefault) !important +} + +.timeline-editor-time-unit { + border-right: 1px solid var(--colors-borderDefault); +} + +.timeline-editor-action { + background-color: unset; +} + +.timeline-editor-time-area .ReactVirtualized__Grid{ + overflow: hidden !important; +} + +.timeline-editor { + font-family: 'Inter', sans-serif !important; + height: 100% !important; + width: unset !important; + flex: 1; + background-color: inherit !important; +} + +.timeline-editor-edit-row { + background: var(--colors-bgSubtle); +} + +.timeline-player { + height: 32px; + + padding: 0 10px; + display: flex; + flex-direction: row; + align-items: center; + background-color: #3a3a3a; + color: #ddd; + + .play-control { + width: 24px; + height: 24px; + border-radius: 4px; + display: flex; + background-color: #666; + justify-content: center; + align-items: center; + } + + .time { + font-size: 12px; + margin: 0 20px; + width: 70px; + } + + .rate-control { + justify-self: flex-end; + + + } +} + + +.timeline-list { + min-width: 150px; + margin-top: 42px; + height: 258px; + flex: 0 1 auto; + overflow-y: auto; + box-sizing: border-box; + overflow-x: clip; + + &-item { + height: 32px; + align-items: center; + width: 100%; + display: flex; + padding: 0 10px; + box-sizing: border-box; + + &:hover { + background-color: var(--colors-focus); + } + + & .text { + height: 28px; + width: 100%; + padding-left: 10px; + border-radius: 4px; + + } + } +} \ No newline at end of file diff --git a/packages/graph-editor/src/components/debugger/index.stories.tsx b/packages/graph-editor/src/components/debugger/index.stories.tsx index 33de7133..7903efa8 100644 --- a/packages/graph-editor/src/components/debugger/index.stories.tsx +++ b/packages/graph-editor/src/components/debugger/index.stories.tsx @@ -1,16 +1,44 @@ -import { Debugger } from './index'; +import { CustomTimelineRow, Debugger } from './index'; import React from 'react'; import type { Meta, StoryObj } from '@storybook/react'; +import { TimelineEffect, TimelineRow } from '@xzdarcy/react-timeline-editor'; const meta: Meta = { title: 'Components/Debugger', component: Debugger, }; +const mockEffect: Record = { + effect0: { + id: "effect0", + name: "foo", + }, + effect1: { + id: "effect1", + name: "bar", + }, +}; + + +const mockData: CustomTimelineRow[] = new Array(20).fill(0).map((_, i) => { + + return { + id: i.toString(), + name: 'Row ' + i, + actions: new Array(1).fill(0).map(y => { + return { + id: `${i}-${y}`, + start: i * 2, + effectId: 'effect0', + end: i * 2 + 2, + } + }) + } +}) export default meta; type Story = StoryObj; export const Default: Story = { - render: (args) => , + render: (args) => , args: {}, }; diff --git a/packages/graph-editor/src/components/debugger/index.tsx b/packages/graph-editor/src/components/debugger/index.tsx index 246de3c5..e2d3444a 100644 --- a/packages/graph-editor/src/components/debugger/index.tsx +++ b/packages/graph-editor/src/components/debugger/index.tsx @@ -1,144 +1,117 @@ -import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; -import { Timeline, TimelineInteractionMode, TimelineModel } from "animation-timeline-js"; -import { Box, Text, Stack, IconButton } from "@tokens-studio/ui"; -import './debugger.css'; -import { DragHandGesture, ZoomIn, ZoomOut } from "iconoir-react"; -type Props = { - time?: number; - model: TimelineModel; -}; +import { Timeline, TimelineAction, TimelineEffect, TimelineRow, TimelineState } from '@xzdarcy/react-timeline-editor'; +import React, { useRef, useState } from 'react'; +import { Box, Stack, Text } from "@tokens-studio/ui"; +import TimelinePlayer from './player'; +import './debugger.scss'; +import { DebugInfo } from './data'; +import { observer } from "mobx-react-lite" + + +export interface CustomTimelineRow extends TimelineRow { + name: string + actions: CustomTimeLineAction[] +} -function TimelineComponent(props: Props) { - const { model, time } = props; - const timelineElRef = useRef(null); - const [timeline, setTimeline] = useState(); - - - // Example to subscribe and pass model or time update: - useEffect(() => { - timeline?.setModel(model); - }, [model, timeline]); - - // Example to subscribe and pass model or time update: - useEffect(() => { - if (time || time === 0) { - timeline?.setTime(time); - } - }, [time, timeline]); - - useEffect(() => { - let newTimeline: Timeline | null = null; - // On component init - if (timelineElRef.current) { - newTimeline = new Timeline({ id: timelineElRef.current }); - newTimeline.setInteractionMode(TimelineInteractionMode.Selection); - newTimeline.onSelected(function (obj) { - console.log('Selected Event: (' + obj.selected.length + '). changed selection :' + obj.changed.length, 2); - }); - - - // Here you can subscribe on timeline component events - setTimeline(newTimeline); - } - - // cleanup on component unmounted. - return () => newTimeline?.dispose(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [timelineElRef.current]); - - useEffect(() => { - if (!timeline) return; - const start = Date.now(); - const interval = setInterval(() => { - if (!timeline) return; - const model = timeline?.getModel(); - const groupKey = '' + Date.now(); - - model?.rows[0].keyframes!.push( - { - val: Date.now() - start, - group: groupKey, - }, - - ) - model?.rows[0].keyframes!.push( - { - val: Date.now() + 1000 - start, - group: groupKey, - }, - - ) - timeline?.setModel(model!); - } - , 1500); - return () => clearInterval(interval); - } - , [timeline]); - - - const setZoomMode = useCallback(() => { - timeline?.setInteractionMode(TimelineInteractionMode.Zoom); - }, [timeline]); - - const setDrag = useCallback(() => { - timeline?.setInteractionMode(TimelineInteractionMode.Pan); - }, [timeline]); - - return - - }> - }> - - - -
-
- - {model.rows.map((row, index) => { - return
{row.title}
- })} -
-
-
- - ; +export interface CustomTimeLineAction extends TimelineAction { + /** + * The color to render the action + */ + color?: string; } -export function Debugger() { - return ( - ; +} + + +const DebuggerInner = observer(({ data, domRef, timeline, scale }) => { + + return +
{ + const target = e.target as HTMLDivElement; + timeline.current?.setScrollTop(target.scrollTop); + }} + className={'timeline-list'} + > + {data.rows.map((item) => { + return ( + + {item.name} + + ); + })} +
+ + + + { }} + disableDrag + onScroll={({ scrollTop }) => { + if (domRef.current) { + domRef.current.scrollTop = scrollTop; + } + }} + autoScroll={true} + scaleSplitCount={~~(scale / 10)} + // scale={scale} + scaleWidth={scale} + ref={timeline} + effects={{}} + getActionRender={(action, row) => { + return ; }} /> +
+}); + + +export const Debugger = ({ data, effects }: DebuggerProps) => { + + const timelineState = useRef(); + const autoScrollWhenPlay = useRef(true); + const domRef = useRef(); + + const [scale, setScale] = useState(100); + + return ( + + + + + ); -} +}; + + +const colors = [ + '#F94144', + "#F3722C", + "#F8961E", + "#F9844A", + "#F9C74F", + "#90BE6D", + "#43AA8B", + "#577590", + "#277DA1", + "#2A9D8F", + "#2B9348", + +] + +export const CustomRender0: React.FC<{ action: CustomTimeLineAction; row: TimelineRow, index: number }> = ({ action, row, index }) => { + return ( + + {Math.round(action.end - action.start)}ms + + ); +}; \ No newline at end of file diff --git a/packages/graph-editor/src/components/debugger/player.tsx b/packages/graph-editor/src/components/debugger/player.tsx new file mode 100644 index 00000000..d9b2784c --- /dev/null +++ b/packages/graph-editor/src/components/debugger/player.tsx @@ -0,0 +1,114 @@ +import { Pause, Play, UndoAction, ZoomIn, ZoomOut, Trash } from 'iconoir-react'; +import { TimelineState } from '@xzdarcy/react-timeline-editor'; +import { IconButton, Text, Stack, Select } from '@tokens-studio/ui'; +import React, { useEffect, useState } from 'react'; +import { DebugInfo } from './data'; + + +export const scaleWidth = 160; +export const scale = 5; +export const startLeft = 20; + +export const Rates = [0.2, 0.5, 1.0, 1.5, 2.0]; + + +export interface TimelinePlayerProps { + timelineState: React.MutableRefObject | undefined; + autoScrollWhenPlay: React.MutableRefObject; + setScale: React.Dispatch>; + data: DebugInfo; + +} + +const TimelinePlayer = (props: TimelinePlayerProps) => { + const { timelineState, autoScrollWhenPlay, setScale, data } = props; + const [isPlaying, setIsPlaying] = useState(false); + const [time, setTime] = useState(0); + const [rate,setRate ] = useState(1); + + useEffect(() => { + if (!timelineState?.current) return; + const engine = timelineState.current; + engine.listener.on('play', () => setIsPlaying(true)); + engine.listener.on('paused', () => setIsPlaying(false)); + engine.listener.on('afterSetTime', ({ time }) => setTime(time)); + engine.listener.on('setTimeByTick', ({ time }) => { + setTime(time); + + if (autoScrollWhenPlay.current) { + const autoScrollFrom = 500; + const left = time * (scaleWidth / scale) + startLeft - autoScrollFrom; + timelineState.current.setScrollLeft(left) + } + }); + + return () => { + if (!engine) return; + engine.pause(); + engine.listener.offAll(); + }; + }, []); + + const handlePlayOrPause = () => { + if (!timelineState?.current) return; + if (timelineState.current.isPlaying) { + timelineState.current.pause(); + } else { + timelineState.current.play({ autoEnd: true }); + } + }; + + const handleRateChange = (rate: number) => { + + if (!timelineState?.current) return; + setRate(rate); + timelineState.current.setPlayRate(rate); + }; + + const timeRender = (time: number) => { + const float = (parseInt((time % 1) * 100 + '') + '').padStart(2, '0'); + const min = (parseInt(time / 60 + '') + '').padStart(2, '0'); + const second = (parseInt((time % 60) + '') + '').padStart(2, '0'); + return <>{`${min}:${second}.${float.replace('0.', '')}`}; + }; + + const reset = () => { + if (!timelineState?.current) return; + timelineState.current.setTime(0); + } + const zoomIn = () => { + setScale((prev) => prev * 1.2) + + } + const zoomOut = () => { + setScale((prev) => prev * 0.8) + } + const trash = () => { + data.clear() + } + + + return ( + + : } onClick={handlePlayOrPause} variant={'primary'}> + + + + } onClick={reset}> + } onClick={() => zoomIn()}> + } onClick={() => zoomOut()}> + } onClick={() => trash()}> + {timeRender(time)} + + ); +}; + +export default TimelinePlayer; \ No newline at end of file diff --git a/packages/graph-editor/src/components/menubar/defaults.tsx b/packages/graph-editor/src/components/menubar/defaults.tsx index 680a10d3..161afaf9 100644 --- a/packages/graph-editor/src/components/menubar/defaults.tsx +++ b/packages/graph-editor/src/components/menubar/defaults.tsx @@ -7,7 +7,7 @@ import { dockerSelector } from '@/redux/selectors/refs'; import DockLayout, { TabData } from 'rc-dock'; import { OutputSheet } from '../panels/output'; import { Legend } from '../panels/legend'; -import { FlameGraph } from '../panels/flamegraph'; +import { DebugPanel } from '../panels/debugger'; import { ImperativeEditorRef } from '@/editor/editorTypes'; import { Settings } from '../panels/settings'; import { Inputsheet } from '../panels/inputs'; @@ -256,10 +256,10 @@ export const defaultMenuDataFactory = (): Menu => content: , }), windowButton({ - name: 'flamegraph', - id: 'flamegraph', - title: 'FlameGraph', - content: , + name: 'debugger', + id: 'debugger', + title: 'Debugger', + content: , }), windowButton({ name: 'settings', diff --git a/packages/graph-editor/src/components/panels/debugger/index.tsx b/packages/graph-editor/src/components/panels/debugger/index.tsx new file mode 100644 index 00000000..c1c066fc --- /dev/null +++ b/packages/graph-editor/src/components/panels/debugger/index.tsx @@ -0,0 +1,11 @@ + +import React from 'react'; + +import { Debugger } from '@/components/debugger'; +import { debugInfo } from '../../debugger/data'; + + + +export const DebugPanel = () => { + return +}; diff --git a/packages/graph-editor/src/components/panels/flamegraph/index.tsx b/packages/graph-editor/src/components/panels/flamegraph/index.tsx deleted file mode 100644 index 803fa255..00000000 --- a/packages/graph-editor/src/components/panels/flamegraph/index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { useGraph } from '@/hooks/useGraph'; -import { Box, Button, Text } from '@tokens-studio/ui'; -import React, { useCallback, useState } from 'react'; -import { FlameGraph as FlameGraphPrimitive } from 'react-flame-graph'; -import { convertToFlameGraph } from './utils'; - -export type FlameNode = { - name: string; - backgroundColor: string; - color: string; - value: number; - children?: FlameNode[]; - tooltip?: undefined; -}; - -export const FlameGraph = () => { - const graph = useGraph(); - const [lastExecution, setLastExecution] = useState(-1); - const [totalTime, setTotalTime] = useState(0); - const [data, setData] = useState({ - name: 'root', - value: 1, - children: [], - }); - - const getData = useCallback(async () => { - if (!graph) { - return - } - - const res = await graph.execute({ stats: true }); - const calculated = convertToFlameGraph(graph, res); - setData(calculated); - setTotalTime(res.end - res.start); - setLastExecution(Date.now()); - }, [graph]); - - return ( - - - Last executed Flamegraph:{' '} - {lastExecution == -1 - ? 'never' - : new Date(lastExecution).toLocaleTimeString()} - - Total Time : {totalTime}ms - - - - ); -}; diff --git a/packages/graph-editor/src/components/panels/flamegraph/story.stories.tsx b/packages/graph-editor/src/components/panels/flamegraph/story.stories.tsx deleted file mode 100644 index f02302ea..00000000 --- a/packages/graph-editor/src/components/panels/flamegraph/story.stories.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import { FlameGraph } from './index'; -import React from 'react'; -import type { Meta, StoryObj } from '@storybook/react'; - -const meta: Meta = { - title: 'Panels/FlameGraph', - component: FlameGraph, -}; - -export default meta; -type Story = StoryObj; -export const Default: Story = { - render: (args) => , - args: {}, -}; diff --git a/packages/graph-editor/src/components/panels/flamegraph/utils.tsx b/packages/graph-editor/src/components/panels/flamegraph/utils.tsx deleted file mode 100644 index cf06271b..00000000 --- a/packages/graph-editor/src/components/panels/flamegraph/utils.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import { BatchExecution, Graph } from '@tokens-studio/graph-engine'; - -// const data = { -// name: 'root', -// value: 5, -// children: [ -// { -// name: 'custom tooltip', -// value: 1, - -// // Each node can specify a "tooltip" to be shown on hover. -// // By default, the node's "name" will be used for this. -// tooltip: 'Custom tooltip shown on hover', -// }, -// { -// name: 'custom colors', - -// // Each node can also provide a custom "backgroundColor" or text "color". -// backgroundColor: '#35f', -// color: '#fff', - -// value: 3, -// children: [ -// { -// name: 'leaf', -// value: 2 -// } -// ] -// }, -// ], -// }; - -export const convertToFlameGraph = ( - graph: Graph, - batchExecution: BatchExecution, -) => { - const data = { - name: 'root', - //Has to be a minimum of 1 - value: batchExecution.end - batchExecution.start || 1, - children: [], - }; - - data.children = batchExecution.order.reduce((acc, id) => { - const actualNode = graph.getNode(id); - - const node = batchExecution.stats[id]; - const start = node.start - batchExecution.start; - const end = node.end - batchExecution.start; - - const value = end - start; - - if (!value) { - return acc; - } - - return acc.concat([ - { - name: graph.getNode(id).factory.title, - value, - tooltip: id, - // children: [], - }, - ]); - }, []); - - return data; -}; diff --git a/packages/graph-editor/src/editor/graph.tsx b/packages/graph-editor/src/editor/graph.tsx index c7a04535..91395513 100644 --- a/packages/graph-editor/src/editor/graph.tsx +++ b/packages/graph-editor/src/editor/graph.tsx @@ -68,6 +68,7 @@ import { SelectionContextMenu } from '@/components/contextMenus/selectionContext import { ActionProvider } from './actions/provider.js'; import { HotKeys } from '@/components/hotKeys/index.js'; import { currentPanelIdSelector } from '@/redux/selectors/graph.js'; +import { debugInfo } from '@/components/debugger/data.js'; const snapGridCoords: SnapGrid = [16, 16]; const defaultViewport = { x: 0, y: 0, zoom: 1.5 }; @@ -132,7 +133,7 @@ export const EditorApp = React.forwardRef }, [dispatch.graph, graph, id, ref]) //Attach sideeffect listeners - useMemo(() => { + useEffect(() => { capabilities.forEach((factory) => graph.registerCapability(factory)); graph.onFinalize('serialize', (serialized) => { @@ -159,7 +160,7 @@ export const EditorApp = React.forwardRef }) - graph.on('valueSent', (edges) => { + const valueDetecterDisposer =graph.on('valueSent', (edges) => { edges const edgeLookup = edges.reduce((acc, edge) => { acc[edge.id] = edge; @@ -201,7 +202,8 @@ export const EditorApp = React.forwardRef }, 400) }); - graph.on('edgeIndexUpdated', (edge) => { + + const EdgeUpdaterDisposer = graph.on('edgeIndexUpdated', (edge) => { setEdges((eds) => { return eds.map((ed) => { if (ed.id == edge.id) { @@ -216,6 +218,34 @@ export const EditorApp = React.forwardRef }); }); }); + + const NodeStartListener = graph.on('nodeExecuted', (run) => { + + const existing = debugInfo.rows.find(x => x.id == run.node.id); + + if (!existing) { + debugInfo.addRow({ + id: run.node.id, + name: run.node.factory.type, + actions: [] + }); + } + + //Now we need to add the actions + debugInfo.addAction(run.node.id, { + id: `${run.node.id}-${Date.now()}`, + start: run.start, + end: run.end, + effectId: 'effect0' + }); + + }); + + return ()=>{ + valueDetecterDisposer(); + EdgeUpdaterDisposer(); + } + }, [graph]) const [contextNode, setContextNode] = React.useState([]); diff --git a/packages/graph-editor/src/editor/layoutController.tsx b/packages/graph-editor/src/editor/layoutController.tsx index cfb14015..53e0418e 100644 --- a/packages/graph-editor/src/editor/layoutController.tsx +++ b/packages/graph-editor/src/editor/layoutController.tsx @@ -6,7 +6,7 @@ import { Legend } from "@/components/panels/legend"; import { LogsPanel } from "@/components/panels/logs"; import { OutputSheet } from "@/components/panels/output"; import { GraphPanel } from "@/components/panels/graph"; -import { FlameGraph } from "@/components/panels/flamegraph"; +import { DebugPanel } from "@/components/panels/debugger"; import { Maximize, Reduce, Xmark } from 'iconoir-react'; import { IconButton, Stack, Tooltip } from '@tokens-studio/ui'; import { DropPanel } from '@/components/panels/dropPanel/index.js'; @@ -199,25 +199,48 @@ const layoutDataFactory = (props, ref): LayoutData => { }, ], }, - { - id: 'graphs', + size: 700, - group: 'graph', - panelLock: { panelStyle: 'graph' }, - tabs: [ + mode: 'vertical', + children:[ { - closable: true, - cached: true, - id: 'graph1', + id: 'graphs', + size: 700, group: 'graph', - title: 'Graph', - content: ( - - ), + panelLock: { panelStyle: 'graph' }, + tabs: [ + { + closable: true, + cached: true, + id: 'graph1', + group: 'graph', + title: 'Graph', + content: ( + + ), + }, + ], }, - ], + + { + size:300, + tabs:[ + { + closable: true, + group: 'popout', + id: 'debugger', + title: 'Debugger', + content: , + }, + ] + } + + + ] + }, + { size: 300, mode: 'vertical', @@ -269,13 +292,7 @@ const layoutDataFactory = (props, ref): LayoutData => { title: 'Graph Settings', content: , }, - { - closable: true, - group: 'popout', - id: 'flamegraph', - title: 'Flamegraph', - content: , - }, + ], }, ], diff --git a/packages/graph-editor/src/registry/inputControls.tsx b/packages/graph-editor/src/registry/inputControls.tsx index 1196fe55..c89c4acf 100644 --- a/packages/graph-editor/src/registry/inputControls.tsx +++ b/packages/graph-editor/src/registry/inputControls.tsx @@ -1,27 +1,19 @@ import { - AllSchemas, - AnySchema, - NumberSchema, NodeTypes, - STRING, StringSchema, } from '@tokens-studio/graph-engine'; import { - Box, Button, - Checkbox, Heading, - Label, Scroll, Select, Stack, - TextInput, } from '@tokens-studio/ui'; import { observer } from 'mobx-react-lite'; import React, { useMemo } from 'react'; import { Node } from '@tokens-studio/graph-engine'; -import properties from 'mdn-data/css/properties.json'; +import properties from 'mdn-data/css/properties.json' assert { type: "json" }; ; import { deletable } from '@/annotations'; const CSSProperties = Object.keys(properties); diff --git a/packages/graph-engine/src/graph/graph.ts b/packages/graph-engine/src/graph/graph.ts index 85d2dbad..521ed66c 100644 --- a/packages/graph-engine/src/graph/graph.ts +++ b/packages/graph-engine/src/graph/graph.ts @@ -11,7 +11,7 @@ import { topologicalSort } from "./topologicSort.js"; import { v4 as uuid } from 'uuid'; import cmp from "semver-compare"; import type { NodeFactory, SerializedGraph, } from "./types.js"; -import type { NodeRun } from "../types.js"; +import type { NodeRun, NodeStart } from "../types.js"; export type CapabilityFactory = { name: string; @@ -77,6 +77,7 @@ export type SubscriptionLookup = { edgeIndexUpdated: Edge; valueSent: Edge[]; nodeExecuted: NodeRun; + nodeStarted: NodeStart }; export type ListenerType = [T] extends [(...args: infer U) => any] @@ -287,7 +288,7 @@ export class Graph { outEdges.forEach((edge) => this.removeEdge(edge.id)); //Cleanup the node - node.clear(); + node.dispose(); //Remove from the lookup delete this.nodes[nodeId]; @@ -381,7 +382,7 @@ export class Graph { return; } - await this.propagate(node.id); + // await this.propagate(node.id); } /** * Serialize the graph for transport diff --git a/packages/graph-engine/src/nodes/array/reduce.ts b/packages/graph-engine/src/nodes/array/reduce.ts index 7d6cd7eb..b47f7a09 100644 --- a/packages/graph-engine/src/nodes/array/reduce.ts +++ b/packages/graph-engine/src/nodes/array/reduce.ts @@ -98,7 +98,6 @@ export default class ReduceSubgraph extends SubgraphNode { const output = await (input.value as any[]).reduce(async (acc, item, i) => { const previousAcc = await acc; - console.log(previousAcc) const result = await this._innerGraph.execute({ //By default this is any so we need to overwrite it with its runtime type inputs: { diff --git a/packages/graph-engine/src/nodes/css/cssFunction.ts b/packages/graph-engine/src/nodes/css/cssFunction.ts index e801b80e..7473ed7c 100644 --- a/packages/graph-engine/src/nodes/css/cssFunction.ts +++ b/packages/graph-engine/src/nodes/css/cssFunction.ts @@ -3,7 +3,7 @@ import { NodeTypes } from "../../types.js"; import { Node } from "../../programmatic/node.js"; import { StringSchema } from "../../schemas/index.js"; //@ts-ignore -import cssFunctionsData from "mdn-data/css/functions.json" ; +import cssFunctionsData from "mdn-data/css/functions.json" assert { type: "json" } ; const FUNCTION_NAMES = Object.keys(cssFunctionsData); diff --git a/packages/graph-engine/src/programmatic/node.ts b/packages/graph-engine/src/programmatic/node.ts index be46eb0b..9b5696e6 100644 --- a/packages/graph-engine/src/programmatic/node.ts +++ b/packages/graph-engine/src/programmatic/node.ts @@ -142,8 +142,13 @@ export class Node { * Runs the node. Internally this calls the execute method, but the run entrypoint allows for additional tracking and lifecycle management */ async run(): Promise { + this.annotations[annotatedNodeRunning] = true; const start = performance.now(); + this.getGraph()?.emit("nodeStarted", { + node: this, + start, + }); try { await this.execute(); this.error = undefined; @@ -280,8 +285,21 @@ export class Node { /** * Handles cleanup for nodes with state. * Use the super method to clear the graph reference + * + * @example + * ```typescript + * class MyNode extends Node { + * dispose() { + * + * Node.prototype.dispose.call(this); + * // or if you have full ES6 support + * super.dispose(); + * + * //Some additional manual cleanup + * // ... + * } */ - clear = () => { + dispose = () => { //@ts-ignore This is forcing manual cleanup this._graph = undefined; }; diff --git a/packages/graph-engine/src/types.ts b/packages/graph-engine/src/types.ts index 65173a72..3672ecf1 100644 --- a/packages/graph-engine/src/types.ts +++ b/packages/graph-engine/src/types.ts @@ -121,6 +121,11 @@ export type BatchRunError = Error & { nodeId: string }; +export type NodeStart ={ + node: Node , + start: number +} + export type NodeRun = { node: Node; error?: Error; diff --git a/packages/storybook-lit/package.json b/packages/storybook-lit/package.json index 1025deeb..cf83041a 100644 --- a/packages/storybook-lit/package.json +++ b/packages/storybook-lit/package.json @@ -1,8 +1,9 @@ { - "name": "lit-storybook", + "name": "@tokens-studio/lit-storybook", "version": "1.0.0", "main": "index.js", "license": "MIT", + "author": "andrew@hyma.io", "dependencies": { "@lit/context": "^1.1.1", "lit": "^3.1.3" @@ -20,4 +21,4 @@ "@storybook/web-components-vite": "^8.1.1", "storybook": "^8.1.1" } -} +} \ No newline at end of file diff --git a/packages/storybook-react/package.json b/packages/storybook-react/package.json index 7ee4d6dc..5d7b1b07 100644 --- a/packages/storybook-react/package.json +++ b/packages/storybook-react/package.json @@ -3,6 +3,7 @@ "version": "1.0.0", "main": "index.js", "license": "MIT", + "author": "andrew@hyma.io", "private": true, "type": "module", "scripts": { diff --git a/patches/react-virtualized+9.22.5.patch b/patches/react-virtualized+9.22.5.patch new file mode 100644 index 00000000..68db8584 --- /dev/null +++ b/patches/react-virtualized+9.22.5.patch @@ -0,0 +1,11 @@ +diff --git a/node_modules/react-virtualized/dist/es/WindowScroller/utils/onScroll.js b/node_modules/react-virtualized/dist/es/WindowScroller/utils/onScroll.js +index d00f0f1..c8496e8 100644 +--- a/node_modules/react-virtualized/dist/es/WindowScroller/utils/onScroll.js ++++ b/node_modules/react-virtualized/dist/es/WindowScroller/utils/onScroll.js +@@ -71,4 +71,3 @@ export function unregisterScrollListener(component, element) { + } + } + } +\ No newline at end of file +-import { bpfrpt_proptype_WindowScroller } from "../WindowScroller.js"; +\ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 3d074561..48efc0e4 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3106,6 +3106,13 @@ dependencies: regenerator-runtime "^0.14.0" +"@babel/runtime@^7.7.2", "@babel/runtime@^7.8.7": + version "7.24.6" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.24.6.tgz#5b76eb89ad45e2e4a0a8db54c456251469a3358e" + integrity sha512-Ja18XcETdEl5mzzACGd+DKgaGJzPTCow7EglgwTmHdwokzDFYh/MHua6lU6DV/hjF2IaOJ4oX2nqnjG7RElKOw== + dependencies: + regenerator-runtime "^0.14.0" + "@babel/template@^7.22.15", "@babel/template@^7.22.5", "@babel/template@^7.3.3": version "7.22.15" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38" @@ -4871,6 +4878,11 @@ resolved "https://registry.yarnpkg.com/@iconicicons/react/-/react-1.5.1.tgz#1ddb561dc51b5b65f8e8f98fdbc019327e658662" integrity sha512-/Y23Y5NBBK7h/1/R8vTdHM0zwX9Yhk8L58QRhpD1Zu8heXDOz89Lqhzb8VQ1DAdYnd0vjfcGcQaUSgZQVbnlOw== +"@interactjs/types@1.10.27", "@interactjs/types@^1.10.11": + version "1.10.27" + resolved "https://registry.yarnpkg.com/@interactjs/types/-/types-1.10.27.tgz#10afd71cef2498e2b5192cf0d46f937d8ceb767f" + integrity sha512-BUdv0cvs4H5ODuwft2Xp4eL8Vmi3LcihK42z0Ft/FbVJZoRioBsxH+LlsBdK4tAie7PqlKGy+1oyOncu1nQ6eA== + "@isaacs/cliui@^8.0.2": version "8.0.2" resolved "https://registry.yarnpkg.com/@isaacs/cliui/-/cliui-8.0.2.tgz#b37667b7bc181c168782259bab42474fbf52b550" @@ -10715,6 +10727,14 @@ "@types/history" "^4.7.11" "@types/react" "*" +"@types/react-virtualized@^9.21.14": + version "9.21.30" + resolved "https://registry.yarnpkg.com/@types/react-virtualized/-/react-virtualized-9.21.30.tgz#ba39821bcb2487512a8a2cdd9fbdb5e6fc87fedb" + integrity sha512-4l2TFLQ8BCjNDQlvH85tU6gctuZoEdgYzENQyZHpgTHU7hoLzYgPSOALMAeA58LOWua8AzC6wBivPj1lfl6JgQ== + dependencies: + "@types/prop-types" "*" + "@types/react" "*" + "@types/react@*": version "18.2.21" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.21.tgz#774c37fd01b522d0b91aed04811b58e4e0514ed9" @@ -11273,6 +11293,17 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== +"@xzdarcy/react-timeline-editor@^0.1.9": + version "0.1.9" + resolved "https://registry.yarnpkg.com/@xzdarcy/react-timeline-editor/-/react-timeline-editor-0.1.9.tgz#77ee7f6d3f5c3dae1c445351895bf89f6771c14a" + integrity sha512-NiYMF+eVmpUzsnUufPIyFn0zAn9ArecnP7UApXYV4fJyCllj7VIoo1ig0fjak0jdON3uaO+k5W5B5K3jf0Djhw== + dependencies: + "@interactjs/types" "^1.10.11" + "@types/react-virtualized" "^9.21.14" + framework-utils "^1.1.0" + interactjs "^1.10.11" + react-virtualized "^9.22.3" + "@yarnpkg/esbuild-plugin-pnp@^3.0.0-rc.10": version "3.0.0-rc.15" resolved "https://registry.yarnpkg.com/@yarnpkg/esbuild-plugin-pnp/-/esbuild-plugin-pnp-3.0.0-rc.15.tgz#4e40e7d2eb28825c9a35ab9d04c363931d7c0e67" @@ -11446,11 +11477,6 @@ algoliasearch@^4.18.0, algoliasearch@^4.19.1: "@algolia/requester-node-http" "4.23.3" "@algolia/transporter" "4.23.3" -animation-timeline-js@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/animation-timeline-js/-/animation-timeline-js-2.3.2.tgz#b925bae56b5b7946ef572eb8bec08c884a63291a" - integrity sha512-IKP2mGRaa2bFQw+5eheSqiFc2Pa2X4UsBQPa4U2UqED4YhVWJ5svK0j2+Kko11Ug0j7X9XI58Na9zeZKkYrYdA== - ansi-align@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" @@ -12881,7 +12907,7 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== -clsx@^1.1.0, clsx@^1.2.1: +clsx@^1.0.4, clsx@^1.1.0, clsx@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== @@ -14493,6 +14519,14 @@ dom-converter@^0.2.0: dependencies: utila "~0.4" +dom-helpers@^5.1.3: + version "5.2.1" + resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.2.1.tgz#d9400536b2bf8225ad98fe052e029451ac40e902" + integrity sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA== + dependencies: + "@babel/runtime" "^7.8.7" + csstype "^3.0.2" + dom-serializer@^1.0.1: version "1.4.1" resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" @@ -16220,6 +16254,11 @@ fraction.js@^4.3.7: resolved "https://registry.yarnpkg.com/fraction.js/-/fraction.js-4.3.7.tgz#06ca0085157e42fda7f9e726e79fefc4068840f7" integrity sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew== +framework-utils@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/framework-utils/-/framework-utils-1.1.0.tgz#a3b528bce838dfd623148847dc92371b09d0da2d" + integrity sha512-KAfqli5PwpFJ8o3psRNs8svpMGyCSAe8nmGcjQ0zZBWN2H6dZDnq+ABp3N3hdUmFeMrLtjOCTXD4yplUJIWceg== + fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" @@ -17467,6 +17506,13 @@ inline-style-prefixer@^6.0.0: css-in-js-utils "^3.1.0" fast-loops "^1.1.3" +interactjs@^1.10.11: + version "1.10.27" + resolved "https://registry.yarnpkg.com/interactjs/-/interactjs-1.10.27.tgz#16499aba4987a5ccfdaddca7d1ba7bb1118e14d0" + integrity sha512-y/8RcCftGAF24gSp76X2JS3XpHiUvDQyhF8i7ujemBz77hwiHDuJzftHx7thY8cxGogwGiPJ+o97kWB6eAXnsA== + dependencies: + "@interactjs/types" "1.10.27" + internal-slot@^1.0.4: version "1.0.7" resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" @@ -23076,6 +23122,11 @@ react-json-view-lite@^1.2.0: resolved "https://registry.yarnpkg.com/react-json-view-lite/-/react-json-view-lite-1.3.0.tgz#1f1feee6f1b1d75cc498cd57812f441b88b51e21" integrity sha512-aN1biKC5v4DQkmQBlZjuMFR09MKZGMPtIg+cut8zEeg2HXd6gl2gRy0n4HMacHf0dznQgo0SVXN7eT8zV3hEuQ== +react-lifecycles-compat@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" + integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== + react-live@4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/react-live/-/react-live-4.0.1.tgz#7075310ecfa21a85bc588fd8c0c416d954655497" @@ -23227,6 +23278,18 @@ react-use@17.4.0: ts-easing "^0.2.0" tslib "^2.1.0" +react-virtualized@^9.22.3: + version "9.22.5" + resolved "https://registry.yarnpkg.com/react-virtualized/-/react-virtualized-9.22.5.tgz#bfb96fed519de378b50d8c0064b92994b3b91620" + integrity sha512-YqQMRzlVANBv1L/7r63OHa2b0ZsAaDp1UhVNEdUaXI8A5u6hTpA5NYtUueLH2rFuY/27mTGIBl7ZhqFKzw18YQ== + dependencies: + "@babel/runtime" "^7.7.2" + clsx "^1.0.4" + dom-helpers "^5.1.3" + loose-envify "^1.4.0" + prop-types "^15.7.2" + react-lifecycles-compat "^3.0.4" + react-window@^1: version "1.8.10" resolved "https://registry.yarnpkg.com/react-window/-/react-window-1.8.10.tgz#9e6b08548316814b443f7002b1cf8fd3a1bdde03"