your personal website on atproto - mirror blento.app

improve youtube card

Florian 1d2c5f48 edbf90ae

+87 -47
+2 -1
.claude/settings.local.json
··· 2 "permissions": { 3 "allow": [ 4 "Bash(pnpm check:*)", 5 - "mcp__ide__getDiagnostics" 6 ] 7 } 8 }
··· 2 "permissions": { 3 "allow": [ 4 "Bash(pnpm check:*)", 5 + "mcp__ide__getDiagnostics", 6 + "mcp__plugin_svelte_svelte__svelte-autofixer" 7 ] 8 } 9 }
+1 -7
src/lib/cards/VideoCard/index.ts
··· 59 }, 60 settingsComponent: VideoCardSettings, 61 62 - canChange: (item) => Boolean(item.cardData.video), 63 - 64 - change: (item) => { 65 - return item; 66 - }, 67 - name: 'Video Card', 68 - sidebarButtonText: 'Video' 69 } as CardDefinition & { type: 'video' };
··· 59 }, 60 settingsComponent: VideoCardSettings, 61 62 + name: 'Video Card' 63 } as CardDefinition & { type: 'video' };
-32
src/lib/cards/YoutubeVideo/YoutubeCard.svelte
··· 1 - <script lang="ts"> 2 - import { videoPlayer } from '../utils/YoutubeVideoPlayer.svelte'; 3 - import type { ContentComponentProps } from '../types'; 4 - 5 - let { item }: ContentComponentProps = $props(); 6 - </script> 7 - 8 - <img 9 - class={[ 10 - 'absolute inset-0 h-full w-full object-cover opacity-100 transition-transform duration-300 ease-in-out', 11 - item.cardData.href ? 'group-hover:scale-102' : '' 12 - ]} 13 - src={item.cardData.poster} 14 - alt="" 15 - /> 16 - <button 17 - onclick={() => { 18 - videoPlayer.show(item.cardData.youtubeId); 19 - }} 20 - class="absolute inset-0 flex h-full w-full cursor-pointer items-center justify-center" 21 - > 22 - <span class="sr-only"> 23 - {item.cardData.hrefText ?? 'Learn more'} 24 - </span> 25 - 26 - <svg xmlns="http://www.w3.org/2000/svg" class="text-accent-500 w-14" viewBox="0 0 256 180" 27 - ><path 28 - fill="currentColor" 29 - 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" 30 - /><path fill="#fff" d="m102.421 128.06l66.328-38.418l-66.328-38.418z" /></svg 31 - > 32 - </button>
···
+2
src/lib/cards/YoutubeVideo/index.ts src/lib/cards/YoutubeVideoCard/index.ts
··· 1 import type { CardDefinition } from '../types'; 2 import YoutubeCard from './YoutubeCard.svelte'; 3 4 export const YoutubeCardDefinition = { 5 type: 'youtubeVideo', 6 contentComponent: YoutubeCard, 7 createNew: (card) => { 8 card.cardType = 'youtubeVideo'; 9 card.cardData = {};
··· 1 import type { CardDefinition } from '../types'; 2 import YoutubeCard from './YoutubeCard.svelte'; 3 + import YoutubeCardSettings from './YoutubeCardSettings.svelte'; 4 5 export const YoutubeCardDefinition = { 6 type: 'youtubeVideo', 7 contentComponent: YoutubeCard, 8 + settingsComponent: YoutubeCardSettings, 9 createNew: (card) => { 10 card.cardType = 'youtubeVideo'; 11 card.cardData = {};
+46
src/lib/cards/YoutubeVideoCard/YoutubeCard.svelte
···
··· 1 + <script lang="ts"> 2 + import { videoPlayer } from '../utils/YoutubeVideoPlayer.svelte'; 3 + import type { ContentComponentProps } from '../types'; 4 + 5 + let { item }: ContentComponentProps = $props(); 6 + 7 + let isPlaying = $state(false); 8 + </script> 9 + 10 + {#if isPlaying && item.cardData.showInline} 11 + <iframe 12 + class="absolute inset-0 h-full w-full" 13 + src="https://www.youtube.com/embed/{item.cardData.youtubeId}?autoplay=1" 14 + title="YouTube video player" 15 + frameborder="0" 16 + allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" 17 + allowfullscreen 18 + ></iframe> 19 + {:else} 20 + <img 21 + class={[ 22 + 'absolute inset-0 h-full w-full object-cover opacity-100 transition-transform duration-300 ease-in-out', 23 + item.cardData.href ? 'group-hover:scale-102' : '' 24 + ]} 25 + src={item.cardData.poster} 26 + alt="" 27 + /> 28 + <button 29 + onclick={() => { 30 + if (item.cardData.showInline) isPlaying = true; 31 + else videoPlayer.show(item.cardData.youtubeId); 32 + }} 33 + class="absolute inset-0 flex h-full w-full cursor-pointer items-center justify-center" 34 + > 35 + <span class="sr-only"> 36 + {item.cardData.hrefText ?? 'Learn more'} 37 + </span> 38 + 39 + <svg xmlns="http://www.w3.org/2000/svg" class="text-accent-500 w-14" viewBox="0 0 256 180" 40 + ><path 41 + fill="currentColor" 42 + 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" 43 + /><path fill="#fff" d="m102.421 128.06l66.328-38.418l-66.328-38.418z" /></svg 44 + > 45 + </button> 46 + {/if}
+24
src/lib/cards/YoutubeVideoCard/YoutubeCardSettings.svelte
···
··· 1 + <script lang="ts"> 2 + import type { Item } from '$lib/types'; 3 + import { Checkbox, Label } from '@foxui/core'; 4 + 5 + let { item }: { item: Item; onclose: () => void } = $props(); 6 + </script> 7 + 8 + <div class="flex items-center space-x-2"> 9 + <Checkbox 10 + bind:checked={ 11 + () => Boolean(item.cardData.showInline), (val) => (item.cardData.showInline = val) 12 + } 13 + id="show-inline" 14 + aria-labelledby="show-inline-label" 15 + variant="secondary" 16 + /> 17 + <Label 18 + id="show-inline-label" 19 + for="show-inline" 20 + class="text-sm leading-none font-medium peer-disabled:cursor-not-allowed peer-disabled:opacity-70" 21 + > 22 + Show video in card 23 + </Label> 24 + </div>
+1 -1
src/lib/cards/index.ts
··· 14 import { TextCardDefinition } from './TextCard'; 15 import type { CardDefinition } from './types'; 16 import { VideoCardDefinition } from './VideoCard'; 17 - import { YoutubeCardDefinition } from './YoutubeVideo'; 18 import { BlueskyProfileCardDefinition } from './BlueskyProfileCard'; 19 20 export const AllCardDefinitions = [
··· 14 import { TextCardDefinition } from './TextCard'; 15 import type { CardDefinition } from './types'; 16 import { VideoCardDefinition } from './VideoCard'; 17 + import { YoutubeCardDefinition } from './YoutubeVideoCard'; 18 import { BlueskyProfileCardDefinition } from './BlueskyProfileCard'; 19 20 export const AllCardDefinitions = [
+9 -6
src/lib/cards/utils/YoutubeVideoPlayer.svelte
··· 19 20 <script lang="ts"> 21 import { cn } from '@foxui/core'; 22 - import { onMount } from 'svelte'; 23 24 const { class: className }: { class?: string } = $props(); 25 26 let Plyr = $state(); 27 28 onMount(async () => { 29 if (!Plyr) Plyr = (await import('plyr')).default; ··· 60 player.play(); 61 //player.fullscreen.enter(); 62 }); 63 - 64 - return () => { 65 - player.destroy(); 66 - }; 67 }); 68 69 let glow = 50; 70 </script> ··· 142 143 <svg width="0" height="0"> 144 <filter id="blur" y="-50%" x="-50%" width="300%" height="300%"> 145 - <feGaussianBlur in="SourceGraphic" stdDeviation={50} result="blurred" /> 146 <feColorMatrix type="saturate" in="blurred" values="3" /> 147 <feComposite in="SourceGraphic" operator="over" /> 148 </filter>
··· 19 20 <script lang="ts"> 21 import { cn } from '@foxui/core'; 22 + import { onDestroy, onMount } from 'svelte'; 23 24 const { class: className }: { class?: string } = $props(); 25 26 let Plyr = $state(); 27 + 28 + let player = $state(); 29 + 30 31 onMount(async () => { 32 if (!Plyr) Plyr = (await import('plyr')).default; ··· 63 player.play(); 64 //player.fullscreen.enter(); 65 }); 66 }); 67 + 68 + onDestroy(() => { 69 + player?.destroy(); 70 + }) 71 72 let glow = 50; 73 </script> ··· 145 146 <svg width="0" height="0"> 147 <filter id="blur" y="-50%" x="-50%" width="300%" height="300%"> 148 + <feGaussianBlur in="SourceGraphic" stdDeviation={glow} result="blurred" /> 149 <feColorMatrix type="saturate" in="blurred" values="3" /> 150 <feComposite in="SourceGraphic" operator="over" /> 151 </filter>
+2
src/lib/website/EditableWebsite.svelte
··· 869 </svg> 870 </Button> 871 872 <Button 873 size="iconLg" 874 variant="ghost" ··· 891 /> 892 </svg> 893 </Button> 894 895 <Button 896 size="iconLg"
··· 869 </svg> 870 </Button> 871 872 + {#if dev} 873 <Button 874 size="iconLg" 875 variant="ghost" ··· 892 /> 893 </svg> 894 </Button> 895 + {/if} 896 897 <Button 898 size="iconLg"