@4399ywkf/editor 0.5.0 → 0.7.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 (49) hide show
  1. package/README.md +164 -11
  2. package/dist/PptxView-ZH4FZKNO.js +3 -0
  3. package/dist/PptxView-ZH4FZKNO.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-5XG4YMAB.js +149 -0
  9. package/dist/chunk-5XG4YMAB.js.map +1 -0
  10. package/dist/chunk-DGUNVCOM.js +711 -0
  11. package/dist/chunk-DGUNVCOM.js.map +1 -0
  12. package/dist/chunk-HPSPOM2O.js +128 -0
  13. package/dist/chunk-HPSPOM2O.js.map +1 -0
  14. package/dist/chunk-KRP5V7VF.js +130 -0
  15. package/dist/chunk-KRP5V7VF.js.map +1 -0
  16. package/dist/chunk-SBTKDJAL.js +107 -0
  17. package/dist/chunk-SBTKDJAL.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/csv-parse-D0zTBvDB.d.ts +216 -0
  23. package/dist/csv.d.ts +86 -0
  24. package/dist/csv.js +34 -0
  25. package/dist/csv.js.map +1 -0
  26. package/dist/docx.d.ts +15 -0
  27. package/dist/docx.js +61 -3
  28. package/dist/docx.js.map +1 -1
  29. package/dist/import-GVZDLWA2.js +6 -0
  30. package/dist/import-GVZDLWA2.js.map +1 -0
  31. package/dist/index.d.ts +2 -2
  32. package/dist/index.js +21 -2
  33. package/dist/index.js.map +1 -1
  34. package/dist/pdf.d.ts +1 -1
  35. package/dist/pptx.d.ts +36 -0
  36. package/dist/pptx.js +3 -0
  37. package/dist/pptx.js.map +1 -0
  38. package/dist/{registry-BBL8yeIT.d.ts → registry-XJFJXtZ_.d.ts} +6 -0
  39. package/dist/sheet-runtime.d.ts +212 -0
  40. package/dist/sheet-runtime.js +416 -0
  41. package/dist/sheet-runtime.js.map +1 -0
  42. package/dist/sheet.d.ts +7 -5
  43. package/dist/sheet.js +1 -1
  44. package/dist/xlsx.d.ts +127 -189
  45. package/dist/xlsx.js +8 -104
  46. package/dist/xlsx.js.map +1 -1
  47. package/package.json +19 -3
  48. package/dist/SheetView-DSADIZNS.js +0 -3
  49. 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":";;;;AAmBe,SAAR,SAAA,CAA2B;AAAA,EAChC,MAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,KAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA;AACF,CAAA,EAAsB;AACpB,EAAA,MAAM,OAAA,GAAU,OAAuB,IAAI,CAAA;AAC3C,EAAA,MAAM,UAAA,GAAa,OAA4B,IAAI,CAAA;AAKnD,EAAA,MAAM,SAAS,MAAA,CAAO,EAAE,OAAO,OAAA,EAAS,OAAA,EAAS,YAAY,CAAA;AAC7D,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAA,CAAO,OAAA,GAAU,EAAE,KAAA,EAAO,OAAA,EAAS,SAAS,UAAA,EAAW;AAAA,EACzD,CAAC,CAAA;AAED,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,IAAI,SAAA,GAAY,KAAA;AAChB,IAAA,MAAM,OAAO,OAAA,CAAQ,OAAA;AACrB,IAAA,IAAI,CAAC,IAAA,EAAM;AAEV,IAAA,CAAC,YAAY;AACZ,MAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,SAAS,IAAA,EAAM,UAAA,EAAY,IAAA,EAAK,GAAI,MAAA,CAAO,OAAA;AAChF,MAAA,IAAI;AAGF,QAAA,MAAM,aAAA,GACJ,CAAC,OAAA,IAAW,MAAA,GACR,OAAO,sBAAgB,CAAA,CAAE,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,iBAAA,CAAkB,MAAM,CAAC,CAAA,GAChE,IAAA;AAGN,QAAA,MAAM,CAAC,EAAE,YAAA,EAAc,UAAA,EAAY,KAAA,EAAM,EAAG,UAAA,EAAY,IAAI,CAAA,GAAI,MAAM,OAAA,CAAQ,GAAA,CAAI;AAAA,UAChF,OAAO,mBAAmB,CAAA;AAAA,UAC1B,OAAO,sCAAsC,CAAA;AAAA,UAC7C,OAAO,oDAAoD;AAAA,SAC5D,CAAA;AACD,QAAA,IAAI,SAAA,EAAW;AAIf,QAAA,MAAM,EAAA,GAAqB,EAAE,GAAI,QAAA,GAAW,EAAE,OAAA,EAAS,KAAA,EAAM,GAAI,IAAA,EAAO,GAAG,IAAA,EAAM,EAAA,EAAG;AAIpF,QAAA,MAAM,OAAA,GAAqB;AAAA,UAEvB,WACA,sBAAA,CAAuB,EAAE,WAAW,IAAA,EAAM,GAAG,IAAI;AAAA,SACrD;AACA,QAAA,MAAM,OAAA,GAAqB,CAAE,IAAA,CAAwC,OAAO,CAAA;AAE5E,QAAA,IAAI,MAAM,WAAA,EAAa;AAGrB,UAAA,MAAM,CAAC,WAAA,EAAa,eAAe,CAAA,GAAI,MAAM,QAAQ,GAAA,CAAI;AAAA,YACvD,OAAO,8CAA8C,CAAA;AAAA,YACrD,OAAO,4DAA4D;AAAA,WACpE,CAAA;AACD,UAAA,IAAI,SAAA,EAAW;AACf,UAAA,OAAA,CAAQ,IAAA;AAAA,YAEJ,YACA,6BAAA;AAA8B,WAClC;AACA,UAAA,OAAA,CAAQ,IAAA,CAAM,gBAAmD,OAAO,CAAA;AAAA,QAC1E;AAEA,QAAA,IAAI,MAAM,OAAA,EAAS,MAAA,UAAgB,IAAA,CAAK,GAAG,KAAK,OAAO,CAAA;AACvD,QAAA,IAAI,MAAM,OAAA,EAAS,MAAA,UAAgB,IAAA,CAAK,GAAG,KAAK,OAAO,CAAA;AAGvD,QAAA,MAAM,QAAA,GAAW,aAAA,GAAgB,MAAM,aAAA,GAAgB,IAAA;AACvD,QAAA,IAAI,SAAA,EAAW;AAEf,QAAA,MAAM,EAAE,SAAA,EAAW,MAAA,EAAO,GAAI,YAAA,CAAa;AAAA,UACzC,QAAQ,UAAA,CAAW,KAAA;AAAA;AAAA;AAAA,UAGnB,OAAA,EAAS,EAAE,CAAC,UAAA,CAAW,KAAK,GAAG,KAAA,CAAM,EAAC,EAAG,GAAI,OAAoB,CAAA,EAAE;AAAA,UACnE;AAAA,SACD,CAAA;AAED,QAAA,MAAM,OAAA,GAAU,MAAO,MAAA,CAA8C,OAAA,EAAQ;AAG7E,QAAA,IAAI,SAAA,SAAkB,OAAA,EAAQ;AAE9B,QAAA,SAAA,CAAU,cAAA,CAAgB,OAAA,IAAW,QAAA,EAAU,QAAA,IAAY,EAAY,CAAA;AACvE,QAAA,IAAI,QAAA,EAAU,SAAA,CAAU,iBAAA,IAAoB,EAAG,cAAc,KAAK,CAAA;AAClE,QAAA,IAAI,QAAA,EAAU,QAAA,CAAS,MAAA,EAAQ,IAAA,GAAO,SAAS,QAAQ,CAAA;AAEvD,QAAA,UAAA,CAAW,OAAA,GAAU,OAAA;AACrB,QAAA,KAAA,GAAQ,SAAS,CAAA;AAAA,MACnB,SAAS,CAAA,EAAG;AACV,QAAA,MAAM,GAAA,GAAM,aAAa,KAAA,GAAQ,CAAA,GAAI,IAAI,KAAA,CAAM,MAAA,CAAO,CAAC,CAAC,CAAA;AACxD,QAAA,IAAA;AAAA,UACE,IAAI,KAAA;AAAA,YACF,CAAA,OAAA,EAAU,IAAI,OAAO,CAAA,gEAAA;AAAA;AAEvB,SACF;AAAA,MACF;AAAA,IACF,CAAA,GAAG;AAEH,IAAA,OAAO,MAAM;AACX,MAAA,SAAA,GAAY,IAAA;AACZ,MAAA,UAAA,CAAW,OAAA,IAAU;AACrB,MAAA,UAAA,CAAW,OAAA,GAAU,IAAA;AAAA,IACvB,CAAA;AAAA,EACF,CAAA,EAAG,CAAC,MAAA,EAAQ,OAAA,EAAS,QAAQ,CAAC,CAAA;AAE9B,EAAA,uBAAO,GAAA,CAAC,KAAA,EAAA,EAAI,GAAA,EAAK,OAAA,EAAS,SAAA,EAAsB,KAAA,EAAO,EAAE,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAQ,MAAA,EAAO,EAAG,CAAA;AAC5F","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,216 @@
