Website Redesign 7

This commit is contained in:
2025-11-28 02:40:12 +00:00
parent 9b9a201c3e
commit 96e2d0650c
72 changed files with 7504 additions and 1231 deletions
+194 -55
View File
@@ -1,55 +1,194 @@
<svelte:head>
<script type="module" src="https://unpkg.com/@google/model-viewer/dist/model-viewer.min.js"></script>
</svelte:head>
<script lang="ts">
// @ts-nocheck
import { page } from '$app/stores';
import { onMount } from "svelte";
import { getModalStore } from '@skeletonlabs/skeleton';
const modalStore = getModalStore();
let container, row, modelViewer, modelViewerTitle, modelViewerBtn;
// const modal: ModalSettings = {
// type: 'alert',
// // Data
// title: 'Example Alert',
// body: 'This is an example modal.',
// image: 'https://i.imgur.com/WOgTG96.gif',
// };
// modalStore.trigger(modal);
onMount(() => {
modelViewerTitle = document.querySelector("#model-viewer-bts-title");
for(let i = 0; i < $page.data.files.length; i++) {
console.log($page.data.files[i]);
}
});
function download() {
modelViewerBtn.disabled = true;
modelViewerBtn.innerHTML = "Downloading...";
modelViewer.exportScene("glb").then((blob) => {
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = modelViewerTitle.innerText + ".glb";
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
modelViewerBtn.disabled = false;
modelViewerBtn.innerHTML = "Download";
});
}
</script>
<section class="h-full mx-auto justify-center p-10">
</section>
<script lang="ts">
import TerminalTUI from '$lib/components/TerminalTUI.svelte';
import type { TerminalLine } from '$lib/components/tui/types';
import ModelViewer from '$lib/components/ModelViewer.svelte';
import { user } from '$lib/config';
import { getPageSpeedMultiplier } from '$lib';
import { themeColors } from '$lib/stores/theme';
import Icon from '@iconify/svelte';
const speed = getPageSpeedMultiplier('models');
// Available GLB files in static/models/
const glbModels = [
{ name: 'Bake Potato', path: '/models/BakePotato.glb' },
{ name: 'Soda Can', path: '/models/Soda.glb' }
];
let selectedModel = $state(glbModels[0]);
let showViewer = $state(false);
function selectModel(model: typeof glbModels[0]) {
selectedModel = model;
showViewer = true;
}
// Build the terminal lines for the models page
const lines: TerminalLine[] = [
{ type: 'command', content: 'ls ~/3d-models/' },
{ type: 'blank', content: '' },
{ type: 'header', content: '(&primary,bold)3D Models(&)' },
{ type: 'output', content: `(&muted)A collection of 3D models created for games, visualization, and fun.(&)` },
{ type: 'blank', content: '' },
{ type: 'divider', content: 'VIEWER' },
{ type: 'blank', content: '' },
// Interactive model buttons
...glbModels.map(model => ({
type: 'button' as const,
content: model.name,
icon: 'mdi:cube-scan',
style: 'primary' as const,
action: () => selectModel(model)
})),
{ type: 'blank', content: '' },
{ type: 'success', content: `(&success)${glbModels.length} models available(&)` }
];
</script>
<svelte:head>
<title>3D Models | {user.name}</title>
<meta name="description" content="3D Models portfolio - Characters, Environments, Props and more" />
</svelte:head>
<div class="models-page">
<div
class="models-layout"
class:viewer-open={showViewer}
style="--theme-bg: {$themeColors.background};"
>
<!-- Terminal Section -->
<div class="terminal-section">
<TerminalTUI {lines} title="~/3d-models" interactive={true} {speed} />
</div>
<!-- 3D Viewer Section -->
{#if showViewer}
<div class="viewer-section">
<div class="viewer-tabs">
{#each glbModels as model}
<button
class="viewer-tab"
class:active={selectedModel.path === model.path}
onclick={() => selectModel(model)}
>
<Icon icon="mdi:cube-outline" width="14" />
{model.name}
</button>
{/each}
<button class="viewer-tab close" onclick={() => showViewer = false}>
<Icon icon="mdi:close" width="14" />
</button>
</div>
<ModelViewer
modelPath={selectedModel.path}
modelName={selectedModel.name}
class="model-viewer-container"
/>
</div>
{/if}
</div>
</div>
<style>
.models-page {
min-height: calc(100vh - 60px);
padding: 2rem 1rem;
}
.models-layout {
display: flex;
gap: 1.5rem;
max-width: 1600px;
margin: 0 auto;
transition: all 0.3s ease;
}
.terminal-section {
flex: 1;
min-width: 0;
}
.viewer-section {
width: 500px;
flex-shrink: 0;
display: flex;
flex-direction: column;
gap: 0;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateX(20px);
}
to {
opacity: 1;
transform: translateX(0);
}
}
.viewer-tabs {
display: flex;
background: var(--theme-bg);
border-radius: 8px 8px 0 0;
overflow: hidden;
}
.viewer-tab {
display: flex;
align-items: center;
gap: 0.35rem;
padding: 0.5rem 1rem;
background: transparent;
border: none;
color: inherit;
font-family: 'JetBrains Mono', monospace;
font-size: 0.8rem;
cursor: pointer;
opacity: 0.6;
transition: all 0.2s ease;
}
.viewer-tab:hover {
opacity: 0.9;
background: rgba(255, 255, 255, 0.05);
}
.viewer-tab.active {
opacity: 1;
background: rgba(255, 255, 255, 0.1);
}
.viewer-tab.close {
margin-left: auto;
opacity: 0.5;
}
.viewer-tab.close:hover {
opacity: 1;
color: #f38ba8;
}
:global(.model-viewer-container) {
flex: 1;
min-height: 400px;
border-radius: 0 0 8px 8px;
}
@media (max-width: 1024px) {
.models-layout {
flex-direction: column;
}
.viewer-section {
width: 100%;
order: -1;
}
}
</style>