public commit

This commit is contained in:
dswbx
2024-11-16 12:01:47 +01:00
commit 90f80c4280
582 changed files with 49291 additions and 0 deletions
@@ -0,0 +1,241 @@
import { Slider, Tabs } from "@mantine/core";
import { MarkerType, ReactFlowProvider } from "@xyflow/react";
import { objectDepth } from "core/utils";
import { useAtom } from "jotai";
import { atomWithStorage } from "jotai/utils";
import { pick, throttle } from "lodash-es";
import { useEffect, useRef, useState } from "react";
import { TbArrowLeft } from "react-icons/tb";
import { Canvas } from "ui/components/canvas/Canvas";
import { Panels } from "ui/components/canvas/panels";
import { Panel } from "ui/components/canvas/panels/Panel";
import { nodeTypes } from "ui/modules/flows/components2/nodes";
import {
FlowCanvasProvider,
flowToEdges,
flowToNodes,
useFlowCanvas,
useFlowCanvasState,
useFlowSelector
} from "ui/modules/flows/hooks/use-flow";
import { JsonViewer } from "../../components/code/JsonViewer";
import { routes, useGoBack, useNavigate } from "../../lib/routes";
/**
* @todo: AppFlows config must be updated to have fixed ids per task and connection
* ideally in array format
*
*/
export function FlowsEdit(props) {
return (
<FlowCanvasProvider name={props.params.flow}>
<ReactFlowProvider>
<FlowsEditInner />
</ReactFlowProvider>
</FlowCanvasProvider>
);
}
function FlowsEditInner() {
const ref = useRef<HTMLDivElement>(null);
const [rect, setRect] = useState<DOMRect>();
const $flow = useFlowCanvas();
if (!$flow.data || !$flow.name) return "no flow";
useEffect(() => {
// get width and height of ref object
console.log("ref", ref.current?.getBoundingClientRect());
setRect(ref.current?.getBoundingClientRect());
}, []);
const nodes = flowToNodes($flow.data, $flow.name);
const edges = flowToEdges($flow.data) as any;
console.log("nodes", nodes);
console.log("edges", edges);
const triggerHeight = 260;
const offset = 50;
const viewport = {
zoom: 1,
x: rect?.width ? rect.width * 0.1 : 0,
y: rect?.height ? rect.height / 2 - triggerHeight / 2 - offset : 0
};
return (
<div className="flex flex-col w-full h-full" ref={ref}>
{rect && (
<>
<Canvas
externalProvider
backgroundStyle="dots"
nodes={nodes}
edges={edges}
nodeTypes={nodeTypes}
nodeDragThreshold={0}
fitView={false}
defaultViewport={viewport}
nodesConnectable={true}
onDropNewNode={(node) => ({
...node,
type: "select",
data: { label: "" }
})}
onDropNewEdge={(edge) => ({
...edge,
style: {
strokeWidth: 2
},
markerEnd: {
type: MarkerType.ArrowClosed,
width: 10,
height: 10
}
})}
>
<FlowPanels />
</Canvas>
<Debugger />
</>
)}
</div>
);
}
function FlowPanels() {
const state = useFlowSelector((s) => pick(s, ["name", "dirty"]));
const [navigate] = useNavigate();
const { goBack } = useGoBack(() => navigate(routes.flows.flows.list()));
return (
<Panels minimap zoom>
<Panel position="top-left" className="gap-2 pr-6">
<Panel.IconButton Icon={TbArrowLeft} round onClick={goBack} />
<Panel.Text>
{state.name} {state.dirty ? "*" : ""}
</Panel.Text>
</Panel>
</Panels>
);
}
/*
function PanelsOld() {
//console.log("Panels");
const state = useFlowSelector((s) => pick(s, ["name", "dirty"]));
const [navigate] = useNavigate();
const { goBack } = useGoBack(() => navigate(routes.flows.flows.list()));
const [minimap, setMinimap] = useState(false);
const reactFlow = useReactFlow();
const { zoom, x, y } = useViewport();
const percent = Math.round(zoom * 100);
const handleZoomIn = async () => await reactFlow.zoomIn();
const handleZoomReset = async () => reactFlow.zoomTo(1);
const handleZoomOut = async () => await reactFlow.zoomOut();
function toggleMinimap() {
setMinimap((p) => !p);
}
return (
<>
<FlowPanel position="top-left" className="gap-2 pr-6">
<FlowPanel.IconButton Icon={TbArrowLeft} round onClick={goBack} />
<FlowPanel.Text>
{state.name} {state.dirty ? "*" : ""}
</FlowPanel.Text>
</FlowPanel>
<FlowPanel position="bottom-center">
<FlowPanel.Text className="px-2" mono>
{x.toFixed(2)},{y.toFixed(2)}
</FlowPanel.Text>
</FlowPanel>
<FlowPanel unstyled position="bottom-right">
<FlowPanel.Wrapper className="px-1.5">
<FlowPanel.IconButton Icon={TbPlus} round onClick={handleZoomIn} />
<FlowPanel.Text className="px-2" mono onClick={handleZoomReset}>
{percent}%
</FlowPanel.Text>
<FlowPanel.IconButton Icon={TbMinus} round onClick={handleZoomOut} />
<FlowPanel.IconButton Icon={TbMaximize} round onClick={handleZoomReset} />
</FlowPanel.Wrapper>
<FlowPanel.Wrapper>
<FlowPanel.IconButton
Icon={minimap ? TbSitemap : TbSitemap}
round
onClick={toggleMinimap}
variant={minimap ? "default" : "ghost"}
/>
</FlowPanel.Wrapper>
{minimap && <MiniMap style={{ bottom: 50, right: -5 }} ariaLabel={null} />}
</FlowPanel>
</>
);
}*/
type DebuggerTabProps = {
tab: string | null;
store?: Record<string, any>;
};
const debuggerTabAtom = atomWithStorage<DebuggerTabProps>("__dev_flow_debugger_tab", { tab: null });
const Debugger = () => {
const [_state, _setState] = useAtom(debuggerTabAtom);
const $flow = useFlowCanvas();
const state = useFlowCanvasState();
function handleTabChange(tab: string | null) {
_setState((prev) => ({ ...prev, tab: prev.tab === tab ? null : tab }));
}
const expand = _state.store?.expand || 3;
return (
<div className="flex fixed left-5 bottom-5 z-20">
<Tabs value={_state.tab} onChange={handleTabChange}>
<div className="max-h-96 overflow-y-scroll bg-background/70">
<Tabs.Panel value="store">
<div className="flex flex-row text-sm">
<JsonViewer
className="max-w-96 break-all"
title="Context"
json={{
name: $flow.name,
...$flow.data
}}
expand={expand}
/>
<JsonViewer
className="max-w-96 break-all"
title="State"
json={{
name: state.name,
...state.flow
}}
expand={expand}
/>
</div>
<Slider
className="w-36"
defaultValue={expand}
min={0}
max={objectDepth(state.flow ?? {})}
onChange={throttle(
(n) =>
_setState((prev) => ({
...prev,
store: { ...prev.store, expand: n }
})),
250
)}
/>
</Tabs.Panel>
</div>
<Tabs.List>
<Tabs.Tab value="store">Store</Tabs.Tab>
</Tabs.List>
</Tabs>
</div>
);
};