@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,641 @@
|
|
|
1
|
+
import React, { createElement, isValidElement, type ReactElement, type ReactNode } from 'react';
|
|
2
|
+
import * as F from '@formepdf/react';
|
|
3
|
+
import { relative, resolve, sep } from 'node:path';
|
|
4
|
+
import { readFileSync, realpathSync, statSync } from 'node:fs';
|
|
5
|
+
import { readyMedia, mediaUri, framedMedia, type ImageFrameOptions } from '../media/render';
|
|
6
|
+
import type { MediaUse } from '../shared/media';
|
|
7
|
+
import type { AssetUse, DocumentAssets } from '../shared/assets';
|
|
8
|
+
import { documentThemeAssets, withDocumentAssets, type AppliedDocumentAssets } from '../assets';
|
|
9
|
+
import { assetFile, assetRevisionPath, readAssetRevision } from '../assets/files';
|
|
10
|
+
import { neutral, themePage, themeType, validateTheme, type DocTheme, type ThemeTypeRole } from '../themes/index';
|
|
11
|
+
import type { BlockInfo, SourceLocation, DocumentFormat, SlideInfo } from '../shared/types';
|
|
12
|
+
import { Page } from './page';
|
|
13
|
+
import { TextCapture, TextSlot, type TextSlotProps } from './text-targets';
|
|
14
|
+
export { TextSlot, type TextSlotProps, type TextFieldPath } from './text-targets';
|
|
15
|
+
|
|
16
|
+
export type { DocumentMeta } from '../shared/types';
|
|
17
|
+
export type { DocTheme } from '../themes/index';
|
|
18
|
+
export { Page } from './page';
|
|
19
|
+
export type { PageProps } from './page';
|
|
20
|
+
export type { ImageFrameOptions } from '../media/render';
|
|
21
|
+
export { PageBreak, Em, Svg, View, Text, Link, Image } from '@formepdf/react';
|
|
22
|
+
/** Match the bundled semibold face; Forme does not choose the nearest weight for inline bold. */
|
|
23
|
+
export function Strong({ children, style }: F.StrongProps) { return <F.Strong style={{ fontWeight: 600, ...style }}><TextSlot slot="children" from="children">{children}</TextSlot></F.Strong>; }
|
|
24
|
+
export type Reference = { title: string; author?: string; year?: string; url?: string };
|
|
25
|
+
export type ReferenceMap = Record<string, Reference>;
|
|
26
|
+
export type CitationStyle = 'numeric' | 'author-date';
|
|
27
|
+
type Props = { id: string; children?: ReactNode; style?: F.Style; href?: string };
|
|
28
|
+
type Target = { label: string; needsCaption?: boolean };
|
|
29
|
+
type Endnote = { id: string; children: ReactNode };
|
|
30
|
+
type Runtime = {
|
|
31
|
+
format: DocumentFormat; slides: SlideInfo[]; currentSlide?: string; slidePages: WeakSet<object>;
|
|
32
|
+
media: MediaUse[];
|
|
33
|
+
assets: AssetUse[]; managed?: AppliedDocumentAssets; currentBlock?: string;
|
|
34
|
+
theme: DocTheme; refs: ReferenceMap; cited: string[]; citationStyle: CitationStyle;
|
|
35
|
+
blocks: Record<string, BlockInfo>; targets: Map<string, Target>; notes: Endnote[];
|
|
36
|
+
figureCount: number; tableCount: number; documentCount: number; referencesCount: number; notesCount: number;
|
|
37
|
+
};
|
|
38
|
+
let runtime: Runtime;
|
|
39
|
+
const roots = new Set(Object.values(F));
|
|
40
|
+
const nativeCharts = new Map<unknown, string>([[F.BarChart, 'BarChart'], [F.LineChart, 'LineChart'], [F.PieChart, 'PieChart'], [F.AreaChart, 'AreaChart'], [F.DotPlot, 'DotPlot']]);
|
|
41
|
+
type Globals = typeof globalThis & { __formeSourceMap?: WeakMap<object, SourceLocation>; __opendocRoot?: string; __opendocDocumentDirectory?: string; __opendocDocumentAssets?: DocumentAssets };
|
|
42
|
+
const globals = globalThis as Globals;
|
|
43
|
+
const blockKinds = new Map<unknown, string>();
|
|
44
|
+
function block<T>(fn: T, kind: string): T { blockKinds.set(fn, kind); return fn; }
|
|
45
|
+
const validId = /^[a-zA-Z0-9][a-zA-Z0-9._-]*$/;
|
|
46
|
+
function checkId(id: unknown, description: string): asserts id is string {
|
|
47
|
+
if (typeof id !== 'string' || !validId.test(id)) throw new Error(`${description} needs a stable id (letters, numbers, dots, hyphens, underscores).`);
|
|
48
|
+
}
|
|
49
|
+
function plainText(node: ReactNode): string {
|
|
50
|
+
if (typeof node === 'string' || typeof node === 'number') return String(node);
|
|
51
|
+
if (Array.isArray(node)) return node.map(plainText).join('');
|
|
52
|
+
if (isValidElement<{ children?: ReactNode }>(node)) return plainText(node.props.children);
|
|
53
|
+
return '';
|
|
54
|
+
}
|
|
55
|
+
function requireText(value: unknown, label: string): asserts value is string {
|
|
56
|
+
if (typeof value !== 'string' || !value.trim()) throw new Error(`${label} must be nonempty text.`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** Expand pure author components once. Resolve citations and cross-references after collecting every target. */
|
|
60
|
+
export function prepareDocument(input: ReactNode) {
|
|
61
|
+
runtime = {
|
|
62
|
+
format: 'document', slides: [], slidePages: new WeakSet(),
|
|
63
|
+
media: [], assets: [], theme: neutral, refs: {}, cited: [], citationStyle: 'numeric', blocks: Object.create(null), targets: new Map(), notes: [],
|
|
64
|
+
figureCount: 0, tableCount: 0, documentCount: 0, referencesCount: 0, notesCount: 0,
|
|
65
|
+
};
|
|
66
|
+
const map = globals.__formeSourceMap ??= new WeakMap();
|
|
67
|
+
const textCapture = new TextCapture(map);
|
|
68
|
+
// A passed React child retains the component that constructed it. This is
|
|
69
|
+
// the explicit provenance chain for TextSlot, not a search through ancestors.
|
|
70
|
+
const componentOwners = new WeakMap<object, SourceLocation | undefined>();
|
|
71
|
+
function elementsIn(value: unknown, found: WeakSet<object>, callback?: (element: ReactElement) => void) {
|
|
72
|
+
if (!value || typeof value !== 'object' || found.has(value)) return;
|
|
73
|
+
found.add(value);
|
|
74
|
+
if (isValidElement(value)) callback?.(value);
|
|
75
|
+
if (Array.isArray(value)) value.forEach(item => elementsIn(item, found, callback));
|
|
76
|
+
else if (isValidElement(value)) elementsIn(value.props, found, callback);
|
|
77
|
+
else for (const descriptor of Object.values(Object.getOwnPropertyDescriptors(value))) {
|
|
78
|
+
if (descriptor.enumerable && 'value' in descriptor) elementsIn(descriptor.value, found, callback);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
const deferred = new WeakMap<object, { parentId?: string; source?: SourceLocation; slideId?: string }>();
|
|
82
|
+
let proseDepth = 0;
|
|
83
|
+
const lateTypes = new Set([Cite, References, CrossReference, Note, Notes]);
|
|
84
|
+
function typesetProse(input: ReactNode, paragraphGap = 0): ReactNode {
|
|
85
|
+
if (!Number.isFinite(paragraphGap) || paragraphGap < 0) throw new Error('Prose paragraphGap must be a finite, non-negative number.');
|
|
86
|
+
let continuation = false;
|
|
87
|
+
function format(node: ReactNode): ReactNode {
|
|
88
|
+
if (Array.isArray(node)) return node.map(format);
|
|
89
|
+
if (!isValidElement<{ children?: ReactNode; style?: F.Style }>(node)) return node;
|
|
90
|
+
const location = map.get(node);
|
|
91
|
+
const id = location?.file.startsWith('opendoc:block:') ? location.file.slice('opendoc:block:'.length) : undefined;
|
|
92
|
+
const kind = id ? runtime.blocks[id]?.kind : undefined;
|
|
93
|
+
const isParagraph = node.type === F.Text && kind === 'paragraph';
|
|
94
|
+
if (isParagraph) {
|
|
95
|
+
const style = node.props.style ?? {};
|
|
96
|
+
const em = style.fontSize ?? runtime.theme.fontSize;
|
|
97
|
+
// An isolated em-space run has a fixed advance in Forme. A leading
|
|
98
|
+
// whitespace string is stretchable glue and is NOT a reliable indent.
|
|
99
|
+
// No tabs, word joiners, manual line splitting, or whole-block padding.
|
|
100
|
+
const spacer = continuation ? <F.Text style={{ fontSize: em * 1.5 }}>{'\u2003'}</F.Text> : null;
|
|
101
|
+
continuation = true;
|
|
102
|
+
const result = React.cloneElement(node, { style: { lineBreaking: 'greedy', ...style, marginBottom: paragraphGap } }, spacer, node.props.children);
|
|
103
|
+
if (location) map.set(result, location);
|
|
104
|
+
textCapture.copy(node, result);
|
|
105
|
+
return result;
|
|
106
|
+
}
|
|
107
|
+
// Inline text and deferred references belong to the current paragraph;
|
|
108
|
+
// don't treat their runs as new paragraphs or disturb citation identity.
|
|
109
|
+
if (node.type === F.Text || lateTypes.has(node.type as typeof Cite)) return node;
|
|
110
|
+
if (kind && ['figure', 'table', 'callout', 'code', 'list', 'title'].includes(kind)) { continuation = false; return node; }
|
|
111
|
+
const separates = kind && ['heading', 'section', 'figure', 'table', 'callout', 'code', 'list', 'title', 'block'].includes(kind);
|
|
112
|
+
if (separates || node.type === F.PageBreak || !node.props.children) continuation = false;
|
|
113
|
+
const result = React.cloneElement(node, {}, format(node.props.children));
|
|
114
|
+
if (location) map.set(result, location);
|
|
115
|
+
textCapture.copy(node, result);
|
|
116
|
+
if (separates && kind !== 'section' && kind !== 'heading' && kind !== 'block') continuation = false;
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
return format(input);
|
|
120
|
+
}
|
|
121
|
+
function visit(node: ReactNode, parentId?: string, caller?: SourceLocation, resolving = false): ReactNode {
|
|
122
|
+
if (Array.isArray(node)) return node.map(child => visit(child, parentId, caller, resolving));
|
|
123
|
+
if (!isValidElement(node)) {
|
|
124
|
+
if (runtime.format === 'presentation' && !runtime.currentSlide && (typeof node === 'string' || typeof node === 'number') && String(node).trim()) throw new Error('Presentation content must be inside a Slide.');
|
|
125
|
+
return node;
|
|
126
|
+
}
|
|
127
|
+
const el = node as ReactElement<Record<string, any>>;
|
|
128
|
+
if (runtime.format === 'presentation') {
|
|
129
|
+
if (el.type === F.PageBreak || el.type === Pages || el.type === F.Fixed) throw new Error(`Slide ${runtime.currentSlide ?? ''}: flowing pages, page breaks, and running furniture are not supported in presentations.`);
|
|
130
|
+
if ((el.type === Page || el.type === F.Page) && !runtime.slidePages.has(el)) throw new Error('Presentations must use Slide instead of Page or Pages.');
|
|
131
|
+
if (el.type === Slide && runtime.currentSlide) throw new Error(`Slide ${runtime.currentSlide}: slides cannot nest.`);
|
|
132
|
+
if (roots.has(el.type as never) && el.type !== F.Document && !runtime.currentSlide) throw new Error('Presentation content must be inside a Slide.');
|
|
133
|
+
}
|
|
134
|
+
if (el.type === TextSlot) {
|
|
135
|
+
const props = el.props as TextSlotProps;
|
|
136
|
+
return textCapture.wrap(visit(props.children, parentId, caller, resolving), props, componentOwners.get(el));
|
|
137
|
+
}
|
|
138
|
+
const nativeChart = nativeCharts.get(el.type);
|
|
139
|
+
if (nativeChart) throw new Error(`${parentId ? `${parentId}: ` : ''}${nativeChart} is not supported by the current PDF engine with OpenDoc fonts: chart labels are encoded incorrectly. Generate a local chart asset inside Figure and review its labels, units, scale, and legend.`);
|
|
140
|
+
const ownSource = map.get(el);
|
|
141
|
+
const source = ownSource?.file.includes('/src/document/') ? caller ?? ownSource : ownSource ?? caller;
|
|
142
|
+
if (el.type === Prose) {
|
|
143
|
+
if (proseDepth) throw new Error('Prose regions cannot nest. Use separate sibling regions to restart paragraph flow.');
|
|
144
|
+
proseDepth++;
|
|
145
|
+
try { return typesetProse(visit(el.props.children, parentId, source, resolving), el.props.paragraphGap); }
|
|
146
|
+
finally { proseDepth--; }
|
|
147
|
+
}
|
|
148
|
+
let id = parentId;
|
|
149
|
+
if (el.type === Media || el.type === MediaFrame) runtime.media.push({ item: el.props.item, blockId: parentId });
|
|
150
|
+
if (el.type === Page && el.props.backgroundMedia !== undefined) runtime.media.push({ item: el.props.backgroundMedia, blockId: parentId });
|
|
151
|
+
if (el.type === Slide && el.props.backgroundMedia !== undefined) runtime.media.push({ item: el.props.backgroundMedia, blockId: el.props.id });
|
|
152
|
+
const kind = blockKinds.get(el.type);
|
|
153
|
+
if (kind) {
|
|
154
|
+
id = el.props.id;
|
|
155
|
+
checkId(id, kind);
|
|
156
|
+
if (Object.hasOwn(runtime.blocks, id)) throw new Error(`Duplicate block id: ${id}. Keep every block ID unique within a document.`);
|
|
157
|
+
runtime.blocks[id] = { id, kind, source, text: '', ...(runtime.currentSlide || kind === 'slide' ? { slideId: kind === 'slide' ? id : runtime.currentSlide } : {}) };
|
|
158
|
+
if (kind === 'heading' || kind === 'section') {
|
|
159
|
+
const title = plainText(kind === 'section' ? el.props.title : el.props.children);
|
|
160
|
+
requireText(title, `${kind} ${id} title`);
|
|
161
|
+
runtime.targets.set(id, { label: title.trim() });
|
|
162
|
+
} else if (kind === 'figure' || kind === 'table') {
|
|
163
|
+
const number = kind === 'figure' ? ++runtime.figureCount : ++runtime.tableCount;
|
|
164
|
+
runtime.targets.set(id, { label: `${kind === 'figure' ? 'Figure' : 'Table'} ${number}`, needsCaption: kind === 'table' && !el.props.caption });
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (!resolving && lateTypes.has(el.type as typeof Cite)) {
|
|
168
|
+
if (el.type === Cite) {
|
|
169
|
+
checkId(el.props.source, 'Citation source');
|
|
170
|
+
if (!runtime.cited.includes(el.props.source)) runtime.cited.push(el.props.source);
|
|
171
|
+
}
|
|
172
|
+
if (el.type === References) runtime.referencesCount++;
|
|
173
|
+
if (el.type === Notes) runtime.notesCount++;
|
|
174
|
+
if (el.type === Note) {
|
|
175
|
+
checkId(el.props.id, 'Note');
|
|
176
|
+
if (runtime.notes.some(note => note.id === el.props.id)) throw new Error(`Duplicate note id: ${el.props.id}.`);
|
|
177
|
+
requireText(plainText(el.props.children), `Note ${el.props.id}`);
|
|
178
|
+
// Note bodies are prepared now so citations in them join the same reference list.
|
|
179
|
+
const note = { id: el.props.id, children: visit(el.props.children, parentId, source) };
|
|
180
|
+
runtime.notes.push(note);
|
|
181
|
+
}
|
|
182
|
+
deferred.set(el, { parentId: id, source, slideId: runtime.currentSlide });
|
|
183
|
+
return el;
|
|
184
|
+
}
|
|
185
|
+
if (typeof el.type === 'function' && !roots.has(el.type as never)) {
|
|
186
|
+
const inputs = new WeakSet<object>();
|
|
187
|
+
elementsIn(el.props, inputs);
|
|
188
|
+
const previousBlock = runtime.currentBlock;
|
|
189
|
+
const previousSlide = runtime.currentSlide;
|
|
190
|
+
runtime.currentBlock = id;
|
|
191
|
+
if (el.type === Slide) runtime.currentSlide = id;
|
|
192
|
+
let output: ReactNode;
|
|
193
|
+
try {
|
|
194
|
+
output = (el.type as (p: unknown) => ReactNode)(el.props);
|
|
195
|
+
elementsIn(output, new WeakSet(), element => {
|
|
196
|
+
if (!inputs.has(element) && !componentOwners.has(element)) componentOwners.set(element, ownSource);
|
|
197
|
+
});
|
|
198
|
+
return visit(output, id, source, resolving);
|
|
199
|
+
} finally { runtime.currentBlock = previousBlock; runtime.currentSlide = previousSlide; }
|
|
200
|
+
}
|
|
201
|
+
if (el.type === React.Fragment) return visit(el.props.children, id, source, resolving);
|
|
202
|
+
const cloned = createElement(el.type, { ...el.props, key: el.key }, visit(el.props.children, id, source, resolving));
|
|
203
|
+
const location = id ? { file: `opendoc:block:${id}`, line: 1, column: 1 } : map.get(el);
|
|
204
|
+
if (location) map.set(cloned, location);
|
|
205
|
+
textCapture.remember(cloned, ownSource);
|
|
206
|
+
return cloned;
|
|
207
|
+
}
|
|
208
|
+
const collected = visit(input);
|
|
209
|
+
if (runtime.documentCount !== 1) throw new Error('A document must contain exactly one OpenDoc Document root.');
|
|
210
|
+
if (runtime.format === 'presentation' && !runtime.slides.length) throw new Error('A presentation needs at least one Slide.');
|
|
211
|
+
if (runtime.referencesCount > 1) throw new Error('Use one References list per document.');
|
|
212
|
+
if (runtime.notesCount > 1) throw new Error('Use one Notes list per document.');
|
|
213
|
+
for (const key of runtime.cited) {
|
|
214
|
+
if (!Object.hasOwn(runtime.refs, key)) throw new Error(`Unknown citation source: ${key}`);
|
|
215
|
+
const ref = runtime.refs[key];
|
|
216
|
+
if (runtime.citationStyle === 'author-date' && (!ref.author?.trim() || !ref.year?.trim())) {
|
|
217
|
+
throw new Error(`Source ${key} needs author and year for author-date citations.`);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (runtime.cited.length && !runtime.referencesCount) throw new Error('This document contains citations. Add <References /> to include their source records.');
|
|
221
|
+
if (runtime.notes.length && !runtime.notesCount) throw new Error('This document contains notes. Add <Notes /> to include their text.');
|
|
222
|
+
function finish(node: ReactNode): ReactNode {
|
|
223
|
+
if (Array.isArray(node)) return node.map(finish);
|
|
224
|
+
if (!isValidElement(node)) return node;
|
|
225
|
+
const el = node as ReactElement<Record<string, any>>;
|
|
226
|
+
const pending = deferred.get(el);
|
|
227
|
+
if (pending) {
|
|
228
|
+
const previousSlide = runtime.currentSlide;
|
|
229
|
+
runtime.currentSlide = pending.slideId;
|
|
230
|
+
try { return textCapture.protect(finish(visit(el, pending.parentId, pending.source, true)), 'Generated reference content. Ask your agent to change its inputs.'); }
|
|
231
|
+
finally { runtime.currentSlide = previousSlide; }
|
|
232
|
+
}
|
|
233
|
+
const cloned = createElement(el.type, { ...el.props, key: el.key }, finish(el.props.children));
|
|
234
|
+
const location = map.get(el);
|
|
235
|
+
if (location) map.set(cloned, location);
|
|
236
|
+
textCapture.copy(el, cloned);
|
|
237
|
+
return cloned;
|
|
238
|
+
}
|
|
239
|
+
type FontStyle = { family?: string; weight: number; renderedWeight?: number; style: string };
|
|
240
|
+
const headings = new Set<unknown>([F.H1, F.H2, F.H3, F.H4, F.H5, F.H6]);
|
|
241
|
+
function applyFonts(node: ReactNode, inherited: FontStyle = { weight: 400, style: 'normal' }, parentId?: string): ReactNode {
|
|
242
|
+
if (Array.isArray(node)) return node.map(child => applyFonts(child, inherited, parentId));
|
|
243
|
+
if (!isValidElement(node)) {
|
|
244
|
+
if ((typeof node === 'string' || typeof node === 'number') && String(node).trim() && inherited.family) {
|
|
245
|
+
const font = runtime.managed?.fonts.get(inherited.family);
|
|
246
|
+
if (font) {
|
|
247
|
+
const face = font.faces.find(face => face.weight === (inherited.renderedWeight ?? inherited.weight) && face.style === inherited.style);
|
|
248
|
+
if (!face) throw new Error(`Font “${font.name}” does not include ${inherited.renderedWeight ?? inherited.weight} ${inherited.style}${parentId ? ` (block ${parentId})` : ''}. Add this face and rebind the document, or choose a style that the family provides.`);
|
|
249
|
+
const use: AssetUse = { kind: 'font', id: font.id, revision: font.revision, face: face.id, ...(parentId ? { blockId: parentId } : {}) };
|
|
250
|
+
if (!runtime.assets.some(item => item.kind === use.kind && item.id === use.id && item.revision === use.revision && item.face === use.face && item.blockId === use.blockId)) runtime.assets.push(use);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
return node;
|
|
254
|
+
}
|
|
255
|
+
const el = node as ReactElement<Record<string, any>>;
|
|
256
|
+
const location = map.get(el);
|
|
257
|
+
const id = location?.file.startsWith('opendoc:block:') ? location.file.slice('opendoc:block:'.length) : parentId;
|
|
258
|
+
const style: F.Style = el.props.style ?? {};
|
|
259
|
+
const weight = style.fontWeight === 'bold' ? 700 : style.fontWeight === 'normal' ? 400 : style.fontWeight;
|
|
260
|
+
const current: FontStyle = {
|
|
261
|
+
family: style.fontFamily ?? (el.type === F.Code ? 'Courier' : inherited.family),
|
|
262
|
+
weight: weight ?? (el.type === F.Strong || headings.has(el.type) ? 700 : inherited.weight),
|
|
263
|
+
style: style.fontStyle ?? (el.type === F.Em ? 'italic' : inherited.style),
|
|
264
|
+
};
|
|
265
|
+
const font = current.family && runtime.managed?.fonts.get(current.family);
|
|
266
|
+
// Emphasis may use the family's real bold face when semibold is absent.
|
|
267
|
+
// Resolve the run's true weight; registering bold bytes as 600 causes spacing errors.
|
|
268
|
+
const fallback = !!font && current.weight === 600 && !font.faces.some(face => face.weight === 600 && face.style === current.style) && font.faces.some(face => face.weight === 700 && face.style === current.style);
|
|
269
|
+
current.renderedWeight = fallback ? 700 : current.weight;
|
|
270
|
+
const naturalWeight = weight ?? (el.type === F.Strong || headings.has(el.type) ? 700 : inherited.renderedWeight ?? inherited.weight);
|
|
271
|
+
// Preserve the requested weight through nested style changes. A bold fallback
|
|
272
|
+
// on a parent must not hide an available semibold italic face on its child.
|
|
273
|
+
const adjusted = current.renderedWeight !== naturalWeight;
|
|
274
|
+
const cloned = createElement(el.type, { ...el.props, ...(adjusted ? { style: { ...style, fontWeight: current.renderedWeight } } : {}), key: el.key }, applyFonts(el.props.children, current, id));
|
|
275
|
+
if (location) map.set(cloned, location);
|
|
276
|
+
textCapture.copy(el, cloned);
|
|
277
|
+
return cloned;
|
|
278
|
+
}
|
|
279
|
+
const finished = finish(collected);
|
|
280
|
+
const complete = textCapture.complete(runtime.managed?.fonts.size ? applyFonts(finished) : finished);
|
|
281
|
+
return { ...complete, blocks: runtime.blocks, media: runtime.media, assets: runtime.assets, assetBindings: runtime.managed?.bindings,
|
|
282
|
+
assetDependencies: [...(runtime.managed?.dependencies ?? [])].sort(), themeId: runtime.theme.id, format: runtime.format, slides: runtime.slides };
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** A deck uses the same document runtime, fonts, assets, references, and text bindings. */
|
|
286
|
+
export function Presentation(props: Parameters<typeof Document>[0]) {
|
|
287
|
+
if (runtime.documentCount || runtime.format === 'presentation') throw new Error('Presentation must be the single root.');
|
|
288
|
+
runtime.format = 'presentation';
|
|
289
|
+
return <Document {...props} />;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
export const SLIDE_SIZE = { width: 960, height: 540 } as const;
|
|
293
|
+
export type SlideProps = Pick<import('./page').PageProps, 'backgroundMedia' | 'backgroundImage' | 'backgroundOpacity' | 'backgroundSize' | 'backgroundPosition'> & {
|
|
294
|
+
id: string; children?: ReactNode; padding?: number; style?: F.Style;
|
|
295
|
+
};
|
|
296
|
+
/** Fixed canvas: content must fit; the worker rejects clipping or continuation pages. */
|
|
297
|
+
export const Slide = block(function Slide({ id, children, padding = 40, style, ...background }: SlideProps) {
|
|
298
|
+
if (runtime.format !== 'presentation') throw new Error('Slide must be inside Presentation.');
|
|
299
|
+
if (!Number.isFinite(padding) || padding < 0 || padding >= SLIDE_SIZE.height / 2) throw new Error(`Slide ${id}: padding must be between 0 and 270 points.`);
|
|
300
|
+
runtime.slides.push({ id });
|
|
301
|
+
const page = Page({ ...background, size: SLIDE_SIZE, margin: 0, style: { backgroundColor: runtime.theme.paper },
|
|
302
|
+
children: <F.View wrap={false} style={{ ...style, width: SLIDE_SIZE.width, height: SLIDE_SIZE.height, padding, flexShrink: 0, overflow: 'hidden' }}>{children}</F.View> });
|
|
303
|
+
runtime.slidePages.add(page);
|
|
304
|
+
return page;
|
|
305
|
+
}, 'slide');
|
|
306
|
+
|
|
307
|
+
export function Document({ title, author, theme = neutral, references = {}, citationStyle = 'numeric', children }: {
|
|
308
|
+
title: string; author?: string; theme?: DocTheme; references?: ReferenceMap; citationStyle?: CitationStyle; children: ReactNode;
|
|
309
|
+
}) {
|
|
310
|
+
runtime.documentCount++;
|
|
311
|
+
if (!documentThemeAssets(theme) && globals.__opendocDocumentAssets) theme = withDocumentAssets(theme, globals.__opendocDocumentAssets);
|
|
312
|
+
runtime.managed = documentThemeAssets(theme);
|
|
313
|
+
// Authored theme registrations keep their own bounded contract. Each managed
|
|
314
|
+
// family is separately validated and may contribute up to 24 additional faces.
|
|
315
|
+
validateTheme(runtime.managed ? { ...theme, fonts: theme.fonts?.filter(font => !runtime.managed!.fonts.has(font.family)) } : theme);
|
|
316
|
+
requireText(title, 'Document title');
|
|
317
|
+
if (!['numeric', 'author-date'].includes(citationStyle)) throw new Error('citationStyle must be numeric or author-date.');
|
|
318
|
+
if (!references || typeof references !== 'object' || Array.isArray(references)) throw new Error('references must be an object of named source records.');
|
|
319
|
+
for (const [key, ref] of Object.entries(references)) {
|
|
320
|
+
checkId(key, 'Reference key');
|
|
321
|
+
if (!ref || typeof ref !== 'object') throw new Error(`Source ${key} must be a reference record.`);
|
|
322
|
+
requireText(ref.title, `Source ${key} title`);
|
|
323
|
+
if (ref.author !== undefined) requireText(ref.author, `Source ${key} author`);
|
|
324
|
+
if (ref.year !== undefined) requireText(ref.year, `Source ${key} year`);
|
|
325
|
+
if (ref.url !== undefined) {
|
|
326
|
+
try {
|
|
327
|
+
const url = new URL(ref.url);
|
|
328
|
+
if (!['https:', 'http:'].includes(url.protocol) || url.username || url.password) throw new Error();
|
|
329
|
+
} catch { throw new Error(`Source ${key} needs a valid http or https URL without credentials.`); }
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
runtime.theme = theme;
|
|
333
|
+
runtime.refs = references;
|
|
334
|
+
runtime.citationStyle = citationStyle;
|
|
335
|
+
const fonts: NonNullable<F.DocumentProps['fonts']> = ['Sans', 'Serif'].flatMap(family => ['Regular', 'Semibold', 'Italic', 'SemiboldItalic'].map(face => ({
|
|
336
|
+
family: `OpenDoc ${family}`,
|
|
337
|
+
src: resolve(globals.__opendocRoot ?? process.cwd(), 'assets/fonts', `OpenDoc${family}-${face}.ttf`),
|
|
338
|
+
fontWeight: face.startsWith('Semibold') ? 600 : 400,
|
|
339
|
+
fontStyle: face.endsWith('Italic') ? 'italic' as const : 'normal' as const,
|
|
340
|
+
})));
|
|
341
|
+
fonts.push({ family: 'OpenDoc Mono', src: resolve(globals.__opendocRoot ?? process.cwd(), 'assets/fonts/OpenDocMono-Regular.ttf'), fontWeight: 400, fontStyle: 'normal' });
|
|
342
|
+
for (const font of theme.fonts ?? []) {
|
|
343
|
+
const root = realpathSync(globals.__opendocRoot ?? process.cwd());
|
|
344
|
+
const path = resolve(root, font.src);
|
|
345
|
+
let local: string;
|
|
346
|
+
try { local = realpathSync(path); } catch { throw new Error(`Theme ${theme.id}: font file ${font.src} was not found.`); }
|
|
347
|
+
if (!local.startsWith(root + sep) || !statSync(local).isFile()) throw new Error(`Theme ${theme.id}: font ${font.src} must be a file inside this workspace.`);
|
|
348
|
+
const managedRoot = realpathSync(resolve(root, font.src.startsWith('assets/') ? 'assets' : `themes/${theme.id}`));
|
|
349
|
+
if (!local.startsWith(managedRoot + sep)) throw new Error(`Theme ${theme.id}: font ${font.src} must stay inside its theme or shared assets folder.`);
|
|
350
|
+
fonts.push({ ...font, src: local });
|
|
351
|
+
}
|
|
352
|
+
const registered = new Set(fonts.map(font => font.family));
|
|
353
|
+
const families = new Set([theme.body, theme.heading]);
|
|
354
|
+
function collectFonts(value: unknown) {
|
|
355
|
+
if (!value || typeof value !== 'object') return;
|
|
356
|
+
for (const [key, item] of Object.entries(value)) {
|
|
357
|
+
if (key === 'fontFamily' && typeof item === 'string') families.add(item);
|
|
358
|
+
else if (item && typeof item === 'object') collectFonts(item);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
collectFonts(theme.design);
|
|
362
|
+
for (const family of families) if (!registered.has(family)) throw new Error(`Theme ${theme.id}: register local font family ${family} in fonts.`);
|
|
363
|
+
return <F.Document title={title} author={author} lang="en" tagged fonts={fonts} style={{ fontFamily: theme.body, fontSize: theme.fontSize, color: theme.ink, lineHeight: theme.lineHeight }}>{children}</F.Document>;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/** Semantic families for theme components. Read inside the component's render function. */
|
|
367
|
+
export function documentFont(role: 'body' | 'heading') {
|
|
368
|
+
if (!runtime?.documentCount) throw new Error('documentFont must be called while rendering an OpenDoc document. Use it inside a component, not at module scope.');
|
|
369
|
+
return runtime.theme[role];
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export function Pages({ title, children, size, margin, header, footer, pageNumbers = true }: {
|
|
373
|
+
title: string; children: ReactNode; size?: F.PageProps['size']; margin?: F.PageProps['margin'];
|
|
374
|
+
header?: string | false; footer?: string | false; pageNumbers?: boolean;
|
|
375
|
+
}) {
|
|
376
|
+
const t = runtime.theme;
|
|
377
|
+
const furniture = t.design?.furniture;
|
|
378
|
+
const headerText = header === undefined ? (t.runningHeader ? (furniture?.uppercaseHeader === false ? title : title.toUpperCase()) : false) : header;
|
|
379
|
+
const footerText = footer === undefined ? (t.runningFooter ? t.footerLabel : false) : footer;
|
|
380
|
+
const furnitureText = { ...themeType(t, 'small', { fontSize: 8 }), ...furniture?.text };
|
|
381
|
+
return <Page {...themePage(t)} size={size ?? t.pageSize} margin={margin ?? themePage(t).margin}>
|
|
382
|
+
{headerText !== false && <F.Fixed position="header"><F.Text style={{ ...furnitureText, paddingBottom: 18, ...furniture?.header }}>{headerText}</F.Text></F.Fixed>}
|
|
383
|
+
{footerText !== false && <F.Fixed position="footer"><F.View style={{ borderTopWidth: 0.5, borderColor: t.line, paddingTop: 10, flexDirection: 'row', justifyContent: 'space-between', ...furniture?.footer }}>
|
|
384
|
+
<F.Text style={furnitureText}>{footerText}</F.Text>
|
|
385
|
+
{pageNumbers && <F.Text style={furnitureText}>{furniture?.pageNumber === 'page' ? '{{pageNumber}}' : '{{pageNumber}} / {{totalPages}}'}</F.Text>}
|
|
386
|
+
</F.View></F.Fixed>}{children}
|
|
387
|
+
</Page>;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export function Cover({ eyebrow, title, subtitle, footer, idPrefix = 'cover' }: {
|
|
391
|
+
eyebrow: string; title: string; subtitle: string; footer: string; idPrefix?: string;
|
|
392
|
+
}) {
|
|
393
|
+
const t = runtime.theme;
|
|
394
|
+
checkId(idPrefix, 'Cover prefix');
|
|
395
|
+
requireText(title, 'Cover title');
|
|
396
|
+
return <Page {...themePage(t, { top: t.margin, left: t.margin, right: t.margin, bottom: t.margin + 36 })} style={{ backgroundColor: t.paper, ...t.design?.page?.style, ...t.design?.cover?.page }}>
|
|
397
|
+
<Paragraph id={`${idPrefix}-eyebrow`} style={{ ...themeType(t, 'label', { fontSize: 10, letterSpacing: 1.6 }), ...t.design?.title?.eyebrow, ...t.design?.cover?.eyebrow }}>{t.design?.title?.uppercaseEyebrow === false ? <TextSlot slot="eyebrow" from="eyebrow">{eyebrow}</TextSlot> : eyebrow.toUpperCase()}</Paragraph>
|
|
398
|
+
<F.View style={{ marginTop: 72, borderTopWidth: 1, borderColor: t.accent, paddingTop: 26, ...t.design?.cover?.block }}>
|
|
399
|
+
<TextSlot slot="title" from="title"><Heading id={`${idPrefix}-title`} level={1} baseStyle={{ fontSize: 44, lineHeight: 1.1, marginBottom: 26 }} style={t.design?.cover?.title}>{title}</Heading></TextSlot>
|
|
400
|
+
<TextSlot slot="subtitle" from="subtitle"><Paragraph id={`${idPrefix}-subtitle`} style={{ ...themeType(t, 'lead', { fontSize: 16, lineHeight: 1.5, color: t.muted }), ...t.design?.cover?.subtitle }}>{subtitle}</Paragraph></TextSlot>
|
|
401
|
+
</F.View>
|
|
402
|
+
<F.Fixed position="footer"><TextSlot slot="footer" from="footer"><Paragraph id={`${idPrefix}-footer`} style={{ ...themeType(t, 'small', { fontSize: 10, color: t.accent }), marginBottom: 0, ...t.design?.furniture?.text, ...t.design?.cover?.footer }}>{footer}</Paragraph></TextSlot></F.Fixed>
|
|
403
|
+
</Page>;
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export const Heading = block(function Heading({ level = 2, children, style, baseStyle, bookmark }: Props & { level?: 1 | 2 | 3; baseStyle?: F.Style; bookmark?: string | false }) {
|
|
407
|
+
const t = runtime.theme;
|
|
408
|
+
const Tag = level === 1 ? F.H1 : level === 2 ? F.H2 : F.H3;
|
|
409
|
+
return <Tag bookmark={bookmark === false ? undefined : bookmark ?? plainText(children).trim()} style={{ ...themeType(t, `h${level}`, baseStyle), ...style }}><TextSlot slot="children" from="children">{children}</TextSlot></Tag>;
|
|
410
|
+
}, 'heading');
|
|
411
|
+
|
|
412
|
+
export const Paragraph = block(function Paragraph({ children, style, baseStyle, role, href }: Props & { role?: ThemeTypeRole; baseStyle?: F.Style }) {
|
|
413
|
+
return <F.Text href={href} style={{ marginBottom: runtime.theme.paragraphGap, minWidowLines: 2, minOrphanLines: 2, ...baseStyle, ...(role && runtime.theme.design?.typography?.[role]), ...style }}><TextSlot slot="children" from="children">{children}</TextSlot></F.Text>;
|
|
414
|
+
}, 'paragraph');
|
|
415
|
+
|
|
416
|
+
/** Continuous prose: flush openings, fixed first-line indents, optional paragraph spacing in points. */
|
|
417
|
+
export function Prose({ children }: { children: ReactNode; paragraphGap?: number }) { return <>{children}</>; }
|
|
418
|
+
|
|
419
|
+
export const Block = block(function Block({ children, style }: Props) { return <F.View style={style}>{children}</F.View>; }, 'block');
|
|
420
|
+
|
|
421
|
+
/** A short opening stays with its heading; the rest of the section remains normal flowing content. */
|
|
422
|
+
export const Section = block(function Section({ id, title, lead, level = 2, children, style }: Props & { title: ReactNode; lead: ReactNode; level?: 1 | 2 | 3 }) {
|
|
423
|
+
const leadText = plainText(lead);
|
|
424
|
+
if (!leadText.trim()) throw new Error(`Section ${id} needs a short lead paragraph.`);
|
|
425
|
+
if (leadText.length > 700) throw new Error(`Section ${id} lead is too long to keep with its heading. Use a short opening of up to 700 characters, then put the remaining content inside the section.`);
|
|
426
|
+
return <F.View style={style}><F.View wrap={false}><TextSlot slot="title" from="title"><Heading id={`${id}-heading`} level={level}>{title}</Heading></TextSlot><TextSlot slot="lead" from="lead"><Paragraph id={`${id}-lead`}>{lead}</Paragraph></TextSlot></F.View>{children}</F.View>;
|
|
427
|
+
}, 'section');
|
|
428
|
+
|
|
429
|
+
export const TitleBlock = block(function TitleBlock({ id, eyebrow, title, subtitle, byline, style }: Props & { eyebrow?: string; title: ReactNode; subtitle?: ReactNode; byline?: ReactNode }) {
|
|
430
|
+
requireText(plainText(title), `TitleBlock ${id} title`);
|
|
431
|
+
const t = runtime.theme;
|
|
432
|
+
const titleDesign = t.design?.title;
|
|
433
|
+
return <F.View wrap={false} style={{ marginBottom: 24, paddingBottom: 18, borderBottomWidth: 0.6, borderColor: t.line, ...titleDesign?.block, ...style }}>
|
|
434
|
+
{eyebrow && <F.Text style={{ ...themeType(t, 'label', { marginBottom: 12 }), ...titleDesign?.eyebrow }}>{titleDesign?.uppercaseEyebrow === false ? <TextSlot slot="eyebrow" from="eyebrow">{eyebrow}</TextSlot> : eyebrow.toUpperCase()}</F.Text>}
|
|
435
|
+
<TextSlot slot="title" from="title"><Heading id={`${id}-title`} level={1} baseStyle={{ fontSize: 32, marginBottom: 12 }} style={titleDesign?.heading}>{title}</Heading></TextSlot>
|
|
436
|
+
{subtitle && <TextSlot slot="subtitle" from="subtitle"><Paragraph id={`${id}-subtitle`} style={{ ...themeType(t, 'lead', { marginBottom: byline ? 12 : 0 }), ...titleDesign?.subtitle }}>{subtitle}</Paragraph></TextSlot>}
|
|
437
|
+
{byline && <TextSlot slot="byline" from="byline"><Paragraph id={`${id}-byline`} style={{ ...themeType(t, 'small', { fontSize: 9, marginBottom: 0 }), ...titleDesign?.byline }}>{byline}</Paragraph></TextSlot>}
|
|
438
|
+
</F.View>;
|
|
439
|
+
}, 'title');
|
|
440
|
+
|
|
441
|
+
export const Callout = block(function Callout({ title, children, style }: Props & { title?: string }) {
|
|
442
|
+
const t = runtime.theme;
|
|
443
|
+
const inlineTypes = new Set<unknown>([F.Text, F.Em, F.Strong, Strong, F.Link, Cite, CrossReference, Note, TextSlot]);
|
|
444
|
+
const content: ReactNode[] = [];
|
|
445
|
+
let inline: ReactNode[] = [];
|
|
446
|
+
function flush() {
|
|
447
|
+
if (inline.length) content.push(<F.Text key={`inline-${content.length}`} style={{ fontSize: t.fontSize, minWidowLines: 2, minOrphanLines: 2, ...t.design?.callout?.text }}>{inline}</F.Text>);
|
|
448
|
+
inline = [];
|
|
449
|
+
}
|
|
450
|
+
function collect(node: ReactNode) {
|
|
451
|
+
if (Array.isArray(node)) { node.forEach(collect); return; }
|
|
452
|
+
if (isValidElement<{ children?: ReactNode }>(node) && node.type === React.Fragment) { collect(node.props.children); return; }
|
|
453
|
+
if (!isValidElement(node) || inlineTypes.has(node.type)) inline.push(node);
|
|
454
|
+
else { flush(); content.push(node); }
|
|
455
|
+
}
|
|
456
|
+
const authored = Array.isArray(children) ? children : [children];
|
|
457
|
+
authored.forEach((child, childIndex) => collect(typeof child === 'string' || typeof child === 'number'
|
|
458
|
+
? <TextSlot slot="children" from="children" childIndex={childIndex}>{child}</TextSlot> : child));
|
|
459
|
+
flush();
|
|
460
|
+
return <F.View style={{ backgroundColor: t.paper, borderLeftWidth: 2, borderColor: t.accent, padding: 14, marginTop: 6, marginBottom: 18, ...t.design?.callout?.text, ...t.design?.callout?.block, ...style }}>
|
|
461
|
+
{title && <F.Text style={{ ...themeType(t, 'label', { fontWeight: 600, fontSize: 10, letterSpacing: 0 }), marginBottom: 6, ...t.design?.callout?.title }}><TextSlot slot="title" from="title">{title}</TextSlot></F.Text>}
|
|
462
|
+
{content}
|
|
463
|
+
</F.View>;
|
|
464
|
+
}, 'callout');
|
|
465
|
+
|
|
466
|
+
const ListEntry = block(function ListEntry({ id, children, ordered, number }: Props & { ordered: boolean; number: number }) {
|
|
467
|
+
if (!plainText(children).trim()) throw new Error(`List item ${id} needs nonempty text.`);
|
|
468
|
+
// Native Forme lists mismeasure wrapped items and can panic on page breaks.
|
|
469
|
+
// Normal flex rows use the same text width during measurement and drawing.
|
|
470
|
+
const design = runtime.theme.design?.list;
|
|
471
|
+
const markerWidth = Math.max(design?.markerWidth ?? 18, String(number).length * runtime.theme.fontSize * 0.7);
|
|
472
|
+
return <F.View wrap={false} style={{ flexDirection: 'row', gap: design?.gap ?? 8, marginBottom: 7, ...design?.item }}>
|
|
473
|
+
<F.Text style={{ width: markerWidth, flexShrink: 0 }}>{ordered ? `${number}.` : '•'}</F.Text>
|
|
474
|
+
<F.Text style={{ flex: 1, minWidth: 0, minWidowLines: 2, minOrphanLines: 2, lineBreaking: 'greedy' }}>{children}</F.Text>
|
|
475
|
+
</F.View>;
|
|
476
|
+
}, 'list-item');
|
|
477
|
+
export const List = block(function List({ id, items, ordered = false, start = 1, style }: {
|
|
478
|
+
id: string; items: { id: string; children: ReactNode }[]; ordered?: boolean; start?: number; style?: F.Style;
|
|
479
|
+
}) {
|
|
480
|
+
if (!Array.isArray(items) || !items.length) throw new Error(`List ${id} needs at least one item. Omit the List when there is nothing to show.`);
|
|
481
|
+
if (!Number.isSafeInteger(start) || start < 1) throw new Error(`List ${id} start must be a positive integer.`);
|
|
482
|
+
const children = items.map((item, index) => { checkId(item.id, `List ${id} item`); return <ListEntry id={`${id}-${item.id}`} key={item.id} ordered={ordered} number={start + index}>{item.children}</ListEntry>; });
|
|
483
|
+
const listStyle: F.Style = { marginBottom: runtime.theme.paragraphGap, paddingLeft: 18, ...runtime.theme.design?.list?.block, ...style };
|
|
484
|
+
return <F.View style={listStyle}>{children}</F.View>;
|
|
485
|
+
}, 'list');
|
|
486
|
+
|
|
487
|
+
export const CodeBlock = block(function CodeBlock({ id, children, language, caption, style, tabSize = 4 }: {
|
|
488
|
+
id: string; children: string; language?: string; caption?: string; style?: F.Style; tabSize?: 2 | 4 | 8;
|
|
489
|
+
}) {
|
|
490
|
+
if (typeof children !== 'string' || !children.trim()) throw new Error(`CodeBlock ${id} needs nonempty source text.`);
|
|
491
|
+
if (/[\u0000-\u0008\u000b\u000c\u000e-\u001f]/.test(children)) throw new Error(`CodeBlock ${id} contains unsupported control characters.`);
|
|
492
|
+
if (![2, 4, 8].includes(tabSize)) throw new Error(`CodeBlock ${id} tabSize must be 2, 4, or 8.`);
|
|
493
|
+
const t = runtime.theme;
|
|
494
|
+
const code = children.replace(/\r\n?/g, '\n').replace(/\t/g, ' '.repeat(tabSize));
|
|
495
|
+
return <F.View style={{ marginTop: 8, marginBottom: 18, ...t.design?.code?.block, ...style }}>
|
|
496
|
+
{(caption || language) && <F.Text style={themeType(t, 'caption', { marginBottom: 7 })}><TextSlot slot="caption" from="caption">{caption}</TextSlot>{caption && language ? ' / ' : ''}<TextSlot slot="language" from="language">{language}</TextSlot></F.Text>}
|
|
497
|
+
<F.View style={{ padding: 14, backgroundColor: t.paper, ...t.design?.code?.panel }}><F.Text style={{ ...themeType(t, 'code'), minWidowLines: 2, minOrphanLines: 2, hyphens: 'none', lineBreaking: 'greedy' }}><TextSlot slot="children" from="children">{code}</TextSlot></F.Text></F.View>
|
|
498
|
+
</F.View>;
|
|
499
|
+
}, 'code');
|
|
500
|
+
|
|
501
|
+
function numberedCaption(id: string, caption: string) {
|
|
502
|
+
const label = runtime.targets.get(id)!.label;
|
|
503
|
+
// Preserve the original API, whose callers supplied their own numbered captions.
|
|
504
|
+
const numbered = caption.match(/^(Figure|Table)\s+\d+[.:]\s/i);
|
|
505
|
+
if (numbered) {
|
|
506
|
+
if (numbered[0].replace(/[.:]\s$/, '').toLowerCase() !== label.toLowerCase()) throw new Error(`${id} caption numbering does not match ${label}. Remove the manual number and OpenDoc will keep the caption and cross-references in sync.`);
|
|
507
|
+
return <TextSlot slot="caption" from="caption">{caption}</TextSlot>;
|
|
508
|
+
}
|
|
509
|
+
return <><TextSlot slot="caption-number" reason="Numbered label. Ask your agent to change the figure or table order.">{label}. </TextSlot><TextSlot slot="caption" from="caption">{caption}</TextSlot></>;
|
|
510
|
+
}
|
|
511
|
+
export const Figure = block(function Figure({ id, children, caption, sourceNote, style }: Props & { caption: string; sourceNote?: string }) {
|
|
512
|
+
const t = runtime.theme;
|
|
513
|
+
requireText(caption, `Figure ${id} caption`);
|
|
514
|
+
return <F.View wrap={false} style={{ marginTop: 8, marginBottom: 18, ...t.design?.figure?.block, ...style }}>{children}
|
|
515
|
+
<F.Text style={themeType(t, 'caption', { marginTop: 8 })}>{numberedCaption(id, caption)}</F.Text>
|
|
516
|
+
{sourceNote && <F.Text style={themeType(t, 'small', { marginTop: 4 })}><TextSlot slot="sourceNote" from="sourceNote">{sourceNote}</TextSlot></F.Text>}
|
|
517
|
+
</F.View>;
|
|
518
|
+
}, 'figure');
|
|
519
|
+
|
|
520
|
+
export type TableColumn = { label: string; width?: number; align?: 'left' | 'center' | 'right' };
|
|
521
|
+
export const DataTable = block(function DataTable({ id, columns, rows, rowIds, caption, sourceNote, emptyMessage = 'No records to display.', style }: {
|
|
522
|
+
id: string; columns: TableColumn[]; rows: (string | number | ReactElement<TextSlotProps>)[][]; rowIds?: string[]; caption?: string; sourceNote?: string; emptyMessage?: string; style?: F.Style;
|
|
523
|
+
}) {
|
|
524
|
+
const t = runtime.theme;
|
|
525
|
+
if (!Array.isArray(columns) || !columns.length) throw new Error(`Table ${id} needs at least one column.`);
|
|
526
|
+
if (columns.some(column => !column || typeof column !== 'object' || Array.isArray(column))) throw new Error(`Table ${id} columns must be objects with a label and optional width or alignment.`);
|
|
527
|
+
const total = columns.reduce((sum, c) => sum + (c.width ?? 1), 0);
|
|
528
|
+
if (columns.some(c => !Number.isFinite(c.width ?? 1) || (c.width ?? 1) <= 0) || !Number.isFinite(total)) throw new Error('Table columns need positive finite widths.');
|
|
529
|
+
for (const column of columns) {
|
|
530
|
+
requireText(column.label, `Table ${id} column label`);
|
|
531
|
+
if (column.align !== undefined && !['left', 'center', 'right'].includes(column.align)) throw new Error(`Table ${id} column alignment must be left, center, or right.`);
|
|
532
|
+
}
|
|
533
|
+
if (!Array.isArray(rows) || rows.some(row => !Array.isArray(row) || row.length !== columns.length)) throw new Error('Every table row must match the number of columns.');
|
|
534
|
+
rows.forEach((row, r) => row.forEach((value, c) => {
|
|
535
|
+
if ((typeof value !== 'string' && typeof value !== 'number' && !(isValidElement(value) && value.type === TextSlot)) || (typeof value === 'number' && !Number.isFinite(value))) throw new Error(`Table ${id} row ${r + 1}, column ${c + 1} must be text or a finite number.`);
|
|
536
|
+
}));
|
|
537
|
+
if (rowIds && (rowIds.length !== rows.length || new Set(rowIds).size !== rowIds.length || rowIds.some(value => typeof value !== 'string' || !value))) throw new Error(`Table ${id} rowIds must identify every row uniquely.`);
|
|
538
|
+
requireText(emptyMessage, `Table ${id} emptyMessage`);
|
|
539
|
+
const alignments = columns.map((column, i) => column.align ?? (rows.length && rows.every(row => typeof row[i] === 'number') ? 'right' : 'left'));
|
|
540
|
+
const noteStyle = themeType(t, 'caption');
|
|
541
|
+
const design = t.design?.table;
|
|
542
|
+
const alternate = design?.alternate === false ? '#ffffff' : design?.alternate ?? t.paper;
|
|
543
|
+
return <F.Table columns={columns.map(c => ({ width: { fraction: (c.width ?? 1) / total } }))} style={{ marginTop: 8, marginBottom: 16, ...design?.block, ...style }}>
|
|
544
|
+
{caption && <F.Row header><F.Cell colSpan={columns.length} style={{ paddingBottom: 8 }}><F.Text style={{ ...noteStyle, color: t.ink, fontWeight: 600 }}>{numberedCaption(id, caption)}</F.Text></F.Cell></F.Row>}
|
|
545
|
+
<F.Row header style={{ backgroundColor: t.accent, ...design?.header }}>{columns.map((col, i) => <F.Cell key={i} style={{ padding: 9, ...design?.cell, ...design?.header }}><F.Text style={{ fontFamily: 'OpenDoc Sans', fontSize: 9, color: '#ffffff', fontWeight: 600, ...design?.headerText, textAlign: alignments[i] }}>{col.label}</F.Text></F.Cell>)}</F.Row>
|
|
546
|
+
{rows.length ? rows.map((row, i) => <F.Row key={rowIds?.[i] ?? i} style={{ backgroundColor: i % 2 === 0 ? alternate : '#ffffff' }}>{row.map((cell, j) => <F.Cell key={j} style={{ padding: 9, borderBottomWidth: 0.4, borderColor: t.line, ...design?.cell }}><F.Text style={{ fontFamily: 'OpenDoc Sans', fontSize: 10, ...design?.text, textAlign: alignments[j] }}>{isValidElement(cell) ? cell : <TextSlot slot={`row-${rowIds?.[i] ?? i}-column-${j}`} stable={false}>{typeof cell === 'number' ? String(cell) : cell}</TextSlot>}</F.Text></F.Cell>)}</F.Row>) : <F.Row><F.Cell colSpan={columns.length} style={{ padding: 12, backgroundColor: t.paper }}><F.Text style={noteStyle}>{emptyMessage}</F.Text></F.Cell></F.Row>}
|
|
547
|
+
{sourceNote && <F.Row><F.Cell colSpan={columns.length} style={{ paddingTop: 8 }}><F.Text style={noteStyle}><TextSlot slot="sourceNote" from="sourceNote">{sourceNote}</TextSlot></F.Text></F.Cell></F.Row>}
|
|
548
|
+
</F.Table>;
|
|
549
|
+
}, 'table');
|
|
550
|
+
|
|
551
|
+
function citationYear(key: string) {
|
|
552
|
+
const ref = runtime.refs[key];
|
|
553
|
+
const duplicates = runtime.cited.filter(other => runtime.refs[other].author === ref.author && runtime.refs[other].year === ref.year);
|
|
554
|
+
let suffix = '';
|
|
555
|
+
if (duplicates.length > 1) {
|
|
556
|
+
let index = duplicates.indexOf(key) + 1;
|
|
557
|
+
while (index > 0) { index--; suffix = String.fromCharCode(97 + index % 26) + suffix; index = Math.floor(index / 26); }
|
|
558
|
+
}
|
|
559
|
+
return `${ref.year}${suffix}`;
|
|
560
|
+
}
|
|
561
|
+
function authorDate(key: string) {
|
|
562
|
+
return `${runtime.refs[key].author}, ${citationYear(key)}`;
|
|
563
|
+
}
|
|
564
|
+
export function Cite({ source, locator }: { source: string; locator?: string }) {
|
|
565
|
+
if (locator !== undefined) requireText(locator, `Citation ${source} locator`);
|
|
566
|
+
const detail = locator ? `, ${locator}` : '';
|
|
567
|
+
const label = runtime.citationStyle === 'author-date' ? `(${authorDate(source)}${detail})` : `[${runtime.cited.indexOf(source) + 1}${detail}]`;
|
|
568
|
+
return <F.Text style={{ color: runtime.theme.accent }}>{label}</F.Text>;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/** Labels are resolved from existing targets; this deliberately does not invent page numbers. */
|
|
572
|
+
export function CrossReference({ target, children }: { target: string; children?: ReactNode }) {
|
|
573
|
+
const found = runtime.targets.get(target);
|
|
574
|
+
if (!found) throw new Error(`Unknown cross-reference target: ${target}. Use a heading, section, figure, or table id from this document.`);
|
|
575
|
+
if (found.needsCaption) throw new Error(`Cross-reference target ${target} needs a table caption so readers can find ${found.label}.`);
|
|
576
|
+
return <F.Text style={{ color: runtime.theme.accent }}>{children ?? found.label}</F.Text>;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
export function References({ title = 'References', id = 'references', headingStyle, headingBaseStyle }: { title?: string; id?: string; headingStyle?: F.Style; headingBaseStyle?: F.Style } = {}) {
|
|
580
|
+
const entries = runtime.cited.map((key, i) => {
|
|
581
|
+
const ref = runtime.refs[key];
|
|
582
|
+
const prefix = runtime.citationStyle === 'author-date' ? `${ref.author} (${citationYear(key)}). ` : `[${i + 1}] ${ref.author ? `${ref.author}. ` : ''}`;
|
|
583
|
+
return <Paragraph key={key} id={`reference-${key}`} href={ref.url} role="small" baseStyle={{ fontSize: 9.5 }} style={{ color: ref.url ? runtime.theme.accent : runtime.theme.ink }}>{prefix}{ref.title}{runtime.citationStyle === 'numeric' && ref.year ? ` (${ref.year})` : ''}.{ref.url ? ` ${ref.url}` : ''}</Paragraph>;
|
|
584
|
+
});
|
|
585
|
+
if (!entries.length) return null;
|
|
586
|
+
return plainText(entries[0]).length <= 700
|
|
587
|
+
? <><F.View wrap={false}><Heading id={id} baseStyle={headingBaseStyle} style={headingStyle}>{title}</Heading>{entries[0]}</F.View>{entries.slice(1)}</>
|
|
588
|
+
: <><Heading id={id} baseStyle={headingBaseStyle} style={headingStyle}>{title}</Heading>{entries}</>;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/** Inline markers with an explicit Notes section; these are endnotes, never simulated page footnotes. */
|
|
592
|
+
export function Note({ id }: { id: string; children: ReactNode }) {
|
|
593
|
+
const number = runtime.notes.findIndex(note => note.id === id) + 1;
|
|
594
|
+
return <F.Text style={{ color: runtime.theme.accent, fontSize: runtime.theme.fontSize * 0.8 }}> [note {number}]</F.Text>;
|
|
595
|
+
}
|
|
596
|
+
export function Notes({ title = 'Notes', id = 'notes' }: { title?: string; id?: string } = {}) {
|
|
597
|
+
const entries = runtime.notes.map((note, i) => <Paragraph key={note.id} id={`note-${note.id}`} role="small" baseStyle={{ fontSize: 9.5 }}>[note {i + 1}] {note.children}</Paragraph>);
|
|
598
|
+
if (!entries.length) return null;
|
|
599
|
+
return plainText(entries[0]).length <= 700
|
|
600
|
+
? <><F.View wrap={false}><Heading id={id}>{title}</Heading>{entries[0]}</F.View>{entries.slice(1)}</>
|
|
601
|
+
: <><Heading id={id}>{title}</Heading>{entries}</>;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
/** Embed a document-owned image by its media folder identity. */
|
|
605
|
+
export function Media({ item, width, height, alt, style }: { item: string; width: number; height?: number; alt?: string; style?: F.Style }) {
|
|
606
|
+
if (!Number.isFinite(width) || width <= 0 || (height !== undefined && (!Number.isFinite(height) || height <= 0))) throw new Error('Media needs a positive display width and optional positive height in points.');
|
|
607
|
+
const asset = readyMedia(item);
|
|
608
|
+
const scale = Math.min(width / asset.width, (height ?? Infinity) / asset.height);
|
|
609
|
+
return <F.Image src={mediaUri(asset)} alt={alt ?? asset.meta.alt ?? asset.meta.description}
|
|
610
|
+
style={{ ...style, width: asset.width * scale, height: asset.height * scale }} />;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
/** An exact image box: crop without distortion, or contain the whole image with transparent padding. */
|
|
614
|
+
export function MediaFrame({ item, alt, style, ...frame }: ImageFrameOptions & { item: string; alt?: string; style?: F.Style }) {
|
|
615
|
+
const asset = readyMedia(item);
|
|
616
|
+
return <F.Image src={framedMedia(asset, frame)} alt={alt ?? asset.meta.alt ?? asset.meta.description}
|
|
617
|
+
style={{ ...style, width: frame.width, height: frame.height }} />;
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/** Embed a saved logo binding. Variation guidance is a choice for the authoring agent. */
|
|
621
|
+
export function Logo({ name, variation, width, height, alt, style }: { name?: string; variation?: string; width: number; height?: number; alt?: string; style?: F.Style }) {
|
|
622
|
+
if (!runtime?.managed) throw new Error('This document has no saved asset selections. Bind a logo with npx opendoc assets before using Logo.');
|
|
623
|
+
if (!Number.isFinite(width) || width <= 0 || (height !== undefined && (!Number.isFinite(height) || height <= 0))) throw new Error('Logo needs a positive width and optional positive height in points.');
|
|
624
|
+
const bindings = runtime.managed.bindings;
|
|
625
|
+
const binding = name ? (Object.hasOwn(bindings.logos ?? {}, name) ? bindings.logos![name] : undefined) : bindings.logo;
|
|
626
|
+
if (!binding) throw new Error(name ? `No logo is bound as “${name}”. Inspect this document's assets and bind that name before using it.` : 'This document has no preferred logo. Bind a logo with npx opendoc assets before using <Logo />.');
|
|
627
|
+
const root = globals.__opendocRoot ?? process.cwd();
|
|
628
|
+
const asset = readAssetRevision(root, 'logo', binding.id, binding.revision);
|
|
629
|
+
if (asset.kind !== 'logo') throw new Error(`Asset ${binding.id} is not a logo.`);
|
|
630
|
+
const selected = variation ?? binding.variation ?? asset.defaultVariation;
|
|
631
|
+
const artwork = asset.variations.find(item => item.id === selected);
|
|
632
|
+
if (!artwork) throw new Error(`Logo “${asset.name}” has no variation “${selected}” in saved revision ${asset.revision}. Available variations: ${asset.variations.map(item => item.id).join(', ')}. Use one of these IDs or rebind to a newer revision.`);
|
|
633
|
+
const file = assetFile(root, 'logo', asset.id, artwork.image);
|
|
634
|
+
runtime.managed.dependencies.add(relative(root, assetRevisionPath(root, 'logo', asset.id, asset.revision)));
|
|
635
|
+
runtime.managed.dependencies.add(relative(root, file));
|
|
636
|
+
runtime.assets.push({ kind: 'logo', id: asset.id, revision: asset.revision, variation: artwork.id, ...(runtime.currentBlock ? { blockId: runtime.currentBlock } : {}) });
|
|
637
|
+
if (!artwork.image.width || !artwork.image.height) throw new Error(`Logo “${asset.name}” has no prepared dimensions. Reimport this variation.`);
|
|
638
|
+
const scale = Math.min(width / artwork.image.width, (height ?? Infinity) / artwork.image.height);
|
|
639
|
+
return <F.Image src={`data:${artwork.image.mime};base64,${readFileSync(file).toString('base64')}`} alt={alt ?? `${asset.name} — ${artwork.name}`}
|
|
640
|
+
style={{ ...style, width: artwork.image.width * scale, height: artwork.image.height * scale }} />;
|
|
641
|
+
}
|