@agentaily/design-system 0.6.0 → 0.7.1

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/CHANGELOG.md ADDED
@@ -0,0 +1,81 @@
1
+ # @agentaily/design-system
2
+
3
+ ## 0.7.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#18](https://github.com/agentaily/design-system/pull/18) [`9365141`](https://github.com/agentaily/design-system/commit/9365141e53688f67a1fa9da40e3d4923a05c1ef6) Thanks [@yarnovo](https://github.com/yarnovo)! - Ship `CHANGELOG.md` inside the npm package (added to `files`), so downstreams can read the changelog from `node_modules/@agentaily/design-system/CHANGELOG.md` instead of only on GitHub.
8
+
9
+ ## 0.7.0
10
+
11
+ ### Minor Changes
12
+
13
+ - [#16](https://github.com/agentaily/design-system/pull/16) [`6fbf758`](https://github.com/agentaily/design-system/commit/6fbf758040deb10465cb2a736ecf752089c7bd35) Thanks [@yarnovo](https://github.com/yarnovo)! - feat(chat): 新增 `<Markdown>` 原语 + `<Message>` 正文支持 markdown 渲染
14
+
15
+ 新增可组合的 `<Markdown>` 原语(`components/chat/Markdown`),把模型输出的 markdown 字符串渲染成排版好的、双主题的 React 节点:段落 + 软换行、加粗、斜体、删除线、行内代码、围栏代码块(复用 `CodeBlock`)、有序/无序/嵌套/任务列表、引用块(可嵌套)、GFM 表格(列对齐 + 溢出横向滚动)、水平线、`#`/`##`/`###` 标题、`[链接](url)` + 裸 URL 自动链接;图片 `![]()` 渲染为惰性占位 chip(不抓取)。
16
+
17
+ `<Message>` 正文现在接受 markdown 字符串 —— 新增 `markdown` prop,或直接把字符串作为 children —— 经 `<Markdown>` 渲染;传 React-node children 仍原样渲染,**完全向后兼容**。
18
+
19
+ 安全:解析成 React 元素树后发出,不用 `dangerouslySetInnerHTML`(所有文本自动转义),链接 href 做协议净化(丢弃 `javascript:` / `data:` / `vbscript:`)。流式容错:半截表格 / 半截引用 / 未闭合标记与围栏降级为字面或部分渲染,流式中途不抛错。
20
+
21
+ Net +1 component → 115 barrel modules / 148 exports。
22
+
23
+ ## 0.6.0
24
+
25
+ ### Minor Changes
26
+
27
+ - [#11](https://github.com/agentaily/design-system/pull/11) [`ab5bd2d`](https://github.com/agentaily/design-system/commit/ab5bd2dca4f7d42e81c81c007721b1381b208fa0) Thanks [@yarnovo](https://github.com/yarnovo)! - Replace the `IntegrationSettings` modal with two pure-display connection cards, and turn the BrandMark cursor off by default. Second round aligned with downstream `form-design` after the 0.5.0 backend-seam round — the seam approach was the wrong shape; state belongs to the caller, not the component.
28
+
29
+ - **BREAKING — `IntegrationSettings` removed.** The all-in-one modal welded localStorage persistence + a Save bar + readiness gating onto the two cards; downstream hit hard walls (an empty `catch` swallowing `onSave` rejects, no backend-error slot, Save blocked by `allReady` so the "save → backend validates → 400" path was unreachable, and a built-in `localStorage` nobody downstream uses). It is gone, along with its internal `localStorage`. Consumers move to the two cards below and own their own config / persistence / Save / gating. The only current downstream (`form-design`) is switching to the cards, so this breaks no one in practice; on `0.x` it ships as a **minor**.
30
+ - **New `settings/DeepSeekCard`** (LLM key) **+ `settings/FeishuCard`** (Feishu Bitable data sink) — **pure-display** connection cards: props in (per-field `value` / `onChange`, `status`, `result`, `masked`, field `error`s, `help`…), events out (`onTest`). **Zero state, zero localStorage, zero save bar, zero readiness gating** — the caller owns the config object, persistence, the Save button, backend-error display, and any "both connected?" gate (the same headless philosophy as `Form.useForm` / `Queue.useQueue`). Each composes `SecretField` / `StatusPill` / `TestRow` / `HelpSteps`; the masked-secret echo is derived from props (`masked && !value`) and is never re-submitted. Both ship `.d.ts` + `.prompt.md` (a controlled + backend-wired example) and Storybook stories, plus a `ConnectionCards` reference story where the caller owns the readiness rail + Save bar.
31
+ - **`BrandMark` `cursor` now defaults to `false`.** The block cursor (a brand liveness motif) is off by default so every layout shell (`DesignerShell` / `SignInPage` / `AppShell` / `SettingsPage` / `DocsLayout` …) reads clean without each shell passing a prop. Opt back in with `<BrandMark wordmark cursor />`. Behavioral default change, visual-only.
32
+
33
+ Now 114 components.
34
+
35
+ ## 0.5.0
36
+
37
+ ### Minor Changes
38
+
39
+ - [#9](https://github.com/agentaily/design-system/pull/9) [`64629e7`](https://github.com/agentaily/design-system/commit/64629e7a1a45221b80ade3e9eaa58e7f711e2f11) Thanks [@yarnovo](https://github.com/yarnovo)! - Add backward-compatible backend seams to `IntegrationSettings` and `SignInPage` (contracts requested by downstream consumers — pass nothing and behavior is unchanged).
40
+
41
+ - **`IntegrationSettings` — controlled / backend-wired (BYOK) seam.** New optional props let a caller own the config and reach a real backend instead of the built-in localStorage: `value` + `onChange` (controlled config; when `value` is passed the modal stops touching localStorage), `onSave(value)` → `Promise` (Save disables + spins while pending, marks saved on resolve, stays dirty on reject), `onTest(which)` → `Promise<{ ok, message }>` (drives the per-card `StatusPill` + `TestRow`; falls back to the built-in mock probe when omitted), `readiness` (external `{ deepseek, feishu }` override for the 0/2 rail, Save gating, and the green pills), and `masked` (stored secrets echo as a masked placeholder with an empty value and are never re-submitted; typing overrides the mask). Omit all of them and the component self-persists to localStorage exactly as before.
42
+ - **`SignInPage` — backend-error + submit-busy seam.** New optional props: `error` (ReactNode shown in a danger banner directly above the submit button for server failures like 409/401/400; caller-owned, cleared by you) and `submitting` (disables the submit button, shows a spinner, and blocks double-submit while an async `onSubmit` is in flight). Client-side validation is unchanged; the component still owns and clears its own field errors on input/mode change.
43
+
44
+ Both ship updated `.d.ts` + `.prompt.md` (controlled + async wiring examples) and new Storybook stories.
45
+
46
+ ## 0.4.0
47
+
48
+ ### Minor Changes
49
+
50
+ - [#7](https://github.com/agentaily/design-system/pull/7) [`bb7af40`](https://github.com/agentaily/design-system/commit/bb7af407f960a3960cb8bf062eb6b51c07cc5353) Thanks [@yarnovo](https://github.com/yarnovo)! - Add the `RotatingTagline` brand component and align `SignInPage` to use it.
51
+
52
+ - **New `utilities/RotatingTagline`** — the animated brand headline: a fixed `prefix` then `phrases[]` that type in, hold, delete, and advance; the rotating phrase wears the flowing geek-rainbow gradient with a trailing block cursor that blinks only at rest. Props: `prefix` / `phrases` / `gradient` / `cursor` / `breakAfterPrefix` / `typeSpeed` / `deleteSpeed` / `hold` / `flowDuration` / `className`. Self-handles `prefers-reduced-motion` (whole-phrase swap, no flow). Now 113 components.
53
+ - **`SignInPage`** — the brand-panel tagline is now a `RotatingTagline` (new `tagline` prop passes through to it). All user-facing strings moved into a layered, deep-mergeable `copy` prop (signin/signup + labels + placeholders + errors + terms) with an English baseline. Desktop tagline enlarged (`clamp(40px, 4.4vw, --text-hero)`, nowrap); mobile reworked — brand panel collapses, compact in-card logo + tagline, `SIGN IN` demoted to a mono label, left-aligned with roomier spacing.
54
+
55
+ ## 0.3.0
56
+
57
+ ### Minor Changes
58
+
59
+ - [#4](https://github.com/agentaily/design-system/pull/4) [`636a97c`](https://github.com/agentaily/design-system/commit/636a97c145481663f335b261edf0e9b818c4565d) Thanks [@yarnovo](https://github.com/yarnovo)! - Sync the latest Claude Design handoff (`6Ovub8OJ`): light-first theme flip, 16 new components, and a `Queue.useQueue` hook.
60
+
61
+ - **BREAKING (visual): default theme flipped dark → light.** `:root` is now the `paper` (light) scale; the `ink` (dark) scale moved to `[data-theme="dark"]`. Consumers who relied on the dark default must add `data-theme="dark"` to opt back in. Shadow tokens and per-component theme overrides flipped to match.
62
+ - **New layout tokens** `--topbar-h` (52px) and `--bar-h` (48px) so app/pane bars align.
63
+ - **16 new components across 3 new categories + existing ones** (112 total): `auth/` (`AuthDialog` + `AuthDialog.useAuth`, `AccountControl`, `SignInPage`), `settings/` (`IntegrationSettings`, `SecretField`, `StatusPill`, `TestRow`, `HelpSteps`), `review/` (`MarkupLayer`), plus `layout/` full-page shells (`AppShell`, `DesignerShell`, `DocsLayout`, `SettingsPage`), `chat/ConversationThread`, and `utilities/` (`Icon`, `BrandMark`).
64
+ - **`Queue.useQueue`** — headless keep-sending-while-busy buffer, exposed as a static on `Queue` (pairs with `ConversationThread`).
65
+
66
+ ## 0.2.0
67
+
68
+ ### Minor Changes
69
+
70
+ - [#2](https://github.com/agentaily/design-system/pull/2) [`55bc378`](https://github.com/agentaily/design-system/commit/55bc378cfb13f060c46e50a95a78b91856f21889) Thanks [@yarnovo](https://github.com/yarnovo)! - Add the production-grade form layer (synced from Claude Design `RbM7-_aESQSDeDy1U-T1NQ`).
71
+
72
+ - **New `Form` / `FormActions`** layout primitives (`gap`, `align`, `bordered`, `full`) — pure structure, no state.
73
+ - **New `Form.useForm`** — optional, zero-dependency controlled hook with an API aligned to react-hook-form: validation modes (`onSubmit/onBlur/onChange/onTouched/all` + `reValidateMode`), per-field rules (`required/minLength/maxLength/min/max/pattern/validate`, incl. async), schema-style `validate(values)`, imperative `trigger/setError/clearErrors/setValue/getValues/watch/reset`, dual-signature `handleSubmit`, and full `formState`. Presentational controls never depend on it — swap in RHF/TanStack and they still work.
74
+ - **New `Form.useFieldArray`** — dynamic lists (`append/prepend/remove/insert/move/replace/update`) with stable per-row `id`s.
75
+ - **`Input`** gains a `required` prop (red asterisk on the label + native `required`); the error state now uses an `outline` ring so it survives overlay/instrumentation layers and never shifts layout.
76
+
77
+ ## 0.1.1
78
+
79
+ ### Patch Changes
80
+
81
+ - [`1af5ce3`](https://github.com/agentaily/design-system/commit/1af5ce3d0bb60503b666bbdc054124bf9391b91b) Thanks [@yarnbcoder-lgtm](https://github.com/yarnbcoder-lgtm)! - Verify the automated release pipeline (Changesets + OIDC trusted publishing). No functional changes — component code is identical to 0.1.0.
package/DESIGN.md CHANGED
@@ -93,7 +93,7 @@ Examples — ✓ "Rate limited. Retry in 18s." ✗ "Oops! Something went wrong
93
93
  | `components/feedback/` | Spinner, Toast, Tooltip, Dialog, Alert |
94
94
  | `components/overlay/` | Popover, DropdownMenu, Command, Sheet, **HoverCard, ContextMenu, Menubar, NavigationMenu, AlertDialog** |
95
95
  | `components/layout/` | **AspectRatio, ScrollArea, Resizable, Sidebar, AppShell, DesignerShell, DocsLayout, SettingsPage** — incl. full-page shells/frames |
96
- | `components/chat/` | Message, Composer, CodeBlock, **ConversationThread** |
96
+ | `components/chat/` | Message, Composer, CodeBlock, **ConversationThread, Markdown** |
97
97
  | `components/ai/` | Reasoning, ToolCall, Sources + Citation, Suggestion/Suggestions, ModelSelector, Attachments, Shimmer, **Conversation, Task, Plan, Context, Confirmation, Checkpoint, Queue** |
98
98
  | `components/code/` | **Terminal, FileTree, Snippet, StackTrace, TestResults, Artifact, WebPreview, Agent, Commit, EnvironmentVariables, PackageInfo, Sandbox, SchemaDisplay, JSXPreview** |
99
99
  | `components/voice/` | **AudioPlayer, MicSelector, VoiceSelector, SpeechInput, Transcription, Persona** |
@@ -108,7 +108,7 @@ Examples — ✓ "Rate limited. Retry in 18s." ✗ "Oops! Something went wrong
108
108
  | `ui_kits/docs/` | Documentation site (interactive) |
109
109
  | `SKILL.md` | Agent-skill entry point |
110
110
 
111
- Every component ships `<Name>.jsx` + `<Name>.d.ts` (props) + `<Name>.prompt.md` (usage) — **147 component exports** across the primitive categories (buttons, inputs, display, feedback, overlay, layout, chat, ai, code, voice, workflow, utilities) plus product-domain layers (**settings, auth, review**). Full-page frames — `AppShell` / `DesignerShell` / `DocsLayout` / `SettingsPage` (layout), `ConversationThread` (chat), `SignInPage` (auth) — are **live components, not copy-templates**: change one, every consuming project benefits on re-sync. Consume via the compiled bundle: `window.AxiomDesignSystem_7fc962`. Read each component's `.prompt.md` for copy-paste usage.
111
+ Every component ships `<Name>.jsx` + `<Name>.d.ts` (props) + `<Name>.prompt.md` (usage) — **148 component exports** across the primitive categories (buttons, inputs, display, feedback, overlay, layout, chat, ai, code, voice, workflow, utilities) plus product-domain layers (**settings, auth, review**). Full-page frames — `AppShell` / `DesignerShell` / `DocsLayout` / `SettingsPage` (layout), `ConversationThread` (chat), `SignInPage` (auth) — are **live components, not copy-templates**: change one, every consuming project benefits on re-sync. Consume via the compiled bundle: `window.AxiomDesignSystem_7fc962`. Read each component's `.prompt.md` for copy-paste usage.
112
112
 
113
113
  **Forms are layered, not monolithic.** Presentational controls (Input/Select/Field…) own layout and never depend on a form engine. `Form` + `FormActions` add pure structure. `Form.useForm` is an **optional**, zero-dependency orchestration hook (values/errors/touched/validate/submit) exposed off the capitalized `Form` export — drop it for react-hook-form or TanStack and the controls still work. Errors surface only after blur or submit; spread `form.field(name)` onto any value control, or `form.field(name, {type:"checkbox"})` for boolean ones.
114
114
 
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # agentaily design system
2
2
 
3
- Agentaily(AI chatbot)设计系统:114 个 React 组件 + Storybook,单色亮色优先(暗色可切)。品牌一句话:**极客风格,简约,大气,科技感**。
3
+ Agentaily(AI chatbot)设计系统:115 个 React 组件 + Storybook,单色亮色优先(暗色可切)。品牌一句话:**极客风格,简约,大气,科技感**。
4
4
 
5
5
  📖 **在线 Storybook:** https://agentaily.github.io/design-system/
6
6
 
@@ -28,7 +28,7 @@ npm run build:lib # 产出 dist/:每组件一个 .js + index.d.ts + styles.css
28
28
 
29
29
  | 路径 | 内容 |
30
30
  | ----------------------------------------------- | ----------------------------------------- |
31
- | `dist/index.js` | ESM 入口,re-export 全部 114 个组件符号 |
31
+ | `dist/index.js` | ESM 入口,re-export 全部 115 个组件符号 |
32
32
  | `dist/components/**/*.js` | 每个组件独立模块(含运行时 CSS 注入) |
33
33
  | `dist/index.d.ts` + `dist/components/**/*.d.ts` | TypeScript 类型契约 |
34
34
  | `dist/styles.css` | 内联好的 tokens + 字体,消费方 import 一次 |
@@ -74,8 +74,11 @@ src/components/ 15 个类别的组件源码 + stories(含 auth / settings /
74
74
  <cat>/<Name>.d.ts props 契约
75
75
  <cat>/<Name>.prompt.md 用法说明
76
76
  <cat>/<Name>.stories.jsx
77
+ tests/ 逻辑原语的单测(vitest;镜像组件之外,design-sync 不覆盖)— 见 TESTING.md
77
78
  ```
78
79
 
80
+ 测试约定(分层 / 选型 / 护栏)见 [TESTING.md](./TESTING.md):多数组件是展示型镜像,无逻辑可测;`vitest` 单测只覆盖**有逻辑的原语**(解析 / 净化 / 有状态 hook,目前是 `<Markdown>`)。`npm test` 跑单测,`npm run test:watch` 本地内环。
81
+
79
82
  ## 约定
80
83
 
81
84
  - 亮色 paper 是默认主题(`:root`);暗色 ink 走 `[data-theme="dark"]`,用 Storybook 工具栏切换。
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Renders a model's markdown STRING into typeset, theme-aware React nodes.
3
+ *
4
+ * Composable primitive for any surface that shows model output. `<Message>`
5
+ * uses it internally to render assistant prose, but it stands alone too.
6
+ *
7
+ * Coverage: paragraphs + soft line breaks, **bold**, *italic*, ~~strikethrough~~,
8
+ * `inline code`, fenced ```code blocks``` (rendered via <CodeBlock>), unordered +
9
+ * ordered + nested (mixed) lists, GitHub task lists (- [ ] / - [x]), blockquotes
10
+ * (nested), GFM tables with per-column alignment (overflow scrolls), horizontal
11
+ * rules (--- / *** / ___), #/##/### headings, [links](url), and bare-URL autolinks.
12
+ * Images ![alt](url) render as an inert placeholder chip — never fetched.
13
+ *
14
+ * Safety: the string is parsed to a node tree and emitted as React elements
15
+ * only — never dangerouslySetInnerHTML — so all literal text is auto-escaped
16
+ * and there is no HTML-injection surface. Link hrefs are scheme-sanitized
17
+ * (javascript:/data:/vbscript: are dropped); images are not loaded.
18
+ *
19
+ * Streaming: unterminated inline delimiters (** ` _ ~~ [..]( ) degrade to literal
20
+ * text; a half table, a half blockquote, and an unclosed ``` fence render their
21
+ * partial content, so a half-streamed token never throws or vanishes mid-stream.
22
+ */
23
+ export interface MarkdownProps {
24
+ /** The markdown source string. Takes precedence over a string `children`. */
25
+ content?: string;
26
+ /** Alternative to `content`: a markdown string passed as children. */
27
+ children?: string;
28
+ className?: string;
29
+ }
30
+ export declare function Markdown(props: MarkdownProps): JSX.Element;
@@ -0,0 +1,502 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import React from "react";
3
+ import { CodeBlock } from "./CodeBlock.js";
4
+ const AX_MD_CSS = `
5
+ .ax-md { font-size: var(--text-md); line-height: var(--leading-body); color: var(--text-body); min-width: 0; }
6
+ .ax-md > :first-child { margin-top: 0; }
7
+ .ax-md > :last-child { margin-bottom: 0; }
8
+
9
+ .ax-md__p { margin: 0 0 0.7em; text-wrap: pretty; }
10
+
11
+ .ax-md__ul, .ax-md__ol { margin: 0 0 0.7em; padding-left: 1.5em; }
12
+ .ax-md__li { margin: 0.2em 0; }
13
+ .ax-md__li > .ax-md__ul, .ax-md__li > .ax-md__ol { margin: 0.25em 0 0; padding-left: 1.3em; }
14
+
15
+ .ax-md__ul { list-style: none; padding-left: 1.35em; }
16
+ .ax-md__ul > .ax-md__li { position: relative; }
17
+ .ax-md__ul > .ax-md__li::before {
18
+ content: ""; position: absolute; left: -1.05em; top: 0.62em;
19
+ width: 4px; height: 4px; background: var(--text-faint);
20
+ }
21
+
22
+ .ax-md__ol { list-style: decimal; padding-left: 1.7em; }
23
+ .ax-md__ol > .ax-md__li::marker {
24
+ font-family: var(--font-mono); font-size: 0.9em; color: var(--text-faint);
25
+ }
26
+
27
+ .ax-md__li--task { list-style: none; display: flex; align-items: flex-start; gap: 8px; }
28
+ .ax-md__ul > .ax-md__li--task::before { display: none; }
29
+ .ax-md__check {
30
+ appearance: none; -webkit-appearance: none; margin: 3px 0 0; flex: none;
31
+ width: 15px; height: 15px; border: 1px solid var(--border-strong);
32
+ border-radius: var(--radius-1); background: var(--bg-0); position: relative; cursor: default;
33
+ }
34
+ .ax-md__check:checked { background: var(--accent); border-color: var(--accent); }
35
+ .ax-md__check:checked::after {
36
+ content: ""; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px;
37
+ border: solid var(--accent-fg); border-width: 0 1.6px 1.6px 0; transform: rotate(45deg);
38
+ }
39
+ .ax-md__li--task > .ax-md__taskbody { flex: 1; min-width: 0; }
40
+
41
+ .ax-md__strong { font-weight: var(--weight-bold); color: var(--text-body); }
42
+ .ax-md__em { font-style: italic; }
43
+ .ax-md__del { text-decoration: line-through; text-decoration-thickness: 1px; color: var(--text-muted); }
44
+
45
+ .ax-md__code {
46
+ font-family: var(--font-mono); font-size: 0.86em;
47
+ background: var(--bg-3); color: var(--text-body);
48
+ border: 1px solid var(--border-default);
49
+ border-radius: var(--radius-1); padding: 0.12em 0.36em;
50
+ white-space: break-spaces; word-break: break-word;
51
+ }
52
+ [data-theme="dark"] .ax-md__code { background: var(--bg-2); }
53
+
54
+ .ax-md > .ax-code, .ax-md__li > .ax-code, .ax-md__quote > .ax-code { margin: 0 0 0.7em; }
55
+
56
+ .ax-md__link {
57
+ color: var(--text-body); text-decoration: underline;
58
+ text-underline-offset: 2px; text-decoration-color: var(--border-strong);
59
+ transition: text-decoration-color var(--dur-1) var(--ease-out);
60
+ word-break: break-word;
61
+ }
62
+ .ax-md__link:hover { text-decoration-color: var(--text-body); }
63
+
64
+ .ax-md__img {
65
+ display: inline-flex; align-items: center; gap: 6px; vertical-align: baseline;
66
+ padding: 1px 8px; border: 1px dashed var(--border-strong); border-radius: var(--radius-1);
67
+ font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-faint);
68
+ }
69
+ .ax-md__img b { font-weight: var(--weight-medium); letter-spacing: var(--tracking-label); }
70
+
71
+ .ax-md__quote {
72
+ margin: 0 0 0.7em; padding: 2px 0 2px 14px;
73
+ border-left: 2px solid var(--border-strong); color: var(--text-muted);
74
+ }
75
+ .ax-md__quote > :last-child { margin-bottom: 0; }
76
+ .ax-md__quote .ax-md__quote { margin-top: 0.5em; }
77
+
78
+ .ax-md__hr { border: none; border-top: 1px solid var(--border-default); margin: 1.2em 0; }
79
+
80
+ .ax-md__tablewrap {
81
+ margin: 0 0 0.7em; overflow-x: auto;
82
+ border: 1px solid var(--border-default); border-radius: var(--radius-2);
83
+ }
84
+ .ax-md__table { border-collapse: collapse; width: 100%; font-size: var(--text-sm); }
85
+ .ax-md__table th, .ax-md__table td {
86
+ padding: 7px 12px; text-align: left; vertical-align: top;
87
+ border-bottom: 1px solid var(--border-default); border-right: 1px solid var(--border-default);
88
+ }
89
+ .ax-md__table th:last-child, .ax-md__table td:last-child { border-right: none; }
90
+ .ax-md__table tbody tr:last-child td { border-bottom: none; }
91
+ .ax-md__table thead th {
92
+ background: var(--surface-card); color: var(--text-body);
93
+ font-weight: var(--weight-medium); white-space: nowrap;
94
+ }
95
+
96
+ .ax-md__h { font-family: var(--font-display); font-weight: var(--weight-medium);
97
+ line-height: var(--leading-snug); letter-spacing: var(--tracking-tight);
98
+ margin: 1.1em 0 0.5em; color: var(--text-body); }
99
+ .ax-md__h--1 { font-size: var(--text-xl); }
100
+ .ax-md__h--2 { font-size: var(--text-lg); }
101
+ .ax-md__h--3 { font-size: var(--text-md); }
102
+ `;
103
+ if (typeof document !== "undefined" && !document.getElementById("ax-md-css")) {
104
+ const s = document.createElement("style");
105
+ s.id = "ax-md-css";
106
+ s.textContent = AX_MD_CSS;
107
+ document.head.appendChild(s);
108
+ }
109
+ function sanitizeUrl(raw) {
110
+ const url = String(raw || "").replace(/[\u0000-\u001F\u007F]/g, "").trim();
111
+ if (!url) return null;
112
+ if (/^[a-z][a-z0-9+.-]*:/i.test(url)) {
113
+ if (/^(https?|mailto|tel):/i.test(url)) return url;
114
+ return null;
115
+ }
116
+ return url;
117
+ }
118
+ function parseInline(text, keyBase) {
119
+ const out = [];
120
+ let buf = "";
121
+ let i = 0;
122
+ let k = 0;
123
+ const len = text.length;
124
+ const flush = () => {
125
+ if (buf) {
126
+ out.push(buf);
127
+ buf = "";
128
+ }
129
+ };
130
+ while (i < len) {
131
+ const ch = text[i];
132
+ const rest = text.slice(i);
133
+ if (ch === "\n") {
134
+ flush();
135
+ out.push(/* @__PURE__ */ jsx("br", {}, keyBase + "-br" + k++));
136
+ i++;
137
+ continue;
138
+ }
139
+ if (ch === "\\" && i + 1 < len && /[\\`*_{}\[\]()#+\-.!~|>]/.test(text[i + 1])) {
140
+ buf += text[i + 1];
141
+ i += 2;
142
+ continue;
143
+ }
144
+ let m;
145
+ if (ch === "`" && (m = /^`([^`\n]+?)`/.exec(rest))) {
146
+ flush();
147
+ out.push(
148
+ /* @__PURE__ */ jsx("code", { className: "ax-md__code", children: m[1] }, keyBase + "-c" + k++)
149
+ );
150
+ i += m[0].length;
151
+ continue;
152
+ }
153
+ if (ch === "!" && text[i + 1] === "[" && (m = /^!\[([^\]]*)\]\(([^)\s]*)\)/.exec(rest))) {
154
+ flush();
155
+ out.push(
156
+ /* @__PURE__ */ jsxs("span", { className: "ax-md__img", title: m[2], children: [
157
+ /* @__PURE__ */ jsx("b", { children: "IMG" }),
158
+ m[1] ? " · " + m[1] : ""
159
+ ] }, keyBase + "-img" + k++)
160
+ );
161
+ i += m[0].length;
162
+ continue;
163
+ }
164
+ if (ch === "[" && (m = /^\[([^\]]*)\]\(([^)\s]*)\)/.exec(rest))) {
165
+ flush();
166
+ const href = sanitizeUrl(m[2]);
167
+ const inner = parseInline(m[1], keyBase + "-lt" + k);
168
+ out.push(
169
+ href ? /* @__PURE__ */ jsx(
170
+ "a",
171
+ {
172
+ className: "ax-md__link",
173
+ href,
174
+ target: "_blank",
175
+ rel: "noopener noreferrer nofollow",
176
+ children: inner
177
+ },
178
+ keyBase + "-l" + k++
179
+ ) : /* @__PURE__ */ jsx("span", { children: inner }, keyBase + "-l" + k++)
180
+ );
181
+ i += m[0].length;
182
+ continue;
183
+ }
184
+ if ((ch === "h" || ch === "w") && (i === 0 || /[\s(]/.test(text[i - 1])) && (m = /^((?:https?:\/\/|www\.)[^\s<>]+)/i.exec(rest))) {
185
+ let urlText = m[1];
186
+ const trail = /[.,;:!?'")\]]+$/.exec(urlText);
187
+ if (trail) urlText = urlText.slice(0, urlText.length - trail[0].length);
188
+ const href = sanitizeUrl(/^www\./i.test(urlText) ? "http://" + urlText : urlText);
189
+ if (href) {
190
+ flush();
191
+ out.push(
192
+ /* @__PURE__ */ jsx(
193
+ "a",
194
+ {
195
+ className: "ax-md__link",
196
+ href,
197
+ target: "_blank",
198
+ rel: "noopener noreferrer nofollow",
199
+ children: urlText
200
+ },
201
+ keyBase + "-a" + k++
202
+ )
203
+ );
204
+ i += urlText.length;
205
+ continue;
206
+ }
207
+ }
208
+ if (ch === "*" && (m = /^\*\*([\s\S]+?)\*\*/.exec(rest))) {
209
+ flush();
210
+ out.push(
211
+ /* @__PURE__ */ jsx("strong", { className: "ax-md__strong", children: parseInline(m[1], keyBase + "-bt" + k) }, keyBase + "-b" + k++)
212
+ );
213
+ i += m[0].length;
214
+ continue;
215
+ }
216
+ if (ch === "_" && (m = /^__([\s\S]+?)__/.exec(rest))) {
217
+ flush();
218
+ out.push(
219
+ /* @__PURE__ */ jsx("strong", { className: "ax-md__strong", children: parseInline(m[1], keyBase + "-bt" + k) }, keyBase + "-b" + k++)
220
+ );
221
+ i += m[0].length;
222
+ continue;
223
+ }
224
+ if (ch === "~" && (m = /^~~([\s\S]+?)~~/.exec(rest))) {
225
+ flush();
226
+ out.push(
227
+ /* @__PURE__ */ jsx("del", { className: "ax-md__del", children: parseInline(m[1], keyBase + "-st" + k) }, keyBase + "-s" + k++)
228
+ );
229
+ i += m[0].length;
230
+ continue;
231
+ }
232
+ if (ch === "*" && (m = new RegExp("^\\*(?!\\s)([^*]+?)(?<!\\s)\\*").exec(rest))) {
233
+ flush();
234
+ out.push(
235
+ /* @__PURE__ */ jsx("em", { className: "ax-md__em", children: parseInline(m[1], keyBase + "-it" + k) }, keyBase + "-i" + k++)
236
+ );
237
+ i += m[0].length;
238
+ continue;
239
+ }
240
+ if (ch === "_" && (m = new RegExp("^_(?!\\s)([^_]+?)(?<!\\s)_").exec(rest))) {
241
+ flush();
242
+ out.push(
243
+ /* @__PURE__ */ jsx("em", { className: "ax-md__em", children: parseInline(m[1], keyBase + "-it" + k) }, keyBase + "-i" + k++)
244
+ );
245
+ i += m[0].length;
246
+ continue;
247
+ }
248
+ buf += ch;
249
+ i++;
250
+ }
251
+ flush();
252
+ return out;
253
+ }
254
+ const RE_FENCE = /^(\s{0,3})(`{3,}|~{3,})\s*([\w+#.-]*)\s*$/;
255
+ const RE_HEADING = /^(#{1,6})\s+(.*)$/;
256
+ const RE_HR = /^\s{0,3}([-*_])(?:\s*\1){2,}\s*$/;
257
+ const RE_BLANK = /^\s*$/;
258
+ const RE_QUOTE = /^\s*>/;
259
+ const RE_LIST_ITEM = /^([ \t]*)([-*+]|\d{1,9}[.)])\s+(.*)$/;
260
+ function leadingSpaces(s) {
261
+ const m = /^[ \t]*/.exec(s);
262
+ return m[0].length;
263
+ }
264
+ function listMarkerInfo(line) {
265
+ const m = RE_LIST_ITEM.exec(line);
266
+ if (!m) return null;
267
+ const ordered = /\d/.test(m[2]);
268
+ return {
269
+ indent: m[1].length,
270
+ bullet: ordered ? "ol" : "ul",
271
+ start: ordered ? parseInt(m[2], 10) : null,
272
+ text: m[3],
273
+ raw: m[0]
274
+ };
275
+ }
276
+ function splitTableRow(row) {
277
+ let s = row.trim();
278
+ if (s.startsWith("|")) s = s.slice(1);
279
+ if (s.endsWith("|")) s = s.slice(0, -1);
280
+ return s.split(new RegExp("(?<!\\\\)\\|")).map((c) => c.trim().replace(/\\\|/g, "|"));
281
+ }
282
+ function isTableSep(line) {
283
+ if (!line.includes("|")) return false;
284
+ const cells = splitTableRow(line);
285
+ return cells.length > 0 && cells.every((c) => /^:?-{1,}:?$/.test(c.trim()));
286
+ }
287
+ function alignOf(cell) {
288
+ const c = cell.trim();
289
+ const l = c.startsWith(":"), r = c.endsWith(":");
290
+ if (l && r) return "center";
291
+ if (r) return "right";
292
+ if (l) return "left";
293
+ return null;
294
+ }
295
+ function startsBlock(lines, i) {
296
+ const line = lines[i];
297
+ if (RE_FENCE.test(line)) return true;
298
+ if (RE_HR.test(line)) return true;
299
+ if (RE_HEADING.test(line)) return true;
300
+ if (RE_QUOTE.test(line)) return true;
301
+ if (listMarkerInfo(line)) return true;
302
+ if (line.includes("|") && i + 1 < lines.length && isTableSep(lines[i + 1])) return true;
303
+ return false;
304
+ }
305
+ function parseList(lines, i) {
306
+ const base = leadingSpaces(lines[i]);
307
+ const baseInfo = listMarkerInfo(lines[i]);
308
+ const type = baseInfo.bullet;
309
+ const start = baseInfo.start;
310
+ const items = [];
311
+ while (i < lines.length) {
312
+ if (RE_BLANK.test(lines[i])) {
313
+ let j = i;
314
+ while (j < lines.length && RE_BLANK.test(lines[j])) j++;
315
+ const info2 = j < lines.length ? listMarkerInfo(lines[j]) : null;
316
+ if (j < lines.length && (info2 && leadingSpaces(lines[j]) === base || leadingSpaces(lines[j]) > base)) {
317
+ i = j;
318
+ continue;
319
+ }
320
+ break;
321
+ }
322
+ const ind = leadingSpaces(lines[i]);
323
+ const info = listMarkerInfo(lines[i]);
324
+ if (info && ind === base && info.bullet === type) {
325
+ const markerWidth = info.raw.length - info.text.length;
326
+ const itemLines = [info.text];
327
+ i++;
328
+ while (i < lines.length) {
329
+ if (RE_BLANK.test(lines[i])) {
330
+ let j = i;
331
+ while (j < lines.length && RE_BLANK.test(lines[j])) j++;
332
+ if (j < lines.length && leadingSpaces(lines[j]) > base) {
333
+ itemLines.push("");
334
+ i++;
335
+ continue;
336
+ }
337
+ break;
338
+ }
339
+ if (leadingSpaces(lines[i]) > base) {
340
+ itemLines.push(lines[i].slice(Math.min(markerWidth, leadingSpaces(lines[i]))));
341
+ i++;
342
+ } else break;
343
+ }
344
+ while (itemLines.length && itemLines[itemLines.length - 1] === "") itemLines.pop();
345
+ let checked = null;
346
+ const tm = /^\[([ xX])\]\s+([\s\S]*)$/.exec(itemLines[0] || "");
347
+ if (tm) {
348
+ checked = tm[1].toLowerCase() === "x";
349
+ itemLines[0] = tm[2];
350
+ }
351
+ items.push({ checked, blocks: parseBlocks(itemLines.join("\n")) });
352
+ } else {
353
+ break;
354
+ }
355
+ }
356
+ return { type, start, items, next: i };
357
+ }
358
+ function parseBlocks(src) {
359
+ const lines = String(src || "").replace(/\r\n?/g, "\n").split("\n");
360
+ const blocks = [];
361
+ let i = 0;
362
+ while (i < lines.length) {
363
+ const line = lines[i];
364
+ const fence = RE_FENCE.exec(line);
365
+ if (fence) {
366
+ const mark = fence[2][0];
367
+ const len = fence[2].length;
368
+ const close = new RegExp("^\\s{0,3}" + mark + "{" + len + ",}\\s*$");
369
+ i++;
370
+ const code = [];
371
+ while (i < lines.length && !close.test(lines[i])) {
372
+ code.push(lines[i]);
373
+ i++;
374
+ }
375
+ if (i < lines.length) i++;
376
+ blocks.push({ type: "code", lang: fence[3] || "", code: code.join("\n") });
377
+ continue;
378
+ }
379
+ if (RE_BLANK.test(line)) {
380
+ i++;
381
+ continue;
382
+ }
383
+ if (RE_HR.test(line)) {
384
+ blocks.push({ type: "hr" });
385
+ i++;
386
+ continue;
387
+ }
388
+ const h = RE_HEADING.exec(line);
389
+ if (h) {
390
+ blocks.push({ type: "heading", level: h[1].length, text: h[2] });
391
+ i++;
392
+ continue;
393
+ }
394
+ if (RE_QUOTE.test(line)) {
395
+ const inner = [];
396
+ while (i < lines.length && RE_QUOTE.test(lines[i])) {
397
+ inner.push(lines[i].replace(/^\s*>\s?/, ""));
398
+ i++;
399
+ }
400
+ blocks.push({ type: "quote", blocks: parseBlocks(inner.join("\n")) });
401
+ continue;
402
+ }
403
+ if (line.includes("|") && i + 1 < lines.length && isTableSep(lines[i + 1])) {
404
+ const header = splitTableRow(line);
405
+ const aligns = splitTableRow(lines[i + 1]).map(alignOf);
406
+ i += 2;
407
+ const rows = [];
408
+ while (i < lines.length && !RE_BLANK.test(lines[i]) && lines[i].includes("|") && !RE_FENCE.test(lines[i]) && !RE_QUOTE.test(lines[i])) {
409
+ rows.push(splitTableRow(lines[i]));
410
+ i++;
411
+ }
412
+ blocks.push({ type: "table", header, aligns, rows });
413
+ continue;
414
+ }
415
+ if (listMarkerInfo(line)) {
416
+ const res = parseList(lines, i);
417
+ blocks.push({ type: "list", listType: res.type, start: res.start, items: res.items });
418
+ i = res.next;
419
+ continue;
420
+ }
421
+ const para = [line];
422
+ i++;
423
+ while (i < lines.length && !RE_BLANK.test(lines[i]) && !startsBlock(lines, i)) {
424
+ para.push(lines[i]);
425
+ i++;
426
+ }
427
+ blocks.push({ type: "p", text: para.join("\n") });
428
+ }
429
+ return blocks;
430
+ }
431
+ function renderItemContent(blocks, key) {
432
+ if (blocks.length === 1 && blocks[0].type === "p") return parseInline(blocks[0].text, key);
433
+ return blocks.map(
434
+ (b, j) => b.type === "p" ? /* @__PURE__ */ jsx(React.Fragment, { children: parseInline(b.text, key + "-" + j) }, j) : renderBlock(b, key + "-" + j)
435
+ );
436
+ }
437
+ function renderList(b, key) {
438
+ const Tag = b.listType === "ol" ? "ol" : "ul";
439
+ const cls = b.listType === "ol" ? "ax-md__ol" : "ax-md__ul";
440
+ const startAttr = b.listType === "ol" && b.start && b.start !== 1 ? b.start : void 0;
441
+ return /* @__PURE__ */ jsx(Tag, { className: cls, start: startAttr, children: b.items.map((it, j) => {
442
+ const content = renderItemContent(it.blocks, key + "-" + j);
443
+ if (it.checked !== null) {
444
+ return /* @__PURE__ */ jsxs("li", { className: "ax-md__li ax-md__li--task", children: [
445
+ /* @__PURE__ */ jsx(
446
+ "input",
447
+ {
448
+ type: "checkbox",
449
+ className: "ax-md__check",
450
+ checked: it.checked,
451
+ disabled: true,
452
+ readOnly: true
453
+ }
454
+ ),
455
+ /* @__PURE__ */ jsx("span", { className: "ax-md__taskbody", children: content })
456
+ ] }, j);
457
+ }
458
+ return /* @__PURE__ */ jsx("li", { className: "ax-md__li", children: content }, j);
459
+ }) }, key);
460
+ }
461
+ function renderTable(b, key) {
462
+ const cols = b.header.length;
463
+ return /* @__PURE__ */ jsx("div", { className: "ax-md__tablewrap", children: /* @__PURE__ */ jsxs("table", { className: "ax-md__table", children: [
464
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsx("tr", { children: b.header.map((c, ci) => /* @__PURE__ */ jsx("th", { style: { textAlign: b.aligns[ci] || "left" }, children: parseInline(c, key + "-h" + ci) }, ci)) }) }),
465
+ /* @__PURE__ */ jsx("tbody", { children: b.rows.map((r, ri) => /* @__PURE__ */ jsx("tr", { children: Array.from({ length: cols }).map((_, ci) => /* @__PURE__ */ jsx("td", { style: { textAlign: b.aligns[ci] || "left" }, children: parseInline(r[ci] || "", key + "-r" + ri + "c" + ci) }, ci)) }, ri)) })
466
+ ] }) }, key);
467
+ }
468
+ function renderBlock(b, key) {
469
+ switch (b.type) {
470
+ case "code":
471
+ return /* @__PURE__ */ jsx(CodeBlock, { code: b.code, lang: b.lang || "text" }, key);
472
+ case "hr":
473
+ return /* @__PURE__ */ jsx("hr", { className: "ax-md__hr" }, key);
474
+ case "quote":
475
+ return /* @__PURE__ */ jsx("blockquote", { className: "ax-md__quote", children: renderBlocks(b.blocks, key) }, key);
476
+ case "table":
477
+ return renderTable(b, key);
478
+ case "list":
479
+ return renderList(b, key);
480
+ case "heading": {
481
+ const lvl = Math.min(b.level, 3);
482
+ const Tag = "h" + Math.min(b.level + 1, 6);
483
+ return /* @__PURE__ */ jsx(Tag, { className: "ax-md__h ax-md__h--" + lvl, children: parseInline(b.text, key) }, key);
484
+ }
485
+ case "p":
486
+ default:
487
+ return /* @__PURE__ */ jsx("p", { className: "ax-md__p", children: parseInline(b.text, key) }, key);
488
+ }
489
+ }
490
+ function renderBlocks(blocks, keyBase) {
491
+ return blocks.map((b, idx) => renderBlock(b, keyBase + "-blk" + idx));
492
+ }
493
+ function Markdown({ content, children, className = "", ...rest }) {
494
+ const src = typeof content === "string" ? content : typeof children === "string" ? children : "";
495
+ const blocks = React.useMemo(() => parseBlocks(src), [src]);
496
+ const cls = ["ax-md", className].filter(Boolean).join(" ");
497
+ return /* @__PURE__ */ jsx("div", { className: cls, ...rest, children: renderBlocks(blocks, "md") });
498
+ }
499
+ export {
500
+ Markdown
501
+ };
502
+ //# sourceMappingURL=Markdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Markdown.js","sources":["../../../src/components/chat/Markdown.jsx"],"sourcesContent":["import React from \"react\";\nimport { CodeBlock } from \"./CodeBlock.jsx\";\n\n/* ============================================================\n Agentaily — Markdown primitive\n Renders a model's markdown STRING into typeset React nodes.\n\n Safety model: the source string is parsed into a block/inline\n tree and rendered as React elements only. All literal text\n becomes React text nodes (auto-escaped) — we NEVER touch\n dangerouslySetInnerHTML, so there is no HTML-injection surface.\n Link hrefs are scheme-sanitized; images render as inert\n placeholders (no remote fetch).\n\n Streaming model: inline delimiters match only when their\n closing delimiter is present, so an unterminated ** ` _ ~~ or\n [..]( degrades to literal text. A half table, a half blockquote,\n and an unclosed ``` fence all render their partial content\n instead of throwing — nothing disappears mid-stream.\n\n Coverage: paragraphs + soft breaks, bold, italic, strikethrough,\n inline code, code blocks, ordered/unordered/nested/task lists,\n blockquotes (nested), tables (GFM, column alignment), horizontal\n rules, headings, links, bare-URL autolinks, image placeholders.\n ============================================================ */\n\nconst AX_MD_CSS = `\n.ax-md { font-size: var(--text-md); line-height: var(--leading-body); color: var(--text-body); min-width: 0; }\n.ax-md > :first-child { margin-top: 0; }\n.ax-md > :last-child { margin-bottom: 0; }\n\n.ax-md__p { margin: 0 0 0.7em; text-wrap: pretty; }\n\n.ax-md__ul, .ax-md__ol { margin: 0 0 0.7em; padding-left: 1.5em; }\n.ax-md__li { margin: 0.2em 0; }\n.ax-md__li > .ax-md__ul, .ax-md__li > .ax-md__ol { margin: 0.25em 0 0; padding-left: 1.3em; }\n\n.ax-md__ul { list-style: none; padding-left: 1.35em; }\n.ax-md__ul > .ax-md__li { position: relative; }\n.ax-md__ul > .ax-md__li::before {\n content: \"\"; position: absolute; left: -1.05em; top: 0.62em;\n width: 4px; height: 4px; background: var(--text-faint);\n}\n\n.ax-md__ol { list-style: decimal; padding-left: 1.7em; }\n.ax-md__ol > .ax-md__li::marker {\n font-family: var(--font-mono); font-size: 0.9em; color: var(--text-faint);\n}\n\n.ax-md__li--task { list-style: none; display: flex; align-items: flex-start; gap: 8px; }\n.ax-md__ul > .ax-md__li--task::before { display: none; }\n.ax-md__check {\n appearance: none; -webkit-appearance: none; margin: 3px 0 0; flex: none;\n width: 15px; height: 15px; border: 1px solid var(--border-strong);\n border-radius: var(--radius-1); background: var(--bg-0); position: relative; cursor: default;\n}\n.ax-md__check:checked { background: var(--accent); border-color: var(--accent); }\n.ax-md__check:checked::after {\n content: \"\"; position: absolute; left: 4px; top: 1px; width: 4px; height: 8px;\n border: solid var(--accent-fg); border-width: 0 1.6px 1.6px 0; transform: rotate(45deg);\n}\n.ax-md__li--task > .ax-md__taskbody { flex: 1; min-width: 0; }\n\n.ax-md__strong { font-weight: var(--weight-bold); color: var(--text-body); }\n.ax-md__em { font-style: italic; }\n.ax-md__del { text-decoration: line-through; text-decoration-thickness: 1px; color: var(--text-muted); }\n\n.ax-md__code {\n font-family: var(--font-mono); font-size: 0.86em;\n background: var(--bg-3); color: var(--text-body);\n border: 1px solid var(--border-default);\n border-radius: var(--radius-1); padding: 0.12em 0.36em;\n white-space: break-spaces; word-break: break-word;\n}\n[data-theme=\"dark\"] .ax-md__code { background: var(--bg-2); }\n\n.ax-md > .ax-code, .ax-md__li > .ax-code, .ax-md__quote > .ax-code { margin: 0 0 0.7em; }\n\n.ax-md__link {\n color: var(--text-body); text-decoration: underline;\n text-underline-offset: 2px; text-decoration-color: var(--border-strong);\n transition: text-decoration-color var(--dur-1) var(--ease-out);\n word-break: break-word;\n}\n.ax-md__link:hover { text-decoration-color: var(--text-body); }\n\n.ax-md__img {\n display: inline-flex; align-items: center; gap: 6px; vertical-align: baseline;\n padding: 1px 8px; border: 1px dashed var(--border-strong); border-radius: var(--radius-1);\n font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-faint);\n}\n.ax-md__img b { font-weight: var(--weight-medium); letter-spacing: var(--tracking-label); }\n\n.ax-md__quote {\n margin: 0 0 0.7em; padding: 2px 0 2px 14px;\n border-left: 2px solid var(--border-strong); color: var(--text-muted);\n}\n.ax-md__quote > :last-child { margin-bottom: 0; }\n.ax-md__quote .ax-md__quote { margin-top: 0.5em; }\n\n.ax-md__hr { border: none; border-top: 1px solid var(--border-default); margin: 1.2em 0; }\n\n.ax-md__tablewrap {\n margin: 0 0 0.7em; overflow-x: auto;\n border: 1px solid var(--border-default); border-radius: var(--radius-2);\n}\n.ax-md__table { border-collapse: collapse; width: 100%; font-size: var(--text-sm); }\n.ax-md__table th, .ax-md__table td {\n padding: 7px 12px; text-align: left; vertical-align: top;\n border-bottom: 1px solid var(--border-default); border-right: 1px solid var(--border-default);\n}\n.ax-md__table th:last-child, .ax-md__table td:last-child { border-right: none; }\n.ax-md__table tbody tr:last-child td { border-bottom: none; }\n.ax-md__table thead th {\n background: var(--surface-card); color: var(--text-body);\n font-weight: var(--weight-medium); white-space: nowrap;\n}\n\n.ax-md__h { font-family: var(--font-display); font-weight: var(--weight-medium);\n line-height: var(--leading-snug); letter-spacing: var(--tracking-tight);\n margin: 1.1em 0 0.5em; color: var(--text-body); }\n.ax-md__h--1 { font-size: var(--text-xl); }\n.ax-md__h--2 { font-size: var(--text-lg); }\n.ax-md__h--3 { font-size: var(--text-md); }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-md-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-md-css\";\n s.textContent = AX_MD_CSS;\n document.head.appendChild(s);\n}\n\n/* ---- url sanitizer: only allow safe schemes / relative ---- */\nfunction sanitizeUrl(raw) {\n // eslint-disable-next-line no-control-regex\n const url = String(raw || \"\")\n .replace(/[\\u0000-\\u001F\\u007F]/g, \"\")\n .trim();\n if (!url) return null;\n if (/^[a-z][a-z0-9+.-]*:/i.test(url)) {\n if (/^(https?|mailto|tel):/i.test(url)) return url;\n return null; // blocks javascript:, data:, vbscript:, file:, etc.\n }\n return url; // relative, anchor, query, protocol-relative\n}\n\n/* ==================== inline parser ==================== */\n/* string -> React node array. At each position it tries inline\n rules in priority order; a rule fires only when its CLOSING\n delimiter exists, so streamed/partial delimiters fall through\n to literal text. */\nfunction parseInline(text, keyBase) {\n const out = [];\n let buf = \"\";\n let i = 0;\n let k = 0;\n const len = text.length;\n const flush = () => {\n if (buf) {\n out.push(buf);\n buf = \"\";\n }\n };\n\n while (i < len) {\n const ch = text[i];\n const rest = text.slice(i);\n\n if (ch === \"\\n\") {\n flush();\n out.push(<br key={keyBase + \"-br\" + k++} />);\n i++;\n continue;\n }\n\n if (ch === \"\\\\\" && i + 1 < len && /[\\\\`*_{}\\[\\]()#+\\-.!~|>]/.test(text[i + 1])) {\n buf += text[i + 1];\n i += 2;\n continue;\n }\n\n let m;\n // inline code\n if (ch === \"`\" && (m = /^`([^`\\n]+?)`/.exec(rest))) {\n flush();\n out.push(\n <code key={keyBase + \"-c\" + k++} className=\"ax-md__code\">\n {m[1]}\n </code>,\n );\n i += m[0].length;\n continue;\n }\n // image placeholder ![alt](url) — inert, never fetched\n if (ch === \"!\" && text[i + 1] === \"[\" && (m = /^!\\[([^\\]]*)\\]\\(([^)\\s]*)\\)/.exec(rest))) {\n flush();\n out.push(\n <span key={keyBase + \"-img\" + k++} className=\"ax-md__img\" title={m[2]}>\n <b>IMG</b>\n {m[1] ? \" · \" + m[1] : \"\"}\n </span>,\n );\n i += m[0].length;\n continue;\n }\n // link [text](url)\n if (ch === \"[\" && (m = /^\\[([^\\]]*)\\]\\(([^)\\s]*)\\)/.exec(rest))) {\n flush();\n const href = sanitizeUrl(m[2]);\n const inner = parseInline(m[1], keyBase + \"-lt\" + k);\n out.push(\n href ? (\n <a\n key={keyBase + \"-l\" + k++}\n className=\"ax-md__link\"\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer nofollow\"\n >\n {inner}\n </a>\n ) : (\n <span key={keyBase + \"-l\" + k++}>{inner}</span>\n ),\n );\n i += m[0].length;\n continue;\n }\n // bare-URL autolink (word boundary on the left)\n if (\n (ch === \"h\" || ch === \"w\") &&\n (i === 0 || /[\\s(]/.test(text[i - 1])) &&\n (m = /^((?:https?:\\/\\/|www\\.)[^\\s<>]+)/i.exec(rest))\n ) {\n let urlText = m[1];\n const trail = /[.,;:!?'\")\\]]+$/.exec(urlText);\n if (trail) urlText = urlText.slice(0, urlText.length - trail[0].length);\n const href = sanitizeUrl(/^www\\./i.test(urlText) ? \"http://\" + urlText : urlText);\n if (href) {\n flush();\n out.push(\n <a\n key={keyBase + \"-a\" + k++}\n className=\"ax-md__link\"\n href={href}\n target=\"_blank\"\n rel=\"noopener noreferrer nofollow\"\n >\n {urlText}\n </a>,\n );\n i += urlText.length;\n continue;\n }\n }\n // bold ** ** or __ __\n if (ch === \"*\" && (m = /^\\*\\*([\\s\\S]+?)\\*\\*/.exec(rest))) {\n flush();\n out.push(\n <strong key={keyBase + \"-b\" + k++} className=\"ax-md__strong\">\n {parseInline(m[1], keyBase + \"-bt\" + k)}\n </strong>,\n );\n i += m[0].length;\n continue;\n }\n if (ch === \"_\" && (m = /^__([\\s\\S]+?)__/.exec(rest))) {\n flush();\n out.push(\n <strong key={keyBase + \"-b\" + k++} className=\"ax-md__strong\">\n {parseInline(m[1], keyBase + \"-bt\" + k)}\n </strong>,\n );\n i += m[0].length;\n continue;\n }\n // strikethrough ~~ ~~\n if (ch === \"~\" && (m = /^~~([\\s\\S]+?)~~/.exec(rest))) {\n flush();\n out.push(\n <del key={keyBase + \"-s\" + k++} className=\"ax-md__del\">\n {parseInline(m[1], keyBase + \"-st\" + k)}\n </del>,\n );\n i += m[0].length;\n continue;\n }\n // italic * * or _ _ (no adjacent whitespace)\n if (ch === \"*\" && (m = /^\\*(?!\\s)([^*]+?)(?<!\\s)\\*/.exec(rest))) {\n flush();\n out.push(\n <em key={keyBase + \"-i\" + k++} className=\"ax-md__em\">\n {parseInline(m[1], keyBase + \"-it\" + k)}\n </em>,\n );\n i += m[0].length;\n continue;\n }\n if (ch === \"_\" && (m = /^_(?!\\s)([^_]+?)(?<!\\s)_/.exec(rest))) {\n flush();\n out.push(\n <em key={keyBase + \"-i\" + k++} className=\"ax-md__em\">\n {parseInline(m[1], keyBase + \"-it\" + k)}\n </em>,\n );\n i += m[0].length;\n continue;\n }\n\n buf += ch;\n i++;\n }\n flush();\n return out;\n}\n\n/* ==================== block parser ==================== */\nconst RE_FENCE = /^(\\s{0,3})(`{3,}|~{3,})\\s*([\\w+#.-]*)\\s*$/;\nconst RE_HEADING = /^(#{1,6})\\s+(.*)$/;\nconst RE_HR = /^\\s{0,3}([-*_])(?:\\s*\\1){2,}\\s*$/;\nconst RE_BLANK = /^\\s*$/;\nconst RE_QUOTE = /^\\s*>/;\nconst RE_LIST_ITEM = /^([ \\t]*)([-*+]|\\d{1,9}[.)])\\s+(.*)$/;\n\nfunction leadingSpaces(s) {\n const m = /^[ \\t]*/.exec(s);\n return m[0].length;\n}\n\nfunction listMarkerInfo(line) {\n const m = RE_LIST_ITEM.exec(line);\n if (!m) return null;\n const ordered = /\\d/.test(m[2]);\n return {\n indent: m[1].length,\n bullet: ordered ? \"ol\" : \"ul\",\n start: ordered ? parseInt(m[2], 10) : null,\n text: m[3],\n raw: m[0],\n };\n}\n\nfunction splitTableRow(row) {\n let s = row.trim();\n if (s.startsWith(\"|\")) s = s.slice(1);\n if (s.endsWith(\"|\")) s = s.slice(0, -1);\n return s.split(/(?<!\\\\)\\|/).map((c) => c.trim().replace(/\\\\\\|/g, \"|\"));\n}\n\nfunction isTableSep(line) {\n if (!line.includes(\"|\")) return false;\n const cells = splitTableRow(line);\n return cells.length > 0 && cells.every((c) => /^:?-{1,}:?$/.test(c.trim()));\n}\n\nfunction alignOf(cell) {\n const c = cell.trim();\n const l = c.startsWith(\":\"),\n r = c.endsWith(\":\");\n if (l && r) return \"center\";\n if (r) return \"right\";\n if (l) return \"left\";\n return null;\n}\n\nfunction startsBlock(lines, i) {\n const line = lines[i];\n if (RE_FENCE.test(line)) return true;\n if (RE_HR.test(line)) return true;\n if (RE_HEADING.test(line)) return true;\n if (RE_QUOTE.test(line)) return true;\n if (listMarkerInfo(line)) return true;\n if (line.includes(\"|\") && i + 1 < lines.length && isTableSep(lines[i + 1])) return true;\n return false;\n}\n\nfunction parseList(lines, i) {\n const base = leadingSpaces(lines[i]);\n const baseInfo = listMarkerInfo(lines[i]);\n const type = baseInfo.bullet;\n const start = baseInfo.start;\n const items = [];\n\n while (i < lines.length) {\n if (RE_BLANK.test(lines[i])) {\n let j = i;\n while (j < lines.length && RE_BLANK.test(lines[j])) j++;\n const info = j < lines.length ? listMarkerInfo(lines[j]) : null;\n if (\n j < lines.length &&\n ((info && leadingSpaces(lines[j]) === base) || leadingSpaces(lines[j]) > base)\n ) {\n i = j;\n continue;\n }\n break;\n }\n const ind = leadingSpaces(lines[i]);\n const info = listMarkerInfo(lines[i]);\n if (info && ind === base && info.bullet === type) {\n const markerWidth = info.raw.length - info.text.length;\n const itemLines = [info.text];\n i++;\n while (i < lines.length) {\n if (RE_BLANK.test(lines[i])) {\n let j = i;\n while (j < lines.length && RE_BLANK.test(lines[j])) j++;\n if (j < lines.length && leadingSpaces(lines[j]) > base) {\n itemLines.push(\"\");\n i++;\n continue;\n }\n break;\n }\n if (leadingSpaces(lines[i]) > base) {\n itemLines.push(lines[i].slice(Math.min(markerWidth, leadingSpaces(lines[i]))));\n i++;\n } else break;\n }\n while (itemLines.length && itemLines[itemLines.length - 1] === \"\") itemLines.pop();\n\n let checked = null;\n const tm = /^\\[([ xX])\\]\\s+([\\s\\S]*)$/.exec(itemLines[0] || \"\");\n if (tm) {\n checked = tm[1].toLowerCase() === \"x\";\n itemLines[0] = tm[2];\n }\n\n items.push({ checked, blocks: parseBlocks(itemLines.join(\"\\n\")) });\n } else {\n break; // sibling of different type, dedent, or non-list line ends the list\n }\n }\n return { type, start, items, next: i };\n}\n\nfunction parseBlocks(src) {\n const lines = String(src || \"\")\n .replace(/\\r\\n?/g, \"\\n\")\n .split(\"\\n\");\n const blocks = [];\n let i = 0;\n\n while (i < lines.length) {\n const line = lines[i];\n\n // fenced code (tolerates a missing closing fence)\n const fence = RE_FENCE.exec(line);\n if (fence) {\n const mark = fence[2][0];\n const len = fence[2].length;\n const close = new RegExp(\"^\\\\s{0,3}\" + mark + \"{\" + len + \",}\\\\s*$\");\n i++;\n const code = [];\n while (i < lines.length && !close.test(lines[i])) {\n code.push(lines[i]);\n i++;\n }\n if (i < lines.length) i++;\n blocks.push({ type: \"code\", lang: fence[3] || \"\", code: code.join(\"\\n\") });\n continue;\n }\n\n if (RE_BLANK.test(line)) {\n i++;\n continue;\n }\n\n if (RE_HR.test(line)) {\n blocks.push({ type: \"hr\" });\n i++;\n continue;\n }\n\n const h = RE_HEADING.exec(line);\n if (h) {\n blocks.push({ type: \"heading\", level: h[1].length, text: h[2] });\n i++;\n continue;\n }\n\n // blockquote — strip one '>' level, recurse (enables nesting + inner blocks)\n if (RE_QUOTE.test(line)) {\n const inner = [];\n while (i < lines.length && RE_QUOTE.test(lines[i])) {\n inner.push(lines[i].replace(/^\\s*>\\s?/, \"\"));\n i++;\n }\n blocks.push({ type: \"quote\", blocks: parseBlocks(inner.join(\"\\n\")) });\n continue;\n }\n\n // GFM table (header + separator). Half tables render their partial rows.\n if (line.includes(\"|\") && i + 1 < lines.length && isTableSep(lines[i + 1])) {\n const header = splitTableRow(line);\n const aligns = splitTableRow(lines[i + 1]).map(alignOf);\n i += 2;\n const rows = [];\n while (\n i < lines.length &&\n !RE_BLANK.test(lines[i]) &&\n lines[i].includes(\"|\") &&\n !RE_FENCE.test(lines[i]) &&\n !RE_QUOTE.test(lines[i])\n ) {\n rows.push(splitTableRow(lines[i]));\n i++;\n }\n blocks.push({ type: \"table\", header, aligns, rows });\n continue;\n }\n\n // lists (nested + mixed handled recursively)\n if (listMarkerInfo(line)) {\n const res = parseList(lines, i);\n blocks.push({ type: \"list\", listType: res.type, start: res.start, items: res.items });\n i = res.next;\n continue;\n }\n\n // paragraph\n const para = [line];\n i++;\n while (i < lines.length && !RE_BLANK.test(lines[i]) && !startsBlock(lines, i)) {\n para.push(lines[i]);\n i++;\n }\n blocks.push({ type: \"p\", text: para.join(\"\\n\") });\n }\n\n return blocks;\n}\n\n/* ==================== render ==================== */\nfunction renderItemContent(blocks, key) {\n if (blocks.length === 1 && blocks[0].type === \"p\") return parseInline(blocks[0].text, key);\n return blocks.map((b, j) =>\n b.type === \"p\" ? (\n <React.Fragment key={j}>{parseInline(b.text, key + \"-\" + j)}</React.Fragment>\n ) : (\n renderBlock(b, key + \"-\" + j)\n ),\n );\n}\n\nfunction renderList(b, key) {\n const Tag = b.listType === \"ol\" ? \"ol\" : \"ul\";\n const cls = b.listType === \"ol\" ? \"ax-md__ol\" : \"ax-md__ul\";\n const startAttr = b.listType === \"ol\" && b.start && b.start !== 1 ? b.start : undefined;\n return (\n <Tag key={key} className={cls} start={startAttr}>\n {b.items.map((it, j) => {\n const content = renderItemContent(it.blocks, key + \"-\" + j);\n if (it.checked !== null) {\n return (\n <li key={j} className=\"ax-md__li ax-md__li--task\">\n <input\n type=\"checkbox\"\n className=\"ax-md__check\"\n checked={it.checked}\n disabled\n readOnly\n />\n <span className=\"ax-md__taskbody\">{content}</span>\n </li>\n );\n }\n return (\n <li key={j} className=\"ax-md__li\">\n {content}\n </li>\n );\n })}\n </Tag>\n );\n}\n\nfunction renderTable(b, key) {\n const cols = b.header.length;\n return (\n <div key={key} className=\"ax-md__tablewrap\">\n <table className=\"ax-md__table\">\n <thead>\n <tr>\n {b.header.map((c, ci) => (\n <th key={ci} style={{ textAlign: b.aligns[ci] || \"left\" }}>\n {parseInline(c, key + \"-h\" + ci)}\n </th>\n ))}\n </tr>\n </thead>\n <tbody>\n {b.rows.map((r, ri) => (\n <tr key={ri}>\n {Array.from({ length: cols }).map((_, ci) => (\n <td key={ci} style={{ textAlign: b.aligns[ci] || \"left\" }}>\n {parseInline(r[ci] || \"\", key + \"-r\" + ri + \"c\" + ci)}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\nfunction renderBlock(b, key) {\n switch (b.type) {\n case \"code\":\n return <CodeBlock key={key} code={b.code} lang={b.lang || \"text\"} />;\n case \"hr\":\n return <hr key={key} className=\"ax-md__hr\" />;\n case \"quote\":\n return (\n <blockquote key={key} className=\"ax-md__quote\">\n {renderBlocks(b.blocks, key)}\n </blockquote>\n );\n case \"table\":\n return renderTable(b, key);\n case \"list\":\n return renderList(b, key);\n case \"heading\": {\n const lvl = Math.min(b.level, 3);\n const Tag = \"h\" + Math.min(b.level + 1, 6);\n return (\n <Tag key={key} className={\"ax-md__h ax-md__h--\" + lvl}>\n {parseInline(b.text, key)}\n </Tag>\n );\n }\n case \"p\":\n default:\n return (\n <p key={key} className=\"ax-md__p\">\n {parseInline(b.text, key)}\n </p>\n );\n }\n}\n\nfunction renderBlocks(blocks, keyBase) {\n return blocks.map((b, idx) => renderBlock(b, keyBase + \"-blk\" + idx));\n}\n\nexport function Markdown({ content, children, className = \"\", ...rest }) {\n const src = typeof content === \"string\" ? content : typeof children === \"string\" ? children : \"\";\n const blocks = React.useMemo(() => parseBlocks(src), [src]);\n const cls = [\"ax-md\", className].filter(Boolean).join(\" \");\n return (\n <div className={cls} {...rest}>\n {renderBlocks(blocks, \"md\")}\n </div>\n );\n}\n"],"names":["info"],"mappings":"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"}
@@ -11,6 +11,12 @@ export interface MessageProps {
11
11
  time?: string;
12
12
  /** Append a blinking block cursor (response in progress). @default false */
13
13
  streaming?: boolean;
14
+ /**
15
+ * Markdown source for the body, rendered through <Markdown>. Overrides
16
+ * `children`. (A plain string passed as `children` is also auto-rendered
17
+ * as markdown; React-node children render unchanged for back-compat.)
18
+ */
19
+ markdown?: string;
14
20
  children?: React.ReactNode;
15
21
  }
16
22
  export declare function Message(props: MessageProps): JSX.Element;
@@ -1,5 +1,6 @@
1
1
  import { jsxs, jsx } from "react/jsx-runtime";
2
2
  import "react";
3
+ import { Markdown } from "./Markdown.js";
3
4
  const AX_MSG_CSS = `
4
5
  .ax-msg { display: flex; flex-direction: column; }
5
6
  .ax-msg--user { align-items: flex-end; }
@@ -40,19 +41,28 @@ function Message({
40
41
  name,
41
42
  time,
42
43
  streaming = false,
44
+ markdown,
43
45
  children,
44
46
  className = "",
45
47
  ...rest
46
48
  }) {
47
49
  const isUser = role === "user";
48
50
  const cls = ["ax-msg", isUser ? "ax-msg--user" : "ax-msg--assistant", className].filter(Boolean).join(" ");
51
+ let body;
52
+ if (typeof markdown === "string") {
53
+ body = /* @__PURE__ */ jsx(Markdown, { content: markdown });
54
+ } else if (typeof children === "string") {
55
+ body = /* @__PURE__ */ jsx(Markdown, { content: children });
56
+ } else {
57
+ body = children;
58
+ }
49
59
  return /* @__PURE__ */ jsxs("div", { className: cls, ...rest, children: [
50
60
  !isUser ? /* @__PURE__ */ jsxs("div", { className: "ax-msg__head", children: [
51
61
  /* @__PURE__ */ jsx("span", { className: "ax-msg__name", children: name || "Agentaily" }),
52
62
  time ? /* @__PURE__ */ jsx("span", { className: "ax-msg__time", children: time }) : null
53
63
  ] }) : null,
54
64
  /* @__PURE__ */ jsxs("div", { className: "ax-msg__body", children: [
55
- children,
65
+ body,
56
66
  streaming ? /* @__PURE__ */ jsx("span", { className: "ax-msg__cursor" }) : null
57
67
  ] })
58
68
  ] });
@@ -1 +1 @@
1
- {"version":3,"file":"Message.js","sources":["../../../src/components/chat/Message.jsx"],"sourcesContent":["import React from \"react\";\n\nconst AX_MSG_CSS = `\n.ax-msg { display: flex; flex-direction: column; }\n.ax-msg--user { align-items: flex-end; }\n.ax-msg--user .ax-msg__body {\n max-width: 78%;\n background: var(--surface-card);\n border: 1px solid var(--border-default);\n border-radius: var(--radius-3);\n padding: 10px 14px;\n}\n.ax-msg--assistant { align-items: stretch; }\n.ax-msg__head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; }\n.ax-msg__name {\n font-family: var(--font-mono); font-size: var(--text-xs);\n font-weight: var(--weight-medium); letter-spacing: var(--tracking-label);\n text-transform: uppercase; color: var(--text-faint);\n}\n.ax-msg__time { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-faint); opacity: 0.7; }\n.ax-msg__body { font-size: var(--text-md); line-height: var(--leading-body); color: var(--text-body); min-width: 0; }\n.ax-msg__body > p { margin: 0 0 0.7em; }\n.ax-msg__body > p:last-child { margin-bottom: 0; }\n.ax-msg__cursor {\n display: inline-block; width: 0.55em; height: 1.05em;\n background: currentColor; vertical-align: text-bottom; margin-left: 2px;\n animation: ax-msg-blink 1.1s steps(1) infinite;\n}\n@keyframes ax-msg-blink { 50% { opacity: 0; } }\n@media (prefers-reduced-motion: reduce) { .ax-msg__cursor { animation: none; } }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-msg-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-msg-css\";\n s.textContent = AX_MSG_CSS;\n document.head.appendChild(s);\n}\n\nexport function Message({\n role = \"assistant\",\n name,\n time,\n streaming = false,\n children,\n className = \"\",\n ...rest\n}) {\n const isUser = role === \"user\";\n const cls = [\"ax-msg\", isUser ? \"ax-msg--user\" : \"ax-msg--assistant\", className].filter(Boolean).join(\" \");\n return (\n <div className={cls} {...rest}>\n {!isUser ? (\n <div className=\"ax-msg__head\">\n <span className=\"ax-msg__name\">{name || \"Agentaily\"}</span>\n {time ? <span className=\"ax-msg__time\">{time}</span> : null}\n </div>\n ) : null}\n <div className=\"ax-msg__body\">\n {children}\n {streaming ? <span className=\"ax-msg__cursor\"></span> : null}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":";;AAEA,MAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BnB,IAAI,OAAO,aAAa,eAAe,CAAC,SAAS,eAAe,YAAY,GAAG;AAC7E,QAAM,IAAI,SAAS,cAAc,OAAO;AACxC,IAAE,KAAK;AACP,IAAE,cAAc;AAChB,WAAS,KAAK,YAAY,CAAC;AAC7B;AAEO,SAAS,QAAQ;AAAA,EACtB,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA,YAAY;AAAA,EACZ,GAAG;AACL,GAAG;AACD,QAAM,SAAS,SAAS;AACxB,QAAM,MAAM,CAAC,UAAU,SAAS,iBAAiB,qBAAqB,SAAS,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG;AACzG,SACE,qBAAC,OAAA,EAAI,WAAW,KAAM,GAAG,MACtB,UAAA;AAAA,IAAA,CAAC,SACA,qBAAC,OAAA,EAAI,WAAU,gBACb,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAK,WAAU,gBAAgB,UAAA,QAAQ,aAAY;AAAA,MACnD,OAAO,oBAAC,QAAA,EAAK,WAAU,gBAAgB,gBAAK,IAAU;AAAA,IAAA,EAAA,CACzD,IACE;AAAA,IACJ,qBAAC,OAAA,EAAI,WAAU,gBACZ,UAAA;AAAA,MAAA;AAAA,MACA,YAAY,oBAAC,QAAA,EAAK,WAAU,kBAAiB,IAAU;AAAA,IAAA,EAAA,CAC1D;AAAA,EAAA,GACF;AAEJ;"}
1
+ {"version":3,"file":"Message.js","sources":["../../../src/components/chat/Message.jsx"],"sourcesContent":["import React from \"react\";\nimport { Markdown } from \"./Markdown.jsx\";\n\nconst AX_MSG_CSS = `\n.ax-msg { display: flex; flex-direction: column; }\n.ax-msg--user { align-items: flex-end; }\n.ax-msg--user .ax-msg__body {\n max-width: 78%;\n background: var(--surface-card);\n border: 1px solid var(--border-default);\n border-radius: var(--radius-3);\n padding: 10px 14px;\n}\n.ax-msg--assistant { align-items: stretch; }\n.ax-msg__head { display: flex; align-items: baseline; gap: 8px; margin-bottom: 6px; }\n.ax-msg__name {\n font-family: var(--font-mono); font-size: var(--text-xs);\n font-weight: var(--weight-medium); letter-spacing: var(--tracking-label);\n text-transform: uppercase; color: var(--text-faint);\n}\n.ax-msg__time { font-family: var(--font-mono); font-size: var(--text-xs); color: var(--text-faint); opacity: 0.7; }\n.ax-msg__body { font-size: var(--text-md); line-height: var(--leading-body); color: var(--text-body); min-width: 0; }\n.ax-msg__body > p { margin: 0 0 0.7em; }\n.ax-msg__body > p:last-child { margin-bottom: 0; }\n.ax-msg__cursor {\n display: inline-block; width: 0.55em; height: 1.05em;\n background: currentColor; vertical-align: text-bottom; margin-left: 2px;\n animation: ax-msg-blink 1.1s steps(1) infinite;\n}\n@keyframes ax-msg-blink { 50% { opacity: 0; } }\n@media (prefers-reduced-motion: reduce) { .ax-msg__cursor { animation: none; } }\n`;\n\nif (typeof document !== \"undefined\" && !document.getElementById(\"ax-msg-css\")) {\n const s = document.createElement(\"style\");\n s.id = \"ax-msg-css\";\n s.textContent = AX_MSG_CSS;\n document.head.appendChild(s);\n}\n\nexport function Message({\n role = \"assistant\",\n name,\n time,\n streaming = false,\n markdown,\n children,\n className = \"\",\n ...rest\n}) {\n const isUser = role === \"user\";\n const cls = [\"ax-msg\", isUser ? \"ax-msg--user\" : \"ax-msg--assistant\", className]\n .filter(Boolean)\n .join(\" \");\n\n // Back-compat: React-node children render as-is. A markdown string (via the\n // `markdown` prop, or a plain string passed as children) routes through\n // <Markdown> so model output is typeset without breaking existing usage.\n let body;\n if (typeof markdown === \"string\") {\n body = <Markdown content={markdown} />;\n } else if (typeof children === \"string\") {\n body = <Markdown content={children} />;\n } else {\n body = children;\n }\n\n return (\n <div className={cls} {...rest}>\n {!isUser ? (\n <div className=\"ax-msg__head\">\n <span className=\"ax-msg__name\">{name || \"Agentaily\"}</span>\n {time ? <span className=\"ax-msg__time\">{time}</span> : null}\n </div>\n ) : null}\n <div className=\"ax-msg__body\">\n {body}\n {streaming ? <span className=\"ax-msg__cursor\"></span> : null}\n </div>\n </div>\n );\n}\n"],"names":[],"mappings":";;;AAGA,MAAM,aAAa;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA8BnB,IAAI,OAAO,aAAa,eAAe,CAAC,SAAS,eAAe,YAAY,GAAG;AAC7E,QAAM,IAAI,SAAS,cAAc,OAAO;AACxC,IAAE,KAAK;AACP,IAAE,cAAc;AAChB,WAAS,KAAK,YAAY,CAAC;AAC7B;AAEO,SAAS,QAAQ;AAAA,EACtB,OAAO;AAAA,EACP;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ;AAAA,EACA;AAAA,EACA,YAAY;AAAA,EACZ,GAAG;AACL,GAAG;AACD,QAAM,SAAS,SAAS;AACxB,QAAM,MAAM,CAAC,UAAU,SAAS,iBAAiB,qBAAqB,SAAS,EAC5E,OAAO,OAAO,EACd,KAAK,GAAG;AAKX,MAAI;AACJ,MAAI,OAAO,aAAa,UAAU;AAChC,WAAO,oBAAC,UAAA,EAAS,SAAS,SAAA,CAAU;AAAA,EACtC,WAAW,OAAO,aAAa,UAAU;AACvC,WAAO,oBAAC,UAAA,EAAS,SAAS,SAAA,CAAU;AAAA,EACtC,OAAO;AACL,WAAO;AAAA,EACT;AAEA,SACE,qBAAC,OAAA,EAAI,WAAW,KAAM,GAAG,MACtB,UAAA;AAAA,IAAA,CAAC,SACA,qBAAC,OAAA,EAAI,WAAU,gBACb,UAAA;AAAA,MAAA,oBAAC,QAAA,EAAK,WAAU,gBAAgB,UAAA,QAAQ,aAAY;AAAA,MACnD,OAAO,oBAAC,QAAA,EAAK,WAAU,gBAAgB,gBAAK,IAAU;AAAA,IAAA,EAAA,CACzD,IACE;AAAA,IACJ,qBAAC,OAAA,EAAI,WAAU,gBACZ,UAAA;AAAA,MAAA;AAAA,MACA,YAAY,oBAAC,QAAA,EAAK,WAAU,kBAAiB,IAAU;AAAA,IAAA,EAAA,CAC1D;AAAA,EAAA,GACF;AAEJ;"}
package/dist/index.d.ts CHANGED
@@ -30,6 +30,7 @@ export * from "./components/buttons/IconButton";
30
30
  export * from "./components/chat/CodeBlock";
31
31
  export * from "./components/chat/Composer";
32
32
  export * from "./components/chat/ConversationThread";
33
+ export * from "./components/chat/Markdown";
33
34
  export * from "./components/chat/Message";
34
35
 
35
36
  // code
package/dist/index.js CHANGED
@@ -21,6 +21,7 @@ import { IconButton } from "./components/buttons/IconButton.js";
21
21
  import { CodeBlock } from "./components/chat/CodeBlock.js";
22
22
  import { Composer } from "./components/chat/Composer.js";
23
23
  import { ConversationThread } from "./components/chat/ConversationThread.js";
24
+ import { Markdown } from "./components/chat/Markdown.js";
24
25
  import { Message } from "./components/chat/Message.js";
25
26
  import { Agent } from "./components/code/Agent.js";
26
27
  import { Artifact } from "./components/code/Artifact.js";
@@ -182,6 +183,7 @@ export {
182
183
  Kbd,
183
184
  Label,
184
185
  LineChart,
186
+ Markdown,
185
187
  MarkupLayer,
186
188
  Menubar,
187
189
  Message,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
1
+ {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@agentaily/design-system",
3
- "version": "0.6.0",
4
- "description": "Agentaily design system — light-first monochrome React component library (114 components) + Storybook.",
3
+ "version": "0.7.1",
4
+ "description": "Agentaily design system — light-first monochrome React component library (115 components) + Storybook.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
7
7
  "author": "Agentaily",
@@ -36,7 +36,8 @@
36
36
  },
37
37
  "files": [
38
38
  "dist",
39
- "DESIGN.md"
39
+ "DESIGN.md",
40
+ "CHANGELOG.md"
40
41
  ],
41
42
  "publishConfig": {
42
43
  "access": "public"
@@ -48,6 +49,8 @@
48
49
  "release": "changeset publish",
49
50
  "storybook": "storybook dev -p 6006",
50
51
  "build-storybook": "storybook build",
52
+ "test": "vitest run",
53
+ "test:watch": "vitest",
51
54
  "gen:barrel": "node scripts/generate-barrel.mjs",
52
55
  "clean": "node scripts/clean.mjs",
53
56
  "build:js": "vite build --config vite.lib.config.js",
@@ -66,12 +69,17 @@
66
69
  "@changesets/cli": "^2.31.0",
67
70
  "@storybook/addon-docs": "^10.4.3",
68
71
  "@storybook/react-vite": "^10.4.3",
72
+ "@testing-library/dom": "^10.4.1",
73
+ "@testing-library/jest-dom": "^6.9.1",
74
+ "@testing-library/react": "^16.3.2",
69
75
  "@vitejs/plugin-react": "^5.0.0",
76
+ "jsdom": "^29.1.1",
70
77
  "lefthook": "^2.1.9",
71
78
  "prettier": "^3.8.4",
72
79
  "react": "^19.2.7",
73
80
  "react-dom": "^19.2.7",
74
81
  "storybook": "^10.4.3",
75
- "vite": "^7.0.0"
82
+ "vite": "^7.0.0",
83
+ "vitest": "^4.1.8"
76
84
  }
77
85
  }