your personal website on atproto - mirror blento.app

some fixes

Florian 5bb2c796 0588b574

+111 -10
+1
CLAUDE.md
··· 64 64 - `auth.svelte.ts` - OAuth client state and login/logout flow using `@atcute/oauth-browser-client` 65 65 - `atproto.ts` - ATProto API helpers: `resolveHandle`, `listRecords`, `getRecord`, `putRecord`, `deleteRecord`, `uploadImage` 66 66 - Data is stored in user's PDS under collection `app.blento.card` 67 + - **Important**: ATProto does not allow floating point numbers in records. All numeric values must be integers. 67 68 68 69 **Data Loading (`src/lib/website/`):** 69 70
+2 -2
src/lib/cards/DrawCard/EditingDrawCard.svelte
··· 152 152 {@const pathData = getSvgPathFromStroke( 153 153 getStroke(stroke.points, getStrokeOptions(stroke.size ?? 3)) 154 154 )} 155 - <path d={pathData} class="fill-black accent:fill-white dark:fill-white" /> 155 + <path d={pathData} class="accent:fill-white fill-black dark:fill-white" /> 156 156 {/each} 157 157 {#if currentStroke.length > 0} 158 158 {@const pathData = getSvgPathFromStroke( 159 159 getStroke(currentStroke, getStrokeOptions(strokeSizes[strokeWidth])) 160 160 )} 161 - <path d={pathData} class="fill-black accent:fill-white dark:fill-white" /> 161 + <path d={pathData} class="accent:fill-white fill-black dark:fill-white" /> 162 162 {/if} 163 163 </svg> 164 164
+34
src/lib/helper.ts
··· 268 268 } 269 269 } 270 270 271 + /** 272 + * Find a valid position for a new item in a single mode (desktop or mobile). 273 + * This modifies the item's position properties in-place. 274 + */ 275 + export function findValidPosition(newItem: Item, items: Item[], mobile: boolean) { 276 + if (mobile) { 277 + let foundPosition = false; 278 + newItem.mobileY = 0; 279 + while (!foundPosition) { 280 + for (newItem.mobileX = 0; newItem.mobileX <= COLUMNS - newItem.mobileW; newItem.mobileX++) { 281 + const collision = items.find((item) => overlaps(newItem, item, true)); 282 + if (!collision) { 283 + foundPosition = true; 284 + break; 285 + } 286 + } 287 + if (!foundPosition) newItem.mobileY! += 1; 288 + } 289 + } else { 290 + let foundPosition = false; 291 + newItem.y = 0; 292 + while (!foundPosition) { 293 + for (newItem.x = 0; newItem.x <= COLUMNS - newItem.w; newItem.x++) { 294 + const collision = items.find((item) => overlaps(newItem, item, false)); 295 + if (!collision) { 296 + foundPosition = true; 297 + break; 298 + } 299 + } 300 + if (!foundPosition) newItem.y += 1; 301 + } 302 + } 303 + } 304 + 271 305 export async function refreshData(data: { updatedAt?: number; handle: string }) { 272 306 const TEN_MINUTES = 10 * 60 * 1000; 273 307 const now = Date.now();
+48 -5
src/lib/website/EditableProfile.svelte
··· 62 62 : '@5xl/wrapper:max-w-4xl @5xl/wrapper:px-12' 63 63 ]} 64 64 > 65 - <div class={['absolute left-2 z-20 flex gap-2', profilePosition === 'side' ? 'top-12' : 'top-4']}> 65 + <div class={['absolute left-2 z-20 flex gap-2', profilePosition === 'side' ? 'left-14 top-2' : 'top-2']}> 66 66 <Button 67 - size="sm" 67 + size="icon" 68 68 onclick={() => { 69 69 data.publication.preferences ??= {}; 70 70 data.publication.preferences.hideProfileSection = true; ··· 72 72 }} 73 73 variant="ghost" 74 74 > 75 - hide profile 75 + <svg 76 + xmlns="http://www.w3.org/2000/svg" 77 + fill="none" 78 + viewBox="0 0 24 24" 79 + stroke-width="1.5" 80 + stroke="currentColor" 81 + class="size-6" 82 + > 83 + <path 84 + stroke-linecap="round" 85 + stroke-linejoin="round" 86 + d="M3.98 8.223A10.477 10.477 0 0 0 1.934 12C3.226 16.338 7.244 19.5 12 19.5c.993 0 1.953-.138 2.863-.395M6.228 6.228A10.451 10.451 0 0 1 12 4.5c4.756 0 8.773 3.162 10.065 7.498a10.522 10.522 0 0 1-4.293 5.774M6.228 6.228 3 3m3.228 3.228 3.65 3.65m7.894 7.894L21 21m-3.228-3.228-3.65-3.65m0 0a3 3 0 1 0-4.243-4.243m4.242 4.242L9.88 9.88" 87 + /> 88 + </svg> 76 89 </Button> 77 90 78 91 <!-- Position toggle button (desktop only) --> 79 92 {#if !isMobile()} 80 - <Button size="sm" type="button" onclick={toggleProfilePosition} variant="ghost"> 81 - {profilePosition === 'side' ? 'Move to top' : 'Move to side'} 93 + <Button size="icon" type="button" onclick={toggleProfilePosition} variant="ghost"> 94 + {#if profilePosition === 'side'} 95 + <svg 96 + xmlns="http://www.w3.org/2000/svg" 97 + fill="none" 98 + viewBox="0 0 24 24" 99 + stroke-width="1.5" 100 + stroke="currentColor" 101 + class="size-6" 102 + > 103 + <path 104 + stroke-linecap="round" 105 + stroke-linejoin="round" 106 + d="m4.5 19.5 15-15m0 0H8.25m11.25 0v11.25" 107 + /> 108 + </svg> 109 + {:else} 110 + <svg 111 + xmlns="http://www.w3.org/2000/svg" 112 + fill="none" 113 + viewBox="0 0 24 24" 114 + stroke-width="1.5" 115 + stroke="currentColor" 116 + class="size-6" 117 + > 118 + <path 119 + stroke-linecap="round" 120 + stroke-linejoin="round" 121 + d="m19.5 4.5-15 15m0 0h11.25m-11.25 0V8.25" 122 + /> 123 + </svg> 124 + {/if} 82 125 </Button> 83 126 {/if} 84 127 </div>
+26 -3
src/lib/website/EditableWebsite.svelte
··· 6 6 clamp, 7 7 compactItems, 8 8 createEmptyCard, 9 + findValidPosition, 9 10 fixCollisions, 10 11 getHideProfileSection, 11 12 getProfilePosition, ··· 334 335 if (isMobile) { 335 336 item.mobileX = gridX; 336 337 item.mobileY = gridY; 338 + // Find valid desktop position 339 + findValidPosition(item, items, false); 337 340 } else { 338 341 item.x = gridX; 339 342 item.y = gridY; 343 + // Find valid mobile position 344 + findValidPosition(item, items, true); 340 345 } 341 346 342 347 items = [...items, item]; ··· 576 581 > 577 582 {#if getHideProfileSection(data)} 578 583 <Button 579 - size="sm" 584 + size="icon" 580 585 variant="ghost" 581 586 onclick={() => { 582 587 data.publication.preferences ??= {}; 583 588 data.publication.preferences.hideProfileSection = false; 584 589 data = { ...data }; 585 590 }} 586 - class="pointer-events-auto absolute top-2 left-4 z-20" 591 + class="pointer-events-auto absolute top-2 left-2 z-20" 587 592 > 588 - show profile 593 + <svg 594 + xmlns="http://www.w3.org/2000/svg" 595 + fill="none" 596 + viewBox="0 0 24 24" 597 + stroke-width="1.5" 598 + stroke="currentColor" 599 + class="size-6" 600 + > 601 + <path 602 + stroke-linecap="round" 603 + stroke-linejoin="round" 604 + d="M2.036 12.322a1.012 1.012 0 0 1 0-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178Z" 605 + /> 606 + <path 607 + stroke-linecap="round" 608 + stroke-linejoin="round" 609 + d="M15 12a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" 610 + /> 611 + </svg> 589 612 </Button> 590 613 {/if} 591 614 <div class="pointer-events-none"></div>