Mission Control Dashboard - Initial implementation

This commit is contained in:
Daniel Arroyo
2026-03-27 18:36:05 +00:00
parent 257cea2c7d
commit a8fb4d4555
12516 changed files with 2307128 additions and 2 deletions

View File

@@ -0,0 +1,34 @@
/**
* Bounding boxes tend to be defined as top, left, right, bottom. For various operations
* it's easier to consider each axis individually. This function returns a bounding box
* as a map of single-axis min/max values.
*/
function convertBoundingBoxToBox({ top, left, right, bottom, }) {
return {
x: { min: left, max: right },
y: { min: top, max: bottom },
};
}
function convertBoxToBoundingBox({ x, y }) {
return { top: y.min, right: x.max, bottom: y.max, left: x.min };
}
/**
* Applies a TransformPoint function to a bounding box. TransformPoint is usually a function
* provided by Framer to allow measured points to be corrected for device scaling. This is used
* when measuring DOM elements and DOM event points.
*/
function transformBoxPoints(point, transformPoint) {
if (!transformPoint)
return point;
const topLeft = transformPoint({ x: point.left, y: point.top });
const bottomRight = transformPoint({ x: point.right, y: point.bottom });
return {
top: topLeft.y,
left: topLeft.x,
bottom: bottomRight.y,
right: bottomRight.x,
};
}
export { convertBoundingBoxToBox, convertBoxToBoundingBox, transformBoxPoints };
//# sourceMappingURL=conversion.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"conversion.mjs","sources":["../../../../src/projection/geometry/conversion.ts"],"sourcesContent":["import { BoundingBox, Box, TransformPoint } from \"motion-utils\"\n\n/**\n * Bounding boxes tend to be defined as top, left, right, bottom. For various operations\n * it's easier to consider each axis individually. This function returns a bounding box\n * as a map of single-axis min/max values.\n */\nexport function convertBoundingBoxToBox({\n top,\n left,\n right,\n bottom,\n}: BoundingBox): Box {\n return {\n x: { min: left, max: right },\n y: { min: top, max: bottom },\n }\n}\n\nexport function convertBoxToBoundingBox({ x, y }: Box): BoundingBox {\n return { top: y.min, right: x.max, bottom: y.max, left: x.min }\n}\n\n/**\n * Applies a TransformPoint function to a bounding box. TransformPoint is usually a function\n * provided by Framer to allow measured points to be corrected for device scaling. This is used\n * when measuring DOM elements and DOM event points.\n */\nexport function transformBoxPoints(\n point: BoundingBox,\n transformPoint?: TransformPoint\n) {\n if (!transformPoint) return point\n const topLeft = transformPoint({ x: point.left, y: point.top })\n const bottomRight = transformPoint({ x: point.right, y: point.bottom })\n\n return {\n top: topLeft.y,\n left: topLeft.x,\n bottom: bottomRight.y,\n right: bottomRight.x,\n }\n}\n"],"names":[],"mappings":"AAEA;;;;AAIG;AACG,SAAU,uBAAuB,CAAC,EACpC,GAAG,EACH,IAAI,EACJ,KAAK,EACL,MAAM,GACI,EAAA;IACV,OAAO;QACH,CAAC,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE;QAC5B,CAAC,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE;KAC/B;AACL;SAEgB,uBAAuB,CAAC,EAAE,CAAC,EAAE,CAAC,EAAO,EAAA;IACjD,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,EAAE,KAAK,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,EAAE;AACnE;AAEA;;;;AAIG;AACG,SAAU,kBAAkB,CAC9B,KAAkB,EAClB,cAA+B,EAAA;AAE/B,IAAA,IAAI,CAAC,cAAc;AAAE,QAAA,OAAO,KAAK;AACjC,IAAA,MAAM,OAAO,GAAG,cAAc,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,CAAC,GAAG,EAAE,CAAC;AAC/D,IAAA,MAAM,WAAW,GAAG,cAAc,CAAC,EAAE,CAAC,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,MAAM,EAAE,CAAC;IAEvE,OAAO;QACH,GAAG,EAAE,OAAO,CAAC,CAAC;QACd,IAAI,EAAE,OAAO,CAAC,CAAC;QACf,MAAM,EAAE,WAAW,CAAC,CAAC;QACrB,KAAK,EAAE,WAAW,CAAC,CAAC;KACvB;AACL;;;;"}

