Styles and About Page Fixes
This commit is contained in:
@@ -20,7 +20,7 @@
|
|||||||
"svelte-check": "^4.3.4",
|
"svelte-check": "^4.3.4",
|
||||||
"tailwindcss": "^4.1.17",
|
"tailwindcss": "^4.1.17",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.2.4"
|
"vite": "^7.2.6"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify/svelte": "^5.1.0",
|
"@iconify/svelte": "^5.1.0",
|
||||||
@@ -31,7 +31,7 @@
|
|||||||
"dotenv": "^17.2.3",
|
"dotenv": "^17.2.3",
|
||||||
"express": "^5.1.0",
|
"express": "^5.1.0",
|
||||||
"hotkeys-js": "^4.0.0-beta.7",
|
"hotkeys-js": "^4.0.0-beta.7",
|
||||||
"three": "^0.181.2",
|
"play-dl": "^1.9.7",
|
||||||
"ytpl": "^2.3.0"
|
"three": "^0.181.2"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tui-line.blank {
|
.tui-line.blank {
|
||||||
min-height: 0.5em;
|
min-height: 0.15em;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes lineSlideIn {
|
@keyframes lineSlideIn {
|
||||||
@@ -73,6 +73,7 @@
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateX(-5px);
|
transform: translateX(-5px);
|
||||||
}
|
}
|
||||||
|
|
||||||
to {
|
to {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transform: translateX(0);
|
transform: translateX(0);
|
||||||
@@ -173,12 +174,10 @@
|
|||||||
.divider-line {
|
.divider-line {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background: linear-gradient(
|
background: linear-gradient(to right,
|
||||||
to right,
|
transparent,
|
||||||
transparent,
|
var(--terminal-border),
|
||||||
var(--terminal-border),
|
transparent);
|
||||||
transparent
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.divider-text {
|
.divider-text {
|
||||||
@@ -223,15 +222,26 @@
|
|||||||
width: 0.5em;
|
width: 0.5em;
|
||||||
height: 1.1em;
|
height: 1.1em;
|
||||||
background: var(--terminal-primary);
|
background: var(--terminal-primary);
|
||||||
animation: cursorBlink 1s step-end infinite;
|
background: var(--terminal-primary);
|
||||||
margin-left: 2px;
|
margin-left: 2px;
|
||||||
vertical-align: baseline;
|
vertical-align: baseline;
|
||||||
transform: translateY(0.15em);
|
transform: translateY(0.15em);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cursor.blink {
|
||||||
|
animation: cursorBlink 1s step-end infinite;
|
||||||
|
}
|
||||||
|
|
||||||
@keyframes cursorBlink {
|
@keyframes cursorBlink {
|
||||||
0%, 100% { opacity: 1; }
|
|
||||||
50% { opacity: 0; }
|
0%,
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scrollbar */
|
/* Scrollbar */
|
||||||
@@ -265,6 +275,15 @@
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Keep inline flow for specific containers */
|
||||||
|
.tui-card .tui-inline-group,
|
||||||
|
.tui-group .tui-inline-group,
|
||||||
|
.tui-accordion .tui-inline-group {
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
.tui-inline-group .inline-image img {
|
.tui-inline-group .inline-image img {
|
||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
@@ -280,4 +299,4 @@
|
|||||||
max-width: 100% !important;
|
max-width: 100% !important;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -137,7 +137,7 @@
|
|||||||
class="symbol">$</span
|
class="symbol">$</span
|
||||||
>
|
>
|
||||||
</span>
|
</span>
|
||||||
<span class="cursor"></span>
|
<span class="cursor blink"></span>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
getSegmentStyle,
|
getSegmentStyle,
|
||||||
parseDimension,
|
parseDimension,
|
||||||
} from "./utils";
|
} from "./utils";
|
||||||
import type { CardLine } from "./types";
|
import type { CardLine, TerminalLine } from "./types";
|
||||||
import TuiLine from "./TuiLine.svelte";
|
import TuiLine from "./TuiLine.svelte";
|
||||||
import "$lib/assets/css/tui-card.css";
|
import "$lib/assets/css/tui-card.css";
|
||||||
|
|
||||||
@@ -46,6 +46,43 @@
|
|||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.join("; "),
|
.join("; "),
|
||||||
);
|
);
|
||||||
|
const processedChildren = $derived.by(() => {
|
||||||
|
if (!line.children) return [];
|
||||||
|
const groups: Array<
|
||||||
|
| { kind: "single"; index: number; line: TerminalLine }
|
||||||
|
| {
|
||||||
|
kind: "inline";
|
||||||
|
items: Array<{ index: number; line: TerminalLine }>;
|
||||||
|
}
|
||||||
|
> = [];
|
||||||
|
let i = 0;
|
||||||
|
|
||||||
|
while (i < line.children.length) {
|
||||||
|
const child = line.children[i];
|
||||||
|
const isInline = child.inline || child.display === "inline";
|
||||||
|
|
||||||
|
if (isInline) {
|
||||||
|
const inlineItems: Array<{
|
||||||
|
index: number;
|
||||||
|
line: TerminalLine;
|
||||||
|
}> = [];
|
||||||
|
while (i < line.children.length) {
|
||||||
|
const nextChild = line.children[i];
|
||||||
|
const nextIsInline =
|
||||||
|
nextChild.inline || nextChild.display === "inline";
|
||||||
|
if (!nextIsInline) break;
|
||||||
|
|
||||||
|
inlineItems.push({ index: i, line: nextChild });
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
groups.push({ kind: "inline", items: inlineItems });
|
||||||
|
} else {
|
||||||
|
groups.push({ kind: "single", index: i, line: child });
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return groups;
|
||||||
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="tui-card" class:inline style={cardStyle}>
|
<div class="tui-card" class:inline style={cardStyle}>
|
||||||
@@ -91,19 +128,38 @@
|
|||||||
class:flex={line.display === "flex"}
|
class:flex={line.display === "flex"}
|
||||||
class:grid={line.display === "grid"}
|
class:grid={line.display === "grid"}
|
||||||
>
|
>
|
||||||
{#each line.children as child, i}
|
{#each processedChildren as group}
|
||||||
<TuiLine
|
{#if group.kind === "inline"}
|
||||||
line={child}
|
<div class="tui-inline-group">
|
||||||
index={i}
|
{#each group.items as item}
|
||||||
segments={parseColorText(child.content)}
|
<TuiLine
|
||||||
complete={true}
|
line={item.line}
|
||||||
showImage={true}
|
index={item.index}
|
||||||
selectedIndex={-1}
|
segments={parseColorText(item.line.content)}
|
||||||
inline={false}
|
complete={true}
|
||||||
{onButtonClick}
|
showImage={true}
|
||||||
{onHoverButton}
|
selectedIndex={-1}
|
||||||
{onLinkClick}
|
inline={true}
|
||||||
/>
|
{onButtonClick}
|
||||||
|
{onHoverButton}
|
||||||
|
{onLinkClick}
|
||||||
|
/>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<TuiLine
|
||||||
|
line={group.line}
|
||||||
|
index={group.index}
|
||||||
|
segments={parseColorText(group.line.content)}
|
||||||
|
complete={true}
|
||||||
|
showImage={true}
|
||||||
|
selectedIndex={-1}
|
||||||
|
inline={false}
|
||||||
|
{onButtonClick}
|
||||||
|
{onHoverButton}
|
||||||
|
{onLinkClick}
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|||||||
@@ -120,10 +120,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 768px) {
|
@media (max-width: 768px) {
|
||||||
.tui-group.inline {
|
/* .tui-group.inline removed to allow inline flow on mobile */
|
||||||
flex-direction: column;
|
|
||||||
align-items: stretch;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tui-group.grid {
|
.tui-group.grid {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
|||||||
@@ -39,8 +39,7 @@ export const lines: TerminalLine[] = [
|
|||||||
{ type: 'command', content: 'cat ~/about.md' },
|
{ type: 'command', content: 'cat ~/about.md' },
|
||||||
{ type: 'blank', content: '' },
|
{ type: 'blank', content: '' },
|
||||||
|
|
||||||
{ type: 'image', content: '', image: user.avatar, imageAlt: user.name, imageWidth: 180, inline: true },
|
{ type: 'image', content: '', image: user.avatar, imageAlt: user.name, imageWidth: 200, inline: true },
|
||||||
|
|
||||||
{
|
{
|
||||||
type: 'group',
|
type: 'group',
|
||||||
content: '',
|
content: '',
|
||||||
@@ -48,6 +47,7 @@ export const lines: TerminalLine[] = [
|
|||||||
groupAlign: 'start',
|
groupAlign: 'start',
|
||||||
inline: true,
|
inline: true,
|
||||||
children: [
|
children: [
|
||||||
|
{ type: 'blank', content: '' },
|
||||||
{ type: 'header', content: `(&bold)${user.name}(&)` },
|
{ type: 'header', content: `(&bold)${user.name}(&)` },
|
||||||
{ type: 'info', content: `(&accent)${user.title}(&)` },
|
{ type: 'info', content: `(&accent)${user.title}(&)` },
|
||||||
{ type: 'output', content: `(&white)Location:(&) (&primary)${user.location}(&)` },
|
{ type: 'output', content: `(&white)Location:(&) (&primary)${user.location}(&)` },
|
||||||
@@ -57,18 +57,25 @@ export const lines: TerminalLine[] = [
|
|||||||
{ type: 'blank', content: '' },
|
{ type: 'blank', content: '' },
|
||||||
{ type: 'divider', content: 'Music Recommendations' },
|
{ type: 'divider', content: 'Music Recommendations' },
|
||||||
|
|
||||||
{ type: 'output', content: '(&accent)Artist:(&) ', inline: true },
|
{
|
||||||
...artist.flatMap((a, i): TerminalLine[] => {
|
type: 'group',
|
||||||
const item: TerminalLine = { type: 'link', content: `(&white)${a.name}(&)`, href: a.url, inline: true };
|
content: '',
|
||||||
return i < artist.length - 1 ? [item, { type: 'output', content: ' (&muted)•(&) ', inline: true }] : [item];
|
groupDirection: 'row',
|
||||||
}),
|
children: [
|
||||||
{ type: 'blank', content: '' },
|
{ type: 'output', content: '(&accent)Artist:(&) ', inline: true },
|
||||||
|
...artist.flatMap((a, i): TerminalLine[] => {
|
||||||
|
const item: TerminalLine = { type: 'link', content: `(&white)${a.name}(&)`, href: a.url, inline: true };
|
||||||
|
return i < artist.length - 1 ? [item, { type: 'output', content: ' (&muted)•(&) ', inline: true }] : [item];
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
{ type: 'output', content: '(&accent)Songs:(&) ', inline: true },
|
// { type: 'blank', content: '' },
|
||||||
...songs.flatMap((s, i): TerminalLine[] => {
|
// { type: 'output', content: '(&accent)Songs:(&) ', inline: true },
|
||||||
const item: TerminalLine = { type: 'link', content: `(&white)${s.name}(&)`, href: s.url, inline: true };
|
// ...songs.flatMap((s, i): TerminalLine[] => {
|
||||||
return i < songs.length - 1 ? [item, { type: 'output', content: ' (&muted)•(&) ', inline: true }] : [item];
|
// const item: TerminalLine = { type: 'link', content: `(&white)${s.name}(&)`, href: s.url, inline: true };
|
||||||
}),
|
// return i < songs.length - 1 ? [item, { type: 'output', content: ' (&muted)•(&) ', inline: true }] : [item];
|
||||||
|
// }),
|
||||||
{ type: 'blank', content: '' },
|
{ type: 'blank', content: '' },
|
||||||
{
|
{
|
||||||
type: 'card',
|
type: 'card',
|
||||||
|
|||||||
@@ -141,7 +141,7 @@
|
|||||||
</svelte:head>
|
</svelte:head>
|
||||||
|
|
||||||
<div class="error-container">
|
<div class="error-container">
|
||||||
<TerminalTUI {lines} title="error" interactive={true} speed="fast" />
|
<TerminalTUI {lines} title="error" interactive={true} speed="instant" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|||||||
@@ -1,25 +1,33 @@
|
|||||||
import ytpl from 'ytpl';
|
import playdl, { YouTubeVideo } from 'play-dl';
|
||||||
|
|
||||||
|
function shuffleArray(array: any[]) {
|
||||||
|
for (let i = array.length - 1; i > 0; i--) {
|
||||||
|
const j = Math.floor(Math.random() * (i + 1));
|
||||||
|
[array[i], array[j]] = [array[j], array[i]];
|
||||||
|
}
|
||||||
|
return array;
|
||||||
|
}
|
||||||
|
|
||||||
/** @type {import('./$types').PageServerLoad} */
|
/** @type {import('./$types').PageServerLoad} */
|
||||||
export async function load({ params }) {
|
export async function load({ params }) {
|
||||||
|
|
||||||
const playlistUrl = 'https://music.youtube.com/playlist?list=PLHqPHHvmOjJ6JwiYc3Fg-O5JGbVppI1VR&si=CIb9WIKhjKsRV3p3';
|
const playlistUrl = 'https://music.youtube.com/playlist?list=PLHqPHHvmOjJ6JwiYc3Fg-O5JGbVppI1VR&si=pJnM95siw3kQRlnr';
|
||||||
|
|
||||||
const playlist = await ytpl(playlistUrl, { limit: Infinity });
|
const playlist = await playdl.playlist_info(playlistUrl);
|
||||||
|
|
||||||
const songs = playlist.items.map((item, index) => {
|
let songs = (await playlist.all_videos()).map((video: YouTubeVideo) => {
|
||||||
return {
|
return {
|
||||||
index: index + 1,
|
title: video.title,
|
||||||
title: item.title,
|
url: video.url,
|
||||||
duration: item.duration, // format is usually mm:ss
|
author: video.channel?.name,
|
||||||
author: item.author.name,
|
authorUrl: video.channel?.url,
|
||||||
isLive: item.isLive,
|
thumbnail: video.thumbnails[0].url,
|
||||||
url: item.shortUrl
|
duration: video.durationRaw,
|
||||||
};
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
songs
|
songs: shuffleArray(songs)
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -77,11 +77,28 @@
|
|||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
type: "output",
|
type: "output",
|
||||||
content: `(&primary)Now Playing:(&) (&text)${song.title}(&)`,
|
content: `(&primary)Now Playing:(&)`,
|
||||||
|
inline: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: "link",
|
||||||
|
content: `(&blue)${song.title}(&)`,
|
||||||
|
href: song.url,
|
||||||
|
inline: true,
|
||||||
|
external: true,
|
||||||
|
},
|
||||||
|
{ type: "blank", content: "" },
|
||||||
{
|
{
|
||||||
type: "output",
|
type: "output",
|
||||||
content: `(&primary)Artist:(&) (&text)${song.author}(&)`,
|
content: `(&primary)Artist:(&)`,
|
||||||
|
inline: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "link",
|
||||||
|
content: `(&blue)${song.author}(&)`,
|
||||||
|
href: song.authorUrl,
|
||||||
|
inline: true,
|
||||||
|
external: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "progress",
|
type: "progress",
|
||||||
@@ -120,15 +137,7 @@
|
|||||||
textStyle: "center",
|
textStyle: "center",
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
}
|
||||||
{
|
|
||||||
type: "button",
|
|
||||||
content: "(&icon, mdi:youtube) Listen on YouTube",
|
|
||||||
style: "primary",
|
|
||||||
href: song.url,
|
|
||||||
external: true,
|
|
||||||
textStyle: "center",
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user