 85bf1341f3
			
		
	
	85bf1341f3
	
	
	
		
			
			Frontend Enhancements: - Complete React TypeScript frontend with modern UI components - Distributed workflows management interface with real-time updates - Socket.IO integration for live agent status monitoring - Agent management dashboard with cluster visualization - Project management interface with metrics and task tracking - Responsive design with proper error handling and loading states Backend Infrastructure: - Distributed coordinator for multi-agent workflow orchestration - Cluster management API with comprehensive agent operations - Enhanced database models for agents and projects - Project service for filesystem-based project discovery - Performance monitoring and metrics collection - Comprehensive API documentation and error handling Documentation: - Complete distributed development guide (README_DISTRIBUTED.md) - Comprehensive development report with architecture insights - System configuration templates and deployment guides The platform now provides a complete web interface for managing the distributed AI cluster with real-time monitoring, workflow orchestration, and agent coordination capabilities. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
		
			
				
	
	
		
			482 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			482 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| import { invariant } from '../../utils/errors.mjs';
 | |
| import { PanSession } from '../pan/PanSession.mjs';
 | |
| import { getGlobalLock } from './utils/lock.mjs';
 | |
| import { isRefObject } from '../../utils/is-ref-object.mjs';
 | |
| import { addPointerEvent } from '../../events/add-pointer-event.mjs';
 | |
| import { applyConstraints, calcRelativeConstraints, resolveDragElastic, calcViewportConstraints, defaultElastic, rebaseAxisConstraints, calcOrigin } from './utils/constraints.mjs';
 | |
| import { createBox } from '../../projection/geometry/models.mjs';
 | |
| import { eachAxis } from '../../projection/utils/each-axis.mjs';
 | |
| import { measurePageBox } from '../../projection/utils/measure.mjs';
 | |
| import { extractEventInfo } from '../../events/event-info.mjs';
 | |
| import { convertBoxToBoundingBox, convertBoundingBoxToBox } from '../../projection/geometry/conversion.mjs';
 | |
| import { addDomEvent } from '../../events/add-dom-event.mjs';
 | |
| import { calcLength } from '../../projection/geometry/delta-calc.mjs';
 | |
| import { mix } from '../../utils/mix.mjs';
 | |
| import { percent } from '../../value/types/numbers/units.mjs';
 | |
| import { animateMotionValue } from '../../animation/interfaces/motion-value.mjs';
 | |
| import { getContextWindow } from '../../utils/get-context-window.mjs';
 | |
| import { frame } from '../../frameloop/frame.mjs';
 | |
| 
 | |
| const elementDragControls = new WeakMap();
 | |
| /**
 | |
|  *
 | |
|  */
 | |
| // let latestPointerEvent: PointerEvent
 | |
