tiptap-markdown-react 0.17.0 → 0.18.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 +75 -3
- package/dist/{ReportContent-CwcppHl8.js → ReportContent-MgbebxPV.js} +8 -8
- package/dist/{ReportContent-CwcppHl8.js.map → ReportContent-MgbebxPV.js.map} +1 -1
- package/dist/components/EditorToolbar.d.ts +12 -1
- package/dist/components/EditorToolbar.dom.test.d.ts +1 -0
- package/dist/components/FindReplaceBar.d.ts +0 -7
- package/dist/components/LinkPopover.d.ts +28 -0
- package/dist/components/LinkPopover.dom.test.d.ts +1 -0
- package/dist/components/MarkdownWysiwygEditor.d.ts +12 -0
- package/dist/components/TableHandles.d.ts +19 -0
- package/dist/components/TableHandles.dom.test.d.ts +1 -0
- package/dist/components/findEntry.dom.test.d.ts +1 -0
- package/dist/{extractToc-CheLC9kh.js → extractToc-BaR6LP1f.js} +42 -4
- package/dist/extractToc-BaR6LP1f.js.map +1 -0
- package/dist/findReplace.d.ts +18 -4
- package/dist/icons/index.d.ts +6 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +1320 -608
- package/dist/index.js.map +1 -1
- package/dist/labels.d.ts +41 -1
- package/dist/linkEditing.d.ts +45 -0
- package/dist/linkEditing.test.d.ts +1 -0
- package/dist/placeholder.test.d.ts +1 -0
- package/dist/reader.js +2 -2
- package/dist/{scrollToTocHeading-CBvptTaQ.js → scrollToTocHeading-BTQWajGD.js} +2 -2
- package/dist/{scrollToTocHeading-CBvptTaQ.js.map → scrollToTocHeading-BTQWajGD.js.map} +1 -1
- package/dist/server.js +4 -4
- package/dist/style-inline.css +398 -174
- package/dist/style.css +398 -174
- package/dist/supSubMarkdown.test.d.ts +1 -0
- package/dist/supSubServer.test.d.ts +1 -0
- package/dist/tableSelection.d.ts +25 -5
- package/package.json +1 -1
- package/dist/extractToc-CheLC9kh.js.map +0 -1
package/README.md
CHANGED
|
@@ -295,14 +295,34 @@ Override any of these CSS variables on an ancestor (e.g. `:root` or the editor c
|
|
|
295
295
|
| `findBarOffset` | `{ top?, right?, bottom?, left? } \| number = px` | Where the library-rendered bar sits inside its positioning context. Default `{ top: 4, right: 4 }` (top-right); pass e.g. `{ bottom: 8, left: 8 }` to dock it elsewhere. The context is the editor, or your `findBarContainer` element when you supply one — the library adds `position: relative` to that container if it is `static` (otherwise the bar would anchor to some unexpected ancestor). |
|
|
296
296
|
| `findShortcut` | `boolean` | Take over <kbd>Cmd/Ctrl</kbd>+<kbd>F</kbd> while the editor has focus (default `true`; bound only when the editor owns a bar). `false` keeps the browser's native find — wire your own entry with `handle.openFind()`. |
|
|
297
297
|
| `findLabels` | `Partial<FindLabels>` | Localize the find & replace bar. |
|
|
298
|
+
| `onFindOpenChange` | `(open: boolean) => void` | Fired when the bar actually shows or hides (Esc / × included). Use it to drive an external entry such as the toolbar magnifier. Fires only on a real change — never a `false` on mount — and stays `false` while `findBar={false}`, so a host-placed bar never lights up an entry you did not open. |
|
|
298
299
|
| `className` | `string` | Class on the scroll container. |
|
|
299
300
|
|
|
300
301
|
Ref handle (`MarkdownWysiwygEditorHandle`): `getMarkdown()`, `getHTML()`, `getJSON()`, `getEditor()`,
|
|
301
|
-
`focusComment(id)`, `nextComment(dir?)`, `getCommentIds()`, `openFind()`, `closeFind()`.
|
|
302
|
+
`focusComment(id)`, `nextComment(dir?)`, `getCommentIds()`, `openFind()`, `closeFind()`, `toggleFind()`.
|
|
303
|
+
`openFind()` keeps the same "press again" feel as <kbd>Cmd/Ctrl</kbd>+<kbd>F</kbd> — refocus the search field and
|
|
304
|
+
select the query — while `toggleFind()` flips the bar and pairs with an entry that shows an active state
|
|
305
|
+
(`onSearch={() => handle.current?.toggleFind()}`).
|
|
302
306
|
|
|
303
307
|
#### Find & replace
|
|
304
308
|
|
|
305
|
-
<kbd>Cmd/Ctrl</kbd>+<kbd>F</kbd> (while focus is inside the editor) opens a floating bar: match counter inside the search field, wrap-around next/previous, match-case and whole-word toggles, replace and replace-all.
|
|
309
|
+
<kbd>Cmd/Ctrl</kbd>+<kbd>F</kbd> (while focus is inside the editor) opens a floating bar: match counter inside the search field, wrap-around next/previous, match-case and whole-word toggles, replace and replace-all. Read-only editors (`editable={false}`) can search but not replace. <kbd>Enter</kbd> in the search field jumps to the next match — but an <kbd>Enter</kbd> that is committing an IME candidate (Chinese/Japanese input) is left alone, so confirming a word never jumps or replaces.
|
|
310
|
+
|
|
311
|
+
**Closing it.** `Esc` closes the bar, clears the highlights and returns focus to the editor; it works from anywhere that belongs to the editor (focus inside the bar, inside the document, or on the page while this is the only editor), and the keystroke is consumed so a host popover or drawer wrapping the bar does not close along with it. Other ways out: the bar's ×, `handle.closeFind()`, `toggleFind()`, and unmounting the bar. Deliberately **not** closing on: clicking into the document or another input, an empty query, zero results, after replace-all, or switching to read-only (search stays available then — only the replace row goes away). If an editor is swapped out (remounted for a new document), the library reports `onFindOpenChange(false)` so an entry elsewhere never stays lit.
|
|
312
|
+
|
|
313
|
+
`<EditorToolbar>` can carry an entry to the same bar. Pass `onSearch` (wired to the ref handle) and the magnifier appears just left of the More menu; omit it and the button does not render. Feed `searchActive` from `onFindOpenChange` and it lights up while the bar is open — pair it with `toggleFind()` so the lit button also turns the bar back off:
|
|
314
|
+
|
|
315
|
+
```tsx
|
|
316
|
+
const find = useRef<MarkdownWysiwygEditorHandle>(null)
|
|
317
|
+
const [findOpen, setFindOpen] = useState(false)
|
|
318
|
+
|
|
319
|
+
<EditorToolbar
|
|
320
|
+
editor={editor}
|
|
321
|
+
onSearch={() => find.current?.toggleFind()}
|
|
322
|
+
searchActive={findOpen}
|
|
323
|
+
/>
|
|
324
|
+
<MarkdownWysiwygEditor ref={find} onFindOpenChange={setFindOpen} />
|
|
325
|
+
```
|
|
306
326
|
|
|
307
327
|
Matching is done by the official extension, so the semantics are its semantics — worth knowing before you rely on them:
|
|
308
328
|
|
|
@@ -352,13 +372,15 @@ With `findBar={false}` the editor does not bind <kbd>Cmd/Ctrl</kbd>+<kbd>F</kbd>
|
|
|
352
372
|
|
|
353
373
|
`FindReplaceBar` is exported if you'd rather place the bar yourself, `FindLabels` / `defaultFindLabels` for i18n, and `FindAndReplace` for hand-built pipelines. Two details to reuse when driving it yourself: keep `searchDebounceMs: 0` on the extension and debounce in your own UI, and route calls through `runFindCommand()` — it absorbs a Tiptap 3.31.3 transaction mismatch that fires on the first search when the document ends with a code block / table / chart (see the comment on `runFindCommand` for the mechanism).
|
|
354
374
|
|
|
375
|
+
Styling the bar from the outside: its internals use hashed CSS-module classes, so the bar carries stable hooks instead — `[data-find-bar]` on the root, `[data-find-field="search|replace"]` on the inputs, `[data-find-counter]` on the counter, `[data-find-option="match-case|whole-words"]` on the toggles and `[data-find-action="previous|next|close|replace|replace-all"]` on the buttons. Colors stay on the theme variables (`--tmr-find-*` for the highlights, `--tmr-toolbar-*` / `--tmr-accent` for the bar chrome).
|
|
376
|
+
|
|
355
377
|
#### i18n (labels)
|
|
356
378
|
|
|
357
379
|
There is no global locale and no provider: every visible string comes from a `Partial<XLabels>` prop that is merged over built-in English defaults (`{ ...defaultToolbarLabels, ...labels }`). A host ships one object per language and passes it down; switching languages is picking a different object. Override only the keys you care about — everything else falls back to English.
|
|
358
380
|
|
|
359
381
|
| Component | Prop | Type |
|
|
360
382
|
| --- | --- | --- |
|
|
361
|
-
| `<EditorToolbar>` | `labels` | `Partial<ToolbarLabels>` (also covers the Import menu and the
|
|
383
|
+
| `<EditorToolbar>` | `labels` | `Partial<ToolbarLabels>` (also covers the Import menu, the equation popover and the link popover) |
|
|
362
384
|
| `<MarkdownWysiwygEditor>` | `findLabels` | `Partial<FindLabels>` (replace / replace-all appear as icon buttons, so their labels are the tooltip + accessible name) |
|
|
363
385
|
| `<MarkdownWysiwygEditor>` | `codeBlockLabels` | `Partial<CodeBlockLabels>` |
|
|
364
386
|
| `<TocPanel>` | `labels` | `Partial<TocLabels>` |
|
|
@@ -386,6 +408,8 @@ The underlying extensions `MarkdownPaste` / `MarkdownFileDrop` (and the `looksLi
|
|
|
386
408
|
|
|
387
409
|
### `<EditorToolbar>` (client)
|
|
388
410
|
|
|
411
|
+
The row is centred and, when the controls no longer fit the container, **wraps onto a second line** rather than scrolling sideways — a hidden control is worse than a taller toolbar. The full set (search entry included) needs about 1045 px of container width; narrower containers get a wrapped line with 4 px of vertical breathing room. `onSearch` renders the magnifier just left of the More menu.
|
|
412
|
+
|
|
389
413
|
| Prop | Type | Description |
|
|
390
414
|
| --- | --- | --- |
|
|
391
415
|
| `editor` | `Editor` | The instance from `onEditorReady`. |
|
|
@@ -396,9 +420,57 @@ The underlying extensions `MarkdownPaste` / `MarkdownFileDrop` (and the `looksLi
|
|
|
396
420
|
| `importMenuItems` | `ImportMenuItem[]` | Import dropdown options (`label` + `accept`). Hosts that want Markdown / Word / PDF pass three items. |
|
|
397
421
|
| `showImport` | `boolean` | Show the Import dropdown (default `true`). |
|
|
398
422
|
| `labels` | `Partial<ToolbarLabels>` | i18n labels. |
|
|
423
|
+
| `onSearch` | `() => void` | Search entry (magnifier) left of the More menu. Wire it to `handle.toggleFind()` (or `openFind()` if you'd rather it only ever open); omit it and no button renders. Worth passing only when the editor owns a bar (`findBar` on) — a host-placed bar has nothing for it to open. Search works read-only, so the button never greys out. |
|
|
424
|
+
| `searchActive` | `boolean` | Whether the bar is open; drives `aria-pressed` and the active highlight of the search entry (default `false`). Feed it from the editor's `onFindOpenChange`. |
|
|
399
425
|
| `extraToolbarItems` | `ExtraToolbarItem[]` | Custom items appended to the "More" menu. |
|
|
400
426
|
| `labels.inlineMath` / `blockMath` | `string` | More-menu equation items. |
|
|
401
427
|
| `labels.mathPlaceholder` / `mathDone` / `mathNewInline` / `mathNewBlock` | `string` | Equation editor (empty chip/hint, input, Done). |
|
|
428
|
+
| `labels.tableRowMenu` / `tableColumnMenu` | `string` | Aria-labels of the table hover handles. |
|
|
429
|
+
| `labels.linkPrompt` / `linkApply` / `linkOpen` / `linkRemove` / `linkInvalid` | `string` | Link popover: field placeholder, apply, open in new window, remove, rejected-address hint. |
|
|
430
|
+
|
|
431
|
+
#### Table editing (hover handles)
|
|
432
|
+
|
|
433
|
+
Hover any cell and four affordances appear: a `⋮` handle on the row's left edge, a `⋯` handle above the table aligned to the column, a `+` pill on the table's right edge (**append a column**) and a `+` pill below it (**append a row**). They are all sized to what they act on: the row handle spans the row's height, the column handle spans the column's width, and each `+` spans the table's edge it sits on. Clicking a handle selects that whole row / column and opens its menu: add before / after, delete, **Clear content**, **Reset cell styles**, plus **Delete table**. The handles ship with `<EditorToolbar>` (they are its table UI), reuse the same commands as the insert-table grid, and do not appear on read-only editors.
|
|
434
|
+
|
|
435
|
+
Right-click is still wired for exactly one case: a **multi-cell selection** (drag across cells). A handle can only express one row or one column, so batch add/delete of a rectangle keeps the context menu.
|
|
436
|
+
|
|
437
|
+
**Clear content** empties the selected cells but keeps them (a header row therefore never degrades into a data row), and **Reset cell styles** puts `colwidth` / `align` back to their schema defaults while leaving `colspan` / `rowspan` alone — those are structural, and the extension's `mergeCells` / `splitCell` are deliberately **not** exposed here: GFM has no `colspan`, so a merged cell exports as `| 1<br>2 | |` and cannot be restored on reload. Column width is a *column* property managed by the resize plugin, so clearing it needs the whole column selected (from the row handle it is restored from the widest cell in that column, which is the plugin's normal behavior).
|
|
438
|
+
|
|
439
|
+
Styling hooks (internals are hashed CSS-module classes): `[data-table-handle="row|col"]` on the handles, `[data-table-menu]` on the menu. Colors ride `--tmr-table-handle-bg` / `-fg` / `-border` (a light pill by default; hovering turns it into `--tmr-accent`).
|
|
440
|
+
|
|
441
|
+
**Not included: dragging a handle to reorder rows or columns.** That is precisely the part the official `table-node` component implements inside its own source — which is a **paid** Start-plan component (the registry answers 401 for `table-node`, `toc-node`, `drag-context-menu` while free ones answer 200), so it cannot be vendored into this MIT package. The free `@tiptap/extension-table` ships the add/delete/header/merge commands used here but no move commands; reordering would be our own transaction code on top of `TableMap`.
|
|
442
|
+
|
|
443
|
+
### `<LinkPopover>` (client)
|
|
444
|
+
|
|
445
|
+
The toolbar's link button opens this popover — type an address, apply it, open it in a new window, or remove the link. With the caret inside a link it prefills that link's href, so editing a URL no longer means deleting and re-adding it (the old `window.prompt` also could not be edited, gave no feedback for a rejected address, and is unavailable in Electron and some WebViews).
|
|
446
|
+
|
|
447
|
+
```tsx
|
|
448
|
+
<LinkPopover
|
|
449
|
+
editor={editor}
|
|
450
|
+
labels={{ field: 'Paste a link…' }}
|
|
451
|
+
trigger={<button type="button" className="my-btn"><LinkIcon /></button>}
|
|
452
|
+
/>
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
| Prop | Type | Description |
|
|
456
|
+
| --- | --- | --- |
|
|
457
|
+
| `editor` | `Editor` | The editor to operate on. |
|
|
458
|
+
| `trigger` | `ReactNode` | Your own trigger element. Radix's `asChild` takes over its click / ref / aria, so it must be a DOM element or a `forwardRef` component (the toolbar passes its `ToolbarButton`). |
|
|
459
|
+
| `labels` | `Partial<LinkPopoverLabels>` | i18n labels (`defaultLinkPopoverLabels` is exported). |
|
|
460
|
+
| `className` / `style` | `string` / `CSSProperties` | Extra class / inline style on the panel; placement stays with Radix. |
|
|
461
|
+
|
|
462
|
+
Styling hooks (internals are hashed CSS-module classes): `[data-link-popover]` on the panel, `[data-link-field]` on the input, `[data-link-action="apply|open|remove"]` on the buttons, `[data-link-invalid]` on the input while the editor rejects the address. Colors come from the theme variables (`--tmr-accent`, `--tmr-toolbar-border`, `--tmr-toolbar-muted`, `--tmr-text`, `--tmr-danger`).
|
|
463
|
+
|
|
464
|
+
The logic follows the official link-popover component (MIT) — ported, not vendored, so no Tailwind, no primitive design system and no extra runtime deps. It also fixes two flaws in that reference implementation, both caught by tests here: a rejected address used to leave the document modified (the insert-after-a-failed-`setLink` chain), and editing a link whose caret sits inside it replaced the link's text with the URL. What it does:
|
|
465
|
+
|
|
466
|
+
- applies over `extendMarkRange('link')`, so changes hit the whole link;
|
|
467
|
+
- inserts the address as its own text when the selection is empty and there is no link;
|
|
468
|
+
- removes without letting the autolink plugin re-add the link (`preventAutolink`);
|
|
469
|
+
- normalises bare domains and `host:port` with the extension's `defaultProtocol` (typing `example.com` stores `http://example.com`, matching what Tiptap's autolink does on paste — otherwise markdown would export a relative link);
|
|
470
|
+
- opens through a protocol allowlist with `noopener,noreferrer`;
|
|
471
|
+
- keeps its Enter IME-safe (the official panel's handler would apply the link while you commit a Chinese/Japanese candidate).
|
|
472
|
+
|
|
473
|
+
The commands are exported for headless or self-drawn UIs: `applyLink`, `removeLink`, `canSetLink`, `isLinkActive`, `readLinkHref`, `normalizeLinkHref`, `sanitizeLinkUrl`, `openLinkUrl`.
|
|
402
474
|
|
|
403
475
|
### `<MarkdownPreview>` (client)
|
|
404
476
|
|
|
@@ -423,13 +423,13 @@ ${fenceBody}
|
|
|
423
423
|
}
|
|
424
424
|
return out;
|
|
425
425
|
}
|
|
426
|
-
const editorHost = "
|
|
427
|
-
const findBarAnchor = "
|
|
428
|
-
const findBar = "
|
|
429
|
-
const editorScroll = "
|
|
430
|
-
const editorContent = "
|
|
431
|
-
const shortcutFab = "
|
|
432
|
-
const shortcutFabHosted = "
|
|
426
|
+
const editorHost = "_editorHost_1teg4_6";
|
|
427
|
+
const findBarAnchor = "_findBarAnchor_1teg4_13";
|
|
428
|
+
const findBar = "_findBar_1teg4_13";
|
|
429
|
+
const editorScroll = "_editorScroll_1teg4_5";
|
|
430
|
+
const editorContent = "_editorContent_1teg4_30";
|
|
431
|
+
const shortcutFab = "_shortcutFab_1teg4_391";
|
|
432
|
+
const shortcutFabHosted = "_shortcutFabHosted_1teg4_419";
|
|
433
433
|
const styles = {
|
|
434
434
|
editorHost,
|
|
435
435
|
findBarAnchor,
|
|
@@ -459,4 +459,4 @@ export {
|
|
|
459
459
|
parseChartFenceBody as p,
|
|
460
460
|
serializeChartPayload as s
|
|
461
461
|
};
|
|
462
|
-
//# sourceMappingURL=ReportContent-
|
|
462
|
+
//# sourceMappingURL=ReportContent-MgbebxPV.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReportContent-CwcppHl8.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, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>');\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 (ported from agentic-ui columnMatching).\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 aligned with agentic-ui (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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"}
|
|
1
|
+
{"version":3,"file":"ReportContent-MgbebxPV.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, '&')\n .replace(/\"/g, '"')\n .replace(/</g, '<')\n .replace(/>/g, '>');\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 (ported from agentic-ui columnMatching).\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 aligned with agentic-ui (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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"}
|
|
@@ -61,6 +61,17 @@ export interface EditorToolbarProps {
|
|
|
61
61
|
/** 是否显示导入下拉,默认 `true`。卡片等场景可关掉。 */
|
|
62
62
|
showImport?: boolean;
|
|
63
63
|
labels?: Partial<ToolbarLabels>;
|
|
64
|
+
/**
|
|
65
|
+
* 工具栏最右端的搜索入口(放大镜)。**不传就不渲染**——条子本身归
|
|
66
|
+
* `<MarkdownWysiwygEditor>`,这里只给一个入口,宿主接上自己的编辑器句柄即可:
|
|
67
|
+
* `onSearch={() => handle.current?.openFind()}`。
|
|
68
|
+
*
|
|
69
|
+
* 条子归编辑器渲染(`findBar` 开)时才接;`findBar={false}` 的宿主自己摆条子,
|
|
70
|
+
* 这个入口点了也不会弹东西。只读态照样可查,所以不随 `editable` 灰显。
|
|
71
|
+
*/
|
|
72
|
+
onSearch?: () => void;
|
|
73
|
+
/** 条子当前是否开着(驱动按钮的 aria-pressed / 高亮)。不传恒为未激活。 */
|
|
74
|
+
searchActive?: boolean;
|
|
64
75
|
/** 追加到 More 菜单末尾的自定义项。 */
|
|
65
76
|
extraToolbarItems?: ExtraToolbarItem[];
|
|
66
77
|
className?: string;
|
|
@@ -70,4 +81,4 @@ export type { ImportMenuItem };
|
|
|
70
81
|
* 顶部操作栏(antd-free,Radix Popover 菜单 + 内联 SVG 图标)。位置由父级控制,
|
|
71
82
|
* 这里只渲染按钮 + 反映 editor 激活态。不含业务耦合,扩展项经 extraToolbarItems 注入。
|
|
72
83
|
*/
|
|
73
|
-
export declare function EditorToolbar({ editor, onImageUpload, onError, onImportDocument, importAccept, importMenuItems, showImport, labels, extraToolbarItems, className, }: EditorToolbarProps): import("react").JSX.Element;
|
|
84
|
+
export declare function EditorToolbar({ editor, onImageUpload, onError, onImportDocument, importAccept, importMenuItems, showImport, labels, onSearch, searchActive, extraToolbarItems, className, }: EditorToolbarProps): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -17,11 +17,4 @@ export interface FindReplaceBarProps {
|
|
|
17
17
|
/** 根节点内联样式(编辑器用它落 findBarOffset;宿主自绘时也可用)。 */
|
|
18
18
|
style?: CSSProperties;
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
* 查找替换浮动条。匹配、高亮、替换全部由官方
|
|
22
|
-
* `@tiptap/extension-find-and-replace` 承担(含 RE2 安全正则、跨标记匹配、单事务全部替换),
|
|
23
|
-
* 这里只做输入、计数、开关与键位。
|
|
24
|
-
*
|
|
25
|
-
* 打开即把查询推给编辑器,关闭时 `clearSearch()`——高亮是会话级的,不留残影。
|
|
26
|
-
*/
|
|
27
20
|
export declare function FindReplaceBar({ editor, onClose, labels, inputRef, className, style, }: FindReplaceBarProps): import("react").JSX.Element | null;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/react';
|
|
2
|
+
import { CSSProperties, ReactNode } from 'react';
|
|
3
|
+
import { LinkPopoverLabels } from '../labels';
|
|
4
|
+
export interface LinkPopoverProps {
|
|
5
|
+
/** 编辑器实例。 */
|
|
6
|
+
editor: Editor;
|
|
7
|
+
/**
|
|
8
|
+
* 触发器:传宿主自己的按钮元素。Radix 的 `asChild` 要接管它的点击 / ref / aria——
|
|
9
|
+
* 传函数组件时得先 `forwardRef`(本库的 `<EditorToolbar>` 就是这么接的)。
|
|
10
|
+
*/
|
|
11
|
+
trigger: ReactNode;
|
|
12
|
+
labels?: Partial<LinkPopoverLabels>;
|
|
13
|
+
/** 挂到浮层根节点上的 class / 内联样式(定位与层级归宿主,Radix 只管浮层方向)。 */
|
|
14
|
+
className?: string;
|
|
15
|
+
style?: CSSProperties;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 链接编辑浮层:输入地址 + 应用 / 在新窗口打开 / 移除。
|
|
19
|
+
*
|
|
20
|
+
* 逻辑全在 `linkEditing.ts`(从官方 UI Components 的 `useLinkPopover` 搬的),这里只负责输入、
|
|
21
|
+
* 按钮与状态。取代表驱动器的 `window.prompt`:能**编辑已有链接**(打开即预填当前地址,
|
|
22
|
+
* 而旧实现是「在链接里点一下就删除」)、能打开、能移除,且 `window.prompt` 在 Electron /
|
|
23
|
+
* 部分 WebView 里根本不可用。
|
|
24
|
+
*
|
|
25
|
+
* 地址被编辑器拒掉时(Tiptap 的 `isAllowedUri`)浮层不关,输入框转成危险色并带上 title,
|
|
26
|
+
* 让用户改——静默什么都不发生是最难查的。
|
|
27
|
+
*/
|
|
28
|
+
export declare function LinkPopover({ editor, trigger, labels, className, style, }: LinkPopoverProps): import("react").JSX.Element;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -32,6 +32,11 @@ export interface MarkdownWysiwygEditorHandle {
|
|
|
32
32
|
openFind: () => void;
|
|
33
33
|
/** 收起自带浮动条,清空高亮并把焦点还给编辑器。findBar={false} 时不生效。 */
|
|
34
34
|
closeFind: () => void;
|
|
35
|
+
/**
|
|
36
|
+
* 反转自带浮动条:关着就开、开着就关。工具栏那个带 active 态的放大镜接它
|
|
37
|
+
* (`onSearch={() => handle.current?.toggleFind()}`)。findBar={false} 时不生效。
|
|
38
|
+
*/
|
|
39
|
+
toggleFind: () => void;
|
|
35
40
|
}
|
|
36
41
|
export interface MarkdownWysiwygEditorProps {
|
|
37
42
|
/** 初始 markdown 内容。 */
|
|
@@ -145,6 +150,13 @@ export interface MarkdownWysiwygEditorProps {
|
|
|
145
150
|
findShortcut?: boolean;
|
|
146
151
|
/** 查找替换浮动条的本地化文案。 */
|
|
147
152
|
findLabels?: Partial<FindLabels>;
|
|
153
|
+
/**
|
|
154
|
+
* 浮动条开合状态变化的通知(条子开着时 `handle.openFind()`、Esc / × 关掉都会触发)。
|
|
155
|
+
* 典型用法是驱动别处的入口按钮:`<EditorToolbar onSearch={open} searchActive={isOpen} />`。
|
|
156
|
+
* 只在真正变化时回调,挂载时不会先报一次 `false`;报的是「条子真的在屏幕上」——
|
|
157
|
+
* `findBar={false}`(宿主自己摆条子)时恒为 `false`,不会因为 `openFind()` 空转就亮起入口。
|
|
158
|
+
*/
|
|
159
|
+
onFindOpenChange?: (open: boolean) => void;
|
|
148
160
|
/**
|
|
149
161
|
* 启用快捷键抽屉:编辑器右下角出现键盘悬浮按钮,点开为
|
|
150
162
|
* 「格式 / 快捷键 / Markdown」三列对照抽屉(默认 true,只读态隐藏)。
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Editor } from '@tiptap/core';
|
|
2
|
+
import { ToolbarLabels } from '../labels';
|
|
3
|
+
export interface TableHandlesProps {
|
|
4
|
+
/** 编辑器实例。只读态不出手柄。 */
|
|
5
|
+
editor: Editor;
|
|
6
|
+
/** 已合并默认值的工具栏文案(行列菜单项与两个手柄的 aria-label 都取自它)。 */
|
|
7
|
+
labels: ToolbarLabels;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* 表格的悬停手柄(Notion 式):鼠标落在单元格上时,行左缘出一条竖胶囊(⋮)、表格上方出一条
|
|
11
|
+
* 横胶囊(⋯),点开就是该行 / 该列的操作菜单。
|
|
12
|
+
*
|
|
13
|
+
* 这是原来「表内右键出菜单」的替代——右键只保留**多格选区**的批量增删(手柄只覆盖单行单列,
|
|
14
|
+
* 那个场景手柄表达不了)。命令全部复用 `tableSelection.ts`,与工具栏的「插入表格」共用一套。
|
|
15
|
+
*
|
|
16
|
+
* 手柄是 fixed 定位、渲染在 body 的 portal 里:它要贴着单元格边缘,而工具栏并不在编辑器 DOM
|
|
17
|
+
* 内。滚动 / 缩放时重新量一遍悬停的那颗单元格,让手柄跟着走。
|
|
18
|
+
*/
|
|
19
|
+
export declare function TableHandles({ editor, labels }: TableHandlesProps): import('react').ReactPortal | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -12,7 +12,7 @@ import { Color, TextStyle, FontSize } from "@tiptap/extension-text-style";
|
|
|
12
12
|
import StarterKit from "@tiptap/starter-kit";
|
|
13
13
|
import { createLowlight, common } from "lowlight";
|
|
14
14
|
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
15
|
-
import { p as parseChartFenceBody, s as serializeChartPayload, n as normalizeChartMarkdown } from "./ReportContent-
|
|
15
|
+
import { p as parseChartFenceBody, s as serializeChartPayload, n as normalizeChartMarkdown } from "./ReportContent-MgbebxPV.js";
|
|
16
16
|
import { InlineMath, BlockMath } from "@tiptap/extension-mathematics";
|
|
17
17
|
import katex from "katex";
|
|
18
18
|
const CitationRef = Node.create({
|
|
@@ -698,6 +698,44 @@ const MarkdownTextStyle = TextStyle.extend({
|
|
|
698
698
|
return `<span style="${styles.join(";")}">${children}</span>`;
|
|
699
699
|
}
|
|
700
700
|
});
|
|
701
|
+
function htmlTagTokenizer(tag, tokenType) {
|
|
702
|
+
const opening = new RegExp(`<${tag}(?:\\s[^>]*)?>`, "i");
|
|
703
|
+
const pair = new RegExp(
|
|
704
|
+
`^<${tag}(?:\\s[^>]*)?>([\\s\\S]*?)<\\/${tag}\\s*>`,
|
|
705
|
+
"i"
|
|
706
|
+
);
|
|
707
|
+
return {
|
|
708
|
+
name: tokenType,
|
|
709
|
+
level: "inline",
|
|
710
|
+
start(src) {
|
|
711
|
+
const match = opening.exec(src);
|
|
712
|
+
return match ? match.index : -1;
|
|
713
|
+
},
|
|
714
|
+
tokenize(src, _tokens, lexer) {
|
|
715
|
+
const match = pair.exec(src);
|
|
716
|
+
if (!match) return void 0;
|
|
717
|
+
return {
|
|
718
|
+
type: tokenType,
|
|
719
|
+
raw: match[0],
|
|
720
|
+
tokens: lexer.inlineTokens(match[1])
|
|
721
|
+
};
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
const MarkdownSuperscript = Superscript.extend({
|
|
726
|
+
renderMarkdown(node, helpers) {
|
|
727
|
+
return `<sup>${helpers.renderChildren(node)}</sup>`;
|
|
728
|
+
},
|
|
729
|
+
markdownTokenizer: htmlTagTokenizer("sup", "superscript"),
|
|
730
|
+
parseMarkdown: (token, helpers) => helpers.applyMark("superscript", helpers.parseInline(token.tokens ?? []))
|
|
731
|
+
});
|
|
732
|
+
const MarkdownSubscript = Subscript.extend({
|
|
733
|
+
renderMarkdown(node, helpers) {
|
|
734
|
+
return `<sub>${helpers.renderChildren(node)}</sub>`;
|
|
735
|
+
},
|
|
736
|
+
markdownTokenizer: htmlTagTokenizer("sub", "subscript"),
|
|
737
|
+
parseMarkdown: (token, helpers) => helpers.applyMark("subscript", helpers.parseInline(token.tokens ?? []))
|
|
738
|
+
});
|
|
701
739
|
const pureCodeBlock = CodeBlockLowlight.extend({
|
|
702
740
|
parseMarkdown: (token, helpers) => parseCodeTokenAsChartOrCodeBlock(token, helpers, "codeBlock")
|
|
703
741
|
}).configure({ lowlight });
|
|
@@ -712,8 +750,8 @@ const baseExtensions = [
|
|
|
712
750
|
// 的 <span> 上(如 style="font-size: 16px"),与 Color 同基座,三端共用。
|
|
713
751
|
FontSize.configure({ types: [TextStyle.name] }),
|
|
714
752
|
Highlight.configure({ multicolor: true }),
|
|
715
|
-
|
|
716
|
-
|
|
753
|
+
MarkdownSuperscript,
|
|
754
|
+
MarkdownSubscript,
|
|
717
755
|
TableKit.configure({
|
|
718
756
|
table: {
|
|
719
757
|
resizable: true,
|
|
@@ -822,4 +860,4 @@ export {
|
|
|
822
860
|
renderMathElementHtml as r,
|
|
823
861
|
stabilizeMarkdown as s
|
|
824
862
|
};
|
|
825
|
-
//# sourceMappingURL=extractToc-
|
|
863
|
+
//# sourceMappingURL=extractToc-BaR6LP1f.js.map
|