@turquoisehealth/pit-viper 2.255.1 → 2.256.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 +1 @@
1
- {"version":3,"file":"pv-components-ai.mjs","names":["$slots","$attrs","$slots","$attrs","$slots","$emit","$slots"],"sources":["../../../src/components/base/PvSpinner/PvSpinner.vue","../../../src/components/base/PvSpinner/PvSpinner.vue","../../../src/components/base/PvButton/helpers.ts","../../../src/web-components/utils.ts","../../../src/components/base/baseProps.ts","../../../src/components/base/PvIcon/PvIcon.vue","../../../src/components/base/PvIcon/PvIcon.vue","../../../src/components/base/PvCounterBadge/PvCounterBadge.vue","../../../src/components/base/PvCounterBadge/PvCounterBadge.vue","../../../src/components/base/PvButton/PvButton.vue","../../../src/components/base/PvButton/PvButton.vue","../../../src/components/base/PvInput/PvInput.vue","../../../src/components/base/PvInput/PvInput.vue","../../../src/components/ai/PvQuestion/PvQuestion.vue","../../../src/components/ai/PvQuestion/PvQuestion.vue","../../../src/components/ai/PvConfirmation/PvConfirmation.vue","../../../src/components/ai/PvConfirmation/PvConfirmation.vue","../../../src/components/base/PvTextArea/PvTextArea.vue","../../../src/components/base/PvTextArea/PvTextArea.vue","../../../src/components/ai/PvPromptInput/PvPromptInput.vue","../../../src/components/ai/PvPromptInput/PvPromptInput.vue","../../../src/components/ai/PvPromptInput/types.ts","../../../src/components/base/PvAccordion/PvAccordion.vue","../../../src/components/base/PvAccordion/PvAccordion.vue","../../../src/composables/useSlotPresence.ts","../../../src/components/ai/PvStepList/PvStepList.vue","../../../src/components/ai/PvStepList/PvStepList.vue","../../../src/components/ai/PvReasoning/PvReasoning.vue","../../../src/components/ai/PvReasoning/PvReasoning.vue","../../../src/components/ai/PvTool/PvTool.vue","../../../src/components/ai/PvTool/PvTool.vue","../../../src/components/ai/toolLifecycle.ts","../../../src/components/ai/useStickToBottom.ts"],"sourcesContent":["<script setup lang=\"ts\">\nimport { PvSpinnerSize, PvSpinnerVariant } from \"./types\";\n\ninterface PvSpinnerProps {\n /** The size of the spinner. Controls the rendered diameter via a CSS custom property. */\n size?: PvSpinnerSize;\n /** The color variant of the spinner. Use \"white\" on dark backgrounds and \"dark\" on light backgrounds. */\n variant?: PvSpinnerVariant;\n}\n\nconst mapSizeToPx: Record<PvSpinnerSize, string> = {\n xs: \"0.75rem\",\n sm: \"1rem\",\n md: \"1.25rem\",\n lg: \"1.5rem\",\n xl: \"2rem\",\n};\n\nconst mapVariantToPvClass: Record<PvSpinnerVariant, string> = {\n primary: \"pv-spinner\",\n dark: \"pv-spinner-dark\",\n white: \"pv-spinner-light\",\n};\n\nwithDefaults(defineProps<PvSpinnerProps>(), {\n size: \"lg\",\n variant: \"primary\",\n});\n</script>\n\n<template>\n <div data-testid=\"pv-spinner\" :class=\"mapVariantToPvClass[variant]\" :style=\"{ '--size': mapSizeToPx[size] }\" />\n</template>\n","<script setup lang=\"ts\">\nimport { PvSpinnerSize, PvSpinnerVariant } from \"./types\";\n\ninterface PvSpinnerProps {\n /** The size of the spinner. Controls the rendered diameter via a CSS custom property. */\n size?: PvSpinnerSize;\n /** The color variant of the spinner. Use \"white\" on dark backgrounds and \"dark\" on light backgrounds. */\n variant?: PvSpinnerVariant;\n}\n\nconst mapSizeToPx: Record<PvSpinnerSize, string> = {\n xs: \"0.75rem\",\n sm: \"1rem\",\n md: \"1.25rem\",\n lg: \"1.5rem\",\n xl: \"2rem\",\n};\n\nconst mapVariantToPvClass: Record<PvSpinnerVariant, string> = {\n primary: \"pv-spinner\",\n dark: \"pv-spinner-dark\",\n white: \"pv-spinner-light\",\n};\n\nwithDefaults(defineProps<PvSpinnerProps>(), {\n size: \"lg\",\n variant: \"primary\",\n});\n</script>\n\n<template>\n <div data-testid=\"pv-spinner\" :class=\"mapVariantToPvClass[variant]\" :style=\"{ '--size': mapSizeToPx[size] }\" />\n</template>\n","import { PvButtonSize } from \"@/components/base/PvButton/types.ts\";\n\nexport const supportedInverseButtonVariants = [\"ghost\"];\n\nconst sizeToClassMap: Record<PvButtonSize, string | undefined> = {\n md: \"pv-button-small\",\n lg: undefined,\n xl: \"pv-button-large\",\n};\n\nexport const pvButtonSizeToClass = (size: PvButtonSize | null | undefined): string | null => {\n if (size == null || !sizeToClassMap.hasOwnProperty(size)) {\n return null;\n }\n return sizeToClassMap[size] || null;\n};\n","/**\n * Utility functions for web components\n */\n\nimport { ref, getCurrentInstance } from \"vue\";\n\n/**\n * Composable to detect if a component is running as a web component\n * Returns a reactive ref that's set to true/false on mount\n *\n * @returns Reactive ref boolean indicating if running as web component\n *\n * @example\n * import { useIsWebComponent } from '@/web-components/utils';\n *\n * export default {\n * setup() {\n * const isWc = useIsWebComponent();\n * // isWc.value is true if running as web component, false if Vue component\n * }\n * }\n */\nexport function useIsWebComponent() {\n const isWc = ref(false);\n const instance = getCurrentInstance();\n const root = instance?.root || {};\n if (\"isCE\" in root && root.isCE === true) {\n isWc.value = true;\n }\n return isWc;\n}\n","export const PvSizes = [\"xs\", \"sm\", \"md\", \"lg\", \"xl\", \"2x\"] as const;\nexport type PvSize = (typeof PvSizes)[number];\n\nexport const PvTheme = [\"white\", \"dark\"] as const;\nexport type PvThemes = (typeof PvTheme)[number];\n\nexport const PvVariants = [\"primary\", \"secondary\", \"tertiary\", \"ghost\", \"destructive\", \"transparent\"] as const;\nexport type PvVariants = (typeof PvVariants)[number];\n\nexport const PvColorVariants = [\"default\", \"success\", \"warning\", \"critical\", \"highlight\"] as const;\nexport type PvColorVariants = (typeof PvColorVariants)[number];\n\nexport const PvInputVariants = [\"h1\", \"h2\", \"h3\", \"h4\", \"text-md\", \"text-sm\", \"text-lg\", \"caption\"] as const;\nexport type PvInputVariants = (typeof PvInputVariants)[number];\n\nexport const PvTextInputColors = [\"white\", \"grey\"] as const;\nexport type PvTextInputColors = (typeof PvTextInputColors)[number];\n\nexport const PvOrientations = [\"vertical\", \"horizontal\"] as const;\nexport type PvOrientations = (typeof PvOrientations)[number];\n\nexport const sizeMap: Record<PvSize, string> = {\n xs: \"12px\",\n sm: \"16px\",\n md: \"20px\",\n lg: \"24px\",\n xl: \"32px\",\n \"2x\": \"40px\",\n};\n\nexport const PvIconSizes = [undefined, 10, 12, 20, 24, 32, 64] as const;\nexport type PvIconSize = (typeof PvIconSizes)[number];\n\nexport type PvAlignmentPositions =\n | \"bottom-left\"\n | \"bottom-center\"\n | \"bottom-right\"\n | \"top-left\"\n | \"top-center\"\n | \"top-right\"\n | \"center-left\"\n | \"center-right\"\n | \"right-top\"\n | \"right-bottom\";\n","<script lang=\"ts\">\nimport { PvIconSize, PvIconSizes } from \"../baseProps\";\n</script>\n<script setup lang=\"ts\">\nimport { computed, ref, Ref } from \"vue\";\nimport { useIsWebComponent } from \"@/web-components/utils\";\n\ntype PvIconProps = {\n /** Name of the icon from the SVG sprite sheet (e.g. \"user\", \"check-circle\"). */\n name: string;\n /** Pixel size of the icon. When omitted the icon inherits its container's size. */\n size?: PvIconSize;\n};\n\nconst props = defineProps<PvIconProps>();\nconst isWeb = useIsWebComponent();\n\nconst globalSpritePath: Ref<string | null> = ref(null);\n\nconst classes = computed(() => ({\n \"pv-icon\": true,\n [`pv-icon-${props.size}`]: props.size != null && PvIconSizes.includes(props.size),\n}));\n\nconst svgHref = computed(() => {\n // If the global sprite path is set, use it\n if (isWeb.value && globalSpritePath.value) {\n return `${globalSpritePath.value}#${props.name}`;\n }\n return `#${props.name}`;\n});\n\n// @ts-expect-error ignore type error for globalThis\nif (globalThis.__PV_GLOBAL_SPRITE_PATH__) {\n // We only want to use sprite paths for web components\n // @ts-expect-error ignore type error for globalThis\n globalSpritePath.value = globalThis.__PV_GLOBAL_SPRITE_PATH__;\n}\n</script>\n\n<template>\n <svg data-testid=\"pv-icon\" aria-hidden=\"true\" :class=\"classes\">\n <use :xlink:href=\"svgHref\"></use>\n </svg>\n</template>\n","<script lang=\"ts\">\nimport { PvIconSize, PvIconSizes } from \"../baseProps\";\n</script>\n<script setup lang=\"ts\">\nimport { computed, ref, Ref } from \"vue\";\nimport { useIsWebComponent } from \"@/web-components/utils\";\n\ntype PvIconProps = {\n /** Name of the icon from the SVG sprite sheet (e.g. \"user\", \"check-circle\"). */\n name: string;\n /** Pixel size of the icon. When omitted the icon inherits its container's size. */\n size?: PvIconSize;\n};\n\nconst props = defineProps<PvIconProps>();\nconst isWeb = useIsWebComponent();\n\nconst globalSpritePath: Ref<string | null> = ref(null);\n\nconst classes = computed(() => ({\n \"pv-icon\": true,\n [`pv-icon-${props.size}`]: props.size != null && PvIconSizes.includes(props.size),\n}));\n\nconst svgHref = computed(() => {\n // If the global sprite path is set, use it\n if (isWeb.value && globalSpritePath.value) {\n return `${globalSpritePath.value}#${props.name}`;\n }\n return `#${props.name}`;\n});\n\n// @ts-expect-error ignore type error for globalThis\nif (globalThis.__PV_GLOBAL_SPRITE_PATH__) {\n // We only want to use sprite paths for web components\n // @ts-expect-error ignore type error for globalThis\n globalSpritePath.value = globalThis.__PV_GLOBAL_SPRITE_PATH__;\n}\n</script>\n\n<template>\n <svg data-testid=\"pv-icon\" aria-hidden=\"true\" :class=\"classes\">\n <use :xlink:href=\"svgHref\"></use>\n </svg>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { PvCounterBadgeSize, PvCounterBadgeVariant } from \"./types\";\n\nexport interface PvCounterBadgeProps {\n /** Maximum value before showing overflow indicator (e.g. \"99+\") */\n maxValue?: number;\n /** Text prefix displayed before the value */\n prefix?: string;\n /** Display size of the badge */\n size?: PvCounterBadgeSize;\n /** Numeric value to display */\n value?: number;\n /** Visual style variant */\n variant?: PvCounterBadgeVariant;\n /** Fully rounded */\n rounded?: boolean;\n}\n\nconst props = withDefaults(defineProps<PvCounterBadgeProps>(), {\n variant: \"primary\",\n maxValue: 99,\n size: \"sm\",\n prefix: \"\",\n});\n\nconst displayValue = computed(() => {\n if (props.maxValue && props.value && props.value > props.maxValue) {\n return `${props.maxValue}+`;\n }\n if (props.value != null) {\n return `${props.prefix}${props.value}`;\n }\n return \"-\";\n});\n\nconst classes = computed(() => ({\n \"pv-inline-block pv-inset-square pv-radius pv-text-center\": true,\n \"pv-badge-md pv-text-body-md\": props.size == \"md\",\n \"pv-badge-sm pv-text-body-sm\": props.size == \"sm\",\n \"pv-surface-brand-inverse pv-text-inverse\": props.variant == \"primary\",\n \"pv-badge-secondary\": props.variant == \"secondary\",\n \"pv-surface-lighten-5\": props.variant == \"tertiary\",\n \"pv-text-subdued\": props.variant == \"ghost\" || props.variant == \"tertiary\",\n \"pv-surface\": props.variant == \"ghost\",\n \"pv-rounded\": props.rounded,\n}));\n</script>\n<template>\n <div :class=\"classes\" data-testid=\"pv-counter-badge\">\n {{ displayValue }}\n </div>\n</template>\n\n<style scoped>\n.pv-badge-md {\n --inset-size: 2px 2px;\n min-height: 20px;\n max-height: 20px;\n min-width: 20px;\n font-weight: 500;\n}\n.pv-badge-sm {\n --inset-size: 0 2px;\n min-height: 16px;\n max-height: 16px;\n min-width: 16px;\n font-weight: 500;\n}\n.pv-rounded {\n border-radius: 50%;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { PvCounterBadgeSize, PvCounterBadgeVariant } from \"./types\";\n\nexport interface PvCounterBadgeProps {\n /** Maximum value before showing overflow indicator (e.g. \"99+\") */\n maxValue?: number;\n /** Text prefix displayed before the value */\n prefix?: string;\n /** Display size of the badge */\n size?: PvCounterBadgeSize;\n /** Numeric value to display */\n value?: number;\n /** Visual style variant */\n variant?: PvCounterBadgeVariant;\n /** Fully rounded */\n rounded?: boolean;\n}\n\nconst props = withDefaults(defineProps<PvCounterBadgeProps>(), {\n variant: \"primary\",\n maxValue: 99,\n size: \"sm\",\n prefix: \"\",\n});\n\nconst displayValue = computed(() => {\n if (props.maxValue && props.value && props.value > props.maxValue) {\n return `${props.maxValue}+`;\n }\n if (props.value != null) {\n return `${props.prefix}${props.value}`;\n }\n return \"-\";\n});\n\nconst classes = computed(() => ({\n \"pv-inline-block pv-inset-square pv-radius pv-text-center\": true,\n \"pv-badge-md pv-text-body-md\": props.size == \"md\",\n \"pv-badge-sm pv-text-body-sm\": props.size == \"sm\",\n \"pv-surface-brand-inverse pv-text-inverse\": props.variant == \"primary\",\n \"pv-badge-secondary\": props.variant == \"secondary\",\n \"pv-surface-lighten-5\": props.variant == \"tertiary\",\n \"pv-text-subdued\": props.variant == \"ghost\" || props.variant == \"tertiary\",\n \"pv-surface\": props.variant == \"ghost\",\n \"pv-rounded\": props.rounded,\n}));\n</script>\n<template>\n <div :class=\"classes\" data-testid=\"pv-counter-badge\">\n {{ displayValue }}\n </div>\n</template>\n\n<style scoped>\n.pv-badge-md {\n --inset-size: 2px 2px;\n min-height: 20px;\n max-height: 20px;\n min-width: 20px;\n font-weight: 500;\n}\n.pv-badge-sm {\n --inset-size: 0 2px;\n min-height: 16px;\n max-height: 16px;\n min-width: 16px;\n font-weight: 500;\n}\n.pv-rounded {\n border-radius: 50%;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { PvButtonProps } from \"./types\";\nimport PvSpinner from \"../PvSpinner/PvSpinner.vue\";\nimport { pvButtonSizeToClass, supportedInverseButtonVariants } from \"@/components/base/PvButton/helpers.ts\";\nimport PvIcon from \"../PvIcon/PvIcon.vue\";\nimport PvCounterBadge from \"../PvCounterBadge/PvCounterBadge.vue\";\n\nconst props = withDefaults(defineProps<PvButtonProps>(), {\n variant: \"primary\",\n disabled: false,\n size: \"lg\",\n loading: false,\n inverse: false,\n});\n\nconst computedAriaLabel = computed(() => (!props.label || props.loading ? props.label || props.ariaLabel : undefined));\n\nconst classes = computed(() => {\n const output = [];\n if (props.inverse && supportedInverseButtonVariants.includes(props.variant)) {\n output.push(`pv-button-${props.variant}-inverse`);\n } else {\n output.push(`pv-button-${props.variant}`);\n }\n const sizeClass = pvButtonSizeToClass(props.size);\n if (sizeClass) {\n output.push(sizeClass);\n }\n return output;\n});\n</script>\n\n<template>\n <button type=\"button\" :class=\"classes\" :disabled=\"disabled\" :aria-label=\"computedAriaLabel\" data-testid=\"pv-button\">\n <PvSpinner v-if=\"loading\" size=\"sm\" />\n <template v-else>\n <PvCounterBadge v-if=\"leftCounterBadge\" :value=\"leftCounterBadge\" variant=\"tertiary\" />\n <PvIcon v-if=\"leftIcon\" :name=\"leftIcon\" data-testid=\"pv-button-left-icon\" />\n <span v-if=\"label\" data-testid=\"pv-button-label\">{{ label }}</span>\n <PvCounterBadge v-if=\"rightCounterBadge\" :value=\"rightCounterBadge\" variant=\"tertiary\" />\n <PvIcon v-if=\"rightIcon\" :name=\"rightIcon\" data-testid=\"pv-button-right-icon\" />\n </template>\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { PvButtonProps } from \"./types\";\nimport PvSpinner from \"../PvSpinner/PvSpinner.vue\";\nimport { pvButtonSizeToClass, supportedInverseButtonVariants } from \"@/components/base/PvButton/helpers.ts\";\nimport PvIcon from \"../PvIcon/PvIcon.vue\";\nimport PvCounterBadge from \"../PvCounterBadge/PvCounterBadge.vue\";\n\nconst props = withDefaults(defineProps<PvButtonProps>(), {\n variant: \"primary\",\n disabled: false,\n size: \"lg\",\n loading: false,\n inverse: false,\n});\n\nconst computedAriaLabel = computed(() => (!props.label || props.loading ? props.label || props.ariaLabel : undefined));\n\nconst classes = computed(() => {\n const output = [];\n if (props.inverse && supportedInverseButtonVariants.includes(props.variant)) {\n output.push(`pv-button-${props.variant}-inverse`);\n } else {\n output.push(`pv-button-${props.variant}`);\n }\n const sizeClass = pvButtonSizeToClass(props.size);\n if (sizeClass) {\n output.push(sizeClass);\n }\n return output;\n});\n</script>\n\n<template>\n <button type=\"button\" :class=\"classes\" :disabled=\"disabled\" :aria-label=\"computedAriaLabel\" data-testid=\"pv-button\">\n <PvSpinner v-if=\"loading\" size=\"sm\" />\n <template v-else>\n <PvCounterBadge v-if=\"leftCounterBadge\" :value=\"leftCounterBadge\" variant=\"tertiary\" />\n <PvIcon v-if=\"leftIcon\" :name=\"leftIcon\" data-testid=\"pv-button-left-icon\" />\n <span v-if=\"label\" data-testid=\"pv-button-label\">{{ label }}</span>\n <PvCounterBadge v-if=\"rightCounterBadge\" :value=\"rightCounterBadge\" variant=\"tertiary\" />\n <PvIcon v-if=\"rightIcon\" :name=\"rightIcon\" data-testid=\"pv-button-right-icon\" />\n </template>\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { PvInputType, PvInputVariant } from \"./types\";\nimport PvIcon from \"../PvIcon/PvIcon.vue\";\ninterface PvInputProps {\n /** When true, upper label is visible */\n showLabel?: boolean;\n /** When true, applies invalid/error styling to the input and lower label. */\n error?: boolean;\n /** When true, the input is non-interactive and visually dimmed. */\n disabled?: boolean;\n /** Placeholder text shown when the input is empty. */\n placeholder?: string;\n /**\n * The HTML input type.\n */\n type?: PvInputType;\n /** HTML id for the input element. Auto-generated when omitted. */\n id?: string;\n /** Helper or validation text displayed below the input. Turns red when error is true. */\n lowerLabel?: string;\n /**\n * Background color variant of the input field.\n */\n variant?: PvInputVariant;\n /** The step attribute for number inputs (e.g. '0.01' for currency). */\n step?: string;\n /** Static text shown before the Input */\n prefixLabel?: string;\n /** Optional Icon on the left of the Input */\n inputIcon?: string;\n}\n\nconst props = withDefaults(defineProps<PvInputProps>(), {\n id: Math.random().toString(),\n type: \"text\",\n variant: \"white\",\n});\n\nconst value = defineModel<string>();\n\nconst inputPadding = computed(() => {\n if (props.inputIcon) {\n return {\n \"--inset-size\": \"8px 12px 6px 36px\",\n };\n }\n\n return {};\n});\n</script>\n\n<template>\n <label v-if=\"$slots.label || showLabel\" data-testid=\"pv-input-label\" class=\"pv-label pv-stack-8\" :for=\"id\">\n <slot name=\"label\" />\n </label>\n <div\n class=\"pv-flex pv-flex-item\"\n :class=\"{\n 'pv-stack-8': lowerLabel,\n }\"\n >\n <div\n v-if=\"prefixLabel\"\n class=\"pv-surface-accent pv-inset-square pv-text-body-md pv-radius pv-bordered\"\n style=\"--inset-size: 6px 10px\"\n >\n {{ prefixLabel }}\n </div>\n <div class=\"pv-relative pv-flex-item\">\n <PvIcon class=\"pv-absolute input-icon-position\" v-if=\"inputIcon\" :name=\"inputIcon\" />\n <input\n v-bind=\"$attrs\"\n v-model=\"value\"\n data-testid=\"pv-input\"\n :class=\"[\n 'pv-input-text',\n {\n 'pv-inset-square': inputIcon,\n 'pv-surface-accent': variant === 'grey',\n },\n ]\"\n :style=\"[inputPadding, { width: '100%' }]\"\n :disabled=\"disabled\"\n :type=\"type\"\n :placeholder=\"placeholder\"\n :id=\"id\"\n :data-invalid=\"error ? true : undefined\"\n :step=\"step\"\n />\n </div>\n </div>\n <p\n v-if=\"lowerLabel\"\n data-testid=\"pv-input-lower-label\"\n :class=\"[\n 'pv-text-body-xs',\n {\n 'pv-text-red': error,\n 'pv-text-subdued': !error,\n },\n ]\"\n >\n {{ lowerLabel }}\n </p>\n</template>\n\n<style>\n.input-icon-position {\n position: absolute;\n left: 12px;\n top: 8px;\n pointer-events: none;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { PvInputType, PvInputVariant } from \"./types\";\nimport PvIcon from \"../PvIcon/PvIcon.vue\";\ninterface PvInputProps {\n /** When true, upper label is visible */\n showLabel?: boolean;\n /** When true, applies invalid/error styling to the input and lower label. */\n error?: boolean;\n /** When true, the input is non-interactive and visually dimmed. */\n disabled?: boolean;\n /** Placeholder text shown when the input is empty. */\n placeholder?: string;\n /**\n * The HTML input type.\n */\n type?: PvInputType;\n /** HTML id for the input element. Auto-generated when omitted. */\n id?: string;\n /** Helper or validation text displayed below the input. Turns red when error is true. */\n lowerLabel?: string;\n /**\n * Background color variant of the input field.\n */\n variant?: PvInputVariant;\n /** The step attribute for number inputs (e.g. '0.01' for currency). */\n step?: string;\n /** Static text shown before the Input */\n prefixLabel?: string;\n /** Optional Icon on the left of the Input */\n inputIcon?: string;\n}\n\nconst props = withDefaults(defineProps<PvInputProps>(), {\n id: Math.random().toString(),\n type: \"text\",\n variant: \"white\",\n});\n\nconst value = defineModel<string>();\n\nconst inputPadding = computed(() => {\n if (props.inputIcon) {\n return {\n \"--inset-size\": \"8px 12px 6px 36px\",\n };\n }\n\n return {};\n});\n</script>\n\n<template>\n <label v-if=\"$slots.label || showLabel\" data-testid=\"pv-input-label\" class=\"pv-label pv-stack-8\" :for=\"id\">\n <slot name=\"label\" />\n </label>\n <div\n class=\"pv-flex pv-flex-item\"\n :class=\"{\n 'pv-stack-8': lowerLabel,\n }\"\n >\n <div\n v-if=\"prefixLabel\"\n class=\"pv-surface-accent pv-inset-square pv-text-body-md pv-radius pv-bordered\"\n style=\"--inset-size: 6px 10px\"\n >\n {{ prefixLabel }}\n </div>\n <div class=\"pv-relative pv-flex-item\">\n <PvIcon class=\"pv-absolute input-icon-position\" v-if=\"inputIcon\" :name=\"inputIcon\" />\n <input\n v-bind=\"$attrs\"\n v-model=\"value\"\n data-testid=\"pv-input\"\n :class=\"[\n 'pv-input-text',\n {\n 'pv-inset-square': inputIcon,\n 'pv-surface-accent': variant === 'grey',\n },\n ]\"\n :style=\"[inputPadding, { width: '100%' }]\"\n :disabled=\"disabled\"\n :type=\"type\"\n :placeholder=\"placeholder\"\n :id=\"id\"\n :data-invalid=\"error ? true : undefined\"\n :step=\"step\"\n />\n </div>\n </div>\n <p\n v-if=\"lowerLabel\"\n data-testid=\"pv-input-lower-label\"\n :class=\"[\n 'pv-text-body-xs',\n {\n 'pv-text-red': error,\n 'pv-text-subdued': !error,\n },\n ]\"\n >\n {{ lowerLabel }}\n </p>\n</template>\n\n<style>\n.input-icon-position {\n position: absolute;\n left: 12px;\n top: 8px;\n pointer-events: none;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref, useId, watch } from \"vue\";\n\nimport PvButton from \"@/components/base/PvButton/PvButton.vue\";\nimport PvCounterBadge from \"@/components/base/PvCounterBadge/PvCounterBadge.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\nimport PvInput from \"@/components/base/PvInput/PvInput.vue\";\n\nimport type { PvQuestionOption, PvQuestionProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvQuestionProps>(), {\n chatInputLabel: \"Other answer\",\n chatInputPlaceholder: \"Something else...\",\n currentQuestion: 1,\n headerLabel: \"Question\",\n options: () => [],\n subheader: \"\",\n totalQuestions: 1,\n});\n\nconst emit = defineEmits<{\n /** The close (X) button was pressed to dismiss the question. */\n (event: \"close\"): void;\n /** An option was chosen from the list. */\n (event: \"select\", option: PvQuestionOption): void;\n /** The question was skipped via the Skip button or the Escape key. */\n (event: \"skip\"): void;\n /** A free-text answer was submitted from the chat input. */\n (event: \"submit\", value: string): void;\n}>();\n\nconst selectedId = defineModel<string>();\nconst chatInput = defineModel<string>(\"chatInput\", { default: \"\" });\n\nconst componentId = useId();\nconst headerId = `${componentId}-heading`;\nconst hintId = `${componentId}-keyboard-hint`;\nconst listboxRef = ref<HTMLElement>();\nconst chatInputWrapperRef = ref<HTMLElement>();\nconst selectedIndex = props.options.findIndex((option) => option.id === selectedId.value);\nconst activeIndex = ref(Math.max(selectedIndex, 0));\n\nconst showQuestionCounter = computed(() => props.totalQuestions > 1);\nconst activeDescendant = computed(() =>\n activeIndex.value < props.options.length ? optionDomId(activeIndex.value) : undefined,\n);\n\nwatch(selectedId, (value) => {\n const selectedIndex = props.options.findIndex((option) => option.id === value);\n if (selectedIndex >= 0) activeIndex.value = selectedIndex;\n});\n\nwatch(\n () => props.options.map((option) => option.id),\n (optionIds, previousOptionIds) => {\n const selectedIndex = optionIds.findIndex((id) => id === selectedId.value);\n if (selectedIndex >= 0) {\n activeIndex.value = selectedIndex;\n return;\n }\n\n if (activeIndex.value === previousOptionIds.length) {\n activeIndex.value = optionIds.length;\n return;\n }\n\n const previousActiveId = previousOptionIds[activeIndex.value];\n const reorderedIndex = optionIds.indexOf(previousActiveId);\n activeIndex.value =\n reorderedIndex >= 0 ? reorderedIndex : Math.min(activeIndex.value, Math.max(optionIds.length - 1, 0));\n },\n);\n\nfunction optionDomId(index: number) {\n return `${componentId}-option-${index}`;\n}\n\nfunction focusListbox(index: number) {\n if (!props.options.length) return;\n\n activeIndex.value = index;\n nextTick(() => listboxRef.value?.focus());\n}\n\nfunction focusChatInput() {\n activeIndex.value = props.options.length;\n nextTick(() => chatInputWrapperRef.value?.querySelector<HTMLInputElement>(\"input\")?.focus());\n}\n\nfunction selectOption(option: PvQuestionOption, index: number) {\n activeIndex.value = index;\n selectedId.value = option.id;\n emit(\"select\", option);\n}\n\nfunction handleOptionClick(option: PvQuestionOption, index: number) {\n selectOption(option, index);\n nextTick(() => listboxRef.value?.focus());\n}\n\nfunction handleListboxFocus() {\n if (activeIndex.value >= props.options.length) activeIndex.value = 0;\n}\n\nfunction handleListboxKeydown(event: KeyboardEvent) {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n if (activeIndex.value >= props.options.length - 1) {\n focusChatInput();\n } else {\n activeIndex.value += 1;\n }\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n if (activeIndex.value <= 0) {\n focusChatInput();\n } else {\n activeIndex.value -= 1;\n }\n } else if (event.key === \"Enter\" && props.options[activeIndex.value]) {\n event.preventDefault();\n selectOption(props.options[activeIndex.value], activeIndex.value);\n }\n}\n\nfunction handleChatFocus() {\n activeIndex.value = props.options.length;\n // The free-text answer replaces any option answer, so drop the previous selection.\n selectedId.value = undefined;\n}\n\nfunction handleChatKeydown(event: KeyboardEvent) {\n if (!props.options.length && (event.key === \"ArrowDown\" || event.key === \"ArrowUp\")) return;\n\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n focusListbox(0);\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n focusListbox(props.options.length - 1);\n } else if (event.key === \"Enter\" && !event.isComposing) {\n event.preventDefault();\n const value = chatInput.value.trim();\n if (value) emit(\"submit\", value);\n }\n}\n\nfunction handleClose() {\n emit(\"close\");\n}\n\nfunction handleSkip() {\n emit(\"skip\");\n}\n</script>\n\n<template>\n <section\n class=\"pv-question pv-card pv-surface pv-full-width\"\n data-testid=\"pv-question\"\n :aria-labelledby=\"headerId\"\n @keydown.esc.stop.prevent=\"handleSkip\"\n >\n <header class=\"pv-question-header pv-inset-square-16 pv-space-between\">\n <div\n class=\"pv-question-header-copy pv-flex-vertical pv-flex-item\"\n style=\"--flex-align: flex-start; --flex-gap: 4px; min-width: 0\"\n >\n <h3 :id=\"headerId\" class=\"pv-heading-3\">{{ headerLabel }}</h3>\n <p v-if=\"subheader\" class=\"pv-text-body-md pv-text-tertiary\">{{ subheader }}</p>\n </div>\n <div class=\"pv-question-header-actions pv-flex\">\n <span v-if=\"showQuestionCounter\" class=\"pv-text-body-xs pv-text-tertiary\" role=\"status\" aria-live=\"polite\">\n {{ currentQuestion }} of {{ totalQuestions }}\n </span>\n <PvButton ariaLabel=\"Close\" leftIcon=\"close\" size=\"md\" variant=\"ghost\" @click=\"handleClose\" />\n </div>\n </header>\n\n <div class=\"pv-question-body pv-border-bottom\" style=\"padding-bottom: 2px\">\n <div\n class=\"pv-question-options pv-flex-vertical pv-inset-inline-16\"\n style=\"--flex-align: stretch; --flex-gap: 2px\"\n >\n <div\n ref=\"listboxRef\"\n class=\"pv-question-listbox pv-flex-vertical\"\n style=\"--flex-align: stretch; --flex-gap: 2px\"\n data-testid=\"pv-question-listbox\"\n role=\"listbox\"\n tabindex=\"0\"\n :aria-activedescendant=\"activeDescendant\"\n :aria-describedby=\"hintId\"\n :aria-labelledby=\"headerId\"\n @focus=\"handleListboxFocus\"\n @keydown=\"handleListboxKeydown\"\n >\n <div\n v-for=\"(option, index) in options\"\n :id=\"optionDomId(index)\"\n :key=\"option.id\"\n class=\"pv-question-option pv-flex pv-inset-squish-12 pv-radius-lg\"\n data-testid=\"pv-question-option\"\n role=\"option\"\n :aria-selected=\"selectedId === option.id\"\n :data-active=\"activeIndex === index || undefined\"\n @click=\"handleOptionClick(option, index)\"\n @mouseenter=\"activeIndex = index\"\n >\n <PvCounterBadge :value=\"index + 1\" size=\"sm\" variant=\"tertiary\" />\n <span class=\"pv-question-answer-text pv-flex-item pv-text-title-md\" style=\"min-width: 0\">{{\n option.label\n }}</span>\n <span\n v-if=\"option.helpText\"\n class=\"pv-question-help pv-flex\"\n style=\"--flex-gap: 0\"\n :title=\"option.helpText\"\n >\n <PvIcon class=\"pv-text-tertiary\" name=\"info-circle\" :size=\"12\" />\n <span class=\"pv-visually-hidden\">{{ option.helpText }}</span>\n </span>\n <PvIcon v-if=\"selectedId === option.id\" class=\"pv-question-check pv-text-brand\" name=\"check\" />\n <PvIcon class=\"pv-question-chevron pv-text-tertiary\" name=\"chevron-right\" />\n </div>\n </div>\n\n <div\n ref=\"chatInputWrapperRef\"\n class=\"pv-question-chat pv-flex pv-inset-squish-12 pv-radius-lg\"\n data-testid=\"pv-question-chat\"\n :data-active=\"activeIndex === options.length || undefined\"\n @click=\"focusChatInput\"\n @mouseenter=\"activeIndex = options.length\"\n >\n <span class=\"pv-question-chat-badge pv-flex pv-radius pv-text-subdued\">\n <PvIcon name=\"text-search\" :size=\"12\" />\n </span>\n <div class=\"pv-question-chat-input pv-flex-item\" style=\"min-width: 0\">\n <PvInput\n v-model=\"chatInput\"\n :aria-label=\"chatInputLabel\"\n :placeholder=\"chatInputPlaceholder\"\n @focus=\"handleChatFocus\"\n @keydown=\"handleChatKeydown\"\n />\n </div>\n <PvIcon class=\"pv-question-chevron pv-text-tertiary\" name=\"chevron-right\" />\n </div>\n </div>\n </div>\n\n <footer class=\"pv-question-footer pv-inset-squish-12 pv-space-between\">\n <span :id=\"hintId\" class=\"pv-text-body-xs pv-text-tertiary\">↑↓ to navigate • Enter to select • Esc to skip</span>\n <PvButton class=\"pv-question-skip\" label=\"Skip\" size=\"lg\" variant=\"secondary\" @click=\"handleSkip\" />\n </footer>\n </section>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref, useId, watch } from \"vue\";\n\nimport PvButton from \"@/components/base/PvButton/PvButton.vue\";\nimport PvCounterBadge from \"@/components/base/PvCounterBadge/PvCounterBadge.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\nimport PvInput from \"@/components/base/PvInput/PvInput.vue\";\n\nimport type { PvQuestionOption, PvQuestionProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvQuestionProps>(), {\n chatInputLabel: \"Other answer\",\n chatInputPlaceholder: \"Something else...\",\n currentQuestion: 1,\n headerLabel: \"Question\",\n options: () => [],\n subheader: \"\",\n totalQuestions: 1,\n});\n\nconst emit = defineEmits<{\n /** The close (X) button was pressed to dismiss the question. */\n (event: \"close\"): void;\n /** An option was chosen from the list. */\n (event: \"select\", option: PvQuestionOption): void;\n /** The question was skipped via the Skip button or the Escape key. */\n (event: \"skip\"): void;\n /** A free-text answer was submitted from the chat input. */\n (event: \"submit\", value: string): void;\n}>();\n\nconst selectedId = defineModel<string>();\nconst chatInput = defineModel<string>(\"chatInput\", { default: \"\" });\n\nconst componentId = useId();\nconst headerId = `${componentId}-heading`;\nconst hintId = `${componentId}-keyboard-hint`;\nconst listboxRef = ref<HTMLElement>();\nconst chatInputWrapperRef = ref<HTMLElement>();\nconst selectedIndex = props.options.findIndex((option) => option.id === selectedId.value);\nconst activeIndex = ref(Math.max(selectedIndex, 0));\n\nconst showQuestionCounter = computed(() => props.totalQuestions > 1);\nconst activeDescendant = computed(() =>\n activeIndex.value < props.options.length ? optionDomId(activeIndex.value) : undefined,\n);\n\nwatch(selectedId, (value) => {\n const selectedIndex = props.options.findIndex((option) => option.id === value);\n if (selectedIndex >= 0) activeIndex.value = selectedIndex;\n});\n\nwatch(\n () => props.options.map((option) => option.id),\n (optionIds, previousOptionIds) => {\n const selectedIndex = optionIds.findIndex((id) => id === selectedId.value);\n if (selectedIndex >= 0) {\n activeIndex.value = selectedIndex;\n return;\n }\n\n if (activeIndex.value === previousOptionIds.length) {\n activeIndex.value = optionIds.length;\n return;\n }\n\n const previousActiveId = previousOptionIds[activeIndex.value];\n const reorderedIndex = optionIds.indexOf(previousActiveId);\n activeIndex.value =\n reorderedIndex >= 0 ? reorderedIndex : Math.min(activeIndex.value, Math.max(optionIds.length - 1, 0));\n },\n);\n\nfunction optionDomId(index: number) {\n return `${componentId}-option-${index}`;\n}\n\nfunction focusListbox(index: number) {\n if (!props.options.length) return;\n\n activeIndex.value = index;\n nextTick(() => listboxRef.value?.focus());\n}\n\nfunction focusChatInput() {\n activeIndex.value = props.options.length;\n nextTick(() => chatInputWrapperRef.value?.querySelector<HTMLInputElement>(\"input\")?.focus());\n}\n\nfunction selectOption(option: PvQuestionOption, index: number) {\n activeIndex.value = index;\n selectedId.value = option.id;\n emit(\"select\", option);\n}\n\nfunction handleOptionClick(option: PvQuestionOption, index: number) {\n selectOption(option, index);\n nextTick(() => listboxRef.value?.focus());\n}\n\nfunction handleListboxFocus() {\n if (activeIndex.value >= props.options.length) activeIndex.value = 0;\n}\n\nfunction handleListboxKeydown(event: KeyboardEvent) {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n if (activeIndex.value >= props.options.length - 1) {\n focusChatInput();\n } else {\n activeIndex.value += 1;\n }\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n if (activeIndex.value <= 0) {\n focusChatInput();\n } else {\n activeIndex.value -= 1;\n }\n } else if (event.key === \"Enter\" && props.options[activeIndex.value]) {\n event.preventDefault();\n selectOption(props.options[activeIndex.value], activeIndex.value);\n }\n}\n\nfunction handleChatFocus() {\n activeIndex.value = props.options.length;\n // The free-text answer replaces any option answer, so drop the previous selection.\n selectedId.value = undefined;\n}\n\nfunction handleChatKeydown(event: KeyboardEvent) {\n if (!props.options.length && (event.key === \"ArrowDown\" || event.key === \"ArrowUp\")) return;\n\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n focusListbox(0);\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n focusListbox(props.options.length - 1);\n } else if (event.key === \"Enter\" && !event.isComposing) {\n event.preventDefault();\n const value = chatInput.value.trim();\n if (value) emit(\"submit\", value);\n }\n}\n\nfunction handleClose() {\n emit(\"close\");\n}\n\nfunction handleSkip() {\n emit(\"skip\");\n}\n</script>\n\n<template>\n <section\n class=\"pv-question pv-card pv-surface pv-full-width\"\n data-testid=\"pv-question\"\n :aria-labelledby=\"headerId\"\n @keydown.esc.stop.prevent=\"handleSkip\"\n >\n <header class=\"pv-question-header pv-inset-square-16 pv-space-between\">\n <div\n class=\"pv-question-header-copy pv-flex-vertical pv-flex-item\"\n style=\"--flex-align: flex-start; --flex-gap: 4px; min-width: 0\"\n >\n <h3 :id=\"headerId\" class=\"pv-heading-3\">{{ headerLabel }}</h3>\n <p v-if=\"subheader\" class=\"pv-text-body-md pv-text-tertiary\">{{ subheader }}</p>\n </div>\n <div class=\"pv-question-header-actions pv-flex\">\n <span v-if=\"showQuestionCounter\" class=\"pv-text-body-xs pv-text-tertiary\" role=\"status\" aria-live=\"polite\">\n {{ currentQuestion }} of {{ totalQuestions }}\n </span>\n <PvButton ariaLabel=\"Close\" leftIcon=\"close\" size=\"md\" variant=\"ghost\" @click=\"handleClose\" />\n </div>\n </header>\n\n <div class=\"pv-question-body pv-border-bottom\" style=\"padding-bottom: 2px\">\n <div\n class=\"pv-question-options pv-flex-vertical pv-inset-inline-16\"\n style=\"--flex-align: stretch; --flex-gap: 2px\"\n >\n <div\n ref=\"listboxRef\"\n class=\"pv-question-listbox pv-flex-vertical\"\n style=\"--flex-align: stretch; --flex-gap: 2px\"\n data-testid=\"pv-question-listbox\"\n role=\"listbox\"\n tabindex=\"0\"\n :aria-activedescendant=\"activeDescendant\"\n :aria-describedby=\"hintId\"\n :aria-labelledby=\"headerId\"\n @focus=\"handleListboxFocus\"\n @keydown=\"handleListboxKeydown\"\n >\n <div\n v-for=\"(option, index) in options\"\n :id=\"optionDomId(index)\"\n :key=\"option.id\"\n class=\"pv-question-option pv-flex pv-inset-squish-12 pv-radius-lg\"\n data-testid=\"pv-question-option\"\n role=\"option\"\n :aria-selected=\"selectedId === option.id\"\n :data-active=\"activeIndex === index || undefined\"\n @click=\"handleOptionClick(option, index)\"\n @mouseenter=\"activeIndex = index\"\n >\n <PvCounterBadge :value=\"index + 1\" size=\"sm\" variant=\"tertiary\" />\n <span class=\"pv-question-answer-text pv-flex-item pv-text-title-md\" style=\"min-width: 0\">{{\n option.label\n }}</span>\n <span\n v-if=\"option.helpText\"\n class=\"pv-question-help pv-flex\"\n style=\"--flex-gap: 0\"\n :title=\"option.helpText\"\n >\n <PvIcon class=\"pv-text-tertiary\" name=\"info-circle\" :size=\"12\" />\n <span class=\"pv-visually-hidden\">{{ option.helpText }}</span>\n </span>\n <PvIcon v-if=\"selectedId === option.id\" class=\"pv-question-check pv-text-brand\" name=\"check\" />\n <PvIcon class=\"pv-question-chevron pv-text-tertiary\" name=\"chevron-right\" />\n </div>\n </div>\n\n <div\n ref=\"chatInputWrapperRef\"\n class=\"pv-question-chat pv-flex pv-inset-squish-12 pv-radius-lg\"\n data-testid=\"pv-question-chat\"\n :data-active=\"activeIndex === options.length || undefined\"\n @click=\"focusChatInput\"\n @mouseenter=\"activeIndex = options.length\"\n >\n <span class=\"pv-question-chat-badge pv-flex pv-radius pv-text-subdued\">\n <PvIcon name=\"text-search\" :size=\"12\" />\n </span>\n <div class=\"pv-question-chat-input pv-flex-item\" style=\"min-width: 0\">\n <PvInput\n v-model=\"chatInput\"\n :aria-label=\"chatInputLabel\"\n :placeholder=\"chatInputPlaceholder\"\n @focus=\"handleChatFocus\"\n @keydown=\"handleChatKeydown\"\n />\n </div>\n <PvIcon class=\"pv-question-chevron pv-text-tertiary\" name=\"chevron-right\" />\n </div>\n </div>\n </div>\n\n <footer class=\"pv-question-footer pv-inset-squish-12 pv-space-between\">\n <span :id=\"hintId\" class=\"pv-text-body-xs pv-text-tertiary\">↑↓ to navigate • Enter to select • Esc to skip</span>\n <PvButton class=\"pv-question-skip\" label=\"Skip\" size=\"lg\" variant=\"secondary\" @click=\"handleSkip\" />\n </footer>\n </section>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport PvButton from \"@/components/base/PvButton/PvButton.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\n\nimport type { PvConfirmationProps, PvConfirmationResponse } from \"./types\";\n\nconst props = withDefaults(defineProps<PvConfirmationProps>(), {\n approveLabel: \"Approve\",\n disabled: false,\n message: \"This tool requires your approval before it can run.\",\n rejectLabel: \"Reject\",\n});\n\nconst emit = defineEmits<{\n /** The user approved or rejected the tool execution. */\n (event: \"respond\", response: PvConfirmationResponse): void;\n}>();\n\nconst isPending = computed(\n () => props.state === \"approval-requested\" && props.approval !== undefined && props.approval.approved === undefined,\n);\nconst isApproved = computed(\n () =>\n props.approval?.approved === true &&\n (props.state === \"approval-responded\" || props.state === \"input-available\" || props.state === \"output-available\"),\n);\nconst isRejected = computed(() => props.approval?.approved === false || props.state === \"output-denied\");\n\nfunction respond(approved: boolean) {\n emit(\"respond\", { id: props.approval!.id, approved });\n}\n</script>\n\n<template>\n <section\n v-if=\"isPending || isApproved || isRejected\"\n class=\"pv-confirmation pv-bordered pv-radius pv-inset-square-16 pv-flow-16\"\n data-testid=\"pv-confirmation\"\n aria-label=\"Tool approval\"\n >\n <template v-if=\"isPending\">\n <div class=\"pv-text-body-sm\">\n <slot>{{ message }}</slot>\n </div>\n <div class=\"pv-flex\" style=\"--flex-justify: flex-end\">\n <PvButton variant=\"secondary\" size=\"md\" :label=\"rejectLabel\" :disabled=\"disabled\" @click=\"respond(false)\" />\n <PvButton size=\"md\" :label=\"approveLabel\" :disabled=\"disabled\" @click=\"respond(true)\" />\n </div>\n </template>\n\n <p v-else-if=\"isApproved\" class=\"pv-flex pv-text-body-sm pv-text-success\" role=\"status\">\n <PvIcon name=\"check-circle\" :size=\"20\" />\n <slot name=\"accepted\">You approved this tool execution.</slot>\n </p>\n\n <p v-else class=\"pv-flex pv-text-body-sm pv-text-critical\" role=\"status\">\n <PvIcon name=\"close-circle\" :size=\"20\" />\n <slot name=\"rejected\">You rejected this tool execution.</slot>\n </p>\n </section>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport PvButton from \"@/components/base/PvButton/PvButton.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\n\nimport type { PvConfirmationProps, PvConfirmationResponse } from \"./types\";\n\nconst props = withDefaults(defineProps<PvConfirmationProps>(), {\n approveLabel: \"Approve\",\n disabled: false,\n message: \"This tool requires your approval before it can run.\",\n rejectLabel: \"Reject\",\n});\n\nconst emit = defineEmits<{\n /** The user approved or rejected the tool execution. */\n (event: \"respond\", response: PvConfirmationResponse): void;\n}>();\n\nconst isPending = computed(\n () => props.state === \"approval-requested\" && props.approval !== undefined && props.approval.approved === undefined,\n);\nconst isApproved = computed(\n () =>\n props.approval?.approved === true &&\n (props.state === \"approval-responded\" || props.state === \"input-available\" || props.state === \"output-available\"),\n);\nconst isRejected = computed(() => props.approval?.approved === false || props.state === \"output-denied\");\n\nfunction respond(approved: boolean) {\n emit(\"respond\", { id: props.approval!.id, approved });\n}\n</script>\n\n<template>\n <section\n v-if=\"isPending || isApproved || isRejected\"\n class=\"pv-confirmation pv-bordered pv-radius pv-inset-square-16 pv-flow-16\"\n data-testid=\"pv-confirmation\"\n aria-label=\"Tool approval\"\n >\n <template v-if=\"isPending\">\n <div class=\"pv-text-body-sm\">\n <slot>{{ message }}</slot>\n </div>\n <div class=\"pv-flex\" style=\"--flex-justify: flex-end\">\n <PvButton variant=\"secondary\" size=\"md\" :label=\"rejectLabel\" :disabled=\"disabled\" @click=\"respond(false)\" />\n <PvButton size=\"md\" :label=\"approveLabel\" :disabled=\"disabled\" @click=\"respond(true)\" />\n </div>\n </template>\n\n <p v-else-if=\"isApproved\" class=\"pv-flex pv-text-body-sm pv-text-success\" role=\"status\">\n <PvIcon name=\"check-circle\" :size=\"20\" />\n <slot name=\"accepted\">You approved this tool execution.</slot>\n </p>\n\n <p v-else class=\"pv-flex pv-text-body-sm pv-text-critical\" role=\"status\">\n <PvIcon name=\"close-circle\" :size=\"20\" />\n <slot name=\"rejected\">You rejected this tool execution.</slot>\n </p>\n </section>\n</template>\n","<script setup lang=\"ts\">\nimport { PvTextAreaVariant } from \"./types\";\n\ninterface PvTextareaProps {\n /** When true, applies invalid/error styling to the textarea and lower label. */\n error?: boolean;\n /** When true, the textarea is non-interactive and visually dimmed. */\n disabled?: boolean;\n /** Placeholder text shown when the textarea is empty. */\n placeholder?: string;\n /** HTML id for the textarea element. Auto-generated when omitted. */\n id?: string;\n /** Helper or validation text displayed below the textarea. Turns red when error is true. */\n lowerLabel?: string;\n /**\n * Background color variant of the textarea.\n */\n variant?: PvTextAreaVariant;\n /**\n * Number of visible text rows.\n */\n rows?: number;\n}\n\nwithDefaults(defineProps<PvTextareaProps>(), {\n id: Math.random().toString(),\n variant: \"white\",\n rows: 4,\n});\n\nconst value = defineModel<string>();\n</script>\n\n<template>\n <div class=\"pv-flex-vertical\" style=\"--flex-align: flex-start\">\n <label v-if=\"$slots.label\" class=\"pv-label\" :for=\"id\">\n <slot name=\"label\" />\n </label>\n <textarea\n v-bind=\"$attrs\"\n v-model=\"value\"\n :class=\"[\n 'pv-textarea',\n {\n 'pv-surface-accent': variant === 'grey',\n },\n ]\"\n :rows=\"rows\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :id=\"id\"\n :data-invalid=\"error ? true : undefined\"\n />\n <p\n v-if=\"lowerLabel\"\n style=\"margin-left: 12px\"\n :class=\"[\n 'pv-text-body-xs',\n {\n 'pv-text-red': error,\n 'pv-text-subdued': !error,\n },\n ]\"\n >\n {{ lowerLabel }}\n </p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { PvTextAreaVariant } from \"./types\";\n\ninterface PvTextareaProps {\n /** When true, applies invalid/error styling to the textarea and lower label. */\n error?: boolean;\n /** When true, the textarea is non-interactive and visually dimmed. */\n disabled?: boolean;\n /** Placeholder text shown when the textarea is empty. */\n placeholder?: string;\n /** HTML id for the textarea element. Auto-generated when omitted. */\n id?: string;\n /** Helper or validation text displayed below the textarea. Turns red when error is true. */\n lowerLabel?: string;\n /**\n * Background color variant of the textarea.\n */\n variant?: PvTextAreaVariant;\n /**\n * Number of visible text rows.\n */\n rows?: number;\n}\n\nwithDefaults(defineProps<PvTextareaProps>(), {\n id: Math.random().toString(),\n variant: \"white\",\n rows: 4,\n});\n\nconst value = defineModel<string>();\n</script>\n\n<template>\n <div class=\"pv-flex-vertical\" style=\"--flex-align: flex-start\">\n <label v-if=\"$slots.label\" class=\"pv-label\" :for=\"id\">\n <slot name=\"label\" />\n </label>\n <textarea\n v-bind=\"$attrs\"\n v-model=\"value\"\n :class=\"[\n 'pv-textarea',\n {\n 'pv-surface-accent': variant === 'grey',\n },\n ]\"\n :rows=\"rows\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :id=\"id\"\n :data-invalid=\"error ? true : undefined\"\n />\n <p\n v-if=\"lowerLabel\"\n style=\"margin-left: 12px\"\n :class=\"[\n 'pv-text-body-xs',\n {\n 'pv-text-red': error,\n 'pv-text-subdued': !error,\n },\n ]\"\n >\n {{ lowerLabel }}\n </p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from \"vue\";\n\nimport PvButton from \"@/components/base/PvButton/PvButton.vue\";\nimport PvTextArea from \"@/components/base/PvTextArea/PvTextArea.vue\";\n\nimport type { PvPromptInputProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvPromptInputProps>(), {\n ariaLabel: \"Message\",\n disabled: false,\n maxHeight: \"200px\",\n placeholder: \"Ask anything...\",\n rows: 1,\n status: \"ready\",\n stopLabel: \"Stop generating\",\n submitLabel: \"Send message\",\n});\n\nconst emit = defineEmits<{\n /** A non-empty, trimmed prompt was submitted. */\n (event: \"submit\", value: string): void;\n /** The stop button was pressed while a response was streaming. */\n (event: \"stop\"): void;\n}>();\n\nconst value = defineModel<string>({ default: \"\" });\nconst formRef = ref<HTMLFormElement>();\nconst isBusy = computed(() => props.status === \"submitted\" || props.status === \"streaming\");\nconst canSubmit = computed(() => !props.disabled && !isBusy.value && value.value.trim().length > 0);\n\nfunction submit() {\n if (!canSubmit.value) return;\n emit(\"submit\", value.value.trim());\n}\n\n// The textarea is only one row tall, so most of the box is empty space around it.\n// Clicking that space should put the caret in the textarea, like any other text field.\nfunction handleBoxClick(event: MouseEvent) {\n if (props.disabled) return;\n\n const target = event.target as HTMLElement | null;\n if (target?.closest(\"button, a, input, textarea, select, [tabindex]\")) return;\n\n formRef.value?.querySelector(\"textarea\")?.focus();\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (event.key !== \"Enter\" || event.shiftKey || event.isComposing) return;\n event.preventDefault();\n event.stopPropagation();\n submit();\n}\n</script>\n\n<template>\n <form\n ref=\"formRef\"\n class=\"pv-prompt-input pv-bordered pv-radius pv-inset-square-8 pv-flex-vertical\"\n data-testid=\"pv-prompt-input\"\n :aria-busy=\"isBusy\"\n @submit.prevent=\"submit\"\n @click=\"handleBoxClick\"\n >\n <div v-if=\"$slots.header\" class=\"pv-full-width pv-flow-8\">\n <slot name=\"header\" />\n </div>\n\n <PvTextArea\n v-model=\"value\"\n class=\"pv-prompt-input-textarea\"\n :style=\"{ '--prompt-input-max-height': maxHeight }\"\n :rows=\"rows\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n @keydown=\"handleKeydown\"\n />\n\n <div class=\"pv-prompt-input-footer pv-flex pv-full-width\">\n <div class=\"pv-prompt-input-actions pv-flex pv-flex-item\">\n <slot name=\"actions\" />\n </div>\n <PvButton\n :type=\"isBusy ? 'button' : 'submit'\"\n :variant=\"isBusy ? 'secondary' : 'primary'\"\n size=\"md\"\n :left-icon=\"isBusy ? 'stop' : 'send'\"\n :aria-label=\"isBusy ? stopLabel : submitLabel\"\n :disabled=\"disabled || (!isBusy && !canSubmit)\"\n @click=\"isBusy && emit('stop')\"\n />\n </div>\n\n <p class=\"pv-visually-hidden\" aria-live=\"polite\">\n <template v-if=\"status === 'submitted'\">Message submitted.</template>\n <template v-else-if=\"status === 'streaming'\">Response is streaming.</template>\n <template v-else-if=\"status === 'error'\">The message could not be sent.</template>\n </p>\n </form>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from \"vue\";\n\nimport PvButton from \"@/components/base/PvButton/PvButton.vue\";\nimport PvTextArea from \"@/components/base/PvTextArea/PvTextArea.vue\";\n\nimport type { PvPromptInputProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvPromptInputProps>(), {\n ariaLabel: \"Message\",\n disabled: false,\n maxHeight: \"200px\",\n placeholder: \"Ask anything...\",\n rows: 1,\n status: \"ready\",\n stopLabel: \"Stop generating\",\n submitLabel: \"Send message\",\n});\n\nconst emit = defineEmits<{\n /** A non-empty, trimmed prompt was submitted. */\n (event: \"submit\", value: string): void;\n /** The stop button was pressed while a response was streaming. */\n (event: \"stop\"): void;\n}>();\n\nconst value = defineModel<string>({ default: \"\" });\nconst formRef = ref<HTMLFormElement>();\nconst isBusy = computed(() => props.status === \"submitted\" || props.status === \"streaming\");\nconst canSubmit = computed(() => !props.disabled && !isBusy.value && value.value.trim().length > 0);\n\nfunction submit() {\n if (!canSubmit.value) return;\n emit(\"submit\", value.value.trim());\n}\n\n// The textarea is only one row tall, so most of the box is empty space around it.\n// Clicking that space should put the caret in the textarea, like any other text field.\nfunction handleBoxClick(event: MouseEvent) {\n if (props.disabled) return;\n\n const target = event.target as HTMLElement | null;\n if (target?.closest(\"button, a, input, textarea, select, [tabindex]\")) return;\n\n formRef.value?.querySelector(\"textarea\")?.focus();\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (event.key !== \"Enter\" || event.shiftKey || event.isComposing) return;\n event.preventDefault();\n event.stopPropagation();\n submit();\n}\n</script>\n\n<template>\n <form\n ref=\"formRef\"\n class=\"pv-prompt-input pv-bordered pv-radius pv-inset-square-8 pv-flex-vertical\"\n data-testid=\"pv-prompt-input\"\n :aria-busy=\"isBusy\"\n @submit.prevent=\"submit\"\n @click=\"handleBoxClick\"\n >\n <div v-if=\"$slots.header\" class=\"pv-full-width pv-flow-8\">\n <slot name=\"header\" />\n </div>\n\n <PvTextArea\n v-model=\"value\"\n class=\"pv-prompt-input-textarea\"\n :style=\"{ '--prompt-input-max-height': maxHeight }\"\n :rows=\"rows\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n @keydown=\"handleKeydown\"\n />\n\n <div class=\"pv-prompt-input-footer pv-flex pv-full-width\">\n <div class=\"pv-prompt-input-actions pv-flex pv-flex-item\">\n <slot name=\"actions\" />\n </div>\n <PvButton\n :type=\"isBusy ? 'button' : 'submit'\"\n :variant=\"isBusy ? 'secondary' : 'primary'\"\n size=\"md\"\n :left-icon=\"isBusy ? 'stop' : 'send'\"\n :aria-label=\"isBusy ? stopLabel : submitLabel\"\n :disabled=\"disabled || (!isBusy && !canSubmit)\"\n @click=\"isBusy && emit('stop')\"\n />\n </div>\n\n <p class=\"pv-visually-hidden\" aria-live=\"polite\">\n <template v-if=\"status === 'submitted'\">Message submitted.</template>\n <template v-else-if=\"status === 'streaming'\">Response is streaming.</template>\n <template v-else-if=\"status === 'error'\">The message could not be sent.</template>\n </p>\n </form>\n</template>\n","export const promptInputStatuses = [\"ready\", \"submitted\", \"streaming\", \"error\"] as const;\n\nexport type PvPromptInputStatus = (typeof promptInputStatuses)[number];\n\nexport interface PvPromptInputProps {\n /** Accessible label for the textarea. */\n ariaLabel?: string;\n /** Disables the composer. */\n disabled?: boolean;\n /** Maximum auto-grown textarea height. */\n maxHeight?: string;\n /** Placeholder shown when the prompt is empty. */\n placeholder?: string;\n /** Initial visible textarea rows. */\n rows?: number;\n /** Current chat request status. */\n status?: PvPromptInputStatus;\n /** Accessible label for the stop button. */\n stopLabel?: string;\n /** Accessible label for the submit button. */\n submitLabel?: string;\n}\n","<script setup lang=\"ts\">\nimport PvCounterBadge from \"../PvCounterBadge/PvCounterBadge.vue\";\nimport PvIcon from \"../PvIcon/PvIcon.vue\";\nimport { ChevronPosition } from \"./types\";\nimport { computed } from \"vue\";\nimport type { CSSProperties } from \"vue\";\n\ninterface PvAccordionProps {\n /** Position of the chevron icon relative to the header text */\n chevronPosition?: ChevronPosition;\n /** Orientation of the chevron for the open/closed states (up-down vs right-down) */\n chevronVariant?: \"vertical\" | \"horizontal\";\n /** Whether the accordion is open by default on initial render */\n defaultOpen?: boolean;\n /** Optional counter badge value displayed next to the header text */\n counter?: number;\n /** Enables a custom trigger slot instead of the default header text */\n enableTriggerSlot?: boolean;\n /** Makes the trigger element expand to fill its container width */\n enableTriggerFullWidth?: boolean;\n /** Text displayed as the accordion header */\n header?: string;\n /** Custom CSS styles applied to the summary/trigger element */\n summaryStyles?: CSSProperties;\n /** CSS class bindings applied to the summary/trigger element */\n summaryClasses?: Record<string, boolean>;\n /** Custom CSS styles applied to the content container */\n contentStyles?: CSSProperties;\n /** Additional attrs for a summary element */\n summaryAttrs?: Record<string, string | undefined>;\n /**\n * Only a click on the chevron opens/closes the accordion. Any other mouse click on the summary\n * emits `summary-click` instead; keyboard toggling (Enter/Space on the summary) is unchanged.\n */\n toggleOnChevronOnly?: boolean;\n}\n\nconst props = withDefaults(defineProps<PvAccordionProps>(), {\n chevronVariant: \"vertical\",\n chevronPosition: \"right\",\n enableTriggerSlot: false,\n enableTriggerFullWidth: false,\n toggleOnChevronOnly: false,\n});\n\nconst accordionOpen = defineModel<boolean>();\n\nconst emit = defineEmits<{\n (e: \"summary-mouseenter\"): void;\n (e: \"summary-mouseleave\"): void;\n (e: \"summary-click\", event: MouseEvent): void;\n}>();\n\nif (props.defaultOpen !== undefined) {\n accordionOpen.value = props.defaultOpen;\n}\n\nconst toggleAccordionState = (event: Event) => {\n const detailsElement = event.target as HTMLDetailsElement;\n accordionOpen.value = detailsElement.open;\n};\n\nconst handleSummaryClick = (event: MouseEvent) => {\n // `detail` is 0 for the click a keyboard Enter/Space synthesizes, which must keep toggling.\n if (event.detail === 0) return;\n if ((event.target as Element | null)?.closest(\"[data-pv-accordion-chevron]\")) return;\n // Slot content that already handled the click (and prevented the toggle itself) owns it.\n if (event.defaultPrevented) return;\n event.preventDefault();\n emit(\"summary-click\", event);\n};\n\n// Attached only when the prop is on: any Vue listener on the summary stamps the click it sees, and an\n// ancestor's Vue listener ignores a click stamped no later than its own attach time - which is every\n// click while `Date.now()` is frozen, e.g. under fake timers. Consumers listening on the accordion itself\n// (PvReasoning's `@click`) would otherwise stop hearing trigger clicks.\nconst summaryListeners = computed(() => (props.toggleOnChevronOnly ? { click: handleSummaryClick } : {}));\n\nconst triggerStyles = {\n width: props.enableTriggerFullWidth ? \"100%\" : \"fit-content\",\n};\n\nconst chevronState = computed(() => {\n if (props.chevronVariant === \"horizontal\") {\n if (props.chevronPosition === \"left\") {\n return accordionOpen.value ? \"chevron-down\" : \"chevron-right\";\n } else {\n return accordionOpen.value ? \"chevron-down\" : \"chevron-left\";\n }\n } else {\n return accordionOpen.value ? \"chevron-up\" : \"chevron-down\";\n }\n});\n</script>\n\n<template>\n <details data-testid=\"pv-accordion\" class=\"pv-accordion\" :open=\"accordionOpen\" @toggle=\"toggleAccordionState\">\n <summary\n v-bind=\"summaryAttrs\"\n :class=\"['pv-flex pv-space-between pv-relative', summaryClasses]\"\n :style=\"{ ...triggerStyles, ...summaryStyles }\"\n @mouseenter=\"$emit('summary-mouseenter')\"\n @mouseleave=\"$emit('summary-mouseleave')\"\n v-on=\"summaryListeners\"\n >\n <!-- Content placed before a left chevron, e.g. a tree connector that must sit in the indent column -->\n <slot name=\"leading\" />\n <PvIcon v-if=\"chevronPosition === 'left'\" :name=\"chevronState\" data-pv-accordion-chevron />\n <template v-if=\"enableTriggerSlot\">\n <slot name=\"trigger\" />\n </template>\n <template v-else>\n <span>\n {{ header }}\n </span>\n <PvCounterBadge v-if=\"counter\" :value=\"counter\" variant=\"secondary\" size=\"sm\" />\n </template>\n <PvIcon v-if=\"chevronPosition === 'right'\" :name=\"chevronState\" data-pv-accordion-chevron />\n </summary>\n <div :style=\"contentStyles\">\n <slot />\n </div>\n </details>\n</template>\n\n<style scoped>\nsummary {\n background-image: none;\n padding-right: 2px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport PvCounterBadge from \"../PvCounterBadge/PvCounterBadge.vue\";\nimport PvIcon from \"../PvIcon/PvIcon.vue\";\nimport { ChevronPosition } from \"./types\";\nimport { computed } from \"vue\";\nimport type { CSSProperties } from \"vue\";\n\ninterface PvAccordionProps {\n /** Position of the chevron icon relative to the header text */\n chevronPosition?: ChevronPosition;\n /** Orientation of the chevron for the open/closed states (up-down vs right-down) */\n chevronVariant?: \"vertical\" | \"horizontal\";\n /** Whether the accordion is open by default on initial render */\n defaultOpen?: boolean;\n /** Optional counter badge value displayed next to the header text */\n counter?: number;\n /** Enables a custom trigger slot instead of the default header text */\n enableTriggerSlot?: boolean;\n /** Makes the trigger element expand to fill its container width */\n enableTriggerFullWidth?: boolean;\n /** Text displayed as the accordion header */\n header?: string;\n /** Custom CSS styles applied to the summary/trigger element */\n summaryStyles?: CSSProperties;\n /** CSS class bindings applied to the summary/trigger element */\n summaryClasses?: Record<string, boolean>;\n /** Custom CSS styles applied to the content container */\n contentStyles?: CSSProperties;\n /** Additional attrs for a summary element */\n summaryAttrs?: Record<string, string | undefined>;\n /**\n * Only a click on the chevron opens/closes the accordion. Any other mouse click on the summary\n * emits `summary-click` instead; keyboard toggling (Enter/Space on the summary) is unchanged.\n */\n toggleOnChevronOnly?: boolean;\n}\n\nconst props = withDefaults(defineProps<PvAccordionProps>(), {\n chevronVariant: \"vertical\",\n chevronPosition: \"right\",\n enableTriggerSlot: false,\n enableTriggerFullWidth: false,\n toggleOnChevronOnly: false,\n});\n\nconst accordionOpen = defineModel<boolean>();\n\nconst emit = defineEmits<{\n (e: \"summary-mouseenter\"): void;\n (e: \"summary-mouseleave\"): void;\n (e: \"summary-click\", event: MouseEvent): void;\n}>();\n\nif (props.defaultOpen !== undefined) {\n accordionOpen.value = props.defaultOpen;\n}\n\nconst toggleAccordionState = (event: Event) => {\n const detailsElement = event.target as HTMLDetailsElement;\n accordionOpen.value = detailsElement.open;\n};\n\nconst handleSummaryClick = (event: MouseEvent) => {\n // `detail` is 0 for the click a keyboard Enter/Space synthesizes, which must keep toggling.\n if (event.detail === 0) return;\n if ((event.target as Element | null)?.closest(\"[data-pv-accordion-chevron]\")) return;\n // Slot content that already handled the click (and prevented the toggle itself) owns it.\n if (event.defaultPrevented) return;\n event.preventDefault();\n emit(\"summary-click\", event);\n};\n\n// Attached only when the prop is on: any Vue listener on the summary stamps the click it sees, and an\n// ancestor's Vue listener ignores a click stamped no later than its own attach time - which is every\n// click while `Date.now()` is frozen, e.g. under fake timers. Consumers listening on the accordion itself\n// (PvReasoning's `@click`) would otherwise stop hearing trigger clicks.\nconst summaryListeners = computed(() => (props.toggleOnChevronOnly ? { click: handleSummaryClick } : {}));\n\nconst triggerStyles = {\n width: props.enableTriggerFullWidth ? \"100%\" : \"fit-content\",\n};\n\nconst chevronState = computed(() => {\n if (props.chevronVariant === \"horizontal\") {\n if (props.chevronPosition === \"left\") {\n return accordionOpen.value ? \"chevron-down\" : \"chevron-right\";\n } else {\n return accordionOpen.value ? \"chevron-down\" : \"chevron-left\";\n }\n } else {\n return accordionOpen.value ? \"chevron-up\" : \"chevron-down\";\n }\n});\n</script>\n\n<template>\n <details data-testid=\"pv-accordion\" class=\"pv-accordion\" :open=\"accordionOpen\" @toggle=\"toggleAccordionState\">\n <summary\n v-bind=\"summaryAttrs\"\n :class=\"['pv-flex pv-space-between pv-relative', summaryClasses]\"\n :style=\"{ ...triggerStyles, ...summaryStyles }\"\n @mouseenter=\"$emit('summary-mouseenter')\"\n @mouseleave=\"$emit('summary-mouseleave')\"\n v-on=\"summaryListeners\"\n >\n <!-- Content placed before a left chevron, e.g. a tree connector that must sit in the indent column -->\n <slot name=\"leading\" />\n <PvIcon v-if=\"chevronPosition === 'left'\" :name=\"chevronState\" data-pv-accordion-chevron />\n <template v-if=\"enableTriggerSlot\">\n <slot name=\"trigger\" />\n </template>\n <template v-else>\n <span>\n {{ header }}\n </span>\n <PvCounterBadge v-if=\"counter\" :value=\"counter\" variant=\"secondary\" size=\"sm\" />\n </template>\n <PvIcon v-if=\"chevronPosition === 'right'\" :name=\"chevronState\" data-pv-accordion-chevron />\n </summary>\n <div :style=\"contentStyles\">\n <slot />\n </div>\n </details>\n</template>\n\n<style scoped>\nsummary {\n background-image: none;\n padding-right: 2px;\n}\n</style>\n","import { computed, onBeforeUnmount, onBeforeUpdate, onMounted, ref, unref, useSlots, watch } from \"vue\";\nimport type { Ref } from \"vue\";\nimport { useIsWebComponent } from \"@/web-components/utils\";\n\ntype MaybeRef<T> = T | Ref<T>;\n\nexport type UseSlotPresenceOptions = {\n /**\n * Host element for native slot detection (required for web components).\n * When running as a web component, light DOM children of this element are inspected.\n */\n host?: MaybeRef<HTMLElement | null | undefined>;\n\n /**\n * A slots object (useful for tests or to override `useSlots()`).\n * Only used when running as a Vue component (not a web component).\n */\n vueSlots?: Record<string, unknown> | null | undefined;\n\n /**\n * Watch light DOM changes so presence updates if children are added/removed.\n * Only applies to web component mode. Defaults to true.\n */\n observe?: boolean;\n\n /**\n * Listen to slotchange events on shadow DOM slots.\n * Only applies to web component mode. Defaults to true.\n */\n listenSlotChange?: boolean;\n\n /**\n * If true, whitespace-only text nodes count as content in the default slot.\n * Only applies to web component mode. Defaults to false.\n */\n countWhitespaceTextInDefaultSlot?: boolean;\n};\n\nfunction isMeaningfulTextNode(node: Node, countWhitespace: boolean) {\n if (node.nodeType !== Node.TEXT_NODE) return false;\n const txt = node.textContent ?? \"\";\n return countWhitespace ? txt.length > 0 : txt.trim().length > 0;\n}\n\nfunction hasNativeSlottedContent(host: HTMLElement, name: string, countWhitespaceTextInDefaultSlot: boolean): boolean {\n const slotName = name === \"default\" ? \"\" : name;\n const nodes = Array.from(host.childNodes);\n\n if (slotName === \"\") {\n // Default slot: nodes without a `slot` attribute\n return nodes.some((n) => {\n if (n.nodeType === Node.ELEMENT_NODE) {\n const el = n as Element;\n return !el.hasAttribute(\"slot\");\n }\n return isMeaningfulTextNode(n, countWhitespaceTextInDefaultSlot);\n });\n }\n\n // Named slot: element children with slot=\"name\"\n return nodes.some((n) => {\n if (n.nodeType !== Node.ELEMENT_NODE) return false;\n const el = n as Element;\n return el.getAttribute(\"slot\") === slotName;\n });\n}\n\n/**\n * Vue's light DOM custom elements (`shadowRoot: false`) move every child off the\n * host into an internal map during `connectedCallback`, before the component\n * renders, and splice them back in wherever a `<slot>` outlet appears. The host\n * is therefore empty by the time we can inspect it, and this map is the only\n * surviving record of what was slotted. Vue populates it before `setup()` runs\n * and keeps it as the source `_renderSlots()` reads on every update, so it is a\n * stable answer for a slot outlet that is itself gated on slot presence.\n */\ntype LightDomSlotHost = HTMLElement & { _slots?: Record<string, Node[]> };\n\nfunction getParsedLightDomSlots(host: HTMLElement): Record<string, Node[]> | null {\n if (host.shadowRoot) return null;\n const slots = (host as LightDomSlotHost)._slots;\n if (!slots || typeof slots !== \"object\") return null;\n return slots;\n}\n\nfunction hasParsedSlotContent(\n slots: Record<string, Node[]>,\n name: string,\n countWhitespaceTextInDefaultSlot: boolean,\n): boolean {\n // Vue keys the default slot as \"default\" and named slots by their `slot` attribute.\n const nodes = slots[name === \"\" ? \"default\" : name];\n if (!nodes) return false;\n return nodes.some((n) => {\n if (n.nodeType === Node.ELEMENT_NODE) return true;\n return isMeaningfulTextNode(n, countWhitespaceTextInDefaultSlot);\n });\n}\n\nfunction getShadowSlotEl(host: HTMLElement, name: string): HTMLSlotElement | null {\n const sr = host.shadowRoot;\n if (!sr) return null;\n if (name === \"default\" || name === \"\") {\n return sr.querySelector(\"slot:not([name])\");\n }\n return sr.querySelector(`slot[name=\"${CSS.escape(name)}\"]`);\n}\n\n/**\n * Reports whether a custom element host has content for `name`, across all three\n * ways slotted content can be held: assigned to a shadow `<slot>`, parsed off the\n * host by Vue in light DOM, or still sitting as a light DOM child.\n *\n * Exported so callers that resolve a slot name dynamically (and so cannot hold a\n * `useSlotPresence` instance per slot) resolve it the same way.\n */\nexport function hasHostSlotContent(\n host: HTMLElement,\n name: string,\n { useShadowSlot = true, countWhitespaceTextInDefaultSlot = false } = {},\n): boolean {\n const slotEl = useShadowSlot ? getShadowSlotEl(host, name) : null;\n if (slotEl) {\n return slotEl.assignedNodes({ flatten: true }).some((n) => {\n if (n.nodeType === Node.ELEMENT_NODE) return true;\n return isMeaningfulTextNode(n, countWhitespaceTextInDefaultSlot);\n });\n }\n\n // Light DOM custom element: the host has been drained, so read the slots Vue\n // parsed off it. Note that Vue only parses on first connect, so children added\n // to the host later are never rendered either way.\n const parsedSlots = getParsedLightDomSlots(host);\n if (parsedSlots) return hasParsedSlotContent(parsedSlots, name, countWhitespaceTextInDefaultSlot);\n\n return hasNativeSlottedContent(host, name, countWhitespaceTextInDefaultSlot);\n}\n\n/**\n * Returns a computed boolean indicating if a slot has content.\n *\n * Automatically detects if running as a Vue component or web component:\n * - Vue component: checks Vue's slot system via useSlots()\n * - Web component: inspects light DOM children of the host element\n */\nexport function useSlotPresence(slotName: string, options: UseSlotPresenceOptions = {}) {\n const { host, vueSlots, observe = true, listenSlotChange = true, countWhitespaceTextInDefaultSlot = false } = options;\n\n const isWebComponent = useIsWebComponent();\n const slots = vueSlots ?? useSlots();\n\n // Vue's slots object is updated in place and is not reactive, so a computed\n // would cache the initial slot table forever. Refresh before each component\n // update so conditional named slots can appear and disappear at runtime.\n const vueSlotKey = slotName === \"default\" || slotName === \"\" ? \"default\" : slotName;\n const readVueSlot = () => !!slots && !!(slots as Record<string, unknown>)[vueSlotKey];\n const hasVueSlot = ref(readVueSlot());\n onBeforeUpdate(() => {\n hasVueSlot.value = readVueSlot();\n });\n\n // Native/web-component slot presence\n const hasNativeSlot = ref(false);\n\n let mo: MutationObserver | null = null;\n let shadowSlot: HTMLSlotElement | null = null;\n\n const cleanup = () => {\n if (mo) {\n mo.disconnect();\n mo = null;\n }\n if (shadowSlot) {\n shadowSlot.removeEventListener(\"slotchange\", updateNative);\n shadowSlot = null;\n }\n };\n\n const updateNative = () => {\n if (!isWebComponent.value) {\n hasNativeSlot.value = false;\n return;\n }\n\n const el = unref(host);\n if (!el) {\n hasNativeSlot.value = false;\n return;\n }\n\n hasNativeSlot.value = hasHostSlotContent(el, slotName, {\n // Prefer assignedNodes when we can reach the <slot> element in an open shadowRoot\n useShadowSlot: listenSlotChange,\n countWhitespaceTextInDefaultSlot,\n });\n };\n\n const attach = () => {\n if (!isWebComponent.value) return;\n\n cleanup();\n\n const el = unref(host);\n if (!el) {\n hasNativeSlot.value = false;\n return;\n }\n\n updateNative();\n\n if (listenSlotChange) {\n shadowSlot = getShadowSlotEl(el, slotName);\n if (shadowSlot) shadowSlot.addEventListener(\"slotchange\", updateNative);\n }\n\n if (observe) {\n mo = new MutationObserver(() => updateNative());\n mo.observe(el, {\n childList: true,\n subtree: false,\n attributes: true,\n attributeFilter: [\"slot\"],\n characterData: true,\n });\n }\n };\n\n onMounted(attach);\n onBeforeUnmount(cleanup);\n\n // If host ref changes, reattach\n watch(\n () => unref(host),\n () => {\n if (isWebComponent.value) attach();\n },\n );\n\n // Select presence based on component type\n const present = computed(() => (isWebComponent.value ? hasNativeSlot.value : hasVueSlot.value));\n\n return {\n present,\n hasVueSlot,\n hasNativeSlot,\n refresh: updateNative,\n };\n}\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\n\nimport type { PvStep, PvStepListProps, PvStepStatus } from \"./types\";\n\nconst props = withDefaults(defineProps<PvStepListProps>(), {\n inProgress: false,\n steps: () => [],\n});\n\n// The only step that can still be running is the last one that is actually a\n// step: an aside is prose the model has already written, and the step above it\n// stays the running one while that prose is typed out.\nconst lastStepIndex = computed(() => {\n for (let index = props.steps.length - 1; index >= 0; index -= 1) {\n if (props.steps[index].variant !== \"aside\") return index;\n }\n return -1;\n});\n\nfunction statusOf(step: PvStep, index: number): PvStepStatus {\n if (step.status) return step.status;\n if (step.variant === \"aside\") return \"complete\";\n return props.inProgress && index === lastStepIndex.value ? \"active\" : \"complete\";\n}\n\n// A finished step is history: it stays readable, but the step being worked on is\n// what the reader is looking for, so it is the only one at full weight.\nfunction labelClasses(step: PvStep, index: number): string {\n return statusOf(step, index) === \"active\" ? \"pv-text-medium pv-text-default\" : \"pv-text-quaternary\";\n}\n</script>\n\n<template>\n <TransitionGroup tag=\"ol\" name=\"pv-step\" class=\"pv-step-list\" role=\"list\">\n <li v-for=\"(step, index) in steps\" :key=\"step.id ?? index\" class=\"pv-step\" :data-status=\"statusOf(step, index)\">\n <span v-if=\"step.variant !== 'aside'\" class=\"pv-step-marker\" aria-hidden=\"true\">\n <PvIcon v-if=\"step.icon\" :name=\"step.icon\" :size=\"12\" />\n <span v-else class=\"pv-step-dot\"></span>\n </span>\n\n <div class=\"pv-step-body pv-flow-8\">\n <p v-if=\"step.variant === 'aside'\" class=\"pv-step-aside pv-text-body-md pv-text-tertiary\">\n <em>{{ step.label }}</em>\n </p>\n\n <template v-else>\n <!-- A flex row so the shimmer, which paints across its whole box, covers\n the label rather than the empty width beside it. -->\n <p class=\"pv-flex pv-text-body-md\" :class=\"labelClasses(step, index)\" style=\"--flex-wrap: wrap\">\n <span :class=\"{ 'pv-shimmer': statusOf(step, index) === 'active' }\">{{ step.label }}</span>\n <span v-if=\"step.artifact\" class=\"pv-tag-highlight\">{{ step.artifact }}</span>\n </p>\n <p v-if=\"step.note\" class=\"pv-text-body-md pv-text-tertiary\">{{ step.note }}</p>\n <ul\n v-if=\"step.tags && step.tags.length > 0\"\n class=\"pv-flex\"\n role=\"list\"\n style=\"--flex-wrap: wrap; --flex-gap: 4px\"\n >\n <li\n v-for=\"(tag, tagIndex) in step.tags\"\n :key=\"tagIndex\"\n class=\"pv-tag-secondary\"\n :class=\"{ 'pv-text-quaternary': tag.variant === 'muted' }\"\n data-style=\"rounded\"\n >\n {{ tag.label }}\n </li>\n </ul>\n <p v-if=\"step.outcome\" class=\"pv-text-body-md pv-text-medium pv-text-brand\">\n &#8594;&#160;{{ step.outcome }}\n </p>\n </template>\n </div>\n </li>\n </TransitionGroup>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\n\nimport type { PvStep, PvStepListProps, PvStepStatus } from \"./types\";\n\nconst props = withDefaults(defineProps<PvStepListProps>(), {\n inProgress: false,\n steps: () => [],\n});\n\n// The only step that can still be running is the last one that is actually a\n// step: an aside is prose the model has already written, and the step above it\n// stays the running one while that prose is typed out.\nconst lastStepIndex = computed(() => {\n for (let index = props.steps.length - 1; index >= 0; index -= 1) {\n if (props.steps[index].variant !== \"aside\") return index;\n }\n return -1;\n});\n\nfunction statusOf(step: PvStep, index: number): PvStepStatus {\n if (step.status) return step.status;\n if (step.variant === \"aside\") return \"complete\";\n return props.inProgress && index === lastStepIndex.value ? \"active\" : \"complete\";\n}\n\n// A finished step is history: it stays readable, but the step being worked on is\n// what the reader is looking for, so it is the only one at full weight.\nfunction labelClasses(step: PvStep, index: number): string {\n return statusOf(step, index) === \"active\" ? \"pv-text-medium pv-text-default\" : \"pv-text-quaternary\";\n}\n</script>\n\n<template>\n <TransitionGroup tag=\"ol\" name=\"pv-step\" class=\"pv-step-list\" role=\"list\">\n <li v-for=\"(step, index) in steps\" :key=\"step.id ?? index\" class=\"pv-step\" :data-status=\"statusOf(step, index)\">\n <span v-if=\"step.variant !== 'aside'\" class=\"pv-step-marker\" aria-hidden=\"true\">\n <PvIcon v-if=\"step.icon\" :name=\"step.icon\" :size=\"12\" />\n <span v-else class=\"pv-step-dot\"></span>\n </span>\n\n <div class=\"pv-step-body pv-flow-8\">\n <p v-if=\"step.variant === 'aside'\" class=\"pv-step-aside pv-text-body-md pv-text-tertiary\">\n <em>{{ step.label }}</em>\n </p>\n\n <template v-else>\n <!-- A flex row so the shimmer, which paints across its whole box, covers\n the label rather than the empty width beside it. -->\n <p class=\"pv-flex pv-text-body-md\" :class=\"labelClasses(step, index)\" style=\"--flex-wrap: wrap\">\n <span :class=\"{ 'pv-shimmer': statusOf(step, index) === 'active' }\">{{ step.label }}</span>\n <span v-if=\"step.artifact\" class=\"pv-tag-highlight\">{{ step.artifact }}</span>\n </p>\n <p v-if=\"step.note\" class=\"pv-text-body-md pv-text-tertiary\">{{ step.note }}</p>\n <ul\n v-if=\"step.tags && step.tags.length > 0\"\n class=\"pv-flex\"\n role=\"list\"\n style=\"--flex-wrap: wrap; --flex-gap: 4px\"\n >\n <li\n v-for=\"(tag, tagIndex) in step.tags\"\n :key=\"tagIndex\"\n class=\"pv-tag-secondary\"\n :class=\"{ 'pv-text-quaternary': tag.variant === 'muted' }\"\n data-style=\"rounded\"\n >\n {{ tag.label }}\n </li>\n </ul>\n <p v-if=\"step.outcome\" class=\"pv-text-body-md pv-text-medium pv-text-brand\">\n &#8594;&#160;{{ step.outcome }}\n </p>\n </template>\n </div>\n </li>\n </TransitionGroup>\n</template>\n","<script setup lang=\"ts\">\nimport { Comment, Fragment, Text, computed, onBeforeUnmount, ref, useSlots, useTemplateRef, watch } from \"vue\";\nimport type { VNode } from \"vue\";\n\nimport PvAccordion from \"@/components/base/PvAccordion/PvAccordion.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\nimport { useSlotPresence } from \"@/composables/useSlotPresence\";\nimport PvStepList from \"../PvStepList/PvStepList.vue\";\n\nimport type { PvReasoningProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvReasoningProps>(), {\n autoCloseDelay: 1000,\n completedLabel: \"Reasoning\",\n defaultOpen: false,\n isStreaming: false,\n steps: () => [],\n streamingLabel: \"Thinking...\",\n});\n\nconst open = defineModel<boolean>(\"open\", { default: false });\nconst internalDuration = ref<number>();\nconst startedAt = ref<number>();\nconst disclosureChosen = ref(false);\nlet closeTimer: ReturnType<typeof setTimeout> | undefined;\n\nif (props.defaultOpen || props.isStreaming) open.value = true;\nif (props.isStreaming) startedAt.value = Date.now();\n\nconst displayedDuration = computed(() => props.duration ?? internalDuration.value);\nconst triggerLabel = computed(() => {\n if (props.isStreaming) return props.streamingLabel;\n if (displayedDuration.value === undefined) return props.completedLabel;\n const roundedDuration = Math.max(1, Math.round(displayedDuration.value));\n return `Thought for ${roundedDuration} ${roundedDuration === 1 ? \"second\" : \"seconds\"}`;\n});\n\nconst slots = useSlots();\nconst triggerRef = useTemplateRef<HTMLElement | null>(\"triggerRef\");\n// As a web component the prose arrives as light DOM rather than through Vue's\n// slot table, so it has to be resolved off the host element instead.\nconst slotHost = computed(() => {\n const rootNode = triggerRef.value?.getRootNode();\n return rootNode instanceof ShadowRoot ? (rootNode.host as HTMLElement) : null;\n});\nconst { hasNativeSlot: hasNativeProse } = useSlotPresence(\"default\", { host: slotHost });\n\n// A slot function exists even when everything inside it is switched off by the\n// consumer's `v-if`, which leaves nothing but comment nodes behind, so what the\n// slot renders has to be inspected rather than whether it was passed.\nfunction rendersContent(vnodes: VNode[]): boolean {\n return vnodes.some((vnode) => {\n if (vnode.type === Comment) return false;\n if (vnode.type === Text) return String(vnode.children ?? \"\").trim().length > 0;\n if (vnode.type === Fragment) return Array.isArray(vnode.children) && rendersContent(vnode.children as VNode[]);\n return true;\n });\n}\n\n// Reasoning streams before it has anything to say: the first steps and the first\n// line of prose land after the label does. A disclosure whose panel is empty\n// offers a chevron that reveals nothing, so until then the label stands alone.\n// Called from the template rather than cached in a computed because the slot\n// belongs to the parent and has to be re-read on each render of this component.\nfunction hasDisclosableContent() {\n if (props.steps.length > 0 || hasNativeProse.value) return true;\n return rendersContent(slots.default?.({}) ?? []);\n}\n\nfunction clearCloseTimer() {\n if (closeTimer !== undefined) {\n clearTimeout(closeTimer);\n closeTimer = undefined;\n }\n}\n\n// Only a click on the trigger is a disclosure choice, and only a disclosure\n// choice suppresses the auto-close. Reading the content - selecting text,\n// following a link - is not a decision about whether the panel should stay open,\n// so it must not permanently disable auto-close for the rest of the turn.\nfunction handleClick(event: MouseEvent) {\n const target = event.target as Element | null;\n const summary = target?.closest?.(\"summary\");\n if (!summary || summary.parentElement !== event.currentTarget) return;\n disclosureChosen.value = true;\n clearCloseTimer();\n}\n\nwatch(\n () => props.isStreaming,\n (isStreaming, wasStreaming) => {\n clearCloseTimer();\n\n if (isStreaming) {\n startedAt.value = Date.now();\n internalDuration.value = undefined;\n disclosureChosen.value = false;\n open.value = true;\n return;\n }\n\n if (!wasStreaming) return;\n if (startedAt.value !== undefined) {\n internalDuration.value = (Date.now() - startedAt.value) / 1000;\n }\n if (!disclosureChosen.value) {\n closeTimer = setTimeout(() => {\n if (!disclosureChosen.value) open.value = false;\n }, props.autoCloseDelay);\n }\n },\n);\n\nonBeforeUnmount(clearCloseTimer);\n</script>\n\n<template>\n <PvAccordion\n v-if=\"hasDisclosableContent()\"\n v-model=\"open\"\n :default-open=\"open\"\n class=\"pv-reasoning\"\n chevron-position=\"left\"\n chevron-variant=\"horizontal\"\n enable-trigger-full-width\n enable-trigger-slot\n :summary-classes=\"{ 'pv-text-tertiary': true }\"\n :summary-styles=\"{ justifyContent: 'flex-start' }\"\n @click=\"handleClick\"\n >\n <template #trigger>\n <!-- Icons take their colour from the summary rather than from here, so that\n the chevron - a sibling of this span, filled with `currentcolor` - matches\n the brain inside it. The label is a step darker than either. -->\n <span ref=\"triggerRef\" class=\"pv-reasoning-trigger pv-flex pv-text-body-md pv-text-medium\" aria-live=\"polite\">\n <slot name=\"trigger\">\n <PvIcon name=\"brain\" />\n <!-- The label carries the shimmer, so it sweeps the words rather than the\n full width of the trigger. -->\n <span class=\"pv-text-subdued\" :class=\"{ 'pv-shimmer': isStreaming }\">{{ triggerLabel }}</span>\n </slot>\n </span>\n </template>\n\n <div class=\"pv-reasoning-content pv-text-body-md pv-text-tertiary\">\n <PvStepList v-if=\"steps.length > 0\" :steps=\"steps\" :in-progress=\"isStreaming\" />\n <slot />\n </div>\n </PvAccordion>\n\n <span\n v-else\n ref=\"triggerRef\"\n class=\"pv-reasoning-trigger pv-reasoning-standalone pv-flex pv-text-body-md pv-text-medium pv-text-tertiary\"\n aria-live=\"polite\"\n >\n <slot name=\"trigger\">\n <PvIcon name=\"brain\" />\n <span class=\"pv-text-subdued\" :class=\"{ 'pv-shimmer': isStreaming }\">{{ triggerLabel }}</span>\n </slot>\n </span>\n</template>\n","<script setup lang=\"ts\">\nimport { Comment, Fragment, Text, computed, onBeforeUnmount, ref, useSlots, useTemplateRef, watch } from \"vue\";\nimport type { VNode } from \"vue\";\n\nimport PvAccordion from \"@/components/base/PvAccordion/PvAccordion.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\nimport { useSlotPresence } from \"@/composables/useSlotPresence\";\nimport PvStepList from \"../PvStepList/PvStepList.vue\";\n\nimport type { PvReasoningProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvReasoningProps>(), {\n autoCloseDelay: 1000,\n completedLabel: \"Reasoning\",\n defaultOpen: false,\n isStreaming: false,\n steps: () => [],\n streamingLabel: \"Thinking...\",\n});\n\nconst open = defineModel<boolean>(\"open\", { default: false });\nconst internalDuration = ref<number>();\nconst startedAt = ref<number>();\nconst disclosureChosen = ref(false);\nlet closeTimer: ReturnType<typeof setTimeout> | undefined;\n\nif (props.defaultOpen || props.isStreaming) open.value = true;\nif (props.isStreaming) startedAt.value = Date.now();\n\nconst displayedDuration = computed(() => props.duration ?? internalDuration.value);\nconst triggerLabel = computed(() => {\n if (props.isStreaming) return props.streamingLabel;\n if (displayedDuration.value === undefined) return props.completedLabel;\n const roundedDuration = Math.max(1, Math.round(displayedDuration.value));\n return `Thought for ${roundedDuration} ${roundedDuration === 1 ? \"second\" : \"seconds\"}`;\n});\n\nconst slots = useSlots();\nconst triggerRef = useTemplateRef<HTMLElement | null>(\"triggerRef\");\n// As a web component the prose arrives as light DOM rather than through Vue's\n// slot table, so it has to be resolved off the host element instead.\nconst slotHost = computed(() => {\n const rootNode = triggerRef.value?.getRootNode();\n return rootNode instanceof ShadowRoot ? (rootNode.host as HTMLElement) : null;\n});\nconst { hasNativeSlot: hasNativeProse } = useSlotPresence(\"default\", { host: slotHost });\n\n// A slot function exists even when everything inside it is switched off by the\n// consumer's `v-if`, which leaves nothing but comment nodes behind, so what the\n// slot renders has to be inspected rather than whether it was passed.\nfunction rendersContent(vnodes: VNode[]): boolean {\n return vnodes.some((vnode) => {\n if (vnode.type === Comment) return false;\n if (vnode.type === Text) return String(vnode.children ?? \"\").trim().length > 0;\n if (vnode.type === Fragment) return Array.isArray(vnode.children) && rendersContent(vnode.children as VNode[]);\n return true;\n });\n}\n\n// Reasoning streams before it has anything to say: the first steps and the first\n// line of prose land after the label does. A disclosure whose panel is empty\n// offers a chevron that reveals nothing, so until then the label stands alone.\n// Called from the template rather than cached in a computed because the slot\n// belongs to the parent and has to be re-read on each render of this component.\nfunction hasDisclosableContent() {\n if (props.steps.length > 0 || hasNativeProse.value) return true;\n return rendersContent(slots.default?.({}) ?? []);\n}\n\nfunction clearCloseTimer() {\n if (closeTimer !== undefined) {\n clearTimeout(closeTimer);\n closeTimer = undefined;\n }\n}\n\n// Only a click on the trigger is a disclosure choice, and only a disclosure\n// choice suppresses the auto-close. Reading the content - selecting text,\n// following a link - is not a decision about whether the panel should stay open,\n// so it must not permanently disable auto-close for the rest of the turn.\nfunction handleClick(event: MouseEvent) {\n const target = event.target as Element | null;\n const summary = target?.closest?.(\"summary\");\n if (!summary || summary.parentElement !== event.currentTarget) return;\n disclosureChosen.value = true;\n clearCloseTimer();\n}\n\nwatch(\n () => props.isStreaming,\n (isStreaming, wasStreaming) => {\n clearCloseTimer();\n\n if (isStreaming) {\n startedAt.value = Date.now();\n internalDuration.value = undefined;\n disclosureChosen.value = false;\n open.value = true;\n return;\n }\n\n if (!wasStreaming) return;\n if (startedAt.value !== undefined) {\n internalDuration.value = (Date.now() - startedAt.value) / 1000;\n }\n if (!disclosureChosen.value) {\n closeTimer = setTimeout(() => {\n if (!disclosureChosen.value) open.value = false;\n }, props.autoCloseDelay);\n }\n },\n);\n\nonBeforeUnmount(clearCloseTimer);\n</script>\n\n<template>\n <PvAccordion\n v-if=\"hasDisclosableContent()\"\n v-model=\"open\"\n :default-open=\"open\"\n class=\"pv-reasoning\"\n chevron-position=\"left\"\n chevron-variant=\"horizontal\"\n enable-trigger-full-width\n enable-trigger-slot\n :summary-classes=\"{ 'pv-text-tertiary': true }\"\n :summary-styles=\"{ justifyContent: 'flex-start' }\"\n @click=\"handleClick\"\n >\n <template #trigger>\n <!-- Icons take their colour from the summary rather than from here, so that\n the chevron - a sibling of this span, filled with `currentcolor` - matches\n the brain inside it. The label is a step darker than either. -->\n <span ref=\"triggerRef\" class=\"pv-reasoning-trigger pv-flex pv-text-body-md pv-text-medium\" aria-live=\"polite\">\n <slot name=\"trigger\">\n <PvIcon name=\"brain\" />\n <!-- The label carries the shimmer, so it sweeps the words rather than the\n full width of the trigger. -->\n <span class=\"pv-text-subdued\" :class=\"{ 'pv-shimmer': isStreaming }\">{{ triggerLabel }}</span>\n </slot>\n </span>\n </template>\n\n <div class=\"pv-reasoning-content pv-text-body-md pv-text-tertiary\">\n <PvStepList v-if=\"steps.length > 0\" :steps=\"steps\" :in-progress=\"isStreaming\" />\n <slot />\n </div>\n </PvAccordion>\n\n <span\n v-else\n ref=\"triggerRef\"\n class=\"pv-reasoning-trigger pv-reasoning-standalone pv-flex pv-text-body-md pv-text-medium pv-text-tertiary\"\n aria-live=\"polite\"\n >\n <slot name=\"trigger\">\n <PvIcon name=\"brain\" />\n <span class=\"pv-text-subdued\" :class=\"{ 'pv-shimmer': isStreaming }\">{{ triggerLabel }}</span>\n </slot>\n </span>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, watch } from \"vue\";\n\nimport PvAccordion from \"@/components/base/PvAccordion/PvAccordion.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\n\nimport type { ToolLifecycleState } from \"../toolLifecycle\";\nimport type { PvToolProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvToolProps>(), {\n defaultOpen: false,\n});\n\nconst open = defineModel<boolean>(\"open\", { default: false });\nconst completedStates: ToolLifecycleState[] = [\"output-available\", \"output-error\"];\n\nif (props.defaultOpen || completedStates.includes(props.state)) open.value = true;\n\nconst status = computed(() => {\n const statuses: Record<ToolLifecycleState, { label: string; className: string }> = {\n \"input-streaming\": { label: \"Pending\", className: \"pv-tag-tertiary\" },\n \"input-available\": { label: \"Running\", className: \"pv-tag-primary\" },\n \"approval-requested\": { label: \"Awaiting approval\", className: \"pv-tag-yellow\" },\n \"approval-responded\": { label: \"Responded\", className: \"pv-tag-tertiary\" },\n \"output-available\": { label: \"Completed\", className: \"pv-tag-success\" },\n \"output-error\": { label: \"Error\", className: \"pv-tag-error\" },\n \"output-denied\": { label: \"Denied\", className: \"pv-tag-error\" },\n };\n return statuses[props.state];\n});\n\nfunction formatValue(value: unknown) {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value, null, 2) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\nwatch(\n () => props.state,\n (state, previousState) => {\n if (state !== previousState && completedStates.includes(state)) open.value = true;\n },\n);\n</script>\n\n<template>\n <PvAccordion\n v-model=\"open\"\n :default-open=\"open\"\n class=\"pv-tool pv-bordered pv-radius\"\n chevron-position=\"right\"\n chevron-variant=\"horizontal\"\n enable-trigger-full-width\n enable-trigger-slot\n :summary-classes=\"{ 'pv-inset-square-12': true }\"\n >\n <template #trigger>\n <span class=\"pv-tool-heading pv-flex pv-flex-item\">\n <PvIcon name=\"settings\" :size=\"20\" />\n <span class=\"pv-text-title-sm pv-truncate\">{{ toolName }}</span>\n <span :class=\"[status.className, 'pv-tag-sm']\" role=\"status\">{{ status.label }}</span>\n </span>\n </template>\n\n <div class=\"pv-tool-disclosure-content\">\n <div class=\"pv-flow-16 pv-border-top pv-tool-content\">\n <section v-if=\"$slots.input || input !== undefined\" class=\"pv-flow-8\">\n <h3 class=\"pv-text-title-xs\">Parameters</h3>\n <slot name=\"input\" :input=\"input\">\n <pre\n class=\"pv-surface-accent pv-inset-square-12 pv-radius-sm pv-text-body-xs\"\n ><code>{{ formatValue(input) }}</code></pre>\n </slot>\n </section>\n\n <section v-if=\"errorText\" class=\"pv-flow-8\" role=\"alert\" aria-label=\"Tool error\">\n <h3 class=\"pv-text-title-xs pv-text-critical\">Error</h3>\n <pre class=\"pv-surface-critical pv-inset-square-12 pv-radius-sm pv-text-body-xs pv-text-critical\">{{\n errorText\n }}</pre>\n </section>\n\n <section v-else-if=\"$slots.output || output !== undefined\" class=\"pv-flow-8\">\n <h3 class=\"pv-text-title-xs\">Result</h3>\n <slot name=\"output\" :output=\"output\">\n <pre\n class=\"pv-surface-accent pv-inset-square-12 pv-radius-sm pv-text-body-xs\"\n ><code>{{ formatValue(output) }}</code></pre>\n </slot>\n </section>\n </div>\n </div>\n </PvAccordion>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, watch } from \"vue\";\n\nimport PvAccordion from \"@/components/base/PvAccordion/PvAccordion.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\n\nimport type { ToolLifecycleState } from \"../toolLifecycle\";\nimport type { PvToolProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvToolProps>(), {\n defaultOpen: false,\n});\n\nconst open = defineModel<boolean>(\"open\", { default: false });\nconst completedStates: ToolLifecycleState[] = [\"output-available\", \"output-error\"];\n\nif (props.defaultOpen || completedStates.includes(props.state)) open.value = true;\n\nconst status = computed(() => {\n const statuses: Record<ToolLifecycleState, { label: string; className: string }> = {\n \"input-streaming\": { label: \"Pending\", className: \"pv-tag-tertiary\" },\n \"input-available\": { label: \"Running\", className: \"pv-tag-primary\" },\n \"approval-requested\": { label: \"Awaiting approval\", className: \"pv-tag-yellow\" },\n \"approval-responded\": { label: \"Responded\", className: \"pv-tag-tertiary\" },\n \"output-available\": { label: \"Completed\", className: \"pv-tag-success\" },\n \"output-error\": { label: \"Error\", className: \"pv-tag-error\" },\n \"output-denied\": { label: \"Denied\", className: \"pv-tag-error\" },\n };\n return statuses[props.state];\n});\n\nfunction formatValue(value: unknown) {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value, null, 2) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\nwatch(\n () => props.state,\n (state, previousState) => {\n if (state !== previousState && completedStates.includes(state)) open.value = true;\n },\n);\n</script>\n\n<template>\n <PvAccordion\n v-model=\"open\"\n :default-open=\"open\"\n class=\"pv-tool pv-bordered pv-radius\"\n chevron-position=\"right\"\n chevron-variant=\"horizontal\"\n enable-trigger-full-width\n enable-trigger-slot\n :summary-classes=\"{ 'pv-inset-square-12': true }\"\n >\n <template #trigger>\n <span class=\"pv-tool-heading pv-flex pv-flex-item\">\n <PvIcon name=\"settings\" :size=\"20\" />\n <span class=\"pv-text-title-sm pv-truncate\">{{ toolName }}</span>\n <span :class=\"[status.className, 'pv-tag-sm']\" role=\"status\">{{ status.label }}</span>\n </span>\n </template>\n\n <div class=\"pv-tool-disclosure-content\">\n <div class=\"pv-flow-16 pv-border-top pv-tool-content\">\n <section v-if=\"$slots.input || input !== undefined\" class=\"pv-flow-8\">\n <h3 class=\"pv-text-title-xs\">Parameters</h3>\n <slot name=\"input\" :input=\"input\">\n <pre\n class=\"pv-surface-accent pv-inset-square-12 pv-radius-sm pv-text-body-xs\"\n ><code>{{ formatValue(input) }}</code></pre>\n </slot>\n </section>\n\n <section v-if=\"errorText\" class=\"pv-flow-8\" role=\"alert\" aria-label=\"Tool error\">\n <h3 class=\"pv-text-title-xs pv-text-critical\">Error</h3>\n <pre class=\"pv-surface-critical pv-inset-square-12 pv-radius-sm pv-text-body-xs pv-text-critical\">{{\n errorText\n }}</pre>\n </section>\n\n <section v-else-if=\"$slots.output || output !== undefined\" class=\"pv-flow-8\">\n <h3 class=\"pv-text-title-xs\">Result</h3>\n <slot name=\"output\" :output=\"output\">\n <pre\n class=\"pv-surface-accent pv-inset-square-12 pv-radius-sm pv-text-body-xs\"\n ><code>{{ formatValue(output) }}</code></pre>\n </slot>\n </section>\n </div>\n </div>\n </PvAccordion>\n</template>\n","export const toolLifecycleStates = [\n \"input-streaming\",\n \"input-available\",\n \"approval-requested\",\n \"approval-responded\",\n \"output-available\",\n \"output-error\",\n \"output-denied\",\n] as const;\n\n/**\n * Tool lifecycle states. The `input-*` and `output-available`/`output-error`\n * values mirror the Vercel AI SDK tool-part state union; `approval-requested`,\n * `approval-responded`, and `output-denied` are Pit Viper additions for the\n * human-in-the-loop approval flow.\n */\nexport type ToolLifecycleState = (typeof toolLifecycleStates)[number];\n\n/** Any JSON-serializable value, matching the shape of AI SDK tool input/output. */\nexport type JSONValue = string | number | boolean | null | { [key: string]: JSONValue } | JSONValue[];\n\n/** Approval metadata for the tool confirmation flow. */\nexport interface ToolApproval {\n /** Identifier of the approval request, echoed back in the response. */\n id: string;\n /** Whether the tool execution was approved; undefined until answered. */\n approved?: boolean;\n /** Optional reason supplied with the decision, primarily for rejections. */\n reason?: string;\n}\n","import { onScopeDispose, ref, watch, type Ref } from \"vue\";\n\nexport interface UseStickToBottomOptions {\n /** Distance in px from the bottom that still counts as \"at the bottom\". Defaults to 32. */\n threshold?: number;\n}\n\nexport interface UseStickToBottom {\n /** Whether the container is scrolled to within `threshold` of the bottom. */\n isAtBottom: Ref<boolean>;\n /** Scroll the container to the bottom and mark it as pinned. */\n scrollToBottom: (behavior?: ScrollBehavior) => void;\n}\n\n/**\n * Chat-style auto-scroll for a scrollable element. Tracks whether the reader is\n * at the bottom and keeps new content pinned to the bottom - but only while the\n * reader is already there, so scrolling up to read history is never interrupted.\n *\n * @param container - Ref to the scrollable element (may be null before mount).\n */\nexport function useStickToBottom(\n container: Ref<HTMLElement | null | undefined>,\n options: UseStickToBottomOptions = {},\n): UseStickToBottom {\n const threshold = options.threshold ?? 32;\n const isAtBottom = ref(true);\n\n function update() {\n const el = container.value;\n if (el) isAtBottom.value = el.scrollHeight - el.scrollTop - el.clientHeight <= threshold;\n }\n\n function scrollToBottom(behavior: ScrollBehavior = \"smooth\") {\n const el = container.value;\n if (!el) return;\n el.scrollTo({ top: el.scrollHeight, behavior });\n isAtBottom.value = true;\n }\n\n let detach: (() => void) | undefined;\n\n function attach(el: HTMLElement) {\n const onScroll = () => update();\n el.addEventListener(\"scroll\", onScroll, { passive: true });\n\n // Keep the view pinned as content grows, but only when already at the\n // bottom - otherwise leave the reader where they scrolled to.\n const stickIfPinned = () => {\n if (isAtBottom.value) scrollToBottom(\"auto\");\n };\n const observers: Array<{ disconnect: () => void }> = [];\n\n // Observe the container (viewport size) and its content children (so pure\n // reflow - fonts loading, a nested accordion expanding - re-sticks too, not\n // just add/remove of children).\n let resizeObserver: ResizeObserver | undefined;\n const observed = new WeakSet<Element>();\n const observeNewChildren = () => {\n if (!resizeObserver) return;\n for (const child of Array.from(el.children)) {\n if (!observed.has(child)) {\n observed.add(child);\n resizeObserver.observe(child);\n }\n }\n };\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver = new ResizeObserver(stickIfPinned);\n resizeObserver.observe(el);\n observeNewChildren();\n observers.push(resizeObserver);\n }\n if (typeof MutationObserver !== \"undefined\") {\n // childList only: character-level streaming reflow is already caught by\n // the child ResizeObservers, so there's no need to fire per keystroke.\n const contentObserver = new MutationObserver(() => {\n observeNewChildren();\n stickIfPinned();\n });\n contentObserver.observe(el, { childList: true });\n observers.push(contentObserver);\n }\n\n update();\n\n detach = () => {\n el.removeEventListener(\"scroll\", onScroll);\n observers.forEach((observer) => observer.disconnect());\n };\n }\n\n watch(\n container,\n (el) => {\n detach?.();\n detach = undefined;\n if (el) attach(el);\n },\n { immediate: true },\n );\n\n onScopeDispose(() => detach?.());\n\n return { isAtBottom, scrollToBottom };\n}\n"],"mappings":";;;;;;;;;;EAUA,IAAM,IAA6C;GACjD,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EAEK,IAAwD;GAC5D,SAAS;GACT,MAAM;GACN,OAAO;GACR;yBASC,EAA+G,OAAA;GAA1G,eAAY;GAAc,OAAK,EAAE,EAAoB,EAAA,SAAO;GAAI,OAAK,EAAA,EAAA,UAAc,EAAY,EAAA,OAAI,CAAA;;;IE7B7F,IAAiC,CAAC,QAAQ,EAEjD,IAA2D;CAC/D,IAAI;CACJ,IAAI,KAAA;CACJ,IAAI;CACL,EAEY,MAAuB,MAC9B,KAAQ,QAAQ,CAAC,EAAe,eAAe,EAAK,GAC/C,OAEF,EAAe,MAAS;;;ACQjC,SAAgB,IAAoB;CAClC,IAAM,IAAO,EAAI,GAAM,EAEjB,IADW,GAAoB,EACd,QAAQ,EAAE;AAIjC,QAHI,UAAU,KAAQ,EAAK,SAAS,OAClC,EAAK,QAAQ,KAER;;;;ACCT,IAAa,KAAc;CAAC,KAAA;CAAW;CAAI;CAAI;CAAI;CAAI;CAAI;CAAG;;;;;;;EChB9D,IAAM,IAAQ,GACR,IAAQ,GAAmB,EAE3B,IAAuC,EAAI,KAAK,EAEhD,IAAU,SAAgB;GAC9B,WAAW;IACV,WAAW,EAAM,SAAS,EAAM,QAAQ,QAAQ,GAAY,SAAS,EAAM,KAAK;GAClF,EAAE,EAEG,IAAU,QAEV,EAAM,SAAS,EAAiB,QAC3B,GAAG,EAAiB,MAAM,GAAG,EAAM,SAErC,IAAI,EAAM,OACjB;SAGE,WAAW,8BAGb,EAAiB,QAAQ,WAAW,4CAKpC,EAEM,OAAA;GAFD,eAAY;GAAU,eAAY;GAAQ,OAAK,EAAE,EAAA,MAAO;MAC3D,EAAiC,OAAA,EAA3B,cAAY,EAAA,OAAO,EAAA,MAAA,GAAA,EAAA,CAAA,EAAA,EAAA;;;;;;;;;;;;;EEvB7B,IAAM,IAAQ,GAOR,IAAe,QACf,EAAM,YAAY,EAAM,SAAS,EAAM,QAAQ,EAAM,WAChD,GAAG,EAAM,SAAS,KAEvB,EAAM,SAAS,OAGZ,MAFE,GAAG,EAAM,SAAS,EAAM,QAGjC,EAEI,IAAU,SAAgB;GAC9B,4DAA4D;GAC5D,+BAA+B,EAAM,QAAQ;GAC7C,+BAA+B,EAAM,QAAQ;GAC7C,4CAA4C,EAAM,WAAW;GAC7D,sBAAsB,EAAM,WAAW;GACvC,wBAAwB,EAAM,WAAW;GACzC,mBAAmB,EAAM,WAAW,WAAW,EAAM,WAAW;GAChE,cAAc,EAAM,WAAW;GAC/B,cAAc,EAAM;GACrB,EAAE;yBAGD,EAEM,OAAA;GAFA,OAAK,EAAE,EAAA,MAAO;GAAE,eAAY;OAC7B,EAAA,MAAY,EAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1CnB,IAAM,IAAQ,GAQR,IAAoB,QAAgB,CAAC,EAAM,SAAS,EAAM,UAAU,EAAM,SAAS,EAAM,YAAY,KAAA,EAAW,EAEhH,IAAU,QAAe;GAC7B,IAAM,IAAS,EAAE;AACjB,GAAI,EAAM,WAAW,EAA+B,SAAS,EAAM,QAAQ,GACzE,EAAO,KAAK,aAAa,EAAM,QAAQ,UAAU,GAEjD,EAAO,KAAK,aAAa,EAAM,UAAU;GAE3C,IAAM,IAAY,GAAoB,EAAM,KAAK;AAIjD,UAHI,KACF,EAAO,KAAK,EAAU,EAEjB;IACP;yBAIA,EASS,UAAA;GATD,MAAK;GAAU,OAAK,EAAE,EAAA,MAAO;GAAG,UAAU,EAAA;GAAW,cAAY,EAAA;GAAmB,eAAY;MACrF,EAAA,WAAA,GAAA,EAAjB,EAAsC,GAAA;;GAAZ,MAAK;cAC/B,EAMW,GAAA,EAAA,KAAA,GAAA,EAAA;GALa,EAAA,oBAAA,GAAA,EAAtB,EAAuF,GAAA;;IAA9C,OAAO,EAAA;IAAkB,SAAQ;;GAC5D,EAAA,YAAA,GAAA,EAAd,EAA6E,GAAA;;IAApD,MAAM,EAAA;IAAU,eAAY;;GACzC,EAAA,SAAA,GAAA,EAAZ,EAAmE,QAAnE,IAAmE,EAAf,EAAA,MAAK,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA;GACnC,EAAA,qBAAA,GAAA,EAAtB,EAAyF,GAAA;;IAA/C,OAAO,EAAA;IAAmB,SAAQ;;GAC9D,EAAA,aAAA,GAAA,EAAd,EAAgF,GAAA;;IAAtD,MAAM,EAAA;IAAW,eAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EER7D,IAAM,IAAQ,GAMR,IAAQ,EAAmB,GAAA,aAAE,EAE7B,IAAe,QACf,EAAM,YACD,EACL,gBAAgB,qBACjB,GAGI,EAAE,CACT;;GAIaA,EAAAA,OAAO,SAAS,EAAA,aAAA,GAAA,EAA7B,EAEQ,SAAA;;IAFgC,eAAY;IAAiB,OAAM;IAAuB,KAAK,EAAA;OACrG,EAAqB,EAAA,QAAA,QAAA,CAAA,EAAA,GAAA,EAAA,IAAA,EAAA,IAAA,GAAA;GAEvB,EAmCM,OAAA,EAlCJ,OAAK,EAAA,CAAC,wBAAsB,EAAA,cACE,EAAA,YAAA,CAAA,CAAA,EAAA,EAAA,CAKtB,EAAA,eAAA,GAAA,EADR,EAMM,OANN,IAMM,EADD,EAAA,YAAW,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA,EAEhB,EAqBM,OArBN,IAqBM,CApBkD,EAAA,aAAA,GAAA,EAAtD,EAAqF,GAAA;;IAA7E,OAAM;IAAoD,MAAM,EAAA;yCACxE,EAkBE,SAlBF,EACUC,EAiBR,QAjBc;6CACA,QAAA;IACd,eAAY;IACX,OAAK,CAAA,iBAAA;wBAA0E,EAAA;0BAA4C,EAAA,YAAO;;IAOlI,OAAK,CAAG,EAAA,OAAY,EAAA,OAAA,QAAA,CAAA;IACpB,UAAU,EAAA;IACV,MAAM,EAAA;IACN,aAAa,EAAA;IACb,IAAI,EAAA;IACJ,gBAAc,EAAA,QAAK,KAAU,KAAA;IAC7B,MAAM,EAAA;2BAfE,EAAA,MAAK,CAAA,CAAA,CAAA,CAAA,CAAA,EAAA,EAAA;GAoBZ,EAAA,cAAA,GAAA,EADR,EAYI,KAAA;;IAVF,eAAY;IACX,OAAK,EAAA,CAAA,mBAAA;oBAA4D,EAAA;yBAAmC,EAAA;;QAQlG,EAAA,WAAU,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE7FjB,IAAM,IAAQ,GAUR,IAAO,GAWP,IAAa,EAAmB,GAAA,aAAE,EAClC,IAAY,EAAmB,GAAC,YAA6B,EAE7D,IAAc,GAAO,EACrB,IAAW,GAAG,EAAY,WAC1B,IAAS,GAAG,EAAY,iBACxB,IAAa,GAAkB,EAC/B,IAAsB,GAAkB,EACxC,IAAgB,EAAM,QAAQ,WAAW,MAAW,EAAO,OAAO,EAAW,MAAM,EACnF,IAAc,EAAI,KAAK,IAAI,GAAe,EAAE,CAAC,EAE7C,IAAsB,QAAe,EAAM,iBAAiB,EAAE,EAC9D,IAAmB,QACvB,EAAY,QAAQ,EAAM,QAAQ,SAAS,EAAY,EAAY,MAAM,GAAG,KAAA,EAC7E;AAOD,EALA,EAAM,IAAa,MAAU;GAC3B,IAAM,IAAgB,EAAM,QAAQ,WAAW,MAAW,EAAO,OAAO,EAAM;AAC9E,GAAI,KAAiB,MAAG,EAAY,QAAQ;IAC5C,EAEF,QACQ,EAAM,QAAQ,KAAK,MAAW,EAAO,GAAG,GAC7C,GAAW,MAAsB;GAChC,IAAM,IAAgB,EAAU,WAAW,MAAO,MAAO,EAAW,MAAM;AAC1E,OAAI,KAAiB,GAAG;AACtB,MAAY,QAAQ;AACpB;;AAGF,OAAI,EAAY,UAAU,EAAkB,QAAQ;AAClD,MAAY,QAAQ,EAAU;AAC9B;;GAGF,IAAM,IAAmB,EAAkB,EAAY,QACjD,IAAiB,EAAU,QAAQ,EAAiB;AAC1D,KAAY,QACV,KAAkB,IAAI,IAAiB,KAAK,IAAI,EAAY,OAAO,KAAK,IAAI,EAAU,SAAS,GAAG,EAAE,CAAC;IAE1G;EAED,SAAS,EAAY,GAAe;AAClC,UAAO,GAAG,EAAY,UAAU;;EAGlC,SAAS,EAAa,GAAe;AAC9B,KAAM,QAAQ,WAEnB,EAAY,QAAQ,GACpB,QAAe,EAAW,OAAO,OAAO,CAAC;;EAG3C,SAAS,IAAiB;AAExB,GADA,EAAY,QAAQ,EAAM,QAAQ,QAClC,QAAe,EAAoB,OAAO,cAAgC,QAAQ,EAAE,OAAO,CAAC;;EAG9F,SAAS,EAAa,GAA0B,GAAe;AAG7D,GAFA,EAAY,QAAQ,GACpB,EAAW,QAAQ,EAAO,IAC1B,EAAK,UAAU,EAAO;;EAGxB,SAAS,EAAkB,GAA0B,GAAe;AAElE,GADA,EAAa,GAAQ,EAAM,EAC3B,QAAe,EAAW,OAAO,OAAO,CAAC;;EAG3C,SAAS,IAAqB;AAC5B,GAAI,EAAY,SAAS,EAAM,QAAQ,WAAQ,EAAY,QAAQ;;EAGrE,SAAS,EAAqB,GAAsB;AAClD,GAAI,EAAM,QAAQ,eAChB,EAAM,gBAAgB,EAClB,EAAY,SAAS,EAAM,QAAQ,SAAS,IAC9C,GAAgB,GAEhB,EAAY,SAAS,KAEd,EAAM,QAAQ,aACvB,EAAM,gBAAgB,EAClB,EAAY,SAAS,IACvB,GAAgB,GAEhB,IAAY,SAEL,EAAM,QAAQ,WAAW,EAAM,QAAQ,EAAY,WAC5D,EAAM,gBAAgB,EACtB,EAAa,EAAM,QAAQ,EAAY,QAAQ,EAAY,MAAM;;EAIrE,SAAS,IAAkB;AAGzB,GAFA,EAAY,QAAQ,EAAM,QAAQ,QAElC,EAAW,QAAQ,KAAA;;EAGrB,SAAS,EAAkB,GAAsB;AAC3C,UAAC,EAAM,QAAQ,WAAW,EAAM,QAAQ,eAAe,EAAM,QAAQ,aAEzE;QAAI,EAAM,QAAQ,YAEhB,CADA,EAAM,gBAAgB,EACtB,EAAa,EAAE;aACN,EAAM,QAAQ,UAEvB,CADA,EAAM,gBAAgB,EACtB,EAAa,EAAM,QAAQ,SAAS,EAAE;aAC7B,EAAM,QAAQ,WAAW,CAAC,EAAM,aAAa;AACtD,OAAM,gBAAgB;KACtB,IAAM,IAAQ,EAAU,MAAM,MAAM;AACpC,KAAI,KAAO,EAAK,UAAU,EAAM;;;;EAIpC,SAAS,IAAc;AACrB,KAAK,QAAQ;;EAGf,SAAS,IAAa;AACpB,KAAK,OAAO;;yBAKZ,EAmGU,WAAA;GAlGR,OAAM;GACN,eAAY;GACX,mBAAiB;GACjB,WAAO,GAAA,EAAmB,GAAU,CAAA,QAAA,UAAA,CAAA,EAAA,CAAA,MAAA,CAAA;;GAErC,EAcS,UAdT,IAcS,CAbP,EAMM,OANN,IAMM,CAFJ,EAA8D,MAAA;IAAzD,IAAI;IAAU,OAAM;QAAkB,EAAA,YAAW,EAAA,EAAA,EAC7C,EAAA,aAAA,GAAA,EAAT,EAAgF,KAAhF,IAAgF,EAAhB,EAAA,UAAS,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA,CAAA,CAAA,EAE3E,EAKM,OALN,IAKM,CAJQ,EAAA,SAAA,GAAA,EAAZ,EAEO,QAFP,IAEO,EADF,EAAA,gBAAe,GAAG,SAAI,EAAG,EAAA,eAAc,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA,EAE5C,EAA8F,GAAA;IAApF,WAAU;IAAQ,UAAS;IAAQ,MAAK;IAAK,SAAQ;IAAS,SAAO;;GAInF,EAuEM,OAvEN,IAuEM,CAtEJ,EAqEM,OArEN,IAqEM,CAjEJ,EAyCM,OAAA;aAxCA;IAAJ,KAAI;IACJ,OAAM;IACN,OAAA;KAAA,gBAAA;KAAA,cAAA;KAA8C;IAC9C,eAAY;IACZ,MAAK;IACL,UAAS;IACR,yBAAuB,EAAA;IACvB,oBAAkB;IAClB,mBAAiB;IACjB,SAAO;IACP,WAAS;eAEV,EA2BM,GAAA,MAAA,EA1BsB,EAAA,UAAlB,GAAQ,YADlB,EA2BM,OAAA;IAzBH,IAAI,EAAY,EAAK;IACrB,KAAK,EAAO;IACb,OAAM;IACN,eAAY;IACZ,MAAK;IACJ,iBAAe,EAAA,UAAe,EAAO;IACrC,eAAa,EAAA,UAAgB,KAAS,KAAA;IACtC,UAAK,MAAE,EAAkB,GAAQ,EAAK;IACtC,eAAU,MAAE,EAAA,QAAc;;IAE3B,EAAkE,GAAA;KAAjD,OAAO,IAAK;KAAM,MAAK;KAAK,SAAQ;;IACrD,EAES,QAFT,IAES,EADP,EAAO,MAAK,EAAA,EAAA;IAGN,EAAO,YAAA,GAAA,EADf,EAQO,QAAA;;KANL,OAAM;KACN,OAAA,EAAA,cAAA,KAAqB;KACpB,OAAO,EAAO;QAEf,EAAiE,GAAA;KAAzD,OAAM;KAAmB,MAAK;KAAe,MAAM;QAC3D,EAA6D,QAA7D,IAA6D,EAAzB,EAAO,SAAQ,EAAA,EAAA,CAAA,EAAA,GAAA,GAAA,IAAA,EAAA,IAAA,GAAA;IAEvC,EAAA,UAAe,EAAO,MAAA,GAAA,EAApC,EAA+F,GAAA;;KAAvD,OAAM;KAAkC,MAAK;;IACrF,EAA4E,GAAA;KAApE,OAAM;KAAuC,MAAK;;kCAI9D,EAqBM,OAAA;aApBA;IAAJ,KAAI;IACJ,OAAM;IACN,eAAY;IACX,eAAa,EAAA,UAAgB,EAAA,QAAQ,UAAU,KAAA;IAC/C,SAAO;IACP,cAAU,AAAA,EAAA,QAAA,MAAE,EAAA,QAAc,EAAA,QAAQ;;IAEnC,EAEO,QAFP,IAEO,CADL,EAAwC,GAAA;KAAhC,MAAK;KAAe,MAAM;;IAEpC,EAQM,OARN,IAQM,CAPJ,EAME,IAAA;iBALS,EAAA;8CAAS,QAAA;KACjB,cAAY,EAAA;KACZ,aAAa,EAAA;KACb,SAAO;KACP,WAAS;;;;;;IAGd,EAA4E,GAAA;KAApE,OAAM;KAAuC,MAAK;;;GAKhE,EAGS,UAHT,IAGS,CAFP,EAAiH,QAAA;IAA1G,IAAI;IAAQ,OAAM;MAAmC,iDAA8C,EAC1G,EAAoG,GAAA;IAA1F,OAAM;IAAmB,OAAM;IAAO,MAAK;IAAK,SAAQ;IAAa,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEtP5F,IAAM,IAAQ,GAOR,IAAO,GAKP,IAAY,QACV,EAAM,UAAU,wBAAwB,EAAM,aAAa,KAAA,KAAa,EAAM,SAAS,aAAa,KAAA,EAC3G,EACK,IAAa,QAEf,EAAM,UAAU,aAAa,OAC5B,EAAM,UAAU,wBAAwB,EAAM,UAAU,qBAAqB,EAAM,UAAU,oBACjG,EACK,IAAa,QAAe,EAAM,UAAU,aAAa,MAAS,EAAM,UAAU,gBAAgB;EAExG,SAAS,EAAQ,GAAmB;AAClC,KAAK,WAAW;IAAE,IAAI,EAAM,SAAU;IAAI;IAAU,CAAC;;mBAM7C,EAAA,SAAa,EAAA,SAAc,EAAA,SAAA,GAAA,EADnC,EAyBU,WAzBV,IAyBU,CAnBQ,EAAA,SAAA,GAAA,EAAhB,EAQW,GAAA,EAAA,KAAA,GAAA,EAAA,CAPT,EAEM,OAFN,IAEM,CADJ,EAA0B,EAAA,QAAA,WAAA,EAAA,QAAA,CAAA,EAAA,EAAjB,EAAA,QAAO,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,EAElB,EAGM,OAHN,IAGM,CAFJ,EAA4G,GAAA;GAAlG,SAAQ;GAAY,MAAK;GAAM,OAAO,EAAA;GAAc,UAAU,EAAA;GAAW,SAAK,AAAA,EAAA,QAAA,MAAE,EAAO,GAAA;sCACjG,EAAwF,GAAA;GAA9E,MAAK;GAAM,OAAO,EAAA;GAAe,UAAU,EAAA;GAAW,SAAK,AAAA,EAAA,QAAA,MAAE,EAAO,GAAA;gDAIpE,EAAA,SAAA,GAAA,EAAd,EAGI,KAHJ,IAGI,CAFF,EAAyC,GAAA;GAAjC,MAAK;GAAgB,MAAM;MACnC,EAA8D,EAAA,QAAA,YAAA,EAAA,QAAA,CAAA,AAAA,EAAA,OAAA,EAAxC,qCAAiC,GAAA,CAAA,CAAA,CAAA,CAAA,KAAA,GAAA,EAGzD,EAGI,KAHJ,IAGI,CAFF,EAAyC,GAAA;GAAjC,MAAK;GAAgB,MAAM;MACnC,EAA8D,EAAA,QAAA,YAAA,EAAA,QAAA,CAAA,AAAA,EAAA,OAAA,EAAxC,qCAAiC,GAAA,CAAA,CAAA,CAAA,CAAA,EAAA,CAAA,IAAA,EAAA,IAAA,GAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;EE7B7D,IAAM,IAAQ,EAAmB,GAAA,aAAE;yBAIjC,EAgCM,OAhCN,IAgCM;GA/BSC,EAAAA,OAAO,SAAA,GAAA,EAApB,EAEQ,SAAA;;IAFmB,OAAM;IAAY,KAAK,EAAA;OAChD,EAAqB,EAAA,QAAA,QAAA,CAAA,EAAA,GAAA,GAAA,IAAA,EAAA,IAAA,GAAA;KAEvB,EAcE,YAdF,EACUC,EAaR,QAbc;6CACA,QAAA;IACb,OAAK,CAAA,eAAA,EAAA,qBAAoE,EAAA,YAAO,QAAA,CAAA;IAMhF,MAAM,EAAA;IACN,UAAU,EAAA;IACV,aAAa,EAAA;IACb,IAAI,EAAA;IACJ,gBAAc,EAAA,QAAK,KAAU,KAAA;2BAXrB,EAAA,MAAK,CAAA,CAAA;GAcR,EAAA,cAAA,GAAA,EADR,EAYI,KAAA;;IAVF,OAAA,EAAA,eAAA,QAAyB;IACxB,OAAK,EAAA,CAAA,mBAAA;oBAAkE,EAAA;yBAAqC,EAAA;;QAQ1G,EAAA,WAAU,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EExDnB,IAAM,IAAQ,GAWR,IAAO,GAOP,IAAQ,EAAmB,GAAA,aAAiB,EAC5C,IAAU,GAAsB,EAChC,IAAS,QAAe,EAAM,WAAW,eAAe,EAAM,WAAW,YAAY,EACrF,IAAY,QAAe,CAAC,EAAM,YAAY,CAAC,EAAO,SAAS,EAAM,MAAM,MAAM,CAAC,SAAS,EAAE;EAEnG,SAAS,IAAS;AACX,KAAU,SACf,EAAK,UAAU,EAAM,MAAM,MAAM,CAAC;;EAKpC,SAAS,EAAe,GAAmB;AACrC,KAAM,YAEK,EAAM,QACT,QAAQ,iDAAiD,IAErE,EAAQ,OAAO,cAAc,WAAW,EAAE,OAAO;;EAGnD,SAAS,EAAc,GAAsB;AACvC,KAAM,QAAQ,WAAW,EAAM,YAAY,EAAM,gBACrD,EAAM,gBAAgB,EACtB,EAAM,iBAAiB,EACvB,GAAQ;;yBAKR,EA2CO,QAAA;YA1CD;GAAJ,KAAI;GACJ,OAAM;GACN,eAAY;GACX,aAAW,EAAA;GACX,UAAM,EAAU,GAAM,CAAA,UAAA,CAAA;GACtB,SAAO;;GAEGC,EAAAA,OAAO,UAAA,GAAA,EAAlB,EAEM,OAFN,IAEM,CADJ,EAAsB,EAAA,QAAA,SAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA;GAGxB,EASE,IAAA;gBARS,EAAA;6CAAK,QAAA;IACd,OAAM;IACL,OAAK,EAAA,EAAA,6BAAiC,EAAA,WAAS,CAAA;IAC/C,MAAM,EAAA;IACN,aAAa,EAAA;IACb,UAAU,EAAA;IACV,cAAY,EAAA;IACZ,WAAS;;;;;;;;;GAGZ,EAaM,OAbN,IAaM,CAZJ,EAEM,OAFN,IAEM,CADJ,EAAuB,EAAA,QAAA,UAAA,CAAA,CAAA,EAEzB,EAQE,GAAA;IAPC,MAAM,EAAA,QAAM,WAAA;IACZ,SAAS,EAAA,QAAM,cAAA;IAChB,MAAK;IACJ,aAAW,EAAA,QAAM,SAAA;IACjB,cAAY,EAAA,QAAS,EAAA,YAAY,EAAA;IACjC,UAAU,EAAA,YAAQ,CAAM,EAAA,SAAM,CAAK,EAAA;IACnC,SAAK,AAAA,EAAA,QAAA,MAAE,EAAA,SAAU,EAAI,OAAA;;;;;;;;GAI1B,EAII,KAJJ,IAII,CAHc,EAAA,WAAM,eAAA,GAAA,EAAtB,EAAqE,GAAA,EAAA,KAAA,GAAA,EAAA,CAAA,EAA7B,qBAAkB,CAAA,EAAA,GAAA,IACrC,EAAA,WAAM,eAAA,GAAA,EAA3B,EAA8E,GAAA,EAAA,KAAA,GAAA,EAAA,CAAA,EAAjC,yBAAsB,CAAA,EAAA,GAAA,IAC9C,EAAA,WAAM,WAAA,GAAA,EAA3B,EAAkF,GAAA,EAAA,KAAA,GAAA,EAAA,CAAA,EAAzC,iCAA8B,CAAA,EAAA,GAAA,IAAA,EAAA,IAAA,GAAA,CAAA,CAAA;;;IEjGhE,KAAsB;CAAC;CAAS;CAAa;CAAa;CAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECqC/E,IAAM,IAAQ,GAQR,IAAgB,EAAoB,GAAA,aAAE,EAEtC,IAAO;AAMb,EAAI,EAAM,gBAAgB,KAAA,MACxB,EAAc,QAAQ,EAAM;EAG9B,IAAM,KAAwB,MAAiB;AAE7C,KAAc,QADS,EAAM,OACQ;KAGjC,KAAsB,MAAsB;AAE5C,KAAM,WAAW,MAChB,EAAM,QAA2B,QAAQ,8BAA8B,IAExE,EAAM,qBACV,EAAM,gBAAgB,EACtB,EAAK,iBAAiB,EAAM;KAOxB,IAAmB,QAAgB,EAAM,sBAAsB,EAAE,OAAO,GAAoB,GAAG,EAAE,CAAE,EAEnG,IAAgB,EACpB,OAAO,EAAM,yBAAyB,SAAS,eAChD,EAEK,IAAe,QACf,EAAM,mBAAmB,eACvB,EAAM,oBAAoB,SACrB,EAAc,QAAQ,iBAAiB,kBAEvC,EAAc,QAAQ,iBAAiB,iBAGzC,EAAc,QAAQ,eAAe,eAE9C;yBAIA,EA0BU,WAAA;GA1BD,eAAY;GAAe,OAAM;GAAgB,MAAM,EAAA;GAAgB,UAAQ;MACtF,EAqBU,WArBV,EACU,EAoBA,cApBY;GACnB,OAAK,CAAA,wCAA2C,EAAA,eAAc;GAC9D,OAAK;IAAA,GAAO;IAAa,GAAK,EAAA;IAAa;GAC3C,cAAU,AAAA,EAAA,QAAA,MAAEC,EAAAA,MAAK,qBAAA;GACjB,cAAU,AAAA,EAAA,QAAA,MAAEA,EAAAA,MAAK,qBAAA;KAClB,EAAM,EAAiB,OAAD,GAAA,CAAA,EAAA;GAGtB,EAAuB,EAAA,QAAA,WAAA,EAAA,EAAA,KAAA,GAAA,GAAA;GACT,EAAA,oBAAe,UAAA,GAAA,EAA7B,EAA2F,GAAA;;IAAhD,MAAM,EAAA;IAAc,6BAAA;;GAC/C,EAAA,oBACd,EAAuB,EAAA,QAAA,WAAA,EAAA,KAAA,GAAA,EAAA,KAAA,GAAA,GAAA,IAAA,GAAA,EAEzB,EAKW,GAAA,EAAA,KAAA,GAAA,EAAA,CAJT,EAEO,QAAA,MAAA,EADF,EAAA,OAAM,EAAA,EAAA,EAEW,EAAA,WAAA,GAAA,EAAtB,EAAgF,GAAA;;IAAhD,OAAO,EAAA;IAAS,SAAQ;IAAY,MAAK;;GAE7D,EAAA,oBAAe,WAAA,GAAA,EAA7B,EAA4F,GAAA;;IAAhD,MAAM,EAAA;IAAc,6BAAA;;UAElE,EAEM,OAAA,EAFA,OAAK,EAAE,EAAA,cAAa,EAAA,EAAA,CACxB,EAAQ,EAAA,QAAA,WAAA,EAAA,EAAA,KAAA,GAAA,GAAA,CAAA,EAAA,EAAA,CAAA,EAAA,IAAA,GAAA;;;;;AElFd,SAAS,EAAqB,GAAY,GAA0B;AAClE,KAAI,EAAK,aAAa,KAAK,UAAW,QAAO;CAC7C,IAAM,IAAM,EAAK,eAAe;AAChC,QAAO,IAAkB,EAAI,SAAS,IAAI,EAAI,MAAM,CAAC,SAAS;;AAGhE,SAAS,GAAwB,GAAmB,GAAc,GAAoD;CACpH,IAAM,IAAW,MAAS,YAAY,KAAK,GACrC,IAAQ,MAAM,KAAK,EAAK,WAAW;AAczC,QAZI,MAAa,KAER,EAAM,MAAM,MACb,EAAE,aAAa,KAAK,eAEf,CADI,EACA,aAAa,OAAO,GAE1B,EAAqB,GAAG,EAAiC,CAChE,GAIG,EAAM,MAAM,MACb,EAAE,aAAa,KAAK,eACb,EACD,aAAa,OAAO,KAAK,IAFU,GAG7C;;AAcJ,SAAS,GAAuB,GAAkD;AAChF,KAAI,EAAK,WAAY,QAAO;CAC5B,IAAM,IAAS,EAA0B;AAEzC,QADI,CAAC,KAAS,OAAO,KAAU,WAAiB,OACzC;;AAGT,SAAS,GACP,GACA,GACA,GACS;CAET,IAAM,IAAQ,EAAM,MAAS,KAAK,YAAY;AAE9C,QADK,IACE,EAAM,MAAM,MACb,EAAE,aAAa,KAAK,eAAqB,KACtC,EAAqB,GAAG,EAAiC,CAChE,GAJiB;;AAOrB,SAAS,EAAgB,GAAmB,GAAsC;CAChF,IAAM,IAAK,EAAK;AAKhB,QAJK,IACD,MAAS,aAAa,MAAS,KAC1B,EAAG,cAAc,mBAAmB,GAEtC,EAAG,cAAc,cAAc,IAAI,OAAO,EAAK,CAAC,IAAI,GAJ3C;;AAelB,SAAgB,GACd,GACA,GACA,EAAE,mBAAgB,IAAM,sCAAmC,OAAU,EAAE,EAC9D;CACT,IAAM,IAAS,IAAgB,EAAgB,GAAM,EAAK,GAAG;AAC7D,KAAI,EACF,QAAO,EAAO,cAAc,EAAE,SAAS,IAAM,CAAC,CAAC,MAAM,MAC/C,EAAE,aAAa,KAAK,eAAqB,KACtC,EAAqB,GAAG,EAAiC,CAChE;CAMJ,IAAM,IAAc,GAAuB,EAAK;AAGhD,QAFI,IAAoB,GAAqB,GAAa,GAAM,EAAiC,GAE1F,GAAwB,GAAM,GAAM,EAAiC;;AAU9E,SAAgB,GAAgB,GAAkB,IAAkC,EAAE,EAAE;CACtF,IAAM,EAAE,SAAM,aAAU,aAAU,IAAM,sBAAmB,IAAM,sCAAmC,OAAU,GAExG,IAAiB,GAAmB,EACpC,IAAQ,KAAY,GAAU,EAK9B,IAAa,MAAa,aAAa,MAAa,KAAK,YAAY,GACrE,UAAoB,CAAC,CAAC,KAAS,CAAC,CAAE,EAAkC,IACpE,IAAa,EAAI,GAAa,CAAC;AACrC,SAAqB;AACnB,IAAW,QAAQ,GAAa;GAChC;CAGF,IAAM,IAAgB,EAAI,GAAM,EAE5B,IAA8B,MAC9B,IAAqC,MAEnC,UAAgB;AAKpB,EAJA,AAEE,OADA,EAAG,YAAY,EACV,OAEP,AAEE,OADA,EAAW,oBAAoB,cAAc,EAAa,EAC7C;IAIX,UAAqB;AACzB,MAAI,CAAC,EAAe,OAAO;AACzB,KAAc,QAAQ;AACtB;;EAGF,IAAM,IAAK,EAAM,EAAK;AACtB,MAAI,CAAC,GAAI;AACP,KAAc,QAAQ;AACtB;;AAGF,IAAc,QAAQ,GAAmB,GAAI,GAAU;GAErD,eAAe;GACf;GACD,CAAC;IAGE,UAAe;AACnB,MAAI,CAAC,EAAe,MAAO;AAE3B,KAAS;EAET,IAAM,IAAK,EAAM,EAAK;AACtB,MAAI,CAAC,GAAI;AACP,KAAc,QAAQ;AACtB;;AAUF,EAPA,GAAc,EAEV,MACF,IAAa,EAAgB,GAAI,EAAS,EACtC,KAAY,EAAW,iBAAiB,cAAc,EAAa,GAGrE,MACF,IAAK,IAAI,uBAAuB,GAAc,CAAC,EAC/C,EAAG,QAAQ,GAAI;GACb,WAAW;GACX,SAAS;GACT,YAAY;GACZ,iBAAiB,CAAC,OAAO;GACzB,eAAe;GAChB,CAAC;;AAkBN,QAdA,EAAU,EAAO,EACjB,EAAgB,EAAQ,EAGxB,QACQ,EAAM,EAAK,QACX;AACJ,EAAI,EAAe,SAAO,GAAQ;GAErC,EAKM;EACL,SAHc,QAAgB,EAAe,QAAQ,EAAc,QAAQ,EAAW,MAAO;EAI7F;EACA;EACA,SAAS;EACV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EC/OH,IAAM,IAAQ,GAQR,IAAgB,QAAe;AACnC,QAAK,IAAI,IAAQ,EAAM,MAAM,SAAS,GAAG,KAAS,GAAG,IACnD,KAAI,EAAM,MAAM,GAAO,YAAY,QAAS,QAAO;AAErD,UAAO;IACP;EAEF,SAAS,EAAS,GAAc,GAA6B;AAG3D,UAFI,EAAK,SAAe,EAAK,SACzB,EAAK,YAAY,UAAgB,aAC9B,EAAM,cAAc,MAAU,EAAc,QAAQ,WAAW;;EAKxE,SAAS,EAAa,GAAc,GAAuB;AACzD,UAAO,EAAS,GAAM,EAAM,KAAK,WAAW,mCAAmC;;yBAK/E,EA0CkB,GAAA;GA1CD,KAAI;GAAK,MAAK;GAAU,OAAM;GAAe,MAAK;;oBAC/B,EAAA,EAAA,GAAA,EAAlC,EAwCK,GAAA,MAAA,EAxCuB,EAAA,QAAhB,GAAM,YAAlB,EAwCK,MAAA;IAxC+B,KAAK,EAAK,MAAM;IAAO,OAAM;IAAW,eAAa,EAAS,GAAM,EAAK;OAC/F,EAAK,YAAO,UAEkB,EAAA,IAAA,GAAA,IAFlB,GAAA,EAAxB,EAGO,QAHP,IAGO,CAFS,EAAK,QAAA,GAAA,EAAnB,EAAwD,GAAA;;IAA9B,MAAM,EAAK;IAAO,MAAM;kCAClD,EAAwC,QAAxC,GAAwC,EAAA,CAAA,GAG1C,EAiCM,OAjCN,IAiCM,CAhCK,EAAK,YAAO,WAAA,GAAA,EAArB,EAEI,KAFJ,IAEI,CADF,EAAyB,MAAA,MAAA,EAAlB,EAAK,MAAK,EAAA,EAAA,CAAA,CAAA,KAAA,GAAA,EAGnB,EA2BW,GAAA,EAAA,KAAA,GAAA,EAAA;IAxBT,EAGI,KAAA;KAHD,OAAK,EAAA,CAAC,2BAAkC,EAAa,GAAM,EAAK,CAAA,CAAA;KAAG,OAAA,EAAA,eAAA,QAAyB;QAC7F,EAA2F,QAAA,EAApF,OAAK,EAAA,EAAA,cAAkB,EAAS,GAAM,EAAK,KAAA,UAAA,CAAA,EAAA,EAAA,EAAqB,EAAK,MAAK,EAAA,EAAA,EACrE,EAAK,YAAA,GAAA,EAAjB,EAA8E,QAA9E,IAA8E,EAAvB,EAAK,SAAQ,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA,CAAA,EAAA,EAAA;IAE7D,EAAK,QAAA,GAAA,EAAd,EAAgF,KAAhF,IAAgF,EAAhB,EAAK,KAAI,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA;IAEjE,EAAK,QAAQ,EAAK,KAAK,SAAM,KAAA,GAAA,EADrC,EAeK,MAfL,IAeK,EAAA,EAAA,GAAA,EATH,EAQK,GAAA,MAAA,EAPuB,EAAK,OAAvB,GAAK,YADf,EAQK,MAAA;KANF,KAAK;KACN,OAAK,EAAA,CAAC,oBAAkB,EAAA,sBACQ,EAAI,YAAO,SAAA,CAAA,CAAA;KAC3C,cAAW;SAER,EAAI,MAAK,EAAA,EAAA;IAGP,EAAK,WAAA,GAAA,EAAd,EAEI,KAFJ,IAA4E,WAC7D,EAAG,EAAK,QAAO,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE9DxC,IAAM,IAAQ,GASR,IAAO,EAAoB,GAAC,OAA2B,EACvD,IAAmB,GAAa,EAChC,IAAY,GAAa,EACzB,IAAmB,EAAI,GAAM,EAC/B;AAGJ,GADI,EAAM,eAAe,EAAM,iBAAa,EAAK,QAAQ,KACrD,EAAM,gBAAa,EAAU,QAAQ,KAAK,KAAK;EAEnD,IAAM,IAAoB,QAAe,EAAM,YAAY,EAAiB,MAAM,EAC5E,IAAe,QAAe;AAClC,OAAI,EAAM,YAAa,QAAO,EAAM;AACpC,OAAI,EAAkB,UAAU,KAAA,EAAW,QAAO,EAAM;GACxD,IAAM,IAAkB,KAAK,IAAI,GAAG,KAAK,MAAM,EAAkB,MAAM,CAAC;AACxE,UAAO,eAAe,EAAgB,GAAG,MAAoB,IAAI,WAAW;IAC5E,EAEI,IAAQ,GAAU,EAClB,IAAa,EAAmC,aAAa,EAO7D,EAAE,eAAe,MAAmB,GAAgB,WAAW,EAAE,MAJtD,QAAe;GAC9B,IAAM,IAAW,EAAW,OAAO,aAAa;AAChD,UAAO,aAAoB,aAAc,EAAS,OAAuB;IACzE,EACqF,CAAC;EAKxF,SAAS,EAAe,GAA0B;AAChD,UAAO,EAAO,MAAM,MACd,EAAM,SAAS,IAAgB,KAC/B,EAAM,SAAS,IAAa,OAAO,EAAM,YAAY,GAAG,CAAC,MAAM,CAAC,SAAS,IACzE,EAAM,SAAS,IAAiB,MAAM,QAAQ,EAAM,SAAS,IAAI,EAAe,EAAM,SAAoB,GACvG,GACP;;EAQJ,SAAS,IAAwB;AAE/B,UADI,EAAM,MAAM,SAAS,KAAK,EAAe,QAAc,KACpD,EAAe,EAAM,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC;;EAGlD,SAAS,IAAkB;AACzB,GAAI,MAAe,KAAA,MACjB,aAAa,EAAW,EACxB,IAAa,KAAA;;EAQjB,SAAS,EAAY,GAAmB;GAEtC,IAAM,IADS,EAAM,QACG,UAAU,UAAU;AACxC,IAAC,KAAW,EAAQ,kBAAkB,EAAM,kBAChD,EAAiB,QAAQ,IACzB,GAAiB;;SAGnB,QACQ,EAAM,cACX,GAAa,MAAiB;AAG7B,OAFA,GAAiB,EAEb,GAAa;AAIf,IAHA,EAAU,QAAQ,KAAK,KAAK,EAC5B,EAAiB,QAAQ,KAAA,GACzB,EAAiB,QAAQ,IACzB,EAAK,QAAQ;AACb;;AAGG,SACD,EAAU,UAAU,KAAA,MACtB,EAAiB,SAAS,KAAK,KAAK,GAAG,EAAU,SAAS,MAEvD,EAAiB,UACpB,IAAa,iBAAiB;AAC5B,IAAK,EAAiB,UAAO,EAAK,QAAQ;MACzC,EAAM,eAAe;IAG7B,EAED,EAAgB,EAAgB,YAKtB,GAAqB,IAAA,GAAA,EAD7B,EA+Bc,GAAA;;eA7BH,EAAA;4CAAI,QAAA;GACZ,gBAAc,EAAA;GACf,OAAM;GACN,oBAAiB;GACjB,mBAAgB;GAChB,6BAAA;GACA,uBAAA;GACC,mBAAiB,EAAA,oBAAA,IAA4B;GAC7C,kBAAgB,EAAA,gBAAA,cAAgC;GAChD,SAAO;;GAEG,SAAO,QAWT,CAPP,EAOO,QAAA;aAPG;IAAJ,KAAI;IAAa,OAAM;IAA8D,aAAU;OACnG,EAKO,EAAA,QAAA,WAAA,EAAA,QAAA,CAJL,EAAuB,GAAA,EAAf,MAAK,SAAO,CAAA,EAGpB,EAA8F,QAAA,EAAxF,OAAK,EAAA,CAAC,mBAAiB,EAAA,cAAyB,EAAA,aAAW,CAAA,CAAA,EAAA,EAAA,EAAO,EAAA,MAAY,EAAA,EAAA,CAAA,CAAA,CAAA,EAAA,IAAA,CAAA,CAAA;oBAQpF,CAHN,EAGM,OAHN,IAGM,CAFc,EAAA,MAAM,SAAM,KAAA,GAAA,EAA9B,EAAgF,GAAA;;IAA3C,OAAO,EAAA;IAAQ,eAAa,EAAA;uDACjE,EAAQ,EAAA,QAAA,UAAA,CAAA,CAAA,CAAA,CAAA;;iDAIZ,EAUO,QAAA;;YARD;GAAJ,KAAI;GACJ,OAAM;GACN,aAAU;MAEV,EAGO,EAAA,QAAA,WAAA,EAAA,QAAA,CAFL,EAAuB,GAAA,EAAf,MAAK,SAAO,CAAA,EACpB,EAA8F,QAAA,EAAxF,OAAK,EAAA,CAAC,mBAAiB,EAAA,cAAyB,EAAA,aAAW,CAAA,CAAA,EAAA,EAAA,EAAO,EAAA,MAAY,EAAA,EAAA,CAAA,CAAA,CAAA,EAAA,IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EErJ1F,IAAM,IAAQ,GAIR,IAAO,EAAoB,GAAC,OAA2B,EACvD,IAAwC,CAAC,oBAAoB,eAAe;AAElF,GAAI,EAAM,eAAe,EAAgB,SAAS,EAAM,MAAM,MAAE,EAAK,QAAQ;EAE7E,IAAM,IAAS,SACsE;GACjF,mBAAmB;IAAE,OAAO;IAAW,WAAW;IAAmB;GACrE,mBAAmB;IAAE,OAAO;IAAW,WAAW;IAAkB;GACpE,sBAAsB;IAAE,OAAO;IAAqB,WAAW;IAAiB;GAChF,sBAAsB;IAAE,OAAO;IAAa,WAAW;IAAmB;GAC1E,oBAAoB;IAAE,OAAO;IAAa,WAAW;IAAkB;GACvE,gBAAgB;IAAE,OAAO;IAAS,WAAW;IAAgB;GAC7D,iBAAiB;IAAE,OAAO;IAAU,WAAW;IAAgB;GAChE,EACe,EAAM,OACtB;EAEF,SAAS,EAAY,GAAgB;AACnC,OAAI,OAAO,KAAU,SAAU,QAAO;AACtC,OAAI;AACF,WAAO,KAAK,UAAU,GAAO,MAAM,EAAE,IAAI,OAAO,EAAM;WAChD;AACN,WAAO,OAAO,EAAM;;;SAIxB,QACQ,EAAM,QACX,GAAO,MAAkB;AACxB,GAAI,MAAU,KAAiB,EAAgB,SAAS,EAAM,KAAE,EAAK,QAAQ;IAEhF,kBAIC,EA8Cc,GAAA;eA7CH,EAAA;4CAAI,QAAA;GACZ,gBAAc,EAAA;GACf,OAAM;GACN,oBAAiB;GACjB,mBAAgB;GAChB,6BAAA;GACA,uBAAA;GACC,mBAAiB,EAAA,sBAAA,IAA8B;;GAErC,SAAO,QAKT,CAJP,EAIO,QAJP,IAIO;IAHL,EAAqC,GAAA;KAA7B,MAAK;KAAY,MAAM;;IAC/B,EAAgE,QAAhE,IAAgE,EAAlB,EAAA,SAAQ,EAAA,EAAA;IACtD,EAAsF,QAAA;KAA/E,OAAK,EAAA,CAAG,EAAA,MAAO,WAAS,YAAA,CAAA;KAAgB,MAAK;SAAY,EAAA,MAAO,MAAK,EAAA,EAAA;;oBA+B1E,CA3BN,EA2BM,OA3BN,IA2BM,CA1BJ,EAyBM,OAzBN,IAyBM,CAxBWC,EAAAA,OAAO,SAAS,EAAA,UAAU,KAAA,KAAA,GAAA,EAAzC,EAOU,WAPV,IAOU,CAAA,AAAA,EAAA,OANR,EAA4C,MAAA,EAAxC,OAAM,oBAAkB,EAAC,cAAU,GAAA,EACvC,EAIO,EAAA,QAAA,SAAA,EAJa,OAAO,EAAA,OAAK,QAIzB,CAHL,EAE4C,OAF5C,IAE4C,CAA3C,EAAqC,QAAA,MAAA,EAA5B,EAAY,EAAA,MAAK,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA,EAIhB,EAAA,aAAA,GAAA,EAAf,EAKU,WALV,IAKU,CAAA,AAAA,EAAA,OAJR,EAAwD,MAAA,EAApD,OAAM,qCAAmC,EAAC,SAAK,GAAA,EACnD,EAEQ,OAFR,IAEQ,EADN,EAAA,UAAS,EAAA,EAAA,CAAA,CAAA,IAIOA,EAAAA,OAAO,UAAU,EAAA,WAAW,KAAA,KAAA,GAAA,EAAhD,EAOU,WAPV,IAOU,CAAA,AAAA,EAAA,OANR,EAAwC,MAAA,EAApC,OAAM,oBAAkB,EAAC,UAAM,GAAA,EACnC,EAIO,EAAA,QAAA,UAAA,EAJc,QAAQ,EAAA,QAAM,QAI5B,CAHL,EAE6C,OAF7C,IAE6C,CAA5C,EAAsC,QAAA,MAAA,EAA7B,EAAY,EAAA,OAAM,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;;;IE1F3B,IAAsB;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;ACaD,SAAgB,GACd,GACA,IAAmC,EAAE,EACnB;CAClB,IAAM,IAAY,EAAQ,aAAa,IACjC,IAAa,EAAI,GAAK;CAE5B,SAAS,IAAS;EAChB,IAAM,IAAK,EAAU;AACrB,EAAI,MAAI,EAAW,QAAQ,EAAG,eAAe,EAAG,YAAY,EAAG,gBAAgB;;CAGjF,SAAS,EAAe,IAA2B,UAAU;EAC3D,IAAM,IAAK,EAAU;AAChB,QACL,EAAG,SAAS;GAAE,KAAK,EAAG;GAAc;GAAU,CAAC,EAC/C,EAAW,QAAQ;;CAGrB,IAAI;CAEJ,SAAS,EAAO,GAAiB;EAC/B,IAAM,UAAiB,GAAQ;AAC/B,IAAG,iBAAiB,UAAU,GAAU,EAAE,SAAS,IAAM,CAAC;EAI1D,IAAM,UAAsB;AAC1B,GAAI,EAAW,SAAO,EAAe,OAAO;KAExC,IAA+C,EAAE,EAKnD,GACE,oBAAW,IAAI,SAAkB,EACjC,UAA2B;AAC1B,SACL,MAAK,IAAM,KAAS,MAAM,KAAK,EAAG,SAAS,CACzC,CAAK,EAAS,IAAI,EAAM,KACtB,EAAS,IAAI,EAAM,EACnB,EAAe,QAAQ,EAAM;;AAUnC,MANI,OAAO,iBAAmB,QAC5B,IAAiB,IAAI,eAAe,EAAc,EAClD,EAAe,QAAQ,EAAG,EAC1B,GAAoB,EACpB,EAAU,KAAK,EAAe,GAE5B,OAAO,mBAAqB,KAAa;GAG3C,IAAM,IAAkB,IAAI,uBAAuB;AAEjD,IADA,GAAoB,EACpB,GAAe;KACf;AAEF,GADA,EAAgB,QAAQ,GAAI,EAAE,WAAW,IAAM,CAAC,EAChD,EAAU,KAAK,EAAgB;;AAKjC,EAFA,GAAQ,EAER,UAAe;AAEb,GADA,EAAG,oBAAoB,UAAU,EAAS,EAC1C,EAAU,SAAS,MAAa,EAAS,YAAY,CAAC;;;AAgB1D,QAZA,EACE,IACC,MAAO;AAGN,EAFA,KAAU,EACV,IAAS,KAAA,GACL,KAAI,EAAO,EAAG;IAEpB,EAAE,WAAW,IAAM,CACpB,EAED,QAAqB,KAAU,CAAC,EAEzB;EAAE;EAAY;EAAgB"}
1
+ {"version":3,"file":"pv-components-ai.mjs","names":["$slots","$attrs","$slots","$attrs","$slots","$emit","$slots"],"sources":["../../../src/components/base/PvSpinner/PvSpinner.vue","../../../src/components/base/PvSpinner/PvSpinner.vue","../../../src/components/base/PvButton/helpers.ts","../../../src/web-components/utils.ts","../../../src/components/base/baseProps.ts","../../../src/components/base/PvIcon/PvIcon.vue","../../../src/components/base/PvIcon/PvIcon.vue","../../../src/components/base/PvCounterBadge/PvCounterBadge.vue","../../../src/components/base/PvCounterBadge/PvCounterBadge.vue","../../../src/components/base/PvButton/PvButton.vue","../../../src/components/base/PvButton/PvButton.vue","../../../src/components/base/PvInput/PvInput.vue","../../../src/components/base/PvInput/PvInput.vue","../../../src/components/ai/PvQuestion/PvQuestion.vue","../../../src/components/ai/PvQuestion/PvQuestion.vue","../../../src/components/ai/PvConfirmation/PvConfirmation.vue","../../../src/components/ai/PvConfirmation/PvConfirmation.vue","../../../src/components/base/PvTextArea/PvTextArea.vue","../../../src/components/base/PvTextArea/PvTextArea.vue","../../../src/components/ai/PvPromptInput/PvPromptInput.vue","../../../src/components/ai/PvPromptInput/PvPromptInput.vue","../../../src/components/ai/PvPromptInput/types.ts","../../../src/components/base/PvAccordion/PvAccordion.vue","../../../src/components/base/PvAccordion/PvAccordion.vue","../../../src/composables/useSlotPresence.ts","../../../src/components/ai/PvStepList/PvStepList.vue","../../../src/components/ai/PvStepList/PvStepList.vue","../../../src/components/ai/PvReasoning/PvReasoning.vue","../../../src/components/ai/PvReasoning/PvReasoning.vue","../../../src/components/ai/PvTool/PvTool.vue","../../../src/components/ai/PvTool/PvTool.vue","../../../src/components/ai/toolLifecycle.ts","../../../src/components/ai/useStickToBottom.ts"],"sourcesContent":["<script setup lang=\"ts\">\nimport { PvSpinnerSize, PvSpinnerVariant } from \"./types\";\n\ninterface PvSpinnerProps {\n /** The size of the spinner. Controls the rendered diameter via a CSS custom property. */\n size?: PvSpinnerSize;\n /** The color variant of the spinner. Use \"white\" on dark backgrounds and \"dark\" on light backgrounds. */\n variant?: PvSpinnerVariant;\n}\n\nconst mapSizeToPx: Record<PvSpinnerSize, string> = {\n xs: \"0.75rem\",\n sm: \"1rem\",\n md: \"1.25rem\",\n lg: \"1.5rem\",\n xl: \"2rem\",\n};\n\nconst mapVariantToPvClass: Record<PvSpinnerVariant, string> = {\n primary: \"pv-spinner\",\n dark: \"pv-spinner-dark\",\n white: \"pv-spinner-light\",\n};\n\nwithDefaults(defineProps<PvSpinnerProps>(), {\n size: \"lg\",\n variant: \"primary\",\n});\n</script>\n\n<template>\n <div data-testid=\"pv-spinner\" :class=\"mapVariantToPvClass[variant]\" :style=\"{ '--size': mapSizeToPx[size] }\" />\n</template>\n","<script setup lang=\"ts\">\nimport { PvSpinnerSize, PvSpinnerVariant } from \"./types\";\n\ninterface PvSpinnerProps {\n /** The size of the spinner. Controls the rendered diameter via a CSS custom property. */\n size?: PvSpinnerSize;\n /** The color variant of the spinner. Use \"white\" on dark backgrounds and \"dark\" on light backgrounds. */\n variant?: PvSpinnerVariant;\n}\n\nconst mapSizeToPx: Record<PvSpinnerSize, string> = {\n xs: \"0.75rem\",\n sm: \"1rem\",\n md: \"1.25rem\",\n lg: \"1.5rem\",\n xl: \"2rem\",\n};\n\nconst mapVariantToPvClass: Record<PvSpinnerVariant, string> = {\n primary: \"pv-spinner\",\n dark: \"pv-spinner-dark\",\n white: \"pv-spinner-light\",\n};\n\nwithDefaults(defineProps<PvSpinnerProps>(), {\n size: \"lg\",\n variant: \"primary\",\n});\n</script>\n\n<template>\n <div data-testid=\"pv-spinner\" :class=\"mapVariantToPvClass[variant]\" :style=\"{ '--size': mapSizeToPx[size] }\" />\n</template>\n","import { PvButtonSize } from \"@/components/base/PvButton/types.ts\";\n\nexport const supportedInverseButtonVariants = [\"ghost\"];\n\nconst sizeToClassMap: Record<PvButtonSize, string | undefined> = {\n md: \"pv-button-small\",\n lg: undefined,\n xl: \"pv-button-large\",\n};\n\nexport const pvButtonSizeToClass = (size: PvButtonSize | null | undefined): string | null => {\n if (size == null || !sizeToClassMap.hasOwnProperty(size)) {\n return null;\n }\n return sizeToClassMap[size] || null;\n};\n","/**\n * Utility functions for web components\n */\n\nimport { ref, getCurrentInstance } from \"vue\";\n\n/**\n * Composable to detect if a component is running as a web component\n * Returns a reactive ref that's set to true/false on mount\n *\n * @returns Reactive ref boolean indicating if running as web component\n *\n * @example\n * import { useIsWebComponent } from '@/web-components/utils';\n *\n * export default {\n * setup() {\n * const isWc = useIsWebComponent();\n * // isWc.value is true if running as web component, false if Vue component\n * }\n * }\n */\nexport function useIsWebComponent() {\n const isWc = ref(false);\n const instance = getCurrentInstance();\n const root = instance?.root || {};\n if (\"isCE\" in root && root.isCE === true) {\n isWc.value = true;\n }\n return isWc;\n}\n","export const PvSizes = [\"xs\", \"sm\", \"md\", \"lg\", \"xl\", \"2x\"] as const;\nexport type PvSize = (typeof PvSizes)[number];\n\nexport const PvTheme = [\"white\", \"dark\"] as const;\nexport type PvThemes = (typeof PvTheme)[number];\n\nexport const PvVariants = [\"primary\", \"secondary\", \"tertiary\", \"ghost\", \"destructive\", \"transparent\"] as const;\nexport type PvVariants = (typeof PvVariants)[number];\n\nexport const PvColorVariants = [\"default\", \"success\", \"warning\", \"critical\", \"highlight\"] as const;\nexport type PvColorVariants = (typeof PvColorVariants)[number];\n\nexport const PvInputVariants = [\"h1\", \"h2\", \"h3\", \"h4\", \"text-md\", \"text-sm\", \"text-lg\", \"caption\"] as const;\nexport type PvInputVariants = (typeof PvInputVariants)[number];\n\nexport const PvTextInputColors = [\"white\", \"grey\"] as const;\nexport type PvTextInputColors = (typeof PvTextInputColors)[number];\n\nexport const PvOrientations = [\"vertical\", \"horizontal\"] as const;\nexport type PvOrientations = (typeof PvOrientations)[number];\n\nexport const sizeMap: Record<PvSize, string> = {\n xs: \"12px\",\n sm: \"16px\",\n md: \"20px\",\n lg: \"24px\",\n xl: \"32px\",\n \"2x\": \"40px\",\n};\n\nexport const PvIconSizes = [undefined, 10, 12, 20, 24, 32, 64] as const;\nexport type PvIconSize = (typeof PvIconSizes)[number];\n\nexport type PvAlignmentPositions =\n | \"bottom-left\"\n | \"bottom-center\"\n | \"bottom-right\"\n | \"top-left\"\n | \"top-center\"\n | \"top-right\"\n | \"center-left\"\n | \"center-right\"\n | \"right-top\"\n | \"right-bottom\";\n","<script lang=\"ts\">\nimport { PvIconSize, PvIconSizes } from \"../baseProps\";\n</script>\n<script setup lang=\"ts\">\nimport { computed, ref, Ref } from \"vue\";\nimport { useIsWebComponent } from \"@/web-components/utils\";\n\ntype PvIconProps = {\n /** Name of the icon from the SVG sprite sheet (e.g. \"user\", \"check-circle\"). */\n name: string;\n /** Pixel size of the icon. When omitted the icon inherits its container's size. */\n size?: PvIconSize;\n};\n\nconst props = defineProps<PvIconProps>();\nconst isWeb = useIsWebComponent();\n\nconst globalSpritePath: Ref<string | null> = ref(null);\n\nconst classes = computed(() => ({\n \"pv-icon\": true,\n [`pv-icon-${props.size}`]: props.size != null && PvIconSizes.includes(props.size),\n}));\n\nconst svgHref = computed(() => {\n // If the global sprite path is set, use it\n if (isWeb.value && globalSpritePath.value) {\n return `${globalSpritePath.value}#${props.name}`;\n }\n return `#${props.name}`;\n});\n\n// @ts-expect-error ignore type error for globalThis\nif (globalThis.__PV_GLOBAL_SPRITE_PATH__) {\n // We only want to use sprite paths for web components\n // @ts-expect-error ignore type error for globalThis\n globalSpritePath.value = globalThis.__PV_GLOBAL_SPRITE_PATH__;\n}\n</script>\n\n<template>\n <svg data-testid=\"pv-icon\" aria-hidden=\"true\" :class=\"classes\">\n <use :xlink:href=\"svgHref\"></use>\n </svg>\n</template>\n","<script lang=\"ts\">\nimport { PvIconSize, PvIconSizes } from \"../baseProps\";\n</script>\n<script setup lang=\"ts\">\nimport { computed, ref, Ref } from \"vue\";\nimport { useIsWebComponent } from \"@/web-components/utils\";\n\ntype PvIconProps = {\n /** Name of the icon from the SVG sprite sheet (e.g. \"user\", \"check-circle\"). */\n name: string;\n /** Pixel size of the icon. When omitted the icon inherits its container's size. */\n size?: PvIconSize;\n};\n\nconst props = defineProps<PvIconProps>();\nconst isWeb = useIsWebComponent();\n\nconst globalSpritePath: Ref<string | null> = ref(null);\n\nconst classes = computed(() => ({\n \"pv-icon\": true,\n [`pv-icon-${props.size}`]: props.size != null && PvIconSizes.includes(props.size),\n}));\n\nconst svgHref = computed(() => {\n // If the global sprite path is set, use it\n if (isWeb.value && globalSpritePath.value) {\n return `${globalSpritePath.value}#${props.name}`;\n }\n return `#${props.name}`;\n});\n\n// @ts-expect-error ignore type error for globalThis\nif (globalThis.__PV_GLOBAL_SPRITE_PATH__) {\n // We only want to use sprite paths for web components\n // @ts-expect-error ignore type error for globalThis\n globalSpritePath.value = globalThis.__PV_GLOBAL_SPRITE_PATH__;\n}\n</script>\n\n<template>\n <svg data-testid=\"pv-icon\" aria-hidden=\"true\" :class=\"classes\">\n <use :xlink:href=\"svgHref\"></use>\n </svg>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { PvCounterBadgeSize, PvCounterBadgeVariant } from \"./types\";\n\nexport interface PvCounterBadgeProps {\n /** Maximum value before showing overflow indicator (e.g. \"99+\") */\n maxValue?: number;\n /** Text prefix displayed before the value */\n prefix?: string;\n /** Display size of the badge */\n size?: PvCounterBadgeSize;\n /** Numeric value to display */\n value?: number;\n /** Visual style variant */\n variant?: PvCounterBadgeVariant;\n /** Fully rounded */\n rounded?: boolean;\n}\n\nconst props = withDefaults(defineProps<PvCounterBadgeProps>(), {\n variant: \"primary\",\n maxValue: 99,\n size: \"sm\",\n prefix: \"\",\n});\n\nconst displayValue = computed(() => {\n if (props.maxValue && props.value && props.value > props.maxValue) {\n return `${props.maxValue}+`;\n }\n if (props.value != null) {\n return `${props.prefix}${props.value}`;\n }\n return \"-\";\n});\n\nconst classes = computed(() => ({\n \"pv-inline-block pv-inset-square pv-radius pv-text-center\": true,\n \"pv-badge-md pv-text-body-md\": props.size == \"md\",\n \"pv-badge-sm pv-text-body-sm\": props.size == \"sm\",\n \"pv-surface-brand-inverse pv-text-inverse\": props.variant == \"primary\",\n \"pv-badge-secondary\": props.variant == \"secondary\",\n \"pv-surface-lighten-5\": props.variant == \"tertiary\",\n \"pv-text-subdued\": props.variant == \"ghost\" || props.variant == \"tertiary\",\n \"pv-surface\": props.variant == \"ghost\",\n \"pv-rounded\": props.rounded,\n}));\n</script>\n<template>\n <div :class=\"classes\" data-testid=\"pv-counter-badge\">\n {{ displayValue }}\n </div>\n</template>\n\n<style scoped>\n.pv-badge-md {\n --inset-size: 2px 2px;\n min-height: 20px;\n max-height: 20px;\n min-width: 20px;\n font-weight: 500;\n}\n.pv-badge-sm {\n --inset-size: 0 2px;\n min-height: 16px;\n max-height: 16px;\n min-width: 16px;\n font-weight: 500;\n}\n.pv-rounded {\n border-radius: 50%;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { PvCounterBadgeSize, PvCounterBadgeVariant } from \"./types\";\n\nexport interface PvCounterBadgeProps {\n /** Maximum value before showing overflow indicator (e.g. \"99+\") */\n maxValue?: number;\n /** Text prefix displayed before the value */\n prefix?: string;\n /** Display size of the badge */\n size?: PvCounterBadgeSize;\n /** Numeric value to display */\n value?: number;\n /** Visual style variant */\n variant?: PvCounterBadgeVariant;\n /** Fully rounded */\n rounded?: boolean;\n}\n\nconst props = withDefaults(defineProps<PvCounterBadgeProps>(), {\n variant: \"primary\",\n maxValue: 99,\n size: \"sm\",\n prefix: \"\",\n});\n\nconst displayValue = computed(() => {\n if (props.maxValue && props.value && props.value > props.maxValue) {\n return `${props.maxValue}+`;\n }\n if (props.value != null) {\n return `${props.prefix}${props.value}`;\n }\n return \"-\";\n});\n\nconst classes = computed(() => ({\n \"pv-inline-block pv-inset-square pv-radius pv-text-center\": true,\n \"pv-badge-md pv-text-body-md\": props.size == \"md\",\n \"pv-badge-sm pv-text-body-sm\": props.size == \"sm\",\n \"pv-surface-brand-inverse pv-text-inverse\": props.variant == \"primary\",\n \"pv-badge-secondary\": props.variant == \"secondary\",\n \"pv-surface-lighten-5\": props.variant == \"tertiary\",\n \"pv-text-subdued\": props.variant == \"ghost\" || props.variant == \"tertiary\",\n \"pv-surface\": props.variant == \"ghost\",\n \"pv-rounded\": props.rounded,\n}));\n</script>\n<template>\n <div :class=\"classes\" data-testid=\"pv-counter-badge\">\n {{ displayValue }}\n </div>\n</template>\n\n<style scoped>\n.pv-badge-md {\n --inset-size: 2px 2px;\n min-height: 20px;\n max-height: 20px;\n min-width: 20px;\n font-weight: 500;\n}\n.pv-badge-sm {\n --inset-size: 0 2px;\n min-height: 16px;\n max-height: 16px;\n min-width: 16px;\n font-weight: 500;\n}\n.pv-rounded {\n border-radius: 50%;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { PvButtonProps } from \"./types\";\nimport PvSpinner from \"../PvSpinner/PvSpinner.vue\";\nimport { pvButtonSizeToClass, supportedInverseButtonVariants } from \"@/components/base/PvButton/helpers.ts\";\nimport PvIcon from \"../PvIcon/PvIcon.vue\";\nimport PvCounterBadge from \"../PvCounterBadge/PvCounterBadge.vue\";\n\nconst props = withDefaults(defineProps<PvButtonProps>(), {\n variant: \"primary\",\n disabled: false,\n size: \"lg\",\n loading: false,\n inverse: false,\n});\n\nconst computedAriaLabel = computed(() => (!props.label || props.loading ? props.label || props.ariaLabel : undefined));\n\nconst classes = computed(() => {\n const output = [];\n if (props.inverse && supportedInverseButtonVariants.includes(props.variant)) {\n output.push(`pv-button-${props.variant}-inverse`);\n } else {\n output.push(`pv-button-${props.variant}`);\n }\n const sizeClass = pvButtonSizeToClass(props.size);\n if (sizeClass) {\n output.push(sizeClass);\n }\n return output;\n});\n</script>\n\n<template>\n <button type=\"button\" :class=\"classes\" :disabled=\"disabled\" :aria-label=\"computedAriaLabel\" data-testid=\"pv-button\">\n <PvSpinner v-if=\"loading\" size=\"sm\" />\n <template v-else>\n <PvCounterBadge v-if=\"leftCounterBadge\" :value=\"leftCounterBadge\" variant=\"tertiary\" />\n <PvIcon v-if=\"leftIcon\" :name=\"leftIcon\" data-testid=\"pv-button-left-icon\" />\n <span v-if=\"label\" data-testid=\"pv-button-label\">{{ label }}</span>\n <PvCounterBadge v-if=\"rightCounterBadge\" :value=\"rightCounterBadge\" variant=\"tertiary\" />\n <PvIcon v-if=\"rightIcon\" :name=\"rightIcon\" data-testid=\"pv-button-right-icon\" />\n </template>\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { PvButtonProps } from \"./types\";\nimport PvSpinner from \"../PvSpinner/PvSpinner.vue\";\nimport { pvButtonSizeToClass, supportedInverseButtonVariants } from \"@/components/base/PvButton/helpers.ts\";\nimport PvIcon from \"../PvIcon/PvIcon.vue\";\nimport PvCounterBadge from \"../PvCounterBadge/PvCounterBadge.vue\";\n\nconst props = withDefaults(defineProps<PvButtonProps>(), {\n variant: \"primary\",\n disabled: false,\n size: \"lg\",\n loading: false,\n inverse: false,\n});\n\nconst computedAriaLabel = computed(() => (!props.label || props.loading ? props.label || props.ariaLabel : undefined));\n\nconst classes = computed(() => {\n const output = [];\n if (props.inverse && supportedInverseButtonVariants.includes(props.variant)) {\n output.push(`pv-button-${props.variant}-inverse`);\n } else {\n output.push(`pv-button-${props.variant}`);\n }\n const sizeClass = pvButtonSizeToClass(props.size);\n if (sizeClass) {\n output.push(sizeClass);\n }\n return output;\n});\n</script>\n\n<template>\n <button type=\"button\" :class=\"classes\" :disabled=\"disabled\" :aria-label=\"computedAriaLabel\" data-testid=\"pv-button\">\n <PvSpinner v-if=\"loading\" size=\"sm\" />\n <template v-else>\n <PvCounterBadge v-if=\"leftCounterBadge\" :value=\"leftCounterBadge\" variant=\"tertiary\" />\n <PvIcon v-if=\"leftIcon\" :name=\"leftIcon\" data-testid=\"pv-button-left-icon\" />\n <span v-if=\"label\" data-testid=\"pv-button-label\">{{ label }}</span>\n <PvCounterBadge v-if=\"rightCounterBadge\" :value=\"rightCounterBadge\" variant=\"tertiary\" />\n <PvIcon v-if=\"rightIcon\" :name=\"rightIcon\" data-testid=\"pv-button-right-icon\" />\n </template>\n </button>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { PvInputType, PvInputVariant } from \"./types\";\nimport PvIcon from \"../PvIcon/PvIcon.vue\";\ninterface PvInputProps {\n /** When true, upper label is visible */\n showLabel?: boolean;\n /** When true, applies invalid/error styling to the input and lower label. */\n error?: boolean;\n /** When true, the input is non-interactive and visually dimmed. */\n disabled?: boolean;\n /** Placeholder text shown when the input is empty. */\n placeholder?: string;\n /**\n * The HTML input type.\n */\n type?: PvInputType;\n /** HTML id for the input element. Auto-generated when omitted. */\n id?: string;\n /** Helper or validation text displayed below the input. Turns red when error is true. */\n lowerLabel?: string;\n /**\n * Background color variant of the input field.\n */\n variant?: PvInputVariant;\n /** The step attribute for number inputs (e.g. '0.01' for currency). */\n step?: string;\n /** Static text shown before the Input */\n prefixLabel?: string;\n /** Optional Icon on the left of the Input */\n inputIcon?: string;\n}\n\nconst props = withDefaults(defineProps<PvInputProps>(), {\n id: Math.random().toString(),\n type: \"text\",\n variant: \"white\",\n});\n\nconst value = defineModel<string>();\n\nconst inputPadding = computed(() => {\n if (props.inputIcon) {\n return {\n \"--inset-size\": \"8px 12px 6px 36px\",\n };\n }\n\n return {};\n});\n</script>\n\n<template>\n <label v-if=\"$slots.label || showLabel\" data-testid=\"pv-input-label\" class=\"pv-label pv-stack-8\" :for=\"id\">\n <slot name=\"label\" />\n </label>\n <div\n class=\"pv-flex pv-flex-item\"\n :class=\"{\n 'pv-stack-8': lowerLabel,\n }\"\n >\n <div\n v-if=\"prefixLabel\"\n class=\"pv-surface-accent pv-inset-square pv-text-body-md pv-radius pv-bordered\"\n style=\"--inset-size: 6px 10px\"\n >\n {{ prefixLabel }}\n </div>\n <div class=\"pv-relative pv-flex-item\">\n <PvIcon class=\"pv-absolute input-icon-position\" v-if=\"inputIcon\" :name=\"inputIcon\" />\n <input\n v-bind=\"$attrs\"\n v-model=\"value\"\n data-testid=\"pv-input\"\n :class=\"[\n 'pv-input-text',\n {\n 'pv-inset-square': inputIcon,\n 'pv-surface-accent': variant === 'grey',\n },\n ]\"\n :style=\"[inputPadding, { width: '100%' }]\"\n :disabled=\"disabled\"\n :type=\"type\"\n :placeholder=\"placeholder\"\n :id=\"id\"\n :data-invalid=\"error ? true : undefined\"\n :step=\"step\"\n />\n </div>\n </div>\n <p\n v-if=\"lowerLabel\"\n data-testid=\"pv-input-lower-label\"\n :class=\"[\n 'pv-text-body-xs',\n {\n 'pv-text-red': error,\n 'pv-text-subdued': !error,\n },\n ]\"\n >\n {{ lowerLabel }}\n </p>\n</template>\n\n<style>\n.input-icon-position {\n position: absolute;\n left: 12px;\n top: 8px;\n pointer-events: none;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\nimport { PvInputType, PvInputVariant } from \"./types\";\nimport PvIcon from \"../PvIcon/PvIcon.vue\";\ninterface PvInputProps {\n /** When true, upper label is visible */\n showLabel?: boolean;\n /** When true, applies invalid/error styling to the input and lower label. */\n error?: boolean;\n /** When true, the input is non-interactive and visually dimmed. */\n disabled?: boolean;\n /** Placeholder text shown when the input is empty. */\n placeholder?: string;\n /**\n * The HTML input type.\n */\n type?: PvInputType;\n /** HTML id for the input element. Auto-generated when omitted. */\n id?: string;\n /** Helper or validation text displayed below the input. Turns red when error is true. */\n lowerLabel?: string;\n /**\n * Background color variant of the input field.\n */\n variant?: PvInputVariant;\n /** The step attribute for number inputs (e.g. '0.01' for currency). */\n step?: string;\n /** Static text shown before the Input */\n prefixLabel?: string;\n /** Optional Icon on the left of the Input */\n inputIcon?: string;\n}\n\nconst props = withDefaults(defineProps<PvInputProps>(), {\n id: Math.random().toString(),\n type: \"text\",\n variant: \"white\",\n});\n\nconst value = defineModel<string>();\n\nconst inputPadding = computed(() => {\n if (props.inputIcon) {\n return {\n \"--inset-size\": \"8px 12px 6px 36px\",\n };\n }\n\n return {};\n});\n</script>\n\n<template>\n <label v-if=\"$slots.label || showLabel\" data-testid=\"pv-input-label\" class=\"pv-label pv-stack-8\" :for=\"id\">\n <slot name=\"label\" />\n </label>\n <div\n class=\"pv-flex pv-flex-item\"\n :class=\"{\n 'pv-stack-8': lowerLabel,\n }\"\n >\n <div\n v-if=\"prefixLabel\"\n class=\"pv-surface-accent pv-inset-square pv-text-body-md pv-radius pv-bordered\"\n style=\"--inset-size: 6px 10px\"\n >\n {{ prefixLabel }}\n </div>\n <div class=\"pv-relative pv-flex-item\">\n <PvIcon class=\"pv-absolute input-icon-position\" v-if=\"inputIcon\" :name=\"inputIcon\" />\n <input\n v-bind=\"$attrs\"\n v-model=\"value\"\n data-testid=\"pv-input\"\n :class=\"[\n 'pv-input-text',\n {\n 'pv-inset-square': inputIcon,\n 'pv-surface-accent': variant === 'grey',\n },\n ]\"\n :style=\"[inputPadding, { width: '100%' }]\"\n :disabled=\"disabled\"\n :type=\"type\"\n :placeholder=\"placeholder\"\n :id=\"id\"\n :data-invalid=\"error ? true : undefined\"\n :step=\"step\"\n />\n </div>\n </div>\n <p\n v-if=\"lowerLabel\"\n data-testid=\"pv-input-lower-label\"\n :class=\"[\n 'pv-text-body-xs',\n {\n 'pv-text-red': error,\n 'pv-text-subdued': !error,\n },\n ]\"\n >\n {{ lowerLabel }}\n </p>\n</template>\n\n<style>\n.input-icon-position {\n position: absolute;\n left: 12px;\n top: 8px;\n pointer-events: none;\n}\n</style>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref, useId, watch } from \"vue\";\n\nimport PvButton from \"@/components/base/PvButton/PvButton.vue\";\nimport PvCounterBadge from \"@/components/base/PvCounterBadge/PvCounterBadge.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\nimport PvInput from \"@/components/base/PvInput/PvInput.vue\";\n\nimport type { PvQuestionOption, PvQuestionProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvQuestionProps>(), {\n chatInputLabel: \"Other answer\",\n chatInputPlaceholder: \"Something else...\",\n currentQuestion: 1,\n headerLabel: \"Question\",\n options: () => [],\n subheader: \"\",\n totalQuestions: 1,\n});\n\nconst emit = defineEmits<{\n /** The close (X) button was pressed to dismiss the question. */\n (event: \"close\"): void;\n /** An option was chosen from the list. */\n (event: \"select\", option: PvQuestionOption): void;\n /** The question was skipped via the Skip button or the Escape key. */\n (event: \"skip\"): void;\n /** A free-text answer was submitted from the chat input. */\n (event: \"submit\", value: string): void;\n}>();\n\nconst selectedId = defineModel<string>();\nconst chatInput = defineModel<string>(\"chatInput\", { default: \"\" });\n\nconst componentId = useId();\nconst headerId = `${componentId}-heading`;\nconst hintId = `${componentId}-keyboard-hint`;\nconst listboxRef = ref<HTMLElement>();\nconst chatInputWrapperRef = ref<HTMLElement>();\nconst selectedIndex = props.options.findIndex((option) => option.id === selectedId.value);\nconst activeIndex = ref(Math.max(selectedIndex, 0));\n\nconst showQuestionCounter = computed(() => props.totalQuestions > 1);\nconst activeDescendant = computed(() =>\n activeIndex.value < props.options.length ? optionDomId(activeIndex.value) : undefined,\n);\n\nwatch(selectedId, (value) => {\n const selectedIndex = props.options.findIndex((option) => option.id === value);\n if (selectedIndex >= 0) activeIndex.value = selectedIndex;\n});\n\nwatch(\n () => props.options.map((option) => option.id),\n (optionIds, previousOptionIds) => {\n const selectedIndex = optionIds.findIndex((id) => id === selectedId.value);\n if (selectedIndex >= 0) {\n activeIndex.value = selectedIndex;\n return;\n }\n\n if (activeIndex.value === previousOptionIds.length) {\n activeIndex.value = optionIds.length;\n return;\n }\n\n const previousActiveId = previousOptionIds[activeIndex.value];\n const reorderedIndex = optionIds.indexOf(previousActiveId);\n activeIndex.value =\n reorderedIndex >= 0 ? reorderedIndex : Math.min(activeIndex.value, Math.max(optionIds.length - 1, 0));\n },\n);\n\nfunction optionDomId(index: number) {\n return `${componentId}-option-${index}`;\n}\n\nfunction focusListbox(index: number) {\n if (!props.options.length) return;\n\n activeIndex.value = index;\n nextTick(() => listboxRef.value?.focus());\n}\n\nfunction focusChatInput() {\n activeIndex.value = props.options.length;\n nextTick(() => chatInputWrapperRef.value?.querySelector<HTMLInputElement>(\"input\")?.focus());\n}\n\nfunction selectOption(option: PvQuestionOption, index: number) {\n activeIndex.value = index;\n selectedId.value = option.id;\n emit(\"select\", option);\n}\n\nfunction handleOptionClick(option: PvQuestionOption, index: number) {\n selectOption(option, index);\n nextTick(() => listboxRef.value?.focus());\n}\n\nfunction handleListboxFocus() {\n if (activeIndex.value >= props.options.length) activeIndex.value = 0;\n}\n\nfunction handleListboxKeydown(event: KeyboardEvent) {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n if (activeIndex.value >= props.options.length - 1) {\n focusChatInput();\n } else {\n activeIndex.value += 1;\n }\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n if (activeIndex.value <= 0) {\n focusChatInput();\n } else {\n activeIndex.value -= 1;\n }\n } else if (event.key === \"Enter\" && props.options[activeIndex.value]) {\n event.preventDefault();\n selectOption(props.options[activeIndex.value], activeIndex.value);\n }\n}\n\nfunction handleChatFocus() {\n activeIndex.value = props.options.length;\n // The free-text answer replaces any option answer, so drop the previous selection.\n selectedId.value = undefined;\n}\n\nfunction handleChatKeydown(event: KeyboardEvent) {\n if (!props.options.length && (event.key === \"ArrowDown\" || event.key === \"ArrowUp\")) return;\n\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n focusListbox(0);\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n focusListbox(props.options.length - 1);\n } else if (event.key === \"Enter\" && !event.isComposing) {\n event.preventDefault();\n const value = chatInput.value.trim();\n if (value) emit(\"submit\", value);\n }\n}\n\nfunction handleClose() {\n emit(\"close\");\n}\n\nfunction handleSkip() {\n emit(\"skip\");\n}\n</script>\n\n<template>\n <section\n class=\"pv-question pv-card pv-surface pv-full-width\"\n data-testid=\"pv-question\"\n :aria-labelledby=\"headerId\"\n @keydown.esc.stop.prevent=\"handleSkip\"\n >\n <header class=\"pv-question-header pv-inset-square-16 pv-space-between\">\n <div\n class=\"pv-question-header-copy pv-flex-vertical pv-flex-item\"\n style=\"--flex-align: flex-start; --flex-gap: 4px; min-width: 0\"\n >\n <h3 :id=\"headerId\" class=\"pv-heading-3\">{{ headerLabel }}</h3>\n <p v-if=\"subheader\" class=\"pv-text-body-md pv-text-tertiary\">{{ subheader }}</p>\n </div>\n <div class=\"pv-question-header-actions pv-flex\">\n <span v-if=\"showQuestionCounter\" class=\"pv-text-body-xs pv-text-tertiary\" role=\"status\" aria-live=\"polite\">\n {{ currentQuestion }} of {{ totalQuestions }}\n </span>\n <PvButton ariaLabel=\"Close\" leftIcon=\"close\" size=\"md\" variant=\"ghost\" @click=\"handleClose\" />\n </div>\n </header>\n\n <div class=\"pv-question-body pv-border-bottom\" style=\"padding-bottom: 2px\">\n <div\n class=\"pv-question-options pv-flex-vertical pv-inset-inline-16\"\n style=\"--flex-align: stretch; --flex-gap: 2px\"\n >\n <div\n ref=\"listboxRef\"\n class=\"pv-question-listbox pv-flex-vertical\"\n style=\"--flex-align: stretch; --flex-gap: 2px\"\n data-testid=\"pv-question-listbox\"\n role=\"listbox\"\n tabindex=\"0\"\n :aria-activedescendant=\"activeDescendant\"\n :aria-describedby=\"hintId\"\n :aria-labelledby=\"headerId\"\n @focus=\"handleListboxFocus\"\n @keydown=\"handleListboxKeydown\"\n >\n <div\n v-for=\"(option, index) in options\"\n :id=\"optionDomId(index)\"\n :key=\"option.id\"\n class=\"pv-question-option pv-flex pv-inset-squish-12 pv-radius-lg\"\n data-testid=\"pv-question-option\"\n role=\"option\"\n :aria-selected=\"selectedId === option.id\"\n :data-active=\"activeIndex === index || undefined\"\n @click=\"handleOptionClick(option, index)\"\n @mouseenter=\"activeIndex = index\"\n >\n <PvCounterBadge :value=\"index + 1\" size=\"sm\" variant=\"tertiary\" />\n <span class=\"pv-question-answer-text pv-flex-item pv-text-title-md\" style=\"min-width: 0\">{{\n option.label\n }}</span>\n <span\n v-if=\"option.helpText\"\n class=\"pv-question-help pv-flex\"\n style=\"--flex-gap: 0\"\n :title=\"option.helpText\"\n >\n <PvIcon class=\"pv-text-tertiary\" name=\"info-circle\" :size=\"12\" />\n <span class=\"pv-visually-hidden\">{{ option.helpText }}</span>\n </span>\n <PvIcon v-if=\"selectedId === option.id\" class=\"pv-question-check pv-text-brand\" name=\"check\" />\n <PvIcon class=\"pv-question-chevron pv-text-tertiary\" name=\"chevron-right\" />\n </div>\n </div>\n\n <div\n ref=\"chatInputWrapperRef\"\n class=\"pv-question-chat pv-flex pv-inset-squish-12 pv-radius-lg\"\n data-testid=\"pv-question-chat\"\n :data-active=\"activeIndex === options.length || undefined\"\n @click=\"focusChatInput\"\n @mouseenter=\"activeIndex = options.length\"\n >\n <span class=\"pv-question-chat-badge pv-flex pv-radius pv-text-subdued\">\n <PvIcon name=\"text-search\" :size=\"12\" />\n </span>\n <div class=\"pv-question-chat-input pv-flex-item\" style=\"min-width: 0\">\n <PvInput\n v-model=\"chatInput\"\n :aria-label=\"chatInputLabel\"\n :placeholder=\"chatInputPlaceholder\"\n @focus=\"handleChatFocus\"\n @keydown=\"handleChatKeydown\"\n />\n </div>\n <PvIcon class=\"pv-question-chevron pv-text-tertiary\" name=\"chevron-right\" />\n </div>\n </div>\n </div>\n\n <footer class=\"pv-question-footer pv-inset-squish-12 pv-space-between\">\n <span :id=\"hintId\" class=\"pv-text-body-xs pv-text-tertiary\">↑↓ to navigate • Enter to select • Esc to skip</span>\n <PvButton class=\"pv-question-skip\" label=\"Skip\" size=\"lg\" variant=\"secondary\" @click=\"handleSkip\" />\n </footer>\n </section>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, nextTick, ref, useId, watch } from \"vue\";\n\nimport PvButton from \"@/components/base/PvButton/PvButton.vue\";\nimport PvCounterBadge from \"@/components/base/PvCounterBadge/PvCounterBadge.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\nimport PvInput from \"@/components/base/PvInput/PvInput.vue\";\n\nimport type { PvQuestionOption, PvQuestionProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvQuestionProps>(), {\n chatInputLabel: \"Other answer\",\n chatInputPlaceholder: \"Something else...\",\n currentQuestion: 1,\n headerLabel: \"Question\",\n options: () => [],\n subheader: \"\",\n totalQuestions: 1,\n});\n\nconst emit = defineEmits<{\n /** The close (X) button was pressed to dismiss the question. */\n (event: \"close\"): void;\n /** An option was chosen from the list. */\n (event: \"select\", option: PvQuestionOption): void;\n /** The question was skipped via the Skip button or the Escape key. */\n (event: \"skip\"): void;\n /** A free-text answer was submitted from the chat input. */\n (event: \"submit\", value: string): void;\n}>();\n\nconst selectedId = defineModel<string>();\nconst chatInput = defineModel<string>(\"chatInput\", { default: \"\" });\n\nconst componentId = useId();\nconst headerId = `${componentId}-heading`;\nconst hintId = `${componentId}-keyboard-hint`;\nconst listboxRef = ref<HTMLElement>();\nconst chatInputWrapperRef = ref<HTMLElement>();\nconst selectedIndex = props.options.findIndex((option) => option.id === selectedId.value);\nconst activeIndex = ref(Math.max(selectedIndex, 0));\n\nconst showQuestionCounter = computed(() => props.totalQuestions > 1);\nconst activeDescendant = computed(() =>\n activeIndex.value < props.options.length ? optionDomId(activeIndex.value) : undefined,\n);\n\nwatch(selectedId, (value) => {\n const selectedIndex = props.options.findIndex((option) => option.id === value);\n if (selectedIndex >= 0) activeIndex.value = selectedIndex;\n});\n\nwatch(\n () => props.options.map((option) => option.id),\n (optionIds, previousOptionIds) => {\n const selectedIndex = optionIds.findIndex((id) => id === selectedId.value);\n if (selectedIndex >= 0) {\n activeIndex.value = selectedIndex;\n return;\n }\n\n if (activeIndex.value === previousOptionIds.length) {\n activeIndex.value = optionIds.length;\n return;\n }\n\n const previousActiveId = previousOptionIds[activeIndex.value];\n const reorderedIndex = optionIds.indexOf(previousActiveId);\n activeIndex.value =\n reorderedIndex >= 0 ? reorderedIndex : Math.min(activeIndex.value, Math.max(optionIds.length - 1, 0));\n },\n);\n\nfunction optionDomId(index: number) {\n return `${componentId}-option-${index}`;\n}\n\nfunction focusListbox(index: number) {\n if (!props.options.length) return;\n\n activeIndex.value = index;\n nextTick(() => listboxRef.value?.focus());\n}\n\nfunction focusChatInput() {\n activeIndex.value = props.options.length;\n nextTick(() => chatInputWrapperRef.value?.querySelector<HTMLInputElement>(\"input\")?.focus());\n}\n\nfunction selectOption(option: PvQuestionOption, index: number) {\n activeIndex.value = index;\n selectedId.value = option.id;\n emit(\"select\", option);\n}\n\nfunction handleOptionClick(option: PvQuestionOption, index: number) {\n selectOption(option, index);\n nextTick(() => listboxRef.value?.focus());\n}\n\nfunction handleListboxFocus() {\n if (activeIndex.value >= props.options.length) activeIndex.value = 0;\n}\n\nfunction handleListboxKeydown(event: KeyboardEvent) {\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n if (activeIndex.value >= props.options.length - 1) {\n focusChatInput();\n } else {\n activeIndex.value += 1;\n }\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n if (activeIndex.value <= 0) {\n focusChatInput();\n } else {\n activeIndex.value -= 1;\n }\n } else if (event.key === \"Enter\" && props.options[activeIndex.value]) {\n event.preventDefault();\n selectOption(props.options[activeIndex.value], activeIndex.value);\n }\n}\n\nfunction handleChatFocus() {\n activeIndex.value = props.options.length;\n // The free-text answer replaces any option answer, so drop the previous selection.\n selectedId.value = undefined;\n}\n\nfunction handleChatKeydown(event: KeyboardEvent) {\n if (!props.options.length && (event.key === \"ArrowDown\" || event.key === \"ArrowUp\")) return;\n\n if (event.key === \"ArrowDown\") {\n event.preventDefault();\n focusListbox(0);\n } else if (event.key === \"ArrowUp\") {\n event.preventDefault();\n focusListbox(props.options.length - 1);\n } else if (event.key === \"Enter\" && !event.isComposing) {\n event.preventDefault();\n const value = chatInput.value.trim();\n if (value) emit(\"submit\", value);\n }\n}\n\nfunction handleClose() {\n emit(\"close\");\n}\n\nfunction handleSkip() {\n emit(\"skip\");\n}\n</script>\n\n<template>\n <section\n class=\"pv-question pv-card pv-surface pv-full-width\"\n data-testid=\"pv-question\"\n :aria-labelledby=\"headerId\"\n @keydown.esc.stop.prevent=\"handleSkip\"\n >\n <header class=\"pv-question-header pv-inset-square-16 pv-space-between\">\n <div\n class=\"pv-question-header-copy pv-flex-vertical pv-flex-item\"\n style=\"--flex-align: flex-start; --flex-gap: 4px; min-width: 0\"\n >\n <h3 :id=\"headerId\" class=\"pv-heading-3\">{{ headerLabel }}</h3>\n <p v-if=\"subheader\" class=\"pv-text-body-md pv-text-tertiary\">{{ subheader }}</p>\n </div>\n <div class=\"pv-question-header-actions pv-flex\">\n <span v-if=\"showQuestionCounter\" class=\"pv-text-body-xs pv-text-tertiary\" role=\"status\" aria-live=\"polite\">\n {{ currentQuestion }} of {{ totalQuestions }}\n </span>\n <PvButton ariaLabel=\"Close\" leftIcon=\"close\" size=\"md\" variant=\"ghost\" @click=\"handleClose\" />\n </div>\n </header>\n\n <div class=\"pv-question-body pv-border-bottom\" style=\"padding-bottom: 2px\">\n <div\n class=\"pv-question-options pv-flex-vertical pv-inset-inline-16\"\n style=\"--flex-align: stretch; --flex-gap: 2px\"\n >\n <div\n ref=\"listboxRef\"\n class=\"pv-question-listbox pv-flex-vertical\"\n style=\"--flex-align: stretch; --flex-gap: 2px\"\n data-testid=\"pv-question-listbox\"\n role=\"listbox\"\n tabindex=\"0\"\n :aria-activedescendant=\"activeDescendant\"\n :aria-describedby=\"hintId\"\n :aria-labelledby=\"headerId\"\n @focus=\"handleListboxFocus\"\n @keydown=\"handleListboxKeydown\"\n >\n <div\n v-for=\"(option, index) in options\"\n :id=\"optionDomId(index)\"\n :key=\"option.id\"\n class=\"pv-question-option pv-flex pv-inset-squish-12 pv-radius-lg\"\n data-testid=\"pv-question-option\"\n role=\"option\"\n :aria-selected=\"selectedId === option.id\"\n :data-active=\"activeIndex === index || undefined\"\n @click=\"handleOptionClick(option, index)\"\n @mouseenter=\"activeIndex = index\"\n >\n <PvCounterBadge :value=\"index + 1\" size=\"sm\" variant=\"tertiary\" />\n <span class=\"pv-question-answer-text pv-flex-item pv-text-title-md\" style=\"min-width: 0\">{{\n option.label\n }}</span>\n <span\n v-if=\"option.helpText\"\n class=\"pv-question-help pv-flex\"\n style=\"--flex-gap: 0\"\n :title=\"option.helpText\"\n >\n <PvIcon class=\"pv-text-tertiary\" name=\"info-circle\" :size=\"12\" />\n <span class=\"pv-visually-hidden\">{{ option.helpText }}</span>\n </span>\n <PvIcon v-if=\"selectedId === option.id\" class=\"pv-question-check pv-text-brand\" name=\"check\" />\n <PvIcon class=\"pv-question-chevron pv-text-tertiary\" name=\"chevron-right\" />\n </div>\n </div>\n\n <div\n ref=\"chatInputWrapperRef\"\n class=\"pv-question-chat pv-flex pv-inset-squish-12 pv-radius-lg\"\n data-testid=\"pv-question-chat\"\n :data-active=\"activeIndex === options.length || undefined\"\n @click=\"focusChatInput\"\n @mouseenter=\"activeIndex = options.length\"\n >\n <span class=\"pv-question-chat-badge pv-flex pv-radius pv-text-subdued\">\n <PvIcon name=\"text-search\" :size=\"12\" />\n </span>\n <div class=\"pv-question-chat-input pv-flex-item\" style=\"min-width: 0\">\n <PvInput\n v-model=\"chatInput\"\n :aria-label=\"chatInputLabel\"\n :placeholder=\"chatInputPlaceholder\"\n @focus=\"handleChatFocus\"\n @keydown=\"handleChatKeydown\"\n />\n </div>\n <PvIcon class=\"pv-question-chevron pv-text-tertiary\" name=\"chevron-right\" />\n </div>\n </div>\n </div>\n\n <footer class=\"pv-question-footer pv-inset-squish-12 pv-space-between\">\n <span :id=\"hintId\" class=\"pv-text-body-xs pv-text-tertiary\">↑↓ to navigate • Enter to select • Esc to skip</span>\n <PvButton class=\"pv-question-skip\" label=\"Skip\" size=\"lg\" variant=\"secondary\" @click=\"handleSkip\" />\n </footer>\n </section>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport PvButton from \"@/components/base/PvButton/PvButton.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\n\nimport type { PvConfirmationProps, PvConfirmationResponse } from \"./types\";\n\nconst props = withDefaults(defineProps<PvConfirmationProps>(), {\n approveLabel: \"Approve\",\n disabled: false,\n message: \"This tool requires your approval before it can run.\",\n rejectLabel: \"Reject\",\n});\n\nconst emit = defineEmits<{\n /** The user approved or rejected the tool execution. */\n (event: \"respond\", response: PvConfirmationResponse): void;\n}>();\n\nconst isPending = computed(\n () => props.state === \"approval-requested\" && props.approval !== undefined && props.approval.approved === undefined,\n);\nconst isApproved = computed(\n () =>\n props.approval?.approved === true &&\n (props.state === \"approval-responded\" || props.state === \"input-available\" || props.state === \"output-available\"),\n);\nconst isRejected = computed(() => props.approval?.approved === false || props.state === \"output-denied\");\n\nfunction respond(approved: boolean) {\n emit(\"respond\", { id: props.approval!.id, approved });\n}\n</script>\n\n<template>\n <section\n v-if=\"isPending || isApproved || isRejected\"\n class=\"pv-confirmation pv-bordered pv-radius pv-inset-square-16 pv-flow-16\"\n data-testid=\"pv-confirmation\"\n aria-label=\"Tool approval\"\n >\n <template v-if=\"isPending\">\n <div class=\"pv-text-body-sm\">\n <slot>{{ message }}</slot>\n </div>\n <div class=\"pv-flex\" style=\"--flex-justify: flex-end\">\n <PvButton variant=\"secondary\" size=\"md\" :label=\"rejectLabel\" :disabled=\"disabled\" @click=\"respond(false)\" />\n <PvButton size=\"md\" :label=\"approveLabel\" :disabled=\"disabled\" @click=\"respond(true)\" />\n </div>\n </template>\n\n <p v-else-if=\"isApproved\" class=\"pv-flex pv-text-body-sm pv-text-success\" role=\"status\">\n <PvIcon name=\"check-circle\" :size=\"20\" />\n <slot name=\"accepted\">You approved this tool execution.</slot>\n </p>\n\n <p v-else class=\"pv-flex pv-text-body-sm pv-text-critical\" role=\"status\">\n <PvIcon name=\"close-circle\" :size=\"20\" />\n <slot name=\"rejected\">You rejected this tool execution.</slot>\n </p>\n </section>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport PvButton from \"@/components/base/PvButton/PvButton.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\n\nimport type { PvConfirmationProps, PvConfirmationResponse } from \"./types\";\n\nconst props = withDefaults(defineProps<PvConfirmationProps>(), {\n approveLabel: \"Approve\",\n disabled: false,\n message: \"This tool requires your approval before it can run.\",\n rejectLabel: \"Reject\",\n});\n\nconst emit = defineEmits<{\n /** The user approved or rejected the tool execution. */\n (event: \"respond\", response: PvConfirmationResponse): void;\n}>();\n\nconst isPending = computed(\n () => props.state === \"approval-requested\" && props.approval !== undefined && props.approval.approved === undefined,\n);\nconst isApproved = computed(\n () =>\n props.approval?.approved === true &&\n (props.state === \"approval-responded\" || props.state === \"input-available\" || props.state === \"output-available\"),\n);\nconst isRejected = computed(() => props.approval?.approved === false || props.state === \"output-denied\");\n\nfunction respond(approved: boolean) {\n emit(\"respond\", { id: props.approval!.id, approved });\n}\n</script>\n\n<template>\n <section\n v-if=\"isPending || isApproved || isRejected\"\n class=\"pv-confirmation pv-bordered pv-radius pv-inset-square-16 pv-flow-16\"\n data-testid=\"pv-confirmation\"\n aria-label=\"Tool approval\"\n >\n <template v-if=\"isPending\">\n <div class=\"pv-text-body-sm\">\n <slot>{{ message }}</slot>\n </div>\n <div class=\"pv-flex\" style=\"--flex-justify: flex-end\">\n <PvButton variant=\"secondary\" size=\"md\" :label=\"rejectLabel\" :disabled=\"disabled\" @click=\"respond(false)\" />\n <PvButton size=\"md\" :label=\"approveLabel\" :disabled=\"disabled\" @click=\"respond(true)\" />\n </div>\n </template>\n\n <p v-else-if=\"isApproved\" class=\"pv-flex pv-text-body-sm pv-text-success\" role=\"status\">\n <PvIcon name=\"check-circle\" :size=\"20\" />\n <slot name=\"accepted\">You approved this tool execution.</slot>\n </p>\n\n <p v-else class=\"pv-flex pv-text-body-sm pv-text-critical\" role=\"status\">\n <PvIcon name=\"close-circle\" :size=\"20\" />\n <slot name=\"rejected\">You rejected this tool execution.</slot>\n </p>\n </section>\n</template>\n","<script setup lang=\"ts\">\nimport { PvTextAreaVariant } from \"./types\";\n\ninterface PvTextareaProps {\n /** When true, applies invalid/error styling to the textarea and lower label. */\n error?: boolean;\n /** When true, the textarea is non-interactive and visually dimmed. */\n disabled?: boolean;\n /** Placeholder text shown when the textarea is empty. */\n placeholder?: string;\n /** HTML id for the textarea element. Auto-generated when omitted. */\n id?: string;\n /** Helper or validation text displayed below the textarea. Turns red when error is true. */\n lowerLabel?: string;\n /**\n * Background color variant of the textarea.\n */\n variant?: PvTextAreaVariant;\n /**\n * Number of visible text rows.\n */\n rows?: number;\n}\n\nwithDefaults(defineProps<PvTextareaProps>(), {\n id: Math.random().toString(),\n variant: \"white\",\n rows: 4,\n});\n\nconst value = defineModel<string>();\n</script>\n\n<template>\n <div class=\"pv-flex-vertical\" style=\"--flex-align: flex-start\">\n <label v-if=\"$slots.label\" class=\"pv-label\" :for=\"id\">\n <slot name=\"label\" />\n </label>\n <textarea\n v-bind=\"$attrs\"\n v-model=\"value\"\n :class=\"[\n 'pv-textarea',\n {\n 'pv-surface-accent': variant === 'grey',\n },\n ]\"\n :rows=\"rows\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :id=\"id\"\n :data-invalid=\"error ? true : undefined\"\n />\n <p\n v-if=\"lowerLabel\"\n style=\"margin-left: 12px\"\n :class=\"[\n 'pv-text-body-xs',\n {\n 'pv-text-red': error,\n 'pv-text-subdued': !error,\n },\n ]\"\n >\n {{ lowerLabel }}\n </p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { PvTextAreaVariant } from \"./types\";\n\ninterface PvTextareaProps {\n /** When true, applies invalid/error styling to the textarea and lower label. */\n error?: boolean;\n /** When true, the textarea is non-interactive and visually dimmed. */\n disabled?: boolean;\n /** Placeholder text shown when the textarea is empty. */\n placeholder?: string;\n /** HTML id for the textarea element. Auto-generated when omitted. */\n id?: string;\n /** Helper or validation text displayed below the textarea. Turns red when error is true. */\n lowerLabel?: string;\n /**\n * Background color variant of the textarea.\n */\n variant?: PvTextAreaVariant;\n /**\n * Number of visible text rows.\n */\n rows?: number;\n}\n\nwithDefaults(defineProps<PvTextareaProps>(), {\n id: Math.random().toString(),\n variant: \"white\",\n rows: 4,\n});\n\nconst value = defineModel<string>();\n</script>\n\n<template>\n <div class=\"pv-flex-vertical\" style=\"--flex-align: flex-start\">\n <label v-if=\"$slots.label\" class=\"pv-label\" :for=\"id\">\n <slot name=\"label\" />\n </label>\n <textarea\n v-bind=\"$attrs\"\n v-model=\"value\"\n :class=\"[\n 'pv-textarea',\n {\n 'pv-surface-accent': variant === 'grey',\n },\n ]\"\n :rows=\"rows\"\n :disabled=\"disabled\"\n :placeholder=\"placeholder\"\n :id=\"id\"\n :data-invalid=\"error ? true : undefined\"\n />\n <p\n v-if=\"lowerLabel\"\n style=\"margin-left: 12px\"\n :class=\"[\n 'pv-text-body-xs',\n {\n 'pv-text-red': error,\n 'pv-text-subdued': !error,\n },\n ]\"\n >\n {{ lowerLabel }}\n </p>\n </div>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from \"vue\";\n\nimport PvButton from \"@/components/base/PvButton/PvButton.vue\";\nimport PvTextArea from \"@/components/base/PvTextArea/PvTextArea.vue\";\n\nimport type { PvPromptInputProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvPromptInputProps>(), {\n ariaLabel: \"Message\",\n disabled: false,\n maxHeight: \"200px\",\n placeholder: \"Ask anything...\",\n rows: 1,\n status: \"ready\",\n stopLabel: \"Stop generating\",\n submitLabel: \"Send message\",\n});\n\nconst emit = defineEmits<{\n /** A non-empty, trimmed prompt was submitted. */\n (event: \"submit\", value: string): void;\n /** The stop button was pressed while a response was streaming. */\n (event: \"stop\"): void;\n}>();\n\nconst value = defineModel<string>({ default: \"\" });\nconst formRef = ref<HTMLFormElement>();\nconst isBusy = computed(() => props.status === \"submitted\" || props.status === \"streaming\");\nconst canSubmit = computed(() => !props.disabled && !isBusy.value && value.value.trim().length > 0);\n\nfunction submit() {\n if (!canSubmit.value) return;\n emit(\"submit\", value.value.trim());\n}\n\nfunction focus() {\n formRef.value?.querySelector(\"textarea\")?.focus();\n}\n\n// The textarea is only one row tall, so most of the box is empty space around it.\n// Clicking that space should put the caret in the textarea, like any other text field.\nfunction handleBoxClick(event: MouseEvent) {\n if (props.disabled) return;\n\n const target = event.target as HTMLElement | null;\n if (target?.closest(\"button, a, input, textarea, select, [tabindex]\")) return;\n\n focus();\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (event.key !== \"Enter\" || event.shiftKey || event.isComposing) return;\n event.preventDefault();\n event.stopPropagation();\n submit();\n}\n\ndefineExpose({ focus });\n</script>\n\n<template>\n <form\n ref=\"formRef\"\n class=\"pv-prompt-input pv-bordered pv-radius pv-inset-square-8 pv-flex-vertical\"\n data-testid=\"pv-prompt-input\"\n :aria-busy=\"isBusy\"\n @submit.prevent=\"submit\"\n @click=\"handleBoxClick\"\n >\n <div v-if=\"$slots.header\" class=\"pv-full-width pv-flow-8\">\n <slot name=\"header\" />\n </div>\n\n <PvTextArea\n v-model=\"value\"\n class=\"pv-prompt-input-textarea\"\n :style=\"{ '--prompt-input-max-height': maxHeight }\"\n :rows=\"rows\"\n :maxlength=\"maxLength\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n @keydown=\"handleKeydown\"\n />\n\n <div class=\"pv-prompt-input-footer pv-flex pv-full-width\">\n <div class=\"pv-prompt-input-actions pv-flex pv-flex-item\">\n <slot name=\"actions\" />\n </div>\n <PvButton\n :type=\"isBusy ? 'button' : 'submit'\"\n :variant=\"isBusy ? 'secondary' : 'primary'\"\n size=\"md\"\n :left-icon=\"isBusy ? 'stop' : 'send'\"\n :aria-label=\"isBusy ? stopLabel : submitLabel\"\n :disabled=\"disabled || (!isBusy && !canSubmit)\"\n @click=\"isBusy && emit('stop')\"\n />\n </div>\n\n <p class=\"pv-visually-hidden\" aria-live=\"polite\">\n <template v-if=\"status === 'submitted'\">Message submitted.</template>\n <template v-else-if=\"status === 'streaming'\">Response is streaming.</template>\n <template v-else-if=\"status === 'error'\">The message could not be sent.</template>\n </p>\n </form>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, ref } from \"vue\";\n\nimport PvButton from \"@/components/base/PvButton/PvButton.vue\";\nimport PvTextArea from \"@/components/base/PvTextArea/PvTextArea.vue\";\n\nimport type { PvPromptInputProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvPromptInputProps>(), {\n ariaLabel: \"Message\",\n disabled: false,\n maxHeight: \"200px\",\n placeholder: \"Ask anything...\",\n rows: 1,\n status: \"ready\",\n stopLabel: \"Stop generating\",\n submitLabel: \"Send message\",\n});\n\nconst emit = defineEmits<{\n /** A non-empty, trimmed prompt was submitted. */\n (event: \"submit\", value: string): void;\n /** The stop button was pressed while a response was streaming. */\n (event: \"stop\"): void;\n}>();\n\nconst value = defineModel<string>({ default: \"\" });\nconst formRef = ref<HTMLFormElement>();\nconst isBusy = computed(() => props.status === \"submitted\" || props.status === \"streaming\");\nconst canSubmit = computed(() => !props.disabled && !isBusy.value && value.value.trim().length > 0);\n\nfunction submit() {\n if (!canSubmit.value) return;\n emit(\"submit\", value.value.trim());\n}\n\nfunction focus() {\n formRef.value?.querySelector(\"textarea\")?.focus();\n}\n\n// The textarea is only one row tall, so most of the box is empty space around it.\n// Clicking that space should put the caret in the textarea, like any other text field.\nfunction handleBoxClick(event: MouseEvent) {\n if (props.disabled) return;\n\n const target = event.target as HTMLElement | null;\n if (target?.closest(\"button, a, input, textarea, select, [tabindex]\")) return;\n\n focus();\n}\n\nfunction handleKeydown(event: KeyboardEvent) {\n if (event.key !== \"Enter\" || event.shiftKey || event.isComposing) return;\n event.preventDefault();\n event.stopPropagation();\n submit();\n}\n\ndefineExpose({ focus });\n</script>\n\n<template>\n <form\n ref=\"formRef\"\n class=\"pv-prompt-input pv-bordered pv-radius pv-inset-square-8 pv-flex-vertical\"\n data-testid=\"pv-prompt-input\"\n :aria-busy=\"isBusy\"\n @submit.prevent=\"submit\"\n @click=\"handleBoxClick\"\n >\n <div v-if=\"$slots.header\" class=\"pv-full-width pv-flow-8\">\n <slot name=\"header\" />\n </div>\n\n <PvTextArea\n v-model=\"value\"\n class=\"pv-prompt-input-textarea\"\n :style=\"{ '--prompt-input-max-height': maxHeight }\"\n :rows=\"rows\"\n :maxlength=\"maxLength\"\n :placeholder=\"placeholder\"\n :disabled=\"disabled\"\n :aria-label=\"ariaLabel\"\n @keydown=\"handleKeydown\"\n />\n\n <div class=\"pv-prompt-input-footer pv-flex pv-full-width\">\n <div class=\"pv-prompt-input-actions pv-flex pv-flex-item\">\n <slot name=\"actions\" />\n </div>\n <PvButton\n :type=\"isBusy ? 'button' : 'submit'\"\n :variant=\"isBusy ? 'secondary' : 'primary'\"\n size=\"md\"\n :left-icon=\"isBusy ? 'stop' : 'send'\"\n :aria-label=\"isBusy ? stopLabel : submitLabel\"\n :disabled=\"disabled || (!isBusy && !canSubmit)\"\n @click=\"isBusy && emit('stop')\"\n />\n </div>\n\n <p class=\"pv-visually-hidden\" aria-live=\"polite\">\n <template v-if=\"status === 'submitted'\">Message submitted.</template>\n <template v-else-if=\"status === 'streaming'\">Response is streaming.</template>\n <template v-else-if=\"status === 'error'\">The message could not be sent.</template>\n </p>\n </form>\n</template>\n","export const promptInputStatuses = [\"ready\", \"submitted\", \"streaming\", \"error\"] as const;\n\nexport type PvPromptInputStatus = (typeof promptInputStatuses)[number];\n\nexport interface PvPromptInputProps {\n /** Accessible label for the textarea. */\n ariaLabel?: string;\n /** Disables the composer. */\n disabled?: boolean;\n /** Maximum auto-grown textarea height. */\n maxHeight?: string;\n /** Maximum number of characters the textarea accepts. Unlimited when omitted. */\n maxLength?: number;\n /** Placeholder shown when the prompt is empty. */\n placeholder?: string;\n /** Initial visible textarea rows. */\n rows?: number;\n /** Current chat request status. */\n status?: PvPromptInputStatus;\n /** Accessible label for the stop button. */\n stopLabel?: string;\n /** Accessible label for the submit button. */\n submitLabel?: string;\n}\n","<script setup lang=\"ts\">\nimport PvCounterBadge from \"../PvCounterBadge/PvCounterBadge.vue\";\nimport PvIcon from \"../PvIcon/PvIcon.vue\";\nimport { ChevronPosition } from \"./types\";\nimport { computed } from \"vue\";\nimport type { CSSProperties } from \"vue\";\n\ninterface PvAccordionProps {\n /** Position of the chevron icon relative to the header text */\n chevronPosition?: ChevronPosition;\n /** Orientation of the chevron for the open/closed states (up-down vs right-down) */\n chevronVariant?: \"vertical\" | \"horizontal\";\n /** Whether the accordion is open by default on initial render */\n defaultOpen?: boolean;\n /** Optional counter badge value displayed next to the header text */\n counter?: number;\n /** Enables a custom trigger slot instead of the default header text */\n enableTriggerSlot?: boolean;\n /** Makes the trigger element expand to fill its container width */\n enableTriggerFullWidth?: boolean;\n /** Text displayed as the accordion header */\n header?: string;\n /** Custom CSS styles applied to the summary/trigger element */\n summaryStyles?: CSSProperties;\n /** CSS class bindings applied to the summary/trigger element */\n summaryClasses?: Record<string, boolean>;\n /** Custom CSS styles applied to the content container */\n contentStyles?: CSSProperties;\n /** Additional attrs for a summary element */\n summaryAttrs?: Record<string, string | undefined>;\n /**\n * Only a click on the chevron opens/closes the accordion. Any other mouse click on the summary\n * emits `summary-click` instead; keyboard toggling (Enter/Space on the summary) is unchanged.\n */\n toggleOnChevronOnly?: boolean;\n}\n\nconst props = withDefaults(defineProps<PvAccordionProps>(), {\n chevronVariant: \"vertical\",\n chevronPosition: \"right\",\n enableTriggerSlot: false,\n enableTriggerFullWidth: false,\n toggleOnChevronOnly: false,\n});\n\nconst accordionOpen = defineModel<boolean>();\n\nconst emit = defineEmits<{\n (e: \"summary-mouseenter\"): void;\n (e: \"summary-mouseleave\"): void;\n (e: \"summary-click\", event: MouseEvent): void;\n}>();\n\nif (props.defaultOpen !== undefined) {\n accordionOpen.value = props.defaultOpen;\n}\n\nconst toggleAccordionState = (event: Event) => {\n const detailsElement = event.target as HTMLDetailsElement;\n accordionOpen.value = detailsElement.open;\n};\n\nconst handleSummaryClick = (event: MouseEvent) => {\n // `detail` is 0 for the click a keyboard Enter/Space synthesizes, which must keep toggling.\n if (event.detail === 0) return;\n if ((event.target as Element | null)?.closest(\"[data-pv-accordion-chevron]\")) return;\n // Slot content that already handled the click (and prevented the toggle itself) owns it.\n if (event.defaultPrevented) return;\n event.preventDefault();\n emit(\"summary-click\", event);\n};\n\n// Attached only when the prop is on: any Vue listener on the summary stamps the click it sees, and an\n// ancestor's Vue listener ignores a click stamped no later than its own attach time - which is every\n// click while `Date.now()` is frozen, e.g. under fake timers. Consumers listening on the accordion itself\n// (PvReasoning's `@click`) would otherwise stop hearing trigger clicks.\nconst summaryListeners = computed(() => (props.toggleOnChevronOnly ? { click: handleSummaryClick } : {}));\n\nconst triggerStyles = {\n width: props.enableTriggerFullWidth ? \"100%\" : \"fit-content\",\n};\n\nconst chevronState = computed(() => {\n if (props.chevronVariant === \"horizontal\") {\n if (props.chevronPosition === \"left\") {\n return accordionOpen.value ? \"chevron-down\" : \"chevron-right\";\n } else {\n return accordionOpen.value ? \"chevron-down\" : \"chevron-left\";\n }\n } else {\n return accordionOpen.value ? \"chevron-up\" : \"chevron-down\";\n }\n});\n</script>\n\n<template>\n <details data-testid=\"pv-accordion\" class=\"pv-accordion\" :open=\"accordionOpen\" @toggle=\"toggleAccordionState\">\n <summary\n v-bind=\"summaryAttrs\"\n :class=\"['pv-flex pv-space-between pv-relative', summaryClasses]\"\n :style=\"{ ...triggerStyles, ...summaryStyles }\"\n @mouseenter=\"$emit('summary-mouseenter')\"\n @mouseleave=\"$emit('summary-mouseleave')\"\n v-on=\"summaryListeners\"\n >\n <!-- Content placed before a left chevron, e.g. a tree connector that must sit in the indent column -->\n <slot name=\"leading\" />\n <PvIcon v-if=\"chevronPosition === 'left'\" :name=\"chevronState\" data-pv-accordion-chevron />\n <template v-if=\"enableTriggerSlot\">\n <slot name=\"trigger\" />\n </template>\n <template v-else>\n <span>\n {{ header }}\n </span>\n <PvCounterBadge v-if=\"counter\" :value=\"counter\" variant=\"secondary\" size=\"sm\" />\n </template>\n <PvIcon v-if=\"chevronPosition === 'right'\" :name=\"chevronState\" data-pv-accordion-chevron />\n </summary>\n <div :style=\"contentStyles\">\n <slot />\n </div>\n </details>\n</template>\n\n<style scoped>\nsummary {\n background-image: none;\n padding-right: 2px;\n}\n</style>\n","<script setup lang=\"ts\">\nimport PvCounterBadge from \"../PvCounterBadge/PvCounterBadge.vue\";\nimport PvIcon from \"../PvIcon/PvIcon.vue\";\nimport { ChevronPosition } from \"./types\";\nimport { computed } from \"vue\";\nimport type { CSSProperties } from \"vue\";\n\ninterface PvAccordionProps {\n /** Position of the chevron icon relative to the header text */\n chevronPosition?: ChevronPosition;\n /** Orientation of the chevron for the open/closed states (up-down vs right-down) */\n chevronVariant?: \"vertical\" | \"horizontal\";\n /** Whether the accordion is open by default on initial render */\n defaultOpen?: boolean;\n /** Optional counter badge value displayed next to the header text */\n counter?: number;\n /** Enables a custom trigger slot instead of the default header text */\n enableTriggerSlot?: boolean;\n /** Makes the trigger element expand to fill its container width */\n enableTriggerFullWidth?: boolean;\n /** Text displayed as the accordion header */\n header?: string;\n /** Custom CSS styles applied to the summary/trigger element */\n summaryStyles?: CSSProperties;\n /** CSS class bindings applied to the summary/trigger element */\n summaryClasses?: Record<string, boolean>;\n /** Custom CSS styles applied to the content container */\n contentStyles?: CSSProperties;\n /** Additional attrs for a summary element */\n summaryAttrs?: Record<string, string | undefined>;\n /**\n * Only a click on the chevron opens/closes the accordion. Any other mouse click on the summary\n * emits `summary-click` instead; keyboard toggling (Enter/Space on the summary) is unchanged.\n */\n toggleOnChevronOnly?: boolean;\n}\n\nconst props = withDefaults(defineProps<PvAccordionProps>(), {\n chevronVariant: \"vertical\",\n chevronPosition: \"right\",\n enableTriggerSlot: false,\n enableTriggerFullWidth: false,\n toggleOnChevronOnly: false,\n});\n\nconst accordionOpen = defineModel<boolean>();\n\nconst emit = defineEmits<{\n (e: \"summary-mouseenter\"): void;\n (e: \"summary-mouseleave\"): void;\n (e: \"summary-click\", event: MouseEvent): void;\n}>();\n\nif (props.defaultOpen !== undefined) {\n accordionOpen.value = props.defaultOpen;\n}\n\nconst toggleAccordionState = (event: Event) => {\n const detailsElement = event.target as HTMLDetailsElement;\n accordionOpen.value = detailsElement.open;\n};\n\nconst handleSummaryClick = (event: MouseEvent) => {\n // `detail` is 0 for the click a keyboard Enter/Space synthesizes, which must keep toggling.\n if (event.detail === 0) return;\n if ((event.target as Element | null)?.closest(\"[data-pv-accordion-chevron]\")) return;\n // Slot content that already handled the click (and prevented the toggle itself) owns it.\n if (event.defaultPrevented) return;\n event.preventDefault();\n emit(\"summary-click\", event);\n};\n\n// Attached only when the prop is on: any Vue listener on the summary stamps the click it sees, and an\n// ancestor's Vue listener ignores a click stamped no later than its own attach time - which is every\n// click while `Date.now()` is frozen, e.g. under fake timers. Consumers listening on the accordion itself\n// (PvReasoning's `@click`) would otherwise stop hearing trigger clicks.\nconst summaryListeners = computed(() => (props.toggleOnChevronOnly ? { click: handleSummaryClick } : {}));\n\nconst triggerStyles = {\n width: props.enableTriggerFullWidth ? \"100%\" : \"fit-content\",\n};\n\nconst chevronState = computed(() => {\n if (props.chevronVariant === \"horizontal\") {\n if (props.chevronPosition === \"left\") {\n return accordionOpen.value ? \"chevron-down\" : \"chevron-right\";\n } else {\n return accordionOpen.value ? \"chevron-down\" : \"chevron-left\";\n }\n } else {\n return accordionOpen.value ? \"chevron-up\" : \"chevron-down\";\n }\n});\n</script>\n\n<template>\n <details data-testid=\"pv-accordion\" class=\"pv-accordion\" :open=\"accordionOpen\" @toggle=\"toggleAccordionState\">\n <summary\n v-bind=\"summaryAttrs\"\n :class=\"['pv-flex pv-space-between pv-relative', summaryClasses]\"\n :style=\"{ ...triggerStyles, ...summaryStyles }\"\n @mouseenter=\"$emit('summary-mouseenter')\"\n @mouseleave=\"$emit('summary-mouseleave')\"\n v-on=\"summaryListeners\"\n >\n <!-- Content placed before a left chevron, e.g. a tree connector that must sit in the indent column -->\n <slot name=\"leading\" />\n <PvIcon v-if=\"chevronPosition === 'left'\" :name=\"chevronState\" data-pv-accordion-chevron />\n <template v-if=\"enableTriggerSlot\">\n <slot name=\"trigger\" />\n </template>\n <template v-else>\n <span>\n {{ header }}\n </span>\n <PvCounterBadge v-if=\"counter\" :value=\"counter\" variant=\"secondary\" size=\"sm\" />\n </template>\n <PvIcon v-if=\"chevronPosition === 'right'\" :name=\"chevronState\" data-pv-accordion-chevron />\n </summary>\n <div :style=\"contentStyles\">\n <slot />\n </div>\n </details>\n</template>\n\n<style scoped>\nsummary {\n background-image: none;\n padding-right: 2px;\n}\n</style>\n","import { computed, onBeforeUnmount, onBeforeUpdate, onMounted, ref, unref, useSlots, watch } from \"vue\";\nimport type { Ref } from \"vue\";\nimport { useIsWebComponent } from \"@/web-components/utils\";\n\ntype MaybeRef<T> = T | Ref<T>;\n\nexport type UseSlotPresenceOptions = {\n /**\n * Host element for native slot detection (required for web components).\n * When running as a web component, light DOM children of this element are inspected.\n */\n host?: MaybeRef<HTMLElement | null | undefined>;\n\n /**\n * A slots object (useful for tests or to override `useSlots()`).\n * Only used when running as a Vue component (not a web component).\n */\n vueSlots?: Record<string, unknown> | null | undefined;\n\n /**\n * Watch light DOM changes so presence updates if children are added/removed.\n * Only applies to web component mode. Defaults to true.\n */\n observe?: boolean;\n\n /**\n * Listen to slotchange events on shadow DOM slots.\n * Only applies to web component mode. Defaults to true.\n */\n listenSlotChange?: boolean;\n\n /**\n * If true, whitespace-only text nodes count as content in the default slot.\n * Only applies to web component mode. Defaults to false.\n */\n countWhitespaceTextInDefaultSlot?: boolean;\n};\n\nfunction isMeaningfulTextNode(node: Node, countWhitespace: boolean) {\n if (node.nodeType !== Node.TEXT_NODE) return false;\n const txt = node.textContent ?? \"\";\n return countWhitespace ? txt.length > 0 : txt.trim().length > 0;\n}\n\nfunction hasNativeSlottedContent(host: HTMLElement, name: string, countWhitespaceTextInDefaultSlot: boolean): boolean {\n const slotName = name === \"default\" ? \"\" : name;\n const nodes = Array.from(host.childNodes);\n\n if (slotName === \"\") {\n // Default slot: nodes without a `slot` attribute\n return nodes.some((n) => {\n if (n.nodeType === Node.ELEMENT_NODE) {\n const el = n as Element;\n return !el.hasAttribute(\"slot\");\n }\n return isMeaningfulTextNode(n, countWhitespaceTextInDefaultSlot);\n });\n }\n\n // Named slot: element children with slot=\"name\"\n return nodes.some((n) => {\n if (n.nodeType !== Node.ELEMENT_NODE) return false;\n const el = n as Element;\n return el.getAttribute(\"slot\") === slotName;\n });\n}\n\n/**\n * Vue's light DOM custom elements (`shadowRoot: false`) move every child off the\n * host into an internal map during `connectedCallback`, before the component\n * renders, and splice them back in wherever a `<slot>` outlet appears. The host\n * is therefore empty by the time we can inspect it, and this map is the only\n * surviving record of what was slotted. Vue populates it before `setup()` runs\n * and keeps it as the source `_renderSlots()` reads on every update, so it is a\n * stable answer for a slot outlet that is itself gated on slot presence.\n */\ntype LightDomSlotHost = HTMLElement & { _slots?: Record<string, Node[]> };\n\nfunction getParsedLightDomSlots(host: HTMLElement): Record<string, Node[]> | null {\n if (host.shadowRoot) return null;\n const slots = (host as LightDomSlotHost)._slots;\n if (!slots || typeof slots !== \"object\") return null;\n return slots;\n}\n\nfunction hasParsedSlotContent(\n slots: Record<string, Node[]>,\n name: string,\n countWhitespaceTextInDefaultSlot: boolean,\n): boolean {\n // Vue keys the default slot as \"default\" and named slots by their `slot` attribute.\n const nodes = slots[name === \"\" ? \"default\" : name];\n if (!nodes) return false;\n return nodes.some((n) => {\n if (n.nodeType === Node.ELEMENT_NODE) return true;\n return isMeaningfulTextNode(n, countWhitespaceTextInDefaultSlot);\n });\n}\n\nfunction getShadowSlotEl(host: HTMLElement, name: string): HTMLSlotElement | null {\n const sr = host.shadowRoot;\n if (!sr) return null;\n if (name === \"default\" || name === \"\") {\n return sr.querySelector(\"slot:not([name])\");\n }\n return sr.querySelector(`slot[name=\"${CSS.escape(name)}\"]`);\n}\n\n/**\n * Reports whether a custom element host has content for `name`, across all three\n * ways slotted content can be held: assigned to a shadow `<slot>`, parsed off the\n * host by Vue in light DOM, or still sitting as a light DOM child.\n *\n * Exported so callers that resolve a slot name dynamically (and so cannot hold a\n * `useSlotPresence` instance per slot) resolve it the same way.\n */\nexport function hasHostSlotContent(\n host: HTMLElement,\n name: string,\n { useShadowSlot = true, countWhitespaceTextInDefaultSlot = false } = {},\n): boolean {\n const slotEl = useShadowSlot ? getShadowSlotEl(host, name) : null;\n if (slotEl) {\n return slotEl.assignedNodes({ flatten: true }).some((n) => {\n if (n.nodeType === Node.ELEMENT_NODE) return true;\n return isMeaningfulTextNode(n, countWhitespaceTextInDefaultSlot);\n });\n }\n\n // Light DOM custom element: the host has been drained, so read the slots Vue\n // parsed off it. Note that Vue only parses on first connect, so children added\n // to the host later are never rendered either way.\n const parsedSlots = getParsedLightDomSlots(host);\n if (parsedSlots) return hasParsedSlotContent(parsedSlots, name, countWhitespaceTextInDefaultSlot);\n\n return hasNativeSlottedContent(host, name, countWhitespaceTextInDefaultSlot);\n}\n\n/**\n * Returns a computed boolean indicating if a slot has content.\n *\n * Automatically detects if running as a Vue component or web component:\n * - Vue component: checks Vue's slot system via useSlots()\n * - Web component: inspects light DOM children of the host element\n */\nexport function useSlotPresence(slotName: string, options: UseSlotPresenceOptions = {}) {\n const { host, vueSlots, observe = true, listenSlotChange = true, countWhitespaceTextInDefaultSlot = false } = options;\n\n const isWebComponent = useIsWebComponent();\n const slots = vueSlots ?? useSlots();\n\n // Vue's slots object is updated in place and is not reactive, so a computed\n // would cache the initial slot table forever. Refresh before each component\n // update so conditional named slots can appear and disappear at runtime.\n const vueSlotKey = slotName === \"default\" || slotName === \"\" ? \"default\" : slotName;\n const readVueSlot = () => !!slots && !!(slots as Record<string, unknown>)[vueSlotKey];\n const hasVueSlot = ref(readVueSlot());\n onBeforeUpdate(() => {\n hasVueSlot.value = readVueSlot();\n });\n\n // Native/web-component slot presence\n const hasNativeSlot = ref(false);\n\n let mo: MutationObserver | null = null;\n let shadowSlot: HTMLSlotElement | null = null;\n\n const cleanup = () => {\n if (mo) {\n mo.disconnect();\n mo = null;\n }\n if (shadowSlot) {\n shadowSlot.removeEventListener(\"slotchange\", updateNative);\n shadowSlot = null;\n }\n };\n\n const updateNative = () => {\n if (!isWebComponent.value) {\n hasNativeSlot.value = false;\n return;\n }\n\n const el = unref(host);\n if (!el) {\n hasNativeSlot.value = false;\n return;\n }\n\n hasNativeSlot.value = hasHostSlotContent(el, slotName, {\n // Prefer assignedNodes when we can reach the <slot> element in an open shadowRoot\n useShadowSlot: listenSlotChange,\n countWhitespaceTextInDefaultSlot,\n });\n };\n\n const attach = () => {\n if (!isWebComponent.value) return;\n\n cleanup();\n\n const el = unref(host);\n if (!el) {\n hasNativeSlot.value = false;\n return;\n }\n\n updateNative();\n\n if (listenSlotChange) {\n shadowSlot = getShadowSlotEl(el, slotName);\n if (shadowSlot) shadowSlot.addEventListener(\"slotchange\", updateNative);\n }\n\n if (observe) {\n mo = new MutationObserver(() => updateNative());\n mo.observe(el, {\n childList: true,\n subtree: false,\n attributes: true,\n attributeFilter: [\"slot\"],\n characterData: true,\n });\n }\n };\n\n onMounted(attach);\n onBeforeUnmount(cleanup);\n\n // If host ref changes, reattach\n watch(\n () => unref(host),\n () => {\n if (isWebComponent.value) attach();\n },\n );\n\n // Select presence based on component type\n const present = computed(() => (isWebComponent.value ? hasNativeSlot.value : hasVueSlot.value));\n\n return {\n present,\n hasVueSlot,\n hasNativeSlot,\n refresh: updateNative,\n };\n}\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\n\nimport type { PvStep, PvStepListProps, PvStepStatus } from \"./types\";\n\nconst props = withDefaults(defineProps<PvStepListProps>(), {\n inProgress: false,\n steps: () => [],\n});\n\n// The only step that can still be running is the last one that is actually a\n// step: an aside is prose the model has already written, and the step above it\n// stays the running one while that prose is typed out.\nconst lastStepIndex = computed(() => {\n for (let index = props.steps.length - 1; index >= 0; index -= 1) {\n if (props.steps[index].variant !== \"aside\") return index;\n }\n return -1;\n});\n\nfunction statusOf(step: PvStep, index: number): PvStepStatus {\n if (step.status) return step.status;\n if (step.variant === \"aside\") return \"complete\";\n return props.inProgress && index === lastStepIndex.value ? \"active\" : \"complete\";\n}\n\n// A finished step is history: it stays readable, but the step being worked on is\n// what the reader is looking for, so it is the only one at full weight.\nfunction labelClasses(step: PvStep, index: number): string {\n return statusOf(step, index) === \"active\" ? \"pv-text-medium pv-text-default\" : \"pv-text-quaternary\";\n}\n</script>\n\n<template>\n <TransitionGroup tag=\"ol\" name=\"pv-step\" class=\"pv-step-list\" role=\"list\">\n <li v-for=\"(step, index) in steps\" :key=\"step.id ?? index\" class=\"pv-step\" :data-status=\"statusOf(step, index)\">\n <span v-if=\"step.variant !== 'aside'\" class=\"pv-step-marker\" aria-hidden=\"true\">\n <PvIcon v-if=\"step.icon\" :name=\"step.icon\" :size=\"12\" />\n <span v-else class=\"pv-step-dot\"></span>\n </span>\n\n <div class=\"pv-step-body pv-flow-8\">\n <p v-if=\"step.variant === 'aside'\" class=\"pv-step-aside pv-text-body-md pv-text-tertiary\">\n <em>{{ step.label }}</em>\n </p>\n\n <template v-else>\n <!-- A flex row so the shimmer, which paints across its whole box, covers\n the label rather than the empty width beside it. -->\n <p class=\"pv-flex pv-text-body-md\" :class=\"labelClasses(step, index)\" style=\"--flex-wrap: wrap\">\n <span :class=\"{ 'pv-shimmer': statusOf(step, index) === 'active' }\">{{ step.label }}</span>\n <span v-if=\"step.artifact\" class=\"pv-tag-highlight\">{{ step.artifact }}</span>\n </p>\n <p v-if=\"step.note\" class=\"pv-text-body-md pv-text-tertiary\">{{ step.note }}</p>\n <ul\n v-if=\"step.tags && step.tags.length > 0\"\n class=\"pv-flex\"\n role=\"list\"\n style=\"--flex-wrap: wrap; --flex-gap: 4px\"\n >\n <li\n v-for=\"(tag, tagIndex) in step.tags\"\n :key=\"tagIndex\"\n class=\"pv-tag-secondary\"\n :class=\"{ 'pv-text-quaternary': tag.variant === 'muted' }\"\n data-style=\"rounded\"\n >\n {{ tag.label }}\n </li>\n </ul>\n <p v-if=\"step.outcome\" class=\"pv-text-body-md pv-text-medium pv-text-brand\">\n &#8594;&#160;{{ step.outcome }}\n </p>\n </template>\n </div>\n </li>\n </TransitionGroup>\n</template>\n","<script setup lang=\"ts\">\nimport { computed } from \"vue\";\n\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\n\nimport type { PvStep, PvStepListProps, PvStepStatus } from \"./types\";\n\nconst props = withDefaults(defineProps<PvStepListProps>(), {\n inProgress: false,\n steps: () => [],\n});\n\n// The only step that can still be running is the last one that is actually a\n// step: an aside is prose the model has already written, and the step above it\n// stays the running one while that prose is typed out.\nconst lastStepIndex = computed(() => {\n for (let index = props.steps.length - 1; index >= 0; index -= 1) {\n if (props.steps[index].variant !== \"aside\") return index;\n }\n return -1;\n});\n\nfunction statusOf(step: PvStep, index: number): PvStepStatus {\n if (step.status) return step.status;\n if (step.variant === \"aside\") return \"complete\";\n return props.inProgress && index === lastStepIndex.value ? \"active\" : \"complete\";\n}\n\n// A finished step is history: it stays readable, but the step being worked on is\n// what the reader is looking for, so it is the only one at full weight.\nfunction labelClasses(step: PvStep, index: number): string {\n return statusOf(step, index) === \"active\" ? \"pv-text-medium pv-text-default\" : \"pv-text-quaternary\";\n}\n</script>\n\n<template>\n <TransitionGroup tag=\"ol\" name=\"pv-step\" class=\"pv-step-list\" role=\"list\">\n <li v-for=\"(step, index) in steps\" :key=\"step.id ?? index\" class=\"pv-step\" :data-status=\"statusOf(step, index)\">\n <span v-if=\"step.variant !== 'aside'\" class=\"pv-step-marker\" aria-hidden=\"true\">\n <PvIcon v-if=\"step.icon\" :name=\"step.icon\" :size=\"12\" />\n <span v-else class=\"pv-step-dot\"></span>\n </span>\n\n <div class=\"pv-step-body pv-flow-8\">\n <p v-if=\"step.variant === 'aside'\" class=\"pv-step-aside pv-text-body-md pv-text-tertiary\">\n <em>{{ step.label }}</em>\n </p>\n\n <template v-else>\n <!-- A flex row so the shimmer, which paints across its whole box, covers\n the label rather than the empty width beside it. -->\n <p class=\"pv-flex pv-text-body-md\" :class=\"labelClasses(step, index)\" style=\"--flex-wrap: wrap\">\n <span :class=\"{ 'pv-shimmer': statusOf(step, index) === 'active' }\">{{ step.label }}</span>\n <span v-if=\"step.artifact\" class=\"pv-tag-highlight\">{{ step.artifact }}</span>\n </p>\n <p v-if=\"step.note\" class=\"pv-text-body-md pv-text-tertiary\">{{ step.note }}</p>\n <ul\n v-if=\"step.tags && step.tags.length > 0\"\n class=\"pv-flex\"\n role=\"list\"\n style=\"--flex-wrap: wrap; --flex-gap: 4px\"\n >\n <li\n v-for=\"(tag, tagIndex) in step.tags\"\n :key=\"tagIndex\"\n class=\"pv-tag-secondary\"\n :class=\"{ 'pv-text-quaternary': tag.variant === 'muted' }\"\n data-style=\"rounded\"\n >\n {{ tag.label }}\n </li>\n </ul>\n <p v-if=\"step.outcome\" class=\"pv-text-body-md pv-text-medium pv-text-brand\">\n &#8594;&#160;{{ step.outcome }}\n </p>\n </template>\n </div>\n </li>\n </TransitionGroup>\n</template>\n","<script setup lang=\"ts\">\nimport { Comment, Fragment, Text, computed, onBeforeUnmount, ref, useSlots, useTemplateRef, watch } from \"vue\";\nimport type { VNode } from \"vue\";\n\nimport PvAccordion from \"@/components/base/PvAccordion/PvAccordion.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\nimport { useSlotPresence } from \"@/composables/useSlotPresence\";\nimport PvStepList from \"../PvStepList/PvStepList.vue\";\n\nimport type { PvReasoningProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvReasoningProps>(), {\n autoCloseDelay: 1000,\n completedLabel: \"Reasoning\",\n defaultOpen: false,\n isStreaming: false,\n steps: () => [],\n streamingLabel: \"Thinking...\",\n});\n\nconst open = defineModel<boolean>(\"open\", { default: false });\nconst internalDuration = ref<number>();\nconst startedAt = ref<number>();\nconst disclosureChosen = ref(false);\nlet closeTimer: ReturnType<typeof setTimeout> | undefined;\n\nif (props.defaultOpen || props.isStreaming) open.value = true;\nif (props.isStreaming) startedAt.value = Date.now();\n\nconst displayedDuration = computed(() => props.duration ?? internalDuration.value);\nconst triggerLabel = computed(() => {\n if (props.isStreaming) return props.streamingLabel;\n if (displayedDuration.value === undefined) return props.completedLabel;\n const roundedDuration = Math.max(1, Math.round(displayedDuration.value));\n return `Thought for ${roundedDuration} ${roundedDuration === 1 ? \"second\" : \"seconds\"}`;\n});\n\nconst slots = useSlots();\nconst triggerRef = useTemplateRef<HTMLElement | null>(\"triggerRef\");\n// As a web component the prose arrives as light DOM rather than through Vue's\n// slot table, so it has to be resolved off the host element instead.\nconst slotHost = computed(() => {\n const rootNode = triggerRef.value?.getRootNode();\n return rootNode instanceof ShadowRoot ? (rootNode.host as HTMLElement) : null;\n});\nconst { hasNativeSlot: hasNativeProse } = useSlotPresence(\"default\", { host: slotHost });\n\n// A slot function exists even when everything inside it is switched off by the\n// consumer's `v-if`, which leaves nothing but comment nodes behind, so what the\n// slot renders has to be inspected rather than whether it was passed.\nfunction rendersContent(vnodes: VNode[]): boolean {\n return vnodes.some((vnode) => {\n if (vnode.type === Comment) return false;\n if (vnode.type === Text) return String(vnode.children ?? \"\").trim().length > 0;\n if (vnode.type === Fragment) return Array.isArray(vnode.children) && rendersContent(vnode.children as VNode[]);\n return true;\n });\n}\n\n// Reasoning streams before it has anything to say: the first steps and the first\n// line of prose land after the label does. A disclosure whose panel is empty\n// offers a chevron that reveals nothing, so until then the label stands alone.\n// Called from the template rather than cached in a computed because the slot\n// belongs to the parent and has to be re-read on each render of this component.\nfunction hasDisclosableContent() {\n if (props.steps.length > 0 || hasNativeProse.value) return true;\n return rendersContent(slots.default?.({}) ?? []);\n}\n\nfunction clearCloseTimer() {\n if (closeTimer !== undefined) {\n clearTimeout(closeTimer);\n closeTimer = undefined;\n }\n}\n\n// Only a click on the trigger is a disclosure choice, and only a disclosure\n// choice suppresses the auto-close. Reading the content - selecting text,\n// following a link - is not a decision about whether the panel should stay open,\n// so it must not permanently disable auto-close for the rest of the turn.\nfunction handleClick(event: MouseEvent) {\n const target = event.target as Element | null;\n const summary = target?.closest?.(\"summary\");\n if (!summary || summary.parentElement !== event.currentTarget) return;\n disclosureChosen.value = true;\n clearCloseTimer();\n}\n\nwatch(\n () => props.isStreaming,\n (isStreaming, wasStreaming) => {\n clearCloseTimer();\n\n if (isStreaming) {\n startedAt.value = Date.now();\n internalDuration.value = undefined;\n disclosureChosen.value = false;\n open.value = true;\n return;\n }\n\n if (!wasStreaming) return;\n if (startedAt.value !== undefined) {\n internalDuration.value = (Date.now() - startedAt.value) / 1000;\n }\n if (!disclosureChosen.value) {\n closeTimer = setTimeout(() => {\n if (!disclosureChosen.value) open.value = false;\n }, props.autoCloseDelay);\n }\n },\n);\n\nonBeforeUnmount(clearCloseTimer);\n</script>\n\n<template>\n <PvAccordion\n v-if=\"hasDisclosableContent()\"\n v-model=\"open\"\n :default-open=\"open\"\n class=\"pv-reasoning\"\n chevron-position=\"left\"\n chevron-variant=\"horizontal\"\n enable-trigger-full-width\n enable-trigger-slot\n :summary-classes=\"{ 'pv-text-tertiary': true }\"\n :summary-styles=\"{ justifyContent: 'flex-start' }\"\n @click=\"handleClick\"\n >\n <template #trigger>\n <!-- Icons take their colour from the summary rather than from here, so that\n the chevron - a sibling of this span, filled with `currentcolor` - matches\n the brain inside it. The label is a step darker than either. -->\n <span ref=\"triggerRef\" class=\"pv-reasoning-trigger pv-flex pv-text-body-md pv-text-medium\" aria-live=\"polite\">\n <slot name=\"trigger\">\n <PvIcon name=\"brain\" />\n <!-- The label carries the shimmer, so it sweeps the words rather than the\n full width of the trigger. -->\n <span class=\"pv-text-subdued\" :class=\"{ 'pv-shimmer': isStreaming }\">{{ triggerLabel }}</span>\n </slot>\n </span>\n </template>\n\n <div class=\"pv-reasoning-content pv-text-body-md pv-text-tertiary\">\n <PvStepList v-if=\"steps.length > 0\" :steps=\"steps\" :in-progress=\"isStreaming\" />\n <slot />\n </div>\n </PvAccordion>\n\n <span\n v-else\n ref=\"triggerRef\"\n class=\"pv-reasoning-trigger pv-reasoning-standalone pv-flex pv-text-body-md pv-text-medium pv-text-tertiary\"\n aria-live=\"polite\"\n >\n <slot name=\"trigger\">\n <PvIcon name=\"brain\" />\n <span class=\"pv-text-subdued\" :class=\"{ 'pv-shimmer': isStreaming }\">{{ triggerLabel }}</span>\n </slot>\n </span>\n</template>\n","<script setup lang=\"ts\">\nimport { Comment, Fragment, Text, computed, onBeforeUnmount, ref, useSlots, useTemplateRef, watch } from \"vue\";\nimport type { VNode } from \"vue\";\n\nimport PvAccordion from \"@/components/base/PvAccordion/PvAccordion.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\nimport { useSlotPresence } from \"@/composables/useSlotPresence\";\nimport PvStepList from \"../PvStepList/PvStepList.vue\";\n\nimport type { PvReasoningProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvReasoningProps>(), {\n autoCloseDelay: 1000,\n completedLabel: \"Reasoning\",\n defaultOpen: false,\n isStreaming: false,\n steps: () => [],\n streamingLabel: \"Thinking...\",\n});\n\nconst open = defineModel<boolean>(\"open\", { default: false });\nconst internalDuration = ref<number>();\nconst startedAt = ref<number>();\nconst disclosureChosen = ref(false);\nlet closeTimer: ReturnType<typeof setTimeout> | undefined;\n\nif (props.defaultOpen || props.isStreaming) open.value = true;\nif (props.isStreaming) startedAt.value = Date.now();\n\nconst displayedDuration = computed(() => props.duration ?? internalDuration.value);\nconst triggerLabel = computed(() => {\n if (props.isStreaming) return props.streamingLabel;\n if (displayedDuration.value === undefined) return props.completedLabel;\n const roundedDuration = Math.max(1, Math.round(displayedDuration.value));\n return `Thought for ${roundedDuration} ${roundedDuration === 1 ? \"second\" : \"seconds\"}`;\n});\n\nconst slots = useSlots();\nconst triggerRef = useTemplateRef<HTMLElement | null>(\"triggerRef\");\n// As a web component the prose arrives as light DOM rather than through Vue's\n// slot table, so it has to be resolved off the host element instead.\nconst slotHost = computed(() => {\n const rootNode = triggerRef.value?.getRootNode();\n return rootNode instanceof ShadowRoot ? (rootNode.host as HTMLElement) : null;\n});\nconst { hasNativeSlot: hasNativeProse } = useSlotPresence(\"default\", { host: slotHost });\n\n// A slot function exists even when everything inside it is switched off by the\n// consumer's `v-if`, which leaves nothing but comment nodes behind, so what the\n// slot renders has to be inspected rather than whether it was passed.\nfunction rendersContent(vnodes: VNode[]): boolean {\n return vnodes.some((vnode) => {\n if (vnode.type === Comment) return false;\n if (vnode.type === Text) return String(vnode.children ?? \"\").trim().length > 0;\n if (vnode.type === Fragment) return Array.isArray(vnode.children) && rendersContent(vnode.children as VNode[]);\n return true;\n });\n}\n\n// Reasoning streams before it has anything to say: the first steps and the first\n// line of prose land after the label does. A disclosure whose panel is empty\n// offers a chevron that reveals nothing, so until then the label stands alone.\n// Called from the template rather than cached in a computed because the slot\n// belongs to the parent and has to be re-read on each render of this component.\nfunction hasDisclosableContent() {\n if (props.steps.length > 0 || hasNativeProse.value) return true;\n return rendersContent(slots.default?.({}) ?? []);\n}\n\nfunction clearCloseTimer() {\n if (closeTimer !== undefined) {\n clearTimeout(closeTimer);\n closeTimer = undefined;\n }\n}\n\n// Only a click on the trigger is a disclosure choice, and only a disclosure\n// choice suppresses the auto-close. Reading the content - selecting text,\n// following a link - is not a decision about whether the panel should stay open,\n// so it must not permanently disable auto-close for the rest of the turn.\nfunction handleClick(event: MouseEvent) {\n const target = event.target as Element | null;\n const summary = target?.closest?.(\"summary\");\n if (!summary || summary.parentElement !== event.currentTarget) return;\n disclosureChosen.value = true;\n clearCloseTimer();\n}\n\nwatch(\n () => props.isStreaming,\n (isStreaming, wasStreaming) => {\n clearCloseTimer();\n\n if (isStreaming) {\n startedAt.value = Date.now();\n internalDuration.value = undefined;\n disclosureChosen.value = false;\n open.value = true;\n return;\n }\n\n if (!wasStreaming) return;\n if (startedAt.value !== undefined) {\n internalDuration.value = (Date.now() - startedAt.value) / 1000;\n }\n if (!disclosureChosen.value) {\n closeTimer = setTimeout(() => {\n if (!disclosureChosen.value) open.value = false;\n }, props.autoCloseDelay);\n }\n },\n);\n\nonBeforeUnmount(clearCloseTimer);\n</script>\n\n<template>\n <PvAccordion\n v-if=\"hasDisclosableContent()\"\n v-model=\"open\"\n :default-open=\"open\"\n class=\"pv-reasoning\"\n chevron-position=\"left\"\n chevron-variant=\"horizontal\"\n enable-trigger-full-width\n enable-trigger-slot\n :summary-classes=\"{ 'pv-text-tertiary': true }\"\n :summary-styles=\"{ justifyContent: 'flex-start' }\"\n @click=\"handleClick\"\n >\n <template #trigger>\n <!-- Icons take their colour from the summary rather than from here, so that\n the chevron - a sibling of this span, filled with `currentcolor` - matches\n the brain inside it. The label is a step darker than either. -->\n <span ref=\"triggerRef\" class=\"pv-reasoning-trigger pv-flex pv-text-body-md pv-text-medium\" aria-live=\"polite\">\n <slot name=\"trigger\">\n <PvIcon name=\"brain\" />\n <!-- The label carries the shimmer, so it sweeps the words rather than the\n full width of the trigger. -->\n <span class=\"pv-text-subdued\" :class=\"{ 'pv-shimmer': isStreaming }\">{{ triggerLabel }}</span>\n </slot>\n </span>\n </template>\n\n <div class=\"pv-reasoning-content pv-text-body-md pv-text-tertiary\">\n <PvStepList v-if=\"steps.length > 0\" :steps=\"steps\" :in-progress=\"isStreaming\" />\n <slot />\n </div>\n </PvAccordion>\n\n <span\n v-else\n ref=\"triggerRef\"\n class=\"pv-reasoning-trigger pv-reasoning-standalone pv-flex pv-text-body-md pv-text-medium pv-text-tertiary\"\n aria-live=\"polite\"\n >\n <slot name=\"trigger\">\n <PvIcon name=\"brain\" />\n <span class=\"pv-text-subdued\" :class=\"{ 'pv-shimmer': isStreaming }\">{{ triggerLabel }}</span>\n </slot>\n </span>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, watch } from \"vue\";\n\nimport PvAccordion from \"@/components/base/PvAccordion/PvAccordion.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\n\nimport type { ToolLifecycleState } from \"../toolLifecycle\";\nimport type { PvToolProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvToolProps>(), {\n defaultOpen: false,\n});\n\nconst open = defineModel<boolean>(\"open\", { default: false });\nconst completedStates: ToolLifecycleState[] = [\"output-available\", \"output-error\"];\n\nif (props.defaultOpen || completedStates.includes(props.state)) open.value = true;\n\nconst status = computed(() => {\n const statuses: Record<ToolLifecycleState, { label: string; className: string }> = {\n \"input-streaming\": { label: \"Pending\", className: \"pv-tag-tertiary\" },\n \"input-available\": { label: \"Running\", className: \"pv-tag-primary\" },\n \"approval-requested\": { label: \"Awaiting approval\", className: \"pv-tag-yellow\" },\n \"approval-responded\": { label: \"Responded\", className: \"pv-tag-tertiary\" },\n \"output-available\": { label: \"Completed\", className: \"pv-tag-success\" },\n \"output-error\": { label: \"Error\", className: \"pv-tag-error\" },\n \"output-denied\": { label: \"Denied\", className: \"pv-tag-error\" },\n };\n return statuses[props.state];\n});\n\nfunction formatValue(value: unknown) {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value, null, 2) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\nwatch(\n () => props.state,\n (state, previousState) => {\n if (state !== previousState && completedStates.includes(state)) open.value = true;\n },\n);\n</script>\n\n<template>\n <PvAccordion\n v-model=\"open\"\n :default-open=\"open\"\n class=\"pv-tool pv-bordered pv-radius\"\n chevron-position=\"right\"\n chevron-variant=\"horizontal\"\n enable-trigger-full-width\n enable-trigger-slot\n :summary-classes=\"{ 'pv-inset-square-12': true }\"\n >\n <template #trigger>\n <span class=\"pv-tool-heading pv-flex pv-flex-item\">\n <PvIcon name=\"settings\" :size=\"20\" />\n <span class=\"pv-text-title-sm pv-truncate\">{{ toolName }}</span>\n <span :class=\"[status.className, 'pv-tag-sm']\" role=\"status\">{{ status.label }}</span>\n </span>\n </template>\n\n <div class=\"pv-tool-disclosure-content\">\n <div class=\"pv-flow-16 pv-border-top pv-tool-content\">\n <section v-if=\"$slots.input || input !== undefined\" class=\"pv-flow-8\">\n <h3 class=\"pv-text-title-xs\">Parameters</h3>\n <slot name=\"input\" :input=\"input\">\n <pre\n class=\"pv-surface-accent pv-inset-square-12 pv-radius-sm pv-text-body-xs\"\n ><code>{{ formatValue(input) }}</code></pre>\n </slot>\n </section>\n\n <section v-if=\"errorText\" class=\"pv-flow-8\" role=\"alert\" aria-label=\"Tool error\">\n <h3 class=\"pv-text-title-xs pv-text-critical\">Error</h3>\n <pre class=\"pv-surface-critical pv-inset-square-12 pv-radius-sm pv-text-body-xs pv-text-critical\">{{\n errorText\n }}</pre>\n </section>\n\n <section v-else-if=\"$slots.output || output !== undefined\" class=\"pv-flow-8\">\n <h3 class=\"pv-text-title-xs\">Result</h3>\n <slot name=\"output\" :output=\"output\">\n <pre\n class=\"pv-surface-accent pv-inset-square-12 pv-radius-sm pv-text-body-xs\"\n ><code>{{ formatValue(output) }}</code></pre>\n </slot>\n </section>\n </div>\n </div>\n </PvAccordion>\n</template>\n","<script setup lang=\"ts\">\nimport { computed, watch } from \"vue\";\n\nimport PvAccordion from \"@/components/base/PvAccordion/PvAccordion.vue\";\nimport PvIcon from \"@/components/base/PvIcon/PvIcon.vue\";\n\nimport type { ToolLifecycleState } from \"../toolLifecycle\";\nimport type { PvToolProps } from \"./types\";\n\nconst props = withDefaults(defineProps<PvToolProps>(), {\n defaultOpen: false,\n});\n\nconst open = defineModel<boolean>(\"open\", { default: false });\nconst completedStates: ToolLifecycleState[] = [\"output-available\", \"output-error\"];\n\nif (props.defaultOpen || completedStates.includes(props.state)) open.value = true;\n\nconst status = computed(() => {\n const statuses: Record<ToolLifecycleState, { label: string; className: string }> = {\n \"input-streaming\": { label: \"Pending\", className: \"pv-tag-tertiary\" },\n \"input-available\": { label: \"Running\", className: \"pv-tag-primary\" },\n \"approval-requested\": { label: \"Awaiting approval\", className: \"pv-tag-yellow\" },\n \"approval-responded\": { label: \"Responded\", className: \"pv-tag-tertiary\" },\n \"output-available\": { label: \"Completed\", className: \"pv-tag-success\" },\n \"output-error\": { label: \"Error\", className: \"pv-tag-error\" },\n \"output-denied\": { label: \"Denied\", className: \"pv-tag-error\" },\n };\n return statuses[props.state];\n});\n\nfunction formatValue(value: unknown) {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value, null, 2) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\nwatch(\n () => props.state,\n (state, previousState) => {\n if (state !== previousState && completedStates.includes(state)) open.value = true;\n },\n);\n</script>\n\n<template>\n <PvAccordion\n v-model=\"open\"\n :default-open=\"open\"\n class=\"pv-tool pv-bordered pv-radius\"\n chevron-position=\"right\"\n chevron-variant=\"horizontal\"\n enable-trigger-full-width\n enable-trigger-slot\n :summary-classes=\"{ 'pv-inset-square-12': true }\"\n >\n <template #trigger>\n <span class=\"pv-tool-heading pv-flex pv-flex-item\">\n <PvIcon name=\"settings\" :size=\"20\" />\n <span class=\"pv-text-title-sm pv-truncate\">{{ toolName }}</span>\n <span :class=\"[status.className, 'pv-tag-sm']\" role=\"status\">{{ status.label }}</span>\n </span>\n </template>\n\n <div class=\"pv-tool-disclosure-content\">\n <div class=\"pv-flow-16 pv-border-top pv-tool-content\">\n <section v-if=\"$slots.input || input !== undefined\" class=\"pv-flow-8\">\n <h3 class=\"pv-text-title-xs\">Parameters</h3>\n <slot name=\"input\" :input=\"input\">\n <pre\n class=\"pv-surface-accent pv-inset-square-12 pv-radius-sm pv-text-body-xs\"\n ><code>{{ formatValue(input) }}</code></pre>\n </slot>\n </section>\n\n <section v-if=\"errorText\" class=\"pv-flow-8\" role=\"alert\" aria-label=\"Tool error\">\n <h3 class=\"pv-text-title-xs pv-text-critical\">Error</h3>\n <pre class=\"pv-surface-critical pv-inset-square-12 pv-radius-sm pv-text-body-xs pv-text-critical\">{{\n errorText\n }}</pre>\n </section>\n\n <section v-else-if=\"$slots.output || output !== undefined\" class=\"pv-flow-8\">\n <h3 class=\"pv-text-title-xs\">Result</h3>\n <slot name=\"output\" :output=\"output\">\n <pre\n class=\"pv-surface-accent pv-inset-square-12 pv-radius-sm pv-text-body-xs\"\n ><code>{{ formatValue(output) }}</code></pre>\n </slot>\n </section>\n </div>\n </div>\n </PvAccordion>\n</template>\n","export const toolLifecycleStates = [\n \"input-streaming\",\n \"input-available\",\n \"approval-requested\",\n \"approval-responded\",\n \"output-available\",\n \"output-error\",\n \"output-denied\",\n] as const;\n\n/**\n * Tool lifecycle states. The `input-*` and `output-available`/`output-error`\n * values mirror the Vercel AI SDK tool-part state union; `approval-requested`,\n * `approval-responded`, and `output-denied` are Pit Viper additions for the\n * human-in-the-loop approval flow.\n */\nexport type ToolLifecycleState = (typeof toolLifecycleStates)[number];\n\n/** Any JSON-serializable value, matching the shape of AI SDK tool input/output. */\nexport type JSONValue = string | number | boolean | null | { [key: string]: JSONValue } | JSONValue[];\n\n/** Approval metadata for the tool confirmation flow. */\nexport interface ToolApproval {\n /** Identifier of the approval request, echoed back in the response. */\n id: string;\n /** Whether the tool execution was approved; undefined until answered. */\n approved?: boolean;\n /** Optional reason supplied with the decision, primarily for rejections. */\n reason?: string;\n}\n","import { onScopeDispose, ref, watch, type Ref } from \"vue\";\n\nexport interface UseStickToBottomOptions {\n /** Distance in px from the bottom that still counts as \"at the bottom\". Defaults to 32. */\n threshold?: number;\n}\n\nexport interface UseStickToBottom {\n /** Whether the container is scrolled to within `threshold` of the bottom. */\n isAtBottom: Ref<boolean>;\n /** Scroll the container to the bottom and mark it as pinned. */\n scrollToBottom: (behavior?: ScrollBehavior) => void;\n}\n\n/**\n * Chat-style auto-scroll for a scrollable element. Tracks whether the reader is\n * at the bottom and keeps new content pinned to the bottom - but only while the\n * reader is already there, so scrolling up to read history is never interrupted.\n *\n * @param container - Ref to the scrollable element (may be null before mount).\n */\nexport function useStickToBottom(\n container: Ref<HTMLElement | null | undefined>,\n options: UseStickToBottomOptions = {},\n): UseStickToBottom {\n const threshold = options.threshold ?? 32;\n const isAtBottom = ref(true);\n\n function update() {\n const el = container.value;\n if (el) isAtBottom.value = el.scrollHeight - el.scrollTop - el.clientHeight <= threshold;\n }\n\n function scrollToBottom(behavior: ScrollBehavior = \"smooth\") {\n const el = container.value;\n if (!el) return;\n el.scrollTo({ top: el.scrollHeight, behavior });\n isAtBottom.value = true;\n }\n\n let detach: (() => void) | undefined;\n\n function attach(el: HTMLElement) {\n const onScroll = () => update();\n el.addEventListener(\"scroll\", onScroll, { passive: true });\n\n // Keep the view pinned as content grows, but only when already at the\n // bottom - otherwise leave the reader where they scrolled to.\n const stickIfPinned = () => {\n if (isAtBottom.value) scrollToBottom(\"auto\");\n };\n const observers: Array<{ disconnect: () => void }> = [];\n\n // Observe the container (viewport size) and its content children (so pure\n // reflow - fonts loading, a nested accordion expanding - re-sticks too, not\n // just add/remove of children).\n let resizeObserver: ResizeObserver | undefined;\n const observed = new WeakSet<Element>();\n const observeNewChildren = () => {\n if (!resizeObserver) return;\n for (const child of Array.from(el.children)) {\n if (!observed.has(child)) {\n observed.add(child);\n resizeObserver.observe(child);\n }\n }\n };\n if (typeof ResizeObserver !== \"undefined\") {\n resizeObserver = new ResizeObserver(stickIfPinned);\n resizeObserver.observe(el);\n observeNewChildren();\n observers.push(resizeObserver);\n }\n if (typeof MutationObserver !== \"undefined\") {\n // childList only: character-level streaming reflow is already caught by\n // the child ResizeObservers, so there's no need to fire per keystroke.\n const contentObserver = new MutationObserver(() => {\n observeNewChildren();\n stickIfPinned();\n });\n contentObserver.observe(el, { childList: true });\n observers.push(contentObserver);\n }\n\n update();\n\n detach = () => {\n el.removeEventListener(\"scroll\", onScroll);\n observers.forEach((observer) => observer.disconnect());\n };\n }\n\n watch(\n container,\n (el) => {\n detach?.();\n detach = undefined;\n if (el) attach(el);\n },\n { immediate: true },\n );\n\n onScopeDispose(() => detach?.());\n\n return { isAtBottom, scrollToBottom };\n}\n"],"mappings":";;;;;;;;;;EAUA,IAAM,IAA6C;GACjD,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACJ,IAAI;GACL,EAEK,IAAwD;GAC5D,SAAS;GACT,MAAM;GACN,OAAO;GACR;yBASC,EAA+G,OAAA;GAA1G,eAAY;GAAc,OAAK,EAAE,EAAoB,EAAA,SAAO;GAAI,OAAK,EAAA,EAAA,UAAc,EAAY,EAAA,OAAI,CAAA;;;IE7B7F,IAAiC,CAAC,QAAQ,EAEjD,IAA2D;CAC/D,IAAI;CACJ,IAAI,KAAA;CACJ,IAAI;CACL,EAEY,MAAuB,MAC9B,KAAQ,QAAQ,CAAC,EAAe,eAAe,EAAK,GAC/C,OAEF,EAAe,MAAS;;;ACQjC,SAAgB,IAAoB;CAClC,IAAM,IAAO,EAAI,GAAM,EAEjB,IADW,GAAoB,EACd,QAAQ,EAAE;AAIjC,QAHI,UAAU,KAAQ,EAAK,SAAS,OAClC,EAAK,QAAQ,KAER;;;;ACCT,IAAa,KAAc;CAAC,KAAA;CAAW;CAAI;CAAI;CAAI;CAAI;CAAI;CAAG;;;;;;;EChB9D,IAAM,IAAQ,GACR,IAAQ,GAAmB,EAE3B,IAAuC,EAAI,KAAK,EAEhD,IAAU,SAAgB;GAC9B,WAAW;IACV,WAAW,EAAM,SAAS,EAAM,QAAQ,QAAQ,GAAY,SAAS,EAAM,KAAK;GAClF,EAAE,EAEG,IAAU,QAEV,EAAM,SAAS,EAAiB,QAC3B,GAAG,EAAiB,MAAM,GAAG,EAAM,SAErC,IAAI,EAAM,OACjB;SAGE,WAAW,8BAGb,EAAiB,QAAQ,WAAW,4CAKpC,EAEM,OAAA;GAFD,eAAY;GAAU,eAAY;GAAQ,OAAK,EAAE,EAAA,MAAO;MAC3D,EAAiC,OAAA,EAA3B,cAAY,EAAA,OAAO,EAAA,MAAA,GAAA,EAAA,CAAA,EAAA,EAAA;;;;;;;;;;;;;EEvB7B,IAAM,IAAQ,GAOR,IAAe,QACf,EAAM,YAAY,EAAM,SAAS,EAAM,QAAQ,EAAM,WAChD,GAAG,EAAM,SAAS,KAEvB,EAAM,SAAS,OAGZ,MAFE,GAAG,EAAM,SAAS,EAAM,QAGjC,EAEI,IAAU,SAAgB;GAC9B,4DAA4D;GAC5D,+BAA+B,EAAM,QAAQ;GAC7C,+BAA+B,EAAM,QAAQ;GAC7C,4CAA4C,EAAM,WAAW;GAC7D,sBAAsB,EAAM,WAAW;GACvC,wBAAwB,EAAM,WAAW;GACzC,mBAAmB,EAAM,WAAW,WAAW,EAAM,WAAW;GAChE,cAAc,EAAM,WAAW;GAC/B,cAAc,EAAM;GACrB,EAAE;yBAGD,EAEM,OAAA;GAFA,OAAK,EAAE,EAAA,MAAO;GAAE,eAAY;OAC7B,EAAA,MAAY,EAAA,EAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE1CnB,IAAM,IAAQ,GAQR,IAAoB,QAAgB,CAAC,EAAM,SAAS,EAAM,UAAU,EAAM,SAAS,EAAM,YAAY,KAAA,EAAW,EAEhH,IAAU,QAAe;GAC7B,IAAM,IAAS,EAAE;AACjB,GAAI,EAAM,WAAW,EAA+B,SAAS,EAAM,QAAQ,GACzE,EAAO,KAAK,aAAa,EAAM,QAAQ,UAAU,GAEjD,EAAO,KAAK,aAAa,EAAM,UAAU;GAE3C,IAAM,IAAY,GAAoB,EAAM,KAAK;AAIjD,UAHI,KACF,EAAO,KAAK,EAAU,EAEjB;IACP;yBAIA,EASS,UAAA;GATD,MAAK;GAAU,OAAK,EAAE,EAAA,MAAO;GAAG,UAAU,EAAA;GAAW,cAAY,EAAA;GAAmB,eAAY;MACrF,EAAA,WAAA,GAAA,EAAjB,EAAsC,GAAA;;GAAZ,MAAK;cAC/B,EAMW,GAAA,EAAA,KAAA,GAAA,EAAA;GALa,EAAA,oBAAA,GAAA,EAAtB,EAAuF,GAAA;;IAA9C,OAAO,EAAA;IAAkB,SAAQ;;GAC5D,EAAA,YAAA,GAAA,EAAd,EAA6E,GAAA;;IAApD,MAAM,EAAA;IAAU,eAAY;;GACzC,EAAA,SAAA,GAAA,EAAZ,EAAmE,QAAnE,IAAmE,EAAf,EAAA,MAAK,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA;GACnC,EAAA,qBAAA,GAAA,EAAtB,EAAyF,GAAA;;IAA/C,OAAO,EAAA;IAAmB,SAAQ;;GAC9D,EAAA,aAAA,GAAA,EAAd,EAAgF,GAAA;;IAAtD,MAAM,EAAA;IAAW,eAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EER7D,IAAM,IAAQ,GAMR,IAAQ,EAAmB,GAAA,aAAE,EAE7B,IAAe,QACf,EAAM,YACD,EACL,gBAAgB,qBACjB,GAGI,EAAE,CACT;;GAIaA,EAAAA,OAAO,SAAS,EAAA,aAAA,GAAA,EAA7B,EAEQ,SAAA;;IAFgC,eAAY;IAAiB,OAAM;IAAuB,KAAK,EAAA;OACrG,EAAqB,EAAA,QAAA,QAAA,CAAA,EAAA,GAAA,EAAA,IAAA,EAAA,IAAA,GAAA;GAEvB,EAmCM,OAAA,EAlCJ,OAAK,EAAA,CAAC,wBAAsB,EAAA,cACE,EAAA,YAAA,CAAA,CAAA,EAAA,EAAA,CAKtB,EAAA,eAAA,GAAA,EADR,EAMM,OANN,IAMM,EADD,EAAA,YAAW,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA,EAEhB,EAqBM,OArBN,IAqBM,CApBkD,EAAA,aAAA,GAAA,EAAtD,EAAqF,GAAA;;IAA7E,OAAM;IAAoD,MAAM,EAAA;yCACxE,EAkBE,SAlBF,EACUC,EAiBR,QAjBc;6CACA,QAAA;IACd,eAAY;IACX,OAAK,CAAA,iBAAA;wBAA0E,EAAA;0BAA4C,EAAA,YAAO;;IAOlI,OAAK,CAAG,EAAA,OAAY,EAAA,OAAA,QAAA,CAAA;IACpB,UAAU,EAAA;IACV,MAAM,EAAA;IACN,aAAa,EAAA;IACb,IAAI,EAAA;IACJ,gBAAc,EAAA,QAAK,KAAU,KAAA;IAC7B,MAAM,EAAA;2BAfE,EAAA,MAAK,CAAA,CAAA,CAAA,CAAA,CAAA,EAAA,EAAA;GAoBZ,EAAA,cAAA,GAAA,EADR,EAYI,KAAA;;IAVF,eAAY;IACX,OAAK,EAAA,CAAA,mBAAA;oBAA4D,EAAA;yBAAmC,EAAA;;QAQlG,EAAA,WAAU,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE7FjB,IAAM,IAAQ,GAUR,IAAO,GAWP,IAAa,EAAmB,GAAA,aAAE,EAClC,IAAY,EAAmB,GAAC,YAA6B,EAE7D,IAAc,GAAO,EACrB,IAAW,GAAG,EAAY,WAC1B,IAAS,GAAG,EAAY,iBACxB,IAAa,GAAkB,EAC/B,IAAsB,GAAkB,EACxC,IAAgB,EAAM,QAAQ,WAAW,MAAW,EAAO,OAAO,EAAW,MAAM,EACnF,IAAc,EAAI,KAAK,IAAI,GAAe,EAAE,CAAC,EAE7C,IAAsB,QAAe,EAAM,iBAAiB,EAAE,EAC9D,IAAmB,QACvB,EAAY,QAAQ,EAAM,QAAQ,SAAS,EAAY,EAAY,MAAM,GAAG,KAAA,EAC7E;AAOD,EALA,EAAM,IAAa,MAAU;GAC3B,IAAM,IAAgB,EAAM,QAAQ,WAAW,MAAW,EAAO,OAAO,EAAM;AAC9E,GAAI,KAAiB,MAAG,EAAY,QAAQ;IAC5C,EAEF,QACQ,EAAM,QAAQ,KAAK,MAAW,EAAO,GAAG,GAC7C,GAAW,MAAsB;GAChC,IAAM,IAAgB,EAAU,WAAW,MAAO,MAAO,EAAW,MAAM;AAC1E,OAAI,KAAiB,GAAG;AACtB,MAAY,QAAQ;AACpB;;AAGF,OAAI,EAAY,UAAU,EAAkB,QAAQ;AAClD,MAAY,QAAQ,EAAU;AAC9B;;GAGF,IAAM,IAAmB,EAAkB,EAAY,QACjD,IAAiB,EAAU,QAAQ,EAAiB;AAC1D,KAAY,QACV,KAAkB,IAAI,IAAiB,KAAK,IAAI,EAAY,OAAO,KAAK,IAAI,EAAU,SAAS,GAAG,EAAE,CAAC;IAE1G;EAED,SAAS,EAAY,GAAe;AAClC,UAAO,GAAG,EAAY,UAAU;;EAGlC,SAAS,EAAa,GAAe;AAC9B,KAAM,QAAQ,WAEnB,EAAY,QAAQ,GACpB,QAAe,EAAW,OAAO,OAAO,CAAC;;EAG3C,SAAS,IAAiB;AAExB,GADA,EAAY,QAAQ,EAAM,QAAQ,QAClC,QAAe,EAAoB,OAAO,cAAgC,QAAQ,EAAE,OAAO,CAAC;;EAG9F,SAAS,EAAa,GAA0B,GAAe;AAG7D,GAFA,EAAY,QAAQ,GACpB,EAAW,QAAQ,EAAO,IAC1B,EAAK,UAAU,EAAO;;EAGxB,SAAS,EAAkB,GAA0B,GAAe;AAElE,GADA,EAAa,GAAQ,EAAM,EAC3B,QAAe,EAAW,OAAO,OAAO,CAAC;;EAG3C,SAAS,IAAqB;AAC5B,GAAI,EAAY,SAAS,EAAM,QAAQ,WAAQ,EAAY,QAAQ;;EAGrE,SAAS,EAAqB,GAAsB;AAClD,GAAI,EAAM,QAAQ,eAChB,EAAM,gBAAgB,EAClB,EAAY,SAAS,EAAM,QAAQ,SAAS,IAC9C,GAAgB,GAEhB,EAAY,SAAS,KAEd,EAAM,QAAQ,aACvB,EAAM,gBAAgB,EAClB,EAAY,SAAS,IACvB,GAAgB,GAEhB,IAAY,SAEL,EAAM,QAAQ,WAAW,EAAM,QAAQ,EAAY,WAC5D,EAAM,gBAAgB,EACtB,EAAa,EAAM,QAAQ,EAAY,QAAQ,EAAY,MAAM;;EAIrE,SAAS,IAAkB;AAGzB,GAFA,EAAY,QAAQ,EAAM,QAAQ,QAElC,EAAW,QAAQ,KAAA;;EAGrB,SAAS,EAAkB,GAAsB;AAC3C,UAAC,EAAM,QAAQ,WAAW,EAAM,QAAQ,eAAe,EAAM,QAAQ,aAEzE;QAAI,EAAM,QAAQ,YAEhB,CADA,EAAM,gBAAgB,EACtB,EAAa,EAAE;aACN,EAAM,QAAQ,UAEvB,CADA,EAAM,gBAAgB,EACtB,EAAa,EAAM,QAAQ,SAAS,EAAE;aAC7B,EAAM,QAAQ,WAAW,CAAC,EAAM,aAAa;AACtD,OAAM,gBAAgB;KACtB,IAAM,IAAQ,EAAU,MAAM,MAAM;AACpC,KAAI,KAAO,EAAK,UAAU,EAAM;;;;EAIpC,SAAS,IAAc;AACrB,KAAK,QAAQ;;EAGf,SAAS,IAAa;AACpB,KAAK,OAAO;;yBAKZ,EAmGU,WAAA;GAlGR,OAAM;GACN,eAAY;GACX,mBAAiB;GACjB,WAAO,GAAA,EAAmB,GAAU,CAAA,QAAA,UAAA,CAAA,EAAA,CAAA,MAAA,CAAA;;GAErC,EAcS,UAdT,IAcS,CAbP,EAMM,OANN,IAMM,CAFJ,EAA8D,MAAA;IAAzD,IAAI;IAAU,OAAM;QAAkB,EAAA,YAAW,EAAA,EAAA,EAC7C,EAAA,aAAA,GAAA,EAAT,EAAgF,KAAhF,IAAgF,EAAhB,EAAA,UAAS,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA,CAAA,CAAA,EAE3E,EAKM,OALN,IAKM,CAJQ,EAAA,SAAA,GAAA,EAAZ,EAEO,QAFP,IAEO,EADF,EAAA,gBAAe,GAAG,SAAI,EAAG,EAAA,eAAc,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA,EAE5C,EAA8F,GAAA;IAApF,WAAU;IAAQ,UAAS;IAAQ,MAAK;IAAK,SAAQ;IAAS,SAAO;;GAInF,EAuEM,OAvEN,IAuEM,CAtEJ,EAqEM,OArEN,IAqEM,CAjEJ,EAyCM,OAAA;aAxCA;IAAJ,KAAI;IACJ,OAAM;IACN,OAAA;KAAA,gBAAA;KAAA,cAAA;KAA8C;IAC9C,eAAY;IACZ,MAAK;IACL,UAAS;IACR,yBAAuB,EAAA;IACvB,oBAAkB;IAClB,mBAAiB;IACjB,SAAO;IACP,WAAS;eAEV,EA2BM,GAAA,MAAA,EA1BsB,EAAA,UAAlB,GAAQ,YADlB,EA2BM,OAAA;IAzBH,IAAI,EAAY,EAAK;IACrB,KAAK,EAAO;IACb,OAAM;IACN,eAAY;IACZ,MAAK;IACJ,iBAAe,EAAA,UAAe,EAAO;IACrC,eAAa,EAAA,UAAgB,KAAS,KAAA;IACtC,UAAK,MAAE,EAAkB,GAAQ,EAAK;IACtC,eAAU,MAAE,EAAA,QAAc;;IAE3B,EAAkE,GAAA;KAAjD,OAAO,IAAK;KAAM,MAAK;KAAK,SAAQ;;IACrD,EAES,QAFT,IAES,EADP,EAAO,MAAK,EAAA,EAAA;IAGN,EAAO,YAAA,GAAA,EADf,EAQO,QAAA;;KANL,OAAM;KACN,OAAA,EAAA,cAAA,KAAqB;KACpB,OAAO,EAAO;QAEf,EAAiE,GAAA;KAAzD,OAAM;KAAmB,MAAK;KAAe,MAAM;QAC3D,EAA6D,QAA7D,IAA6D,EAAzB,EAAO,SAAQ,EAAA,EAAA,CAAA,EAAA,GAAA,GAAA,IAAA,EAAA,IAAA,GAAA;IAEvC,EAAA,UAAe,EAAO,MAAA,GAAA,EAApC,EAA+F,GAAA;;KAAvD,OAAM;KAAkC,MAAK;;IACrF,EAA4E,GAAA;KAApE,OAAM;KAAuC,MAAK;;kCAI9D,EAqBM,OAAA;aApBA;IAAJ,KAAI;IACJ,OAAM;IACN,eAAY;IACX,eAAa,EAAA,UAAgB,EAAA,QAAQ,UAAU,KAAA;IAC/C,SAAO;IACP,cAAU,AAAA,EAAA,QAAA,MAAE,EAAA,QAAc,EAAA,QAAQ;;IAEnC,EAEO,QAFP,IAEO,CADL,EAAwC,GAAA;KAAhC,MAAK;KAAe,MAAM;;IAEpC,EAQM,OARN,IAQM,CAPJ,EAME,IAAA;iBALS,EAAA;8CAAS,QAAA;KACjB,cAAY,EAAA;KACZ,aAAa,EAAA;KACb,SAAO;KACP,WAAS;;;;;;IAGd,EAA4E,GAAA;KAApE,OAAM;KAAuC,MAAK;;;GAKhE,EAGS,UAHT,IAGS,CAFP,EAAiH,QAAA;IAA1G,IAAI;IAAQ,OAAM;MAAmC,iDAA8C,EAC1G,EAAoG,GAAA;IAA1F,OAAM;IAAmB,OAAM;IAAO,MAAK;IAAK,SAAQ;IAAa,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EEtP5F,IAAM,IAAQ,GAOR,IAAO,GAKP,IAAY,QACV,EAAM,UAAU,wBAAwB,EAAM,aAAa,KAAA,KAAa,EAAM,SAAS,aAAa,KAAA,EAC3G,EACK,IAAa,QAEf,EAAM,UAAU,aAAa,OAC5B,EAAM,UAAU,wBAAwB,EAAM,UAAU,qBAAqB,EAAM,UAAU,oBACjG,EACK,IAAa,QAAe,EAAM,UAAU,aAAa,MAAS,EAAM,UAAU,gBAAgB;EAExG,SAAS,EAAQ,GAAmB;AAClC,KAAK,WAAW;IAAE,IAAI,EAAM,SAAU;IAAI;IAAU,CAAC;;mBAM7C,EAAA,SAAa,EAAA,SAAc,EAAA,SAAA,GAAA,EADnC,EAyBU,WAzBV,IAyBU,CAnBQ,EAAA,SAAA,GAAA,EAAhB,EAQW,GAAA,EAAA,KAAA,GAAA,EAAA,CAPT,EAEM,OAFN,IAEM,CADJ,EAA0B,EAAA,QAAA,WAAA,EAAA,QAAA,CAAA,EAAA,EAAjB,EAAA,QAAO,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,EAElB,EAGM,OAHN,IAGM,CAFJ,EAA4G,GAAA;GAAlG,SAAQ;GAAY,MAAK;GAAM,OAAO,EAAA;GAAc,UAAU,EAAA;GAAW,SAAK,AAAA,EAAA,QAAA,MAAE,EAAO,GAAA;sCACjG,EAAwF,GAAA;GAA9E,MAAK;GAAM,OAAO,EAAA;GAAe,UAAU,EAAA;GAAW,SAAK,AAAA,EAAA,QAAA,MAAE,EAAO,GAAA;gDAIpE,EAAA,SAAA,GAAA,EAAd,EAGI,KAHJ,IAGI,CAFF,EAAyC,GAAA;GAAjC,MAAK;GAAgB,MAAM;MACnC,EAA8D,EAAA,QAAA,YAAA,EAAA,QAAA,CAAA,AAAA,EAAA,OAAA,EAAxC,qCAAiC,GAAA,CAAA,CAAA,CAAA,CAAA,KAAA,GAAA,EAGzD,EAGI,KAHJ,IAGI,CAFF,EAAyC,GAAA;GAAjC,MAAK;GAAgB,MAAM;MACnC,EAA8D,EAAA,QAAA,YAAA,EAAA,QAAA,CAAA,AAAA,EAAA,OAAA,EAAxC,qCAAiC,GAAA,CAAA,CAAA,CAAA,CAAA,EAAA,CAAA,IAAA,EAAA,IAAA,GAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;EE7B7D,IAAM,IAAQ,EAAmB,GAAA,aAAE;yBAIjC,EAgCM,OAhCN,IAgCM;GA/BSC,EAAAA,OAAO,SAAA,GAAA,EAApB,EAEQ,SAAA;;IAFmB,OAAM;IAAY,KAAK,EAAA;OAChD,EAAqB,EAAA,QAAA,QAAA,CAAA,EAAA,GAAA,GAAA,IAAA,EAAA,IAAA,GAAA;KAEvB,EAcE,YAdF,EACUC,EAaR,QAbc;6CACA,QAAA;IACb,OAAK,CAAA,eAAA,EAAA,qBAAoE,EAAA,YAAO,QAAA,CAAA;IAMhF,MAAM,EAAA;IACN,UAAU,EAAA;IACV,aAAa,EAAA;IACb,IAAI,EAAA;IACJ,gBAAc,EAAA,QAAK,KAAU,KAAA;2BAXrB,EAAA,MAAK,CAAA,CAAA;GAcR,EAAA,cAAA,GAAA,EADR,EAYI,KAAA;;IAVF,OAAA,EAAA,eAAA,QAAyB;IACxB,OAAK,EAAA,CAAA,mBAAA;oBAAkE,EAAA;yBAAqC,EAAA;;QAQ1G,EAAA,WAAU,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EExDnB,IAAM,IAAQ,GAWR,IAAO,GAOP,IAAQ,EAAmB,GAAA,aAAiB,EAC5C,IAAU,GAAsB,EAChC,IAAS,QAAe,EAAM,WAAW,eAAe,EAAM,WAAW,YAAY,EACrF,IAAY,QAAe,CAAC,EAAM,YAAY,CAAC,EAAO,SAAS,EAAM,MAAM,MAAM,CAAC,SAAS,EAAE;EAEnG,SAAS,IAAS;AACX,KAAU,SACf,EAAK,UAAU,EAAM,MAAM,MAAM,CAAC;;EAGpC,SAAS,IAAQ;AACf,KAAQ,OAAO,cAAc,WAAW,EAAE,OAAO;;EAKnD,SAAS,EAAe,GAAmB;AACrC,KAAM,YAEK,EAAM,QACT,QAAQ,iDAAiD,IAErE,GAAO;;EAGT,SAAS,EAAc,GAAsB;AACvC,KAAM,QAAQ,WAAW,EAAM,YAAY,EAAM,gBACrD,EAAM,gBAAgB,EACtB,EAAM,iBAAiB,EACvB,GAAQ;;SAGV,EAAa,EAAE,UAAO,CAAC,kBAIrB,EA4CO,QAAA;YA3CD;GAAJ,KAAI;GACJ,OAAM;GACN,eAAY;GACX,aAAW,EAAA;GACX,UAAM,EAAU,GAAM,CAAA,UAAA,CAAA;GACtB,SAAO;;GAEGC,EAAAA,OAAO,UAAA,GAAA,EAAlB,EAEM,OAFN,IAEM,CADJ,EAAsB,EAAA,QAAA,SAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA;GAGxB,EAUE,IAAA;gBATS,EAAA;6CAAK,QAAA;IACd,OAAM;IACL,OAAK,EAAA,EAAA,6BAAiC,EAAA,WAAS,CAAA;IAC/C,MAAM,EAAA;IACN,WAAW,EAAA;IACX,aAAa,EAAA;IACb,UAAU,EAAA;IACV,cAAY,EAAA;IACZ,WAAS;;;;;;;;;;GAGZ,EAaM,OAbN,IAaM,CAZJ,EAEM,OAFN,IAEM,CADJ,EAAuB,EAAA,QAAA,UAAA,CAAA,CAAA,EAEzB,EAQE,GAAA;IAPC,MAAM,EAAA,QAAM,WAAA;IACZ,SAAS,EAAA,QAAM,cAAA;IAChB,MAAK;IACJ,aAAW,EAAA,QAAM,SAAA;IACjB,cAAY,EAAA,QAAS,EAAA,YAAY,EAAA;IACjC,UAAU,EAAA,YAAQ,CAAM,EAAA,SAAM,CAAK,EAAA;IACnC,SAAK,AAAA,EAAA,QAAA,MAAE,EAAA,SAAU,EAAI,OAAA;;;;;;;;GAI1B,EAII,KAJJ,IAII,CAHc,EAAA,WAAM,eAAA,GAAA,EAAtB,EAAqE,GAAA,EAAA,KAAA,GAAA,EAAA,CAAA,EAA7B,qBAAkB,CAAA,EAAA,GAAA,IACrC,EAAA,WAAM,eAAA,GAAA,EAA3B,EAA8E,GAAA,EAAA,KAAA,GAAA,EAAA,CAAA,EAAjC,yBAAsB,CAAA,EAAA,GAAA,IAC9C,EAAA,WAAM,WAAA,GAAA,EAA3B,EAAkF,GAAA,EAAA,KAAA,GAAA,EAAA,CAAA,EAAzC,iCAA8B,CAAA,EAAA,GAAA,IAAA,EAAA,IAAA,GAAA,CAAA,CAAA;;;IExGhE,KAAsB;CAAC;CAAS;CAAa;CAAa;CAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ECqC/E,IAAM,IAAQ,GAQR,IAAgB,EAAoB,GAAA,aAAE,EAEtC,IAAO;AAMb,EAAI,EAAM,gBAAgB,KAAA,MACxB,EAAc,QAAQ,EAAM;EAG9B,IAAM,KAAwB,MAAiB;AAE7C,KAAc,QADS,EAAM,OACQ;KAGjC,KAAsB,MAAsB;AAE5C,KAAM,WAAW,MAChB,EAAM,QAA2B,QAAQ,8BAA8B,IAExE,EAAM,qBACV,EAAM,gBAAgB,EACtB,EAAK,iBAAiB,EAAM;KAOxB,IAAmB,QAAgB,EAAM,sBAAsB,EAAE,OAAO,GAAoB,GAAG,EAAE,CAAE,EAEnG,IAAgB,EACpB,OAAO,EAAM,yBAAyB,SAAS,eAChD,EAEK,IAAe,QACf,EAAM,mBAAmB,eACvB,EAAM,oBAAoB,SACrB,EAAc,QAAQ,iBAAiB,kBAEvC,EAAc,QAAQ,iBAAiB,iBAGzC,EAAc,QAAQ,eAAe,eAE9C;yBAIA,EA0BU,WAAA;GA1BD,eAAY;GAAe,OAAM;GAAgB,MAAM,EAAA;GAAgB,UAAQ;MACtF,EAqBU,WArBV,EACU,EAoBA,cApBY;GACnB,OAAK,CAAA,wCAA2C,EAAA,eAAc;GAC9D,OAAK;IAAA,GAAO;IAAa,GAAK,EAAA;IAAa;GAC3C,cAAU,AAAA,EAAA,QAAA,MAAEC,EAAAA,MAAK,qBAAA;GACjB,cAAU,AAAA,EAAA,QAAA,MAAEA,EAAAA,MAAK,qBAAA;KAClB,EAAM,EAAiB,OAAD,GAAA,CAAA,EAAA;GAGtB,EAAuB,EAAA,QAAA,WAAA,EAAA,EAAA,KAAA,GAAA,GAAA;GACT,EAAA,oBAAe,UAAA,GAAA,EAA7B,EAA2F,GAAA;;IAAhD,MAAM,EAAA;IAAc,6BAAA;;GAC/C,EAAA,oBACd,EAAuB,EAAA,QAAA,WAAA,EAAA,KAAA,GAAA,EAAA,KAAA,GAAA,GAAA,IAAA,GAAA,EAEzB,EAKW,GAAA,EAAA,KAAA,GAAA,EAAA,CAJT,EAEO,QAAA,MAAA,EADF,EAAA,OAAM,EAAA,EAAA,EAEW,EAAA,WAAA,GAAA,EAAtB,EAAgF,GAAA;;IAAhD,OAAO,EAAA;IAAS,SAAQ;IAAY,MAAK;;GAE7D,EAAA,oBAAe,WAAA,GAAA,EAA7B,EAA4F,GAAA;;IAAhD,MAAM,EAAA;IAAc,6BAAA;;UAElE,EAEM,OAAA,EAFA,OAAK,EAAE,EAAA,cAAa,EAAA,EAAA,CACxB,EAAQ,EAAA,QAAA,WAAA,EAAA,EAAA,KAAA,GAAA,GAAA,CAAA,EAAA,EAAA,CAAA,EAAA,IAAA,GAAA;;;;;AElFd,SAAS,EAAqB,GAAY,GAA0B;AAClE,KAAI,EAAK,aAAa,KAAK,UAAW,QAAO;CAC7C,IAAM,IAAM,EAAK,eAAe;AAChC,QAAO,IAAkB,EAAI,SAAS,IAAI,EAAI,MAAM,CAAC,SAAS;;AAGhE,SAAS,GAAwB,GAAmB,GAAc,GAAoD;CACpH,IAAM,IAAW,MAAS,YAAY,KAAK,GACrC,IAAQ,MAAM,KAAK,EAAK,WAAW;AAczC,QAZI,MAAa,KAER,EAAM,MAAM,MACb,EAAE,aAAa,KAAK,eAEf,CADI,EACA,aAAa,OAAO,GAE1B,EAAqB,GAAG,EAAiC,CAChE,GAIG,EAAM,MAAM,MACb,EAAE,aAAa,KAAK,eACb,EACD,aAAa,OAAO,KAAK,IAFU,GAG7C;;AAcJ,SAAS,GAAuB,GAAkD;AAChF,KAAI,EAAK,WAAY,QAAO;CAC5B,IAAM,IAAS,EAA0B;AAEzC,QADI,CAAC,KAAS,OAAO,KAAU,WAAiB,OACzC;;AAGT,SAAS,GACP,GACA,GACA,GACS;CAET,IAAM,IAAQ,EAAM,MAAS,KAAK,YAAY;AAE9C,QADK,IACE,EAAM,MAAM,MACb,EAAE,aAAa,KAAK,eAAqB,KACtC,EAAqB,GAAG,EAAiC,CAChE,GAJiB;;AAOrB,SAAS,EAAgB,GAAmB,GAAsC;CAChF,IAAM,IAAK,EAAK;AAKhB,QAJK,IACD,MAAS,aAAa,MAAS,KAC1B,EAAG,cAAc,mBAAmB,GAEtC,EAAG,cAAc,cAAc,IAAI,OAAO,EAAK,CAAC,IAAI,GAJ3C;;AAelB,SAAgB,GACd,GACA,GACA,EAAE,mBAAgB,IAAM,sCAAmC,OAAU,EAAE,EAC9D;CACT,IAAM,IAAS,IAAgB,EAAgB,GAAM,EAAK,GAAG;AAC7D,KAAI,EACF,QAAO,EAAO,cAAc,EAAE,SAAS,IAAM,CAAC,CAAC,MAAM,MAC/C,EAAE,aAAa,KAAK,eAAqB,KACtC,EAAqB,GAAG,EAAiC,CAChE;CAMJ,IAAM,IAAc,GAAuB,EAAK;AAGhD,QAFI,IAAoB,GAAqB,GAAa,GAAM,EAAiC,GAE1F,GAAwB,GAAM,GAAM,EAAiC;;AAU9E,SAAgB,GAAgB,GAAkB,IAAkC,EAAE,EAAE;CACtF,IAAM,EAAE,SAAM,aAAU,aAAU,IAAM,sBAAmB,IAAM,sCAAmC,OAAU,GAExG,IAAiB,GAAmB,EACpC,IAAQ,KAAY,GAAU,EAK9B,IAAa,MAAa,aAAa,MAAa,KAAK,YAAY,GACrE,UAAoB,CAAC,CAAC,KAAS,CAAC,CAAE,EAAkC,IACpE,IAAa,EAAI,GAAa,CAAC;AACrC,SAAqB;AACnB,IAAW,QAAQ,GAAa;GAChC;CAGF,IAAM,IAAgB,EAAI,GAAM,EAE5B,IAA8B,MAC9B,IAAqC,MAEnC,UAAgB;AAKpB,EAJA,AAEE,OADA,EAAG,YAAY,EACV,OAEP,AAEE,OADA,EAAW,oBAAoB,cAAc,EAAa,EAC7C;IAIX,UAAqB;AACzB,MAAI,CAAC,EAAe,OAAO;AACzB,KAAc,QAAQ;AACtB;;EAGF,IAAM,IAAK,EAAM,EAAK;AACtB,MAAI,CAAC,GAAI;AACP,KAAc,QAAQ;AACtB;;AAGF,IAAc,QAAQ,GAAmB,GAAI,GAAU;GAErD,eAAe;GACf;GACD,CAAC;IAGE,UAAe;AACnB,MAAI,CAAC,EAAe,MAAO;AAE3B,KAAS;EAET,IAAM,IAAK,EAAM,EAAK;AACtB,MAAI,CAAC,GAAI;AACP,KAAc,QAAQ;AACtB;;AAUF,EAPA,GAAc,EAEV,MACF,IAAa,EAAgB,GAAI,EAAS,EACtC,KAAY,EAAW,iBAAiB,cAAc,EAAa,GAGrE,MACF,IAAK,IAAI,uBAAuB,GAAc,CAAC,EAC/C,EAAG,QAAQ,GAAI;GACb,WAAW;GACX,SAAS;GACT,YAAY;GACZ,iBAAiB,CAAC,OAAO;GACzB,eAAe;GAChB,CAAC;;AAkBN,QAdA,EAAU,EAAO,EACjB,EAAgB,EAAQ,EAGxB,QACQ,EAAM,EAAK,QACX;AACJ,EAAI,EAAe,SAAO,GAAQ;GAErC,EAKM;EACL,SAHc,QAAgB,EAAe,QAAQ,EAAc,QAAQ,EAAW,MAAO;EAI7F;EACA;EACA,SAAS;EACV;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EC/OH,IAAM,IAAQ,GAQR,IAAgB,QAAe;AACnC,QAAK,IAAI,IAAQ,EAAM,MAAM,SAAS,GAAG,KAAS,GAAG,IACnD,KAAI,EAAM,MAAM,GAAO,YAAY,QAAS,QAAO;AAErD,UAAO;IACP;EAEF,SAAS,EAAS,GAAc,GAA6B;AAG3D,UAFI,EAAK,SAAe,EAAK,SACzB,EAAK,YAAY,UAAgB,aAC9B,EAAM,cAAc,MAAU,EAAc,QAAQ,WAAW;;EAKxE,SAAS,EAAa,GAAc,GAAuB;AACzD,UAAO,EAAS,GAAM,EAAM,KAAK,WAAW,mCAAmC;;yBAK/E,EA0CkB,GAAA;GA1CD,KAAI;GAAK,MAAK;GAAU,OAAM;GAAe,MAAK;;oBAC/B,EAAA,EAAA,GAAA,EAAlC,EAwCK,GAAA,MAAA,EAxCuB,EAAA,QAAhB,GAAM,YAAlB,EAwCK,MAAA;IAxC+B,KAAK,EAAK,MAAM;IAAO,OAAM;IAAW,eAAa,EAAS,GAAM,EAAK;OAC/F,EAAK,YAAO,UAEkB,EAAA,IAAA,GAAA,IAFlB,GAAA,EAAxB,EAGO,QAHP,IAGO,CAFS,EAAK,QAAA,GAAA,EAAnB,EAAwD,GAAA;;IAA9B,MAAM,EAAK;IAAO,MAAM;kCAClD,EAAwC,QAAxC,GAAwC,EAAA,CAAA,GAG1C,EAiCM,OAjCN,IAiCM,CAhCK,EAAK,YAAO,WAAA,GAAA,EAArB,EAEI,KAFJ,IAEI,CADF,EAAyB,MAAA,MAAA,EAAlB,EAAK,MAAK,EAAA,EAAA,CAAA,CAAA,KAAA,GAAA,EAGnB,EA2BW,GAAA,EAAA,KAAA,GAAA,EAAA;IAxBT,EAGI,KAAA;KAHD,OAAK,EAAA,CAAC,2BAAkC,EAAa,GAAM,EAAK,CAAA,CAAA;KAAG,OAAA,EAAA,eAAA,QAAyB;QAC7F,EAA2F,QAAA,EAApF,OAAK,EAAA,EAAA,cAAkB,EAAS,GAAM,EAAK,KAAA,UAAA,CAAA,EAAA,EAAA,EAAqB,EAAK,MAAK,EAAA,EAAA,EACrE,EAAK,YAAA,GAAA,EAAjB,EAA8E,QAA9E,IAA8E,EAAvB,EAAK,SAAQ,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA,CAAA,EAAA,EAAA;IAE7D,EAAK,QAAA,GAAA,EAAd,EAAgF,KAAhF,IAAgF,EAAhB,EAAK,KAAI,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA;IAEjE,EAAK,QAAQ,EAAK,KAAK,SAAM,KAAA,GAAA,EADrC,EAeK,MAfL,IAeK,EAAA,EAAA,GAAA,EATH,EAQK,GAAA,MAAA,EAPuB,EAAK,OAAvB,GAAK,YADf,EAQK,MAAA;KANF,KAAK;KACN,OAAK,EAAA,CAAC,oBAAkB,EAAA,sBACQ,EAAI,YAAO,SAAA,CAAA,CAAA;KAC3C,cAAW;SAER,EAAI,MAAK,EAAA,EAAA;IAGP,EAAK,WAAA,GAAA,EAAd,EAEI,KAFJ,IAA4E,WAC7D,EAAG,EAAK,QAAO,EAAA,EAAA,IAAA,EAAA,IAAA,GAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EE9DxC,IAAM,IAAQ,GASR,IAAO,EAAoB,GAAC,OAA2B,EACvD,IAAmB,GAAa,EAChC,IAAY,GAAa,EACzB,IAAmB,EAAI,GAAM,EAC/B;AAGJ,GADI,EAAM,eAAe,EAAM,iBAAa,EAAK,QAAQ,KACrD,EAAM,gBAAa,EAAU,QAAQ,KAAK,KAAK;EAEnD,IAAM,IAAoB,QAAe,EAAM,YAAY,EAAiB,MAAM,EAC5E,IAAe,QAAe;AAClC,OAAI,EAAM,YAAa,QAAO,EAAM;AACpC,OAAI,EAAkB,UAAU,KAAA,EAAW,QAAO,EAAM;GACxD,IAAM,IAAkB,KAAK,IAAI,GAAG,KAAK,MAAM,EAAkB,MAAM,CAAC;AACxE,UAAO,eAAe,EAAgB,GAAG,MAAoB,IAAI,WAAW;IAC5E,EAEI,IAAQ,GAAU,EAClB,IAAa,EAAmC,aAAa,EAO7D,EAAE,eAAe,MAAmB,GAAgB,WAAW,EAAE,MAJtD,QAAe;GAC9B,IAAM,IAAW,EAAW,OAAO,aAAa;AAChD,UAAO,aAAoB,aAAc,EAAS,OAAuB;IACzE,EACqF,CAAC;EAKxF,SAAS,EAAe,GAA0B;AAChD,UAAO,EAAO,MAAM,MACd,EAAM,SAAS,IAAgB,KAC/B,EAAM,SAAS,IAAa,OAAO,EAAM,YAAY,GAAG,CAAC,MAAM,CAAC,SAAS,IACzE,EAAM,SAAS,IAAiB,MAAM,QAAQ,EAAM,SAAS,IAAI,EAAe,EAAM,SAAoB,GACvG,GACP;;EAQJ,SAAS,IAAwB;AAE/B,UADI,EAAM,MAAM,SAAS,KAAK,EAAe,QAAc,KACpD,EAAe,EAAM,UAAU,EAAE,CAAC,IAAI,EAAE,CAAC;;EAGlD,SAAS,IAAkB;AACzB,GAAI,MAAe,KAAA,MACjB,aAAa,EAAW,EACxB,IAAa,KAAA;;EAQjB,SAAS,EAAY,GAAmB;GAEtC,IAAM,IADS,EAAM,QACG,UAAU,UAAU;AACxC,IAAC,KAAW,EAAQ,kBAAkB,EAAM,kBAChD,EAAiB,QAAQ,IACzB,GAAiB;;SAGnB,QACQ,EAAM,cACX,GAAa,MAAiB;AAG7B,OAFA,GAAiB,EAEb,GAAa;AAIf,IAHA,EAAU,QAAQ,KAAK,KAAK,EAC5B,EAAiB,QAAQ,KAAA,GACzB,EAAiB,QAAQ,IACzB,EAAK,QAAQ;AACb;;AAGG,SACD,EAAU,UAAU,KAAA,MACtB,EAAiB,SAAS,KAAK,KAAK,GAAG,EAAU,SAAS,MAEvD,EAAiB,UACpB,IAAa,iBAAiB;AAC5B,IAAK,EAAiB,UAAO,EAAK,QAAQ;MACzC,EAAM,eAAe;IAG7B,EAED,EAAgB,EAAgB,YAKtB,GAAqB,IAAA,GAAA,EAD7B,EA+Bc,GAAA;;eA7BH,EAAA;4CAAI,QAAA;GACZ,gBAAc,EAAA;GACf,OAAM;GACN,oBAAiB;GACjB,mBAAgB;GAChB,6BAAA;GACA,uBAAA;GACC,mBAAiB,EAAA,oBAAA,IAA4B;GAC7C,kBAAgB,EAAA,gBAAA,cAAgC;GAChD,SAAO;;GAEG,SAAO,QAWT,CAPP,EAOO,QAAA;aAPG;IAAJ,KAAI;IAAa,OAAM;IAA8D,aAAU;OACnG,EAKO,EAAA,QAAA,WAAA,EAAA,QAAA,CAJL,EAAuB,GAAA,EAAf,MAAK,SAAO,CAAA,EAGpB,EAA8F,QAAA,EAAxF,OAAK,EAAA,CAAC,mBAAiB,EAAA,cAAyB,EAAA,aAAW,CAAA,CAAA,EAAA,EAAA,EAAO,EAAA,MAAY,EAAA,EAAA,CAAA,CAAA,CAAA,EAAA,IAAA,CAAA,CAAA;oBAQpF,CAHN,EAGM,OAHN,IAGM,CAFc,EAAA,MAAM,SAAM,KAAA,GAAA,EAA9B,EAAgF,GAAA;;IAA3C,OAAO,EAAA;IAAQ,eAAa,EAAA;uDACjE,EAAQ,EAAA,QAAA,UAAA,CAAA,CAAA,CAAA,CAAA;;iDAIZ,EAUO,QAAA;;YARD;GAAJ,KAAI;GACJ,OAAM;GACN,aAAU;MAEV,EAGO,EAAA,QAAA,WAAA,EAAA,QAAA,CAFL,EAAuB,GAAA,EAAf,MAAK,SAAO,CAAA,EACpB,EAA8F,QAAA,EAAxF,OAAK,EAAA,CAAC,mBAAiB,EAAA,cAAyB,EAAA,aAAW,CAAA,CAAA,EAAA,EAAA,EAAO,EAAA,MAAY,EAAA,EAAA,CAAA,CAAA,CAAA,EAAA,IAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EErJ1F,IAAM,IAAQ,GAIR,IAAO,EAAoB,GAAC,OAA2B,EACvD,IAAwC,CAAC,oBAAoB,eAAe;AAElF,GAAI,EAAM,eAAe,EAAgB,SAAS,EAAM,MAAM,MAAE,EAAK,QAAQ;EAE7E,IAAM,IAAS,SACsE;GACjF,mBAAmB;IAAE,OAAO;IAAW,WAAW;IAAmB;GACrE,mBAAmB;IAAE,OAAO;IAAW,WAAW;IAAkB;GACpE,sBAAsB;IAAE,OAAO;IAAqB,WAAW;IAAiB;GAChF,sBAAsB;IAAE,OAAO;IAAa,WAAW;IAAmB;GAC1E,oBAAoB;IAAE,OAAO;IAAa,WAAW;IAAkB;GACvE,gBAAgB;IAAE,OAAO;IAAS,WAAW;IAAgB;GAC7D,iBAAiB;IAAE,OAAO;IAAU,WAAW;IAAgB;GAChE,EACe,EAAM,OACtB;EAEF,SAAS,EAAY,GAAgB;AACnC,OAAI,OAAO,KAAU,SAAU,QAAO;AACtC,OAAI;AACF,WAAO,KAAK,UAAU,GAAO,MAAM,EAAE,IAAI,OAAO,EAAM;WAChD;AACN,WAAO,OAAO,EAAM;;;SAIxB,QACQ,EAAM,QACX,GAAO,MAAkB;AACxB,GAAI,MAAU,KAAiB,EAAgB,SAAS,EAAM,KAAE,EAAK,QAAQ;IAEhF,kBAIC,EA8Cc,GAAA;eA7CH,EAAA;4CAAI,QAAA;GACZ,gBAAc,EAAA;GACf,OAAM;GACN,oBAAiB;GACjB,mBAAgB;GAChB,6BAAA;GACA,uBAAA;GACC,mBAAiB,EAAA,sBAAA,IAA8B;;GAErC,SAAO,QAKT,CAJP,EAIO,QAJP,IAIO;IAHL,EAAqC,GAAA;KAA7B,MAAK;KAAY,MAAM;;IAC/B,EAAgE,QAAhE,IAAgE,EAAlB,EAAA,SAAQ,EAAA,EAAA;IACtD,EAAsF,QAAA;KAA/E,OAAK,EAAA,CAAG,EAAA,MAAO,WAAS,YAAA,CAAA;KAAgB,MAAK;SAAY,EAAA,MAAO,MAAK,EAAA,EAAA;;oBA+B1E,CA3BN,EA2BM,OA3BN,IA2BM,CA1BJ,EAyBM,OAzBN,IAyBM,CAxBWC,EAAAA,OAAO,SAAS,EAAA,UAAU,KAAA,KAAA,GAAA,EAAzC,EAOU,WAPV,IAOU,CAAA,AAAA,EAAA,OANR,EAA4C,MAAA,EAAxC,OAAM,oBAAkB,EAAC,cAAU,GAAA,EACvC,EAIO,EAAA,QAAA,SAAA,EAJa,OAAO,EAAA,OAAK,QAIzB,CAHL,EAE4C,OAF5C,IAE4C,CAA3C,EAAqC,QAAA,MAAA,EAA5B,EAAY,EAAA,MAAK,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA,EAIhB,EAAA,aAAA,GAAA,EAAf,EAKU,WALV,IAKU,CAAA,AAAA,EAAA,OAJR,EAAwD,MAAA,EAApD,OAAM,qCAAmC,EAAC,SAAK,GAAA,EACnD,EAEQ,OAFR,IAEQ,EADN,EAAA,UAAS,EAAA,EAAA,CAAA,CAAA,IAIOA,EAAAA,OAAO,UAAU,EAAA,WAAW,KAAA,KAAA,GAAA,EAAhD,EAOU,WAPV,IAOU,CAAA,AAAA,EAAA,OANR,EAAwC,MAAA,EAApC,OAAM,oBAAkB,EAAC,UAAM,GAAA,EACnC,EAIO,EAAA,QAAA,UAAA,EAJc,QAAQ,EAAA,QAAM,QAI5B,CAHL,EAE6C,OAF7C,IAE6C,CAA5C,EAAsC,QAAA,MAAA,EAA7B,EAAY,EAAA,OAAM,CAAA,EAAA,EAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,IAAA,EAAA,IAAA,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA;;;;IE1F3B,IAAsB;CACjC;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;ACaD,SAAgB,GACd,GACA,IAAmC,EAAE,EACnB;CAClB,IAAM,IAAY,EAAQ,aAAa,IACjC,IAAa,EAAI,GAAK;CAE5B,SAAS,IAAS;EAChB,IAAM,IAAK,EAAU;AACrB,EAAI,MAAI,EAAW,QAAQ,EAAG,eAAe,EAAG,YAAY,EAAG,gBAAgB;;CAGjF,SAAS,EAAe,IAA2B,UAAU;EAC3D,IAAM,IAAK,EAAU;AAChB,QACL,EAAG,SAAS;GAAE,KAAK,EAAG;GAAc;GAAU,CAAC,EAC/C,EAAW,QAAQ;;CAGrB,IAAI;CAEJ,SAAS,EAAO,GAAiB;EAC/B,IAAM,UAAiB,GAAQ;AAC/B,IAAG,iBAAiB,UAAU,GAAU,EAAE,SAAS,IAAM,CAAC;EAI1D,IAAM,UAAsB;AAC1B,GAAI,EAAW,SAAO,EAAe,OAAO;KAExC,IAA+C,EAAE,EAKnD,GACE,oBAAW,IAAI,SAAkB,EACjC,UAA2B;AAC1B,SACL,MAAK,IAAM,KAAS,MAAM,KAAK,EAAG,SAAS,CACzC,CAAK,EAAS,IAAI,EAAM,KACtB,EAAS,IAAI,EAAM,EACnB,EAAe,QAAQ,EAAM;;AAUnC,MANI,OAAO,iBAAmB,QAC5B,IAAiB,IAAI,eAAe,EAAc,EAClD,EAAe,QAAQ,EAAG,EAC1B,GAAoB,EACpB,EAAU,KAAK,EAAe,GAE5B,OAAO,mBAAqB,KAAa;GAG3C,IAAM,IAAkB,IAAI,uBAAuB;AAEjD,IADA,GAAoB,EACpB,GAAe;KACf;AAEF,GADA,EAAgB,QAAQ,GAAI,EAAE,WAAW,IAAM,CAAC,EAChD,EAAU,KAAK,EAAgB;;AAKjC,EAFA,GAAQ,EAER,UAAe;AAEb,GADA,EAAG,oBAAoB,UAAU,EAAS,EAC1C,EAAU,SAAS,MAAa,EAAS,YAAY,CAAC;;;AAgB1D,QAZA,EACE,IACC,MAAO;AAGN,EAFA,KAAU,EACV,IAAS,KAAA,GACL,KAAI,EAAO,EAAG;IAEpB,EAAE,WAAW,IAAM,CACpB,EAED,QAAqB,KAAU,CAAC,EAEzB;EAAE;EAAY;EAAgB"}