@sparkelf/dsh-task-bus 0.0.0-stage → 0.1.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 +32 -2
- package/cordis.patch.yml +3 -0
- package/lib/client.js +1869 -0
- package/lib/client.js.map +1 -0
- package/lib/index.d.ts +8 -0
- package/lib/index.js +343 -0
- package/package.json +86 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"client.js","names":["Tag","Button","useState","useMemo","Modal","IconCloseOutlineRegular","SegmentedTabs"],"sources":["../src/client/index.tsx"],"sourcesContent":["/**\n * The Task Center surface in the frame-wide overlay.\n *\n * The button is global because a task can arrive while the reader is in any\n * session. The panel only renders cards returned by the Host bridge; it never\n * talks to DataOps directly and never derives a second approval status.\n */\nimport { useEffect, useMemo, useState, type CSSProperties } from 'react'\nimport {\n Button,\n IconCloseOutlineRegular,\n Modal,\n SegmentedTabs,\n Tag,\n type SegmentedTab,\n type TagTone,\n} from '@deepseek-ai/dsh-client-ui-primitives'\nimport type { Context } from '@deepseek-ai/cordis'\nimport type {} from '@deepseek-ai/dsh-client-ui-layout/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-slots'\nimport type {} from '@deepseek-ai/dsh-client-ui-conversation/client'\nimport type {} from '@deepseek-ai/dsh-client-ui-sidebar/client'\n\nexport const inject = ['slots', 'locale', 'uiWorkspace', 'sessions', 'sidebarRight']\n\ninterface TaskItem {\n id: string\n sourceType: 'dataops_approval' | 'task_bus'\n title: string\n code?: string\n description?: string | null\n status: string\n statusLabel: string\n priority?: string | null\n tags?: string[]\n source: {\n type: string\n id?: string | null\n label?: string | null\n workflowCode?: string | null\n deepLink?: string | null\n }\n assignee?: { type: string; id: string } | null\n creator?: { type: string; id: string } | null\n actions?: string[]\n dueAt?: string | null\n updatedAt: string\n}\n\nconst ROUTE = '/dsh-task-bus/api'\n\nconst colors = {\n panel: 'var(--dsw-alias-bg-layer-1, #ffffff)',\n panelAlt: 'var(--dsw-alias-bg-layer-2, #f7f8fa)',\n text: 'var(--dsw-alias-label-primary, #111827)',\n muted: 'var(--dsw-alias-label-secondary, #6b7280)',\n line: 'var(--dsw-alias-border-l1, #e5e7eb)',\n accent: 'var(--dsw-alias-brand-primary, #2563eb)',\n accentSoft: 'var(--dsw-alias-brand-primary-alpha, #eff6ff)',\n warning: 'var(--dsw-alias-state-warning, #b45309)',\n success: 'var(--dsw-alias-state-success, #047857)',\n danger: 'var(--dsw-alias-state-danger, #b91c1c)',\n}\n\nconst TASK_BUS_MODAL_CLASS = 'task-bus-center-modal'\nconst TASK_BUS_HISTORY_CLASS = 'task-bus-history-modal'\nconst TASK_BUS_STYLE_ID = 'task-bus-center-modal-style'\nconst TASK_BUS_MODAL_CSS = `\n.${TASK_BUS_MODAL_CLASS} {\n width: min(960px, calc(100vw - 64px)) !important;\n height: min(760px, calc(100vh - 64px)) !important;\n gap: 0 !important;\n padding: 0 !important;\n}\n@media (max-width: 700px) {\n .${TASK_BUS_MODAL_CLASS} {\n width: calc(100vw - 24px) !important;\n height: calc(100vh - 24px) !important;\n }\n}\n.${TASK_BUS_HISTORY_CLASS} {\n width: min(960px, 92vw) !important;\n max-width: calc(100vw - 32px) !important;\n height: auto !important;\n max-height: min(780px, calc(100vh - 48px)) !important;\n}\n@media (max-width: 700px) {\n .${TASK_BUS_HISTORY_CLASS} {\n width: calc(100vw - 24px) !important;\n max-width: calc(100vw - 24px) !important;\n max-height: calc(100vh - 24px) !important;\n }\n}\n`\n\nconst HISTORY_NS = 'task-bus.history'\nconst HISTORY_COPY = {\n zh: {\n title: '消息记录',\n open: '消息记录',\n close: '关闭',\n searchPlaceholder: '搜索消息或发送人…',\n searchLabel: '搜索消息记录',\n clear: '清空搜索',\n initial: '输入关键词搜索消息或发送人',\n initialHint: '可按消息正文或发送人筛选,点结果跳到原消息。',\n loading: '正在加载消息记录…',\n error: '消息记录加载失败',\n empty: '暂无消息记录',\n noResults: '没有匹配的消息',\n jump: '跳到该消息',\n me: '我',\n assistant: '助手',\n dateLocale: 'zh-CN',\n },\n en: {\n title: 'Message history',\n open: 'Message history',\n close: 'Close',\n searchPlaceholder: 'Search messages or senders…',\n searchLabel: 'Search message history',\n clear: 'Clear search',\n initial: 'Search messages or senders to begin',\n initialHint: 'Filter by message text or sender, then click a result to jump to it.',\n loading: 'Loading message history…',\n error: 'Could not load message history',\n empty: 'No message history',\n noResults: 'No matching messages',\n jump: 'Jump to message',\n me: 'Me',\n assistant: 'Assistant',\n dateLocale: 'en-US',\n },\n} as const\n\ntype HistoryCopyKey = keyof typeof HISTORY_COPY.zh\ntype HistoryTranslate = (key: HistoryCopyKey) => string\n\nconst buttonStyle: CSSProperties = {\n border: '1px solid ' + colors.line,\n background: colors.panel,\n color: colors.text,\n borderRadius: 8,\n padding: '7px 11px',\n fontSize: 12,\n cursor: 'pointer',\n}\n\nconst statusTagTone = (status: string): TagTone => {\n if (status === 'completed') return 'success'\n if (status === 'cancelled') return 'neutral'\n if (status === 'blocked') return 'danger'\n if (status === 'pending_confirm' || status === 'returned' || status === 'pending_accept') return 'warning'\n return 'info'\n}\n\nconst relativeTime = (value: string) => {\n const time = Date.parse(value)\n if (!Number.isFinite(time)) return ''\n const minutes = Math.max(0, Math.round((Date.now() - time) / 60_000))\n if (minutes < 1) return '刚刚'\n if (minutes < 60) return String(minutes) + ' 分钟前'\n const hours = Math.round(minutes / 60)\n if (hours < 24) return String(hours) + ' 小时前'\n return String(Math.round(hours / 24)) + ' 天前'\n}\n\nconst fetchJson = async <T,>(path: string): Promise<T> => {\n const response = await fetch(ROUTE + path, { headers: { accept: 'application/json' } })\n const payload = await response.json() as unknown\n if (!response.ok) {\n const message = payload !== null && typeof payload === 'object' && 'error' in payload\n ? String((payload as { error: unknown }).error)\n : '任务中心请求失败(' + response.status + ')'\n throw new Error(message)\n }\n return payload as T\n}\n\n\nconst approvalHref = (raw: string | null): string | null => {\n if (raw === null) return null\n try {\n const target = new URL(raw)\n const referrer = document.referrer ? new URL(document.referrer) : null\n if (referrer !== null && referrer.origin !== target.origin) {\n return referrer.origin + target.pathname + target.search + target.hash\n }\n return target.href\n } catch {\n return raw\n }\n}\n\nconst TaskCard = ({\n task,\n busy,\n aiBusy,\n onTransition,\n onAiAccept,\n onOpen,\n}: {\n task: TaskItem\n busy: boolean\n aiBusy: boolean\n onTransition: (task: TaskItem, action: string) => void\n onAiAccept: (task: TaskItem) => void\n onOpen: (task: TaskItem) => void\n}) => {\n const isApproval = task.sourceType === 'dataops_approval'\n const deepLink = approvalHref(task.source.deepLink ?? null)\n return (\n <article\n style={{\n border: '1px solid ' + colors.line,\n borderRadius: 10,\n background: colors.panel,\n padding: '12px 13px',\n display: 'grid',\n gap: 8,\n }}\n >\n <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 7, flexWrap: 'wrap', marginBottom: 4 }}>\n <Tag tone={isApproval ? 'info' : 'neutral'}>\n {isApproval ? (task.source.label ?? 'DataOps 审批') : '任务总线'}\n </Tag>\n {task.tags?.map((tag) => (\n <Tag key={tag} tone=\"quiet\">{tag}</Tag>\n ))}\n </div>\n <div style={{ fontSize: 13, fontWeight: 650, color: colors.text, lineHeight: 1.45 }}>\n {task.title}\n </div>\n {task.description ? (\n <div style={{ fontSize: 11, color: colors.muted, marginTop: 4, lineHeight: 1.5 }}>\n {task.description}\n </div>\n ) : null}\n </div>\n <span style={{ flex: 'none' }}><Tag tone={statusTagTone(task.status)}>{task.statusLabel}</Tag></span>\n </div>\n <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap', fontSize: 10, color: colors.muted }}>\n <span>{isApproval ? '审批任务' : '任务'}</span>\n {task.assignee?.id ? <span>受理人:{task.assignee.id}</span> : null}\n <span style={{ marginLeft: 'auto' }}>{relativeTime(task.updatedAt)}</span>\n </div>\n <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap' }}>\n {isApproval && deepLink ? (\n <Button size=\"sm\" variant=\"primary\" onClick={() => window.open(deepLink, '_blank', 'noopener,noreferrer')}>\n 办理\n </Button>\n ) : null}\n {task.status !== 'completed' && task.status !== 'cancelled' ? (\n <Button size=\"sm\" variant=\"outline\" disabled={aiBusy || busy} onClick={() => onAiAccept(task)}>\n {aiBusy ? '受理中…' : 'AI受理'}\n </Button>\n ) : null}\n <Button size=\"sm\" variant=\"ghost\" onClick={() => onOpen(task)}>详情</Button>\n {!isApproval && task.status === 'pending_accept' ? (\n <Button size=\"sm\" variant=\"primary\" disabled={busy} onClick={() => onTransition(task, 'accept')}>受理</Button>\n ) : null}\n {!isApproval && task.status === 'in_progress' ? (\n <Button size=\"sm\" variant=\"outline\" disabled={busy} onClick={() => onTransition(task, 'submit')}>提交确认</Button>\n ) : null}\n {!isApproval && task.status === 'pending_confirm' ? (\n <Button size=\"sm\" variant=\"primary\" disabled={busy} onClick={() => onTransition(task, 'complete')}>完成</Button>\n ) : null}\n {!isApproval && task.status !== 'completed' && task.status !== 'cancelled' ? (\n <Button size=\"sm\" variant=\"ghost\" disabled={busy} onClick={() => onTransition(task, 'cancel')}>取消</Button>\n ) : null}\n </div>\n </article>\n )\n}\n\n\ninterface TaskAttachmentRecord {\n id: string\n originalName: string\n contentType: string\n sizeBytes: number\n kind: string\n}\n\ninterface FormFieldData {\n key: string\n label: string\n value: unknown\n type?: 'multiline' | 'resources'\n span?: 1 | 2\n}\n\ninterface FormSectionData {\n title: string\n fields: FormFieldData[]\n}\n\ninterface TaskDetailPayload {\n id?: number | string\n payload?: {\n form?: Record<string, unknown>\n selectedItems?: Array<Record<string, unknown>>\n resourceAccessRequest?: {\n form?: Record<string, unknown>\n selectedItems?: Array<Record<string, unknown>>\n }\n }\n form?: { sections?: FormSectionData[] }\n attachments?: TaskAttachmentRecord[]\n}\n\nconst FORM_LABELS: Record<string, string> = {\n applicationMode: '申请方式',\n requestKind: '申请类型',\n requestedPermissions: '申请权限',\n requesterDepartment: '申请人部门',\n requesterPhone: '申请人电话',\n validFrom: '授权开始时间',\n expiresAt: '授权结束时间',\n selectionSummary: '资源摘要',\n code: '应用编码',\n name: '应用名称',\n reason: '申请理由',\n description: '应用说明',\n departmentName: '归属部门',\n departmentCode: '归属部门编码',\n ownerName: '归属负责人',\n ownerPhone: '归属负责人电话',\n constructionUnit: '建设单位',\n constructionOwner: '建设单位负责人',\n constructionOwnerPhone: '建设单位负责人电话',\n operationsUnit: '运维单位',\n operationsOwner: '运维单位负责人',\n operationsOwnerPhone: '运维单位负责人电话',\n requestTime: '申请时间',\n resourceName: '资源名称',\n resourceType: '资源类型',\n accessType: '访问方式',\n accessScope: '访问范围',\n applicationName: '所属应用',\n applicationCode: '应用编码',\n environment: '环境',\n accountCount: '账号数量',\n}\n\nconst SKIP_FORM_KEYS = new Set([\n 'ownerUserId',\n 'requesterUserId',\n 'requesterDept',\n 'existingApplicationId',\n 'applicationId',\n 'taskType',\n])\n\nconst formatBytes = (bytes: number) => {\n if (!Number.isFinite(bytes) || bytes < 0) return ''\n if (bytes < 1024) return bytes + ' B'\n if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB'\n return (bytes / (1024 * 1024)).toFixed(1) + ' MB'\n}\n\nconst humanizeField = (key: string) => key\n .replace(/([a-z0-9])([A-Z])/g, '$1 $2')\n .replace(/[_-]+/g, ' ')\n .trim()\n\nconst formatFormValue = (key: string, value: unknown): string => {\n if (key === 'applicationMode') {\n if (value === 'EXISTING') return '使用已有应用'\n if (value === 'NEW') return '新建应用'\n }\n if (typeof value === 'boolean') return value ? '是' : '否'\n if (Array.isArray(value)) return value.map((item) => String(item)).join('、')\n if (value !== null && typeof value === 'object') {\n const record = value as Record<string, unknown>\n if (typeof record.name === 'string') return record.name\n return JSON.stringify(value)\n }\n return String(value)\n}\n\nconst toFormField = (key: string, value: unknown): FormFieldData => ({\n key,\n label: FORM_LABELS[key] ?? humanizeField(key),\n value,\n ...(key === 'reason' || key === 'description' || key === 'accessScope'\n ? { type: 'multiline' as const, span: 2 as const }\n : {}),\n})\n\nconst formFields = (form: Record<string, unknown> | undefined): FormFieldData[] => (\n form === undefined\n ? []\n : Object.entries(form)\n .filter(([key, value]) => !SKIP_FORM_KEYS.has(key) && value !== null && value !== undefined && value !== '')\n .map(([key, value]) => toFormField(key, value))\n)\n\nconst approvalFormSections = (detail: TaskDetailPayload | null): FormSectionData[] => {\n if (detail === null) return []\n const sections: FormSectionData[] = []\n const applicationFields = formFields(detail.payload?.form)\n if (applicationFields.length > 0) sections.push({ title: '申请信息', fields: applicationFields })\n const resourceFields = formFields(detail.payload?.resourceAccessRequest?.form)\n if (resourceFields.length > 0) sections.push({ title: '资源申请', fields: resourceFields })\n const resources = [\n ...(detail.payload?.selectedItems ?? []),\n ...(detail.payload?.resourceAccessRequest?.selectedItems ?? []),\n ]\n if (resources.length > 0) {\n sections.push({\n title: '申请资源',\n fields: [{ key: 'resources', label: '资源', value: resources, type: 'resources', span: 2 }],\n })\n }\n return sections\n}\n\nconst ResourceValue = ({ items }: { items: Array<Record<string, unknown>> }) => (\n <div style={{ display: 'grid', gap: 5 }}>\n {items.map((item, index) => {\n const label = typeof item.title === 'string' ? item.title : typeof item.name === 'string' ? item.name : `资源 ${index + 1}`\n const detail = [item.scope, item.urn, item.id].filter((value) => typeof value === 'string' || typeof value === 'number').join(' · ')\n return (\n <div key={String(item.urn ?? item.id ?? index)} style={{ display: 'flex', alignItems: 'baseline', gap: 8, minWidth: 0 }}>\n <span style={{ fontSize: 12, color: colors.text }}>{label}</span>\n {detail ? <span style={{ fontSize: 10, color: colors.muted, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{detail}</span> : null}\n </div>\n )\n })}\n </div>\n)\n\nconst FormDescriptions = ({ sections }: { sections: FormSectionData[] }) => {\n if (sections.length === 0) return null\n return (\n <div style={{ display: 'grid', gap: 10 }}>\n {sections.map((section, sectionIndex) => (\n <section key={section.title + sectionIndex} style={{ border: '1px solid ' + colors.line, borderRadius: 10, background: colors.panel, padding: 14 }}>\n <div style={{ fontSize: 12, fontWeight: 650, color: colors.text, marginBottom: 9 }}>{section.title}</div>\n <dl style={{ margin: 0, display: 'grid', gridTemplateColumns: 'repeat(2, minmax(0, 1fr))', gap: '9px 24px' }}>\n {section.fields.map((field) => (\n <div\n key={field.key}\n style={{\n display: 'grid',\n gridTemplateColumns: '94px minmax(0, 1fr)',\n alignItems: 'start',\n gap: 8,\n minWidth: 0,\n gridColumn: field.span === 2 ? '1 / -1' : undefined,\n }}\n >\n <dt style={{ fontSize: 10, lineHeight: 1.6, color: colors.muted }}>{field.label}</dt>\n <dd style={{ margin: 0, fontSize: 12, lineHeight: 1.6, color: colors.text, whiteSpace: 'pre-wrap', overflowWrap: 'anywhere' }}>\n {field.type === 'resources' && Array.isArray(field.value)\n ? <ResourceValue items={field.value as Array<Record<string, unknown>>} />\n : field.value === null || field.value === undefined || field.value === ''\n ? '—'\n : formatFormValue(field.key, field.value)}\n </dd>\n </div>\n ))}\n </dl>\n </section>\n ))}\n </div>\n )\n}\n\nconst TaskDetail = ({ task, onBack }: { task: TaskItem; onBack: () => void }) => {\n const isApproval = task.sourceType === 'dataops_approval'\n const [detail, setDetail] = useState<TaskDetailPayload | null>(null)\n const [detailLoading, setDetailLoading] = useState(true)\n const [detailError, setDetailError] = useState<string | null>(null)\n\n useEffect(() => {\n let alive = true\n setDetailLoading(true)\n setDetailError(null)\n void fetchJson<{ detail?: TaskDetailPayload }>('/items/' + encodeURIComponent(task.id))\n .then((payload) => { if (alive) setDetail(payload.detail ?? null) })\n .catch((cause) => { if (alive) setDetailError(cause instanceof Error ? cause.message : String(cause)) })\n .finally(() => { if (alive) setDetailLoading(false) })\n return () => { alive = false }\n }, [task.id])\n\n const sections = isApproval ? approvalFormSections(detail) : detail?.form?.sections ?? []\n const attachments = detail?.attachments ?? []\n const approvalTaskId = isApproval && typeof detail?.id === 'number' ? detail.id : null\n const attachmentHref = (attachment: TaskAttachmentRecord) => approvalTaskId === null\n ? null\n : ROUTE + '/attachment?taskId=' + encodeURIComponent(String(approvalTaskId))\n + '&attachmentId=' + encodeURIComponent(attachment.id)\n\n return (\n <div style={{ display: 'flex', flexDirection: 'column', gap: 10, flex: 1, minHeight: '100%' }}>\n <div style={{ display: 'flex', alignItems: 'flex-start', gap: 10 }}>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 14, fontWeight: 700, color: colors.text, lineHeight: 1.45 }}>{task.title}</div>\n <div style={{ marginTop: 3, fontSize: 10, color: colors.muted }}>\n {task.source.label ?? (isApproval ? 'DataOps 审批' : '任务总线')} · {relativeTime(task.updatedAt)}\n </div>\n </div>\n <Tag tone={statusTagTone(task.status)}>{task.statusLabel}</Tag>\n </div>\n\n {detailLoading ? <div style={{ padding: 18, textAlign: 'center', color: colors.muted, fontSize: 12 }}>正在加载表单…</div> : null}\n {detailError ? <div style={{ padding: '8px 10px', borderRadius: 8, background: '#fef2f2', color: colors.danger, fontSize: 11 }}>{detailError}</div> : null}\n {!detailLoading && detailError === null && sections.length > 0 ? <FormDescriptions sections={sections} /> : null}\n {!detailLoading && detailError === null && sections.length === 0 && !isApproval && task.description ? (\n <div style={{ border: '1px solid ' + colors.line, borderRadius: 10, background: colors.panel, padding: 14, fontSize: 12, lineHeight: 1.6, color: colors.text, whiteSpace: 'pre-wrap' }}>{task.description}</div>\n ) : null}\n {!detailLoading && detailError === null && sections.length === 0 && (isApproval || !task.description) ? (\n <div style={{ padding: 18, textAlign: 'center', color: colors.muted, fontSize: 12 }}>暂无表单信息</div>\n ) : null}\n\n {isApproval || attachments.length > 0 ? (\n <section style={{ border: '1px solid ' + colors.line, borderRadius: 10, background: colors.panel, padding: 14 }}>\n <div style={{ fontSize: 12, fontWeight: 650, color: colors.text, marginBottom: 8 }}>附件</div>\n {attachments.length === 0 ? <div style={{ fontSize: 11, color: colors.muted }}>暂无附件</div> : (\n <div style={{ display: 'grid', gap: 6 }}>\n {attachments.map((attachment) => {\n const href = attachmentHref(attachment)\n return (\n <div key={attachment.id} style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0 }}>\n <span style={{ flex: 1, minWidth: 0, fontSize: 11, color: colors.text, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{attachment.originalName}</span>\n <span style={{ flex: 'none', fontSize: 10, color: colors.muted }}>{formatBytes(attachment.sizeBytes)}</span>\n {href !== null ? <Button size=\"sm\" variant=\"ghost\" onClick={() => window.open(href, '_blank', 'noopener,noreferrer')}>下载</Button> : null}\n </div>\n )\n })}\n </div>\n )}\n </section>\n ) : null}\n\n <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, flexWrap: 'wrap', marginTop: 'auto', paddingTop: 2 }}>\n <Button size=\"sm\" variant=\"ghost\" onClick={onBack}>← 返回列表</Button>\n {isApproval && task.source.deepLink ? (\n <Button size=\"sm\" variant=\"outline\" onClick={() => window.open(approvalHref(task.source.deepLink ?? null) ?? '#', '_blank', 'noopener,noreferrer')}>\n 打开 DataOps 审批详情\n </Button>\n ) : null}\n </div>\n </div>\n )\n}\n\nconst CONNECTOR_STORAGE_KEY = 'dsh.task-bus.dataops-connector.enabled'\n\nconst DataOpsConnectorCard = ({\n approvalCount,\n connected,\n enabled,\n onEnable,\n}: {\n approvalCount: number\n connected: boolean\n enabled: boolean\n onEnable: () => void\n}) => {\n const [detailOpen, setDetailOpen] = useState(false)\n const status = !enabled ? '未启用' : connected ? '已连接' : '连接异常'\n const tone: TagTone = !enabled ? 'neutral' : connected ? 'success' : 'danger'\n return (\n <article style={{ width: '100%', minWidth: 0, border: '1px solid ' + colors.line, borderRadius: 12, background: colors.panelAlt, padding: 14, display: 'flex', flexDirection: 'column', gap: 10 }}>\n <div style={{ display: 'flex', alignItems: 'center', gap: 9 }}>\n <span aria-hidden=\"true\" style={{ width: 32, height: 32, flex: 'none', borderRadius: 9, background: '#2563eb', color: '#fff', display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M10 13a5 5 0 0 0 7.54.54l2-2a5 5 0 0 0-7.07-7.07l-1.15 1.15\" />\n <path d=\"M14 11a5 5 0 0 0-7.54-.54l-2 2a5 5 0 0 0 7.07 7.07l1.15-1.15\" />\n </svg>\n </span>\n <div style={{ flex: 1, minWidth: 0 }}>\n <div style={{ fontSize: 13, fontWeight: 700, color: colors.text }}>万相 AI 中台</div>\n <div style={{ marginTop: 2, fontSize: 10, color: colors.muted }}>DataOps 连接器</div>\n </div>\n <Tag tone={tone}>{status}</Tag>\n </div>\n <div style={{ fontSize: 11, lineHeight: 1.55, color: colors.muted }}>\n 接收万相 AI 中台的审批任务\n </div>\n {detailOpen ? (\n <div style={{ borderTop: '1px solid ' + colors.line, paddingTop: 8, display: 'grid', gap: 6 }}>\n <div style={{ display: 'grid', gridTemplateColumns: '68px minmax(0,1fr)', gap: 8, fontSize: 10 }}>\n <span style={{ color: colors.muted }}>协议</span>\n <span style={{ color: colors.text }}>DataOps Task Center API</span>\n </div>\n <div style={{ display: 'grid', gridTemplateColumns: '68px minmax(0,1fr)', gap: 8, fontSize: 10 }}>\n <span style={{ color: colors.muted }}>审批任务</span>\n <span style={{ color: colors.text }}>{approvalCount}</span>\n </div>\n <div style={{ display: 'grid', gridTemplateColumns: '68px minmax(0,1fr)', gap: 8, fontSize: 10 }}>\n <span style={{ color: colors.muted }}>能力</span>\n <span style={{ color: colors.text }}>审批 / 通用任务 / AI 受理</span>\n </div>\n </div>\n ) : null}\n <div style={{ display: 'flex', gap: 6, marginTop: 'auto' }}>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setDetailOpen((value) => !value)}>{detailOpen ? '收起' : '详情'}</Button>\n <Button size=\"sm\" variant={enabled ? 'outline' : 'primary'} disabled={enabled} onClick={onEnable}>{enabled ? '已启用' : '启用'}</Button>\n </div>\n </article>\n )\n}\n\ntype TaskTab = 'all' | 'mine' | 'personal' | 'group' | 'connectors'\n\nconst TaskCenterButton = ({ openSession }: { openSession: (sessionId: string) => void }) => {\n const [open, setOpen] = useState(false)\n const [tasks, setTasks] = useState<TaskItem[]>([])\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState<string | null>(null)\n const [tab, setTab] = useState<TaskTab>('all')\n const [busyId, setBusyId] = useState<string | null>(null)\n const [aiBusyId, setAiBusyId] = useState<string | null>(null)\n const [creating, setCreating] = useState(false)\n const [draftTitle, setDraftTitle] = useState('')\n const [draftSource, setDraftSource] = useState('manual')\n const [createBusy, setCreateBusy] = useState(false)\n const [selected, setSelected] = useState<TaskItem | null>(null)\n const [anchor, setAnchor] = useState<{ left: number; top: number } | null>(null)\n const [connectorEnabled, setConnectorEnabled] = useState(() => {\n try { return window.localStorage.getItem(CONNECTOR_STORAGE_KEY) === 'true' } catch { return false }\n })\n const [pendingCount, setPendingCount] = useState(0)\n\n useEffect(() => {\n if (document.getElementById(TASK_BUS_STYLE_ID) !== null) return\n const style = document.createElement('style')\n style.id = TASK_BUS_STYLE_ID\n style.textContent = TASK_BUS_MODAL_CSS\n document.head.append(style)\n return () => { style.remove() }\n }, [])\n\n useEffect(() => {\n const update = () => {\n const collapsed = document.querySelector('[data-sidebar-collapsed=\"true\"]') !== null\n if (collapsed) {\n setAnchor((current) => (current === null ? current : null))\n return\n }\n const buttons = Array.from(document.querySelectorAll('button'))\n const toggle = buttons.find((button) => /收起侧边栏|Collapse sidebar/i.test((button.getAttribute('aria-label') ?? '').trim()))\n if (!toggle) {\n setAnchor((current) => (current === null ? current : null))\n return\n }\n const label = (toggle.getAttribute('aria-label') ?? '').trim()\n if (/展开侧边栏|Expand sidebar/i.test(label)) {\n setAnchor((current) => (current === null ? current : null))\n return\n }\n const rect = toggle.getBoundingClientRect()\n if (rect.width === 0 || rect.height === 0) return\n const next = { left: Math.max(8, rect.left - 36), top: rect.top + (rect.height - 28) / 2 }\n setAnchor((current) => (\n current !== null && Math.abs(current.left - next.left) < 0.5 && Math.abs(current.top - next.top) < 0.5\n ? current\n : next\n ))\n }\n update()\n const timer = window.setInterval(update, 400)\n window.addEventListener('resize', update)\n return () => {\n window.clearInterval(timer)\n window.removeEventListener('resize', update)\n }\n }, [])\n\n const load = async (silent = false) => {\n if (!connectorEnabled) {\n setTasks([])\n setLoading(false)\n setError(null)\n return\n }\n if (!silent) setLoading(true)\n setError(null)\n try {\n const overview = await fetchJson<{ items: TaskItem[] }>('/overview')\n setTasks([...overview.items].sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)))\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n if (!silent) setLoading(false)\n }\n }\n\n const enableConnector = () => {\n setConnectorEnabled(true)\n try { window.localStorage.setItem(CONNECTOR_STORAGE_KEY, 'true') } catch { /* local preference only */ }\n }\n\n useEffect(() => {\n if (!open) return\n if (!connectorEnabled) {\n setTab('connectors')\n setTasks([])\n setError(null)\n setLoading(false)\n return\n }\n void load()\n const timer = window.setInterval(() => { void load(true) }, 60_000)\n return () => { window.clearInterval(timer) }\n }, [open, connectorEnabled])\n\n useEffect(() => {\n if (!connectorEnabled) {\n setPendingCount(0)\n return\n }\n let alive = true\n const refresh = async () => {\n try {\n const result = await fetchJson<{ count?: number }>('/count')\n if (alive) setPendingCount(Math.max(0, Math.trunc(Number(result.count) || 0)))\n } catch {\n // The panel's overview error is the visible connection state; the badge keeps its last value.\n }\n }\n void refresh()\n const timer = window.setInterval(() => { void refresh() }, 300_000)\n return () => { alive = false; window.clearInterval(timer) }\n }, [connectorEnabled])\n\n\n const filtered = useMemo(() => tasks.filter((task) => {\n if (tab === 'connectors') return task.sourceType === 'dataops_approval'\n if (tab === 'group') return task.sourceType === 'task_bus' && task.source.type.includes('group')\n if (tab === 'mine') return task.status !== 'completed' && task.status !== 'cancelled'\n if (tab === 'personal') return task.sourceType === 'task_bus' && !task.source.type.includes('group')\n return true\n }), [tasks, tab])\n\n const actionable = tasks.filter((task) => task.status !== 'completed' && task.status !== 'cancelled').length\n const tabItems: [SegmentedTab<TaskTab>, ...SegmentedTab<TaskTab>[]] = [\n { value: 'all', id: 'task-bus-tab-all', panelId: 'task-bus-panel-all', label: <span>全部 <span style={{ opacity: 0.55 }}>{tasks.length}</span></span> },\n { value: 'mine', id: 'task-bus-tab-mine', panelId: 'task-bus-panel-mine', label: <span>待我处理 <span style={{ opacity: 0.55 }}>{actionable}</span></span> },\n { value: 'personal', id: 'task-bus-tab-personal', panelId: 'task-bus-panel-personal', label: <span>个人 <span style={{ opacity: 0.55 }}>{tasks.filter((task) => task.sourceType === 'task_bus' && !task.source.type.includes('group')).length}</span></span> },\n { value: 'group', id: 'task-bus-tab-group', panelId: 'task-bus-panel-group', label: <span>群聊 <span style={{ opacity: 0.55 }}>{tasks.filter((task) => task.sourceType === 'task_bus' && task.source.type.includes('group')).length}</span></span> },\n { value: 'connectors', id: 'task-bus-tab-connectors', panelId: 'task-bus-panel-connectors', label: <span>连接器 <span style={{ opacity: 0.55 }}>1</span></span> },\n ]\n\n const transition = async (task: TaskItem, action: string) => {\n setBusyId(task.id)\n setError(null)\n try {\n await fetch(ROUTE + '/generic/' + encodeURIComponent(task.id.slice(5)) + '/transition', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ action }),\n }).then(async (response) => {\n if (!response.ok) {\n const payload = await response.json().catch(() => null) as { error?: string } | null\n throw new Error(payload?.error ?? '任务操作失败')\n }\n })\n await load()\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n setBusyId(null)\n }\n }\n\n const createTask = async () => {\n const title = draftTitle.trim()\n if (!title) return\n setCreateBusy(true)\n setError(null)\n try {\n const response = await fetch(ROUTE + '/generic', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ title, sourceType: draftSource }),\n })\n const payload = await response.json() as { error?: string }\n if (!response.ok) throw new Error(payload.error ?? '创建任务失败')\n setDraftTitle('')\n setCreating(false)\n await load()\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n setCreateBusy(false)\n }\n }\n\n const aiAccept = async (task: TaskItem) => {\n setAiBusyId(task.id)\n setError(null)\n try {\n const response = await fetch(ROUTE + '/ai-accept', {\n method: 'POST',\n headers: { 'content-type': 'application/json' },\n body: JSON.stringify({ id: task.id }),\n })\n const payload = await response.json() as { sessionId?: string; error?: string }\n if (!response.ok || typeof payload.sessionId !== 'string') {\n throw new Error(payload.error ?? 'AI 受理会话创建失败')\n }\n openSession(payload.sessionId)\n setOpen(false)\n } catch (cause) {\n setError(cause instanceof Error ? cause.message : String(cause))\n } finally {\n setAiBusyId(null)\n }\n }\n\n return (\n <>\n {anchor !== null ? (\n <button\n type=\"button\"\n aria-label=\"任务中心\"\n title=\"任务中心\"\n onClick={() => setOpen((value) => !value)}\n style={{\n position: 'fixed',\n left: anchor.left,\n top: anchor.top,\n zIndex: 45,\n width: 28,\n height: 28,\n padding: 0,\n border: 'none',\n borderRadius: 8,\n background: 'transparent',\n color: actionable > 0 ? colors.accent : colors.muted,\n cursor: 'pointer',\n pointerEvents: 'auto',\n display: 'inline-flex',\n alignItems: 'center',\n justifyContent: 'center',\n }}\n >\n <svg width=\"17\" height=\"17\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"1.8\" strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n <path d=\"M18 8a6 6 0 0 0-12 0c0 7-3 7-3 9h18c0-2-3-2-3-9\" />\n <path d=\"M13.7 21a2 2 0 0 1-3.4 0\" />\n </svg>\n {pendingCount > 0 ? (\n <span style={{ position: 'absolute', bottom: -2, right: -2, minWidth: 14, height: 14, padding: '0 3px', boxSizing: 'border-box', borderRadius: 999, background: '#2563eb', color: '#fff', fontSize: 9, lineHeight: '14px', textAlign: 'center' }}>\n {pendingCount > 99 ? '99+' : pendingCount}\n </span>\n ) : null}\n </button>\n ) : null}\n\n <Modal open={open} onClose={() => setOpen(false)} title=\"任务中心\" headless className={TASK_BUS_MODAL_CLASS}>\n <div style={{ display: 'flex', flexDirection: 'column', width: '100%', height: '100%', minHeight: 0 }}>\n <header style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '22px 24px 12px' }}>\n <div style={{ flex: 1, minWidth: 0 }}>\n <h2 style={{ margin: 0, fontSize: 16, lineHeight: '24px', fontWeight: 500, color: colors.text }}>任务中心</h2>\n </div>\n <Button size=\"sm\" variant=\"outline\" disabled={!connectorEnabled} onClick={() => setCreating((value) => !value)}>新建</Button>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => void load()}>刷新</Button>\n <Button size=\"sm\" variant=\"ghost\" aria-label=\"关闭\" title=\"关闭\" onClick={() => setOpen(false)} icon={<IconCloseOutlineRegular size={14} />} style={{ width: 28, padding: 0 }} />\n </header>\n <div style={{ padding: '0 24px 14px', borderBottom: '1px solid ' + colors.line }}>\n <SegmentedTabs items={tabItems} value={tab} onChange={setTab} label=\"任务中心分类\" />\n </div>\n {creating ? (\n <div style={{ margin: '0 24px 12px', padding: 12, border: '1px solid ' + colors.line, borderRadius: 9, display: 'grid', gap: 7 }}>\n <input\n value={draftTitle}\n onChange={(event) => setDraftTitle(event.target.value)}\n placeholder=\"任务标题,例如:整理演示材料\"\n autoFocus\n style={{ ...buttonStyle, cursor: 'text', padding: '7px 9px' }}\n />\n <div style={{ display: 'flex', gap: 6 }}>\n <select value={draftSource} onChange={(event) => setDraftSource(event.target.value)} style={{ ...buttonStyle, padding: '6px 8px' }}>\n <option value=\"manual\">手工任务</option>\n <option value=\"session\">会话任务</option>\n <option value=\"group\">群聊任务</option>\n </select>\n <Button size=\"sm\" variant=\"primary\" disabled={createBusy || draftTitle.trim() === ''} onClick={() => void createTask()}>\n {createBusy ? '创建中…' : '创建'}\n </Button>\n <Button size=\"sm\" variant=\"ghost\" onClick={() => setCreating(false)}>取消</Button>\n </div>\n </div>\n ) : null}\n {error ? <div style={{ padding: '8px 10px', borderRadius: 8, background: '#fef2f2', color: colors.danger, fontSize: 11, margin: '0 24px 12px' }}>{error}</div> : null}\n <div id={'task-bus-panel-' + tab} role=\"tabpanel\" aria-labelledby={'task-bus-tab-' + tab} style={{ padding: '16px 24px 24px', display: 'flex', flexDirection: 'column', gap: 10, overflowY: 'auto', flex: 1 }}>\n {selected !== null ? (\n <TaskDetail task={selected} onBack={() => setSelected(null)} />\n ) : (\n <>\n {tab === 'connectors' ? <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 320px))', gap: 10 }}><DataOpsConnectorCard approvalCount={tasks.filter((task) => task.sourceType === 'dataops_approval').length} connected={error === null} enabled={connectorEnabled} onEnable={enableConnector} /></div> : null}\n {loading ? <div style={{ padding: 18, textAlign: 'center', color: colors.muted, fontSize: 12 }}>正在加载…</div> : null}\n {!loading && filtered.length === 0 ? <div style={{ padding: 18, textAlign: 'center', color: colors.muted, fontSize: 12 }}>{tab === 'connectors' ? (connectorEnabled ? '暂无来自该连接器的任务' : '启用连接器后在此显示任务') : '暂无任务'}</div> : null}\n {!loading && tab === 'connectors' && filtered.length > 0 ? <div style={{ fontSize: 10, color: colors.muted }}>来自该连接器的任务</div> : null}\n {!loading && filtered.map((task) => (\n <TaskCard key={task.id} task={task} busy={busyId === task.id} aiBusy={aiBusyId === task.id} onTransition={(item, action) => void transition(item, action)} onAiAccept={(item) => void aiAccept(item)} onOpen={setSelected} />\n ))}\n </>\n )}\n </div>\n </div>\n </Modal>\n </>\n )\n}\n\n\n\ninterface HistoryEntry {\n key: string\n role: 'user' | 'assistant'\n author: string\n time: number | undefined\n text: string\n jumpId?: string\n}\n\nconst HISTORY_CONTENT_TYPES = new Set(['user/message', 'assistant/message'])\n\nconst historyText = (content: unknown): string => {\n if (!Array.isArray(content)) return ''\n return content.map((block) => {\n if (block === null || typeof block !== 'object') return ''\n const record = block as Record<string, unknown>\n return record.type === 'text' && typeof record.text === 'string' ? record.text : ''\n }).filter(Boolean).join('\\n').trim()\n}\n\nconst historyRole = (event: Record<string, unknown>): 'user' | 'assistant' => {\n const data = event.data as Record<string, unknown> | undefined\n return data?.role === 'user' || event.type === 'user/message' ? 'user' : 'assistant'\n}\n\nconst historyAuthor = (event: Record<string, unknown>, role: 'user' | 'assistant', t: HistoryTranslate): string => {\n const data = event.data as Record<string, unknown> | undefined\n const source = data?.source as Record<string, unknown> | undefined\n const memberName = typeof source?.memberName === 'string' ? source.memberName.trim() : ''\n if (memberName !== '') return memberName\n return role === 'user' ? t('me') : t('assistant')\n}\n\nconst groupHistory = async (roomId: string, t: HistoryTranslate): Promise<HistoryEntry[]> => {\n const entries: HistoryEntry[] = []\n let cursor: string | null = null\n for (let page = 0; page < 20; page += 1) {\n const suffix = cursor === null ? '' : '?cursor=' + encodeURIComponent(cursor)\n const response = await fetch('/dsh-group-chat/api/rooms/' + encodeURIComponent(roomId) + '/events' + suffix)\n if (!response.ok) throw new Error(t('error'))\n const payload = await response.json() as { events?: Array<{ id?: string; event?: Record<string, unknown>; createdAt?: string }>; nextCursor?: string | null; hasMore?: boolean }\n for (const record of payload.events ?? []) {\n const event = record.event\n if (event === undefined || typeof event.type !== 'string' || !HISTORY_CONTENT_TYPES.has(event.type)) continue\n const data = event.data as Record<string, unknown> | undefined\n const role = historyRole(event)\n const text = historyText(data?.content)\n if (text === '') continue\n entries.push({ key: record.id ?? String(event.seq ?? entries.length), role, author: historyAuthor(event, role, t), time: typeof event.time === 'number' ? event.time : undefined, text, jumpId: record.id })\n }\n if (payload.hasMore !== true || payload.nextCursor === null || payload.nextCursor === undefined) break\n cursor = payload.nextCursor\n }\n return entries.sort((left, right) => (right.time ?? 0) - (left.time ?? 0))\n}\n\nconst sessionHistory = async (sessionId: string, sessions: unknown, t: HistoryTranslate): Promise<HistoryEntry[]> => {\n const service = sessions as {\n retain?: (id: string, options: { source: string }) => {\n ready?: Promise<unknown>\n binding?: {\n eventSource?: { getSnapshot(): { entries?: Array<{ type?: string; event?: Record<string, unknown> }> } }\n session?: { loadOlder?: () => Promise<void> }\n }\n release?: () => void\n }\n }\n if (service.retain === undefined) return []\n const reference = service.retain(sessionId, { source: 'gateway' })\n try {\n await reference.ready\n const binding = reference.binding\n if (binding === undefined) return []\n await binding.session?.loadOlder?.().catch(() => undefined)\n const entries = binding.eventSource?.getSnapshot().entries ?? []\n return entries\n .filter((entry): entry is { type: 'event'; event: Record<string, unknown> } => entry.type === 'event' && entry.event !== undefined)\n .filter((entry) => typeof entry.event.type === 'string' && HISTORY_CONTENT_TYPES.has(entry.event.type))\n .map((entry) => {\n const event = entry.event\n const data = event.data as Record<string, unknown> | undefined\n const role = historyRole(event)\n return {\n key: String(event.seq ?? entries.indexOf(entry)),\n role,\n author: historyAuthor(event, role, t),\n time: typeof event.time === 'number' ? event.time : undefined,\n text: historyText(data?.content),\n }\n })\n .filter((entry) => entry.text !== '')\n .sort((left, right) => (right.time ?? 0) - (left.time ?? 0))\n } finally {\n reference.release?.()\n }\n}\n\nconst HistoryGlyph = () => (\n <svg width=\"16\" height=\"16\" viewBox=\"0 0 16 16\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\" aria-hidden=\"true\" strokeWidth=\"1.5\">\n <path d=\"M2.4 6.8A5.6 5.6 0 1 1 2.4 9.2\" stroke=\"currentColor\" strokeLinecap=\"round\" />\n <path d=\"M2.2 3.4v3.4h3.4\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n <path d=\"M8 5.4V8l1.9 1.2\" stroke=\"currentColor\" strokeLinecap=\"round\" strokeLinejoin=\"round\" />\n </svg>\n)\n\n\ninterface FileAttachmentRef {\n attachmentId: string\n name: string\n bytes: number\n}\n\nconst fileAttachmentIn = (content: unknown, name: string): FileAttachmentRef | undefined => {\n if (!Array.isArray(content)) return undefined\n for (const block of content) {\n if (block === null || typeof block !== 'object') continue\n const record = block as Record<string, unknown>\n if (record.type !== 'file') continue\n const attachment = record.attachment as Record<string, unknown> | undefined\n if (attachment?.name === name && typeof attachment.attachmentId === 'string' && typeof attachment.bytes === 'number') {\n return { attachmentId: attachment.attachmentId, name, bytes: attachment.bytes }\n }\n }\n return undefined\n}\n\nconst sessionFileAttachment = async (sessionId: string, sessions: unknown, name: string): Promise<FileAttachmentRef | undefined> => {\n const service = sessions as {\n retain?: (id: string, options: { source: string }) => {\n ready?: Promise<unknown>\n binding?: {\n eventSource?: { getSnapshot(): { entries?: Array<{ type?: string; event?: Record<string, unknown> }> } }\n session?: { loadOlder?: () => Promise<void> }\n }\n release?: () => void\n }\n }\n if (service.retain === undefined) return undefined\n const reference = service.retain(sessionId, { source: 'gateway' })\n try {\n await reference.ready\n const binding = reference.binding\n if (binding === undefined) return undefined\n const find = (): FileAttachmentRef | undefined => {\n const entries = binding.eventSource?.getSnapshot().entries ?? []\n for (const entry of entries) {\n if (entry.type !== 'event' || entry.event === undefined) continue\n if (entry.event.type !== 'user/message') continue\n const data = entry.event.data as Record<string, unknown> | undefined\n const found = fileAttachmentIn(data?.content, name)\n if (found !== undefined) return found\n }\n return undefined\n }\n let found = find()\n for (let page = 0; page < 5 && found === undefined; page += 1) {\n await binding.session?.loadOlder?.().catch(() => undefined)\n found = find()\n }\n return found\n } finally {\n reference.release?.()\n }\n}\n\nconst HistoryButton = ({ sessionId, sessions, t }: { sessionId: string; sessions: unknown; t: HistoryTranslate }) => {\n const [open, setOpen] = useState(false)\n const [entries, setEntries] = useState<HistoryEntry[]>([])\n const [loading, setLoading] = useState(false)\n const [error, setError] = useState('')\n const [query, setQuery] = useState('')\n const isGroup = sessionId.startsWith('group-room-')\n\n useEffect(() => {\n if (!open) return\n let alive = true\n setLoading(true)\n setError('')\n setQuery('')\n setEntries([])\n const load = isGroup ? groupHistory(sessionId.slice('group-room-'.length), t) : sessionHistory(sessionId, sessions, t)\n void load.then((items) => { if (alive) setEntries(items) })\n .catch((cause) => { if (alive) setError(cause instanceof Error ? cause.message : String(cause)) })\n .finally(() => { if (alive) setLoading(false) })\n return () => { alive = false }\n }, [open, isGroup, sessionId, sessions, t])\n\n const needle = query.trim().toLocaleLowerCase()\n const results = useMemo(() => {\n if (needle === '') return []\n return entries.filter((entry) =>\n entry.text.toLocaleLowerCase().includes(needle) || entry.author.toLocaleLowerCase().includes(needle))\n }, [entries, needle])\n\n const jump = (entry: HistoryEntry) => {\n setOpen(false)\n window.setTimeout(() => {\n let target: HTMLElement | null = entry.jumpId === undefined\n ? null\n : document.querySelector<HTMLElement>('[data-group-chat-message=\"' + CSS.escape(entry.jumpId) + '\"]')\n if (target === null) {\n const text = entry.text.slice(0, 80)\n target = [...document.querySelectorAll<HTMLElement>('[data-group-chat-message], [data-slot=\"conversation.user.text.node\"]')]\n .find((element) => (element.innerText ?? '').includes(text)) ?? null\n }\n if (target === null && (window as unknown as { find: (...args: unknown[]) => boolean }).find(entry.text.slice(0, 80), false, false, true, false, false, false)) {\n const selection = window.getSelection()\n const node = selection?.anchorNode\n target = node instanceof HTMLElement ? node : node?.parentElement ?? null\n }\n target?.scrollIntoView({ behavior: 'smooth', block: 'center' })\n if (target !== null) {\n target.style.outline = '2px solid #2563eb'\n target.style.outlineOffset = '4px'\n window.setTimeout(() => { target!.style.outline = ''; target!.style.outlineOffset = '' }, 1800)\n }\n }, 80)\n }\n\n const searchInputStyle: CSSProperties = {\n width: '100%',\n boxSizing: 'border-box',\n height: 36,\n padding: '0 34px 0 12px',\n borderRadius: 8,\n border: '1px solid ' + colors.line,\n background: colors.panel,\n color: colors.text,\n fontSize: 13,\n outline: 'none',\n }\n\n return (\n <>\n <button\n type=\"button\"\n aria-label={t('open')}\n title={t('open')}\n data-session-id={sessionId}\n onClick={() => setOpen(true)}\n style={{ display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: 28, height: 28, border: 'none', borderRadius: 6, background: 'transparent', color: 'inherit', cursor: 'pointer' }}\n >\n <HistoryGlyph />\n </button>\n <Modal open={open} onClose={() => setOpen(false)} title={t('title')} closeLabel={t('close')} className={TASK_BUS_HISTORY_CLASS}>\n <div style={{ width: '100%', maxHeight: 'min(68vh, 620px)', overflowY: 'auto', display: 'grid', gap: 10, padding: '4px 0 8px' }}>\n <div style={{ position: 'relative', width: '100%' }}>\n <input\n type=\"text\"\n value={query}\n autoFocus\n spellCheck={false}\n placeholder={t('searchPlaceholder')}\n aria-label={t('searchLabel')}\n onChange={(event) => { setQuery(event.target.value) }}\n style={searchInputStyle}\n />\n {query !== '' ? (\n <button\n type=\"button\"\n aria-label={t('clear')}\n title={t('clear')}\n onClick={() => { setQuery('') }}\n style={{ position: 'absolute', right: 6, top: '50%', transform: 'translateY(-50%)', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', width: 24, height: 24, border: 'none', borderRadius: 5, background: 'transparent', color: colors.muted, cursor: 'pointer' }}\n >\n <IconCloseOutlineRegular size={14} />\n </button>\n ) : null}\n </div>\n\n {loading ? <div style={{ padding: 28, textAlign: 'center', color: colors.muted, fontSize: 12 }}>{t('loading')}</div> : null}\n {!loading && error !== '' ? <div role=\"alert\" style={{ padding: 12, color: colors.danger, fontSize: 12 }}>{error}</div> : null}\n {!loading && error === '' && needle === '' ? (\n <div style={{ display: 'grid', justifyItems: 'center', gap: 8, padding: '36px 20px 40px', color: colors.muted, textAlign: 'center' }}>\n <span style={{ display: 'inline-flex', width: 36, height: 36, alignItems: 'center', justifyContent: 'center', borderRadius: 18, background: colors.panelAlt, color: colors.muted }}>\n <HistoryGlyph />\n </span>\n <span style={{ fontSize: 13, fontWeight: 600, color: colors.text }}>{t('initial')}</span>\n <span style={{ fontSize: 12, lineHeight: 1.6 }}>{t('initialHint')}</span>\n </div>\n ) : null}\n {!loading && error === '' && needle !== '' && results.length === 0 ? (\n <div style={{ padding: 28, textAlign: 'center', color: colors.muted, fontSize: 12 }}>\n {entries.length === 0 ? t('empty') : t('noResults')}\n </div>\n ) : null}\n {!loading && error === '' && needle !== '' && results.map((entry) => (\n <button\n key={entry.key}\n type=\"button\"\n onClick={() => jump(entry)}\n style={{ display: 'grid', gap: 6, width: '100%', minWidth: 0, textAlign: 'left', padding: '10px 12px', borderRadius: 8, border: '1px solid ' + colors.line, background: colors.panelAlt, color: colors.text, cursor: 'pointer' }}\n >\n <span style={{ display: 'flex', alignItems: 'center', gap: 8, minWidth: 0, flexWrap: 'wrap', fontSize: 10, color: colors.muted }}>\n <span style={{ fontWeight: 650, color: entry.role === 'user' ? '#2563eb' : colors.text }}>{entry.author}</span>\n {entry.time === undefined ? null : <span>{new Date(entry.time).toLocaleString(t('dateLocale'))}</span>}\n <span style={{ marginLeft: 'auto' }}>{t('jump')}</span>\n </span>\n <span style={{ display: 'block', minWidth: 0, fontSize: 13, lineHeight: 1.65, whiteSpace: 'pre-wrap', overflowWrap: 'anywhere', wordBreak: 'break-word' }}>{entry.text}</span>\n </button>\n ))}\n </div>\n </Modal>\n </>\n )\n}\n\nexport const apply = (ctx: Context) => {\n const locale = (ctx as unknown as {\n locale: {\n register(namespace: string, dictionaries: { zh: typeof HISTORY_COPY.zh; en: typeof HISTORY_COPY.en }): () => void\n bind(namespace: string): (key: HistoryCopyKey) => string\n }\n }).locale\n ctx.effect(() => locale.register(HISTORY_NS, { zh: HISTORY_COPY.zh, en: HISTORY_COPY.en }), 'task-bus: history copy')\n const t = locale.bind(HISTORY_NS)\n const surface = ctx as unknown as {\n slots: {\n inject(name: string, factory: () => () => void): () => void\n register(options: Record<string, unknown>, component: (props: never) => JSX.Element | null): () => void\n }\n uiWorkspace: { openSession(sessionId: string): void }\n }\n const surfaceWithServices = surface as unknown as {\n sidebarRight?: { openResource?(address: string): void }\n get?: (name: string) => unknown\n }\n const sidebarRight = (surfaceWithServices.sidebarRight ?? surfaceWithServices.get?.('sidebarRight')) as\n { openResource?(address: string): void } | undefined\n // Native file cards are spans owned by the official Chat renderer, so delegate from the\n // document. The active Session header carries its id; group rooms have their own handler.\n ctx.effect(() => {\n const onFileClick = (event: MouseEvent): void => {\n const target = event.target\n if (!(target instanceof Element)) return\n const card = target.closest('[data-message-attachments] > *')\n if (!(card instanceof HTMLElement)) return\n const header = document.querySelector<HTMLElement>('button[aria-label=\"消息记录\"][data-session-id]')\n const sessionId = header?.getAttribute('data-session-id') ?? ''\n if (sessionId === '' || sessionId.startsWith('group-room-')) return\n const name = (card.getAttribute('title') ?? card.innerText.split('\\n')[0] ?? '').trim()\n if (name === '') return\n void (async () => {\n const attachment = await sessionFileAttachment(sessionId, (ctx as unknown as { sessions: unknown }).sessions, name)\n if (attachment === undefined) return\n const sessionState = (ctx as unknown as {\n sessions?: { list?: { getSnapshot(): { byId?: Record<string, { cwd?: string }> } } }\n }).sessions?.list?.getSnapshot()\n const cwd = sessionState?.byId?.[sessionId]?.cwd ?? ''\n const query = 'attachmentId=' + encodeURIComponent(attachment.attachmentId)\n + '&name=' + encodeURIComponent(attachment.name)\n + '&bytes=' + encodeURIComponent(String(attachment.bytes))\n + (cwd === '' ? '' : '&cwd=' + encodeURIComponent(cwd))\n const response = await fetch(ROUTE + '/attachment-materialize?' + query)\n if (!response.ok) throw new Error('附件预览准备失败')\n const materialized = await response.json() as { path?: string }\n if (typeof materialized.path !== 'string') throw new Error('附件预览路径缺失')\n const address = 'dsh-resource://file/session/' + encodeURIComponent(sessionId)\n + '/' + materialized.path.split('/').map((part) => encodeURIComponent(part)).join('/')\n const right = (ctx as unknown as { sidebarRight?: { openResource?(address: string): void } }).sidebarRight\n if (right?.openResource !== undefined) right.openResource(address)\n else window.open(new URL('/dsh-task-bus/api/attachment-materialize?' + query, window.location.origin).toString(), '_blank', 'noopener,noreferrer')\n })().catch((cause) => { console.error('[task-bus] file preview failed', cause) })\n }\n document.addEventListener('click', onFileClick, true)\n return () => { document.removeEventListener('click', onFileClick, true) }\n }, 'task-bus: native file card preview')\n const Surface = () => <TaskCenterButton openSession={(sessionId) => surface.uiWorkspace.openSession(sessionId)} />\n ctx.effect(() => surface.slots.inject('shell.overlay', () => surface.slots.register(\n { name: 'shell.overlay', id: 'task-bus.center' },\n Surface,\n )), 'task-bus: Task Center overlay')\n\n // The native header's file-manager jump is the slot this product wants for session history.\n // Hide that one button rather than replacing the whole header seat; every other native\n // control keeps its position and behavior.\n ctx.effect(() => {\n const style = document.createElement('style')\n style.id = 'task-bus-history-replaces-file-manager'\n style.textContent = 'button[aria-label=\"用 文件管理器 打开\"] { display: none !important; }\\n[data-message-attachments] > * { cursor: pointer; }'\n document.head.appendChild(style)\n return () => { style.remove() }\n }, 'task-bus: replace file manager header button')\n\n ctx.effect(() => surface.slots.inject('conversation.session.header.utilities', () => surface.slots.register({\n name: 'conversation.session.header.utilities',\n id: 'task-bus.history',\n order: -4,\n inject: ((sessionId: string) => ({ sessionId, sessions: (ctx as unknown as { sessions: unknown }).sessions, t })) as never,\n } as never, HistoryButton as never)), 'task-bus: message history')\n}"],"mappings":";;;;;;;;;;;;;;;;;EAuBA,MAAa,SAAS;GAAC;GAAS;GAAU;GAAe;GAAY;EAAc;EA0BnF,MAAM,QAAQ;EAEd,MAAM,SAAS;GACb,OAAO;GACP,UAAU;GACV,MAAM;GACN,OAAO;GACP,MAAM;GACN,QAAQ;GACR,YAAY;GACZ,SAAS;GACT,SAAS;GACT,QAAQ;EACV;EAEA,MAAM,uBAAuB;EAC7B,MAAM,yBAAyB;EAC/B,MAAM,oBAAoB;EAC1B,MAAM,qBAAqB;GACxB,qBAAqB;;;;;;;KAOnB,qBAAqB;;;;;GAKvB,uBAAuB;;;;;;;KAOrB,uBAAuB;;;;;;;EAQ5B,MAAM,aAAa;EACnB,MAAM,eAAe;GACnB,IAAI;IACF,OAAO;IACP,MAAM;IACN,OAAO;IACP,mBAAmB;IACnB,aAAa;IACb,OAAO;IACP,SAAS;IACT,aAAa;IACb,SAAS;IACT,OAAO;IACP,OAAO;IACP,WAAW;IACX,MAAM;IACN,IAAI;IACJ,WAAW;IACX,YAAY;GACd;GACA,IAAI;IACF,OAAO;IACP,MAAM;IACN,OAAO;IACP,mBAAmB;IACnB,aAAa;IACb,OAAO;IACP,SAAS;IACT,aAAa;IACb,SAAS;IACT,OAAO;IACP,OAAO;IACP,WAAW;IACX,MAAM;IACN,IAAI;IACJ,WAAW;IACX,YAAY;GACd;EACF;EAKA,MAAM,cAA6B;GACjC,QAAQ,eAAe,OAAO;GAC9B,YAAY,OAAO;GACnB,OAAO,OAAO;GACd,cAAc;GACd,SAAS;GACT,UAAU;GACV,QAAQ;EACV;EAEA,MAAM,iBAAiB,WAA4B;GACjD,IAAI,WAAW,aAAa,OAAO;GACnC,IAAI,WAAW,aAAa,OAAO;GACnC,IAAI,WAAW,WAAW,OAAO;GACjC,IAAI,WAAW,qBAAqB,WAAW,cAAc,WAAW,kBAAkB,OAAO;GACjG,OAAO;EACT;EAEA,MAAM,gBAAgB,UAAkB;GACtC,MAAM,OAAO,KAAK,MAAM,KAAK;GAC7B,IAAI,CAAC,OAAO,SAAS,IAAI,GAAG,OAAO;GACnC,MAAM,UAAU,KAAK,IAAI,GAAG,KAAK,OAAO,KAAK,IAAI,IAAI,QAAQ,GAAM,CAAC;GACpE,IAAI,UAAU,GAAG,OAAO;GACxB,IAAI,UAAU,IAAI,OAAO,OAAO,OAAO,IAAI;GAC3C,MAAM,QAAQ,KAAK,MAAM,UAAU,EAAE;GACrC,IAAI,QAAQ,IAAI,OAAO,OAAO,KAAK,IAAI;GACvC,OAAO,OAAO,KAAK,MAAM,QAAQ,EAAE,CAAC,IAAI;EAC1C;EAEA,MAAM,YAAY,OAAW,SAA6B;GACxD,MAAM,WAAW,MAAM,MAAM,QAAQ,MAAM,EAAE,SAAS,EAAE,QAAQ,mBAAmB,EAAE,CAAC;GACtF,MAAM,UAAU,MAAM,SAAS,KAAK;GACpC,IAAI,CAAC,SAAS,IAAI;IAChB,MAAM,UAAU,YAAY,QAAQ,OAAO,YAAY,YAAY,WAAW,UAC1E,OAAQ,QAA+B,KAAK,IAC5C,cAAc,SAAS,SAAS;IACpC,MAAM,IAAI,MAAM,OAAO;GACzB;GACA,OAAO;EACT;EAGA,MAAM,gBAAgB,QAAsC;GAC1D,IAAI,QAAQ,MAAM,OAAO;GACzB,IAAI;IACF,MAAM,SAAS,IAAI,IAAI,GAAG;IAC1B,MAAM,WAAW,SAAS,WAAW,IAAI,IAAI,SAAS,QAAQ,IAAI;IAClE,IAAI,aAAa,QAAQ,SAAS,WAAW,OAAO,QAClD,OAAO,SAAS,SAAS,OAAO,WAAW,OAAO,SAAS,OAAO;IAEpE,OAAO,OAAO;GAChB,QAAQ;IACN,OAAO;GACT;EACF;EAEA,MAAM,YAAY,EAChB,MACA,MACA,QACA,cACA,YACA,aAQI;GACJ,MAAM,aAAa,KAAK,eAAe;GACvC,MAAM,WAAW,aAAa,KAAK,OAAO,YAAY,IAAI;GAC1D,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD;IACE,OAAO;KACL,QAAQ,eAAe,OAAO;KAC9B,cAAc;KACd,YAAY,OAAO;KACnB,SAAS;KACT,SAAS;KACT,KAAK;IACP;IARF,UAAA;KAUE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,YAAY;OAAc,KAAK;MAAG;MAAjE,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,OAAO;QAAE,MAAM;QAAG,UAAU;OAAE;OAAnC,UAAA;QACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAQ,YAAY;UAAU,KAAK;UAAG,UAAU;UAAQ,cAAc;SAAE;SAA/F,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,KAAD;UAAK,MAAM,aAAa,SAAS;UAC9B,UAAA,aAAc,KAAK,OAAO,SAAS,eAAgB;SACjD,CAAA,GACJ,KAAK,MAAM,KAAK,QACf,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,KAAD;UAAe,MAAK;UAAS,UAAA;SAAS,GAA5B,GAA4B,CACvC,CACE;;QACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,OAAO;UAAE,UAAU;UAAI,YAAY;UAAK,OAAO,OAAO;UAAM,YAAY;SAAK;SAC/E,UAAA,KAAK;QACH,CAAA;QACJ,KAAK,cACJ,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,OAAO;UAAE,UAAU;UAAI,OAAO,OAAO;UAAO,WAAW;UAAG,YAAY;SAAI;SAC5E,UAAA,KAAK;QACH,CAAA,IACH;OACD;MACL,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAAM,OAAO,EAAE,MAAM,OAAO;OAAG,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,KAAD;QAAK,MAAM,cAAc,KAAK,MAAM;QAAI,UAAA,KAAK;OAAiB,CAAA;MAAO,CAAA,CACjG;;KACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,YAAY;OAAU,KAAK;OAAG,UAAU;OAAQ,UAAU;OAAI,OAAO,OAAO;MAAM;MAAjH,UAAA;OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,aAAa,SAAS,KAAW,CAAA;OACvC,KAAK,UAAU,KAAK,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD,EAAA,UAAA,CAAM,QAAK,KAAK,SAAS,EAAS,EAAA,CAAA,IAAI;OAC3D,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,OAAO,EAAE,YAAY,OAAO;QAAI,UAAA,aAAa,KAAK,SAAS;OAAQ,CAAA;MACtE;;KACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,KAAK;OAAG,UAAU;MAAO;MAAxD,UAAA;OACG,cAAc,WACb,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,QAAD;QAAQ,MAAK;QAAK,SAAQ;QAAU,eAAe,OAAO,KAAK,UAAU,UAAU,qBAAqB;QAAG,UAAA;OAEnG,CAAA,IACN;OACH,KAAK,WAAW,eAAe,KAAK,WAAW,cAC9C,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;QAAQ,MAAK;QAAK,SAAQ;QAAU,UAAU,UAAU;QAAM,eAAe,WAAW,IAAI;QACzF,UAAA,SAAS,SAAS;OACb,CAAA,IACN;OACJ,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;QAAQ,MAAK;QAAK,SAAQ;QAAQ,eAAe,OAAO,IAAI;QAAG,UAAA;OAAU,CAAA;OACxE,CAAC,cAAc,KAAK,WAAW,mBAC9B,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;QAAQ,MAAK;QAAK,SAAQ;QAAU,UAAU;QAAM,eAAe,aAAa,MAAM,QAAQ;QAAG,UAAA;OAAU,CAAA,IACzG;OACH,CAAC,cAAc,KAAK,WAAW,gBAC9B,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;QAAQ,MAAK;QAAK,SAAQ;QAAU,UAAU;QAAM,eAAe,aAAa,MAAM,QAAQ;QAAG,UAAA;OAAY,CAAA,IAC3G;OACH,CAAC,cAAc,KAAK,WAAW,oBAC9B,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;QAAQ,MAAK;QAAK,SAAQ;QAAU,UAAU;QAAM,eAAe,aAAa,MAAM,UAAU;QAAG,UAAA;OAAU,CAAA,IAC3G;OACH,CAAC,cAAc,KAAK,WAAW,eAAe,KAAK,WAAW,cAC7D,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;QAAQ,MAAK;QAAK,SAAQ;QAAQ,UAAU;QAAM,eAAe,aAAa,MAAM,QAAQ;QAAG,UAAA;OAAU,CAAA,IACvG;MACD;;IACE;;EAEb;EAsCA,MAAM,cAAsC;GAC1C,iBAAiB;GACjB,aAAa;GACb,sBAAsB;GACtB,qBAAqB;GACrB,gBAAgB;GAChB,WAAW;GACX,WAAW;GACX,kBAAkB;GAClB,MAAM;GACN,MAAM;GACN,QAAQ;GACR,aAAa;GACb,gBAAgB;GAChB,gBAAgB;GAChB,WAAW;GACX,YAAY;GACZ,kBAAkB;GAClB,mBAAmB;GACnB,wBAAwB;GACxB,gBAAgB;GAChB,iBAAiB;GACjB,sBAAsB;GACtB,aAAa;GACb,cAAc;GACd,cAAc;GACd,YAAY;GACZ,aAAa;GACb,iBAAiB;GACjB,iBAAiB;GACjB,aAAa;GACb,cAAc;EAChB;EAEA,MAAM,iCAAiB,IAAI,IAAI;GAC7B;GACA;GACA;GACA;GACA;GACA;EACF,CAAC;EAED,MAAM,eAAe,UAAkB;GACrC,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GAAG,OAAO;GACjD,IAAI,QAAQ,MAAM,OAAO,QAAQ;GACjC,IAAI,QAAQ,SAAa,QAAQ,QAAQ,KAAA,CAAM,QAAQ,CAAC,IAAI;GAC5D,QAAQ,QAAS,QAAA,CAAc,QAAQ,CAAC,IAAI;EAC9C;EAEA,MAAM,iBAAiB,QAAgB,IACpC,QAAQ,sBAAsB,OAAO,CAAC,CACtC,QAAQ,UAAU,GAAG,CAAC,CACtB,KAAK;EAER,MAAM,mBAAmB,KAAa,UAA2B;GAC/D,IAAI,QAAQ,mBAAmB;IAC7B,IAAI,UAAU,YAAY,OAAO;IACjC,IAAI,UAAU,OAAO,OAAO;GAC9B;GACA,IAAI,OAAO,UAAU,WAAW,OAAO,QAAQ,MAAM;GACrD,IAAI,MAAM,QAAQ,KAAK,GAAG,OAAO,MAAM,KAAK,SAAS,OAAO,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;GAC3E,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;IAC/C,MAAM,SAAS;IACf,IAAI,OAAO,OAAO,SAAS,UAAU,OAAO,OAAO;IACnD,OAAO,KAAK,UAAU,KAAK;GAC7B;GACA,OAAO,OAAO,KAAK;EACrB;EAEA,MAAM,eAAe,KAAa,WAAmC;GACnE;GACA,OAAO,YAAY,QAAQ,cAAc,GAAG;GAC5C;GACA,GAAI,QAAQ,YAAY,QAAQ,iBAAiB,QAAQ,gBACrD;IAAE,MAAM;IAAsB,MAAM;GAAW,IAC/C,CAAC;EACP;EAEA,MAAM,cAAc,SAClB,SAAS,KAAA,IACL,CAAC,IACD,OAAO,QAAQ,IAAI,CAAC,CACnB,QAAQ,CAAC,KAAK,WAAW,CAAC,eAAe,IAAI,GAAG,KAAK,UAAU,QAAQ,UAAU,KAAA,KAAa,UAAU,EAAE,CAAC,CAC3G,KAAK,CAAC,KAAK,WAAW,YAAY,KAAK,KAAK,CAAC;EAGpD,MAAM,wBAAwB,WAAwD;GACpF,IAAI,WAAW,MAAM,OAAO,CAAC;GAC7B,MAAM,WAA8B,CAAC;GACrC,MAAM,oBAAoB,WAAW,OAAO,SAAS,IAAI;GACzD,IAAI,kBAAkB,SAAS,GAAG,SAAS,KAAK;IAAE,OAAO;IAAQ,QAAQ;GAAkB,CAAC;GAC5F,MAAM,iBAAiB,WAAW,OAAO,SAAS,uBAAuB,IAAI;GAC7E,IAAI,eAAe,SAAS,GAAG,SAAS,KAAK;IAAE,OAAO;IAAQ,QAAQ;GAAe,CAAC;GACtF,MAAM,YAAY,CAChB,GAAI,OAAO,SAAS,iBAAiB,CAAC,GACtC,GAAI,OAAO,SAAS,uBAAuB,iBAAiB,CAAC,CAC/D;GACA,IAAI,UAAU,SAAS,GACrB,SAAS,KAAK;IACZ,OAAO;IACP,QAAQ,CAAC;KAAE,KAAK;KAAa,OAAO;KAAM,OAAO;KAAW,MAAM;KAAa,MAAM;IAAE,CAAC;GAC1F,CAAC;GAEH,OAAO;EACT;EAEA,MAAM,iBAAiB,EAAE,YACvB,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;GAAK,OAAO;IAAE,SAAS;IAAQ,KAAK;GAAE;GACnC,UAAA,MAAM,KAAK,MAAM,UAAU;IAC1B,MAAM,QAAQ,OAAO,KAAK,UAAU,WAAW,KAAK,QAAQ,OAAO,KAAK,SAAS,WAAW,KAAK,OAAO,MAAM,QAAQ;IACtH,MAAM,SAAS;KAAC,KAAK;KAAO,KAAK;KAAK,KAAK;IAAE,CAAC,CAAC,QAAQ,UAAU,OAAO,UAAU,YAAY,OAAO,UAAU,QAAQ,CAAC,CAAC,KAAK,KAAK;IACnI,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAgD,OAAO;MAAE,SAAS;MAAQ,YAAY;MAAY,KAAK;MAAG,UAAU;KAAE;KAAtH,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,OAAO;OAAE,UAAU;OAAI,OAAO,OAAO;MAAK;MAAI,UAAA;KAAY,CAAA,GAC/D,SAAS,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,OAAO;OAAE,UAAU;OAAI,OAAO,OAAO;OAAO,UAAU;OAAU,cAAc;OAAY,YAAY;MAAS;MAAI,UAAA;KAAa,CAAA,IAAI,IACjJ;IAHK,GAAA,OAAO,KAAK,OAAO,KAAK,MAAM,KAAK,CAGxC;GAET,CAAC;EACE,CAAA;EAGP,MAAM,oBAAoB,EAAE,eAAgD;GAC1E,IAAI,SAAS,WAAW,GAAG,OAAO;GAClC,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,KAAK;IAAG;IACpC,UAAA,SAAS,KAAK,SAAS,iBACtB,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD;KAA4C,OAAO;MAAE,QAAQ,eAAe,OAAO;MAAM,cAAc;MAAI,YAAY,OAAO;MAAO,SAAS;KAAG;KAAjJ,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAI,YAAY;OAAK,OAAO,OAAO;OAAM,cAAc;MAAE;MAAI,UAAA,QAAQ;KAAW,CAAA,GACxG,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;MAAI,OAAO;OAAE,QAAQ;OAAG,SAAS;OAAQ,qBAAqB;OAA6B,KAAK;MAAW;MACxG,UAAA,QAAQ,OAAO,KAAK,UACnB,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAEE,OAAO;QACL,SAAS;QACT,qBAAqB;QACrB,YAAY;QACZ,KAAK;QACL,UAAU;QACV,YAAY,MAAM,SAAS,IAAI,WAAW,KAAA;OAC5C;OATF,UAAA,CAWE,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;QAAI,OAAO;SAAE,UAAU;SAAI,YAAY;SAAK,OAAO,OAAO;QAAM;QAAI,UAAA,MAAM;OAAU,CAAA,GACpF,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;QAAI,OAAO;SAAE,QAAQ;SAAG,UAAU;SAAI,YAAY;SAAK,OAAO,OAAO;SAAM,YAAY;SAAY,cAAc;QAAW;QACzH,UAAA,MAAM,SAAS,eAAe,MAAM,QAAQ,MAAM,KAAK,IACpD,iBAAA,GAAA,kBAAA,IAAA,CAAC,eAAD,EAAe,OAAO,MAAM,MAA0C,CAAA,IACtE,MAAM,UAAU,QAAQ,MAAM,UAAU,KAAA,KAAa,MAAM,UAAU,KACnE,MACA,gBAAgB,MAAM,KAAK,MAAM,KAAK;OAC1C,CAAA,CACD;MAlBE,GAAA,MAAM,GAkBR,CACN;KACC,CAAA,CACG;IA1BK,GAAA,QAAQ,QAAQ,YA0BrB,CACV;GACE,CAAA;EAET;EAEA,MAAM,cAAc,EAAE,MAAM,aAAqD;GAC/E,MAAM,aAAa,KAAK,eAAe;GACvC,MAAM,CAAC,QAAQ,cAAA,GAAaC,MAAAA,SAAAA,CAAmC,IAAI;GACnE,MAAM,CAAC,eAAe,qBAAA,GAAoBA,MAAAA,SAAAA,CAAS,IAAI;GACvD,MAAM,CAAC,aAAa,mBAAA,GAAkBA,MAAAA,SAAAA,CAAwB,IAAI;GAElE,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,QAAQ;IACZ,iBAAiB,IAAI;IACrB,eAAe,IAAI;IACnB,UAA+C,YAAY,mBAAmB,KAAK,EAAE,CAAC,CAAC,CACpF,MAAM,YAAY;KAAE,IAAI,OAAO,UAAU,QAAQ,UAAU,IAAI;IAAE,CAAC,CAAC,CACnE,OAAO,UAAU;KAAE,IAAI,OAAO,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;IAAE,CAAC,CAAC,CACvG,cAAc;KAAE,IAAI,OAAO,iBAAiB,KAAK;IAAE,CAAC;IACvD,aAAa;KAAE,QAAQ;IAAM;GAC/B,GAAG,CAAC,KAAK,EAAE,CAAC;GAEZ,MAAM,WAAW,aAAa,qBAAqB,MAAM,IAAI,QAAQ,MAAM,YAAY,CAAC;GACxF,MAAM,cAAc,QAAQ,eAAe,CAAC;GAC5C,MAAM,iBAAiB,cAAc,OAAO,QAAQ,OAAO,WAAW,OAAO,KAAK;GAClF,MAAM,kBAAkB,eAAqC,mBAAmB,OAC5E,OACA,QAAQ,wBAAwB,mBAAmB,OAAO,cAAc,CAAC,IACvE,mBAAmB,mBAAmB,WAAW,EAAE;GAEzD,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;IAAK,OAAO;KAAE,SAAS;KAAQ,eAAe;KAAU,KAAK;KAAI,MAAM;KAAG,WAAW;IAAO;IAA5F,UAAA;KACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,YAAY;OAAc,KAAK;MAAG;MAAjE,UAAA,CACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,OAAO;QAAE,MAAM;QAAG,UAAU;OAAE;OAAnC,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;QAAK,OAAO;SAAE,UAAU;SAAI,YAAY;SAAK,OAAO,OAAO;SAAM,YAAY;QAAK;QAAI,UAAA,KAAK;OAAW,CAAA,GACtG,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,OAAO;SAAE,WAAW;SAAG,UAAU;SAAI,OAAO,OAAO;QAAM;QAA9D,UAAA;SACG,KAAK,OAAO,UAAU,aAAa,eAAe;SAAQ;SAAI,aAAa,KAAK,SAAS;QACvF;OACF,CAAA,CAAA;MACL,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAACF,sCAAAA,KAAD;OAAK,MAAM,cAAc,KAAK,MAAM;OAAI,UAAA,KAAK;MAAiB,CAAA,CAC3D;;KAEJ,gBAAgB,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAI,WAAW;OAAU,OAAO,OAAO;OAAO,UAAU;MAAG;MAAG,UAAA;KAAY,CAAA,IAAI;KACrH,cAAc,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAY,cAAc;OAAG,YAAY;OAAW,OAAO,OAAO;OAAQ,UAAU;MAAG;MAAI,UAAA;KAAiB,CAAA,IAAI;KACrJ,CAAC,iBAAiB,gBAAgB,QAAQ,SAAS,SAAS,IAAI,iBAAA,GAAA,kBAAA,IAAA,CAAC,kBAAD,EAA4B,SAAW,CAAA,IAAI;KAC3G,CAAC,iBAAiB,gBAAgB,QAAQ,SAAS,WAAW,KAAK,CAAC,cAAc,KAAK,cACtF,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,OAAO;OAAE,QAAQ,eAAe,OAAO;OAAM,cAAc;OAAI,YAAY,OAAO;OAAO,SAAS;OAAI,UAAU;OAAI,YAAY;OAAK,OAAO,OAAO;OAAM,YAAY;MAAW;MAAI,UAAA,KAAK;KAAiB,CAAA,IAC7M;KACH,CAAC,iBAAiB,gBAAgB,QAAQ,SAAS,WAAW,MAAM,cAAc,CAAC,KAAK,eACvF,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAI,WAAW;OAAU,OAAO,OAAO;OAAO,UAAU;MAAG;MAAG,UAAA;KAAW,CAAA,IAC9F;KAEH,cAAc,YAAY,SAAS,IAClC,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD;MAAS,OAAO;OAAE,QAAQ,eAAe,OAAO;OAAM,cAAc;OAAI,YAAY,OAAO;OAAO,SAAS;MAAG;MAA9G,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAI,YAAY;QAAK,OAAO,OAAO;QAAM,cAAc;OAAE;OAAG,UAAA;MAAO,CAAA,GAC1F,YAAY,WAAW,IAAI,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAI,OAAO,OAAO;OAAM;OAAG,UAAA;MAAS,CAAA,IACtF,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,KAAK;OAAE;OACnC,UAAA,YAAY,KAAK,eAAe;QAC/B,MAAM,OAAO,eAAe,UAAU;QACtC,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;SAAyB,OAAO;UAAE,SAAS;UAAQ,YAAY;UAAU,KAAK;UAAG,UAAU;SAAE;SAA7F,UAAA;UACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,OAAO;YAAE,MAAM;YAAG,UAAU;YAAG,UAAU;YAAI,OAAO,OAAO;YAAM,UAAU;YAAU,cAAc;YAAY,YAAY;WAAS;WAAI,UAAA,WAAW;UAAmB,CAAA;UAC5K,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;WAAM,OAAO;YAAE,MAAM;YAAQ,UAAU;YAAI,OAAO,OAAO;WAAM;WAAI,UAAA,YAAY,WAAW,SAAS;UAAQ,CAAA;UAC1G,SAAS,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,QAAD;WAAQ,MAAK;WAAK,SAAQ;WAAQ,eAAe,OAAO,KAAK,MAAM,UAAU,qBAAqB;WAAG,UAAA;UAAU,CAAA,IAAI;SACjI;QAJK,GAAA,WAAW,EAIhB;OAET,CAAC;MACE,CAAA,CAEA;KACP,CAAA,IAAA;KAEJ,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,gBAAgB;OAAY,KAAK;OAAG,UAAU;OAAQ,WAAW;OAAQ,YAAY;MAAE;MAAtH,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;OAAQ,MAAK;OAAK,SAAQ;OAAQ,SAAS;OAAQ,UAAA;MAAc,CAAA,GAChE,cAAc,KAAK,OAAO,WACzB,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;OAAQ,MAAK;OAAK,SAAQ;OAAU,eAAe,OAAO,KAAK,aAAa,KAAK,OAAO,YAAY,IAAI,KAAK,KAAK,UAAU,qBAAqB;OAAG,UAAA;MAE5I,CAAA,IACN,IACD;;IACF;;EAET;EAEA,MAAM,wBAAwB;EAE9B,MAAM,wBAAwB,EAC5B,eACA,WACA,SACA,eAMI;GACJ,MAAM,CAAC,YAAY,kBAAA,GAAiBC,MAAAA,SAAAA,CAAS,KAAK;GAClD,MAAM,SAAS,CAAC,UAAU,QAAQ,YAAY,QAAQ;GACtD,MAAM,OAAgB,CAAC,UAAU,YAAY,YAAY,YAAY;GACrE,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,WAAD;IAAS,OAAO;KAAE,OAAO;KAAQ,UAAU;KAAG,QAAQ,eAAe,OAAO;KAAM,cAAc;KAAI,YAAY,OAAO;KAAU,SAAS;KAAI,SAAS;KAAQ,eAAe;KAAU,KAAK;IAAG;IAAhM,UAAA;KACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,YAAY;OAAU,KAAK;MAAE;MAA5D,UAAA;OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,eAAY;QAAO,OAAO;SAAE,OAAO;SAAI,QAAQ;SAAI,MAAM;SAAQ,cAAc;SAAG,YAAY;SAAW,OAAO;SAAQ,SAAS;SAAe,YAAY;SAAU,gBAAgB;QAAS;QACnM,UAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;SAAK,OAAM;SAAK,QAAO;SAAK,SAAQ;SAAY,MAAK;SAAO,QAAO;SAAe,aAAY;SAAM,eAAc;SAAQ,gBAAe;SAAzI,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,GAAE,8DAA+D,CAAA,GACvE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,GAAE,+DAAgE,CAAA,CACrE;;OACD,CAAA;OACN,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,OAAO;SAAE,MAAM;SAAG,UAAU;QAAE;QAAnC,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,OAAO;UAAE,UAAU;UAAI,YAAY;UAAK,OAAO,OAAO;SAAK;SAAG,UAAA;QAAa,CAAA,GAChF,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,OAAO;UAAE,WAAW;UAAG,UAAU;UAAI,OAAO,OAAO;SAAM;SAAG,UAAA;QAAgB,CAAA,CAC9E;;OACL,iBAAA,GAAA,kBAAA,IAAA,CAACF,sCAAAA,KAAD;QAAW;QAAO,UAAA;OAAY,CAAA;MAC3B;;KACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MAAK,OAAO;OAAE,UAAU;OAAI,YAAY;OAAM,OAAO,OAAO;MAAM;MAAG,UAAA;KAEhE,CAAA;KACJ,aACC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,OAAO;OAAE,WAAW,eAAe,OAAO;OAAM,YAAY;OAAG,SAAS;OAAQ,KAAK;MAAE;MAA5F,UAAA;OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,OAAO;SAAE,SAAS;SAAQ,qBAAqB;SAAsB,KAAK;SAAG,UAAU;QAAG;QAA/F,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,OAAO,EAAE,OAAO,OAAO,MAAM;SAAG,UAAA;QAAQ,CAAA,GAC9C,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,OAAO,EAAE,OAAO,OAAO,KAAK;SAAG,UAAA;QAA6B,CAAA,CAC/D;;OACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,OAAO;SAAE,SAAS;SAAQ,qBAAqB;SAAsB,KAAK;SAAG,UAAU;QAAG;QAA/F,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,OAAO,EAAE,OAAO,OAAO,MAAM;SAAG,UAAA;QAAU,CAAA,GAChD,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,OAAO,EAAE,OAAO,OAAO,KAAK;SAAI,UAAA;QAAoB,CAAA,CACvD;;OACL,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,OAAO;SAAE,SAAS;SAAQ,qBAAqB;SAAsB,KAAK;SAAG,UAAU;QAAG;QAA/F,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,OAAO,EAAE,OAAO,OAAO,MAAM;SAAG,UAAA;QAAQ,CAAA,GAC9C,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,OAAO,EAAE,OAAO,OAAO,KAAK;SAAG,UAAA;QAAuB,CAAA,CACzD;;MACF;KACH,CAAA,IAAA;KACJ,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;MAAK,OAAO;OAAE,SAAS;OAAQ,KAAK;OAAG,WAAW;MAAO;MAAzD,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,QAAD;OAAQ,MAAK;OAAK,SAAQ;OAAQ,eAAe,eAAe,UAAU,CAAC,KAAK;OAAI,UAAA,aAAa,OAAO;MAAa,CAAA,GACrH,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;OAAQ,MAAK;OAAK,SAAS,UAAU,YAAY;OAAW,UAAU;OAAS,SAAS;OAAW,UAAA,UAAU,QAAQ;MAAa,CAAA,CAC/H;;IACE;;EAEb;EAIA,MAAM,oBAAoB,EAAE,kBAAgE;GAC1F,MAAM,CAAC,MAAM,YAAA,GAAWC,MAAAA,SAAAA,CAAS,KAAK;GACtC,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAqB,CAAC,CAAC;GACjD,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,KAAK;GAC5C,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAwB,IAAI;GACtD,MAAM,CAAC,KAAK,WAAA,GAAUA,MAAAA,SAAAA,CAAkB,KAAK;GAC7C,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAAwB,IAAI;GACxD,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAwB,IAAI;GAC5D,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAAS,KAAK;GAC9C,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAS,EAAE;GAC/C,MAAM,CAAC,aAAa,mBAAA,GAAkBA,MAAAA,SAAAA,CAAS,QAAQ;GACvD,MAAM,CAAC,YAAY,kBAAA,GAAiBA,MAAAA,SAAAA,CAAS,KAAK;GAClD,MAAM,CAAC,UAAU,gBAAA,GAAeA,MAAAA,SAAAA,CAA0B,IAAI;GAC9D,MAAM,CAAC,QAAQ,cAAA,GAAaA,MAAAA,SAAAA,CAA+C,IAAI;GAC/E,MAAM,CAAC,kBAAkB,wBAAA,GAAuBA,MAAAA,SAAAA,OAAe;IAC7D,IAAI;KAAE,OAAO,OAAO,aAAa,QAAQ,qBAAqB,MAAM;IAAO,QAAQ;KAAE,OAAO;IAAM;GACpG,CAAC;GACD,MAAM,CAAC,cAAc,oBAAA,GAAmBA,MAAAA,SAAAA,CAAS,CAAC;GAElD,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,SAAS,eAAe,iBAAiB,MAAM,MAAM;IACzD,MAAM,QAAQ,SAAS,cAAc,OAAO;IAC5C,MAAM,KAAK;IACX,MAAM,cAAc;IACpB,SAAS,KAAK,OAAO,KAAK;IAC1B,aAAa;KAAE,MAAM,OAAO;IAAE;GAChC,GAAG,CAAC,CAAC;GAEL,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,MAAM,eAAe;KAEnB,IADkB,SAAS,cAAc,mCAAiC,MAAM,MACjE;MACb,WAAW,YAAa,YAAY,OAAO,UAAU,IAAK;MAC1D;KACF;KAEA,MAAM,SADU,MAAM,KAAK,SAAS,iBAAiB,QAAQ,CACxC,CAAC,CAAC,MAAM,WAAW,0BAA0B,MAAM,OAAO,aAAa,YAAY,KAAK,GAAA,CAAI,KAAK,CAAC,CAAC;KACxH,IAAI,CAAC,QAAQ;MACX,WAAW,YAAa,YAAY,OAAO,UAAU,IAAK;MAC1D;KACF;KACA,MAAM,SAAS,OAAO,aAAa,YAAY,KAAK,GAAA,CAAI,KAAK;KAC7D,IAAI,wBAAwB,KAAK,KAAK,GAAG;MACvC,WAAW,YAAa,YAAY,OAAO,UAAU,IAAK;MAC1D;KACF;KACA,MAAM,OAAO,OAAO,sBAAsB;KAC1C,IAAI,KAAK,UAAU,KAAK,KAAK,WAAW,GAAG;KAC3C,MAAM,OAAO;MAAE,MAAM,KAAK,IAAI,GAAG,KAAK,OAAO,EAAE;MAAG,KAAK,KAAK,OAAO,KAAK,SAAS,MAAM;KAAE;KACzF,WAAW,YACT,YAAY,QAAQ,KAAK,IAAI,QAAQ,OAAO,KAAK,IAAI,IAAI,MAAO,KAAK,IAAI,QAAQ,MAAM,KAAK,GAAG,IAAI,KAC/F,UACA,IACL;IACH;IACA,OAAO;IACP,MAAM,QAAQ,OAAO,YAAY,QAAQ,GAAG;IAC5C,OAAO,iBAAiB,UAAU,MAAM;IACxC,aAAa;KACX,OAAO,cAAc,KAAK;KAC1B,OAAO,oBAAoB,UAAU,MAAM;IAC7C;GACF,GAAG,CAAC,CAAC;GAEL,MAAM,OAAO,OAAO,SAAS,UAAU;IACrC,IAAI,CAAC,kBAAkB;KACrB,SAAS,CAAC,CAAC;KACX,WAAW,KAAK;KAChB,SAAS,IAAI;KACb;IACF;IACA,IAAI,CAAC,QAAQ,WAAW,IAAI;IAC5B,SAAS,IAAI;IACb,IAAI;KACF,MAAM,WAAW,MAAM,UAAiC,WAAW;KACnE,SAAS,CAAC,GAAG,SAAS,KAAK,CAAC,CAAC,MAAM,GAAG,MAAM,EAAE,UAAU,cAAc,EAAE,SAAS,CAAC,CAAC;IACrF,SAAS,OAAO;KACd,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;IACjE,UAAU;KACR,IAAI,CAAC,QAAQ,WAAW,KAAK;IAC/B;GACF;GAEA,MAAM,wBAAwB;IAC5B,oBAAoB,IAAI;IACxB,IAAI;KAAE,OAAO,aAAa,QAAQ,uBAAuB,MAAM;IAAE,QAAQ,CAA8B;GACzG;GAEA,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,CAAC,MAAM;IACX,IAAI,CAAC,kBAAkB;KACrB,OAAO,YAAY;KACnB,SAAS,CAAC,CAAC;KACX,SAAS,IAAI;KACb,WAAW,KAAK;KAChB;IACF;IACA,KAAU;IACV,MAAM,QAAQ,OAAO,kBAAkB;KAAE,KAAU,IAAI;IAAE,GAAG,GAAM;IAClE,aAAa;KAAE,OAAO,cAAc,KAAK;IAAE;GAC7C,GAAG,CAAC,MAAM,gBAAgB,CAAC;GAE3B,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,CAAC,kBAAkB;KACrB,gBAAgB,CAAC;KACjB;IACF;IACA,IAAI,QAAQ;IACZ,MAAM,UAAU,YAAY;KAC1B,IAAI;MACF,MAAM,SAAS,MAAM,UAA8B,QAAQ;MAC3D,IAAI,OAAO,gBAAgB,KAAK,IAAI,GAAG,KAAK,MAAM,OAAO,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC;KAC/E,QAAQ,CAER;IACF;IACA,QAAa;IACb,MAAM,QAAQ,OAAO,kBAAkB;KAAE,QAAa;IAAE,GAAG,GAAO;IAClE,aAAa;KAAE,QAAQ;KAAO,OAAO,cAAc,KAAK;IAAE;GAC5D,GAAG,CAAC,gBAAgB,CAAC;GAGrB,MAAM,YAAA,GAAWC,MAAAA,QAAAA,OAAc,MAAM,QAAQ,SAAS;IACpD,IAAI,QAAQ,cAAc,OAAO,KAAK,eAAe;IACrD,IAAI,QAAQ,SAAS,OAAO,KAAK,eAAe,cAAc,KAAK,OAAO,KAAK,SAAS,OAAO;IAC/F,IAAI,QAAQ,QAAQ,OAAO,KAAK,WAAW,eAAe,KAAK,WAAW;IAC1E,IAAI,QAAQ,YAAY,OAAO,KAAK,eAAe,cAAc,CAAC,KAAK,OAAO,KAAK,SAAS,OAAO;IACnG,OAAO;GACT,CAAC,GAAG,CAAC,OAAO,GAAG,CAAC;GAEhB,MAAM,aAAa,MAAM,QAAQ,SAAS,KAAK,WAAW,eAAe,KAAK,WAAW,WAAW,CAAC,CAAC;GACtG,MAAM,WAAgE;IACpE;KAAE,OAAO;KAAO,IAAI;KAAoB,SAAS;KAAsB,OAAO,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD,EAAA,UAAA,CAAM,OAAG,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,OAAO,EAAE,SAAS,IAAK;MAAI,UAAA,MAAM;KAAa,CAAA,CAAO,EAAA,CAAA;IAAE;IACpJ;KAAE,OAAO;KAAQ,IAAI;KAAqB,SAAS;KAAuB,OAAO,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD,EAAA,UAAA,CAAM,SAAK,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,OAAO,EAAE,SAAS,IAAK;MAAI,UAAA;KAAiB,CAAA,CAAO,EAAA,CAAA;IAAE;IACvJ;KAAE,OAAO;KAAY,IAAI;KAAyB,SAAS;KAA2B,OAAO,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD,EAAA,UAAA,CAAM,OAAG,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,OAAO,EAAE,SAAS,IAAK;MAAI,UAAA,MAAM,QAAQ,SAAS,KAAK,eAAe,cAAc,CAAC,KAAK,OAAO,KAAK,SAAS,OAAO,CAAC,CAAC,CAAC;KAAa,CAAA,CAAO,EAAA,CAAA;IAAE;IAC3P;KAAE,OAAO;KAAS,IAAI;KAAsB,SAAS;KAAwB,OAAO,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD,EAAA,UAAA,CAAM,OAAG,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,OAAO,EAAE,SAAS,IAAK;MAAI,UAAA,MAAM,QAAQ,SAAS,KAAK,eAAe,cAAc,KAAK,OAAO,KAAK,SAAS,OAAO,CAAC,CAAC,CAAC;KAAa,CAAA,CAAO,EAAA,CAAA;IAAE;IACjP;KAAE,OAAO;KAAc,IAAI;KAA2B,SAAS;KAA6B,OAAO,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD,EAAA,UAAA,CAAM,QAAI,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MAAM,OAAO,EAAE,SAAS,IAAK;MAAG,UAAA;KAAO,CAAA,CAAO,EAAA,CAAA;IAAE;GAC/J;GAEA,MAAM,aAAa,OAAO,MAAgB,WAAmB;IAC3D,UAAU,KAAK,EAAE;IACjB,SAAS,IAAI;IACb,IAAI;KACF,MAAM,MAAM,QAAQ,cAAc,mBAAmB,KAAK,GAAG,MAAM,CAAC,CAAC,IAAI,eAAe;MACtF,QAAQ;MACR,SAAS,EAAE,gBAAgB,mBAAmB;MAC9C,MAAM,KAAK,UAAU,EAAE,OAAO,CAAC;KACjC,CAAC,CAAC,CAAC,KAAK,OAAO,aAAa;MAC1B,IAAI,CAAC,SAAS,IAAI;OAChB,MAAM,UAAU,MAAM,SAAS,KAAK,CAAC,CAAC,YAAY,IAAI;OACtD,MAAM,IAAI,MAAM,SAAS,SAAS,QAAQ;MAC5C;KACF,CAAC;KACD,MAAM,KAAK;IACb,SAAS,OAAO;KACd,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;IACjE,UAAU;KACR,UAAU,IAAI;IAChB;GACF;GAEA,MAAM,aAAa,YAAY;IAC7B,MAAM,QAAQ,WAAW,KAAK;IAC9B,IAAI,CAAC,OAAO;IACZ,cAAc,IAAI;IAClB,SAAS,IAAI;IACb,IAAI;KACF,MAAM,WAAW,MAAM,MAAM,QAAQ,YAAY;MAC/C,QAAQ;MACR,SAAS,EAAE,gBAAgB,mBAAmB;MAC9C,MAAM,KAAK,UAAU;OAAE;OAAO,YAAY;MAAY,CAAC;KACzD,CAAC;KACD,MAAM,UAAU,MAAM,SAAS,KAAK;KACpC,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,QAAQ,SAAS,QAAQ;KAC3D,cAAc,EAAE;KAChB,YAAY,KAAK;KACjB,MAAM,KAAK;IACb,SAAS,OAAO;KACd,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;IACjE,UAAU;KACR,cAAc,KAAK;IACrB;GACF;GAEA,MAAM,WAAW,OAAO,SAAmB;IACzC,YAAY,KAAK,EAAE;IACnB,SAAS,IAAI;IACb,IAAI;KACF,MAAM,WAAW,MAAM,MAAM,QAAQ,cAAc;MACjD,QAAQ;MACR,SAAS,EAAE,gBAAgB,mBAAmB;MAC9C,MAAM,KAAK,UAAU,EAAE,IAAI,KAAK,GAAG,CAAC;KACtC,CAAC;KACD,MAAM,UAAU,MAAM,SAAS,KAAK;KACpC,IAAI,CAAC,SAAS,MAAM,OAAO,QAAQ,cAAc,UAC/C,MAAM,IAAI,MAAM,QAAQ,SAAS,aAAa;KAEhD,YAAY,QAAQ,SAAS;KAC7B,QAAQ,KAAK;IACf,SAAS,OAAO;KACd,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;IACjE,UAAU;KACR,YAAY,IAAI;IAClB;GACF;GAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACG,WAAW,OACV,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;IACE,MAAK;IACL,cAAW;IACX,OAAM;IACN,eAAe,SAAS,UAAU,CAAC,KAAK;IACxC,OAAO;KACL,UAAU;KACV,MAAM,OAAO;KACb,KAAK,OAAO;KACZ,QAAQ;KACR,OAAO;KACP,QAAQ;KACR,SAAS;KACT,QAAQ;KACR,cAAc;KACd,YAAY;KACZ,OAAO,aAAa,IAAI,OAAO,SAAS,OAAO;KAC/C,QAAQ;KACR,eAAe;KACf,SAAS;KACT,YAAY;KACZ,gBAAgB;IAClB;IAtBF,UAAA,CAwBE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,OAAM;KAAK,QAAO;KAAK,SAAQ;KAAY,MAAK;KAAO,QAAO;KAAe,aAAY;KAAM,eAAc;KAAQ,gBAAe;KAAQ,eAAY;KAA7J,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,GAAE,kDAAmD,CAAA,GAC3D,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAM,GAAE,2BAA4B,CAAA,CACjC;IACJ,CAAA,GAAA,eAAe,IACd,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,OAAO;MAAE,UAAU;MAAY,QAAQ;MAAI,OAAO;MAAI,UAAU;MAAI,QAAQ;MAAI,SAAS;MAAS,WAAW;MAAc,cAAc;MAAK,YAAY;MAAW,OAAO;MAAQ,UAAU;MAAG,YAAY;MAAQ,WAAW;KAAS;KAC5O,UAAA,eAAe,KAAK,QAAQ;IACzB,CAAA,IACJ,IACE;GACN,CAAA,IAAA,MAEJ,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,OAAD;IAAa;IAAM,eAAe,QAAQ,KAAK;IAAG,OAAM;IAAO,UAAA;IAAS,WAAW;IACjF,UAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,OAAO;MAAE,SAAS;MAAQ,eAAe;MAAU,OAAO;MAAQ,QAAQ;MAAQ,WAAW;KAAE;KAApG,UAAA;MACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;OAAQ,OAAO;QAAE,SAAS;QAAQ,YAAY;QAAU,KAAK;QAAG,SAAS;OAAiB;OAA1F,UAAA;QACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,OAAO;UAAE,MAAM;UAAG,UAAU;SAAE;SACjC,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;UAAI,OAAO;WAAE,QAAQ;WAAG,UAAU;WAAI,YAAY;WAAQ,YAAY;WAAK,OAAO,OAAO;UAAK;UAAG,UAAA;SAAQ,CAAA;QACtG,CAAA;QACL,iBAAA,GAAA,kBAAA,IAAA,CAACH,sCAAAA,QAAD;SAAQ,MAAK;SAAK,SAAQ;SAAU,UAAU,CAAC;SAAkB,eAAe,aAAa,UAAU,CAAC,KAAK;SAAG,UAAA;QAAU,CAAA;QAC1H,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;SAAQ,MAAK;SAAK,SAAQ;SAAQ,eAAe,KAAK,KAAK;SAAG,UAAA;QAAU,CAAA;QACxE,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;SAAQ,MAAK;SAAK,SAAQ;SAAQ,cAAW;SAAK,OAAM;SAAK,eAAe,QAAQ,KAAK;SAAG,MAAM,iBAAA,GAAA,kBAAA,IAAA,CAACI,sCAAAA,yBAAD,EAAyB,MAAM,GAAK,CAAA;SAAG,OAAO;UAAE,OAAO;UAAI,SAAS;SAAE;QAAI,CAAA;OACtK;;MACR,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAe,cAAc,eAAe,OAAO;OAAK;OAC7E,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,eAAD;QAAe,OAAO;QAAU,OAAO;QAAK,UAAU;QAAQ,OAAM;OAAU,CAAA;MAC3E,CAAA;MACJ,WACC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,OAAO;QAAE,QAAQ;QAAe,SAAS;QAAI,QAAQ,eAAe,OAAO;QAAM,cAAc;QAAG,SAAS;QAAQ,KAAK;OAAE;OAA/H,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;QACE,OAAO;QACP,WAAW,UAAU,cAAc,MAAM,OAAO,KAAK;QACrD,aAAY;QACZ,WAAA;QACA,OAAO;SAAE,GAAG;SAAa,QAAQ;SAAQ,SAAS;QAAU;OAC7D,CAAA,GACD,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;QAAK,OAAO;SAAE,SAAS;SAAQ,KAAK;QAAE;QAAtC,UAAA;SACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;UAAQ,OAAO;UAAa,WAAW,UAAU,eAAe,MAAM,OAAO,KAAK;UAAG,OAAO;WAAE,GAAG;WAAa,SAAS;UAAU;UAAjI,UAAA;WACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;YAAQ,OAAM;YAAS,UAAA;WAAY,CAAA;WACnC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;YAAQ,OAAM;YAAU,UAAA;WAAY,CAAA;WACpC,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;YAAQ,OAAM;YAAQ,UAAA;WAAY,CAAA;UAC5B;;SACR,iBAAA,GAAA,kBAAA,IAAA,CAACL,sCAAAA,QAAD;UAAQ,MAAK;UAAK,SAAQ;UAAU,UAAU,cAAc,WAAW,KAAK,MAAM;UAAI,eAAe,KAAK,WAAW;UAClH,UAAA,aAAa,SAAS;SACjB,CAAA;SACR,iBAAA,GAAA,kBAAA,IAAA,CAACA,sCAAAA,QAAD;UAAQ,MAAK;UAAK,SAAQ;UAAQ,eAAe,YAAY,KAAK;UAAG,UAAA;SAAU,CAAA;QAC5E;OACF,CAAA,CAAA;MACH,CAAA,IAAA;MACH,QAAQ,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAY,cAAc;QAAG,YAAY;QAAW,OAAO,OAAO;QAAQ,UAAU;QAAI,QAAQ;OAAc;OAAI,UAAA;MAAW,CAAA,IAAI;MACjK,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,IAAI,oBAAoB;OAAK,MAAK;OAAW,mBAAiB,kBAAkB;OAAK,OAAO;QAAE,SAAS;QAAkB,SAAS;QAAQ,eAAe;QAAU,KAAK;QAAI,WAAW;QAAQ,MAAM;OAAE;OACzM,UAAA,aAAa,OACZ,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;QAAY,MAAM;QAAU,cAAc,YAAY,IAAI;OAAI,CAAA,IAE9D,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA;QACG,QAAQ,eAAe,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAQ,qBAAqB;UAA2C,KAAK;SAAG;SAAG,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,sBAAD;UAAsB,eAAe,MAAM,QAAQ,SAAS,KAAK,eAAe,kBAAkB,CAAC,CAAC;UAAQ,WAAW,UAAU;UAAM,SAAS;UAAkB,UAAU;SAAkB,CAAA;QAAM,CAAA,IAAI;QACzU,UAAU,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAI,WAAW;UAAU,OAAO,OAAO;UAAO,UAAU;SAAG;SAAG,UAAA;QAAU,CAAA,IAAI;QAC7G,CAAC,WAAW,SAAS,WAAW,IAAI,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,OAAO;UAAE,SAAS;UAAI,WAAW;UAAU,OAAO,OAAO;UAAO,UAAU;SAAG;SAAI,UAAA,QAAQ,eAAgB,mBAAmB,gBAAgB,iBAAkB;QAAY,CAAA,IAAI;QACvN,CAAC,WAAW,QAAQ,gBAAgB,SAAS,SAAS,IAAI,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;SAAK,OAAO;UAAE,UAAU;UAAI,OAAO,OAAO;SAAM;SAAG,UAAA;QAAc,CAAA,IAAI;QAC/H,CAAC,WAAW,SAAS,KAAK,SACzB,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;SAA8B;SAAM,MAAM,WAAW,KAAK;SAAI,QAAQ,aAAa,KAAK;SAAI,eAAe,MAAM,WAAW,KAAK,WAAW,MAAM,MAAM;SAAG,aAAa,SAAS,KAAK,SAAS,IAAI;SAAG,QAAQ;QAAc,GAA7M,KAAK,EAAwM,CAC7N;OACD,EAAA,CAAA;MAED,CAAA;KACF;;GACA,CAAA,CACP,EAAA,CAAA;EAEN;EAaA,MAAM,wCAAwB,IAAI,IAAI,CAAC,gBAAgB,mBAAmB,CAAC;EAE3E,MAAM,eAAe,YAA6B;GAChD,IAAI,CAAC,MAAM,QAAQ,OAAO,GAAG,OAAO;GACpC,OAAO,QAAQ,KAAK,UAAU;IAC5B,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU,OAAO;IACxD,MAAM,SAAS;IACf,OAAO,OAAO,SAAS,UAAU,OAAO,OAAO,SAAS,WAAW,OAAO,OAAO;GACnF,CAAC,CAAC,CAAC,OAAO,OAAO,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,KAAK;EACrC;EAEA,MAAM,eAAe,UAAyD;GAE5E,OADa,MAAM,MACN,SAAS,UAAU,MAAM,SAAS,iBAAiB,SAAS;EAC3E;EAEA,MAAM,iBAAiB,OAAgC,MAA4B,MAAgC;GAEjH,MAAM,SADO,MAAM,MACE;GACrB,MAAM,aAAa,OAAO,QAAQ,eAAe,WAAW,OAAO,WAAW,KAAK,IAAI;GACvF,IAAI,eAAe,IAAI,OAAO;GAC9B,OAAO,SAAS,SAAS,EAAE,IAAI,IAAI,EAAE,WAAW;EAClD;EAEA,MAAM,eAAe,OAAO,QAAgB,MAAiD;GAC3F,MAAM,UAA0B,CAAC;GACjC,IAAI,SAAwB;GAC5B,KAAK,IAAI,OAAO,GAAG,OAAO,IAAI,QAAQ,GAAG;IACvC,MAAM,SAAS,WAAW,OAAO,KAAK,aAAa,mBAAmB,MAAM;IAC5E,MAAM,WAAW,MAAM,MAAM,+BAA+B,mBAAmB,MAAM,IAAI,YAAY,MAAM;IAC3G,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,EAAE,OAAO,CAAC;IAC5C,MAAM,UAAU,MAAM,SAAS,KAAK;IACpC,KAAK,MAAM,UAAU,QAAQ,UAAU,CAAC,GAAG;KACzC,MAAM,QAAQ,OAAO;KACrB,IAAI,UAAU,KAAA,KAAa,OAAO,MAAM,SAAS,YAAY,CAAC,sBAAsB,IAAI,MAAM,IAAI,GAAG;KACrG,MAAM,OAAO,MAAM;KACnB,MAAM,OAAO,YAAY,KAAK;KAC9B,MAAM,OAAO,YAAY,MAAM,OAAO;KACtC,IAAI,SAAS,IAAI;KACjB,QAAQ,KAAK;MAAE,KAAK,OAAO,MAAM,OAAO,MAAM,OAAO,QAAQ,MAAM;MAAG;MAAM,QAAQ,cAAc,OAAO,MAAM,CAAC;MAAG,MAAM,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAA;MAAW;MAAM,QAAQ,OAAO;KAAG,CAAC;IAC7M;IACA,IAAI,QAAQ,YAAY,QAAQ,QAAQ,eAAe,QAAQ,QAAQ,eAAe,KAAA,GAAW;IACjG,SAAS,QAAQ;GACnB;GACA,OAAO,QAAQ,MAAM,MAAM,WAAW,MAAM,QAAQ,MAAM,KAAK,QAAQ,EAAE;EAC3E;EAEA,MAAM,iBAAiB,OAAO,WAAmB,UAAmB,MAAiD;GACnH,MAAM,UAAU;GAUhB,IAAI,QAAQ,WAAW,KAAA,GAAW,OAAO,CAAC;GAC1C,MAAM,YAAY,QAAQ,OAAO,WAAW,EAAE,QAAQ,UAAU,CAAC;GACjE,IAAI;IACF,MAAM,UAAU;IAChB,MAAM,UAAU,UAAU;IAC1B,IAAI,YAAY,KAAA,GAAW,OAAO,CAAC;IACnC,MAAM,QAAQ,SAAS,YAAY,CAAC,CAAC,YAAY,KAAA,CAAS;IAC1D,MAAM,UAAU,QAAQ,aAAa,YAAY,CAAC,CAAC,WAAW,CAAC;IAC/D,OAAO,QACJ,QAAQ,UAAsE,MAAM,SAAS,WAAW,MAAM,UAAU,KAAA,CAAS,CAAC,CAClI,QAAQ,UAAU,OAAO,MAAM,MAAM,SAAS,YAAY,sBAAsB,IAAI,MAAM,MAAM,IAAI,CAAC,CAAC,CACtG,KAAK,UAAU;KACd,MAAM,QAAQ,MAAM;KACpB,MAAM,OAAO,MAAM;KACnB,MAAM,OAAO,YAAY,KAAK;KAC9B,OAAO;MACL,KAAK,OAAO,MAAM,OAAO,QAAQ,QAAQ,KAAK,CAAC;MAC/C;MACA,QAAQ,cAAc,OAAO,MAAM,CAAC;MACpC,MAAM,OAAO,MAAM,SAAS,WAAW,MAAM,OAAO,KAAA;MACpD,MAAM,YAAY,MAAM,OAAO;KACjC;IACF,CAAC,CAAC,CACD,QAAQ,UAAU,MAAM,SAAS,EAAE,CAAC,CACpC,MAAM,MAAM,WAAW,MAAM,QAAQ,MAAM,KAAK,QAAQ,EAAE;GAC/D,UAAU;IACR,UAAU,UAAU;GACtB;EACF;EAEA,MAAM,qBACJ,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;GAAK,OAAM;GAAK,QAAO;GAAK,SAAQ;GAAY,MAAK;GAAO,OAAM;GAA6B,eAAY;GAAO,aAAY;GAA9H,UAAA;IACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,GAAE;KAAiC,QAAO;KAAe,eAAc;IAAS,CAAA;IACtF,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,GAAE;KAAmB,QAAO;KAAe,eAAc;KAAQ,gBAAe;IAAS,CAAA;IAC/F,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAAM,GAAE;KAAmB,QAAO;KAAe,eAAc;KAAQ,gBAAe;IAAS,CAAA;GAC5F;;EAUP,MAAM,oBAAoB,SAAkB,SAAgD;GAC1F,IAAI,CAAC,MAAM,QAAQ,OAAO,GAAG,OAAO,KAAA;GACpC,KAAK,MAAM,SAAS,SAAS;IAC3B,IAAI,UAAU,QAAQ,OAAO,UAAU,UAAU;IACjD,MAAM,SAAS;IACf,IAAI,OAAO,SAAS,QAAQ;IAC5B,MAAM,aAAa,OAAO;IAC1B,IAAI,YAAY,SAAS,QAAQ,OAAO,WAAW,iBAAiB,YAAY,OAAO,WAAW,UAAU,UAC1G,OAAO;KAAE,cAAc,WAAW;KAAc;KAAM,OAAO,WAAW;IAAM;GAElF;EAEF;EAEA,MAAM,wBAAwB,OAAO,WAAmB,UAAmB,SAAyD;GAClI,MAAM,UAAU;GAUhB,IAAI,QAAQ,WAAW,KAAA,GAAW,OAAO,KAAA;GACzC,MAAM,YAAY,QAAQ,OAAO,WAAW,EAAE,QAAQ,UAAU,CAAC;GACjE,IAAI;IACF,MAAM,UAAU;IAChB,MAAM,UAAU,UAAU;IAC1B,IAAI,YAAY,KAAA,GAAW,OAAO,KAAA;IAClC,MAAM,aAA4C;KAChD,MAAM,UAAU,QAAQ,aAAa,YAAY,CAAC,CAAC,WAAW,CAAC;KAC/D,KAAK,MAAM,SAAS,SAAS;MAC3B,IAAI,MAAM,SAAS,WAAW,MAAM,UAAU,KAAA,GAAW;MACzD,IAAI,MAAM,MAAM,SAAS,gBAAgB;MACzC,MAAM,OAAO,MAAM,MAAM;MACzB,MAAM,QAAQ,iBAAiB,MAAM,SAAS,IAAI;MAClD,IAAI,UAAU,KAAA,GAAW,OAAO;KAClC;IAEF;IACA,IAAI,QAAQ,KAAK;IACjB,KAAK,IAAI,OAAO,GAAG,OAAO,KAAK,UAAU,KAAA,GAAW,QAAQ,GAAG;KAC7D,MAAM,QAAQ,SAAS,YAAY,CAAC,CAAC,YAAY,KAAA,CAAS;KAC1D,QAAQ,KAAK;IACf;IACA,OAAO;GACT,UAAU;IACR,UAAU,UAAU;GACtB;EACF;EAEA,MAAM,iBAAiB,EAAE,WAAW,UAAU,QAAuE;GACnH,MAAM,CAAC,MAAM,YAAA,GAAWC,MAAAA,SAAAA,CAAS,KAAK;GACtC,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAyB,CAAC,CAAC;GACzD,MAAM,CAAC,SAAS,eAAA,GAAcA,MAAAA,SAAAA,CAAS,KAAK;GAC5C,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAS,EAAE;GACrC,MAAM,CAAC,OAAO,aAAA,GAAYA,MAAAA,SAAAA,CAAS,EAAE;GACrC,MAAM,UAAU,UAAU,WAAW,aAAa;GAElD,CAAA,GAAA,MAAA,UAAA,OAAgB;IACd,IAAI,CAAC,MAAM;IACX,IAAI,QAAQ;IACZ,WAAW,IAAI;IACf,SAAS,EAAE;IACX,SAAS,EAAE;IACX,WAAW,CAAC,CAAC;IAEb,CADa,UAAU,aAAa,UAAU,MAAM,EAAoB,GAAG,CAAC,IAAI,eAAe,WAAW,UAAU,CAAC,EAAA,CAC3G,MAAM,UAAU;KAAE,IAAI,OAAO,WAAW,KAAK;IAAE,CAAC,CAAC,CACxD,OAAO,UAAU;KAAE,IAAI,OAAO,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;IAAE,CAAC,CAAC,CACjG,cAAc;KAAE,IAAI,OAAO,WAAW,KAAK;IAAE,CAAC;IACjD,aAAa;KAAE,QAAQ;IAAM;GAC/B,GAAG;IAAC;IAAM;IAAS;IAAW;IAAU;GAAC,CAAC;GAE1C,MAAM,SAAS,MAAM,KAAK,CAAC,CAAC,kBAAkB;GAC9C,MAAM,WAAA,GAAUC,MAAAA,QAAAA,OAAc;IAC5B,IAAI,WAAW,IAAI,OAAO,CAAC;IAC3B,OAAO,QAAQ,QAAQ,UACrB,MAAM,KAAK,kBAAkB,CAAC,CAAC,SAAS,MAAM,KAAK,MAAM,OAAO,kBAAkB,CAAC,CAAC,SAAS,MAAM,CAAC;GACxG,GAAG,CAAC,SAAS,MAAM,CAAC;GAEpB,MAAM,QAAQ,UAAwB;IACpC,QAAQ,KAAK;IACb,OAAO,iBAAiB;KACtB,IAAI,SAA6B,MAAM,WAAW,KAAA,IAC9C,OACA,SAAS,cAA2B,gCAA+B,IAAI,OAAO,MAAM,MAAM,IAAI,KAAI;KACtG,IAAI,WAAW,MAAM;MACnB,MAAM,OAAO,MAAM,KAAK,MAAM,GAAG,EAAE;MACnC,SAAS,CAAC,GAAG,SAAS,iBAA8B,wEAAsE,CAAC,CAAC,CACzH,MAAM,aAAa,QAAQ,aAAa,GAAA,CAAI,SAAS,IAAI,CAAC,KAAK;KACpE;KACA,IAAI,WAAW,QAAS,OAAgE,KAAK,MAAM,KAAK,MAAM,GAAG,EAAE,GAAG,OAAO,OAAO,MAAM,OAAO,OAAO,KAAK,GAAG;MAE9J,MAAM,OADY,OAAO,aACJ,CAAC,EAAE;MACxB,SAAS,gBAAgB,cAAc,OAAO,MAAM,iBAAiB;KACvE;KACA,QAAQ,eAAe;MAAE,UAAU;MAAU,OAAO;KAAS,CAAC;KAC9D,IAAI,WAAW,MAAM;MACnB,OAAO,MAAM,UAAU;MACvB,OAAO,MAAM,gBAAgB;MAC7B,OAAO,iBAAiB;OAAE,OAAQ,MAAM,UAAU;OAAI,OAAQ,MAAM,gBAAgB;MAAG,GAAG,IAAI;KAChG;IACF,GAAG,EAAE;GACP;GAEA,MAAM,mBAAkC;IACtC,OAAO;IACP,WAAW;IACX,QAAQ;IACR,SAAS;IACT,cAAc;IACd,QAAQ,eAAe,OAAO;IAC9B,YAAY,OAAO;IACnB,OAAO,OAAO;IACd,UAAU;IACV,SAAS;GACX;GAEA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;IACE,MAAK;IACL,cAAY,EAAE,MAAM;IACpB,OAAO,EAAE,MAAM;IACf,mBAAiB;IACjB,eAAe,QAAQ,IAAI;IAC3B,OAAO;KAAE,SAAS;KAAe,YAAY;KAAU,gBAAgB;KAAU,OAAO;KAAI,QAAQ;KAAI,QAAQ;KAAQ,cAAc;KAAG,YAAY;KAAe,OAAO;KAAW,QAAQ;IAAU;IAExM,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,cAAD,CAAe,CAAA;GACT,CAAA,GACR,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,OAAD;IAAa;IAAM,eAAe,QAAQ,KAAK;IAAG,OAAO,EAAE,OAAO;IAAG,YAAY,EAAE,OAAO;IAAG,WAAW;IACtG,UAAA,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;KAAK,OAAO;MAAE,OAAO;MAAQ,WAAW;MAAoB,WAAW;MAAQ,SAAS;MAAQ,KAAK;MAAI,SAAS;KAAY;KAA9H,UAAA;MACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,OAAO;QAAE,UAAU;QAAY,OAAO;OAAO;OAAlD,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,SAAD;QACE,MAAK;QACL,OAAO;QACP,WAAA;QACA,YAAY;QACZ,aAAa,EAAE,mBAAmB;QAClC,cAAY,EAAE,aAAa;QAC3B,WAAW,UAAU;SAAE,SAAS,MAAM,OAAO,KAAK;QAAE;QACpD,OAAO;OACR,CAAA,GACA,UAAU,KACT,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;QACE,MAAK;QACL,cAAY,EAAE,OAAO;QACrB,OAAO,EAAE,OAAO;QAChB,eAAe;SAAE,SAAS,EAAE;QAAE;QAC9B,OAAO;SAAE,UAAU;SAAY,OAAO;SAAG,KAAK;SAAO,WAAW;SAAoB,SAAS;SAAe,YAAY;SAAU,gBAAgB;SAAU,OAAO;SAAI,QAAQ;SAAI,QAAQ;SAAQ,cAAc;SAAG,YAAY;SAAe,OAAO,OAAO;SAAO,QAAQ;QAAU;QAEtR,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACC,sCAAAA,yBAAD,EAAyB,MAAM,GAAK,CAAA;OAC9B,CAAA,IACN,IACD;;MAEJ,UAAU,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAI,WAAW;QAAU,OAAO,OAAO;QAAO,UAAU;OAAG;OAAI,UAAA,EAAE,SAAS;MAAO,CAAA,IAAI;MACtH,CAAC,WAAW,UAAU,KAAK,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,MAAK;OAAQ,OAAO;QAAE,SAAS;QAAI,OAAO,OAAO;QAAQ,UAAU;OAAG;OAAI,UAAA;MAAW,CAAA,IAAI;MACzH,CAAC,WAAW,UAAU,MAAM,WAAW,KACtC,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAQ,cAAc;QAAU,KAAK;QAAG,SAAS;QAAkB,OAAO,OAAO;QAAO,WAAW;OAAS;OAAnI,UAAA;QACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,OAAO;UAAE,SAAS;UAAe,OAAO;UAAI,QAAQ;UAAI,YAAY;UAAU,gBAAgB;UAAU,cAAc;UAAI,YAAY,OAAO;UAAU,OAAO,OAAO;SAAM;SAC/K,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,cAAD,CAAe,CAAA;QACX,CAAA;QACN,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,OAAO;UAAE,UAAU;UAAI,YAAY;UAAK,OAAO,OAAO;SAAK;SAAI,UAAA,EAAE,SAAS;QAAQ,CAAA;QACxF,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;SAAM,OAAO;UAAE,UAAU;UAAI,YAAY;SAAI;SAAI,UAAA,EAAE,aAAa;QAAQ,CAAA;OACrE;MACH,CAAA,IAAA;MACH,CAAC,WAAW,UAAU,MAAM,WAAW,MAAM,QAAQ,WAAW,IAC/D,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAAK,OAAO;QAAE,SAAS;QAAI,WAAW;QAAU,OAAO,OAAO;QAAO,UAAU;OAAG;OAC/E,UAAA,QAAQ,WAAW,IAAI,EAAE,OAAO,IAAI,EAAE,WAAW;MAC/C,CAAA,IACH;MACH,CAAC,WAAW,UAAU,MAAM,WAAW,MAAM,QAAQ,KAAK,UACzD,iBAAA,GAAA,kBAAA,KAAA,CAAC,UAAD;OAEE,MAAK;OACL,eAAe,KAAK,KAAK;OACzB,OAAO;QAAE,SAAS;QAAQ,KAAK;QAAG,OAAO;QAAQ,UAAU;QAAG,WAAW;QAAQ,SAAS;QAAa,cAAc;QAAG,QAAQ,eAAe,OAAO;QAAM,YAAY,OAAO;QAAU,OAAO,OAAO;QAAM,QAAQ;OAAU;OAJjO,UAAA,CAME,iBAAA,GAAA,kBAAA,KAAA,CAAC,QAAD;QAAM,OAAO;SAAE,SAAS;SAAQ,YAAY;SAAU,KAAK;SAAG,UAAU;SAAG,UAAU;SAAQ,UAAU;SAAI,OAAO,OAAO;QAAM;QAA/H,UAAA;SACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;UAAM,OAAO;WAAE,YAAY;WAAK,OAAO,MAAM,SAAS,SAAS,YAAY,OAAO;UAAK;UAAI,UAAA,MAAM;SAAa,CAAA;SAC7G,MAAM,SAAS,KAAA,IAAY,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD,EAAA,UAAO,IAAI,KAAK,MAAM,IAAI,CAAC,CAAC,eAAe,EAAE,YAAY,CAAC,EAAQ,CAAA;SACrG,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;UAAM,OAAO,EAAE,YAAY,OAAO;UAAI,UAAA,EAAE,MAAM;SAAQ,CAAA;QAClD;OACN,CAAA,GAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;QAAM,OAAO;SAAE,SAAS;SAAS,UAAU;SAAG,UAAU;SAAI,YAAY;SAAM,YAAY;SAAY,cAAc;SAAY,WAAW;QAAa;QAAI,UAAA,MAAM;OAAW,CAAA,CACvK;MAXD,GAAA,MAAM,GAWL,CACT;KACE;;GACA,CAAA,CACP,EAAA,CAAA;EAEN;EAEA,MAAa,SAAS,QAAiB;GACrC,MAAM,SAAU,IAKb;GACH,IAAI,aAAa,OAAO,SAAS,YAAY;IAAE,IAAI,aAAa;IAAI,IAAI,aAAa;GAAG,CAAC,GAAG,wBAAwB;GACpH,MAAM,IAAI,OAAO,KAAK,UAAU;GAChC,MAAM,UAAU;GAOhB,MAAM,sBAAsB;GAIN,oBAAoB,gBAAgB,oBAAoB,MAAM,cAAc;GAIlG,IAAI,aAAa;IACf,MAAM,eAAe,UAA4B;KAC/C,MAAM,SAAS,MAAM;KACrB,IAAI,EAAE,kBAAkB,UAAU;KAClC,MAAM,OAAO,OAAO,QAAQ,gCAAgC;KAC5D,IAAI,EAAE,gBAAgB,cAAc;KAEpC,MAAM,YADS,SAAS,cAA2B,8CAC5B,CAAC,EAAE,aAAa,iBAAiB,KAAK;KAC7D,IAAI,cAAc,MAAM,UAAU,WAAW,aAAa,GAAG;KAC7D,MAAM,QAAQ,KAAK,aAAa,OAAO,KAAK,KAAK,UAAU,MAAM,IAAI,CAAC,CAAC,MAAM,GAAA,CAAI,KAAK;KACtF,IAAI,SAAS,IAAI;KACjB,CAAM,YAAY;MAChB,MAAM,aAAa,MAAM,sBAAsB,WAAY,IAAyC,UAAU,IAAI;MAClH,IAAI,eAAe,KAAA,GAAW;MAI9B,MAAM,OAHgB,IAEnB,UAAU,MAAM,YAAY,EAAA,EACL,OAAO,UAAU,EAAE,OAAO;MACpD,MAAM,QAAQ,kBAAkB,mBAAmB,WAAW,YAAY,IACtE,WAAW,mBAAmB,WAAW,IAAI,IAC7C,YAAY,mBAAmB,OAAO,WAAW,KAAK,CAAC,KACtD,QAAQ,KAAK,KAAK,UAAU,mBAAmB,GAAG;MACvD,MAAM,WAAW,MAAM,MAAM,QAAQ,6BAA6B,KAAK;MACvE,IAAI,CAAC,SAAS,IAAI,MAAM,IAAI,MAAM,UAAU;MAC5C,MAAM,eAAe,MAAM,SAAS,KAAK;MACzC,IAAI,OAAO,aAAa,SAAS,UAAU,MAAM,IAAI,MAAM,UAAU;MACrE,MAAM,UAAU,iCAAiC,mBAAmB,SAAS,IACzE,MAAM,aAAa,KAAK,MAAM,GAAG,CAAC,CAAC,KAAK,SAAS,mBAAmB,IAAI,CAAC,CAAC,CAAC,KAAK,GAAG;MACvF,MAAM,QAAS,IAA+E;MAC9F,IAAI,OAAO,iBAAiB,KAAA,GAAW,MAAM,aAAa,OAAO;WAC5D,OAAO,KAAK,IAAI,IAAI,8CAA8C,OAAO,OAAO,SAAS,MAAM,CAAC,CAAC,SAAS,GAAG,UAAU,qBAAqB;KACnJ,EAAA,CAAG,CAAC,CAAC,OAAO,UAAU;MAAE,QAAQ,MAAM,kCAAkC,KAAK;KAAE,CAAC;IAClF;IACA,SAAS,iBAAiB,SAAS,aAAa,IAAI;IACpD,aAAa;KAAE,SAAS,oBAAoB,SAAS,aAAa,IAAI;IAAE;GAC1E,GAAG,oCAAoC;GACvC,MAAM,gBAAgB,iBAAA,GAAA,kBAAA,IAAA,CAAC,kBAAD,EAAkB,cAAc,cAAc,QAAQ,YAAY,YAAY,SAAS,EAAI,CAAA;GACjH,IAAI,aAAa,QAAQ,MAAM,OAAO,uBAAuB,QAAQ,MAAM,SACzE;IAAE,MAAM;IAAiB,IAAI;GAAkB,GAC/C,OACF,CAAC,GAAG,+BAA+B;GAKnC,IAAI,aAAa;IACf,MAAM,QAAQ,SAAS,cAAc,OAAO;IAC5C,MAAM,KAAK;IACX,MAAM,cAAc;IACpB,SAAS,KAAK,YAAY,KAAK;IAC/B,aAAa;KAAE,MAAM,OAAO;IAAE;GAChC,GAAG,8CAA8C;GAEjD,IAAI,aAAa,QAAQ,MAAM,OAAO,+CAA+C,QAAQ,MAAM,SAAS;IAC1G,MAAM;IACN,IAAI;IACJ,OAAO;IACP,UAAU,eAAuB;KAAE;KAAW,UAAW,IAAyC;KAAU;IAAE;GAChH,GAAY,aAAsB,CAAC,GAAG,2BAA2B;EACnE"}
|
package/lib/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { Context } from "@deepseek-ai/cordis";
|
|
2
|
+
//#region src/index.d.ts
|
|
3
|
+
declare const name = "task-bus";
|
|
4
|
+
declare const inject: string[];
|
|
5
|
+
declare const TASK_BUS_ROUTE = "/dsh-task-bus/api";
|
|
6
|
+
declare const apply: (ctx: Context) => void;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { TASK_BUS_ROUTE, apply, inject, name };
|
package/lib/index.js
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { basename, isAbsolute, join } from "node:path";
|
|
3
|
+
import { credentialRef } from "@deepseek-ai/dsh-credentials";
|
|
4
|
+
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
5
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
6
|
+
//#region src/index.ts
|
|
7
|
+
const name = "task-bus";
|
|
8
|
+
const inject = [
|
|
9
|
+
"webServer",
|
|
10
|
+
"tools",
|
|
11
|
+
"sessionController",
|
|
12
|
+
"credentials",
|
|
13
|
+
"attachments"
|
|
14
|
+
];
|
|
15
|
+
const TASK_BUS_ROUTE = "/dsh-task-bus/api";
|
|
16
|
+
const TASK_CENTER_PATH = "/api/task-center";
|
|
17
|
+
const DATAOPS_CREDENTIAL_REF = "DATAOPS_ACCESS_TOKEN";
|
|
18
|
+
const resolveBase = () => {
|
|
19
|
+
const base = process.env.DATAOPS_AI_WORKSPACE_BACKEND_BASE_URL?.trim().replace(/\/+$/u, "");
|
|
20
|
+
if (!base) throw new Error("DataOps Task Center is not configured in this workspace");
|
|
21
|
+
return base;
|
|
22
|
+
};
|
|
23
|
+
/** Make approval links usable inside the DataOps iframe, where `/tasks` is not this origin. */
|
|
24
|
+
const absoluteApprovalLinks = (payload) => {
|
|
25
|
+
const origin = process.env.DATAOPS_PUBLIC_APP_ORIGIN?.trim().replace(/\/+$/u, "");
|
|
26
|
+
if (!origin || payload === null || typeof payload !== "object") return payload;
|
|
27
|
+
const fix = (value) => {
|
|
28
|
+
if (value === null || typeof value !== "object") return;
|
|
29
|
+
const item = value;
|
|
30
|
+
if (item.source !== void 0 && typeof item.source.deepLink === "string" && item.source.deepLink.startsWith("/")) item.source.deepLink = origin + item.source.deepLink;
|
|
31
|
+
};
|
|
32
|
+
const record = payload;
|
|
33
|
+
fix(record.item);
|
|
34
|
+
for (const item of record.items ?? []) fix(item);
|
|
35
|
+
return payload;
|
|
36
|
+
};
|
|
37
|
+
const createTaskCenterClient = (host) => {
|
|
38
|
+
const base = resolveBase();
|
|
39
|
+
return async (path, method = "GET", body) => {
|
|
40
|
+
const token = (await host.credentials.resolve(credentialRef(DATAOPS_CREDENTIAL_REF)))?.value?.trim();
|
|
41
|
+
if (!token) throw new Error("DataOps Task Center credential is not configured");
|
|
42
|
+
const response = await fetch(base + TASK_CENTER_PATH + path, {
|
|
43
|
+
method,
|
|
44
|
+
headers: {
|
|
45
|
+
"content-type": "application/json",
|
|
46
|
+
authorization: `Bearer ${token}`
|
|
47
|
+
},
|
|
48
|
+
...body === void 0 ? {} : { body: JSON.stringify(body) }
|
|
49
|
+
});
|
|
50
|
+
const text = await response.text();
|
|
51
|
+
const payload = text ? JSON.parse(text) : null;
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
const message = payload !== null && typeof payload === "object" && "message" in payload ? String(payload.message) : `Task Center request failed (${response.status})`;
|
|
54
|
+
throw new Error(message);
|
|
55
|
+
}
|
|
56
|
+
return payload;
|
|
57
|
+
};
|
|
58
|
+
};
|
|
59
|
+
const readJsonBody = async (request) => {
|
|
60
|
+
if (request.on === void 0) return void 0;
|
|
61
|
+
let body = "";
|
|
62
|
+
for await (const chunk of request) body += typeof chunk === "string" ? chunk : Buffer.from(chunk).toString("utf8");
|
|
63
|
+
return body.trim() ? JSON.parse(body) : void 0;
|
|
64
|
+
};
|
|
65
|
+
const writeJson = (response, status, body) => {
|
|
66
|
+
response.writeHead(status, { "content-type": "application/json; charset=utf-8" });
|
|
67
|
+
response.end(JSON.stringify(body));
|
|
68
|
+
};
|
|
69
|
+
const taskListPath = (search) => {
|
|
70
|
+
const params = new URLSearchParams(search);
|
|
71
|
+
if (!params.has("status")) params.set("status", "all");
|
|
72
|
+
return "/items?" + params.toString();
|
|
73
|
+
};
|
|
74
|
+
const genericListPath = (search) => {
|
|
75
|
+
const params = new URLSearchParams(search);
|
|
76
|
+
if (!params.has("scope")) params.set("scope", "mine");
|
|
77
|
+
if (!params.has("pageSize")) params.set("pageSize", "20");
|
|
78
|
+
return "/generic?" + params.toString();
|
|
79
|
+
};
|
|
80
|
+
const open = {
|
|
81
|
+
type: "object",
|
|
82
|
+
additionalProperties: true
|
|
83
|
+
};
|
|
84
|
+
const renderJson = (_args, value) => [{
|
|
85
|
+
type: "text",
|
|
86
|
+
text: JSON.stringify(value, null, 2)
|
|
87
|
+
}];
|
|
88
|
+
const tool = (name, description, parameters, execute) => defineTool({
|
|
89
|
+
name,
|
|
90
|
+
description,
|
|
91
|
+
parameters,
|
|
92
|
+
output: {
|
|
93
|
+
schema: open,
|
|
94
|
+
render: renderJson
|
|
95
|
+
},
|
|
96
|
+
execute: (async (args) => execute(args))
|
|
97
|
+
});
|
|
98
|
+
const apply = (ctx) => {
|
|
99
|
+
const host = ctx;
|
|
100
|
+
const client = createTaskCenterClient(host);
|
|
101
|
+
ctx.effect(() => host.webServer.register({
|
|
102
|
+
kind: "prefix",
|
|
103
|
+
path: TASK_BUS_ROUTE,
|
|
104
|
+
handler: async (rawRequest, rawResponse) => {
|
|
105
|
+
const request = rawRequest;
|
|
106
|
+
const response = rawResponse;
|
|
107
|
+
const url = new URL(request.url ?? "/", "http://dsh.internal");
|
|
108
|
+
const route = url.pathname.slice(17).replace(/^\/+/u, "");
|
|
109
|
+
try {
|
|
110
|
+
if (request.method === "GET" && route === "health") {
|
|
111
|
+
resolveBase();
|
|
112
|
+
writeJson(response, 200, {
|
|
113
|
+
ok: true,
|
|
114
|
+
source: "dataops_approval"
|
|
115
|
+
});
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
if (request.method === "GET" && route === "overview") {
|
|
119
|
+
writeJson(response, 200, absoluteApprovalLinks(await client("/overview")));
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
if (request.method === "GET" && route === "attachment-materialize") {
|
|
123
|
+
const attachmentId = url.searchParams.get("attachmentId") ?? "";
|
|
124
|
+
const name = url.searchParams.get("name") ?? "attachment";
|
|
125
|
+
const bytes = Number(url.searchParams.get("bytes") ?? "0");
|
|
126
|
+
if (!attachmentId.startsWith("sha256:") || !Number.isSafeInteger(bytes) || bytes <= 0) throw new Error("attachment-materialize requires a sha256 attachmentId and byte count");
|
|
127
|
+
const attachments = ctx.attachments;
|
|
128
|
+
if (attachments === void 0) throw new Error("attachment store is unavailable");
|
|
129
|
+
const chunks = [];
|
|
130
|
+
for await (const chunk of attachments.readFileStream({
|
|
131
|
+
attachmentId,
|
|
132
|
+
name,
|
|
133
|
+
bytes
|
|
134
|
+
})) chunks.push(chunk);
|
|
135
|
+
const safeName = basename(name).replace(/[^\p{L}\p{N}._-]+/gu, "_") || "attachment";
|
|
136
|
+
const requestedCwd = url.searchParams.get("cwd")?.trim() ?? "";
|
|
137
|
+
const root = requestedCwd !== "" && isAbsolute(requestedCwd) ? requestedCwd : process.cwd();
|
|
138
|
+
const directory = join(root, ".dsh-attachment-preview");
|
|
139
|
+
await mkdir(directory, { recursive: true });
|
|
140
|
+
const relativePath = ".dsh-attachment-preview/" + attachmentId.slice(7, 19) + "-" + safeName;
|
|
141
|
+
await writeFile(join(root, relativePath), Buffer.concat(chunks));
|
|
142
|
+
writeJson(response, 200, {
|
|
143
|
+
path: relativePath,
|
|
144
|
+
absolutePath: join(root, relativePath),
|
|
145
|
+
name: safeName
|
|
146
|
+
});
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
if (request.method === "GET" && route === "count") {
|
|
150
|
+
writeJson(response, 200, await client("/count"));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
if (request.method === "GET" && route === "attachment") {
|
|
154
|
+
const taskId = url.searchParams.get("taskId")?.trim() ?? "";
|
|
155
|
+
const attachmentId = url.searchParams.get("attachmentId")?.trim() ?? "";
|
|
156
|
+
if (!/^\d+$/u.test(taskId) || !/^[0-9a-fA-F-]{36}$/u.test(attachmentId)) throw new Error("attachment requires numeric taskId and UUID attachmentId");
|
|
157
|
+
const token = (await host.credentials.resolve(credentialRef(DATAOPS_CREDENTIAL_REF)))?.value?.trim();
|
|
158
|
+
if (!token) throw new Error("DataOps Task Center credential is not configured");
|
|
159
|
+
const upstream = await fetch(resolveBase() + "/api/tasks/" + encodeURIComponent(taskId) + "/attachments/" + encodeURIComponent(attachmentId) + "/download", { headers: { authorization: `Bearer ${token}` } });
|
|
160
|
+
if (!upstream.ok) {
|
|
161
|
+
const text = await upstream.text();
|
|
162
|
+
throw new Error(text || `Attachment download failed (${upstream.status})`);
|
|
163
|
+
}
|
|
164
|
+
const buffer = Buffer.from(await upstream.arrayBuffer());
|
|
165
|
+
response.writeHead(upstream.status, {
|
|
166
|
+
"content-type": upstream.headers.get("content-type") ?? "application/octet-stream",
|
|
167
|
+
"content-disposition": upstream.headers.get("content-disposition") ?? "attachment",
|
|
168
|
+
"content-length": String(buffer.length),
|
|
169
|
+
"cache-control": "private, no-store"
|
|
170
|
+
});
|
|
171
|
+
response.end(buffer);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (request.method === "GET" && route === "items") {
|
|
175
|
+
writeJson(response, 200, absoluteApprovalLinks(await client(taskListPath(url.search))));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (request.method === "GET" && route === "generic") {
|
|
179
|
+
writeJson(response, 200, await client(genericListPath(url.search)));
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (request.method === "POST" && route === "ai-accept") {
|
|
183
|
+
const body = await readJsonBody(request);
|
|
184
|
+
const id = typeof body?.id === "string" ? body.id : "";
|
|
185
|
+
if (!id) throw new Error("ai-accept requires a task id");
|
|
186
|
+
const task = id.startsWith("approval:") ? await client(`/items/${encodeURIComponent(id)}`) : id.startsWith("task:") ? await client(`/generic/${encodeURIComponent(id.slice(5))}`) : null;
|
|
187
|
+
if (task === null) throw new Error("ai-accept requires an approval:<id> or task:<uuid> id");
|
|
188
|
+
const card = task.item;
|
|
189
|
+
const title = card?.title ?? id;
|
|
190
|
+
const sessionId = `task-bus-${crypto.randomUUID()}`;
|
|
191
|
+
await host.sessionController.create({
|
|
192
|
+
sessionId,
|
|
193
|
+
...process.env.DATAOPS_AI_WORKSPACE_MOUNT_PATH === void 0 ? {} : { cwd: process.env.DATAOPS_AI_WORKSPACE_MOUNT_PATH }
|
|
194
|
+
});
|
|
195
|
+
const resolved = await host.sessionController.resolveAgent(sessionId);
|
|
196
|
+
const agent = "agent" in resolved ? resolved.agent : void 0;
|
|
197
|
+
if (agent === void 0) throw new Error("无法为任务绑定 AI 会话");
|
|
198
|
+
const prompt = String(body?.prompt ?? "") || `这是 DataOps 任务「${title}」(${id}),来源:${card?.source?.label ?? card?.sourceType ?? "Task Center"}。请先阅读任务上下文,整理材料并给出办理建议;不要替审批人通过、驳回或提交审批。`;
|
|
199
|
+
agent.followup(createUserMessage({
|
|
200
|
+
source: {
|
|
201
|
+
kind: "task-bus",
|
|
202
|
+
taskId: id
|
|
203
|
+
},
|
|
204
|
+
content: [{
|
|
205
|
+
type: "text",
|
|
206
|
+
text: prompt
|
|
207
|
+
}]
|
|
208
|
+
}));
|
|
209
|
+
writeJson(response, 201, {
|
|
210
|
+
sessionId,
|
|
211
|
+
taskId: id
|
|
212
|
+
});
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
const transition = /^generic\/([^/]+)\/transition$/u.exec(route);
|
|
216
|
+
if (request.method === "POST" && transition !== null) {
|
|
217
|
+
writeJson(response, 200, await client(`/generic/${encodeURIComponent(transition[1])}/transition`, "POST", await readJsonBody(request)));
|
|
218
|
+
return;
|
|
219
|
+
}
|
|
220
|
+
if (request.method === "POST" && route === "generic") {
|
|
221
|
+
writeJson(response, 201, await client("/generic", "POST", await readJsonBody(request)));
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const detail = /^generic\/([^/]+)$/u.exec(route);
|
|
225
|
+
if (request.method === "GET" && detail !== null) {
|
|
226
|
+
writeJson(response, 200, await client(`/generic/${encodeURIComponent(detail[1])}`));
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
const approval = /^items\/([^/]+)$/u.exec(route);
|
|
230
|
+
if (request.method === "GET" && approval !== null) {
|
|
231
|
+
writeJson(response, 200, absoluteApprovalLinks(await client(`/items/${encodeURIComponent(decodeURIComponent(approval[1]))}`)));
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
writeJson(response, 404, { error: "Unknown Task Bus route" });
|
|
235
|
+
} catch (error) {
|
|
236
|
+
writeJson(response, 502, { error: error instanceof Error ? error.message : String(error) });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}), "task-bus: browser API");
|
|
240
|
+
ctx.effect(() => host.tools.register(tool("task_center_list", "List DataOps Task Center cards visible to the current workspace user, including approval tasks and generic Task Bus tasks. Approval cards are read-only projections of DataOps tasks.", {
|
|
241
|
+
status: {
|
|
242
|
+
type: "string",
|
|
243
|
+
enum: [
|
|
244
|
+
"all",
|
|
245
|
+
"pending",
|
|
246
|
+
"done"
|
|
247
|
+
]
|
|
248
|
+
},
|
|
249
|
+
scope: {
|
|
250
|
+
type: "string",
|
|
251
|
+
enum: [
|
|
252
|
+
"mine",
|
|
253
|
+
"created",
|
|
254
|
+
"all"
|
|
255
|
+
]
|
|
256
|
+
},
|
|
257
|
+
limit: { type: "number" }
|
|
258
|
+
}, async (args) => {
|
|
259
|
+
const status = typeof args.status === "string" ? args.status : "all";
|
|
260
|
+
typeof args.scope === "string" && args.scope;
|
|
261
|
+
const limit = typeof args.limit === "number" ? Math.max(1, Math.min(50, args.limit)) : 20;
|
|
262
|
+
const result = absoluteApprovalLinks(await client("/overview"));
|
|
263
|
+
const items = (result.items ?? []).filter((item) => {
|
|
264
|
+
if (status === "pending") return item.status !== "completed" && item.status !== "cancelled";
|
|
265
|
+
if (status === "done") return item.status === "completed" || item.status === "cancelled";
|
|
266
|
+
return true;
|
|
267
|
+
}).slice(0, limit);
|
|
268
|
+
return {
|
|
269
|
+
...result,
|
|
270
|
+
items,
|
|
271
|
+
total: items.length
|
|
272
|
+
};
|
|
273
|
+
})), "task-bus: list tool");
|
|
274
|
+
ctx.effect(() => host.tools.register(tool("task_center_get", "Read one Task Center card by its stable id. Use approval:<id> for a DataOps approval task or task:<uuid> for a generic Task Bus task.", { id: {
|
|
275
|
+
type: "string",
|
|
276
|
+
required: true
|
|
277
|
+
} }, async (args) => {
|
|
278
|
+
const id = String(args.id ?? "");
|
|
279
|
+
if (id.startsWith("approval:")) return client(`/items/${encodeURIComponent(id)}`);
|
|
280
|
+
if (id.startsWith("task:")) return client(`/generic/${encodeURIComponent(id.slice(5))}`);
|
|
281
|
+
throw new Error("Task Center id must start with approval: or task:");
|
|
282
|
+
})), "task-bus: get tool");
|
|
283
|
+
ctx.effect(() => host.tools.register(tool("task_center_create", [
|
|
284
|
+
"Create a generic Task Center card ONLY when the user explicitly asks you to create a task/card in this turn.",
|
|
285
|
+
"Never call this tool proactively, speculatively, as a side effect of another request, or merely because work could be tracked.",
|
|
286
|
+
"If the user did not explicitly ask for a new card, do not call it; ask for confirmation instead.",
|
|
287
|
+
"This never creates or mutates a DataOps approval task."
|
|
288
|
+
].join(" "), {
|
|
289
|
+
title: {
|
|
290
|
+
type: "string",
|
|
291
|
+
required: true
|
|
292
|
+
},
|
|
293
|
+
description: { type: "string" },
|
|
294
|
+
source_type: { type: "string" },
|
|
295
|
+
source_id: { type: "string" },
|
|
296
|
+
assignee_user_id: { type: "string" },
|
|
297
|
+
priority: { type: "string" },
|
|
298
|
+
user_requested: {
|
|
299
|
+
type: "boolean",
|
|
300
|
+
required: true,
|
|
301
|
+
description: "Must be true only when the user explicitly requested creation of this card in the current turn."
|
|
302
|
+
}
|
|
303
|
+
}, async (args) => {
|
|
304
|
+
if (args.user_requested !== true) throw new Error("task_center_create requires an explicit user request; do not create Task Center cards proactively");
|
|
305
|
+
return client("/generic", "POST", {
|
|
306
|
+
title: String(args.title ?? ""),
|
|
307
|
+
description: typeof args.description === "string" ? args.description : void 0,
|
|
308
|
+
sourceType: typeof args.source_type === "string" ? args.source_type : "agent",
|
|
309
|
+
sourceId: typeof args.source_id === "string" ? args.source_id : void 0,
|
|
310
|
+
assigneeUserId: typeof args.assignee_user_id === "string" ? args.assignee_user_id : void 0,
|
|
311
|
+
priority: typeof args.priority === "string" ? args.priority : void 0
|
|
312
|
+
});
|
|
313
|
+
})), "task-bus: create tool");
|
|
314
|
+
ctx.effect(() => host.tools.register(tool("task_center_transition", "Advance a generic Task Bus task. Approval tasks must be handled through the existing DataOps approval UI and APIs; this tool refuses approval ids.", {
|
|
315
|
+
id: {
|
|
316
|
+
type: "string",
|
|
317
|
+
required: true
|
|
318
|
+
},
|
|
319
|
+
action: {
|
|
320
|
+
type: "string",
|
|
321
|
+
required: true,
|
|
322
|
+
enum: [
|
|
323
|
+
"accept",
|
|
324
|
+
"start",
|
|
325
|
+
"submit",
|
|
326
|
+
"complete",
|
|
327
|
+
"cancel",
|
|
328
|
+
"block",
|
|
329
|
+
"unblock"
|
|
330
|
+
]
|
|
331
|
+
},
|
|
332
|
+
comment: { type: "string" }
|
|
333
|
+
}, async (args) => {
|
|
334
|
+
const id = String(args.id ?? "");
|
|
335
|
+
if (!id.startsWith("task:")) throw new Error("Only generic task:<uuid> cards can be transitioned here");
|
|
336
|
+
return client(`/generic/${encodeURIComponent(id.slice(5))}/transition`, "POST", {
|
|
337
|
+
action: String(args.action ?? ""),
|
|
338
|
+
comment: typeof args.comment === "string" ? args.comment : void 0
|
|
339
|
+
});
|
|
340
|
+
})), "task-bus: transition tool");
|
|
341
|
+
};
|
|
342
|
+
//#endregion
|
|
343
|
+
export { TASK_BUS_ROUTE, apply, inject, name };
|