@alfadocs/ui-kit-debug 1.68.0 → 1.69.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.
- package/dist/_chunks/{alia-sidebar-B06ZSOaF.js → alia-sidebar-DEMMoefU.js} +3 -3
- package/dist/_chunks/{alia-sidebar-B06ZSOaF.js.map → alia-sidebar-DEMMoefU.js.map} +1 -1
- package/dist/_chunks/{anamnesis-card-CsUBOG32.js → anamnesis-card-Bey9qQu4.js} +2 -2
- package/dist/_chunks/{anamnesis-card-CsUBOG32.js.map → anamnesis-card-Bey9qQu4.js.map} +1 -1
- package/dist/_chunks/{anamnesis-summary-rows-CNK0fIJk.js → anamnesis-summary-rows-rNwSrNWZ.js} +2 -2
- package/dist/_chunks/{anamnesis-summary-rows-CNK0fIJk.js.map → anamnesis-summary-rows-rNwSrNWZ.js.map} +1 -1
- package/dist/_chunks/{appointment-peek-D5XCfYKu.js → appointment-peek-CpcqB8oO.js} +2 -2
- package/dist/_chunks/{appointment-peek-D5XCfYKu.js.map → appointment-peek-CpcqB8oO.js.map} +1 -1
- package/dist/_chunks/{calendar-workspace-CYK1M83j.js → calendar-workspace-Ct8_454k.js} +3 -3
- package/dist/_chunks/{calendar-workspace-CYK1M83j.js.map → calendar-workspace-Ct8_454k.js.map} +1 -1
- package/dist/_chunks/{chat-message-Be-9vIIQ.js → chat-message-C2VXQMoB.js} +2 -2
- package/dist/_chunks/{chat-message-Be-9vIIQ.js.map → chat-message-C2VXQMoB.js.map} +1 -1
- package/dist/_chunks/{code-block-vwEv1jEa.js → code-block-RSKdF3E-.js} +35 -35
- package/dist/_chunks/code-block-RSKdF3E-.js.map +1 -0
- package/dist/_chunks/{connection-status--wYsgreh.js → connection-status-BOZC5QU5.js} +2 -2
- package/dist/_chunks/{connection-status--wYsgreh.js.map → connection-status-BOZC5QU5.js.map} +1 -1
- package/dist/_chunks/{description-list-Q8640NHu.js → description-list-DQQ2OA_t.js} +68 -66
- package/dist/_chunks/description-list-DQQ2OA_t.js.map +1 -0
- package/dist/_chunks/{optical-prescription-ulXnDbBe.js → optical-prescription-D5c7FBlT.js} +2 -2
- package/dist/_chunks/{optical-prescription-ulXnDbBe.js.map → optical-prescription-D5c7FBlT.js.map} +1 -1
- package/dist/_chunks/{patient-details-C4Dyo7Ck.js → patient-details-qWOsn1MX.js} +3 -3
- package/dist/_chunks/{patient-details-C4Dyo7Ck.js.map → patient-details-qWOsn1MX.js.map} +1 -1
- package/dist/_chunks/{patient-field-row-DROvUkFu.js → patient-field-row-CAl5ydtX.js} +2 -2
- package/dist/_chunks/{patient-field-row-DROvUkFu.js.map → patient-field-row-CAl5ydtX.js.map} +1 -1
- package/dist/_chunks/{patient-summary-card-CAimxbuI.js → patient-summary-card-BhuRh9AE.js} +2 -2
- package/dist/_chunks/{patient-summary-card-CAimxbuI.js.map → patient-summary-card-BhuRh9AE.js.map} +1 -1
- package/dist/_chunks/{payment-request-card-0DjQgVgX.js → payment-request-card-bysjRZrP.js} +2 -2
- package/dist/_chunks/{payment-request-card-0DjQgVgX.js.map → payment-request-card-bysjRZrP.js.map} +1 -1
- package/dist/_chunks/{reviews-panel-CosDZyow.js → reviews-panel-sbsVmmCA.js} +2 -2
- package/dist/_chunks/{reviews-panel-CosDZyow.js.map → reviews-panel-sbsVmmCA.js.map} +1 -1
- package/dist/_chunks/{streaming-text-zFViGexf.js → streaming-text-DbqKTsmj.js} +2 -2
- package/dist/_chunks/{streaming-text-zFViGexf.js.map → streaming-text-DbqKTsmj.js.map} +1 -1
- package/dist/_chunks/{use-appointment-details-read-CUGfE0hV.js → use-appointment-details-read-1gjZuup4.js} +3 -3
- package/dist/_chunks/{use-appointment-details-read-CUGfE0hV.js.map → use-appointment-details-read-1gjZuup4.js.map} +1 -1
- package/dist/_chunks/workflow-webhook-action-Ct8tFYi7.js +691 -0
- package/dist/_chunks/workflow-webhook-action-Ct8tFYi7.js.map +1 -0
- package/dist/agent-catalog.json +100 -1
- package/dist/agent-i18n/en.json +9 -0
- package/dist/components/_shared/code-block.d.ts +6 -0
- package/dist/components/_shared/code-block.d.ts.map +1 -1
- package/dist/components/anamnesis-card/index.js +1 -1
- package/dist/components/appointment-details-panel/index.js +1 -1
- package/dist/components/appointment-peek/index.js +1 -1
- package/dist/components/chat-message/index.js +1 -1
- package/dist/components/connection-status/index.js +1 -1
- package/dist/components/description-list/description-list.d.ts.map +1 -1
- package/dist/components/description-list/index.js +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/optical-prescription/index.js +1 -1
- package/dist/components/patient-details/index.js +1 -1
- package/dist/components/patient-summary-card/index.js +1 -1
- package/dist/components/payment-card/index.js +1 -1
- package/dist/components/reviews-panel/index.js +1 -1
- package/dist/components/streaming-text/index.js +1 -1
- package/dist/i18n/locales/ar.d.ts +55 -0
- package/dist/i18n/locales/ar.d.ts.map +1 -1
- package/dist/i18n/locales/ar.js +55 -0
- package/dist/i18n/locales/ar.js.map +1 -1
- package/dist/i18n/locales/de.d.ts +55 -0
- package/dist/i18n/locales/de.d.ts.map +1 -1
- package/dist/i18n/locales/de.js +55 -0
- package/dist/i18n/locales/de.js.map +1 -1
- package/dist/i18n/locales/el.d.ts +55 -0
- package/dist/i18n/locales/el.d.ts.map +1 -1
- package/dist/i18n/locales/el.js +55 -0
- package/dist/i18n/locales/el.js.map +1 -1
- package/dist/i18n/locales/en.d.ts +55 -0
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/en.js +55 -0
- package/dist/i18n/locales/en.js.map +1 -1
- package/dist/i18n/locales/es.d.ts +55 -0
- package/dist/i18n/locales/es.d.ts.map +1 -1
- package/dist/i18n/locales/es.js +55 -0
- package/dist/i18n/locales/es.js.map +1 -1
- package/dist/i18n/locales/fr.d.ts +55 -0
- package/dist/i18n/locales/fr.d.ts.map +1 -1
- package/dist/i18n/locales/fr.js +55 -0
- package/dist/i18n/locales/fr.js.map +1 -1
- package/dist/i18n/locales/hi.d.ts +55 -0
- package/dist/i18n/locales/hi.d.ts.map +1 -1
- package/dist/i18n/locales/hi.js +55 -0
- package/dist/i18n/locales/hi.js.map +1 -1
- package/dist/i18n/locales/it.d.ts +55 -0
- package/dist/i18n/locales/it.d.ts.map +1 -1
- package/dist/i18n/locales/it.js +55 -0
- package/dist/i18n/locales/it.js.map +1 -1
- package/dist/i18n/locales/ja.d.ts +55 -0
- package/dist/i18n/locales/ja.d.ts.map +1 -1
- package/dist/i18n/locales/ja.js +55 -0
- package/dist/i18n/locales/ja.js.map +1 -1
- package/dist/i18n/locales/nl.d.ts +55 -0
- package/dist/i18n/locales/nl.d.ts.map +1 -1
- package/dist/i18n/locales/nl.js +55 -0
- package/dist/i18n/locales/nl.js.map +1 -1
- package/dist/i18n/locales/pl.d.ts +55 -0
- package/dist/i18n/locales/pl.d.ts.map +1 -1
- package/dist/i18n/locales/pl.js +55 -0
- package/dist/i18n/locales/pl.js.map +1 -1
- package/dist/i18n/locales/pt.d.ts +55 -0
- package/dist/i18n/locales/pt.d.ts.map +1 -1
- package/dist/i18n/locales/pt.js +55 -0
- package/dist/i18n/locales/pt.js.map +1 -1
- package/dist/i18n/locales/ro.d.ts +55 -0
- package/dist/i18n/locales/ro.d.ts.map +1 -1
- package/dist/i18n/locales/ro.js +55 -0
- package/dist/i18n/locales/ro.js.map +1 -1
- package/dist/i18n/locales/ru.d.ts +55 -0
- package/dist/i18n/locales/ru.d.ts.map +1 -1
- package/dist/i18n/locales/ru.js +55 -0
- package/dist/i18n/locales/ru.js.map +1 -1
- package/dist/i18n/locales/sq.d.ts +55 -0
- package/dist/i18n/locales/sq.d.ts.map +1 -1
- package/dist/i18n/locales/sq.js +55 -0
- package/dist/i18n/locales/sq.js.map +1 -1
- package/dist/i18n/locales/sv.d.ts +55 -0
- package/dist/i18n/locales/sv.d.ts.map +1 -1
- package/dist/i18n/locales/sv.js +55 -0
- package/dist/i18n/locales/sv.js.map +1 -1
- package/dist/i18n/locales/tr.d.ts +55 -0
- package/dist/i18n/locales/tr.d.ts.map +1 -1
- package/dist/i18n/locales/tr.js +55 -0
- package/dist/i18n/locales/tr.js.map +1 -1
- package/dist/i18n/locales/zh.d.ts +55 -0
- package/dist/i18n/locales/zh.d.ts.map +1 -1
- package/dist/i18n/locales/zh.js +55 -0
- package/dist/i18n/locales/zh.js.map +1 -1
- package/dist/index.js +120 -117
- package/dist/index.js.map +1 -1
- package/dist/locales/ar.json +55 -0
- package/dist/locales/de.json +55 -0
- package/dist/locales/el.json +55 -0
- package/dist/locales/en.json +55 -0
- package/dist/locales/es.json +55 -0
- package/dist/locales/fr.json +55 -0
- package/dist/locales/hi.json +55 -0
- package/dist/locales/it.json +55 -0
- package/dist/locales/ja.json +55 -0
- package/dist/locales/nl.json +55 -0
- package/dist/locales/pl.json +55 -0
- package/dist/locales/pt.json +55 -0
- package/dist/locales/ro.json +55 -0
- package/dist/locales/ru.json +55 -0
- package/dist/locales/sq.json +55 -0
- package/dist/locales/sv.json +55 -0
- package/dist/locales/tr.json +55 -0
- package/dist/locales/zh.json +55 -0
- package/dist/patterns/alia-assistant/index.js +1 -1
- package/dist/patterns/anamnesis/index.js +1 -1
- package/dist/patterns/calendar/index.js +1 -1
- package/dist/patterns/workflow-webhook-action/index.d.ts +3 -0
- package/dist/patterns/workflow-webhook-action/index.d.ts.map +1 -0
- package/dist/patterns/workflow-webhook-action/index.js +6 -0
- package/dist/patterns/workflow-webhook-action/index.js.map +1 -0
- package/dist/patterns/workflow-webhook-action/workflow-webhook-action.d.ts +93 -0
- package/dist/patterns/workflow-webhook-action/workflow-webhook-action.d.ts.map +1 -0
- package/dist/tokens.css +1 -1
- package/package.json +5 -1
- package/dist/_chunks/code-block-vwEv1jEa.js.map +0 -1
- package/dist/_chunks/description-list-Q8640NHu.js.map +0 -1
|
@@ -5,7 +5,7 @@ import { useTranslation as B } from "react-i18next";
|
|
|
5
5
|
import { B as F } from "./button-D-MQxsA_.js";
|
|
6
6
|
import { A as I } from "./avatar-BPiPL7ND.js";
|
|
7
7
|
import { T as A } from "./timestamp-DPLDWdxA.js";
|
|
8
|
-
import { s as M, C as S } from "./code-block-
|
|
8
|
+
import { s as M, C as S } from "./code-block-RSKdF3E-.js";
|
|
9
9
|
import { s as V } from "./safe-image-src-DstKgCo7.js";
|
|
10
10
|
import { R as D } from "./rotate-ccw-BBQnIVTr.js";
|
|
11
11
|
import { C as E } from "./clock-Drd4Wtis.js";
|
|
@@ -236,4 +236,4 @@ J.displayName = "ChatMessage";
|
|
|
236
236
|
export {
|
|
237
237
|
J as C
|
|
238
238
|
};
|
|
239
|
-
//# sourceMappingURL=chat-message-
|
|
239
|
+
//# sourceMappingURL=chat-message-C2VXQMoB.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chat-message-Be-9vIIQ.js","sources":["../../src/components/chat-message/chat-message.tsx"],"sourcesContent":["import {\n forwardRef,\n Fragment,\n useMemo,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { CircleAlert, Check, Clock, RotateCcw } from 'lucide-react';\nimport { Button } from '../button';\nimport { Avatar } from '../avatar';\nimport { Timestamp } from '../timestamp';\nimport { safeImageSrc } from '../_shared';\nimport { CodeBlock, splitCodeFences } from '../_shared/code-block';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport type ChatMessageRole = 'user' | 'assistant' | 'system';\nexport type ChatMessageStatus = 'sending' | 'sent' | 'error' | 'edited';\n\nexport interface ChatMessageAvatar {\n name?: string;\n src?: string;\n /**\n * Optional custom avatar node. When provided, it REPLACES the built-in\n * `<Avatar>` (initials / image) for this message. Used by the Alia\n * pattern to render a Astroid identity tile instead of an avatar, and\n * by consumers that want to swap in a user-icon chip, brand glyph, etc.\n * The caller is responsible for accessible labelling on the custom node\n * — ChatMessage's own `aria-label` already announces the role/time, so\n * purely decorative nodes can be `aria-hidden`.\n */\n slot?: ReactNode;\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst rowVariants = cva(\n 'ds:flex ds:w-full ds:items-start ds:gap-[var(--spacing-sm)]',\n {\n variants: {\n role: {\n user: 'ds:flex-row-reverse',\n assistant: 'ds:flex-row',\n system: 'ds:flex-col ds:items-center ds:text-center',\n },\n },\n defaultVariants: { role: 'assistant' },\n },\n);\n\nconst bubbleVariants = cva(\n [\n // Cap at 42rem on wide surfaces; on narrow docks (Alia sidebar ≈22rem)\n // leave room for the avatar column (size-8 = 32px = --spacing-xl) +\n // `gap-sm` (8px = --spacing-sm) so avatars don't clip off the inline\n // edge when the row is width-constrained.\n 'ds:relative ds:max-w-[min(42rem,calc(100%-var(--spacing-xl)-var(--spacing-sm)))]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n 'ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n 'type-body',\n 'ds:break-words',\n ].join(' '),\n {\n variants: {\n role: {\n user: 'ds:bg-[color:var(--primary)] ds:text-[color:var(--primary-foreground)] ds:rounded-[var(--radius-md)] ds:rounded-ee-[var(--radius-sm)]',\n assistant:\n 'ds:bg-muted/40 ds:text-foreground ds:rounded-[var(--radius-md)] ds:rounded-es-[var(--radius-sm)]',\n system:\n 'ds:bg-transparent ds:text-[color:var(--muted-foreground)] type-body-sm ds:italic',\n },\n },\n defaultVariants: { role: 'assistant' },\n },\n);\n\n/* ------------------------------------------------------------------ */\n/* Safe markdown-ish (bold, inline code, line-breaks, links as text) */\n/* No dangerouslySetInnerHTML. */\n/* ------------------------------------------------------------------ */\n\ntype Token =\n | { type: 'text'; value: string }\n | { type: 'bold'; value: string }\n | { type: 'code'; value: string }\n | { type: 'br' }\n | { type: 'link'; label: string; href: string };\n\nconst SAFE_SCHEMES = /^(https?:|mailto:)/i;\n\nfunction tokenize(input: string): Token[] {\n const tokens: Token[] = [];\n let remaining = input;\n const pattern = /\\*\\*([^*]+)\\*\\*|`([^`]+)`|\\[([^\\]]+)\\]\\(([^)]+)\\)|\\n/;\n while (remaining.length > 0) {\n const match = pattern.exec(remaining);\n if (!match) {\n tokens.push({ type: 'text', value: remaining });\n break;\n }\n if (match.index > 0) {\n tokens.push({ type: 'text', value: remaining.slice(0, match.index) });\n }\n if (match[1] !== undefined) {\n tokens.push({ type: 'bold', value: match[1] });\n } else if (match[2] !== undefined) {\n tokens.push({ type: 'code', value: match[2] });\n } else if (match[3] !== undefined && match[4] !== undefined) {\n tokens.push({ type: 'link', label: match[3], href: match[4] });\n } else {\n tokens.push({ type: 'br' });\n }\n remaining = remaining.slice(match.index + match[0].length);\n }\n return tokens;\n}\n\nfunction renderInline(input: string): ReactNode {\n return tokenize(input).map((tok, i) => {\n if (tok.type === 'bold') return <strong key={i}>{tok.value}</strong>;\n if (tok.type === 'code') {\n return (\n <code\n key={i}\n dir=\"ltr\"\n className=\"ds:rounded-[var(--radius-sm)] ds:bg-muted/30 ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:font-[family-name:var(--font-mono)]\"\n >\n {tok.value}\n </code>\n );\n }\n if (tok.type === 'br') return <br key={i} />;\n if (tok.type === 'link') {\n if (!SAFE_SCHEMES.test(tok.href)) {\n // Unsafe scheme (javascript:, data:) — render as inert text.\n return <Fragment key={i}>{tok.label}</Fragment>;\n }\n return (\n <a\n key={i}\n href={tok.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"ds:underline ds:underline-offset-2 ds:hover:opacity-80\"\n >\n {tok.label}\n </a>\n );\n }\n return <Fragment key={i}>{tok.value}</Fragment>;\n });\n}\n\n/**\n * Render a message body: split fenced code blocks out to `<CodeBlock>` and run\n * the inline tokenizer over the surrounding text runs.\n */\nfunction renderBody(input: string): ReactNode {\n return splitCodeFences(input).map((seg, i) => {\n if (seg.type === 'code') {\n return <CodeBlock key={i} code={seg.value} language={seg.lang} />;\n }\n return <Fragment key={i}>{renderInline(seg.value)}</Fragment>;\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* ChatMessage */\n/* ------------------------------------------------------------------ */\n\ntype NativeProps = Omit<HTMLAttributes<HTMLElement>, 'content' | 'role'>;\n\nexport interface ChatMessageProps\n extends NativeProps, VariantProps<typeof bubbleVariants> {\n role: ChatMessageRole;\n content: string;\n avatar?: ChatMessageAvatar;\n timestamp?: Date | number | string;\n status?: ChatMessageStatus;\n /** Parse a small, safe subset of markdown. Never renders raw HTML. */\n renderMarkdown?: boolean;\n /** Invoked when the retry button is activated (error status only). */\n onRetry?: () => void;\n /**\n * Optional action row rendered beneath the message body + timestamp,\n * aligned with the message (e.g. the Alia pattern's Copy / Download\n * buttons on assistant messages). Default off → renders nothing.\n */\n actions?: ReactNode;\n /**\n * Custom body node rendered verbatim INSTEAD of `content`. When set, the\n * markdown tokenizer (`renderBody`) is skipped and the node is rendered\n * directly inside the bubble. `content` is still required (used for the\n * aria-label context and consumer copy / download). Default off.\n */\n body?: ReactNode;\n}\n\nexport const ChatMessage = forwardRef<HTMLElement, ChatMessageProps>(\n (\n {\n role,\n content,\n avatar,\n timestamp,\n status,\n renderMarkdown = false,\n onRetry,\n actions,\n body,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation('ui');\n\n // Local format — used for the aria-label interpolation only. Visible\n // label is rendered by `<Timestamp>` below, which computes the same\n // locale-aware HH:MM internally.\n const formattedTime = useMemo(() => {\n if (!timestamp) return null;\n const date = timestamp instanceof Date ? timestamp : new Date(timestamp);\n if (Number.isNaN(date.getTime())) return null;\n return new Intl.DateTimeFormat(i18n.language, {\n hour: '2-digit',\n minute: '2-digit',\n }).format(date);\n }, [timestamp, i18n.language]);\n\n const roleLabel = t(`chat.message.role.${role}`);\n const messageLabel = formattedTime\n ? t('chat.message.label', { role: roleLabel, time: formattedTime })\n : t('chat.message.labelNoTime', { role: roleLabel });\n\n const resolvedBody =\n body ?? (renderMarkdown ? renderBody(content) : content);\n\n const statusIcon = (() => {\n if (!status || status === 'edited') return null;\n if (status === 'sending')\n return <Clock aria-hidden=\"true\" className=\"ds:size-3.5\" />;\n if (status === 'sent')\n return <Check aria-hidden=\"true\" className=\"ds:size-3.5\" />;\n return (\n <CircleAlert\n aria-hidden=\"true\"\n className=\"ds:size-3.5 ds:text-[color:var(--destructive)]\"\n />\n );\n })();\n\n const statusText = status ? t(`chat.message.status.${status}`) : null;\n\n return (\n <article\n ref={ref}\n aria-label={messageLabel}\n data-component=\"chat-message\"\n // Structural state surfaced for agent reads (see chat-message.agent.ts).\n // `status` is optional → the attribute is omitted when unset.\n data-role={role}\n data-status={status}\n className={[\n rowVariants({ role, className }),\n // Entrance: fade + slide up on mount. `--animation-duration` is 0ms\n // under `prefers-reduced-motion` and `.theme-accessible`, so both\n // collapse the animation to an instant state change automatically.\n 'ds:motion-safe:animate-in ds:motion-safe:fade-in-0 ds:motion-safe:slide-in-from-bottom-2',\n 'ds:duration-[var(--animation-duration)] ds:ease-[var(--ease-out)]',\n ].join(' ')}\n {...rest}\n >\n {role !== 'system' && avatar ? (\n avatar.slot ? (\n // Consumer-supplied avatar node (e.g. Alia's Astroid tile).\n avatar.slot\n ) : (\n // Allow-list the src: LLM-supplied values can carry\n // `javascript:` / `data:text/html` / `data:image/svg+xml` and so\n // are scrubbed here before reaching <Avatar>. Fallback to initials.\n <Avatar\n name={avatar.name}\n src={safeImageSrc(avatar.src)}\n size=\"sm\"\n />\n )\n ) : null}\n\n <div\n className={[\n // min-w-0 allows this flex item to shrink below its content's\n // natural width — without it, a single unbreakable token (URL,\n // CJK run, hash) can force the bubble wider than the parent\n // and spill outside narrow containers like the Alia sidebar.\n 'ds:flex ds:flex-col ds:min-w-0',\n role === 'user'\n ? 'ds:items-end'\n : role === 'assistant'\n ? 'ds:items-start'\n : 'ds:items-center',\n ].join(' ')}\n >\n <div\n dir=\"auto\"\n className={[\n bubbleVariants({ role }),\n // `break-words` (from bubbleVariants) handles soft hyphens;\n // `overflow-wrap: anywhere` is the last-resort break for\n // URLs, hashes, or CJK runs that would otherwise push the\n // bubble wider than its parent in narrow docks like Alia.\n 'ds:[overflow-wrap:anywhere]',\n ].join(' ')}\n >\n {resolvedBody}\n </div>\n\n {(formattedTime || statusText) && role !== 'system' ? (\n <div\n className={[\n 'ds:mt-[var(--spacing-xs)] ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'type-meta ds:text-[color:var(--muted-foreground)]',\n ].join(' ')}\n >\n {timestamp ? (\n <Timestamp\n value={timestamp}\n format=\"absolute\"\n absoluteFormat={{ hour: '2-digit', minute: '2-digit' }}\n dir=\"ltr\"\n />\n ) : null}\n {statusIcon ? (\n // `key={status}` re-mounts the span on every status change so\n // `animate-in` re-runs — gives the sending → sent → error\n // swap a subtle cross-fade instead of an abrupt pop.\n <span\n key={status}\n className={[\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'ds:motion-safe:animate-in ds:motion-safe:fade-in-0',\n 'ds:duration-[var(--animation-duration)] ds:ease-[var(--ease-out)]',\n ].join(' ')}\n >\n {statusIcon}\n <span className=\"ds:sr-only\">{statusText}</span>\n </span>\n ) : null}\n {status === 'error' && onRetry ? (\n <Button\n intent=\"ghost\"\n size=\"sm\"\n onClick={onRetry}\n startIcon={<RotateCcw />}\n >\n {t('chat.message.retry')}\n </Button>\n ) : null}\n </div>\n ) : null}\n {actions ? (\n <div className=\"ds:mt-[var(--spacing-xs)] ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n {actions}\n </div>\n ) : null}\n </div>\n </article>\n );\n },\n);\n\nChatMessage.displayName = 'ChatMessage';\n"],"names":["rowVariants","cva","bubbleVariants","SAFE_SCHEMES","tokenize","input","tokens","remaining","pattern","match","renderInline","tok","i","jsx","Fragment","renderBody","splitCodeFences","seg","CodeBlock","ChatMessage","forwardRef","role","content","avatar","timestamp","status","renderMarkdown","onRetry","actions","body","className","rest","ref","t","i18n","useTranslation","formattedTime","useMemo","date","roleLabel","messageLabel","resolvedBody","statusIcon","Clock","Check","CircleAlert","statusText","jsxs","Avatar","safeImageSrc","Timestamp","Button","RotateCcw"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"chat-message-C2VXQMoB.js","sources":["../../src/components/chat-message/chat-message.tsx"],"sourcesContent":["import {\n forwardRef,\n Fragment,\n useMemo,\n type HTMLAttributes,\n type ReactNode,\n} from 'react';\nimport { cva, type VariantProps } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { CircleAlert, Check, Clock, RotateCcw } from 'lucide-react';\nimport { Button } from '../button';\nimport { Avatar } from '../avatar';\nimport { Timestamp } from '../timestamp';\nimport { safeImageSrc } from '../_shared';\nimport { CodeBlock, splitCodeFences } from '../_shared/code-block';\n\n/* ------------------------------------------------------------------ */\n/* Types */\n/* ------------------------------------------------------------------ */\n\nexport type ChatMessageRole = 'user' | 'assistant' | 'system';\nexport type ChatMessageStatus = 'sending' | 'sent' | 'error' | 'edited';\n\nexport interface ChatMessageAvatar {\n name?: string;\n src?: string;\n /**\n * Optional custom avatar node. When provided, it REPLACES the built-in\n * `<Avatar>` (initials / image) for this message. Used by the Alia\n * pattern to render a Astroid identity tile instead of an avatar, and\n * by consumers that want to swap in a user-icon chip, brand glyph, etc.\n * The caller is responsible for accessible labelling on the custom node\n * — ChatMessage's own `aria-label` already announces the role/time, so\n * purely decorative nodes can be `aria-hidden`.\n */\n slot?: ReactNode;\n}\n\n/* ------------------------------------------------------------------ */\n/* CVA */\n/* ------------------------------------------------------------------ */\n\nconst rowVariants = cva(\n 'ds:flex ds:w-full ds:items-start ds:gap-[var(--spacing-sm)]',\n {\n variants: {\n role: {\n user: 'ds:flex-row-reverse',\n assistant: 'ds:flex-row',\n system: 'ds:flex-col ds:items-center ds:text-center',\n },\n },\n defaultVariants: { role: 'assistant' },\n },\n);\n\nconst bubbleVariants = cva(\n [\n // Cap at 42rem on wide surfaces; on narrow docks (Alia sidebar ≈22rem)\n // leave room for the avatar column (size-8 = 32px = --spacing-xl) +\n // `gap-sm` (8px = --spacing-sm) so avatars don't clip off the inline\n // edge when the row is width-constrained.\n 'ds:relative ds:max-w-[min(42rem,calc(100%-var(--spacing-xl)-var(--spacing-sm)))]',\n 'ds:ps-[var(--spacing-md)] ds:pe-[var(--spacing-md)]',\n 'ds:pt-[var(--spacing-sm)] ds:pb-[var(--spacing-sm)]',\n 'type-body',\n 'ds:break-words',\n ].join(' '),\n {\n variants: {\n role: {\n user: 'ds:bg-[color:var(--primary)] ds:text-[color:var(--primary-foreground)] ds:rounded-[var(--radius-md)] ds:rounded-ee-[var(--radius-sm)]',\n assistant:\n 'ds:bg-muted/40 ds:text-foreground ds:rounded-[var(--radius-md)] ds:rounded-es-[var(--radius-sm)]',\n system:\n 'ds:bg-transparent ds:text-[color:var(--muted-foreground)] type-body-sm ds:italic',\n },\n },\n defaultVariants: { role: 'assistant' },\n },\n);\n\n/* ------------------------------------------------------------------ */\n/* Safe markdown-ish (bold, inline code, line-breaks, links as text) */\n/* No dangerouslySetInnerHTML. */\n/* ------------------------------------------------------------------ */\n\ntype Token =\n | { type: 'text'; value: string }\n | { type: 'bold'; value: string }\n | { type: 'code'; value: string }\n | { type: 'br' }\n | { type: 'link'; label: string; href: string };\n\nconst SAFE_SCHEMES = /^(https?:|mailto:)/i;\n\nfunction tokenize(input: string): Token[] {\n const tokens: Token[] = [];\n let remaining = input;\n const pattern = /\\*\\*([^*]+)\\*\\*|`([^`]+)`|\\[([^\\]]+)\\]\\(([^)]+)\\)|\\n/;\n while (remaining.length > 0) {\n const match = pattern.exec(remaining);\n if (!match) {\n tokens.push({ type: 'text', value: remaining });\n break;\n }\n if (match.index > 0) {\n tokens.push({ type: 'text', value: remaining.slice(0, match.index) });\n }\n if (match[1] !== undefined) {\n tokens.push({ type: 'bold', value: match[1] });\n } else if (match[2] !== undefined) {\n tokens.push({ type: 'code', value: match[2] });\n } else if (match[3] !== undefined && match[4] !== undefined) {\n tokens.push({ type: 'link', label: match[3], href: match[4] });\n } else {\n tokens.push({ type: 'br' });\n }\n remaining = remaining.slice(match.index + match[0].length);\n }\n return tokens;\n}\n\nfunction renderInline(input: string): ReactNode {\n return tokenize(input).map((tok, i) => {\n if (tok.type === 'bold') return <strong key={i}>{tok.value}</strong>;\n if (tok.type === 'code') {\n return (\n <code\n key={i}\n dir=\"ltr\"\n className=\"ds:rounded-[var(--radius-sm)] ds:bg-muted/30 ds:ps-[var(--spacing-xs)] ds:pe-[var(--spacing-xs)] ds:font-[family-name:var(--font-mono)]\"\n >\n {tok.value}\n </code>\n );\n }\n if (tok.type === 'br') return <br key={i} />;\n if (tok.type === 'link') {\n if (!SAFE_SCHEMES.test(tok.href)) {\n // Unsafe scheme (javascript:, data:) — render as inert text.\n return <Fragment key={i}>{tok.label}</Fragment>;\n }\n return (\n <a\n key={i}\n href={tok.href}\n target=\"_blank\"\n rel=\"noopener noreferrer\"\n className=\"ds:underline ds:underline-offset-2 ds:hover:opacity-80\"\n >\n {tok.label}\n </a>\n );\n }\n return <Fragment key={i}>{tok.value}</Fragment>;\n });\n}\n\n/**\n * Render a message body: split fenced code blocks out to `<CodeBlock>` and run\n * the inline tokenizer over the surrounding text runs.\n */\nfunction renderBody(input: string): ReactNode {\n return splitCodeFences(input).map((seg, i) => {\n if (seg.type === 'code') {\n return <CodeBlock key={i} code={seg.value} language={seg.lang} />;\n }\n return <Fragment key={i}>{renderInline(seg.value)}</Fragment>;\n });\n}\n\n/* ------------------------------------------------------------------ */\n/* ChatMessage */\n/* ------------------------------------------------------------------ */\n\ntype NativeProps = Omit<HTMLAttributes<HTMLElement>, 'content' | 'role'>;\n\nexport interface ChatMessageProps\n extends NativeProps, VariantProps<typeof bubbleVariants> {\n role: ChatMessageRole;\n content: string;\n avatar?: ChatMessageAvatar;\n timestamp?: Date | number | string;\n status?: ChatMessageStatus;\n /** Parse a small, safe subset of markdown. Never renders raw HTML. */\n renderMarkdown?: boolean;\n /** Invoked when the retry button is activated (error status only). */\n onRetry?: () => void;\n /**\n * Optional action row rendered beneath the message body + timestamp,\n * aligned with the message (e.g. the Alia pattern's Copy / Download\n * buttons on assistant messages). Default off → renders nothing.\n */\n actions?: ReactNode;\n /**\n * Custom body node rendered verbatim INSTEAD of `content`. When set, the\n * markdown tokenizer (`renderBody`) is skipped and the node is rendered\n * directly inside the bubble. `content` is still required (used for the\n * aria-label context and consumer copy / download). Default off.\n */\n body?: ReactNode;\n}\n\nexport const ChatMessage = forwardRef<HTMLElement, ChatMessageProps>(\n (\n {\n role,\n content,\n avatar,\n timestamp,\n status,\n renderMarkdown = false,\n onRetry,\n actions,\n body,\n className,\n ...rest\n },\n ref,\n ) => {\n const { t, i18n } = useTranslation('ui');\n\n // Local format — used for the aria-label interpolation only. Visible\n // label is rendered by `<Timestamp>` below, which computes the same\n // locale-aware HH:MM internally.\n const formattedTime = useMemo(() => {\n if (!timestamp) return null;\n const date = timestamp instanceof Date ? timestamp : new Date(timestamp);\n if (Number.isNaN(date.getTime())) return null;\n return new Intl.DateTimeFormat(i18n.language, {\n hour: '2-digit',\n minute: '2-digit',\n }).format(date);\n }, [timestamp, i18n.language]);\n\n const roleLabel = t(`chat.message.role.${role}`);\n const messageLabel = formattedTime\n ? t('chat.message.label', { role: roleLabel, time: formattedTime })\n : t('chat.message.labelNoTime', { role: roleLabel });\n\n const resolvedBody =\n body ?? (renderMarkdown ? renderBody(content) : content);\n\n const statusIcon = (() => {\n if (!status || status === 'edited') return null;\n if (status === 'sending')\n return <Clock aria-hidden=\"true\" className=\"ds:size-3.5\" />;\n if (status === 'sent')\n return <Check aria-hidden=\"true\" className=\"ds:size-3.5\" />;\n return (\n <CircleAlert\n aria-hidden=\"true\"\n className=\"ds:size-3.5 ds:text-[color:var(--destructive)]\"\n />\n );\n })();\n\n const statusText = status ? t(`chat.message.status.${status}`) : null;\n\n return (\n <article\n ref={ref}\n aria-label={messageLabel}\n data-component=\"chat-message\"\n // Structural state surfaced for agent reads (see chat-message.agent.ts).\n // `status` is optional → the attribute is omitted when unset.\n data-role={role}\n data-status={status}\n className={[\n rowVariants({ role, className }),\n // Entrance: fade + slide up on mount. `--animation-duration` is 0ms\n // under `prefers-reduced-motion` and `.theme-accessible`, so both\n // collapse the animation to an instant state change automatically.\n 'ds:motion-safe:animate-in ds:motion-safe:fade-in-0 ds:motion-safe:slide-in-from-bottom-2',\n 'ds:duration-[var(--animation-duration)] ds:ease-[var(--ease-out)]',\n ].join(' ')}\n {...rest}\n >\n {role !== 'system' && avatar ? (\n avatar.slot ? (\n // Consumer-supplied avatar node (e.g. Alia's Astroid tile).\n avatar.slot\n ) : (\n // Allow-list the src: LLM-supplied values can carry\n // `javascript:` / `data:text/html` / `data:image/svg+xml` and so\n // are scrubbed here before reaching <Avatar>. Fallback to initials.\n <Avatar\n name={avatar.name}\n src={safeImageSrc(avatar.src)}\n size=\"sm\"\n />\n )\n ) : null}\n\n <div\n className={[\n // min-w-0 allows this flex item to shrink below its content's\n // natural width — without it, a single unbreakable token (URL,\n // CJK run, hash) can force the bubble wider than the parent\n // and spill outside narrow containers like the Alia sidebar.\n 'ds:flex ds:flex-col ds:min-w-0',\n role === 'user'\n ? 'ds:items-end'\n : role === 'assistant'\n ? 'ds:items-start'\n : 'ds:items-center',\n ].join(' ')}\n >\n <div\n dir=\"auto\"\n className={[\n bubbleVariants({ role }),\n // `break-words` (from bubbleVariants) handles soft hyphens;\n // `overflow-wrap: anywhere` is the last-resort break for\n // URLs, hashes, or CJK runs that would otherwise push the\n // bubble wider than its parent in narrow docks like Alia.\n 'ds:[overflow-wrap:anywhere]',\n ].join(' ')}\n >\n {resolvedBody}\n </div>\n\n {(formattedTime || statusText) && role !== 'system' ? (\n <div\n className={[\n 'ds:mt-[var(--spacing-xs)] ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'type-meta ds:text-[color:var(--muted-foreground)]',\n ].join(' ')}\n >\n {timestamp ? (\n <Timestamp\n value={timestamp}\n format=\"absolute\"\n absoluteFormat={{ hour: '2-digit', minute: '2-digit' }}\n dir=\"ltr\"\n />\n ) : null}\n {statusIcon ? (\n // `key={status}` re-mounts the span on every status change so\n // `animate-in` re-runs — gives the sending → sent → error\n // swap a subtle cross-fade instead of an abrupt pop.\n <span\n key={status}\n className={[\n 'ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]',\n 'ds:motion-safe:animate-in ds:motion-safe:fade-in-0',\n 'ds:duration-[var(--animation-duration)] ds:ease-[var(--ease-out)]',\n ].join(' ')}\n >\n {statusIcon}\n <span className=\"ds:sr-only\">{statusText}</span>\n </span>\n ) : null}\n {status === 'error' && onRetry ? (\n <Button\n intent=\"ghost\"\n size=\"sm\"\n onClick={onRetry}\n startIcon={<RotateCcw />}\n >\n {t('chat.message.retry')}\n </Button>\n ) : null}\n </div>\n ) : null}\n {actions ? (\n <div className=\"ds:mt-[var(--spacing-xs)] ds:inline-flex ds:items-center ds:gap-[var(--spacing-xs)]\">\n {actions}\n </div>\n ) : null}\n </div>\n </article>\n );\n },\n);\n\nChatMessage.displayName = 'ChatMessage';\n"],"names":["rowVariants","cva","bubbleVariants","SAFE_SCHEMES","tokenize","input","tokens","remaining","pattern","match","renderInline","tok","i","jsx","Fragment","renderBody","splitCodeFences","seg","CodeBlock","ChatMessage","forwardRef","role","content","avatar","timestamp","status","renderMarkdown","onRetry","actions","body","className","rest","ref","t","i18n","useTranslation","formattedTime","useMemo","date","roleLabel","messageLabel","resolvedBody","statusIcon","Clock","Check","CircleAlert","statusText","jsxs","Avatar","safeImageSrc","Timestamp","Button","RotateCcw"],"mappings":"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"}
|
|
@@ -1,46 +1,46 @@
|
|
|
1
|
-
import { jsxs as m, jsx as
|
|
2
|
-
import { forwardRef as
|
|
3
|
-
import { c as
|
|
4
|
-
import { useTranslation as
|
|
5
|
-
import { I as
|
|
6
|
-
import { C } from "./check-DrFSN__t.js";
|
|
7
|
-
import { C as
|
|
8
|
-
function
|
|
9
|
-
const s = [],
|
|
1
|
+
import { jsxs as m, jsx as t } from "react/jsx-runtime";
|
|
2
|
+
import { forwardRef as g, useState as h, useCallback as b } from "react";
|
|
3
|
+
import { c as x } from "./index-D2ZczOXr.js";
|
|
4
|
+
import { useTranslation as y } from "react-i18next";
|
|
5
|
+
import { I as C } from "./icon-button-C_1TgRiK.js";
|
|
6
|
+
import { C as k } from "./check-DrFSN__t.js";
|
|
7
|
+
import { C as w } from "./copy-vRpqqHtY.js";
|
|
8
|
+
function R(e) {
|
|
9
|
+
const s = [], a = /```([^\n`]*)\n([\s\S]*?)(?:\n?```|$)/g;
|
|
10
10
|
let o = 0, r;
|
|
11
|
-
for (; (r =
|
|
11
|
+
for (; (r = a.exec(e)) !== null; )
|
|
12
12
|
r.index > o && s.push({ type: "text", value: e.slice(o, r.index) }), s.push({
|
|
13
13
|
type: "code",
|
|
14
14
|
lang: r[1].trim() || void 0,
|
|
15
15
|
value: r[2]
|
|
16
|
-
}), o =
|
|
16
|
+
}), o = a.lastIndex;
|
|
17
17
|
return o < e.length && s.push({ type: "text", value: e.slice(o) }), s;
|
|
18
18
|
}
|
|
19
|
-
const
|
|
19
|
+
const B = x(
|
|
20
20
|
[
|
|
21
21
|
"ds:my-[var(--spacing-sm)] ds:overflow-hidden",
|
|
22
22
|
"ds:rounded-[var(--radius-md)] ds:border ds:border-[color:var(--border)]",
|
|
23
23
|
"ds:bg-[var(--muted)]"
|
|
24
24
|
].join(" ")
|
|
25
|
-
),
|
|
26
|
-
({ code: e, language: s, showCopy:
|
|
27
|
-
var
|
|
28
|
-
const { t:
|
|
25
|
+
), j = g(
|
|
26
|
+
({ code: e, language: s, showCopy: a = !0, copyLabel: o, className: r, ...f }, u) => {
|
|
27
|
+
var c;
|
|
28
|
+
const { t: i } = y("ui"), [n, d] = h(!1), l = a && typeof navigator < "u" && typeof ((c = navigator.clipboard) == null ? void 0 : c.writeText) == "function", v = b(() => {
|
|
29
29
|
var p;
|
|
30
30
|
(p = navigator.clipboard) == null || p.writeText(e).then(() => {
|
|
31
|
-
|
|
31
|
+
d(!0), setTimeout(() => d(!1), 2e3);
|
|
32
32
|
}).catch(() => {
|
|
33
33
|
});
|
|
34
34
|
}, [e]);
|
|
35
35
|
return /* @__PURE__ */ m(
|
|
36
36
|
"div",
|
|
37
37
|
{
|
|
38
|
-
ref:
|
|
38
|
+
ref: u,
|
|
39
39
|
"data-component": "code-block",
|
|
40
|
-
className: [
|
|
41
|
-
...
|
|
40
|
+
className: [B(), r].filter(Boolean).join(" "),
|
|
41
|
+
...f,
|
|
42
42
|
children: [
|
|
43
|
-
s ||
|
|
43
|
+
s || l ? /* @__PURE__ */ m(
|
|
44
44
|
"div",
|
|
45
45
|
{
|
|
46
46
|
className: [
|
|
@@ -51,7 +51,7 @@ const w = h(
|
|
|
51
51
|
"ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]"
|
|
52
52
|
].join(" "),
|
|
53
53
|
children: [
|
|
54
|
-
/* @__PURE__ */
|
|
54
|
+
/* @__PURE__ */ t(
|
|
55
55
|
"span",
|
|
56
56
|
{
|
|
57
57
|
dir: "ltr",
|
|
@@ -59,25 +59,25 @@ const w = h(
|
|
|
59
59
|
children: s ?? ""
|
|
60
60
|
}
|
|
61
61
|
),
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
l ? /* @__PURE__ */ t(
|
|
63
|
+
C,
|
|
64
64
|
{
|
|
65
|
-
icon: n ? /* @__PURE__ */
|
|
66
|
-
"aria-label":
|
|
65
|
+
icon: n ? /* @__PURE__ */ t(k, {}) : /* @__PURE__ */ t(w, {}),
|
|
66
|
+
"aria-label": n ? i("codeBlock.copied") : o ?? i("codeBlock.copy"),
|
|
67
67
|
intent: "ghost",
|
|
68
68
|
size: "sm",
|
|
69
|
-
onClick:
|
|
69
|
+
onClick: v
|
|
70
70
|
}
|
|
71
71
|
) : null
|
|
72
72
|
]
|
|
73
73
|
}
|
|
74
74
|
) : null,
|
|
75
|
-
/* @__PURE__ */
|
|
75
|
+
/* @__PURE__ */ t(
|
|
76
76
|
"pre",
|
|
77
77
|
{
|
|
78
78
|
tabIndex: 0,
|
|
79
|
-
className: "ds:m-0 ds:overflow-x-auto ds:p-[var(--spacing-md)]",
|
|
80
|
-
children: /* @__PURE__ */
|
|
79
|
+
className: "ds:m-0 ds:overflow-x-auto ds:p-[var(--spacing-md)] ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:calc(var(--focus-ring-width)*-1)]",
|
|
80
|
+
children: /* @__PURE__ */ t(
|
|
81
81
|
"code",
|
|
82
82
|
{
|
|
83
83
|
dir: "ltr",
|
|
@@ -92,15 +92,15 @@ const w = h(
|
|
|
92
92
|
)
|
|
93
93
|
}
|
|
94
94
|
),
|
|
95
|
-
/* @__PURE__ */
|
|
95
|
+
/* @__PURE__ */ t("span", { role: "status", "aria-live": "polite", className: "ds:sr-only", children: n ? i("codeBlock.copied") : "" })
|
|
96
96
|
]
|
|
97
97
|
}
|
|
98
98
|
);
|
|
99
99
|
}
|
|
100
100
|
);
|
|
101
|
-
|
|
101
|
+
j.displayName = "CodeBlock";
|
|
102
102
|
export {
|
|
103
|
-
|
|
104
|
-
|
|
103
|
+
j as C,
|
|
104
|
+
R as s
|
|
105
105
|
};
|
|
106
|
-
//# sourceMappingURL=code-block-
|
|
106
|
+
//# sourceMappingURL=code-block-RSKdF3E-.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-block-RSKdF3E-.js","sources":["../../src/components/_shared/code-block.tsx"],"sourcesContent":["/**\n * Shared fenced-code-block renderer for the chat markdown path.\n *\n * `splitCodeFences` slices a markdown string into plain-text and fenced-code\n * segments so `ChatMessage` / `StreamingText` can keep their existing inline\n * tokenizers for the text runs and render `<CodeBlock>` for the code runs.\n * It tolerates an UNTERMINATED trailing fence — the streaming case, where the\n * closing ``` has not arrived yet — so a prompt renders as a code block as it\n * streams in.\n *\n * `CodeBlock` renders the code in a scrollable monospace surface with a Copy\n * affordance (clipboard + a brief \"Copied\" confirmation, graceful no-op when\n * the clipboard API is unavailable). No `dangerouslySetInnerHTML`.\n */\nimport { forwardRef, useCallback, useState, type HTMLAttributes } from 'react';\nimport { cva } from 'class-variance-authority';\nimport { useTranslation } from 'react-i18next';\nimport { Check, Copy } from 'lucide-react';\nimport { IconButton } from '../button';\n\n/* ------------------------------------------------------------------ */\n/* splitCodeFences */\n/* ------------------------------------------------------------------ */\n\nexport type MarkdownSegment =\n | { type: 'text'; value: string }\n | { type: 'code'; lang?: string; value: string };\n\n/**\n * Split markdown into text + fenced-code segments. Matches an opening\n * ` ```lang\\n `, lazy content, then either a closing ` \\n``` ` on its own line\n * or end-of-input (the unterminated streaming case).\n */\nexport function splitCodeFences(input: string): MarkdownSegment[] {\n const segments: MarkdownSegment[] = [];\n // `\\n?` before the closing fence so an EMPTY block (```ts\\n```) closes\n // correctly rather than swallowing its own closing backticks as content.\n const fence = /```([^\\n`]*)\\n([\\s\\S]*?)(?:\\n?```|$)/g;\n let last = 0;\n let m: RegExpExecArray | null;\n while ((m = fence.exec(input)) !== null) {\n if (m.index > last) {\n segments.push({ type: 'text', value: input.slice(last, m.index) });\n }\n segments.push({\n type: 'code',\n lang: m[1].trim() || undefined,\n value: m[2],\n });\n last = fence.lastIndex;\n }\n if (last < input.length) {\n segments.push({ type: 'text', value: input.slice(last) });\n }\n return segments;\n}\n\n/* ------------------------------------------------------------------ */\n/* CodeBlock */\n/* ------------------------------------------------------------------ */\n\nconst codeBlockVariants = cva(\n [\n 'ds:my-[var(--spacing-sm)] ds:overflow-hidden',\n 'ds:rounded-[var(--radius-md)] ds:border ds:border-[color:var(--border)]',\n 'ds:bg-[var(--muted)]',\n ].join(' '),\n);\n\nexport interface CodeBlockProps extends Omit<\n HTMLAttributes<HTMLDivElement>,\n 'children'\n> {\n /** Raw code text. */\n code: string;\n /** Optional info string after the opening fence (e.g. `ts`, `bash`). */\n language?: string;\n /** Render the Copy affordance. Default `true`. */\n showCopy?: boolean;\n /**\n * The copy button's accessible name, translated. Default \"Copy code\".\n * Pass one per block when a page shows several, so each says what it\n * copies.\n */\n copyLabel?: string;\n}\n\nexport const CodeBlock = forwardRef<HTMLDivElement, CodeBlockProps>(\n ({ code, language, showCopy = true, copyLabel, className, ...rest }, ref) => {\n const { t } = useTranslation('ui');\n const [copied, setCopied] = useState(false);\n\n const canCopy =\n showCopy &&\n typeof navigator !== 'undefined' &&\n typeof navigator.clipboard?.writeText === 'function';\n\n const handleCopy = useCallback(() => {\n navigator.clipboard\n ?.writeText(code)\n .then(() => {\n setCopied(true);\n setTimeout(() => setCopied(false), 2000);\n })\n .catch(() => {\n // Clipboard blocked (insecure context / permissions) — graceful no-op.\n });\n }, [code]);\n\n return (\n <div\n ref={ref}\n data-component=\"code-block\"\n className={[codeBlockVariants(), className].filter(Boolean).join(' ')}\n {...rest}\n >\n {language || canCopy ? (\n <div\n className={[\n 'ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]',\n 'ds:border-b ds:border-[color:var(--border)]',\n 'ds:bg-[color-mix(in_srgb,var(--muted-foreground)_8%,transparent)]',\n 'ds:ps-[var(--spacing-sm)] ds:pe-[var(--spacing-xs)]',\n 'ds:pt-[var(--spacing-xs)] ds:pb-[var(--spacing-xs)]',\n ].join(' ')}\n >\n <span\n dir=\"ltr\"\n className=\"type-meta ds:text-[color:var(--muted-foreground)]\"\n >\n {language ?? ''}\n </span>\n {canCopy ? (\n <IconButton\n icon={copied ? <Check /> : <Copy />}\n aria-label={\n copied\n ? t('codeBlock.copied')\n : (copyLabel ?? t('codeBlock.copy'))\n }\n intent=\"ghost\"\n size=\"sm\"\n onClick={handleCopy}\n />\n ) : null}\n </div>\n ) : null}\n {/* eslint-disable jsx-a11y/no-noninteractive-tabindex --\n the scrollable <pre> must be keyboard-focusable so keyboard users\n can scroll the overflow (axe scrollable-region-focusable); the\n browser default focus ring satisfies WCAG 2.4.7. */}\n {/* The ring is drawn inside the block: its rounded frame clips\n anything outside. */}\n <pre\n tabIndex={0}\n className=\"ds:m-0 ds:overflow-x-auto ds:p-[var(--spacing-md)] ds:focus-visible:outline-[length:var(--focus-ring-width)] ds:focus-visible:outline-solid ds:focus-visible:outline-ring ds:focus-visible:outline-offset-[length:calc(var(--focus-ring-width)*-1)]\"\n >\n <code\n dir=\"ltr\"\n className={[\n 'ds:font-[family-name:var(--font-mono)]',\n 'ds:text-[length:var(--font-size-sm)] ds:leading-relaxed',\n 'ds:text-[color:var(--foreground)]',\n 'ds:whitespace-pre',\n ].join(' ')}\n >\n {code}\n </code>\n </pre>\n {/* eslint-enable jsx-a11y/no-noninteractive-tabindex */}\n {/* Polite status so AT confirms the copy without moving focus. */}\n <span role=\"status\" aria-live=\"polite\" className=\"ds:sr-only\">\n {copied ? t('codeBlock.copied') : ''}\n </span>\n </div>\n );\n },\n);\n\nCodeBlock.displayName = 'CodeBlock';\n"],"names":["splitCodeFences","input","segments","fence","last","m","codeBlockVariants","cva","CodeBlock","forwardRef","code","language","showCopy","copyLabel","className","rest","ref","t","useTranslation","copied","setCopied","useState","canCopy","_a","handleCopy","useCallback","jsxs","jsx","IconButton","Check","Copy"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ import { useTranslation as A } from "react-i18next";
|
|
|
4
4
|
import { C as a } from "./card-BA4Ig_kn.js";
|
|
5
5
|
import { B as C } from "./badge-BUk4RjTS.js";
|
|
6
6
|
import { B as u } from "./button-D-MQxsA_.js";
|
|
7
|
-
import { D as d } from "./description-list-
|
|
7
|
+
import { D as d } from "./description-list-DQQ2OA_t.js";
|
|
8
8
|
import { u as N } from "./registry-DvAUVLHh.js";
|
|
9
9
|
const w = {
|
|
10
10
|
id: "connection-status",
|
|
@@ -105,4 +105,4 @@ export {
|
|
|
105
105
|
K as C,
|
|
106
106
|
w as c
|
|
107
107
|
};
|
|
108
|
-
//# sourceMappingURL=connection-status
|
|
108
|
+
//# sourceMappingURL=connection-status-BOZC5QU5.js.map
|
package/dist/_chunks/{connection-status--wYsgreh.js.map → connection-status-BOZC5QU5.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"connection-status--wYsgreh.js","sources":["../../src/components/connection-status/connection-status.agent.ts","../../src/components/connection-status/connection-status.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — ConnectionStatus. */\n/* */\n/* See `src/docs/26-agent-readiness.mdx` for the contract. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { ConnectionStatusHandle } from './connection-status';\n\nexport const connectionStatusAgent: AgentAdapter<ConnectionStatusHandle> = {\n id: 'connection-status',\n capabilities: ['submit', 'dismiss'],\n state: {\n status: {\n type: 'string',\n descriptionKey: 'ui.agent.connectionStatus.state.status',\n description:\n \"Connection state of the external service: 'connected', 'disconnected', 'pending', or 'error'.\",\n read: (handle) => handle.getStatus(),\n },\n },\n actions: {\n connect: {\n // Reaches an external service (OAuth handoff / server write), so it is\n // both a `write` and `openWorld`.\n safety: 'write',\n openWorld: true,\n descriptionKey: 'ui.agent.connectionStatus.actions.connect',\n description:\n 'Begin connecting the external service — invokes the consumer-supplied onConnect handler.',\n invoke: (handle) => {\n handle.connect();\n },\n },\n disconnect: {\n // Tears down an established external connection — irreversible from the\n // agent's perspective, so `destructive` + `openWorld`.\n safety: 'destructive',\n openWorld: true,\n descriptionKey: 'ui.agent.connectionStatus.actions.disconnect',\n description:\n 'Disconnect the external service — invokes the consumer-supplied onDisconnect handler.',\n invoke: (handle) => {\n handle.disconnect();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'connection-status',\n description: 'Marks the ConnectionStatus wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import { forwardRef, useMemo } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Card } from '../card';\nimport { Badge, type BadgeProps } from '../badge';\nimport { Button } from '../button';\nimport { DescriptionList } from '../description-list';\nimport { useAgentRegistration } from '../../agent';\nimport { connectionStatusAgent } from './connection-status.agent';\n\nexport type ConnectionStatusValue =\n | 'connected'\n | 'disconnected'\n | 'pending'\n | 'error';\n\n/** One read-only account row, shown under a connected service. */\nexport interface ConnectionStatusSummaryRow {\n label: string;\n value: string;\n}\n\n/** Agent-readiness curated handle for ConnectionStatus. */\nexport interface ConnectionStatusHandle {\n getStatus: () => ConnectionStatusValue;\n connect: () => void;\n disconnect: () => void;\n}\n\nexport interface ConnectionStatusProps {\n /** Service name — consumer-domain text (e.g. `\"Stripe\"`). */\n service: string;\n /** External-service connection state. */\n status: ConnectionStatusValue;\n /** Short explanatory line — consumer-domain text. */\n description?: string;\n /**\n * Read-only account rows rendered via the kit DescriptionList. Consumer-domain\n * text. Shown only when `status === 'connected'`.\n */\n summary?: ConnectionStatusSummaryRow[];\n /** Called by the Connect action (disconnected / error states). */\n onConnect?: () => void;\n /** Called by the Disconnect action (connected state). */\n onDisconnect?: () => void;\n /** Disable the connect / disconnect action. */\n disabled?: boolean;\n id?: string;\n className?: string;\n 'aria-label'?: string;\n}\n\n// Status → Badge tone. The status word itself is the Badge label, so the tone\n// is redundant colour reinforcement — the meaning is always conveyed by text\n// (constraint: status must never be communicated by colour alone).\nconst BADGE_TONE: Record<\n ConnectionStatusValue,\n NonNullable<BadgeProps['variant']>\n> = {\n connected: 'success',\n disconnected: 'neutral',\n pending: 'warning',\n error: 'error',\n};\n\n// Status → intrinsic-chrome i18n key for the status word.\nconst STATUS_LABEL_KEY: Record<ConnectionStatusValue, string> = {\n connected: 'connectionStatus.statusConnected',\n disconnected: 'connectionStatus.statusDisconnected',\n pending: 'connectionStatus.statusPending',\n error: 'connectionStatus.statusError',\n};\n\nexport const ConnectionStatus = forwardRef<\n HTMLDivElement,\n ConnectionStatusProps\n>(function ConnectionStatus(\n {\n service,\n status,\n description,\n summary,\n onConnect,\n onDisconnect,\n disabled = false,\n id,\n className,\n 'aria-label': ariaLabel,\n },\n ref,\n) {\n const { t } = useTranslation('ui');\n\n const handle = useMemo<ConnectionStatusHandle>(\n () => ({\n getStatus: () => status,\n connect: () => onConnect?.(),\n disconnect: () => onDisconnect?.(),\n }),\n [status, onConnect, onDisconnect],\n );\n useAgentRegistration(connectionStatusAgent, handle, id);\n\n // Footer action. The visual variant is delegated to Button's CVA (intent);\n // pending shows a disabled affordance carrying the status word.\n const action =\n status === 'pending' ? (\n <Button intent=\"primary\" disabled>\n {t('connectionStatus.statusPending')}\n </Button>\n ) : status === 'connected' ? (\n <Button intent=\"outline\" disabled={disabled} onClick={onDisconnect}>\n {t('connectionStatus.disconnect')}\n </Button>\n ) : (\n <Button intent=\"primary\" disabled={disabled} onClick={onConnect}>\n {t('connectionStatus.connect')}\n </Button>\n );\n\n const showSummary =\n status === 'connected' && Array.isArray(summary) && summary.length > 0;\n\n return (\n <div ref={ref} data-component=\"connection-status\" data-component-id={id}>\n <Card className={className} aria-label={ariaLabel}>\n <Card.Header>\n <div className=\"ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <span className=\"type-title-card ds:text-foreground\">\n {service}\n </span>\n <Badge variant={BADGE_TONE[status]}>\n {t(STATUS_LABEL_KEY[status])}\n </Badge>\n </div>\n </Card.Header>\n <Card.Body>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n {description ? (\n <p className=\"type-body-sm ds:text-muted-foreground\">\n {description}\n </p>\n ) : null}\n {showSummary && summary ? (\n <DescriptionList layout=\"inline\" density=\"compact\">\n {summary.map((row) => (\n <DescriptionList.Row key={row.label}>\n <DescriptionList.Term>{row.label}</DescriptionList.Term>\n <DescriptionList.Detail>{row.value}</DescriptionList.Detail>\n </DescriptionList.Row>\n ))}\n </DescriptionList>\n ) : null}\n </div>\n </Card.Body>\n <Card.Footer>{action}</Card.Footer>\n </Card>\n </div>\n );\n});\n\nConnectionStatus.displayName = 'ConnectionStatus';\n"],"names":["connectionStatusAgent","handle","BADGE_TONE","STATUS_LABEL_KEY","ConnectionStatus","forwardRef","service","status","description","summary","onConnect","onDisconnect","disabled","id","className","ariaLabel","ref","t","useTranslation","useMemo","useAgentRegistration","action","jsx","Button","showSummary","jsxs","Card","Badge","DescriptionList","row"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"connection-status-BOZC5QU5.js","sources":["../../src/components/connection-status/connection-status.agent.ts","../../src/components/connection-status/connection-status.tsx"],"sourcesContent":["/* -------------------------------------------------------------------- */\n/* Agent adapter — ConnectionStatus. */\n/* */\n/* See `src/docs/26-agent-readiness.mdx` for the contract. */\n/* -------------------------------------------------------------------- */\n\nimport type { AgentAdapter } from '../../agent/types';\nimport type { ConnectionStatusHandle } from './connection-status';\n\nexport const connectionStatusAgent: AgentAdapter<ConnectionStatusHandle> = {\n id: 'connection-status',\n capabilities: ['submit', 'dismiss'],\n state: {\n status: {\n type: 'string',\n descriptionKey: 'ui.agent.connectionStatus.state.status',\n description:\n \"Connection state of the external service: 'connected', 'disconnected', 'pending', or 'error'.\",\n read: (handle) => handle.getStatus(),\n },\n },\n actions: {\n connect: {\n // Reaches an external service (OAuth handoff / server write), so it is\n // both a `write` and `openWorld`.\n safety: 'write',\n openWorld: true,\n descriptionKey: 'ui.agent.connectionStatus.actions.connect',\n description:\n 'Begin connecting the external service — invokes the consumer-supplied onConnect handler.',\n invoke: (handle) => {\n handle.connect();\n },\n },\n disconnect: {\n // Tears down an established external connection — irreversible from the\n // agent's perspective, so `destructive` + `openWorld`.\n safety: 'destructive',\n openWorld: true,\n descriptionKey: 'ui.agent.connectionStatus.actions.disconnect',\n description:\n 'Disconnect the external service — invokes the consumer-supplied onDisconnect handler.',\n invoke: (handle) => {\n handle.disconnect();\n },\n },\n },\n domHooks: {\n root: {\n attr: 'data-component',\n value: 'connection-status',\n description: 'Marks the ConnectionStatus wrapper.',\n },\n instanceId: {\n attr: 'data-component-id',\n sourceProp: 'id',\n description: 'Sourced from the id prop.',\n },\n },\n};\n","import { forwardRef, useMemo } from 'react';\nimport { useTranslation } from 'react-i18next';\nimport { Card } from '../card';\nimport { Badge, type BadgeProps } from '../badge';\nimport { Button } from '../button';\nimport { DescriptionList } from '../description-list';\nimport { useAgentRegistration } from '../../agent';\nimport { connectionStatusAgent } from './connection-status.agent';\n\nexport type ConnectionStatusValue =\n | 'connected'\n | 'disconnected'\n | 'pending'\n | 'error';\n\n/** One read-only account row, shown under a connected service. */\nexport interface ConnectionStatusSummaryRow {\n label: string;\n value: string;\n}\n\n/** Agent-readiness curated handle for ConnectionStatus. */\nexport interface ConnectionStatusHandle {\n getStatus: () => ConnectionStatusValue;\n connect: () => void;\n disconnect: () => void;\n}\n\nexport interface ConnectionStatusProps {\n /** Service name — consumer-domain text (e.g. `\"Stripe\"`). */\n service: string;\n /** External-service connection state. */\n status: ConnectionStatusValue;\n /** Short explanatory line — consumer-domain text. */\n description?: string;\n /**\n * Read-only account rows rendered via the kit DescriptionList. Consumer-domain\n * text. Shown only when `status === 'connected'`.\n */\n summary?: ConnectionStatusSummaryRow[];\n /** Called by the Connect action (disconnected / error states). */\n onConnect?: () => void;\n /** Called by the Disconnect action (connected state). */\n onDisconnect?: () => void;\n /** Disable the connect / disconnect action. */\n disabled?: boolean;\n id?: string;\n className?: string;\n 'aria-label'?: string;\n}\n\n// Status → Badge tone. The status word itself is the Badge label, so the tone\n// is redundant colour reinforcement — the meaning is always conveyed by text\n// (constraint: status must never be communicated by colour alone).\nconst BADGE_TONE: Record<\n ConnectionStatusValue,\n NonNullable<BadgeProps['variant']>\n> = {\n connected: 'success',\n disconnected: 'neutral',\n pending: 'warning',\n error: 'error',\n};\n\n// Status → intrinsic-chrome i18n key for the status word.\nconst STATUS_LABEL_KEY: Record<ConnectionStatusValue, string> = {\n connected: 'connectionStatus.statusConnected',\n disconnected: 'connectionStatus.statusDisconnected',\n pending: 'connectionStatus.statusPending',\n error: 'connectionStatus.statusError',\n};\n\nexport const ConnectionStatus = forwardRef<\n HTMLDivElement,\n ConnectionStatusProps\n>(function ConnectionStatus(\n {\n service,\n status,\n description,\n summary,\n onConnect,\n onDisconnect,\n disabled = false,\n id,\n className,\n 'aria-label': ariaLabel,\n },\n ref,\n) {\n const { t } = useTranslation('ui');\n\n const handle = useMemo<ConnectionStatusHandle>(\n () => ({\n getStatus: () => status,\n connect: () => onConnect?.(),\n disconnect: () => onDisconnect?.(),\n }),\n [status, onConnect, onDisconnect],\n );\n useAgentRegistration(connectionStatusAgent, handle, id);\n\n // Footer action. The visual variant is delegated to Button's CVA (intent);\n // pending shows a disabled affordance carrying the status word.\n const action =\n status === 'pending' ? (\n <Button intent=\"primary\" disabled>\n {t('connectionStatus.statusPending')}\n </Button>\n ) : status === 'connected' ? (\n <Button intent=\"outline\" disabled={disabled} onClick={onDisconnect}>\n {t('connectionStatus.disconnect')}\n </Button>\n ) : (\n <Button intent=\"primary\" disabled={disabled} onClick={onConnect}>\n {t('connectionStatus.connect')}\n </Button>\n );\n\n const showSummary =\n status === 'connected' && Array.isArray(summary) && summary.length > 0;\n\n return (\n <div ref={ref} data-component=\"connection-status\" data-component-id={id}>\n <Card className={className} aria-label={ariaLabel}>\n <Card.Header>\n <div className=\"ds:flex ds:items-center ds:justify-between ds:gap-[var(--spacing-sm)]\">\n <span className=\"type-title-card ds:text-foreground\">\n {service}\n </span>\n <Badge variant={BADGE_TONE[status]}>\n {t(STATUS_LABEL_KEY[status])}\n </Badge>\n </div>\n </Card.Header>\n <Card.Body>\n <div className=\"ds:flex ds:flex-col ds:gap-[var(--spacing-md)]\">\n {description ? (\n <p className=\"type-body-sm ds:text-muted-foreground\">\n {description}\n </p>\n ) : null}\n {showSummary && summary ? (\n <DescriptionList layout=\"inline\" density=\"compact\">\n {summary.map((row) => (\n <DescriptionList.Row key={row.label}>\n <DescriptionList.Term>{row.label}</DescriptionList.Term>\n <DescriptionList.Detail>{row.value}</DescriptionList.Detail>\n </DescriptionList.Row>\n ))}\n </DescriptionList>\n ) : null}\n </div>\n </Card.Body>\n <Card.Footer>{action}</Card.Footer>\n </Card>\n </div>\n );\n});\n\nConnectionStatus.displayName = 'ConnectionStatus';\n"],"names":["connectionStatusAgent","handle","BADGE_TONE","STATUS_LABEL_KEY","ConnectionStatus","forwardRef","service","status","description","summary","onConnect","onDisconnect","disabled","id","className","ariaLabel","ref","t","useTranslation","useMemo","useAgentRegistration","action","jsx","Button","showSummary","jsxs","Card","Badge","DescriptionList","row"],"mappings":"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"}
|