@kouji-ui/components 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,10 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { ChangeDetectionStrategy, ViewEncapsulation, Component, input, inject, Injectable, booleanAttribute, computed, TemplateRef, viewChild, ElementRef, contentChildren, contentChild, model, ViewChild, PLATFORM_ID, DestroyRef, afterNextRender, effect, signal, Injector, untracked, output, EventEmitter, Output, makeEnvironmentProviders, provideEnvironmentInitializer, forwardRef, EnvironmentInjector, Directive, InjectionToken, ViewContainerRef, numberAttribute } from '@angular/core';
2
+ import { ChangeDetectionStrategy, ViewEncapsulation, Component, input, inject, Injectable, booleanAttribute, computed, TemplateRef, viewChild, ElementRef, contentChildren, contentChild, model, ViewChild, PLATFORM_ID, DestroyRef, afterNextRender, effect, signal, SecurityContext, Injector, untracked, output, EventEmitter, Output, makeEnvironmentProviders, provideEnvironmentInitializer, forwardRef, EnvironmentInjector, Directive, InjectionToken, ViewContainerRef, numberAttribute } from '@angular/core';
3
3
  import * as i1 from '@kouji-ui/core';
4
- import { KjAccordion, KjAccordionTrigger, KjAccordionItem, KjAccordionContent, SHEET_DATA, KjSheetRef, KjSheet, KjIconDirective, KjSheetService, KjAlert, KjAlertIcon, KjAlertTitle, KjAlertDescription, KjAlertActions, KjAlertDismiss, KJ_AVATAR_GROUP, KjAvatarImage, KjAvatarFallback, KjAvatar, KjPopoverTrigger, KjPopoverContent, KjPopoverArrow, KjPopoverClose, KjPopoverTitle, KjTranslateService, KjOverflowContent, KjAvatarGroup, KjBadge, KjBreadcrumb, KjSize, KjBreadcrumbList, KjBreadcrumbItem, KJ_BREADCRUMB_CONFIG, KjLink, KjBreadcrumbCurrent, KjBreadcrumbSeparator, KjBreadcrumbEllipsis, KjButton, KjButtonGroup, KjCalendar, KjCalendarHeader, KjCalendarGrid, KjCalendarDay, KjCarousel, KjLiveRegion, KjVisuallyHidden, KjCarouselViewport, KjCarouselSlide, KjCarouselPrevious, KjCarouselNext, KJ_CAROUSEL, KjCarouselIndicator, KjCarouselIndicators, KjCarouselAutoplay, KjCarouselPauseToggle, KjChatLog, KjChat, KjChatAvatar, KjChatHeader, KjChatBubble, KjChatFooter, KjChatAnnouncer, parseSlash, matchSlashCommands, KjCheckbox, KjColorPicker, KjColorPickerTrigger, KjColorPickerPanel, KjColorPickerArea, KjColorPickerHueSlider, KjColorPickerAlphaSlider, KjColorPickerInput, KjComboboxInput, KjComboboxListbox, KjCombobox, KjComboboxOption, KjConfirmPopup, KjConfirmPopupTrigger, KjConfirmPopupContent, KjConfirmPopupMessage, KjConfirmPopupAction, KjConfirmPopupCancel, KjDatePicker, KjDatePickerTrigger, KjDatePickerCalendar, defaultDateRangePresets, KjDateRangePresets, KjDateRangePresetOption, KjLocale, KjDropdownMenuTrigger, KjDropdownMenuContent, KjDropdownMenuItem, KjDropdownMenuSeparator, KjDropdownMenuLabel, KjDropdownMenuGroup, KjSpinner, KjVariant, KjEditorLoader, KjEditor, KJ_SIZE_PRESET, KjField, KjFieldLabel, KjFieldHelp, KjFieldError, KjFieldGroup, KjFileUpload, KjFileUploadTrigger, KjFileUploadDropzone, KjFileUploadList, KjFileUploadItem, KjProgressBar, KjProgressBarFill, KjForm, KjFormErrorSummary, KJ_ICON_REGISTRY, KjInput, KjInputGroup, KjInputGroupAddon, KjInputMask, KJ_INPUT_MASK_PRESETS, KjInputOtp, KjInputOtpCell, KjKbd, KJ_KBD_SIZE_PRESET, KjDivider, KJ_VARIANT_PRESET, KjList, KjListRow, KjMenubar, KjMenubarItem, KjNumberInput, KjNumberInputGroup, KjNumberStepper, KJ_OVERLAY_BADGE, KjOverlayBadgeContent, KjOverlayBadge, KjPagination, KjPaginationItem, KjPaginationPrevious, KjPaginationNext, KjPaginationFirst, KjPaginationLast, KjPaginationEllipsis, KjPaginationInfo, KJ_PASSWORD_INPUT, KjPasswordInput, KjPasswordInputScope, KjPasswordToggle, KjPasswordStrength, KjPasswordCapsLockWarning, KjRadioGroup, KjRadio, injectRteOverlayData, createKjImageNode, KJ_RTE_OVERLAY_DATA, KjRichTextEditor, KjRovingTabindex, KjRovingTabindexItemDirective, KjSelect, KjSelectTrigger, KjSelectContent, KjOption, KjSkeleton, KjSkipLink, KjSlider, KjSliderTrack, KjSliderRange, KjSliderThumb, KjSpeedDial, KjSpeedDialTrigger, KjSpeedDialActions, KjSpeedDialAction, KjStepper, KjStep, KjStepLabel, KjStepContent, KjStepperNext, KjStepperPrevious, KjStepperReset, KjToggle, KJ_TABLE, provideKjFilterParams, KjTable, KJ_TABLE_STORAGE, KjTableHeader, KjTableRow, KjTableCell, KjTableKeyboardNav, DRAWER_DATA, KjDrawer, KjDrawerService, bindPresets, KJ_TABS_CONFIG, KjTabs, KjTabList, KjTab, KjTabPanel, KjTag, KjTagRemove, KjTagList, KjTextarea, KjTimePicker, KjTimePickerHours, KjTimePickerMinutes, KjTimePickerSeconds, KjTimePickerMeridiem, KjToastViewport, KjToast, KjToastClose, KjTooltipTrigger, KjTooltipContent, KjTooltipArrow, KjTooltipGroup, KjCascadeSelect, KjCascadeSelectTrigger, KjCascadeSelectPanel, KjCascadeSelectOption, KjCascadeSelectSubPanel, KjCommandPalette, KjCommandInput, KjCommandList, KjCommandEmpty, injectListItem, KjListItem, KjCommandGroup, KjCommandSeparator, KjTreeSelectNode, KjTreeSelectToggle, KjTreeSelect, KjTreeSelectTrigger, KjTreeSelectContent } from '@kouji-ui/core';
4
+ import { KjAccordion, KjAccordionTrigger, KjAccordionItem, KjAccordionContent, SHEET_DATA, KjSheetRef, KjSheet, KjIconDirective, KjSheetService, KjAlert, KjAlertIcon, KjAlertTitle, KjAlertDescription, KjAlertActions, KjAlertDismiss, KJ_AVATAR_GROUP, KjAvatarImage, KjAvatarFallback, KjAvatar, KjPopoverTrigger, KjPopoverContent, KjPopoverArrow, KjPopoverClose, KjPopoverTitle, KjTranslateService, KjOverflowContent, KjAvatarGroup, KjBadge, KjBreadcrumb, KjSize, KjBreadcrumbList, KjBreadcrumbItem, KJ_BREADCRUMB_CONFIG, KjLink, KjBreadcrumbCurrent, KjBreadcrumbSeparator, KjBreadcrumbEllipsis, KjButton, KjButtonGroup, KjCalendar, KjCalendarHeader, KjCalendarGrid, KjCalendarDay, KjCarousel, KjLiveRegion, KjVisuallyHidden, KjCarouselViewport, KjCarouselSlide, KjCarouselPrevious, KjCarouselNext, KJ_CAROUSEL, KjCarouselIndicator, KjCarouselIndicators, KjCarouselAutoplay, KjCarouselPauseToggle, KjChatLog, KjChat, KjChatAvatar, KjChatHeader, KjChatBubble, KjChatFooter, KjChatAnnouncer, KJ_CHAT_CONFIG, parseSlash, matchSlashCommands, KjCheckbox, KjColorPicker, KjColorPickerTrigger, KjColorPickerPanel, KjColorPickerArea, KjColorPickerHueSlider, KjColorPickerAlphaSlider, KjColorPickerInput, KjComboboxInput, KjComboboxListbox, KjCombobox, KjComboboxOption, KjConfirmPopup, KjConfirmPopupTrigger, KjConfirmPopupContent, KjConfirmPopupMessage, KjConfirmPopupAction, KjConfirmPopupCancel, KjDatePicker, KjDatePickerTrigger, KjDatePickerCalendar, defaultDateRangePresets, KjDateRangePresets, KjDateRangePresetOption, KjLocale, KjDropdownMenuTrigger, KjDropdownMenuContent, KjDropdownMenuItem, KjDropdownMenuSeparator, KjDropdownMenuLabel, KjDropdownMenuGroup, KjSpinner, KjVariant, KjEditorLoader, KjEditor, KJ_SIZE_PRESET, KjField, KjFieldLabel, KjFieldHelp, KjFieldError, KjFieldGroup, KjFileUpload, KjFileUploadTrigger, KjFileUploadDropzone, KjFileUploadList, KjFileUploadItem, KjProgressBar, KjProgressBarFill, KjForm, KjFormErrorSummary, KJ_ICON_REGISTRY, KjInput, KjInputGroup, KjInputGroupAddon, KjInputMask, KJ_INPUT_MASK_PRESETS, KjInputOtp, KjInputOtpCell, KjKbd, KJ_KBD_SIZE_PRESET, KjDivider, KJ_VARIANT_PRESET, KjList, KjListRow, KjMenubar, KjMenubarItem, KjNumberInput, KjNumberInputGroup, KjNumberStepper, KJ_OVERLAY_BADGE, KjOverlayBadgeContent, KjOverlayBadge, KjPagination, KjPaginationItem, KjPaginationPrevious, KjPaginationNext, KjPaginationFirst, KjPaginationLast, KjPaginationEllipsis, KjPaginationInfo, KJ_PASSWORD_INPUT, KjPasswordInput, KjPasswordInputScope, KjPasswordToggle, KjPasswordStrength, KjPasswordCapsLockWarning, KjRadioGroup, KjRadio, injectRteOverlayData, createKjImageNode, KJ_RTE_OVERLAY_DATA, KjRichTextEditor, KjRovingTabindex, KjRovingTabindexItemDirective, KjSelect, KjSelectTrigger, KjSelectContent, KjOption, KjSkeleton, KjSkipLink, KjSlider, KjSliderTrack, KjSliderRange, KjSliderThumb, KjSpeedDial, KjSpeedDialTrigger, KjSpeedDialActions, KjSpeedDialAction, KjStepper, KjStep, KjStepLabel, KjStepContent, KjStepperNext, KjStepperPrevious, KjStepperReset, KjToggle, KJ_TABLE, provideKjFilterParams, KjTable, KJ_TABLE_STORAGE, KjTableHeader, KjTableRow, KjTableCell, KjTableKeyboardNav, DRAWER_DATA, KjDrawer, KjDrawerService, bindPresets, KJ_TABS_CONFIG, KjTabs, KjTabList, KjTab, KjTabPanel, KjTag, KjTagRemove, KjTagList, KjTextarea, KjTimePicker, KjTimePickerHours, KjTimePickerMinutes, KjTimePickerSeconds, KjTimePickerMeridiem, KjToastViewport, KjToast, KjToastClose, KjTooltipTrigger, KjTooltipContent, KjTooltipArrow, KjTooltipGroup, KjCascadeSelect, KjCascadeSelectTrigger, KjCascadeSelectPanel, KjCascadeSelectOption, KjCascadeSelectSubPanel, KjCommandPalette, KjCommandInput, KjCommandList, KjCommandEmpty, injectListItem, KjListItem, KjCommandGroup, KjCommandSeparator, KjTreeSelectNode, KjTreeSelectToggle, KjTreeSelect, KjTreeSelectTrigger, KjTreeSelectContent } from '@kouji-ui/core';
5
5
  export { KJ_DROPDOWN_MENU, KjBlockquote, KjCarousel, KjCarouselSlide, KjCascadeSelectOption, KjChatAnnouncer, KjChatStore, KjCode, KjColorPicker, KjCombobox, KjDatePicker, KjDialog, KjDialogRef, KjDialogService, KjDrawer, KjDrawerRef, KjDrawerService, KjDropdownMenuContent, KjDropdownMenuGroup, KjDropdownMenuItem, KjDropdownMenuLabel, KjDropdownMenuSeparator, KjDropdownMenuTrigger, KjFileUpload, KjInput, KjLead, KjLiveRegion, KjMuted, KjOverlayPanel, KjPopoverArrow, KjPopoverClose, KjPopoverContent, KjPopoverTitle, KjPopoverTrigger, KjRichTextEditor, KjSheet, KjSheetRef, KjSheetService, KjTabPanel, KjTable, KjTooltipArrow, KjTooltipContent, KjTooltipGroup, KjTooltipTrigger, KjTreeSelect, KjTruncate, SHEET_ARIA_LABEL, SHEET_DATA, SHEET_DETENT, SHEET_DISMISSIBLE } from '@kouji-ui/core';
