144 lines
5.7 KiB
Svelte
144 lines
5.7 KiB
Svelte
<script lang="ts">
|
|
import Icon from '@iconify/svelte';
|
|
import { getSegmentStyle, getLinePrefix } from './utils';
|
|
import { handleNavigation } from './terminal-keyboard';
|
|
import { user } from '$lib/config';
|
|
import TuiButton from './TuiButton.svelte';
|
|
import TuiLink from './TuiLink.svelte';
|
|
import TuiCard from './TuiCard.svelte';
|
|
import TuiProgress from './TuiProgress.svelte';
|
|
import TuiAccordion from './TuiAccordion.svelte';
|
|
import TuiTable from './TuiTable.svelte';
|
|
import TuiTooltip from './TuiTooltip.svelte';
|
|
import TuiCardGrid from './TuiCardGrid.svelte';
|
|
import TuiInput from './TuiInput.svelte';
|
|
import TuiTextarea from './TuiTextarea.svelte';
|
|
import TuiCheckbox from './TuiCheckbox.svelte';
|
|
import TuiRadio from './TuiRadio.svelte';
|
|
import TuiSelect from './TuiSelect.svelte';
|
|
import TuiToggle from './TuiToggle.svelte';
|
|
import TuiGroup from './TuiGroup.svelte';
|
|
import type { TerminalLine, TextSegment } from './types';
|
|
|
|
interface Props {
|
|
line: TerminalLine;
|
|
index: number;
|
|
segments: TextSegment[];
|
|
complete?: boolean;
|
|
showImage?: boolean;
|
|
selectedIndex?: number;
|
|
inline?: boolean;
|
|
showCursor?: boolean;
|
|
onButtonClick?: (idx: number) => void;
|
|
onHoverButton?: (idx: number) => void;
|
|
onLinkClick?: (idx: number) => void;
|
|
}
|
|
|
|
let {
|
|
line,
|
|
index,
|
|
segments,
|
|
complete = true,
|
|
showImage = false,
|
|
selectedIndex = -1,
|
|
inline = false,
|
|
showCursor = false,
|
|
onButtonClick = () => {},
|
|
onHoverButton = () => {},
|
|
onLinkClick = () => {}
|
|
}: Props = $props();
|
|
|
|
// Component types that have their own wrapper
|
|
const componentTypes = new Set(['button', 'card', 'cardgrid', 'progress', 'accordion', 'table', 'input', 'textarea', 'checkbox', 'radio', 'select', 'toggle', 'group']);
|
|
|
|
// Types that need special handling
|
|
const isComponent = $derived(componentTypes.has(line.type));
|
|
const isBlank = $derived(line.type === 'blank');
|
|
const isDivider = $derived(line.type === 'divider');
|
|
const isImage = $derived(line.type === 'image');
|
|
const isPrompt = $derived(line.type === 'command' || line.type === 'prompt');
|
|
const isHeader = $derived(line.type === 'header');
|
|
const isLink = $derived(line.type === 'link');
|
|
const isTooltip = $derived(line.type === 'tooltip');
|
|
</script>
|
|
|
|
{#if isBlank}
|
|
{#if inline}<span class="inline-blank"></span>{:else}<div class="tui-line blank"></div>{/if}
|
|
{:else if isDivider}
|
|
<div class="tui-divider" id={line.id}>
|
|
<span class="divider-line"></span>
|
|
{#if line.content}<span class="divider-text">{line.content}</span>{/if}
|
|
<span class="divider-line"></span>
|
|
</div>
|
|
{:else if line.type === 'button'}
|
|
<TuiButton {line} {index} selected={selectedIndex === index} onClick={onButtonClick} onHover={onHoverButton} {inline} />
|
|
{:else if isLink}
|
|
{#if inline}
|
|
<TuiLink {line} onClick={() => handleNavigation(line)} />
|
|
{:else}
|
|
<div class="tui-line link"><TuiLink {line} onClick={() => onLinkClick(index)} /></div>
|
|
{/if}
|
|
{:else if isTooltip}
|
|
{#if inline}<TuiTooltip {line} />{:else}<div class="tui-line"><TuiTooltip {line} /></div>{/if}
|
|
{:else if line.type === 'card'}
|
|
<TuiCard {line} />
|
|
{:else if line.type === 'cardgrid'}
|
|
<TuiCardGrid {line} />
|
|
{:else if line.type === 'progress'}
|
|
<TuiProgress {line} {inline} />
|
|
{:else if line.type === 'accordion'}
|
|
<TuiAccordion {line} />
|
|
{:else if line.type === 'table'}
|
|
<TuiTable {line} />
|
|
{:else if line.type === 'input'}
|
|
<TuiInput {line} {inline} />
|
|
{:else if line.type === 'textarea'}
|
|
<TuiTextarea {line} />
|
|
{:else if line.type === 'checkbox'}
|
|
<TuiCheckbox {line} {inline} />
|
|
{:else if line.type === 'radio'}
|
|
<TuiRadio {line} />
|
|
{:else if line.type === 'select'}
|
|
<TuiSelect {line} />
|
|
{:else if line.type === 'toggle'}
|
|
<TuiToggle {line} {inline} />
|
|
{:else if line.type === 'group'}
|
|
<TuiGroup {line} {inline} {onButtonClick} {onHoverButton} {onLinkClick} />
|
|
{:else if isImage && showImage}
|
|
<div class="tui-image" class:inline-image={inline}>
|
|
<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 !isImage}
|
|
{#if inline && isHeader}
|
|
<span class="header-text inline-header">
|
|
<Icon icon="mdi:pound" width="20" class="header-icon" />
|
|
{#each segments as seg}{#if seg.icon}<Icon icon={seg.icon} width="20" class="inline-icon" />{:else if getSegmentStyle(seg)}<span style={getSegmentStyle(seg)}>{seg.text}</span>{:else}{seg.text}{/if}{/each}
|
|
</span>
|
|
{:else if inline}
|
|
<span class="inline-content {line.type}">
|
|
{getLinePrefix(line.type)}{#each segments as seg}{#if seg.icon}<Icon icon={seg.icon} width="14" class="inline-icon" />{:else if getSegmentStyle(seg)}<span style={getSegmentStyle(seg)}>{seg.text}</span>{:else}{seg.text}{/if}{/each}
|
|
</span>
|
|
{:else}
|
|
<div class="tui-line {line.type}" class:complete id={line.id}>
|
|
{#if isPrompt}
|
|
<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>
|
|
{/if}
|
|
{#if isHeader}
|
|
<span class="content header-text">
|
|
<Icon icon="mdi:pound" width="25" class="header-icon" />
|
|
{#each segments as seg}{#if seg.icon}<Icon icon={seg.icon} width="25" class="inline-icon" />{:else if getSegmentStyle(seg)}<span style={getSegmentStyle(seg)}>{seg.text}</span>{:else}{seg.text}{/if}{/each}
|
|
</span>
|
|
{:else}
|
|
<span class="content">
|
|
{getLinePrefix(line.type)}{#each segments as seg}{#if seg.icon}<Icon icon={seg.icon} width="14" class="inline-icon" />{:else if getSegmentStyle(seg)}<span style={getSegmentStyle(seg)}>{seg.text}</span>{:else}{seg.text}{/if}{/each}
|
|
</span>
|
|
{/if}
|
|
{#if showCursor}<span class="cursor"></span>{/if}
|
|
</div>
|
|
{/if}
|
|
{/if}
|