@4399ywkf/editor 0.6.0 → 0.8.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 +161 -2
  2. package/dist/{DocView-XQMJUUVK.js → DocView-WEKRSKRU.js} +6 -4
  3. package/dist/DocView-WEKRSKRU.js.map +1 -0
  4. package/dist/SheetView-SDBRSTVS.js +3 -0
  5. package/dist/{SheetView-DSADIZNS.js.map → SheetView-SDBRSTVS.js.map} +1 -1
  6. package/dist/chunk-5HMKRNIH.js +27 -0
  7. package/dist/chunk-5HMKRNIH.js.map +1 -0
  8. package/dist/chunk-DGUNVCOM.js +711 -0
  9. package/dist/chunk-DGUNVCOM.js.map +1 -0
  10. package/dist/chunk-HPSPOM2O.js +128 -0
  11. package/dist/chunk-HPSPOM2O.js.map +1 -0
  12. package/dist/chunk-KRP5V7VF.js +130 -0
  13. package/dist/chunk-KRP5V7VF.js.map +1 -0
  14. package/dist/chunk-SBTKDJAL.js +107 -0
  15. package/dist/chunk-SBTKDJAL.js.map +1 -0
  16. package/dist/{chunk-IUDLMN53.js → chunk-SGLNT6Z2.js} +2416 -176
  17. package/dist/chunk-SGLNT6Z2.js.map +1 -0
  18. package/dist/{chunk-L6ZR5HHW.js → chunk-SYCK6Y5F.js} +15 -10
  19. package/dist/chunk-SYCK6Y5F.js.map +1 -0
  20. package/dist/chunk-WF7PIIFR.js +21 -0
  21. package/dist/chunk-WF7PIIFR.js.map +1 -0
  22. package/dist/chunk-ZY5KLA7T.js +258 -0
  23. package/dist/chunk-ZY5KLA7T.js.map +1 -0
  24. package/dist/code-types-Dqmit3xx.d.ts +65 -0
  25. package/dist/{types-C37MAt8o.d.ts → csv-parse-D0zTBvDB.d.ts} +39 -1
  26. package/dist/csv.d.ts +6 -3
  27. package/dist/csv.js +6 -125
  28. package/dist/csv.js.map +1 -1
  29. package/dist/doc-runtime.d.ts +1 -1
  30. package/dist/doc-runtime.js +7 -1
  31. package/dist/doc-runtime.js.map +1 -1
  32. package/dist/doc.d.ts +113 -6
  33. package/dist/doc.js +2 -1
  34. package/dist/doc.js.map +1 -1
  35. package/dist/docx.d.ts +15 -0
  36. package/dist/docx.js +116 -8
  37. package/dist/docx.js.map +1 -1
  38. package/dist/import-GVZDLWA2.js +6 -0
  39. package/dist/import-GVZDLWA2.js.map +1 -0
  40. package/dist/index.d.ts +4 -2
  41. package/dist/index.js +7 -3
  42. package/dist/index.js.map +1 -1
  43. package/dist/pdf.d.ts +3 -1
  44. package/dist/pptx.d.ts +3 -1
  45. package/dist/{registry-BBL8yeIT.d.ts → registry-CYKivRDh.d.ts} +13 -1
  46. package/dist/sheet-runtime.d.ts +212 -0
  47. package/dist/sheet-runtime.js +416 -0
  48. package/dist/sheet-runtime.js.map +1 -0
  49. package/dist/sheet.d.ts +9 -5
  50. package/dist/sheet.js +1 -1
  51. package/dist/style.css +421 -111
  52. package/dist/xlsx.d.ts +118 -7
  53. package/dist/xlsx.js +7 -78
  54. package/dist/xlsx.js.map +1 -1
  55. package/package.json +11 -5
  56. package/dist/DocView-XQMJUUVK.js.map +0 -1
  57. package/dist/SheetView-DSADIZNS.js +0 -3
  58. package/dist/chunk-32WQX3NR.js +0 -37
  59. package/dist/chunk-32WQX3NR.js.map +0 -1
  60. package/dist/chunk-IUDLMN53.js.map +0 -1
  61. package/dist/chunk-L6ZR5HHW.js.map +0 -1
@@ -3,26 +3,29 @@ import { jsx } from 'react/jsx-runtime';
3
3
 
4
4
  // src/views/SheetView.tsx
