Particles Update
This commit is contained in:
@@ -7,10 +7,10 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
width: 95%;
|
width: 100%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
height: calc(100vh - var(--navbar-height) - 80px);
|
height: calc(100vh - var(--navbar-height) - 65px);
|
||||||
max-height: calc(100vh - var(--navbar-height) - 80px);
|
max-height: calc(100vh - var(--navbar-height) - 65px);
|
||||||
animation: tuiFadeIn 0.4s ease-out;
|
animation: tuiFadeIn 0.4s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,11 +12,23 @@
|
|||||||
.tui-inline-group {
|
.tui-inline-group {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
gap: 0.5rem;
|
gap: 0.5rem;
|
||||||
margin-bottom: 0.2rem;
|
margin-bottom: 0.2rem;
|
||||||
animation: lineSlideIn 0.15s ease-out;
|
animation: lineSlideIn 0.15s ease-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* When inline group contains an image, allow text to wrap beside it */
|
||||||
|
.tui-inline-group:has(.inline-image) {
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tui-inline-group:has(.inline-image) .inline-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.inline-content {
|
.inline-content {
|
||||||
display: inline;
|
display: inline;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
@@ -179,6 +191,14 @@
|
|||||||
padding: 0.5rem 0;
|
padding: 0.5rem 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Inline images */
|
||||||
|
.tui-image.inline-image {
|
||||||
|
display: inline-flex;
|
||||||
|
padding: 0;
|
||||||
|
vertical-align: middle;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
.tui-image img {
|
.tui-image img {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
border: 1px solid var(--terminal-border);
|
border: 1px solid var(--terminal-border);
|
||||||
|
|||||||
@@ -3,10 +3,11 @@
|
|||||||
import { T } from '@threlte/core';
|
import { T } from '@threlte/core';
|
||||||
import ParticleField from './ParticleField.svelte';
|
import ParticleField from './ParticleField.svelte';
|
||||||
import { themeColors, mode } from '$lib/stores/theme';
|
import { themeColors, mode } from '$lib/stores/theme';
|
||||||
import * as THREE from 'three';
|
|
||||||
import '$lib/assets/css/background-3d.css';
|
import '$lib/assets/css/background-3d.css';
|
||||||
|
|
||||||
let bgColor = $derived($mode === 'dark' ? $themeColors.background : $themeColors.background);
|
// Reactive theme colors
|
||||||
|
const bgColor = $derived($themeColors.background);
|
||||||
|
const primaryColor = $derived($themeColors.primary);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="scene-container">
|
<div class="scene-container">
|
||||||
@@ -26,7 +27,7 @@
|
|||||||
<T.DirectionalLight
|
<T.DirectionalLight
|
||||||
position={[5, 5, 5]}
|
position={[5, 5, 5]}
|
||||||
intensity={0.5}
|
intensity={0.5}
|
||||||
color={$themeColors.primary}
|
color={primaryColor}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- Particle system -->
|
<!-- Particle system -->
|
||||||
|
|||||||
@@ -17,6 +17,14 @@
|
|||||||
let velocities: Float32Array;
|
let velocities: Float32Array;
|
||||||
let time = 0;
|
let time = 0;
|
||||||
|
|
||||||
|
// Reactive theme values - use accent or a darker color for light mode visibility
|
||||||
|
const currentMode = $derived($mode);
|
||||||
|
const particleColor = $derived(
|
||||||
|
currentMode === 'dark'
|
||||||
|
? $themeColors.primary
|
||||||
|
: $themeColors.accent || $themeColors.primary
|
||||||
|
);
|
||||||
|
|
||||||
// Create particles
|
// Create particles
|
||||||
function createParticles() {
|
function createParticles() {
|
||||||
positions = new Float32Array(particleCount * 3);
|
positions = new Float32Array(particleCount * 3);
|
||||||
@@ -40,10 +48,11 @@
|
|||||||
|
|
||||||
particlesMaterial = new THREE.PointsMaterial({
|
particlesMaterial = new THREE.PointsMaterial({
|
||||||
size: 0.05,
|
size: 0.05,
|
||||||
|
color: new THREE.Color(particleColor),
|
||||||
sizeAttenuation: true,
|
sizeAttenuation: true,
|
||||||
transparent: true,
|
transparent: true,
|
||||||
opacity: 0.6,
|
opacity: currentMode === 'dark' ? 0.6 : 0.8,
|
||||||
blending: THREE.AdditiveBlending,
|
blending: currentMode === 'dark' ? THREE.AdditiveBlending : THREE.NormalBlending,
|
||||||
depthWrite: false
|
depthWrite: false
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -54,12 +63,13 @@
|
|||||||
createParticles();
|
createParticles();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Update particle color based on theme
|
// Update particle color and opacity based on theme
|
||||||
$effect(() => {
|
$effect(() => {
|
||||||
if (particlesMaterial) {
|
if (particlesMaterial) {
|
||||||
const color = new THREE.Color($themeColors.primary);
|
particlesMaterial.color.set(particleColor);
|
||||||
particlesMaterial.color = color;
|
particlesMaterial.opacity = currentMode === 'dark' ? 0.6 : 0.8;
|
||||||
particlesMaterial.opacity = $mode === 'dark' ? 0.6 : 0.4;
|
particlesMaterial.blending = currentMode === 'dark' ? THREE.AdditiveBlending : THREE.NormalBlending;
|
||||||
|
particlesMaterial.needsUpdate = true;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,7 @@
|
|||||||
{@const line = displayed.parsed.line}
|
{@const line = displayed.parsed.line}
|
||||||
{@const idx = group.indices[j]}
|
{@const idx = group.indices[j]}
|
||||||
{@const visibleSegments = getSegmentsUpToChar(displayed.parsed.segments, displayed.charIndex)}
|
{@const visibleSegments = getSegmentsUpToChar(displayed.parsed.segments, displayed.charIndex)}
|
||||||
|
{@const showImage = displayed.showImage}
|
||||||
{#if line.type === 'button'}
|
{#if line.type === 'button'}
|
||||||
<TuiButton {line} index={idx} selected={selectedIndex === idx} onClick={onButtonClick} onHover={onHoverButton} inline={true} />
|
<TuiButton {line} index={idx} selected={selectedIndex === idx} onClick={onButtonClick} onHover={onHoverButton} inline={true} />
|
||||||
{:else if line.type === 'link'}
|
{:else if line.type === 'link'}
|
||||||
@@ -84,7 +85,14 @@
|
|||||||
<TuiCheckbox {line} inline={true} />
|
<TuiCheckbox {line} inline={true} />
|
||||||
{:else if line.type === 'toggle'}
|
{:else if line.type === 'toggle'}
|
||||||
<TuiToggle {line} inline={true} />
|
<TuiToggle {line} inline={true} />
|
||||||
{:else}
|
{:else if line.type === 'image' && showImage}
|
||||||
|
<div class="tui-image inline-image">
|
||||||
|
<img src={line.image} alt={line.imageAlt || 'Image'} style="max-width: {line.imageWidth || 300}px" />
|
||||||
|
{#if line.content}
|
||||||
|
<span class="image-caption">{line.content}</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
{:else if line.type !== 'image'}
|
||||||
<span class="inline-content {line.type}">
|
<span class="inline-content {line.type}">
|
||||||
{getLinePrefix(line.type)}{#each visibleSegments as segment}{#if segment.icon}<Icon icon={segment.icon} width="14" class="inline-icon" />{:else if getSegmentStyle(segment)}<span style={getSegmentStyle(segment)}>{segment.text}</span>{:else}{segment.text}{/if}{/each}
|
{getLinePrefix(line.type)}{#each visibleSegments as segment}{#if segment.icon}<Icon icon={segment.icon} width="14" class="inline-icon" />{:else if getSegmentStyle(segment)}<span style={getSegmentStyle(segment)}>{segment.text}</span>{:else}{segment.text}{/if}{/each}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ export function registerKeybinds(opts: KeybindsOptions) {
|
|||||||
|
|
||||||
// alt/option + t: toggle mode (hold+digit to pick theme)
|
// alt/option + t: toggle mode (hold+digit to pick theme)
|
||||||
// keydown: start hold
|
// keydown: start hold
|
||||||
hotkeys('alt+t', (e) => {
|
hotkeys('alt+b', (e) => {
|
||||||
if (e.repeat) return;
|
if (e.repeat) return;
|
||||||
tHeld = true;
|
tHeld = true;
|
||||||
tDigitUsed = false;
|
tDigitUsed = false;
|
||||||
@@ -26,7 +26,7 @@ export function registerKeybinds(opts: KeybindsOptions) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// macOS alias: option
|
// macOS alias: option
|
||||||
hotkeys('option+t', (e) => {
|
hotkeys('option+b', (e) => {
|
||||||
if (e.repeat) return;
|
if (e.repeat) return;
|
||||||
tHeld = true;
|
tHeld = true;
|
||||||
tDigitUsed = false;
|
tDigitUsed = false;
|
||||||
@@ -34,7 +34,7 @@ export function registerKeybinds(opts: KeybindsOptions) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// keyup: toggle if no digit used
|
// keyup: toggle if no digit used
|
||||||
hotkeys('alt+t', { keyup: true }, (e) => {
|
hotkeys('alt+b', { keyup: true }, (e) => {
|
||||||
if (!tDigitUsed && tHeld) {
|
if (!tDigitUsed && tHeld) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (now - lastToggleAt > 400) {
|
if (now - lastToggleAt > 400) {
|
||||||
@@ -47,7 +47,7 @@ export function registerKeybinds(opts: KeybindsOptions) {
|
|||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
});
|
});
|
||||||
|
|
||||||
hotkeys('option+t', { keyup: true }, (e) => {
|
hotkeys('option+b', { keyup: true }, (e) => {
|
||||||
if (!tDigitUsed && tHeld) {
|
if (!tDigitUsed && tHeld) {
|
||||||
const now = Date.now();
|
const now = Date.now();
|
||||||
if (now - lastToggleAt > 400) {
|
if (now - lastToggleAt > 400) {
|
||||||
@@ -99,8 +99,8 @@ export function registerKeybinds(opts: KeybindsOptions) {
|
|||||||
|
|
||||||
export function unregisterKeybinds() {
|
export function unregisterKeybinds() {
|
||||||
// Unbind all key patterns we registered
|
// Unbind all key patterns we registered
|
||||||
hotkeys.unbind('alt+t');
|
hotkeys.unbind('alt+b');
|
||||||
hotkeys.unbind('option+t');
|
hotkeys.unbind('option+b');
|
||||||
const tThemeKeys = Array.from({ length: 9 }, (_, i) => `alt+t+${i + 1}`).join(',');
|
const tThemeKeys = Array.from({ length: 9 }, (_, i) => `alt+t+${i + 1}`).join(',');
|
||||||
const tThemeKeysOption = Array.from({ length: 9 }, (_, i) => `option+t+${i + 1}`).join(',');
|
const tThemeKeysOption = Array.from({ length: 9 }, (_, i) => `option+t+${i + 1}`).join(',');
|
||||||
hotkeys.unbind(`${tThemeKeys},${tThemeKeysOption}`);
|
hotkeys.unbind(`${tThemeKeys},${tThemeKeysOption}`);
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export const lines: TerminalLine[] = [
|
|||||||
{ type: 'blank', content: '' },
|
{ type: 'blank', content: '' },
|
||||||
{ type: 'header', content: `Welcome to ${user.displayname}'s Portfolio` },
|
{ type: 'header', content: `Welcome to ${user.displayname}'s Portfolio` },
|
||||||
{ type: 'blank', content: '' },
|
{ type: 'blank', content: '' },
|
||||||
// { type: 'image', content: '', image: user.avatar, imageAlt: user.name, imageWidth: 120, inline: false },
|
{ type: 'image', content: '', image: user.avatar, imageAlt: user.name, imageWidth: 120, inline: true },
|
||||||
{ type: 'output', content: `(&muted)${user.bio}(&)`, inline: true },
|
{ type: 'output', content: `(&muted)${user.bio}(&)`, inline: true },
|
||||||
{ type: 'blank', content: '' },
|
{ type: 'blank', content: '' },
|
||||||
|
|
||||||
@@ -34,9 +34,9 @@ export const lines: TerminalLine[] = [
|
|||||||
{ type: 'blank', content: '' },
|
{ type: 'blank', content: '' },
|
||||||
{ type: 'divider', content: 'Website Keybinds' },
|
{ type: 'divider', content: 'Website Keybinds' },
|
||||||
{ type: 'blank', content: '' },
|
{ type: 'blank', content: '' },
|
||||||
{ type: 'output', content: '(&orange)Toggle Light/Dark Mode(&) (&text, bold)(Alt/Option+T)(&)' , inline: true },
|
{ type: 'output', content: '(&orange)Toggle Light/Dark Mode(&) (&text, bold)(Alt/Option+B)(&)' , inline: true },
|
||||||
{ type: 'output', content: '(&muted)•(&)' , inline: true },
|
{ type: 'output', content: '(&muted)•(&)' , inline: true },
|
||||||
{ type: 'output', content: '(&orange)Select Theme(&) (&text, bold)Use Alt/Option+T + [#](&)' , inline: true },
|
{ type: 'output', content: '(&orange)Select Theme(&) (&text, bold)(Alt/Option+T + [#])(&)' , inline: true },
|
||||||
{ type: 'output', content: '(&muted)•(&)' , inline: true },
|
{ type: 'output', content: '(&muted)•(&)' , inline: true },
|
||||||
{ type: 'output', content: '(&orange)Skip typing animation(&) (&text, bold)(Y)(&)' , inline: true },
|
{ type: 'output', content: '(&orange)Skip typing animation(&) (&text, bold)(Y)(&)' , inline: true },
|
||||||
{ type: 'output', content: '(&muted)•(&)' , inline: true },
|
{ type: 'output', content: '(&muted)•(&)' , inline: true },
|
||||||
|
|||||||
@@ -73,10 +73,10 @@
|
|||||||
color: {$themeColors.text};
|
color: {$themeColors.text};
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<Background3D />
|
|
||||||
<NavbarWaybar />
|
<NavbarWaybar />
|
||||||
<!-- <Navbar /> -->
|
<!-- <Navbar /> -->
|
||||||
<main class="main-content">
|
<main class="main-content">
|
||||||
|
<Background3D />
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user