6
6
  import { NgTemplateOutlet, isPlatformBrowser, NgComponentOutlet } from '@angular/common';
7
7
  import { DomSanitizer } from '@angular/platform-browser';
8
+ import { marked } from 'marked';
8
9
  import * as i1$1 from '@angular/forms';
9
10
  import { NG_VALUE_ACCESSOR, FormsModule, FormControl, Validators, ReactiveFormsModule } from '@angular/forms';
10
11
  import * as lucideIcons from 'lucide-static';
@@ -1781,7 +1782,7 @@ class KjButtonComponent {
1781
1782
  <ng-content />
1782
1783
  }
1783
1784
  </button>
1784
- `, isInline: true, styles: ["@layer kj.component{.kj-button{display:inline-flex;align-items:center;justify-content:var(--kj-button-justify, center);gap:var(--kj-button-gap, var(--kj-space-sm));min-width:var(--kj-button-min-width, auto);height:var(--kj-button-height, auto);align-self:center;background:var(--kj-button-bg, var(--kj-bg-primary));color:var(--kj-button-fg, var(--kj-fg-on-primary));border:var(--kj-button-border-width, var(--kj-border)) var(--kj-button-border-style, solid) var(--kj-button-border-color, transparent);border-radius:var(--kj-button-radius, var(--kj-radius-field));padding:var(--kj-button-padding-y, var(--kj-space-sm)) var(--kj-button-padding-x, var(--kj-space-md));font:var(--kj-button-font-weight, 600) var(--kj-button-font-size, var(--kj-text-sm)) / 1.2 var(--kj-button-font, var(--kj-font-sans));letter-spacing:var(--kj-button-letter-spacing, -.01em);transition:color var(--kj-transition),border-color var(--kj-transition),box-shadow var(--kj-transition),transform var(--kj-transition),opacity var(--kj-transition);cursor:pointer;text-decoration:none}.kj-button[data-variant=destructive]{--kj-button-bg: var(--kj-bg-danger);--kj-button-fg: var(--kj-fg-on-danger)}.kj-button[data-variant=destructive]:hover{--kj-button-bg: color-mix(in oklch, var(--kj-bg-danger), black 8%)}.kj-button[data-variant=ghost]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-default);--kj-button-border-color: transparent}.kj-button[data-variant=ghost]:hover{--kj-button-bg: color-mix(in oklab, var(--kj-fg-default) 10%, transparent)}.kj-button[data-variant=outline]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-default);--kj-button-border-color: var(--kj-border-default)}.kj-button[data-variant=outline]:hover{--kj-button-bg: var(--kj-bg-surface)}.kj-button[data-variant=link]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-primary);--kj-button-padding-x: 0;--kj-button-padding-y: 0;--kj-button-shadow: none;--kj-button-shadow-hover: none;--kj-button-shadow-active: none;text-decoration:underline;text-underline-offset:4px}.kj-button[data-variant=link]:hover{--kj-button-bg: transparent;--kj-button-fg: color-mix(in oklch, var(--kj-fg-primary), black 8%)}.kj-button[data-size=xs]{--kj-button-padding-x: var(--kj-space-sm);--kj-button-padding-y: 0;--kj-button-font-size: .75rem;--kj-button-height: var(--kj-ctl-h-xs)}.kj-button[data-size=sm]{--kj-button-padding-x: var(--kj-space-sm);--kj-button-padding-y: .25rem;--kj-button-font-size: .8125rem;--kj-button-height: var(--kj-ctl-h-sm)}.kj-button[data-size=md]{--kj-button-height: var(--kj-ctl-h-md)}.kj-button[data-size=lg]{--kj-button-padding-x: var(--kj-space-lg);--kj-button-padding-y: .625rem;--kj-button-font-size: 1rem;--kj-button-height: var(--kj-ctl-h-lg)}.kj-button[data-size=xl]{--kj-button-padding-x: var(--kj-space-2xl);--kj-button-padding-y: .875rem;--kj-button-font-size: 1.0625rem;--kj-button-gap: var(--kj-space-md);--kj-button-height: var(--kj-ctl-h-xl)}.kj-button[data-size=icon]{--kj-button-padding-x: var(--kj-space-sm);--kj-button-padding-y: var(--kj-space-sm);--kj-button-height: var(--kj-ctl-h-md);aspect-ratio:1}.kj-button[data-full=true]{width:100%}.kj-button:hover{--kj-button-bg: var(--kj-button-bg-hover, color-mix(in oklch, var(--kj-bg-primary), black 8%));transform:translateY(var(--kj-button-hover-translate, var(--kj-hover-translate, -2px)))}.kj-button[data-variant=default]:hover{--kj-button-fg: var(--kj-button-fg-hover, var(--kj-fg-on-primary))}.kj-button[data-variant=default]{box-shadow:var(--kj-button-shadow, none)}.kj-button[data-variant=default]:hover{box-shadow:var(--kj-button-shadow-hover, var(--kj-button-shadow, none))}.kj-button[data-variant=default]:active{box-shadow:var(--kj-button-shadow-active, var(--kj-button-shadow, none))}.kj-button:active{transform:var(--kj-button-translate-active, none)}.kj-button:focus-visible{outline:2px solid var(--kj-border-focus);outline-offset:2px}.kj-button[aria-disabled=true]{opacity:.45;cursor:not-allowed;pointer-events:none}.kj-button[data-size=icon]{min-width:2.75rem;min-height:2.75rem}.kj-button[aria-busy=true]{opacity:.7;cursor:progress}.kj-button[aria-pressed=true]{--kj-button-bg: var(--kj-bg-field);box-shadow:inset 0 1px 2px #0000001f}.kj-button__spinner{display:inline-block;width:1em;height:1em;vertical-align:-.125em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:kj-button-spinner-rotate .6s linear infinite}}@keyframes kj-button-spinner-rotate{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: KjButton, selector: "[kjButton]", inputs: ["kjDisabled", "kjLoading", "kjFullWidth", "kjPressed"], outputs: ["kjPressedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1785
+ `, isInline: true, styles: ["@layer kj.component{.kj-button{display:inline-flex;align-items:center;justify-content:var(--kj-button-justify, center);gap:var(--kj-button-gap, var(--kj-space-sm));min-width:var(--kj-button-min-width, auto);height:var(--kj-button-height, auto);align-self:center;background:var(--kj-button-bg, var(--kj-bg-primary));color:var(--kj-button-fg, var(--kj-fg-on-primary));border:var(--kj-button-border-width, var(--kj-border)) var(--kj-button-border-style, solid) var(--kj-button-border-color, transparent);border-radius:var(--kj-button-radius, var(--kj-radius-field));padding:var(--kj-button-padding-y, var(--kj-space-sm)) var(--kj-button-padding-x, var(--kj-space-md));font:var(--kj-button-font-weight, 600) var(--kj-button-font-size, var(--kj-text-sm)) / 1.2 var(--kj-button-font, var(--kj-font-sans));letter-spacing:var(--kj-button-letter-spacing, -.01em);text-transform:var(--kj-button-text-transform, none);transition:color var(--kj-transition),border-color var(--kj-transition),box-shadow var(--kj-transition),transform var(--kj-transition),opacity var(--kj-transition);cursor:pointer;text-decoration:none}.kj-button[data-variant=destructive]{--kj-button-bg: var(--kj-bg-danger);--kj-button-fg: var(--kj-fg-on-danger)}.kj-button[data-variant=destructive]:hover{--kj-button-bg: color-mix(in oklch, var(--kj-bg-danger), black 8%)}.kj-button[data-variant=ghost]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-default);--kj-button-border-color: transparent}.kj-button[data-variant=ghost]:hover{--kj-button-bg: color-mix(in oklab, var(--kj-fg-default) 10%, transparent)}.kj-button[data-variant=outline]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-default);--kj-button-border-color: var(--kj-border-default)}.kj-button[data-variant=outline]:hover{--kj-button-bg: var(--kj-bg-surface)}.kj-button[data-variant=segmented]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-muted, var(--kj-fg-default));--kj-button-border-color: transparent;--kj-button-border-width: 0;--kj-button-radius: 0;--kj-button-hover-translate: none}.kj-button[data-variant=segmented]:hover{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-default)}.kj-button[data-variant=segmented][aria-pressed=true]{--kj-button-bg: var(--kj-segmented-bg-on, var(--kj-bg-surface));--kj-button-fg: var(--kj-segmented-fg-on, var(--kj-fg-default));cursor:default}.kj-button[data-variant=segmented][aria-pressed=true]:hover{--kj-button-bg: var(--kj-segmented-bg-on, var(--kj-bg-surface));--kj-button-fg: var(--kj-segmented-fg-on, var(--kj-fg-default))}.kj-button[data-variant=link]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-primary);--kj-button-padding-x: 0;--kj-button-padding-y: 0;--kj-button-shadow: none;--kj-button-shadow-hover: none;--kj-button-shadow-active: none;text-decoration:underline;text-underline-offset:4px}.kj-button[data-variant=link]:hover{--kj-button-bg: transparent;--kj-button-fg: color-mix(in oklch, var(--kj-fg-primary), black 8%)}.kj-button[data-size=xs]{--kj-button-padding-x: var(--kj-space-sm);--kj-button-padding-y: 0;--kj-button-font-size: .75rem;--kj-button-height: var(--kj-ctl-h-xs)}.kj-button[data-size=sm]{--kj-button-padding-x: var(--kj-space-sm);--kj-button-padding-y: .25rem;--kj-button-font-size: .8125rem;--kj-button-height: var(--kj-ctl-h-sm)}.kj-button[data-size=md]{--kj-button-height: var(--kj-ctl-h-md)}.kj-button[data-size=lg]{--kj-button-padding-x: var(--kj-space-lg);--kj-button-padding-y: .625rem;--kj-button-font-size: 1rem;--kj-button-height: var(--kj-ctl-h-lg)}.kj-button[data-size=xl]{--kj-button-padding-x: var(--kj-space-2xl);--kj-button-padding-y: .875rem;--kj-button-font-size: 1.0625rem;--kj-button-gap: var(--kj-space-md);--kj-button-height: var(--kj-ctl-h-xl)}.kj-button[data-size=icon]{--kj-button-padding-x: var(--kj-space-sm);--kj-button-padding-y: var(--kj-space-sm);--kj-button-height: var(--kj-ctl-h-md);aspect-ratio:1}.kj-button[data-full=true]{width:100%}.kj-button:hover{--kj-button-bg: var(--kj-button-bg-hover, color-mix(in oklch, var(--kj-bg-primary), black 8%));transform:translateY(var(--kj-button-hover-translate, var(--kj-hover-translate, -2px)))}.kj-button[data-variant=default]:hover{--kj-button-fg: var(--kj-button-fg-hover, var(--kj-fg-on-primary))}.kj-button[data-variant=default]{box-shadow:var(--kj-button-shadow, none)}.kj-button[data-variant=default]:hover{box-shadow:var(--kj-button-shadow-hover, var(--kj-button-shadow, none))}.kj-button[data-variant=default]:active{box-shadow:var(--kj-button-shadow-active, var(--kj-button-shadow, none))}.kj-button:active{transform:var(--kj-button-translate-active, none)}.kj-button:focus-visible{outline:2px solid var(--kj-border-focus);outline-offset:2px}.kj-button[aria-disabled=true]{opacity:.45;cursor:not-allowed;pointer-events:none}.kj-button[data-size=icon]{min-width:2.75rem;min-height:2.75rem}.kj-button[aria-busy=true]{opacity:.7;cursor:progress}.kj-button[aria-pressed=true]{--kj-button-bg: var(--kj-bg-field);box-shadow:inset 0 1px 2px #0000001f}.kj-button__spinner{display:inline-block;width:1em;height:1em;vertical-align:-.125em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:kj-button-spinner-rotate .6s linear infinite}}@keyframes kj-button-spinner-rotate{to{transform:rotate(360deg)}}\n"], dependencies: [{ kind: "directive", type: KjButton, selector: "[kjButton]", inputs: ["kjDisabled", "kjLoading", "kjFullWidth", "kjPressed"], outputs: ["kjPressedChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1785
1786
  }
1786
1787
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjButtonComponent, decorators: [{
1787
1788
  type: Component,
@@ -1804,7 +1805,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1804
1805
  <ng-content />
1805
1806
  }
1806
1807
  </button>
1807
- `, encapsulation: ViewEncapsulation.None, host: { style: 'display: contents;' }, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["@layer kj.component{.kj-button{display:inline-flex;align-items:center;justify-content:var(--kj-button-justify, center);gap:var(--kj-button-gap, var(--kj-space-sm));min-width:var(--kj-button-min-width, auto);height:var(--kj-button-height, auto);align-self:center;background:var(--kj-button-bg, var(--kj-bg-primary));color:var(--kj-button-fg, var(--kj-fg-on-primary));border:var(--kj-button-border-width, var(--kj-border)) var(--kj-button-border-style, solid) var(--kj-button-border-color, transparent);border-radius:var(--kj-button-radius, var(--kj-radius-field));padding:var(--kj-button-padding-y, var(--kj-space-sm)) var(--kj-button-padding-x, var(--kj-space-md));font:var(--kj-button-font-weight, 600) var(--kj-button-font-size, var(--kj-text-sm)) / 1.2 var(--kj-button-font, var(--kj-font-sans));letter-spacing:var(--kj-button-letter-spacing, -.01em);transition:color var(--kj-transition),border-color var(--kj-transition),box-shadow var(--kj-transition),transform var(--kj-transition),opacity var(--kj-transition);cursor:pointer;text-decoration:none}.kj-button[data-variant=destructive]{--kj-button-bg: var(--kj-bg-danger);--kj-button-fg: var(--kj-fg-on-danger)}.kj-button[data-variant=destructive]:hover{--kj-button-bg: color-mix(in oklch, var(--kj-bg-danger), black 8%)}.kj-button[data-variant=ghost]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-default);--kj-button-border-color: transparent}.kj-button[data-variant=ghost]:hover{--kj-button-bg: color-mix(in oklab, var(--kj-fg-default) 10%, transparent)}.kj-button[data-variant=outline]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-default);--kj-button-border-color: var(--kj-border-default)}.kj-button[data-variant=outline]:hover{--kj-button-bg: var(--kj-bg-surface)}.kj-button[data-variant=link]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-primary);--kj-button-padding-x: 0;--kj-button-padding-y: 0;--kj-button-shadow: none;--kj-button-shadow-hover: none;--kj-button-shadow-active: none;text-decoration:underline;text-underline-offset:4px}.kj-button[data-variant=link]:hover{--kj-button-bg: transparent;--kj-button-fg: color-mix(in oklch, var(--kj-fg-primary), black 8%)}.kj-button[data-size=xs]{--kj-button-padding-x: var(--kj-space-sm);--kj-button-padding-y: 0;--kj-button-font-size: .75rem;--kj-button-height: var(--kj-ctl-h-xs)}.kj-button[data-size=sm]{--kj-button-padding-x: var(--kj-space-sm);--kj-button-padding-y: .25rem;--kj-button-font-size: .8125rem;--kj-button-height: var(--kj-ctl-h-sm)}.kj-button[data-size=md]{--kj-button-height: var(--kj-ctl-h-md)}.kj-button[data-size=lg]{--kj-button-padding-x: var(--kj-space-lg);--kj-button-padding-y: .625rem;--kj-button-font-size: 1rem;--kj-button-height: var(--kj-ctl-h-lg)}.kj-button[data-size=xl]{--kj-button-padding-x: var(--kj-space-2xl);--kj-button-padding-y: .875rem;--kj-button-font-size: 1.0625rem;--kj-button-gap: var(--kj-space-md);--kj-button-height: var(--kj-ctl-h-xl)}.kj-button[data-size=icon]{--kj-button-padding-x: var(--kj-space-sm);--kj-button-padding-y: var(--kj-space-sm);--kj-button-height: var(--kj-ctl-h-md);aspect-ratio:1}.kj-button[data-full=true]{width:100%}.kj-button:hover{--kj-button-bg: var(--kj-button-bg-hover, color-mix(in oklch, var(--kj-bg-primary), black 8%));transform:translateY(var(--kj-button-hover-translate, var(--kj-hover-translate, -2px)))}.kj-button[data-variant=default]:hover{--kj-button-fg: var(--kj-button-fg-hover, var(--kj-fg-on-primary))}.kj-button[data-variant=default]{box-shadow:var(--kj-button-shadow, none)}.kj-button[data-variant=default]:hover{box-shadow:var(--kj-button-shadow-hover, var(--kj-button-shadow, none))}.kj-button[data-variant=default]:active{box-shadow:var(--kj-button-shadow-active, var(--kj-button-shadow, none))}.kj-button:active{transform:var(--kj-button-translate-active, none)}.kj-button:focus-visible{outline:2px solid var(--kj-border-focus);outline-offset:2px}.kj-button[aria-disabled=true]{opacity:.45;cursor:not-allowed;pointer-events:none}.kj-button[data-size=icon]{min-width:2.75rem;min-height:2.75rem}.kj-button[aria-busy=true]{opacity:.7;cursor:progress}.kj-button[aria-pressed=true]{--kj-button-bg: var(--kj-bg-field);box-shadow:inset 0 1px 2px #0000001f}.kj-button__spinner{display:inline-block;width:1em;height:1em;vertical-align:-.125em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:kj-button-spinner-rotate .6s linear infinite}}@keyframes kj-button-spinner-rotate{to{transform:rotate(360deg)}}\n"] }]
1808
+ `, encapsulation: ViewEncapsulation.None, host: { style: 'display: contents;' }, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["@layer kj.component{.kj-button{display:inline-flex;align-items:center;justify-content:var(--kj-button-justify, center);gap:var(--kj-button-gap, var(--kj-space-sm));min-width:var(--kj-button-min-width, auto);height:var(--kj-button-height, auto);align-self:center;background:var(--kj-button-bg, var(--kj-bg-primary));color:var(--kj-button-fg, var(--kj-fg-on-primary));border:var(--kj-button-border-width, var(--kj-border)) var(--kj-button-border-style, solid) var(--kj-button-border-color, transparent);border-radius:var(--kj-button-radius, var(--kj-radius-field));padding:var(--kj-button-padding-y, var(--kj-space-sm)) var(--kj-button-padding-x, var(--kj-space-md));font:var(--kj-button-font-weight, 600) var(--kj-button-font-size, var(--kj-text-sm)) / 1.2 var(--kj-button-font, var(--kj-font-sans));letter-spacing:var(--kj-button-letter-spacing, -.01em);text-transform:var(--kj-button-text-transform, none);transition:color var(--kj-transition),border-color var(--kj-transition),box-shadow var(--kj-transition),transform var(--kj-transition),opacity var(--kj-transition);cursor:pointer;text-decoration:none}.kj-button[data-variant=destructive]{--kj-button-bg: var(--kj-bg-danger);--kj-button-fg: var(--kj-fg-on-danger)}.kj-button[data-variant=destructive]:hover{--kj-button-bg: color-mix(in oklch, var(--kj-bg-danger), black 8%)}.kj-button[data-variant=ghost]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-default);--kj-button-border-color: transparent}.kj-button[data-variant=ghost]:hover{--kj-button-bg: color-mix(in oklab, var(--kj-fg-default) 10%, transparent)}.kj-button[data-variant=outline]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-default);--kj-button-border-color: var(--kj-border-default)}.kj-button[data-variant=outline]:hover{--kj-button-bg: var(--kj-bg-surface)}.kj-button[data-variant=segmented]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-muted, var(--kj-fg-default));--kj-button-border-color: transparent;--kj-button-border-width: 0;--kj-button-radius: 0;--kj-button-hover-translate: none}.kj-button[data-variant=segmented]:hover{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-default)}.kj-button[data-variant=segmented][aria-pressed=true]{--kj-button-bg: var(--kj-segmented-bg-on, var(--kj-bg-surface));--kj-button-fg: var(--kj-segmented-fg-on, var(--kj-fg-default));cursor:default}.kj-button[data-variant=segmented][aria-pressed=true]:hover{--kj-button-bg: var(--kj-segmented-bg-on, var(--kj-bg-surface));--kj-button-fg: var(--kj-segmented-fg-on, var(--kj-fg-default))}.kj-button[data-variant=link]{--kj-button-bg: transparent;--kj-button-fg: var(--kj-fg-primary);--kj-button-padding-x: 0;--kj-button-padding-y: 0;--kj-button-shadow: none;--kj-button-shadow-hover: none;--kj-button-shadow-active: none;text-decoration:underline;text-underline-offset:4px}.kj-button[data-variant=link]:hover{--kj-button-bg: transparent;--kj-button-fg: color-mix(in oklch, var(--kj-fg-primary), black 8%)}.kj-button[data-size=xs]{--kj-button-padding-x: var(--kj-space-sm);--kj-button-padding-y: 0;--kj-button-font-size: .75rem;--kj-button-height: var(--kj-ctl-h-xs)}.kj-button[data-size=sm]{--kj-button-padding-x: var(--kj-space-sm);--kj-button-padding-y: .25rem;--kj-button-font-size: .8125rem;--kj-button-height: var(--kj-ctl-h-sm)}.kj-button[data-size=md]{--kj-button-height: var(--kj-ctl-h-md)}.kj-button[data-size=lg]{--kj-button-padding-x: var(--kj-space-lg);--kj-button-padding-y: .625rem;--kj-button-font-size: 1rem;--kj-button-height: var(--kj-ctl-h-lg)}.kj-button[data-size=xl]{--kj-button-padding-x: var(--kj-space-2xl);--kj-button-padding-y: .875rem;--kj-button-font-size: 1.0625rem;--kj-button-gap: var(--kj-space-md);--kj-button-height: var(--kj-ctl-h-xl)}.kj-button[data-size=icon]{--kj-button-padding-x: var(--kj-space-sm);--kj-button-padding-y: var(--kj-space-sm);--kj-button-height: var(--kj-ctl-h-md);aspect-ratio:1}.kj-button[data-full=true]{width:100%}.kj-button:hover{--kj-button-bg: var(--kj-button-bg-hover, color-mix(in oklch, var(--kj-bg-primary), black 8%));transform:translateY(var(--kj-button-hover-translate, var(--kj-hover-translate, -2px)))}.kj-button[data-variant=default]:hover{--kj-button-fg: var(--kj-button-fg-hover, var(--kj-fg-on-primary))}.kj-button[data-variant=default]{box-shadow:var(--kj-button-shadow, none)}.kj-button[data-variant=default]:hover{box-shadow:var(--kj-button-shadow-hover, var(--kj-button-shadow, none))}.kj-button[data-variant=default]:active{box-shadow:var(--kj-button-shadow-active, var(--kj-button-shadow, none))}.kj-button:active{transform:var(--kj-button-translate-active, none)}.kj-button:focus-visible{outline:2px solid var(--kj-border-focus);outline-offset:2px}.kj-button[aria-disabled=true]{opacity:.45;cursor:not-allowed;pointer-events:none}.kj-button[data-size=icon]{min-width:2.75rem;min-height:2.75rem}.kj-button[aria-busy=true]{opacity:.7;cursor:progress}.kj-button[aria-pressed=true]{--kj-button-bg: var(--kj-bg-field);box-shadow:inset 0 1px 2px #0000001f}.kj-button__spinner{display:inline-block;width:1em;height:1em;vertical-align:-.125em;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:kj-button-spinner-rotate .6s linear infinite}}@keyframes kj-button-spinner-rotate{to{transform:rotate(360deg)}}\n"] }]
1808
1809
  }], propDecorators: { kjVariant: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjVariant", required: false }] }], kjSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjSize", required: false }] }], kjDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjDisabled", required: false }] }], kjLoading: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjLoading", required: false }] }], kjFullWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjFullWidth", required: false }] }], kjPressed: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjPressed", required: false }] }, { type: i0.Output, args: ["kjPressedChange"] }], kjType: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjType", required: false }] }], kjAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjAriaLabel", required: false }] }] } });
