@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,187 @@
|
|
|
1
|
+
import { constants, lstatSync, readFileSync, unlinkSync } from 'node:fs';
|
|
2
|
+
import { lstat, mkdir, open, readFile, realpath, unlink } from 'node:fs/promises';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { setTimeout } from 'node:timers/promises';
|
|
5
|
+
import { readTheme } from './themes';
|
|
6
|
+
import { emptyProjects, type Project, type ProjectsManifest } from '../shared/projects';
|
|
7
|
+
import { atomicWrite } from './files';
|
|
8
|
+
import { documentEntry, validId } from './render';
|
|
9
|
+
|
|
10
|
+
export class ProjectError extends Error {
|
|
11
|
+
constructor(message: string, public status: 400 | 404 | 409 = 400) { super(message); this.name = 'ProjectError'; }
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function record(value: unknown): value is Record<string, unknown> {
|
|
15
|
+
return Boolean(value && typeof value === 'object' && !Array.isArray(value));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** A terminated CLI must not leave all later project changes permanently busy. */
|
|
19
|
+
function releaseDeadProjectLock(lock: string) {
|
|
20
|
+
try {
|
|
21
|
+
const before = lstatSync(lock);
|
|
22
|
+
if (!before.isFile() || before.isSymbolicLink() || before.size > 128) return;
|
|
23
|
+
let pid: number | undefined;
|
|
24
|
+
try { pid = (JSON.parse(readFileSync(lock, 'utf8')) as { pid?: number } | null)?.pid; }
|
|
25
|
+
catch (error) { if (!(error instanceof SyntaxError)) throw error; }
|
|
26
|
+
if (Number.isSafeInteger(pid) && pid! > 0) {
|
|
27
|
+
try { process.kill(pid!, 0); return; }
|
|
28
|
+
catch (error) { if ((error as NodeJS.ErrnoException).code !== 'ESRCH') return; }
|
|
29
|
+
} else if (Date.now() - before.mtimeMs < 30_000) return;
|
|
30
|
+
const current = lstatSync(lock);
|
|
31
|
+
if (current.dev === before.dev && current.ino === before.ino && current.mtimeMs === before.mtimeMs) unlinkSync(lock);
|
|
32
|
+
} catch (error) {
|
|
33
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') throw error;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function projectName(value: unknown) {
|
|
37
|
+
if (typeof value !== 'string' || !value.trim() || value.trim().length > 120 || /[\u0000-\u001f\u007f\u2028\u2029]/u.test(value)) throw new ProjectError('Give the project a name of 120 characters or fewer, on one line.');
|
|
38
|
+
return value.trim();
|
|
39
|
+
}
|
|
40
|
+
export async function selectedTheme(root: string, value: unknown): Promise<string | null> {
|
|
41
|
+
if (value === null || value === undefined) return null;
|
|
42
|
+
if (typeof value !== 'string' || !validId(value)) throw new ProjectError('Choose an available theme.');
|
|
43
|
+
try { await readTheme(root, value); } catch { throw new ProjectError('Choose an available theme.'); }
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parseManifest(value: unknown): ProjectsManifest {
|
|
48
|
+
const invalid = () => new ProjectError('projects.json is invalid. Ask your agent to repair it; no project data was replaced.');
|
|
49
|
+
if (!record(value) || value.version !== 1 || !Array.isArray(value.projects) || !record(value.assignments) || Object.keys(value).some(key => !['version', 'projects', 'assignments', 'names', 'formats'].includes(key))) throw invalid();
|
|
50
|
+
const ids = new Set<string>();
|
|
51
|
+
const projects = value.projects.map((entry): Project => {
|
|
52
|
+
if (!record(entry) || typeof entry.id !== 'string' || !validId(entry.id) || entry.id.length > 80 || ids.has(entry.id) || Object.keys(entry).some(key => !['id', 'name', 'defaultTheme'].includes(key))) throw invalid();
|
|
53
|
+
ids.add(entry.id);
|
|
54
|
+
// Keep a removed custom theme readable. Creation will ask for an available replacement.
|
|
55
|
+
if (entry.defaultTheme !== null && (typeof entry.defaultTheme !== 'string' || !entry.defaultTheme.trim())) throw invalid();
|
|
56
|
+
return { id: entry.id, name: projectName(entry.name), defaultTheme: entry.defaultTheme as string | null };
|
|
57
|
+
});
|
|
58
|
+
const assignments: Record<string, string> = {};
|
|
59
|
+
for (const [id, projectId] of Object.entries(value.assignments)) {
|
|
60
|
+
if (!validId(id) || id.length > 80 || typeof projectId !== 'string' || !ids.has(projectId)) throw invalid();
|
|
61
|
+
assignments[id] = projectId;
|
|
62
|
+
}
|
|
63
|
+
const names: Record<string, string> = {};
|
|
64
|
+
if (value.names !== undefined) {
|
|
65
|
+
if (!record(value.names)) throw invalid();
|
|
66
|
+
for (const [id, name] of Object.entries(value.names)) {
|
|
67
|
+
if (!validId(id) || id.length > 80) throw invalid();
|
|
68
|
+
names[id] = documentDisplayName(name);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const formats: NonNullable<ProjectsManifest['formats']> = {};
|
|
72
|
+
if (value.formats !== undefined) {
|
|
73
|
+
if (!record(value.formats)) throw invalid();
|
|
74
|
+
for (const [id, format] of Object.entries(value.formats)) {
|
|
75
|
+
if (!validId(id) || id.length > 80 || !['document', 'presentation'].includes(format as string)) throw invalid();
|
|
76
|
+
formats[id] = format as 'document' | 'presentation';
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return { version: 1, projects, assignments, ...(value.names === undefined ? {} : { names }), ...(value.formats === undefined ? {} : { formats }) };
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function documentDisplayName(value: unknown) {
|
|
83
|
+
if (typeof value !== 'string' || !value.trim() || value.trim().length > 160 || /[\u0000-\u001f\u007f\u2028\u2029]/u.test(value)) throw new ProjectError('Give the document a name of 160 characters or fewer, on one line.');
|
|
84
|
+
return value.trim();
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
async function manifestFile(root: string) {
|
|
88
|
+
const file = resolve(await realpath(root), 'projects.json');
|
|
89
|
+
const info = await lstat(file).catch(error => { if (error.code === 'ENOENT') return null; throw error; });
|
|
90
|
+
if (info && (!info.isFile() || info.isSymbolicLink() || info.size > 1_000_000)) throw new ProjectError('projects.json must be a regular local file smaller than 1 MB.');
|
|
91
|
+
return { file, exists: Boolean(info) };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export async function readProjects(root: string): Promise<ProjectsManifest> {
|
|
95
|
+
const { file, exists } = await manifestFile(root);
|
|
96
|
+
if (!exists) return emptyProjects();
|
|
97
|
+
try { return parseManifest(JSON.parse(await readFile(file, 'utf8'))); }
|
|
98
|
+
catch (error) {
|
|
99
|
+
if (error instanceof ProjectError) throw error;
|
|
100
|
+
throw new ProjectError('Could not read projects.json. Ask your agent to repair it; no project data was replaced.');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Serialize browser and CLI changes, including document publication and its assignment. */
|
|
105
|
+
export async function withProjects<T>(root: string, change: (manifest: ProjectsManifest, save: () => Promise<void>) => Promise<T>): Promise<T> {
|
|
106
|
+
const workspace = await realpath(root);
|
|
107
|
+
const runtime = resolve(workspace, '.opendoc');
|
|
108
|
+
await mkdir(runtime, { recursive: true });
|
|
109
|
+
const lock = resolve(runtime, 'projects.lock');
|
|
110
|
+
let handle;
|
|
111
|
+
const started = Date.now();
|
|
112
|
+
while (!handle) {
|
|
113
|
+
try { handle = await open(lock, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | constants.O_NOFOLLOW, 0o600); }
|
|
114
|
+
catch (error) {
|
|
115
|
+
if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;
|
|
116
|
+
releaseDeadProjectLock(lock);
|
|
117
|
+
if (Date.now() - started > 5000) throw new ProjectError('Project files are busy. Try again when the other operation finishes.', 409);
|
|
118
|
+
await setTimeout(25);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
try {
|
|
122
|
+
await handle.writeFile(JSON.stringify({ pid: process.pid }));
|
|
123
|
+
const manifest = await readProjects(workspace);
|
|
124
|
+
return await change(manifest, async () => {
|
|
125
|
+
const { file } = await manifestFile(workspace);
|
|
126
|
+
await atomicWrite(file, `${JSON.stringify(parseManifest(manifest), null, 2)}\n`);
|
|
127
|
+
});
|
|
128
|
+
} finally { await handle.close(); await unlink(lock); }
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function requireProject(manifest: ProjectsManifest, id: unknown): Project {
|
|
132
|
+
if (typeof id !== 'string' || !validId(id)) throw new ProjectError('Choose a project before creating a document.');
|
|
133
|
+
const project = manifest.projects.find(project => project.id === id);
|
|
134
|
+
if (!project) throw new ProjectError('This project no longer exists. Choose another project.', 404);
|
|
135
|
+
return project;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function createProject(root: string, input: unknown): Promise<Project> {
|
|
139
|
+
if (!record(input) || Object.keys(input).some(key => !['name', 'id', 'defaultTheme'].includes(key))) throw new ProjectError('Provide a project name and an optional default theme.');
|
|
140
|
+
const name = projectName(input.name);
|
|
141
|
+
const defaultTheme = await selectedTheme(root, input.defaultTheme);
|
|
142
|
+
const derived = name.normalize('NFKD').replace(/\p{M}/gu, '').toLowerCase().replace(/[’']/g, '').replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 80).replace(/-$/, '') || 'new-project';
|
|
143
|
+
const id = input.id ?? derived;
|
|
144
|
+
if (typeof id !== 'string' || !validId(id) || id.length > 80) throw new ProjectError('Use a project ID with lowercase letters, numbers, and single hyphens, up to 80 characters.');
|
|
145
|
+
return withProjects(root, async (manifest, save) => {
|
|
146
|
+
if (manifest.projects.some(project => project.id === id || project.name.toLowerCase() === name.toLowerCase())) throw new ProjectError('A project with this name or ID already exists. Choose another name.', 409);
|
|
147
|
+
const project = { id, name, defaultTheme };
|
|
148
|
+
manifest.projects.push(project); await save(); return project;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export async function updateProject(root: string, id: string, input: unknown): Promise<Project> {
|
|
153
|
+
if (!record(input) || !Object.keys(input).length || Object.keys(input).some(key => !['name', 'defaultTheme'].includes(key))) throw new ProjectError('Provide a project name or default theme.');
|
|
154
|
+
return withProjects(root, async (manifest, save) => {
|
|
155
|
+
const project = requireProject(manifest, id);
|
|
156
|
+
if (input.name !== undefined) {
|
|
157
|
+
const name = projectName(input.name);
|
|
158
|
+
if (manifest.projects.some(other => other.id !== id && other.name.toLowerCase() === name.toLowerCase())) throw new ProjectError('A project with this name already exists.', 409);
|
|
159
|
+
project.name = name;
|
|
160
|
+
}
|
|
161
|
+
if (Object.hasOwn(input, 'defaultTheme')) project.defaultTheme = await selectedTheme(root, input.defaultTheme);
|
|
162
|
+
await save(); return project;
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export async function assignProject(root: string, documentId: string, projectId: unknown) {
|
|
167
|
+
return withProjects(root, async (manifest, save) => {
|
|
168
|
+
const project = requireProject(manifest, projectId);
|
|
169
|
+
await documentEntry(root, documentId);
|
|
170
|
+
manifest.assignments[documentId] = project.id;
|
|
171
|
+
await save(); return { id: documentId, projectId: project.id };
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function deleteProject(root: string, id: string) {
|
|
176
|
+
return withProjects(root, async (manifest, save) => {
|
|
177
|
+
requireProject(manifest, id);
|
|
178
|
+
for (const [documentId, projectId] of Object.entries(manifest.assignments)) {
|
|
179
|
+
if (projectId !== id) continue;
|
|
180
|
+
const exists = await lstat(resolve(root, 'documents', documentId)).catch(error => { if (error.code === 'ENOENT') return null; throw error; });
|
|
181
|
+
if (exists) throw new ProjectError('Move this project’s documents to another project before deleting it.', 409);
|
|
182
|
+
delete manifest.assignments[documentId];
|
|
183
|
+
}
|
|
184
|
+
manifest.projects = manifest.projects.filter(project => project.id !== id);
|
|
185
|
+
await save();
|
|
186
|
+
});
|
|
187
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReviewIssue } from '../shared/types';
|
|
2
|
+
|
|
3
|
+
/** Preserve actionable layout diagnostics across the isolated render process. */
|
|
4
|
+
export class RenderFailure extends Error {
|
|
5
|
+
constructor(message: string, readonly issues: ReviewIssue[] = []) {
|
|
6
|
+
super(message);
|
|
7
|
+
this.name = 'RenderFailure';
|
|
8
|
+
}
|
|
9
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { writeFile, unlink } from 'node:fs/promises';
|
|
2
|
+
import { dirname, resolve } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { resolveAllSources } from '../rendering/resolve-sources';
|
|
6
|
+
import { renderPdfWithLayout } from '@formepdf/core';
|
|
7
|
+
import type { FormeDocument } from '@formepdf/react';
|
|
8
|
+
import { bundleSourceOverrides, type SourceOverride } from './source-overrides';
|
|
9
|
+
|
|
10
|
+
/** One serialization and asset resolution supplies both the PDF and editable exports. */
|
|
11
|
+
export async function renderDocumentSource(source: string, entry: string, overrides = new Map<string, SourceOverride>()) {
|
|
12
|
+
const directory = dirname(entry);
|
|
13
|
+
const code = await bundleSourceOverrides(`${source}\nexport { serialize as __serialize } from '@formepdf/react';`, entry, overrides);
|
|
14
|
+
const temporary = resolve(directory, `.forme-render-${randomUUID()}.mjs`);
|
|
15
|
+
await writeFile(temporary, code, { flag: 'wx' });
|
|
16
|
+
let doc: FormeDocument;
|
|
17
|
+
try {
|
|
18
|
+
const module = await import(pathToFileURL(temporary).href);
|
|
19
|
+
doc = module.__serialize(module.default());
|
|
20
|
+
} finally { await unlink(temporary); }
|
|
21
|
+
await resolveAllSources(doc as unknown as Record<string, unknown>, directory);
|
|
22
|
+
const result = await renderPdfWithLayout(JSON.stringify(doc));
|
|
23
|
+
return { ...result, doc, warnings: [] as string[] };
|
|
24
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { fork } from 'node:child_process';
|
|
2
|
+
import { mkdir, readFile, realpath, rm } from 'node:fs/promises';
|
|
3
|
+
import { resolve, relative, sep } from 'node:path';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { RenderFailure } from './render-error';
|
|
6
|
+
import type { RenderArtifact, ReviewIssue } from '../shared/types';
|
|
7
|
+
import type { SourceOverride } from './source-overrides';
|
|
8
|
+
import { runtimeResolve, runtimeSource } from '../runtime/paths';
|
|
9
|
+
|
|
10
|
+
export function validId(id: string) { return /^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(id); }
|
|
11
|
+
export async function documentEntry(root: string, id: string) {
|
|
12
|
+
if (!validId(id)) throw new Error('Invalid document ID.');
|
|
13
|
+
const docsRoot = await realpath(resolve(root, 'documents'));
|
|
14
|
+
const path = await realpath(resolve(docsRoot, id, 'index.tsx'));
|
|
15
|
+
const rel = relative(docsRoot, path);
|
|
16
|
+
if (rel.startsWith(`..${sep}`) || rel === '..' || resolve(docsRoot, rel) !== path) throw new Error('Document path is outside this workspace.');
|
|
17
|
+
return path;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function renderOnce(root: string, id: string, timeoutMs = 30_000, sourceOverrides?: SourceOverride[]): Promise<{ artifact: RenderArtifact; directory: string }> {
|
|
21
|
+
const entry = await documentEntry(root, id);
|
|
22
|
+
return renderEntry(root, entry, id, timeoutMs, sourceOverrides);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/** Render another workspace entry through the same isolated PDF worker. */
|
|
26
|
+
export async function renderEntry(root: string, entry: string, label: string, timeoutMs = 30_000, sourceOverrides?: SourceOverride[]): Promise<{ artifact: RenderArtifact; directory: string }> {
|
|
27
|
+
const directory = resolve(root, '.opendoc/renders', `${label}-${randomUUID()}`);
|
|
28
|
+
await mkdir(directory, { recursive: true });
|
|
29
|
+
try {
|
|
30
|
+
await new Promise<void>((accept, reject) => {
|
|
31
|
+
const child = fork(runtimeSource('server/worker.ts'), [entry, directory, ...(sourceOverrides ? ['--source-overrides'] : [])], {
|
|
32
|
+
cwd: root, execArgv: ['--import', runtimeResolve('tsx')], stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
|
|
33
|
+
});
|
|
34
|
+
let stderr = '', failure: string | undefined, issues: ReviewIssue[] = [], signaled = false;
|
|
35
|
+
child.stderr?.on('data', data => { stderr = (stderr + data.toString()).slice(-8000); });
|
|
36
|
+
const timer = setTimeout(() => { failure = `Rendering timed out after ${timeoutMs / 1000} seconds.`; child.kill('SIGKILL'); }, timeoutMs);
|
|
37
|
+
child.on('message', (message: { ok: boolean; error?: string; issues?: ReviewIssue[] }) => { signaled = message.ok; if (!message.ok) { failure = message.error; issues = message.issues ?? []; } });
|
|
38
|
+
child.on('error', error => { clearTimeout(timer); reject(error); });
|
|
39
|
+
child.on('exit', code => {
|
|
40
|
+
clearTimeout(timer);
|
|
41
|
+
if (code === 0 && signaled && !failure) accept();
|
|
42
|
+
else reject(new RenderFailure(failure || stderr || `Render worker exited unexpectedly (${code ?? 'signal'}).`, issues));
|
|
43
|
+
});
|
|
44
|
+
if (sourceOverrides) child.send({ sourceOverrides });
|
|
45
|
+
});
|
|
46
|
+
return { artifact: JSON.parse(await readFile(resolve(directory, 'artifact.json'), 'utf8')), directory };
|
|
47
|
+
} catch (error) { await rm(directory, { recursive: true, force: true }); throw error; }
|
|
48
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { parseArgs } from 'node:util';
|
|
4
|
+
import { reviewTarget, type ReviewTarget } from './review';
|
|
5
|
+
import { validId } from './render';
|
|
6
|
+
|
|
7
|
+
export const reviewUsage = 'Usage: npx opendoc review <document-id> [--json]\n npx opendoc review --theme <id> [--json]\n npx opendoc review --template <id> [--json]';
|
|
8
|
+
|
|
9
|
+
export function parseReviewArgs(args: string[]) {
|
|
10
|
+
const { values, positionals, tokens } = parseArgs({ args: args[0] === '--' ? args.slice(1) : args, allowPositionals: true, tokens: true, options: {
|
|
11
|
+
json: { type: 'boolean' }, help: { type: 'boolean', short: 'h' }, theme: { type: 'string' }, template: { type: 'string' },
|
|
12
|
+
} });
|
|
13
|
+
if (values.help) return { help: true as const, json: values.json };
|
|
14
|
+
if (positionals.length + tokens.filter(token => token.kind === 'option' && (token.name === 'theme' || token.name === 'template')).length !== 1) throw new Error(reviewUsage);
|
|
15
|
+
const target: ReviewTarget = values.theme !== undefined ? { kind: 'theme', id: values.theme } : values.template !== undefined ? { kind: 'template', id: values.template } : { kind: 'document', id: positionals[0] };
|
|
16
|
+
if (!validId(target.id)) throw new Error(reviewUsage);
|
|
17
|
+
return { help: false as const, json: values.json, target };
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function runReviewCli(args: string[], root = process.cwd()): Promise<void> {
|
|
21
|
+
const options = parseReviewArgs(args);
|
|
22
|
+
if (options.help) { console.log(options.json ? JSON.stringify({ usage: reviewUsage }, null, 2) : reviewUsage); return; }
|
|
23
|
+
const result = await reviewTarget(root, options.target);
|
|
24
|
+
if (options.json) console.log(JSON.stringify(result, null, 2));
|
|
25
|
+
else if (result.status === 'ready') {
|
|
26
|
+
console.log(`Prepared ${result.pageCount} pages for review: ${result.outputs.review}\nPDF: ${result.outputs.pdf}\nInspect every page image and check the content before delivery; visual and factual review are still required.`);
|
|
27
|
+
if (result.warning) console.error(result.warning);
|
|
28
|
+
} else console.error(`${result.id}: ${result.error}\n${result.previousOutputRetained ? 'Previous review artifacts retained.' : 'No review artifacts were published.'}`);
|
|
29
|
+
if (result.status === 'error') process.exitCode = 1;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
33
|
+
try { await runReviewCli(process.argv.slice(2)); }
|
|
34
|
+
catch (error) {
|
|
35
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
36
|
+
if (process.argv.includes('--json')) console.log(JSON.stringify({ status: 'error', error: message }, null, 2));
|
|
37
|
+
else console.error(message);
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { lstat, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { lstatSync, readdirSync, renameSync } from 'node:fs';
|
|
3
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
4
|
+
import { dirname, resolve, sep } from 'node:path';
|
|
5
|
+
import { createCanvas } from '@napi-rs/canvas';
|
|
6
|
+
import { getDocument } from 'pdfjs-dist/legacy/build/pdf.mjs';
|
|
7
|
+
import type { BlockInfo, DocumentFormat, RenderArtifact, ReviewIssue } from '../shared/types';
|
|
8
|
+
import { runtimeResolve } from '../runtime/paths';
|
|
9
|
+
import { captureEntryExportInputs, captureExportInputs } from './export-inputs';
|
|
10
|
+
import { ExportChangedError, outputFolder } from './export-file';
|
|
11
|
+
import { renderOnce, validId } from './render';
|
|
12
|
+
import { RenderFailure } from './render-error';
|
|
13
|
+
import { ThemeCatalog, themeFile } from './themes';
|
|
14
|
+
import { TemplateCatalog, templateFile } from './templates';
|
|
15
|
+
|
|
16
|
+
export interface ReviewTarget { kind: 'document' | 'theme' | 'template'; id: string }
|
|
17
|
+
export interface ReviewPage {
|
|
18
|
+
page: number; width: number; height: number; image: string; text: string;
|
|
19
|
+
blockIds: string[]; issues: ReviewIssue[];
|
|
20
|
+
}
|
|
21
|
+
export interface ReviewReport extends ReviewTarget {
|
|
22
|
+
version: 1; status: 'ready'; hash: string; format: DocumentFormat; renderedAt: string; pageCount: number;
|
|
23
|
+
visualReview: 'required'; factualReview: 'required';
|
|
24
|
+
outputs: { directory: string; pdf: string; text: string; review: string };
|
|
25
|
+
pages: ReviewPage[]; issues: ReviewIssue[]; blocks: BlockInfo[]; warning?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ReviewError extends ReviewTarget {
|
|
28
|
+
status: 'error'; error: string; issues: ReviewIssue[]; updated: false; previousOutputRetained: boolean;
|
|
29
|
+
}
|
|
30
|
+
export type ReviewResult = ReviewReport | ReviewError;
|
|
31
|
+
const collections = { document: 'documents', theme: 'themes', template: 'templates' } as const;
|
|
32
|
+
|
|
33
|
+
/** Prepare one render for review. Neither this path nor the catalog paths inspect GUI state. */
|
|
34
|
+
export async function reviewTarget(root: string, target: ReviewTarget, options: { renderTimeoutMs?: number } = {}): Promise<ReviewResult> {
|
|
35
|
+
const { id, kind } = target;
|
|
36
|
+
if (!validId(id) || !Object.hasOwn(collections, kind)) throw new Error('Choose a valid document, theme, or template ID.');
|
|
37
|
+
let cleanup: (() => Promise<void>) | undefined;
|
|
38
|
+
try {
|
|
39
|
+
let artifact: RenderArtifact, bytes: Uint8Array, unchanged: () => boolean;
|
|
40
|
+
if (kind === 'document') {
|
|
41
|
+
unchanged = await captureExportInputs(root, id);
|
|
42
|
+
const rendered = await renderOnce(root, id, options.renderTimeoutMs);
|
|
43
|
+
cleanup = () => rm(rendered.directory, { recursive: true, force: true });
|
|
44
|
+
artifact = rendered.artifact;
|
|
45
|
+
bytes = await readFile(resolve(rendered.directory, 'document.pdf'));
|
|
46
|
+
} else {
|
|
47
|
+
const entry = kind === 'theme' ? await themeFile(root, id, 'preview.tsx') : await templateFile(root, id, 'preview.tsx');
|
|
48
|
+
unchanged = await captureEntryExportInputs(root, entry);
|
|
49
|
+
// Catalogs validate their guides, definitions, theme identities, and format before returning a specimen.
|
|
50
|
+
const catalog = kind === 'theme' ? new ThemeCatalog(root) : new TemplateCatalog(root);
|
|
51
|
+
cleanup = () => catalog.close();
|
|
52
|
+
const preview = await catalog.preview(id);
|
|
53
|
+
if (!preview.artifact) throw new RenderFailure(preview.error ?? 'Specimen rendering failed.', preview.issues);
|
|
54
|
+
artifact = preview.artifact;
|
|
55
|
+
bytes = await catalog.pdf(id, artifact.hash);
|
|
56
|
+
}
|
|
57
|
+
if (!unchanged()) throw new ExportChangedError();
|
|
58
|
+
const report = await publishReview(root, target, artifact, bytes, unchanged);
|
|
59
|
+
try { await cleanup?.(); cleanup = undefined; }
|
|
60
|
+
catch (error) { report.warning = `Review artifacts were saved, but temporary render cleanup failed: ${message(error)}`; }
|
|
61
|
+
return report;
|
|
62
|
+
} catch (error) {
|
|
63
|
+
const prior = resolve(root, 'output/reviews', collections[kind], id, 'review.json');
|
|
64
|
+
const previousOutputRetained = await lstat(prior).then(info => info.isFile() && !info.isSymbolicLink(), () => false);
|
|
65
|
+
return { ...target, status: 'error', error: message(error), issues: error instanceof RenderFailure ? error.issues : [], updated: false, previousOutputRetained };
|
|
66
|
+
} finally { await cleanup?.().catch(() => {}); }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function message(error: unknown) { return error instanceof Error ? error.message : String(error); }
|
|
70
|
+
|
|
71
|
+
/** Review files are user-accessible while a new set is prepared; preserve concurrent additions and edits. */
|
|
72
|
+
function reviewFilesStamp(directory: string) {
|
|
73
|
+
return JSON.stringify(readdirSync(directory).sort().map(name => {
|
|
74
|
+
const info = lstatSync(resolve(directory, name), { bigint: true });
|
|
75
|
+
if (!info.isFile() || info.isSymbolicLink() || !/^(?:document\.(?:pdf|txt)|review\.json|page-\d{3,}\.(?:png|txt))$/.test(name)) throw new Error('The review destination contains other files. Move them before creating this review.');
|
|
76
|
+
return [name, `${info.dev}:${info.ino}:${info.mode}:${info.size}:${info.mtimeNs}:${info.ctimeNs}`];
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Rasterize and extract text from the exact PDF bytes, then publish the complete set together. */
|
|
81
|
+
export async function publishReview(root: string, target: ReviewTarget, artifact: RenderArtifact, bytes: Uint8Array, isCurrent: () => boolean): Promise<ReviewReport> {
|
|
82
|
+
if (!validId(target.id) || !Object.hasOwn(collections, target.kind)) throw new Error('Invalid review target.');
|
|
83
|
+
if (createHash('sha256').update(bytes).digest('hex') !== artifact.hash) throw new Error('Review PDF bytes do not match their render artifact.');
|
|
84
|
+
const folder = await outputFolder(root, ['reviews', collections[target.kind]]);
|
|
85
|
+
const parents = [resolve(root, 'output'), resolve(root, 'output/reviews'), folder].map(path => ({ path, info: lstatSync(path) }));
|
|
86
|
+
const directory = resolve(folder, target.id);
|
|
87
|
+
const previous = await lstat(directory).catch(error => { if (error.code === 'ENOENT') return undefined; throw error; });
|
|
88
|
+
let previousFiles: string | undefined;
|
|
89
|
+
if (previous) {
|
|
90
|
+
if (!previous.isDirectory() || previous.isSymbolicLink()) throw new Error('The review destination must be a regular local directory.');
|
|
91
|
+
previousFiles = reviewFilesStamp(directory);
|
|
92
|
+
const manifest = resolve(directory, 'review.json');
|
|
93
|
+
const manifestInfo = await lstat(manifest).catch(() => undefined);
|
|
94
|
+
const prior = manifestInfo?.isFile() && !manifestInfo.isSymbolicLink() ? await readFile(manifest, 'utf8').then(JSON.parse).catch(() => undefined) : undefined;
|
|
95
|
+
if (prior?.version !== 1 || prior.id !== target.id || prior.kind !== target.kind) throw new Error('The review destination contains other files. Move them before creating this review.');
|
|
96
|
+
}
|
|
97
|
+
const temporary = resolve(folder, `.${target.id}-${randomUUID()}.tmp`);
|
|
98
|
+
const backup = resolve(folder, `.${target.id}-${randomUUID()}.previous`);
|
|
99
|
+
await mkdir(temporary);
|
|
100
|
+
let published = false, retained = false;
|
|
101
|
+
const report: ReviewReport = {
|
|
102
|
+
version: 1, status: 'ready', ...target, hash: artifact.hash, format: artifact.format ?? 'document', renderedAt: artifact.renderedAt,
|
|
103
|
+
pageCount: artifact.pages.length, visualReview: 'required', factualReview: 'required',
|
|
104
|
+
outputs: { directory, pdf: resolve(directory, 'document.pdf'), text: resolve(directory, 'document.txt'), review: resolve(directory, 'review.json') },
|
|
105
|
+
pages: [], issues: artifact.issues ?? [], blocks: Object.values(artifact.blocks),
|
|
106
|
+
};
|
|
107
|
+
try {
|
|
108
|
+
const pdfjs = dirname(runtimeResolve('pdfjs-dist/package.json'));
|
|
109
|
+
const loading = getDocument({
|
|
110
|
+
data: new Uint8Array(bytes), useSystemFonts: false, verbosity: 0,
|
|
111
|
+
standardFontDataUrl: resolve(pdfjs, 'standard_fonts') + sep,
|
|
112
|
+
cMapUrl: resolve(pdfjs, 'cmaps') + sep, cMapPacked: true, wasmUrl: resolve(pdfjs, 'wasm') + sep,
|
|
113
|
+
});
|
|
114
|
+
const texts: string[] = [];
|
|
115
|
+
try {
|
|
116
|
+
const pdf = await loading.promise;
|
|
117
|
+
if (pdf.numPages !== artifact.pages.length) throw new Error('The PDF page count does not match its render artifact.');
|
|
118
|
+
for (let number = 1; number <= pdf.numPages; number++) {
|
|
119
|
+
const page = await pdf.getPage(number);
|
|
120
|
+
const name = `page-${String(number).padStart(3, '0')}`;
|
|
121
|
+
const content = await page.getTextContent();
|
|
122
|
+
const text = content.items.map(item => 'str' in item ? item.str + (item.hasEOL ? '\n' : ' ') : '').join('').trim() + '\n';
|
|
123
|
+
const viewport = page.getViewport({ scale: 1.5 });
|
|
124
|
+
const canvas = createCanvas(Math.ceil(viewport.width), Math.ceil(viewport.height));
|
|
125
|
+
try {
|
|
126
|
+
await page.render({ canvas: canvas as unknown as HTMLCanvasElement, canvasContext: canvas.getContext('2d') as unknown as CanvasRenderingContext2D, viewport }).promise;
|
|
127
|
+
await writeFile(resolve(temporary, `${name}.png`), await canvas.encode('png'));
|
|
128
|
+
} finally { canvas.width = 0; canvas.height = 0; }
|
|
129
|
+
await writeFile(resolve(temporary, `${name}.txt`), text);
|
|
130
|
+
texts.push(text);
|
|
131
|
+
const info = artifact.pages[number - 1];
|
|
132
|
+
report.pages.push({ page: number, width: info.width, height: info.height, image: resolve(directory, `${name}.png`), text: resolve(directory, `${name}.txt`), blockIds: [...new Set(info.fragments.map(fragment => fragment.id))], issues: report.issues.filter(issue => issue.page === number) });
|
|
133
|
+
page.cleanup();
|
|
134
|
+
}
|
|
135
|
+
} finally { await loading.destroy(); }
|
|
136
|
+
await writeFile(resolve(temporary, 'document.pdf'), bytes);
|
|
137
|
+
await writeFile(resolve(temporary, 'document.txt'), texts.join('\n\f\n'));
|
|
138
|
+
await writeFile(resolve(temporary, 'review.json'), JSON.stringify(report, null, 2) + '\n');
|
|
139
|
+
// No event-loop turn may separate freshness and publication. A failed rename restores the old set.
|
|
140
|
+
if (!isCurrent()) throw new ExportChangedError();
|
|
141
|
+
for (const { path, info: expected } of parents) {
|
|
142
|
+
const info = lstatSync(path);
|
|
143
|
+
if (!info.isDirectory() || info.isSymbolicLink() || info.ino !== expected.ino || info.dev !== expected.dev) throw new Error('The review output folder changed during preparation.');
|
|
144
|
+
}
|
|
145
|
+
const current = (() => { try { return lstatSync(directory); } catch (error) { if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined; throw error; } })();
|
|
146
|
+
if (current?.ino !== previous?.ino || current?.dev !== previous?.dev) throw new Error('The review destination changed during preparation. Retry after the other review finishes.');
|
|
147
|
+
if (previousFiles !== undefined) {
|
|
148
|
+
let unchanged = false;
|
|
149
|
+
try { unchanged = reviewFilesStamp(directory) === previousFiles; } catch { /* A new file or link is a concurrent edit too. */ }
|
|
150
|
+
if (!unchanged) throw new Error('The review files changed during preparation. Your existing files were retained; retry after edits finish.');
|
|
151
|
+
}
|
|
152
|
+
if (previous) { renameSync(directory, backup); retained = true; }
|
|
153
|
+
try { renameSync(temporary, directory); published = true; }
|
|
154
|
+
catch (error) { if (retained) { renameSync(backup, directory); retained = false; } throw error; }
|
|
155
|
+
if (retained) {
|
|
156
|
+
try { await rm(backup, { recursive: true, force: true }); retained = false; }
|
|
157
|
+
catch (error) { report.warning = `Review artifacts were saved; the prior review remains at ${backup}: ${message(error)}`; }
|
|
158
|
+
}
|
|
159
|
+
return report;
|
|
160
|
+
} finally { if (!published) await rm(temporary, { recursive: true, force: true }); }
|
|
161
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DocumentSelection } from '../shared/selection';
|
|
2
|
+
|
|
3
|
+
/** Keep observed UI context small and typed; it is not an instruction to the agent. */
|
|
4
|
+
export function readSelection(input: unknown, blockId: string | null): DocumentSelection | null {
|
|
5
|
+
if (input == null) return null;
|
|
6
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new Error('Invalid selection.');
|
|
7
|
+
const value = input as DocumentSelection;
|
|
8
|
+
if (!blockId || value.blockId !== blockId) throw new Error('The selection does not match the active block.');
|
|
9
|
+
// The user can scroll elsewhere while keeping a phrase selected.
|
|
10
|
+
if (!Number.isInteger(value.page) || value.page < 1) throw new Error('Invalid selection page.');
|
|
11
|
+
if (value.targetId !== undefined && (typeof value.targetId !== 'string' || !value.targetId || value.targetId.length > 1000)) throw new Error('Invalid selected field.');
|
|
12
|
+
if ((value.start !== undefined || value.end !== undefined) && (!value.targetId || !Number.isInteger(value.start) || !Number.isInteger(value.end) || value.start! < 0 || value.end! <= value.start!)) throw new Error('Invalid selected text range.');
|
|
13
|
+
if (value.quote !== undefined && (typeof value.quote !== 'string' || value.quote.length > 8000)) throw new Error('Selected text is too long. Select a shorter passage.');
|
|
14
|
+
if (value.renderHash !== undefined && (typeof value.renderHash !== 'string' || value.renderHash.length > 128)) throw new Error('Invalid selection version.');
|
|
15
|
+
if (value.revision !== undefined && (!Number.isInteger(value.revision) || value.revision < 0)) throw new Error('Invalid selection revision.');
|
|
16
|
+
return { blockId, page: value.page,
|
|
17
|
+
...(value.targetId === undefined ? {} : { targetId: value.targetId }),
|
|
18
|
+
...(value.start === undefined ? {} : { start: value.start, end: value.end }),
|
|
19
|
+
...(value.quote === undefined ? {} : { quote: value.quote }),
|
|
20
|
+
...(value.renderHash === undefined ? {} : { renderHash: value.renderHash }),
|
|
21
|
+
...(value.revision === undefined ? {} : { revision: value.revision }),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { build, type Plugin } from 'esbuild';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import { realpath } from 'node:fs/promises';
|
|
4
|
+
import { isBuiltin } from 'node:module';
|
|
5
|
+
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import type { TextSourceValue } from '../shared/selection';
|
|
7
|
+
import { textSourceBindingId } from './text-source';
|
|
8
|
+
import { runtimeResolve, runtimeSource } from '../runtime/paths';
|
|
9
|
+
|
|
10
|
+
const authoringModules: Record<string, string> = {
|
|
11
|
+
opendoc: 'document/index.tsx',
|
|
12
|
+
'opendoc/themes': 'themes/index.ts',
|
|
13
|
+
'opendoc/assets': 'assets/index.ts',
|
|
14
|
+
'opendoc/template': 'template/index.ts',
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** Keep the authoring runtime in one bundle and installed dependencies in one Node instance. */
|
|
18
|
+
export function authoringResolutionPlugin(): Plugin {
|
|
19
|
+
return { name: 'opendoc-installed-runtime', setup(builder) {
|
|
20
|
+
builder.onResolve({ filter: /^[^./]/ }, args => {
|
|
21
|
+
if (authoringModules[args.path]) return { path: runtimeSource(authoringModules[args.path]) };
|
|
22
|
+
if (isBuiltin(args.path)) return { path: args.path, external: true };
|
|
23
|
+
if (args.path.startsWith('file:')) return { path: args.path, external: true };
|
|
24
|
+
try { return { path: pathToFileURL(runtimeResolve(args.path)).href, external: true }; }
|
|
25
|
+
catch (error) {
|
|
26
|
+
// Additional packages authored in a workspace still use esbuild's normal resolution.
|
|
27
|
+
if (['MODULE_NOT_FOUND', 'ERR_MODULE_NOT_FOUND'].includes((error as NodeJS.ErrnoException).code ?? '')) return undefined;
|
|
28
|
+
throw error;
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
} };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Trusted candidates assembled from renderer-issued bindings, never client paths. */
|
|
35
|
+
export interface SourceOverride {
|
|
36
|
+
file: string;
|
|
37
|
+
contents: string;
|
|
38
|
+
originalDigest: string;
|
|
39
|
+
replacements: { start: number; end: number; tokenLength: number; value: string }[];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Keep the authored identity while candidate tokens and all following offsets move. */
|
|
43
|
+
export function originalBinding(source: TextSourceValue | undefined, override: SourceOverride | undefined) {
|
|
44
|
+
if (!source || !override) return source;
|
|
45
|
+
let delta = 0;
|
|
46
|
+
for (const replacement of override.replacements) {
|
|
47
|
+
const start = replacement.start + delta, end = start + replacement.tokenLength;
|
|
48
|
+
if (source.start >= start && source.end <= end && source.value === replacement.value) {
|
|
49
|
+
return { ...source, bindingId: textSourceBindingId(source.file, override.originalDigest, replacement.start, replacement.end) };
|
|
50
|
+
}
|
|
51
|
+
if (source.end <= start) break;
|
|
52
|
+
if (source.start < end) return { ...source, bindingId: undefined };
|
|
53
|
+
delta += replacement.tokenLength - (replacement.end - replacement.start);
|
|
54
|
+
}
|
|
55
|
+
return { ...source, bindingId: textSourceBindingId(source.file, override.originalDigest, source.start - delta, source.end - delta) };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Bundle authored code once, preserving Forme's JSX source-map contract for
|
|
60
|
+
* original sources and drafts. Installed packages never depend on workspace hoisting.
|
|
61
|
+
*/
|
|
62
|
+
export async function bundleSourceOverrides(source: string, entry: string, overrides: Map<string, SourceOverride>) {
|
|
63
|
+
const used = new Set<string>();
|
|
64
|
+
const result = await build({
|
|
65
|
+
stdin: { contents: source, resolveDir: dirname(entry), sourcefile: '.opendoc-entry.tsx', loader: 'tsx' },
|
|
66
|
+
absWorkingDir: dirname(entry), bundle: true, write: false, format: 'esm', platform: 'node', target: 'node20',
|
|
67
|
+
jsx: 'automatic', jsxDev: true, jsxImportSource: 'react',
|
|
68
|
+
logLevel: 'silent', plugins: [{ name: 'opendoc-draft-sources', setup(builder) {
|
|
69
|
+
builder.onResolve({ filter: /^(?:react|opendoc)\/jsx-dev-runtime$/ }, () => ({ path: 'opendoc-jsx-source', namespace: 'opendoc-jsx-source' }));
|
|
70
|
+
builder.onLoad({ filter: /.*/, namespace: 'opendoc-jsx-source' }, () => ({
|
|
71
|
+
loader: 'js', resolveDir: dirname(entry), contents: `
|
|
72
|
+
import { jsx, Fragment } from 'react/jsx-runtime';
|
|
73
|
+
import { resolve, isAbsolute } from 'node:path';
|
|
74
|
+
export { Fragment };
|
|
75
|
+
globalThis.__formeSourceMap ??= new WeakMap();
|
|
76
|
+
export function jsxDEV(type, props, key, staticChildren, source) {
|
|
77
|
+
const element = jsx(type, props, key);
|
|
78
|
+
if (source?.fileName) globalThis.__formeSourceMap.set(element, {
|
|
79
|
+
file: isAbsolute(source.fileName) ? source.fileName : resolve(${JSON.stringify(dirname(entry))}, source.fileName),
|
|
80
|
+
line: source.lineNumber, column: source.columnNumber,
|
|
81
|
+
});
|
|
82
|
+
return element;
|
|
83
|
+
}
|
|
84
|
+
`,
|
|
85
|
+
}));
|
|
86
|
+
builder.onLoad({ filter: /\.(?:tsx?|json)$/ }, async args => {
|
|
87
|
+
const path = await realpath(args.path);
|
|
88
|
+
const override = overrides.get(path);
|
|
89
|
+
if (!override) return undefined;
|
|
90
|
+
used.add(path);
|
|
91
|
+
return { contents: override.contents, loader: path.endsWith('.json') ? 'json' : path.endsWith('.tsx') ? 'tsx' : 'ts', resolveDir: dirname(path) };
|
|
92
|
+
});
|
|
93
|
+
} }, authoringResolutionPlugin()],
|
|
94
|
+
}).catch(error => { throw new Error(`Build error: ${error instanceof Error ? error.message : String(error)}`, { cause: error }); });
|
|
95
|
+
if ([...overrides.keys()].some(file => !used.has(file))) throw new Error('An edited source is no longer used by this document. Refresh the document before saving.');
|
|
96
|
+
return result.outputFiles[0].text;
|
|
97
|
+
}
|