···9797 compactItems(items, mobile);
9898}
9999100100+// Fix all collisions between items (not just one moved item)
101101+// Items higher on the page have priority and stay in place
102102+export function fixAllCollisions(items: Item[], mobile: boolean = false) {
103103+ // Sort by Y position (top-to-bottom, then left-to-right)
104104+ // Items at the top have priority and won't be moved
105105+ const sortedItems = items.toSorted((a, b) =>
106106+ mobile ? a.mobileY - b.mobileY || a.mobileX - b.mobileX : a.y - b.y || a.x - b.x
107107+ );
108108+109109+ // Process each item and push it down if it overlaps with any item above it
110110+ for (let i = 0; i < sortedItems.length; i++) {
111111+ const item = sortedItems[i];
112112+113113+ // Clamp X to valid range
114114+ if (mobile) {
115115+ item.mobileX = clamp(item.mobileX, 0, COLUMNS - item.mobileW);
116116+ } else {
117117+ item.x = clamp(item.x, 0, COLUMNS - item.w);
118118+ }
119119+120120+ // Check for collisions with all items that come before (higher priority)
121121+ let hasCollision = true;
122122+ while (hasCollision) {
123123+ hasCollision = false;
124124+ for (let j = 0; j < i; j++) {
125125+ const other = sortedItems[j];
126126+ if (overlaps(item, other, mobile)) {
127127+ // Push item down below the colliding item
128128+ if (mobile) {
129129+ item.mobileY = other.mobileY + other.mobileH;
130130+ } else {
131131+ item.y = other.y + other.h;
132132+ }
133133+ hasCollision = true;
134134+ break; // Restart collision check from the beginning
135135+ }
136136+ }
137137+ }
138138+ }
139139+140140+ compactItems(items, mobile);
141141+}
142142+100143// Move all items up as far as possible without collisions
101144export function compactItems(items: Item[], mobile: boolean = false) {
102145 // Sort by Y position (top-to-bottom) so upper items settle first.