1809
1810
 
1810
1811
  /**
@@ -1839,6 +1840,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1839
1840
  * @doc-example Variants
1840
1841
  * Set `kjVariant` on the group to cascade onto every child button.
1841
1842
  * @doc-file button-group.variants.example.ts
1843
+ * @doc-example Segmented
1844
+ * `kjVariant="segmented"` moves the border onto the group and fills only
1845
+ * the pressed child — a mode switcher rather than a toolbar.
1846
+ * @doc-file button-group.segmented.example.ts
1842
1847
  * @doc-example Toggle
1843
1848
  * Pair with `[kjPressed]` on each child to build a segmented control.
1844
1849
  * @doc-file button-group.toggle.example.ts
@@ -1870,7 +1875,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1870
1875
  * @doc-related button,toggle,tabs
1871
1876
  *
1872
1877
  * @doc-css-var
1873
- * --kj-button-group-gap — Gap between adjacent buttons. Defaults to 0 (segmented look); raise for a spaced cluster.
1878
+ * --kj-button-group-gap — Gap between adjacent buttons. Defaults to 0 (segmented look); raise for a spaced cluster.
1879
+ * --kj-segmented-border — Shell and divider colour, `kjVariant="segmented"` only. Defaults to `--kj-border-default`.
1880
+ * --kj-segmented-bg-on — Fill behind the pressed segment. Defaults to `--kj-bg-surface`.
1881
+ * --kj-segmented-fg-on — Ink on the pressed segment. Defaults to `--kj-fg-default`.
1882
+ *
1883
+ * Typography needs no dedicated hook: set the ordinary `--kj-button-font`,
1884
+ * `--kj-button-font-size`, `--kj-button-letter-spacing` and
1885
+ * `--kj-button-text-transform` on this host and they reach each child,
1886
+ * since every knob is read as `var(name, default)`. The on-state colours
1887
+ * are the exception — variant rules declare those knobs on the element,
1888
+ * so they get their own `--kj-segmented-*` indirection.
1874
1889
  *
1875
1890
  * @doc-category Library/Actions
1876
1891
  * @doc
@@ -1888,7 +1903,9 @@ class KjButtonGroupComponent {
1888
1903
  ...(ngDevMode ? [{ debugName: "kjOrientation" }] : /* istanbul ignore next */ []));
