diff --git a/CLAUDE.md b/CLAUDE.md
index 67e8bfb..3e7480d 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -64,6 +64,7 @@ Grid margins: 16px desktop, 12px mobile.
- `auth.svelte.ts` - OAuth client state and login/logout flow using `@atcute/oauth-browser-client`
- `atproto.ts` - ATProto API helpers: `resolveHandle`, `listRecords`, `getRecord`, `putRecord`, `deleteRecord`, `uploadImage`
- Data is stored in user's PDS under collection `app.blento.card`
+- **Important**: ATProto does not allow floating point numbers in records. All numeric values must be integers.
**Data Loading (`src/lib/website/`):**
diff --git a/src/lib/cards/DrawCard/EditingDrawCard.svelte b/src/lib/cards/DrawCard/EditingDrawCard.svelte
index 7aba179..35862dc 100644
--- a/src/lib/cards/DrawCard/EditingDrawCard.svelte
+++ b/src/lib/cards/DrawCard/EditingDrawCard.svelte
@@ -152,13 +152,13 @@
{@const pathData = getSvgPathFromStroke(
getStroke(stroke.points, getStrokeOptions(stroke.size ?? 3))
)}
-
+
{/each}
{#if currentStroke.length > 0}
{@const pathData = getSvgPathFromStroke(
getStroke(currentStroke, getStrokeOptions(strokeSizes[strokeWidth]))
)}
-
+
{/if}
diff --git a/src/lib/helper.ts b/src/lib/helper.ts
index 6bfc9f8..935db87 100644
--- a/src/lib/helper.ts
+++ b/src/lib/helper.ts
@@ -268,6 +268,40 @@ export function setPositionOfNewItem(newItem: Item, items: Item[]) {
}
}
+/**
+ * Find a valid position for a new item in a single mode (desktop or mobile).
+ * This modifies the item's position properties in-place.
+ */
+export function findValidPosition(newItem: Item, items: Item[], mobile: boolean) {
+ if (mobile) {
+ let foundPosition = false;
+ newItem.mobileY = 0;
+ while (!foundPosition) {
+ for (newItem.mobileX = 0; newItem.mobileX <= COLUMNS - newItem.mobileW; newItem.mobileX++) {
+ const collision = items.find((item) => overlaps(newItem, item, true));
+ if (!collision) {
+ foundPosition = true;
+ break;
+ }
+ }
+ if (!foundPosition) newItem.mobileY! += 1;
+ }
+ } else {
+ let foundPosition = false;
+ newItem.y = 0;
+ while (!foundPosition) {
+ for (newItem.x = 0; newItem.x <= COLUMNS - newItem.w; newItem.x++) {
+ const collision = items.find((item) => overlaps(newItem, item, false));
+ if (!collision) {
+ foundPosition = true;
+ break;
+ }
+ }
+ if (!foundPosition) newItem.y += 1;
+ }
+ }
+}
+
export async function refreshData(data: { updatedAt?: number; handle: string }) {
const TEN_MINUTES = 10 * 60 * 1000;
const now = Date.now();
diff --git a/src/lib/website/EditableProfile.svelte b/src/lib/website/EditableProfile.svelte
index df8352c..b75728f 100644
--- a/src/lib/website/EditableProfile.svelte
+++ b/src/lib/website/EditableProfile.svelte
@@ -62,9 +62,9 @@
: '@5xl/wrapper:max-w-4xl @5xl/wrapper:px-12'
]}
>
-
+
{#if !isMobile()}
-
diff --git a/src/lib/website/EditableWebsite.svelte b/src/lib/website/EditableWebsite.svelte
index 5275355..f681c80 100644
--- a/src/lib/website/EditableWebsite.svelte
+++ b/src/lib/website/EditableWebsite.svelte
@@ -6,6 +6,7 @@
clamp,
compactItems,
createEmptyCard,
+ findValidPosition,
fixCollisions,
getHideProfileSection,
getProfilePosition,
@@ -334,9 +335,13 @@
if (isMobile) {
item.mobileX = gridX;
item.mobileY = gridY;
+ // Find valid desktop position
+ findValidPosition(item, items, false);
} else {
item.x = gridX;
item.y = gridY;
+ // Find valid mobile position
+ findValidPosition(item, items, true);
}
items = [...items, item];
@@ -576,16 +581,34 @@
>
{#if getHideProfileSection(data)}
{
data.publication.preferences ??= {};
data.publication.preferences.hideProfileSection = false;
data = { ...data };
}}
- class="pointer-events-auto absolute top-2 left-4 z-20"
+ class="pointer-events-auto absolute top-2 left-2 z-20"
>
- show profile
+
{/if}