your personal website on atproto - mirror

refactor pt6

Florian d529a5d5 a028e39d

+122 -132
+8 -5
src/lib/cards/BlueskyMediaCard/CreateBlueskyMediaCardModal.svelte
··· 9 9 10 10 let did = getDidContext(); 11 11 12 - let mediaList: { fullsize: string; isVideo?: boolean; playlist?: string }[] = $state([]); 12 + let mediaList: { fullsize: string; isVideo?: boolean; playlist?: string, thumbnail?: string }[] = $state([]); 13 13 14 14 let isLoading = $state(true); 15 15 ··· 17 17 const authorFeed = await getAuthorFeed({ did }); 18 18 19 19 for (let post of authorFeed?.feed ?? []) { 20 - for (let image of post.post.embed?.images ?? []) { 20 + let images = post.post.embed?.$type === 'app.bsky.embed.images#view' ? post.post.embed : undefined 21 + 22 + for (let image of images?.images ?? []) { 21 23 mediaList.push(image); 22 24 } 23 25 24 - if (post.post.embed.thumbnail && post.post.embed.playlist) { 26 + if (post.post.embed?.$type === 'app.bsky.embed.video#view' && post.post.embed.thumbnail && post.post.embed.playlist) { 25 27 mediaList.push({ 26 28 ...post.post.embed, 27 - isVideo: true 29 + isVideo: true, 30 + fullsize: '' 28 31 }); 29 32 } 30 33 } ··· 64 67 class="relative cursor-pointer" 65 68 > 66 69 <img 67 - src={media.fullsize ?? media.thumbnail} 70 + src={media.fullsize || media.thumbnail} 68 71 alt="" 69 72 class={[ 70 73 'h-32 w-full rounded-xl object-cover',
+111 -125
src/lib/cards/FluidTextCard/FluidTextCard.svelte
··· 1 1 <script lang="ts"> 2 2 import type { ContentComponentProps } from '../types'; 3 3 import { onMount, onDestroy, tick } from 'svelte'; 4 - import ditheringTextureUrl from './LDR_LLL1_0.png'; 5 - 6 4 let { item }: ContentComponentProps = $props(); 7 5 8 6 let container: HTMLDivElement; ··· 15 13 let isInitialized = $state(false); 16 14 let resizeObserver: ResizeObserver | null = null; 17 15 16 + // Pure hash function for shader keyword caching 17 + function hashCode(s: string) { 18 + if (s.length === 0) return 0; 19 + let hash = 0; 20 + for (let i = 0; i < s.length; i++) { 21 + hash = (hash << 5) - hash + s.charCodeAt(i); 22 + hash |= 0; 23 + } 24 + return hash; 25 + } 26 + 27 + // WebGL helper types 28 + type CompileShaderFn = (type: number, source: string, keywords?: string[]) => WebGLShader; 29 + type CreateProgramFn = (vs: WebGLShader, fs: WebGLShader) => WebGLProgram; 30 + type GetUniformsFn = (program: WebGLProgram) => Record<string, WebGLUniformLocation | null>; 31 + 32 + // Material class for shader programs with keyword variants 33 + class Material { 34 + private gl: WebGL2RenderingContext; 35 + private compileShader: CompileShaderFn; 36 + private createProgramFn: CreateProgramFn; 37 + private getUniformsFn: GetUniformsFn; 38 + vertexShader: WebGLShader; 39 + fragmentShaderSource: string; 40 + programs: Record<number, WebGLProgram> = {}; 41 + activeProgram: WebGLProgram | null = null; 42 + uniforms: Record<string, WebGLUniformLocation | null> = {}; 43 + 44 + constructor( 45 + gl: WebGL2RenderingContext, 46 + vertexShader: WebGLShader, 47 + fragmentShaderSource: string, 48 + compileShader: CompileShaderFn, 49 + createProgramFn: CreateProgramFn, 50 + getUniformsFn: GetUniformsFn 51 + ) { 52 + this.gl = gl; 53 + this.compileShader = compileShader; 54 + this.createProgramFn = createProgramFn; 55 + this.getUniformsFn = getUniformsFn; 56 + this.vertexShader = vertexShader; 57 + this.fragmentShaderSource = fragmentShaderSource; 58 + } 59 + 60 + setKeywords(keywords: string[]) { 61 + let hash = 0; 62 + for (let i = 0; i < keywords.length; i++) hash += hashCode(keywords[i]); 63 + 64 + let program = this.programs[hash]; 65 + if (!program) { 66 + const fragmentShader = this.compileShader( 67 + this.gl.FRAGMENT_SHADER, 68 + this.fragmentShaderSource, 69 + keywords 70 + ); 71 + program = this.createProgramFn(this.vertexShader, fragmentShader); 72 + this.programs[hash] = program; 73 + } 74 + 75 + if (program === this.activeProgram) return; 76 + 77 + this.uniforms = this.getUniformsFn(program); 78 + this.activeProgram = program; 79 + } 80 + 81 + bind() { 82 + this.gl.useProgram(this.activeProgram); 83 + } 84 + } 85 + 86 + // Program class for simple shader programs 87 + class Program { 88 + private gl: WebGL2RenderingContext; 89 + uniforms: Record<string, WebGLUniformLocation | null> = {}; 90 + program: WebGLProgram; 91 + 92 + constructor( 93 + gl: WebGL2RenderingContext, 94 + vertexShader: WebGLShader, 95 + fragmentShader: WebGLShader, 96 + createProgramFn: CreateProgramFn, 97 + getUniformsFn: GetUniformsFn 98 + ) { 99 + this.gl = gl; 100 + this.program = createProgramFn(vertexShader, fragmentShader); 101 + this.uniforms = getUniformsFn(this.program); 102 + } 103 + 104 + bind() { 105 + this.gl.useProgram(this.program); 106 + } 107 + } 108 + 18 109 // Get text from card data 19 110 const text = $derived((item.cardData?.text as string) || 'hello'); 20 111 const fontWeight = '900'; ··· 180 271 let pointers: Pointer[] = [PointerPrototype()]; 181 272 let splatStack: number[] = []; 182 273 183 - const { gl, ext } = getWebGLContext(fluidCanvas); 184 - if (!gl) return; 274 + const { gl: glMaybeNull, ext } = getWebGLContext(fluidCanvas); 275 + if (!glMaybeNull) return; 276 + const gl = glMaybeNull; 185 277 186 278 if (isMobile()) { 187 279 config.DYE_RESOLUTION = 512; ··· 316 408 return /Mobi|Android/i.test(navigator.userAgent); 317 409 } 318 410 319 - class Material { 320 - vertexShader: WebGLShader; 321 - fragmentShaderSource: string; 322 - programs: Record<number, WebGLProgram> = {}; 323 - activeProgram: WebGLProgram | null = null; 324 - uniforms: Record<string, WebGLUniformLocation | null> = {}; 325 - 326 - constructor(vertexShader: WebGLShader, fragmentShaderSource: string) { 327 - this.vertexShader = vertexShader; 328 - this.fragmentShaderSource = fragmentShaderSource; 329 - } 330 - 331 - setKeywords(keywords: string[]) { 332 - let hash = 0; 333 - for (let i = 0; i < keywords.length; i++) hash += hashCode(keywords[i]); 334 - 335 - let program = this.programs[hash]; 336 - if (!program) { 337 - const fragmentShader = compileShader( 338 - gl.FRAGMENT_SHADER, 339 - this.fragmentShaderSource, 340 - keywords 341 - ); 342 - program = createProgram(this.vertexShader, fragmentShader); 343 - this.programs[hash] = program; 344 - } 345 - 346 - if (program === this.activeProgram) return; 347 - 348 - this.uniforms = getUniforms(program); 349 - this.activeProgram = program; 350 - } 351 - 352 - bind() { 353 - gl.useProgram(this.activeProgram); 354 - } 355 - } 356 - 357 - class Program { 358 - uniforms: Record<string, WebGLUniformLocation | null> = {}; 359 - program: WebGLProgram; 360 - 361 - constructor(vertexShader: WebGLShader, fragmentShader: WebGLShader) { 362 - this.program = createProgram(vertexShader, fragmentShader); 363 - this.uniforms = getUniforms(this.program); 364 - } 365 - 366 - bind() { 367 - gl.useProgram(this.program); 368 - } 369 - } 370 - 371 - function createProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader) { 411 + function createWebGLProgram(vertexShader: WebGLShader, fragmentShader: WebGLShader) { 372 412 const program = gl.createProgram()!; 373 413 gl.attachShader(program, vertexShader); 374 414 gl.attachShader(program, fragmentShader); ··· 848 888 let sunrays: FBO; 849 889 let sunraysTemp: FBO; 850 890 851 - const ditheringTexture = createTextureAsync(ditheringTextureUrl); 852 - 853 - const blurProgram = new Program(blurVertexShader, blurShader); 854 - const copyProgram = new Program(baseVertexShader, copyShader); 855 - const clearProgram = new Program(baseVertexShader, clearShader); 856 - const colorProgram = new Program(baseVertexShader, colorShader); 857 - const splatProgram = new Program(baseVertexShader, splatShader); 858 - const advectionProgram = new Program(baseVertexShader, advectionShader); 859 - const divergenceProgram = new Program(baseVertexShader, divergenceShader); 860 - const curlProgram = new Program(baseVertexShader, curlShader); 861 - const vorticityProgram = new Program(baseVertexShader, vorticityShader); 862 - const pressureProgram = new Program(baseVertexShader, pressureShader); 863 - const gradienSubtractProgram = new Program(baseVertexShader, gradientSubtractShader); 864 - const sunraysMaskProgram = new Program(baseVertexShader, sunraysMaskShader); 865 - const sunraysProgram = new Program(baseVertexShader, sunraysShader); 891 + const blurProgram = new Program(gl, blurVertexShader, blurShader, createWebGLProgram, getUniforms); 892 + const copyProgram = new Program(gl, baseVertexShader, copyShader, createWebGLProgram, getUniforms); 893 + const clearProgram = new Program(gl, baseVertexShader, clearShader, createWebGLProgram, getUniforms); 894 + const colorProgram = new Program(gl, baseVertexShader, colorShader, createWebGLProgram, getUniforms); 895 + const splatProgram = new Program(gl, baseVertexShader, splatShader, createWebGLProgram, getUniforms); 896 + const advectionProgram = new Program(gl, baseVertexShader, advectionShader, createWebGLProgram, getUniforms); 897 + const divergenceProgram = new Program(gl, baseVertexShader, divergenceShader, createWebGLProgram, getUniforms); 898 + const curlProgram = new Program(gl, baseVertexShader, curlShader, createWebGLProgram, getUniforms); 899 + const vorticityProgram = new Program(gl, baseVertexShader, vorticityShader, createWebGLProgram, getUniforms); 900 + const pressureProgram = new Program(gl, baseVertexShader, pressureShader, createWebGLProgram, getUniforms); 901 + const gradienSubtractProgram = new Program(gl, baseVertexShader, gradientSubtractShader, createWebGLProgram, getUniforms); 902 + const sunraysMaskProgram = new Program(gl, baseVertexShader, sunraysMaskShader, createWebGLProgram, getUniforms); 903 + const sunraysProgram = new Program(gl, baseVertexShader, sunraysShader, createWebGLProgram, getUniforms); 866 904 867 - const displayMaterial = new Material(baseVertexShader, displayShaderSource); 905 + const displayMaterial = new Material(gl, baseVertexShader, displayShaderSource, compileShader, createWebGLProgram, getUniforms); 868 906 869 907 function getResolution(resolution: number) { 870 908 let aspectRatio = gl.drawingBufferWidth / gl.drawingBufferHeight; ··· 985 1023 target.texelSizeX = 1.0 / w; 986 1024 target.texelSizeY = 1.0 / h; 987 1025 return target; 988 - } 989 - 990 - function createTextureAsync(url: string) { 991 - const texture = gl.createTexture()!; 992 - gl.bindTexture(gl.TEXTURE_2D, texture); 993 - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 994 - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 995 - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); 996 - gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); 997 - gl.texImage2D( 998 - gl.TEXTURE_2D, 999 - 0, 1000 - gl.RGB, 1001 - 1, 1002 - 1, 1003 - 0, 1004 - gl.RGB, 1005 - gl.UNSIGNED_BYTE, 1006 - new Uint8Array([255, 255, 255]) 1007 - ); 1008 - 1009 - const obj = { 1010 - texture, 1011 - width: 1, 1012 - height: 1, 1013 - attach(id: number) { 1014 - gl.activeTexture(gl.TEXTURE0 + id); 1015 - gl.bindTexture(gl.TEXTURE_2D, texture); 1016 - return id; 1017 - } 1018 - }; 1019 - 1020 - const image = new Image(); 1021 - image.onload = () => { 1022 - obj.width = image.width; 1023 - obj.height = image.height; 1024 - gl.bindTexture(gl.TEXTURE_2D, texture); 1025 - gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image); 1026 - }; 1027 - image.src = url; 1028 - 1029 - return obj; 1030 1026 } 1031 1027 1032 1028 function initFramebuffers() { ··· 1450 1446 if (!config.PAUSED) step(dt); 1451 1447 render(null); 1452 1448 animationId = requestAnimationFrame(update); 1453 - } 1454 - 1455 - function hashCode(s: string) { 1456 - if (s.length === 0) return 0; 1457 - let hash = 0; 1458 - for (let i = 0; i < s.length; i++) { 1459 - hash = (hash << 5) - hash + s.charCodeAt(i); 1460 - hash |= 0; 1461 - } 1462 - return hash; 1463 1449 } 1464 1450 1465 1451 function correctDeltaX(delta: number) {
src/lib/cards/FluidTextCard/LDR_LLL1_0.png

This is a binary file and will not be displayed.

+3 -2
src/lib/components/post/embeds/External.svelte
··· 3 3 4 4 const { data }: { data: PostEmbedExternal } = $props(); 5 5 6 + // svelte-ignore state_referenced_locally 6 7 const domain = new URL(data.external.href).hostname.replace('www.', ''); 7 8 </script> 8 9 9 10 <article 10 11 class={[ 11 12 'group dark:bg-base-900 bg-base-200 border-base-300 dark:border-base-600/30 relative isolate flex max-w-md flex-col justify-end overflow-hidden rounded-2xl border p-4', 12 - data.external.thumb ? 'aspect-[16/9]' : '' 13 + data.external.thumb ? 'aspect-video' : '' 13 14 ]} 14 15 > 15 16 {#if data.external.thumb} ··· 20 21 /> 21 22 {/if} 22 23 <div 23 - class="dark:from-base-950/90 dark:via-base-950/40 from-base-50/90 via-base-50/40 absolute inset-0 -z-10 bg-gradient-to-t" 24 + class="dark:from-base-950/90 dark:via-base-950/40 from-base-50/90 via-base-50/40 absolute inset-0 -z-10 bg-linear-to-t" 24 25 ></div> 25 26 26 27 <div