your personal website on atproto - mirror blento.app

more cleanup etc

Florian b2bf519d 1b8f6238

+65 -105
+8
docs/Beta.md
··· 26 26 - allow setting base and accent color 27 27 28 28 - edit link of image card 29 + 30 + - go straight to edit mode (and redirect to edit mode on succesfull login) 31 + 32 + - ask to fill with some default cards on page creation 33 + 34 + - share button (copy share link to blento, maybe post to bluesky?) 35 + 36 + - add icons to "change card to..." popover
+1
src/lib/cards/BaseCard/BaseEditingCard.svelte
··· 179 179 class="scale-100 opacity-100 starting:scale-0 starting:opacity-0" 180 180 {...rest} 181 181 > 182 + <div class="absolute inset-0 cursor-grab"></div> 182 183 {@render children?.()} 183 184 184 185 {#snippet controls()}
-21
src/lib/cards/BigSocialCard/SidebarItemBigSocialCard.svelte
··· 1 - <script lang="ts"> 2 - import { Button } from '@foxui/core'; 3 - 4 - let { onclick }: { onclick: () => void } = $props(); 5 - </script> 6 - 7 - <Button {onclick} variant="ghost" class="w-full justify-start"> 8 - <svg 9 - xmlns="http://www.w3.org/2000/svg" 10 - viewBox="0 0 24 24" 11 - fill="currentColor" 12 - class="text-accent-600 dark:text-accent-400" 13 - > 14 - <path 15 - fill-rule="evenodd" 16 - d="M4.848 2.771A49.144 49.144 0 0 1 12 2.25c2.43 0 4.817.178 7.152.52 1.978.292 3.348 2.024 3.348 3.97v6.02c0 1.946-1.37 3.678-3.348 3.97a48.901 48.901 0 0 1-3.476.383.39.39 0 0 0-.297.17l-2.755 4.133a.75.75 0 0 1-1.248 0l-2.755-4.133a.39.39 0 0 0-.297-.17 48.9 48.9 0 0 1-3.476-.384c-1.978-.29-3.348-2.024-3.348-3.97V6.741c0-1.946 1.37-3.68 3.348-3.97ZM6.75 8.25a.75.75 0 0 1 .75-.75h9a.75.75 0 0 1 0 1.5h-9a.75.75 0 0 1-.75-.75Zm.75 2.25a.75.75 0 0 0 0 1.5H12a.75.75 0 0 0 0-1.5H7.5Z" 17 - clip-rule="evenodd" 18 - /> 19 - </svg> 20 - Big Social Icon</Button 21 - >
+2 -2
src/lib/cards/BigSocialCard/index.ts
··· 1 1 import type { CardDefinition } from '../types'; 2 2 import BigSocialCard from './BigSocialCard.svelte'; 3 3 import CreateBigSocialCardModal from './CreateBigSocialCardModal.svelte'; 4 - import SidebarItemBigSocialCard from './SidebarItemBigSocialCard.svelte'; 5 4 6 5 export const BigSocialCardDefinition = { 7 6 type: 'bigsocial', 8 7 contentComponent: BigSocialCard, 9 8 creationModalComponent: CreateBigSocialCardModal, 10 - sidebarComponent: SidebarItemBigSocialCard, 9 + 11 10 createNew: (card) => { 12 11 card.cardType = 'bigsocial'; 13 12 card.cardData = { ··· 19 18 card.mobileW = 4; 20 19 card.mobileH = 4; 21 20 }, 21 + 22 22 canChange: (item) => { 23 23 const href = item.cardData?.href; 24 24 if (!href) return false;
+4 -1
src/lib/cards/EmbedCard/EmbedCard.svelte
··· 1 1 <script lang="ts"> 2 + import { getCanEdit } from '$lib/website/context'; 2 3 import type { ContentComponentProps } from '../types'; 3 4 4 5 let { item }: ContentComponentProps = $props(); 6 + 7 + let isEditing = getCanEdit() 5 8 </script> 6 9 7 10 <iframe 8 11 src={item.cardData.href} 9 12 sandbox="allow-scripts" 10 13 referrerpolicy="no-referrer" 11 - class="absolute inset-0 h-full w-full" 14 + class={["absolute inset-0 h-full w-full", isEditing() && "pointer-events-none"]} 12 15 title="" 13 16 ></iframe>
+8 -1
src/lib/cards/EmbedCard/index.ts
··· 13 13 card.h = 4; 14 14 card.mobileH = 8; 15 15 card.mobileW = 8; 16 - } 16 + }, 17 + 18 + canChange: (item) => Boolean(item.cardData.href), 19 + 20 + change: (item) => { 21 + return item; 22 + }, 23 + name: 'Embed Card' 17 24 } as CardDefinition & { type: 'embed' };
-1
src/lib/cards/LivestreamCard/index.ts
··· 1 1 import { client } from '$lib/oauth'; 2 2 import { listRecords } from '$lib/oauth/atproto'; 3 3 import { getImageBlobUrl } from '$lib/oauth/utils'; 4 - import EmbedCard from '../EmbedCard/EmbedCard.svelte'; 5 4 import type { CardDefinition } from '../types'; 6 5 import LivestreamCard from './LivestreamCard.svelte'; 7 6 import LivestreamEmbedCard from './LivestreamEmbedCard.svelte';
-54
src/lib/cards/YoutubeVideo/CreateYoutubeCardModal.svelte
··· 1 - <script lang="ts"> 2 - import { Alert, Button, Input, Modal, Subheading } from '@foxui/core'; 3 - import type { CreationModalComponentProps } from '../types'; 4 - import { matcher } from '.'; 5 - 6 - let { item = $bindable(), oncreate, oncancel }: CreationModalComponentProps = $props(); 7 - 8 - let isFetchingMetadata = $state(false); 9 - 10 - let errorMessage = $state(''); 11 - 12 - const idRegExp = /^[A-Za-z0-9-_]+$/; 13 - 14 - function extractID(idOrUrl: string) { 15 - if (idRegExp.test(idOrUrl)) return idOrUrl; 16 - return matcher(idOrUrl); 17 - } 18 - 19 - async function fetchMetadata() { 20 - errorMessage = ''; 21 - 22 - const videoid = extractID(item.cardData.href); 23 - if (!videoid) { 24 - errorMessage = 'Not a valid youtube URL!'; 25 - return false; 26 - } 27 - const posterFile = 'hqdefault'; 28 - const posterURL = `https://i.ytimg.com/vi/${videoid}/${posterFile}.jpg`; 29 - 30 - item.cardData.poster = posterURL; 31 - item.cardData.youtubeId = videoid; 32 - 33 - return true; 34 - } 35 - </script> 36 - 37 - <Modal open={true} closeButton={false}> 38 - <Subheading>Enter a link to a youtube video</Subheading> 39 - <Input bind:value={item.cardData.href} /> 40 - 41 - {#if errorMessage} 42 - <Alert type="error" title="Failed to create youtube card"><span>{errorMessage}</span></Alert> 43 - {/if} 44 - 45 - <div class="mt-4 flex justify-end gap-2"> 46 - <Button onclick={oncancel} variant="ghost">Cancel</Button> 47 - <Button 48 - disabled={isFetchingMetadata} 49 - onclick={async () => { 50 - if (await fetchMetadata()) oncreate(); 51 - }}>{isFetchingMetadata ? 'Creating...' : 'Create'}</Button 52 - > 53 - </div> 54 - </Modal>
-15
src/lib/cards/YoutubeVideo/SidebarItemYoutubeCard.svelte
··· 1 - <script lang="ts"> 2 - import { Button } from '@foxui/core'; 3 - 4 - let { onclick }: { onclick: () => void } = $props(); 5 - </script> 6 - 7 - <Button {onclick} variant="ghost" class="w-full justify-start"> 8 - <svg xmlns="http://www.w3.org/2000/svg" class="text-accent-600 dark:text-accent-400 h-4" viewBox="0 0 256 180" 9 - ><path 10 - fill="currentColor" 11 - d="M250.346 28.075A32.18 32.18 0 0 0 227.69 5.418C207.824 0 127.87 0 127.87 0S47.912.164 28.046 5.582A32.18 32.18 0 0 0 5.39 28.24c-6.009 35.298-8.34 89.084.165 122.97a32.18 32.18 0 0 0 22.656 22.657c19.866 5.418 99.822 5.418 99.822 5.418s79.955 0 99.82-5.418a32.18 32.18 0 0 0 22.657-22.657c6.338-35.348 8.291-89.1-.164-123.134" 12 - /><path fill="#fff" d="m102.421 128.06l66.328-38.418l-66.328-38.418z" /></svg 13 - > 14 - Youtube Video Card 15 - </Button>
+41 -5
src/lib/cards/YoutubeVideo/index.ts
··· 1 1 import type { CardDefinition } from '../types'; 2 - import CreateYoutubeCardModal from './CreateYoutubeCardModal.svelte'; 3 - import SidebarItemYoutubeCard from './SidebarItemYoutubeCard.svelte'; 4 2 import YoutubeCard from './YoutubeCard.svelte'; 5 3 6 4 export const YoutubeCardDefinition = { 7 5 type: 'youtubeVideo', 8 6 contentComponent: YoutubeCard, 9 - creationModalComponent: CreateYoutubeCardModal, 10 7 createNew: (card) => { 11 8 card.cardType = 'youtubeVideo'; 12 9 card.cardData = {}; 13 10 card.w = 4; 14 11 card.mobileW = 8; 15 12 }, 16 - sidebarComponent: SidebarItemYoutubeCard 13 + 14 + onUrlHandler: (url, item) => { 15 + const id = matcher(url); 16 + if (!id) return; 17 + 18 + const posterFile = 'hqdefault'; 19 + const posterURL = `https://i.ytimg.com/vi/${id}/${posterFile}.jpg`; 20 + 21 + item.cardData.poster = posterURL; 22 + item.cardData.youtubeId = id; 23 + item.cardData.href = url; 24 + 25 + item.w = 4; 26 + item.mobileW = 8; 27 + item.h = 3; 28 + item.mobileH = 5; 29 + 30 + return item; 31 + }, 32 + urlHandlerPriority: 2, 33 + 34 + canChange: (item) => Boolean(matcher(item.cardData.href)), 35 + 36 + change: (item) => { 37 + const href = item.cardData?.href; 38 + 39 + const id = matcher(href); 40 + if (!id) return; 41 + 42 + const posterFile = 'hqdefault'; 43 + const posterURL = `https://i.ytimg.com/vi/${id}/${posterFile}.jpg`; 44 + 45 + item.cardData.poster = posterURL; 46 + item.cardData.youtubeId = id; 47 + 48 + return item; 49 + }, 50 + name: 'Youtube Video' 17 51 } as CardDefinition & { type: 'youtubeVideo' }; 18 52 19 53 // Thanks to eleventy-plugin-youtube-embed ··· 26 60 * @param url URL to test 27 61 * @returns A YouTube video ID or undefined if none matched 28 62 */ 29 - export function matcher(url: string): string | undefined { 63 + export function matcher(url: string | undefined): string | undefined { 64 + if (!url) return; 65 + 30 66 const match = url.match(urlPattern); 31 67 return match?.[3]; 32 68 }
+1 -1
src/lib/cards/utils/MarkdownTextEditor.svelte
··· 119 119 }); 120 120 </script> 121 121 122 - <div class="w-full" bind:this={element}></div> 122 + <div class="w-full cursor-text" bind:this={element}></div> 123 123 124 124 <style> 125 125 :global(.tiptap p.is-editor-empty:first-child::before) {
-4
todo.md
··· 1 1 # todo 2 2 3 3 - general video card 4 - - link card: save favicon and og image to pds 5 - 6 4 - option to hide cards on mobile 7 5 - separate og image for main page 8 6 - image cards: different images for dark and light mode 9 7 - allow adding background image 10 - 11 - 12 8 13 9 - analytics (get page views) 14 10 - custom subdomain