1
+ /**
2
+ * Univer 快照的结构化镜像。
3
+ *
4
+ * 为什么不直接 `import type { IWorkbookData } from "@univerjs/core"`:
5
+ * 那是**可选 peer**。没装 `@univerjs/presets` 的项目一解析不到这个模块,
6
+ * 整个包的 typecheck 就红了 —— 而「拿快照出 xlsx」恰恰是最不该逼人装 871KB
7
+ * 渲染引擎的场景(服务端定时导出、Node 里跑批都只有 JSON)。
8
+ *
9
+ * 所以这里只声明导出真正读到的字段,字段名与取值语义和 `@univerjs/core`
10
+ * 逐一对齐。Univer 加字段不会影响这里;改字段含义会 —— 那种时候本文件是
11
+ * 唯一需要改的地方。
12
+ */
13
+ type CellValue = string | number | boolean;
14
+ interface ColorStyle {
15
+ /** `#RRGGBB` / `rgb(r,g,b)` / `rgba(r,g,b,a)` */
16
+ rgb?: string | null;
17
+ /** 主题色 token。xlsx 侧解析不了,直接忽略 */
18
+ th?: number;
19
+ }
20
+ /** 下划线 / 删除线 / 上划线 */
21
+ interface TextDecorationData {
22
+ /** 0 关 1 开 */
23
+ s?: number;
24
+ cl?: ColorStyle | null;
25
+ t?: number;
26
+ }
27
+ interface BorderStyleData {
28
+ /** 对齐 `BorderStyleTypes` */
29
+ s?: number;
30
+ cl?: ColorStyle | null;
31
+ }
32
+ interface BorderData {
33
+ t?: BorderStyleData | null;
34
+ r?: BorderStyleData | null;
35
+ b?: BorderStyleData | null;
36
+ l?: BorderStyleData | null;
37
+ }
38
+ interface TextRotation {
39
+ /** 角度,-90 ~ 90,正数逆时针 */
40
+ a?: number;
41
+ /** 1 = 竖排 */
42
+ v?: number;
43
+ }
44
+ interface StyleData {
45
+ /** fontFamily */
46
+ ff?: string | null;
47
+ /** fontSize,pt */
48
+ fs?: number;
49
+ /** italic */
50
+ it?: number;
51
+ /** bold */
52
+ bl?: number;
53
+ /** underline */
54
+ ul?: TextDecorationData | null;
55
+ /** strikethrough */
56
+ st?: TextDecorationData | null;
57
+ /** overline —— xlsx 没有对应表达 */
58
+ ol?: TextDecorationData | null;
59
+ /** background */
60
+ bg?: ColorStyle | null;
61
+ /** border */
62
+ bd?: BorderData | null;
63
+ /** font color */
64
+ cl?: ColorStyle | null;
65
+ /** 上下标 */
66
+ va?: number | null;
67
+ /** 数字格式 */
68
+ n?: {
69
+ pattern?: string;
70
+ } | null;
71
+ /** textRotation */
72
+ tr?: TextRotation | null;
73
+ /** horizontalAlign */
74
+ ht?: number | null;
75
+ /** verticalAlign */
76
+ vt?: number | null;
77
+ /** wrapStrategy */
78
+ tb?: number | null;
79
+ }
80
+ /** 富文本单元格。导出只取纯文本 —— xlsx 的富文本要拆成 `<si><r>` 段,收益不抵复杂度 */
81
+ interface RichTextData {
82
+ body?: {
83
+ dataStream?: string;
84
+ } | null;
85
+ }
86
+ interface CellData {
87
+ v?: CellValue | null;
88
+ /** 对齐 `CellValueType` */
89
+ t?: number | null;
90
+ /** 原始公式,含前导 `=` */
91
+ f?: string | null;
92
+ /** 共享公式组 id。同组里只有一个单元格带 `f` */
93
+ si?: string | null;
94
+ /** 数组公式的引用范围 */
95
+ ref?: string | null;
96
+ /** 样式 id 或内联样式 */
97
+ s?: StyleData | string | null;
98
+ /** 富文本 */
99
+ p?: RichTextData | null;
100
+ }
101
+ interface RowData {
102
+ /** 高度 px */
103
+ h?: number;
104
+ /** 自适应高度 px,`ia` 为 1 时用它 */
105
+ ah?: number;
106
+ /** 是否自适应 */
107
+ ia?: number;
108
+ /** 隐藏 */
109
+ hd?: number;
110
+ s?: StyleData | string | null;
111
+ }
112
+ interface ColumnData {
113
+ /** 宽度 px */
114
+ w?: number;
115
+ hd?: number;
116
+ s?: StyleData | string | null;
117
+ }
118
+ interface CellRange {
119
+ startRow: number;
120
+ startColumn: number;
121
+ endRow: number;
122
+ endColumn: number;
123
+ }
124
+ interface FreezeData {
125
+ /** 冻结列数 */
126
+ xSplit: number;
127
+ /** 冻结行数 */
128
+ ySplit: number;
129
+ startRow: number;
130
+ startColumn: number;
131
+ }
132
+ /** `IObjectMatrixPrimitiveType<T>`:行号 → 列号 → 值,两层都是稀疏对象 */
133
+ type Matrix<T> = Record<number, Record<number, T> | undefined>;
134
+ interface WorksheetData {
135
+ id?: string;
136
+ name?: string;
137
+ /** `#RRGGBB` */
138
+ tabColor?: string;
139
+ /** 1 = 隐藏 */
140
+ hidden?: number;
141
+ freeze?: FreezeData;
142
+ rowCount?: number;
143
+ columnCount?: number;
144
+ /** px */
145
+ defaultColumnWidth?: number;
146
+ /** px */
147
+ defaultRowHeight?: number;
148
+ mergeData?: CellRange[];
149
+ cellData?: Matrix<CellData>;
150
+ rowData?: Record<number, RowData | undefined>;
151
+ columnData?: Record<number, ColumnData | undefined>;
152
+ /** 0 = 不显示网格线 */
153
+ showGridlines?: number;
154
+ /** 1 = 从右向左 */
155
+ rightToLeft?: number;
156
+ defaultStyle?: StyleData | string | null;
157
+ }
158
+ interface WorkbookData {
159
+ id?: string;
160
+ name?: string;
161
+ /** 工作表顺序。缺省时按 `sheets` 的键序 */
162
+ sheetOrder?: string[];
163
+ sheets?: Record<string, WorksheetData | undefined>;
164
+ /** 样式表:`CellData.s` 为字符串时查这里 */
165
+ styles?: Record<string, StyleData | null | undefined>;
166
+ defaultStyle?: StyleData | string | null;
167
+ /**
168
+ * 各插件自己序列化的数据(图片、条件格式、数据校验、筛选……)。
169
+ * 导出读不了它们的私有结构,但**能据此如实报告丢了什么** —— 见 `warnings`。
170
+ */
171
+ resources?: Array<{
172
+ id?: string;
173
+ name: string;
174
+ data: string;
175
+ }>;
176
+ }
177
+
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 ADDED
@@ -0,0 +1,86 @@
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
+
4
+ /**
5
+ * Univer 快照 → CSV。
6
+ *
7
+ * **csv 是单表格式,工作簿是多表的**,这是产品决定不是技术决定。本实现的口径:
8
+ * 导**一张**表,其余的在 `warnings` 里**逐个点名**,和 xlsx 导出「如实报告丢了什么」
9
+ * 保持一致 —— 静悄悄只给一张表,用户往往到对账时才发现少了东西。
10
+ *
11
+ * 「哪张是活动表」只有运行时的 Univer 实例知道,**快照里根本没有这个字段**。
12
+ * 所以:传 `univerAPI` 时取真正的活动表;只传快照时取 `sheetOrder` 的第一张,
13
+ * 并且照样把这件事写进 warnings。要指定就用 `options.sheet`(表 id 或表名)。
14
+ */
15
+
16
+ interface CsvOptions {
17
+ /** 导哪张表:工作表 id 或表名。不传则用活动表(拿不到就用第一张) */
18
+ sheet?: string;
19
+ /** 分隔符,默认 `,`。用 `\t` 就是 tsv */
20
+ delimiter?: string;
21
+ /** 行分隔,默认 `\r\n`(RFC 4180 规定,Excel 也最稳) */
22
+ newline?: string;
23
+ /**
24
+ * 是否加 UTF-8 BOM,默认 **true**。
25
+ * 不加的话 Excel 会按本地编码(简中系统是 GBK)解析,中文全是乱码 ——
26
+ * 这是 csv 导出最常见的投诉,默认就得挡掉。
27
+ */
28
+ bom?: boolean;
29
+ /** 公式格写 Univer 算好的值(默认)还是写公式文本 */
30
+ formulas?: "value" | "formula";
31
+ }
32
+ interface CsvResult {
33
+ /** 文本内容,不含 BOM */
34
+ text: string;
35
+ /** 可直接落盘/上传的字节,按 `bom` 决定是否带 BOM */
36
+ bytes: Uint8Array;
37
+ /** 导出时丢掉/降级了什么。**要展示给用户** */
38
+ warnings: string[];
39
+ stats: {
40
+ sheetName: string;
41
+ rows: number;
42
+ columns: number;
43
+ cells: number;
44
+ /** 没被导出的工作表数 */
45
+ skippedSheets: number;
46
+ };
47
+ }
48
+ declare const CSV_MIME = "text/csv;charset=utf-8";
49
+ declare function csvFromSnapshot(workbook: WorkbookData, options?: CsvOptions): CsvResult;
50
+
51
+ /**
52
+ * `@4399ywkf/editor/csv` —— 表格导入导出 CSV。
53
+ *
54
+ * 独立子路径而不是塞进 `./xlsx`:csv 这条路**一个依赖都不需要**
55
+ * (xlsx 那边要 fflate 打 zip),几 KB 就够。只要 csv 的宿主不该为 zip 买单。
56
+ *
57
+ * ```ts
58
+ * import { downloadCsv } from "@4399ywkf/editor/csv"
59
+ *
60
+ * const { warnings } = downloadCsv(univerAPI, "明细.csv")
61
+ * if (warnings.length) toast(warnings.join("\n")) // ← 里面会点名哪些表没导
62
+ * ```
63
+ *
64
+ * **csv 是单表格式,工作簿是多表的。** 本实现只导一张表,其余的在 `warnings`
65
+ * 里逐个点名。传 `univerAPI` 时导真正的活动表;只传快照时导第一张 ——
66
+ * 快照里根本没有「活动表」这个字段。要指定就用 `options.sheet`(表 id 或表名)。
67
+ *
68
+ * 导入是反方向(csv 文本 → 快照),见 `csvToSnapshot` / `parseCsv`。
69
+ * 要连 File/Blob/编码嗅探一起管的,用 `@4399ywkf/editor/xlsx` 的 `importCsv`。
70
+ */
71
+
72
+ /**
73
+ * Univer 实例或快照 → CSV。
74
+ *
75
+ * 传实例时会自动锁定**当前活动表** —— 这也是唯一能拿到活动表的途径。
76
+ */
77
+ declare function exportCsv(source: WorkbookData | unknown, options?: CsvOptions): CsvResult;
78
+ /** Univer 实例或快照 → Blob,便于交给上传接口 */
79
+ declare function csvBlob(source: WorkbookData | unknown, options?: CsvOptions): Blob;
80
+ /**
81
+ * 触发浏览器下载。返回 warnings / stats —— 别丢,
82
+ * 「另外几张表没导出」只能从这里知道。
83
+ */
84
+ declare function downloadCsv(source: WorkbookData | unknown, filename?: string, options?: CsvOptions): CsvResult;
85
+
86
+ export { CSV_MIME, type CsvOptions, type CsvResult, WorkbookData, csvBlob, csvFromSnapshot, downloadCsv, exportCsv };
package/dist/csv.js ADDED
@@ -0,0 +1,34 @@
1
+ import { activeSheetId, csvFromSnapshot, CSV_MIME } from './chunk-HPSPOM2O.js';
2
+ export { CSV_MIME, csvFromSnapshot } from './chunk-HPSPOM2O.js';
3
+ import { toSnapshot } from './chunk-WF7PIIFR.js';
4
+ export { csvToSnapshot, parseCsv } from './chunk-KRP5V7VF.js';
5
+ import './chunk-5HMKRNIH.js';
6
+
7
+ // src/csv.ts
8
+ function exportCsv(source, options = {}) {
9
+ const snapshot = toSnapshot(source);
10
+ const sheet = options.sheet ?? (snapshot === source ? void 0 : activeSheetId(source));
11
+ return csvFromSnapshot(snapshot, { ...options, sheet });
12
+ }
13
+ function csvBlob(source, options) {
14
+ return new Blob([exportCsv(source, options).bytes], { type: CSV_MIME });
15
+ }
16
+ function downloadCsv(source, filename = "表格.csv", options) {
17
+ const result = exportCsv(source, options);
18
+ if (typeof document === "undefined") {
19
+ throw new Error("downloadCsv 需要浏览器环境,服务端请改用 exportCsv 自己落盘");
20
+ }
21
+ const url = URL.createObjectURL(new Blob([result.bytes], { type: CSV_MIME }));
22
+ const a = document.createElement("a");
23
+ a.href = url;
24
+ a.download = filename.endsWith(".csv") ? filename : `${filename}.csv`;
25
+ document.body.appendChild(a);
26
+ a.click();
27
+ a.remove();
28
+ setTimeout(() => URL.revokeObjectURL(url), 1e4);
29
+ return result;
30
+ }
31
+
32
+ export { csvBlob, downloadCsv, exportCsv };
33
+ //# sourceMappingURL=csv.js.map
34
+ //# sourceMappingURL=csv.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/csv.ts"],"names":[],"mappings":";;;;;;;AA0CO,SAAS,SAAA,CAAU,MAAA,EAAgC,OAAA,GAAsB,EAAC,EAAc;AAC7F,EAAA,MAAM,QAAA,GAAW,WAAW,MAAM,CAAA;AAElC,EAAA,MAAM,QAAQ,OAAA,CAAQ,KAAA,KAAU,aAAa,MAAA,GAAS,MAAA,GAAY,cAAc,MAAM,CAAA,CAAA;AACtF,EAAA,OAAO,gBAAgB,QAAA,EAAU,EAAE,GAAG,OAAA,EAAS,OAAO,CAAA;AACxD;AAGO,SAAS,OAAA,CAAQ,QAAgC,OAAA,EAA4B;AAClF,EAAA,OAAO,IAAI,IAAA,CAAK,CAAC,SAAA,CAAU,MAAA,EAAQ,OAAO,CAAA,CAAE,KAAiB,CAAA,EAAG,EAAE,IAAA,EAAM,QAAA,EAAU,CAAA;AACpF;AAMO,SAAS,WAAA,CACd,MAAA,EACA,QAAA,GAAW,QAAA,EACX,OAAA,EACW;AACX,EAAA,MAAM,MAAA,GAAS,SAAA,CAAU,MAAA,EAAQ,OAAO,CAAA;AACxC,EAAA,IAAI,OAAO,aAAa,WAAA,EAAa;AACnC,IAAA,MAAM,IAAI,MAAM,2CAA2C,CAAA;AAAA,EAC7D;AACA,EAAA,MAAM,GAAA,GAAM,GAAA,CAAI,eAAA,CAAgB,IAAI,IAAA,CAAK,CAAC,MAAA,CAAO,KAAiB,CAAA,EAAG,EAAE,IAAA,EAAM,QAAA,EAAU,CAAC,CAAA;AACxF,EAAA,MAAM,CAAA,GAAI,QAAA,CAAS,aAAA,CAAc,GAAG,CAAA;AACpC,EAAA,CAAA,CAAE,IAAA,GAAO,GAAA;AACT,EAAA,CAAA,CAAE,WAAW,QAAA,CAAS,QAAA,CAAS,MAAM,CAAA,GAAI,QAAA,GAAW,GAAG,QAAQ,CAAA,IAAA,CAAA;AAC/D,EAAA,QAAA,CAAS,IAAA,CAAK,YAAY,CAAC,CAAA;AAC3B,EAAA,CAAA,CAAE,KAAA,EAAM;AACR,EAAA,CAAA,CAAE,MAAA,EAAO;AAET,EAAA,UAAA,CAAW,MAAM,GAAA,CAAI,eAAA,CAAgB,GAAG,GAAG,GAAM,CAAA;AACjD,EAAA,OAAO,MAAA;AACT","file":"csv.js","sourcesContent":["/**\n * `@4399ywkf/editor/csv` —— 表格导入导出 CSV。\n *\n * 独立子路径而不是塞进 `./xlsx`:csv 这条路**一个依赖都不需要**\n * (xlsx 那边要 fflate 打 zip),几 KB 就够。只要 csv 的宿主不该为 zip 买单。\n *\n * ```ts\n * import { downloadCsv } from \"@4399ywkf/editor/csv\"\n *\n * const { warnings } = downloadCsv(univerAPI, \"明细.csv\")\n * if (warnings.length) toast(warnings.join(\"\\n\")) // ← 里面会点名哪些表没导\n * ```\n *\n * **csv 是单表格式,工作簿是多表的。** 本实现只导一张表,其余的在 `warnings`\n * 里逐个点名。传 `univerAPI` 时导真正的活动表;只传快照时导第一张 ——\n * 快照里根本没有「活动表」这个字段。要指定就用 `options.sheet`(表 id 或表名)。\n *\n * 导入是反方向(csv 文本 → 快照),见 `csvToSnapshot` / `parseCsv`。\n * 要连 File/Blob/编码嗅探一起管的,用 `@4399ywkf/editor/xlsx` 的 `importCsv`。\n */\n\nimport {\n activeSheetId,\n CSV_MIME,\n type CsvOptions,\n type CsvResult,\n csvFromSnapshot,\n} from \"./xlsx/csv\"\nimport { toSnapshot } from \"./xlsx/snapshot\"\nimport type { WorkbookData } from \"./xlsx/types\"\n\nexport type { CsvOptions, CsvResult } from \"./xlsx/csv\"\nexport { CSV_MIME, csvFromSnapshot } from \"./xlsx/csv\"\nexport type { CsvParseOptions, CsvToSnapshotResult } from \"./xlsx/csv-parse\"\nexport { csvToSnapshot, parseCsv } from \"./xlsx/csv-parse\"\nexport type { CellData, WorkbookData, WorksheetData } from \"./xlsx/types\"\n\n/**\n * Univer 实例或快照 → CSV。\n *\n * 传实例时会自动锁定**当前活动表** —— 这也是唯一能拿到活动表的途径。\n */\nexport function exportCsv(source: WorkbookData | unknown, options: CsvOptions = {}): CsvResult {\n const snapshot = toSnapshot(source)\n // 已显式指定就不去猜;否则只在传的是实例时才问得出活动表\n const sheet = options.sheet ?? (snapshot === source ? undefined : activeSheetId(source))\n return csvFromSnapshot(snapshot, { ...options, sheet })\n}\n\n/** Univer 实例或快照 → Blob,便于交给上传接口 */\nexport function csvBlob(source: WorkbookData | unknown, options?: CsvOptions): Blob {\n return new Blob([exportCsv(source, options).bytes as BlobPart], { type: CSV_MIME })\n}\n\n/**\n * 触发浏览器下载。返回 warnings / stats —— 别丢,\n * 「另外几张表没导出」只能从这里知道。\n */\nexport function downloadCsv(\n source: WorkbookData | unknown,\n filename = \"表格.csv\",\n options?: CsvOptions,\n): CsvResult {\n const result = exportCsv(source, options)\n if (typeof document === \"undefined\") {\n throw new Error(\"downloadCsv 需要浏览器环境,服务端请改用 exportCsv 自己落盘\")\n }\n const url = URL.createObjectURL(new Blob([result.bytes as BlobPart], { type: CSV_MIME }))\n const a = document.createElement(\"a\")\n a.href = url\n a.download = filename.endsWith(\".csv\") ? filename : `${filename}.csv`\n document.body.appendChild(a)\n a.click()\n a.remove()\n // 立刻 revoke 会让部分浏览器(Safari)来不及取数据\n setTimeout(() => URL.revokeObjectURL(url), 10_000)\n return result\n}\n"]}
package/dist/docx.d.ts CHANGED
@@ -104,6 +104,14 @@ interface ParseDocxResult {
104
104
  header: Record<string, unknown> | null;
105
105
  /** 默认页脚的结构化内容,同上 */
106
106
  footer: Record<string, unknown> | null;
107
+ /**
108
+ * 脚注。**键是正文上标标记 `[n]` 里的编号**(按引用出现顺序 1 起编),
109
+ * 宿主拿 `footnotes["1"]` 就是正文里 `[1]` 指的那条 —— 不用自己解 w:id。
110
+ * 编辑器没有脚注节点,所以注文不进文档树、导出 docx 时也不会写回。
111
+ */
112
+ footnotes: Record<string, Record<string, unknown>>;
113
+ /** 尾注,键的语义同 `footnotes`(编号序列与脚注各自独立) */
114
+ endnotes: Record<string, Record<string, unknown>>;
107
115
  /** 节属性原件,交给导出侧原样写回 */
108
116
  section: DocxSection;
109
117
  }
