Theme Rendering Fixes and Keybinds Update
This commit is contained in:
@@ -28,6 +28,7 @@
|
|||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
|
"hotkeys-js": "^4.0.0-beta.7",
|
||||||
"three": "^0.181.2"
|
"three": "^0.181.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -208,7 +208,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: calc(100% + 0.5rem);
|
top: calc(100% + 0.5rem);
|
||||||
right: 0;
|
right: 0;
|
||||||
min-width: 175px;
|
min-width: 200px;
|
||||||
background: var(--bar-bg);
|
background: var(--bar-bg);
|
||||||
border: 1px solid var(--bar-border);
|
border: 1px solid var(--bar-border);
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
|
|||||||
@@ -201,14 +201,16 @@
|
|||||||
transition:fly={{ y: -10, duration: 150 }}
|
transition:fly={{ y: -10, duration: 150 }}
|
||||||
>
|
>
|
||||||
<div class="dropdown-header">Theme</div>
|
<div class="dropdown-header">Theme</div>
|
||||||
{#each themeOptions as option}
|
{#each themeOptions as option, i}
|
||||||
<button
|
<button
|
||||||
class="theme-option"
|
class="theme-option"
|
||||||
class:active={$colorTheme === option.value}
|
class:active={$colorTheme === option.value}
|
||||||
onclick={() => handleThemeSelect(option.value)}
|
onclick={() => handleThemeSelect(option.value)}
|
||||||
>
|
>
|
||||||
<Icon icon={getThemeIcon(option.value)} width="16" />
|
<Icon icon={getThemeIcon(option.value)} width="16" />
|
||||||
<span>{option.label}</span>
|
<span class="theme-label">
|
||||||
|
{option.label} <span class="theme-number">[{i+1}]</span>
|
||||||
|
</span>
|
||||||
{#if $colorTheme === option.value}
|
{#if $colorTheme === option.value}
|
||||||
<Icon icon="mdi:check" width="14" class="check" />
|
<Icon icon="mdi:check" width="14" class="check" />
|
||||||
{/if}
|
{/if}
|
||||||
@@ -218,7 +220,7 @@
|
|||||||
<div class="dropdown-header">Mode</div>
|
<div class="dropdown-header">Mode</div>
|
||||||
<button class="theme-option" onclick={toggleMode}>
|
<button class="theme-option" onclick={toggleMode}>
|
||||||
<Icon icon={$mode === 'dark' ? 'mdi:weather-sunny' : 'mdi:weather-night'} width="16" />
|
<Icon icon={$mode === 'dark' ? 'mdi:weather-sunny' : 'mdi:weather-night'} width="16" />
|
||||||
<span>{$mode === 'dark' ? 'Light Mode' : 'Dark Mode'} (T)</span>
|
<span>{$mode === 'dark' ? 'Light Mode' : 'Dark Mode'} [T]</span>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
@@ -313,14 +315,15 @@
|
|||||||
<div class="mobile-theme-section">
|
<div class="mobile-theme-section">
|
||||||
<div class="mobile-section-header">Theme</div>
|
<div class="mobile-section-header">Theme</div>
|
||||||
<div class="mobile-theme-options">
|
<div class="mobile-theme-options">
|
||||||
{#each themeOptions as option}
|
{#each themeOptions as option, i}
|
||||||
<button
|
<button
|
||||||
class="mobile-theme-btn"
|
class="mobile-theme-btn"
|
||||||
class:active={$colorTheme === option.value}
|
class:active={$colorTheme === option.value}
|
||||||
onclick={() => { handleThemeSelect(option.value); closeMobileMenu(); }}
|
onclick={() => { handleThemeSelect(option.value); closeMobileMenu(); }}
|
||||||
|
title={`Press T+${i+1} to switch to ${option.label}`}
|
||||||
>
|
>
|
||||||
<Icon icon={getThemeIcon(option.value)} width="18" />
|
<Icon icon={getThemeIcon(option.value)} width="18" />
|
||||||
<span>{option.label}</span>
|
<span class="theme-label"><span class="theme-number">{i+1}.</span> {option.label}</span>
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -61,6 +61,31 @@
|
|||||||
let terminalElement: HTMLDivElement;
|
let terminalElement: HTMLDivElement;
|
||||||
let bodyElement = $state<HTMLDivElement>();
|
let bodyElement = $state<HTMLDivElement>();
|
||||||
|
|
||||||
|
// Track colorMap identity to detect theme/mode changes
|
||||||
|
let lastColorMapId = $state('');
|
||||||
|
|
||||||
|
// When colorMap changes (theme/mode toggle), update displayedLines with new parsed segments
|
||||||
|
$effect(() => {
|
||||||
|
// Create a simple identity string from a few colorMap values
|
||||||
|
const colorMapId = `${colorMap.red}-${colorMap.text}-${colorMap.primary}`;
|
||||||
|
|
||||||
|
// Only update if colorMap actually changed and animation is complete
|
||||||
|
if (colorMapId !== lastColorMapId && isComplete && displayedLines.length > 0) {
|
||||||
|
// Store current showImage states before updating
|
||||||
|
const showImageStates = displayedLines.map(d => d.showImage);
|
||||||
|
|
||||||
|
// Update with new parsed content
|
||||||
|
displayedLines = parsedLines.map((parsed, i) => ({
|
||||||
|
parsed,
|
||||||
|
charIndex: parsed.plainText.length,
|
||||||
|
complete: true,
|
||||||
|
showImage: showImageStates[i] ?? (parsed.line.type === 'image')
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
lastColorMapId = colorMapId;
|
||||||
|
});
|
||||||
|
|
||||||
// Autoscroll to bottom (respects autoscroll prop)
|
// Autoscroll to bottom (respects autoscroll prop)
|
||||||
function scrollToBottom() {
|
function scrollToBottom() {
|
||||||
if (!autoscroll || !bodyElement) return;
|
if (!autoscroll || !bodyElement) return;
|
||||||
@@ -251,12 +276,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function handleKeydown(event: KeyboardEvent) {
|
function handleKeydown(event: KeyboardEvent) {
|
||||||
// Toggle theme with T key
|
|
||||||
if (event.key === 't' || event.key === 'T') {
|
|
||||||
event.preventDefault();
|
|
||||||
toggleMode();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (isTyping && (event.key === 'y' || event.key === 'Y')) {
|
if (isTyping && (event.key === 'y' || event.key === 'Y')) {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
skipAnimation();
|
skipAnimation();
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export interface TextSegment {
|
|||||||
// Color maps for each theme
|
// Color maps for each theme
|
||||||
export type ThemeColorMap = Record<string, string>;
|
export type ThemeColorMap = Record<string, string>;
|
||||||
|
|
||||||
// Default color map (fallback) - includes theme colors for backwards compatibility
|
// Default color map (fallback) - hex values as defaults, overwritten by theme colorMap at runtime
|
||||||
export const defaultColorMap: ThemeColorMap = {
|
export const defaultColorMap: ThemeColorMap = {
|
||||||
// Basic colors
|
// Basic colors
|
||||||
'red': '#f38ba8',
|
'red': '#f38ba8',
|
||||||
@@ -36,7 +36,7 @@ export const defaultColorMap: ThemeColorMap = {
|
|||||||
'pink': '#f5c2e7',
|
'pink': '#f5c2e7',
|
||||||
'black': '#1e1e2e',
|
'black': '#1e1e2e',
|
||||||
'surface': '#313244',
|
'surface': '#313244',
|
||||||
// Catppuccin extended colors
|
// Extended colors
|
||||||
'teal': '#94e2d5',
|
'teal': '#94e2d5',
|
||||||
'sky': '#89dceb',
|
'sky': '#89dceb',
|
||||||
'sapphire': '#74c7ec',
|
'sapphire': '#74c7ec',
|
||||||
@@ -47,14 +47,14 @@ export const defaultColorMap: ThemeColorMap = {
|
|||||||
'flamingo': '#f2cdcd',
|
'flamingo': '#f2cdcd',
|
||||||
'rosewater': '#f5e0dc',
|
'rosewater': '#f5e0dc',
|
||||||
// Semantic colors
|
// Semantic colors
|
||||||
'primary': 'var(--terminal-primary)',
|
'primary': '#cba6f7',
|
||||||
'accent': 'var(--terminal-accent)',
|
'accent': '#a6e3a1',
|
||||||
'muted': 'var(--terminal-muted)',
|
'muted': '#6c7086',
|
||||||
'error': '#f38ba8',
|
'error': '#f38ba8',
|
||||||
'success': '#a6e3a1',
|
'success': '#a6e3a1',
|
||||||
'warning': '#f9e2af',
|
'warning': '#f9e2af',
|
||||||
'info': '#89b4fa',
|
'info': '#89b4fa',
|
||||||
// Theme colors (for using text, textMuted, etc. in formatter)
|
// Theme colors
|
||||||
'text': '#cdd6f4',
|
'text': '#cdd6f4',
|
||||||
'textMuted': '#a6adc8',
|
'textMuted': '#a6adc8',
|
||||||
'background': '#1e1e2e',
|
'background': '#1e1e2e',
|
||||||
|
|||||||
113
src/lib/keybinds.ts
Normal file
113
src/lib/keybinds.ts
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
import hotkeys from 'hotkeys-js';
|
||||||
|
|
||||||
|
export interface KeybindsOptions {
|
||||||
|
themeOptions: Array<{ value: string }>;
|
||||||
|
setColorTheme: (t: any) => void;
|
||||||
|
toggleMode: () => void;
|
||||||
|
navigation: Array<{ path: string; external?: boolean }>;
|
||||||
|
goto?: (path: string) => void;
|
||||||
|
browser: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
let lastToggleAt = 0;
|
||||||
|
let tHeld = false;
|
||||||
|
let tDigitUsed = false;
|
||||||
|
|
||||||
|
export function registerKeybinds(opts: KeybindsOptions) {
|
||||||
|
const { themeOptions, setColorTheme, toggleMode, navigation, goto, browser } = opts;
|
||||||
|
|
||||||
|
// alt/option + t: toggle mode (hold+digit to pick theme)
|
||||||
|
// keydown: start hold
|
||||||
|
hotkeys('alt+t', (e) => {
|
||||||
|
if (e.repeat) return;
|
||||||
|
tHeld = true;
|
||||||
|
tDigitUsed = false;
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
// macOS alias: option
|
||||||
|
hotkeys('option+t', (e) => {
|
||||||
|
if (e.repeat) return;
|
||||||
|
tHeld = true;
|
||||||
|
tDigitUsed = false;
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
// keyup: toggle if no digit used
|
||||||
|
hotkeys('alt+t', { keyup: true }, (e) => {
|
||||||
|
if (!tDigitUsed && tHeld) {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastToggleAt > 400) {
|
||||||
|
toggleMode();
|
||||||
|
lastToggleAt = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tHeld = false;
|
||||||
|
tDigitUsed = false;
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
hotkeys('option+t', { keyup: true }, (e) => {
|
||||||
|
if (!tDigitUsed && tHeld) {
|
||||||
|
const now = Date.now();
|
||||||
|
if (now - lastToggleAt > 400) {
|
||||||
|
toggleMode();
|
||||||
|
lastToggleAt = now;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tHeld = false;
|
||||||
|
tDigitUsed = false;
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
// alt/option + t + N => set theme
|
||||||
|
const tThemeKeysAlt = Array.from({ length: 9 }, (_, i) => `alt+t+${i + 1}`).join(',');
|
||||||
|
const tThemeKeysOption = Array.from({ length: 9 }, (_, i) => `option+t+${i + 1}`).join(',');
|
||||||
|
hotkeys(`${tThemeKeysAlt},${tThemeKeysOption}`, (e, handler) => {
|
||||||
|
const m = handler.key.match(/(?:alt|option)\+t\+(\d)/);
|
||||||
|
if (!m) return;
|
||||||
|
const idx = parseInt(m[1], 10) - 1;
|
||||||
|
if (themeOptions[idx]) {
|
||||||
|
setColorTheme(themeOptions[idx].value);
|
||||||
|
tDigitUsed = true;
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// ctrl/cmd + N => navigate. Include numpad variants. Command (cmd) aliases on mac
|
||||||
|
const ctrlKeys = Array.from({ length: 9 }, (_, i) => `ctrl+${i + 1}`).join(',');
|
||||||
|
const ctrlNumKeys = Array.from({ length: 9 }, (_, i) => `ctrl+num_${i + 1}`).join(',');
|
||||||
|
const cmdKeys = Array.from({ length: 9 }, (_, i) => `cmd+${i + 1}`).join(',');
|
||||||
|
const cmdNumKeys = Array.from({ length: 9 }, (_, i) => `cmd+num_${i + 1}`).join(',');
|
||||||
|
|
||||||
|
hotkeys(`${ctrlKeys},${ctrlNumKeys},${cmdKeys},${cmdNumKeys}`, (e, handler) => {
|
||||||
|
const digitMatch = handler.key.match(/(\d)/);
|
||||||
|
if (!digitMatch) return;
|
||||||
|
const digit = parseInt(digitMatch[1], 10);
|
||||||
|
if (isNaN(digit)) return;
|
||||||
|
const idx = digit - 1;
|
||||||
|
if (navigation && navigation[idx]) {
|
||||||
|
const { path, external } = navigation[idx] as any;
|
||||||
|
if (browser) {
|
||||||
|
if (external) window.location.href = path;
|
||||||
|
else if (typeof goto === 'function') goto?.(path);
|
||||||
|
}
|
||||||
|
e.preventDefault();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function unregisterKeybinds() {
|
||||||
|
// Unbind all key patterns we registered
|
||||||
|
hotkeys.unbind('alt+t');
|
||||||
|
hotkeys.unbind('option+t');
|
||||||
|
const tThemeKeys = Array.from({ length: 9 }, (_, i) => `alt+t+${i + 1}`).join(',');
|
||||||
|
const tThemeKeysOption = Array.from({ length: 9 }, (_, i) => `option+t+${i + 1}`).join(',');
|
||||||
|
hotkeys.unbind(`${tThemeKeys},${tThemeKeysOption}`);
|
||||||
|
|
||||||
|
const ctrlKeys = Array.from({ length: 9 }, (_, i) => `ctrl+${i + 1}`).join(',');
|
||||||
|
const ctrlNumKeys = Array.from({ length: 9 }, (_, i) => `ctrl+num_${i + 1}`).join(',');
|
||||||
|
const cmdKeys = Array.from({ length: 9 }, (_, i) => `cmd+${i + 1}`).join(',');
|
||||||
|
const cmdNumKeys = Array.from({ length: 9 }, (_, i) => `cmd+num_${i + 1}`).join(',');
|
||||||
|
hotkeys.unbind(`${ctrlKeys},${ctrlNumKeys},${cmdKeys},${cmdNumKeys}`);
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { TerminalLine } from "$lib/components/tui/types";
|
import type { TerminalLine } from "$lib/components/tui/types";
|
||||||
import { user } from "$lib/config";
|
import { user } from "$lib/config";
|
||||||
import { navigation } from "$lib/config";
|
import { navigation } from "$lib/config";
|
||||||
|
import { themeOptions } from "$lib/stores/theme";
|
||||||
|
|
||||||
export const lines: TerminalLine[] = [
|
export const lines: TerminalLine[] = [
|
||||||
// neofetch style intro
|
// neofetch style intro
|
||||||
@@ -14,7 +15,8 @@ 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: 'output', content: `(&muted)${user.bio}(&)` },
|
// { type: 'image', content: '', image: user.avatar, imageAlt: user.name, imageWidth: 120, inline: false },
|
||||||
|
{ type: 'output', content: `(&muted)${user.bio}(&)`, inline: true },
|
||||||
{ type: 'blank', content: '' },
|
{ type: 'blank', content: '' },
|
||||||
|
|
||||||
{ type: 'divider', content: 'NAVIGATION' },
|
{ type: 'divider', content: 'NAVIGATION' },
|
||||||
@@ -32,10 +34,16 @@ 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)(T)(&)' , inline: true },
|
{ type: 'output', content: '(&orange)Toggle Light/Dark Mode(&) (&text, bold)(Alt/Option+T)(&)' , 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: '(&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 },
|
||||||
{ type: 'output', content: '(&orange)Open/Close Navbar(&) (&text, bold)(N)(&)' , inline: true },
|
{ type: 'output', content: '(&orange)Navigate to page(&) (&text, bold)(Ctrl/Cmd+ [#])(&)' , inline: true },
|
||||||
|
{ type: 'blank', content: '' },
|
||||||
|
// list navigation with numeric shortcuts
|
||||||
|
{ type: 'output', content: '(&text, bold)Pages -(&)', inline: true },
|
||||||
|
...navigation.map((nav, i) => ({ type: 'output' as const, content: `(&blue)${nav.name}(&) (&text, bold)[${i+1}](&)`, inline: true })),
|
||||||
{ type: 'blank', content: '' },
|
{ type: 'blank', content: '' },
|
||||||
];
|
];
|
||||||
@@ -48,30 +48,27 @@ const themes: Record<ColorTheme, ThemeJson> = {
|
|||||||
// Export themes for external access
|
// Export themes for external access
|
||||||
export { themes };
|
export { themes };
|
||||||
|
|
||||||
// CSS variable mappings for theme colors - these update dynamically with mode changes
|
|
||||||
const themeColorVars: Record<string, string> = {
|
|
||||||
'primary': 'var(--terminal-primary)',
|
|
||||||
'secondary': 'var(--terminal-secondary)',
|
|
||||||
'accent': 'var(--terminal-accent)',
|
|
||||||
'background': 'var(--terminal-bg)',
|
|
||||||
'backgroundLight': 'var(--terminal-bg-light)',
|
|
||||||
'text': 'var(--terminal-text)',
|
|
||||||
'textMuted': 'var(--terminal-muted)',
|
|
||||||
'border': 'var(--terminal-border)',
|
|
||||||
'terminal': 'var(--terminal-bg)',
|
|
||||||
'terminalPrompt': 'var(--terminal-prompt)',
|
|
||||||
'terminalUser': 'var(--terminal-user)',
|
|
||||||
'terminalPath': 'var(--terminal-path)',
|
|
||||||
};
|
|
||||||
|
|
||||||
// Build theme colors from JSON - merges colors into colorMap so formatter can use both
|
// Build theme colors from JSON - merges colors into colorMap so formatter can use both
|
||||||
function buildThemeColors(theme: ThemeJson, mode: Mode): ThemeColors {
|
function buildThemeColors(theme: ThemeJson, mode: Mode): ThemeColors {
|
||||||
const modeData = theme[mode];
|
const modeData = theme[mode];
|
||||||
// Merge colors and colorMap - use CSS variables for theme colors so they update dynamically
|
// Merge: defaults -> theme palette -> theme colors (so text, primary, etc. are correct hex values)
|
||||||
const mergedColorMap: ThemeColorMap = {
|
const mergedColorMap: ThemeColorMap = {
|
||||||
...defaultColorMap, // Fallback defaults
|
...defaultColorMap, // Fallback defaults (hex values)
|
||||||
...(modeData.colorMap ?? {}), // Theme's color palette
|
...(modeData.colorMap ?? {}), // Theme's color palette (hex values)
|
||||||
...themeColorVars // Theme colors as CSS variables - update with mode changes
|
// Also add theme colors so (&text), (&primary), etc. resolve to correct hex for this mode
|
||||||
|
'text': modeData.colors.text,
|
||||||
|
'textMuted': modeData.colors.textMuted,
|
||||||
|
'primary': modeData.colors.primary,
|
||||||
|
'secondary': modeData.colors.secondary,
|
||||||
|
'accent': modeData.colors.accent,
|
||||||
|
'background': modeData.colors.background,
|
||||||
|
'backgroundLight': modeData.colors.backgroundLight,
|
||||||
|
'border': modeData.colors.border,
|
||||||
|
'terminal': modeData.colors.terminal,
|
||||||
|
'terminalPrompt': modeData.colors.terminalPrompt,
|
||||||
|
'terminalUser': modeData.colors.terminalUser,
|
||||||
|
'terminalPath': modeData.colors.terminalPath,
|
||||||
|
'muted': modeData.colors.textMuted, // alias
|
||||||
};
|
};
|
||||||
return {
|
return {
|
||||||
primary: modeData.colors.primary,
|
primary: modeData.colors.primary,
|
||||||
|
|||||||
@@ -1,17 +1,19 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import './layout.css';
|
import './layout.css';
|
||||||
import Navbar from '$lib/components/Navbar.svelte';
|
|
||||||
import Background3D from '$lib/components/Background3D.svelte';
|
import Background3D from '$lib/components/Background3D.svelte';
|
||||||
import { themeColors, mode, colorTheme } from '$lib/stores/theme';
|
import { themeColors, mode, colorTheme, toggleMode, setColorTheme, themeOptions } from '$lib/stores/theme';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { navigation } from '$lib/config';
|
||||||
import { browser } from '$app/environment';
|
import { browser } from '$app/environment';
|
||||||
import { onMount } from 'svelte';
|
import { onMount, onDestroy } from 'svelte';
|
||||||
|
import { registerKeybinds, unregisterKeybinds } from '$lib/keybinds';
|
||||||
import NavbarWaybar from '$lib/components/NavbarWaybar.svelte';
|
import NavbarWaybar from '$lib/components/NavbarWaybar.svelte';
|
||||||
|
|
||||||
let { children } = $props();
|
let { children } = $props();
|
||||||
let mounted = $state(false);
|
let mounted = $state(false);
|
||||||
// don't bind the component; find the DOM element by its class
|
|
||||||
|
|
||||||
function updateNavbarHeight() {
|
function updateNavbarHeight() {
|
||||||
|
if (!browser) return;
|
||||||
const elem = document.querySelector('.navbar') as HTMLElement | null;
|
const elem = document.querySelector('.navbar') as HTMLElement | null;
|
||||||
if (!elem) return;
|
if (!elem) return;
|
||||||
const rect = elem.getBoundingClientRect();
|
const rect = elem.getBoundingClientRect();
|
||||||
@@ -28,6 +30,24 @@
|
|||||||
// Update CSS var with actual navbar height
|
// Update CSS var with actual navbar height
|
||||||
updateNavbarHeight();
|
updateNavbarHeight();
|
||||||
window.addEventListener('resize', updateNavbarHeight);
|
window.addEventListener('resize', updateNavbarHeight);
|
||||||
|
|
||||||
|
// Register centralized keybinds
|
||||||
|
registerKeybinds({
|
||||||
|
themeOptions: themeOptions,
|
||||||
|
setColorTheme: setColorTheme,
|
||||||
|
toggleMode: toggleMode,
|
||||||
|
navigation: navigation,
|
||||||
|
goto: goto,
|
||||||
|
browser: browser
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
onDestroy(() => {
|
||||||
|
if (browser) {
|
||||||
|
window.removeEventListener('resize', updateNavbarHeight);
|
||||||
|
// Unregister centralized keybinds
|
||||||
|
unregisterKeybinds();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$effect(() => updateNavbarHeight());
|
$effect(() => updateNavbarHeight());
|
||||||
|
|||||||
Reference in New Issue
Block a user