View File

@@ -0,0 +1,32 @@
/**
* Reset an axis to the provided origin box.
*
* This is a mutative operation.
*/
function copyAxisInto(axis, originAxis) {
axis.min = originAxis.min;
axis.max = originAxis.max;
}
/**
* Reset a box to the provided origin box.
*
* This is a mutative operation.
*/
function copyBoxInto(box, originBox) {
copyAxisInto(box.x, originBox.x);
copyAxisInto(box.y, originBox.y);
}
/**
* Reset a delta to the provided origin box.
*
* This is a mutative operation.
*/
function copyAxisDeltaInto(delta, originDelta) {
delta.translate = originDelta.translate;
delta.scale = originDelta.scale;
delta.originPoint = originDelta.originPoint;
delta.origin = originDelta.origin;
}
export { copyAxisDeltaInto, copyAxisInto, copyBoxInto };
//# sourceMappingURL=copy.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"copy.mjs","sources":["../../../../src/projection/geometry/copy.ts"],"sourcesContent":["import { Axis, AxisDelta, Box } from \"motion-utils\"\n\n/**\n * Reset an axis to the provided origin box.\n *\n * This is a mutative operation.\n */\nexport function copyAxisInto(axis: Axis, originAxis: Axis) {\n axis.min = originAxis.min\n axis.max = originAxis.max\n}\n\n/**\n * Reset a box to the provided origin box.\n *\n * This is a mutative operation.\n */\nexport function copyBoxInto(box: Box, originBox: Box) {\n copyAxisInto(box.x, originBox.x)\n copyAxisInto(box.y, originBox.y)\n}\n\n/**\n * Reset a delta to the provided origin box.\n *\n * This is a mutative operation.\n */\nexport function copyAxisDeltaInto(delta: AxisDelta, originDelta: AxisDelta) {\n delta.translate = originDelta.translate\n delta.scale = originDelta.scale\n delta.originPoint = originDelta.originPoint\n delta.origin = originDelta.origin\n}\n"],"names":[],"mappings":"AAEA;;;;AAIG;AACG,SAAU,YAAY,CAAC,IAAU,EAAE,UAAgB,EAAA;AACrD,IAAA,IAAI,CAAC,GAAG,GAAG,UAAU,CAAC,GAAG;AACzB,IAAA,IAAI,CAAC,GAAG,GAAG,UAAU,CAAC,GAAG;AAC7B;AAEA;;;;AAIG;AACG,SAAU,WAAW,CAAC,GAAQ,EAAE,SAAc,EAAA;IAChD,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;IAChC,YAAY,CAAC,GAAG,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,CAAC;AACpC;AAEA;;;;AAIG;AACG,SAAU,iBAAiB,CAAC,KAAgB,EAAE,WAAsB,EAAA;AACtE,IAAA,KAAK,CAAC,SAAS,GAAG,WAAW,CAAC,SAAS;AACvC,IAAA,KAAK,CAAC,KAAK,GAAG,WAAW,CAAC,KAAK;AAC/B,IAAA,KAAK,CAAC,WAAW,GAAG,WAAW,CAAC,WAAW;AAC3C,IAAA,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC,MAAM;AACrC;;;;"}

View File