1889
1904
  /**
1890
1905
  * Default variant forwarded to children via `KJ_BUTTON_GROUP`. Children
1891
- * that set their own `kjVariant` override this fallback.
1906
+ * that set their own `kjVariant` override this fallback. Also mirrored to
1907
+ * `data-variant` on the host, which is how `segmented` draws its shared
1908
+ * shell and dividers.
1892
1909
  */
1893
1910
  kjVariant = input(undefined, /* @ts-ignore */
1894
1911
  ...(ngDevMode ? [{ debugName: "kjVariant" }] : /* istanbul ignore next */ []));
@@ -1910,7 +1927,7 @@ class KjButtonGroupComponent {
1910
1927
  kjAriaLabel = input(undefined, /* @ts-ignore */
1911
1928
  ...(ngDevMode ? [{ debugName: "kjAriaLabel" }] : /* istanbul ignore next */ []));
1912
1929
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjButtonGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1913
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.5", type: KjButtonGroupComponent, isStandalone: true, selector: "kj-button-group", inputs: { kjOrientation: { classPropertyName: "kjOrientation", publicName: "kjOrientation", isSignal: true, isRequired: false, transformFunction: null }, kjVariant: { classPropertyName: "kjVariant", publicName: "kjVariant", isSignal: true, isRequired: false, transformFunction: null }, kjSize: { classPropertyName: "kjSize", publicName: "kjSize", isSignal: true, isRequired: false, transformFunction: null }, kjDisabled: { classPropertyName: "kjDisabled", publicName: "kjDisabled", isSignal: true, isRequired: false, transformFunction: null }, kjAriaLabel: { classPropertyName: "kjAriaLabel", publicName: "kjAriaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-label": "kjAriaLabel()" }, classAttribute: "kj-button-group" }, hostDirectives: [{ directive: i1.KjButtonGroup, inputs: ["kjOrientation", "kjOrientation", "kjVariant", "kjVariant", "kjSize", "kjSize", "kjDisabled", "kjDisabled"] }], ngImport: i0, template: `<ng-content />`, isInline: true, styles: ["@layer kj.component{.kj-button-group{--kj-button-group-gap: 0;display:inline-flex;flex-direction:row;align-items:stretch;gap:var(--kj-button-group-gap);border-radius:var(--kj-radius-field);isolation:isolate}.kj-button-group[data-orientation=vertical]{flex-direction:column}.kj-button-group:not([data-orientation=vertical])>kj-button>.kj-button{border-radius:0;margin-left:calc(-1 * var(--kj-border))}.kj-button-group:not([data-orientation=vertical])>kj-button:first-child>.kj-button{border-top-left-radius:var(--kj-radius-field);border-bottom-left-radius:var(--kj-radius-field);margin-left:0}.kj-button-group:not([data-orientation=vertical])>kj-button:last-child>.kj-button{border-top-right-radius:var(--kj-radius-field);border-bottom-right-radius:var(--kj-radius-field)}.kj-button-group[data-orientation=vertical]>kj-button>.kj-button{border-radius:0;margin-top:calc(-1 * var(--kj-border));width:100%}.kj-button-group[data-orientation=vertical]>kj-button:first-child>.kj-button{border-top-left-radius:var(--kj-radius-field);border-top-right-radius:var(--kj-radius-field);margin-top:0}.kj-button-group[data-orientation=vertical]>kj-button:last-child>.kj-button{border-bottom-left-radius:var(--kj-radius-field);border-bottom-right-radius:var(--kj-radius-field)}.kj-button-group>kj-button>.kj-button:hover,.kj-button-group>kj-button>.kj-button:focus-visible,.kj-button-group>kj-button>.kj-button[aria-pressed=true]{z-index:1}.kj-button-group[data-disabled]{opacity:.7}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1930
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.0.5", type: KjButtonGroupComponent, isStandalone: true, selector: "kj-button-group", inputs: { kjOrientation: { classPropertyName: "kjOrientation", publicName: "kjOrientation", isSignal: true, isRequired: false, transformFunction: null }, kjVariant: { classPropertyName: "kjVariant", publicName: "kjVariant", isSignal: true, isRequired: false, transformFunction: null }, kjSize: { classPropertyName: "kjSize", publicName: "kjSize", isSignal: true, isRequired: false, transformFunction: null }, kjDisabled: { classPropertyName: "kjDisabled", publicName: "kjDisabled", isSignal: true, isRequired: false, transformFunction: null }, kjAriaLabel: { classPropertyName: "kjAriaLabel", publicName: "kjAriaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-label": "kjAriaLabel()" }, classAttribute: "kj-button-group" }, hostDirectives: [{ directive: i1.KjButtonGroup, inputs: ["kjOrientation", "kjOrientation", "kjVariant", "kjVariant", "kjSize", "kjSize", "kjDisabled", "kjDisabled"] }], ngImport: i0, template: `<ng-content />`, isInline: true, styles: ["@layer kj.component{.kj-button-group{--kj-button-group-gap: 0;display:inline-flex;flex-direction:row;align-items:stretch;gap:var(--kj-button-group-gap);border-radius:var(--kj-radius-field);isolation:isolate}.kj-button-group[data-orientation=vertical]{flex-direction:column}.kj-button-group:not([data-orientation=vertical])>kj-button>.kj-button{border-radius:0;margin-left:calc(-1 * var(--kj-border))}.kj-button-group:not([data-orientation=vertical])>kj-button:first-child>.kj-button{border-top-left-radius:var(--kj-radius-field);border-bottom-left-radius:var(--kj-radius-field);margin-left:0}.kj-button-group:not([data-orientation=vertical])>kj-button:last-child>.kj-button{border-top-right-radius:var(--kj-radius-field);border-bottom-right-radius:var(--kj-radius-field)}.kj-button-group[data-orientation=vertical]>kj-button>.kj-button{border-radius:0;margin-top:calc(-1 * var(--kj-border));width:100%}.kj-button-group[data-orientation=vertical]>kj-button:first-child>.kj-button{border-top-left-radius:var(--kj-radius-field);border-top-right-radius:var(--kj-radius-field);margin-top:0}.kj-button-group[data-orientation=vertical]>kj-button:last-child>.kj-button{border-bottom-left-radius:var(--kj-radius-field);border-bottom-right-radius:var(--kj-radius-field)}.kj-button-group>kj-button>.kj-button:hover,.kj-button-group>kj-button>.kj-button:focus-visible,.kj-button-group>kj-button>.kj-button[aria-pressed=true]{z-index:1}.kj-button-group[data-variant=segmented]{border:var(--kj-border) solid var(--kj-segmented-border, var(--kj-border-default));border-radius:var(--kj-radius-field);overflow:hidden}.kj-button-group[data-variant=segmented]>kj-button>.kj-button{margin-left:0;margin-top:0;border-radius:0}.kj-button-group[data-variant=segmented]:not([data-orientation=vertical])>kj-button+kj-button>.kj-button{border-left:var(--kj-border) solid var(--kj-segmented-border, var(--kj-border-default))}.kj-button-group[data-variant=segmented][data-orientation=vertical]>kj-button+kj-button>.kj-button{border-top:var(--kj-border) solid var(--kj-segmented-border, var(--kj-border-default))}.kj-button-group[data-disabled]{opacity:.7}}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
1914
1931
  }
1915
1932
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjButtonGroupComponent, decorators: [{
1916
1933
  type: Component,
@@ -1922,7 +1939,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
1922
1939
  ], template: `<ng-content />`, encapsulation: ViewEncapsulation.None, host: {
1923
1940
  class: 'kj-button-group',
1924
1941
  '[attr.aria-label]': 'kjAriaLabel()',
1925
- }, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["@layer kj.component{.kj-button-group{--kj-button-group-gap: 0;display:inline-flex;flex-direction:row;align-items:stretch;gap:var(--kj-button-group-gap);border-radius:var(--kj-radius-field);isolation:isolate}.kj-button-group[data-orientation=vertical]{flex-direction:column}.kj-button-group:not([data-orientation=vertical])>kj-button>.kj-button{border-radius:0;margin-left:calc(-1 * var(--kj-border))}.kj-button-group:not([data-orientation=vertical])>kj-button:first-child>.kj-button{border-top-left-radius:var(--kj-radius-field);border-bottom-left-radius:var(--kj-radius-field);margin-left:0}.kj-button-group:not([data-orientation=vertical])>kj-button:last-child>.kj-button{border-top-right-radius:var(--kj-radius-field);border-bottom-right-radius:var(--kj-radius-field)}.kj-button-group[data-orientation=vertical]>kj-button>.kj-button{border-radius:0;margin-top:calc(-1 * var(--kj-border));width:100%}.kj-button-group[data-orientation=vertical]>kj-button:first-child>.kj-button{border-top-left-radius:var(--kj-radius-field);border-top-right-radius:var(--kj-radius-field);margin-top:0}.kj-button-group[data-orientation=vertical]>kj-button:last-child>.kj-button{border-bottom-left-radius:var(--kj-radius-field);border-bottom-right-radius:var(--kj-radius-field)}.kj-button-group>kj-button>.kj-button:hover,.kj-button-group>kj-button>.kj-button:focus-visible,.kj-button-group>kj-button>.kj-button[aria-pressed=true]{z-index:1}.kj-button-group[data-disabled]{opacity:.7}}\n"] }]
1942
+ }, changeDetection: ChangeDetectionStrategy.OnPush, styles: ["@layer kj.component{.kj-button-group{--kj-button-group-gap: 0;display:inline-flex;flex-direction:row;align-items:stretch;gap:var(--kj-button-group-gap);border-radius:var(--kj-radius-field);isolation:isolate}.kj-button-group[data-orientation=vertical]{flex-direction:column}.kj-button-group:not([data-orientation=vertical])>kj-button>.kj-button{border-radius:0;margin-left:calc(-1 * var(--kj-border))}.kj-button-group:not([data-orientation=vertical])>kj-button:first-child>.kj-button{border-top-left-radius:var(--kj-radius-field);border-bottom-left-radius:var(--kj-radius-field);margin-left:0}.kj-button-group:not([data-orientation=vertical])>kj-button:last-child>.kj-button{border-top-right-radius:var(--kj-radius-field);border-bottom-right-radius:var(--kj-radius-field)}.kj-button-group[data-orientation=vertical]>kj-button>.kj-button{border-radius:0;margin-top:calc(-1 * var(--kj-border));width:100%}.kj-button-group[data-orientation=vertical]>kj-button:first-child>.kj-button{border-top-left-radius:var(--kj-radius-field);border-top-right-radius:var(--kj-radius-field);margin-top:0}.kj-button-group[data-orientation=vertical]>kj-button:last-child>.kj-button{border-bottom-left-radius:var(--kj-radius-field);border-bottom-right-radius:var(--kj-radius-field)}.kj-button-group>kj-button>.kj-button:hover,.kj-button-group>kj-button>.kj-button:focus-visible,.kj-button-group>kj-button>.kj-button[aria-pressed=true]{z-index:1}.kj-button-group[data-variant=segmented]{border:var(--kj-border) solid var(--kj-segmented-border, var(--kj-border-default));border-radius:var(--kj-radius-field);overflow:hidden}.kj-button-group[data-variant=segmented]>kj-button>.kj-button{margin-left:0;margin-top:0;border-radius:0}.kj-button-group[data-variant=segmented]:not([data-orientation=vertical])>kj-button+kj-button>.kj-button{border-left:var(--kj-border) solid var(--kj-segmented-border, var(--kj-border-default))}.kj-button-group[data-variant=segmented][data-orientation=vertical]>kj-button+kj-button>.kj-button{border-top:var(--kj-border) solid var(--kj-segmented-border, var(--kj-border-default))}.kj-button-group[data-disabled]{opacity:.7}}\n"] }]
1926
1943
  }], propDecorators: { kjOrientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjOrientation", required: false }] }], kjVariant: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjVariant", required: false }] }], kjSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjSize", required: false }] }], kjDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjDisabled", required: false }] }], kjAriaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "kjAriaLabel", required: false }] }] } });
