tiptap-markdown-react 1.0.0 → 1.2.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 CHANGED
@@ -121,16 +121,14 @@ Use the `./server` entry from a Server Component — it has no client code and n
121
121
  ```tsx
122
122
  // app/p/[slug]/page.tsx (React Server Component)
123
123
  import { renderReportHtml, ReportContent } from 'tiptap-markdown-react/server';
124
- import { TocPanel } from 'tiptap-markdown-react'; // client component, for the sidebar
125
124
  import 'tiptap-markdown-react/style.css';
126
125
 
127
126
  export default async function Page() {
128
127
  const markdown = await loadMarkdown();
129
- const { html, toc } = renderReportHtml(markdown);
128
+ const { html } = renderReportHtml(markdown);
130
129
 
131
130
  return (
132
131
  <article>
133
- {/* toc can be passed to a client <TocPanel toc={toc} /> */}
134
132
  <ReportContent html={html} />
135
133
  </article>
136
134
  );
@@ -285,10 +283,11 @@ Override any of these CSS variables on an ancestor (e.g. `:root` or the editor c
285
283
  | --- | --- | --- |
286
284
  | `initialMarkdown` | `string` | Initial content (markdown). |
287
285
  | `editable` | `boolean` | `false` renders read-only with the **same** extensions/NodeViews/styles — interactive controls (code-block language dropdown, delete) collapse. Comments are ignored when `false`. Default `true`. |
288
- | `locale` | `'en' \| 'zh'` | Built-in UI language (default `en`). Pass the same value to the toolbar, TOC and any other sibling you render. |
286
+ | `locale` | `'en' \| 'zh'` | Built-in UI language (default `en`). Pass the same value to the toolbar and any other sibling you render. The outline uses the editor's locale. |
289
287
  | `placeholder` | `string` | Placeholder for the empty document. |
290
288
  | `onEditorReady` | `(editor: Editor \| null) => void` | Get the Tiptap instance (for the toolbar). |
291
- | `onTocChange` | `(items: TocItem[]) => void` | Fires when headings change. |
289
+ | `toc` | `boolean` | Built-in outline on the left edge (default `false`). When on, collapsed ticks expand on hover and click scrolls to the heading. Hidden below 768px and when there are no headings. |
290
+ | `onTocChange` | `(items: TocItem[]) => void` | Optional. Fires when the heading list changes. The editor draws the outline itself. |
292
291
  | `markdownPaste` | `boolean` | Auto-detect markdown in plain-text paste and convert it (Shift+paste keeps plain text). Default `true`. Init-only. |
293
292
  | `markdownFileDrop` | `boolean` | Drop or paste `.md` / `.markdown` files into the editor to insert their parsed content. Default `true`. Init-only. |
294
293
  | `imageUpload` | `ImageUploadConfig` | Official `ImageUploadNode`-style upload (see [Image upload](#image-upload)). The toolbar's image button inserts a placeholder block you can drop a file on or click to pick; progress shows in place and the block is swapped for the image when every file finishes. `upload(file, onProgress?, signal?) => Promise<url>` is required; `accept` (`image/*`), `limit` (1), `maxSize` (0 = unlimited), `onError`, `onSuccess` are optional. Placeholder copy follows `locale`. The block never reaches the saved markdown. Init-only. |
@@ -380,12 +379,11 @@ Styling the bar from the outside: its internals use hashed CSS-module classes, s
380
379
 
381
380
  #### i18n
382
381
 
383
- English and Chinese ship inside the package. Each visible component takes `locale?: 'en' | 'zh'` (default `en`). There is no labels prop and no provider. The toolbar, TOC, palette, find bar and link popover are rendered by the host, so pass the same `locale` to each of them.
382
+ English and Chinese ship inside the package. Each visible component takes `locale?: 'en' | 'zh'` (default `en`). There is no labels prop and no provider. The toolbar, palette, find bar and link popover are rendered by the host, so pass the same `locale` to each of them. The outline is drawn by the editor and follows the editor's `locale`.
384
383
 
385
384
  ```tsx
386
385
  <EditorToolbar editor={editor} locale="zh" />
387
386
  <MarkdownWysiwygEditor locale="zh" onEditorReady={setEditor} />
388
- <TocPanel items={toc} locale="zh" />
389
387
  ```
390
388
 
391
389
  Switching `locale` updates toolbar, find bar, shortcuts, code blocks, charts and upload placeholders without remounting the editor. Comment popover content is still whatever you pass as `children`. `LocaleCode` is exported.
@@ -540,17 +538,9 @@ The commands are exported for headless or self-drawn UIs: `applyLink`, `removeLi
540
538
  | Prop | Type | Description |
541
539
  | --- | --- | --- |
542
540
  | `markdown` | `string` | Markdown to render read-only. |
543
- | `locale` | `'en' \| 'zh'` | Built-in copy for the read-only code block and charts (default `en`). |
541
+ | `locale` | `'en' \| 'zh'` | Built-in copy for the read-only code block, charts, and outline (default `en`). |
544
542
  | `className` | `string` | Class on the container. |
545
-
546
- ### `<TocPanel>` (client)
547
-
548
- | Prop | Type | Description |
549
- | --- | --- | --- |
550
- | `items` | `TocItem[]` | Table of contents items. |
551
- | `activeId` | `string` | Currently highlighted anchor id. |
552
- | `onItemClick` | `(item: TocItem) => void` | Click handler (locked items are skipped). |
553
- | `locale` | `'en' \| 'zh'` | Built-in panel copy (default `en`). |
543
+ | `toc` | `boolean` | Built-in left outline (default `false`). Hidden under 768px or when there are no headings. |
554
544
 
555
545
  ### `tiptap-markdown-react/server` (RSC-safe)
556
546
 
@@ -558,7 +548,7 @@ The commands are exported for headless or self-drawn UIs: `applyLink`, `removeLi
558
548
  | --- | --- |
559
549
  | `renderReportHtml(markdown, options?)` | `{ html, toc, ok }` — markdown → HTML。`includeToc` 默认 true;卡片预览传 false。`stabilize` 给流式半成品补未闭合围栏。解析失败 `ok: false`,不把已有 HTML 吞成空串。 |
560
550
  | `stabilizeMarkdown(markdown)` | 给未闭合 ` ``` ` / `~~~` 补闭合。 |
561
- | `ReportContent` | `<ReportContent html={...} />` static reader with editor content styles. |
551
+ | `ReportContent` | Static HTML shell with editor content styles. No outline. The `./reader` export of the same name draws the left outline. |
562
552
  | `extractToc`, `makeTocGetId` | TOC helpers. |
563
553
  | `baseExtensions`, `pureCodeBlock`, `pureImage`, `lowlight` | Schema-level extensions. |
564
554
 
@@ -566,8 +556,10 @@ The commands are exported for headless or self-drawn UIs: `applyLink`, `removeLi
566
556
 
567
557
  | Export | Description |
568
558
  | --- | --- |
569
- | `ReportContentInteractive` | `ReportContent` + citation click delegation. |
570
- | `ReportContent`, `CitationInteractive` | Compose your own wrapper. |
559
+ | `ReportContent` | Static HTML. Pass `toc` for the same left outline as the editor (default off). |
560
+ | `ReportContentInteractive` | `ReportContent` + citation click delegation. Outline stays off unless `toc` is set. |
561
+ | `ReportContentWithCharts` | `ReportContent` + chart hydration. Outline stays off unless `toc` is set. |
562
+ | `CitationInteractive` | Compose your own wrapper around server `ReportContent` when you do not want the outline. |
571
563
  | `SourceRef`, citation DOM/types | Footnote helpers. |
572
564
  | `extractFootnoteSources(markdown)` | `{ markdown, sources }` — 把 GFM 脚注定义行(`[^n]: …`)抽成 `SourceRef[]` 并从正文剥离;标记那一半交给 enrich / apply。 |
573
565
  | `scrollToTocHeading` | Scroll the reading container to a heading. |
@@ -423,23 +423,25 @@ ${fenceBody}
423
423
  }
424
424
  return out;
425
425
  }
426
- const editorHost = "_editorHost_1vmxj_6";
427
- const findBarAnchor = "_findBarAnchor_1vmxj_13";
428
- const findBar = "_findBar_1vmxj_13";
429
- const editorScroll = "_editorScroll_1vmxj_5";
430
- const withBlockDrag = "_withBlockDrag_1vmxj_33";
431
- const editorContent = "_editorContent_1vmxj_41";
432
- const editorStatus = "_editorStatus_1vmxj_1935";
433
- const shortcutFab = "_shortcutFab_1vmxj_1947";
434
- const charCount = "_charCount_1vmxj_1953";
435
- const charCountSep = "_charCountSep_1vmxj_1975";
436
- const shortcutFabHosted = "_shortcutFabHosted_1vmxj_2009";
426
+ const editorHost = "_editorHost_1v6pj_6";
427
+ const findBarAnchor = "_findBarAnchor_1v6pj_13";
428
+ const findBar = "_findBar_1v6pj_13";
429
+ const editorScroll = "_editorScroll_1v6pj_5";
430
+ const withBlockDrag = "_withBlockDrag_1v6pj_33";
431
+ const withToc = "_withToc_1v6pj_38";
432
+ const editorContent = "_editorContent_1v6pj_50";
433
+ const editorStatus = "_editorStatus_1v6pj_1944";
434
+ const shortcutFab = "_shortcutFab_1v6pj_1956";
435
+ const charCount = "_charCount_1v6pj_1962";
436
+ const charCountSep = "_charCountSep_1v6pj_1984";
437
+ const shortcutFabHosted = "_shortcutFabHosted_1v6pj_2018";
437
438
  const styles = {
438
439
  editorHost,
439
440
  findBarAnchor,
440
441
  findBar,
441
442
  editorScroll,
442
443
  withBlockDrag,
444
+ withToc,
443
445
  editorContent,
444
446
  editorStatus,
445
447
  shortcutFab,
@@ -467,4 +469,4 @@ export {
467
469
  parseChartFenceBody as p,
468
470
  serializeChartPayload as s
469
471
  };
470
- //# sourceMappingURL=ReportContent-w08GFm8v.js.map
472
+ //# sourceMappingURL=ReportContent-B7okwVW0.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"ReportContent-w08GFm8v.js","sources":["../src/citationUtils.ts","../src/chart/columnMatching.ts","../src/chart/types.ts","../src/chart/parse.ts","../src/ReportContent.tsx"],"sourcesContent":["import type { JSONContent } from '@tiptap/core';\n\nexport interface SourceRef {\n /** 与 markdown `[^n]` 中的 n 对齐(字符串,如 `\"1\"`)。 */\n index: string;\n url?: string;\n title?: string;\n /** 原文片段等;库本身不渲染 Popover,留给消费方。 */\n excerpt?: string;\n}\n\nfunction escapeAttr(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n}\n\n/**\n * 将 markdown 中能匹配到 `sources` 的 `[^n]` 换成带 data-* 的\n * `<span class=\"citation-ref\">`(**不是** `<sup>`,避免被 Superscript 吃掉、\n * 也避免视觉上标)。未匹配的 `[^n]` 原样保留,留给 CitationRef tokenizer。\n *\n * 浏览器端 @tiptap/markdown 可解析内联 HTML;server 端无 DOMParser 时请改用\n * {@link applyCitationSources}(先 parse 再补 attrs)。\n */\nexport function enrichMarkdownCitations(\n markdown: string,\n sources: SourceRef[],\n): string {\n if (!sources.length) return markdown;\n\n return markdown.replace(/\\[\\^(\\d+)\\]/g, (match, num: string) => {\n const source = sources.find((s) => s.index === num);\n if (!source) return match;\n\n const attrs = [`class=\"citation-ref\"`, `data-index=\"${num}\"`];\n if (source.url) attrs.push(`data-url=\"${escapeAttr(source.url)}\"`);\n if (source.title) attrs.push(`data-title=\"${escapeAttr(source.title)}\"`);\n\n if (source.url) {\n return `<span ${attrs.join(' ')}><a href=\"${escapeAttr(source.url)}\" title=\"${escapeAttr(source.title || '')}\" target=\"_blank\" rel=\"noopener\">${num}</a></span>`;\n }\n return `<span ${attrs.join(' ')}>${num}</span>`;\n });\n}\n\nfunction patchCitationNode(\n node: JSONContent,\n byIndex: Map<string, SourceRef>,\n): JSONContent {\n if (node.type === 'citationRef') {\n const index = String(node.attrs?.index ?? '');\n const source = byIndex.get(index);\n if (!source) return node;\n return {\n ...node,\n attrs: {\n ...node.attrs,\n index,\n url: source.url ?? node.attrs?.url ?? null,\n title: source.title ?? node.attrs?.title ?? null,\n },\n };\n }\n\n if (!node.content?.length) return node;\n return {\n ...node,\n content: node.content.map((child) => patchCitationNode(child, byIndex)),\n };\n}\n\n/**\n * 在已 parse 的 Tiptap JSON 上,按 `sources` 给 `citationRef` 节点补 url/title。\n * SSR / 无 DOMParser 环境的首选路径。\n */\nexport function applyCitationSources(\n doc: JSONContent,\n sources: SourceRef[],\n): JSONContent {\n if (!sources.length) return doc;\n const byIndex = new Map(sources.map((s) => [s.index, s]));\n return patchCitationNode(doc, byIndex);\n}\n\n/** 单条脚注定义行:行首 0–3 空格 + `[^数字]:`。 */\nconst FOOTNOTE_DEF_RE = /^ {0,3}\\[\\^(\\d+)\\]:[ \\t]?(.*)$/;\n\n/** 内联链接:`[文本](url)`。URL 支持 `<...>` 包裹(可含空格)或一层配对括号(维基百科式)。 */\nconst MD_LINK_RE =\n /\\[([^\\]]*)\\]\\(\\s*(?:<([^<>]*)>|((?:[^()\\s]|\\([^()]*\\))+)(?:[ \\t]+\"[^\"]*\")?)\\s*\\)/;\nconst MD_LINK_RE_G = new RegExp(MD_LINK_RE.source, 'g');\n\n/** 把定义内容压成纯文本(链接只留文本),供 excerpt 与 title 兜底。 */\nfunction flattenFootnoteContent(content: string): string {\n return content\n .replace(\n MD_LINK_RE_G,\n (_match, text: string, angleUrl: string, plainUrl: string) =>\n text || angleUrl || plainUrl || '',\n )\n .trim();\n}\n\nexport interface ExtractedFootnotes {\n /** 剥离定义行后的正文(首尾与多余空行已收敛)。 */\n markdown: string;\n /** 解析出的来源列表,同一 label 重复定义取首次。 */\n sources: SourceRef[];\n}\n\n/**\n * 把 markdown 中的 GFM 脚注定义行(`[^n]: …`)抽成 {@link SourceRef} 并从正文剥离。\n *\n * 与 {@link enrichMarkdownCitations} / {@link applyCitationSources} 同属脚注一族:\n * 它们消费 `sources` 处理 `[^n]` 标记这一半,本函数生产 `sources`、处理定义行\n * 另一半——不剥离,定义行会以普通段落渲染;不抽取,标记没有 url/title 可补。\n * 典型用法(纯字符串变换,流式每帧重算也便宜):\n *\n * ```ts\n * const { markdown: body, sources } = extractFootnoteSources(raw);\n * // <MarkdownPreview markdown={body} sources={sources} … />\n * ```\n *\n * 规则与边界:\n * - label 仅支持数字,与 enrich / CitationRef tokenizer 的 `\\[\\^(\\d+)\\]` 一致;\n * 非数字定义行不识别、原样留在正文;\n * - 围栏代码块(``` / ~~~,判定与 {@link stabilizeMarkdown} 一致)内的定义行跳过;\n * 缩进代码块不识别(已知限制);\n * - 续行:紧跟定义的缩进(≥4 空格或 Tab)非空行并入内容,空行结束定义;\n * - 内容中第一个内联链接取作 url、链接文本作 title;excerpt 取纯文本全文;\n * 无链接时 title 取纯文本首行;\n * - 流式半行(如 `[^1]: [原文](htt`)按 best-effort 解析,下一帧自愈。\n */\nexport function extractFootnoteSources(markdown: string): ExtractedFootnotes {\n if (!markdown || !markdown.includes('[^')) {\n return { markdown, sources: [] };\n }\n\n const lines = markdown.split('\\n');\n const bodyLines: string[] = [];\n const sources: SourceRef[] = [];\n const seen = new Set<string>();\n let removed = false;\n\n let inFence = false;\n let fenceChar = '`';\n let fenceLen = 3;\n\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n\n const fence = line.match(/^(`{3,}|~{3,})/);\n if (fence) {\n const char = fence[1][0];\n if (!inFence) {\n inFence = true;\n fenceChar = char;\n fenceLen = fence[1].length;\n } else if (\n char === fenceChar &&\n fence[1].length >= fenceLen &&\n /^[`~]+\\s*$/.test(line)\n ) {\n inFence = false;\n }\n bodyLines.push(line);\n continue;\n }\n if (inFence) {\n bodyLines.push(line);\n continue;\n }\n\n const def = FOOTNOTE_DEF_RE.exec(line);\n if (!def) {\n bodyLines.push(line);\n continue;\n }\n removed = true;\n\n const parts = [def[2]];\n let j = i + 1;\n while (\n j < lines.length &&\n lines[j].trim() &&\n /^(?: {4,}|\\t)/.test(lines[j])\n ) {\n parts.push(lines[j].trim());\n j += 1;\n }\n i = j - 1;\n\n const index = def[1];\n if (seen.has(index)) continue;\n seen.add(index);\n\n const content = parts.join('\\n');\n const link = MD_LINK_RE.exec(content);\n const source: SourceRef = { index };\n if (link) {\n const url = link[2] ?? link[3];\n if (url) source.url = url;\n const text = link[1]?.trim();\n if (text) source.title = text;\n }\n const plain = flattenFootnoteContent(content);\n if (plain) {\n source.excerpt = plain;\n if (!source.title) source.title = plain.split('\\n')[0];\n }\n sources.push(source);\n }\n\n if (!removed) {\n return { markdown, sources: [] };\n }\n\n return {\n markdown: bodyLines\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .replace(/^\\n+/, '')\n .replace(/\\s+$/, ''),\n sources,\n };\n}\n","/**\n * Loose header ↔ config field matching for chart data tables.\n * Zero runtime deps beyond string/regex.\n */\n\nconst TRAILING_UNIT_SUFFIX_PATTERN = /^(\\s*[((][^))]+[))])+\\s*$/;\n\n/** Exact match, or column = field + parenthetical unit suffix. */\nexport function columnKeyMatchesConfiguredField(\n columnKey: string,\n configuredField: string,\n): boolean {\n const ck = (columnKey || '').trim();\n const f = (configuredField || '').trim();\n if (!ck || !f) return false;\n if (ck === f) return true;\n if (!ck.startsWith(f)) return false;\n const rest = ck.slice(f.length);\n return rest === '' || TRAILING_UNIT_SUFFIX_PATTERN.test(rest);\n}\n\n/**\n * Resolve configured x/y name to a real column dataIndex.\n * Returns original field when no match (caller decides degrade).\n */\nexport function resolveChartAxisFieldToColumnKey(\n configuredField: string | undefined,\n columnKeys: string[],\n): string | undefined {\n if (configuredField === undefined || configuredField === null) {\n return configuredField;\n }\n const f = configuredField.trim();\n if (!f) return configuredField;\n const keySet = new Set(columnKeys);\n if (keySet.has(f)) return f;\n const hit = columnKeys.find((k) => columnKeyMatchesConfiguredField(k, f));\n return hit ?? configuredField;\n}\n\nexport function normalizeChartConfigAxisFields<\n T extends { x?: string; y?: string; groupBy?: string; filterBy?: string; colorLegend?: string; sortBy?: string },\n>(cfg: T, columnKeys: string[]): T {\n return {\n ...cfg,\n ...(cfg.x !== undefined\n ? { x: resolveChartAxisFieldToColumnKey(cfg.x, columnKeys) }\n : {}),\n ...(cfg.y !== undefined\n ? { y: resolveChartAxisFieldToColumnKey(cfg.y, columnKeys) }\n : {}),\n ...(cfg.groupBy !== undefined\n ? { groupBy: resolveChartAxisFieldToColumnKey(cfg.groupBy, columnKeys) }\n : {}),\n ...(cfg.filterBy !== undefined\n ? {\n filterBy: resolveChartAxisFieldToColumnKey(cfg.filterBy, columnKeys),\n }\n : {}),\n ...(cfg.colorLegend !== undefined\n ? {\n colorLegend: resolveChartAxisFieldToColumnKey(\n cfg.colorLegend,\n columnKeys,\n ),\n }\n : {}),\n ...(cfg.sortBy !== undefined\n ? { sortBy: resolveChartAxisFieldToColumnKey(cfg.sortBy, columnKeys) }\n : {}),\n };\n}\n","/** MVP chart types (subset). */\nexport const MVP_CHART_TYPES = [\n 'line',\n 'bar',\n 'column',\n 'pie',\n 'donut',\n 'area',\n] as const;\n\nexport type MvpChartType = (typeof MVP_CHART_TYPES)[number];\n\nexport function isMvpChartType(value: unknown): value is MvpChartType {\n return (\n typeof value === 'string' &&\n (MVP_CHART_TYPES as readonly string[]).includes(value)\n );\n}\n\n/**\n * Flat chart config (author / LLM form).\n * `subgraphBy` is accepted but ignored in v1.\n */\nexport interface ChartConfig {\n chartType: string;\n x?: string;\n y?: string;\n title?: string;\n height?: number;\n dataTime?: string;\n groupBy?: string;\n filterBy?: string;\n colorLegend?: string;\n sortBy?: string;\n subgraphBy?: string;\n [key: string]: unknown;\n}\n\nexport interface ChartColumn {\n title: string;\n dataIndex: string;\n}\n\n/** Normalized payload stored on the TipTap chart node. */\nexport interface ChartPayload {\n config: ChartConfig[];\n columns: ChartColumn[];\n dataSource: Record<string, unknown>[];\n}\n\n/** Optional host override for Chart.js colors / fonts. */\nexport interface ChartTheme {\n backgroundColor?: string;\n textColor?: string;\n gridColor?: string;\n borderColor?: string;\n fontFamily?: string;\n palette?: string[];\n}\n","import { normalizeChartConfigAxisFields } from './columnMatching';\nimport {\n isMvpChartType,\n type ChartColumn,\n type ChartConfig,\n type ChartPayload,\n type MvpChartType,\n} from './types';\n\nconst CHART_COMMENT_RE = /^<!--\\s*(\\[[\\s\\S]*?\\]|\\{[\\s\\S]*?\\})\\s*-->\\s*$/;\n\n/** GFM table: header + separator + optional body rows. */\nconst GFM_TABLE_BLOCK_RE =\n /^\\|(.+)\\|\\s*\\n\\|([-:\\s|]+)\\|\\s*\\n((?:\\|.*\\|\\s*\\n?)*)/;\n\nfunction splitRow(line: string): string[] {\n const trimmed = line.trim();\n const inner =\n trimmed.startsWith('|') && trimmed.endsWith('|')\n ? trimmed.slice(1, -1)\n : trimmed;\n return inner.split('|').map((c) => c.trim());\n}\n\nfunction isSeparatorRow(line: string): boolean {\n const cells = splitRow(line);\n return cells.length > 0 && cells.every((c) => /^:?-+:?$/.test(c));\n}\n\nfunction coerceCell(raw: string): string | number {\n const t = raw.trim();\n if (t === '') return t;\n const normalized = t.replace(/,/g, '');\n if (/^[-+]?(\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?$/.test(normalized)) {\n const n = Number(normalized);\n if (!Number.isNaN(n)) return n;\n }\n return t;\n}\n\n/** Parse a GFM markdown table into columns + rows. */\nexport function parseGfmTable(markdown: string): {\n columns: ChartColumn[];\n dataSource: Record<string, unknown>[];\n rawLength: number;\n} | null {\n const match = GFM_TABLE_BLOCK_RE.exec(markdown);\n if (!match) return null;\n const headerLine = `|${match[1]}|`;\n const sepLine = `|${match[2]}|`;\n if (!isSeparatorRow(sepLine)) return null;\n\n const headers = splitRow(headerLine);\n if (headers.length === 0 || headers.every((h) => !h)) return null;\n\n const columns: ChartColumn[] = headers.map((h) => ({\n title: h,\n dataIndex: h,\n }));\n\n const body = match[3] ?? '';\n const dataSource: Record<string, unknown>[] = [];\n for (const line of body.split('\\n')) {\n if (!line.trim()) continue;\n if (!line.includes('|')) continue;\n const cells = splitRow(line);\n if (cells.length === 0) continue;\n const row: Record<string, unknown> = {};\n headers.forEach((h, i) => {\n row[h] = coerceCell(cells[i] ?? '');\n });\n dataSource.push(row);\n }\n\n return {\n columns,\n dataSource,\n rawLength: match[0].length,\n };\n}\n\nfunction looksLikeChartCommentJson(parsed: unknown): boolean {\n if (Array.isArray(parsed)) {\n return parsed.some(\n (c) =>\n c &&\n typeof c === 'object' &&\n 'chartType' in (c as object) &&\n typeof (c as ChartConfig).chartType === 'string',\n );\n }\n if (parsed && typeof parsed === 'object') {\n const o = parsed as Record<string, unknown>;\n if (Array.isArray(o.config)) return true;\n if (typeof o.chartType === 'string') return true;\n }\n return false;\n}\n\n/** Unwrap comment JSON into a flat config list (may include non-MVP types). */\nexport function unwrapChartConfigs(raw: unknown): ChartConfig[] | null {\n if (raw == null) return null;\n if (Array.isArray(raw)) {\n return raw.filter(\n (c): c is ChartConfig =>\n !!c && typeof c === 'object' && typeof (c as ChartConfig).chartType === 'string',\n );\n }\n if (typeof raw !== 'object') return null;\n const o = raw as Record<string, unknown>;\n if (Array.isArray(o.config)) {\n return unwrapChartConfigs(o.config);\n }\n if (typeof o.chartType === 'string') {\n return [o as ChartConfig];\n }\n return null;\n}\n\nfunction inferPieAxes(\n cfg: ChartConfig,\n columns: ChartColumn[],\n): ChartConfig {\n if (cfg.x && cfg.y) return cfg;\n if (columns.length < 2) return cfg;\n return {\n ...cfg,\n x: cfg.x ?? columns[0].dataIndex,\n y: cfg.y ?? columns[1].dataIndex,\n };\n}\n\nfunction axisExists(field: string | undefined, keys: string[]): boolean {\n if (!field) return false;\n return keys.includes(field);\n}\n\n/**\n * Keep only MVP chart configs that have resolvable axes.\n * Returns null when nothing left (caller should leave as plain table).\n */\nexport function normalizeChartPayload(\n configs: ChartConfig[],\n columns: ChartColumn[],\n dataSource: Record<string, unknown>[],\n): ChartPayload | null {\n const keys = columns.map((c) => c.dataIndex);\n const normalized: ChartConfig[] = [];\n\n for (const raw of configs) {\n if (!isMvpChartType(raw.chartType)) continue;\n let cfg = normalizeChartConfigAxisFields({ ...raw }, keys);\n const type = cfg.chartType as MvpChartType;\n if (type === 'pie' || type === 'donut') {\n cfg = inferPieAxes(cfg, columns);\n }\n if (!axisExists(cfg.x, keys) || !axisExists(cfg.y, keys)) continue;\n // v1: ignore subgraphBy\n if (cfg.subgraphBy) {\n const { subgraphBy: _drop, ...rest } = cfg;\n cfg = rest;\n }\n normalized.push(cfg);\n }\n\n if (normalized.length === 0) return null;\n return { config: normalized, columns, dataSource };\n}\n\n/** Parse ```chart / ```json-chart / ```tmr-chart fence body JSON. */\nexport function parseChartFenceBody(body: string): ChartPayload | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(body.trim());\n } catch {\n return null;\n }\n if (!parsed || typeof parsed !== 'object') return null;\n const o = parsed as Record<string, unknown>;\n\n // Full form\n if (Array.isArray(o.columns) || Array.isArray(o.dataSource)) {\n const configs = unwrapChartConfigs(o.config ?? o) ?? [];\n const columns = (o.columns as ChartColumn[]) ?? [];\n const dataSource =\n (o.dataSource as Record<string, unknown>[]) ??\n (o.data as Record<string, unknown>[]) ??\n [];\n if (!columns.length) {\n // Simple { chartType, x, y, data }\n if (Array.isArray(o.data) && typeof o.chartType === 'string') {\n const simpleCols: ChartColumn[] = [\n { title: String(o.x ?? 'x'), dataIndex: 'x' },\n { title: String(o.y ?? 'y'), dataIndex: 'y' },\n ];\n const rows = (o.data as unknown[]).map((item) => {\n if (item && typeof item === 'object') return item as Record<string, unknown>;\n return {};\n });\n return normalizeChartPayload(\n [{ chartType: String(o.chartType), x: 'x', y: 'y', title: o.title as string | undefined }],\n simpleCols,\n rows,\n );\n }\n return null;\n }\n const colObjs: ChartColumn[] = columns.map((c) =>\n typeof c === 'string'\n ? { title: c, dataIndex: c }\n : {\n title: String((c as ChartColumn).title ?? (c as ChartColumn).dataIndex),\n dataIndex: String((c as ChartColumn).dataIndex ?? (c as ChartColumn).title),\n },\n );\n return normalizeChartPayload(configs, colObjs, dataSource);\n }\n\n const configs = unwrapChartConfigs(parsed);\n if (!configs) return null;\n return null;\n}\n\n/**\n * Try to parse leading `<!-- chart json -->` + GFM table from `src`.\n * Returns payload + `raw`(`src` 里被消费掉的**精确前缀**,调用方按 `raw.length`\n * 跳过这一段), or null.\n */\nexport function tryParseCommentTableChart(\n src: string,\n): { payload: ChartPayload; raw: string } | null {\n const trimmedStart = src.replace(/^\\s*/, '');\n const offset = src.length - trimmedStart.length;\n if (!trimmedStart.startsWith('<!--')) return null;\n\n const endComment = trimmedStart.indexOf('-->');\n if (endComment < 0) return null;\n const commentBlock = trimmedStart.slice(0, endComment + 3);\n const cm = CHART_COMMENT_RE.exec(commentBlock.trim());\n if (!cm) return null;\n\n let json: unknown;\n try {\n json = JSON.parse(cm[1]);\n } catch {\n return null;\n }\n if (!looksLikeChartCommentJson(json)) return null;\n\n const configs = unwrapChartConfigs(json);\n if (!configs || configs.length === 0) return null;\n // All table-only → not a chart\n if (configs.every((c) => c.chartType === 'table')) return null;\n\n const afterCommentRaw = trimmedStart.slice(endComment + 3);\n // 注释与表格之间隔空行时折成一个 \\n 再解析(表格内容本身不受影响)。\n const afterComment = afterCommentRaw.replace(/^\\s*\\n/, '\\n');\n const tableStart = afterComment.search(/\\|/);\n if (tableStart < 0) return null;\n const beforeTable = afterComment.slice(0, tableStart);\n if (beforeTable.replace(/\\s/g, '') !== '') return null;\n\n const tableMd = afterComment.slice(tableStart);\n const table = parseGfmTable(tableMd);\n if (!table) return null;\n\n const payload = normalizeChartPayload(\n configs,\n table.columns,\n table.dataSource,\n );\n if (!payload) return null;\n\n // 表格起点要在**原文**里定位:折过的 afterComment 比原文短,拿它当基准会让\n // raw 短掉那几个空行字符,调用方按 raw.length 切 src 就会多吃掉后一个字符\n // —— 表格是最后一块时,被吃掉的正是表格收尾的 `|`。\n const tableStartInSrc = afterCommentRaw.search(/\\|/);\n const raw = src.slice(\n 0,\n offset + commentBlock.length + tableStartInSrc + table.rawLength,\n );\n\n return { payload, raw };\n}\n\n/** Serialize payload to flat comment + GFM table (author form). */\nexport function serializeChartPayload(payload: ChartPayload): string {\n const flat =\n payload.config.length === 1 ? payload.config[0] : payload.config;\n const comment = `<!-- ${JSON.stringify(flat)} -->`;\n const headers = payload.columns.map((c) => c.title);\n const keys = payload.columns.map((c) => c.dataIndex);\n const header = `| ${headers.join(' | ')} |`;\n const sep = `| ${headers.map(() => '---').join(' | ')} |`;\n const rows = payload.dataSource.map((row) => {\n const cells = keys.map((k) => {\n const v = row[k];\n if (v == null) return '';\n return String(v);\n });\n return `| ${cells.join(' | ')} |`;\n });\n return [comment, '', header, sep, ...rows].join('\\n');\n}\n\n/**\n * Replace valid comment+table chart pairs with ```tmr-chart fences\n * so TipTap markdown can tokenize a single fence.\n */\nexport function normalizeChartMarkdown(markdown: string): string {\n if (!markdown) return markdown;\n let rest = markdown;\n let out = '';\n\n while (rest.length > 0) {\n const idx = rest.indexOf('<!--');\n if (idx < 0) {\n out += rest;\n break;\n }\n out += rest.slice(0, idx);\n const attempt = tryParseCommentTableChart(rest.slice(idx));\n if (!attempt) {\n // Skip this comment opener without consuming as chart\n out += '<!--';\n rest = rest.slice(idx + 4);\n continue;\n }\n const fenceBody = JSON.stringify({\n config: attempt.payload.config,\n columns: attempt.payload.columns,\n dataSource: attempt.payload.dataSource,\n });\n out += `\\`\\`\\`tmr-chart\\n${fenceBody}\\n\\`\\`\\``;\n rest = rest.slice(idx + attempt.raw.length);\n // Preserve a newline after fence if original had trailing content\n if (rest.startsWith('\\n')) {\n /* keep */\n } else if (rest.length > 0) {\n out += '\\n';\n }\n }\n\n return out;\n}\n","import type { HTMLAttributes } from 'react';\nimport styles from './styles/content.module.css';\n\nexport interface ReportContentProps extends HTMLAttributes<HTMLDivElement> {\n /** `renderReportHtml(...).html` 产出的正文 HTML 字符串。 */\n html: string;\n}\n\n/**\n * 只读静态正文渲染(RSC / SSR 安全,无 client 代码、无编辑器实例)。\n *\n * 把 `renderReportHtml` 产出的 HTML 注入容器,并套上与编辑器 / 只读预览**同一个\n * 规范正文类**,保证「编辑所见 = 预览所见 = 发布后读者所见」。\n * 用于阅读页首屏直出(SEO 可见),无需 JS 即可显示。\n */\nexport function ReportContent({ html, className, ...rest }: ReportContentProps) {\n return (\n <div\n {...rest}\n className={\n className ? `${styles.editorContent} ${className}` : styles.editorContent\n }\n dangerouslySetInnerHTML={{ __html: html }}\n />\n );\n}\n"],"names":["configs"],"mappings":";AAWA,SAAS,WAAW,OAAuB;AACzC,SAAO,MACJ,QAAQ,MAAM,OAAO,EACrB,QAAQ,MAAM,QAAQ,EACtB,QAAQ,MAAM,MAAM,EACpB,QAAQ,MAAM,MAAM;AACzB;AAUO,SAAS,wBACd,UACA,SACQ;AACR,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAE5B,SAAO,SAAS,QAAQ,gBAAgB,CAAC,OAAO,QAAgB;AAC9D,UAAM,SAAS,QAAQ,KAAK,CAAC,MAAM,EAAE,UAAU,GAAG;AAClD,QAAI,CAAC,OAAQ,QAAO;AAEpB,UAAM,QAAQ,CAAC,wBAAwB,eAAe,GAAG,GAAG;AAC5D,QAAI,OAAO,IAAK,OAAM,KAAK,aAAa,WAAW,OAAO,GAAG,CAAC,GAAG;AACjE,QAAI,OAAO,MAAO,OAAM,KAAK,eAAe,WAAW,OAAO,KAAK,CAAC,GAAG;AAEvE,QAAI,OAAO,KAAK;AACd,aAAO,SAAS,MAAM,KAAK,GAAG,CAAC,aAAa,WAAW,OAAO,GAAG,CAAC,YAAY,WAAW,OAAO,SAAS,EAAE,CAAC,oCAAoC,GAAG;AAAA,IACrJ;AACA,WAAO,SAAS,MAAM,KAAK,GAAG,CAAC,IAAI,GAAG;AAAA,EACxC,CAAC;AACH;AAEA,SAAS,kBACP,MACA,SACa;;AACb,MAAI,KAAK,SAAS,eAAe;AAC/B,UAAM,QAAQ,SAAO,UAAK,UAAL,mBAAY,UAAS,EAAE;AAC5C,UAAM,SAAS,QAAQ,IAAI,KAAK;AAChC,QAAI,CAAC,OAAQ,QAAO;AACpB,WAAO;AAAA,MACL,GAAG;AAAA,MACH,OAAO;AAAA,QACL,GAAG,KAAK;AAAA,QACR;AAAA,QACA,KAAK,OAAO,SAAO,UAAK,UAAL,mBAAY,QAAO;AAAA,QACtC,OAAO,OAAO,WAAS,UAAK,UAAL,mBAAY,UAAS;AAAA,MAAA;AAAA,IAC9C;AAAA,EAEJ;AAEA,MAAI,GAAC,UAAK,YAAL,mBAAc,QAAQ,QAAO;AAClC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,SAAS,KAAK,QAAQ,IAAI,CAAC,UAAU,kBAAkB,OAAO,OAAO,CAAC;AAAA,EAAA;AAE1E;AAMO,SAAS,qBACd,KACA,SACa;AACb,MAAI,CAAC,QAAQ,OAAQ,QAAO;AAC5B,QAAM,UAAU,IAAI,IAAI,QAAQ,IAAI,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC;AACxD,SAAO,kBAAkB,KAAK,OAAO;AACvC;AAGA,MAAM,kBAAkB;AAGxB,MAAM,aACJ;AACF,MAAM,eAAe,IAAI,OAAO,WAAW,QAAQ,GAAG;AAGtD,SAAS,uBAAuB,SAAyB;AACvD,SAAO,QACJ;AAAA,IACC;AAAA,IACA,CAAC,QAAQ,MAAc,UAAkB,aACvC,QAAQ,YAAY,YAAY;AAAA,EAAA,EAEnC,KAAA;AACL;AAgCO,SAAS,uBAAuB,UAAsC;;AAC3E,MAAI,CAAC,YAAY,CAAC,SAAS,SAAS,IAAI,GAAG;AACzC,WAAO,EAAE,UAAU,SAAS,GAAC;AAAA,EAC/B;AAEA,QAAM,QAAQ,SAAS,MAAM,IAAI;AACjC,QAAM,YAAsB,CAAA;AAC5B,QAAM,UAAuB,CAAA;AAC7B,QAAM,2BAAW,IAAA;AACjB,MAAI,UAAU;AAEd,MAAI,UAAU;AACd,MAAI,YAAY;AAChB,MAAI,WAAW;AAEf,WAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GAAG;AACxC,UAAM,OAAO,MAAM,CAAC;AAEpB,UAAM,QAAQ,KAAK,MAAM,gBAAgB;AACzC,QAAI,OAAO;AACT,YAAM,OAAO,MAAM,CAAC,EAAE,CAAC;AACvB,UAAI,CAAC,SAAS;AACZ,kBAAU;AACV,oBAAY;AACZ,mBAAW,MAAM,CAAC,EAAE;AAAA,MACtB,WACE,SAAS,aACT,MAAM,CAAC,EAAE,UAAU,YACnB,aAAa,KAAK,IAAI,GACtB;AACA,kBAAU;AAAA,MACZ;AACA,gBAAU,KAAK,IAAI;AACnB;AAAA,IACF;AACA,QAAI,SAAS;AACX,gBAAU,KAAK,IAAI;AACnB;AAAA,IACF;AAEA,UAAM,MAAM,gBAAgB,KAAK,IAAI;AACrC,QAAI,CAAC,KAAK;AACR,gBAAU,KAAK,IAAI;AACnB;AAAA,IACF;AACA,cAAU;AAEV,UAAM,QAAQ,CAAC,IAAI,CAAC,CAAC;AACrB,QAAI,IAAI,IAAI;AACZ,WACE,IAAI,MAAM,UACV,MAAM,CAAC,EAAE,KAAA,KACT,gBAAgB,KAAK,MAAM,CAAC,CAAC,GAC7B;AACA,YAAM,KAAK,MAAM,CAAC,EAAE,MAAM;AAC1B,WAAK;AAAA,IACP;AACA,QAAI,IAAI;AAER,UAAM,QAAQ,IAAI,CAAC;AACnB,QAAI,KAAK,IAAI,KAAK,EAAG;AACrB,SAAK,IAAI,KAAK;AAEd,UAAM,UAAU,MAAM,KAAK,IAAI;AAC/B,UAAM,OAAO,WAAW,KAAK,OAAO;AACpC,UAAM,SAAoB,EAAE,MAAA;AAC5B,QAAI,MAAM;AACR,YAAM,MAAM,KAAK,CAAC,KAAK,KAAK,CAAC;AAC7B,UAAI,YAAY,MAAM;AACtB,YAAM,QAAO,UAAK,CAAC,MAAN,mBAAS;AACtB,UAAI,aAAa,QAAQ;AAAA,IAC3B;AACA,UAAM,QAAQ,uBAAuB,OAAO;AAC5C,QAAI,OAAO;AACT,aAAO,UAAU;AACjB,UAAI,CAAC,OAAO,MAAO,QAAO,QAAQ,MAAM,MAAM,IAAI,EAAE,CAAC;AAAA,IACvD;AACA,YAAQ,KAAK,MAAM;AAAA,EACrB;AAEA,MAAI,CAAC,SAAS;AACZ,WAAO,EAAE,UAAU,SAAS,GAAC;AAAA,EAC/B;AAEA,SAAO;AAAA,IACL,UAAU,UACP,KAAK,IAAI,EACT,QAAQ,WAAW,MAAM,EACzB,QAAQ,QAAQ,EAAE,EAClB,QAAQ,QAAQ,EAAE;AAAA,IACrB;AAAA,EAAA;AAEJ;AC/NA,MAAM,+BAA+B;AAG9B,SAAS,gCACd,WACA,iBACS;AACT,QAAM,MAAM,aAAa,IAAI,KAAA;AAC7B,QAAM,KAAK,mBAAmB,IAAI,KAAA;AAClC,MAAI,CAAC,MAAM,CAAC,EAAG,QAAO;AACtB,MAAI,OAAO,EAAG,QAAO;AACrB,MAAI,CAAC,GAAG,WAAW,CAAC,EAAG,QAAO;AAC9B,QAAM,OAAO,GAAG,MAAM,EAAE,MAAM;AAC9B,SAAO,SAAS,MAAM,6BAA6B,KAAK,IAAI;AAC9D;AAMO,SAAS,iCACd,iBACA,YACoB;AACpB,MAAI,oBAAoB,UAAa,oBAAoB,MAAM;AAC7D,WAAO;AAAA,EACT;AACA,QAAM,IAAI,gBAAgB,KAAA;AAC1B,MAAI,CAAC,EAAG,QAAO;AACf,QAAM,SAAS,IAAI,IAAI,UAAU;AACjC,MAAI,OAAO,IAAI,CAAC,EAAG,QAAO;AAC1B,QAAM,MAAM,WAAW,KAAK,CAAC,MAAM,gCAAgC,GAAG,CAAC,CAAC;AACxE,SAAO,OAAO;AAChB;AAEO,SAAS,+BAEd,KAAQ,YAAyB;AACjC,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAI,IAAI,MAAM,SACV,EAAE,GAAG,iCAAiC,IAAI,GAAG,UAAU,EAAA,IACvD,CAAA;AAAA,IACJ,GAAI,IAAI,MAAM,SACV,EAAE,GAAG,iCAAiC,IAAI,GAAG,UAAU,EAAA,IACvD,CAAA;AAAA,IACJ,GAAI,IAAI,YAAY,SAChB,EAAE,SAAS,iCAAiC,IAAI,SAAS,UAAU,EAAA,IACnE,CAAA;AAAA,IACJ,GAAI,IAAI,aAAa,SACjB;AAAA,MACE,UAAU,iCAAiC,IAAI,UAAU,UAAU;AAAA,IAAA,IAErE,CAAA;AAAA,IACJ,GAAI,IAAI,gBAAgB,SACpB;AAAA,MACE,aAAa;AAAA,QACX,IAAI;AAAA,QACJ;AAAA,MAAA;AAAA,IACF,IAEF,CAAA;AAAA,IACJ,GAAI,IAAI,WAAW,SACf,EAAE,QAAQ,iCAAiC,IAAI,QAAQ,UAAU,MACjE,CAAA;AAAA,EAAC;AAET;ACtEO,MAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,SAAS,eAAe,OAAuC;AACpE,SACE,OAAO,UAAU,YAChB,gBAAsC,SAAS,KAAK;AAEzD;ACRA,MAAM,mBAAmB;AAGzB,MAAM,qBACJ;AAEF,SAAS,SAAS,MAAwB;AACxC,QAAM,UAAU,KAAK,KAAA;AACrB,QAAM,QACJ,QAAQ,WAAW,GAAG,KAAK,QAAQ,SAAS,GAAG,IAC3C,QAAQ,MAAM,GAAG,EAAE,IACnB;AACN,SAAO,MAAM,MAAM,GAAG,EAAE,IAAI,CAAC,MAAM,EAAE,MAAM;AAC7C;AAEA,SAAS,eAAe,MAAuB;AAC7C,QAAM,QAAQ,SAAS,IAAI;AAC3B,SAAO,MAAM,SAAS,KAAK,MAAM,MAAM,CAAC,MAAM,WAAW,KAAK,CAAC,CAAC;AAClE;AAEA,SAAS,WAAW,KAA8B;AAChD,QAAM,IAAI,IAAI,KAAA;AACd,MAAI,MAAM,GAAI,QAAO;AACrB,QAAM,aAAa,EAAE,QAAQ,MAAM,EAAE;AACrC,MAAI,4CAA4C,KAAK,UAAU,GAAG;AAChE,UAAM,IAAI,OAAO,UAAU;AAC3B,QAAI,CAAC,OAAO,MAAM,CAAC,EAAG,QAAO;AAAA,EAC/B;AACA,SAAO;AACT;AAGO,SAAS,cAAc,UAIrB;AACP,QAAM,QAAQ,mBAAmB,KAAK,QAAQ;AAC9C,MAAI,CAAC,MAAO,QAAO;AACnB,QAAM,aAAa,IAAI,MAAM,CAAC,CAAC;AAC/B,QAAM,UAAU,IAAI,MAAM,CAAC,CAAC;AAC5B,MAAI,CAAC,eAAe,OAAO,EAAG,QAAO;AAErC,QAAM,UAAU,SAAS,UAAU;AACnC,MAAI,QAAQ,WAAW,KAAK,QAAQ,MAAM,CAAC,MAAM,CAAC,CAAC,EAAG,QAAO;AAE7D,QAAM,UAAyB,QAAQ,IAAI,CAAC,OAAO;AAAA,IACjD,OAAO;AAAA,IACP,WAAW;AAAA,EAAA,EACX;AAEF,QAAM,OAAO,MAAM,CAAC,KAAK;AACzB,QAAM,aAAwC,CAAA;AAC9C,aAAW,QAAQ,KAAK,MAAM,IAAI,GAAG;AACnC,QAAI,CAAC,KAAK,OAAQ;AAClB,QAAI,CAAC,KAAK,SAAS,GAAG,EAAG;AACzB,UAAM,QAAQ,SAAS,IAAI;AAC3B,QAAI,MAAM,WAAW,EAAG;AACxB,UAAM,MAA+B,CAAA;AACrC,YAAQ,QAAQ,CAAC,GAAG,MAAM;AACxB,UAAI,CAAC,IAAI,WAAW,MAAM,CAAC,KAAK,EAAE;AAAA,IACpC,CAAC;AACD,eAAW,KAAK,GAAG;AAAA,EACrB;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,WAAW,MAAM,CAAC,EAAE;AAAA,EAAA;AAExB;AAEA,SAAS,0BAA0B,QAA0B;AAC3D,MAAI,MAAM,QAAQ,MAAM,GAAG;AACzB,WAAO,OAAO;AAAA,MACZ,CAAC,MACC,KACA,OAAO,MAAM,YACb,eAAgB,KAChB,OAAQ,EAAkB,cAAc;AAAA,IAAA;AAAA,EAE9C;AACA,MAAI,UAAU,OAAO,WAAW,UAAU;AACxC,UAAM,IAAI;AACV,QAAI,MAAM,QAAQ,EAAE,MAAM,EAAG,QAAO;AACpC,QAAI,OAAO,EAAE,cAAc,SAAU,QAAO;AAAA,EAC9C;AACA,SAAO;AACT;AAGO,SAAS,mBAAmB,KAAoC;AACrE,MAAI,OAAO,KAAM,QAAO;AACxB,MAAI,MAAM,QAAQ,GAAG,GAAG;AACtB,WAAO,IAAI;AAAA,MACT,CAAC,MACC,CAAC,CAAC,KAAK,OAAO,MAAM,YAAY,OAAQ,EAAkB,cAAc;AAAA,IAAA;AAAA,EAE9E;AACA,MAAI,OAAO,QAAQ,SAAU,QAAO;AACpC,QAAM,IAAI;AACV,MAAI,MAAM,QAAQ,EAAE,MAAM,GAAG;AAC3B,WAAO,mBAAmB,EAAE,MAAM;AAAA,EACpC;AACA,MAAI,OAAO,EAAE,cAAc,UAAU;AACnC,WAAO,CAAC,CAAgB;AAAA,EAC1B;AACA,SAAO;AACT;AAEA,SAAS,aACP,KACA,SACa;AACb,MAAI,IAAI,KAAK,IAAI,EAAG,QAAO;AAC3B,MAAI,QAAQ,SAAS,EAAG,QAAO;AAC/B,SAAO;AAAA,IACL,GAAG;AAAA,IACH,GAAG,IAAI,KAAK,QAAQ,CAAC,EAAE;AAAA,IACvB,GAAG,IAAI,KAAK,QAAQ,CAAC,EAAE;AAAA,EAAA;AAE3B;AAEA,SAAS,WAAW,OAA2B,MAAyB;AACtE,MAAI,CAAC,MAAO,QAAO;AACnB,SAAO,KAAK,SAAS,KAAK;AAC5B;AAMO,SAAS,sBACd,SACA,SACA,YACqB;AACrB,QAAM,OAAO,QAAQ,IAAI,CAAC,MAAM,EAAE,SAAS;AAC3C,QAAM,aAA4B,CAAA;AAElC,aAAW,OAAO,SAAS;AACzB,QAAI,CAAC,eAAe,IAAI,SAAS,EAAG;AACpC,QAAI,MAAM,+BAA+B,EAAE,GAAG,IAAA,GAAO,IAAI;AACzD,UAAM,OAAO,IAAI;AACjB,QAAI,SAAS,SAAS,SAAS,SAAS;AACtC,YAAM,aAAa,KAAK,OAAO;AAAA,IACjC;AACA,QAAI,CAAC,WAAW,IAAI,GAAG,IAAI,KAAK,CAAC,WAAW,IAAI,GAAG,IAAI,EAAG;AAE1D,QAAI,IAAI,YAAY;AAClB,YAAM,EAAE,YAAY,OAAO,GAAG,SAAS;AACvC,YAAM;AAAA,IACR;AACA,eAAW,KAAK,GAAG;AAAA,EACrB;AAEA,MAAI,WAAW,WAAW,EAAG,QAAO;AACpC,SAAO,EAAE,QAAQ,YAAY,SAAS,WAAA;AACxC;AAGO,SAAS,oBAAoB,MAAmC;AACrE,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,KAAK,KAAA,CAAM;AAAA,EACjC,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,CAAC,UAAU,OAAO,WAAW,SAAU,QAAO;AAClD,QAAM,IAAI;AAGV,MAAI,MAAM,QAAQ,EAAE,OAAO,KAAK,MAAM,QAAQ,EAAE,UAAU,GAAG;AAC3D,UAAMA,WAAU,mBAAmB,EAAE,UAAU,CAAC,KAAK,CAAA;AACrD,UAAM,UAAW,EAAE,WAA6B,CAAA;AAChD,UAAM,aACH,EAAE,cACF,EAAE,QACH,CAAA;AACF,QAAI,CAAC,QAAQ,QAAQ;AAEnB,UAAI,MAAM,QAAQ,EAAE,IAAI,KAAK,OAAO,EAAE,cAAc,UAAU;AAC5D,cAAM,aAA4B;AAAA,UAChC,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAAG,WAAW,IAAA;AAAA,UACxC,EAAE,OAAO,OAAO,EAAE,KAAK,GAAG,GAAG,WAAW,IAAA;AAAA,QAAI;AAE9C,cAAM,OAAQ,EAAE,KAAmB,IAAI,CAAC,SAAS;AAC/C,cAAI,QAAQ,OAAO,SAAS,SAAU,QAAO;AAC7C,iBAAO,CAAA;AAAA,QACT,CAAC;AACD,eAAO;AAAA,UACL,CAAC,EAAE,WAAW,OAAO,EAAE,SAAS,GAAG,GAAG,KAAK,GAAG,KAAK,OAAO,EAAE,OAA6B;AAAA,UACzF;AAAA,UACA;AAAA,QAAA;AAAA,MAEJ;AACA,aAAO;AAAA,IACT;AACA,UAAM,UAAyB,QAAQ;AAAA,MAAI,CAAC,MAC1C,OAAO,MAAM,WACT,EAAE,OAAO,GAAG,WAAW,MACvB;AAAA,QACE,OAAO,OAAQ,EAAkB,SAAU,EAAkB,SAAS;AAAA,QACtE,WAAW,OAAQ,EAAkB,aAAc,EAAkB,KAAK;AAAA,MAAA;AAAA,IAC5E;AAEN,WAAO,sBAAsBA,UAAS,SAAS,UAAU;AAAA,EAC3D;AAEA,QAAM,UAAU,mBAAmB,MAAM;AACzC,MAAI,CAAC,QAAS,QAAO;AACrB,SAAO;AACT;AAOO,SAAS,0BACd,KAC+C;AAC/C,QAAM,eAAe,IAAI,QAAQ,QAAQ,EAAE;AAC3C,QAAM,SAAS,IAAI,SAAS,aAAa;AACzC,MAAI,CAAC,aAAa,WAAW,MAAM,EAAG,QAAO;AAE7C,QAAM,aAAa,aAAa,QAAQ,KAAK;AAC7C,MAAI,aAAa,EAAG,QAAO;AAC3B,QAAM,eAAe,aAAa,MAAM,GAAG,aAAa,CAAC;AACzD,QAAM,KAAK,iBAAiB,KAAK,aAAa,MAAM;AACpD,MAAI,CAAC,GAAI,QAAO;AAEhB,MAAI;AACJ,MAAI;AACF,WAAO,KAAK,MAAM,GAAG,CAAC,CAAC;AAAA,EACzB,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,CAAC,0BAA0B,IAAI,EAAG,QAAO;AAE7C,QAAM,UAAU,mBAAmB,IAAI;AACvC,MAAI,CAAC,WAAW,QAAQ,WAAW,EAAG,QAAO;AAE7C,MAAI,QAAQ,MAAM,CAAC,MAAM,EAAE,cAAc,OAAO,EAAG,QAAO;AAE1D,QAAM,kBAAkB,aAAa,MAAM,aAAa,CAAC;AAEzD,QAAM,eAAe,gBAAgB,QAAQ,UAAU,IAAI;AAC3D,QAAM,aAAa,aAAa,OAAO,IAAI;AAC3C,MAAI,aAAa,EAAG,QAAO;AAC3B,QAAM,cAAc,aAAa,MAAM,GAAG,UAAU;AACpD,MAAI,YAAY,QAAQ,OAAO,EAAE,MAAM,GAAI,QAAO;AAElD,QAAM,UAAU,aAAa,MAAM,UAAU;AAC7C,QAAM,QAAQ,cAAc,OAAO;AACnC,MAAI,CAAC,MAAO,QAAO;AAEnB,QAAM,UAAU;AAAA,IACd;AAAA,IACA,MAAM;AAAA,IACN,MAAM;AAAA,EAAA;AAER,MAAI,CAAC,QAAS,QAAO;AAKrB,QAAM,kBAAkB,gBAAgB,OAAO,IAAI;AACnD,QAAM,MAAM,IAAI;AAAA,IACd;AAAA,IACA,SAAS,aAAa,SAAS,kBAAkB,MAAM;AAAA,EAAA;AAGzD,SAAO,EAAE,SAAS,IAAA;AACpB;AAGO,SAAS,sBAAsB,SAA+B;AACnE,QAAM,OACJ,QAAQ,OAAO,WAAW,IAAI,QAAQ,OAAO,CAAC,IAAI,QAAQ;AAC5D,QAAM,UAAU,QAAQ,KAAK,UAAU,IAAI,CAAC;AAC5C,QAAM,UAAU,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,KAAK;AAClD,QAAM,OAAO,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,SAAS;AACnD,QAAM,SAAS,KAAK,QAAQ,KAAK,KAAK,CAAC;AACvC,QAAM,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,EAAE,KAAK,KAAK,CAAC;AACrD,QAAM,OAAO,QAAQ,WAAW,IAAI,CAAC,QAAQ;AAC3C,UAAM,QAAQ,KAAK,IAAI,CAAC,MAAM;AAC5B,YAAM,IAAI,IAAI,CAAC;AACf,UAAI,KAAK,KAAM,QAAO;AACtB,aAAO,OAAO,CAAC;AAAA,IACjB,CAAC;AACD,WAAO,KAAK,MAAM,KAAK,KAAK,CAAC;AAAA,EAC/B,CAAC;AACD,SAAO,CAAC,SAAS,IAAI,QAAQ,KAAK,GAAG,IAAI,EAAE,KAAK,IAAI;AACtD;AAMO,SAAS,uBAAuB,UAA0B;AAC/D,MAAI,CAAC,SAAU,QAAO;AACtB,MAAI,OAAO;AACX,MAAI,MAAM;AAEV,SAAO,KAAK,SAAS,GAAG;AACtB,UAAM,MAAM,KAAK,QAAQ,MAAM;AAC/B,QAAI,MAAM,GAAG;AACX,aAAO;AACP;AAAA,IACF;AACA,WAAO,KAAK,MAAM,GAAG,GAAG;AACxB,UAAM,UAAU,0BAA0B,KAAK,MAAM,GAAG,CAAC;AACzD,QAAI,CAAC,SAAS;AAEZ,aAAO;AACP,aAAO,KAAK,MAAM,MAAM,CAAC;AACzB;AAAA,IACF;AACA,UAAM,YAAY,KAAK,UAAU;AAAA,MAC/B,QAAQ,QAAQ,QAAQ;AAAA,MACxB,SAAS,QAAQ,QAAQ;AAAA,MACzB,YAAY,QAAQ,QAAQ;AAAA,IAAA,CAC7B;AACD,WAAO;AAAA,EAAoB,SAAS;AAAA;AACpC,WAAO,KAAK,MAAM,MAAM,QAAQ,IAAI,MAAM;AAE1C,QAAI,KAAK,WAAW,IAAI,EAAG;AAAA,aAEhB,KAAK,SAAS,GAAG;AAC1B,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;ACzUO,SAAS,cAAc,EAAE,MAAM,WAAW,GAAG,QAA4B;AAC9E,SACE;AAAA,IAAC;AAAA,IAAA;AAAA,MACE,GAAG;AAAA,MACJ,WACE,YAAY,GAAG,OAAO,aAAa,IAAI,SAAS,KAAK,OAAO;AAAA,MAE9D,yBAAyB,EAAE,QAAQ,KAAA;AAAA,IAAK;AAAA,EAAA;AAG9C;"}
1
+ {"version":3,"file":"ReportContent-B7okwVW0.js","sources":["../src/citationUtils.ts","../src/chart/columnMatching.ts","../src/chart/types.ts","../src/chart/parse.ts","../src/ReportContent.tsx"],"sourcesContent":["import type { JSONContent } from '@tiptap/core';\n\nexport interface SourceRef {\n /** 与 markdown `[^n]` 中的 n 对齐(字符串,如 `\"1\"`)。 */\n index: string;\n url?: string;\n title?: string;\n /** 原文片段等;库本身不渲染 Popover,留给消费方。 */\n excerpt?: string;\n}\n\nfunction escapeAttr(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/\"/g, '&quot;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;');\n}\n\n/**\n * 将 markdown 中能匹配到 `sources` 的 `[^n]` 换成带 data-* 的\n * `<span class=\"citation-ref\">`(**不是** `<sup>`,避免被 Superscript 吃掉、\n * 也避免视觉上标)。未匹配的 `[^n]` 原样保留,留给 CitationRef tokenizer。\n *\n * 浏览器端 @tiptap/markdown 可解析内联 HTML;server 端无 DOMParser 时请改用\n * {@link applyCitationSources}(先 parse 再补 attrs)。\n */\nexport function enrichMarkdownCitations(\n markdown: string,\n sources: SourceRef[],\n): string {\n if (!sources.length) return markdown;\n\n return markdown.replace(/\\[\\^(\\d+)\\]/g, (match, num: string) => {\n const source = sources.find((s) => s.index === num);\n if (!source) return match;\n\n const attrs = [`class=\"citation-ref\"`, `data-index=\"${num}\"`];\n if (source.url) attrs.push(`data-url=\"${escapeAttr(source.url)}\"`);\n if (source.title) attrs.push(`data-title=\"${escapeAttr(source.title)}\"`);\n\n if (source.url) {\n return `<span ${attrs.join(' ')}><a href=\"${escapeAttr(source.url)}\" title=\"${escapeAttr(source.title || '')}\" target=\"_blank\" rel=\"noopener\">${num}</a></span>`;\n }\n return `<span ${attrs.join(' ')}>${num}</span>`;\n });\n}\n\nfunction patchCitationNode(\n node: JSONContent,\n byIndex: Map<string, SourceRef>,\n): JSONContent {\n if (node.type === 'citationRef') {\n const index = String(node.attrs?.index ?? '');\n const source = byIndex.get(index);\n if (!source) return node;\n return {\n ...node,\n attrs: {\n ...node.attrs,\n index,\n url: source.url ?? node.attrs?.url ?? null,\n title: source.title ?? node.attrs?.title ?? null,\n },\n };\n }\n\n if (!node.content?.length) return node;\n return {\n ...node,\n content: node.content.map((child) => patchCitationNode(child, byIndex)),\n };\n}\n\n/**\n * 在已 parse 的 Tiptap JSON 上,按 `sources` 给 `citationRef` 节点补 url/title。\n * SSR / 无 DOMParser 环境的首选路径。\n */\nexport function applyCitationSources(\n doc: JSONContent,\n sources: SourceRef[],\n): JSONContent {\n if (!sources.length) return doc;\n const byIndex = new Map(sources.map((s) => [s.index, s]));\n return patchCitationNode(doc, byIndex);\n}\n\n/** 单条脚注定义行:行首 0–3 空格 + `[^数字]:`。 */\nconst FOOTNOTE_DEF_RE = /^ {0,3}\\[\\^(\\d+)\\]:[ \\t]?(.*)$/;\n\n/** 内联链接:`[文本](url)`。URL 支持 `<...>` 包裹(可含空格)或一层配对括号(维基百科式)。 */\nconst MD_LINK_RE =\n /\\[([^\\]]*)\\]\\(\\s*(?:<([^<>]*)>|((?:[^()\\s]|\\([^()]*\\))+)(?:[ \\t]+\"[^\"]*\")?)\\s*\\)/;\nconst MD_LINK_RE_G = new RegExp(MD_LINK_RE.source, 'g');\n\n/** 把定义内容压成纯文本(链接只留文本),供 excerpt 与 title 兜底。 */\nfunction flattenFootnoteContent(content: string): string {\n return content\n .replace(\n MD_LINK_RE_G,\n (_match, text: string, angleUrl: string, plainUrl: string) =>\n text || angleUrl || plainUrl || '',\n )\n .trim();\n}\n\nexport interface ExtractedFootnotes {\n /** 剥离定义行后的正文(首尾与多余空行已收敛)。 */\n markdown: string;\n /** 解析出的来源列表,同一 label 重复定义取首次。 */\n sources: SourceRef[];\n}\n\n/**\n * 把 markdown 中的 GFM 脚注定义行(`[^n]: …`)抽成 {@link SourceRef} 并从正文剥离。\n *\n * 与 {@link enrichMarkdownCitations} / {@link applyCitationSources} 同属脚注一族:\n * 它们消费 `sources` 处理 `[^n]` 标记这一半,本函数生产 `sources`、处理定义行\n * 另一半——不剥离,定义行会以普通段落渲染;不抽取,标记没有 url/title 可补。\n * 典型用法(纯字符串变换,流式每帧重算也便宜):\n *\n * ```ts\n * const { markdown: body, sources } = extractFootnoteSources(raw);\n * // <MarkdownPreview markdown={body} sources={sources} … />\n * ```\n *\n * 规则与边界:\n * - label 仅支持数字,与 enrich / CitationRef tokenizer 的 `\\[\\^(\\d+)\\]` 一致;\n * 非数字定义行不识别、原样留在正文;\n * - 围栏代码块(``` / ~~~,判定与 {@link stabilizeMarkdown} 一致)内的定义行跳过;\n * 缩进代码块不识别(已知限制);\n * - 续行:紧跟定义的缩进(≥4 空格或 Tab)非空行并入内容,空行结束定义;\n * - 内容中第一个内联链接取作 url、链接文本作 title;excerpt 取纯文本全文;\n * 无链接时 title 取纯文本首行;\n * - 流式半行(如 `[^1]: [原文](htt`)按 best-effort 解析,下一帧自愈。\n */\nexport function extractFootnoteSources(markdown: string): ExtractedFootnotes {\n if (!markdown || !markdown.includes('[^')) {\n return { markdown, sources: [] };\n }\n\n const lines = markdown.split('\\n');\n const bodyLines: string[] = [];\n const sources: SourceRef[] = [];\n const seen = new Set<string>();\n let removed = false;\n\n let inFence = false;\n let fenceChar = '`';\n let fenceLen = 3;\n\n for (let i = 0; i < lines.length; i += 1) {\n const line = lines[i];\n\n const fence = line.match(/^(`{3,}|~{3,})/);\n if (fence) {\n const char = fence[1][0];\n if (!inFence) {\n inFence = true;\n fenceChar = char;\n fenceLen = fence[1].length;\n } else if (\n char === fenceChar &&\n fence[1].length >= fenceLen &&\n /^[`~]+\\s*$/.test(line)\n ) {\n inFence = false;\n }\n bodyLines.push(line);\n continue;\n }\n if (inFence) {\n bodyLines.push(line);\n continue;\n }\n\n const def = FOOTNOTE_DEF_RE.exec(line);\n if (!def) {\n bodyLines.push(line);\n continue;\n }\n removed = true;\n\n const parts = [def[2]];\n let j = i + 1;\n while (\n j < lines.length &&\n lines[j].trim() &&\n /^(?: {4,}|\\t)/.test(lines[j])\n ) {\n parts.push(lines[j].trim());\n j += 1;\n }\n i = j - 1;\n\n const index = def[1];\n if (seen.has(index)) continue;\n seen.add(index);\n\n const content = parts.join('\\n');\n const link = MD_LINK_RE.exec(content);\n const source: SourceRef = { index };\n if (link) {\n const url = link[2] ?? link[3];\n if (url) source.url = url;\n const text = link[1]?.trim();\n if (text) source.title = text;\n }\n const plain = flattenFootnoteContent(content);\n if (plain) {\n source.excerpt = plain;\n if (!source.title) source.title = plain.split('\\n')[0];\n }\n sources.push(source);\n }\n\n if (!removed) {\n return { markdown, sources: [] };\n }\n\n return {\n markdown: bodyLines\n .join('\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .replace(/^\\n+/, '')\n .replace(/\\s+$/, ''),\n sources,\n };\n}\n","/**\n * Loose header ↔ config field matching for chart data tables.\n * Zero runtime deps beyond string/regex.\n */\n\nconst TRAILING_UNIT_SUFFIX_PATTERN = /^(\\s*[((][^))]+[))])+\\s*$/;\n\n/** Exact match, or column = field + parenthetical unit suffix. */\nexport function columnKeyMatchesConfiguredField(\n columnKey: string,\n configuredField: string,\n): boolean {\n const ck = (columnKey || '').trim();\n const f = (configuredField || '').trim();\n if (!ck || !f) return false;\n if (ck === f) return true;\n if (!ck.startsWith(f)) return false;\n const rest = ck.slice(f.length);\n return rest === '' || TRAILING_UNIT_SUFFIX_PATTERN.test(rest);\n}\n\n/**\n * Resolve configured x/y name to a real column dataIndex.\n * Returns original field when no match (caller decides degrade).\n */\nexport function resolveChartAxisFieldToColumnKey(\n configuredField: string | undefined,\n columnKeys: string[],\n): string | undefined {\n if (configuredField === undefined || configuredField === null) {\n return configuredField;\n }\n const f = configuredField.trim();\n if (!f) return configuredField;\n const keySet = new Set(columnKeys);\n if (keySet.has(f)) return f;\n const hit = columnKeys.find((k) => columnKeyMatchesConfiguredField(k, f));\n return hit ?? configuredField;\n}\n\nexport function normalizeChartConfigAxisFields<\n T extends { x?: string; y?: string; groupBy?: string; filterBy?: string; colorLegend?: string; sortBy?: string },\n>(cfg: T, columnKeys: string[]): T {\n return {\n ...cfg,\n ...(cfg.x !== undefined\n ? { x: resolveChartAxisFieldToColumnKey(cfg.x, columnKeys) }\n : {}),\n ...(cfg.y !== undefined\n ? { y: resolveChartAxisFieldToColumnKey(cfg.y, columnKeys) }\n : {}),\n ...(cfg.groupBy !== undefined\n ? { groupBy: resolveChartAxisFieldToColumnKey(cfg.groupBy, columnKeys) }\n : {}),\n ...(cfg.filterBy !== undefined\n ? {\n filterBy: resolveChartAxisFieldToColumnKey(cfg.filterBy, columnKeys),\n }\n : {}),\n ...(cfg.colorLegend !== undefined\n ? {\n colorLegend: resolveChartAxisFieldToColumnKey(\n cfg.colorLegend,\n columnKeys,\n ),\n }\n : {}),\n ...(cfg.sortBy !== undefined\n ? { sortBy: resolveChartAxisFieldToColumnKey(cfg.sortBy, columnKeys) }\n : {}),\n };\n}\n","/** MVP chart types (subset). */\nexport const MVP_CHART_TYPES = [\n 'line',\n 'bar',\n 'column',\n 'pie',\n 'donut',\n 'area',\n] as const;\n\nexport type MvpChartType = (typeof MVP_CHART_TYPES)[number];\n\nexport function isMvpChartType(value: unknown): value is MvpChartType {\n return (\n typeof value === 'string' &&\n (MVP_CHART_TYPES as readonly string[]).includes(value)\n );\n}\n\n/**\n * Flat chart config (author / LLM form).\n * `subgraphBy` is accepted but ignored in v1.\n */\nexport interface ChartConfig {\n chartType: string;\n x?: string;\n y?: string;\n title?: string;\n height?: number;\n dataTime?: string;\n groupBy?: string;\n filterBy?: string;\n colorLegend?: string;\n sortBy?: string;\n subgraphBy?: string;\n [key: string]: unknown;\n}\n\nexport interface ChartColumn {\n title: string;\n dataIndex: string;\n}\n\n/** Normalized payload stored on the TipTap chart node. */\nexport interface ChartPayload {\n config: ChartConfig[];\n columns: ChartColumn[];\n dataSource: Record<string, unknown>[];\n}\n\n/** Optional host override for Chart.js colors / fonts. */\nexport interface ChartTheme {\n backgroundColor?: string;\n textColor?: string;\n gridColor?: string;\n borderColor?: string;\n fontFamily?: string;\n palette?: string[];\n}\n","import { normalizeChartConfigAxisFields } from './columnMatching';\nimport {\n isMvpChartType,\n type ChartColumn,\n type ChartConfig,\n type ChartPayload,\n type MvpChartType,\n} from './types';\n\nconst CHART_COMMENT_RE = /^<!--\\s*(\\[[\\s\\S]*?\\]|\\{[\\s\\S]*?\\})\\s*-->\\s*$/;\n\n/** GFM table: header + separator + optional body rows. */\nconst GFM_TABLE_BLOCK_RE =\n /^\\|(.+)\\|\\s*\\n\\|([-:\\s|]+)\\|\\s*\\n((?:\\|.*\\|\\s*\\n?)*)/;\n\nfunction splitRow(line: string): string[] {\n const trimmed = line.trim();\n const inner =\n trimmed.startsWith('|') && trimmed.endsWith('|')\n ? trimmed.slice(1, -1)\n : trimmed;\n return inner.split('|').map((c) => c.trim());\n}\n\nfunction isSeparatorRow(line: string): boolean {\n const cells = splitRow(line);\n return cells.length > 0 && cells.every((c) => /^:?-+:?$/.test(c));\n}\n\nfunction coerceCell(raw: string): string | number {\n const t = raw.trim();\n if (t === '') return t;\n const normalized = t.replace(/,/g, '');\n if (/^[-+]?(\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?$/.test(normalized)) {\n const n = Number(normalized);\n if (!Number.isNaN(n)) return n;\n }\n return t;\n}\n\n/** Parse a GFM markdown table into columns + rows. */\nexport function parseGfmTable(markdown: string): {\n columns: ChartColumn[];\n dataSource: Record<string, unknown>[];\n rawLength: number;\n} | null {\n const match = GFM_TABLE_BLOCK_RE.exec(markdown);\n if (!match) return null;\n const headerLine = `|${match[1]}|`;\n const sepLine = `|${match[2]}|`;\n if (!isSeparatorRow(sepLine)) return null;\n\n const headers = splitRow(headerLine);\n if (headers.length === 0 || headers.every((h) => !h)) return null;\n\n const columns: ChartColumn[] = headers.map((h) => ({\n title: h,\n dataIndex: h,\n }));\n\n const body = match[3] ?? '';\n const dataSource: Record<string, unknown>[] = [];\n for (const line of body.split('\\n')) {\n if (!line.trim()) continue;\n if (!line.includes('|')) continue;\n const cells = splitRow(line);\n if (cells.length === 0) continue;\n const row: Record<string, unknown> = {};\n headers.forEach((h, i) => {\n row[h] = coerceCell(cells[i] ?? '');\n });\n dataSource.push(row);\n }\n\n return {\n columns,\n dataSource,\n rawLength: match[0].length,\n };\n}\n\nfunction looksLikeChartCommentJson(parsed: unknown): boolean {\n if (Array.isArray(parsed)) {\n return parsed.some(\n (c) =>\n c &&\n typeof c === 'object' &&\n 'chartType' in (c as object) &&\n typeof (c as ChartConfig).chartType === 'string',\n );\n }\n if (parsed && typeof parsed === 'object') {\n const o = parsed as Record<string, unknown>;\n if (Array.isArray(o.config)) return true;\n if (typeof o.chartType === 'string') return true;\n }\n return false;\n}\n\n/** Unwrap comment JSON into a flat config list (may include non-MVP types). */\nexport function unwrapChartConfigs(raw: unknown): ChartConfig[] | null {\n if (raw == null) return null;\n if (Array.isArray(raw)) {\n return raw.filter(\n (c): c is ChartConfig =>\n !!c && typeof c === 'object' && typeof (c as ChartConfig).chartType === 'string',\n );\n }\n if (typeof raw !== 'object') return null;\n const o = raw as Record<string, unknown>;\n if (Array.isArray(o.config)) {\n return unwrapChartConfigs(o.config);\n }\n if (typeof o.chartType === 'string') {\n return [o as ChartConfig];\n }\n return null;\n}\n\nfunction inferPieAxes(\n cfg: ChartConfig,\n columns: ChartColumn[],\n): ChartConfig {\n if (cfg.x && cfg.y) return cfg;\n if (columns.length < 2) return cfg;\n return {\n ...cfg,\n x: cfg.x ?? columns[0].dataIndex,\n y: cfg.y ?? columns[1].dataIndex,\n };\n}\n\nfunction axisExists(field: string | undefined, keys: string[]): boolean {\n if (!field) return false;\n return keys.includes(field);\n}\n\n/**\n * Keep only MVP chart configs that have resolvable axes.\n * Returns null when nothing left (caller should leave as plain table).\n */\nexport function normalizeChartPayload(\n configs: ChartConfig[],\n columns: ChartColumn[],\n dataSource: Record<string, unknown>[],\n): ChartPayload | null {\n const keys = columns.map((c) => c.dataIndex);\n const normalized: ChartConfig[] = [];\n\n for (const raw of configs) {\n if (!isMvpChartType(raw.chartType)) continue;\n let cfg = normalizeChartConfigAxisFields({ ...raw }, keys);\n const type = cfg.chartType as MvpChartType;\n if (type === 'pie' || type === 'donut') {\n cfg = inferPieAxes(cfg, columns);\n }\n if (!axisExists(cfg.x, keys) || !axisExists(cfg.y, keys)) continue;\n // v1: ignore subgraphBy\n if (cfg.subgraphBy) {\n const { subgraphBy: _drop, ...rest } = cfg;\n cfg = rest;\n }\n normalized.push(cfg);\n }\n\n if (normalized.length === 0) return null;\n return { config: normalized, columns, dataSource };\n}\n\n/** Parse ```chart / ```json-chart / ```tmr-chart fence body JSON. */\nexport function parseChartFenceBody(body: string): ChartPayload | null {\n let parsed: unknown;\n try {\n parsed = JSON.parse(body.trim());\n } catch {\n return null;\n }\n if (!parsed || typeof parsed !== 'object') return null;\n const o = parsed as Record<string, unknown>;\n\n // Full form\n if (Array.isArray(o.columns) || Array.isArray(o.dataSource)) {\n const configs = unwrapChartConfigs(o.config ?? o) ?? [];\n const columns = (o.columns as ChartColumn[]) ?? [];\n const dataSource =\n (o.dataSource as Record<string, unknown>[]) ??\n (o.data as Record<string, unknown>[]) ??\n [];\n if (!columns.length) {\n // Simple { chartType, x, y, data }\n if (Array.isArray(o.data) && typeof o.chartType === 'string') {\n const simpleCols: ChartColumn[] = [\n { title: String(o.x ?? 'x'), dataIndex: 'x' },\n { title: String(o.y ?? 'y'), dataIndex: 'y' },\n ];\n const rows = (o.data as unknown[]).map((item) => {\n if (item && typeof item === 'object') return item as Record<string, unknown>;\n return {};\n });\n return normalizeChartPayload(\n [{ chartType: String(o.chartType), x: 'x', y: 'y', title: o.title as string | undefined }],\n simpleCols,\n rows,\n );\n }\n return null;\n }\n const colObjs: ChartColumn[] = columns.map((c) =>\n typeof c === 'string'\n ? { title: c, dataIndex: c }\n : {\n title: String((c as ChartColumn).title ?? (c as ChartColumn).dataIndex),\n dataIndex: String((c as ChartColumn).dataIndex ?? (c as ChartColumn).title),\n },\n );\n return normalizeChartPayload(configs, colObjs, dataSource);\n }\n\n const configs = unwrapChartConfigs(parsed);\n if (!configs) return null;\n return null;\n}\n\n/**\n * Try to parse leading `<!-- chart json -->` + GFM table from `src`.\n * Returns payload + `raw`(`src` 里被消费掉的**精确前缀**,调用方按 `raw.length`\n * 跳过这一段), or null.\n */\nexport function tryParseCommentTableChart(\n src: string,\n): { payload: ChartPayload; raw: string } | null {\n const trimmedStart = src.replace(/^\\s*/, '');\n const offset = src.length - trimmedStart.length;\n if (!trimmedStart.startsWith('<!--')) return null;\n\n const endComment = trimmedStart.indexOf('-->');\n if (endComment < 0) return null;\n const commentBlock = trimmedStart.slice(0, endComment + 3);\n const cm = CHART_COMMENT_RE.exec(commentBlock.trim());\n if (!cm) return null;\n\n let json: unknown;\n try {\n json = JSON.parse(cm[1]);\n } catch {\n return null;\n }\n if (!looksLikeChartCommentJson(json)) return null;\n\n const configs = unwrapChartConfigs(json);\n if (!configs || configs.length === 0) return null;\n // All table-only → not a chart\n if (configs.every((c) => c.chartType === 'table')) return null;\n\n const afterCommentRaw = trimmedStart.slice(endComment + 3);\n // 注释与表格之间隔空行时折成一个 \\n 再解析(表格内容本身不受影响)。\n const afterComment = afterCommentRaw.replace(/^\\s*\\n/, '\\n');\n const tableStart = afterComment.search(/\\|/);\n if (tableStart < 0) return null;\n const beforeTable = afterComment.slice(0, tableStart);\n if (beforeTable.replace(/\\s/g, '') !== '') return null;\n\n const tableMd = afterComment.slice(tableStart);\n const table = parseGfmTable(tableMd);\n if (!table) return null;\n\n const payload = normalizeChartPayload(\n configs,\n table.columns,\n table.dataSource,\n );\n if (!payload) return null;\n\n // 表格起点要在**原文**里定位:折过的 afterComment 比原文短,拿它当基准会让\n // raw 短掉那几个空行字符,调用方按 raw.length 切 src 就会多吃掉后一个字符\n // —— 表格是最后一块时,被吃掉的正是表格收尾的 `|`。\n const tableStartInSrc = afterCommentRaw.search(/\\|/);\n const raw = src.slice(\n 0,\n offset + commentBlock.length + tableStartInSrc + table.rawLength,\n );\n\n return { payload, raw };\n}\n\n/** Serialize payload to flat comment + GFM table (author form). */\nexport function serializeChartPayload(payload: ChartPayload): string {\n const flat =\n payload.config.length === 1 ? payload.config[0] : payload.config;\n const comment = `<!-- ${JSON.stringify(flat)} -->`;\n const headers = payload.columns.map((c) => c.title);\n const keys = payload.columns.map((c) => c.dataIndex);\n const header = `| ${headers.join(' | ')} |`;\n const sep = `| ${headers.map(() => '---').join(' | ')} |`;\n const rows = payload.dataSource.map((row) => {\n const cells = keys.map((k) => {\n const v = row[k];\n if (v == null) return '';\n return String(v);\n });\n return `| ${cells.join(' | ')} |`;\n });\n return [comment, '', header, sep, ...rows].join('\\n');\n}\n\n/**\n * Replace valid comment+table chart pairs with ```tmr-chart fences\n * so TipTap markdown can tokenize a single fence.\n */\nexport function normalizeChartMarkdown(markdown: string): string {\n if (!markdown) return markdown;\n let rest = markdown;\n let out = '';\n\n while (rest.length > 0) {\n const idx = rest.indexOf('<!--');\n if (idx < 0) {\n out += rest;\n break;\n }\n out += rest.slice(0, idx);\n const attempt = tryParseCommentTableChart(rest.slice(idx));\n if (!attempt) {\n // Skip this comment opener without consuming as chart\n out += '<!--';\n rest = rest.slice(idx + 4);\n continue;\n }\n const fenceBody = JSON.stringify({\n config: attempt.payload.config,\n columns: attempt.payload.columns,\n dataSource: attempt.payload.dataSource,\n });\n out += `\\`\\`\\`tmr-chart\\n${fenceBody}\\n\\`\\`\\``;\n rest = rest.slice(idx + attempt.raw.length);\n // Preserve a newline after fence if original had trailing content\n if (rest.startsWith('\\n')) {\n /* keep */\n } else if (rest.length > 0) {\n out += '\\n';\n }\n }\n\n return out;\n}\n","import type { HTMLAttributes } from 'react';\nimport styles from './styles/content.module.css';\n\nexport interface ReportContentProps extends HTMLAttributes<HTMLDivElement> {\n /** `renderReportHtml(...).html` 产出的正文 HTML 字符串。 */\n html: string;\n}\n\n/**\n * 只读静态正文渲染(RSC / SSR 安全,无 client 代码、无编辑器实例)。\n *\n * 把 `renderReportHtml` 产出的 HTML 注入容器,并套上与编辑器 / 只读预览**同一个\n * 规范正文类**,保证「编辑所见 = 预览所见 = 发布后读者所见」。\n * 用于阅读页首屏直出(SEO 可见),无需 JS 即可显示。\n */\nexport function ReportContent({ html, className, ...rest }: ReportContentProps) {\n return (\n <div\n {...rest}\n className={\n className ? `${styles.editorContent} ${className}` : styles.editorContent\n }\n dangerouslySetInnerHTML={{ __html: html }}\n />\n );\n}\n"],"names":["configs"],"mappings":"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"}