| class VisualElementDragControls {
 | |
|     constructor(visualElement) {
 | |
|         // This is a reference to the global drag gesture lock, ensuring only one component
 | |
|         // can "capture" the drag of one or both axes.
 | |
|         // TODO: Look into moving this into pansession?
 | |
|         this.openGlobalLock = null;
 | |
|         this.isDragging = false;
 | |
|         this.currentDirection = null;
 | |
|         this.originPoint = { x: 0, y: 0 };
 | |
|         /**
 | |
|          * The permitted boundaries of travel, in pixels.
 | |
|          */
 | |
|         this.constraints = false;
 | |
|         this.hasMutatedConstraints = false;
 | |
|         /**
 | |
|          * The per-axis resolved elastic values.
 | |
|          */
 | |
|         this.elastic = createBox();
 | |
|         this.visualElement = visualElement;
 | |
|     }
 | |
|     start(originEvent, { snapToCursor = false } = {}) {
 | |
|         /**
 | |
|          * Don't start dragging if this component is exiting
 | |
|          */
 | |
|         const { presenceContext } = this.visualElement;
 | |
|         if (presenceContext && presenceContext.isPresent === false)
 | |
|             return;
 | |
|         const onSessionStart = (event) => {
 | |
|             const { dragSnapToOrigin } = this.getProps();
 | |
|             // Stop or pause any animations on both axis values immediately. This allows the user to throw and catch
 | |
|             // the component.
 | |
|             dragSnapToOrigin ? this.pauseAnimation() : this.stopAnimation();
 | |
|             if (snapToCursor) {
 | |
|                 this.snapToCursor(extractEventInfo(event, "page").point);
 | |
|             }
 | |
|         };
 | |
|         const onStart = (event, info) => {
 | |
|             // Attempt to grab the global drag gesture lock - maybe make this part of PanSession
 | |
|             const { drag, dragPropagation, onDragStart } = this.getProps();
 | |
|             if (drag && !dragPropagation) {
 | |
|                 if (this.openGlobalLock)
 | |
|                     this.openGlobalLock();
 | |
|                 this.openGlobalLock = getGlobalLock(drag);
 | |
|                 // If we don 't have the lock, don't start dragging
 | |
|                 if (!this.openGlobalLock)
 | |
|                     return;
 | |
|             }
 | |
|             this.isDragging = true;
 | |
|             this.currentDirection = null;
 | |
|             this.resolveConstraints();
 | |
|             if (this.visualElement.projection) {
 | |
|                 this.visualElement.projection.isAnimationBlocked = true;
 | |
|                 this.visualElement.projection.target = undefined;
 | |
|             }
 | |
|             /**
 | |
|              * Record gesture origin
 | |
|              */
 | |
|             eachAxis((axis) => {
 | |
|                 let current = this.getAxisMotionValue(axis).get() || 0;
 | |
|                 /**
 | |
|                  * If the MotionValue is a percentage value convert to px
 | |
|                  */
 | |
|                 if (percent.test(current)) {
 | |
|                     const { projection } = this.visualElement;
 | |
|                     if (projection && projection.layout) {
 | |
|                         const measuredAxis = projection.layout.layoutBox[axis];
 | |
|                         if (measuredAxis) {
 | |
|                             const length = calcLength(measuredAxis);
 | |
|                             current = length * (parseFloat(current) / 100);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 this.originPoint[axis] = current;
 | |
|             });
 | |
|             // Fire onDragStart event
 | |
|             if (onDragStart) {
 | |
|                 frame.update(() => onDragStart(event, info), false, true);
 | |
|             }
 | |
|             const { animationState } = this.visualElement;
 | |
|             animationState && animationState.setActive("whileDrag", true);
 | |
|         };
 | |
|         const onMove = (event, info) => {
 | |
|             // latestPointerEvent = event
 | |
|             const { dragPropagation, dragDirectionLock, onDirectionLock, onDrag, } = this.getProps();
 | |
|             // If we didn't successfully receive the gesture lock, early return.
 | |
|             if (!dragPropagation && !this.openGlobalLock)
 | |
|                 return;
 | |
|             const { offset } = info;
 | |
|             // Attempt to detect drag direction if directionLock is true
 | |
|             if (dragDirectionLock && this.currentDirection === null) {
 | |
|                 this.currentDirection = getCurrentDirection(offset);
 | |
|                 // If we've successfully set a direction, notify listener
 | |
|                 if (this.currentDirection !== null) {
 | |
|                     onDirectionLock && onDirectionLock(this.currentDirection);
 | |
|                 }
 | |
|                 return;
 | |
|             }
 | |
|             // Update each point with the latest position
 | |
|             this.updateAxis("x", info.point, offset);
 | |
|             this.updateAxis("y", info.point, offset);
 | |
|             /**
 | |
|              * Ideally we would leave the renderer to fire naturally at the end of
 | |
|              * this frame but if the element is about to change layout as the result
 | |
|              * of a re-render we want to ensure the browser can read the latest
 | |
|              * bounding box to ensure the pointer and element don't fall out of sync.
 | |
|              */
 | |
|             this.visualElement.render();
 | |
|             /**
 | |
|              * This must fire after the render call as it might trigger a state
 | |
|              * change which itself might trigger a layout update.
 | |
|              */
 | |
|             onDrag && onDrag(event, info);
 | |
|         };
 | |
|         const onSessionEnd = (event, info) => this.stop(event, info);
 | |
|         const resumeAnimation = () => eachAxis((axis) => {
 | |
|             var _a;
 | |
|             return this.getAnimationState(axis) === "paused" &&
 | |
|                 ((_a = this.getAxisMotionValue(axis).animation) === null || _a === void 0 ? void 0 : _a.play());
 | |
|         });
 | |
|         const { dragSnapToOrigin } = this.getProps();
 | |
|         this.panSession = new PanSession(originEvent, {
 | |
|             onSessionStart,
 | |
|             onStart,
 | |
|             onMove,
 | |
|             onSessionEnd,
 | |
|             resumeAnimation,
 | |
|         }, {
 | |
|             transformPagePoint: this.visualElement.getTransformPagePoint(),
 | |
|             dragSnapToOrigin,
 | |
|             contextWindow: getContextWindow(this.visualElement),
 | |
|         });
 | |
|     }
 | |
|     stop(event, info) {
 | |
|         const isDragging = this.isDragging;
 | |
|         this.cancel();
 | |
|         if (!isDragging)
 | |
|             return;
 | |
|         const { velocity } = info;
 | |
|         this.startAnimation(velocity);
 | |
|         const { onDragEnd } = this.getProps();
 | |
|         if (onDragEnd) {
 | |
|             frame.update(() => onDragEnd(event, info));
 | |
|         }
 | |
|     }
 | |
|     cancel() {
 | |
|         this.isDragging = false;
 | |
|         const { projection, animationState } = this.visualElement;
 | |
|         if (projection) {
 | |
|             projection.isAnimationBlocked = false;
 | |
|         }
 | |
|         this.panSession && this.panSession.end();
 | |
|         this.panSession = undefined;
 | |
|         const { dragPropagation } = this.getProps();
 | |
|         if (!dragPropagation && this.openGlobalLock) {
 | |
|             this.openGlobalLock();
 | |
|             this.openGlobalLock = null;
 | |
|         }
 | |
|         animationState && animationState.setActive("whileDrag", false);
 | |
|     }
 | |
|     updateAxis(axis, _point, offset) {
 | |
|         const { drag } = this.getProps();
 | |
|         // If we're not dragging this axis, do an early return.
 | |
|         if (!offset || !shouldDrag(axis, drag, this.currentDirection))
 | |
|             return;
 | |
|         const axisValue = this.getAxisMotionValue(axis);
 | |
|         let next = this.originPoint[axis] + offset[axis];
 | |
|         // Apply constraints
 | |
|         if (this.constraints && this.constraints[axis]) {
 | |
|             next = applyConstraints(next, this.constraints[axis], this.elastic[axis]);
 | |
|         }
 | |
|         axisValue.set(next);
 | |
|     }
 | |
|     resolveConstraints() {
 | |
|         var _a;
 | |
|         const { dragConstraints, dragElastic } = this.getProps();
 | |
|         const layout = this.visualElement.projection &&
 | |
|             !this.visualElement.projection.layout
 | |
|             ? this.visualElement.projection.measure(false)
 | |
|             : (_a = this.visualElement.projection) === null || _a === void 0 ? void 0 : _a.layout;
 | |
|         const prevConstraints = this.constraints;
 | |
|         if (dragConstraints && isRefObject(dragConstraints)) {
 | |
|             if (!this.constraints) {
 | |
|                 this.constraints = this.resolveRefConstraints();
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             if (dragConstraints && layout) {
 | |
|                 this.constraints = calcRelativeConstraints(layout.layoutBox, dragConstraints);
 | |
|             }
 | |
|             else {
 | |
|                 this.constraints = false;
 | |
|             }
 | |
|         }
 | |
|         this.elastic = resolveDragElastic(dragElastic);
 | |
|         /**
 | |
|          * If we're outputting to external MotionValues, we want to rebase the measured constraints
 | |
|          * from viewport-relative to component-relative.
 | |
|          */
 | |
|         if (prevConstraints !== this.constraints &&
 | |
|             layout &&
 | |
|             this.constraints &&
 | |
|             !this.hasMutatedConstraints) {
 | |
|             eachAxis((axis) => {
 | |
|                 if (this.getAxisMotionValue(axis)) {
 | |
|                     this.constraints[axis] = rebaseAxisConstraints(layout.layoutBox[axis], this.constraints[axis]);
 | |
|                 }
 | |
|             });
 | |
|         }
 | |
|     }
 | |
|     resolveRefConstraints() {
 | |
|         const { dragConstraints: constraints, onMeasureDragConstraints } = this.getProps();
 | |
|         if (!constraints || !isRefObject(constraints))
 | |
|             return false;
 | |
|         const constraintsElement = constraints.current;
 | |
|         invariant(constraintsElement !== null, "If `dragConstraints` is set as a React ref, that ref must be passed to another component's `ref` prop.");
 | |
|         const { projection } = this.visualElement;
 | |
|         // TODO
 | |
|         if (!projection || !projection.layout)
 | |
|             return false;
 | |
|         const constraintsBox = measurePageBox(constraintsElement, projection.root, this.visualElement.getTransformPagePoint());
 | |
|         let measuredConstraints = calcViewportConstraints(projection.layout.layoutBox, constraintsBox);
 | |
|         /**
 | |
|          * If there's an onMeasureDragConstraints listener we call it and
 | |
|          * if different constraints are returned, set constraints to that
 | |
|          */
 | |
|         if (onMeasureDragConstraints) {
 | |
|             const userConstraints = onMeasureDragConstraints(convertBoxToBoundingBox(measuredConstraints));
 | |
|             this.hasMutatedConstraints = !!userConstraints;
 | |
|             if (userConstraints) {
 | |
|                 measuredConstraints = convertBoundingBoxToBox(userConstraints);
 | |
|             }
 | |
|         }
 | |
|         return measuredConstraints;
 | |
|     }
 | |
|     startAnimation(velocity) {
 | |
|         const { drag, dragMomentum, dragElastic, dragTransition, dragSnapToOrigin, onDragTransitionEnd, } = this.getProps();
 | |
|         const constraints = this.constraints || {};
 | |
|         const momentumAnimations = eachAxis((axis) => {
 | |
|             if (!shouldDrag(axis, drag, this.currentDirection)) {
 | |
|                 return;
 | |
|             }
 | |
|             let transition = (constraints && constraints[axis]) || {};
 | |
|             if (dragSnapToOrigin)
 | |
|                 transition = { min: 0, max: 0 };
 | |
|             /**
 | |
|              * Overdamp the boundary spring if `dragElastic` is disabled. There's still a frame
 | |
|              * of spring animations so we should look into adding a disable spring option to `inertia`.
 | |
|              * We could do something here where we affect the `bounceStiffness` and `bounceDamping`
 | |
|              * using the value of `dragElastic`.
 | |
|              */
 | |
|             const bounceStiffness = dragElastic ? 200 : 1000000;
 | |
|             const bounceDamping = dragElastic ? 40 : 10000000;
 | |
|             const inertia = {
 | |
|                 type: "inertia",
 | |
|                 velocity: dragMomentum ? velocity[axis] : 0,
 | |
|                 bounceStiffness,
 | |
|                 bounceDamping,
 | |
|                 timeConstant: 750,
 | |
|                 restDelta: 1,
 | |
|                 restSpeed: 10,
 | |
|                 ...dragTransition,
 | |
|                 ...transition,
 | |
|             };
 | |
|             // If we're not animating on an externally-provided `MotionValue` we can use the
 | |
|             // component's animation controls which will handle interactions with whileHover (etc),
 | |
|             // otherwise we just have to animate the `MotionValue` itself.
 | |
|             return this.startAxisValueAnimation(axis, inertia);
 | |
|         });
 | |
|         // Run all animations and then resolve the new drag constraints.
 | |
|         return Promise.all(momentumAnimations).then(onDragTransitionEnd);
 | |
|     }
 | |
|     startAxisValueAnimation(axis, transition) {
 | |
|         const axisValue = this.getAxisMotionValue(axis);
 | |
|         return axisValue.start(animateMotionValue(axis, axisValue, 0, transition));
 | |
|     }
 | |
|     stopAnimation() {
 | |
|         eachAxis((axis) => this.getAxisMotionValue(axis).stop());
 | |
|     }
 | |
|     pauseAnimation() {
 | |
|         eachAxis((axis) => { var _a; return (_a = this.getAxisMotionValue(axis).animation) === null || _a === void 0 ? void 0 : _a.pause(); });
 | |
|     }
 | |
|     getAnimationState(axis) {
 | |
|         var _a;
 | |
|         return (_a = this.getAxisMotionValue(axis).animation) === null || _a === void 0 ? void 0 : _a.state;
 | |
|     }
 | |
|     /**
 | |
|      * Drag works differently depending on which props are provided.
 | |
|      *
 | |
|      * - If _dragX and _dragY are provided, we output the gesture delta directly to those motion values.
 | |
|      * - Otherwise, we apply the delta to the x/y motion values.
 | |
|      */
 | |
|     getAxisMotionValue(axis) {
 | |
|         const dragKey = "_drag" + axis.toUpperCase();
 | |
|         const props = this.visualElement.getProps();
 | |
|         const externalMotionValue = props[dragKey];
 | |
|         return externalMotionValue
 | |
|             ? externalMotionValue
 | |
|             : this.visualElement.getValue(axis, (props.initial ? props.initial[axis] : undefined) || 0);
 | |
|     }
 | |
|     snapToCursor(point) {
 | |
|         eachAxis((axis) => {
 | |
|             const { drag } = this.getProps();
 | |
|             // If we're not dragging this axis, do an early return.
 | |
|             if (!shouldDrag(axis, drag, this.currentDirection))
 | |
|                 return;
 | |
|             const { projection } = this.visualElement;
 | |
|             const axisValue = this.getAxisMotionValue(axis);
 | |
|             if (projection && projection.layout) {
 | |
|                 const { min, max } = projection.layout.layoutBox[axis];
 | |
|                 axisValue.set(point[axis] - mix(min, max, 0.5));
 | |
|             }
 | |
|         });
 | |
|     }
 | |
|     /**
 | |
|      * When the viewport resizes we want to check if the measured constraints
 | |
|      * have changed and, if so, reposition the element within those new constraints
 | |
|      * relative to where it was before the resize.
 | |
|      */
 | |
|     scalePositionWithinConstraints() {
 | |
|         if (!this.visualElement.current)
 | |
|             return;
 | |
|         const { drag, dragConstraints } = this.getProps();
 | |
|         const { projection } = this.visualElement;
 | |
|         if (!isRefObject(dragConstraints) || !projection || !this.constraints)
 | |
|             return;
 | |
|         /**
 | |
|          * Stop current animations as there can be visual glitching if we try to do
 | |
|          * this mid-animation
 | |
|          */
 | |
|         this.stopAnimation();
 | |
|         /**
 | |
|          * Record the relative position of the dragged element relative to the
 | |
|          * constraints box and save as a progress value.
 | |
|          */
 | |
|         const boxProgress = { x: 0, y: 0 };
 | |
|         eachAxis((axis) => {
 | |
|             const axisValue = this.getAxisMotionValue(axis);
 | |
|             if (axisValue) {
 | |
|                 const latest = axisValue.get();
 | |
|                 boxProgress[axis] = calcOrigin({ min: latest, max: latest }, this.constraints[axis]);
 | |
|             }
 | |
|         });
 | |
|         /**
 | |
|          * Update the layout of this element and resolve the latest drag constraints
 | |
|          */
 | |
|         const { transformTemplate } = this.visualElement.getProps();
 | |
|         this.visualElement.current.style.transform = transformTemplate
 | |
|             ? transformTemplate({}, "")
 | |
|             : "none";
 | |
|         projection.root && projection.root.updateScroll();
 | |
|         projection.updateLayout();
 | |
|         this.resolveConstraints();
 | |
|         /**
 | |
|          * For each axis, calculate the current progress of the layout axis
 | |
|          * within the new constraints.
 | |
|          */
 | |
|         eachAxis((axis) => {
 | |
|             if (!shouldDrag(axis, drag, null))
 | |
|                 return;
 | |
|             /**
 | |
|              * Calculate a new transform based on the previous box progress
 | |
|              */
 | |
|             const axisValue = this.getAxisMotionValue(axis);
 | |
|             const { min, max } = this.constraints[axis];
 | |
|             axisValue.set(mix(min, max, boxProgress[axis]));
 | |
|         });
 | |
|     }
 | |
|     addListeners() {
 | |
|         if (!this.visualElement.current)
 | |
|             return;
 | |
|         elementDragControls.set(this.visualElement, this);
 | |
|         const element = this.visualElement.current;
 | |
|         /**
 | |
|          * Attach a pointerdown event listener on this DOM element to initiate drag tracking.
 | |
|          */
 | |
|         const stopPointerListener = addPointerEvent(element, "pointerdown", (event) => {
 | |
|             const { drag, dragListener = true } = this.getProps();
 | |
|             drag && dragListener && this.start(event);
 | |
|         });
 | |
|         const measureDragConstraints = () => {
 | |
|             const { dragConstraints } = this.getProps();
 | |
|             if (isRefObject(dragConstraints)) {
 | |
|                 this.constraints = this.resolveRefConstraints();
 | |
|             }
 | |
|         };
 | |
|         const { projection } = this.visualElement;
 | |
|         const stopMeasureLayoutListener = projection.addEventListener("measure", measureDragConstraints);
 | |
|         if (projection && !projection.layout) {
 | |
|             projection.root && projection.root.updateScroll();
 | |
|             projection.updateLayout();
 | |
|         }
 | |
|         measureDragConstraints();
 | |
|         /**
 | |
|          * Attach a window resize listener to scale the draggable target within its defined
 | |
|          * constraints as the window resizes.
 | |
|          */
 | |
|         const stopResizeListener = addDomEvent(window, "resize", () => this.scalePositionWithinConstraints());
 | |
|         /**
 | |
|          * If the element's layout changes, calculate the delta and apply that to
 | |
|          * the drag gesture's origin point.
 | |
|          */
 | |
|         const stopLayoutUpdateListener = projection.addEventListener("didUpdate", (({ delta, hasLayoutChanged }) => {
 | |
|             if (this.isDragging && hasLayoutChanged) {
 | |
|                 eachAxis((axis) => {
 | |
|                     const motionValue = this.getAxisMotionValue(axis);
 | |
|                     if (!motionValue)
 | |
|                         return;
 | |
|                     this.originPoint[axis] += delta[axis].translate;
 | |
|                     motionValue.set(motionValue.get() + delta[axis].translate);
 | |
|                 });
 | |
|                 this.visualElement.render();
 | |
|             }
 | |
|         }));
 | |
|         return () => {
 | |
|             stopResizeListener();
 | |
|             stopPointerListener();
 | |
|             stopMeasureLayoutListener();
 | |
|             stopLayoutUpdateListener && stopLayoutUpdateListener();
 | |
|         };
 | |
|     }
 | |
|     getProps() {
 | |
|         const props = this.visualElement.getProps();
 | |
|         const { drag = false, dragDirectionLock = false, dragPropagation = false, dragConstraints = false, dragElastic = defaultElastic, dragMomentum = true, } = props;
 | |
|         return {
 | |
|             ...props,
 | |
|             drag,
 | |
|             dragDirectionLock,
 | |
|             dragPropagation,
 | |
|             dragConstraints,
 | |
|             dragElastic,
 | |
|             dragMomentum,
 | |
|         };
 | |
|     }
 | |
| }
 | |
| function shouldDrag(direction, drag, currentDirection) {
 | |
|     return ((drag === true || drag === direction) &&
 | |
|         (currentDirection === null || currentDirection === direction));
 | |
| }
 | |
| /**
 | |
|  * Based on an x/y offset determine the current drag direction. If both axis' offsets are lower
 | |
|  * than the provided threshold, return `null`.
 | |
|  *
 | |
|  * @param offset - The x/y offset from origin.
 | |
|  * @param lockThreshold - (Optional) - the minimum absolute offset before we can determine a drag direction.
 | |
|  */
 | |
| function getCurrentDirection(offset, lockThreshold = 10) {
 | |
|     let direction = null;
 | |
|     if (Math.abs(offset.y) > lockThreshold) {
 | |
|         direction = "y";
 | |
|     }
 | |
|     else if (Math.abs(offset.x) > lockThreshold) {
 | |
|         direction = "x";
 | |
|     }
 | |
|     return direction;
 | |
| }
 | |
| 
 | |
| export { VisualElementDragControls, elementDragControls };
 |