@ryanyahya/opendoc 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/skills/opendoc-apply-comments/SKILL.md +30 -0
- package/.agents/skills/opendoc-apply-comments/agents/openai.yaml +4 -0
- package/.agents/skills/opendoc-create/SKILL.md +63 -0
- package/.agents/skills/opendoc-create/agents/openai.yaml +6 -0
- package/.agents/skills/opendoc-create-template/SKILL.md +50 -0
- package/.agents/skills/opendoc-create-template/agents/openai.yaml +4 -0
- package/.agents/skills/opendoc-create-template/references/structured-reports.md +11 -0
- package/.agents/skills/opendoc-create-theme/SKILL.md +40 -0
- package/.agents/skills/opendoc-create-theme/agents/openai.yaml +4 -0
- package/.agents/skills/opendoc-current-document/SKILL.md +32 -0
- package/.agents/skills/opendoc-current-document/agents/openai.yaml +4 -0
- package/.agents/skills/opendoc-current-document/references/context-fields.md +20 -0
- package/.agents/skills/opendoc-review-document/SKILL.md +60 -0
- package/.agents/skills/opendoc-review-document/agents/openai.yaml +4 -0
- package/AGENTS.md +69 -0
- package/CONTRIBUTING.md +38 -0
- package/LICENSE +21 -0
- package/README.md +179 -0
- package/SECURITY.md +9 -0
- package/THIRD_PARTY.md +49 -0
- package/VALIDATION.md +99 -0
- package/bin/opendoc.mjs +6 -0
- package/dist/assets/Markdown-sNZyjJi7.js +29 -0
- package/dist/assets/OpenDocSerif-Regular-D3OXs9A9.ttf +0 -0
- package/dist/assets/OpenDocSerif-Semibold-CjL9oDRo.ttf +0 -0
- package/dist/assets/geist-latin-ext-wght-italic-XgKbg5n7.woff2 +0 -0
- package/dist/assets/geist-latin-ext-wght-normal-DC-KSUi6.woff2 +0 -0
- package/dist/assets/geist-latin-wght-italic-dTnRTFFJ.woff2 +0 -0
- package/dist/assets/geist-latin-wght-normal-BgDaEnEv.woff2 +0 -0
- package/dist/assets/index-DdrZ6ozO.css +1 -0
- package/dist/assets/index-DgNBz7mL.js +39 -0
- package/dist/assets/pdf-B7Emtyld.js +54 -0
- package/dist/assets/pdf.worker.min-Dswkl-cV.mjs +29 -0
- package/dist/assets/wordmark-DsqLlZpb.png +0 -0
- package/dist/index.html +17 -0
- package/docs/APPEARANCE.md +21 -0
- package/docs/ASSETS.md +176 -0
- package/docs/AUTHORING.md +178 -0
- package/docs/HEADLESS.md +120 -0
- package/docs/MEDIA.md +112 -0
- package/docs/PROJECTS.md +55 -0
- package/docs/PROSE.md +15 -0
- package/docs/SELECTION.md +59 -0
- package/docs/TEMPLATES.md +55 -0
- package/docs/THEMES.md +106 -0
- package/node_modules/@formepdf/core/LICENSE +21 -0
- package/node_modules/@formepdf/core/README.md +53 -0
- package/node_modules/@formepdf/core/dist/attachments.d.ts +55 -0
- package/node_modules/@formepdf/core/dist/attachments.js +59 -0
- package/node_modules/@formepdf/core/dist/extract.d.ts +8 -0
- package/node_modules/@formepdf/core/dist/extract.js +56 -0
- package/node_modules/@formepdf/core/dist/index.d.ts +311 -0
- package/node_modules/@formepdf/core/dist/index.js +167 -0
- package/node_modules/@formepdf/core/dist/layout.d.ts +132 -0
- package/node_modules/@formepdf/core/dist/layout.js +240 -0
- package/node_modules/@formepdf/core/dist/shared/browserHelpers.d.ts +19 -0
- package/node_modules/@formepdf/core/dist/shared/browserHelpers.js +128 -0
- package/node_modules/@formepdf/core/package.json +35 -0
- package/node_modules/@formepdf/core/pkg-node/LICENSE +21 -0
- package/node_modules/@formepdf/core/pkg-node/README.md +84 -0
- package/node_modules/@formepdf/core/pkg-node/forme.d.ts +39 -0
- package/node_modules/@formepdf/core/pkg-node/forme.js +394 -0
- package/node_modules/@formepdf/core/pkg-node/forme_bg.wasm +0 -0
- package/node_modules/@formepdf/core/pkg-node/forme_bg.wasm.d.ts +21 -0
- package/node_modules/@formepdf/core/pkg-node/package.json +25 -0
- package/node_modules/@formepdf/react/README.md +70 -0
- package/node_modules/@formepdf/react/dist/charts.d.ts +72 -0
- package/node_modules/@formepdf/react/dist/charts.js +338 -0
- package/node_modules/@formepdf/react/dist/components.d.ts +533 -0
- package/node_modules/@formepdf/react/dist/components.js +608 -0
- package/node_modules/@formepdf/react/dist/expr.d.ts +20 -0
- package/node_modules/@formepdf/react/dist/expr.js +39 -0
- package/node_modules/@formepdf/react/dist/font.d.ts +2 -0
- package/node_modules/@formepdf/react/dist/font.js +1 -0
- package/node_modules/@formepdf/react/dist/index.d.ts +11 -0
- package/node_modules/@formepdf/react/dist/index.js +15 -0
- package/node_modules/@formepdf/react/dist/render.d.ts +12 -0
- package/node_modules/@formepdf/react/dist/render.js +15 -0
- package/node_modules/@formepdf/react/dist/serialize.d.ts +12 -0
- package/node_modules/@formepdf/react/dist/serialize.js +1470 -0
- package/node_modules/@formepdf/react/dist/stylesheet.d.ts +4 -0
- package/node_modules/@formepdf/react/dist/stylesheet.js +5 -0
- package/node_modules/@formepdf/react/dist/template-proxy.d.ts +38 -0
- package/node_modules/@formepdf/react/dist/template-proxy.js +100 -0
- package/node_modules/@formepdf/react/dist/types.d.ts +287 -0
- package/node_modules/@formepdf/react/dist/types.js +1 -0
- package/node_modules/@formepdf/react/package.json +41 -0
- package/node_modules/@formepdf/shared/README.md +7 -0
- package/node_modules/@formepdf/shared/dist/canvas.d.ts +6 -0
- package/node_modules/@formepdf/shared/dist/canvas.js +43 -0
- package/node_modules/@formepdf/shared/dist/charts.d.ts +100 -0
- package/node_modules/@formepdf/shared/dist/charts.js +98 -0
- package/node_modules/@formepdf/shared/dist/encode.d.ts +27 -0
- package/node_modules/@formepdf/shared/dist/encode.js +93 -0
- package/node_modules/@formepdf/shared/dist/font.d.ts +13 -0
- package/node_modules/@formepdf/shared/dist/font.js +52 -0
- package/node_modules/@formepdf/shared/dist/index.d.ts +10 -0
- package/node_modules/@formepdf/shared/dist/index.js +15 -0
- package/node_modules/@formepdf/shared/dist/parser.d.ts +30 -0
- package/node_modules/@formepdf/shared/dist/parser.js +756 -0
- package/node_modules/@formepdf/shared/dist/semantics.d.ts +15 -0
- package/node_modules/@formepdf/shared/dist/semantics.js +44 -0
- package/node_modules/@formepdf/shared/dist/style.d.ts +7 -0
- package/node_modules/@formepdf/shared/dist/style.js +1024 -0
- package/node_modules/@formepdf/shared/dist/types.d.ts +743 -0
- package/node_modules/@formepdf/shared/dist/types.js +2 -0
- package/node_modules/@formepdf/shared/package.json +35 -0
- package/node_modules/@types/node/LICENSE +21 -0
- package/node_modules/@types/node/README.md +15 -0
- package/node_modules/@types/node/assert/strict.d.ts +111 -0
- package/node_modules/@types/node/assert.d.ts +1078 -0
- package/node_modules/@types/node/async_hooks.d.ts +603 -0
- package/node_modules/@types/node/buffer.buffer.d.ts +472 -0
- package/node_modules/@types/node/buffer.d.ts +1934 -0
- package/node_modules/@types/node/child_process.d.ts +1476 -0
- package/node_modules/@types/node/cluster.d.ts +578 -0
- package/node_modules/@types/node/compatibility/disposable.d.ts +14 -0
- package/node_modules/@types/node/compatibility/index.d.ts +9 -0
- package/node_modules/@types/node/compatibility/indexable.d.ts +20 -0
- package/node_modules/@types/node/compatibility/iterators.d.ts +20 -0
- package/node_modules/@types/node/console.d.ts +452 -0
- package/node_modules/@types/node/constants.d.ts +21 -0
- package/node_modules/@types/node/crypto.d.ts +4545 -0
- package/node_modules/@types/node/dgram.d.ts +600 -0
- package/node_modules/@types/node/diagnostics_channel.d.ts +576 -0
- package/node_modules/@types/node/dns/promises.d.ts +503 -0
- package/node_modules/@types/node/dns.d.ts +923 -0
- package/node_modules/@types/node/domain.d.ts +170 -0
- package/node_modules/@types/node/events.d.ts +976 -0
- package/node_modules/@types/node/fs/promises.d.ts +1306 -0
- package/node_modules/@types/node/fs.d.ts +4463 -0
- package/node_modules/@types/node/globals.d.ts +172 -0
- package/node_modules/@types/node/globals.typedarray.d.ts +38 -0
- package/node_modules/@types/node/http.d.ts +2097 -0
- package/node_modules/@types/node/http2.d.ts +2843 -0
- package/node_modules/@types/node/https.d.ts +579 -0
- package/node_modules/@types/node/index.d.ts +98 -0
- package/node_modules/@types/node/inspector.d.ts +253 -0
- package/node_modules/@types/node/inspector.generated.d.ts +4052 -0
- package/node_modules/@types/node/module.d.ts +891 -0
- package/node_modules/@types/node/net.d.ts +1076 -0
- package/node_modules/@types/node/os.d.ts +506 -0
- package/node_modules/@types/node/package.json +145 -0
- package/node_modules/@types/node/path.d.ts +200 -0
- package/node_modules/@types/node/perf_hooks.d.ts +968 -0
- package/node_modules/@types/node/process.d.ts +2084 -0
- package/node_modules/@types/node/punycode.d.ts +117 -0
- package/node_modules/@types/node/querystring.d.ts +152 -0
- package/node_modules/@types/node/readline/promises.d.ts +161 -0
- package/node_modules/@types/node/readline.d.ts +594 -0
- package/node_modules/@types/node/repl.d.ts +428 -0
- package/node_modules/@types/node/sea.d.ts +162 -0
- package/node_modules/@types/node/sqlite.d.ts +722 -0
- package/node_modules/@types/node/stream/consumers.d.ts +38 -0
- package/node_modules/@types/node/stream/promises.d.ts +90 -0
- package/node_modules/@types/node/stream/web.d.ts +573 -0
- package/node_modules/@types/node/stream.d.ts +1694 -0
- package/node_modules/@types/node/string_decoder.d.ts +67 -0
- package/node_modules/@types/node/test.d.ts +2265 -0
- package/node_modules/@types/node/timers/promises.d.ts +108 -0
- package/node_modules/@types/node/timers.d.ts +287 -0
- package/node_modules/@types/node/tls.d.ts +1319 -0
- package/node_modules/@types/node/trace_events.d.ts +197 -0
- package/node_modules/@types/node/ts5.6/buffer.buffer.d.ts +468 -0
- package/node_modules/@types/node/ts5.6/globals.typedarray.d.ts +34 -0
- package/node_modules/@types/node/ts5.6/index.d.ts +98 -0
- package/node_modules/@types/node/tty.d.ts +208 -0
- package/node_modules/@types/node/url.d.ts +984 -0
- package/node_modules/@types/node/util.d.ts +2606 -0
- package/node_modules/@types/node/v8.d.ts +920 -0
- package/node_modules/@types/node/vm.d.ts +1053 -0
- package/node_modules/@types/node/wasi.d.ts +181 -0
- package/node_modules/@types/node/web-globals/abortcontroller.d.ts +34 -0
- package/node_modules/@types/node/web-globals/domexception.d.ts +68 -0
- package/node_modules/@types/node/web-globals/events.d.ts +97 -0
- package/node_modules/@types/node/web-globals/fetch.d.ts +55 -0
- package/node_modules/@types/node/web-globals/navigator.d.ts +22 -0
- package/node_modules/@types/node/web-globals/storage.d.ts +24 -0
- package/node_modules/@types/node/web-globals/streams.d.ts +22 -0
- package/node_modules/@types/node/worker_threads.d.ts +819 -0
- package/node_modules/@types/node/zlib.d.ts +747 -0
- package/node_modules/@types/react/LICENSE +21 -0
- package/node_modules/@types/react/README.md +15 -0
- package/node_modules/@types/react/canary.d.ts +129 -0
- package/node_modules/@types/react/compiler-runtime.d.ts +4 -0
- package/node_modules/@types/react/experimental.d.ts +184 -0
- package/node_modules/@types/react/global.d.ts +166 -0
- package/node_modules/@types/react/index.d.ts +4378 -0
- package/node_modules/@types/react/jsx-dev-runtime.d.ts +33 -0
- package/node_modules/@types/react/jsx-runtime.d.ts +24 -0
- package/node_modules/@types/react/package.json +210 -0
- package/node_modules/@types/react/ts5.0/canary.d.ts +129 -0
- package/node_modules/@types/react/ts5.0/experimental.d.ts +184 -0
- package/node_modules/@types/react/ts5.0/global.d.ts +166 -0
- package/node_modules/@types/react/ts5.0/index.d.ts +4365 -0
- package/node_modules/@types/react/ts5.0/jsx-dev-runtime.d.ts +33 -0
- package/node_modules/@types/react/ts5.0/jsx-runtime.d.ts +24 -0
- package/node_modules/csstype/LICENSE +19 -0
- package/node_modules/csstype/README.md +291 -0
- package/node_modules/csstype/index.d.ts +22569 -0
- package/node_modules/csstype/index.js.flow +6863 -0
- package/node_modules/csstype/package.json +70 -0
- package/node_modules/entities/LICENSE +11 -0
- package/node_modules/entities/decode.d.ts +1 -0
- package/node_modules/entities/decode.js +3 -0
- package/node_modules/entities/dist/commonjs/decode-codepoint.d.ts +19 -0
- package/node_modules/entities/dist/commonjs/decode-codepoint.d.ts.map +1 -0
- package/node_modules/entities/dist/commonjs/decode-codepoint.js +77 -0
- package/node_modules/entities/dist/commonjs/decode-codepoint.js.map +1 -0
- package/node_modules/entities/dist/commonjs/decode.d.ts +209 -0
- package/node_modules/entities/dist/commonjs/decode.d.ts.map +1 -0
- package/node_modules/entities/dist/commonjs/decode.js +511 -0
- package/node_modules/entities/dist/commonjs/decode.js.map +1 -0
- package/node_modules/entities/dist/commonjs/encode.d.ts +22 -0
- package/node_modules/entities/dist/commonjs/encode.d.ts.map +1 -0
- package/node_modules/entities/dist/commonjs/encode.js +73 -0
- package/node_modules/entities/dist/commonjs/encode.js.map +1 -0
- package/node_modules/entities/dist/commonjs/escape.d.ts +43 -0
- package/node_modules/entities/dist/commonjs/escape.d.ts.map +1 -0
- package/node_modules/entities/dist/commonjs/escape.js +121 -0
- package/node_modules/entities/dist/commonjs/escape.js.map +1 -0
- package/node_modules/entities/dist/commonjs/generated/decode-data-html.d.ts +2 -0
- package/node_modules/entities/dist/commonjs/generated/decode-data-html.d.ts.map +1 -0
- package/node_modules/entities/dist/commonjs/generated/decode-data-html.js +10 -0
- package/node_modules/entities/dist/commonjs/generated/decode-data-html.js.map +1 -0
- package/node_modules/entities/dist/commonjs/generated/decode-data-xml.d.ts +2 -0
- package/node_modules/entities/dist/commonjs/generated/decode-data-xml.d.ts.map +1 -0
- package/node_modules/entities/dist/commonjs/generated/decode-data-xml.js +10 -0
- package/node_modules/entities/dist/commonjs/generated/decode-data-xml.js.map +1 -0
- package/node_modules/entities/dist/commonjs/generated/encode-html.d.ts +8 -0
- package/node_modules/entities/dist/commonjs/generated/encode-html.d.ts.map +1 -0
- package/node_modules/entities/dist/commonjs/generated/encode-html.js +13 -0
- package/node_modules/entities/dist/commonjs/generated/encode-html.js.map +1 -0
- package/node_modules/entities/dist/commonjs/index.d.ts +96 -0
- package/node_modules/entities/dist/commonjs/index.d.ts.map +1 -0
- package/node_modules/entities/dist/commonjs/index.js +131 -0
- package/node_modules/entities/dist/commonjs/index.js.map +1 -0
- package/node_modules/entities/dist/commonjs/package.json +3 -0
- package/node_modules/entities/dist/esm/decode-codepoint.d.ts +19 -0
- package/node_modules/entities/dist/esm/decode-codepoint.d.ts.map +1 -0
- package/node_modules/entities/dist/esm/decode-codepoint.js +72 -0
- package/node_modules/entities/dist/esm/decode-codepoint.js.map +1 -0
- package/node_modules/entities/dist/esm/decode.d.ts +209 -0
- package/node_modules/entities/dist/esm/decode.d.ts.map +1 -0
- package/node_modules/entities/dist/esm/decode.js +497 -0
- package/node_modules/entities/dist/esm/decode.js.map +1 -0
- package/node_modules/entities/dist/esm/encode.d.ts +22 -0
- package/node_modules/entities/dist/esm/encode.d.ts.map +1 -0
- package/node_modules/entities/dist/esm/encode.js +69 -0
- package/node_modules/entities/dist/esm/encode.js.map +1 -0
- package/node_modules/entities/dist/esm/escape.d.ts +43 -0
- package/node_modules/entities/dist/esm/escape.d.ts.map +1 -0
- package/node_modules/entities/dist/esm/escape.js +117 -0
- package/node_modules/entities/dist/esm/escape.js.map +1 -0
- package/node_modules/entities/dist/esm/generated/decode-data-html.d.ts +2 -0
- package/node_modules/entities/dist/esm/generated/decode-data-html.d.ts.map +1 -0
- package/node_modules/entities/dist/esm/generated/decode-data-html.js +7 -0
- package/node_modules/entities/dist/esm/generated/decode-data-html.js.map +1 -0
- package/node_modules/entities/dist/esm/generated/decode-data-xml.d.ts +2 -0
- package/node_modules/entities/dist/esm/generated/decode-data-xml.d.ts.map +1 -0
- package/node_modules/entities/dist/esm/generated/decode-data-xml.js +7 -0
- package/node_modules/entities/dist/esm/generated/decode-data-xml.js.map +1 -0
- package/node_modules/entities/dist/esm/generated/encode-html.d.ts +8 -0
- package/node_modules/entities/dist/esm/generated/encode-html.d.ts.map +1 -0
- package/node_modules/entities/dist/esm/generated/encode-html.js +10 -0
- package/node_modules/entities/dist/esm/generated/encode-html.js.map +1 -0
- package/node_modules/entities/dist/esm/index.d.ts +96 -0
- package/node_modules/entities/dist/esm/index.d.ts.map +1 -0
- package/node_modules/entities/dist/esm/index.js +107 -0
- package/node_modules/entities/dist/esm/index.js.map +1 -0
- package/node_modules/entities/dist/esm/package.json +3 -0
- package/node_modules/entities/escape.d.ts +1 -0
- package/node_modules/entities/escape.js +3 -0
- package/node_modules/entities/package.json +118 -0
- package/node_modules/entities/readme.md +122 -0
- package/node_modules/entities/src/decode-codepoint.ts +81 -0
- package/node_modules/entities/src/decode.spec.ts +320 -0
- package/node_modules/entities/src/decode.ts +620 -0
- package/node_modules/entities/src/encode.spec.ts +78 -0
- package/node_modules/entities/src/encode.ts +77 -0
- package/node_modules/entities/src/escape.spec.ts +14 -0
- package/node_modules/entities/src/escape.ts +148 -0
- package/node_modules/entities/src/generated/.eslintrc.json +10 -0
- package/node_modules/entities/src/generated/decode-data-html.ts +8 -0
- package/node_modules/entities/src/generated/decode-data-xml.ts +8 -0
- package/node_modules/entities/src/generated/encode-html.ts +17 -0
- package/node_modules/entities/src/index.spec.ts +125 -0
- package/node_modules/entities/src/index.ts +188 -0
- package/node_modules/parse5/LICENSE +19 -0
- package/node_modules/parse5/README.md +38 -0
- package/node_modules/parse5/dist/cjs/common/doctype.d.ts +4 -0
- package/node_modules/parse5/dist/cjs/common/doctype.js +118 -0
- package/node_modules/parse5/dist/cjs/common/error-codes.d.ts +67 -0
- package/node_modules/parse5/dist/cjs/common/error-codes.js +66 -0
- package/node_modules/parse5/dist/cjs/common/foreign-content.d.ts +9 -0
- package/node_modules/parse5/dist/cjs/common/foreign-content.js +237 -0
- package/node_modules/parse5/dist/cjs/common/html.d.ts +289 -0
- package/node_modules/parse5/dist/cjs/common/html.js +528 -0
- package/node_modules/parse5/dist/cjs/common/token.d.ts +84 -0
- package/node_modules/parse5/dist/cjs/common/token.js +24 -0
- package/node_modules/parse5/dist/cjs/common/unicode.d.ts +42 -0
- package/node_modules/parse5/dist/cjs/common/unicode.js +70 -0
- package/node_modules/parse5/dist/cjs/index.d.ts +71 -0
- package/node_modules/parse5/dist/cjs/index.js +54 -0
- package/node_modules/parse5/dist/cjs/package.json +1 -0
- package/node_modules/parse5/dist/cjs/parser/formatting-element-list.d.ts +36 -0
- package/node_modules/parse5/dist/cjs/parser/formatting-element-list.js +114 -0
- package/node_modules/parse5/dist/cjs/parser/index.d.ts +221 -0
- package/node_modules/parse5/dist/cjs/parser/index.js +3240 -0
- package/node_modules/parse5/dist/cjs/parser/open-element-stack.d.ts +53 -0
- package/node_modules/parse5/dist/cjs/parser/open-element-stack.js +328 -0
- package/node_modules/parse5/dist/cjs/serializer/index.d.ts +60 -0
- package/node_modules/parse5/dist/cjs/serializer/index.js +172 -0
- package/node_modules/parse5/dist/cjs/tokenizer/index.d.ts +247 -0
- package/node_modules/parse5/dist/cjs/tokenizer/index.js +2714 -0
- package/node_modules/parse5/dist/cjs/tokenizer/preprocessor.d.ts +36 -0
- package/node_modules/parse5/dist/cjs/tokenizer/preprocessor.js +200 -0
- package/node_modules/parse5/dist/cjs/tree-adapters/default.d.ts +84 -0
- package/node_modules/parse5/dist/cjs/tree-adapters/default.js +176 -0
- package/node_modules/parse5/dist/cjs/tree-adapters/interface.d.ts +255 -0
- package/node_modules/parse5/dist/cjs/tree-adapters/interface.js +2 -0
- package/node_modules/parse5/dist/common/doctype.d.ts +4 -0
- package/node_modules/parse5/dist/common/doctype.js +114 -0
- package/node_modules/parse5/dist/common/error-codes.d.ts +67 -0
- package/node_modules/parse5/dist/common/error-codes.js +63 -0
- package/node_modules/parse5/dist/common/foreign-content.d.ts +9 -0
- package/node_modules/parse5/dist/common/foreign-content.js +228 -0
- package/node_modules/parse5/dist/common/html.d.ts +289 -0
- package/node_modules/parse5/dist/common/html.js +523 -0
- package/node_modules/parse5/dist/common/token.d.ts +84 -0
- package/node_modules/parse5/dist/common/token.js +20 -0
- package/node_modules/parse5/dist/common/unicode.d.ts +42 -0
- package/node_modules/parse5/dist/common/unicode.js +62 -0
- package/node_modules/parse5/dist/index.d.ts +71 -0
- package/node_modules/parse5/dist/index.js +42 -0
- package/node_modules/parse5/dist/parser/formatting-element-list.d.ts +36 -0
- package/node_modules/parse5/dist/parser/formatting-element-list.js +110 -0
- package/node_modules/parse5/dist/parser/index.d.ts +221 -0
- package/node_modules/parse5/dist/parser/index.js +3245 -0
- package/node_modules/parse5/dist/parser/open-element-stack.d.ts +53 -0
- package/node_modules/parse5/dist/parser/open-element-stack.js +324 -0
- package/node_modules/parse5/dist/serializer/index.d.ts +60 -0
- package/node_modules/parse5/dist/serializer/index.js +168 -0
- package/node_modules/parse5/dist/tokenizer/index.d.ts +247 -0
- package/node_modules/parse5/dist/tokenizer/index.js +2710 -0
- package/node_modules/parse5/dist/tokenizer/preprocessor.d.ts +36 -0
- package/node_modules/parse5/dist/tokenizer/preprocessor.js +196 -0
- package/node_modules/parse5/dist/tree-adapters/default.d.ts +84 -0
- package/node_modules/parse5/dist/tree-adapters/default.js +173 -0
- package/node_modules/parse5/dist/tree-adapters/interface.d.ts +255 -0
- package/node_modules/parse5/dist/tree-adapters/interface.js +1 -0
- package/node_modules/parse5/package.json +50 -0
- package/node_modules/undici-types/LICENSE +21 -0
- package/node_modules/undici-types/README.md +6 -0
- package/node_modules/undici-types/agent.d.ts +31 -0
- package/node_modules/undici-types/api.d.ts +43 -0
- package/node_modules/undici-types/balanced-pool.d.ts +29 -0
- package/node_modules/undici-types/cache.d.ts +36 -0
- package/node_modules/undici-types/client.d.ts +108 -0
- package/node_modules/undici-types/connector.d.ts +34 -0
- package/node_modules/undici-types/content-type.d.ts +21 -0
- package/node_modules/undici-types/cookies.d.ts +28 -0
- package/node_modules/undici-types/diagnostics-channel.d.ts +66 -0
- package/node_modules/undici-types/dispatcher.d.ts +256 -0
- package/node_modules/undici-types/env-http-proxy-agent.d.ts +21 -0
- package/node_modules/undici-types/errors.d.ts +149 -0
- package/node_modules/undici-types/eventsource.d.ts +61 -0
- package/node_modules/undici-types/fetch.d.ts +209 -0
- package/node_modules/undici-types/file.d.ts +39 -0
- package/node_modules/undici-types/filereader.d.ts +54 -0
- package/node_modules/undici-types/formdata.d.ts +108 -0
- package/node_modules/undici-types/global-dispatcher.d.ts +9 -0
- package/node_modules/undici-types/global-origin.d.ts +7 -0
- package/node_modules/undici-types/handlers.d.ts +15 -0
- package/node_modules/undici-types/header.d.ts +4 -0
- package/node_modules/undici-types/index.d.ts +71 -0
- package/node_modules/undici-types/interceptors.d.ts +17 -0
- package/node_modules/undici-types/mock-agent.d.ts +50 -0
- package/node_modules/undici-types/mock-client.d.ts +25 -0
- package/node_modules/undici-types/mock-errors.d.ts +12 -0
- package/node_modules/undici-types/mock-interceptor.d.ts +93 -0
- package/node_modules/undici-types/mock-pool.d.ts +25 -0
- package/node_modules/undici-types/package.json +55 -0
- package/node_modules/undici-types/patch.d.ts +33 -0
- package/node_modules/undici-types/pool-stats.d.ts +19 -0
- package/node_modules/undici-types/pool.d.ts +39 -0
- package/node_modules/undici-types/proxy-agent.d.ts +28 -0
- package/node_modules/undici-types/readable.d.ts +65 -0
- package/node_modules/undici-types/retry-agent.d.ts +8 -0
- package/node_modules/undici-types/retry-handler.d.ts +116 -0
- package/node_modules/undici-types/util.d.ts +18 -0
- package/node_modules/undici-types/webidl.d.ts +228 -0
- package/node_modules/undici-types/websocket.d.ts +150 -0
- package/package.json +80 -0
- package/scripts/rebuild-forme.mjs +61 -0
- package/src/app/AppearanceControl.tsx +44 -0
- package/src/app/AssetThemeDefaults.tsx +87 -0
- package/src/app/AssetsBrowser.tsx +273 -0
- package/src/app/CommentDock.tsx +108 -0
- package/src/app/CreateDocumentDialog.tsx +51 -0
- package/src/app/DocumentActions.tsx +69 -0
- package/src/app/DocumentCard.tsx +50 -0
- package/src/app/DocumentViewControl.tsx +31 -0
- package/src/app/DocumentsBrowser.tsx +70 -0
- package/src/app/ExportHistoryDialog.tsx +86 -0
- package/src/app/ExportMenu.tsx +127 -0
- package/src/app/GuideDialog.tsx +80 -0
- package/src/app/InspectionLayer.tsx +77 -0
- package/src/app/LogoUploadDialog.tsx +92 -0
- package/src/app/Markdown.tsx +34 -0
- package/src/app/MaterialsBrowser.tsx +149 -0
- package/src/app/PageNumberInput.tsx +56 -0
- package/src/app/Pdf.tsx +561 -0
- package/src/app/ProjectDialogs.tsx +100 -0
- package/src/app/Reader.tsx +769 -0
- package/src/app/SearchField.tsx +13 -0
- package/src/app/Sidebar.tsx +86 -0
- package/src/app/SkillIndex.tsx +30 -0
- package/src/app/TemplatesBrowser.tsx +149 -0
- package/src/app/ThemesBrowser.tsx +159 -0
- package/src/app/api.ts +47 -0
- package/src/app/appearance.ts +35 -0
- package/src/app/assets.css +139 -0
- package/src/app/catalogPreview.ts +20 -0
- package/src/app/colors.css +167 -0
- package/src/app/comment-dock.css +53 -0
- package/src/app/commentDrafts.ts +67 -0
- package/src/app/componentCorrection.ts +17 -0
- package/src/app/edit-workbench.css +87 -0
- package/src/app/editSession.ts +238 -0
- package/src/app/editorRecovery.ts +25 -0
- package/src/app/export-history.css +31 -0
- package/src/app/export.css +17 -0
- package/src/app/guideLinks.ts +21 -0
- package/src/app/guides.css +40 -0
- package/src/app/main.tsx +221 -0
- package/src/app/materials.css +53 -0
- package/src/app/pdfLoading.ts +12 -0
- package/src/app/pdfSelection.ts +112 -0
- package/src/app/projects.css +63 -0
- package/src/app/reader-toolbar.css +65 -0
- package/src/app/selection.css +150 -0
- package/src/app/sidebar.css +60 -0
- package/src/app/skills.css +7 -0
- package/src/app/style.css +1061 -0
- package/src/app/templates.css +49 -0
- package/src/app/themes.css +80 -0
- package/src/app/ui/Icon.tsx +92 -0
- package/src/app/ui/README.md +27 -0
- package/src/app/ui/fonts.css +36 -0
- package/src/app/ui/index.tsx +223 -0
- package/src/app/ui/ui.css +388 -0
- package/src/app/useReaderPreview.ts +19 -0
- package/src/app/useTextEditing.ts +155 -0
- package/src/assets/adapter.ts +122 -0
- package/src/assets/files.ts +378 -0
- package/src/assets/import-worker.ts +294 -0
- package/src/assets/imports.ts +124 -0
- package/src/assets/index.ts +60 -0
- package/src/assets/store.ts +798 -0
- package/src/assets/usage.ts +118 -0
- package/src/cli/check.ts +25 -0
- package/src/cli/init.ts +119 -0
- package/src/cli/lock.ts +34 -0
- package/src/cli/main.ts +118 -0
- package/src/cli/process.ts +19 -0
- package/src/cli/start.ts +96 -0
- package/src/cli/update.ts +348 -0
- package/src/cli/workspace.ts +94 -0
- package/src/document/index.tsx +641 -0
- package/src/document/page.tsx +53 -0
- package/src/document/text-targets.tsx +162 -0
- package/src/media/files.ts +124 -0
- package/src/media/render.ts +63 -0
- package/src/rendering/resolve-sources.ts +84 -0
- package/src/runtime/jsx-dev-runtime.ts +2 -0
- package/src/runtime/jsx-runtime.ts +2 -0
- package/src/runtime/paths.ts +12 -0
- package/src/server/assets-cli.ts +162 -0
- package/src/server/assets-http.ts +100 -0
- package/src/server/client.ts +50 -0
- package/src/server/clipboard.ts +22 -0
- package/src/server/comments-cli.ts +64 -0
- package/src/server/comments.ts +130 -0
- package/src/server/create-cli.ts +33 -0
- package/src/server/create.ts +228 -0
- package/src/server/dependencies.ts +91 -0
- package/src/server/documents.ts +143 -0
- package/src/server/edits.ts +249 -0
- package/src/server/export-batch.ts +105 -0
- package/src/server/export-errors.ts +2 -0
- package/src/server/export-file.ts +57 -0
- package/src/server/export-inputs.ts +66 -0
- package/src/server/export-preview.ts +69 -0
- package/src/server/export.ts +32 -0
- package/src/server/exports.ts +283 -0
- package/src/server/files.ts +21 -0
- package/src/server/guides.ts +71 -0
- package/src/server/index.ts +432 -0
- package/src/server/materials.ts +39 -0
- package/src/server/media-cli.ts +73 -0
- package/src/server/media-mutations.ts +168 -0
- package/src/server/pptx.ts +160 -0
- package/src/server/preflight.ts +129 -0
- package/src/server/projects-cli.ts +29 -0
- package/src/server/projects.ts +187 -0
- package/src/server/render-error.ts +9 -0
- package/src/server/render-source.ts +24 -0
- package/src/server/render.ts +48 -0
- package/src/server/review-cli.ts +40 -0
- package/src/server/review.ts +161 -0
- package/src/server/selection.ts +23 -0
- package/src/server/source-overrides.ts +97 -0
- package/src/server/templates-cli.ts +51 -0
- package/src/server/templates.ts +186 -0
- package/src/server/text-layout.ts +41 -0
- package/src/server/text-source.ts +307 -0
- package/src/server/themes-cli.ts +59 -0
- package/src/server/themes.ts +215 -0
- package/src/server/ttf2eot.d.ts +3 -0
- package/src/server/worker.ts +166 -0
- package/src/server/workspace.ts +242 -0
- package/src/shared/anchors.ts +69 -0
- package/src/shared/assets.ts +154 -0
- package/src/shared/export.ts +34 -0
- package/src/shared/media.ts +33 -0
- package/src/shared/projects.ts +19 -0
- package/src/shared/selection.ts +94 -0
- package/src/shared/skill-index.ts +14 -0
- package/src/shared/starters.ts +19 -0
- package/src/shared/templates.ts +18 -0
- package/src/shared/themes.ts +24 -0
- package/src/shared/types.ts +39 -0
- package/src/template/index.ts +27 -0
- package/src/themes/index.ts +3 -0
- package/src/themes/neutral.ts +20 -0
- package/src/themes/types.ts +110 -0
- package/starter/assets/brand/wordmark.png +0 -0
- package/starter/assets/fonts/InterUI-LICENSE.txt +93 -0
- package/starter/assets/fonts/InterUI-Medium.ttf +0 -0
- package/starter/assets/fonts/InterUI-Regular.ttf +0 -0
- package/starter/assets/fonts/InterUI-source.json +19 -0
- package/starter/assets/fonts/OpenDocMono-Regular.ttf +0 -0
- package/starter/assets/fonts/OpenDocSans-Italic.ttf +0 -0
- package/starter/assets/fonts/OpenDocSans-Regular.ttf +0 -0
- package/starter/assets/fonts/OpenDocSans-Semibold.ttf +0 -0
- package/starter/assets/fonts/OpenDocSans-SemiboldItalic.ttf +0 -0
- package/starter/assets/fonts/OpenDocSerif-Italic.ttf +0 -0
- package/starter/assets/fonts/OpenDocSerif-Regular.ttf +0 -0
- package/starter/assets/fonts/OpenDocSerif-Semibold.ttf +0 -0
- package/starter/assets/fonts/OpenDocSerif-SemiboldItalic.ttf +0 -0
- package/starter/assets/fonts/SourceCode-LICENSE.md +93 -0
- package/starter/assets/fonts/SourceSans-LICENSE.md +93 -0
- package/starter/assets/fonts/SourceSerif-LICENSE.md +93 -0
- package/starter/assets/fonts/geist/OFL.txt +93 -0
- package/starter/assets/fonts/geist/asset.json +7 -0
- package/starter/assets/fonts/geist/files/1a9874b3be0fc168692a3d2327bccb4d28835653d3b91537a755296a467a83e3.ttf +0 -0
- package/starter/assets/fonts/geist/files/a8eefb873ba9c9020665f8a98302697ce1f3b79ab2deef6ee508c0ca6a82e876.ttf +0 -0
- package/starter/assets/fonts/geist/files/ccce59465ae5c61930ada5453834f60500fbd95084d85db63e0e65fad0125455.ttf +0 -0
- package/starter/assets/fonts/geist/files/f5e95110d70593672515080bee9148463ada0a7f77b89ed65a3d43cb2b0ff83a.pdf +0 -0
- package/starter/assets/fonts/geist/revisions/9a2bc12fb2f23cb27bc227087e63e1407bb804ee0586480bd6b7966ec69fb474.json +60 -0
- package/starter/assets/fonts/inter/OFL.txt +93 -0
- package/starter/assets/fonts/inter/asset.json +7 -0
- package/starter/assets/fonts/inter/files/1b08e7fc267a5c7e1d614100f604b83e7e8a0be241f0f288faa2b3ac93a683ba.ttf +0 -0
- package/starter/assets/fonts/inter/files/5b6f0c6f26e5c554a64cdcc3a9b5d802b7f9aec6206f1dbcfcdcad7b5860a2a9.ttf +0 -0
- package/starter/assets/fonts/inter/files/957db8560d510a198a7d0333fae7b6b444b00c20801581126273978d778eb3c3.pdf +0 -0
- package/starter/assets/fonts/inter/files/b37284b5701b6b168dfc770aa1a4ac492106422fd3ba76bc7641e37434e8019c.ttf +0 -0
- package/starter/assets/fonts/inter/files/c1811d32ac0f6eed1bbf57529f2ecdfc6c717d48a969dd864942436315a1b198.ttf +0 -0
- package/starter/assets/fonts/inter/files/e7a1aaf7eda9f2fad4131725fa556265ec75ca7b2d756260173a040363e8d4f7.ttf +0 -0
- package/starter/assets/fonts/inter/files/ef8f755d933b9310804cc9152f1f18cb2ac460d6b05054ec913d889c9f11f79e.ttf +0 -0
- package/starter/assets/fonts/inter/revisions/600391517c413963b71e278a57c26c40366c81fa3939a6b9755f446206766d12.json +99 -0
- package/starter/assets/fonts/inter/source.json +57 -0
- package/starter/assets/fonts/lato/OFL.txt +93 -0
- package/starter/assets/fonts/lato/asset.json +7 -0
- package/starter/assets/fonts/lato/files/4a6b566dbb28adb473a8789ce5169306f42f0c8a070ac92bae93636f36348b0d.pdf +0 -0
- package/starter/assets/fonts/lato/files/5adeb6b334d8fb99ef053bc052b952773d544315e0e1d668a12c0ce8bd51375c.ttf +0 -0
- package/starter/assets/fonts/lato/files/62c1b7f0d2e74b45960154c3520efc337b553db0961bfdc950d5618334596cc8.ttf +0 -0
- package/starter/assets/fonts/lato/files/71b8b7decbe75a881ed267be539d402bd1e9420b799658aada4e0d1bd5af803c.ttf +0 -0
- package/starter/assets/fonts/lato/files/8a0aace75d33794eece4b28187bfc1df0bbd2888b5d8a56e01788c8d65d16be1.ttf +0 -0
- package/starter/assets/fonts/lato/files/d636e4683231f931eda222d588e944d082bfd3bdba02f928bee461c0f185b251.ttf +0 -0
- package/starter/assets/fonts/lato/files/e399c44efe1387100531d26c7e4800c5d12251b890d6654a3098c7c679cb1786.ttf +0 -0
- package/starter/assets/fonts/lato/revisions/49ccb93400a9d466fab7d9843b1ecf8916ec6d47a475dd79a597a22eb39616bc.json +99 -0
- package/starter/assets/fonts/lato/source.json +57 -0
- package/starter/assets/fonts/merriweather/OFL.txt +93 -0
- package/starter/assets/fonts/merriweather/asset.json +7 -0
- package/starter/assets/fonts/merriweather/files/1c54f91b910454251c4c2b7bd4e472e1abf265fba7ca3c0ef2eee2f41324099e.ttf +0 -0
- package/starter/assets/fonts/merriweather/files/8bde8da2bb7f0265e27ba0481f70bb48d0f98a976c145f2f239c648ddd6532e0.pdf +0 -0
- package/starter/assets/fonts/merriweather/files/90b7db4460fcd1a5ca49575c2668a793f3cd6447664d312a068d690e1c01e6bc.ttf +0 -0
- package/starter/assets/fonts/merriweather/files/afff81272b7653417bb6f8bef57add055b1f9fda36f27eba5f32b40752bd08da.ttf +0 -0
- package/starter/assets/fonts/merriweather/files/e029d3b71e5bae3877c73f4f2b15af28db16593d49b0aed2a328d37ff7075846.ttf +0 -0
- package/starter/assets/fonts/merriweather/files/e9864a6790bf712a04e20f3662b206a1535186ed3e877fd859dd5fb9682335df.ttf +0 -0
- package/starter/assets/fonts/merriweather/files/fa71baebdd20a7d0de2e5e445a5922530fe9e1d740efbe791f113b682d3cb093.ttf +0 -0
- package/starter/assets/fonts/merriweather/revisions/1888e390b3e4f38a3d7cd2dc1bb805aeb6ce13f986a97656f0265fd436bc91d2.json +99 -0
- package/starter/assets/fonts/merriweather/source.json +57 -0
- package/starter/assets/fonts/open-sans/OFL.txt +92 -0
- package/starter/assets/fonts/open-sans/asset.json +7 -0
- package/starter/assets/fonts/open-sans/files/1bc0594a83762b2c25d38915a34464b7fec6088298ca22d151757bf98d6700ec.ttf +0 -0
- package/starter/assets/fonts/open-sans/files/47ed1cfaa7ffca04a04ab850b2938fd52c09d1b44cfa3e2980878e166b0cbd87.ttf +0 -0
- package/starter/assets/fonts/open-sans/files/574939af301f07df8e21cfff279465c5981dd9d9cf62086f7515897df951c6f5.pdf +0 -0
- package/starter/assets/fonts/open-sans/files/6d3884d0d57c1b67e9c50c54eca7b69c535c114e76bd7a9cfda0258588185553.ttf +0 -0
- package/starter/assets/fonts/open-sans/files/733eb93b167e6d45de09aa768f6c0355647c2c26f5416dbc7407a08ed41e81c6.ttf +0 -0
- package/starter/assets/fonts/open-sans/files/75367e6da7d914eaed87883ba51b2a2b5816b5cdbb39f96ec5c81a5b032bfd5a.ttf +0 -0
- package/starter/assets/fonts/open-sans/files/ff4fdc11204e0edd14e9a0bb6c374b847a9093b0ec2c16702e03afca79ac4ef7.ttf +0 -0
- package/starter/assets/fonts/open-sans/revisions/bbbe3a4915b9bccbc883c0decb4097511c43b6bd09ae22a1516aff128c219bc5.json +99 -0
- package/starter/assets/fonts/open-sans/source.json +57 -0
- package/starter/assets/fonts/roboto/OFL.txt +93 -0
- package/starter/assets/fonts/roboto/asset.json +7 -0
- package/starter/assets/fonts/roboto/files/22d45d989248a624252b8382067ac56550212fa675dcdbaf12f03d297dd87a58.pdf +0 -0
- package/starter/assets/fonts/roboto/files/35757eaa996b3359022f47f164a67ee9eac04971af24c5b8fbd4b89e844ff190.ttf +0 -0
- package/starter/assets/fonts/roboto/files/4d3035883c531846bd1ce2dc89f36d932e72dc114f56062c7e3a9cdd74f3d3ea.ttf +0 -0
- package/starter/assets/fonts/roboto/files/be7639306a4fc4a02ce2ae2ff59eb1335e4db4a7379dd0a384ed7cc053817789.ttf +0 -0
- package/starter/assets/fonts/roboto/files/d2920cbb1b60a3fd11fbf22049286e43a6baf522d37fdf1e7c89562319bcfea5.ttf +0 -0
- package/starter/assets/fonts/roboto/files/dece7c71bbe61710787f0839c722c65e87355dd2eb4a823e6b5488121ff81e4b.ttf +0 -0
- package/starter/assets/fonts/roboto/files/f478ba88b0292e685e487901dfbb1401457d32d6ebee4030cdfd5698235bffa8.ttf +0 -0
- package/starter/assets/fonts/roboto/revisions/bda5e2d84c17313373dd2dbf3e64fe0dd450c7e1b59e6929e69557e122fe976e.json +99 -0
- package/starter/assets/fonts/roboto/source.json +57 -0
- package/starter/assets/logos/opendoc/asset.json +6 -0
- package/starter/assets/logos/opendoc/files/831d5ccd9eb6709cc8439ea257c03d81452e81a28a3d1eedbd6a3b7daff687b6.png +0 -0
- package/starter/assets/logos/opendoc/revisions/834dde8926ddb81de687254195a168bb13bfb77803307f1257de1f2228be63b2.json +33 -0
- package/starter/assets/ui-fonts/geist/OFL.txt +93 -0
- package/starter/assets/ui-fonts/geist/geist-latin-ext-wght-italic.woff2 +0 -0
- package/starter/assets/ui-fonts/geist/geist-latin-ext-wght-normal.woff2 +0 -0
- package/starter/assets/ui-fonts/geist/geist-latin-wght-italic.woff2 +0 -0
- package/starter/assets/ui-fonts/geist/geist-latin-wght-normal.woff2 +0 -0
- package/starter/assets/ui-fonts/geist/source.json +26 -0
- package/starter/documents/welcome/assets.json +7 -0
- package/starter/documents/welcome/index.tsx +160 -0
- package/starter/documents/welcome/media/.gitkeep +0 -0
- package/starter/documents/welcome/media/paper-architecture/image.png +0 -0
- package/starter/documents/welcome/media/paper-architecture/meta.json +9 -0
- package/starter/documents/welcome/media/paper-architecture/prompt.txt +1 -0
- package/starter/documents/welcome/media/studio-rhythm/chart.svg +2343 -0
- package/starter/documents/welcome/media/studio-rhythm/data.json +234 -0
- package/starter/documents/welcome/media/studio-rhythm/generation.json +9 -0
- package/starter/documents/welcome/media/studio-rhythm/image.png +0 -0
- package/starter/documents/welcome/media/studio-rhythm/meta.json +11 -0
- package/starter/documents/welcome/media/studio-rhythm/recipe.py +79 -0
- package/starter/documents/welcome/theme.tsx +5 -0
- package/starter/documents/welcome-presentation/assets.json +7 -0
- package/starter/documents/welcome-presentation/index.tsx +166 -0
- package/starter/documents/welcome-presentation/media/.gitkeep +0 -0
- package/starter/documents/welcome-presentation/media/paper-architecture/image.png +0 -0
- package/starter/documents/welcome-presentation/media/paper-architecture/meta.json +9 -0
- package/starter/documents/welcome-presentation/media/paper-architecture/prompt.txt +1 -0
- package/starter/documents/welcome-presentation/media/studio-rhythm/chart.svg +1336 -0
- package/starter/documents/welcome-presentation/media/studio-rhythm/data.json +234 -0
- package/starter/documents/welcome-presentation/media/studio-rhythm/generation.json +9 -0
- package/starter/documents/welcome-presentation/media/studio-rhythm/image.png +0 -0
- package/starter/documents/welcome-presentation/media/studio-rhythm/meta.json +13 -0
- package/starter/documents/welcome-presentation/media/studio-rhythm/recipe.py +49 -0
- package/starter/documents/welcome-presentation/theme.tsx +5 -0
- package/starter/projects.json +17 -0
- package/starter/templates/AGENTS.md +9 -0
- package/starter/templates/_shared/BusinessLogo.tsx +14 -0
- package/starter/templates/_shared/Pricing.tsx +40 -0
- package/starter/templates/_shared/commerce.ts +93 -0
- package/starter/templates/brand-guidelines/AGENTS.md +32 -0
- package/starter/templates/brand-guidelines/index.tsx +62 -0
- package/starter/templates/brand-guidelines/preview.tsx +181 -0
- package/starter/templates/brand-guidelines/starter.tsx +179 -0
- package/starter/templates/brand-guidelines/template.json +15 -0
- package/starter/templates/business-proposal/AGENTS.md +13 -0
- package/starter/templates/business-proposal/README.md +36 -0
- package/starter/templates/business-proposal/data.json +37 -0
- package/starter/templates/business-proposal/examples/long.json +142 -0
- package/starter/templates/business-proposal/examples/sparse.json +17 -0
- package/starter/templates/business-proposal/examples/typical.json +37 -0
- package/starter/templates/business-proposal/index.tsx +59 -0
- package/starter/templates/business-proposal/preview.tsx +47 -0
- package/starter/templates/business-proposal/schema.ts +14 -0
- package/starter/templates/business-proposal/starter.tsx +18 -0
- package/starter/templates/business-proposal/template.json +11 -0
- package/starter/templates/company-profile/AGENTS.md +48 -0
- package/starter/templates/company-profile/index.tsx +69 -0
- package/starter/templates/company-profile/preview.tsx +185 -0
- package/starter/templates/company-profile/starter.tsx +186 -0
- package/starter/templates/company-profile/template.json +15 -0
- package/starter/templates/consulting-report/AGENTS.md +11 -0
- package/starter/templates/consulting-report/README.md +13 -0
- package/starter/templates/consulting-report/RESEARCH.md +10 -0
- package/starter/templates/consulting-report/index.tsx +44 -0
- package/starter/templates/consulting-report/preview.tsx +39 -0
- package/starter/templates/consulting-report/starter.tsx +15 -0
- package/starter/templates/consulting-report/template.json +11 -0
- package/starter/templates/editorial-essay/AGENTS.md +11 -0
- package/starter/templates/editorial-essay/README.md +11 -0
- package/starter/templates/editorial-essay/index.tsx +30 -0
- package/starter/templates/editorial-essay/preview.tsx +26 -0
- package/starter/templates/editorial-essay/starter.tsx +15 -0
- package/starter/templates/editorial-essay/template.json +11 -0
- package/starter/templates/executive-brief/AGENTS.md +9 -0
- package/starter/templates/executive-brief/README.md +11 -0
- package/starter/templates/executive-brief/index.tsx +33 -0
- package/starter/templates/executive-brief/preview.tsx +37 -0
- package/starter/templates/executive-brief/starter.tsx +11 -0
- package/starter/templates/executive-brief/template.json +11 -0
- package/starter/templates/image-story/AGENTS.md +37 -0
- package/starter/templates/image-story/index.tsx +106 -0
- package/starter/templates/image-story/preview.tsx +33 -0
- package/starter/templates/image-story/starter.tsx +24 -0
- package/starter/templates/image-story/template.json +13 -0
- package/starter/templates/invoice/AGENTS.md +13 -0
- package/starter/templates/invoice/README.md +40 -0
- package/starter/templates/invoice/data.json +61 -0
- package/starter/templates/invoice/examples/long.json +241 -0
- package/starter/templates/invoice/examples/sparse.json +33 -0
- package/starter/templates/invoice/examples/typical.json +61 -0
- package/starter/templates/invoice/index.tsx +57 -0
- package/starter/templates/invoice/preview.tsx +12 -0
- package/starter/templates/invoice/schema.ts +45 -0
- package/starter/templates/invoice/starter.tsx +12 -0
- package/starter/templates/invoice/template.json +11 -0
- package/starter/templates/literary-text/AGENTS.md +9 -0
- package/starter/templates/literary-text/README.md +11 -0
- package/starter/templates/literary-text/index.tsx +31 -0
- package/starter/templates/literary-text/preview.tsx +20 -0
- package/starter/templates/literary-text/starter.tsx +11 -0
- package/starter/templates/literary-text/template.json +11 -0
- package/starter/templates/magazine-feature/AGENTS.md +11 -0
- package/starter/templates/magazine-feature/README.md +13 -0
- package/starter/templates/magazine-feature/index.tsx +41 -0
- package/starter/templates/magazine-feature/preview.tsx +34 -0
- package/starter/templates/magazine-feature/starter.tsx +15 -0
- package/starter/templates/magazine-feature/template.json +11 -0
- package/starter/templates/monthly-report/AGENTS.md +11 -0
- package/starter/templates/monthly-report/README.md +24 -0
- package/starter/templates/monthly-report/data.json +131 -0
- package/starter/templates/monthly-report/examples/long.json +519 -0
- package/starter/templates/monthly-report/examples/sparse.json +20 -0
- package/starter/templates/monthly-report/examples/typical.json +37 -0
- package/starter/templates/monthly-report/index.tsx +92 -0
- package/starter/templates/monthly-report/preview.tsx +14 -0
- package/starter/templates/monthly-report/schema.ts +127 -0
- package/starter/templates/monthly-report/starter.tsx +13 -0
- package/starter/templates/monthly-report/template.json +11 -0
- package/starter/templates/pitch-deck/AGENTS.md +32 -0
- package/starter/templates/pitch-deck/index.tsx +46 -0
- package/starter/templates/pitch-deck/preview.tsx +142 -0
- package/starter/templates/pitch-deck/starter.tsx +140 -0
- package/starter/templates/pitch-deck/template.json +15 -0
- package/starter/templates/proposal-deck/AGENTS.md +25 -0
- package/starter/templates/proposal-deck/index.tsx +52 -0
- package/starter/templates/proposal-deck/preview.tsx +172 -0
- package/starter/templates/proposal-deck/starter.tsx +168 -0
- package/starter/templates/proposal-deck/template.json +7 -0
- package/starter/templates/quotation/AGENTS.md +13 -0
- package/starter/templates/quotation/README.md +42 -0
- package/starter/templates/quotation/data.json +64 -0
- package/starter/templates/quotation/examples/long.json +272 -0
- package/starter/templates/quotation/examples/sparse.json +31 -0
- package/starter/templates/quotation/examples/typical.json +64 -0
- package/starter/templates/quotation/index.tsx +51 -0
- package/starter/templates/quotation/preview.tsx +12 -0
- package/starter/templates/quotation/schema.ts +27 -0
- package/starter/templates/quotation/starter.tsx +12 -0
- package/starter/templates/quotation/template.json +11 -0
- package/starter/templates/scientific-paper/AGENTS.md +11 -0
- package/starter/templates/scientific-paper/README.md +13 -0
- package/starter/templates/scientific-paper/index.tsx +47 -0
- package/starter/templates/scientific-paper/preview.tsx +43 -0
- package/starter/templates/scientific-paper/starter.tsx +15 -0
- package/starter/templates/scientific-paper/template.json +11 -0
- package/starter/themes/Specimen.tsx +54 -0
- package/starter/themes/civic-spectrum/components.tsx +92 -0
- package/starter/themes/civic-spectrum/design.md +70 -0
- package/starter/themes/civic-spectrum/index.ts +57 -0
- package/starter/themes/civic-spectrum/preview.tsx +66 -0
- package/starter/themes/field-manual/components.tsx +106 -0
- package/starter/themes/field-manual/design.md +72 -0
- package/starter/themes/field-manual/index.ts +57 -0
- package/starter/themes/field-manual/preview.tsx +82 -0
- package/starter/themes/index.ts +3 -0
- package/starter/themes/mckinsey-consulting/components.tsx +69 -0
- package/starter/themes/mckinsey-consulting/design.md +68 -0
- package/starter/themes/mckinsey-consulting/index.ts +60 -0
- package/starter/themes/mckinsey-consulting/preview.tsx +53 -0
- package/starter/themes/neutral/design.md +35 -0
- package/starter/themes/neutral/index.ts +21 -0
- package/starter/themes/neutral/preview.tsx +4 -0
- package/starter/themes/opendoc-neutral/components.tsx +77 -0
- package/starter/themes/opendoc-neutral/design.md +70 -0
- package/starter/themes/opendoc-neutral/index.ts +54 -0
- package/starter/themes/opendoc-neutral/preview.tsx +43 -0
- package/starter/themes/types.ts +3 -0
- package/tsconfig.workspace.json +24 -0
- package/vendor/formepdf/LICENSE +21 -0
- package/vendor/formepdf/README.md +97 -0
- package/vendor/formepdf/font-subset.patch +228 -0
- package/vendor/formepdf/text-writer.patch +507 -0
|
@@ -0,0 +1,1470 @@
|
|
|
1
|
+
import { isValidElement, Children, Fragment } from 'react';
|
|
2
|
+
import { Document, Page, View, Text, H1, H2, H3, H4, H5, H6, OrderedList, UnorderedList, ListItem, Strong, Em, Code, Link, Image, Table, Row, Cell, Fixed, Svg, QrCode, Barcode, Canvas, Watermark, PageBreak, BarChart, LineChart, PieChart, AreaChart, DotPlot, TextField, Checkbox, Dropdown, RadioButton } from './components.js';
|
|
3
|
+
import { Font } from './font.js';
|
|
4
|
+
import { mapStyle, parseColor, expandEdges, mapColumnWidth, mergeFonts, recordCanvasOperations, mapListMarker, HEADING_DEFAULTS, STRONG_DEFAULTS, EM_DEFAULTS, CODE_DEFAULTS, LINK_DEFAULTS, buildBarChartKind, buildLineChartKind, buildPieChartKind, buildAreaChartKind, buildDotPlotKind } from '@formepdf/shared';
|
|
5
|
+
import { isRefMarker, getRefPath, isEachMarker, getEachPath, getEachTemplate, isExprMarker, getExpr, REF_SENTINEL, REF_SENTINEL_END, } from './template-proxy.js';
|
|
6
|
+
const VALID_PARENTS = {
|
|
7
|
+
Page: {
|
|
8
|
+
allowed: ['Document'],
|
|
9
|
+
suggestion: '<Page> must be a direct child of <Document>.',
|
|
10
|
+
},
|
|
11
|
+
Row: {
|
|
12
|
+
allowed: ['Table'],
|
|
13
|
+
suggestion: '<Row> must be inside a <Table>. Wrap it: <Table><Row>...</Row></Table>',
|
|
14
|
+
},
|
|
15
|
+
Cell: {
|
|
16
|
+
allowed: ['Row'],
|
|
17
|
+
suggestion: '<Cell> must be inside a <Row>. Wrap it: <Row><Cell>...</Cell></Row>',
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
function validateNesting(componentName, parent) {
|
|
21
|
+
const rule = VALID_PARENTS[componentName];
|
|
22
|
+
if (!rule)
|
|
23
|
+
return;
|
|
24
|
+
if (parent !== null && !rule.allowed.includes(parent)) {
|
|
25
|
+
throw new Error(`Invalid nesting: <${componentName}> found inside <${parent}>. ${rule.suggestion}`);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// ─── Source location extraction ─────────────────────────────────────
|
|
29
|
+
function extractSourceLocation(element) {
|
|
30
|
+
// Check globalThis.__formeSourceMap (populated by CLI dev server's JSX shim for React 19+)
|
|
31
|
+
const map = globalThis.__formeSourceMap;
|
|
32
|
+
if (map) {
|
|
33
|
+
const source = map.get(element);
|
|
34
|
+
if (source)
|
|
35
|
+
return source;
|
|
36
|
+
}
|
|
37
|
+
// Fallback to _source for React 18 and earlier
|
|
38
|
+
const s = element._source;
|
|
39
|
+
if (s && s.fileName) {
|
|
40
|
+
return { file: s.fileName, line: s.lineNumber, column: s.columnNumber };
|
|
41
|
+
}
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
// ─── Version-independent type checks ─────────────────────────────────
|
|
45
|
+
/**
|
|
46
|
+
* Check if a component type is `Document`, even across different package versions.
|
|
47
|
+
* Uses `__formeType` marker first (survives minification), then falls back to
|
|
48
|
+
* `displayName` / `name` for legacy builds without the marker.
|
|
49
|
+
*/
|
|
50
|
+
function isDocumentType(type) {
|
|
51
|
+
if (type === Document)
|
|
52
|
+
return true;
|
|
53
|
+
if (typeof type === 'function') {
|
|
54
|
+
return type.__formeType === 'Document'
|
|
55
|
+
|| type.displayName === 'Document'
|
|
56
|
+
|| type.name === 'Document';
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
// ─── Component resolution ────────────────────────────────────────────
|
|
61
|
+
/**
|
|
62
|
+
* Resolve wrapper function components (e.g. `<MyReport data={...} />`) by
|
|
63
|
+
* calling them until we reach a Forme primitive like `<Document>`. This lets
|
|
64
|
+
* users pass custom components directly to `renderDocument()` /
|
|
65
|
+
* `serialize()` without manually invoking them first.
|
|
66
|
+
*/
|
|
67
|
+
function resolveElement(element) {
|
|
68
|
+
let resolved = element;
|
|
69
|
+
for (let i = 0; i < 10 && typeof resolved.type === 'function' && !isDocumentType(resolved.type); i++) {
|
|
70
|
+
const result = callComponent(resolved.type, resolved.props);
|
|
71
|
+
if (!isValidElement(result))
|
|
72
|
+
break;
|
|
73
|
+
resolved = result;
|
|
74
|
+
}
|
|
75
|
+
return resolved;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Call a function component directly (outside React's render cycle).
|
|
79
|
+
* React Compiler injects `useMemoCache` hooks into compiled components,
|
|
80
|
+
* which throws when called outside a render context. We catch that and
|
|
81
|
+
* give a clear error message pointing to the `'use no memo'` directive.
|
|
82
|
+
*/
|
|
83
|
+
function callComponent(fn, props) {
|
|
84
|
+
try {
|
|
85
|
+
return fn(props);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
if (err instanceof Error && /hook|useMemoCache|Invalid hook call/i.test(err.message)) {
|
|
89
|
+
const name = fn.displayName || fn.name || 'Unknown';
|
|
90
|
+
throw new Error(`Component "${name}" appears to be compiled by React Compiler, which injects hooks ` +
|
|
91
|
+
`that cannot run outside of React's render cycle. Forme's serialize() calls components ` +
|
|
92
|
+
`directly to walk the element tree.\n\n` +
|
|
93
|
+
`Fix: Add 'use no memo' at the top of the component to opt it out of React Compiler:\n\n` +
|
|
94
|
+
` function ${name}(props) {\n` +
|
|
95
|
+
` 'use no memo';\n` +
|
|
96
|
+
` return <Document>...</Document>;\n` +
|
|
97
|
+
` }\n\n` +
|
|
98
|
+
`Alternatively, call the component yourself before passing to renderDocument():\n\n` +
|
|
99
|
+
` const element = <${name} data={data} />;\n` +
|
|
100
|
+
` // becomes:\n` +
|
|
101
|
+
` const element = ${name}({ data });\n` +
|
|
102
|
+
` await renderDocument(element);`);
|
|
103
|
+
}
|
|
104
|
+
throw err;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// ─── Public API ──────────────────────────────────────────────────────
|
|
108
|
+
/**
|
|
109
|
+
* Serialize a React element tree into a Forme JSON document object.
|
|
110
|
+
* The top-level element must be a <Document> (or a component that returns one).
|
|
111
|
+
*/
|
|
112
|
+
export function serialize(element) {
|
|
113
|
+
element = resolveElement(element);
|
|
114
|
+
if (!isDocumentType(element.type)) {
|
|
115
|
+
throw new Error('Top-level element must be <Document>');
|
|
116
|
+
}
|
|
117
|
+
const props = element.props;
|
|
118
|
+
const childElements = flattenChildren(props.children);
|
|
119
|
+
// Separate Page children from content children
|
|
120
|
+
const pageNodes = [];
|
|
121
|
+
const contentNodes = [];
|
|
122
|
+
for (const child of childElements) {
|
|
123
|
+
if (isValidElement(child) && child.type === Page) {
|
|
124
|
+
pageNodes.push(serializePage(child));
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
const node = serializeChild(child, 'Document');
|
|
128
|
+
if (node)
|
|
129
|
+
contentNodes.push(node);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
// If there are page nodes, use them. Otherwise wrap content in a default page.
|
|
133
|
+
let children;
|
|
134
|
+
if (pageNodes.length > 0) {
|
|
135
|
+
// Any loose content nodes get added to the last page's children
|
|
136
|
+
if (contentNodes.length > 0) {
|
|
137
|
+
const lastPage = pageNodes[pageNodes.length - 1];
|
|
138
|
+
lastPage.children.push(...contentNodes);
|
|
139
|
+
}
|
|
140
|
+
children = pageNodes;
|
|
141
|
+
}
|
|
142
|
+
else if (contentNodes.length > 0) {
|
|
143
|
+
children = contentNodes;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
children = [];
|
|
147
|
+
}
|
|
148
|
+
const metadata = {};
|
|
149
|
+
if (props.title !== undefined)
|
|
150
|
+
metadata.title = props.title;
|
|
151
|
+
if (props.author !== undefined)
|
|
152
|
+
metadata.author = props.author;
|
|
153
|
+
if (props.subject !== undefined)
|
|
154
|
+
metadata.subject = props.subject;
|
|
155
|
+
if (props.creator !== undefined)
|
|
156
|
+
metadata.creator = props.creator;
|
|
157
|
+
if (props.lang !== undefined)
|
|
158
|
+
metadata.lang = props.lang;
|
|
159
|
+
// Merge global + document fonts (document fonts override on conflict)
|
|
160
|
+
const mergedFonts = mergeFonts(Font.getRegistered(), props.fonts);
|
|
161
|
+
const result = {
|
|
162
|
+
children,
|
|
163
|
+
metadata,
|
|
164
|
+
defaultPage: {
|
|
165
|
+
size: 'A4',
|
|
166
|
+
margin: { top: 54, right: 54, bottom: 54, left: 54 },
|
|
167
|
+
wrap: true,
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
if (props.style)
|
|
171
|
+
result.defaultStyle = mapStyle(props.style);
|
|
172
|
+
if (props.tagged !== undefined)
|
|
173
|
+
result.tagged = props.tagged;
|
|
174
|
+
if (props.pdfa !== undefined)
|
|
175
|
+
result.pdfa = props.pdfa;
|
|
176
|
+
if (props.pdfUa)
|
|
177
|
+
result.pdfUa = true;
|
|
178
|
+
const cert = props.certification ?? props.signature;
|
|
179
|
+
if (cert) {
|
|
180
|
+
if (props.signature && !props.certification) {
|
|
181
|
+
console.warn('[Forme] The `signature` prop is deprecated. Use `certification` instead.');
|
|
182
|
+
}
|
|
183
|
+
result.certification = cert;
|
|
184
|
+
}
|
|
185
|
+
if (mergedFonts.length > 0) {
|
|
186
|
+
result.fonts = mergedFonts;
|
|
187
|
+
}
|
|
188
|
+
return result;
|
|
189
|
+
}
|
|
190
|
+
// ─── Page serialization ──────────────────────────────────────────────
|
|
191
|
+
function serializePage(element) {
|
|
192
|
+
const props = element.props;
|
|
193
|
+
let size = 'A4';
|
|
194
|
+
if (props.size !== undefined) {
|
|
195
|
+
if (typeof props.size === 'string') {
|
|
196
|
+
size = props.size;
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
size = { Custom: { width: props.size.width, height: props.size.height } };
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
let margin = { top: 54, right: 54, bottom: 54, left: 54 };
|
|
203
|
+
if (props.margin !== undefined) {
|
|
204
|
+
margin = expandEdges(props.margin);
|
|
205
|
+
}
|
|
206
|
+
const config = { size, margin, wrap: true };
|
|
207
|
+
if (props.backgroundImage !== undefined)
|
|
208
|
+
config.backgroundImage = props.backgroundImage;
|
|
209
|
+
if (props.backgroundOpacity !== undefined)
|
|
210
|
+
config.backgroundOpacity = props.backgroundOpacity;
|
|
211
|
+
if (props.backgroundSize !== undefined)
|
|
212
|
+
config.backgroundSize = props.backgroundSize;
|
|
213
|
+
if (props.backgroundPosition !== undefined)
|
|
214
|
+
config.backgroundPosition = props.backgroundPosition;
|
|
215
|
+
const childElements = flattenChildren(props.children);
|
|
216
|
+
const children = serializeChildren(childElements, 'Page');
|
|
217
|
+
return {
|
|
218
|
+
kind: { type: 'Page', config },
|
|
219
|
+
style: props.style ? mapStyle(props.style) : {},
|
|
220
|
+
children,
|
|
221
|
+
sourceLocation: extractSourceLocation(element),
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
// ─── Node serialization ─────────────────────────────────────────────
|
|
225
|
+
function serializeChild(child, parent = null) {
|
|
226
|
+
if (child === null || child === undefined || typeof child === 'boolean') {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
if (typeof child === 'string') {
|
|
230
|
+
return {
|
|
231
|
+
kind: { type: 'Text', content: child },
|
|
232
|
+
style: {},
|
|
233
|
+
children: [],
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
if (typeof child === 'number') {
|
|
237
|
+
return {
|
|
238
|
+
kind: { type: 'Text', content: String(child) },
|
|
239
|
+
style: {},
|
|
240
|
+
children: [],
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
if (!isValidElement(child)) {
|
|
244
|
+
// Detect HTML elements and give helpful suggestion
|
|
245
|
+
if (typeof child === 'object' && child !== null && 'type' in child) {
|
|
246
|
+
const t = child.type;
|
|
247
|
+
if (typeof t === 'string') {
|
|
248
|
+
const suggestions = {
|
|
249
|
+
div: 'View', span: 'Text', p: 'Text', h1: 'Text', h2: 'Text',
|
|
250
|
+
h3: 'Text', img: 'Image', table: 'Table', tr: 'Row', td: 'Cell',
|
|
251
|
+
};
|
|
252
|
+
const suggestion = suggestions[t];
|
|
253
|
+
if (suggestion) {
|
|
254
|
+
throw new Error(`HTML element <${t}> is not supported. Use <${suggestion}> instead.`);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
const element = child;
|
|
261
|
+
if (element.type === View) {
|
|
262
|
+
return serializeView(element, parent);
|
|
263
|
+
}
|
|
264
|
+
if (element.type === Text) {
|
|
265
|
+
return serializeText(element);
|
|
266
|
+
}
|
|
267
|
+
if (element.type === H1)
|
|
268
|
+
return serializeHeading(element, 1);
|
|
269
|
+
if (element.type === H2)
|
|
270
|
+
return serializeHeading(element, 2);
|
|
271
|
+
if (element.type === H3)
|
|
272
|
+
return serializeHeading(element, 3);
|
|
273
|
+
if (element.type === H4)
|
|
274
|
+
return serializeHeading(element, 4);
|
|
275
|
+
if (element.type === H5)
|
|
276
|
+
return serializeHeading(element, 5);
|
|
277
|
+
if (element.type === H6)
|
|
278
|
+
return serializeHeading(element, 6);
|
|
279
|
+
if (element.type === OrderedList)
|
|
280
|
+
return serializeList(element, true);
|
|
281
|
+
if (element.type === UnorderedList)
|
|
282
|
+
return serializeList(element, false);
|
|
283
|
+
if (element.type === ListItem)
|
|
284
|
+
return serializeListItem(element);
|
|
285
|
+
if (element.type === Image) {
|
|
286
|
+
return serializeImage(element);
|
|
287
|
+
}
|
|
288
|
+
if (element.type === Table) {
|
|
289
|
+
return serializeTable(element, parent);
|
|
290
|
+
}
|
|
291
|
+
if (element.type === Row) {
|
|
292
|
+
validateNesting('Row', parent);
|
|
293
|
+
return serializeRow(element);
|
|
294
|
+
}
|
|
295
|
+
if (element.type === Cell) {
|
|
296
|
+
validateNesting('Cell', parent);
|
|
297
|
+
return serializeCell(element);
|
|
298
|
+
}
|
|
299
|
+
if (element.type === Fixed) {
|
|
300
|
+
return serializeFixed(element);
|
|
301
|
+
}
|
|
302
|
+
if (element.type === Svg) {
|
|
303
|
+
return serializeSvg(element);
|
|
304
|
+
}
|
|
305
|
+
if (element.type === QrCode) {
|
|
306
|
+
return serializeQrCode(element);
|
|
307
|
+
}
|
|
308
|
+
if (element.type === Barcode) {
|
|
309
|
+
return serializeBarcode(element);
|
|
310
|
+
}
|
|
311
|
+
if (element.type === TextField) {
|
|
312
|
+
return serializeTextField(element);
|
|
313
|
+
}
|
|
314
|
+
if (element.type === Checkbox) {
|
|
315
|
+
return serializeCheckbox(element);
|
|
316
|
+
}
|
|
317
|
+
if (element.type === Dropdown) {
|
|
318
|
+
return serializeDropdown(element);
|
|
319
|
+
}
|
|
320
|
+
if (element.type === RadioButton) {
|
|
321
|
+
return serializeRadioButton(element);
|
|
322
|
+
}
|
|
323
|
+
if (element.type === Canvas) {
|
|
324
|
+
return serializeCanvas(element);
|
|
325
|
+
}
|
|
326
|
+
if (element.type === Watermark) {
|
|
327
|
+
return serializeWatermark(element);
|
|
328
|
+
}
|
|
329
|
+
if (element.type === BarChart) {
|
|
330
|
+
return serializeBarChart(element);
|
|
331
|
+
}
|
|
332
|
+
if (element.type === LineChart) {
|
|
333
|
+
return serializeLineChart(element);
|
|
334
|
+
}
|
|
335
|
+
if (element.type === PieChart) {
|
|
336
|
+
return serializePieChart(element);
|
|
337
|
+
}
|
|
338
|
+
if (element.type === AreaChart) {
|
|
339
|
+
return serializeAreaChart(element);
|
|
340
|
+
}
|
|
341
|
+
if (element.type === DotPlot) {
|
|
342
|
+
return serializeDotPlot(element);
|
|
343
|
+
}
|
|
344
|
+
if (element.type === PageBreak) {
|
|
345
|
+
return {
|
|
346
|
+
kind: { type: 'PageBreak' },
|
|
347
|
+
style: {},
|
|
348
|
+
children: [],
|
|
349
|
+
sourceLocation: extractSourceLocation(element),
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
if (element.type === Page) {
|
|
353
|
+
validateNesting('Page', parent);
|
|
354
|
+
return serializePage(element);
|
|
355
|
+
}
|
|
356
|
+
if (isDocumentType(element.type)) {
|
|
357
|
+
// Nested Document — just serialize its children
|
|
358
|
+
const props = element.props;
|
|
359
|
+
const childElements = flattenChildren(props.children);
|
|
360
|
+
const nodes = serializeChildren(childElements, parent);
|
|
361
|
+
return nodes.length === 1 ? nodes[0] : {
|
|
362
|
+
kind: { type: 'View' },
|
|
363
|
+
style: {},
|
|
364
|
+
children: nodes,
|
|
365
|
+
};
|
|
366
|
+
}
|
|
367
|
+
// Unknown component — try to call it as a function component
|
|
368
|
+
if (typeof element.type === 'function') {
|
|
369
|
+
const result = callComponent(element.type, element.props);
|
|
370
|
+
if (isValidElement(result)) {
|
|
371
|
+
return serializeChild(result, parent);
|
|
372
|
+
}
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
function serializeView(element, _parent = null) {
|
|
378
|
+
const props = element.props;
|
|
379
|
+
const style = mapStyle(props.style);
|
|
380
|
+
if (props.wrap !== undefined) {
|
|
381
|
+
style.wrap = props.wrap;
|
|
382
|
+
}
|
|
383
|
+
const childElements = flattenChildren(props.children);
|
|
384
|
+
const children = serializeChildren(childElements, 'View');
|
|
385
|
+
const node = {
|
|
386
|
+
kind: { type: 'View' },
|
|
387
|
+
style,
|
|
388
|
+
children,
|
|
389
|
+
sourceLocation: extractSourceLocation(element),
|
|
390
|
+
};
|
|
391
|
+
if (props.bookmark)
|
|
392
|
+
node.bookmark = props.bookmark;
|
|
393
|
+
if (props.href)
|
|
394
|
+
node.href = props.href;
|
|
395
|
+
return node;
|
|
396
|
+
}
|
|
397
|
+
/** Return the component-default style for a recognized inline component,
|
|
398
|
+
* or `null` if the element isn't a recognized inline component.
|
|
399
|
+
* `Text` returns `{}` because it carries no defaults beyond the user's style. */
|
|
400
|
+
function inlineDefaults(type) {
|
|
401
|
+
switch (type) {
|
|
402
|
+
case Text: return {};
|
|
403
|
+
case Strong: return STRONG_DEFAULTS;
|
|
404
|
+
case Em: return EM_DEFAULTS;
|
|
405
|
+
case Code: return CODE_DEFAULTS;
|
|
406
|
+
case Link: return LINK_DEFAULTS;
|
|
407
|
+
default: return null;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
/**
|
|
411
|
+
* Recursively flatten children of a `<Text>` into TextRuns, accumulating
|
|
412
|
+
* styles and href as we descend. Outer style is overlaid by each inline
|
|
413
|
+
* component's defaults, which are in turn overlaid by the user-supplied
|
|
414
|
+
* `style` on that element — so user style wins at every level while
|
|
415
|
+
* still inheriting whatever wasn't explicitly set.
|
|
416
|
+
*
|
|
417
|
+
* E.g. `<Strong><Em>both</Em></Strong>` produces a single run with
|
|
418
|
+
* `{ fontWeight: 700, fontStyle: 'italic' }`. A `<Link href>` nested in a
|
|
419
|
+
* `<Strong>` produces a bold underlined blue run with the href set.
|
|
420
|
+
*/
|
|
421
|
+
function buildTextRuns(children, accStyle = {}, accHref = undefined) {
|
|
422
|
+
const out = [];
|
|
423
|
+
const elements = flattenChildren(children);
|
|
424
|
+
for (const child of elements) {
|
|
425
|
+
if (child === null || child === undefined || typeof child === 'boolean')
|
|
426
|
+
continue;
|
|
427
|
+
if (typeof child === 'string' || typeof child === 'number') {
|
|
428
|
+
const content = String(child);
|
|
429
|
+
if (content === '')
|
|
430
|
+
continue;
|
|
431
|
+
const run = { content };
|
|
432
|
+
if (Object.keys(accStyle).length > 0)
|
|
433
|
+
run.style = mapStyle(accStyle);
|
|
434
|
+
if (accHref)
|
|
435
|
+
run.href = accHref;
|
|
436
|
+
out.push(run);
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
if (!isValidElement(child))
|
|
440
|
+
continue;
|
|
441
|
+
const defaults = inlineDefaults(child.type);
|
|
442
|
+
if (defaults === null)
|
|
443
|
+
continue; // unknown element inside <Text> — skip
|
|
444
|
+
const childProps = child.props;
|
|
445
|
+
// Cascade: outer accumulated → this component's defaults → user style.
|
|
446
|
+
// Later spreads win, so user style overrides defaults and outer.
|
|
447
|
+
const nextStyle = { ...accStyle, ...defaults, ...(childProps.style || {}) };
|
|
448
|
+
const nextHref = childProps.href ?? accHref;
|
|
449
|
+
out.push(...buildTextRuns(childProps.children, nextStyle, nextHref));
|
|
450
|
+
}
|
|
451
|
+
return out;
|
|
452
|
+
}
|
|
453
|
+
function serializeText(element) {
|
|
454
|
+
const props = element.props;
|
|
455
|
+
const childElements = flattenChildren(props.children);
|
|
456
|
+
// Detect mixed content: any inline element child (<Text>, <Strong>, <Em>,
|
|
457
|
+
// <Code>, <Link>) means we need TextRuns rather than a flat content string.
|
|
458
|
+
const hasInlineChild = childElements.some(c => isValidElement(c) && inlineDefaults(c.type) !== null);
|
|
459
|
+
const kind = { type: 'Text', content: '' };
|
|
460
|
+
if (hasInlineChild) {
|
|
461
|
+
kind.runs = buildTextRuns(props.children);
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
kind.content = flattenTextContent(props.children);
|
|
465
|
+
}
|
|
466
|
+
if (props.href)
|
|
467
|
+
kind.href = props.href;
|
|
468
|
+
const node = {
|
|
469
|
+
kind,
|
|
470
|
+
style: mapStyle(props.style),
|
|
471
|
+
children: [],
|
|
472
|
+
sourceLocation: extractSourceLocation(element),
|
|
473
|
+
};
|
|
474
|
+
if (props.bookmark)
|
|
475
|
+
node.bookmark = props.bookmark;
|
|
476
|
+
return node;
|
|
477
|
+
}
|
|
478
|
+
/**
|
|
479
|
+
* Serialize an H1-H6 element. Mirrors serializeText for the children
|
|
480
|
+
* machinery (mixed strings + inline-formatting components produce runs)
|
|
481
|
+
* but emits the engine's `Heading { level }` node kind so the tagged-PDF
|
|
482
|
+
* builder can pick up the semantic role. Default styles per level are
|
|
483
|
+
* merged BEFORE the user's `style` prop, so user values win.
|
|
484
|
+
*/
|
|
485
|
+
function serializeHeading(element, level) {
|
|
486
|
+
const props = element.props;
|
|
487
|
+
const childElements = flattenChildren(props.children);
|
|
488
|
+
const hasInlineChild = childElements.some((c) => isValidElement(c) && inlineDefaults(c.type) !== null);
|
|
489
|
+
const kind = {
|
|
490
|
+
type: 'Heading',
|
|
491
|
+
level,
|
|
492
|
+
content: '',
|
|
493
|
+
};
|
|
494
|
+
if (hasInlineChild) {
|
|
495
|
+
kind.runs = buildTextRuns(props.children);
|
|
496
|
+
}
|
|
497
|
+
else {
|
|
498
|
+
kind.content = flattenTextContent(props.children);
|
|
499
|
+
}
|
|
500
|
+
if (props.href)
|
|
501
|
+
kind.href = props.href;
|
|
502
|
+
// Defaults underlay; user style wins on conflicting keys.
|
|
503
|
+
const mergedStyle = { ...HEADING_DEFAULTS[level], ...(props.style || {}) };
|
|
504
|
+
const node = {
|
|
505
|
+
kind,
|
|
506
|
+
style: mapStyle(mergedStyle),
|
|
507
|
+
children: [],
|
|
508
|
+
sourceLocation: extractSourceLocation(element),
|
|
509
|
+
};
|
|
510
|
+
if (props.bookmark)
|
|
511
|
+
node.bookmark = props.bookmark;
|
|
512
|
+
return node;
|
|
513
|
+
}
|
|
514
|
+
function serializeList(element, ordered) {
|
|
515
|
+
const props = element.props;
|
|
516
|
+
const markerType = ordered
|
|
517
|
+
? mapListMarker(props.type, 'decimal')
|
|
518
|
+
: mapListMarker(props.marker, 'disc');
|
|
519
|
+
const start = typeof props.start === 'number' && props.start >= 1 ? props.start : 1;
|
|
520
|
+
// Children must be ListItems — anything else is silently dropped to
|
|
521
|
+
// keep the serializer tolerant. (We don't throw on stray content because
|
|
522
|
+
// a Fragment / null child in JSX is too common to be a real error.)
|
|
523
|
+
const childElements = flattenChildren(props.children).filter((c) => isValidElement(c) && c.type === ListItem);
|
|
524
|
+
const children = childElements.map((c) => serializeListItem(c));
|
|
525
|
+
const node = {
|
|
526
|
+
kind: { type: 'List', ordered, marker_type: markerType, start },
|
|
527
|
+
style: mapStyle(props.style),
|
|
528
|
+
children,
|
|
529
|
+
sourceLocation: extractSourceLocation(element),
|
|
530
|
+
};
|
|
531
|
+
if (props.bookmark)
|
|
532
|
+
node.bookmark = props.bookmark;
|
|
533
|
+
return node;
|
|
534
|
+
}
|
|
535
|
+
function serializeListItem(element) {
|
|
536
|
+
const props = element.props;
|
|
537
|
+
// ListItem content is layed out by the engine using layout_children, so
|
|
538
|
+
// we serialize whatever the user put inside as the node's children. This
|
|
539
|
+
// covers plain strings, JSX text, nested lists, formatted runs via
|
|
540
|
+
// <Text>, etc.
|
|
541
|
+
const rawChildren = flattenChildren(props.children);
|
|
542
|
+
const children = [];
|
|
543
|
+
for (const c of rawChildren) {
|
|
544
|
+
if (typeof c === 'string' || typeof c === 'number') {
|
|
545
|
+
// Auto-wrap raw strings in a Text node so the engine has something
|
|
546
|
+
// concrete to render. Matches the convention everywhere else.
|
|
547
|
+
children.push({
|
|
548
|
+
kind: { type: 'Text', content: String(c) },
|
|
549
|
+
style: {},
|
|
550
|
+
children: [],
|
|
551
|
+
});
|
|
552
|
+
}
|
|
553
|
+
else if (isValidElement(c)) {
|
|
554
|
+
const node = serializeChild(c, null);
|
|
555
|
+
if (node)
|
|
556
|
+
children.push(node);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return {
|
|
560
|
+
kind: { type: 'ListItem' },
|
|
561
|
+
style: mapStyle(props.style),
|
|
562
|
+
children,
|
|
563
|
+
sourceLocation: extractSourceLocation(element),
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
function serializeImage(element) {
|
|
567
|
+
const props = element.props;
|
|
568
|
+
const kind = { type: 'Image', src: props.src };
|
|
569
|
+
if (props.width !== undefined)
|
|
570
|
+
kind.width = props.width;
|
|
571
|
+
if (props.height !== undefined)
|
|
572
|
+
kind.height = props.height;
|
|
573
|
+
const node = {
|
|
574
|
+
kind,
|
|
575
|
+
style: mapStyle(props.style),
|
|
576
|
+
children: [],
|
|
577
|
+
sourceLocation: extractSourceLocation(element),
|
|
578
|
+
};
|
|
579
|
+
if (props.href)
|
|
580
|
+
node.href = props.href;
|
|
581
|
+
if (props.alt)
|
|
582
|
+
node.alt = props.alt;
|
|
583
|
+
return node;
|
|
584
|
+
}
|
|
585
|
+
function serializeTable(element, _parent = null) {
|
|
586
|
+
const props = element.props;
|
|
587
|
+
const columns = (props.columns ?? []).map(col => ({
|
|
588
|
+
width: mapColumnWidth(col.width),
|
|
589
|
+
}));
|
|
590
|
+
const childElements = flattenChildren(props.children);
|
|
591
|
+
const children = serializeChildren(childElements, 'Table');
|
|
592
|
+
return {
|
|
593
|
+
kind: { type: 'Table', columns },
|
|
594
|
+
style: mapStyle(props.style),
|
|
595
|
+
children,
|
|
596
|
+
sourceLocation: extractSourceLocation(element),
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
function serializeRow(element) {
|
|
600
|
+
const props = element.props;
|
|
601
|
+
const childElements = flattenChildren(props.children);
|
|
602
|
+
const children = serializeChildren(childElements, 'Row');
|
|
603
|
+
return {
|
|
604
|
+
kind: { type: 'TableRow', is_header: props.header ?? false },
|
|
605
|
+
style: mapStyle(props.style),
|
|
606
|
+
children,
|
|
607
|
+
sourceLocation: extractSourceLocation(element),
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
function serializeCell(element) {
|
|
611
|
+
const props = element.props;
|
|
612
|
+
const childElements = flattenChildren(props.children);
|
|
613
|
+
const children = serializeChildren(childElements, 'Cell');
|
|
614
|
+
return {
|
|
615
|
+
kind: { type: 'TableCell', col_span: props.colSpan ?? 1, row_span: props.rowSpan ?? 1 },
|
|
616
|
+
style: mapStyle(props.style),
|
|
617
|
+
children,
|
|
618
|
+
sourceLocation: extractSourceLocation(element),
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
function serializeFixed(element) {
|
|
622
|
+
const props = element.props;
|
|
623
|
+
const position = props.position === 'header' ? 'Header' : 'Footer';
|
|
624
|
+
const childElements = flattenChildren(props.children);
|
|
625
|
+
const children = serializeChildren(childElements, 'Fixed');
|
|
626
|
+
const node = {
|
|
627
|
+
kind: { type: 'Fixed', position },
|
|
628
|
+
style: mapStyle(props.style),
|
|
629
|
+
children,
|
|
630
|
+
sourceLocation: extractSourceLocation(element),
|
|
631
|
+
};
|
|
632
|
+
if (props.bookmark)
|
|
633
|
+
node.bookmark = props.bookmark;
|
|
634
|
+
return node;
|
|
635
|
+
}
|
|
636
|
+
function serializeSvg(element) {
|
|
637
|
+
const props = element.props;
|
|
638
|
+
const content = props.content ?? (props.children ? svgChildrenToString(props.children) : '');
|
|
639
|
+
const kind = {
|
|
640
|
+
type: 'Svg',
|
|
641
|
+
width: props.width,
|
|
642
|
+
height: props.height,
|
|
643
|
+
content,
|
|
644
|
+
};
|
|
645
|
+
if (props.viewBox)
|
|
646
|
+
kind.view_box = props.viewBox;
|
|
647
|
+
const node = {
|
|
648
|
+
kind,
|
|
649
|
+
style: mapStyle(props.style),
|
|
650
|
+
children: [],
|
|
651
|
+
sourceLocation: extractSourceLocation(element),
|
|
652
|
+
};
|
|
653
|
+
if (props.href)
|
|
654
|
+
node.href = props.href;
|
|
655
|
+
if (props.alt)
|
|
656
|
+
node.alt = props.alt;
|
|
657
|
+
return node;
|
|
658
|
+
}
|
|
659
|
+
/** Map camelCase SVG prop names to kebab-case attribute names. */
|
|
660
|
+
const svgCamelToKebab = {
|
|
661
|
+
strokeWidth: 'stroke-width',
|
|
662
|
+
strokeLinecap: 'stroke-linecap',
|
|
663
|
+
strokeLinejoin: 'stroke-linejoin',
|
|
664
|
+
strokeMiterlimit: 'stroke-miterlimit',
|
|
665
|
+
strokeDasharray: 'stroke-dasharray',
|
|
666
|
+
strokeDashoffset: 'stroke-dashoffset',
|
|
667
|
+
strokeOpacity: 'stroke-opacity',
|
|
668
|
+
fillOpacity: 'fill-opacity',
|
|
669
|
+
fillRule: 'fill-rule',
|
|
670
|
+
clipPath: 'clip-path',
|
|
671
|
+
clipRule: 'clip-rule',
|
|
672
|
+
};
|
|
673
|
+
function escapeXmlAttr(s) {
|
|
674
|
+
return s.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
675
|
+
}
|
|
676
|
+
function svgChildrenToString(children) {
|
|
677
|
+
let result = '';
|
|
678
|
+
Children.forEach(children, (child) => {
|
|
679
|
+
if (typeof child === 'string') {
|
|
680
|
+
result += child.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
if (!isValidElement(child))
|
|
684
|
+
return;
|
|
685
|
+
const tag = typeof child.type === 'string' ? child.type : null;
|
|
686
|
+
if (!tag)
|
|
687
|
+
return;
|
|
688
|
+
const { children: nested, ...attrs } = child.props;
|
|
689
|
+
const attrStr = Object.entries(attrs)
|
|
690
|
+
.filter(([, v]) => v !== undefined && v !== null)
|
|
691
|
+
.map(([k, v]) => {
|
|
692
|
+
const name = svgCamelToKebab[k] ?? k;
|
|
693
|
+
return `${name}="${escapeXmlAttr(String(v))}"`;
|
|
694
|
+
})
|
|
695
|
+
.join(' ');
|
|
696
|
+
const open = attrStr ? `<${tag} ${attrStr}` : `<${tag}`;
|
|
697
|
+
if (nested) {
|
|
698
|
+
result += `${open}>${svgChildrenToString(nested)}</${tag}>`;
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
result += `${open}/>`;
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
return result;
|
|
705
|
+
}
|
|
706
|
+
function serializeQrCode(element) {
|
|
707
|
+
const props = element.props;
|
|
708
|
+
const kind = { type: 'QrCode', data: props.data };
|
|
709
|
+
if (props.size !== undefined)
|
|
710
|
+
kind.size = props.size;
|
|
711
|
+
const style = mapStyle(props.style);
|
|
712
|
+
if (props.color)
|
|
713
|
+
style.color = parseColor(props.color);
|
|
714
|
+
return {
|
|
715
|
+
kind,
|
|
716
|
+
style,
|
|
717
|
+
children: [],
|
|
718
|
+
sourceLocation: extractSourceLocation(element),
|
|
719
|
+
};
|
|
720
|
+
}
|
|
721
|
+
function serializeBarcode(element) {
|
|
722
|
+
const props = element.props;
|
|
723
|
+
const kind = {
|
|
724
|
+
type: 'Barcode',
|
|
725
|
+
data: props.data,
|
|
726
|
+
format: props.format ?? 'Code128',
|
|
727
|
+
height: props.height ?? 60,
|
|
728
|
+
};
|
|
729
|
+
if (props.width !== undefined)
|
|
730
|
+
kind.width = props.width;
|
|
731
|
+
const style = mapStyle(props.style);
|
|
732
|
+
if (props.color)
|
|
733
|
+
style.color = parseColor(props.color);
|
|
734
|
+
return {
|
|
735
|
+
kind,
|
|
736
|
+
style,
|
|
737
|
+
children: [],
|
|
738
|
+
sourceLocation: extractSourceLocation(element),
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
function serializeTextField(element) {
|
|
742
|
+
const props = element.props;
|
|
743
|
+
const kind = {
|
|
744
|
+
type: 'TextField',
|
|
745
|
+
name: props.name,
|
|
746
|
+
width: props.width,
|
|
747
|
+
height: props.height ?? 24,
|
|
748
|
+
multiline: props.multiline ?? false,
|
|
749
|
+
password: props.password ?? false,
|
|
750
|
+
read_only: props.readOnly ?? false,
|
|
751
|
+
font_size: props.fontSize ?? 12,
|
|
752
|
+
};
|
|
753
|
+
if (props.value !== undefined)
|
|
754
|
+
kind.value = props.value;
|
|
755
|
+
if (props.placeholder !== undefined)
|
|
756
|
+
kind.placeholder = props.placeholder;
|
|
757
|
+
if (props.maxLength !== undefined)
|
|
758
|
+
kind.max_length = props.maxLength;
|
|
759
|
+
return {
|
|
760
|
+
kind,
|
|
761
|
+
style: mapStyle(props.style),
|
|
762
|
+
children: [],
|
|
763
|
+
sourceLocation: extractSourceLocation(element),
|
|
764
|
+
};
|
|
765
|
+
}
|
|
766
|
+
function serializeCheckbox(element) {
|
|
767
|
+
const props = element.props;
|
|
768
|
+
const kind = {
|
|
769
|
+
type: 'Checkbox',
|
|
770
|
+
name: props.name,
|
|
771
|
+
checked: props.checked ?? false,
|
|
772
|
+
width: props.width ?? 14,
|
|
773
|
+
height: props.height ?? 14,
|
|
774
|
+
read_only: props.readOnly ?? false,
|
|
775
|
+
};
|
|
776
|
+
return {
|
|
777
|
+
kind,
|
|
778
|
+
style: mapStyle(props.style),
|
|
779
|
+
children: [],
|
|
780
|
+
sourceLocation: extractSourceLocation(element),
|
|
781
|
+
};
|
|
782
|
+
}
|
|
783
|
+
function serializeDropdown(element) {
|
|
784
|
+
const props = element.props;
|
|
785
|
+
const kind = {
|
|
786
|
+
type: 'Dropdown',
|
|
787
|
+
name: props.name,
|
|
788
|
+
options: props.options,
|
|
789
|
+
width: props.width,
|
|
790
|
+
height: props.height ?? 24,
|
|
791
|
+
read_only: props.readOnly ?? false,
|
|
792
|
+
font_size: props.fontSize ?? 12,
|
|
793
|
+
};
|
|
794
|
+
if (props.value !== undefined)
|
|
795
|
+
kind.value = props.value;
|
|
796
|
+
return {
|
|
797
|
+
kind,
|
|
798
|
+
style: mapStyle(props.style),
|
|
799
|
+
children: [],
|
|
800
|
+
sourceLocation: extractSourceLocation(element),
|
|
801
|
+
};
|
|
802
|
+
}
|
|
803
|
+
function serializeRadioButton(element) {
|
|
804
|
+
const props = element.props;
|
|
805
|
+
const kind = {
|
|
806
|
+
type: 'RadioButton',
|
|
807
|
+
name: props.name,
|
|
808
|
+
value: props.value,
|
|
809
|
+
checked: props.checked ?? false,
|
|
810
|
+
width: props.width ?? 14,
|
|
811
|
+
height: props.height ?? 14,
|
|
812
|
+
read_only: props.readOnly ?? false,
|
|
813
|
+
};
|
|
814
|
+
return {
|
|
815
|
+
kind,
|
|
816
|
+
style: mapStyle(props.style),
|
|
817
|
+
children: [],
|
|
818
|
+
sourceLocation: extractSourceLocation(element),
|
|
819
|
+
};
|
|
820
|
+
}
|
|
821
|
+
function serializeCanvas(element) {
|
|
822
|
+
const props = element.props;
|
|
823
|
+
const operations = recordCanvasOperations(props.draw);
|
|
824
|
+
return {
|
|
825
|
+
kind: { type: 'Canvas', width: props.width, height: props.height, operations },
|
|
826
|
+
style: mapStyle(props.style),
|
|
827
|
+
children: [],
|
|
828
|
+
sourceLocation: extractSourceLocation(element),
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
function serializeWatermark(element) {
|
|
832
|
+
const props = element.props;
|
|
833
|
+
const fontSize = props.fontSize ?? 60;
|
|
834
|
+
const angle = props.angle ?? -45;
|
|
835
|
+
const colorStr = props.color ?? 'rgba(0,0,0,0.1)';
|
|
836
|
+
// Parse color — extract alpha for opacity
|
|
837
|
+
const parsedColor = parseColor(colorStr);
|
|
838
|
+
const style = mapStyle(props.style);
|
|
839
|
+
style.color = { r: parsedColor.r, g: parsedColor.g, b: parsedColor.b, a: 1 };
|
|
840
|
+
// Multiply color alpha with any existing opacity
|
|
841
|
+
const colorOpacity = parsedColor.a;
|
|
842
|
+
const styleOpacity = (style.opacity !== undefined) ? style.opacity : 1;
|
|
843
|
+
style.opacity = colorOpacity * styleOpacity;
|
|
844
|
+
style.fontSize = fontSize;
|
|
845
|
+
return {
|
|
846
|
+
kind: { type: 'Watermark', text: props.text, font_size: fontSize, angle },
|
|
847
|
+
style,
|
|
848
|
+
children: [],
|
|
849
|
+
sourceLocation: extractSourceLocation(element),
|
|
850
|
+
};
|
|
851
|
+
}
|
|
852
|
+
function serializeBarChart(element) {
|
|
853
|
+
const props = element.props;
|
|
854
|
+
return {
|
|
855
|
+
kind: buildBarChartKind(props),
|
|
856
|
+
style: mapStyle(props.style),
|
|
857
|
+
children: [],
|
|
858
|
+
sourceLocation: extractSourceLocation(element),
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
function serializeLineChart(element) {
|
|
862
|
+
const props = element.props;
|
|
863
|
+
return {
|
|
864
|
+
kind: buildLineChartKind(props),
|
|
865
|
+
style: mapStyle(props.style),
|
|
866
|
+
children: [],
|
|
867
|
+
sourceLocation: extractSourceLocation(element),
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
function serializePieChart(element) {
|
|
871
|
+
const props = element.props;
|
|
872
|
+
return {
|
|
873
|
+
kind: buildPieChartKind(props),
|
|
874
|
+
style: mapStyle(props.style),
|
|
875
|
+
children: [],
|
|
876
|
+
sourceLocation: extractSourceLocation(element),
|
|
877
|
+
};
|
|
878
|
+
}
|
|
879
|
+
function serializeAreaChart(element) {
|
|
880
|
+
const props = element.props;
|
|
881
|
+
return {
|
|
882
|
+
kind: buildAreaChartKind(props),
|
|
883
|
+
style: mapStyle(props.style),
|
|
884
|
+
children: [],
|
|
885
|
+
sourceLocation: extractSourceLocation(element),
|
|
886
|
+
};
|
|
887
|
+
}
|
|
888
|
+
function serializeDotPlot(element) {
|
|
889
|
+
const props = element.props;
|
|
890
|
+
return {
|
|
891
|
+
kind: buildDotPlotKind(props),
|
|
892
|
+
style: mapStyle(props.style),
|
|
893
|
+
children: [],
|
|
894
|
+
sourceLocation: extractSourceLocation(element),
|
|
895
|
+
};
|
|
896
|
+
}
|
|
897
|
+
// ─── Children helpers ────────────────────────────────────────────────
|
|
898
|
+
function flattenChildren(children) {
|
|
899
|
+
const result = [];
|
|
900
|
+
Children.forEach(children, child => {
|
|
901
|
+
if (Array.isArray(child)) {
|
|
902
|
+
result.push(...child.flatMap(c => flattenChildren(c)));
|
|
903
|
+
}
|
|
904
|
+
else if (isValidElement(child) && child.type === Fragment) {
|
|
905
|
+
const fragProps = child.props;
|
|
906
|
+
result.push(...flattenChildren(fragProps.children));
|
|
907
|
+
}
|
|
908
|
+
else {
|
|
909
|
+
result.push(child);
|
|
910
|
+
}
|
|
911
|
+
});
|
|
912
|
+
return result;
|
|
913
|
+
}
|
|
914
|
+
function serializeChildren(children, parent = null) {
|
|
915
|
+
const nodes = [];
|
|
916
|
+
for (const child of children) {
|
|
917
|
+
const node = serializeChild(child, parent);
|
|
918
|
+
if (node)
|
|
919
|
+
nodes.push(node);
|
|
920
|
+
}
|
|
921
|
+
return nodes;
|
|
922
|
+
}
|
|
923
|
+
/**
|
|
924
|
+
* Flatten all text content within a <Text> element to a single string.
|
|
925
|
+
* Nested <Text> children have their content extracted and concatenated.
|
|
926
|
+
*/
|
|
927
|
+
function flattenTextContent(children) {
|
|
928
|
+
if (children === null || children === undefined)
|
|
929
|
+
return '';
|
|
930
|
+
if (typeof children === 'string')
|
|
931
|
+
return children;
|
|
932
|
+
if (typeof children === 'number')
|
|
933
|
+
return String(children);
|
|
934
|
+
if (typeof children === 'boolean')
|
|
935
|
+
return '';
|
|
936
|
+
if (Array.isArray(children)) {
|
|
937
|
+
return children.map(c => flattenTextContent(c)).join('');
|
|
938
|
+
}
|
|
939
|
+
if (isValidElement(children)) {
|
|
940
|
+
const element = children;
|
|
941
|
+
if (element.type === Text) {
|
|
942
|
+
const props = element.props;
|
|
943
|
+
return flattenTextContent(props.children);
|
|
944
|
+
}
|
|
945
|
+
// For other elements inside Text, try to extract text content
|
|
946
|
+
const props = element.props;
|
|
947
|
+
return flattenTextContent(props.children);
|
|
948
|
+
}
|
|
949
|
+
// React.Children.toArray for iterables
|
|
950
|
+
const arr = [];
|
|
951
|
+
Children.forEach(children, c => arr.push(c));
|
|
952
|
+
if (arr.length > 0) {
|
|
953
|
+
return arr.map(c => flattenTextContent(c)).join('');
|
|
954
|
+
}
|
|
955
|
+
return String(children);
|
|
956
|
+
}
|
|
957
|
+
// ─── Template serialization ─────────────────────────────────────────
|
|
958
|
+
//
|
|
959
|
+
// Parallel to `serialize()` but detects proxy markers and expr markers,
|
|
960
|
+
// converting them to `$ref`, `$each`, `$if`, and operator nodes.
|
|
961
|
+
/**
|
|
962
|
+
* Serialize a React element tree into a Forme template JSON document.
|
|
963
|
+
* Like `serialize()` but with expression marker detection for template compilation.
|
|
964
|
+
*/
|
|
965
|
+
export function serializeTemplate(element) {
|
|
966
|
+
element = resolveElement(element);
|
|
967
|
+
if (!isDocumentType(element.type)) {
|
|
968
|
+
throw new Error('Top-level element must be <Document>');
|
|
969
|
+
}
|
|
970
|
+
const props = element.props;
|
|
971
|
+
const childElements = flattenTemplateChildren(props.children);
|
|
972
|
+
const pageNodes = [];
|
|
973
|
+
const contentNodes = [];
|
|
974
|
+
for (const child of childElements) {
|
|
975
|
+
if (isValidElement(child) && child.type === Page) {
|
|
976
|
+
pageNodes.push(serializeTemplatePage(child));
|
|
977
|
+
}
|
|
978
|
+
else {
|
|
979
|
+
const node = serializeTemplateChild(child, 'Document');
|
|
980
|
+
if (node !== null)
|
|
981
|
+
contentNodes.push(node);
|
|
982
|
+
}
|
|
983
|
+
}
|
|
984
|
+
let children;
|
|
985
|
+
if (pageNodes.length > 0) {
|
|
986
|
+
if (contentNodes.length > 0) {
|
|
987
|
+
const lastPage = pageNodes[pageNodes.length - 1];
|
|
988
|
+
lastPage.children.push(...contentNodes);
|
|
989
|
+
}
|
|
990
|
+
children = pageNodes;
|
|
991
|
+
}
|
|
992
|
+
else if (contentNodes.length > 0) {
|
|
993
|
+
children = contentNodes;
|
|
994
|
+
}
|
|
995
|
+
else {
|
|
996
|
+
children = [];
|
|
997
|
+
}
|
|
998
|
+
const metadata = {};
|
|
999
|
+
if (props.title !== undefined)
|
|
1000
|
+
metadata.title = processTemplateValue(props.title);
|
|
1001
|
+
if (props.author !== undefined)
|
|
1002
|
+
metadata.author = processTemplateValue(props.author);
|
|
1003
|
+
if (props.subject !== undefined)
|
|
1004
|
+
metadata.subject = processTemplateValue(props.subject);
|
|
1005
|
+
if (props.creator !== undefined)
|
|
1006
|
+
metadata.creator = processTemplateValue(props.creator);
|
|
1007
|
+
if (props.lang !== undefined)
|
|
1008
|
+
metadata.lang = processTemplateValue(props.lang);
|
|
1009
|
+
const mergedFonts = mergeFonts(Font.getRegistered(), props.fonts);
|
|
1010
|
+
const result = {
|
|
1011
|
+
children,
|
|
1012
|
+
metadata,
|
|
1013
|
+
defaultPage: {
|
|
1014
|
+
size: 'A4',
|
|
1015
|
+
margin: { top: 54, right: 54, bottom: 54, left: 54 },
|
|
1016
|
+
wrap: true,
|
|
1017
|
+
},
|
|
1018
|
+
};
|
|
1019
|
+
if (props.style)
|
|
1020
|
+
result.defaultStyle = mapStyle(props.style);
|
|
1021
|
+
if (props.tagged !== undefined)
|
|
1022
|
+
result.tagged = props.tagged;
|
|
1023
|
+
if (props.pdfa !== undefined)
|
|
1024
|
+
result.pdfa = props.pdfa;
|
|
1025
|
+
if (props.pdfUa)
|
|
1026
|
+
result.pdfUa = true;
|
|
1027
|
+
const cert = props.certification ?? props.signature;
|
|
1028
|
+
if (cert) {
|
|
1029
|
+
if (props.signature && !props.certification) {
|
|
1030
|
+
console.warn('[Forme] The `signature` prop is deprecated. Use `certification` instead.');
|
|
1031
|
+
}
|
|
1032
|
+
result.certification = cert;
|
|
1033
|
+
}
|
|
1034
|
+
if (mergedFonts.length > 0) {
|
|
1035
|
+
result.fonts = mergedFonts;
|
|
1036
|
+
}
|
|
1037
|
+
return result;
|
|
1038
|
+
}
|
|
1039
|
+
function serializeTemplatePage(element) {
|
|
1040
|
+
const props = element.props;
|
|
1041
|
+
let size = 'A4';
|
|
1042
|
+
if (props.size !== undefined) {
|
|
1043
|
+
if (typeof props.size === 'string') {
|
|
1044
|
+
size = props.size;
|
|
1045
|
+
}
|
|
1046
|
+
else {
|
|
1047
|
+
size = { Custom: { width: props.size.width, height: props.size.height } };
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
let margin = { top: 54, right: 54, bottom: 54, left: 54 };
|
|
1051
|
+
if (props.margin !== undefined) {
|
|
1052
|
+
margin = expandEdges(props.margin);
|
|
1053
|
+
}
|
|
1054
|
+
const config = { size, margin, wrap: true };
|
|
1055
|
+
const childElements = flattenTemplateChildren(props.children);
|
|
1056
|
+
const children = serializeTemplateChildren(childElements, 'Page');
|
|
1057
|
+
return {
|
|
1058
|
+
kind: { type: 'Page', config },
|
|
1059
|
+
style: {},
|
|
1060
|
+
children,
|
|
1061
|
+
};
|
|
1062
|
+
}
|
|
1063
|
+
function serializeTemplateChild(child, parent = null) {
|
|
1064
|
+
if (child === null || child === undefined || typeof child === 'boolean') {
|
|
1065
|
+
return null;
|
|
1066
|
+
}
|
|
1067
|
+
// Check for each marker (from .map() on proxy)
|
|
1068
|
+
if (isEachMarker(child)) {
|
|
1069
|
+
const path = getEachPath(child);
|
|
1070
|
+
const template = getEachTemplate(child);
|
|
1071
|
+
// The template is the JSX element returned from the .map() callback
|
|
1072
|
+
const serializedTemplate = isValidElement(template)
|
|
1073
|
+
? serializeTemplateChild(template, parent)
|
|
1074
|
+
: processTemplateValue(template);
|
|
1075
|
+
return {
|
|
1076
|
+
$each: { $ref: path },
|
|
1077
|
+
as: '$item',
|
|
1078
|
+
template: serializedTemplate,
|
|
1079
|
+
};
|
|
1080
|
+
}
|
|
1081
|
+
// Check for expr marker
|
|
1082
|
+
if (isExprMarker(child)) {
|
|
1083
|
+
return serializeExprValues(getExpr(child), parent);
|
|
1084
|
+
}
|
|
1085
|
+
// Check for ref sentinel strings
|
|
1086
|
+
if (typeof child === 'string') {
|
|
1087
|
+
const processed = processTemplateString(child);
|
|
1088
|
+
if (processed !== null)
|
|
1089
|
+
return processed;
|
|
1090
|
+
return {
|
|
1091
|
+
kind: { type: 'Text', content: child },
|
|
1092
|
+
style: {},
|
|
1093
|
+
children: [],
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
1096
|
+
if (typeof child === 'number') {
|
|
1097
|
+
return {
|
|
1098
|
+
kind: { type: 'Text', content: String(child) },
|
|
1099
|
+
style: {},
|
|
1100
|
+
children: [],
|
|
1101
|
+
};
|
|
1102
|
+
}
|
|
1103
|
+
if (!isValidElement(child))
|
|
1104
|
+
return null;
|
|
1105
|
+
const element = child;
|
|
1106
|
+
if (element.type === View)
|
|
1107
|
+
return serializeTemplateView(element, parent);
|
|
1108
|
+
if (element.type === Text)
|
|
1109
|
+
return serializeTemplateText(element);
|
|
1110
|
+
if (element.type === Image)
|
|
1111
|
+
return serializeTemplateImage(element);
|
|
1112
|
+
if (element.type === Table)
|
|
1113
|
+
return serializeTemplateTable(element, parent);
|
|
1114
|
+
if (element.type === Row) {
|
|
1115
|
+
validateNesting('Row', parent);
|
|
1116
|
+
return serializeTemplateRow(element);
|
|
1117
|
+
}
|
|
1118
|
+
if (element.type === Cell) {
|
|
1119
|
+
validateNesting('Cell', parent);
|
|
1120
|
+
return serializeTemplateCell(element);
|
|
1121
|
+
}
|
|
1122
|
+
if (element.type === Fixed)
|
|
1123
|
+
return serializeTemplateFixed(element);
|
|
1124
|
+
if (element.type === Svg)
|
|
1125
|
+
return serializeSvg(element);
|
|
1126
|
+
if (element.type === QrCode)
|
|
1127
|
+
return serializeQrCode(element);
|
|
1128
|
+
if (element.type === Barcode)
|
|
1129
|
+
return serializeBarcode(element);
|
|
1130
|
+
if (element.type === TextField)
|
|
1131
|
+
return serializeTextField(element);
|
|
1132
|
+
if (element.type === Checkbox)
|
|
1133
|
+
return serializeCheckbox(element);
|
|
1134
|
+
if (element.type === Dropdown)
|
|
1135
|
+
return serializeDropdown(element);
|
|
1136
|
+
if (element.type === RadioButton)
|
|
1137
|
+
return serializeRadioButton(element);
|
|
1138
|
+
if (element.type === Canvas)
|
|
1139
|
+
return serializeCanvas(element);
|
|
1140
|
+
if (element.type === Watermark)
|
|
1141
|
+
return serializeWatermark(element);
|
|
1142
|
+
if (element.type === BarChart)
|
|
1143
|
+
return serializeBarChart(element);
|
|
1144
|
+
if (element.type === LineChart)
|
|
1145
|
+
return serializeLineChart(element);
|
|
1146
|
+
if (element.type === PieChart)
|
|
1147
|
+
return serializePieChart(element);
|
|
1148
|
+
if (element.type === AreaChart)
|
|
1149
|
+
return serializeAreaChart(element);
|
|
1150
|
+
if (element.type === DotPlot)
|
|
1151
|
+
return serializeDotPlot(element);
|
|
1152
|
+
if (element.type === PageBreak) {
|
|
1153
|
+
return { kind: { type: 'PageBreak' }, style: {}, children: [] };
|
|
1154
|
+
}
|
|
1155
|
+
if (element.type === Page) {
|
|
1156
|
+
validateNesting('Page', parent);
|
|
1157
|
+
return serializeTemplatePage(element);
|
|
1158
|
+
}
|
|
1159
|
+
// Unknown function component — call it
|
|
1160
|
+
if (typeof element.type === 'function') {
|
|
1161
|
+
const result = callComponent(element.type, element.props);
|
|
1162
|
+
if (isValidElement(result)) {
|
|
1163
|
+
return serializeTemplateChild(result, parent);
|
|
1164
|
+
}
|
|
1165
|
+
return null;
|
|
1166
|
+
}
|
|
1167
|
+
return null;
|
|
1168
|
+
}
|
|
1169
|
+
function serializeTemplateView(element, _parent = null) {
|
|
1170
|
+
const props = element.props;
|
|
1171
|
+
const style = mapTemplateStyle(props.style);
|
|
1172
|
+
if (props.wrap !== undefined)
|
|
1173
|
+
style.wrap = props.wrap;
|
|
1174
|
+
const childElements = flattenTemplateChildren(props.children);
|
|
1175
|
+
const children = serializeTemplateChildren(childElements, 'View');
|
|
1176
|
+
const node = { kind: { type: 'View' }, style, children };
|
|
1177
|
+
if (props.bookmark)
|
|
1178
|
+
node.bookmark = props.bookmark;
|
|
1179
|
+
if (props.href)
|
|
1180
|
+
node.href = props.href;
|
|
1181
|
+
return node;
|
|
1182
|
+
}
|
|
1183
|
+
function serializeTemplateText(element) {
|
|
1184
|
+
const props = element.props;
|
|
1185
|
+
const childElements = flattenTemplateChildren(props.children);
|
|
1186
|
+
const hasTextChild = childElements.some(c => isValidElement(c) && c.type === Text);
|
|
1187
|
+
const kind = { type: 'Text', content: '' };
|
|
1188
|
+
if (hasTextChild) {
|
|
1189
|
+
const runs = [];
|
|
1190
|
+
for (const child of childElements) {
|
|
1191
|
+
if (typeof child === 'string' || typeof child === 'number') {
|
|
1192
|
+
const processed = typeof child === 'string' ? processTemplateString(child) : null;
|
|
1193
|
+
if (processed !== null) {
|
|
1194
|
+
runs.push({ content: processed });
|
|
1195
|
+
}
|
|
1196
|
+
else {
|
|
1197
|
+
runs.push({ content: String(child) });
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
else if (isValidElement(child) && child.type === Text) {
|
|
1201
|
+
const childProps = child.props;
|
|
1202
|
+
const run = {
|
|
1203
|
+
content: flattenTemplateTextContent(childProps.children),
|
|
1204
|
+
};
|
|
1205
|
+
if (childProps.style)
|
|
1206
|
+
run.style = mapTemplateStyle(childProps.style);
|
|
1207
|
+
if (childProps.href)
|
|
1208
|
+
run.href = childProps.href;
|
|
1209
|
+
runs.push(run);
|
|
1210
|
+
}
|
|
1211
|
+
}
|
|
1212
|
+
kind.runs = runs;
|
|
1213
|
+
}
|
|
1214
|
+
else {
|
|
1215
|
+
kind.content = flattenTemplateTextContent(props.children);
|
|
1216
|
+
}
|
|
1217
|
+
if (props.href)
|
|
1218
|
+
kind.href = props.href;
|
|
1219
|
+
const node = {
|
|
1220
|
+
kind,
|
|
1221
|
+
style: mapTemplateStyle(props.style),
|
|
1222
|
+
children: [],
|
|
1223
|
+
};
|
|
1224
|
+
if (props.bookmark)
|
|
1225
|
+
node.bookmark = props.bookmark;
|
|
1226
|
+
return node;
|
|
1227
|
+
}
|
|
1228
|
+
function serializeTemplateImage(element) {
|
|
1229
|
+
const props = element.props;
|
|
1230
|
+
const kind = { type: 'Image', src: processTemplateValue(props.src) };
|
|
1231
|
+
if (props.width !== undefined)
|
|
1232
|
+
kind.width = processTemplateValue(props.width);
|
|
1233
|
+
if (props.height !== undefined)
|
|
1234
|
+
kind.height = processTemplateValue(props.height);
|
|
1235
|
+
return { kind, style: mapTemplateStyle(props.style), children: [] };
|
|
1236
|
+
}
|
|
1237
|
+
function serializeTemplateTable(element, _parent = null) {
|
|
1238
|
+
const props = element.props;
|
|
1239
|
+
const columns = (props.columns ?? []).map(col => ({
|
|
1240
|
+
width: mapColumnWidth(col.width),
|
|
1241
|
+
}));
|
|
1242
|
+
const childElements = flattenTemplateChildren(props.children);
|
|
1243
|
+
const children = serializeTemplateChildren(childElements, 'Table');
|
|
1244
|
+
return { kind: { type: 'Table', columns }, style: mapTemplateStyle(props.style), children };
|
|
1245
|
+
}
|
|
1246
|
+
function serializeTemplateRow(element) {
|
|
1247
|
+
const props = element.props;
|
|
1248
|
+
const childElements = flattenTemplateChildren(props.children);
|
|
1249
|
+
const children = serializeTemplateChildren(childElements, 'Row');
|
|
1250
|
+
return { kind: { type: 'TableRow', is_header: props.header ?? false }, style: mapTemplateStyle(props.style), children };
|
|
1251
|
+
}
|
|
1252
|
+
function serializeTemplateCell(element) {
|
|
1253
|
+
const props = element.props;
|
|
1254
|
+
const childElements = flattenTemplateChildren(props.children);
|
|
1255
|
+
const children = serializeTemplateChildren(childElements, 'Cell');
|
|
1256
|
+
return { kind: { type: 'TableCell', col_span: props.colSpan ?? 1, row_span: props.rowSpan ?? 1 }, style: mapTemplateStyle(props.style), children };
|
|
1257
|
+
}
|
|
1258
|
+
function serializeTemplateFixed(element) {
|
|
1259
|
+
const props = element.props;
|
|
1260
|
+
const position = props.position === 'header' ? 'Header' : 'Footer';
|
|
1261
|
+
const childElements = flattenTemplateChildren(props.children);
|
|
1262
|
+
const children = serializeTemplateChildren(childElements, 'Fixed');
|
|
1263
|
+
const node = { kind: { type: 'Fixed', position }, style: mapTemplateStyle(props.style), children };
|
|
1264
|
+
if (props.bookmark)
|
|
1265
|
+
node.bookmark = props.bookmark;
|
|
1266
|
+
return node;
|
|
1267
|
+
}
|
|
1268
|
+
function serializeTemplateChildren(children, parent = null) {
|
|
1269
|
+
const nodes = [];
|
|
1270
|
+
for (const child of children) {
|
|
1271
|
+
const node = serializeTemplateChild(child, parent);
|
|
1272
|
+
if (node !== null)
|
|
1273
|
+
nodes.push(node);
|
|
1274
|
+
}
|
|
1275
|
+
return nodes;
|
|
1276
|
+
}
|
|
1277
|
+
/**
|
|
1278
|
+
* Flatten children without using React.Children.forEach, which rejects
|
|
1279
|
+
* proxy objects and markers. Handles arrays, Fragments, and raw values.
|
|
1280
|
+
*/
|
|
1281
|
+
function flattenTemplateChildren(children) {
|
|
1282
|
+
if (children === null || children === undefined)
|
|
1283
|
+
return [];
|
|
1284
|
+
const result = [];
|
|
1285
|
+
if (Array.isArray(children)) {
|
|
1286
|
+
for (const child of children) {
|
|
1287
|
+
result.push(...flattenTemplateChildren(child));
|
|
1288
|
+
}
|
|
1289
|
+
return result;
|
|
1290
|
+
}
|
|
1291
|
+
// Fragment unwrapping
|
|
1292
|
+
if (isValidElement(children) && children.type === Fragment) {
|
|
1293
|
+
const fragProps = children.props;
|
|
1294
|
+
return flattenTemplateChildren(fragProps.children);
|
|
1295
|
+
}
|
|
1296
|
+
result.push(children);
|
|
1297
|
+
return result;
|
|
1298
|
+
}
|
|
1299
|
+
// ─── Expression value serialization ─────────────────────────────────
|
|
1300
|
+
/**
|
|
1301
|
+
* Recursively process an expression object, serializing any React elements
|
|
1302
|
+
* found in its values (e.g. $if then/else branches).
|
|
1303
|
+
*/
|
|
1304
|
+
function serializeExprValues(expr, parent) {
|
|
1305
|
+
const result = {};
|
|
1306
|
+
for (const [key, val] of Object.entries(expr)) {
|
|
1307
|
+
if (isValidElement(val)) {
|
|
1308
|
+
result[key] = serializeTemplateChild(val, parent);
|
|
1309
|
+
}
|
|
1310
|
+
else if (Array.isArray(val)) {
|
|
1311
|
+
result[key] = val.map(v => isValidElement(v) ? serializeTemplateChild(v, parent) : v);
|
|
1312
|
+
}
|
|
1313
|
+
else {
|
|
1314
|
+
result[key] = val;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
return result;
|
|
1318
|
+
}
|
|
1319
|
+
// ─── Template value processing ──────────────────────────────────────
|
|
1320
|
+
/**
|
|
1321
|
+
* Process a value that may contain ref markers, expr markers, or proxy objects.
|
|
1322
|
+
* Returns the expression form or the original value.
|
|
1323
|
+
*/
|
|
1324
|
+
function processTemplateValue(v) {
|
|
1325
|
+
if (typeof v === 'string') {
|
|
1326
|
+
if (isRefMarker(v)) {
|
|
1327
|
+
return { $ref: getRefPath(v) };
|
|
1328
|
+
}
|
|
1329
|
+
// Check for embedded sentinels in longer strings
|
|
1330
|
+
if (v.includes(REF_SENTINEL)) {
|
|
1331
|
+
return processTemplateInterpolatedString(v);
|
|
1332
|
+
}
|
|
1333
|
+
return v;
|
|
1334
|
+
}
|
|
1335
|
+
if (isExprMarker(v)) {
|
|
1336
|
+
return getExpr(v);
|
|
1337
|
+
}
|
|
1338
|
+
if (isEachMarker(v)) {
|
|
1339
|
+
return {
|
|
1340
|
+
$each: { $ref: getEachPath(v) },
|
|
1341
|
+
as: '$item',
|
|
1342
|
+
template: processTemplateValue(getEachTemplate(v)),
|
|
1343
|
+
};
|
|
1344
|
+
}
|
|
1345
|
+
// Proxy objects with toPrimitive
|
|
1346
|
+
if (typeof v === 'object' && v !== null && Symbol.toPrimitive in v) {
|
|
1347
|
+
const str = String(v);
|
|
1348
|
+
if (isRefMarker(str)) {
|
|
1349
|
+
return { $ref: getRefPath(str) };
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
return v;
|
|
1353
|
+
}
|
|
1354
|
+
/**
|
|
1355
|
+
* Process a string that contains interpolated ref sentinels.
|
|
1356
|
+
* e.g. "Hello \0FORME_REF:name\0!" → {$concat: ["Hello ", {$ref: "name"}, "!"]}
|
|
1357
|
+
*/
|
|
1358
|
+
function processTemplateInterpolatedString(s) {
|
|
1359
|
+
const parts = [];
|
|
1360
|
+
let remaining = s;
|
|
1361
|
+
while (remaining.length > 0) {
|
|
1362
|
+
const startIdx = remaining.indexOf(REF_SENTINEL);
|
|
1363
|
+
if (startIdx === -1) {
|
|
1364
|
+
parts.push(remaining);
|
|
1365
|
+
break;
|
|
1366
|
+
}
|
|
1367
|
+
if (startIdx > 0) {
|
|
1368
|
+
parts.push(remaining.slice(0, startIdx));
|
|
1369
|
+
}
|
|
1370
|
+
const afterSentinel = remaining.slice(startIdx + REF_SENTINEL.length);
|
|
1371
|
+
const endIdx = afterSentinel.indexOf(REF_SENTINEL_END);
|
|
1372
|
+
if (endIdx === -1) {
|
|
1373
|
+
parts.push(remaining);
|
|
1374
|
+
break;
|
|
1375
|
+
}
|
|
1376
|
+
const path = afterSentinel.slice(0, endIdx);
|
|
1377
|
+
parts.push({ $ref: path });
|
|
1378
|
+
remaining = afterSentinel.slice(endIdx + REF_SENTINEL_END.length);
|
|
1379
|
+
}
|
|
1380
|
+
if (parts.length === 1)
|
|
1381
|
+
return parts[0];
|
|
1382
|
+
return { $concat: parts };
|
|
1383
|
+
}
|
|
1384
|
+
/**
|
|
1385
|
+
* Process a string that might be a pure ref sentinel.
|
|
1386
|
+
* Returns the $ref node if it's a pure ref, null otherwise.
|
|
1387
|
+
*/
|
|
1388
|
+
function processTemplateString(s) {
|
|
1389
|
+
if (isRefMarker(s)) {
|
|
1390
|
+
return { $ref: getRefPath(s) };
|
|
1391
|
+
}
|
|
1392
|
+
if (s.includes(REF_SENTINEL)) {
|
|
1393
|
+
return processTemplateInterpolatedString(s);
|
|
1394
|
+
}
|
|
1395
|
+
return null;
|
|
1396
|
+
}
|
|
1397
|
+
/**
|
|
1398
|
+
* Flatten text content within a <Text> element, detecting ref markers.
|
|
1399
|
+
* Returns either a plain string or a $ref/$concat expression.
|
|
1400
|
+
*/
|
|
1401
|
+
function flattenTemplateTextContent(children) {
|
|
1402
|
+
if (children === null || children === undefined)
|
|
1403
|
+
return '';
|
|
1404
|
+
if (typeof children === 'boolean')
|
|
1405
|
+
return '';
|
|
1406
|
+
if (typeof children === 'string') {
|
|
1407
|
+
if (isRefMarker(children))
|
|
1408
|
+
return { $ref: getRefPath(children) };
|
|
1409
|
+
if (children.includes(REF_SENTINEL))
|
|
1410
|
+
return processTemplateInterpolatedString(children);
|
|
1411
|
+
return children;
|
|
1412
|
+
}
|
|
1413
|
+
if (typeof children === 'number')
|
|
1414
|
+
return String(children);
|
|
1415
|
+
if (isExprMarker(children))
|
|
1416
|
+
return getExpr(children);
|
|
1417
|
+
// Proxy with toPrimitive
|
|
1418
|
+
if (typeof children === 'object' && children !== null && Symbol.toPrimitive in children) {
|
|
1419
|
+
const str = String(children);
|
|
1420
|
+
if (isRefMarker(str))
|
|
1421
|
+
return { $ref: getRefPath(str) };
|
|
1422
|
+
return str;
|
|
1423
|
+
}
|
|
1424
|
+
if (Array.isArray(children)) {
|
|
1425
|
+
const parts = children.map(c => flattenTemplateTextContent(c));
|
|
1426
|
+
// If all parts are strings, join them
|
|
1427
|
+
if (parts.every(p => typeof p === 'string')) {
|
|
1428
|
+
return parts.join('');
|
|
1429
|
+
}
|
|
1430
|
+
// Otherwise produce a $concat
|
|
1431
|
+
return { $concat: parts };
|
|
1432
|
+
}
|
|
1433
|
+
if (isValidElement(children)) {
|
|
1434
|
+
const element = children;
|
|
1435
|
+
if (element.type === Text) {
|
|
1436
|
+
const props = element.props;
|
|
1437
|
+
return flattenTemplateTextContent(props.children);
|
|
1438
|
+
}
|
|
1439
|
+
const props = element.props;
|
|
1440
|
+
return flattenTemplateTextContent(props.children);
|
|
1441
|
+
}
|
|
1442
|
+
const arr = [];
|
|
1443
|
+
Children.forEach(children, c => arr.push(c));
|
|
1444
|
+
if (arr.length > 0) {
|
|
1445
|
+
return flattenTemplateTextContent(arr);
|
|
1446
|
+
}
|
|
1447
|
+
return String(children);
|
|
1448
|
+
}
|
|
1449
|
+
/**
|
|
1450
|
+
* Map style, processing values that may contain template expressions.
|
|
1451
|
+
*/
|
|
1452
|
+
function mapTemplateStyle(style) {
|
|
1453
|
+
if (!style)
|
|
1454
|
+
return {};
|
|
1455
|
+
// Use the regular mapStyle but then post-process values that contain markers
|
|
1456
|
+
const result = mapStyle(style);
|
|
1457
|
+
return processTemplateStyleValues(result);
|
|
1458
|
+
}
|
|
1459
|
+
function processTemplateStyleValues(obj) {
|
|
1460
|
+
const result = {};
|
|
1461
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
1462
|
+
if (typeof val === 'object' && val !== null && !Array.isArray(val)) {
|
|
1463
|
+
result[key] = processTemplateStyleValues(val);
|
|
1464
|
+
}
|
|
1465
|
+
else {
|
|
1466
|
+
result[key] = processTemplateValue(val);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
return result;
|
|
1470
|
+
}
|