|
2 | 2 | import type { Project } from "$lib/data/projects"; |
3 | 3 | import { closeProject } from "$lib/stores/terminalStore"; |
4 | 4 | import GlitchText from "../../ui/GlitchText.svelte"; |
5 | | - import { fade } from "svelte/transition"; |
6 | | -
|
7 | 5 | export let project: Project; |
8 | 6 | </script> |
9 | 7 |
|
10 | | -<div transition:fade> |
11 | | - <header class="mb-4 pb-2"> |
| 8 | +<div class="project-view flex flex-col h-full max-h-full min-h-0"> |
| 9 | + <header class="mb-4 pb-2 flex-shrink-0"> |
12 | 10 | <p>> cat {project.title}.{project.extension}</p> |
13 | 11 | <div class="ascii-hr mt-2"></div> |
14 | 12 | </header> |
15 | 13 |
|
16 | | - <div class="grid grid-cols-1 md:grid-cols-[1fr_auto_1fr] md:gap-x-4"> |
17 | | - <div class="visual-container mb-8 md:mb-0"> |
18 | | - {#if project.visual.type === "image"} |
19 | | - <img |
20 | | - src={project.visual.src} |
21 | | - alt={project.title} |
22 | | - class="w-full h-auto border-2 border-ctp-surface1 p-1" /> |
23 | | - {:else if project.visual.type === "video"} |
24 | | - <video |
25 | | - controls |
26 | | - class="w-full h-auto border-2 border-ctp-surface1 p-1"> |
27 | | - <source src={project.visual.src} type="video/mp4" /> |
28 | | - <track |
29 | | - kind="captions" |
30 | | - src={project.visual.src.replace(".mp4", ".vtt")} |
31 | | - srclang="en" |
32 | | - label="English" /> |
33 | | - Your browser does not support the video tag. |
34 | | - </video> |
35 | | - {/if} |
36 | | - </div> |
| 14 | + <main class="flex-grow overflow-auto min-h-0"> |
| 15 | + <div class="grid grid-cols-1 md:grid-cols-[1fr_auto_1fr] md:gap-x-4"> |
| 16 | + <div class="visual-container mb-8 md:mb-0"> |
| 17 | + {#if project.visual && project.visual.src} |
| 18 | + {#if project.visual.type === "image"} |
| 19 | + <img |
| 20 | + src={project.visual.src} |
| 21 | + alt={project.title} |
| 22 | + class="w-full h-auto border-2 border-ctp-surface1 p-1" |
| 23 | + on:error={(e) => { |
| 24 | + ( |
| 25 | + e.currentTarget as HTMLImageElement |
| 26 | + ).style.display = "none"; |
| 27 | + console.warn( |
| 28 | + `Image failed to load: ${project.visual.src}`, |
| 29 | + ); |
| 30 | + }} /> |
| 31 | + {:else if project.visual.type === "video"} |
| 32 | + <video |
| 33 | + controls |
| 34 | + class="w-full h-auto border-2 border-ctp-surface1 p-1" |
| 35 | + on:error={() => |
| 36 | + console.warn( |
| 37 | + `Video failed to load: ${project.visual.src}`, |
| 38 | + )}> |
| 39 | + <source src={project.visual.src} type="video/mp4" /> |
| 40 | + <track |
| 41 | + kind="captions" |
| 42 | + src={project.visual.src.replace(".mp4", ".vtt")} |
| 43 | + srclang="en" |
| 44 | + label="English" /> |
| 45 | + Your browser does not support the video tag. |
| 46 | + </video> |
| 47 | + {:else if project.visual.type === "embed"} |
| 48 | + <div |
| 49 | + class="aspect-video w-full border-2 border-ctp-surface1 p-1"> |
| 50 | + <iframe |
| 51 | + src={project.visual.src} |
| 52 | + class="w-full h-full" |
| 53 | + frameborder="0" |
| 54 | + allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" |
| 55 | + allowfullscreen |
| 56 | + title={project.title} |
| 57 | + on:load={() => |
| 58 | + console.log("Embed iframe loaded")} |
| 59 | + on:error={() => |
| 60 | + console.warn( |
| 61 | + `Embed failed to load: ${project.visual.src}`, |
| 62 | + )}></iframe> |
| 63 | + </div> |
| 64 | + {:else} |
| 65 | + <p class="text-sm italic text-ctp-subtext0"> |
| 66 | + Unknown visual type: {project.visual.type} |
| 67 | + </p> |
| 68 | + {/if} |
| 69 | + {:else} |
| 70 | + <p class="text-sm italic text-ctp-subtext0"> |
| 71 | + No visual available for this project. |
| 72 | + </p> |
| 73 | + {/if} |
| 74 | + </div> |
37 | 75 |
|
38 | | - <div class="hidden md:flex flex-col items-center text-ctp-surface1"> |
39 | | - <span>|</span><span>|</span><span>|</span><span>|</span><span |
40 | | - >|</span |
41 | | - ><span>|</span><span>|</span><span>|</span><span>|</span><span |
42 | | - >|</span |
43 | | - ><span>|</span><span>|</span><span>|</span><span>|</span><span |
44 | | - >|</span> |
45 | | - </div> |
| 76 | + <div class="hidden md:flex flex-col items-center text-ctp-surface1"> |
| 77 | + <span>|</span><span>|</span><span>|</span><span>|</span><span |
| 78 | + >|</span |
| 79 | + ><span>|</span><span>|</span><span>|</span><span>|</span><span |
| 80 | + >|</span |
| 81 | + ><span>|</span><span>|</span><span>|</span><span>|</span><span |
| 82 | + >|</span> |
| 83 | + </div> |
46 | 84 |
|
47 | | - <div class="details-container"> |
48 | | - <h3 class="text-2xl mb-2"> |
49 | | - <GlitchText text={project.title} color="sky" size="2xl" /> |
50 | | - </h3> |
51 | | - <p class="text-ctp-text mb-4 whitespace-pre-wrap"> |
52 | | - {project.description} |
53 | | - </p> |
| 85 | + <div class="details-container"> |
| 86 | + <h3 class="text-2xl mb-2"> |
| 87 | + <GlitchText text={project.title} color="sky" size="2xl" /> |
| 88 | + </h3> |
| 89 | + <p class="text-ctp-text mb-4 whitespace-pre-wrap"> |
| 90 | + {project.description} |
| 91 | + </p> |
| 92 | + </div> |
54 | 93 | </div> |
55 | | - </div> |
| 94 | + </main> |
56 | 95 |
|
57 | | - <footer class="mt-8 pt-4"> |
58 | | - <div class="ascii-hr"></div> |
| 96 | + <footer class="mt-4 pt-4 flex-shrink-0 border-t border-ctp-surface1"> |
59 | 97 | <button |
60 | 98 | on:click={closeProject} |
61 | 99 | class="text-ctp-red hover:bg-ctp-red hover:text-ctp-base p-1" |
62 | 100 | >[EXIT]</button> |
63 | 101 | </footer> |
64 | 102 | </div> |
| 103 | + |
| 104 | +<style> |
| 105 | + /* Ensure the project-view container fills parent and sets up flex layout */ |
| 106 | + .project-view { |
| 107 | + /* height handled by parent container (set h-full on parent) */ |
| 108 | + display: flex; |
| 109 | + flex-direction: column; |
| 110 | + /* min-h-0 required to allow flex children with overflow to shrink */ |
| 111 | + min-height: 0; |
| 112 | + max-height: 100%; |
| 113 | + } |
| 114 | +
|
| 115 | + main { |
| 116 | + /* This allows main to grow and scroll inside flex container */ |
| 117 | + flex-grow: 1; |
| 118 | + overflow-y: auto; |
| 119 | + min-height: 0; /* essential for correct overflow inside flex */ |
| 120 | + } |
| 121 | +
|
| 122 | + footer { |
| 123 | + /* fixed height footer, flex-shrink: 0 keeps it visible */ |
| 124 | + flex-shrink: 0; |
| 125 | + border-top: 1px solid var(--ctp-surface1); |
| 126 | + } |
| 127 | +</style> |
0 commit comments