diff --git a/README.md b/README.md index 4b58b2d..ab7339a 100644 --- a/README.md +++ b/README.md @@ -307,6 +307,10 @@ Supported inline types: `button`, `link`, `tooltip`, `progress`, `output`, `info icon: 'mdi:star', // Optional header icon image: '/path/to/image.png', // Optional card image style: 'primary', // Border accent color + display: 'flex', // flex | grid | block (controls children layout) + children: [ // Optional nested elements + { type: 'button', content: 'Action', style: 'primary' } + ] } ``` diff --git a/src/lib/assets/css/tui-accordion.css b/src/lib/assets/css/tui-accordion.css index 343ffee..8e9e991 100644 --- a/src/lib/assets/css/tui-accordion.css +++ b/src/lib/assets/css/tui-accordion.css @@ -5,6 +5,14 @@ overflow: hidden; } +.tui-accordion.inline { + display: inline-block; + width: auto; + min-width: 200px; + vertical-align: top; + margin: 0 0.5rem 0 0; +} + .accordion-item { border-bottom: 1px solid var(--terminal-border); } @@ -62,8 +70,9 @@ opacity: 0; transform: translateY(-5px); } + to { opacity: 1; transform: translateY(0); } -} +} \ No newline at end of file diff --git a/src/lib/assets/css/tui-card-grid.css b/src/lib/assets/css/tui-card-grid.css index acd9a64..db6b153 100644 --- a/src/lib/assets/css/tui-card-grid.css +++ b/src/lib/assets/css/tui-card-grid.css @@ -6,6 +6,16 @@ width: 100%; } +.tui-card-grid.inline { + display: inline-flex; + flex-wrap: wrap; + width: auto; + gap: 0.5rem; + vertical-align: top; + padding: 0; + margin: 0 0.5rem 0 0; +} + .tui-card { background: var(--terminal-bg); border: 1px solid var(--terminal-border); @@ -219,4 +229,4 @@ .tui-card-grid { grid-template-columns: 1fr; } -} +} \ No newline at end of file diff --git a/src/lib/assets/css/tui-card.css b/src/lib/assets/css/tui-card.css index 922d30c..0546485 100644 --- a/src/lib/assets/css/tui-card.css +++ b/src/lib/assets/css/tui-card.css @@ -1,58 +1,46 @@ .tui-card { border: 1px solid var(--terminal-border); - border-radius: 6px; - background: color-mix(in srgb, var(--terminal-bg) 80%, var(--card-accent) 5%); + border-left: 3px solid var(--card-color); + background: color-mix(in srgb, var(--terminal-bg) 50%, var(--terminal-bg-light)); + border-radius: 4px; + padding: 0.75rem 1rem; margin: 0.5rem 0; - overflow: hidden; - transition: border-color 0.2s ease; + max-width: 600px; } -.tui-card:hover { - border-color: var(--card-accent); -} - -.card-header { - display: flex; - align-items: center; - gap: 0.5rem; - padding: 0.6rem 0.75rem; - border-bottom: 1px solid var(--terminal-border); - background: rgba(0, 0, 0, 0.1); -} - -:global(.card-icon) { - color: var(--card-accent); +.tui-card.inline { + display: inline-block; + width: auto; + margin: 0 0.5rem 0 0; + vertical-align: top; } .card-title { - font-weight: 600; - color: var(--terminal-text); - font-size: 0.85rem; -} - -.card-body { - padding: 0.75rem; -} - -.card-image { - width: 100%; - max-height: 200px; - object-fit: cover; - border-radius: 4px; + color: var(--card-color); + font-weight: bold; margin-bottom: 0.5rem; + font-size: 1rem; } .card-content { - color: var(--terminal-muted); - font-size: 0.85rem; + color: var(--terminal-text); + font-size: 0.9rem; line-height: 1.5; - white-space: pre-wrap; } .card-footer { - padding: 0.5rem 0.75rem; - border-top: 1px solid var(--terminal-border); - font-size: 0.75rem; + margin-top: 0.75rem; + padding-top: 0.5rem; + border-top: 1px dashed var(--terminal-border); color: var(--terminal-muted); - background: rgba(0, 0, 0, 0.05); + font-size: 0.8rem; +} + +/* Mobile: inline cards become full-width */ +@media (max-width: 768px) { + .tui-card.inline { + display: block; + width: 100%; + margin: 0.5rem 0; + } } diff --git a/src/lib/assets/css/tui-table.css b/src/lib/assets/css/tui-table.css index 7d61c12..6081cde 100644 --- a/src/lib/assets/css/tui-table.css +++ b/src/lib/assets/css/tui-table.css @@ -5,6 +5,13 @@ overflow: hidden; } +.tui-table-wrapper.inline { + display: inline-block; + width: auto; + vertical-align: top; + margin: 0 0.5rem 0 0; +} + .table-title { padding: 0.5rem 0.75rem; font-weight: 600; @@ -46,4 +53,4 @@ tr.alt { tr:hover { background: color-mix(in srgb, var(--table-accent) 5%, transparent); -} +} \ No newline at end of file diff --git a/src/lib/components/tui/TuiAccordion.svelte b/src/lib/components/tui/TuiAccordion.svelte index 6d1f3a4..6a70e47 100644 --- a/src/lib/components/tui/TuiAccordion.svelte +++ b/src/lib/components/tui/TuiAccordion.svelte @@ -1,34 +1,55 @@ -
+
{#each items as item, i} - {@const contentSegments = parseColorText(item.content, $themeColors.colorMap)} + {@const contentSegments = parseColorText( + item.content, + $themeColors.colorMap, + )}
-
@@ -108,6 +151,25 @@ animation: slideDown 0.2s ease-out; } + .accordion-children { + margin-top: 0.5rem; + display: flex; + flex-direction: column; + gap: 0.25rem; + } + + .accordion-children.flex { + flex-direction: row; + flex-wrap: wrap; + gap: 1rem; + } + + .accordion-children.grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 1rem; + } + @keyframes slideDown { from { opacity: 0; diff --git a/src/lib/components/tui/TuiBody.svelte b/src/lib/components/tui/TuiBody.svelte index fbc631a..fc41934 100644 --- a/src/lib/components/tui/TuiBody.svelte +++ b/src/lib/components/tui/TuiBody.svelte @@ -1,56 +1,93 @@
{#each processedGroups as group, gi (gi)} - {#if group.kind === 'inline'} + {#if group.kind === "inline"}
{#each group.items as item (item.index)} 0}
- {user.username}@{user.hostname} - :~$ + {user.username}@{user.hostname} + :~$
{/if}
- - diff --git a/src/lib/components/tui/TuiButton.svelte b/src/lib/components/tui/TuiButton.svelte index 0d3accb..20e1ba4 100644 --- a/src/lib/components/tui/TuiButton.svelte +++ b/src/lib/components/tui/TuiButton.svelte @@ -2,21 +2,32 @@ import Icon from '@iconify/svelte'; import { getButtonStyle, parseColorText, getSegmentStyle } from './utils'; import { themeColors } from '$lib/stores/theme'; - import type { TerminalLine } from './types'; + import type { ButtonLine } from './types'; import '$lib/assets/css/tui-button.css'; - export let line: TerminalLine; - export let index: number; - export let selected: boolean; - export let onClick: (idx: number) => void; - export let onHover: (idx: number) => void; - export let inline: boolean = false; + interface Props { + line: ButtonLine; + index: number; + selected: boolean; + onClick: (idx: number) => void; + onHover: (idx: number) => void; + inline?: boolean; + } + + let { + line, + index, + selected, + onClick, + onHover, + inline = false + }: Props = $props(); // Determine if this is an external link - $: isExternal = line.external || (line.href && (line.href.startsWith('http://') || line.href.startsWith('https://'))); + const isExternal = $derived(line.external || (line.href && (line.href.startsWith('http://') || line.href.startsWith('https://')))); // Parse color formatting in content using theme colorMap - $: segments = parseColorText(line.content, $themeColors.colorMap); + const segments = $derived(parseColorText(line.content, $themeColors.colorMap)); @@ -30,5 +34,3 @@ {/if}
- - diff --git a/src/lib/components/tui/TuiGroup.svelte b/src/lib/components/tui/TuiGroup.svelte index 2c18844..b469f9c 100644 --- a/src/lib/components/tui/TuiGroup.svelte +++ b/src/lib/components/tui/TuiGroup.svelte @@ -1,23 +1,23 @@ -
+
{#each parsedChildren as parsed, idx (idx)} diff --git a/src/lib/components/tui/TuiHeader.svelte b/src/lib/components/tui/TuiHeader.svelte index 0783a36..ec3bafd 100644 --- a/src/lib/components/tui/TuiHeader.svelte +++ b/src/lib/components/tui/TuiHeader.svelte @@ -1,13 +1,24 @@
@@ -23,5 +34,3 @@ {/if}
- - diff --git a/src/lib/components/tui/TuiInput.svelte b/src/lib/components/tui/TuiInput.svelte index 246a21b..b29cd53 100644 --- a/src/lib/components/tui/TuiInput.svelte +++ b/src/lib/components/tui/TuiInput.svelte @@ -1,54 +1,64 @@ -
{/if} - +
{#if prefix} @@ -78,12 +88,12 @@ {#if suffix} {suffix} @@ -136,7 +146,8 @@ .input-wrapper:focus-within { border-color: var(--input-color); - box-shadow: 0 0 0 1px color-mix(in srgb, var(--input-color) 30%, transparent); + box-shadow: 0 0 0 1px + color-mix(in srgb, var(--input-color) 30%, transparent); } .tui-input.error .input-wrapper { diff --git a/src/lib/components/tui/TuiLine.svelte b/src/lib/components/tui/TuiLine.svelte index f35e9a7..d0559b0 100644 --- a/src/lib/components/tui/TuiLine.svelte +++ b/src/lib/components/tui/TuiLine.svelte @@ -1,24 +1,24 @@ {#if isBlank} - {#if inline}{:else}
{/if} + {#if inline}{:else}
{/if} {:else if isDivider}
{#if line.content}{line.content}{/if}
-{:else if line.type === 'button'} - -{:else if isLink} +{:else if line.type === "button"} + +{:else if line.type === "link"} {#if inline} handleNavigation(line)} /> {:else} - + {/if} -{:else if isTooltip} - {#if inline}{:else}
{/if} -{:else if line.type === 'card'} - -{:else if line.type === 'cardgrid'} - -{:else if line.type === 'progress'} +{:else if line.type === "tooltip"} + {#if inline}{:else}
+ +
{/if} +{:else if line.type === "card"} + +{:else if line.type === "cardgrid"} + +{:else if line.type === "progress"} -{:else if line.type === 'accordion'} - -{:else if line.type === 'table'} - -{:else if line.type === 'input'} +{:else if line.type === "accordion"} + +{:else if line.type === "table"} + +{:else if line.type === "input"} -{:else if line.type === 'textarea'} - -{:else if line.type === 'checkbox'} +{:else if line.type === "textarea"} + +{:else if line.type === "checkbox"} -{:else if line.type === 'radio'} - -{:else if line.type === 'select'} - -{:else if line.type === 'toggle'} +{:else if line.type === "radio"} + +{:else if line.type === "select"} + +{:else if line.type === "toggle"} -{:else if line.type === 'group'} +{:else if line.type === "group"} -{:else if isImage && showImage} +{:else if line.type === "image" && showImage}
- {line.imageAlt + {line.imageAlt {#if line.content}{line.content}{/if}
-{:else if !isImage} - {#if inline && isHeader} +{:else if line.type === "header"} + {#if inline} - {#each segments as seg}{#if seg.icon}{:else if getSegmentStyle(seg)}{seg.text}{:else}{seg.text}{/if}{/each} + {#each segments as seg}{#if seg.icon}{:else if getSegmentStyle(seg)}{seg.text}{:else}{seg.text}{/if}{/each} - {:else if inline} + {:else} +
+ + + {#each segments as seg}{#if seg.icon}{:else if getSegmentStyle(seg)}{seg.text}{:else}{seg.text}{/if}{/each} + + {#if showCursor}{/if} +
+ {/if} +{:else if line.type === "command" || line.type === "prompt"} + {#if inline} - {getLinePrefix(line.type)}{#each segments as seg}{#if seg.icon}{:else if getSegmentStyle(seg)}{seg.text}{:else}{seg.text}{/if}{/each} + {getLinePrefix(line.type)}{#each segments as seg}{#if seg.icon}{:else if getSegmentStyle(seg)}{seg.text}{:else}{seg.text}{/if}{/each} {:else}
- {#if isPrompt} - - {user.username}@{user.hostname} - :~$ - - {/if} - {#if isHeader} - - - {#each segments as seg}{#if seg.icon}{:else if getSegmentStyle(seg)}{seg.text}{:else}{seg.text}{/if}{/each} - - {:else} - - {getLinePrefix(line.type)}{#each segments as seg}{#if seg.icon}{:else if getSegmentStyle(seg)}{seg.text}{:else}{seg.text}{/if}{/each} - - {/if} + + {user.username}@{user.hostname} + :~$ + + + {getLinePrefix( + line.type, + )}{#each segments as seg}{#if seg.icon}{:else if getSegmentStyle(seg)}{seg.text}{:else}{seg.text}{/if}{/each} + + {#if showCursor}{/if} +
+ {/if} +{:else if !isImage} + {#if inline} + + {getLinePrefix(line.type)}{#each segments as seg}{#if seg.icon}{:else if getSegmentStyle(seg)}{seg.text}{:else}{seg.text}{/if}{/each} + + {:else} +
+ + {getLinePrefix( + line.type, + )}{#each segments as seg}{#if seg.icon}{:else if getSegmentStyle(seg)}{seg.text}{:else}{seg.text}{/if}{/each} + {#if showCursor}{/if}
{/if} diff --git a/src/lib/components/tui/TuiLink.svelte b/src/lib/components/tui/TuiLink.svelte index 72741b7..69ad3cb 100644 --- a/src/lib/components/tui/TuiLink.svelte +++ b/src/lib/components/tui/TuiLink.svelte @@ -1,25 +1,36 @@ {#if line.icon} {/if} -