TUI Renderer Update
This commit is contained in:
@@ -1,22 +1,7 @@
|
||||
<script lang="ts">
|
||||
import Icon from '@iconify/svelte';
|
||||
import { getSegmentStyle, getLinePrefix, getSegmentsUpToChar } from './utils';
|
||||
import { getSegmentsUpToChar } from './utils';
|
||||
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 TuiLine from './TuiLine.svelte';
|
||||
import type { DisplayedLine } from './types';
|
||||
import '$lib/assets/css/tui-body.css';
|
||||
|
||||
@@ -28,169 +13,68 @@
|
||||
export let onButtonClick: (idx: number) => void;
|
||||
export let onHoverButton: (idx: number) => void;
|
||||
export let onLinkClick: (idx: number) => void;
|
||||
export let terminalSettings: any;
|
||||
export let terminalSettings: { showCursor: boolean };
|
||||
|
||||
// Group displayed lines into regular items and inline groups
|
||||
type GroupedItem =
|
||||
| { type: 'single'; index: number; displayed: DisplayedLine }
|
||||
| { type: 'inline-group'; indices: number[]; items: DisplayedLine[] };
|
||||
// Group consecutive inline items together
|
||||
type ProcessedGroup =
|
||||
| { kind: 'single'; index: number; displayed: DisplayedLine }
|
||||
| { kind: 'inline'; items: Array<{ index: number; displayed: DisplayedLine }> };
|
||||
|
||||
$: groupedLines = (() => {
|
||||
const result: GroupedItem[] = [];
|
||||
$: processedGroups = (() => {
|
||||
const groups: ProcessedGroup[] = [];
|
||||
let i = 0;
|
||||
|
||||
while (i < displayedLines.length) {
|
||||
const displayed = displayedLines[i];
|
||||
const line = displayed.parsed.line;
|
||||
|
||||
if (line.inline) {
|
||||
// Start an inline group
|
||||
const group: GroupedItem = { type: 'inline-group', indices: [], items: [] };
|
||||
if (displayed.parsed.line.inline) {
|
||||
const inlineItems: Array<{ index: number; displayed: DisplayedLine }> = [];
|
||||
while (i < displayedLines.length && displayedLines[i].parsed.line.inline) {
|
||||
group.indices.push(i);
|
||||
group.items.push(displayedLines[i]);
|
||||
inlineItems.push({ index: i, displayed: displayedLines[i] });
|
||||
i++;
|
||||
}
|
||||
result.push(group);
|
||||
groups.push({ kind: 'inline', items: inlineItems });
|
||||
} else {
|
||||
result.push({ type: 'single', index: i, displayed });
|
||||
groups.push({ kind: 'single', index: i, displayed });
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return groups;
|
||||
})();
|
||||
</script>
|
||||
|
||||
<div class="tui-body" bind:this={ref}>
|
||||
{#each groupedLines as group}
|
||||
{#if group.type === 'inline-group'}
|
||||
<!-- Inline group container -->
|
||||
{#each processedGroups as group, gi (gi)}
|
||||
{#if group.kind === 'inline'}
|
||||
<div class="tui-inline-group">
|
||||
{#each group.items as displayed, j}
|
||||
{@const line = displayed.parsed.line}
|
||||
{@const idx = group.indices[j]}
|
||||
{@const visibleSegments = getSegmentsUpToChar(displayed.parsed.segments, displayed.charIndex)}
|
||||
{@const showImage = displayed.showImage}
|
||||
{#if line.type === 'button'}
|
||||
<TuiButton {line} index={idx} selected={selectedIndex === idx} onClick={onButtonClick} onHover={onHoverButton} inline={true} />
|
||||
{:else if line.type === 'link'}
|
||||
<TuiLink {line} onClick={() => onLinkClick(idx)} />
|
||||
{:else if line.type === 'tooltip'}
|
||||
<TuiTooltip {line} />
|
||||
{:else if line.type === 'progress'}
|
||||
<TuiProgress {line} inline={true} />
|
||||
{:else if line.type === 'input'}
|
||||
<TuiInput {line} inline={true} />
|
||||
{:else if line.type === 'checkbox'}
|
||||
<TuiCheckbox {line} inline={true} />
|
||||
{:else if line.type === 'toggle'}
|
||||
<TuiToggle {line} inline={true} />
|
||||
{:else if line.type === 'group'}
|
||||
<TuiGroup {line} inline={true} {onButtonClick} {onHoverButton} {onLinkClick} />
|
||||
{: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}">
|
||||
{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>
|
||||
{/if}
|
||||
{#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)}
|
||||
complete={item.displayed.complete}
|
||||
showImage={item.displayed.showImage}
|
||||
{selectedIndex}
|
||||
inline={true}
|
||||
{onButtonClick}
|
||||
{onHoverButton}
|
||||
{onLinkClick}
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{:else if group.type === 'single'}
|
||||
{@const i = group.index}
|
||||
{@const displayed = group.displayed}
|
||||
{@const line = displayed.parsed.line}
|
||||
{@const visibleSegments = getSegmentsUpToChar(displayed.parsed.segments, displayed.charIndex)}
|
||||
{@const complete = displayed.complete}
|
||||
{@const showImage = displayed.showImage}
|
||||
{#if line.type === 'divider'}
|
||||
<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={i} selected={selectedIndex === i} onClick={onButtonClick} onHover={onHoverButton} />
|
||||
{:else if line.type === 'link'}
|
||||
<div class="tui-line link">
|
||||
<TuiLink {line} onClick={() => onLinkClick(i)} />
|
||||
</div>
|
||||
{:else if line.type === 'card'}
|
||||
<TuiCard {line} />
|
||||
{:else if line.type === 'cardgrid'}
|
||||
<TuiCardGrid {line} />
|
||||
{:else if line.type === 'progress'}
|
||||
<TuiProgress {line} />
|
||||
{:else if line.type === 'accordion'}
|
||||
<TuiAccordion {line} />
|
||||
{:else if line.type === 'table'}
|
||||
<TuiTable {line} />
|
||||
{:else if line.type === 'tooltip'}
|
||||
<div class="tui-line">
|
||||
<TuiTooltip {line} />
|
||||
</div>
|
||||
{:else if line.type === 'input'}
|
||||
<TuiInput {line} />
|
||||
{:else if line.type === 'textarea'}
|
||||
<TuiTextarea {line} />
|
||||
{:else if line.type === 'checkbox'}
|
||||
<TuiCheckbox {line} />
|
||||
{:else if line.type === 'radio'}
|
||||
<TuiRadio {line} />
|
||||
{:else if line.type === 'select'}
|
||||
<TuiSelect {line} />
|
||||
{:else if line.type === 'toggle'}
|
||||
<TuiToggle {line} />
|
||||
{:else if line.type === 'group'}
|
||||
<TuiGroup {line} {onButtonClick} {onHoverButton} {onLinkClick} />
|
||||
{:else}
|
||||
<div class="tui-line {line.type}" class:complete id={line.id}>
|
||||
{#if line.type === 'command' || line.type === '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>
|
||||
{/if}
|
||||
|
||||
{#if line.type === 'image' && showImage}
|
||||
<div class="tui-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 === 'header'}
|
||||
<span class="content header-text">
|
||||
<Icon icon="mdi:pound" width="14" class="header-icon" />
|
||||
{#each visibleSegments as segment}
|
||||
{#if segment.icon}
|
||||
<Icon icon={segment.icon} width="16" class="inline-icon" />
|
||||
{:else if getSegmentStyle(segment)}
|
||||
<span style={getSegmentStyle(segment)}>{segment.text}</span>
|
||||
{:else}
|
||||
{segment.text}
|
||||
{/if}
|
||||
{/each}
|
||||
</span>
|
||||
{:else if line.type !== 'blank'}
|
||||
<span class="content">
|
||||
{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>
|
||||
{/if}
|
||||
|
||||
{#if terminalSettings.showCursor && i === currentLineIndex && !complete && isTyping && line.type !== 'image' && line.type !== 'blank'}
|
||||
<span class="cursor"></span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
{:else}
|
||||
<TuiLine
|
||||
line={group.displayed.parsed.line}
|
||||
index={group.index}
|
||||
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'}
|
||||
{onButtonClick}
|
||||
{onHoverButton}
|
||||
{onLinkClick}
|
||||
/>
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user