@qoretechnologies/qorus-chat 0.1.0-beta.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/README.md +82 -0
- package/dist/client/chatClient.d.ts +16 -0
- package/dist/client/chatClient.d.ts.map +1 -0
- package/dist/client/sseParser.d.ts +3 -0
- package/dist/client/sseParser.d.ts.map +1 -0
- package/dist/client/types.d.ts +38 -0
- package/dist/client/types.d.ts.map +1 -0
- package/dist/components/BubbleLauncher.d.ts +6 -0
- package/dist/components/BubbleLauncher.d.ts.map +1 -0
- package/dist/components/ChatShell.d.ts +24 -0
- package/dist/components/ChatShell.d.ts.map +1 -0
- package/dist/components/ChatWidget.d.ts +26 -0
- package/dist/components/ChatWidget.d.ts.map +1 -0
- package/dist/components/MessageInput.d.ts +11 -0
- package/dist/components/MessageInput.d.ts.map +1 -0
- package/dist/components/MessageList.d.ts +9 -0
- package/dist/components/MessageList.d.ts.map +1 -0
- package/dist/components/Sources.d.ts +6 -0
- package/dist/components/Sources.d.ts.map +1 -0
- package/dist/components/theme.d.ts +20 -0
- package/dist/components/theme.d.ts.map +1 -0
- package/dist/components/types.d.ts +15 -0
- package/dist/components/types.d.ts.map +1 -0
- package/dist/components/useChatSession.d.ts +17 -0
- package/dist/components/useChatSession.d.ts.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/qorus-chat.js +721 -0
- package/dist/qorus-chat.js.map +1 -0
- package/dist/widget.js +6550 -0
- package/dist/widget.js.map +1 -0
- package/package.json +57 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qorus-chat.js","names":[],"sources":["../src/components/Sources.tsx","../src/components/theme.ts","../src/components/MessageList.tsx","../src/components/MessageInput.tsx","../src/client/sseParser.ts","../src/client/chatClient.ts","../src/components/useChatSession.ts","../src/components/ChatShell.tsx","../src/components/BubbleLauncher.tsx","../src/components/ChatWidget.tsx"],"sourcesContent":["import { memo, useState } from 'react';\nimport styled from 'styled-components';\nimport { ReqoreButton, ReqoreP } from '@qoretechnologies/reqore';\nimport type { IChatTurnSource } from '../client/types';\n\nconst StyledSourcesWrap = styled.div`\n margin-top: 10px;\n padding-top: 8px;\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n font-size: 11px;\n opacity: 0.8;\n`;\n\nconst StyledSourceItem = styled.div`\n display: flex;\n align-items: flex-start;\n gap: 6px;\n padding: 4px 0;\n &:not(:last-child) { border-bottom: 1px dashed rgba(255, 255, 255, 0.05); }\n`;\n\nconst StyledSourceLabel = styled.span`\n font-weight: 600;\n font-size: 11px;\n`;\n\nconst StyledSourceExcerpt = styled.span`\n font-size: 11px;\n opacity: 0.75;\n font-style: italic;\n`;\n\nexport interface ISourcesProps {\n sources: IChatTurnSource[];\n}\n\nexport const Sources = memo<ISourcesProps>(({ sources }) => {\n const [open, setOpen] = useState(false);\n\n return (\n <StyledSourcesWrap>\n <ReqoreButton\n flat\n minimal\n size='small'\n icon={open ? 'ArrowDownSLine' : 'ArrowRightSLine'}\n onClick={() => setOpen((v) => !v)}\n >\n {sources.length} {sources.length === 1 ? 'source' : 'sources'}\n </ReqoreButton>\n {open && (\n <div style={{ marginTop: 6 }}>\n {sources.map((s, i) => (\n <StyledSourceItem key={`${s.collection}-${s.chunk_id ?? i}`}>\n <StyledSourceLabel>{s.collection}</StyledSourceLabel>\n {s.excerpt && <StyledSourceExcerpt>{s.excerpt}</StyledSourceExcerpt>}\n {typeof s.score === 'number' && (\n <ReqoreP style={{ fontSize: 10, opacity: 0.6 }}>\n {s.score.toFixed(2)}\n </ReqoreP>\n )}\n </StyledSourceItem>\n ))}\n </div>\n )}\n </StyledSourcesWrap>\n );\n});\n\nSources.displayName = 'Sources';\n","import type { IReqoreTheme } from '@qoretechnologies/reqore/dist/constants/theme';\nimport type { IReqoreEffect } from '@qoretechnologies/reqore/dist/components/Effect';\n\n/** Curated base looks. Customers pick one — they can't pass a broken theme. */\nexport type TWidgetTheme = 'dark' | 'light' | 'qorus';\n\nexport const DEFAULT_THEME: TWidgetTheme = 'qorus';\nexport const DEFAULT_ACCENT = '#7b68ee';\n\nconst PRESETS: Record<TWidgetTheme, { main: `#${string}`; text: `#${string}` }> = {\n // Qorus-branded dark purple — the default.\n qorus: { main: '#1b1226', text: '#ece9f5' },\n // Neutral dark, for dark sites that aren't purple.\n dark: { main: '#17181c', text: '#e7e7ea' },\n // Light, for light sites.\n light: { main: '#f4f4f7', text: '#1d1d20' },\n};\n\nexport function isWidgetTheme(value: unknown): value is TWidgetTheme {\n return value === 'dark' || value === 'light' || value === 'qorus';\n}\n\n/** Build the Reqore theme for a preset — fed to `ReqoreUIProvider`. */\nexport function buildWidgetTheme(theme: TWidgetTheme = DEFAULT_THEME): Partial<IReqoreTheme> {\n const preset = PRESETS[theme] ?? PRESETS[DEFAULT_THEME];\n return { main: preset.main, text: { color: preset.text } };\n}\n\n/** Darken (`percent < 0`) or lighten (`percent > 0`) a `#rrggbb` hex. */\nexport function shade(hex: string, percent: number): `#${string}` {\n const n = parseInt(hex.replace('#', ''), 16);\n const clamp = (c: number) =>\n Math.max(0, Math.min(255, Math.round(c + (percent < 0 ? c : 255 - c) * percent)));\n const r = clamp((n >> 16) & 0xff);\n const g = clamp((n >> 8) & 0xff);\n const b = clamp(n & 0xff);\n return `#${[r, g, b].map((c) => c.toString(16).padStart(2, '0')).join('')}` as `#${string}`;\n}\n\n/** Append a 2-digit alpha to a `#rrggbb` hex (`alpha` 0..1). */\nexport function withAlpha(hex: string, alpha: number): `#${string}` {\n const a = Math.max(0, Math.min(255, Math.round(alpha * 255)))\n .toString(16)\n .padStart(2, '0');\n return `${hex}${a}` as `#${string}`;\n}\n\n/** Solid accent gradient — send button. */\nexport function accentGradient(accent: string): IReqoreEffect {\n return {\n gradient: {\n colors: { 0: accent as `#${string}`, 100: shade(accent, -0.32) },\n direction: 'to bottom right',\n },\n };\n}\n\n/** Accent gradient with a glow — the floating launcher button. */\nexport function fabEffect(accent: string): IReqoreEffect {\n return {\n gradient: {\n colors: { 0: accent as `#${string}`, 100: shade(accent, -0.42) },\n direction: 'to bottom right',\n },\n glow: { color: accent as `#${string}`, size: 2, blur: 10 },\n };\n}\n\n/** Translucent accent gradient — the visitor's own message bubble. */\nexport function accentBubbleEffect(accent: string): IReqoreEffect {\n return {\n gradient: {\n colors: { 0: withAlpha(accent, 0.16), 100: withAlpha(shade(accent, -0.32), 0.16) },\n direction: 'to bottom right',\n },\n };\n}\n","import { Fragment, memo, useEffect, useMemo, useRef } from 'react';\nimport styled from 'styled-components';\nimport {\n ReqoreBubble,\n ReqoreBubbleGroup,\n ReqoreMessage,\n ReqoreP,\n ReqoreSpinner,\n} from '@qoretechnologies/reqore';\nimport { Sources } from './Sources';\nimport { accentBubbleEffect, shade } from './theme';\nimport type { IDisplayMessage } from './types';\n\nconst StyledScroll = styled.div`\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n padding: 14px;\n display: flex;\n flex-direction: column;\n\n scrollbar-width: thin;\n scrollbar-color: rgba(255, 255, 255, 0.16) transparent;\n &::-webkit-scrollbar {\n width: 6px;\n }\n &::-webkit-scrollbar-thumb {\n background: rgba(255, 255, 255, 0.16);\n border-radius: 3px;\n }\n`;\n\nconst StyledEmpty = styled.div`\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 24px;\n`;\n\n// Text caret shown while the assistant reply is still streaming.\nconst StyledCursor = styled.span`\n display: inline-block;\n width: 2px;\n height: 1em;\n margin-left: 1px;\n background: currentColor;\n vertical-align: text-bottom;\n animation: qorus-chat-blink 1s steps(1) infinite;\n\n @keyframes qorus-chat-blink {\n 50% {\n opacity: 0;\n }\n }\n`;\n\nfunction renderText(content: string, streaming: boolean) {\n const lines = content.split('\\n');\n return (\n <>\n {lines.map((line, i) => (\n <Fragment key={i}>\n {line}\n {i < lines.length - 1 && <br />}\n </Fragment>\n ))}\n {streaming && <StyledCursor />}\n </>\n );\n}\n\nfunction formatTime(ts: number): string {\n return new Date(ts).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });\n}\n\nexport interface IMessageListProps {\n messages: IDisplayMessage[];\n isStreaming: boolean;\n emptyText?: string;\n accent: string;\n}\n\nexport const MessageList = memo<IMessageListProps>(({ messages, isStreaming, emptyText, accent }) => {\n const bottomRef = useRef<HTMLDivElement>(null);\n // Translucent accent gradient for the visitor's own messages — the panel\n // shows through. ReqoreBubble drops its solid background-color when an effect\n // gradient is set, so the rgba stops genuinely read as translucent.\n const userEffect = useMemo(() => accentBubbleEffect(accent), [accent]);\n const spinnerColor = useMemo(() => shade(accent, 0.35), [accent]);\n\n useEffect(() => {\n bottomRef.current?.scrollIntoView({ behavior: 'smooth', block: 'end' });\n }, [messages, isStreaming]);\n\n if (messages.length === 0) {\n return (\n <StyledScroll>\n <StyledEmpty>\n <ReqoreMessage flat minimal icon='ChatSmile2Line' size='small'>\n {emptyText ?? 'Ask me anything to get started.'}\n </ReqoreMessage>\n </StyledEmpty>\n </StyledScroll>\n );\n }\n\n return (\n <StyledScroll>\n <ReqoreBubbleGroup>\n {messages.map((m, i) => {\n const isUser = m.role === 'user';\n const isStreamingMsg = m.status === 'streaming';\n const isWaiting = isStreamingMsg && !m.content;\n const isError = m.status === 'error';\n // Assistant reply that finished with no content (e.g. the model\n // errored server-side) — show a hint rather than a blank bubble.\n const isEmptyReply = !isUser && m.status === 'complete' && !m.content;\n // One timestamp per run — only the last message of a consecutive\n // same-sender cluster carries it, so same-minute bursts aren't noisy.\n const isLastOfRun = i === messages.length - 1 || messages[i + 1].role !== m.role;\n\n return (\n <ReqoreBubble\n key={m.id}\n align={isUser ? 'right' : 'left'}\n size='small'\n minimal={!isUser && !isError}\n raised\n intent={isError ? 'danger' : undefined}\n effect={isUser ? userEffect : undefined}\n timestamp={!isWaiting && isLastOfRun ? formatTime(m.createdAt) : undefined}\n >\n {isError\n ? m.error ?? 'Something went wrong.'\n : isWaiting\n ? <ReqoreSpinner size='small' iconColor={spinnerColor} />\n : isEmptyReply\n ? <ReqoreP effect={{ opacity: 0.6, italic: true }}>No response received.</ReqoreP>\n : renderText(m.content, isStreamingMsg)}\n {m.sources && m.sources.length > 0 && <Sources sources={m.sources} />}\n </ReqoreBubble>\n );\n })}\n </ReqoreBubbleGroup>\n <div ref={bottomRef} />\n </StyledScroll>\n );\n});\n\nMessageList.displayName = 'MessageList';\n","import { memo, useCallback, useMemo, useState, type ChangeEvent, type KeyboardEvent } from 'react';\nimport styled from 'styled-components';\nimport { ReqoreButton, ReqoreControlGroup, ReqoreTextarea } from '@qoretechnologies/reqore';\nimport { accentGradient } from './theme';\n\nconst StyledInputWrap = styled.div`\n padding: 10px 12px 10px;\n border-top: 1px solid rgba(255, 255, 255, 0.08);\n flex-shrink: 0;\n`;\n\nconst StyledPoweredBy = styled.div`\n margin-top: 8px;\n text-align: center;\n font-size: 10px;\n letter-spacing: 0.2px;\n opacity: 0.45;\n\n a {\n color: inherit;\n text-decoration: none;\n font-weight: 600;\n }\n a:hover {\n text-decoration: underline;\n }\n`;\n\nexport interface IMessageInputProps {\n isStreaming: boolean;\n onSend: (text: string) => void;\n onCancel: () => void;\n placeholder?: string;\n disabled?: boolean;\n /** Brand accent colour (hex) for the send button. */\n accent: string;\n}\n\nexport const MessageInput = memo<IMessageInputProps>(\n ({ isStreaming, onSend, onCancel, placeholder, disabled, accent }) => {\n const [value, setValue] = useState('');\n const sendEffect = useMemo(() => accentGradient(accent), [accent]);\n\n const submit = useCallback(() => {\n const trimmed = value.trim();\n if (!trimmed || isStreaming || disabled) return;\n onSend(trimmed);\n setValue('');\n }, [value, isStreaming, disabled, onSend]);\n\n const handleKey = useCallback(\n (e: KeyboardEvent<HTMLTextAreaElement>) => {\n if (e.key === 'Enter' && !e.shiftKey) {\n e.preventDefault();\n submit();\n }\n },\n [submit]\n );\n\n return (\n <StyledInputWrap>\n <ReqoreControlGroup fluid verticalAlign='flex-end'>\n <ReqoreTextarea\n fluid\n scaleWithContent\n rows={1}\n value={value}\n onChange={(e: ChangeEvent<HTMLTextAreaElement>) => setValue(e.target.value)}\n onKeyDown={handleKey}\n placeholder={placeholder ?? 'Ask anything…'}\n disabled={disabled}\n />\n <ReqoreButton\n fixed\n icon={isStreaming ? 'StopCircleLine' : 'SendPlane2Fill'}\n intent={isStreaming ? 'danger' : undefined}\n effect={isStreaming ? undefined : sendEffect}\n onClick={isStreaming ? onCancel : submit}\n disabled={!isStreaming && (!value.trim() || disabled)}\n tooltip={isStreaming ? 'Stop' : 'Send'}\n />\n </ReqoreControlGroup>\n <StyledPoweredBy>\n Powered by{' '}\n <a href='https://qorus.cloud' target='_blank' rel='noopener noreferrer'>\n qorus.cloud\n </a>\n </StyledPoweredBy>\n </StyledInputWrap>\n );\n }\n);\n\nMessageInput.displayName = 'MessageInput';\n","import type { TChatStreamEvent } from './types';\n\n// Parses the qorus ai-endpoint SSE wire format described in qorus\n// `Classes/QorusRestApiHandlerV9.qc:25613-25634`:\n//\n// data: {\"delta\": \"Hello\"}\\n\\n\n// data: {\"sources\": [...]}\\n\\n\n// data: {\"usage\": {...}}\\n\\n\n// data: [DONE]\\n\\n\n//\n// Pure function over a ReadableStream<Uint8Array> chunk; emits decoded events.\nexport async function* parseSseStream(\n stream: ReadableStream<Uint8Array>\n): AsyncGenerator<TChatStreamEvent, void, void> {\n const reader = stream.getReader();\n const decoder = new TextDecoder('utf-8');\n let buffer = '';\n\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n\n let nlIdx: number;\n while ((nlIdx = buffer.indexOf('\\n\\n')) !== -1) {\n const rawEvent = buffer.slice(0, nlIdx);\n buffer = buffer.slice(nlIdx + 2);\n const line = rawEvent.replace(/^data:\\s?/, '').trim();\n if (!line) continue;\n if (line === '[DONE]') {\n yield { type: 'done' };\n continue;\n }\n try {\n const payload = JSON.parse(line) as Record<string, unknown>;\n if (typeof payload.delta === 'string') {\n yield { type: 'delta', delta: payload.delta };\n }\n if (Array.isArray(payload.sources)) {\n yield { type: 'sources', sources: payload.sources as never };\n }\n if (payload.usage && typeof payload.usage === 'object') {\n yield { type: 'usage', usage: payload.usage as never };\n }\n } catch (err) {\n yield { type: 'error', error: `parse-error: ${(err as Error).message}` };\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n}\n","import { parseSseStream } from './sseParser';\nimport type { IChatClientOptions, IChatMessage, TChatStreamEvent } from './types';\n\n// Turns a non-OK chat response into a visitor-facing message. A blocked\n// guardrail comes back as 409 AI-GUARDRAIL-VIOLATION; auth and rate-limit\n// failures get their own wording.\nasync function readErrorMessage(res: Response): Promise<string> {\n let detail = '';\n try {\n const text = await res.text();\n try {\n const json = JSON.parse(text) as Record<string, unknown>;\n detail = String(json.desc ?? json.err ?? json.message ?? text);\n } catch {\n detail = text;\n }\n } catch {\n // body unreadable — fall through to status-based wording\n }\n\n if (res.status === 409 || /guardrail/i.test(detail)) {\n return 'Your message was blocked by a content guardrail.';\n }\n if (res.status === 401 || res.status === 403) {\n return 'This chat is not authorized. Please contact the site owner.';\n }\n if (res.status === 429) {\n return 'Too many messages right now — please wait a moment and try again.';\n }\n return detail.trim() ? `Something went wrong: ${detail.trim()}` : 'Something went wrong.';\n}\n\n// Talks to a Qorus AI endpoint.\n// Stateless (`/chat`) and conversational (`/conversations/.../messages`) modes share\n// the SSE wire format defined in qorus QorusRestApiHandlerV9.qc:25506+.\n//\n// Auth model: the public-flavor API key (qorus#226 Gap 1). Sent as `Authorization: Bearer`.\n// CORS + origin enforcement is the server's job; this client just trusts the response.\nexport class QorusChatClient {\n private readonly baseUrl: string;\n private readonly endpoint: string;\n private readonly apiKey: string;\n private readonly visitorId?: string;\n\n constructor(opts: IChatClientOptions) {\n this.baseUrl = (opts.baseUrl ?? '').replace(/\\/+$/, '');\n this.endpoint = opts.endpoint;\n this.apiKey = opts.apiKey;\n this.visitorId = opts.visitorId;\n }\n\n async *streamStateless(message: string): AsyncGenerator<TChatStreamEvent, void, void> {\n const url = `${this.baseUrl}/api/latest/ai-endpoints/${encodeURIComponent(this.endpoint)}/chat`;\n const res = await this.postSse(url, { message, stream: true });\n if (!res.ok) {\n yield { type: 'error', error: await readErrorMessage(res) };\n return;\n }\n if (!res.body) {\n yield { type: 'error', error: 'No response received.' };\n return;\n }\n yield* parseSseStream(res.body);\n }\n\n async createConversation(): Promise<{ uuid: string }> {\n const url = `${this.baseUrl}/api/latest/ai-endpoints/${encodeURIComponent(this.endpoint)}/conversations`;\n const res = await fetch(url, {\n method: 'POST',\n headers: this.headers('application/json'),\n body: JSON.stringify({}),\n });\n if (!res.ok) throw new Error(`createConversation: ${res.status}`);\n const body = (await res.json()) as { conversation_uuid: string };\n return { uuid: body.conversation_uuid };\n }\n\n async *streamMessage(\n conversationUuid: string,\n message: string,\n history?: IChatMessage[]\n ): AsyncGenerator<TChatStreamEvent, void, void> {\n void history;\n const url = `${this.baseUrl}/api/latest/ai-endpoints/${encodeURIComponent(\n this.endpoint\n )}/conversations/${encodeURIComponent(conversationUuid)}/messages`;\n const res = await this.postSse(url, { message, stream: true });\n if (!res.ok) {\n yield { type: 'error', error: await readErrorMessage(res) };\n return;\n }\n if (!res.body) {\n yield { type: 'error', error: 'No response received.' };\n return;\n }\n yield* parseSseStream(res.body);\n }\n\n private postSse(url: string, body: unknown): Promise<Response> {\n return fetch(url, {\n method: 'POST',\n headers: this.headers('text/event-stream'),\n body: JSON.stringify(body),\n });\n }\n\n private headers(accept: string): Record<string, string> {\n const h: Record<string, string> = {\n 'Content-Type': 'application/json',\n Accept: `${accept}, application/json;q=0.5`,\n Authorization: `Bearer ${this.apiKey}`,\n };\n if (this.visitorId) h['qorus-visitor-id'] = this.visitorId;\n return h;\n }\n}\n","import { useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { QorusChatClient } from '../client/chatClient';\nimport type { IDisplayMessage } from './types';\n\nconst VISITOR_ID_KEY_PREFIX = 'qorus-chat-visitor-';\n\nfunction loadOrCreateVisitorId(endpoint: string): string {\n try {\n const key = `${VISITOR_ID_KEY_PREFIX}${endpoint}`;\n const existing = window.localStorage.getItem(key);\n if (existing) return existing;\n const fresh = `v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n window.localStorage.setItem(key, fresh);\n return fresh;\n } catch {\n return `v_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n }\n}\n\nfunction genMessageId(): string {\n return `m_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`;\n}\n\nexport interface IUseChatSessionOptions {\n endpoint: string;\n apiKey: string;\n baseUrl: string;\n visitorId?: string;\n mode?: 'stateless' | 'conversation';\n}\n\nexport interface IUseChatSessionResult {\n messages: IDisplayMessage[];\n isStreaming: boolean;\n send: (text: string) => Promise<void>;\n cancel: () => void;\n reset: () => Promise<void>;\n}\n\n// Owns the chat session: history, streaming state, send/cancel/reset. Pure hook -\n// no Reqore here, so the same hook can power either an inline or bubble mount.\nexport function useChatSession(opts: IUseChatSessionOptions): IUseChatSessionResult {\n const [messages, setMessages] = useState<IDisplayMessage[]>([]);\n const [isStreaming, setIsStreaming] = useState(false);\n const conversationRef = useRef<string | null>(null);\n const abortRef = useRef<AbortController | null>(null);\n\n const visitorId = useMemo(\n () => opts.visitorId ?? loadOrCreateVisitorId(opts.endpoint),\n [opts.endpoint, opts.visitorId]\n );\n\n const client = useMemo(\n () =>\n new QorusChatClient({\n endpoint: opts.endpoint,\n apiKey: opts.apiKey,\n baseUrl: opts.baseUrl,\n visitorId,\n }),\n [opts.endpoint, opts.apiKey, opts.baseUrl, visitorId]\n );\n\n // Default to the stateless /chat endpoint. Conversation mode (server-side\n // history) needs the visitor-identity + TTL work tracked in qorus#226 and is\n // opt-in until that lands.\n const mode = opts.mode ?? 'stateless';\n\n const updateMessage = useCallback((id: string, patch: Partial<IDisplayMessage>) => {\n setMessages((prev) => prev.map((m) => (m.id === id ? { ...m, ...patch } : m)));\n }, []);\n\n const send = useCallback(\n async (text: string) => {\n const trimmed = text.trim();\n if (!trimmed || isStreaming) return;\n\n const userMsg: IDisplayMessage = {\n id: genMessageId(),\n role: 'user',\n content: trimmed,\n status: 'complete',\n createdAt: Date.now(),\n };\n const assistantMsg: IDisplayMessage = {\n id: genMessageId(),\n role: 'assistant',\n content: '',\n status: 'streaming',\n createdAt: Date.now(),\n };\n setMessages((prev) => [...prev, userMsg, assistantMsg]);\n setIsStreaming(true);\n abortRef.current = new AbortController();\n\n try {\n let convUuid = conversationRef.current;\n if (mode === 'conversation' && !convUuid) {\n const created = await client.createConversation();\n convUuid = created.uuid;\n conversationRef.current = convUuid;\n }\n\n const stream =\n mode === 'conversation' && convUuid\n ? client.streamMessage(convUuid, trimmed)\n : client.streamStateless(trimmed);\n\n let accumulated = '';\n for await (const event of stream) {\n if (abortRef.current?.signal.aborted) break;\n switch (event.type) {\n case 'delta':\n accumulated += event.delta;\n updateMessage(assistantMsg.id, { content: accumulated });\n break;\n case 'sources':\n updateMessage(assistantMsg.id, { sources: event.sources });\n break;\n case 'usage':\n updateMessage(assistantMsg.id, { usage: event.usage });\n break;\n case 'error':\n updateMessage(assistantMsg.id, { status: 'error', error: event.error });\n break;\n case 'done':\n break;\n }\n }\n updateMessage(assistantMsg.id, { status: 'complete' });\n } catch (err) {\n const message = err instanceof Error ? err.message : 'unknown error';\n updateMessage(assistantMsg.id, { status: 'error', error: message });\n } finally {\n setIsStreaming(false);\n abortRef.current = null;\n }\n },\n [client, isStreaming, mode, updateMessage]\n );\n\n const cancel = useCallback(() => {\n abortRef.current?.abort();\n setIsStreaming(false);\n }, []);\n\n const reset = useCallback(async () => {\n cancel();\n conversationRef.current = null;\n setMessages([]);\n }, [cancel]);\n\n useEffect(() => () => abortRef.current?.abort(), []);\n\n return { messages, isStreaming, send, cancel, reset };\n}\n","import { memo, useCallback } from 'react';\nimport { ReqorePanel } from '@qoretechnologies/reqore';\nimport type { IReqorePanelAction } from '@qoretechnologies/reqore/dist/components/Panel';\nimport { MessageList } from './MessageList';\nimport { MessageInput } from './MessageInput';\nimport { useChatSession } from './useChatSession';\n\nexport interface IChatShellProps {\n endpoint: string;\n apiKey: string;\n baseUrl: string;\n visitorId?: string;\n title?: string;\n subtitle?: string;\n placeholder?: string;\n emptyText?: string;\n /** Brand accent colour (hex) — send button + user bubble. */\n accent: string;\n showClose?: boolean;\n onClose?: () => void;\n /**\n * Host-supplied panel-header actions, rendered to the **left** of the\n * built-in **New conversation** / **Close** actions. Lets the embedder\n * inject things like \"Edit and publish\" (qorus-ide's AI-endpoint\n * playground) without breaking the widget's chrome.\n */\n extraActions?: IReqorePanelAction[];\n}\n\nexport const ChatShell = memo<IChatShellProps>(\n ({\n endpoint,\n apiKey,\n baseUrl,\n visitorId,\n title = 'Assistant',\n subtitle,\n placeholder,\n emptyText,\n accent,\n showClose,\n onClose,\n extraActions,\n }) => {\n const { messages, isStreaming, send, cancel, reset } = useChatSession({\n endpoint,\n apiKey,\n baseUrl,\n visitorId,\n });\n\n const handleSend = useCallback(\n (text: string) => {\n void send(text);\n },\n [send]\n );\n\n const actions: IReqorePanelAction[] = [\n ...(extraActions ?? []),\n {\n icon: 'RefreshLine',\n tooltip: 'New conversation',\n onClick: () => void reset(),\n disabled: messages.length === 0,\n },\n ];\n if (showClose) {\n actions.push({ icon: 'CloseLine', tooltip: 'Close', onClick: onClose });\n }\n\n return (\n <ReqorePanel\n fill\n rounded\n responsiveTitle={false}\n responsiveActions={false}\n label={title}\n icon='ChatSmile2Line'\n badge={{\n label: isStreaming ? 'Typing…' : 'Online',\n intent: 'success',\n minimal: true,\n }}\n description={subtitle}\n actions={actions}\n contentStyle={{\n padding: 0,\n display: 'flex',\n flexDirection: 'column',\n overflow: 'hidden',\n }}\n >\n <MessageList\n messages={messages}\n isStreaming={isStreaming}\n emptyText={emptyText}\n accent={accent}\n />\n <MessageInput\n isStreaming={isStreaming}\n onSend={handleSend}\n onCancel={cancel}\n placeholder={placeholder}\n accent={accent}\n />\n </ReqorePanel>\n );\n }\n);\n\nChatShell.displayName = 'ChatShell';\n","import { memo, useMemo, useState } from 'react';\nimport styled, { keyframes } from 'styled-components';\nimport { ReqoreButton } from '@qoretechnologies/reqore';\nimport { ChatShell, type IChatShellProps } from './ChatShell';\nimport { fabEffect } from './theme';\n\nconst slideUp = keyframes`\n from { transform: translateY(16px); opacity: 0; }\n to { transform: translateY(0); opacity: 1; }\n`;\n\n// Fixed-positioned launcher stack — positioning is genuine layout glue and\n// has no Reqore equivalent.\nconst StyledLauncher = styled.div`\n position: fixed;\n bottom: 20px;\n right: 20px;\n z-index: 2147483600;\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 14px;\n`;\n\nconst StyledPanel = styled.div`\n width: min(400px, calc(100vw - 32px));\n height: min(600px, calc(100vh - 116px));\n animation: ${slideUp} 0.2s cubic-bezier(0.16, 1, 0.3, 1);\n /* Layered drop-shadows follow the panel's real rounded shape and lift it\n clearly off a dark host page. */\n filter: drop-shadow(0 26px 64px rgba(0, 0, 0, 0.72))\n drop-shadow(0 8px 22px rgba(0, 0, 0, 0.55));\n`;\n\nexport interface IBubbleLauncherProps extends IChatShellProps {\n defaultOpen?: boolean;\n}\n\nexport const BubbleLauncher = memo<IBubbleLauncherProps>(({ defaultOpen, ...shellProps }) => {\n const [open, setOpen] = useState(!!defaultOpen);\n const effect = useMemo(() => fabEffect(shellProps.accent), [shellProps.accent]);\n\n return (\n <StyledLauncher>\n {open && (\n <StyledPanel>\n <ChatShell {...shellProps} showClose onClose={() => setOpen(false)} />\n </StyledPanel>\n )}\n <ReqoreButton\n circle\n compact\n size='huge'\n icon={open ? 'CloseLine' : 'ChatSmile2Fill'}\n effect={effect}\n onClick={() => setOpen((v) => !v)}\n tooltip={open ? 'Close chat' : 'Open chat'}\n // padding:0 — `size='huge'` keeps 14px padding which shoves the icon\n // off-centre inside the forced 58px circle; zeroing it lets the icon\n // centre via the button's own flex + margin:auto.\n style={{ width: '58px', height: '58px', padding: 0 }}\n />\n </StyledLauncher>\n );\n});\n\nBubbleLauncher.displayName = 'BubbleLauncher';\n","import { memo } from 'react';\nimport styled from 'styled-components';\nimport { ReqoreUIProvider } from '@qoretechnologies/reqore';\nimport type { IReqorePanelAction } from '@qoretechnologies/reqore/dist/components/Panel';\nimport { ChatShell } from './ChatShell';\nimport { BubbleLauncher } from './BubbleLauncher';\nimport { buildWidgetTheme, DEFAULT_ACCENT, type TWidgetTheme } from './theme';\nimport type { TMode } from './types';\n\nconst StyledInlineFrame = styled.div`\n width: 100%;\n height: 100%;\n min-height: 320px;\n display: flex;\n`;\n\nexport interface IChatWidgetProps {\n endpoint: string;\n apiKey: string;\n baseUrl: string;\n mode?: TMode;\n visitorId?: string;\n title?: string;\n subtitle?: string;\n placeholder?: string;\n defaultOpen?: boolean;\n /** Curated base look: 'qorus' (default), 'dark' or 'light'. */\n theme?: TWidgetTheme;\n /** Brand accent colour (hex) — drives the launcher, send button and user bubble. */\n accent?: string;\n /**\n * Host-supplied panel-header actions, rendered before the widget's own\n * **New conversation** / **Close** actions. Only honoured in `inline`\n * mode (the bubble launcher hides the panel header by design).\n */\n extraActions?: IReqorePanelAction[];\n}\n\nexport const ChatWidget = memo<IChatWidgetProps>(\n ({\n endpoint,\n apiKey,\n baseUrl,\n mode = 'bubble',\n visitorId,\n title,\n subtitle,\n placeholder,\n defaultOpen,\n theme,\n accent = DEFAULT_ACCENT,\n extraActions,\n }) => {\n const reqoreTheme = buildWidgetTheme(theme);\n const sharedShellProps = {\n endpoint,\n apiKey,\n baseUrl,\n visitorId,\n title,\n subtitle,\n placeholder,\n accent,\n extraActions,\n };\n\n return (\n <ReqoreUIProvider theme={reqoreTheme}>\n {mode === 'bubble' ? (\n <BubbleLauncher {...sharedShellProps} defaultOpen={defaultOpen} />\n ) : (\n <StyledInlineFrame>\n <ChatShell {...sharedShellProps} />\n </StyledInlineFrame>\n )}\n </ReqoreUIProvider>\n );\n }\n);\n\nChatWidget.displayName = 'ChatWidget';\n"],"mappings":";;;;;AAKA,IAAM,oBAAoB,OAAO,GAAG;;;;;;;AAQpC,IAAM,mBAAmB,OAAO,GAAG;;;;;;;AAQnC,IAAM,oBAAoB,OAAO,IAAI;;;;AAKrC,IAAM,sBAAsB,OAAO,IAAI;;;;;AAUvC,IAAa,UAAU,MAAqB,EAAE,cAAc;CAC1D,MAAM,CAAC,MAAM,WAAW,SAAS,KAAK;CAEtC,OACE,qBAAC,mBAAD,EAAA,UAAA,CACE,qBAAC,cAAD;EACE,MAAA;EACA,SAAA;EACA,MAAK;EACL,MAAM,OAAO,mBAAmB;EAChC,eAAe,SAAS,MAAM,CAAC,CAAC;YALlC;GAOG,QAAQ;GAAO;GAAE,QAAQ,WAAW,IAAI,WAAW;EACxC;KACb,QACC,oBAAC,OAAD;EAAK,OAAO,EAAE,WAAW,EAAE;YACxB,QAAQ,KAAK,GAAG,MACf,qBAAC,kBAAD,EAAA,UAAA;GACE,oBAAC,mBAAD,EAAA,UAAoB,EAAE,WAA8B,CAAA;GACnD,EAAE,WAAW,oBAAC,qBAAD,EAAA,UAAsB,EAAE,QAA6B,CAAA;GAClE,OAAO,EAAE,UAAU,YAClB,oBAAC,SAAD;IAAS,OAAO;KAAE,UAAU;KAAI,SAAS;IAAI;cAC1C,EAAE,MAAM,QAAQ,CAAC;GACX,CAAA;EAEK,EAAA,GARK,GAAG,EAAE,WAAW,GAAG,EAAE,YAAY,GAQtC,CACnB;CACE,CAAA,CAEU,EAAA,CAAA;AAEvB,CAAC;AAED,QAAQ,cAAc;;;AC/DtB,IAAa,gBAA8B;AAC3C,IAAa,iBAAiB;AAE9B,IAAM,UAA4E;CAEhF,OAAO;EAAE,MAAM;EAAW,MAAM;CAAU;CAE1C,MAAM;EAAE,MAAM;EAAW,MAAM;CAAU;CAEzC,OAAO;EAAE,MAAM;EAAW,MAAM;CAAU;AAC5C;;AAOA,SAAgB,iBAAiB,QAAsB,eAAsC;CAC3F,MAAM,SAAS,QAAQ,UAAU,QAAA;CACjC,OAAO;EAAE,MAAM,OAAO;EAAM,MAAM,EAAE,OAAO,OAAO,KAAK;CAAE;AAC3D;;AAGA,SAAgB,MAAM,KAAa,SAA+B;CAChE,MAAM,IAAI,SAAS,IAAI,QAAQ,KAAK,EAAE,GAAG,EAAE;CAC3C,MAAM,SAAS,MACb,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,MAAM,KAAK,UAAU,IAAI,IAAI,MAAM,KAAK,OAAO,CAAC,CAAC;CAIlF,OAAO,IAAI;EAHD,MAAO,KAAK,KAAM,GAGhB;EAFF,MAAO,KAAK,IAAK,GAEZ;EADL,MAAM,IAAI,GACF;CAAC,EAAE,KAAK,MAAM,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE;AAC1E;;AAGA,SAAgB,UAAU,KAAa,OAA6B;CAIlE,OAAO,GAAG,MAHA,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,MAAM,QAAQ,GAAG,CAAC,CAAC,EACzD,SAAS,EAAE,EACX,SAAS,GAAG,GACC;AAClB;;AAGA,SAAgB,eAAe,QAA+B;CAC5D,OAAO,EACL,UAAU;EACR,QAAQ;GAAE,GAAG;GAAwB,KAAK,MAAM,QAAQ,IAAK;EAAE;EAC/D,WAAW;CACb,EACF;AACF;;AAGA,SAAgB,UAAU,QAA+B;CACvD,OAAO;EACL,UAAU;GACR,QAAQ;IAAE,GAAG;IAAwB,KAAK,MAAM,QAAQ,IAAK;GAAE;GAC/D,WAAW;EACb;EACA,MAAM;GAAE,OAAO;GAAwB,MAAM;GAAG,MAAM;EAAG;CAC3D;AACF;;AAGA,SAAgB,mBAAmB,QAA+B;CAChE,OAAO,EACL,UAAU;EACR,QAAQ;GAAE,GAAG,UAAU,QAAQ,GAAI;GAAG,KAAK,UAAU,MAAM,QAAQ,IAAK,GAAG,GAAI;EAAE;EACjF,WAAW;CACb,EACF;AACF;;;AC/DA,IAAM,eAAe,OAAO,GAAG;;;;;;;;;;;;;;;;;;AAmB/B,IAAM,cAAc,OAAO,GAAG;;;;;;;AAS9B,IAAM,eAAe,OAAO,IAAI;;;;;;;;;;;;;;;AAgBhC,SAAS,WAAW,SAAiB,WAAoB;CACvD,MAAM,QAAQ,QAAQ,MAAM,IAAI;CAChC,OACE,qBAAA,YAAA,EAAA,UAAA,CACG,MAAM,KAAK,MAAM,MAChB,qBAAC,UAAD,EAAA,UAAA,CACG,MACA,IAAI,MAAM,SAAS,KAAK,oBAAC,MAAD,CAAK,CAAA,CACtB,EAAA,GAHK,CAGL,CACX,GACA,aAAa,oBAAC,cAAD,CAAe,CAAA,CAC7B,EAAA,CAAA;AAEN;AAEA,SAAS,WAAW,IAAoB;CACtC,OAAO,IAAI,KAAK,EAAE,EAAE,mBAAmB,CAAC,GAAG;EAAE,MAAM;EAAW,QAAQ;CAAU,CAAC;AACnF;AASA,IAAa,cAAc,MAAyB,EAAE,UAAU,aAAa,WAAW,aAAa;CACnG,MAAM,YAAY,OAAuB,IAAI;CAI7C,MAAM,aAAa,cAAc,mBAAmB,MAAM,GAAG,CAAC,MAAM,CAAC;CACrE,MAAM,eAAe,cAAc,MAAM,QAAQ,GAAI,GAAG,CAAC,MAAM,CAAC;CAEhE,gBAAgB;EACd,UAAU,SAAS,eAAe;GAAE,UAAU;GAAU,OAAO;EAAM,CAAC;CACxE,GAAG,CAAC,UAAU,WAAW,CAAC;CAE1B,IAAI,SAAS,WAAW,GACtB,OACE,oBAAC,cAAD,EAAA,UACE,oBAAC,aAAD,EAAA,UACE,oBAAC,eAAD;EAAe,MAAA;EAAK,SAAA;EAAQ,MAAK;EAAiB,MAAK;YACpD,aAAa;CACD,CAAA,EACJ,CAAA,EACD,CAAA;CAIlB,OACE,qBAAC,cAAD,EAAA,UAAA,CACE,oBAAC,mBAAD,EAAA,UACG,SAAS,KAAK,GAAG,MAAM;EACtB,MAAM,SAAS,EAAE,SAAS;EAC1B,MAAM,iBAAiB,EAAE,WAAW;EACpC,MAAM,YAAY,kBAAkB,CAAC,EAAE;EACvC,MAAM,UAAU,EAAE,WAAW;EAG7B,MAAM,eAAe,CAAC,UAAU,EAAE,WAAW,cAAc,CAAC,EAAE;EAG9D,MAAM,cAAc,MAAM,SAAS,SAAS,KAAK,SAAS,IAAI,GAAG,SAAS,EAAE;EAE5E,OACE,qBAAC,cAAD;GAEE,OAAO,SAAS,UAAU;GAC1B,MAAK;GACL,SAAS,CAAC,UAAU,CAAC;GACrB,QAAA;GACA,QAAQ,UAAU,WAAW,KAAA;GAC7B,QAAQ,SAAS,aAAa,KAAA;GAC9B,WAAW,CAAC,aAAa,cAAc,WAAW,EAAE,SAAS,IAAI,KAAA;aARnE,CAUG,UACG,EAAE,SAAS,0BACX,YACE,oBAAC,eAAD;IAAe,MAAK;IAAQ,WAAW;GAAe,CAAA,IACtD,eACE,oBAAC,SAAD;IAAS,QAAQ;KAAE,SAAS;KAAK,QAAQ;IAAK;cAAG;GAA8B,CAAA,IAC/E,WAAW,EAAE,SAAS,cAAc,GAC3C,EAAE,WAAW,EAAE,QAAQ,SAAS,KAAK,oBAAC,SAAD,EAAS,SAAS,EAAE,QAAU,CAAA,CACxD;KAjBP,EAAE,EAiBK;CAElB,CAAC,EACgB,CAAA,GACnB,oBAAC,OAAD,EAAK,KAAK,UAAY,CAAA,CACV,EAAA,CAAA;AAElB,CAAC;AAED,YAAY,cAAc;;;ACjJ1B,IAAM,kBAAkB,OAAO,GAAG;;;;;AAMlC,IAAM,kBAAkB,OAAO,GAAG;;;;;;;;;;;;;;;;AA2BlC,IAAa,eAAe,MACzB,EAAE,aAAa,QAAQ,UAAU,aAAa,UAAU,aAAa;CACpE,MAAM,CAAC,OAAO,YAAY,SAAS,EAAE;CACrC,MAAM,aAAa,cAAc,eAAe,MAAM,GAAG,CAAC,MAAM,CAAC;CAEjE,MAAM,SAAS,kBAAkB;EAC/B,MAAM,UAAU,MAAM,KAAK;EAC3B,IAAI,CAAC,WAAW,eAAe,UAAU;EACzC,OAAO,OAAO;EACd,SAAS,EAAE;CACb,GAAG;EAAC;EAAO;EAAa;EAAU;CAAM,CAAC;CAYzC,OACE,qBAAC,iBAAD,EAAA,UAAA,CACE,qBAAC,oBAAD;EAAoB,OAAA;EAAM,eAAc;YAAxC,CACE,oBAAC,gBAAD;GACE,OAAA;GACA,kBAAA;GACA,MAAM;GACC;GACP,WAAW,MAAwC,SAAS,EAAE,OAAO,KAAK;GAC1E,WAnBU,aACf,MAA0C;IACzC,IAAI,EAAE,QAAQ,WAAW,CAAC,EAAE,UAAU;KACpC,EAAE,eAAe;KACjB,OAAO;IACT;GACF,GACA,CAAC,MAAM,CAYU;GACX,aAAa,eAAe;GAClB;EACX,CAAA,GACD,oBAAC,cAAD;GACE,OAAA;GACA,MAAM,cAAc,mBAAmB;GACvC,QAAQ,cAAc,WAAW,KAAA;GACjC,QAAQ,cAAc,KAAA,IAAY;GAClC,SAAS,cAAc,WAAW;GAClC,UAAU,CAAC,gBAAgB,CAAC,MAAM,KAAK,KAAK;GAC5C,SAAS,cAAc,SAAS;EACjC,CAAA,CACiB;KACpB,qBAAC,iBAAD,EAAA,UAAA;EAAiB;EACJ;EACX,oBAAC,KAAD;GAAG,MAAK;GAAsB,QAAO;GAAS,KAAI;aAAsB;EAErE,CAAA;CACY,EAAA,CAAA,CACF,EAAA,CAAA;AAErB,CACF;AAEA,aAAa,cAAc;;;ACnF3B,gBAAuB,eACrB,QAC8C;CAC9C,MAAM,SAAS,OAAO,UAAU;CAChC,MAAM,UAAU,IAAI,YAAY,OAAO;CACvC,IAAI,SAAS;CAEb,IAAI;EACF,OAAO,MAAM;GACX,MAAM,EAAE,OAAO,SAAS,MAAM,OAAO,KAAK;GAC1C,IAAI,MAAM;GACV,UAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;GAEhD,IAAI;GACJ,QAAQ,QAAQ,OAAO,QAAQ,MAAM,OAAO,IAAI;IAC9C,MAAM,WAAW,OAAO,MAAM,GAAG,KAAK;IACtC,SAAS,OAAO,MAAM,QAAQ,CAAC;IAC/B,MAAM,OAAO,SAAS,QAAQ,aAAa,EAAE,EAAE,KAAK;IACpD,IAAI,CAAC,MAAM;IACX,IAAI,SAAS,UAAU;KACrB,MAAM,EAAE,MAAM,OAAO;KACrB;IACF;IACA,IAAI;KACF,MAAM,UAAU,KAAK,MAAM,IAAI;KAC/B,IAAI,OAAO,QAAQ,UAAU,UAC3B,MAAM;MAAE,MAAM;MAAS,OAAO,QAAQ;KAAM;KAE9C,IAAI,MAAM,QAAQ,QAAQ,OAAO,GAC/B,MAAM;MAAE,MAAM;MAAW,SAAS,QAAQ;KAAiB;KAE7D,IAAI,QAAQ,SAAS,OAAO,QAAQ,UAAU,UAC5C,MAAM;MAAE,MAAM;MAAS,OAAO,QAAQ;KAAe;IAEzD,SAAS,KAAK;KACZ,MAAM;MAAE,MAAM;MAAS,OAAO,gBAAiB,IAAc;KAAU;IACzE;GACF;EACF;CACF,UAAU;EACR,OAAO,YAAY;CACrB;AACF;;;AC/CA,eAAe,iBAAiB,KAAgC;CAC9D,IAAI,SAAS;CACb,IAAI;EACF,MAAM,OAAO,MAAM,IAAI,KAAK;EAC5B,IAAI;GACF,MAAM,OAAO,KAAK,MAAM,IAAI;GAC5B,SAAS,OAAO,KAAK,QAAQ,KAAK,OAAO,KAAK,WAAW,IAAI;EAC/D,QAAQ;GACN,SAAS;EACX;CACF,QAAQ,CAER;CAEA,IAAI,IAAI,WAAW,OAAO,aAAa,KAAK,MAAM,GAChD,OAAO;CAET,IAAI,IAAI,WAAW,OAAO,IAAI,WAAW,KACvC,OAAO;CAET,IAAI,IAAI,WAAW,KACjB,OAAO;CAET,OAAO,OAAO,KAAK,IAAI,yBAAyB,OAAO,KAAK,MAAM;AACpE;AAQA,IAAa,kBAAb,MAA6B;CAM3B,YAAY,MAA0B;EACpC,KAAK,WAAW,KAAK,WAAW,IAAI,QAAQ,QAAQ,EAAE;EACtD,KAAK,WAAW,KAAK;EACrB,KAAK,SAAS,KAAK;EACnB,KAAK,YAAY,KAAK;CACxB;CAEA,OAAO,gBAAgB,SAA+D;EACpF,MAAM,MAAM,GAAG,KAAK,QAAQ,2BAA2B,mBAAmB,KAAK,QAAQ,EAAE;EACzF,MAAM,MAAM,MAAM,KAAK,QAAQ,KAAK;GAAE;GAAS,QAAQ;EAAK,CAAC;EAC7D,IAAI,CAAC,IAAI,IAAI;GACX,MAAM;IAAE,MAAM;IAAS,OAAO,MAAM,iBAAiB,GAAG;GAAE;GAC1D;EACF;EACA,IAAI,CAAC,IAAI,MAAM;GACb,MAAM;IAAE,MAAM;IAAS,OAAO;GAAwB;GACtD;EACF;EACA,OAAO,eAAe,IAAI,IAAI;CAChC;CAEA,MAAM,qBAAgD;EACpD,MAAM,MAAM,GAAG,KAAK,QAAQ,2BAA2B,mBAAmB,KAAK,QAAQ,EAAE;EACzF,MAAM,MAAM,MAAM,MAAM,KAAK;GAC3B,QAAQ;GACR,SAAS,KAAK,QAAQ,kBAAkB;GACxC,MAAM,KAAK,UAAU,CAAC,CAAC;EACzB,CAAC;EACD,IAAI,CAAC,IAAI,IAAI,MAAM,IAAI,MAAM,uBAAuB,IAAI,QAAQ;EAEhE,OAAO,EAAE,OAAM,MADK,IAAI,KAAK,GACT,kBAAkB;CACxC;CAEA,OAAO,cACL,kBACA,SACA,SAC8C;EAE9C,MAAM,MAAM,GAAG,KAAK,QAAQ,2BAA2B,mBACrD,KAAK,QACP,EAAE,iBAAiB,mBAAmB,gBAAgB,EAAE;EACxD,MAAM,MAAM,MAAM,KAAK,QAAQ,KAAK;GAAE;GAAS,QAAQ;EAAK,CAAC;EAC7D,IAAI,CAAC,IAAI,IAAI;GACX,MAAM;IAAE,MAAM;IAAS,OAAO,MAAM,iBAAiB,GAAG;GAAE;GAC1D;EACF;EACA,IAAI,CAAC,IAAI,MAAM;GACb,MAAM;IAAE,MAAM;IAAS,OAAO;GAAwB;GACtD;EACF;EACA,OAAO,eAAe,IAAI,IAAI;CAChC;CAEA,QAAgB,KAAa,MAAkC;EAC7D,OAAO,MAAM,KAAK;GAChB,QAAQ;GACR,SAAS,KAAK,QAAQ,mBAAmB;GACzC,MAAM,KAAK,UAAU,IAAI;EAC3B,CAAC;CACH;CAEA,QAAgB,QAAwC;EACtD,MAAM,IAA4B;GAChC,gBAAgB;GAChB,QAAQ,GAAG,OAAO;GAClB,eAAe,UAAU,KAAK;EAChC;EACA,IAAI,KAAK,WAAW,EAAE,sBAAsB,KAAK;EACjD,OAAO;CACT;AACF;;;AC/GA,IAAM,wBAAwB;AAE9B,SAAS,sBAAsB,UAA0B;CACvD,IAAI;EACF,MAAM,MAAM,GAAG,wBAAwB;EACvC,MAAM,WAAW,OAAO,aAAa,QAAQ,GAAG;EAChD,IAAI,UAAU,OAAO;EACrB,MAAM,QAAQ,KAAK,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE;EAC/E,OAAO,aAAa,QAAQ,KAAK,KAAK;EACtC,OAAO;CACT,QAAQ;EACN,OAAO,KAAK,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE;CAC1E;AACF;AAEA,SAAS,eAAuB;CAC9B,OAAO,KAAK,KAAK,OAAO,EAAE,SAAS,EAAE,EAAE,MAAM,CAAC,IAAI,KAAK,IAAI,EAAE,SAAS,EAAE;AAC1E;AAoBA,SAAgB,eAAe,MAAqD;CAClF,MAAM,CAAC,UAAU,eAAe,SAA4B,CAAC,CAAC;CAC9D,MAAM,CAAC,aAAa,kBAAkB,SAAS,KAAK;CACpD,MAAM,kBAAkB,OAAsB,IAAI;CAClD,MAAM,WAAW,OAA+B,IAAI;CAEpD,MAAM,YAAY,cACV,KAAK,aAAa,sBAAsB,KAAK,QAAQ,GAC3D,CAAC,KAAK,UAAU,KAAK,SAAS,CAChC;CAEA,MAAM,SAAS,cAEX,IAAI,gBAAgB;EAClB,UAAU,KAAK;EACf,QAAQ,KAAK;EACb,SAAS,KAAK;EACd;CACF,CAAC,GACH;EAAC,KAAK;EAAU,KAAK;EAAQ,KAAK;EAAS;CAAS,CACtD;CAKA,MAAM,OAAO,KAAK,QAAQ;CAE1B,MAAM,gBAAgB,aAAa,IAAY,UAAoC;EACjF,aAAa,SAAS,KAAK,KAAK,MAAO,EAAE,OAAO,KAAK;GAAE,GAAG;GAAG,GAAG;EAAM,IAAI,CAAE,CAAC;CAC/E,GAAG,CAAC,CAAC;CAEL,MAAM,OAAO,YACX,OAAO,SAAiB;EACtB,MAAM,UAAU,KAAK,KAAK;EAC1B,IAAI,CAAC,WAAW,aAAa;EAE7B,MAAM,UAA2B;GAC/B,IAAI,aAAa;GACjB,MAAM;GACN,SAAS;GACT,QAAQ;GACR,WAAW,KAAK,IAAI;EACtB;EACA,MAAM,eAAgC;GACpC,IAAI,aAAa;GACjB,MAAM;GACN,SAAS;GACT,QAAQ;GACR,WAAW,KAAK,IAAI;EACtB;EACA,aAAa,SAAS;GAAC,GAAG;GAAM;GAAS;EAAY,CAAC;EACtD,eAAe,IAAI;EACnB,SAAS,UAAU,IAAI,gBAAgB;EAEvC,IAAI;GACF,IAAI,WAAW,gBAAgB;GAC/B,IAAI,SAAS,kBAAkB,CAAC,UAAU;IAExC,YAAW,MADW,OAAO,mBAAmB,GAC7B;IACnB,gBAAgB,UAAU;GAC5B;GAEA,MAAM,SACJ,SAAS,kBAAkB,WACvB,OAAO,cAAc,UAAU,OAAO,IACtC,OAAO,gBAAgB,OAAO;GAEpC,IAAI,cAAc;GAClB,WAAW,MAAM,SAAS,QAAQ;IAChC,IAAI,SAAS,SAAS,OAAO,SAAS;IACtC,QAAQ,MAAM,MAAd;KACE,KAAK;MACH,eAAe,MAAM;MACrB,cAAc,aAAa,IAAI,EAAE,SAAS,YAAY,CAAC;MACvD;KACF,KAAK;MACH,cAAc,aAAa,IAAI,EAAE,SAAS,MAAM,QAAQ,CAAC;MACzD;KACF,KAAK;MACH,cAAc,aAAa,IAAI,EAAE,OAAO,MAAM,MAAM,CAAC;MACrD;KACF,KAAK;MACH,cAAc,aAAa,IAAI;OAAE,QAAQ;OAAS,OAAO,MAAM;MAAM,CAAC;MACtE;KACF,KAAK,QACH;IACJ;GACF;GACA,cAAc,aAAa,IAAI,EAAE,QAAQ,WAAW,CAAC;EACvD,SAAS,KAAK;GACZ,MAAM,UAAU,eAAe,QAAQ,IAAI,UAAU;GACrD,cAAc,aAAa,IAAI;IAAE,QAAQ;IAAS,OAAO;GAAQ,CAAC;EACpE,UAAU;GACR,eAAe,KAAK;GACpB,SAAS,UAAU;EACrB;CACF,GACA;EAAC;EAAQ;EAAa;EAAM;CAAa,CAC3C;CAEA,MAAM,SAAS,kBAAkB;EAC/B,SAAS,SAAS,MAAM;EACxB,eAAe,KAAK;CACtB,GAAG,CAAC,CAAC;CAEL,MAAM,QAAQ,YAAY,YAAY;EACpC,OAAO;EACP,gBAAgB,UAAU;EAC1B,YAAY,CAAC,CAAC;CAChB,GAAG,CAAC,MAAM,CAAC;CAEX,sBAAsB,SAAS,SAAS,MAAM,GAAG,CAAC,CAAC;CAEnD,OAAO;EAAE;EAAU;EAAa;EAAM;EAAQ;CAAM;AACtD;;;AC9HA,IAAa,YAAY,MACtB,EACC,UACA,QACA,SACA,WACA,QAAQ,aACR,UACA,aACA,WACA,QACA,WACA,SACA,mBACI;CACJ,MAAM,EAAE,UAAU,aAAa,MAAM,QAAQ,UAAU,eAAe;EACpE;EACA;EACA;EACA;CACF,CAAC;CAED,MAAM,aAAa,aAChB,SAAiB;EAChB,KAAU,IAAI;CAChB,GACA,CAAC,IAAI,CACP;CAEA,MAAM,UAAgC,CACpC,GAAI,gBAAgB,CAAC,GACrB;EACE,MAAM;EACN,SAAS;EACT,eAAe,KAAK,MAAM;EAC1B,UAAU,SAAS,WAAW;CAChC,CACF;CACA,IAAI,WACF,QAAQ,KAAK;EAAE,MAAM;EAAa,SAAS;EAAS,SAAS;CAAQ,CAAC;CAGxE,OACE,qBAAC,aAAD;EACE,MAAA;EACA,SAAA;EACA,iBAAiB;EACjB,mBAAmB;EACnB,OAAO;EACP,MAAK;EACL,OAAO;GACL,OAAO,cAAc,YAAY;GACjC,QAAQ;GACR,SAAS;EACX;EACA,aAAa;EACJ;EACT,cAAc;GACZ,SAAS;GACT,SAAS;GACT,eAAe;GACf,UAAU;EACZ;YAnBF,CAqBE,oBAAC,aAAD;GACY;GACG;GACF;GACH;EACT,CAAA,GACD,oBAAC,cAAD;GACe;GACb,QAAQ;GACR,UAAU;GACG;GACL;EACT,CAAA,CACU;;AAEjB,CACF;AAEA,UAAU,cAAc;;;ACzGxB,IAAM,UAAU,SAAS;;;;AAOzB,IAAM,iBAAiB,OAAO,GAAG;;;;;;;;;;AAWjC,IAAM,cAAc,OAAO,GAAG;;;eAGf,QAAQ;;;;;;AAWvB,IAAa,iBAAiB,MAA4B,EAAE,aAAa,GAAG,iBAAiB;CAC3F,MAAM,CAAC,MAAM,WAAW,SAAS,CAAC,CAAC,WAAW;CAC9C,MAAM,SAAS,cAAc,UAAU,WAAW,MAAM,GAAG,CAAC,WAAW,MAAM,CAAC;CAE9E,OACE,qBAAC,gBAAD,EAAA,UAAA,CACG,QACC,oBAAC,aAAD,EAAA,UACE,oBAAC,WAAD;EAAW,GAAI;EAAY,WAAA;EAAU,eAAe,QAAQ,KAAK;CAAI,CAAA,EAC1D,CAAA,GAEf,oBAAC,cAAD;EACE,QAAA;EACA,SAAA;EACA,MAAK;EACL,MAAM,OAAO,cAAc;EACnB;EACR,eAAe,SAAS,MAAM,CAAC,CAAC;EAChC,SAAS,OAAO,eAAe;EAI/B,OAAO;GAAE,OAAO;GAAQ,QAAQ;GAAQ,SAAS;EAAE;CACpD,CAAA,CACa,EAAA,CAAA;AAEpB,CAAC;AAED,eAAe,cAAc;;;ACzD7B,IAAM,oBAAoB,OAAO,GAAG;;;;;;AA6BpC,IAAa,aAAa,MACvB,EACC,UACA,QACA,SACA,OAAO,UACP,WACA,OACA,UACA,aACA,aACA,OACA,SAAS,gBACT,mBACI;CACJ,MAAM,cAAc,iBAAiB,KAAK;CAC1C,MAAM,mBAAmB;EACvB;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;CACF;CAEA,OACE,oBAAC,kBAAD;EAAkB,OAAO;YACtB,SAAS,WACR,oBAAC,gBAAD;GAAgB,GAAI;GAA+B;EAAc,CAAA,IAEjE,oBAAC,mBAAD,EAAA,UACE,oBAAC,WAAD,EAAW,GAAI,iBAAmB,CAAA,EACjB,CAAA;CAEL,CAAA;AAEtB,CACF;AAEA,WAAW,cAAc"}
|