@@ -276,7 +284,14 @@ interface ImportDocxContext {
276
284
  * 但它只活在内存里:刷新页面 / 换设备就没了,要跨会话往返得自己存。
277
285
  */
278
286
  section: DocxSection;
287
+ /**
288
+ * 脚注。**键是正文上标标记 `[n]` 里的编号**(按引用出现顺序 1 起编)——
289
+ * 宿主拿 `footnotes["1"]` 就是正文 `[1]` 指的那条,不用自己解 w:id。
290
+ * 编辑器没有脚注节点:注文不进文档树,导出 docx 时也不会写回
291
+ * (导入时 warnings 里会明说)。
292
+ */
279
293
  footnotes: Record<string, JSONContent>;
294
+ /** 尾注,键的语义同 `footnotes`(编号序列与脚注各自独立) */
280
295
  endnotes: Record<string, JSONContent>;
281
296
  }
282
297
  interface ImportDocxCommandArgs {
package/dist/docx.js CHANGED
@@ -142,9 +142,23 @@ function parseDocx(entries, opts) {
142
142
  paraSpacing: 0,
143
143
  paraIndent: 0,
144
144
  lists: 0,
145
- listItems: 0
145
+ listItems: 0,
146
+ footnotes: 0,
147
+ endnotes: 0
146
148
  };
147
149
  const warnings = [];
150
+ const noteNumbers = {
151
+ footnote: /* @__PURE__ */ new Map(),
152
+ endnote: /* @__PURE__ */ new Map()
153
+ };
154
+ function noteNumber(kind, id) {
155
+ const seq = noteNumbers[kind];
156
+ const hit = seq.get(id);
157
+ if (hit !== void 0) return hit;
158
+ const n = seq.size + 1;
159
+ seq.set(id, n);
160
+ return n;
161
+ }
148
162
  const text = (p) => {
149
163
  const b = entries.read(p);
150
164
  return b ? decoder.decode(b) : null;
@@ -379,6 +393,17 @@ function parseDocx(entries, opts) {
379
393
  if (!n) continue;
380
394
  if (n.type === "image") out.blocks.push(n);
381
395
  else out.inline.push(n);
396
+ } else if (t === "w:footnoteReference" || t === "w:endnoteReference") {
397
+ const id = c.getAttribute("w:id");
398
+ if (!id) continue;
399
+ const kind = t === "w:footnoteReference" ? "footnote" : "endnote";
400
+ const n = noteNumber(kind, id);
401
+ const hasSuper = marks.some((m) => m.type === "superscript");
402
+ out.inline.push({
403
+ type: "text",
404
+ text: `[${n}]`,
405
+ marks: hasSuper ? marks : [...marks, { type: "superscript" }]
406
+ });
382
407
  } else if (t === "w:pict") {
383
408
  warnings.push("遇到 VML 图形(w:pict),未处理");
384
409
  }
@@ -706,6 +731,17 @@ function parseDocx(entries, opts) {
706
731
  if (!body) throw new Error("docx 缺少 w:body");
707
732
  const content = blocksOf(body);
708
733
  const doc = remapTypes({ type: "doc", content }, opts.prosemirrorNodes, opts.prosemirrorMarks);
734
+ const footnotes = notesOf("word/footnotes.xml", "w:footnote", "footnote");
735
+ const endnotes = notesOf("word/endnotes.xml", "w:endnote", "endnote");
736
+ if (recovered.footnotes || recovered.endnotes) {
737
+ const parts = [
738
+ ...recovered.footnotes ? [`${recovered.footnotes} 条脚注`] : [],
739
+ ...recovered.endnotes ? [`${recovered.endnotes} 条尾注`] : []
740
+ ];
741
+ warnings.push(
742
+ `文档有 ${parts.join("和")}:正文引用以上标 [n] 文本保留,注文在导入结果的 footnotes / endnotes 里(键即编号 n)。编辑器没有脚注节点,导出 docx 时不会写回`
743
+ );
744
+ }
709
745
  const section = readSection();
710
746
  const defaultHeader = section.refs.find((r) => r.kind === "header" && r.type === "default");
711
747
  const defaultFooter = section.refs.find((r) => r.kind === "footer" && r.type === "default");
@@ -716,8 +752,30 @@ function parseDocx(entries, opts) {
716
752
  ...opts.cssStyles ? { css: extractCss(stylesDoc) } : {},
717
753
  header: defaultHeader ? partContent(defaultHeader.path, "w:hdr") : null,
718
754
  footer: defaultFooter ? partContent(defaultFooter.path, "w:ftr") : null,
755
+ footnotes,
756
+ endnotes,
719
757
  section
720
758
  };
759
+ function notesOf(path, tag, kind) {
760
+ const d = xml(path);
761
+ if (!d) return {};
762
+ const out = {};
763
+ for (const el of Array.from(d.getElementsByTagName(tag))) {
764
+ const type = el.getAttribute("w:type");
765
+ if (type && type !== "normal") continue;
766
+ const id = el.getAttribute("w:id");
767
+ if (id === null) continue;
768
+ const blocks = blocksOf(el);
769
+ if (!blocks.length) continue;
770
+ out[String(noteNumber(kind, id))] = remapTypes(
771
+ { type: "doc", content: blocks },
772
+ opts.prosemirrorNodes,
773
+ opts.prosemirrorMarks
774
+ );
775
+ recovered[`${kind}s`] += 1;
776
+ }
777
+ return out;
778
+ }
721
779
  function partContent(path, root) {
722
780
  const d = xml(path);
723
781
  const el = d?.getElementsByTagName(root)[0];
@@ -1542,8 +1600,8 @@ var ImportDocx = Extension.create({
1542
1600
  header: result.header,
1543
1601
  footer: result.footer,
1544
1602
  section: result.section,
1545
- footnotes: {},
1546
- endnotes: {}
1603
+ footnotes: result.footnotes,
1604
+ endnotes: result.endnotes
1547
1605
  });
1548
1606
  } catch (e) {
1549
1607
  const err = e instanceof Error ? e : new Error(String(e));