@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,51 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { pathToFileURL } from 'node:url';
|
|
3
|
+
import { parseArgs } from 'node:util';
|
|
4
|
+
import { TemplateCatalog, readTemplate, readTemplateGuide, templateFile } from './templates';
|
|
5
|
+
import { captureEntryExportInputs } from './export-inputs';
|
|
6
|
+
import { ExportChangedError, publishPDF } from './export-file';
|
|
7
|
+
import { RenderFailure } from './render-error';
|
|
8
|
+
|
|
9
|
+
const usage = 'Usage: npx opendoc templates list [--json]\n npx opendoc templates inspect <id> | check <id> | preview <id> [--json]\n Create an instance with npx opendoc create --template <id> --project <project-id> --title "Title".';
|
|
10
|
+
|
|
11
|
+
export async function runTemplatesCli(args: string[], root = process.cwd()): Promise<void> {
|
|
12
|
+
const { values, positionals } = parseArgs({ args: args[0] === '--' ? args.slice(1) : args, allowPositionals: true, options: {
|
|
13
|
+
json: { type: 'boolean' }, help: { type: 'boolean', short: 'h' },
|
|
14
|
+
} });
|
|
15
|
+
if (values.help) { console.log(values.json ? JSON.stringify({ usage }, null, 2) : usage); return; }
|
|
16
|
+
const [command = 'list', id] = positionals;
|
|
17
|
+
if (command === 'list' && positionals.length <= 1) {
|
|
18
|
+
const catalog = new TemplateCatalog(root);
|
|
19
|
+
try {
|
|
20
|
+
console.log(JSON.stringify((await catalog.list()).map(({ id, descriptor, error }) => ({ id, ...descriptor, ...(error ? { error } : {}) })), null, 2));
|
|
21
|
+
} finally { await catalog.close(); }
|
|
22
|
+
} else if (command === 'inspect' && id && positionals.length === 2) {
|
|
23
|
+
const descriptor = await readTemplate(root, id);
|
|
24
|
+
const guide = await readTemplateGuide(root, id);
|
|
25
|
+
const paths = {
|
|
26
|
+
definition: `templates/${id}/index.tsx`, descriptor: `templates/${id}/template.json`,
|
|
27
|
+
guide: `templates/${id}/AGENTS.md`, preview: `templates/${id}/preview.tsx`, starter: `templates/${id}/starter.tsx`,
|
|
28
|
+
};
|
|
29
|
+
console.log(JSON.stringify({ id, ...descriptor, paths, guide }, null, 2));
|
|
30
|
+
} else if ((command === 'check' || command === 'preview') && id && positionals.length === 2) {
|
|
31
|
+
const catalog = new TemplateCatalog(root);
|
|
32
|
+
try {
|
|
33
|
+
const unchanged = await captureEntryExportInputs(root, await templateFile(root, id, 'preview.tsx'));
|
|
34
|
+
const descriptor = await readTemplate(root, id);
|
|
35
|
+
await readTemplateGuide(root, id);
|
|
36
|
+
const preview = await catalog.preview(id);
|
|
37
|
+
if (!preview.artifact) throw new RenderFailure(preview.error ?? 'Template preview failed.', preview.issues);
|
|
38
|
+
if (!unchanged()) throw new ExportChangedError();
|
|
39
|
+
const artifact = preview.artifact;
|
|
40
|
+
const output = command === 'preview' ? await publishPDF(root, id, await catalog.pdf(id, artifact.hash), unchanged, 'templates') : undefined;
|
|
41
|
+
console.log(JSON.stringify({ id, name: descriptor.name, status: 'ready', format: artifact.format ?? 'document', pages: artifact.pages.length, hash: artifact.hash, issues: artifact.issues ?? [], ...(output ? { output } : {}), review: 'Inspect every PDF page; automated checks do not establish visual quality.' }, null, 2));
|
|
42
|
+
} catch (error) {
|
|
43
|
+
if (error instanceof ExportChangedError) throw new Error('The template changed while preparing its preview. Retry after edits finish; no template PDF was replaced.', { cause: error });
|
|
44
|
+
throw error;
|
|
45
|
+
} finally { await catalog.close(); }
|
|
46
|
+
} else throw new Error(usage);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
50
|
+
runTemplatesCli(process.argv.slice(2)).catch(error => { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 1; });
|
|
51
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { lstat, readdir, readFile, rm, stat } from 'node:fs/promises';
|
|
2
|
+
import { relative, resolve, sep } from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
import { documentDependencies, includesDependency, type DocumentDependencies, isRenderRuntimePath } from './dependencies';
|
|
5
|
+
import type { TemplateDescriptor, TemplateItem, TemplatePreview } from '../shared/templates';
|
|
6
|
+
import { containedFile } from './files';
|
|
7
|
+
import { RenderFailure } from './render-error';
|
|
8
|
+
import { renderEntry, validId } from './render';
|
|
9
|
+
import { createDocumentFromFiles } from './create';
|
|
10
|
+
|
|
11
|
+
export async function templateFile(root: string, id: string, name: string) {
|
|
12
|
+
if (!validId(id)) throw new Error('Invalid template ID.');
|
|
13
|
+
const folder = await containedFile(resolve(root, 'templates'), resolve(root, 'templates', id));
|
|
14
|
+
return containedFile(folder, resolve(folder, name));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function readTemplateGuide(root: string, id: string): Promise<string> {
|
|
18
|
+
const file = await templateFile(root, id, 'AGENTS.md');
|
|
19
|
+
const info = await stat(file);
|
|
20
|
+
if (!info.isFile()) throw new Error('The template guide must be a Markdown file.');
|
|
21
|
+
if (info.size > 128_000) throw new Error('This template guide is too large to display.');
|
|
22
|
+
return readFile(file, 'utf8');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function readTemplate(root: string, id: string): Promise<TemplateDescriptor> {
|
|
26
|
+
const path = await templateFile(root, id, 'template.json');
|
|
27
|
+
if ((await stat(path)).size > 32_000) throw new Error('Template description is too large.');
|
|
28
|
+
const data = JSON.parse(await readFile(path, 'utf8')) as Record<string, unknown>;
|
|
29
|
+
if (!data || typeof data !== 'object' || Array.isArray(data) || Object.keys(data).some(key => !['name', 'description', 'format', 'structure', 'documentFormat'].includes(key))) throw new Error('Invalid template description.');
|
|
30
|
+
if (data.documentFormat !== undefined && data.documentFormat !== 'document' && data.documentFormat !== 'presentation') throw new Error('template.documentFormat must be document or presentation.');
|
|
31
|
+
for (const key of ['name', 'description', 'format']) {
|
|
32
|
+
if (typeof data[key] !== 'string' || !(data[key] as string).trim() || (data[key] as string).length > (key === 'description' ? 600 : 120)) throw new Error(`template.${key} needs a short, nonempty description.`);
|
|
33
|
+
}
|
|
34
|
+
if (!Array.isArray(data.structure) || !data.structure.length || data.structure.length > 8 || data.structure.some(item => typeof item !== 'string' || !item.trim() || item.length > 240)) throw new Error('template.structure needs one to eight short layout notes.');
|
|
35
|
+
await Promise.all(['index.tsx', 'preview.tsx', 'starter.tsx', 'AGENTS.md'].map(name => templateFile(root, id, name)));
|
|
36
|
+
return data as unknown as TemplateDescriptor;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
type Preview = TemplatePreview & { directory?: string };
|
|
40
|
+
|
|
41
|
+
/** Discover descriptions immediately; render only requested previews, with two workers. */
|
|
42
|
+
export class TemplateCatalog {
|
|
43
|
+
private version = 0;
|
|
44
|
+
private globalVersion = 0;
|
|
45
|
+
private versions = new Map<string, number>();
|
|
46
|
+
private session = randomUUID();
|
|
47
|
+
private summaries?: { version: number; result: Promise<TemplateItem[]> };
|
|
48
|
+
private previews = new Map<string, { version: string; result: Promise<Preview> }>();
|
|
49
|
+
private dependencies = new Map<string, DocumentDependencies>();
|
|
50
|
+
private active = 0;
|
|
51
|
+
private waiting: (() => void)[] = [];
|
|
52
|
+
private closed = false;
|
|
53
|
+
constructor(private root: string) {}
|
|
54
|
+
invalidate(id?: string) {
|
|
55
|
+
this.version++;
|
|
56
|
+
if (id) this.dependencies.delete(id); else this.dependencies.clear();
|
|
57
|
+
if (id) this.versions.set(id, (this.versions.get(id) ?? 0) + 1);
|
|
58
|
+
else this.globalVersion++;
|
|
59
|
+
}
|
|
60
|
+
private revision(id: string) { return `${this.session}:${id}:${this.globalVersion}:${this.versions.get(id) ?? 0}`; }
|
|
61
|
+
async noteChange(file: string) {
|
|
62
|
+
const path = resolve(this.root, file);
|
|
63
|
+
const parts = relative(this.root, path).split(sep);
|
|
64
|
+
if (parts[0] === 'templates') this.version++;
|
|
65
|
+
const entries = await readdir(resolve(this.root, 'templates'), { withFileTypes: true }).catch(() => []);
|
|
66
|
+
const ids = new Set([...entries.filter(entry => entry.isDirectory() && validId(entry.name)).map(entry => entry.name), ...this.previews.keys()]);
|
|
67
|
+
for (const id of ids) {
|
|
68
|
+
const dependencies = this.dependencies.get(id);
|
|
69
|
+
if (isRenderRuntimePath(this.root, path) || (parts[0] === 'themes' && parts[1] === 'neutral') || (parts[0] === 'templates' && (!parts[1] || parts[1] === id))
|
|
70
|
+
|| !dependencies || dependencies.readsFiles || includesDependency(dependencies, path)) this.invalidate(id);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async list(): Promise<TemplateItem[]> {
|
|
74
|
+
if (!this.summaries || this.summaries.version !== this.version) {
|
|
75
|
+
const version = this.version;
|
|
76
|
+
const result = (async () => {
|
|
77
|
+
const entries = await readdir(resolve(this.root, 'templates'), { withFileTypes: true }).catch(error => { if (error.code === 'ENOENT') return []; throw error; });
|
|
78
|
+
return Promise.all(entries.filter(folder => folder.isDirectory() && validId(folder.name)).sort((a, b) => a.name.localeCompare(b.name)).map(async folder => {
|
|
79
|
+
try { return { id: folder.name, descriptor: await readTemplate(this.root, folder.name), revision: this.revision(folder.name) }; }
|
|
80
|
+
catch (error) { return { id: folder.name, descriptor: { name: folder.name, description: 'This template needs attention.', format: '', structure: [] }, revision: this.revision(folder.name), error: error instanceof Error ? error.message : String(error) }; }
|
|
81
|
+
}));
|
|
82
|
+
})();
|
|
83
|
+
this.summaries = { version, result };
|
|
84
|
+
}
|
|
85
|
+
const snapshot = this.summaries;
|
|
86
|
+
try {
|
|
87
|
+
const items = await snapshot.result;
|
|
88
|
+
return snapshot.version === this.version ? items : this.list();
|
|
89
|
+
} catch (error) { if (this.summaries === snapshot) this.summaries = undefined; throw error; }
|
|
90
|
+
}
|
|
91
|
+
private async render(id: string, version: string): Promise<Preview> {
|
|
92
|
+
if (this.active >= 2) await new Promise<void>(accept => this.waiting.push(accept));
|
|
93
|
+
else this.active++;
|
|
94
|
+
let directory: string | undefined;
|
|
95
|
+
try {
|
|
96
|
+
if (this.closed) throw new Error('The template catalog is closed.');
|
|
97
|
+
const descriptor = await readTemplate(this.root, id);
|
|
98
|
+
const entry = await templateFile(this.root, id, 'preview.tsx');
|
|
99
|
+
const dependencies = await documentDependencies(this.root, entry).catch(() => undefined);
|
|
100
|
+
const rendered = await renderEntry(this.root, entry, `template-${id}`);
|
|
101
|
+
directory = rendered.directory;
|
|
102
|
+
if (rendered.artifact.meta.theme !== 'neutral') throw new Error('Template previews must use the Neutral theme.');
|
|
103
|
+
if (rendered.artifact.format !== (descriptor.documentFormat ?? 'document')) throw new Error('The template preview must use the root matching its documentFormat.');
|
|
104
|
+
if (dependencies && version === this.revision(id)) {
|
|
105
|
+
for (const file of rendered.artifact.assetDependencies ?? []) dependencies.files.add(resolve(this.root, file));
|
|
106
|
+
this.dependencies.set(id, dependencies);
|
|
107
|
+
}
|
|
108
|
+
return rendered;
|
|
109
|
+
} catch (error) {
|
|
110
|
+
if (directory) await rm(directory, { recursive: true, force: true });
|
|
111
|
+
return { error: error instanceof Error ? error.message : String(error), ...(error instanceof RenderFailure ? { issues: error.issues } : {}) };
|
|
112
|
+
} finally { const next = this.waiting.shift(); if (next) next(); else this.active--; }
|
|
113
|
+
}
|
|
114
|
+
private async current(id: string): Promise<Preview> {
|
|
115
|
+
if (!validId(id)) throw new Error('Invalid template ID.');
|
|
116
|
+
if (this.closed) throw new Error('The template catalog is closed.');
|
|
117
|
+
let snapshot = this.previews.get(id);
|
|
118
|
+
if (!snapshot || snapshot.version !== this.revision(id)) {
|
|
119
|
+
const previous = snapshot;
|
|
120
|
+
const version = this.revision(id);
|
|
121
|
+
const result = (async () => {
|
|
122
|
+
const old = await previous?.result;
|
|
123
|
+
const next = await this.render(id, version);
|
|
124
|
+
if (old?.directory) await rm(old.directory, { recursive: true, force: true });
|
|
125
|
+
return next;
|
|
126
|
+
})();
|
|
127
|
+
snapshot = { version, result }; this.previews.set(id, snapshot);
|
|
128
|
+
}
|
|
129
|
+
const result = await snapshot.result;
|
|
130
|
+
if (snapshot.version !== this.revision(id)) return this.current(id);
|
|
131
|
+
if (result.error && this.previews.get(id) === snapshot) this.previews.delete(id);
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
async preview(id: string): Promise<TemplatePreview> {
|
|
135
|
+
const { directory: _directory, ...item } = await this.current(id);
|
|
136
|
+
return item;
|
|
137
|
+
}
|
|
138
|
+
async pdf(id: string, hash: string) {
|
|
139
|
+
const item = await this.current(id);
|
|
140
|
+
if (!item.directory || item.artifact?.hash !== hash) throw new Error('This template preview changed. Reload Templates.');
|
|
141
|
+
return readFile(resolve(item.directory, 'document.pdf'));
|
|
142
|
+
}
|
|
143
|
+
async close() {
|
|
144
|
+
this.closed = true;
|
|
145
|
+
const entries = await Promise.all([...this.previews.values()].map(item => item.result));
|
|
146
|
+
await Promise.all(entries.map(item => item.directory && rm(item.directory, { recursive: true, force: true })));
|
|
147
|
+
this.previews.clear();
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export async function createFromTemplate(root: string, id: string, input: unknown) {
|
|
152
|
+
if (!input || typeof input !== 'object' || Array.isArray(input)) throw new Error('Provide a title and a project.');
|
|
153
|
+
const value = input as Record<string, unknown>;
|
|
154
|
+
if (Object.keys(value).some(key => !['title', 'theme', 'id', 'projectId', 'format'].includes(key))) throw new Error('Only title, project, theme, format, and an optional document ID are accepted.');
|
|
155
|
+
if (typeof value.title !== 'string' || (value.id !== undefined && typeof value.id !== 'string')) throw new Error('Give your document a title.');
|
|
156
|
+
const descriptor = await readTemplate(root, id);
|
|
157
|
+
const format = descriptor.documentFormat ?? 'document';
|
|
158
|
+
if (value.format !== undefined && value.format !== format) throw new Error(`This template creates a ${format}. Choose a matching template or omit --format.`);
|
|
159
|
+
const starter = await readFile(await templateFile(root, id, 'starter.tsx'), 'utf8');
|
|
160
|
+
const created = await createDocumentFromFiles(root, {
|
|
161
|
+
title: value.title, id: value.id as string | undefined, projectId: value.projectId as string, theme: value.theme as string | null | undefined, format,
|
|
162
|
+
}, async (documentId, chosenTheme) => {
|
|
163
|
+
const replacements: Record<string, string> = { '__OPENDOC_TITLE__': (value.title as string).trim(), '__OPENDOC_THEME__': chosenTheme ?? 'neutral', '__OPENDOC_DOCUMENT_ID__': documentId, '__OPENDOC_THEME_MODULE__': './theme' };
|
|
164
|
+
const files: Record<string, string> = {};
|
|
165
|
+
// A data-bound template gives each instance its own ordinary JSON input.
|
|
166
|
+
const dataPath = resolve(root, 'templates', id, 'data.json');
|
|
167
|
+
const dataStat = await lstat(dataPath).catch(error => { if (error.code === 'ENOENT') return undefined; throw error; });
|
|
168
|
+
let rawData = '';
|
|
169
|
+
if (dataStat) {
|
|
170
|
+
const dataFile = await templateFile(root, id, 'data.json');
|
|
171
|
+
if ((await stat(dataFile)).size > 128_000) throw new Error('Template starter data is too large.');
|
|
172
|
+
rawData = await readFile(dataFile, 'utf8');
|
|
173
|
+
const data = JSON.parse(rawData.replace(/"(__OPENDOC_TITLE__|__OPENDOC_THEME__)"/g, (_match, marker: string) => JSON.stringify(replacements[marker])));
|
|
174
|
+
if (!data || typeof data !== 'object' || Array.isArray(data)) throw new Error('Template starter data must be a JSON object.');
|
|
175
|
+
files['data.json'] = `${JSON.stringify(data, null, 2)}\n`;
|
|
176
|
+
}
|
|
177
|
+
for (const marker of ['__OPENDOC_TITLE__', '__OPENDOC_THEME__']) {
|
|
178
|
+
if (!(starter + rawData).includes(JSON.stringify(marker))) throw new Error(`Template starter is missing its ${marker === '__OPENDOC_TITLE__' ? 'title' : 'theme'} placeholder.`);
|
|
179
|
+
}
|
|
180
|
+
if (starter.split('"__OPENDOC_THEME_MODULE__"').length !== 2) throw new Error('Template starter needs exactly one quoted theme module placeholder.');
|
|
181
|
+
// A single pass over the original source never reinterprets literal user text.
|
|
182
|
+
files['index.tsx'] = starter.replace(/"(__OPENDOC_TITLE__|__OPENDOC_THEME__|__OPENDOC_DOCUMENT_ID__|__OPENDOC_THEME_MODULE__)"/g, (_match, marker: string) => JSON.stringify(replacements[marker]));
|
|
183
|
+
return files;
|
|
184
|
+
});
|
|
185
|
+
return { id: created.id, title: created.title, entry: created.entry, projectId: created.projectId, template: id, format };
|
|
186
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ElementInfo, LayoutInfo } from '@formepdf/core';
|
|
2
|
+
import type { TextTarget } from '../shared/selection';
|
|
3
|
+
|
|
4
|
+
/** Only normalize layout whitespace; never guess through changed words. */
|
|
5
|
+
function comparable(text: string) {
|
|
6
|
+
const positions: number[] = [], chars: string[] = [];
|
|
7
|
+
for (let index = 0; index < text.length; index++) {
|
|
8
|
+
if (/\s/u.test(text[index]) || text[index] === '\u00ad') continue;
|
|
9
|
+
positions.push(index); chars.push(text[index]);
|
|
10
|
+
}
|
|
11
|
+
return { text: chars.join(''), positions };
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Attach line geometry to the logical text tree, including continued pages. */
|
|
15
|
+
export function attachTextLines(layout: LayoutInfo, targets: TextTarget[]) {
|
|
16
|
+
const values = targets.map(target => comparable(target.text));
|
|
17
|
+
const cursors = targets.map(() => 0);
|
|
18
|
+
const blocked = targets.map(() => false);
|
|
19
|
+
for (const [pageIndex, page] of layout.pages.entries()) {
|
|
20
|
+
const visit = (node: ElementInfo, inherited?: number) => {
|
|
21
|
+
const source = node.sourceLocation;
|
|
22
|
+
const index = source?.file.startsWith('opendoc:block:') ? source.line - 1 : -1;
|
|
23
|
+
const candidate = targets[index];
|
|
24
|
+
const own = /^(Text|H[1-6])$/.test(node.nodeType) && candidate && source?.file === `opendoc:block:${candidate.blockId}` ? index : inherited;
|
|
25
|
+
if (node.nodeType === 'TextLine' && own !== undefined && node.textContent && !blocked[own]) {
|
|
26
|
+
const target = targets[own], value = values[own];
|
|
27
|
+
let line = comparable(node.textContent).text;
|
|
28
|
+
let cursor = cursors[own];
|
|
29
|
+
if (cursor === value.text.length) cursor = 0; // repeated table headers / page furniture
|
|
30
|
+
if (!value.text.startsWith(line, cursor) && line.endsWith('-')) line = line.slice(0, -1); // discretionary line-end hyphen
|
|
31
|
+
if (line && value.text.startsWith(line, cursor)) {
|
|
32
|
+
const start = value.positions[cursor], end = value.positions[cursor + line.length - 1] + 1;
|
|
33
|
+
target.lines.push({ page: pageIndex + 1, x: node.x, y: node.y, width: node.width, height: node.height, text: node.textContent, start, end });
|
|
34
|
+
cursors[own] = cursor + line.length;
|
|
35
|
+
} else if (line) blocked[own] = true; // Never resume at a coincidentally repeated phrase after an unaligned line.
|
|
36
|
+
}
|
|
37
|
+
for (const child of node.children) visit(child, own);
|
|
38
|
+
};
|
|
39
|
+
for (const node of page.elements) visit(node);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
3
|
+
import type { TextSourceValue } from '../shared/selection';
|
|
4
|
+
|
|
5
|
+
export const textDigest = (text: string) => createHash('sha256').update(text).digest('hex');
|
|
6
|
+
export const textSourceBindingId = (file: string, digest: string, start: number, end: number) => textDigest(JSON.stringify([file, digest, start, end]));
|
|
7
|
+
|
|
8
|
+
function unwrap(node: ts.Expression): ts.Expression {
|
|
9
|
+
while (ts.isParenthesizedExpression(node) || ts.isAsExpression(node) || ts.isTypeAssertionExpression(node) || ts.isNonNullExpression(node) || ts.isSatisfiesExpression(node)) node = node.expression;
|
|
10
|
+
return node;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function propertyName(node: ts.PropertyName): string | undefined {
|
|
14
|
+
return ts.isIdentifier(node) || ts.isStringLiteral(node) || ts.isNumericLiteral(node) ? node.text : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function propertyValue(node: ts.ObjectLiteralElementLike): ts.Expression | undefined {
|
|
18
|
+
return ts.isPropertyAssignment(node) ? node.initializer : ts.isShorthandPropertyAssignment(node) && !node.objectAssignmentInitializer ? node.name : undefined;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Use the same JSX whitespace/entity rules as the TypeScript compiler, without evaluating code. */
|
|
22
|
+
function jsxValue(raw: string, attribute: boolean): string | undefined {
|
|
23
|
+
const key = attribute ? 'value' : 'children';
|
|
24
|
+
const input = attribute ? `const x = <T value=${raw}/>;` : `const x = <T>${raw}</T>;`;
|
|
25
|
+
const output = ts.transpileModule(input, { compilerOptions: { jsx: ts.JsxEmit.ReactJSX, target: ts.ScriptTarget.ES2022 } }).outputText;
|
|
26
|
+
const tree = ts.createSourceFile('jsx-value.js', output, ts.ScriptTarget.Latest, true, ts.ScriptKind.JS);
|
|
27
|
+
let value: string | undefined;
|
|
28
|
+
function visit(node: ts.Node) {
|
|
29
|
+
if (ts.isPropertyAssignment(node) && propertyName(node.name) === key && ts.isStringLiteral(node.initializer)) value = node.initializer.text;
|
|
30
|
+
ts.forEachChild(node, visit);
|
|
31
|
+
}
|
|
32
|
+
visit(tree);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface TextSourceResolver {
|
|
37
|
+
resolveAt(line: number, column: number, slot?: string, childIndex?: number): TextSourceValue | undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Only provable values in this source file are followed. Imported values and arbitrary expressions stay read-only. */
|
|
41
|
+
export function createTextSourceResolver(file: string, source: string): TextSourceResolver {
|
|
42
|
+
const tree = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TSX);
|
|
43
|
+
const digest = textDigest(source);
|
|
44
|
+
const options: ts.CompilerOptions = { noResolve: true, noLib: true, target: ts.ScriptTarget.ES2022, jsx: ts.JsxEmit.Preserve };
|
|
45
|
+
const host = ts.createCompilerHost(options);
|
|
46
|
+
host.getSourceFile = name => name === file ? tree : undefined;
|
|
47
|
+
host.fileExists = name => name === file;
|
|
48
|
+
host.readFile = name => name === file ? source : undefined;
|
|
49
|
+
const checker = ts.createProgram([file], options, host).getTypeChecker();
|
|
50
|
+
const nodes: ts.Node[] = [];
|
|
51
|
+
const jsxValues = new Map<ts.Node, string | undefined>();
|
|
52
|
+
function collect(node: ts.Node) { nodes.push(node); ts.forEachChild(node, collect); }
|
|
53
|
+
collect(tree);
|
|
54
|
+
|
|
55
|
+
function descriptor(node: ts.Node, kind: TextSourceValue['kind'], value: string): TextSourceValue {
|
|
56
|
+
const start = ts.isJsxText(node) ? node.pos : node.getStart(tree), end = node.end;
|
|
57
|
+
return { file, digest, start, end, kind, value, bindingId: textSourceBindingId(file, digest, start, end) };
|
|
58
|
+
}
|
|
59
|
+
function jsx(node: ts.Node, attribute: boolean) {
|
|
60
|
+
if (!jsxValues.has(node)) jsxValues.set(node, jsxValue(source.slice(ts.isJsxText(node) ? node.pos : node.getStart(tree), node.end), attribute));
|
|
61
|
+
return jsxValues.get(node);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const safeDeclarations = new Map<ts.VariableDeclaration, boolean>();
|
|
65
|
+
function referenceSymbol(node: ts.Identifier) {
|
|
66
|
+
// In `{ meta }`, the identifier's ordinary symbol is the new object's
|
|
67
|
+
// property; the value symbol is the local variable whose identity escapes.
|
|
68
|
+
return ts.isShorthandPropertyAssignment(node.parent) && node.parent.name === node
|
|
69
|
+
? checker.getShorthandAssignmentValueSymbol(node.parent) : checker.getSymbolAtLocation(node);
|
|
70
|
+
}
|
|
71
|
+
function isWriteTarget(node: ts.Node): boolean {
|
|
72
|
+
// A reference may sit inside parentheses, a type assertion, or a nested
|
|
73
|
+
// destructuring target. Inspect ancestors instead of only its immediate parent.
|
|
74
|
+
for (let current = node; current.parent; current = current.parent) {
|
|
75
|
+
const parent = current.parent;
|
|
76
|
+
if (ts.isBinaryExpression(parent) && parent.left === current && parent.operatorToken.kind >= ts.SyntaxKind.FirstAssignment && parent.operatorToken.kind <= ts.SyntaxKind.LastAssignment) return true;
|
|
77
|
+
if ((ts.isPrefixUnaryExpression(parent) || ts.isPostfixUnaryExpression(parent)) && (parent.operator === ts.SyntaxKind.PlusPlusToken || parent.operator === ts.SyntaxKind.MinusMinusToken)) return true;
|
|
78
|
+
if (ts.isDeleteExpression(parent)) return true;
|
|
79
|
+
if ((ts.isForOfStatement(parent) || ts.isForInStatement(parent)) && parent.initializer === current) return true;
|
|
80
|
+
}
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
function safeDeclaration(declaration: ts.VariableDeclaration): boolean {
|
|
84
|
+
if (safeDeclarations.has(declaration)) return safeDeclarations.get(declaration)!;
|
|
85
|
+
safeDeclarations.set(declaration, false);
|
|
86
|
+
if (!declaration.initializer || !ts.isIdentifier(declaration.name) || !ts.isVariableDeclarationList(declaration.parent) || !(declaration.parent.flags & ts.NodeFlags.Const)) return false;
|
|
87
|
+
const symbol = checker.getSymbolAtLocation(declaration.name);
|
|
88
|
+
if (!symbol) return false;
|
|
89
|
+
const object = ts.isObjectLiteralExpression(unwrap(declaration.initializer));
|
|
90
|
+
for (const node of nodes) {
|
|
91
|
+
if (!ts.isIdentifier(node) || node === declaration.name || referenceSymbol(node) !== symbol) continue;
|
|
92
|
+
if (isWriteTarget(node)) return false;
|
|
93
|
+
let usage: ts.Node = node;
|
|
94
|
+
while ((ts.isPropertyAccessExpression(usage.parent) || ts.isElementAccessExpression(usage.parent)) && usage.parent.expression === usage) usage = usage.parent;
|
|
95
|
+
const parent = usage.parent;
|
|
96
|
+
// Objects may only be read through properties. Passing their identity around could hide mutations.
|
|
97
|
+
if (object && usage === node) return false;
|
|
98
|
+
if (object && ts.isCallExpression(parent) && parent.expression === usage) return false;
|
|
99
|
+
if (object) {
|
|
100
|
+
const keys: string[] = [];
|
|
101
|
+
let access = usage;
|
|
102
|
+
while (ts.isPropertyAccessExpression(access) || ts.isElementAccessExpression(access)) {
|
|
103
|
+
const key = ts.isPropertyAccessExpression(access) ? access.name.text : access.argumentExpression && ts.isStringLiteral(access.argumentExpression) ? access.argumentExpression.text : undefined;
|
|
104
|
+
if (key === undefined) return false;
|
|
105
|
+
keys.unshift(key); access = access.expression;
|
|
106
|
+
}
|
|
107
|
+
let read: ts.Expression = unwrap(declaration.initializer);
|
|
108
|
+
for (const key of keys) {
|
|
109
|
+
if (!ts.isObjectLiteralExpression(read) || read.properties.some(item => !propertyValue(item) || !item.name || propertyName(item.name) === undefined)) return false;
|
|
110
|
+
const properties = read.properties.filter(item => propertyName(item.name!) === key);
|
|
111
|
+
if (properties.length !== 1 || !propertyValue(properties[0])) return false;
|
|
112
|
+
read = unwrap(propertyValue(properties[0])!);
|
|
113
|
+
}
|
|
114
|
+
// Never authorize through an object whose nested identity escapes to an alias or a call.
|
|
115
|
+
if (ts.isObjectLiteralExpression(read) || ts.isArrayLiteralExpression(read)) return false;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
safeDeclarations.set(declaration, true);
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function expressionNode(input: ts.Expression, seen = new Set<ts.Node>()): ts.Expression | undefined {
|
|
123
|
+
const node = unwrap(input);
|
|
124
|
+
if (seen.has(node)) return undefined;
|
|
125
|
+
seen.add(node);
|
|
126
|
+
if (ts.isStringLiteral(node) || ts.isNoSubstitutionTemplateLiteral(node) || ts.isObjectLiteralExpression(node)) return node;
|
|
127
|
+
if (ts.isIdentifier(node)) {
|
|
128
|
+
const symbol = referenceSymbol(node);
|
|
129
|
+
const declarations = symbol?.declarations;
|
|
130
|
+
if (declarations?.length !== 1 || !ts.isVariableDeclaration(declarations[0]) || declarations[0].getSourceFile() !== tree || !safeDeclaration(declarations[0])) return undefined;
|
|
131
|
+
return expressionNode(declarations[0].initializer!, seen);
|
|
132
|
+
}
|
|
133
|
+
if (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node)) {
|
|
134
|
+
const key = ts.isPropertyAccessExpression(node) ? node.name.text : node.argumentExpression && ts.isStringLiteral(unwrap(node.argumentExpression)) ? (unwrap(node.argumentExpression) as ts.StringLiteral).text : undefined;
|
|
135
|
+
const object = expressionNode(node.expression, seen);
|
|
136
|
+
if (key === undefined || !object || !ts.isObjectLiteralExpression(object)) return undefined;
|
|
137
|
+
if (object.properties.some(property => !propertyValue(property) || !property.name || propertyName(property.name) === undefined)) return undefined;
|
|
138
|
+
const matches = object.properties.filter(property => propertyName(property.name!) === key);
|
|
139
|
+
if (matches.length !== 1 || !propertyValue(matches[0])) return undefined;
|
|
140
|
+
return expressionNode(propertyValue(matches[0])!, seen);
|
|
141
|
+
}
|
|
142
|
+
return undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const textProps = new Set(['children', 'title', 'subtitle', 'eyebrow', 'byline', 'caption', 'lead', 'body', 'footer', 'description', 'author', 'label']);
|
|
146
|
+
const protectedTokens = new Set<ts.Node>();
|
|
147
|
+
function outerExpression(node: ts.Node) {
|
|
148
|
+
while ((ts.isParenthesizedExpression(node.parent) || ts.isAsExpression(node.parent) || ts.isTypeAssertionExpression(node.parent) || ts.isNonNullExpression(node.parent) || ts.isSatisfiesExpression(node.parent)) && node.parent.expression === node) node = node.parent;
|
|
149
|
+
return node;
|
|
150
|
+
}
|
|
151
|
+
function textConsumer(input: ts.Expression): boolean {
|
|
152
|
+
const node = outerExpression(input), parent = node.parent;
|
|
153
|
+
if (ts.isVariableDeclaration(parent)) {
|
|
154
|
+
// A direct const alias is checked at all of its own uses below.
|
|
155
|
+
return parent.initializer === node && ts.isIdentifier(parent.name) && safeDeclaration(parent);
|
|
156
|
+
}
|
|
157
|
+
if (ts.isJsxExpression(parent) && parent.expression === node) {
|
|
158
|
+
return ts.isJsxAttribute(parent.parent) ? textProps.has(parent.parent.name.getText(tree))
|
|
159
|
+
: ts.isJsxElement(parent.parent) || ts.isJsxFragment(parent.parent);
|
|
160
|
+
}
|
|
161
|
+
// Metadata is a documented text consumer, not an arbitrary escaped object.
|
|
162
|
+
if ((ts.isPropertyAssignment(parent) && parent.initializer === node) || (ts.isShorthandPropertyAssignment(parent) && parent.name === node)) {
|
|
163
|
+
const property = propertyName(parent.name);
|
|
164
|
+
const object = outerExpression(parent.parent);
|
|
165
|
+
const owner = object.parent;
|
|
166
|
+
return property !== undefined && textProps.has(property) && ts.isVariableDeclaration(owner) && ts.isIdentifier(owner.name) && owner.name.text === 'meta' && owner.initializer === object && safeDeclaration(owner);
|
|
167
|
+
}
|
|
168
|
+
return false;
|
|
169
|
+
}
|
|
170
|
+
for (const node of nodes) {
|
|
171
|
+
if (!ts.isIdentifier(node) && !ts.isPropertyAccessExpression(node) && !ts.isElementAccessExpression(node)) continue;
|
|
172
|
+
if (ts.isVariableDeclaration(node.parent) && node.parent.name === node) continue;
|
|
173
|
+
const value = expressionNode(node);
|
|
174
|
+
if (value && (ts.isStringLiteral(value) || ts.isNoSubstitutionTemplateLiteral(value)) && !textConsumer(node)) protectedTokens.add(value);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function expression(input: ts.Expression): TextSourceValue | undefined {
|
|
178
|
+
const node = expressionNode(input);
|
|
179
|
+
if (!node || protectedTokens.has(node) || (!ts.isStringLiteral(node) && !ts.isNoSubstitutionTemplateLiteral(node))) return undefined;
|
|
180
|
+
return descriptor(node, 'string', node.text);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function openingAt(line: number, column: number): ts.JsxOpeningElement | ts.JsxSelfClosingElement | undefined {
|
|
184
|
+
if (!Number.isInteger(line) || !Number.isInteger(column) || line < 1 || column < 1) return undefined;
|
|
185
|
+
const starts = tree.getLineStarts();
|
|
186
|
+
if (line > starts.length) return undefined;
|
|
187
|
+
const position = starts[line - 1] + column - 1;
|
|
188
|
+
return nodes.find(node => (ts.isJsxOpeningElement(node) || ts.isJsxSelfClosingElement(node)) && node.getStart(tree) <= position && node.tagName.end >= position) as ts.JsxOpeningElement | ts.JsxSelfClosingElement | undefined;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function attribute(opening: ts.JsxOpeningElement | ts.JsxSelfClosingElement, slot: string): TextSourceValue | undefined {
|
|
192
|
+
// A spread can override an earlier named field, so even a literal beside it is not a proof.
|
|
193
|
+
if (opening.attributes.properties.some(ts.isJsxSpreadAttribute)) return undefined;
|
|
194
|
+
const matches = opening.attributes.properties.filter(item => ts.isJsxAttribute(item) && item.name.getText(tree) === slot);
|
|
195
|
+
if (matches.length !== 1 || !ts.isJsxAttribute(matches[0])) return undefined;
|
|
196
|
+
const initializer = matches[0].initializer;
|
|
197
|
+
if (!initializer) return undefined;
|
|
198
|
+
if (ts.isStringLiteral(initializer)) {
|
|
199
|
+
const value = jsx(initializer, true);
|
|
200
|
+
return value === undefined ? undefined : descriptor(initializer, 'jsx-attribute', value);
|
|
201
|
+
}
|
|
202
|
+
return ts.isJsxExpression(initializer) && initializer.expression ? expression(initializer.expression) : undefined;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function children(opening: ts.JsxOpeningElement | ts.JsxSelfClosingElement) {
|
|
206
|
+
const rows: { childIndex: number; source?: TextSourceValue }[] = [];
|
|
207
|
+
if (ts.isJsxSelfClosingElement(opening)) {
|
|
208
|
+
const source = attribute(opening, 'children');
|
|
209
|
+
return source ? [{ childIndex: 0, source }] : [];
|
|
210
|
+
}
|
|
211
|
+
const parent = opening.parent;
|
|
212
|
+
if (!ts.isJsxElement(parent)) return rows;
|
|
213
|
+
for (const child of parent.children) {
|
|
214
|
+
if (ts.isJsxExpression(child) && !child.expression) continue;
|
|
215
|
+
let value: TextSourceValue | undefined;
|
|
216
|
+
if (ts.isJsxText(child)) {
|
|
217
|
+
const text = jsx(child, false);
|
|
218
|
+
if (text === undefined || text === '') continue;
|
|
219
|
+
value = descriptor(child, 'jsx-text', text);
|
|
220
|
+
} else if (ts.isJsxExpression(child) && child.expression && !child.dotDotDotToken) value = expression(child.expression);
|
|
221
|
+
rows.push({ childIndex: rows.length, ...(value ? { source: value } : {}) });
|
|
222
|
+
}
|
|
223
|
+
if (!rows.length) {
|
|
224
|
+
const source = attribute(opening, 'children');
|
|
225
|
+
if (source) rows.push({ childIndex: 0, source });
|
|
226
|
+
}
|
|
227
|
+
return rows;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Count linked authored occurrences by token identity, not matching visible text.
|
|
231
|
+
const occurrenceCounts = new Map<string, number>();
|
|
232
|
+
const identity = (value: TextSourceValue) => `${value.start}:${value.end}`;
|
|
233
|
+
for (const node of nodes) {
|
|
234
|
+
if (!ts.isJsxOpeningElement(node) && !ts.isJsxSelfClosingElement(node)) continue;
|
|
235
|
+
const values = children(node).map(child => child.source);
|
|
236
|
+
for (const item of node.attributes.properties) if (ts.isJsxAttribute(item) && item.name.getText(tree) !== 'children') values.push(attribute(node, item.name.getText(tree)));
|
|
237
|
+
for (const value of values) if (value) occurrenceCounts.set(identity(value), (occurrenceCounts.get(identity(value)) ?? 0) + 1);
|
|
238
|
+
}
|
|
239
|
+
const linked = (value: TextSourceValue | undefined) => value ? { ...value, linkedOccurrences: occurrenceCounts.get(identity(value)) ?? 1 } : undefined;
|
|
240
|
+
return {
|
|
241
|
+
resolveAt(line, column, slot = 'children', childIndex = 0) {
|
|
242
|
+
const opening = openingAt(line, column);
|
|
243
|
+
if (!opening) return undefined;
|
|
244
|
+
return linked(slot === 'children' ? children(opening)[childIndex]?.source : attribute(opening, slot));
|
|
245
|
+
},
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
export type JsonTextPath = (string | { id: string })[];
|
|
250
|
+
|
|
251
|
+
/** Index one immutable snapshot; array fields use stable record IDs, never positions. */
|
|
252
|
+
export function createJsonTextSourceResolver(file: string, source: string): (path: JsonTextPath) => TextSourceValue | undefined {
|
|
253
|
+
const tree = ts.parseJsonText(file, source);
|
|
254
|
+
if ((tree as unknown as { parseDiagnostics: ts.Diagnostic[] }).parseDiagnostics.length) return () => undefined;
|
|
255
|
+
const root: ts.Expression | undefined = tree.statements[0] && ts.isExpressionStatement(tree.statements[0]) ? tree.statements[0].expression : undefined;
|
|
256
|
+
const digest = textDigest(source);
|
|
257
|
+
const objects = new WeakMap<ts.ObjectLiteralExpression, Map<string, ts.Expression | undefined>>();
|
|
258
|
+
const arrays = new WeakMap<ts.ArrayLiteralExpression, Map<string, ts.Expression | undefined>>();
|
|
259
|
+
function fields(node: ts.ObjectLiteralExpression) {
|
|
260
|
+
let index = objects.get(node);
|
|
261
|
+
if (!index) {
|
|
262
|
+
index = new Map();
|
|
263
|
+
for (const item of node.properties) {
|
|
264
|
+
if (!ts.isPropertyAssignment(item)) continue;
|
|
265
|
+
const key = propertyName(item.name);
|
|
266
|
+
if (key !== undefined) index.set(key, index.has(key) ? undefined : item.initializer);
|
|
267
|
+
}
|
|
268
|
+
objects.set(node, index);
|
|
269
|
+
}
|
|
270
|
+
return index;
|
|
271
|
+
}
|
|
272
|
+
function records(node: ts.ArrayLiteralExpression) {
|
|
273
|
+
let index = arrays.get(node);
|
|
274
|
+
if (!index) {
|
|
275
|
+
index = new Map();
|
|
276
|
+
for (const item of node.elements) {
|
|
277
|
+
const id = ts.isObjectLiteralExpression(item) ? fields(item).get('id') : undefined;
|
|
278
|
+
if (id && ts.isStringLiteral(id)) index.set(id.text, index.has(id.text) ? undefined : item);
|
|
279
|
+
}
|
|
280
|
+
arrays.set(node, index);
|
|
281
|
+
}
|
|
282
|
+
return index;
|
|
283
|
+
}
|
|
284
|
+
return path => {
|
|
285
|
+
// A record's lookup key is structural identity, even when it is displayed.
|
|
286
|
+
if (path.at(-1) === 'id' && typeof path.at(-2) === 'object') return undefined;
|
|
287
|
+
let node = root;
|
|
288
|
+
for (const part of path) {
|
|
289
|
+
if (!node) return undefined;
|
|
290
|
+
if (typeof part === 'string') node = ts.isObjectLiteralExpression(node) ? fields(node).get(part) : undefined;
|
|
291
|
+
else node = ts.isArrayLiteralExpression(node) && part && typeof part.id === 'string' ? records(node).get(part.id) : undefined;
|
|
292
|
+
}
|
|
293
|
+
return node && ts.isStringLiteral(node) ? { file, digest, start: node.getStart(tree), end: node.end, kind: 'json-string', value: node.text, bindingId: textSourceBindingId(file, digest, node.getStart(tree), node.end) } : undefined;
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function serializeSourceValue(value: TextSourceValue, next: string): string {
|
|
298
|
+
const serialized = JSON.stringify(next);
|
|
299
|
+
return value.kind === 'jsx-text' || value.kind === 'jsx-attribute' ? `{${serialized}}` : serialized;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
export function validateTextSyntax(file: string, source: string) {
|
|
303
|
+
if (file.endsWith('.json')) { JSON.parse(source); return; }
|
|
304
|
+
const tree = ts.createSourceFile(file, source, ts.ScriptTarget.Latest, true, file.endsWith('.tsx') ? ts.ScriptKind.TSX : ts.ScriptKind.TS);
|
|
305
|
+
const diagnostics = (tree as ts.SourceFile & { parseDiagnostics: ts.Diagnostic[] }).parseDiagnostics;
|
|
306
|
+
if (diagnostics.length) throw new Error(`The correction would make the source invalid: ${ts.flattenDiagnosticMessageText(diagnostics[0].messageText, ' ')}`);
|
|
307
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { lstat, mkdir } from 'node:fs/promises';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { pathToFileURL } from 'node:url';
|
|
4
|
+
import { parseArgs } from 'node:util';
|
|
5
|
+
import { ThemeCatalog, readTheme, readThemeGuide, themeFile, readThemePaths } from './themes';
|
|
6
|
+
import { captureEntryExportInputs } from './export-inputs';
|
|
7
|
+
import { RenderFailure } from './render-error';
|
|
8
|
+
import { ExportChangedError, publishPDF } from './export-file';
|
|
9
|
+
|
|
10
|
+
const usage = 'Usage: npx opendoc themes list | inspect <id> | check <id> | preview <id> [--json]';
|
|
11
|
+
|
|
12
|
+
export async function runThemesCli(args: string[], root = process.cwd()): Promise<void> {
|
|
13
|
+
const { values, positionals } = parseArgs({ args: args[0] === '--' ? args.slice(1) : args, allowPositionals: true, options: {
|
|
14
|
+
json: { type: 'boolean' }, help: { type: 'boolean', short: 'h' },
|
|
15
|
+
} });
|
|
16
|
+
if (values.help) { console.log(values.json ? JSON.stringify({ usage }, null, 2) : usage); return; }
|
|
17
|
+
const [command = 'list', id, ...extra] = positionals;
|
|
18
|
+
if (extra.length || !['list', 'inspect', 'check', 'preview'].includes(command) || (command === 'list' ? id !== undefined : !id)) throw new Error(usage);
|
|
19
|
+
const catalog = new ThemeCatalog(root);
|
|
20
|
+
try {
|
|
21
|
+
if (command === 'list') {
|
|
22
|
+
const choices = (await catalog.list()).map(({ id, name, description, error }) => ({ id, name, description, error }));
|
|
23
|
+
console.log(JSON.stringify(choices, null, 2));
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
const unchanged = command === 'inspect' ? () => true : await captureEntryExportInputs(root, await themeFile(root, id, 'preview.tsx'));
|
|
27
|
+
const theme = await readTheme(root, id);
|
|
28
|
+
const guide = await readThemeGuide(root, id);
|
|
29
|
+
const paths = await readThemePaths(root, id);
|
|
30
|
+
if (command === 'inspect') console.log(JSON.stringify({ id, name: theme.name, description: theme.description, useFor: theme.useFor ?? [], principles: theme.principles ?? [], palette: theme.palette ?? [], geometry: theme.geometry ?? [], paths, guideWords: guide.trim().split(/\s+/).length }, null, 2));
|
|
31
|
+
else {
|
|
32
|
+
const preview = await catalog.preview(id);
|
|
33
|
+
if (!preview.artifact) throw new RenderFailure(preview.error ?? 'Theme preview failed.', preview.issues);
|
|
34
|
+
if (!unchanged()) throw new ExportChangedError();
|
|
35
|
+
const artifact = preview.artifact;
|
|
36
|
+
let output: string | undefined;
|
|
37
|
+
if (command === 'preview') {
|
|
38
|
+
for (const folder of [resolve(root, 'output'), resolve(root, 'output/themes')]) {
|
|
39
|
+
await mkdir(folder, { recursive: true });
|
|
40
|
+
const info = await lstat(folder);
|
|
41
|
+
if (!info.isDirectory() || info.isSymbolicLink()) throw new Error('Theme exports need a regular local output/themes folder.');
|
|
42
|
+
}
|
|
43
|
+
output = resolve(root, 'output/themes', `${id}.pdf`);
|
|
44
|
+
const previous = await lstat(output).catch(error => { if (error.code === 'ENOENT') return undefined; throw error; });
|
|
45
|
+
if (previous && (!previous.isFile() || previous.isSymbolicLink())) throw new Error('The theme export destination is not a regular file.');
|
|
46
|
+
await publishPDF(root, id, await catalog.pdf(id, artifact.hash), unchanged, 'themes');
|
|
47
|
+
}
|
|
48
|
+
console.log(JSON.stringify({ id, name: theme.name, status: 'ready', pages: artifact.pages.length, hash: artifact.hash, issues: artifact.issues ?? [], paths, ...(output ? { output } : {}), review: 'Inspect every PDF page; automated checks do not establish visual quality.' }, null, 2));
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
if (error instanceof ExportChangedError) throw new Error('The theme changed while preparing its preview. Retry after edits finish; no theme PDF was replaced.', { cause: error });
|
|
53
|
+
throw error;
|
|
54
|
+
} finally { await catalog.close(); }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
58
|
+
runThemesCli(process.argv.slice(2)).catch(error => { console.error(error instanceof Error ? error.message : String(error)); process.exitCode = 1; });
|
|
59
|
+
}
|