weifuwu 0.85.0 → 0.86.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 (41) hide show
  1. package/README.md +5 -5
  2. package/dist/components/Editor/Editor.d.ts +8 -7
  3. package/dist/components/Editor/edit-events.d.ts +27 -0
  4. package/dist/components/Editor/model/apply.d.ts +23 -0
  5. package/dist/components/Editor/model/diff.d.ts +25 -0
  6. package/dist/components/Editor/model/dom.d.ts +27 -0
  7. package/dist/components/Editor/model/history.d.ts +21 -0
  8. package/dist/components/Editor/model/html.d.ts +16 -0
  9. package/dist/components/Editor/model/inverse.d.ts +20 -0
  10. package/dist/components/Editor/model/types.d.ts +116 -0
  11. package/dist/components/Editor/tools/table.d.ts +5 -2
  12. package/dist/components/Editor/tools/toolbar.d.ts +1 -1
  13. package/dist/components/Editor/tools/types.d.ts +25 -0
  14. package/dist/components/FilePreview/FilePreview.d.ts +42 -0
  15. package/dist/components/FilePreview/markdown.d.ts +23 -0
  16. package/dist/components/OfficeEditor/ai/ai-bridge.d.ts +39 -0
  17. package/dist/components/OfficeEditor/model/apply.d.ts +25 -0
  18. package/dist/components/OfficeEditor/model/types.d.ts +224 -0
  19. package/dist/components/SheetGrid/SheetGrid.d.ts +30 -0
  20. package/dist/components/SlideCanvas/SlideCanvas.d.ts +28 -0
  21. package/dist/components/index.d.ts +7 -0
  22. package/dist/components/index.js +54 -14
  23. package/dist/components/style.css +679 -4
  24. package/dist/index.js +38 -11
  25. package/dist/office/docx.d.ts +33 -0
  26. package/dist/office/pptx.d.ts +24 -0
  27. package/dist/office/xlsx.d.ts +25 -0
  28. package/dist/office/xml-serialize.d.ts +18 -0
  29. package/dist/office/xml.d.ts +21 -0
  30. package/dist/office/zip.d.ts +21 -0
  31. package/dist/ui-dom/index.js +7 -7
  32. package/dist/ui-dom/locale/en_US.d.ts +14 -0
  33. package/dist/ui-dom/locale/zh_CN.d.ts +14 -0
  34. package/dist/ui-dom/testing.js +5 -5
  35. package/dist/ui-dom/types.d.ts +3 -1
  36. package/dist/ui-dom/vdom3/delegate.d.ts +5 -0
  37. package/dist/ui-dom/vdom3.js +7 -7
  38. package/docs/components-map.md +12 -0
  39. package/docs/components.md +124 -2
  40. package/package.json +1 -1
  41. package/dist/components/Editor/tools/format.d.ts +0 -11
package/dist/index.js CHANGED
@@ -3607,6 +3607,17 @@ function createClientBrowser() {
3607
3607
  if (typeof window !== "undefined") window.location.hash = h2;
3608
3608
  },
3609
3609
  timeout: (fn, ms) => typeof window !== "undefined" ? window.setTimeout(fn, ms) : 0,
3610
+ /** 浏览器表单状态恢复(load/pageshow)监听——SSR 安全 no-op */
3611
+ onFormRestore: (cb) => {
3612
+ if (typeof window === "undefined" || typeof document === "undefined") return () => {
3613
+ };
3614
+ window.addEventListener("load", cb, { once: true });
3615
+ window.addEventListener("pageshow", cb);
3616
+ return () => {
3617
+ window.removeEventListener("load", cb);
3618
+ window.removeEventListener("pageshow", cb);
3619
+ };
3620
+ },
3610
3621
  rootElement: () => typeof document !== "undefined" ? document.documentElement : null,