1927
1944
 
1928
1945
  /**
@@ -3209,77 +3226,74 @@ function escapeHtml(s) {
3209
3226
  .replace(/"/g, '&quot;')
3210
3227
  .replace(/'/g, '&#39;');
3211
3228
  }
3212
- /** Only allow safe URL schemes for links. */
3213
- function safeUrl(url) {
3214
- const trimmed = url.trim();
3215
- if (/^(https?:\/\/|\/|#|mailto:)/i.test(trimmed))
3216
- return trimmed;
3217
- return null;
3218
- }
3219
- /** Apply link / bold / italic to a non-code segment. */
3220
- function formatSegment(seg) {
3221
- let s = seg.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (_m, text, url) => {
3222
- const href = safeUrl(url);
3223
- if (!href)
3224
- return text;
3225
- return `<a class="kj-md-link" href="${escapeHtml(href)}" rel="noopener noreferrer" target="_blank">${text}</a>`;
3226
- });
3227
- s = s.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>');
3228
- s = s.replace(/(^|[^*])\*([^*]+)\*/g, '$1<em>$2</em>');
3229
- return s;
3230
- }
3231
3229
  /**
3232
- * Render inline markdown on an **already HTML-escaped** string. Splits on
3233
- * backtick code spans first so their contents are never re-formatted, then
3234
- * applies link / bold / italic to the remaining prose segments.
3230
+ * GFM, with `breaks` on: a chat turn treats a single newline as a line break,
3231
+ * which is what people type. `pedantic` off so the parser stays forgiving of
3232
+ * the half-formed markdown a model emits mid-stream.
3233
+ *
3234
+ * `marked` dropped its sanitizer in v5 and passes source HTML through
3235
+ * verbatim, so both HTML token kinds are overridden to escape instead. A chat
3236
+ * turn is model or user output: it may *describe* markup, never inject it.
3235
3237
  */
3236
- function renderInline(escaped) {
3237
- return escaped
3238
- .split(/(`[^`]+`)/g)
3239
- .map((seg) => {
3240
- if (seg.length >= 2 && seg.startsWith('`') && seg.endsWith('`')) {
3241
- return `<code class="kj-md-code">${seg.slice(1, -1)}</code>`;
3242
- }
3243
- return formatSegment(seg);
3244
- })
3245
- .join('');
3246
- }
3247
- /** Render a prose block (paragraphs + single-newline `<br>`) to safe HTML. */
3248
- function renderProse(block) {
3249
- const paragraphs = block
3250
- .split(/\n{2,}/)
3251
- .map((p) => p.trim())
3252
- .filter(Boolean);
3253
- return paragraphs
3254
- .map((p) => {
3255
- const lines = p.split('\n').map((line) => renderInline(escapeHtml(line)));
3256
- return `<p>${lines.join('<br>')}</p>`;
3257
- })
3258
- .join('');
3259
- }
3238
+ marked.use({
3239
+ gfm: true,
3240
+ breaks: true,
3241
+ pedantic: false,
3242
+ renderer: {
3243
+ html(token) {
3244
+ return escapeHtml(typeof token === 'string' ? token : token.raw);
3245
+ },
3246
+ },
3247
+ tokenizer: {},
3248
+ });
3249
+ marked.use({
3250
+ renderer: {
3251
+ // Inline HTML (`<img …>` mid-sentence) travels a different path than a
3252
+ // block-level HTML token, and needs the same treatment.
3253
+ text(token) {
3254
+ const anyToken = token;
3255
+ if (Array.isArray(anyToken.tokens) && anyToken.tokens.length > 0) {
3256
+ return this.parser.parseInline(anyToken.tokens);
3257
+ }
3258
+ return escapeHtml(String(anyToken.text ?? anyToken.raw ?? ''));
3259
+ },
3260
+ },
3261
+ });
3260
3262
  /**
3261
- * Parse a lightweight markdown subset into blocks: fenced code blocks
3262
- * (```lang) become structural `code` blocks; everything else becomes `prose`
3263
- * blocks with sanitised inline HTML.
3263
+ * Parse markdown into blocks: fenced code becomes a structural `code` block,
3264
+ * everything else is rendered to HTML as a `prose` block.
3264
3265
  *
3265
- * Deliberately minimal (no tables / nested lists) and **self-contained** the
3266
- * repo ships no markdown engine on `main`. Swap in a full parser later without
3267
- * touching the view.
3266
+ * Code is split out at the TOKEN level rather than by regex so an indented
3267
+ * block, a `~~~` fence, or a fence inside a list is still recognised — and so
3268
+ * a stray fence in a partial stream cannot swallow the rest of the message.
3268
3269
  */
3269
3270
  function renderMarkdown(src) {
3271
+ const tokens = marked.lexer(src);
3270
3272
  const blocks = [];
3271
- const fence = /```([\w-]*)\n?([\s\S]*?)```/g;
3272
- let last = 0;
3273
- for (let m = fence.exec(src); m; m = fence.exec(src)) {
3274
- const before = src.slice(last, m.index);
3275
- if (before.trim())
3276
- blocks.push({ kind: 'prose', html: renderProse(before) });
3277
- blocks.push({ kind: 'code', lang: m[1] || '', code: m[2].replace(/\n$/, '') });
3278
- last = fence.lastIndex;
3273
+ let prose = [];
3274
+ const flushProse = () => {
3275
+ if (prose.length === 0)
3276
+ return;
3277
+ // `marked.parser` reads `links` off the token list for reference-style
3278
+ // links; a plain slice would drop them and render `[a][b]` verbatim.
3279
+ const list = prose;
3280
+ list.links = tokens.links;
3281
+ blocks.push({ kind: 'prose', html: marked.parser(list) });
3282
+ prose = [];
3283
+ };
3284
+ for (const token of tokens) {
3285
+ if (token.type === 'code') {
3286
+ flushProse();
3287
+ blocks.push({
3288
+ kind: 'code',
3289
+ lang: typeof token.lang === 'string' ? token.lang.split(/\s+/)[0] : '',
3290
+ code: String(token.text ?? ''),
3291
+ });
3292
+ continue;
3293
+ }
3294
+ prose.push(token);
3279
3295
  }
3280
- const rest = src.slice(last);
3281
- if (rest.trim())
3282
- blocks.push({ kind: 'prose', html: renderProse(rest) });
3296
+ flushProse();
3283
3297
  return blocks;
3284
3298
  }
3285
3299
 
@@ -3325,8 +3339,15 @@ class KjChatMessage {
3325
3339
  _copied = signal(null, /* @ts-ignore */
3326
3340
  ...(ngDevMode ? [{ debugName: "_copied" }] : /* istanbul ignore next */ []));
3327
3341
  copiedIndex = this._copied.asReadonly();
3342
+ /**
3343
+ * Sanitise rather than bypass. `renderMarkdown` already escapes raw HTML at
3344
+ * the parse layer, but a chat body is model or user output — the one place
3345
+ * in a UI kit where trusting a single layer is a bad bet. `sanitize` strips
3346
+ * anything the parser somehow let through instead of waving it past
3347
+ * Angular's checks.
3348
+ */
3328
3349
  safe(html) {
3329
- return this.sanitizer.bypassSecurityTrustHtml(html);
3350
+ return this.sanitizer.sanitize(SecurityContext.HTML, html) ?? '';
3330
3351
  }
3331
3352
  async copy(code, index) {
3332
3353
  try {
@@ -3546,6 +3567,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
3546
3567
  * Provider-agnostic: pair with `KjPromptInput` and drive `KjChatStore` from any
3547
3568
  * model / stream — the kit ships **no** LLM SDK or backend.
3548
3569
  *
3570
+ * @doc-example Custom thread items
3571
+ * `provideKjChat` maps an item `type` to a component, so a thread can render
3572
+ * turns the kit knows nothing about. Ordinary messages are untouched.
3573
+ * @doc-file ai-chat.custom-items.example.ts
3549
3574
  * @doc-example Streaming (simulated)
3550
3575
  * A full send → simulated-stream → reply loop with slash commands, a Stop
3551
3576
  * button, tool-call cards and citations. No backend — a fake token generator
@@ -3587,6 +3612,30 @@ class KjChatThread {
3587
3612
  ...(ngDevMode ? [{ debugName: "kjLabel" }] : /* istanbul ignore next */ []));
3588
3613
  /** @internal — the coalescing announcer for the polite live region. */
3589
3614
  announcer = inject(KjChatAnnouncer);
3615
+ config = inject(KJ_CHAT_CONFIG);
3616
+ /**
3617
+ * The registered component for a message's `type`, or `null` to let the
3618
+ * built-in renderer draw it. A message with no `type` never consults the
3619
+ * registry — that is the overwhelmingly common case and must stay free.
3620
+ *
3621
+ * An unknown `type` falls back to the configured `fallback`, and failing
3622
+ * that to the built-in renderer: a transcript that silently drops a turn is
3623
+ * worse than one that renders it plainly.
3624
+ */
3625
+ rendererFor(message) {
3626
+ if (!message.type)
3627
+ return null;
3628
+ return this.config.renderers[message.type] ?? this.config.fallback ?? null;
3629
+ }
3630
+ /** The neutral shape handed to a registered renderer's `item` input. */
3631
+ itemFor(message) {
3632
+ return {
3633
+ id: message.id,
3634
+ type: message.type,
3635
+ role: message.role,
3636
+ data: message.data ?? message.content,
3637
+ };
3638
+ }
3590
3639
  injector = inject(Injector);
3591
3640
  logEl = viewChild.required('log');
3592
3641
  lastId = null;
@@ -3636,7 +3685,13 @@ class KjChatThread {
3636
3685
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: KjChatThread, isStandalone: true, selector: "kj-chat-thread", inputs: { store: { classPropertyName: "store", publicName: "store", isSignal: true, isRequired: true, transformFunction: null }, kjLabel: { classPropertyName: "kjLabel", publicName: "kjLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "kj-chat-thread-host" }, providers: [KjChatAnnouncer], viewQueries: [{ propertyName: "logEl", first: true, predicate: ["log"], descendants: true, isSignal: true }], ngImport: i0, template: `
3637
3686
  <div #log kjChatLog kjChatLogLive="off" class="kj-chat-thread" [kjChatLogLabel]="kjLabel()">
3638
3687
  @for (m of store().messages(); track m.id) {
3639
- <kj-chat-message [message]="m" />
3688
+ @if (rendererFor(m); as renderer) {
3689
+ <ng-container
3690
+ *ngComponentOutlet="renderer; inputs: { item: itemFor(m), message: m }"
3691
+ ></ng-container>
3692
+ } @else {
3693
+ <kj-chat-message [message]="m" />
3694
+ }
3640
3695
  }
3641
3696
  </div>
3642
3697
 
@@ -3644,14 +3699,20 @@ class KjChatThread {
3644
3699
  <div class="kj-visually-hidden" aria-live="polite" aria-atomic="false">
3645
3700
  {{ announcer.message() }}
3646
3701
  </div>
3647
- `, isInline: true, styles: ["@layer kj.component{.kj-visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.kj-chat-thread{display:flex;flex-direction:column;gap:var(--kj-space-md);padding:var(--kj-space-md);background:var(--kj-bg-body);color:var(--kj-fg-default);border-radius:var(--kj-radius-box);overflow-y:auto;font-family:var(--kj-font-sans)}.kj-chat-msg{display:flex;gap:var(--kj-space-sm);align-items:flex-start;max-width:100%}.kj-chat-msg[data-side=end]{flex-direction:row-reverse;margin-inline-start:auto}.kj-chat-msg[data-role=system]{justify-content:center}.kj-chat-msg__avatar{flex:0 0 auto;width:2rem;height:2rem;border-radius:50%;display:grid;place-items:center;font-size:var(--kj-text-sm);font-weight:600;background:var(--kj-bg-surface);color:var(--kj-fg-default);border:1px solid var(--kj-border-default)}.kj-chat-msg__bubble{display:flex;flex-direction:column;gap:var(--kj-space-xs);padding:var(--kj-space-sm) var(--kj-space-md);border-radius:var(--kj-radius-box);background:var(--kj-bg-surface);color:var(--kj-fg-default);max-width:min(48rem,100%);overflow-wrap:anywhere}.kj-chat-msg[data-role=user] .kj-chat-msg__bubble{background:var(--kj-bg-primary);color:var(--kj-fg-on-primary)}.kj-chat-msg[data-role=system] .kj-chat-msg__bubble{background:transparent;color:var(--kj-fg-muted);font-size:var(--kj-text-sm);text-align:center;padding-block:var(--kj-space-xs)}.kj-chat-msg__body :is(p){margin:0 0 var(--kj-space-xs)}.kj-chat-msg__body :is(p):last-child{margin-bottom:0}.kj-chat-msg__error{margin:0;color:var(--kj-fg-danger);font-size:var(--kj-text-sm)}.kj-md-link{color:inherit;text-decoration:underline}.kj-md-code{font-family:ui-monospace,monospace;background:color-mix(in srgb,currentColor 12%,transparent);padding:.1em .35em;border-radius:var(--kj-radius-field);font-size:.9em}.kj-chat-code{border:1px solid var(--kj-border-default);border-radius:var(--kj-radius-field);overflow:hidden;margin:var(--kj-space-xs) 0;background:var(--kj-bg-body)}.kj-chat-code__bar{display:flex;align-items:center;justify-content:space-between;padding:.25rem var(--kj-space-sm);background:var(--kj-bg-surface);border-bottom:1px solid var(--kj-border-default);font-size:var(--kj-text-sm);color:var(--kj-fg-muted)}.kj-chat-code__copy{min-height:1.75rem;padding:.25rem .6rem;border:1px solid var(--kj-border-default);border-radius:var(--kj-radius-field);background:var(--kj-bg-body);color:var(--kj-fg-default);font-size:var(--kj-text-sm);cursor:pointer}.kj-chat-code__copy:hover{background:var(--kj-bg-surface)}.kj-chat-code__copy:focus-visible{outline:2px solid var(--kj-border-focus);outline-offset:2px}.kj-chat-code__pre{margin:0;padding:var(--kj-space-sm);overflow-x:auto;font-family:ui-monospace,monospace;font-size:.9em;color:var(--kj-fg-default)}.kj-chat-tool{display:flex;align-items:center;gap:var(--kj-space-xs);padding:.35rem var(--kj-space-sm);border:1px solid var(--kj-border-default);border-radius:var(--kj-radius-field);background:var(--kj-bg-body);font-size:var(--kj-text-sm)}.kj-chat-tool__name{font-weight:600;font-family:ui-monospace,monospace}.kj-chat-tool__status{margin-inline-start:auto;color:var(--kj-fg-muted);text-transform:capitalize}.kj-chat-tool[data-status=error]{border-color:var(--kj-border-danger)}.kj-chat-tool[data-status=error] .kj-chat-tool__status,.kj-chat-tool__error{color:var(--kj-fg-danger)}.kj-chat-cites{list-style:none;margin:var(--kj-space-xs) 0 0;padding:0;display:flex;flex-wrap:wrap;gap:var(--kj-space-xs)}.kj-chat-cite{display:inline-flex;align-items:baseline;gap:.35rem;padding:.15rem .5rem;border:1px solid var(--kj-border-default);border-radius:999px;font-size:var(--kj-text-sm);background:var(--kj-bg-body)}.kj-chat-cite__link{color:var(--kj-fg-info);text-decoration:none}.kj-chat-cite__link:hover{text-decoration:underline}.kj-chat-cite__snippet{color:var(--kj-fg-muted)}.kj-chat-typing{display:inline-flex;gap:.25rem;align-items:center;padding:.25rem 0}.kj-chat-typing__dot{width:.5rem;height:.5rem;border-radius:50%;background:var(--kj-fg-muted);animation:kj-chat-bounce 1.2s infinite ease-in-out both}.kj-chat-typing__dot:nth-child(2){animation-delay:.15s}.kj-chat-typing__dot:nth-child(3){animation-delay:.3s}@keyframes kj-chat-bounce{0%,80%,to{transform:scale(.5);opacity:.4}40%{transform:scale(1);opacity:1}}@media(prefers-reduced-motion:reduce){.kj-chat-typing__dot{animation:none;opacity:.6}}.kj-prompt{position:relative;display:flex;align-items:flex-end;gap:var(--kj-space-sm);padding:var(--kj-space-sm);border:1px solid var(--kj-border-default);border-radius:var(--kj-radius-box);background:var(--kj-bg-field, var(--kj-input-bg))}.kj-prompt:focus-within{border-color:var(--kj-border-focus)}.kj-prompt__textarea{flex:1 1 auto;resize:none;border:none;outline:none;background:transparent;color:var(--kj-input-fg, var(--kj-fg-default));font-family:var(--kj-font-sans);font-size:var(--kj-input-font-size, 1rem);line-height:1.5;max-height:200px;overflow-y:auto}.kj-prompt__textarea::placeholder{color:var(--kj-input-placeholder-fg, var(--kj-fg-muted))}.kj-prompt__actions{display:flex;align-items:center;gap:var(--kj-space-xs);flex:0 0 auto}.kj-prompt__btn{display:grid;place-items:center;width:2.75rem;height:2.75rem;border-radius:50%;border:none;cursor:pointer;font-size:1.1rem}.kj-prompt__btn:focus-visible{outline:2px solid var(--kj-border-focus);outline-offset:2px}.kj-prompt__btn--send{background:var(--kj-bg-primary);color:var(--kj-fg-on-primary)}.kj-prompt__btn--send:disabled{opacity:.5;cursor:not-allowed}.kj-prompt__btn--stop{background:var(--kj-bg-danger);color:var(--kj-fg-on-danger)}.kj-prompt__slash{position:absolute;bottom:calc(100% + .25rem);left:0;right:0;margin:0;padding:.25rem;list-style:none;background:var(--kj-bg-surface);border:1px solid var(--kj-border-default);border-radius:var(--kj-radius-field);box-shadow:0 4px 16px #00000026;max-height:14rem;overflow-y:auto;z-index:10}.kj-prompt__slash-item{display:flex;flex-direction:column;gap:.1rem;padding:var(--kj-space-xs) var(--kj-space-sm);border-radius:var(--kj-radius-field);cursor:pointer}.kj-prompt__slash-item.is-active,.kj-prompt__slash-item:hover{background:var(--kj-bg-primary);color:var(--kj-fg-on-primary)}.kj-prompt__slash-name{font-family:ui-monospace,monospace;font-weight:600}.kj-prompt__slash-desc{font-size:var(--kj-text-sm);color:var(--kj-fg-muted)}.kj-prompt__slash-item.is-active .kj-prompt__slash-desc,.kj-prompt__slash-item:hover .kj-prompt__slash-desc{color:inherit}}\n"], dependencies: [{ kind: "directive", type: KjChatLog, selector: "[kjChatLog]", inputs: ["kjChatLogLive", "kjChatLogLabel"] }, { kind: "component", type: KjChatMessage, selector: "kj-chat-message", inputs: ["message"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3702
+ `, isInline: true, styles: ["@layer kj.component{.kj-visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.kj-chat-thread{display:flex;flex-direction:column;gap:var(--kj-space-md);padding:var(--kj-space-md);background:var(--kj-bg-body);color:var(--kj-fg-default);border-radius:var(--kj-radius-box);overflow-y:auto;font-family:var(--kj-font-sans)}.kj-chat-msg{display:flex;gap:var(--kj-space-sm);align-items:flex-start;max-width:100%}.kj-chat-msg[data-side=end]{flex-direction:row-reverse;margin-inline-start:auto}.kj-chat-msg[data-role=system]{justify-content:center}.kj-chat-msg__avatar{flex:0 0 auto;width:2rem;height:2rem;border-radius:50%;display:grid;place-items:center;font-size:var(--kj-text-sm);font-weight:600;background:var(--kj-bg-surface);color:var(--kj-fg-default);border:1px solid var(--kj-border-default)}.kj-chat-msg__bubble{display:flex;flex-direction:column;gap:var(--kj-space-xs);padding:var(--kj-space-sm) var(--kj-space-md);border-radius:var(--kj-radius-box);background:var(--kj-bg-surface);color:var(--kj-fg-default);max-width:min(48rem,100%);overflow-wrap:anywhere}.kj-chat-msg[data-role=user] .kj-chat-msg__bubble{background:var(--kj-bg-primary);color:var(--kj-fg-on-primary)}.kj-chat-msg[data-role=system] .kj-chat-msg__bubble{background:transparent;color:var(--kj-fg-muted);font-size:var(--kj-text-sm);text-align:center;padding-block:var(--kj-space-xs)}.kj-chat-msg__body :is(p){margin:0 0 var(--kj-space-xs)}.kj-chat-msg__body :is(p):last-child{margin-bottom:0}.kj-chat-msg__error{margin:0;color:var(--kj-fg-danger);font-size:var(--kj-text-sm)}.kj-md-link{color:inherit;text-decoration:underline}.kj-md-code{font-family:ui-monospace,monospace;background:color-mix(in srgb,currentColor 12%,transparent);padding:.1em .35em;border-radius:var(--kj-radius-field);font-size:.9em}.kj-chat-code{border:1px solid var(--kj-border-default);border-radius:var(--kj-radius-field);overflow:hidden;margin:var(--kj-space-xs) 0;background:var(--kj-bg-body)}.kj-chat-code__bar{display:flex;align-items:center;justify-content:space-between;padding:.25rem var(--kj-space-sm);background:var(--kj-bg-surface);border-bottom:1px solid var(--kj-border-default);font-size:var(--kj-text-sm);color:var(--kj-fg-muted)}.kj-chat-code__copy{min-height:1.75rem;padding:.25rem .6rem;border:1px solid var(--kj-border-default);border-radius:var(--kj-radius-field);background:var(--kj-bg-body);color:var(--kj-fg-default);font-size:var(--kj-text-sm);cursor:pointer}.kj-chat-code__copy:hover{background:var(--kj-bg-surface)}.kj-chat-code__copy:focus-visible{outline:2px solid var(--kj-border-focus);outline-offset:2px}.kj-chat-code__pre{margin:0;padding:var(--kj-space-sm);overflow-x:auto;font-family:ui-monospace,monospace;font-size:.9em;color:var(--kj-fg-default)}.kj-chat-tool{display:flex;align-items:center;gap:var(--kj-space-xs);padding:.35rem var(--kj-space-sm);border:1px solid var(--kj-border-default);border-radius:var(--kj-radius-field);background:var(--kj-bg-body);font-size:var(--kj-text-sm)}.kj-chat-tool__name{font-weight:600;font-family:ui-monospace,monospace}.kj-chat-tool__status{margin-inline-start:auto;color:var(--kj-fg-muted);text-transform:capitalize}.kj-chat-tool[data-status=error]{border-color:var(--kj-border-danger)}.kj-chat-tool[data-status=error] .kj-chat-tool__status,.kj-chat-tool__error{color:var(--kj-fg-danger)}.kj-chat-cites{list-style:none;margin:var(--kj-space-xs) 0 0;padding:0;display:flex;flex-wrap:wrap;gap:var(--kj-space-xs)}.kj-chat-cite{display:inline-flex;align-items:baseline;gap:.35rem;padding:.15rem .5rem;border:1px solid var(--kj-border-default);border-radius:999px;font-size:var(--kj-text-sm);background:var(--kj-bg-body)}.kj-chat-cite__link{color:var(--kj-fg-info);text-decoration:none}.kj-chat-cite__link:hover{text-decoration:underline}.kj-chat-cite__snippet{color:var(--kj-fg-muted)}.kj-chat-typing{display:inline-flex;gap:.25rem;align-items:center;padding:.25rem 0}.kj-chat-typing__dot{width:.5rem;height:.5rem;border-radius:50%;background:var(--kj-fg-muted);animation:kj-chat-bounce 1.2s infinite ease-in-out both}.kj-chat-typing__dot:nth-child(2){animation-delay:.15s}.kj-chat-typing__dot:nth-child(3){animation-delay:.3s}@keyframes kj-chat-bounce{0%,80%,to{transform:scale(.5);opacity:.4}40%{transform:scale(1);opacity:1}}@media(prefers-reduced-motion:reduce){.kj-chat-typing__dot{animation:none;opacity:.6}}.kj-prompt{position:relative;display:flex;align-items:flex-end;gap:var(--kj-space-sm);padding:var(--kj-space-sm);border:1px solid var(--kj-border-default);border-radius:var(--kj-radius-box);background:var(--kj-bg-field, var(--kj-input-bg))}.kj-prompt:focus-within{border-color:var(--kj-border-focus)}.kj-prompt__textarea{flex:1 1 auto;resize:none;border:none;outline:none;background:transparent;color:var(--kj-input-fg, var(--kj-fg-default));font-family:var(--kj-font-sans);font-size:var(--kj-input-font-size, 1rem);line-height:1.5;max-height:200px;overflow-y:auto}.kj-prompt__textarea::placeholder{color:var(--kj-input-placeholder-fg, var(--kj-fg-muted))}.kj-prompt__actions{display:flex;align-items:center;gap:var(--kj-space-xs);flex:0 0 auto}.kj-prompt__btn{display:grid;place-items:center;width:2.75rem;height:2.75rem;border-radius:50%;border:none;cursor:pointer;font-size:1.1rem}.kj-prompt__btn:focus-visible{outline:2px solid var(--kj-border-focus);outline-offset:2px}.kj-prompt__btn--send{background:var(--kj-bg-primary);color:var(--kj-fg-on-primary)}.kj-prompt__btn--send:disabled{opacity:.5;cursor:not-allowed}.kj-prompt__btn--stop{background:var(--kj-bg-danger);color:var(--kj-fg-on-danger)}.kj-prompt__slash{position:absolute;bottom:calc(100% + .25rem);left:0;right:0;margin:0;padding:.25rem;list-style:none;background:var(--kj-bg-surface);border:1px solid var(--kj-border-default);border-radius:var(--kj-radius-field);box-shadow:0 4px 16px #00000026;max-height:14rem;overflow-y:auto;z-index:10}.kj-prompt__slash-item{display:flex;flex-direction:column;gap:.1rem;padding:var(--kj-space-xs) var(--kj-space-sm);border-radius:var(--kj-radius-field);cursor:pointer}.kj-prompt__slash-item.is-active,.kj-prompt__slash-item:hover{background:var(--kj-bg-primary);color:var(--kj-fg-on-primary)}.kj-prompt__slash-name{font-family:ui-monospace,monospace;font-weight:600}.kj-prompt__slash-desc{font-size:var(--kj-text-sm);color:var(--kj-fg-muted)}.kj-prompt__slash-item.is-active .kj-prompt__slash-desc,.kj-prompt__slash-item:hover .kj-prompt__slash-desc{color:inherit}}\n"], dependencies: [{ kind: "directive", type: KjChatLog, selector: "[kjChatLog]", inputs: ["kjChatLogLive", "kjChatLogLabel"] }, { kind: "component", type: KjChatMessage, selector: "kj-chat-message", inputs: ["message"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3648
3703
  }
3649
3704
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: KjChatThread, decorators: [{
3650
3705
  type: Component,
3651
- args: [{ selector: 'kj-chat-thread', standalone: true, imports: [KjChatLog, KjChatMessage], providers: [KjChatAnnouncer], template: `
3706
+ args: [{ selector: 'kj-chat-thread', standalone: true, imports: [KjChatLog, KjChatMessage, NgComponentOutlet], providers: [KjChatAnnouncer], template: `
3652
3707
  <div #log kjChatLog kjChatLogLive="off" class="kj-chat-thread" [kjChatLogLabel]="kjLabel()">
3653
3708
  @for (m of store().messages(); track m.id) {
3654
- <kj-chat-message [message]="m" />
3709
+ @if (rendererFor(m); as renderer) {
3710
+ <ng-container
3711
+ *ngComponentOutlet="renderer; inputs: { item: itemFor(m), message: m }"
3712
+ ></ng-container>
3713
+ } @else {
3714
+ <kj-chat-message [message]="m" />
3715
+ }
3655
3716
  }
3656
3717
  </div>
3657
3718