130 lines
2.8 KiB
Svelte
130 lines
2.8 KiB
Svelte
<script lang="ts">
|
|
import { parseColorText, getPlainText } from "./utils";
|
|
import { themeColors } from "$lib/stores/theme";
|
|
import TuiLine from "./TuiLine.svelte";
|
|
import type { GroupLine, TerminalLine } from "./types";
|
|
|
|
interface Props {
|
|
line: GroupLine;
|
|
inline?: boolean;
|
|
onButtonClick?: (idx: number, line?: TerminalLine) => void;
|
|
onHoverButton?: (idx: number) => void;
|
|
onLinkClick?: (idx: number) => void;
|
|
}
|
|
|
|
let {
|
|
line,
|
|
inline = false,
|
|
onButtonClick = () => {},
|
|
onHoverButton = () => {},
|
|
onLinkClick = () => {},
|
|
}: Props = $props();
|
|
|
|
// Get colorMap from current theme
|
|
const colorMap = $derived($themeColors.colorMap);
|
|
|
|
// Parse children with current colorMap
|
|
const parsedChildren = $derived(
|
|
(line.children || []).map((child) => {
|
|
const segments = parseColorText(child.content, colorMap);
|
|
return { line: child, segments, plainText: getPlainText(segments) };
|
|
}),
|
|
);
|
|
|
|
// Style for the group container
|
|
const groupStyle = $derived(() => {
|
|
const styles: string[] = [];
|
|
// Only apply flex styles if not grid or block
|
|
if (line.display !== "grid" && line.display !== "block") {
|
|
if (line.groupDirection === "column")
|
|
styles.push("flex-direction: column");
|
|
if (line.groupAlign) {
|
|
const alignMap: Record<string, string> = {
|
|
start: "flex-start",
|
|
center: "center",
|
|
end: "flex-end",
|
|
stretch: "stretch",
|
|
};
|
|
styles.push(
|
|
`align-items: ${alignMap[line.groupAlign] || "flex-start"}`,
|
|
);
|
|
}
|
|
}
|
|
if (line.groupGap) styles.push(`gap: ${line.groupGap}`);
|
|
return styles.join("; ");
|
|
});
|
|
</script>
|
|
|
|
<div
|
|
class="tui-group"
|
|
class:inline
|
|
class:grid={line.display === "grid"}
|
|
class:block={line.display === "block"}
|
|
class:expand={line.groupExpand}
|
|
style={groupStyle()}
|
|
id={line.id}
|
|
>
|
|
{#each parsedChildren as parsed, idx (idx)}
|
|
<TuiLine
|
|
line={parsed.line}
|
|
index={idx}
|
|
segments={parsed.segments}
|
|
complete={true}
|
|
showImage={parsed.line.type === "image"}
|
|
selectedIndex={-1}
|
|
inline={parsed.line.inline !== false}
|
|
{onButtonClick}
|
|
{onHoverButton}
|
|
{onLinkClick}
|
|
/>
|
|
{/each}
|
|
</div>
|
|
|
|
<style>
|
|
.tui-group {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: flex-start;
|
|
gap: 0.75rem;
|
|
animation: lineSlideIn 0.15s ease-out;
|
|
}
|
|
|
|
.tui-group.inline {
|
|
display: inline-flex;
|
|
}
|
|
|
|
.tui-group.grid {
|
|
display: grid;
|
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
width: 100%;
|
|
}
|
|
|
|
.tui-group.block {
|
|
display: block;
|
|
width: 100%;
|
|
}
|
|
|
|
.tui-group.expand > :global(*) {
|
|
flex: 1;
|
|
}
|
|
|
|
@keyframes lineSlideIn {
|
|
from {
|
|
opacity: 0;
|
|
transform: translateX(-5px);
|
|
}
|
|
to {
|
|
opacity: 1;
|
|
transform: translateX(0);
|
|
}
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
/* .tui-group.inline removed to allow inline flow on mobile */
|
|
|
|
.tui-group.grid {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
}
|
|
</style>
|