5
5
  function SheetView({
6
+ source,
6
7
  content,
7
8
  readOnly,
8
9
  className,
9
10
  sheet,
10
11
  onReady,
11
- onError
12
+ onError,
13
+ onWarnings
12
14
  }) {
13
15
  const hostRef = useRef(null);
14
16
  const disposeRef = useRef(null);
15
- const latest = useRef({ sheet, onReady, onError });
17
+ const latest = useRef({ sheet, onReady, onError, onWarnings });
16
18
  useEffect(() => {
17
- latest.current = { sheet, onReady, onError };
19
+ latest.current = { sheet, onReady, onError, onWarnings };
18
20
  });
19
21
  useEffect(() => {
20
22
  let cancelled = false;
21
23
  const host = hostRef.current;
22
24
  if (!host) return;
23
25
  (async () => {
24
- const { sheet: opts, onReady: ready, onError: fail } = latest.current;
26
+ const { sheet: opts, onReady: ready, onError: fail, onWarnings: warn } = latest.current;
25
27
  try {
28
+ const importPromise = !content && source ? import('./import-GVZDLWA2.js').then((m) => m.importSheetSource(source)) : null;
26
29
  const [{ createUniver, LocaleType, merge }, sheetsCore, zhCN] = await Promise.all([
27
30
  import('@univerjs/presets'),
28
31
  import('@univerjs/presets/preset-sheets-core'),
@@ -47,6 +50,8 @@ function SheetView({
47
50
  }
48
51
  if (opts?.presets?.length) presets.push(...opts.presets);
49
52
  if (opts?.locales?.length) locales.push(...opts.locales);
53
+ const imported = importPromise ? await importPromise : null;
54
+ if (cancelled) return;
50
55
  const { univerAPI, univer } = createUniver({
51
56
  locale: LocaleType.ZH_CN,
52
57
  // createUniver 不会自动收集 preset 自带的 locales,得在这里一起合进来,
@@ -56,16 +61,16 @@ function SheetView({
56
61
  });
57
62
  const dispose = () => univer.dispose();
58
63
  if (cancelled) return dispose();
59
- if (content) univerAPI.createWorkbook(content);
60
- else univerAPI.createWorkbook({});
64
+ univerAPI.createWorkbook(content ?? imported?.workbook ?? {});
61
65
  if (readOnly) univerAPI.getActiveWorkbook?.()?.setEditable?.(false);
66
+ if (imported?.warnings.length) warn?.(imported.warnings);
62
67
  disposeRef.current = dispose;
63
68
  ready?.(univerAPI);
64
69
  } catch (e) {
65
70
  const err = e instanceof Error ? e : new Error(String(e));
66
71
  fail?.(
67
72
  new Error(
68
- `表格引擎加载失败:${err.message}。请确认已安装可选依赖 @univerjs/presets(pnpm add @univerjs/presets)`
73
+ `表格加载失败:${err.message}。若是引擎缺失,请确认已安装可选依赖 @univerjs/presets(pnpm add @univerjs/presets)`
69
74
  )
70
75
  );
71
76
  }
@@ -75,10 +80,10 @@ function SheetView({
75
80
  disposeRef.current?.();
76
81
  disposeRef.current = null;
77
82
  };
78
- }, [content, readOnly]);
83
+ }, [source, content, readOnly]);
79
84
  return /* @__PURE__ */ jsx("div", { ref: hostRef, className, style: { width: "100%", height: "100%" } });
80
85
  }
81
86
 
82
87
  export { SheetView };
83
- //# sourceMappingURL=chunk-L6ZR5HHW.js.map
84
- //# sourceMappingURL=chunk-L6ZR5HHW.js.map
88
+ //# sourceMappingURL=chunk-SYCK6Y5F.js.map
89
+ //# sourceMappingURL=chunk-SYCK6Y5F.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/views/SheetView.tsx"],"names":[],"mappings":"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","file":"chunk-SYCK6Y5F.js","sourcesContent":["import { useEffect, useRef } from \"react\"\nimport type { DocumentViewProps } from \"../registry\"\nimport type { SheetUiOptions } from \"../sheet-options\"\n\n/**\n * sheet 通道:Univer 适配。\n *\n * 只用 Apache-2.0 的 OSS 核心。**不碰** `@univerjs-pro/*` 与 `univer-cli`:\n * 前者要自建 Univer Server 且需商业授权,后者 63MB、license 字段为 None、\n * 90/91 个 JS 被混淆、还会向 univer.ai 上报遥测 —— 不能进生产链路。\n *\n * 导入导出都在本包 `./xlsx`:导出直接读 Univer 快照拼 OOXML,导入把\n * xlsx/csv/tsv 解析成快照(`source` prop 走的就是这条路,File / Blob / URL\n * 都收)。两个方向都不引 SheetJS,文档不出内网。导入丢了什么(图片、\n * 条件格式等独立部件)通过 `onWarnings` 如实报告。\n *\n * 配置见 `SheetOptions`:UI 开关直接透传给 preset(Univer 的 class 是\n * `univer-` 前缀的原子类,没有稳定选择器,CSS 硬藏靠不住),额外能力靠追加 preset。\n */\nexport default function SheetView({\n source,\n content,\n readOnly,\n className,\n sheet,\n onReady,\n onError,\n onWarnings,\n}: DocumentViewProps) {\n const hostRef = useRef<HTMLDivElement>(null)\n const disposeRef = useRef<(() => void) | null>(null)\n\n // 这几项都是「创建时读一次」的,且宿主多半传内联字面量/箭头函数。\n // 放进依赖数组 = 父组件每渲染一次就重建一次引擎(重下 871KB + 丢选区滚动位置),\n // 所以走 ref:取最新值,但不参与依赖比较。\n const latest = useRef({ sheet, onReady, onError, onWarnings })\n useEffect(() => {\n latest.current = { sheet, onReady, onError, onWarnings }\n })\n\n useEffect(() => {\n let cancelled = false\n const host = hostRef.current\n if (!host) return\n\n ;(async () => {\n const { sheet: opts, onReady: ready, onError: fail, onWarnings: warn } = latest.current\n try {\n // source 的解析和 871KB 引擎的下载并行:两边都是网络/CPU 大头,串行纯浪费。\n // content 优先于 source —— 已有快照就没有「导入」这回事\n const importPromise =\n !content && source\n ? import(\"../xlsx/import\").then((m) => m.importSheetSource(source))\n : null\n\n // 动态 import:871KB gzip 的引擎只在真的打开表格时才下载\n const [{ createUniver, LocaleType, merge }, sheetsCore, zhCN] = await Promise.all([\n import(\"@univerjs/presets\"),\n import(\"@univerjs/presets/preset-sheets-core\"),\n import(\"@univerjs/presets/preset-sheets-core/locales/zh-CN\"),\n ])\n if (cancelled) return\n\n // 只读时默认摘掉工具栏:整条 ribbon 全是死按钮,窄面板里还挤。\n // 宿主显式传了 toolbar 就以宿主为准。\n const ui: SheetUiOptions = { ...(readOnly ? { toolbar: false } : null), ...opts?.ui }\n\n // 可选 peer:@univerjs/presets 未安装时这里的类型解析不到,\n // 统一用 never 过桥,运行时由外层 try/catch 兜底并给出可读提示\n const presets: unknown[] = [\n (\n sheetsCore as unknown as { UniverSheetsCorePreset: (o: object) => never }\n ).UniverSheetsCorePreset({ container: host, ...ui }),\n ]\n const locales: unknown[] = [(zhCN as unknown as { default: object }).default]\n\n if (opts?.findReplace) {\n // preset-sheets-find-replace 是 @univerjs/presets 自己的 dependency,\n // 走子路径不需要宿主再装包;没开就完全不下载。\n const [findReplace, findReplaceZhCN] = await Promise.all([\n import(\"@univerjs/presets/preset-sheets-find-replace\"),\n import(\"@univerjs/presets/preset-sheets-find-replace/locales/zh-CN\"),\n ])\n if (cancelled) return\n presets.push(\n (\n findReplace as unknown as { UniverSheetsFindReplacePreset: () => never }\n ).UniverSheetsFindReplacePreset(),\n )\n locales.push((findReplaceZhCN as unknown as { default: object }).default)\n }\n\n if (opts?.presets?.length) presets.push(...opts.presets)\n if (opts?.locales?.length) locales.push(...opts.locales)\n\n // 导入失败不该等引擎起完才报:在创建引擎前结算,抛给外层统一 fail\n const imported = importPromise ? await importPromise : null\n if (cancelled) return\n\n const { univerAPI, univer } = createUniver({\n locale: LocaleType.ZH_CN,\n // createUniver 不会自动收集 preset 自带的 locales,得在这里一起合进来,\n // 否则新加的 preset 菜单会显示成原始 i18n key\n locales: { [LocaleType.ZH_CN]: merge({}, ...(locales as object[])) },\n presets: presets as never,\n })\n\n const dispose = () => (univer as unknown as { dispose: () => void }).dispose()\n // 卸载可能发生在上面任一个 await 期间:那时 cleanup 拿到的 disposeRef 还是 null,\n // 不在这儿自己收掉,引擎就挂在一个已脱离文档的 host 上泄漏了\n if (cancelled) return dispose()\n\n univerAPI.createWorkbook((content ?? imported?.workbook ?? {}) as never)\n if (readOnly) univerAPI.getActiveWorkbook?.()?.setEditable?.(false)\n if (imported?.warnings.length) warn?.(imported.warnings)\n\n disposeRef.current = dispose\n ready?.(univerAPI)\n } catch (e) {\n const err = e instanceof Error ? e : new Error(String(e))\n fail?.(\n new Error(\n `表格加载失败:${err.message}。` +\n `若是引擎缺失,请确认已安装可选依赖 @univerjs/presets(pnpm add @univerjs/presets)`,\n ),\n )\n }\n })()\n\n return () => {\n cancelled = true\n disposeRef.current?.()\n disposeRef.current = null\n }\n }, [source, content, readOnly])\n\n return <div ref={hostRef} className={className} style={{ width: \"100%\", height: \"100%\" }} />\n}\n"]}
@@ -0,0 +1,21 @@
1
+ // src/xlsx/snapshot.ts
2
+ function snapshotFromUniver(univerAPI) {
3
+ const api = univerAPI;
4
+ const book = api?.getActiveWorkbook?.();
5
+ if (!book) throw new Error("拿不到当前工作簿:univerAPI 无效,或表格尚未就绪");
6
+ const snapshot = book.getSnapshot?.() ?? book.save?.();
7
+ if (!snapshot || typeof snapshot !== "object") {
8
+ throw new Error("当前 Univer 版本没有 getSnapshot() / save(),无法取快照");
9
+ }
10
+ return snapshot;
11
+ }
12
+ function toSnapshot(source) {
13
+ const maybe = source;
14
+ if (maybe && typeof maybe === "object" && ("sheets" in maybe || "sheetOrder" in maybe))
15
+ return maybe;
16
+ return snapshotFromUniver(source);
17
+ }
18
+
19
+ export { snapshotFromUniver, toSnapshot };
20
+ //# sourceMappingURL=chunk-WF7PIIFR.js.map
21
+ //# sourceMappingURL=chunk-WF7PIIFR.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/xlsx/snapshot.ts"],"names":[],"mappings":";AASO,SAAS,mBAAmB,SAAA,EAAkC;AACnE,EAAA,MAAM,GAAA,GAAM,SAAA;AAGZ,EAAA,MAAM,IAAA,GAAO,KAAK,iBAAA,IAAoB;AACtC,EAAA,IAAI,CAAC,IAAA,EAAM,MAAM,IAAI,MAAM,+BAA+B,CAAA;AAC1D,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,WAAA,IAAc,IAAK,KAAK,IAAA,IAAO;AACrD,EAAA,IAAI,CAAC,QAAA,IAAY,OAAO,QAAA,KAAa,QAAA,EAAU;AAC7C,IAAA,MAAM,IAAI,MAAM,6CAA6C,CAAA;AAAA,EAC/D;AACA,EAAA,OAAO,QAAA;AACT;AAGO,SAAS,WAAW,MAAA,EAA8C;AACvE,EAAA,MAAM,KAAA,GAAQ,MAAA;AACd,EAAA,IAAI,SAAS,OAAO,KAAA,KAAU,QAAA,KAAa,QAAA,IAAY,SAAS,YAAA,IAAgB,KAAA,CAAA;AAC9E,IAAA,OAAO,KAAA;AACT,EAAA,OAAO,mBAAmB,MAAM,CAAA;AAClC","file":"chunk-WF7PIIFR.js","sourcesContent":["/** 从 Univer 实例取快照。xlsx 与 csv 两条导出路径共用。 */\n\nimport type { WorkbookData } from \"./types.ts\"\n\n/**\n * 参数类型是 `unknown` 而不是 `FUniver`:`@univerjs/presets` 是可选 peer,\n * 写成具体类型会让没装它的项目 typecheck 直接红。运行时按 facade 的两个\n * 方法名依次试探(`getSnapshot` 是新名字,`save` 是旧的)。\n */\nexport function snapshotFromUniver(univerAPI: unknown): WorkbookData {\n const api = univerAPI as {\n getActiveWorkbook?: () => { getSnapshot?: () => unknown; save?: () => unknown } | null\n } | null\n const book = api?.getActiveWorkbook?.()\n if (!book) throw new Error(\"拿不到当前工作簿:univerAPI 无效,或表格尚未就绪\")\n const snapshot = book.getSnapshot?.() ?? book.save?.()\n if (!snapshot || typeof snapshot !== \"object\") {\n throw new Error(\"当前 Univer 版本没有 getSnapshot() / save(),无法取快照\")\n }\n return snapshot as WorkbookData\n}\n\n/** 快照或 Univer 实例都收 —— 调用方不必自己判断手里是什么 */\nexport function toSnapshot(source: WorkbookData | unknown): WorkbookData {\n const maybe = source as WorkbookData\n if (maybe && typeof maybe === \"object\" && (\"sheets\" in maybe || \"sheetOrder\" in maybe))\n return maybe\n return snapshotFromUniver(source)\n}\n"]}
@@ -0,0 +1,258 @@
1
+ // src/components/tiptap-node/code-block-node/code-highlighter.ts
2
+ var PLAINTEXT = "plaintext";
3
+ var DEFAULT_THEMES = { light: "github-light", dark: "github-dark" };
4
+ var MAX_HIGHLIGHT_LENGTH = 1e5;
5
+ var MAX_HIGHLIGHT_LINES = 2e3;
6
+ var CACHE_MAX_ENTRIES = 256;
7
+ var CACHE_MAX_SOURCE_LENGTH = 2e6;
8
+ var DEFAULT_ALIASES = {
9
+ sh: "bash",
10
+ shell: "bash",
11
+ zsh: "bash",
12
+ py: "python",
13
+ golang: "go",
14
+ tsx: "tsx",
15
+ jsx: "jsx",
16
+ js: "javascript",
17
+ mjs: "javascript",
18
+ cjs: "javascript",
19
+ ts: "typescript",
20
+ yml: "yaml",
21
+ md: "markdown",
22
+ "c++": "cpp",
23
+ "c#": "csharp",
24
+ cs: "csharp",
25
+ rb: "ruby",
26
+ rs: "rust",
27
+ kt: "kotlin",
28
+ ps1: "powershell",
29
+ dockerfile: "docker",
30
+ text: PLAINTEXT,
31
+ txt: PLAINTEXT,
32
+ plain: PLAINTEXT
33
+ };
34
+ var DISPLAY_NAMES = {
35
+ bash: "Bash",
36
+ python: "Python",
37
+ go: "Go",
38
+ javascript: "JavaScript",
39
+ typescript: "TypeScript",
40
+ jsx: "JSX",
41
+ tsx: "TSX",
42
+ json: "JSON",
43
+ yaml: "YAML",
44
+ toml: "TOML",
45
+ html: "HTML",
46
+ css: "CSS",
47
+ scss: "SCSS",
48
+ less: "Less",
49
+ markdown: "Markdown",
50
+ sql: "SQL",
51
+ java: "Java",
52
+ kotlin: "Kotlin",
53
+ swift: "Swift",
54
+ c: "C",
55
+ cpp: "C++",
56
+ csharp: "C#",
57
+ rust: "Rust",
58
+ ruby: "Ruby",
59
+ php: "PHP",
60
+ lua: "Lua",
61
+ dart: "Dart",
62
+ docker: "Dockerfile",
63
+ powershell: "PowerShell",
64
+ xml: "XML",
65
+ vue: "Vue",
66
+ svelte: "Svelte",
67
+ graphql: "GraphQL",
68
+ nginx: "Nginx",
69
+ ini: "INI",
70
+ diff: "Diff",
71
+ gdscript: "GDScript"
72
+ };
73
+ function getLanguageDisplayName(normalized) {
74
+ return DISPLAY_NAMES[normalized] ?? null;
75
+ }
76
+ function normalizeLanguageId(input) {
77
+ const raw = (input ?? "").trim().toLowerCase();
78
+ if (!raw) return PLAINTEXT;
79
+ return DEFAULT_ALIASES[raw] ?? raw;
80
+ }
81
+ function getCodeBlockTitle(attrs, normalizeLanguage = normalizeLanguageId) {
82
+ const label = attrs.label?.trim();
83
+ if (label) return label;
84
+ const raw = attrs.language?.trim();
85
+ if (!raw) return "纯文本";
86
+ const normalized = normalizeLanguage(raw);
87
+ if (normalized === PLAINTEXT) return "纯文本";
88
+ return getLanguageDisplayName(normalized) ?? raw;
89
+ }
90
+ function resolveShikiTheme(theme, isDark) {
91
+ if (!theme) return isDark ? DEFAULT_THEMES.dark : DEFAULT_THEMES.light;
92
+ if (typeof theme === "string") return theme;
93
+ return isDark ? theme.dark : theme.light;
94
+ }
95
+ var escapeHtml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
96
+ function plaintextResult(source) {
97
+ return {
98
+ language: PLAINTEXT,
99
+ tokens: [],
100
+ html: `<pre class="shiki"><code>${escapeHtml(source)}</code></pre>`
101
+ };
102
+ }
103
+ function exceedsLimits(source) {
104
+ if (source.length > MAX_HIGHLIGHT_LENGTH) return true;
105
+ let lines = 1;
106
+ for (let i = 0; i < source.length; i += 1) {
107
+ if (source.charCodeAt(i) === 10) {
108
+ lines += 1;
109
+ if (lines > MAX_HIGHLIGHT_LINES) return true;
110
+ }
111
+ }
112
+ return false;
113
+ }
114
+ var defaultLoader = () => import('shiki');
115
+ var sharedModules = /* @__PURE__ */ new Map();
116
+ var sharedHighlighters = /* @__PURE__ */ new WeakMap();
117
+ var pendingLoads = /* @__PURE__ */ new WeakMap();
118
+ function loadModule(loader) {
119
+ let pending = sharedModules.get(loader);
120
+ if (!pending) {
121
+ pending = loader().catch((error) => {
122
+ sharedModules.delete(loader);
123
+ throw error;
124
+ });
125
+ sharedModules.set(loader, pending);
126
+ }
127
+ return pending;
128
+ }
129
+ function loadHighlighter(shiki) {
130
+ let pending = sharedHighlighters.get(shiki);
131
+ if (!pending) {
132
+ pending = shiki.createHighlighter({ themes: [], langs: [] }).then((h) => h).catch((error) => {
133
+ sharedHighlighters.delete(shiki);
134
+ throw error;
135
+ });
136
+ sharedHighlighters.set(shiki, pending);
137
+ }
138
+ return pending;
139
+ }
140
+ function loadOnce(highlighter, key, run) {
141
+ let loads = pendingLoads.get(highlighter);
142
+ if (!loads) {
143
+ loads = /* @__PURE__ */ new Map();
144
+ pendingLoads.set(highlighter, loads);
145
+ }
146
+ let pending = loads.get(key);
147
+ if (!pending) {
148
+ pending = run().catch((error) => {
149
+ loads?.delete(key);
150
+ throw error;
151
+ });
152
+ loads.set(key, pending);
153
+ }
154
+ return pending;
155
+ }
156
+ function flattenTokens(lines) {
157
+ const out = [];
158
+ for (const line of lines) {
159
+ for (const token of line) {
160
+ if (!token.content) continue;
161
+ const start = token.offset;
162
+ const end = start + token.content.length;
163
+ const fontStyle = typeof token.fontStyle === "number" && token.fontStyle > 0 ? token.fontStyle : void 0;
164
+ if (!token.color && fontStyle === void 0) continue;
165
+ out.push({ start, end, color: token.color, fontStyle });
166
+ }
167
+ }
168
+ return out;
169
+ }
170
+ var configSerial = 0;
171
+ function createCodeHighlighter(options = {}, deps = {}) {
172
+ const loader = deps.loadShiki ?? defaultLoader;
173
+ const aliases = { ...DEFAULT_ALIASES, ...options.languageAliases ?? {} };
174
+ configSerial += 1;
175
+ const configId = configSerial;
176
+ const cache = /* @__PURE__ */ new Map();
177
+ let cachedSourceLength = 0;
178
+ let customLanguagesLoaded = null;
179
+ const normalizeLanguage = (input) => {
180
+ const raw = (input ?? "").trim().toLowerCase();
181
+ if (!raw) return PLAINTEXT;
182
+ return aliases[raw] ?? raw;
183
+ };
184
+ const evict = () => {
185
+ while (cache.size > CACHE_MAX_ENTRIES || cachedSourceLength > CACHE_MAX_SOURCE_LENGTH) {
186
+ const oldest = cache.keys().next();
187
+ if (oldest.done) break;
188
+ const entry = cache.get(oldest.value);
189
+ cache.delete(oldest.value);
190
+ if (entry) cachedSourceLength -= entry.size;
191
+ }
192
+ };
193
+ const forget = (key) => {
194
+ const entry = cache.get(key);
195
+ if (!entry) return;
196
+ cache.delete(key);
197
+ cachedSourceLength -= entry.size;
198
+ };
199
+ const ensureCustomLanguages = (highlighter) => {
200
+ if (!options.languages) return Promise.resolve();
201
+ if (!customLanguagesLoaded) {
202
+ customLanguagesLoaded = highlighter.loadLanguage(options.languages).catch((error) => {
203
+ customLanguagesLoaded = null;
204
+ throw error;
205
+ });
206
+ }
207
+ return customLanguagesLoaded;
208
+ };
209
+ const isKnownLanguage = (shiki, highlighter, lang) => {
210
+ if (lang === PLAINTEXT) return true;
211
+ if (highlighter.getLoadedLanguages().includes(lang)) return true;
212
+ return Object.hasOwn(shiki.bundledLanguages, lang);
213
+ };
214
+ const run = async (source, lang, theme) => {
215
+ const shiki = await loadModule(loader);
216
+ const highlighter = await loadHighlighter(shiki);
217
+ await loadOnce(highlighter, `theme:${theme}`, () => highlighter.loadTheme(theme));
218
+ await ensureCustomLanguages(highlighter);
219
+ const resolved = isKnownLanguage(shiki, highlighter, lang) ? lang : PLAINTEXT;
220
+ if (resolved !== PLAINTEXT && !highlighter.getLoadedLanguages().includes(resolved)) {
221
+ await loadOnce(
222
+ highlighter,
223
+ `lang:${resolved}`,
224
+ () => highlighter.loadLanguage(resolved)
225
+ );
226
+ }
227
+ const html = highlighter.codeToHtml(source, { lang: resolved, theme });
228
+ if (resolved === PLAINTEXT) return { language: PLAINTEXT, tokens: [], html };
229
+ const { tokens } = highlighter.codeToTokens(source, { lang: resolved, theme });
230
+ return { language: resolved, tokens: flattenTokens(tokens), html };
231
+ };
232
+ const highlight = (source, language, theme) => {
233
+ const lang = normalizeLanguage(language);
234
+ const resolvedTheme = theme ?? resolveShikiTheme(options.shikiTheme, false);
235
+ if (lang === PLAINTEXT && !source) return Promise.resolve(plaintextResult(source));
236
+ if (exceedsLimits(source)) return Promise.resolve(plaintextResult(source));
237
+ const key = `${configId}\0${resolvedTheme}\0${lang}\0${source}`;
238
+ const hit = cache.get(key);
239
+ if (hit) {
240
+ cache.delete(key);
241
+ cache.set(key, hit);
242
+ return hit.promise;
243
+ }
244
+ const promise = run(source, lang, resolvedTheme).catch(() => {
245
+ forget(key);
246
+ return plaintextResult(source);
247
+ });
248
+ cache.set(key, { promise, size: source.length });
249
+ cachedSourceLength += source.length;
250
+ evict();
251
+ return promise;
252
+ };
253
+ return { normalizeLanguage, highlight };
254
+ }
255
+
256
+ export { createCodeHighlighter, getCodeBlockTitle, resolveShikiTheme };
257
+ //# sourceMappingURL=chunk-ZY5KLA7T.js.map
258
+ //# sourceMappingURL=chunk-ZY5KLA7T.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/tiptap-node/code-block-node/code-highlighter.ts"],"names":[],"mappings":"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","file":"chunk-ZY5KLA7T.js","sourcesContent":["/**\n * Shiki 高亮服务 —— 不依赖 Editor / React / DOM 的纯服务。\n *\n * 职责:语言别名解析、Shiki 动态加载(引擎 / 主题 / 语言全部懒加载并共享 Promise)、\n * 把 Shiki 的 token 行拍平成 UTF-16 区间给 ProseMirror Decoration、静态 HTML 输出、\n * 有上限的缓存,以及任何一步失败时的纯文本回退。\n *\n * 为什么用 `import(\"shiki\")` 而不是顶层 import:doc 入口之外(sheet / pdf / pptx)\n * 不能背上 Shiki;而且 bundle-full 的语言表本身只是一组 `() => import()`,\n * 真正的 grammar 只在 `loadLanguage` 时才下载。\n */\nimport type {\n CodeBlockOptions,\n CodeHighlighter,\n HighlightResult,\n HighlightToken,\n ShikiTheme,\n} from \"./code-types.ts\"\n\ntype ShikiModule = typeof import(\"shiki\")\ntype Highlighter = import(\"shiki\").HighlighterGeneric<string, string>\n\n/** Shiki 的特殊语言:不加载 grammar,直接按纯文本输出 */\nexport const PLAINTEXT = \"plaintext\"\n\nexport const DEFAULT_THEMES = { light: \"github-light\", dark: \"github-dark\" } as const\n\n/** 单块超过这个 UTF-16 长度 / 行数就不高亮(源码、复制、导出照常,只是不着色) */\nexport const MAX_HIGHLIGHT_LENGTH = 100_000\nexport const MAX_HIGHLIGHT_LINES = 2_000\n\n/** 缓存上限:条目数与保留的源码总长度,任一超限就淘汰最旧的 */\nconst CACHE_MAX_ENTRIES = 256\nconst CACHE_MAX_SOURCE_LENGTH = 2_000_000\n\n/**\n * 内置别名。Shiki 自己也带一份 `bundledLanguagesAlias`(py / sh / golang…),\n * 这里列的是**加载 Shiki 之前**就要能同步解析的那部分 —— 标签栏显示名和\n * 缓存键都在等待加载前就需要规范化结果。\n */\nconst DEFAULT_ALIASES: Record<string, string> = {\n sh: \"bash\",\n shell: \"bash\",\n zsh: \"bash\",\n py: \"python\",\n golang: \"go\",\n tsx: \"tsx\",\n jsx: \"jsx\",\n js: \"javascript\",\n mjs: \"javascript\",\n cjs: \"javascript\",\n ts: \"typescript\",\n yml: \"yaml\",\n md: \"markdown\",\n \"c++\": \"cpp\",\n \"c#\": \"csharp\",\n cs: \"csharp\",\n rb: \"ruby\",\n rs: \"rust\",\n kt: \"kotlin\",\n ps1: \"powershell\",\n dockerfile: \"docker\",\n text: PLAINTEXT,\n txt: PLAINTEXT,\n plain: PLAINTEXT,\n}\n\n/**\n * 常见语言的显示名(标签栏 / 标题用)。Shiki 的 `bundledLanguagesInfo` 有完整表,\n * 但那要先加载 Shiki;标题在加载前就要能显示,所以这里放一份同步的小表,\n * 没列到的语言直接显示规范化后的 id。\n */\nconst DISPLAY_NAMES: Record<string, string> = {\n bash: \"Bash\",\n python: \"Python\",\n go: \"Go\",\n javascript: \"JavaScript\",\n typescript: \"TypeScript\",\n jsx: \"JSX\",\n tsx: \"TSX\",\n json: \"JSON\",\n yaml: \"YAML\",\n toml: \"TOML\",\n html: \"HTML\",\n css: \"CSS\",\n scss: \"SCSS\",\n less: \"Less\",\n markdown: \"Markdown\",\n sql: \"SQL\",\n java: \"Java\",\n kotlin: \"Kotlin\",\n swift: \"Swift\",\n c: \"C\",\n cpp: \"C++\",\n csharp: \"C#\",\n rust: \"Rust\",\n ruby: \"Ruby\",\n php: \"PHP\",\n lua: \"Lua\",\n dart: \"Dart\",\n docker: \"Dockerfile\",\n powershell: \"PowerShell\",\n xml: \"XML\",\n vue: \"Vue\",\n svelte: \"Svelte\",\n graphql: \"GraphQL\",\n nginx: \"Nginx\",\n ini: \"INI\",\n diff: \"Diff\",\n gdscript: \"GDScript\",\n}\n\n/** 规范化语言 id → 显示名;没有专门显示名时返回 null,由调用方决定回退 */\nexport function getLanguageDisplayName(normalized: string): string | null {\n return DISPLAY_NAMES[normalized] ?? null\n}\n\n/** 只用内置别名的规范化(不需要服务实例的场合,如 docx 导出) */\nexport function normalizeLanguageId(input: string | null | undefined): string {\n const raw = (input ?? \"\").trim().toLowerCase()\n if (!raw) return PLAINTEXT\n return DEFAULT_ALIASES[raw] ?? raw\n}\n\n/** 标签栏 / 标题 / 导出标题的显示文字:label → 语言显示名 → 未知语言原文 → “纯文本” */\nexport function getCodeBlockTitle(\n attrs: { language: string | null; label: string | null },\n normalizeLanguage: (input: string | null | undefined) => string = normalizeLanguageId,\n): string {\n const label = attrs.label?.trim()\n if (label) return label\n const raw = attrs.language?.trim()\n if (!raw) return \"纯文本\"\n const normalized = normalizeLanguage(raw)\n if (normalized === PLAINTEXT) return \"纯文本\"\n return getLanguageDisplayName(normalized) ?? raw\n}\n\n/** 按 `.dark` 状态从配置里挑主题名 */\nexport function resolveShikiTheme(theme: ShikiTheme | undefined, isDark: boolean): string {\n if (!theme) return isDark ? DEFAULT_THEMES.dark : DEFAULT_THEMES.light\n if (typeof theme === \"string\") return theme\n return isDark ? theme.dark : theme.light\n}\n\nconst escapeHtml = (s: string): string =>\n s.replace(/&/g, \"&amp;\").replace(/</g, \"&lt;\").replace(/>/g, \"&gt;\").replace(/\"/g, \"&quot;\")\n\n/** 任何失败路径都落到这里:没有 token(不着色),HTML 只做转义 */\nfunction plaintextResult(source: string): HighlightResult {\n return {\n language: PLAINTEXT,\n tokens: [],\n html: `<pre class=\"shiki\"><code>${escapeHtml(source)}</code></pre>`,\n }\n}\n\nfunction exceedsLimits(source: string): boolean {\n if (source.length > MAX_HIGHLIGHT_LENGTH) return true\n let lines = 1\n for (let i = 0; i < source.length; i += 1) {\n if (source.charCodeAt(i) === 10) {\n lines += 1\n if (lines > MAX_HIGHLIGHT_LINES) return true\n }\n }\n return false\n}\n\n/* --------------------------- 模块级共享状态 --------------------------- */\n\n/**\n * Shiki 模块与 highlighter 实例全模块共享:多个编辑器(编辑器 + ArticleViewer)\n * 同页时只加载一次引擎和主题。失败的 Promise 会被清掉,下次调用重新尝试。\n *\n * 共享状态按 loader 函数 / 模块对象作键:生产环境永远只有 `defaultLoader` 一把键,\n * 测试注入的 loader 各自独立,不会被先前成功加载的真模块顶掉。\n */\ntype ShikiLoader = () => Promise<ShikiModule>\nconst defaultLoader: ShikiLoader = () => import(\"shiki\")\nconst sharedModules = new Map<ShikiLoader, Promise<ShikiModule>>()\nconst sharedHighlighters = new WeakMap<ShikiModule, Promise<Highlighter>>()\n/** 每个 highlighter 已发起过的 loadLanguage / loadTheme,避免并发重复加载 */\nconst pendingLoads = new WeakMap<Highlighter, Map<string, Promise<void>>>()\n\nfunction loadModule(loader: ShikiLoader): Promise<ShikiModule> {\n let pending = sharedModules.get(loader)\n if (!pending) {\n pending = loader().catch((error) => {\n sharedModules.delete(loader)\n throw error\n })\n sharedModules.set(loader, pending)\n }\n return pending\n}\n\nfunction loadHighlighter(shiki: ShikiModule): Promise<Highlighter> {\n let pending = sharedHighlighters.get(shiki)\n if (!pending) {\n pending = shiki\n .createHighlighter({ themes: [], langs: [] })\n .then((h) => h as unknown as Highlighter)\n .catch((error) => {\n sharedHighlighters.delete(shiki)\n throw error\n })\n sharedHighlighters.set(shiki, pending)\n }\n return pending\n}\n\nfunction loadOnce(highlighter: Highlighter, key: string, run: () => Promise<void>): Promise<void> {\n let loads = pendingLoads.get(highlighter)\n if (!loads) {\n loads = new Map()\n pendingLoads.set(highlighter, loads)\n }\n let pending = loads.get(key)\n if (!pending) {\n pending = run().catch((error) => {\n loads?.delete(key)\n throw error\n })\n loads.set(key, pending)\n }\n return pending\n}\n\n/* --------------------------- token 拍平 --------------------------- */\n\n/**\n * Shiki 每个 token 自带相对整段源码的 `offset`(JS 字符串索引 = UTF-16 单位),\n * 与 ProseMirror 文本节点内的位置一一对应,直接拿来做 Decoration 区间。\n * 空 token 和没有任何样式的 token 跳过:不产生无意义的 span。\n */\nfunction flattenTokens(lines: import(\"shiki\").ThemedToken[][]): HighlightToken[] {\n const out: HighlightToken[] = []\n for (const line of lines) {\n for (const token of line) {\n if (!token.content) continue\n const start = token.offset\n const end = start + token.content.length\n const fontStyle =\n typeof token.fontStyle === \"number\" && token.fontStyle > 0 ? token.fontStyle : undefined\n if (!token.color && fontStyle === undefined) continue\n out.push({ start, end, color: token.color, fontStyle })\n }\n }\n return out\n}\n\n/* --------------------------- 服务 --------------------------- */\n\nexport interface CodeHighlighterDeps {\n /** 测试注入:替换 `import(\"shiki\")` */\n loadShiki?: () => Promise<ShikiModule>\n}\n\nlet configSerial = 0\n\nexport function createCodeHighlighter(\n options: CodeBlockOptions = {},\n deps: CodeHighlighterDeps = {},\n): CodeHighlighter {\n const loader = deps.loadShiki ?? defaultLoader\n const aliases: Record<string, string> = { ...DEFAULT_ALIASES, ...(options.languageAliases ?? {}) }\n // 每个服务实例一份缓存;configId 进缓存键,保证不同配置的实例即使合并也不串\n configSerial += 1\n const configId = configSerial\n const cache = new Map<string, { promise: Promise<HighlightResult>; size: number }>()\n let cachedSourceLength = 0\n /** 业务注册的额外语言只往共享 highlighter 里灌一次 */\n let customLanguagesLoaded: Promise<void> | null = null\n\n const normalizeLanguage = (input: string | null | undefined): string => {\n const raw = (input ?? \"\").trim().toLowerCase()\n if (!raw) return PLAINTEXT\n return aliases[raw] ?? raw\n }\n\n const evict = () => {\n while (cache.size > CACHE_MAX_ENTRIES || cachedSourceLength > CACHE_MAX_SOURCE_LENGTH) {\n const oldest = cache.keys().next()\n if (oldest.done) break\n const entry = cache.get(oldest.value)\n cache.delete(oldest.value)\n if (entry) cachedSourceLength -= entry.size\n }\n }\n\n const forget = (key: string) => {\n const entry = cache.get(key)\n if (!entry) return\n cache.delete(key)\n cachedSourceLength -= entry.size\n }\n\n const ensureCustomLanguages = (highlighter: Highlighter): Promise<void> => {\n if (!options.languages) return Promise.resolve()\n if (!customLanguagesLoaded) {\n customLanguagesLoaded = highlighter\n .loadLanguage(options.languages as never)\n .catch((error) => {\n customLanguagesLoaded = null\n throw error\n })\n }\n return customLanguagesLoaded\n }\n\n const isKnownLanguage = (shiki: ShikiModule, highlighter: Highlighter, lang: string): boolean => {\n if (lang === PLAINTEXT) return true\n if (highlighter.getLoadedLanguages().includes(lang)) return true\n return Object.hasOwn(shiki.bundledLanguages, lang)\n }\n\n const run = async (source: string, lang: string, theme: string): Promise<HighlightResult> => {\n const shiki = await loadModule(loader)\n const highlighter = await loadHighlighter(shiki)\n await loadOnce(highlighter, `theme:${theme}`, () => highlighter.loadTheme(theme as never))\n await ensureCustomLanguages(highlighter)\n\n // 未知语言:属性原样留给调用方,服务内部按纯文本输出(仍走 Shiki 以保证 HTML 结构一致)\n const resolved = isKnownLanguage(shiki, highlighter, lang) ? lang : PLAINTEXT\n if (resolved !== PLAINTEXT && !highlighter.getLoadedLanguages().includes(resolved)) {\n await loadOnce(highlighter, `lang:${resolved}`, () =>\n highlighter.loadLanguage(resolved as never),\n )\n }\n\n const html = highlighter.codeToHtml(source, { lang: resolved, theme })\n if (resolved === PLAINTEXT) return { language: PLAINTEXT, tokens: [], html }\n const { tokens } = highlighter.codeToTokens(source, { lang: resolved, theme })\n return { language: resolved, tokens: flattenTokens(tokens), html }\n }\n\n const highlight = (\n source: string,\n language: string | null | undefined,\n theme?: string,\n ): Promise<HighlightResult> => {\n const lang = normalizeLanguage(language)\n const resolvedTheme = theme ?? resolveShikiTheme(options.shikiTheme, false)\n // 超长内容不进 Shiki 也不进缓存:纯文本结果本身就是常量级开销\n if (lang === PLAINTEXT && !source) return Promise.resolve(plaintextResult(source))\n if (exceedsLimits(source)) return Promise.resolve(plaintextResult(source))\n\n const key = `${configId}\u0000${resolvedTheme}\u0000${lang}\u0000${source}`\n const hit = cache.get(key)\n if (hit) {\n // LRU:命中的挪到最新\n cache.delete(key)\n cache.set(key, hit)\n return hit.promise\n }\n\n const promise = run(source, lang, resolvedTheme).catch(() => {\n // 失败不缓存:网络恢复 / 主题修正后下一次调用重新尝试\n forget(key)\n return plaintextResult(source)\n })\n cache.set(key, { promise, size: source.length })\n cachedSourceLength += source.length\n evict()\n return promise\n }\n\n return { normalizeLanguage, highlight }\n}\n"]}
@@ -0,0 +1,65 @@
1
+ import { LanguageInput } from 'shiki';
2
+
3
+ /**
4
+ * 代码块 / 代码分组的公共类型。
5
+ *
6
+ * 这份文件**只有类型**,不带任何运行时导入 —— Shiki 的类型通过 `import type`
7
+ * 引用,编译后整段擦除。这样 schema 文件、Node 测试和 React NodeView 都能
8
+ * 从这里取类型,而不会把 Shiki 拉进不该加载它的入口(sheet / pdf / pptx)。
9
+ */
10
+
11
+ /**
12
+ * 传入编辑器的内容格式。
13
+ *
14
+ * 未声明时沿用旧行为:对象按 JSON、字符串按 HTML。`"json"` 只接受对象,
15
+ * 不会去解析 JSON 文本 —— 那是业务侧的事。
16
+ */
17
+ type ContentFormat = "json" | "html" | "markdown";
18
+ /** 单主题,或按 `.dark` 状态切换的明暗主题对。 */
19
+ type ShikiTheme = string | {
20
+ light: string;
21
+ dark: string;
22
+ };
23
+ /** `codeBlock` 扩展的可选配置。默认值即可用,无需注册语言。 */
24
+ interface CodeBlockOptions {
25
+ /** 默认 `{ light: "github-light", dark: "github-dark" }` */
26
+ shikiTheme?: ShikiTheme;
27
+ /** 自定义别名,优先于内置别名,如 `{ golang: "go" }` */
28
+ languageAliases?: Record<string, string>;
29
+ /** 额外语言注册项,使用 Shiki 的 LanguageInput(注册对象或异步加载器) */
30
+ languages?: LanguageInput;
31
+ }
32
+ /** `codeBlock` 节点属性。`language` 保留输入原值,规范化只在高亮时进行。 */
33
+ interface CodeBlockAttrs {
34
+ id?: string | null;
35
+ language: string | null;
36
+ /** 标签页文字 / 文件名,如 `curl`、`example.py`;为空时显示语言名 */
37
+ label: string | null;
38
+ }
39
+ /** `codeGroup` 节点属性。活动标签不在这里 —— 那是 NodeView 本地状态。 */
40
+ interface CodeGroupAttrs {
41
+ id?: string | null;
42
+ }
43
+ /** 一个 token 的 UTF-16 区间(相对代码块文本起点)与样式。 */
44
+ interface HighlightToken {
45
+ start: number;
46
+ end: number;
47
+ color?: string;
48
+ /** Shiki FontStyle 位掩码:1 斜体、2 粗体、4 下划线、8 删除线 */
49
+ fontStyle?: number;
50
+ }
51
+ /** 高亮服务的输出:Decoration 用 tokens,静态导出用 html。 */
52
+ interface HighlightResult {
53
+ /** 实际用于高亮的规范化语言;未知语言时为 `"plaintext"` */
54
+ language: string;
55
+ tokens: HighlightToken[];
56
+ html: string;
57
+ }
58
+ interface CodeHighlighter {
59
+ /** 别名解析 + 小写化;不认识的语言原样返回(调用方决定回退) */
60
+ normalizeLanguage(input: string | null | undefined): string;
61
+ /** 未指定 theme 时按配置的 light 主题 */
62
+ highlight(source: string, language: string | null | undefined, theme?: string): Promise<HighlightResult>;
63
+ }
64
+
65
+ export type { ContentFormat as C, HighlightResult as H, ShikiTheme as S, CodeBlockOptions as a, CodeHighlighter as b, CodeBlockAttrs as c, CodeGroupAttrs as d, HighlightToken as e };
@@ -175,4 +175,42 @@ interface WorkbookData {
175
175
  }>;
176
176
  }
177
177
 
178
- export type { CellData as C, RowData as R, StyleData as S, WorkbookData as W, WorksheetData as a, CellRange as b, CellValue as c, ColumnData as d };
178
+ /**
179
+ * CSV → Univer 快照。`csv.ts`(导出侧)的逆。
180
+ *
181
+ * 解析按 RFC 4180:引号字段、内部引号翻倍、字段内换行都认;
182
+ * 行分隔 `\r\n` / `\n` / `\r` 混用也收 —— 真实世界的 csv 三种都有。
183
+ *
184
+ * **类型推断是有立场的**:看起来是数字就转数字、TRUE/FALSE 转布尔,
185
+ * 但「会失真的数字」除外 —— 前导零("0123")和超过 15 位有效数字的
186
+ * (超出 IEEE 754 精度,转了就变值)保留为强制文本,宁可让用户看到
187
+ * 原文也不悄悄改数据。不想要推断就 `inferTypes: false`,全部按文本进。
188
+ */
189
+
190
+ interface CsvParseOptions {
191
+ /** 分隔符,默认 `,`。tsv 传 `\t` */
192
+ delimiter?: string;
193
+ /** 是否推断数字/布尔类型,默认 true */
194
+ inferTypes?: boolean;
195
+ /** 工作表名,默认 "Sheet1" */
196
+ sheetName?: string;
197
+ /** 工作簿名 */
198
+ name?: string;
199
+ }
200
+ interface CsvToSnapshotResult {
201
+ workbook: WorkbookData;
202
+ warnings: string[];
203
+ stats: {
204
+ rows: number;
205
+ columns: number;
206
+ cells: number;
207
+ };
208
+ }
209
+ /**
210
+ * RFC 4180 状态机。比 split 系写法多出的能力:引号字段里的
211
+ * 分隔符/换行/翻倍引号。输入带 BOM 时自动剥掉。
212
+ */
213
+ declare function parseCsv(text: string, delimiter?: string): string[][];
214
+ declare function csvToSnapshot(text: string, options?: CsvParseOptions): CsvToSnapshotResult;
215
+
216
+ export { type CellData as C, type RowData as R, type StyleData as S, type WorkbookData as W, type CsvParseOptions as a, type CsvToSnapshotResult as b, type WorksheetData as c, csvToSnapshot as d, type CellRange as e, type CellValue as f, type ColumnData as g, parseCsv as p };
package/dist/csv.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { W as WorkbookData } from './types-C37MAt8o.js';
2
- export { C as CellData, a as WorksheetData } from './types-C37MAt8o.js';
1
+ import { W as WorkbookData } from './csv-parse-D0zTBvDB.js';
2
+ export { C as CellData, a as CsvParseOptions, b as CsvToSnapshotResult, c as WorksheetData, d as csvToSnapshot, p as parseCsv } from './csv-parse-D0zTBvDB.js';
3
3
 
4
4
  /**
5
5
  * Univer 快照 → CSV。
@@ -49,7 +49,7 @@ declare const CSV_MIME = "text/csv;charset=utf-8";
49
49
  declare function csvFromSnapshot(workbook: WorkbookData, options?: CsvOptions): CsvResult;
50
50
 
51
51
  /**
52
- * `@4399ywkf/editor/csv` —— 表格导出 CSV。
52
+ * `@4399ywkf/editor/csv` —— 表格导入导出 CSV。
53
53
  *
54
54
  * 独立子路径而不是塞进 `./xlsx`:csv 这条路**一个依赖都不需要**
55
55
  * (xlsx 那边要 fflate 打 zip),几 KB 就够。只要 csv 的宿主不该为 zip 买单。
@@ -64,6 +64,9 @@ declare function csvFromSnapshot(workbook: WorkbookData, options?: CsvOptions):
64
64
  * **csv 是单表格式,工作簿是多表的。** 本实现只导一张表,其余的在 `warnings`
65
65
  * 里逐个点名。传 `univerAPI` 时导真正的活动表;只传快照时导第一张 ——
66
66
  * 快照里根本没有「活动表」这个字段。要指定就用 `options.sheet`(表 id 或表名)。
67
+ *
68
+ * 导入是反方向(csv 文本 → 快照),见 `csvToSnapshot` / `parseCsv`。
69
+ * 要连 File/Blob/编码嗅探一起管的,用 `@4399ywkf/editor/xlsx` 的 `importCsv`。
67
70
  */
68
71
 
69
72
  /**