tiptap-markdown-react 0.19.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +24 -49
  2. package/THIRD_PARTY_LICENSES.md +155 -1
  3. package/dist/{ReportContent-B8jIzhy3.js → ReportContent-B7okwVW0.js} +18 -8
  4. package/dist/{ReportContent-B8jIzhy3.js.map → ReportContent-B7okwVW0.js.map} +1 -1
  5. package/dist/{scrollToTocHeading-B2QmL4wi.js → ReportContentWithCharts-CDwS4OH9.js} +27 -27
  6. package/dist/ReportContentWithCharts-CDwS4OH9.js.map +1 -0
  7. package/dist/blockDrag/actions.d.ts +30 -0
  8. package/dist/blockDrag/actions.test.d.ts +1 -0
  9. package/dist/blockDrag/listDrop.d.ts +21 -0
  10. package/dist/blockDrag/listDrop.test.d.ts +1 -0
  11. package/dist/blurSelection.dom.test.d.ts +1 -0
  12. package/dist/characterCount.d.ts +1 -0
  13. package/dist/characterCount.dom.test.d.ts +1 -0
  14. package/dist/characterCount.test.d.ts +1 -0
  15. package/dist/chart/createChart.d.ts +0 -3
  16. package/dist/components/BlockDragHandle.d.ts +14 -0
  17. package/dist/components/BlockDragHandle.dom.test.d.ts +1 -0
  18. package/dist/components/CodeBlockView.d.ts +2 -2
  19. package/dist/components/ColorPalette.d.ts +4 -3
  20. package/dist/components/EditorToc.d.ts +13 -0
  21. package/dist/components/EditorToolbar.d.ts +4 -3
  22. package/dist/components/FindReplaceBar.d.ts +4 -3
  23. package/dist/components/LinkPopover.d.ts +4 -3
  24. package/dist/components/MarkdownPreview.d.ts +4 -1
  25. package/dist/components/MarkdownWysiwygEditor.d.ts +39 -10
  26. package/dist/detailsMarkdown.d.ts +4 -0
  27. package/dist/detailsMarkdown.test.d.ts +1 -0
  28. package/dist/emoji/EmojiSuggestion.d.ts +23 -0
  29. package/dist/emoji/emojiCategories.d.ts +52 -0
  30. package/dist/emoji/emojiMarkdown.test.d.ts +1 -0
  31. package/dist/emoji/emojiSuggestion.dom.test.d.ts +1 -0
  32. package/dist/emoji/filterEmoji.d.ts +7 -0
  33. package/dist/emoji/markdownEmoji.d.ts +8 -0
  34. package/dist/{extractToc-CGrBMGvM.js → extractToc-B50WvHao.js} +659 -5
  35. package/dist/extractToc-B50WvHao.js.map +1 -0
  36. package/dist/fonts/OFL.txt +95 -0
  37. package/dist/i18n/index.d.ts +4 -0
  38. package/dist/i18n/store.d.ts +16 -0
  39. package/dist/i18n/types.d.ts +19 -0
  40. package/dist/i18n/zh.d.ts +3 -0
  41. package/dist/icons/index.d.ts +5 -0
  42. package/dist/imageUpload.d.ts +0 -3
  43. package/dist/index.d.ts +1 -2
  44. package/dist/index.js +2307 -652
  45. package/dist/index.js.map +1 -1
  46. package/dist/labels.d.ts +56 -2
  47. package/dist/numberingFormat.d.ts +67 -0
  48. package/dist/numberingFormat.test.d.ts +1 -0
  49. package/dist/reader.js +2 -2
  50. package/dist/server.js +4 -4
  51. package/dist/shortcuts/ShortcutPanel.d.ts +4 -4
  52. package/dist/shortcuts/types.d.ts +2 -2
  53. package/dist/slashMenu/SlashMenuExtension.d.ts +0 -3
  54. package/dist/slashMenu/items.d.ts +2 -2
  55. package/dist/style-inline.css +2141 -384
  56. package/dist/style.css +2141 -384
  57. package/dist/typography.test.d.ts +1 -0
  58. package/package.json +19 -3
  59. package/dist/components/TocPanel.d.ts +0 -20
  60. package/dist/extractToc-CGrBMGvM.js.map +0 -1
  61. package/dist/scrollToTocHeading-B2QmL4wi.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
  import { jsxs, jsx } from "react/jsx-runtime";
3
3
  import { useRef, useEffect } from "react";
4
- import { R as ReportContent, p as parseChartFenceBody } from "./ReportContent-B8jIzhy3.js";
4
+ import { R as ReportContent, p as parseChartFenceBody } from "./ReportContent-B7okwVW0.js";
5
5
  import { Chart, CategoryScale, LinearScale, PointElement, LineElement, BarElement, ArcElement, LineController, BarController, PieController, DoughnutController, Filler, Legend, Tooltip } from "chart.js";
6
6
  function toNumber(value) {
7
7
  if (typeof value === "number" && !Number.isNaN(value)) return value;
@@ -244,6 +244,31 @@ function renderOrUpdateChart({
244
244
  function destroyChart(chart) {
245
245
  chart == null ? void 0 : chart.destroy();
246
246
  }
247
+ function easeOutCubic(t) {
248
+ return 1 - (1 - t) * (1 - t) * (1 - t);
249
+ }
250
+ function scrollToTocHeading(headingId, scrollContainer, options) {
251
+ const el = scrollContainer.querySelector(
252
+ `[data-toc-id="${CSS.escape(headingId)}"]`
253
+ );
254
+ if (!el) return;
255
+ const startTop = scrollContainer.scrollTop;
256
+ const rootRect = scrollContainer.getBoundingClientRect();
257
+ const elRect = el.getBoundingClientRect();
258
+ const targetTop = startTop + (elRect.top - rootRect.top);
259
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
260
+ scrollContainer.scrollTop = targetTop;
261
+ return;
262
+ }
263
+ const duration = (options == null ? void 0 : options.duration) ?? 400;
264
+ const start = performance.now();
265
+ const animate = () => {
266
+ const t = Math.min((performance.now() - start) / duration, 1);
267
+ scrollContainer.scrollTop = startTop + (targetTop - startTop) * easeOutCubic(t);
268
+ if (t < 1) requestAnimationFrame(animate);
269
+ };
270
+ requestAnimationFrame(animate);
271
+ }
247
272
  function findCitationRefElement(target, root) {
248
273
  let node = null;
249
274
  if (target instanceof Element) node = target;
@@ -456,31 +481,6 @@ function ReportContentWithCharts({
456
481
  ) : null
457
482
  ] });
458
483
  }
