···97 compactItems(items, mobile);
98}
990000000000000000000000000000000000000000000100// Move all items up as far as possible without collisions
101export function compactItems(items: Item[], mobile: boolean = false) {
102 // Sort by Y position (top-to-bottom) so upper items settle first.
···97 compactItems(items, mobile);
98}
99100+// Fix all collisions between items (not just one moved item)
101+// Items higher on the page have priority and stay in place
102+export function fixAllCollisions(items: Item[], mobile: boolean = false) {
103+ // Sort by Y position (top-to-bottom, then left-to-right)
104+ // Items at the top have priority and won't be moved
105+ const sortedItems = items.toSorted((a, b) =>
106+ mobile ? a.mobileY - b.mobileY || a.mobileX - b.mobileX : a.y - b.y || a.x - b.x
107+ );
108+109+ // Process each item and push it down if it overlaps with any item above it
110+ for (let i = 0; i < sortedItems.length; i++) {
111+ const item = sortedItems[i];
112+113+ // Clamp X to valid range
114+ if (mobile) {
115+ item.mobileX = clamp(item.mobileX, 0, COLUMNS - item.mobileW);
116+ } else {
117+ item.x = clamp(item.x, 0, COLUMNS - item.w);
118+ }
119+120+ // Check for collisions with all items that come before (higher priority)
121+ let hasCollision = true;
122+ while (hasCollision) {
123+ hasCollision = false;
124+ for (let j = 0; j < i; j++) {
125+ const other = sortedItems[j];
126+ if (overlaps(item, other, mobile)) {
127+ // Push item down below the colliding item
128+ if (mobile) {
129+ item.mobileY = other.mobileY + other.mobileH;
130+ } else {
131+ item.y = other.y + other.h;
132+ }
133+ hasCollision = true;
134+ break; // Restart collision check from the beginning
135+ }
136+ }
137+ }
138+ }
139+140+ compactItems(items, mobile);
141+}
142+143// Move all items up as far as possible without collisions
144export function compactItems(items: Item[], mobile: boolean = false) {
145 // Sort by Y position (top-to-bottom) so upper items settle first.