@@ -0,0 +1,125 @@
import { mixNumber } from '../../utils/mix/number.mjs';
import { hasTransform } from '../utils/has-transform.mjs';
/**
* Scales a point based on a factor and an originPoint
*/
function scalePoint(point, scale, originPoint) {
const distanceFromOrigin = point - originPoint;
const scaled = scale * distanceFromOrigin;
return originPoint + scaled;
}
/**
* Applies a translate/scale delta to a point
*/
function applyPointDelta(point, translate, scale, originPoint, boxScale) {
if (boxScale !== undefined) {
point = scalePoint(point, boxScale, originPoint);
}
return scalePoint(point, scale, originPoint) + translate;
}
/**
* Applies a translate/scale delta to an axis
*/
function applyAxisDelta(axis, translate = 0, scale = 1, originPoint, boxScale) {
axis.min = applyPointDelta(axis.min, translate, scale, originPoint, boxScale);
axis.max = applyPointDelta(axis.max, translate, scale, originPoint, boxScale);
}
/**
* Applies a translate/scale delta to a box
*/
function applyBoxDelta(box, { x, y }) {
applyAxisDelta(box.x, x.translate, x.scale, x.originPoint);
applyAxisDelta(box.y, y.translate, y.scale, y.originPoint);
}
const TREE_SCALE_SNAP_MIN = 0.999999999999;
const TREE_SCALE_SNAP_MAX = 1.0000000000001;
/**
* Apply a tree of deltas to a box. We do this to calculate the effect of all the transforms
* in a tree upon our box before then calculating how to project it into our desired viewport-relative box
*
* This is the final nested loop within updateLayoutDelta for future refactoring
*/
function applyTreeDeltas(box, treeScale, treePath, isSharedTransition = false) {
const treeLength = treePath.length;
if (!treeLength)
return;
// Reset the treeScale
treeScale.x = treeScale.y = 1;
let node;
let delta;
for (let i = 0; i < treeLength; i++) {
node = treePath[i];
delta = node.projectionDelta;
/**
* TODO: Prefer to remove this, but currently we have motion components with
* display: contents in Framer.
*/
const { visualElement } = node.options;
if (visualElement &&
visualElement.props.style &&
visualElement.props.style.display === "contents") {
continue;
}
if (isSharedTransition &&
node.options.layoutScroll &&
node.scroll &&
node !== node.root) {
translateAxis(box.x, -node.scroll.offset.x);
translateAxis(box.y, -node.scroll.offset.y);
}
if (delta) {
// Incoporate each ancestor's scale into a cumulative treeScale for this component
treeScale.x *= delta.x.scale;
treeScale.y *= delta.y.scale;
// Apply each ancestor's calculated delta into this component's recorded layout box
applyBoxDelta(box, delta);
}
if (isSharedTransition && hasTransform(node.latestValues)) {
transformBox(box, node.latestValues, node.layout?.layoutBox);
}
}
/**
* Snap tree scale back to 1 if it's within a non-perceivable threshold.
* This will help reduce useless scales getting rendered.
*/
if (treeScale.x < TREE_SCALE_SNAP_MAX &&
treeScale.x > TREE_SCALE_SNAP_MIN) {
treeScale.x = 1.0;
}
if (treeScale.y < TREE_SCALE_SNAP_MAX &&
treeScale.y > TREE_SCALE_SNAP_MIN) {
treeScale.y = 1.0;
}
}
function translateAxis(axis, distance) {
axis.min += distance;
axis.max += distance;
}
/**
* Apply a transform to an axis from the latest resolved motion values.
* This function basically acts as a bridge between a flat motion value map
* and applyAxisDelta
*/
function transformAxis(axis, axisTranslate, axisScale, boxScale, axisOrigin = 0.5) {
const originPoint = mixNumber(axis.min, axis.max, axisOrigin);
// Apply the axis delta to the final axis
applyAxisDelta(axis, axisTranslate, axisScale, originPoint, boxScale);
}
function resolveAxisTranslate(value, axis) {
if (typeof value === "string") {
return (parseFloat(value) / 100) * (axis.max - axis.min);
}
return value;
}
/**
* Apply a transform to a box from the latest resolved motion values.
*/
function transformBox(box, transform, sourceBox) {
const resolveBox = sourceBox ?? box;
transformAxis(box.x, resolveAxisTranslate(transform.x, resolveBox.x), transform.scaleX, transform.scale, transform.originX);
transformAxis(box.y, resolveAxisTranslate(transform.y, resolveBox.y), transform.scaleY, transform.scale, transform.originY);
}
export { applyAxisDelta, applyBoxDelta, applyPointDelta, applyTreeDeltas, scalePoint, transformAxis, transformBox, translateAxis };
//# sourceMappingURL=delta-apply.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,59 @@
import { mixNumber } from '../../utils/mix/number.mjs';
const SCALE_PRECISION = 0.0001;
const SCALE_MIN = 1 - SCALE_PRECISION;
const SCALE_MAX = 1 + SCALE_PRECISION;
const TRANSLATE_PRECISION = 0.01;
const TRANSLATE_MIN = 0 - TRANSLATE_PRECISION;
const TRANSLATE_MAX = 0 + TRANSLATE_PRECISION;
function calcLength(axis) {
return axis.max - axis.min;
}
function isNear(value, target, maxDistance) {
return Math.abs(value - target) <= maxDistance;
}
function calcAxisDelta(delta, source, target, origin = 0.5) {
delta.origin = origin;
delta.originPoint = mixNumber(source.min, source.max, delta.origin);
delta.scale = calcLength(target) / calcLength(source);
delta.translate =
mixNumber(target.min, target.max, delta.origin) - delta.originPoint;
if ((delta.scale >= SCALE_MIN && delta.scale <= SCALE_MAX) ||
isNaN(delta.scale)) {
delta.scale = 1.0;
}
if ((delta.translate >= TRANSLATE_MIN &&
delta.translate <= TRANSLATE_MAX) ||
isNaN(delta.translate)) {
delta.translate = 0.0;
}
}
function calcBoxDelta(delta, source, target, origin) {
calcAxisDelta(delta.x, source.x, target.x, origin ? origin.originX : undefined);
calcAxisDelta(delta.y, source.y, target.y, origin ? origin.originY : undefined);
}
function calcRelativeAxis(target, relative, parent, anchor = 0) {
const anchorPoint = anchor
? mixNumber(parent.min, parent.max, anchor)
: parent.min;
target.min = anchorPoint + relative.min;
target.max = target.min + calcLength(relative);
}
function calcRelativeBox(target, relative, parent, anchor) {
calcRelativeAxis(target.x, relative.x, parent.x, anchor?.x);
calcRelativeAxis(target.y, relative.y, parent.y, anchor?.y);
}
function calcRelativeAxisPosition(target, layout, parent, anchor = 0) {
const anchorPoint = anchor
? mixNumber(parent.min, parent.max, anchor)
: parent.min;
target.min = layout.min - anchorPoint;
target.max = target.min + calcLength(layout);
}
function calcRelativePosition(target, layout, parent, anchor) {
calcRelativeAxisPosition(target.x, layout.x, parent.x, anchor?.x);
calcRelativeAxisPosition(target.y, layout.y, parent.y, anchor?.y);
}
export { calcAxisDelta, calcBoxDelta, calcLength, calcRelativeAxis, calcRelativeAxisPosition, calcRelativeBox, calcRelativePosition, isNear };
//# sourceMappingURL=delta-calc.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,55 @@
import { mixNumber } from '../../utils/mix/number.mjs';
import { percent } from '../../value/types/numbers/units.mjs';
import { scalePoint } from './delta-apply.mjs';
/**
* Remove a delta from a point. This is essentially the steps of applyPointDelta in reverse
*/
function removePointDelta(point, translate, scale, originPoint, boxScale) {
point -= translate;
point = scalePoint(point, 1 / scale, originPoint);
if (boxScale !== undefined) {
point = scalePoint(point, 1 / boxScale, originPoint);
}
return point;
}
/**
* Remove a delta from an axis. This is essentially the steps of applyAxisDelta in reverse
*/
function removeAxisDelta(axis, translate = 0, scale = 1, origin = 0.5, boxScale, originAxis = axis, sourceAxis = axis) {
if (percent.test(translate)) {
translate = parseFloat(translate);
const relativeProgress = mixNumber(sourceAxis.min, sourceAxis.max, translate / 100);
translate = relativeProgress - sourceAxis.min;
}
if (typeof translate !== "number")
return;
let originPoint = mixNumber(originAxis.min, originAxis.max, origin);
if (axis === originAxis)
originPoint -= translate;
axis.min = removePointDelta(axis.min, translate, scale, originPoint, boxScale);
axis.max = removePointDelta(axis.max, translate, scale, originPoint, boxScale);
}
/**
* Remove a transforms from an axis. This is essentially the steps of applyAxisTransforms in reverse
* and acts as a bridge between motion values and removeAxisDelta
*/
function removeAxisTransforms(axis, transforms, [key, scaleKey, originKey], origin, sourceAxis) {
removeAxisDelta(axis, transforms[key], transforms[scaleKey], transforms[originKey], transforms.scale, origin, sourceAxis);
}
/**
* The names of the motion values we want to apply as translation, scale and origin.
*/
const xKeys = ["x", "scaleX", "originX"];
const yKeys = ["y", "scaleY", "originY"];
/**
* Remove a transforms from an box. This is essentially the steps of applyAxisBox in reverse
* and acts as a bridge between motion values and removeAxisDelta
*/
function removeBoxTransforms(box, transforms, originBox, sourceBox) {
removeAxisTransforms(box.x, transforms, xKeys, originBox ? originBox.x : undefined, sourceBox ? sourceBox.x : undefined);
removeAxisTransforms(box.y, transforms, yKeys, originBox ? originBox.y : undefined, sourceBox ? sourceBox.y : undefined);
}
export { removeAxisDelta, removeAxisTransforms, removeBoxTransforms, removePointDelta };
//# sourceMappingURL=delta-remove.mjs.map

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,18 @@
const createAxisDelta = () => ({
translate: 0,
scale: 1,
origin: 0,
originPoint: 0,
});
const createDelta = () => ({
x: createAxisDelta(),
y: createAxisDelta(),
});
const createAxis = () => ({ min: 0, max: 0 });
const createBox = () => ({
x: createAxis(),
y: createAxis(),
});
export { createAxis, createAxisDelta, createBox, createDelta };
//# sourceMappingURL=models.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"models.mjs","sources":["../../../../src/projection/geometry/models.ts"],"sourcesContent":["import { Axis, AxisDelta, Box, Delta } from \"motion-utils\"\n\nexport const createAxisDelta = (): AxisDelta => ({\n translate: 0,\n scale: 1,\n origin: 0,\n originPoint: 0,\n})\n\nexport const createDelta = (): Delta => ({\n x: createAxisDelta(),\n y: createAxisDelta(),\n})\n\nexport const createAxis = (): Axis => ({ min: 0, max: 0 })\n\nexport const createBox = (): Box => ({\n x: createAxis(),\n y: createAxis(),\n})\n"],"names":[],"mappings":"AAEO,MAAM,eAAe,GAAG,OAAkB;AAC7C,IAAA,SAAS,EAAE,CAAC;AACZ,IAAA,KAAK,EAAE,CAAC;AACR,IAAA,MAAM,EAAE,CAAC;AACT,IAAA,WAAW,EAAE,CAAC;AACjB,CAAA;AAEM,MAAM,WAAW,GAAG,OAAc;IACrC,CAAC,EAAE,eAAe,EAAE;IACpB,CAAC,EAAE,eAAe,EAAE;AACvB,CAAA;AAEM,MAAM,UAAU,GAAG,OAAa,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE;AAElD,MAAM,SAAS,GAAG,OAAY;IACjC,CAAC,EAAE,UAAU,EAAE;IACf,CAAC,EAAE,UAAU,EAAE;AAClB,CAAA;;;;"}