459
- function easeOutCubic(t) {
460
- return 1 - (1 - t) * (1 - t) * (1 - t);
461
- }
462
- function scrollToTocHeading(headingId, scrollContainer, options) {
463
- const el = scrollContainer.querySelector(
464
- `[data-toc-id="${CSS.escape(headingId)}"]`
465
- );
466
- if (!el) return;
467
- const startTop = scrollContainer.scrollTop;
468
- const rootRect = scrollContainer.getBoundingClientRect();
469
- const elRect = el.getBoundingClientRect();
470
- const targetTop = startTop + (elRect.top - rootRect.top);
471
- if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
472
- scrollContainer.scrollTop = targetTop;
473
- return;
474
- }
475
- const duration = (options == null ? void 0 : options.duration) ?? 400;
476
- const start = performance.now();
477
- const animate = () => {
478
- const t = Math.min((performance.now() - start) / duration, 1);
479
- scrollContainer.scrollTop = startTop + (targetTop - startTop) * easeOutCubic(t);
480
- if (t < 1) requestAnimationFrame(animate);
481
- };
482
- requestAnimationFrame(animate);
483
- }
484
484
  export {
485
485
  ChartMount as C,
486
486
  ReportContentInteractive as R,
@@ -493,4 +493,4 @@ export {
493
493
  readCitationAttrs as r,
494
494
  scrollToTocHeading as s
495
495
  };
496
- //# sourceMappingURL=scrollToTocHeading-B2QmL4wi.js.map
496
+ //# sourceMappingURL=ReportContentWithCharts-CDwS4OH9.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReportContentWithCharts-CDwS4OH9.js","sources":["../src/chart/mapData.ts","../src/chart/theme.ts","../src/chart/renderChart.ts","../src/toc/scrollToTocHeading.ts","../src/citationDom.ts","../src/components/CitationInteractive.tsx","../src/components/ReportContentInteractive.tsx","../src/components/ChartMount.tsx","../src/components/ReportContentWithCharts.tsx"],"sourcesContent":["import type { ChartConfig, ChartPayload } from './types';\n\nexport interface ChartJsPoint {\n x: string | number;\n y: number;\n category?: string;\n type?: string;\n}\n\nfunction toNumber(value: unknown): number {\n if (typeof value === 'number' && !Number.isNaN(value)) return value;\n if (typeof value === 'string') {\n const n = Number(value.replace(/,/g, ''));\n if (!Number.isNaN(n)) return n;\n }\n return 0;\n}\n\n/**\n * Map table rows to flat chart points for one config.\n * colorLegend → series `type`; groupBy stored as category (no UI filter in v1).\n */\nexport function mapChartDataItems(\n config: ChartConfig,\n dataSource: Record<string, unknown>[],\n): ChartJsPoint[] {\n const xKey = config.x!;\n const yKey = config.y!;\n const legendKey = config.colorLegend;\n const groupKey = config.groupBy;\n\n let rows = [...dataSource];\n const sortKey = config.sortBy;\n if (sortKey) {\n rows.sort((a, b) => {\n const av = a[sortKey];\n const bv = b[sortKey];\n if (typeof av === 'number' && typeof bv === 'number') return av - bv;\n return String(av ?? '').localeCompare(String(bv ?? ''), undefined, {\n numeric: true,\n });\n });\n }\n\n return rows.map((row) => {\n const point: ChartJsPoint = {\n x: row[xKey] as string | number,\n y: toNumber(row[yKey]),\n };\n if (legendKey) point.type = String(row[legendKey] ?? '');\n if (groupKey) point.category = String(row[groupKey] ?? '');\n return point;\n });\n}\n\nexport interface BuiltChartData {\n labels: string[];\n datasets: {\n label: string;\n data: number[];\n }[];\n isCartesian: boolean;\n}\n\n/** Build Chart.js labels/datasets for one active config. */\nexport function buildChartJsData(\n config: ChartConfig,\n payload: Pick<ChartPayload, 'dataSource'>,\n): BuiltChartData {\n const points = mapChartDataItems(config, payload.dataSource);\n const type = config.chartType;\n const isCartesian = !['pie', 'donut'].includes(type);\n\n if (!isCartesian) {\n return {\n labels: points.map((p) => String(p.x)),\n datasets: [\n {\n label: config.title || config.y || 'value',\n data: points.map((p) => p.y),\n },\n ],\n isCartesian: false,\n };\n }\n\n const seriesKeys = new Set<string>();\n for (const p of points) {\n seriesKeys.add(p.type || config.title || config.y || 'series');\n }\n const seriesList = [...seriesKeys];\n const labelSet: string[] = [];\n for (const p of points) {\n const lab = String(p.x);\n if (!labelSet.includes(lab)) labelSet.push(lab);\n }\n\n const datasets = seriesList.map((series) => ({\n label: series,\n data: labelSet.map((lab) => {\n const hit = points.find(\n (p) =>\n String(p.x) === lab &&\n (p.type || config.title || config.y || 'series') === series,\n );\n return hit ? hit.y : 0;\n }),\n }));\n\n return { labels: labelSet, datasets, isCartesian: true };\n}\n","import type { ChartTheme } from './types';\n\nconst DEFAULT_PALETTE = [\n '#3b82f6',\n '#10b981',\n '#f59e0b',\n '#ef4444',\n '#8b5cf6',\n '#06b6d4',\n '#ec4899',\n '#84cc16',\n];\n\nfunction readCssVar(\n el: HTMLElement | null,\n name: string,\n fallback: string,\n): string {\n if (!el || typeof getComputedStyle === 'undefined') return fallback;\n const v = getComputedStyle(el).getPropertyValue(name).trim();\n return v || fallback;\n}\n\n/** Resolve theme from --tmr-* CSS variables + optional host override. */\nexport function resolveChartTheme(\n host: HTMLElement | null,\n override?: ChartTheme | null,\n): Required<\n Pick<\n ChartTheme,\n | 'backgroundColor'\n | 'textColor'\n | 'gridColor'\n | 'borderColor'\n | 'fontFamily'\n | 'palette'\n >\n> {\n const base = {\n backgroundColor: readCssVar(host, '--tmr-bg', '#ffffff'),\n textColor: readCssVar(host, '--tmr-fg', '#1f2937'),\n gridColor: readCssVar(host, '--tmr-border', '#e5e7eb'),\n borderColor: readCssVar(host, '--tmr-border', '#e5e7eb'),\n fontFamily: readCssVar(\n host,\n '--tmr-font',\n 'ui-sans-serif, system-ui, sans-serif',\n ),\n palette: DEFAULT_PALETTE,\n };\n if (!override) return base;\n return {\n backgroundColor: override.backgroundColor ?? base.backgroundColor,\n textColor: override.textColor ?? base.textColor,\n gridColor: override.gridColor ?? base.gridColor,\n borderColor: override.borderColor ?? base.borderColor,\n fontFamily: override.fontFamily ?? base.fontFamily,\n palette:\n override.palette && override.palette.length > 0\n ? override.palette\n : base.palette,\n };\n}\n","import {\n ArcElement,\n BarController,\n BarElement,\n CategoryScale,\n Chart,\n DoughnutController,\n Filler,\n Legend,\n LineController,\n LineElement,\n LinearScale,\n PieController,\n PointElement,\n Tooltip,\n type ChartConfiguration,\n type ChartType,\n} from 'chart.js';\nimport { buildChartJsData } from './mapData';\nimport { resolveChartTheme } from './theme';\nimport type { ChartConfig, ChartPayload, ChartTheme } from './types';\n\nlet registered = false;\n\nfunction ensureRegister() {\n if (registered) return;\n if (typeof window === 'undefined') return;\n Chart.register(\n CategoryScale,\n LinearScale,\n PointElement,\n LineElement,\n BarElement,\n ArcElement,\n LineController,\n BarController,\n PieController,\n DoughnutController,\n Filler,\n Legend,\n Tooltip,\n );\n registered = true;\n}\n\nfunction toChartJsType(chartType: string): ChartType {\n switch (chartType) {\n case 'bar':\n return 'bar';\n case 'column':\n return 'bar';\n case 'line':\n return 'line';\n case 'area':\n return 'line';\n case 'pie':\n return 'pie';\n case 'donut':\n return 'doughnut';\n default:\n return 'bar';\n }\n}\n\nexport interface RenderChartOptions {\n canvas: HTMLCanvasElement;\n config: ChartConfig;\n payload: Pick<ChartPayload, 'dataSource' | 'columns'>;\n theme?: ChartTheme | null;\n /** Existing instance to update in place (streaming). */\n chart?: Chart | null;\n}\n\nexport function renderOrUpdateChart({\n canvas,\n config,\n payload,\n theme,\n chart,\n}: RenderChartOptions): Chart | null {\n if (typeof window === 'undefined') return null;\n ensureRegister();\n\n const host = canvas.parentElement;\n const colors = resolveChartTheme(host, theme);\n const built = buildChartJsData(config, payload);\n const jsType = toChartJsType(config.chartType);\n const isBarHorizontal = config.chartType === 'bar';\n const isArea = config.chartType === 'area';\n\n const datasets = built.datasets.map((ds, i) => {\n const color = colors.palette[i % colors.palette.length];\n if (!built.isCartesian) {\n return {\n ...ds,\n backgroundColor: built.labels.map(\n (_, j) => colors.palette[j % colors.palette.length],\n ),\n borderWidth: 0,\n };\n }\n return {\n ...ds,\n backgroundColor: isArea ? `${color}33` : color,\n borderColor: color,\n borderWidth: 2,\n fill: isArea,\n tension: 0.25,\n };\n });\n\n const configuration: ChartConfiguration = {\n type: jsType,\n data: {\n labels: built.labels,\n datasets,\n },\n options: {\n indexAxis: isBarHorizontal ? 'y' : 'x',\n responsive: true,\n maintainAspectRatio: false,\n plugins: {\n legend: {\n display: built.datasets.length > 1 || !built.isCartesian,\n labels: {\n color: colors.textColor,\n font: { family: colors.fontFamily },\n },\n },\n title: { display: false },\n tooltip: {\n titleFont: { family: colors.fontFamily },\n bodyFont: { family: colors.fontFamily },\n },\n },\n scales: built.isCartesian\n ? {\n x: {\n ticks: {\n color: colors.textColor,\n font: { family: colors.fontFamily },\n },\n grid: { color: colors.gridColor },\n border: { color: colors.borderColor },\n },\n y: {\n ticks: {\n color: colors.textColor,\n font: { family: colors.fontFamily },\n },\n grid: { color: colors.gridColor },\n border: { color: colors.borderColor },\n },\n }\n : undefined,\n },\n };\n\n if (chart) {\n (chart.config as { type: ChartType }).type = jsType;\n chart.data.labels = configuration.data.labels;\n chart.data.datasets = configuration.data.datasets as never;\n chart.options = configuration.options as never;\n chart.update('none');\n return chart;\n }\n\n return new Chart(canvas, configuration);\n}\n\nexport function destroyChart(chart: Chart | null | undefined) {\n chart?.destroy();\n}\n","/**\n * 目录点击 → 平滑滚到对应标题。\n *\n * 不依赖浏览器 `scrollTo({behavior:'smooth'})` / `scrollIntoView({behavior:'smooth'})`——\n * 在 flex + overflow 嵌套布局下(父级 `overflow:hidden`、自身 `flex:1; overflow-y:auto`),\n * Chrome 的 compositor 驱动 smooth scroll 会静默失效。\n *\n * 改用 `requestAnimationFrame` + easeOutCubic 缓动,直接操作 `scrollTop`,\n * 主线程一次性 400ms 动画,点击触发的单次滚动无感知性能差异。\n *\n * 尊重 `prefers-reduced-motion: reduce`——此时瞬间跳转,无动画。\n */\n\n/** 缓动:easeOutCubic。t ∈ [0, 1]。 */\nfunction easeOutCubic(t: number): number {\n return 1 - (1 - t) * (1 - t) * (1 - t);\n}\n\n/**\n * 平滑滚动(或瞬间跳转)到指定 `data-toc-id` 的标题。\n *\n * @param headingId - TOC item 的 id,对应 DOM 上 `data-toc-id` 属性值\n * @param scrollContainer - 滚动容器(如 `.scrollArea`)\n * @param options - `duration`:动画时长 ms,默认 400\n */\nexport function scrollToTocHeading(\n headingId: string,\n scrollContainer: HTMLElement,\n options?: { duration?: number },\n): void {\n const el = scrollContainer.querySelector<HTMLElement>(\n `[data-toc-id=\"${CSS.escape(headingId)}\"]`,\n );\n if (!el) return;\n\n const startTop = scrollContainer.scrollTop;\n const rootRect = scrollContainer.getBoundingClientRect();\n const elRect = el.getBoundingClientRect();\n const targetTop = startTop + (elRect.top - rootRect.top);\n\n // prefers-reduced-motion:瞬间跳转\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {\n scrollContainer.scrollTop = targetTop;\n return;\n }\n\n // 手动平滑滚动\n const duration = options?.duration ?? 400;\n const start = performance.now();\n const animate = () => {\n const t = Math.min((performance.now() - start) / duration, 1);\n scrollContainer.scrollTop = startTop + (targetTop - startTop) * easeOutCubic(t);\n if (t < 1) requestAnimationFrame(animate);\n };\n requestAnimationFrame(animate);\n}\n","/**\n * 从事件目标向上找到 `.citation-ref`(且必须在 root 内)。\n * SSR 阅读页事件委托与单测共用。\n */\nexport function findCitationRefElement(\n target: EventTarget | null,\n root: ParentNode,\n): HTMLElement | null {\n let node: Node | null = null;\n if (target instanceof Element) node = target;\n else if (target instanceof Text) node = target.parentElement;\n else if (target instanceof Node) node = target.parentElement;\n if (!(node instanceof Element)) return null;\n const el = node.closest('.citation-ref');\n if (!(el instanceof HTMLElement)) return null;\n if (!root.contains(el)) return null;\n return el;\n}\n\n/** 从圆标 DOM 读取 attrs(与 CitationRef renderHTML 的 data-* 对齐)。 */\nexport function readCitationAttrs(el: HTMLElement): {\n index: string;\n url: string | null;\n title: string | null;\n} {\n return {\n index: el.getAttribute('data-index') || el.textContent?.trim() || '',\n url: el.getAttribute('data-url'),\n title: el.getAttribute('data-title'),\n };\n}\n","'use client';\n\nimport { useEffect, useRef, type RefObject } from 'react';\nimport {\n findCitationRefElement,\n readCitationAttrs,\n} from '../citationDom';\nimport type {\n OnCitationEnter,\n OnCitationLeave,\n} from '../citationTypes';\n\nexport interface CitationInteractiveProps {\n /**\n * 包含 `ReportContent` HTML 的根节点(其内应有 `.citation-ref`)。\n * 用同一 ref 包住 ReportContent 即可。\n */\n containerRef: RefObject<HTMLElement | null>;\n /** 点击圆标。宿主据此 setState 打开自己的 Popover。 */\n onCitationEnter: OnCitationEnter;\n /**\n * 点正文非圆标处 / 再点同一圆标 / Escape。\n * 库立即回调;浮层开关由宿主管。\n */\n onCitationLeave: OnCitationLeave;\n}\n\n/**\n * SSR 阅读页脚注增强:只做 **click** 事件委托,不持有 open 状态。\n *\n * ```tsx\n * <CitationInteractive\n * containerRef={ref}\n * onCitationEnter={setActive}\n * onCitationLeave={() => setActive(null)}\n * />\n * ```\n */\nexport function CitationInteractive({\n containerRef,\n onCitationEnter,\n onCitationLeave,\n}: CitationInteractiveProps) {\n const enterRef = useRef(onCitationEnter);\n const leaveRef = useRef(onCitationLeave);\n enterRef.current = onCitationEnter;\n leaveRef.current = onCitationLeave;\n\n useEffect(() => {\n const root = containerRef.current;\n if (!root) return;\n\n let currentEl: HTMLElement | null = null;\n\n const emitEnter = (el: HTMLElement) => {\n currentEl = el;\n const attrs = readCitationAttrs(el);\n enterRef.current({ index: attrs.index, attrs, anchorEl: el });\n };\n\n const emitLeave = () => {\n if (!currentEl) return;\n currentEl = null;\n leaveRef.current();\n };\n\n const ACTIVE_ATTR = 'data-tmr-citation-active';\n\n const onClick = (e: MouseEvent) => {\n const el = findCitationRefElement(e.target, root);\n if (el) {\n // 捕获阶段就取消默认,避免圆标内 <a> 跳转\n e.preventDefault();\n e.stopPropagation();\n // 仅当该圆标仍处于打开态时再点才关闭(宿主可能已外部关闭,attr 已清)\n if (el === currentEl && el.hasAttribute(ACTIVE_ATTR)) {\n emitLeave();\n return;\n }\n emitEnter(el);\n return;\n }\n emitLeave();\n };\n\n const onKeyDown = (e: KeyboardEvent) => {\n if (e.key === 'Escape') emitLeave();\n };\n\n root.addEventListener('click', onClick, true);\n document.addEventListener('keydown', onKeyDown);\n\n return () => {\n root.removeEventListener('click', onClick, true);\n document.removeEventListener('keydown', onKeyDown);\n };\n }, [containerRef]);\n\n return null;\n}\n","'use client';\n\nimport { useRef, type ComponentProps } from 'react';\nimport type { OnCitationEnter, OnCitationLeave } from '../citationTypes';\nimport { ReportContent } from '../ReportContent';\nimport { CitationInteractive } from './CitationInteractive';\n\nexport interface ReportContentInteractiveProps\n extends Omit<ComponentProps<typeof ReportContent>, 'ref'> {\n /** 点击圆标:宿主 setState 打开自己的 Popover。 */\n onCitationEnter: OnCitationEnter;\n /** 点外部 / 再点圆标 / Escape:宿主关闭。 */\n onCitationLeave: OnCitationLeave;\n}\n\n/**\n * SSR HTML 阅读 + 脚注 click 委托的便捷封装。\n * 内部:`ReportContent` + `CitationInteractive`(只报事件,仅 click)。\n */\nexport function ReportContentInteractive({\n onCitationEnter,\n onCitationLeave,\n className,\n html,\n ...rest\n}: ReportContentInteractiveProps) {\n const ref = useRef<HTMLDivElement>(null);\n\n return (\n <div ref={ref} className={className}>\n <ReportContent html={html} {...rest} />\n <CitationInteractive\n containerRef={ref}\n onCitationEnter={onCitationEnter}\n onCitationLeave={onCitationLeave}\n />\n </div>\n );\n}\n","'use client';\n\nimport type { Chart as ChartJs } from 'chart.js';\nimport { useEffect, useRef, type RefObject } from 'react';\nimport { parseChartFenceBody } from '../chart/parse';\nimport { destroyChart, renderOrUpdateChart } from '../chart/renderChart';\nimport type { ChartTheme } from '../chart/types';\nimport '../styles/chart.css';\n\nexport interface ChartMountProps {\n containerRef: RefObject<HTMLElement | null>;\n chartTheme?: ChartTheme | null;\n /** Re-scan when this changes (e.g. html string). */\n html?: string;\n}\n\nfunction hydrateOne(\n el: HTMLElement,\n theme: ChartTheme | null | undefined,\n registry: Map<HTMLElement, ChartJs>,\n) {\n const raw = el.getAttribute('data-chart');\n const payload = parseChartFenceBody(raw ?? '');\n if (!payload?.config.length) return;\n\n let host = el.querySelector('.tmr-chart-canvas-host') as HTMLElement | null;\n if (!host) {\n host = document.createElement('div');\n host.className = 'tmr-chart-canvas-host';\n el.appendChild(host);\n }\n host.classList.remove('tmr-chart-skeleton');\n\n let canvas = host.querySelector('canvas');\n if (!canvas) {\n canvas = document.createElement('canvas');\n host.innerHTML = '';\n host.appendChild(canvas);\n }\n\n const config = payload.config[0];\n const height = config.height ?? 320;\n host.style.height = `${height}px`;\n\n if (payload.config.length > 1 && !el.querySelector('.tmr-chart-tabs')) {\n const tabs = document.createElement('div');\n tabs.className = 'tmr-chart-tabs';\n tabs.setAttribute('role', 'tablist');\n payload.config.forEach((c, i) => {\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'tmr-chart-tab';\n btn.setAttribute('role', 'tab');\n btn.dataset.active = i === 0 ? 'true' : 'false';\n btn.textContent = c.title?.trim() || `${c.chartType} ${i + 1}`;\n btn.addEventListener('click', () => {\n tabs.querySelectorAll('.tmr-chart-tab').forEach((b, j) => {\n (b as HTMLElement).dataset.active = j === i ? 'true' : 'false';\n });\n const existing = registry.get(el);\n destroyChart(existing);\n const next = renderOrUpdateChart({\n canvas: canvas!,\n config: payload.config[i],\n payload,\n theme,\n chart: null,\n });\n if (next) registry.set(el, next);\n const titleEl = el.querySelector('.tmr-chart-title');\n if (titleEl && payload.config[i].title) {\n titleEl.textContent = String(payload.config[i].title);\n titleEl.classList.remove('tmr-chart-title--empty');\n }\n });\n tabs.appendChild(btn);\n });\n el.insertBefore(tabs, host);\n }\n\n const existing = registry.get(el);\n const chart = renderOrUpdateChart({\n canvas,\n config,\n payload,\n theme,\n chart: existing ?? null,\n });\n if (chart) registry.set(el, chart);\n}\n\n/**\n * Hydrate SSR chart placeholders inside a container (reading page).\n */\nexport function ChartMount({\n containerRef,\n chartTheme,\n html,\n}: ChartMountProps) {\n const registryRef = useRef(new Map<HTMLElement, ChartJs>());\n\n useEffect(() => {\n const container = containerRef.current;\n if (!container) return;\n const registry = registryRef.current;\n registry.forEach((chart) => destroyChart(chart));\n registry.clear();\n\n container\n .querySelectorAll<HTMLElement>('div[data-type=\"chart\"]')\n .forEach((el) => hydrateOne(el, chartTheme, registry));\n\n return () => {\n registry.forEach((chart) => destroyChart(chart));\n registry.clear();\n };\n }, [containerRef, chartTheme, html]);\n\n return null;\n}\n\n/** Imperative helper for hosts that prefer not to use the component. */\nexport function mountChartsInContainer(\n container: HTMLElement,\n chartTheme?: ChartTheme | null,\n): () => void {\n const registry = new Map<HTMLElement, ChartJs>();\n container\n .querySelectorAll<HTMLElement>('div[data-type=\"chart\"]')\n .forEach((el) => hydrateOne(el, chartTheme, registry));\n return () => {\n registry.forEach((c) => destroyChart(c));\n registry.clear();\n };\n}\n","'use client';\n\nimport { useRef, type ComponentProps } from 'react';\nimport type { ChartTheme } from '../chart/types';\nimport type { OnCitationEnter, OnCitationLeave } from '../citationTypes';\nimport { ReportContent } from '../ReportContent';\nimport { ChartMount } from './ChartMount';\nimport { CitationInteractive } from './CitationInteractive';\n\nexport interface ReportContentWithChartsProps\n extends Omit<ComponentProps<typeof ReportContent>, 'ref'> {\n onCitationEnter?: OnCitationEnter;\n onCitationLeave?: OnCitationLeave;\n chartTheme?: ChartTheme | null;\n}\n\n/**\n * SSR HTML reading surface with Chart.js hydration (+ optional citation clicks).\n */\nexport function ReportContentWithCharts({\n onCitationEnter,\n onCitationLeave,\n chartTheme,\n className,\n html,\n ...rest\n}: ReportContentWithChartsProps) {\n const ref = useRef<HTMLDivElement>(null);\n\n return (\n <div ref={ref} className={className}>\n <ReportContent html={html} {...rest} />\n <ChartMount\n containerRef={ref}\n chartTheme={chartTheme}\n html={html}\n />\n {onCitationEnter && onCitationLeave ? (\n <CitationInteractive\n containerRef={ref}\n onCitationEnter={onCitationEnter}\n onCitationLeave={onCitationLeave}\n />\n ) : null}\n </div>\n );\n}\n"],"names":["existing"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { Editor } from '@tiptap/core';
2
+ import { Node as PMNode } from '@tiptap/pm/model';
3
+ /**
4
+ * 块拖拽菜单能「转成」的目标。和斜杠菜单里可序列化的块对齐,不含表格 / 公式 / 图片。
5
+ */
6
+ export declare const TURN_INTO_KINDS: readonly ["paragraph", "heading-1", "heading-2", "heading-3", "bulletList", "orderedList", "taskList", "blockquote", "codeBlock"];
7
+ export type TurnIntoKind = (typeof TURN_INTO_KINDS)[number];
8
+ export declare function canTurnBlockInto(node: PMNode | null | undefined): boolean;
9
+ /** 菜单里标出「当前就是这种块」。列表项看它的父列表。 */
10
+ export declare function currentTurnIntoKind(editor: Editor, pos: number, node: PMNode): TurnIntoKind | null;
11
+ /**
12
+ * 把 `pos` 上的块转成另一种。光标落进块内再执行,这样列表项 / 引用里的段落
13
+ * 改的是这一块,而不是文档开头。
14
+ */
15
+ export declare function turnBlockInto(editor: Editor, pos: number, kind: TurnIntoKind): boolean;
16
+ /** 在块后面插入一份拷贝(列表项插成同级下一项)。 */
17
+ export declare function duplicateBlock(editor: Editor, pos: number): boolean;
18
+ /**
19
+ * 删掉这一块。文档里只剩这一块时换成空段落(schema 不允许空 doc)。
20
+ * 列表里只剩这一项时连列表一起删,避免留下空的 ul/ol。
21
+ */
22
+ export declare function deleteBlock(editor: Editor, pos: number): boolean;
23
+ /** 这一块的 markdown。复制进剪贴板前先拿到字符串,失败时调用方可以自己兜底。 */
24
+ export declare function blockMarkdown(editor: Editor, pos: number): string | null;
25
+ export declare function canResetBlockFormatting(node: PMNode | null | undefined): boolean;
26
+ /** 清掉块内的行内 mark,以及段落 / 标题上的对齐、缩进、行高。 */
27
+ export declare function resetBlockFormatting(editor: Editor, pos: number): boolean;
28
+ export declare function isImageBlock(node: PMNode | null | undefined): boolean;
29
+ /** 下载图片节点。跨域地址浏览器会忽略 download,退化为打开链接。 */
30
+ export declare function downloadImageBlock(node: PMNode): void;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,21 @@
1
+ import { Node as PMNode, Slice } from '@tiptap/pm/model';
2
+ import { Plugin, PluginKey } from '@tiptap/pm/state';
3
+ import { EditorView } from '@tiptap/pm/view';
4
+ export declare const blockDragListGuardKey: PluginKey<any>;
5
+ export declare function setBlockHandleDragging(view: EditorView, dragging: boolean): void;
6
+ export declare function isBlockHandleDragging(view: EditorView): boolean;
7
+ /** 坐标落在某份列表(含其中的条目)里面。 */
8
+ export declare function isPosInsideList(doc: PMNode, pos: number): boolean;
9
+ /**
10
+ * 把列表内部的位置抬到最外层那份列表的边界。
11
+ * `after` 为真时放在列表后面,否则放在前面。
12
+ */
13
+ export declare function listEscapePos(doc: PMNode, pos: number, after: boolean): number | null;
14
+ /** 指针在列表盒的下半段时落到列表后面,否则落到前面。 */
15
+ export declare function listEscapeEdge(view: EditorView, pos: number, clientY: number): number | null;
16
+ /**
17
+ * 把手柄拖来的切片挪到 `insertPos`。位置落在被拖的选区内部时不挪。
18
+ * 返回 true 表示这次落点已经由我们处理(包括故意不挪)。
19
+ */
20
+ export declare function moveDraggedSlice(view: EditorView, slice: Slice, insertPos: number): boolean;
21
+ export declare function blockDragListGuardPlugin(): Plugin;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export declare function countWords(text: string): number;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,16 +1,13 @@
1
1
  import { ReportChartOptions } from './ChartExtension';
2
2
  import { ChartTheme } from './types';
3
- import { ChartLabels } from '../labels';
4
3
  export interface CreateChartOptions {
5
4
  /** When true, reserved for future in-place chart editing. Currently unused (no edit UI). Default false. */
6
5
  editable?: boolean;
7
6
  chartTheme?: ChartTheme | null;
8
- chartLabels?: Partial<ChartLabels>;
9
7
  }
10
8
  export interface InteractiveChartOptions extends ReportChartOptions {
11
9
  chartEditable: boolean;
12
10
  chartTheme: ChartTheme | null;
13
- chartLabels?: Partial<ChartLabels>;
14
11
  }
15
12
  /**
16
13
  * Client chart extension with React NodeView.
@@ -0,0 +1,14 @@
1
+ import { Editor } from '@tiptap/core';
2
+ export interface BlockDragHandleProps {
3
+ editor: Editor;
4
+ }
5
+ /**
6
+ * 块拖拽:悬停时在块左侧露出手柄(拖动重排),点手柄打开菜单
7
+ * (转换 / 复制 / 再制一份 / 删除)。交互对齐 Tiptap Drag Context Menu,
8
+ * 样式走本库的 `--tmr-*`。手柄跟着文档最外层的块走,列表和引用整块移动。
9
+ * 松开时如果指针在列表上,块落到整份列表的前面或后面,不会变成其中一条。
10
+ *
11
+ * 不注册 NodeRange 扩展:它会接管 Shift+方向键和 Mod-a。拖拽要用的
12
+ * NodeRangeSelection 在加载 drag-handle 时已经注册进 Selection。
13
+ */
14
+ export declare function BlockDragHandle({ editor }: BlockDragHandleProps): import("react").JSX.Element;
@@ -0,0 +1 @@
1
+ export {};
@@ -1,11 +1,11 @@
1
1
  import { NodeViewProps } from '@tiptap/react';
2
2
  /**
3
3
  * 代码块自定义 NodeView:右上角语言选择器(Radix DropdownMenu,支持方向键导航)+ 删除。
4
- * 文案(Auto-detect / 删除标题)从扩展 options.codeBlockLabels 读取,缺省用英文默认。
4
+ * 文案从编辑器的 locale 读取,缺省英文。
5
5
  *
6
6
  * 只读态(预览 / 只读编辑器)走另一套头部:左侧语言标签,右侧复制 + 展开收起;
7
7
  * 超出 `--tmr-code-max-height`(缺省 480px)的代码块默认限高块内滚动,行号槽
8
8
  * 用 MutationObserver 跟踪 PM 管理的 code 内容保持同步(纯文本变化不触发 React
9
9
  * 重渲染,gutter 行数靠观察器自刷新)。
10
10
  */
11
- export declare function CodeBlockView({ node, updateAttributes, deleteNode, extension, editor, }: NodeViewProps): import("react").JSX.Element;
11
+ export declare function CodeBlockView({ node, updateAttributes, deleteNode, editor, }: NodeViewProps): import("react").JSX.Element;
@@ -1,10 +1,11 @@
1
- import { ColorPaletteLabels } from '../labels';
1
+ import { LocaleCode } from '../i18n';
2
2
  export interface ColorPaletteProps {
3
3
  /** 当前已应用的颜色(用于打勾),无则不选中。 */
4
4
  value?: string;
5
5
  /** 点击某色块。null 表示「None」(清除)。 */
6
6
  onPick: (color: string | null) => void;
7
- labels?: Partial<ColorPaletteLabels>;
7
+ /** 内置语言,默认英文。 */
8
+ locale?: LocaleCode;
8
9
  }
9
10
  /** 颜色面板:None 清除行 + 色板网格 + THEME 品牌色。 */
10
- export declare function ColorPalette({ value, onPick, labels }: ColorPaletteProps): import("react").JSX.Element;
11
+ export declare function ColorPalette({ value, onPick, locale }: ColorPaletteProps): import("react").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { LocaleCode } from '../i18n';
2
+ import { TocItem } from '../toc/extractToc';
3
+ export interface EditorTocProps {
4
+ items: TocItem[];
5
+ /** 编辑器正文根节点,用来找标题和滚动容器。 */
6
+ editorDom: HTMLElement;
7
+ locale?: LocaleCode;
8
+ }
9
+ /**
10
+ * 编辑器内目录(Notion 式,贴在左缘)。
11
+ * 收起是一列短横线;悬停或聚焦向右展开标题,点击滚到对应标题。
12
+ */
13
+ export declare function EditorToc({ items, editorDom, locale }: EditorTocProps): import("react").JSX.Element;
@@ -1,6 +1,6 @@
1
1
  import { Editor } from '@tiptap/react';
2
2
  import { ReactNode } from 'react';
3
- import { ToolbarLabels } from '../labels';
3
+ import { LocaleCode } from '../i18n';
4
4
  import { ImportMenuItem } from '../importMenu';
5
5
  import { SourceRef } from '../citationUtils';
6
6
  /** `onImportDocument` 的成功返回。给字符串等价于 `{ markdown }`。 */
@@ -60,7 +60,8 @@ export interface EditorToolbarProps {
60
60
  importMenuItems?: ImportMenuItem[];
61
61
  /** 是否显示导入下拉,默认 `true`。卡片等场景可关掉。 */
62
62
  showImport?: boolean;
63
- labels?: Partial<ToolbarLabels>;
63
+ /** 内置语言,默认英文。 */
64
+ locale?: LocaleCode;
64
65
  /**
65
66
  * 工具栏最右端的搜索入口(放大镜)。**不传就不渲染**——条子本身归
66
67
  * `<MarkdownWysiwygEditor>`,这里只给一个入口,宿主接上自己的编辑器句柄即可:
@@ -81,4 +82,4 @@ export type { ImportMenuItem };
81
82
  * 顶部操作栏(零 UI 框架依赖,Radix Popover 菜单 + 内联 SVG 图标)。位置由父级控制,
82
83
  * 这里只渲染按钮 + 反映 editor 激活态。不含业务耦合,扩展项经 extraToolbarItems 注入。
83
84
  */
84
- export declare function EditorToolbar({ editor, onImageUpload, onError, onImportDocument, importAccept, importMenuItems, showImport, labels, onSearch, searchActive, extraToolbarItems, className, }: EditorToolbarProps): import("react").JSX.Element;
85
+ export declare function EditorToolbar({ editor, onImageUpload, onError, onImportDocument, importAccept, importMenuItems, showImport, locale, onSearch, searchActive, extraToolbarItems, className, }: EditorToolbarProps): import("react").JSX.Element;
@@ -1,12 +1,13 @@
1
1
  import { Editor } from '@tiptap/react';
2
2
  import { CSSProperties, RefObject } from 'react';
3
- import { FindLabels } from '../labels';
3
+ import { LocaleCode } from '../i18n';
4
4
  export interface FindReplaceBarProps {
5
5
  /** 编辑器实例(由 `<MarkdownWysiwygEditor>` 提供,浮动条不自行创建编辑器)。 */
6
6
  editor: Editor;
7
7
  /** 关闭(Esc / ×):宿主负责收起浮动条并把焦点还给编辑器。 */
8
8
  onClose: () => void;
9
- labels?: Partial<FindLabels>;
9
+ /** 内置语言,默认英文。 */
10
+ locale?: LocaleCode;
10
11
  /**
11
12
  * 查找输入框 ref。宿主拿它实现「再按一次 Mod+F 聚焦并全选当前查询」。
12
13
  * 也用于浮动条打开时自动聚焦。
@@ -17,4 +18,4 @@ export interface FindReplaceBarProps {
17
18
  /** 根节点内联样式(编辑器用它落 findBarOffset;宿主自绘时也可用)。 */
18
19
  style?: CSSProperties;
19
20
  }
20
- export declare function FindReplaceBar({ editor, onClose, labels, inputRef, className, style, }: FindReplaceBarProps): import("react").JSX.Element | null;
21
+ export declare function FindReplaceBar({ editor, onClose, locale, inputRef, className, style, }: FindReplaceBarProps): import("react").JSX.Element | null;
@@ -1,6 +1,6 @@
1
1
  import { Editor } from '@tiptap/react';
2
2
  import { CSSProperties, ReactNode } from 'react';
3
- import { LinkPopoverLabels } from '../labels';
3
+ import { LocaleCode } from '../i18n';
4
4
  export interface LinkPopoverProps {
5
5
  /** 编辑器实例。 */
6
6
  editor: Editor;
@@ -9,7 +9,8 @@ export interface LinkPopoverProps {
9
9
  * 传函数组件时得先 `forwardRef`(本库的 `<EditorToolbar>` 就是这么接的)。
10
10
  */
11
11
  trigger: ReactNode;
12
- labels?: Partial<LinkPopoverLabels>;
12
+ /** 内置语言,默认英文。 */
13
+ locale?: LocaleCode;
13
14
  /** 挂到浮层根节点上的 class / 内联样式(定位与层级归宿主,Radix 只管浮层方向)。 */
14
15
  className?: string;
15
16
  style?: CSSProperties;
@@ -25,4 +26,4 @@ export interface LinkPopoverProps {
25
26
  * 地址被编辑器拒掉时(Tiptap 的 `isAllowedUri`)浮层不关,输入框转成危险色并带上 title,
26
27
  * 让用户改——静默什么都不发生是最难查的。
27
28
  */
28
- export declare function LinkPopover({ editor, trigger, labels, className, style, }: LinkPopoverProps): import("react").JSX.Element;
29
+ export declare function LinkPopover({ editor, trigger, locale, className, style, }: LinkPopoverProps): import("react").JSX.Element;
@@ -1,3 +1,4 @@
1
+ import { LocaleCode } from '../i18n';
1
2
  import { SourceRef } from '../citationUtils';
2
3
  import { RenderCitation } from '../citationTypes';
3
4
  export interface MarkdownPreviewProps {
@@ -14,6 +15,8 @@ export interface MarkdownPreviewProps {
14
15
  */
15
16
  renderCitation?: RenderCitation;
16
17
  className?: string;
18
+ /** 内置界面语言,默认英文。代码块与图表文案跟着变。 */
19
+ locale?: LocaleCode;
17
20
  }
18
21
  /**
19
22
  * 轻量只读 markdown 预览(Tiptap + 官方 @tiptap/markdown,editable:false)。
@@ -24,4 +27,4 @@ export interface MarkdownPreviewProps {
24
27
  * 这是「客户端只读预览」;若需 SEO / 无 JS 静态渲染,请改用 server 入口的
25
28
  * renderReportHtml + ReportContent / ReportContentWithCharts。
26
29
  */
27
- export declare function MarkdownPreview({ markdown, sources, renderCitation, className, }: MarkdownPreviewProps): import("react").JSX.Element;
30
+ export declare function MarkdownPreview({ markdown, sources, renderCitation, className, locale, }: MarkdownPreviewProps): import("react").JSX.Element;
@@ -4,7 +4,7 @@ import { SourceRef } from '../citationUtils';
4
4
  import { RenderCitation } from '../citationTypes';
5
5
  import { CommentClickPayload, CommentRef } from '../commentAnchor/commentTypes';
6
6
  import { ImageUploadConfig } from '../imageUpload';
7
- import { CodeBlockLabels, FindLabels, ShortcutLabels, SlashMenuLabels } from '../labels';
7
+ import { LocaleCode } from '../i18n';
8
8
  import { PendingAnchor } from '../pendingAnchor';
9
9
  import { SelectionKind } from '../selectionKind';
10
10
  import { TocItem } from '../toc/extractToc';
@@ -76,6 +76,8 @@ export interface MarkdownWysiwygEditorProps {
76
76
  * 只读态不应用评论锚定(comments 被忽略),与 MarkdownPreview 语义一致。
77
77
  */
78
78
  editable?: boolean;
79
+ /** 内置界面语言,默认英文。工具栏等并列组件需各自传同一个值。目录跟着编辑器。 */
80
+ locale?: LocaleCode;
79
81
  /**
80
82
  * 初始脚注来源:按 index 对齐 `[^n]`,写入 citationRef 的 url/title。
81
83
  * 仅影响初始 content;后续插入请用 `insertMarkdown(editor, md, sources)`。
@@ -89,8 +91,16 @@ export interface MarkdownWysiwygEditorProps {
89
91
  placeholder?: string;
90
92
  /** editor 实例就绪 / 销毁时回调,供外部工具栏使用。 */
91
93
  onEditorReady?: (editor: Editor | null) => void;
92
- /** 目录变化回调(正文标题增删改时),供侧边目录实时展示。 */
94
+ /**
95
+ * 目录变化回调(正文标题增删改时)。编辑器自己画左侧目录,
96
+ * 这个回调只把同一份 `TocItem[]` 交给还想自己用数据的宿主。
97
+ */
93
98
  onTocChange?: (items: TocItem[]) => void;
99
+ /**
100
+ * 编辑器内目录(默认 true)。有标题时在左缘显示短横线,悬停向右展开,
101
+ * 点击滚到对应标题。宽度小于 768px 时不显示。
102
+ */
103
+ toc?: boolean;
94
104
  /**
95
105
  * 粘贴纯文本时启发式检测 markdown 并自动转富文本(Shift+粘贴保持纯文本)。
96
106
  * 默认 true。仅初始化时生效。
@@ -103,8 +113,6 @@ export interface MarkdownWysiwygEditorProps {
103
113
  markdownFileDrop?: boolean;
104
114
  /** 追加的 Tiptap 扩展(在内置扩展之后注册)。 */
105
115
  extraExtensions?: AnyExtension[];
106
- /** 代码块 NodeView 的本地化文案。 */
107
- codeBlockLabels?: Partial<CodeBlockLabels>;
108
116
  /**
109
117
  * 「这段正在被 AI 改写」的高亮:宿主在提交请求时给上、回填或冲突时传空数组清空。
110
118
  * 走 decoration,**不进 markdown、不进 undo 历史**,能盖住表格与图表这类块级节点。
@@ -177,8 +185,6 @@ export interface MarkdownWysiwygEditorProps {
177
185
  * 保留浏览器原生查找,或自己摆入口。焦点不在编辑器内时不接管,同页多编辑器不会互相抢。
178
186
  */
179
187
  findShortcut?: boolean;
180
- /** 查找替换浮动条的本地化文案。 */
181
- findLabels?: Partial<FindLabels>;
182
188
  /**
183
189
  * 浮动条开合状态变化的通知(条子开着时 `handle.openFind()`、Esc / × 关掉都会触发)。
184
190
  * 典型用法是驱动别处的入口按钮:`<EditorToolbar onSearch={open} searchActive={isOpen} />`。
@@ -191,8 +197,6 @@ export interface MarkdownWysiwygEditorProps {
191
197
  * 「格式 / 快捷键 / Markdown」三列对照抽屉(默认 true,只读态隐藏)。
192
198
  */
193
199
  shortcutPanel?: boolean;
194
- /** 快捷键抽屉的本地化文案。 */
195
- shortcutLabels?: Partial<ShortcutLabels>;
196
200
  /**
197
201
  * 把快捷键悬浮键挂到宿主的容器里(与 `findBarContainer` 同一套):
198
202
  * 给元素或返回元素的函数即可,开合状态与抽屉归库——只换按钮落点。
@@ -207,8 +211,26 @@ export interface MarkdownWysiwygEditorProps {
207
211
  * 追加自定义命令项请传 `extraExtensions` 注册 `SlashMenu.configure({ items })`。
208
212
  */
209
213
  slashMenu?: boolean;
210
- /** 斜杠菜单的本地化文案。 */
211
- slashMenuLabels?: Partial<SlashMenuLabels>;
214
+ /**
215
+ * 智能标点(官方 `@tiptap/extension-typography`,MIT,不是 Pro)。
216
+ * 键入时把直引号收成弯引号、`--` 收成破折号、`...` 收成省略号,以及箭头、©、分数等。
217
+ * 代码块和行内代码里不触发;Backspace 撤销刚发生的替换。`---` 仍是分隔线。
218
+ * 默认 true。仅初始化时生效。
219
+ */
220
+ typography?: boolean;
221
+ /**
222
+ * 表情建议列表:键入 `:`(行首或空白后)弹出,选中后插入 emoji 节点。
223
+ * Markdown 存成 `:shortcode:`。传 false 只是不弹列表——节点、完整短码输入和往返仍在。
224
+ * 只读态不弹出。默认 true。仅初始化时生效。
225
+ */
226
+ emojiSuggestion?: boolean;
227
+ /**
228
+ * 块拖拽(对齐 Tiptap Drag Context Menu):悬停块时左侧出现手柄,拖动重排,
229
+ * 点击打开菜单(转成别的块 / 复制 / 再制一份 / 删除,图片还可下载)。
230
+ * 手柄旁的「+」在块首插入 `/`,唤起斜杠菜单(`slashMenu={false}` 时不显示加号)。
231
+ * 默认 true。只读态和窄屏(宽度小于 768px)不显示,并收起左侧留白。
232
+ */
233
+ blockDrag?: boolean;
212
234
  /**
213
235
  * 图片上传(对齐官方 ImageUploadNode 的交互):配置后工具栏的图片按钮变成
214
236
  * 「插入上传块」——文档里出现拖拽 / 点击的占位块,上传进度就地显示,全部成功后就地
@@ -226,6 +248,13 @@ export interface MarkdownWysiwygEditorProps {
226
248
  * imageMarkdown.ts)。传 `false` 关闭;传对象透传官方 resize 配置。仅初始化时生效。
227
249
  */
228
250
  imageResize?: boolean | ImageResizeOptions;
251
+ /**
252
+ * 全文统计(默认关):传 `true` 才注册 MIT 包 `@tiptap/extensions` 的 CharacterCount
253
+ * (开源扩展,不是付费的 `@tiptap-pro`),在编辑器底部实时显示字符数与词数。
254
+ * 统计条是本库自绘。词数把每个汉字算作 1 个词(该扩展默认按空格切,整段中文会是 1)。
255
+ * 数字也可从 `editor.storage.characterCount` 读取。仅初始化时生效。
256
+ */
257
+ characterCount?: boolean;
229
258
  }
230
259
  /**
231
260
  * 所见即所得 Markdown 编辑器(基于 Tiptap v3 + 官方 @tiptap/markdown)。
@@ -0,0 +1,4 @@
1
+ import { JSONContent, MarkdownParseHelpers, MarkdownRendererHelpers, MarkdownToken, MarkdownTokenizer } from '@tiptap/core';
2
+ export declare const detailsMarkdownTokenizer: MarkdownTokenizer;
3
+ export declare function detailsParseMarkdown(token: MarkdownToken, helpers: MarkdownParseHelpers): JSONContent;
4
+ export declare function detailsRenderMarkdown(node: JSONContent, helpers: MarkdownRendererHelpers): string;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,23 @@
1
+ import { EmojiItem } from '@tiptap/extension-emoji';
2
+ import { AnyExtension } from '@tiptap/core';
3
+ export interface EmojiSuggestionPopupHandle {
4
+ /** 编辑器键盘事件经 IME 守卫后委托进来;返回 true 表示已消费。 */
5
+ onKeyDown: (event: KeyboardEvent) => boolean;
6
+ }
7
+ export interface EmojiSuggestionPopupProps {
8
+ items: EmojiItem[];
9
+ query: string;
10
+ menuLabel: string;
11
+ command: (item: EmojiItem) => void;
12
+ }
13
+ /**
14
+ * 冒号表情格子。空查询按底部分类标签浏览;继续打短码则在全部分类里筛选。
15
+ * 定位由 @tiptap/suggestion 的 mount 接管。
16
+ */
17
+ export declare const EmojiSuggestionPopup: import('react').ForwardRefExoticComponent<EmojiSuggestionPopupProps & import('react').RefAttributes<EmojiSuggestionPopupHandle>>;
18
+ /**
19
+ * 把 schema 上的 emoji 扩展配成带(或不带)建议列表的那一份。
20
+ * configure 深合并 options,官方的 command / char / pluginKey 留着。
21
+ * 关掉时 allow 恒为 false:不弹层,`:smile:` 输入规则仍在。
22
+ */
23
+ export declare function configureEmojiSuggestion(extension: AnyExtension, enabled: boolean): AnyExtension;
@@ -0,0 +1,52 @@
1
+ import { EmojiItem } from '@tiptap/extension-emoji';
2
+ /** 底部标签。顺序与官方数据的 group 一致;图标是分类代表,不依赖某一条数据还在不在。 */
3
+ export declare const EMOJI_CATEGORIES: readonly [{
4
+ readonly id: "smileys";
5
+ readonly group: "";
6
+ readonly label: "Smileys";
7
+ readonly icon: "😀";
8
+ }, {
9
+ readonly id: "people";
10
+ readonly group: "people & body";
11
+ readonly label: "People";
12
+ readonly icon: "👋";
13
+ }, {
14
+ readonly id: "nature";
15
+ readonly group: "animals & nature";
16
+ readonly label: "Animals & nature";
17
+ readonly icon: "🐻";
18
+ }, {
19
+ readonly id: "food";
20
+ readonly group: "food & drink";
21
+ readonly label: "Food & drink";
22
+ readonly icon: "🍔";
23
+ }, {
24
+ readonly id: "travel";
25
+ readonly group: "travel & places";
26
+ readonly label: "Travel & places";
27
+ readonly icon: "✈️";
28
+ }, {
29
+ readonly id: "activities";
30
+ readonly group: "activities";
31
+ readonly label: "Activities";
32
+ readonly icon: "⚽";
33
+ }, {
34
+ readonly id: "objects";
35
+ readonly group: "objects";
36
+ readonly label: "Objects";
37
+ readonly icon: "💡";
38
+ }, {
39
+ readonly id: "symbols";
40
+ readonly group: "symbols";
41
+ readonly label: "Symbols";
42
+ readonly icon: "💯";
43
+ }, {
44
+ readonly id: "flags";
45
+ readonly group: "flags";
46
+ readonly label: "Flags";
47
+ readonly icon: "🏁";
48
+ }];
49
+ export type EmojiCategoryId = (typeof EMOJI_CATEGORIES)[number]['id'];
50
+ /** 肤色和发型零件。官方扩展不支持肤色修饰,单独显示也不是可选表情。 */
51
+ export declare function isEmojiComponent(item: EmojiItem): boolean;
52
+ export declare function itemsInCategory(items: EmojiItem[], id: EmojiCategoryId): EmojiItem[];
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,7 @@
1
+ import { EmojiItem } from '@tiptap/extension-emoji';
2
+ /**
3
+ * 按短码 / 名称优先、标签其次过滤表情。
4
+ * 空查询返回全部可用表情,弹层再按分类切开。
5
+ * 区域指示符和肤色零件不进列表。没有 Unicode 字符的自定义图也不进。
6
+ */
7
+ export declare function filterEmojiItems(items: EmojiItem[], query: string): EmojiItem[];
@@ -0,0 +1,8 @@
1
+ /**
2
+ * 官方 Emoji 节点 + `:shortcode:` 的 markdown 往返。
3
+ *
4
+ * 官方只有 renderMarkdown(输出 `:name:`),没有 tokenizer,读回会变成普通文本。
5
+ * 建议列表的 React 弹层不在这里:server / 预览共用这份 schema,弹层由编辑器
6
+ * configure 贴上去。
7
+ */
8
+ export declare const MarkdownEmoji: import('@tiptap/core').Node<import('@tiptap/extension-emoji').EmojiOptions, import('@tiptap/extension-emoji').EmojiStorage>;