Buckets:
gijl/lv-storage / llama.cpp /tools /ui /src /lib /components /app /content /CollapsibleContentBlock.svelte
| <script lang="ts"> | |
| import ChevronDown from '@lucide/svelte/icons/chevron-down'; | |
| import CollapsibleRegion from '$lib/components/app/misc/CollapsibleRegion.svelte'; | |
| import * as Collapsible from '$lib/components/ui/collapsible/index.js'; | |
| import { cn } from '$lib/components/ui/utils'; | |
| import { ICON_CLASS_DEFAULT } from '$lib/constants'; | |
| import type { Snippet } from 'svelte'; | |
| import type { Component } from 'svelte'; | |
| interface Props { | |
| open?: boolean; | |
| class?: string; | |
| icon?: Component; | |
| iconClass?: string; | |
| iconUrl?: string | null; | |
| title?: string; | |
| titleSnippet?: Snippet; | |
| subtitle?: string; | |
| shimmerTitle?: boolean; | |
| /** | |
| * `divider` centers the toggle between two rules, for blocks that mark a | |
| * section boundary in the conversation instead of decorating a message. | |
| */ | |
| variant?: 'default' | 'divider'; | |
| /** | |
| * Render the header only, without a chevron or a body, for a block that | |
| * has nothing to show yet and turns into a collapsible once it has. | |
| */ | |
| collapsible?: boolean; | |
| /** | |
| * Drop the reasoning-style rule and indent, for a body that spans the | |
| * message width on its own. | |
| */ | |
| flushBody?: boolean; | |
| onToggle?: () => void; | |
| children: Snippet; | |
| } | |
| let { | |
| children, | |
| class: className = '', | |
| collapsible = true, | |
| flushBody = false, | |
| icon: IconComponent, | |
| iconClass = ICON_CLASS_DEFAULT, | |
| iconUrl = null, | |
| onToggle, | |
| open = $bindable(false), | |
| shimmerTitle = false, | |
| subtitle, | |
| title = '', | |
| titleSnippet, | |
| variant = 'default' | |
| }: Props = $props(); | |
| function hideBrokenIcon(event: Event) { | |
| (event.currentTarget as HTMLImageElement).style.display = 'none'; | |
| } | |
| </script> | |
| {#snippet toggleIcon(layoutClass: string)} | |
| {#if iconUrl} | |
| <img | |
| alt="" | |
| class={cn('shrink-0 rounded-sm', layoutClass, iconClass)} | |
| onerror={hideBrokenIcon} | |
| src={iconUrl} | |
| /> | |
| {:else if IconComponent} | |
| <IconComponent class={cn('shrink-0 text-muted-foreground/60', layoutClass, iconClass)} /> | |
| {/if} | |
| {/snippet} | |
| {#snippet dividerRow()} | |
| <span aria-hidden="true" class="h-px min-w-4 flex-1 bg-current opacity-25"></span> | |
| <div class="flex min-w-0 items-center gap-1.5"> | |
| {@render toggleIcon('')} | |
| {@render toggleTitle('text-xs whitespace-nowrap')} | |
| {#if collapsible} | |
| <ChevronDown | |
| class={cn( | |
| 'size-3.5 shrink-0 text-muted-foreground/60 transition-transform duration-150 ease-out', | |
| open && 'rotate-180' | |
| )} | |
| /> | |
| {/if} | |
| </div> | |
| <span aria-hidden="true" class="h-px min-w-4 flex-1 bg-current opacity-25"></span> | |
| {/snippet} | |
| {#snippet toggleTitle(layoutClass: string)} | |
| <span | |
| class={cn( | |
| 'min-w-0 font-medium', | |
| layoutClass, | |
| shimmerTitle ? 'shimmer-text' : 'text-foreground/80' | |
| )} | |
| > | |
| {#if titleSnippet} | |
| { titleSnippet()} | |
| {:else} | |
| {title} | |
| {/if} | |
| </span> | |
| {#if subtitle} | |
| <span class="text-xs italic text-muted-foreground/70">{subtitle}</span> | |
| {/if} | |
| {/snippet} | |
| <Collapsible.Root | |
| class={cn('group/collapsible', 'my-0!', className)} | |
| onOpenChange={(value) => { | |
| open = value; | |
| onToggle?.(); | |
| }} | |
| {open} | |
| > | |
| {#if variant === 'divider' && !collapsible} | |
| <div class="flex w-full items-center gap-2 py-1.5 text-muted-foreground"> | |
| { dividerRow()} | |
| </div> | |
| {:else if variant === 'divider'} | |
| <Collapsible.Trigger | |
| class="flex w-full cursor-pointer items-center gap-2 py-1.5 text-muted-foreground" | |
| > | |
| { dividerRow()} | |
| <span class="sr-only">Toggle content</span> | |
| </Collapsible.Trigger> | |
| {:else} | |
| <Collapsible.Trigger | |
| class={cn( | |
| 'flex w-full cursor-pointer items-start justify-between gap-2 text-left', | |
| 'py-1.5 pr-1' | |
| )} | |
| > | |
| <div class="flex min-w-0 items-start gap-2 text-muted-foreground"> | |
| {@render toggleIcon('mt-0.75')} | |
| {@render toggleTitle('overflow-x-auto text-sm')} | |
| </div> | |
| <ChevronDown | |
| class={cn( | |
| 'size-4 shrink-0 text-muted-foreground/60 transition-all duration-150 ease-out opacity-0 group-hover/collapsible:opacity-100 mt-0.75', | |
| open && 'rotate-180' | |
| )} | |
| /> | |
| <span class="sr-only">Toggle content</span> | |
| </Collapsible.Trigger> | |
| {/if} | |
| {#if collapsible} | |
| <!-- Content is force-mounted so the trigger keeps a real `aria-controls` target; | |
| the region owns opening and closing. It animates the height instead of | |
| snapping it, and unmounts the body once the collapse is over - a closed | |
| block still costs nothing, which matters while tokens stream past it. --> | |
| <Collapsible.Content forceMount> | |
| <CollapsibleRegion {open}> | |
| <div class="pl-1.5 grid min-w-0" style="min-height: var(--min-message-height);"> | |
| <div | |
| class={cn( | |
| 'min-w-0 pb-2 my-2', | |
| !flushBody && 'border-l border-muted-foreground/20 pl-4' | |
| )} | |
| > | |
| {@render children()} | |
| </div> | |
| </div> | |
| </CollapsibleRegion> | |
| </Collapsible.Content> | |
| {/if} | |
| </Collapsible.Root> | |
Xet Storage Details
- Size:
- 4.78 kB
- Xet hash:
- 3f750aeadf3b3ef86d2d46b9837df443f09927fdbbbd096188e988aa43df6ff9
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.