3611
3622
  storageGet: (key) => {
3612
3623
  try {
@@ -3770,7 +3781,15 @@ var globalHandlers = /* @__PURE__ */ new Map();
3770
3781
  var globalRoots = /* @__PURE__ */ new Map();
3771
3782
  var EVENT_MAP = {
3772
3783
  focus: "focusin",
3773
- blur: "focusout"
3784
+ blur: "focusout",
3785
+ mouseenter: "mouseover",
3786
+ mouseleave: "mouseout"
3787
+ };
3788
+ var REVERSE_MAP = {
3789
+ focusin: "focus",
3790
+ focusout: "blur",
3791
+ mouseover: "mouseenter",
3792
+ mouseout: "mouseleave"
3774
3793
  };
3775
3794
  var NON_BUBBLING = /* @__PURE__ */ new Set(["error", "load", "loadstart", "loadend", "abort", "unload", "resize", "message"]);
3776
3795
  var TEXT_INPUT_EVENTS = /* @__PURE__ */ new Set(["input", "change", "compositionstart", "compositionend"]);
@@ -3805,11 +3824,11 @@ function rootOf(node) {
3805
3824
  return null;
3806
3825
  }
3807
3826
  function ensureRootEvent(root, event) {
3827
+ const realEvent = EVENT_MAP[event] ?? event;
3808
3828
  const set = registered.get(root) ?? /* @__PURE__ */ new Set();
3809
- if (set.has(event)) return;
3810
- set.add(event);
3829
+ if (set.has(realEvent)) return;
3830
+ set.add(realEvent);
3811
3831
  registered.set(root, set);
3812
- const realEvent = EVENT_MAP[event] ?? event;
3813
3832
  const fn = (e) => dispatch(e);
3814
3833
  const useCapture = NON_BUBBLING.has(realEvent);
3815
3834
  root.addEventListener(realEvent, fn, useCapture ? { capture: true } : void 0);
@@ -3857,8 +3876,11 @@ function addGlobalListener(target, event, handler, opts) {
3857
3876
  function dispatch(e) {
3858
3877
  let el = e.target;
3859
3878
  if (el && el.nodeType === 3) el = el.parentElement;
3860
- const m = handlers.get(e.type);
3861
- if (!m) return;
3879
+ const keys = [];
3880
+ if (handlers.has(e.type)) keys.push(e.type);
3881
+ const mapped = REVERSE_MAP[e.type];
3882
+ if (mapped && mapped !== e.type && handlers.has(mapped)) keys.push(mapped);
3883
+ if (keys.length === 0) return;
3862
3884
  while (el) {
3863
3885
  if (!el.hasAttribute?.("data-v3-id")) {
3864
3886
  const idEl = el.closest?.("[data-v3-id]");
@@ -3869,8 +3891,9 @@ function dispatch(e) {
3869
3891
  }
3870
3892
  const id = el.getAttribute?.("data-v3-id");
3871
3893
  if (id) {
3872
- const entry = m.get(id);
3873
- if (entry) {
3894
+ for (const k of keys) {
3895
+ const entry = handlers.get(k)?.get(id);
3896
+ if (!entry) continue;
3874
3897
  try {
3875
3898
  Object.defineProperty(e, "currentTarget", { value: el, configurable: true });
3876
3899
  } catch {
@@ -3884,11 +3907,12 @@ function dispatch(e) {
3884
3907
  stream.emit(ev("text", "input", id, { event: e.type, value: typeof value === "string" ? value.slice(0, 100) : null }));
3885
3908
  }
3886
3909
  if (entry.once) {
3887
- m.delete(id);
3910
+ handlers.get(k).delete(id);
3888
3911
  stream.emit(ev("event", "unbind", id, { event: e.type }));
3889
3912
  }
3890
3913
  if (e.cancelBubble) break;
3891
3914
  }
3915
+ if (e.cancelBubble) break;
3892
3916
  }
3893
3917
  el = el.parentElement;
3894
3918
  }
@@ -4402,7 +4426,8 @@ function usePopup(env, options) {
4402
4426
  prevOpen = true;
4403
4427
  lastEl = el;
4404
4428
  }
4405
- const props = content.props ?? {};
4429
+ const props = (content ?? h("span")).props ?? {};
4430
+ if (!content) return null;
4406
4431
  latestContentRef = props.ref ?? null;
4407
4432
  const cls = options.positioning === "none" ? props.class ?? "" : ["wf-popup", props.class].filter(Boolean).join(" ");
4408
4433
  const style = options.positioning === "none" ? { ...props.style ?? {}, position: "fixed" } : {
@@ -5497,7 +5522,9 @@ async function buildVNode(vnode, ctx, oldV, isRoot = false) {
5497
5522
  // SSR 下 document undefined → null/no-op——组件 36 处 ctx.browser 消费
5498
5523
  // 无需各自 fallback(Tour 曾因 ctx.browser 缺失 → query 返回 undefined →
5499
5524
  // targetEl 永不赋值 → rect 恒 0 → 引导高亮定位失效)
5500
- browser: createClientBrowser(),
5525
+ // 注入优先(测试/宿主可覆盖——真实事故:ctx.browser 注入被覆盖——
5526
+ // Editor draftKey 的 storageSet 走真实 browser 而非 fake)
5527
+ browser: ctx.browser ?? createClientBrowser(),
5501
5528
  ui: createV3Ui(compId, componentRender, (fn) => {
5502
5529
  unmountHooks.set(compId, fn);
5503
5530
  })
@@ -0,0 +1,33 @@
1
+ /**
2
+ * weifuwu/office/docx — docx ↔ ODES DocState 双向转换(前端/服务端通用——零依赖)
3
+ *
4
+ * 参考算法:office2json(unzip → XML 树 → 路径提取 w:body → w:p → w:r → w:t),
5
+ * 增强提取到 ODES IR(design/office-events-plan.md):
6
+ * - 段落属性:pStyle → h1/h2/h3、jc → 对齐(裁剪:样式名映射子集)
7
+ * - 内联格式:rPr b/i/u → marks(区间)
8
+ * - 表格:w:tbl → Editor 表格 embed 快照(HTML 复用现有模型)
9
+ * - 图片:w:drawing r:embed → rels → media → data url → img embed
10
+ *
11
+ * 导出(DocState → minimal docx):VNode 组件化(OOXML 也是 VNode——与前端
12
+ * "VNode → DOM"同构)→ vnodeToXml → store ZIP。块级 embed(pre/hr)打断段落。
13
+ *
14
+ * 前端可用(无需后端):ZIP 解压走 DecompressionStream(浏览器/Node 18+);
15
+ * base64 跨环境(Buffer 守卫/atob)。诚实裁剪:分节/页眉页脚/域代码/批注
16
+ * 不导入;http 图片导出裁剪(warning)。
17
+ */
18
+ import type { DocState } from '../components/Editor/model/types.ts';
19
+ /** base64 → Uint8Array(跨环境——浏览器 atob / Node Buffer) */
20
+ export declare function base64ToBytes(b64: string): Uint8Array;
21
+ /** Uint8Array → base64(跨环境) */
22
+ export declare function bytesToBase64(u8: Uint8Array): string;
23
+ export interface DocxImportResult {
24
+ doc: DocState;
25
+ /** 裁剪提示(不可映射内容——诚实不静默) */
26
+ warnings: string[];
27
+ }
28
+ export declare function docxToDoc(u8: Uint8Array): Promise<DocxImportResult>;
29
+ export interface DocxExportResult {
30
+ data: Uint8Array;
31
+ warnings: string[];
32
+ }
33
+ export declare function docToDocx(doc: DocState): DocxExportResult;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * weifuwu/office/pptx — pptx ↔ ODES DeckState 双向转换(前端/服务端通用——零依赖)
3
+ *
4
+ * 参考算法:office2json(ppt/slides/slideN.xml → p:sld → p:cSld → p:spTree →
5
+ * p:sp → p:txBody → a:p → a:r → a:t),增强提取到 ODES IR:
6
+ * - 多幻灯片:slideN.xml 全量(office2json 只取文本——ODES 保留结构)
7
+ * - shape 几何:p:xfrm(a:off x/y + a:ext cx/cy——EMU → px:/9525)
8
+ * - shape 类型:p:sp(文本/图形——p:prstGeom prst)/ p:pic(图片)
9
+ * - 文本:txBody → 段落拼接(a:br 换行)
10
+ *
11
+ * 导出(DeckState → minimal pptx):slideN.xml(spTree 结构)+ presentation.xml
12
+ * + rels + contentTypes——store ZIP(VNode 组件化)。裁剪:动画/母版/备注/主题。
13
+ */
14
+ import type { DeckState } from '../components/OfficeEditor/model/types.ts';
15
+ export interface PptxImportResult {
16
+ deck: DeckState;
17
+ warnings: string[];
18
+ }
19
+ export declare function pptxToDeck(u8: Uint8Array): Promise<PptxImportResult>;
20
+ export interface PptxExportResult {
21
+ data: Uint8Array;
22
+ warnings: string[];
23
+ }
24
+ export declare function deckToPptx(deck: DeckState): PptxExportResult;
@@ -0,0 +1,25 @@
1
+ /**
2
+ * weifuwu/office/xlsx — xlsx ↔ ODES WorkbookState 双向转换(前端/服务端通用——零依赖)
3
+ *
4
+ * 参考算法:office2json(sheet1.xml → sheetData → row → c{t,v} + sharedStrings),
5
+ * 增强提取到 ODES IR(design/office-events-plan.md):
6
+ * - 多 sheet:workbook.xml(sheet 名 + rId)→ rels → 各 sheetN.xml
7
+ * - 单元格引用:c@r(A1——office2json 丢弃——ODES 必须)
8
+ * - 类型:c@t(s=shared/n=number/b=bool/f=formula)→ SheetCell.kind
9
+ * - 公式:f 元素(公式字符串保留——不计算——服务端导出重算)
10
+ * - 行高/列宽/样式索引:裁剪(v1——诚实:不读不写)
11
+ *
12
+ * 导出(WorkbookState → minimal xlsx):共享字符串表去重 + sheetData XML +
13
+ * store ZIP(VNode 组件化——OOXML 也是 VNode)。
14
+ */
15
+ import type { WorkbookState } from '../components/OfficeEditor/model/types.ts';
16
+ export interface XlsxImportResult {
17
+ workbook: WorkbookState;
18
+ warnings: string[];
19
+ }
20
+ export declare function xlsxToWorkbook(u8: Uint8Array): Promise<XlsxImportResult>;
21
+ export interface XlsxExportResult {
22
+ data: Uint8Array;
23
+ warnings: string[];
24
+ }
25
+ export declare function workbookToXlsx(wb: WorkbookState): XlsxExportResult;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * weifuwu/office/xml-serialize — VNode 树 → XML 字符串(服务端 OOXML 生成)
3
+ *
4
+ * 架构(与前端 "VNode → DOM" 同构——"OOXML 也是写 VNode"):
5
+ * DocState → VNode 树(h()——vdom3 纯数据结构,零 DOM 依赖)
6
+ * → vnodeToXml(本模块)→ ZIP 打包
7
+ *
8
+ * 序列化约定(仅输出 XML 子集——与 vdom3 渲染管线解耦):
9
+ * - type: string = 元素名(含命名空间前缀——w:p、a:graphic 等原样输出)
10
+ * - props: 字符串值 = XML 属性;number/boolean 字符串化;key/children 排除
11
+ * - children: 字符串 = 文本(转义);VNode = 子元素;数组 = 序列
12
+ * - 空元素 → 自闭合 <w:b/>(OOXML 合法——MS Word 接受)
13
+ * - 函数/symbol type、事件 props(on*)→ 抛错(诚实——序列化器不支持)
14
+ */
15
+ import type { VNode, VNodeChild } from '../ui-dom/vdom3/types.ts';
16
+ export declare function vnodeToXml(v: VNode | VNodeChild, xmlDecl?: boolean): string;
17
+ /** 便捷:把 VNode 树序列化为 OOXML 部件 XML(名称检查由调用方保证) */
18
+ export declare function vnodeToXmlDecl(v: VNode): string;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * weifuwu/office/xml — 自研轻量 XML 解析(零 npm 依赖)
3
+ *
4
+ * 参考算法:office2json(@xmldom/xmldom → { name, attrs, children } 树)。
5
+ * OOXML 是规整 XML——只做元素树 + 文本;裁剪:DTD/实体展开(除内建五实体)/
6
+ * 命名空间处理(前缀保留原名——w:p 等)。解析失败抛错(诚实——不静默)。
7
+ */
8
+ export interface XmlNode {
9
+ name: string;
10
+ attrs: Record<string, string>;
11
+ children: XmlNode[];
12
+ /** 直接文本内容(子元素外的文本) */
13
+ text: string;
14
+ }
15
+ /** 手写扫描器:<name attrs>...</name> / <name/> / 文本 / 指令 / 注释 / CDATA */
16
+ export declare function parseXml(xml: string): XmlNode;
17
+ /** 便捷:找子元素(按名字——含命名空间前缀匹配) */
18
+ export declare function child(el: XmlNode, name: string): XmlNode | null;
19
+ export declare function children(el: XmlNode, name: string): XmlNode[];
20
+ /** 全部后代文本(拼接) */
21
+ export declare function allText(el: XmlNode): string;
@@ -0,0 +1,21 @@
1
+ /**
2
+ * weifuwu/office/zip — 自研 ZIP 容器读写(零 npm 依赖)
3
+ *
4
+ * 参考算法:office2json(unzip 结构——EOCD → central directory → local header)。
5
+ * 实现取舍:
6
+ * - 读:EOCD(尾部扫描)→ central directory → local header offset 取数据;
7
+ * 压缩方法 0(store)/ 8(deflate——Node 内置 zlib.inflateRawSync)
8
+ * - 写:store(无压缩——合法 ZIP,文件较大但零依赖正确性优先);
9
+ * CRC32 自研查表
10
+ * - 裁剪:ZIP64/加密/数据描述符(flags bit3)/多盘——不支持(诚实——读到即抛错)
11
+ */
12
+ export interface ZipEntry {
13
+ name: string;
14
+ data: Uint8Array;
15
+ }
16
+ export declare function crc32(buf: Uint8Array): number;
17
+ /** inflate(ZIP method 8)——跨环境:浏览器 DecompressionStream / Node 18+(同 API)。
18
+ * 老环境无 DecompressionStream → 抛错(诚实裁剪:OOXML 必用 deflate)。 */
19
+ export declare function inflateRaw(data: Uint8Array): Promise<Uint8Array>;
20
+ export declare function readZip(u8: Uint8Array): Promise<Map<string, Uint8Array>>;
21
+ export declare function writeZip(files: Map<string, Uint8Array>): Uint8Array;