TUI Renderer, Element Types, and Page Updates

This commit is contained in:
2025-11-30 18:26:56 -05:00
parent f09c198f17
commit 1167c686e2
31 changed files with 1642 additions and 809 deletions
+80 -34
View File
@@ -1,56 +1,93 @@
<script lang="ts">
import { getSegmentsUpToChar } from './utils';
import { user } from '$lib/config';
import TuiLine from './TuiLine.svelte';
import type { DisplayedLine } from './types';
import '$lib/assets/css/tui-body.css';
import { getSegmentsUpToChar } from "./utils";
import { user } from "$lib/config";
import TuiLine from "./TuiLine.svelte";
import type { DisplayedLine } from "./types";
import "$lib/assets/css/tui-body.css";
export let displayedLines: DisplayedLine[] = [];
export let currentLineIndex = 0;
export let isTyping = false;
export let selectedIndex = -1;
export let ref: HTMLDivElement | undefined;
export let onButtonClick: (idx: number) => void;
export let onHoverButton: (idx: number) => void;
export let onLinkClick: (idx: number) => void;
export let terminalSettings: { showCursor: boolean };
interface Props {
displayedLines: DisplayedLine[];
currentLineIndex?: number;
isTyping?: boolean;
selectedIndex?: number;
ref?: HTMLDivElement | undefined;
onButtonClick: (idx: number) => void;
onHoverButton: (idx: number) => void;
onLinkClick: (idx: number) => void;
terminalSettings: { showCursor: boolean };
}
let {
displayedLines = [],
currentLineIndex = 0,
isTyping = false,
selectedIndex = -1,
ref = $bindable(undefined),
onButtonClick,
onHoverButton,
onLinkClick,
terminalSettings,
}: Props = $props();
// Group consecutive inline items together
type ProcessedGroup =
| { kind: 'single'; index: number; displayed: DisplayedLine }
| { kind: 'inline'; items: Array<{ index: number; displayed: DisplayedLine }> };
type ProcessedGroup =
| { kind: "single"; index: number; displayed: DisplayedLine }
| {
kind: "inline";
items: Array<{ index: number; displayed: DisplayedLine }>;
};
$: processedGroups = (() => {
const processedGroups = $derived.by(() => {
const groups: ProcessedGroup[] = [];
let i = 0;
while (i < displayedLines.length) {
const displayed = displayedLines[i];
if (displayed.parsed.line.inline) {
const inlineItems: Array<{ index: number; displayed: DisplayedLine }> = [];
while (i < displayedLines.length && displayedLines[i].parsed.line.inline) {
inlineItems.push({ index: i, displayed: displayedLines[i] });
const isInline =
displayed.parsed.line.inline ||
displayed.parsed.line.display === "inline";
if (isInline) {
const inlineItems: Array<{
index: number;
displayed: DisplayedLine;
}> = [];
// Collect consecutive inline items
while (i < displayedLines.length) {
const nextLine = displayedLines[i].parsed.line;
const nextIsInline =
nextLine.inline || nextLine.display === "inline";
if (!nextIsInline) break;
inlineItems.push({
index: i,
displayed: displayedLines[i],
});
i++;
}
groups.push({ kind: 'inline', items: inlineItems });
groups.push({ kind: "inline", items: inlineItems });
} else {
groups.push({ kind: 'single', index: i, displayed });
groups.push({ kind: "single", index: i, displayed });
i++;
}
}
return groups;
})();
});
</script>
<div class="tui-body" bind:this={ref}>
{#each processedGroups as group, gi (gi)}
{#if group.kind === 'inline'}
{#if group.kind === "inline"}
<div class="tui-inline-group">
{#each group.items as item (item.index)}
<TuiLine
line={item.displayed.parsed.line}
index={item.index}
segments={getSegmentsUpToChar(item.displayed.parsed.segments, item.displayed.charIndex)}
segments={getSegmentsUpToChar(
item.displayed.parsed.segments,
item.displayed.charIndex,
)}
complete={item.displayed.complete}
showImage={item.displayed.showImage}
{selectedIndex}
@@ -65,12 +102,19 @@
<TuiLine
line={group.displayed.parsed.line}
index={group.index}
segments={getSegmentsUpToChar(group.displayed.parsed.segments, group.displayed.charIndex)}
segments={getSegmentsUpToChar(
group.displayed.parsed.segments,
group.displayed.charIndex,
)}
complete={group.displayed.complete}
showImage={group.displayed.showImage}
{selectedIndex}
inline={false}
showCursor={terminalSettings.showCursor && group.index === currentLineIndex && !group.displayed.complete && isTyping && group.displayed.parsed.line.type !== 'image'}
showCursor={terminalSettings.showCursor &&
group.index === currentLineIndex &&
!group.displayed.complete &&
isTyping &&
group.displayed.parsed.line.type !== "image"}
{onButtonClick}
{onHoverButton}
{onLinkClick}
@@ -81,12 +125,14 @@
{#if terminalSettings.showCursor && !isTyping && displayedLines.length > 0}
<div class="tui-line prompt">
<span class="prompt">
<span class="user">{user.username}</span><span class="at">@</span><span class="host">{user.hostname}</span>
<span class="separator">:</span><span class="path">~</span><span class="symbol">$</span>
<span class="user">{user.username}</span><span class="at"
>@</span
><span class="host">{user.hostname}</span>
<span class="separator">:</span><span class="path">~</span><span
class="symbol">$</span
>
</span>
<span class="cursor"></span>
</div>
{/if}
</div>