View File

@@ -0,0 +1,32 @@
import { calcLength } from './delta-calc.mjs';
function isAxisDeltaZero(delta) {
return delta.translate === 0 && delta.scale === 1;
}
function isDeltaZero(delta) {
return isAxisDeltaZero(delta.x) && isAxisDeltaZero(delta.y);
}
function axisEquals(a, b) {
return a.min === b.min && a.max === b.max;
}
function boxEquals(a, b) {
return axisEquals(a.x, b.x) && axisEquals(a.y, b.y);
}
function axisEqualsRounded(a, b) {
return (Math.round(a.min) === Math.round(b.min) &&
Math.round(a.max) === Math.round(b.max));
}
function boxEqualsRounded(a, b) {
return axisEqualsRounded(a.x, b.x) && axisEqualsRounded(a.y, b.y);
}
function aspectRatio(box) {
return calcLength(box.x) / calcLength(box.y);
}
function axisDeltaEquals(a, b) {
return (a.translate === b.translate &&
a.scale === b.scale &&
a.originPoint === b.originPoint);
}
export { aspectRatio, axisDeltaEquals, axisEquals, axisEqualsRounded, boxEquals, boxEqualsRounded, isDeltaZero };
//# sourceMappingURL=utils.mjs.map

View File

@@ -0,0 +1 @@
{"version":3,"file":"utils.mjs","sources":["../../../../src/projection/geometry/utils.ts"],"sourcesContent":["import { Axis, AxisDelta, Box, Delta } from \"motion-utils\"\nimport { calcLength } from \"./delta-calc\"\n\nfunction isAxisDeltaZero(delta: AxisDelta) {\n return delta.translate === 0 && delta.scale === 1\n}\n\nexport function isDeltaZero(delta: Delta) {\n return isAxisDeltaZero(delta.x) && isAxisDeltaZero(delta.y)\n}\n\nexport function axisEquals(a: Axis, b: Axis) {\n return a.min === b.min && a.max === b.max\n}\n\nexport function boxEquals(a: Box, b: Box) {\n return axisEquals(a.x, b.x) && axisEquals(a.y, b.y)\n}\n\nexport function axisEqualsRounded(a: Axis, b: Axis) {\n return (\n Math.round(a.min) === Math.round(b.min) &&\n Math.round(a.max) === Math.round(b.max)\n )\n}\n\nexport function boxEqualsRounded(a: Box, b: Box) {\n return axisEqualsRounded(a.x, b.x) && axisEqualsRounded(a.y, b.y)\n}\n\nexport function aspectRatio(box: Box): number {\n return calcLength(box.x) / calcLength(box.y)\n}\n\nexport function axisDeltaEquals(a: AxisDelta, b: AxisDelta) {\n return (\n a.translate === b.translate &&\n a.scale === b.scale &&\n a.originPoint === b.originPoint\n )\n}\n"],"names":[],"mappings":";;AAGA,SAAS,eAAe,CAAC,KAAgB,EAAA;IACrC,OAAO,KAAK,CAAC,SAAS,KAAK,CAAC,IAAI,KAAK,CAAC,KAAK,KAAK,CAAC;AACrD;AAEM,SAAU,WAAW,CAAC,KAAY,EAAA;AACpC,IAAA,OAAO,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,eAAe,CAAC,KAAK,CAAC,CAAC,CAAC;AAC/D;AAEM,SAAU,UAAU,CAAC,CAAO,EAAE,CAAO,EAAA;AACvC,IAAA,OAAO,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,GAAG;AAC7C;AAEM,SAAU,SAAS,CAAC,CAAM,EAAE,CAAM,EAAA;IACpC,OAAO,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACvD;AAEM,SAAU,iBAAiB,CAAC,CAAO,EAAE,CAAO,EAAA;AAC9C,IAAA,QACI,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;AACvC,QAAA,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;AAE/C;AAEM,SAAU,gBAAgB,CAAC,CAAM,EAAE,CAAM,EAAA;IAC3C,OAAO,iBAAiB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,iBAAiB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AACrE;AAEM,SAAU,WAAW,CAAC,GAAQ,EAAA;AAChC,IAAA,OAAO,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;AAChD;AAEM,SAAU,eAAe,CAAC,CAAY,EAAE,CAAY,EAAA;AACtD,IAAA,QACI,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,SAAS;AAC3B,QAAA,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC,KAAK;AACnB,QAAA,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC,WAAW;AAEvC;;;;"}