thursday-agent 0.16.0 → 0.17.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/.next/BUILD_ID +1 -1
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +3 -3
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +1 -1
- package/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/api/artifact/route.js +9 -9
- package/.next/server/app/api/artifact/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/files/route.js +3 -3
- package/.next/server/app/api/bot/memory/files/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/memory/route.js +8 -8
- package/.next/server/app/api/bot/memory/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/route.js +8 -8
- package/.next/server/app/api/bot/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/thread/[id]/route.js +3 -3
- package/.next/server/app/api/bot/thread/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/bot/thread/route.js +3 -3
- package/.next/server/app/api/bot/thread/route.js.nft.json +1 -1
- package/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/server/app/api/events/route.js.nft.json +1 -1
- package/.next/server/app/api/favicon/[host]/route.js.nft.json +1 -1
- package/.next/server/app/api/file/[...path]/route.js +4 -4
- package/.next/server/app/api/file/[...path]/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/catalog/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/credits/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/route.js.nft.json +1 -1
- package/.next/server/app/api/llm-model/subscription/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/oauth/callback/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/route.js.nft.json +1 -1
- package/.next/server/app/api/mcp/tools/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/edit/route.js +11 -11
- package/.next/server/app/api/memory/edit/route.js.nft.json +1 -1
- package/.next/server/app/api/memory/route.js.nft.json +1 -1
- package/.next/server/app/api/reach/route.js +11 -11
- package/.next/server/app/api/reach/route.js.nft.json +1 -1
- package/.next/server/app/api/routine/route.js +7 -7
- package/.next/server/app/api/routine/route.js.nft.json +1 -1
- package/.next/server/app/api/signins/route.js +5 -5
- package/.next/server/app/api/signins/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/[source]/[name]/route.js +7 -7
- package/.next/server/app/api/skills/[source]/[name]/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route.js +7 -7
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/call/route.js +5 -5
- package/.next/server/app/api/thursday/call/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/settings/route.js +5 -5
- package/.next/server/app/api/thursday/settings/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/text/route.js +13 -13
- package/.next/server/app/api/thursday/text/route.js.nft.json +1 -1
- package/.next/server/app/api/thursday/tool-call/route.js +12 -12
- package/.next/server/app/api/thursday/tool-call/route.js.nft.json +1 -1
- package/.next/server/app/api/workspace/files/route.js +8 -8
- package/.next/server/app/api/workspace/files/route.js.nft.json +1 -1
- package/.next/server/app/api/workspace/route.js +9 -9
- package/.next/server/app/api/workspace/route.js.nft.json +1 -1
- package/.next/server/app/artifact/[...path]/page/server-reference-manifest.json +3 -3
- package/.next/server/app/artifact/[...path]/page.js +4 -4
- package/.next/server/app/artifact/[...path]/page.js.nft.json +1 -1
- package/.next/server/app/artifact/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/favicon.ico/route.js.nft.json +1 -1
- package/.next/server/app/manifest.webmanifest/route.js.nft.json +1 -1
- package/.next/server/app/page/server-reference-manifest.json +60 -60
- package/.next/server/app/page.js +5 -5
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__00qyw01._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__01ypm_z._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__021dkui._.js +3 -0
- package/.next/server/chunks/{[root-of-the-server]__18e65_-._.js → [root-of-the-server]__03xamry._.js} +2 -2
- package/.next/server/chunks/[root-of-the-server]__05dp5tr._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__09gdscj._.js +7 -0
- package/.next/server/chunks/{[root-of-the-server]__10_7vqz._.js → [root-of-the-server]__0b36yfw._.js} +1 -1
- package/.next/server/chunks/{[root-of-the-server]__1gamvcz._.js → [root-of-the-server]__0jzwdg-._.js} +2 -2
- package/.next/server/chunks/[root-of-the-server]__0mrk2it._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__0nmust6._.js +5 -0
- package/.next/server/chunks/[root-of-the-server]__0svzjwa._.js +7 -0
- package/.next/server/chunks/{[root-of-the-server]__16yw3mb._.js → [root-of-the-server]__12m24xl._.js} +1 -1
- package/.next/server/chunks/{[root-of-the-server]__1zrwxis._.js → [root-of-the-server]__12tahb4._.js} +1 -1
- package/.next/server/chunks/{[root-of-the-server]__1ep-pao._.js → [root-of-the-server]__1_h4hf9._.js} +4 -4
- package/.next/server/chunks/{[root-of-the-server]__1-tdj60._.js → [root-of-the-server]__1de1o6m._.js} +2 -2
- package/.next/server/chunks/{[root-of-the-server]__1voqqyc._.js → [root-of-the-server]__1ignh6-._.js} +1 -1
- package/.next/server/chunks/[root-of-the-server]__1t02-1p._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1ygplx1._.js +1 -1
- package/.next/server/chunks/{[root-of-the-server]__11g20j-._.js → [root-of-the-server]__1yl5l25._.js} +1 -1
- package/.next/server/chunks/{[root-of-the-server]__1mbd2w_._.js → [root-of-the-server]__208vmyf._.js} +1 -1
- package/.next/server/chunks/{_1mfu9a3._.js → _02lipqs._.js} +1 -1
- package/.next/server/chunks/_05sa7xl._.js +1 -1
- package/.next/server/chunks/{_1rg5bfd._.js → _074f2ts._.js} +1 -1
- package/.next/server/chunks/_07nxi75._.js +3 -3
- package/.next/server/chunks/_08btiir._.js +7 -0
- package/.next/server/chunks/_0caua_s._.js +1 -1
- package/.next/server/chunks/{_0qoush_._.js → _0gt6cbj._.js} +1 -1
- package/.next/server/chunks/_0xygcxs._.js +3 -3
- package/.next/server/chunks/{_09c4we8._.js → _17i1lt2._.js} +1 -1
- package/.next/server/chunks/_19kea1l._.js +9 -9
- package/.next/server/chunks/_1gn2r2q._.js +3 -3
- package/.next/server/chunks/{_03niwml._.js → _1ntwp1s._.js} +1 -1
- package/.next/server/chunks/_1q6e56i._.js +3 -3
- package/.next/server/chunks/{_02fn1o3._.js → _1tkzgtm._.js} +1 -1
- package/.next/server/chunks/_1wl2g8_._.js +2 -2
- package/.next/server/chunks/{_1w6uf9k._.js → _1y3mnz4._.js} +1 -1
- package/.next/server/chunks/features_workspace_workspace_ts_13h82gv._.js +4 -4
- package/.next/server/chunks/features_workspace_workspace_ts_1iyjroj._.js +4 -4
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0cux6zt._.js → node_modules_consola_dist_chunks_prompt_mjs_04oxl25._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_0w32bqh._.js → node_modules_consola_dist_chunks_prompt_mjs_0g87zst._.js} +1 -1
- package/.next/server/chunks/{node_modules_consola_dist_chunks_prompt_mjs_1clmnaj._.js → node_modules_consola_dist_chunks_prompt_mjs_1341cnf._.js} +1 -1
- package/.next/server/chunks/ssr/{[root-of-the-server]__02jdtqa._.js → [root-of-the-server]__07660xu._.js} +2 -2
- package/.next/server/chunks/ssr/{[root-of-the-server]__0rnjkw2._.js → [root-of-the-server]__149x25m._.js} +2 -2
- package/.next/server/chunks/ssr/[root-of-the-server]__17h1ggo._.js +1 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__19s8qyj._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__1jyeu61._.js +1 -1
- package/.next/server/chunks/ssr/_088vzgn._.js +3 -0
- package/.next/server/chunks/ssr/{_0wupyvd._.js → _0_vnw9v._.js} +2 -2
- package/.next/server/chunks/ssr/{_1u5vsy-._.js → _0aeknva._.js} +2 -2
- package/.next/server/chunks/ssr/_0fo4552._.js +3 -3
- package/.next/server/chunks/ssr/{_0-ou1b0._.js → _0n2scfq._.js} +2 -2
- package/.next/server/chunks/ssr/_0o_o9ji._.js +3 -0
- package/.next/server/chunks/ssr/_0qne8xs._.js +3 -0
- package/.next/server/chunks/ssr/_0r0g9ki._.js +1 -1
- package/.next/server/chunks/ssr/_0v9r8g2._.js +3 -0
- package/.next/server/chunks/ssr/_0x8ophf._.js +1 -1
- package/.next/server/chunks/ssr/{_0ncuqct._.js → _0ztbn5q._.js} +2 -2
- package/.next/server/chunks/ssr/{_0w8r7o7._.js → _1-41lck._.js} +2 -2
- package/.next/server/chunks/ssr/{_10fwcz_._.js → _14dfh7a._.js} +2 -2
- package/.next/server/chunks/ssr/{_1f6s34p._.js → _15m5ctz._.js} +2 -2
- package/.next/server/chunks/ssr/{_11pnnvd._.js → _18-e7zi._.js} +2 -2
- package/.next/server/chunks/ssr/{_0y28gl3._.js → _1c5iqz1._.js} +3 -3
- package/.next/server/chunks/ssr/{_1dqaksc._.js → _1hbjh6n._.js} +2 -2
- package/.next/server/chunks/ssr/_1neoj22._.js +1 -1
- package/.next/server/chunks/ssr/{_1k0efi3._.js → _1sttj8x._.js} +2 -2
- package/.next/server/chunks/ssr/_1uij_rj._.js +1 -1
- package/.next/server/chunks/ssr/_1umbr78._.js +1 -1
- package/.next/server/chunks/ssr/{_17ble8k._.js → _1vtgiou._.js} +3 -3
- package/.next/server/chunks/ssr/_1w9w1ok._.js +1 -1
- package/.next/server/chunks/ssr/{_1z586yw._.js → _1z6pirx._.js} +2 -2
- package/.next/server/chunks/ssr/features_0796o8r._.js +1 -1
- package/.next/server/chunks/ssr/features_bot_1knyan7._.js +3 -0
- package/.next/server/chunks/ssr/features_bot_1kr0_m-._.js +3 -0
- package/.next/server/chunks/ssr/{features_config_11dpa7r._.js → features_config_0n0zu89._.js} +3 -3
- package/.next/server/chunks/ssr/features_connectors_components_mcp-setting_tsx_0jmbgvp._.js +1 -1
- package/.next/server/chunks/ssr/features_memory_components_memory-setting_tsx_0oa43sw._.js +1 -1
- package/.next/server/chunks/ssr/features_routine_components_routine-setting_tsx_055fknz._.js +1 -1
- package/.next/server/chunks/ssr/features_workspace_workspace_ts_1wc7_u_._.js +4 -4
- package/.next/server/chunks/ssr/{node_modules_1r5o-bo._.js → node_modules_00h1t1j._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1o9n7_n._.js → node_modules_02ficyi._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0u24mg0._.js → node_modules_03q71_j._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_138kcrz._.js → node_modules_03uejh6._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0x_2io2._.js → node_modules_06grs09._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0n1js5k._.js → node_modules_07mo9rn._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0bas34k._.js → node_modules_09e8kz_._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1dzye0n._.js → node_modules_09ikzel._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_18yn_bs._.js → node_modules_0_d_7gq._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0ka8te1._.js → node_modules_0_p8w7p._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0mark4f._.js → node_modules_0f5bmkb._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0sdjzae._.js → node_modules_0kwie-o._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1ozyial._.js → node_modules_0r-9e4n._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_156m1qh._.js → node_modules_0u_8z64._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0-ew8f8._.js → node_modules_0zf3wuu._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1xua-g1._.js → node_modules_1-7fc7h._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0ld7p-1._.js → node_modules_142z3o5._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0-8r9yw._.js → node_modules_180l3i0._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1tj7_9z._.js → node_modules_197vh1s._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0y5uubt._.js → node_modules_1_n9o9s._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0etz1i0._.js → node_modules_1fw32pg._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0bbwe-u._.js → node_modules_1hefp6h._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_097cl9f._.js → node_modules_1irot3r._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_0uibk9z._.js → node_modules_1om2itc._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_1zkzksd._.js → node_modules_1ubln7w._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_02uwbwp._.js → node_modules_1wyhtw9._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_133hjui._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0aczkbn._.js} +1 -1
- package/.next/server/chunks/ssr/{node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_1b3vwd3._.js → node_modules_@mermaid-js_parser_dist_chunks_mermaid-parser_core_0ah684s._.js} +1 -1
- package/.next/server/middleware-build-manifest.js +3 -3
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +61 -61
- package/.next/static/chunks/{1ub9obhgfm4jl.js → 024plctckvq-1.js} +1 -1
- package/.next/static/chunks/04toe_3nqfjqt.js +1 -0
- package/.next/static/chunks/{0609ejkwuy4sf.js → 0_b_bi2pobasx.js} +2 -2
- package/.next/static/chunks/{3ro66d9dc70bc.js → 0dfvwgnyrp8qt.js} +1 -1
- package/.next/static/chunks/{314szf8yer-3n.js → 0f47nnshdlucs.js} +1 -1
- package/.next/static/chunks/0sowxe4nnl5qu.js +1 -0
- package/.next/static/chunks/{07vcuxousbmy1.js → 0sv142q1yrva2.js} +1 -1
- package/.next/static/chunks/{0q4ws7qd21chx.js → 0tp868040z9nn.js} +2 -2
- package/.next/static/chunks/{0unonxbajyknw.js → 1-63yde5eaz7i.js} +1 -1
- package/.next/static/chunks/14qitm0kwsb6g.js +15 -0
- package/.next/static/chunks/{2xjr5b-yk_h3o.js → 1bgvxd3xny53w.js} +1 -1
- package/.next/static/chunks/{245_cp14iz637.js → 1cyrrz4q871aj.js} +1 -1
- package/.next/static/chunks/1st3b0c9vhxg3.js +1 -0
- package/.next/static/chunks/{39miwvg4k-b2d.js → 1wbqaidq7vc3_.js} +1 -1
- package/.next/static/chunks/{0cl_fgkphk1oz.js → 28sxb_k460e9h.js} +1 -1
- package/.next/static/chunks/{3j-l8un0d77v7.js → 2v6e_9p95o2e3.js} +1 -1
- package/.next/static/chunks/{23tx-2paw9qa6.js → 3-qp777vtfglj.js} +1 -1
- package/.next/static/chunks/{2jbjyazqfqyoh.js → 33ekkscpcq-z1.js} +1 -1
- package/.next/static/chunks/388l-zszwa_jf.js +1 -0
- package/.next/static/chunks/{3vwjs5kq11417.js → 38edd4rpcbvzp.js} +1 -1
- package/.next/static/chunks/3bwrvpjk95_x4.js +1 -0
- package/.next/static/chunks/{42m371fn687r5.js → 3itdg6t243u6f.js} +1 -1
- package/.next/static/chunks/{2oa4gpkzjtpni.js → 44vre0f7xqsh-.js} +1 -1
- package/README.md +12 -12
- package/guide/bots.md +48 -8
- package/guide/calls.md +13 -12
- package/guide/setup.md +4 -2
- package/package.json +1 -1
- package/public/sounds/NOTICE +3 -0
- package/public/sounds/opening-land.ogg +0 -0
- package/public/sounds/opening-rise.ogg +0 -0
- package/public/sounds/opening-step-1.ogg +0 -0
- package/public/sounds/opening-step-2.ogg +0 -0
- package/public/sounds/opening-step-3.ogg +0 -0
- package/skills/README.md +3 -0
- package/skills/artifact/SKILL.md +6 -5
- package/skills/artifact/references/book.md +22 -8
- package/skills/artifact/references/deck.md +4 -2
- package/skills/artifact/references/document.md +9 -0
- package/skills/artifact/references/sheet.md +108 -0
- package/skills/artifact/runtime/book/book.css +75 -0
- package/skills/artifact/runtime/book/book.html +6 -3
- package/skills/artifact/runtime/book/book.js +21 -0
- package/skills/artifact/runtime/canvas/canvas.css +1 -1
- package/skills/artifact/runtime/canvas/canvas.js +11 -1
- package/skills/artifact/runtime/deck/deck.css +164 -1
- package/skills/artifact/runtime/deck/deck.js +76 -1
- package/skills/artifact/runtime/document/docx.js +631 -0
- package/skills/artifact/runtime/document/markdown.mjs +58 -2
- package/skills/artifact/runtime/document/quick.css +66 -0
- package/skills/artifact/runtime/document/quick.html +1 -0
- package/skills/artifact/runtime/document/quick.js +154 -0
- package/skills/artifact/runtime/document/word.mjs +65 -0
- package/skills/artifact/runtime/sheet/format.mjs +385 -0
- package/skills/artifact/runtime/sheet/formula.mjs +755 -0
- package/skills/artifact/runtime/sheet/sheet.css +499 -0
- package/skills/artifact/runtime/sheet/sheet.html +102 -0
- package/skills/artifact/runtime/sheet/sheet.js +1361 -0
- package/skills/artifact/runtime/sheet/xlsx.mjs +320 -0
- package/skills/artifact/runtime/shell/head.html +1 -1
- package/skills/artifact/runtime/shell/shell.css +3 -0
- package/skills/artifact/runtime/shell/wear.mjs +47 -1
- package/skills/artifact/runtime/vendor/fflate.LICENSE.md +23 -0
- package/skills/artifact/runtime/vendor/fflate.mjs +2710 -0
- package/skills/artifact/scripts/book.mjs +141 -5
- package/skills/artifact/scripts/chart.mjs +229 -11
- package/skills/artifact/scripts/document.mjs +29 -2
- package/skills/artifact/scripts/spreadsheet.mjs +1068 -0
- package/skills/data-report/SKILL.md +8 -1
- package/.next/server/chunks/[root-of-the-server]__0aijj66._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0tvoct-._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__118qjk_._.js +0 -7
- package/.next/server/chunks/[root-of-the-server]__1b8tp42._.js +0 -5
- package/.next/server/chunks/[root-of-the-server]__1gzcbda._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1qcn1ti._.js +0 -7
- package/.next/server/chunks/_0xx3ryc._.js +0 -7
- package/.next/server/chunks/ssr/[root-of-the-server]__1y5_1cv._.js +0 -3
- package/.next/server/chunks/ssr/_0l1-xc6._.js +0 -3
- package/.next/server/chunks/ssr/_1g48r6t._.js +0 -3
- package/.next/server/chunks/ssr/features_bot_1k25jdg._.js +0 -3
- package/.next/server/chunks/ssr/features_bot_1s_-2vj._.js +0 -3
- package/.next/server/chunks/ssr/lib_utils_ts_0_f1xvm._.js +0 -3
- package/.next/static/chunks/00d54fcfm-lko.js +0 -1
- package/.next/static/chunks/0gjlsu6vrfzod.js +0 -1
- package/.next/static/chunks/17yqt1p_svv13.js +0 -1
- package/.next/static/chunks/203pbwecfs3tm.js +0 -1
- package/.next/static/chunks/3ghvoe_mlljzs.js +0 -15
- package/.next/static/chunks/3gn7dubb4-pwy.js +0 -1
- /package/.next/static/{RtLRT9EEaGn-OqwpMFdYg → VtbCMn-0G969p8QJsWdGv}/_buildManifest.js +0 -0
- /package/.next/static/{RtLRT9EEaGn-OqwpMFdYg → VtbCMn-0G969p8QJsWdGv}/_clientMiddlewareManifest.js +0 -0
- /package/.next/static/{RtLRT9EEaGn-OqwpMFdYg → VtbCMn-0G969p8QJsWdGv}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,631 @@
|
|
|
1
|
+
// A document as a Word file (.docx), made in the page from what the page shows now, edits and
|
|
2
|
+
// all: headings, paragraphs, lists and checklists, tables, quotes and notes, code, dividers,
|
|
3
|
+
// pictures and charts, links, bold, italic and the rest. An .docx is a zip of a few XML files
|
|
4
|
+
// (Office Open XML, ECMA-376); this writes them and zips them stored, which every reader opens.
|
|
5
|
+
// The paper size is left to Word, which takes the reader's own.
|
|
6
|
+
//
|
|
7
|
+
// `window.shell.docx()` resolves to `{ bytes, missed }`: the file, and the pictures that could
|
|
8
|
+
// not be put in (each stands in the file as its words). Export › Word file downloads it;
|
|
9
|
+
// document.mjs docx runs the same in a headless browser to hand the file back.
|
|
10
|
+
(() => {
|
|
11
|
+
const paper = document.getElementById("paper");
|
|
12
|
+
if (!paper || !window.shell) return;
|
|
13
|
+
|
|
14
|
+
const W = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
|
|
15
|
+
const R =
|
|
16
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
17
|
+
/** Pixels at 96 to the inch, as EMU (English metric units) and as twentieths of a point. */
|
|
18
|
+
const EMU = 9525;
|
|
19
|
+
/** The widest a picture is put in, in inches: a page's width inside its margins. */
|
|
20
|
+
const WIDEST_IN = 6;
|
|
21
|
+
|
|
22
|
+
const xml = (text) =>
|
|
23
|
+
String(text)
|
|
24
|
+
// Characters XML 1.0 cannot hold
|
|
25
|
+
.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f]/g, "")
|
|
26
|
+
.replace(
|
|
27
|
+
/[&<>"]/g,
|
|
28
|
+
(c) => ({ "&": "&", "<": "<", ">": ">", '"': """ })[c],
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
/* ── one export's state ──────────────────────────────────────────────── */
|
|
32
|
+
|
|
33
|
+
let rels; // [{ id, type, target, external }]
|
|
34
|
+
let media; // [{ name, bytes }]
|
|
35
|
+
let nums; // numbering instances: [{ abstract, restart }]
|
|
36
|
+
let drawings;
|
|
37
|
+
let missed;
|
|
38
|
+
let titled; // the first h1 was the title: headings under it are a level up
|
|
39
|
+
const rel = (type, target, external) => {
|
|
40
|
+
const id = `rId${rels.length + 10}`;
|
|
41
|
+
rels.push({ id, type, target, external });
|
|
42
|
+
return id;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/* ── pictures ────────────────────────────────────────────────────────── */
|
|
46
|
+
|
|
47
|
+
const loaded = (img) =>
|
|
48
|
+
img.complete && img.naturalWidth
|
|
49
|
+
? Promise.resolve()
|
|
50
|
+
: new Promise((ok) => {
|
|
51
|
+
img.addEventListener("load", ok, { once: true });
|
|
52
|
+
img.addEventListener("error", ok, { once: true });
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
/** A PNG of what `source` draws (an image or an SVG), at twice its size for a sharp print. */
|
|
56
|
+
const png = async (source, width, height) => {
|
|
57
|
+
const canvas = document.createElement("canvas");
|
|
58
|
+
canvas.width = Math.max(1, Math.round(width * 2));
|
|
59
|
+
canvas.height = Math.max(1, Math.round(height * 2));
|
|
60
|
+
const g = canvas.getContext("2d");
|
|
61
|
+
g.scale(2, 2);
|
|
62
|
+
g.drawImage(source, 0, 0, width, height);
|
|
63
|
+
const blob = await new Promise((ok) => canvas.toBlob(ok, "image/png"));
|
|
64
|
+
if (!blob) throw new Error("the picture could not be drawn");
|
|
65
|
+
return new Uint8Array(await blob.arrayBuffer());
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
/** A picture as a run in the file, its size kept, no wider than the page. */
|
|
69
|
+
const picture = (bytes, ext, width, height, words) => {
|
|
70
|
+
const name = `image${media.length + 1}.${ext}`;
|
|
71
|
+
media.push({ name, bytes });
|
|
72
|
+
const id = rel(
|
|
73
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/image",
|
|
74
|
+
`media/${name}`,
|
|
75
|
+
);
|
|
76
|
+
const scale = Math.min(1, (WIDEST_IN * 96) / width);
|
|
77
|
+
const cx = Math.round(width * scale * EMU);
|
|
78
|
+
const cy = Math.round(height * scale * EMU);
|
|
79
|
+
const n = ++drawings;
|
|
80
|
+
return `<w:r><w:drawing><wp:inline distT="0" distB="0" distL="0" distR="0"><wp:extent cx="${cx}" cy="${cy}"/><wp:docPr id="${n}" name="Picture ${n}" descr="${xml(words)}"/><wp:cNvGraphicFramePr><a:graphicFrameLocks noChangeAspect="1"/></wp:cNvGraphicFramePr><a:graphic><a:graphicData uri="http://schemas.openxmlformats.org/drawingml/2006/picture"><pic:pic><pic:nvPicPr><pic:cNvPr id="${n}" name="${name}"/><pic:cNvPicPr/></pic:nvPicPr><pic:blipFill><a:blip r:embed="${id}"/><a:stretch><a:fillRect/></a:stretch></pic:blipFill><pic:spPr><a:xfrm><a:off x="0" y="0"/><a:ext cx="${cx}" cy="${cy}"/></a:xfrm><a:prstGeom prst="rect"><a:avLst/></a:prstGeom></pic:spPr></pic:pic></a:graphicData></a:graphic></wp:inline></w:drawing></w:r>`;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/** An <img>: its own bytes when Word reads them (PNG, JPEG, GIF), else drawn as a PNG. */
|
|
84
|
+
const image = async (img) => {
|
|
85
|
+
await loaded(img);
|
|
86
|
+
const words = img.alt || img.getAttribute("src") || "picture";
|
|
87
|
+
const width = img.naturalWidth || img.width;
|
|
88
|
+
const height = img.naturalHeight || img.height;
|
|
89
|
+
try {
|
|
90
|
+
if (!width || !height) throw new Error("it did not load");
|
|
91
|
+
const answer = await fetch(img.currentSrc || img.src);
|
|
92
|
+
if (!answer.ok) throw new Error(`it answered ${answer.status}`);
|
|
93
|
+
const blob = await answer.blob();
|
|
94
|
+
const ext = {
|
|
95
|
+
"image/png": "png",
|
|
96
|
+
"image/jpeg": "jpeg",
|
|
97
|
+
"image/gif": "gif",
|
|
98
|
+
}[blob.type];
|
|
99
|
+
const bytes = ext
|
|
100
|
+
? new Uint8Array(await blob.arrayBuffer())
|
|
101
|
+
: await png(img, width, height);
|
|
102
|
+
return picture(bytes, ext ?? "png", width, height, words);
|
|
103
|
+
} catch (failed) {
|
|
104
|
+
missed.push(`${words}: ${failed.message}`);
|
|
105
|
+
return run(`[${words}]`, {});
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/** An <svg> drawn on the page — a chart — as a PNG of it at the size it is drawn. */
|
|
110
|
+
const svgPicture = async (svg) => {
|
|
111
|
+
const box = svg.getBoundingClientRect();
|
|
112
|
+
const words = svg.getAttribute("aria-label") || "chart";
|
|
113
|
+
try {
|
|
114
|
+
const copy = svg.cloneNode(true);
|
|
115
|
+
copy.setAttribute("xmlns", "http://www.w3.org/2000/svg");
|
|
116
|
+
copy.setAttribute("width", String(box.width));
|
|
117
|
+
copy.setAttribute("height", String(box.height));
|
|
118
|
+
// Colours the page's stylesheet gives it, written onto each part, as a file has no stylesheet
|
|
119
|
+
const from = svg.querySelectorAll("*");
|
|
120
|
+
const to = copy.querySelectorAll("*");
|
|
121
|
+
from.forEach((el, i) => {
|
|
122
|
+
const style = getComputedStyle(el);
|
|
123
|
+
for (const prop of [
|
|
124
|
+
"fill",
|
|
125
|
+
"stroke",
|
|
126
|
+
"stroke-width",
|
|
127
|
+
"stroke-dasharray",
|
|
128
|
+
"opacity",
|
|
129
|
+
"font-family",
|
|
130
|
+
"font-size",
|
|
131
|
+
"font-weight",
|
|
132
|
+
"text-anchor",
|
|
133
|
+
"dominant-baseline",
|
|
134
|
+
])
|
|
135
|
+
to[i].style.setProperty(prop, style.getPropertyValue(prop));
|
|
136
|
+
});
|
|
137
|
+
const url = URL.createObjectURL(
|
|
138
|
+
new Blob([new XMLSerializer().serializeToString(copy)], {
|
|
139
|
+
type: "image/svg+xml",
|
|
140
|
+
}),
|
|
141
|
+
);
|
|
142
|
+
try {
|
|
143
|
+
const img = new Image();
|
|
144
|
+
img.src = url;
|
|
145
|
+
await img.decode();
|
|
146
|
+
const bytes = await png(img, box.width, box.height);
|
|
147
|
+
return picture(bytes, "png", box.width, box.height, words);
|
|
148
|
+
} finally {
|
|
149
|
+
URL.revokeObjectURL(url);
|
|
150
|
+
}
|
|
151
|
+
} catch (failed) {
|
|
152
|
+
missed.push(`${words}: ${failed.message}`);
|
|
153
|
+
return run(`[${words}]`, {});
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
/* ── words ───────────────────────────────────────────────────────────── */
|
|
158
|
+
|
|
159
|
+
/** A run of text in a format: `{ b, i, u, s, code, sup, sub, mark, color, size }`. */
|
|
160
|
+
const run = (text, f) => {
|
|
161
|
+
if (!text) return "";
|
|
162
|
+
const props = [
|
|
163
|
+
f.style ? `<w:rStyle w:val="${f.style}"/>` : "",
|
|
164
|
+
f.code ? '<w:rFonts w:ascii="Courier New" w:hAnsi="Courier New"/>' : "",
|
|
165
|
+
f.b ? "<w:b/>" : "",
|
|
166
|
+
f.i ? "<w:i/>" : "",
|
|
167
|
+
f.s ? "<w:strike/>" : "",
|
|
168
|
+
f.color ? `<w:color w:val="${f.color}"/>` : "",
|
|
169
|
+
f.size ? `<w:sz w:val="${f.size}"/>` : "",
|
|
170
|
+
f.mark ? '<w:highlight w:val="yellow"/>' : "",
|
|
171
|
+
f.u ? '<w:u w:val="single"/>' : "",
|
|
172
|
+
f.code ? '<w:shd w:val="clear" w:color="auto" w:fill="F2F4F7"/>' : "",
|
|
173
|
+
f.sup ? '<w:vertAlign w:val="superscript"/>' : "",
|
|
174
|
+
f.sub ? '<w:vertAlign w:val="subscript"/>' : "",
|
|
175
|
+
].join("");
|
|
176
|
+
const rPr = props ? `<w:rPr>${props}</w:rPr>` : "";
|
|
177
|
+
return text
|
|
178
|
+
.split("\n")
|
|
179
|
+
.map(
|
|
180
|
+
(line, i) =>
|
|
181
|
+
(i ? `<w:r>${rPr}<w:br/></w:r>` : "") +
|
|
182
|
+
line
|
|
183
|
+
.split("\t")
|
|
184
|
+
.map(
|
|
185
|
+
(part, j) =>
|
|
186
|
+
(j ? `<w:r>${rPr}<w:tab/></w:r>` : "") +
|
|
187
|
+
(part
|
|
188
|
+
? `<w:r>${rPr}<w:t xml:space="preserve">${xml(part)}</w:t></w:r>`
|
|
189
|
+
: ""),
|
|
190
|
+
)
|
|
191
|
+
.join(""),
|
|
192
|
+
)
|
|
193
|
+
.join("");
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const hidden = (el) =>
|
|
197
|
+
el.hidden ||
|
|
198
|
+
el.getAttribute("aria-hidden") === "true" ||
|
|
199
|
+
getComputedStyle(el).display === "none";
|
|
200
|
+
|
|
201
|
+
const INLINE = new Set([
|
|
202
|
+
"A",
|
|
203
|
+
"ABBR",
|
|
204
|
+
"B",
|
|
205
|
+
"BDI",
|
|
206
|
+
"BR",
|
|
207
|
+
"CITE",
|
|
208
|
+
"CODE",
|
|
209
|
+
"DEL",
|
|
210
|
+
"DFN",
|
|
211
|
+
"EM",
|
|
212
|
+
"I",
|
|
213
|
+
"IMG",
|
|
214
|
+
"INPUT",
|
|
215
|
+
"KBD",
|
|
216
|
+
"LABEL",
|
|
217
|
+
"MARK",
|
|
218
|
+
"Q",
|
|
219
|
+
"S",
|
|
220
|
+
"SAMP",
|
|
221
|
+
"SMALL",
|
|
222
|
+
"SPAN",
|
|
223
|
+
"STRIKE",
|
|
224
|
+
"STRONG",
|
|
225
|
+
"SUB",
|
|
226
|
+
"SUP",
|
|
227
|
+
"TIME",
|
|
228
|
+
"U",
|
|
229
|
+
"VAR",
|
|
230
|
+
"SVG",
|
|
231
|
+
]);
|
|
232
|
+
const isInline = (node) =>
|
|
233
|
+
node.nodeType === Node.TEXT_NODE ||
|
|
234
|
+
(node.nodeType === Node.ELEMENT_NODE &&
|
|
235
|
+
INLINE.has(node.nodeName.toUpperCase()));
|
|
236
|
+
|
|
237
|
+
/** The runs a piece of a paragraph makes, in the format it is in. */
|
|
238
|
+
const inline = async (node, f) => {
|
|
239
|
+
if (node.nodeType === Node.TEXT_NODE) {
|
|
240
|
+
if (f.pre) return run(node.data, f);
|
|
241
|
+
// Spaces as the page shows them: runs of them one, as HTML draws them; a box ticked or
|
|
242
|
+
// not brings its own space
|
|
243
|
+
const text = node.data.replace(/\s+/g, " ");
|
|
244
|
+
const box = node.previousSibling?.nodeName?.toUpperCase() === "INPUT";
|
|
245
|
+
return run(box ? text.trimStart() : text, f);
|
|
246
|
+
}
|
|
247
|
+
if (node.nodeType !== Node.ELEMENT_NODE || hidden(node)) return "";
|
|
248
|
+
const tag = node.nodeName.toUpperCase();
|
|
249
|
+
if (tag === "BR") return "<w:r><w:br/></w:r>";
|
|
250
|
+
if (tag === "IMG") return image(node);
|
|
251
|
+
if (tag === "SVG") return svgPicture(node);
|
|
252
|
+
if (tag === "INPUT")
|
|
253
|
+
return node.type === "checkbox" ? run(node.checked ? "☑ " : "☐ ", f) : "";
|
|
254
|
+
// A footnote's way back to where it was cited, and how long the page takes to read on
|
|
255
|
+
// screen: the page's alone (quick.js clean takes the second out of a saved copy too)
|
|
256
|
+
if (node.classList.contains("fn-back") || node.matches(".chip.read"))
|
|
257
|
+
return "";
|
|
258
|
+
const next = { ...f };
|
|
259
|
+
if (tag === "B" || tag === "STRONG") next.b = true;
|
|
260
|
+
if (tag === "I" || tag === "EM" || tag === "CITE" || tag === "DFN")
|
|
261
|
+
next.i = true;
|
|
262
|
+
if (tag === "S" || tag === "DEL" || tag === "STRIKE") next.s = true;
|
|
263
|
+
if (tag === "U") next.u = true;
|
|
264
|
+
if (tag === "CODE" || tag === "KBD" || tag === "SAMP") next.code = true;
|
|
265
|
+
if (tag === "SUP") next.sup = true;
|
|
266
|
+
if (tag === "SUB") next.sub = true;
|
|
267
|
+
if (tag === "MARK") next.mark = true;
|
|
268
|
+
if (tag === "SMALL") next.size = 18;
|
|
269
|
+
let out = "";
|
|
270
|
+
for (const child of node.childNodes) out += await inline(child, next);
|
|
271
|
+
if (tag === "A") {
|
|
272
|
+
const href = node.getAttribute("href") ?? "";
|
|
273
|
+
if (/^(https?:|mailto:)/i.test(href)) {
|
|
274
|
+
const id = rel(
|
|
275
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink",
|
|
276
|
+
href,
|
|
277
|
+
true,
|
|
278
|
+
);
|
|
279
|
+
let linked = "";
|
|
280
|
+
for (const child of node.childNodes)
|
|
281
|
+
linked += await inline(child, { ...next, style: "Hyperlink" });
|
|
282
|
+
return `<w:hyperlink r:id="${id}">${linked}</w:hyperlink>`;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
return out;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
/** A paragraph of `runs` in `style`, with more of its properties in `props`. */
|
|
289
|
+
const para = (runs, style, props = "") =>
|
|
290
|
+
`<w:p><w:pPr>${style ? `<w:pStyle w:val="${style}"/>` : ""}${props}</w:pPr>${runs}</w:p>`;
|
|
291
|
+
|
|
292
|
+
/* ── blocks ──────────────────────────────────────────────────────────── */
|
|
293
|
+
|
|
294
|
+
const listNum = (ordered, start) => {
|
|
295
|
+
nums.push({ abstract: ordered ? 1 : 0, start });
|
|
296
|
+
return nums.length;
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
/** The paragraphs and tables a block element makes. `at` carries the list or quote it is in. */
|
|
300
|
+
const blocks = async (el, at) => {
|
|
301
|
+
const out = [];
|
|
302
|
+
// Words and inline pieces between blocks gather into one paragraph each
|
|
303
|
+
let pending = [];
|
|
304
|
+
const flush = async () => {
|
|
305
|
+
if (!pending.length) return;
|
|
306
|
+
let runs = "";
|
|
307
|
+
for (const node of pending) runs += await inline(node, at.f ?? {});
|
|
308
|
+
pending = [];
|
|
309
|
+
if (!runs.replace(/<[^>]+>/g, "").trim() && !/w:drawing/.test(runs))
|
|
310
|
+
return;
|
|
311
|
+
// The spaces between the page's tags, at a paragraph's two ends, are not words
|
|
312
|
+
const open = '<w:t xml:space="preserve">';
|
|
313
|
+
const first = runs.indexOf(open) + open.length;
|
|
314
|
+
if (first >= open.length)
|
|
315
|
+
runs = runs.slice(0, first) + runs.slice(first).replace(/^\s+/, "");
|
|
316
|
+
const last = runs.lastIndexOf("</w:t>");
|
|
317
|
+
if (last !== -1)
|
|
318
|
+
runs = runs.slice(0, last).replace(/\s+$/, "") + runs.slice(last);
|
|
319
|
+
// An item's first paragraph carries its number or bullet; the rest sit under it
|
|
320
|
+
// Word reads a paragraph's properties in the schema's order: the number before the rest
|
|
321
|
+
let props = "";
|
|
322
|
+
if (at.list?.first.value && at.list.num) {
|
|
323
|
+
props = `<w:numPr><w:ilvl w:val="${at.list.level}"/><w:numId w:val="${at.list.num}"/></w:numPr>`;
|
|
324
|
+
at.list.first.value = false;
|
|
325
|
+
} else if (at.list)
|
|
326
|
+
props = `<w:ind w:left="${720 + at.list.level * 360}"/>`;
|
|
327
|
+
out.push(para(runs, at.style, props + (at.props ?? "")));
|
|
328
|
+
};
|
|
329
|
+
for (const node of el.childNodes) {
|
|
330
|
+
if (isInline(node)) {
|
|
331
|
+
pending.push(node);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
await flush();
|
|
335
|
+
if (node.nodeType === Node.ELEMENT_NODE && !hidden(node))
|
|
336
|
+
out.push(...(await block(node, at)));
|
|
337
|
+
}
|
|
338
|
+
await flush();
|
|
339
|
+
return out;
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
const block = async (el, at) => {
|
|
343
|
+
const tag = el.nodeName.toUpperCase();
|
|
344
|
+
const heading = /^H([1-6])$/.exec(tag);
|
|
345
|
+
if (heading) {
|
|
346
|
+
const level = Number(heading[1]);
|
|
347
|
+
let style;
|
|
348
|
+
if (level === 1 && !titled) {
|
|
349
|
+
titled = true;
|
|
350
|
+
style = "Title";
|
|
351
|
+
} else
|
|
352
|
+
style = `Heading${Math.min(4, Math.max(1, level - (titled ? 1 : 0)))}`;
|
|
353
|
+
return blocks(el, { ...at, list: null, style, props: "" });
|
|
354
|
+
}
|
|
355
|
+
if (tag === "P") {
|
|
356
|
+
const kind = el.classList;
|
|
357
|
+
const style = kind.contains("lede")
|
|
358
|
+
? "Subtitle"
|
|
359
|
+
: kind.contains("stat")
|
|
360
|
+
? "Stat"
|
|
361
|
+
: kind.contains("byline") || kind.contains("kicker")
|
|
362
|
+
? "Byline"
|
|
363
|
+
: (at.style ?? null);
|
|
364
|
+
return blocks(el, { ...at, style });
|
|
365
|
+
}
|
|
366
|
+
if (tag === "UL" || tag === "OL") {
|
|
367
|
+
const level = at.list ? at.list.level + 1 : 0;
|
|
368
|
+
// A checklist's boxes are its marks: no bullet beside them
|
|
369
|
+
const checks = [...el.children].every(
|
|
370
|
+
(li) => li.querySelector(":scope > input[type=checkbox]") !== null,
|
|
371
|
+
);
|
|
372
|
+
const num = checks
|
|
373
|
+
? 0
|
|
374
|
+
: listNum(tag === "OL", Number(el.getAttribute("start")) || 1);
|
|
375
|
+
const out = [];
|
|
376
|
+
for (const li of el.children) {
|
|
377
|
+
if (li.nodeName.toUpperCase() !== "LI" || hidden(li)) continue;
|
|
378
|
+
const first = { value: true };
|
|
379
|
+
out.push(
|
|
380
|
+
...(await blocks(li, {
|
|
381
|
+
...at,
|
|
382
|
+
list: { num, level, first },
|
|
383
|
+
style: "ListParagraph",
|
|
384
|
+
props: "",
|
|
385
|
+
})),
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
return out;
|
|
389
|
+
}
|
|
390
|
+
if (tag === "TABLE") return [await table(el, at)];
|
|
391
|
+
if (tag === "PRE") {
|
|
392
|
+
const runs = await inline(el, { code: true, pre: true });
|
|
393
|
+
return [para(runs, "Code")];
|
|
394
|
+
}
|
|
395
|
+
if (tag === "HR")
|
|
396
|
+
return [
|
|
397
|
+
para(
|
|
398
|
+
"",
|
|
399
|
+
null,
|
|
400
|
+
'<w:pBdr><w:bottom w:val="single" w:sz="6" w:space="1" w:color="C9CED6"/></w:pBdr>',
|
|
401
|
+
),
|
|
402
|
+
];
|
|
403
|
+
if (tag === "BLOCKQUOTE" || el.classList.contains("note"))
|
|
404
|
+
return blocks(el, { ...at, style: "Quote", props: "" });
|
|
405
|
+
if (tag === "FIGCAPTION" || tag === "CAPTION")
|
|
406
|
+
return blocks(el, { ...at, style: "Caption", props: "" });
|
|
407
|
+
if (tag === "FIGURE")
|
|
408
|
+
return blocks(el, {
|
|
409
|
+
...at,
|
|
410
|
+
style: null,
|
|
411
|
+
props: '<w:jc w:val="center"/>',
|
|
412
|
+
});
|
|
413
|
+
// A chart's own table and CSV, folded under it on the page: the picture carries the chart
|
|
414
|
+
if (tag === "DETAILS" && el.closest("figure.chart")) return [];
|
|
415
|
+
// A fold is open in Word: what it holds, under its summary
|
|
416
|
+
if (tag === "DETAILS") return blocks(el, at);
|
|
417
|
+
if (tag === "SUMMARY") return blocks(el, { ...at, f: { b: true } });
|
|
418
|
+
if (
|
|
419
|
+
tag === "SCRIPT" ||
|
|
420
|
+
tag === "STYLE" ||
|
|
421
|
+
tag === "TEMPLATE" ||
|
|
422
|
+
tag === "BUTTON"
|
|
423
|
+
)
|
|
424
|
+
return [];
|
|
425
|
+
// A box of blocks: what is in it, in order
|
|
426
|
+
return blocks(el, at);
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const table = async (el, at) => {
|
|
430
|
+
const rows = [...el.rows].filter((row) => !hidden(row));
|
|
431
|
+
const width = Math.max(
|
|
432
|
+
1,
|
|
433
|
+
...rows.map((row) =>
|
|
434
|
+
[...row.cells].reduce((n, cell) => n + (cell.colSpan || 1), 0),
|
|
435
|
+
),
|
|
436
|
+
);
|
|
437
|
+
// Even shares of a page's width inside its margins; Word fits them to what they hold
|
|
438
|
+
const grid = `<w:tblGrid>${`<w:gridCol w:w="${Math.floor(9000 / width)}"/>`.repeat(width)}</w:tblGrid>`;
|
|
439
|
+
let body = "";
|
|
440
|
+
for (const row of rows) {
|
|
441
|
+
const head = row.parentElement?.nodeName.toUpperCase() === "THEAD";
|
|
442
|
+
let cells = "";
|
|
443
|
+
for (const cell of row.cells) {
|
|
444
|
+
const inside = await blocks(cell, {
|
|
445
|
+
...at,
|
|
446
|
+
list: null,
|
|
447
|
+
style: null,
|
|
448
|
+
props: /right|end/.test(getComputedStyle(cell).textAlign)
|
|
449
|
+
? '<w:jc w:val="right"/>'
|
|
450
|
+
: "",
|
|
451
|
+
f: head || cell.nodeName === "TH" ? { b: true } : {},
|
|
452
|
+
});
|
|
453
|
+
const span =
|
|
454
|
+
cell.colSpan > 1 ? `<w:gridSpan w:val="${cell.colSpan}"/>` : "";
|
|
455
|
+
const shade = head
|
|
456
|
+
? '<w:shd w:val="clear" w:color="auto" w:fill="F2F4F7"/>'
|
|
457
|
+
: "";
|
|
458
|
+
cells += `<w:tc><w:tcPr><w:tcW w:w="0" w:type="auto"/>${span}${shade}</w:tcPr>${inside.join("") || "<w:p/>"}</w:tc>`;
|
|
459
|
+
}
|
|
460
|
+
body += `<w:tr>${head ? "<w:trPr><w:tblHeader/></w:trPr>" : ""}${cells}</w:tr>`;
|
|
461
|
+
}
|
|
462
|
+
return `<w:tbl><w:tblPr><w:tblStyle w:val="Grid"/><w:tblW w:w="5000" w:type="pct"/><w:tblLook w:val="04A0" w:firstRow="1" w:lastRow="0" w:firstColumn="0" w:lastColumn="0" w:noHBand="0" w:noVBand="1"/></w:tblPr>${grid}${body}</w:tbl><w:p/>`;
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
/* ── the file ────────────────────────────────────────────────────────── */
|
|
466
|
+
|
|
467
|
+
const STYLES = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
468
|
+
<w:styles xmlns:w="${W}"><w:docDefaults><w:rPrDefault><w:rPr><w:rFonts w:ascii="Calibri" w:hAnsi="Calibri" w:cs="Calibri"/><w:sz w:val="22"/><w:szCs w:val="22"/><w:lang w:val="${xml(document.documentElement.lang || "en-US")}"/></w:rPr></w:rPrDefault><w:pPrDefault><w:pPr><w:spacing w:after="160" w:line="276" w:lineRule="auto"/></w:pPr></w:pPrDefault></w:docDefaults>
|
|
469
|
+
<w:style w:type="paragraph" w:default="1" w:styleId="Normal"><w:name w:val="Normal"/><w:qFormat/></w:style>
|
|
470
|
+
<w:style w:type="paragraph" w:styleId="Title"><w:name w:val="Title"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/><w:pPr><w:spacing w:after="120"/></w:pPr><w:rPr><w:b/><w:sz w:val="48"/></w:rPr></w:style>
|
|
471
|
+
<w:style w:type="paragraph" w:styleId="Subtitle"><w:name w:val="Subtitle"/><w:basedOn w:val="Normal"/><w:qFormat/><w:rPr><w:color w:val="4B5563"/><w:sz w:val="26"/></w:rPr></w:style>
|
|
472
|
+
<w:style w:type="paragraph" w:styleId="Byline"><w:name w:val="Byline"/><w:basedOn w:val="Normal"/><w:rPr><w:color w:val="6B7280"/><w:sz w:val="18"/></w:rPr></w:style>
|
|
473
|
+
<w:style w:type="paragraph" w:styleId="Stat"><w:name w:val="Stat"/><w:basedOn w:val="Normal"/><w:rPr><w:b/><w:sz w:val="36"/></w:rPr></w:style>
|
|
474
|
+
${[1, 2, 3, 4]
|
|
475
|
+
.map(
|
|
476
|
+
(n) =>
|
|
477
|
+
`<w:style w:type="paragraph" w:styleId="Heading${n}"><w:name w:val="heading ${n}"/><w:basedOn w:val="Normal"/><w:next w:val="Normal"/><w:qFormat/><w:pPr><w:keepNext/><w:spacing w:before="${[360, 280, 240, 200][n - 1]}" w:after="120"/><w:outlineLvl w:val="${n - 1}"/></w:pPr><w:rPr><w:b/><w:sz w:val="${[36, 30, 26, 22][n - 1]}"/></w:rPr></w:style>`,
|
|
478
|
+
)
|
|
479
|
+
.join("")}
|
|
480
|
+
<w:style w:type="paragraph" w:styleId="ListParagraph"><w:name w:val="List Paragraph"/><w:basedOn w:val="Normal"/><w:qFormat/><w:pPr><w:spacing w:after="60"/><w:contextualSpacing/></w:pPr></w:style>
|
|
481
|
+
<w:style w:type="paragraph" w:styleId="Quote"><w:name w:val="Quote"/><w:basedOn w:val="Normal"/><w:qFormat/><w:pPr><w:pBdr><w:left w:val="single" w:sz="18" w:space="8" w:color="C9CED6"/></w:pBdr><w:ind w:left="284"/></w:pPr><w:rPr><w:color w:val="374151"/></w:rPr></w:style>
|
|
482
|
+
<w:style w:type="paragraph" w:styleId="Code"><w:name w:val="Code"/><w:basedOn w:val="Normal"/><w:pPr><w:shd w:val="clear" w:color="auto" w:fill="F2F4F7"/><w:spacing w:after="160" w:line="240" w:lineRule="auto"/></w:pPr><w:rPr><w:rFonts w:ascii="Courier New" w:hAnsi="Courier New"/><w:sz w:val="19"/></w:rPr></w:style>
|
|
483
|
+
<w:style w:type="paragraph" w:styleId="Caption"><w:name w:val="caption"/><w:basedOn w:val="Normal"/><w:qFormat/><w:pPr><w:jc w:val="center"/></w:pPr><w:rPr><w:i/><w:color w:val="6B7280"/><w:sz w:val="18"/></w:rPr></w:style>
|
|
484
|
+
<w:style w:type="character" w:styleId="Hyperlink"><w:name w:val="Hyperlink"/><w:rPr><w:color w:val="0563C1"/><w:u w:val="single"/></w:rPr></w:style>
|
|
485
|
+
<w:style w:type="table" w:styleId="Grid"><w:name w:val="Table Grid"/><w:pPr><w:spacing w:after="0"/></w:pPr><w:tblPr><w:tblBorders><w:top w:val="single" w:sz="4" w:color="C9CED6"/><w:left w:val="single" w:sz="4" w:color="C9CED6"/><w:bottom w:val="single" w:sz="4" w:color="C9CED6"/><w:right w:val="single" w:sz="4" w:color="C9CED6"/><w:insideH w:val="single" w:sz="4" w:color="C9CED6"/><w:insideV w:val="single" w:sz="4" w:color="C9CED6"/></w:tblBorders><w:tblCellMar><w:top w:w="60" w:type="dxa"/><w:left w:w="100" w:type="dxa"/><w:bottom w:w="60" w:type="dxa"/><w:right w:w="100" w:type="dxa"/></w:tblCellMar></w:tblPr></w:style>
|
|
486
|
+
</w:styles>`;
|
|
487
|
+
|
|
488
|
+
const numbering = () => {
|
|
489
|
+
const level = (ordered, n) => {
|
|
490
|
+
const fmt = ordered
|
|
491
|
+
? ["decimal", "lowerLetter", "lowerRoman"][n % 3]
|
|
492
|
+
: "bullet";
|
|
493
|
+
const text = ordered ? `%${n + 1}.` : ["•", "◦", "▪"][n % 3];
|
|
494
|
+
return `<w:lvl w:ilvl="${n}"><w:start w:val="1"/><w:numFmt w:val="${fmt}"/><w:lvlText w:val="${text}"/><w:lvlJc w:val="left"/><w:pPr><w:ind w:left="${720 + n * 360}" w:hanging="360"/></w:pPr></w:lvl>`;
|
|
495
|
+
};
|
|
496
|
+
const abstract = (id, ordered) =>
|
|
497
|
+
`<w:abstractNum w:abstractNumId="${id}"><w:multiLevelType w:val="hybridMultilevel"/>${Array.from({ length: 9 }, (_, n) => level(ordered, n)).join("")}</w:abstractNum>`;
|
|
498
|
+
return `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
499
|
+
<w:numbering xmlns:w="${W}">${abstract(0, false)}${abstract(1, true)}${nums
|
|
500
|
+
.map(
|
|
501
|
+
(one, i) =>
|
|
502
|
+
`<w:num w:numId="${i + 1}"><w:abstractNumId w:val="${one.abstract}"/>${one.abstract ? `<w:lvlOverride w:ilvl="0"><w:startOverride w:val="${one.start}"/></w:lvlOverride>` : ""}</w:num>`,
|
|
503
|
+
)
|
|
504
|
+
.join("")}</w:numbering>`;
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
/* ── a zip, stored ───────────────────────────────────────────────────── */
|
|
508
|
+
|
|
509
|
+
const CRC = new Uint32Array(256).map((_, n) => {
|
|
510
|
+
let c = n;
|
|
511
|
+
for (let k = 0; k < 8; k++) c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
512
|
+
return c >>> 0;
|
|
513
|
+
});
|
|
514
|
+
const crc32 = (bytes) => {
|
|
515
|
+
let c = 0xffffffff;
|
|
516
|
+
for (const b of bytes) c = CRC[(c ^ b) & 0xff] ^ (c >>> 8);
|
|
517
|
+
return (c ^ 0xffffffff) >>> 0;
|
|
518
|
+
};
|
|
519
|
+
/** Files `{ name: bytes }` as one zip, each stored as it is (method 0). */
|
|
520
|
+
const zip = (files) => {
|
|
521
|
+
const encode = new TextEncoder();
|
|
522
|
+
const parts = [];
|
|
523
|
+
const central = [];
|
|
524
|
+
let offset = 0;
|
|
525
|
+
for (const [name, bytes] of Object.entries(files)) {
|
|
526
|
+
const path = encode.encode(name);
|
|
527
|
+
const crc = crc32(bytes);
|
|
528
|
+
const head = new DataView(new ArrayBuffer(30));
|
|
529
|
+
head.setUint32(0, 0x04034b50, true);
|
|
530
|
+
head.setUint16(4, 20, true);
|
|
531
|
+
head.setUint16(6, 0x0800, true); // names in UTF-8
|
|
532
|
+
head.setUint16(8, 0, true);
|
|
533
|
+
head.setUint16(10, 0, true);
|
|
534
|
+
head.setUint16(12, 0x21, true); // 1980-01-01
|
|
535
|
+
head.setUint32(14, crc, true);
|
|
536
|
+
head.setUint32(18, bytes.length, true);
|
|
537
|
+
head.setUint32(22, bytes.length, true);
|
|
538
|
+
head.setUint16(26, path.length, true);
|
|
539
|
+
parts.push(new Uint8Array(head.buffer), path, bytes);
|
|
540
|
+
const entry = new DataView(new ArrayBuffer(46));
|
|
541
|
+
entry.setUint32(0, 0x02014b50, true);
|
|
542
|
+
entry.setUint16(4, 20, true);
|
|
543
|
+
entry.setUint16(6, 20, true);
|
|
544
|
+
entry.setUint16(8, 0x0800, true);
|
|
545
|
+
entry.setUint16(14, 0x21, true);
|
|
546
|
+
entry.setUint32(16, crc, true);
|
|
547
|
+
entry.setUint32(20, bytes.length, true);
|
|
548
|
+
entry.setUint32(24, bytes.length, true);
|
|
549
|
+
entry.setUint16(28, path.length, true);
|
|
550
|
+
entry.setUint32(42, offset, true);
|
|
551
|
+
central.push(new Uint8Array(entry.buffer), path);
|
|
552
|
+
offset += 30 + path.length + bytes.length;
|
|
553
|
+
}
|
|
554
|
+
const size = central.reduce((n, part) => n + part.length, 0);
|
|
555
|
+
const end = new DataView(new ArrayBuffer(22));
|
|
556
|
+
end.setUint32(0, 0x06054b50, true);
|
|
557
|
+
end.setUint16(8, Object.keys(files).length, true);
|
|
558
|
+
end.setUint16(10, Object.keys(files).length, true);
|
|
559
|
+
end.setUint32(12, size, true);
|
|
560
|
+
end.setUint32(16, offset, true);
|
|
561
|
+
const all = [...parts, ...central, new Uint8Array(end.buffer)];
|
|
562
|
+
const out = new Uint8Array(all.reduce((n, part) => n + part.length, 0));
|
|
563
|
+
let at = 0;
|
|
564
|
+
for (const part of all) {
|
|
565
|
+
out.set(part, at);
|
|
566
|
+
at += part.length;
|
|
567
|
+
}
|
|
568
|
+
return out;
|
|
569
|
+
};
|
|
570
|
+
|
|
571
|
+
/* ── the whole of it ─────────────────────────────────────────────────── */
|
|
572
|
+
|
|
573
|
+
window.shell.docx = async () => {
|
|
574
|
+
rels = [];
|
|
575
|
+
media = [];
|
|
576
|
+
nums = [];
|
|
577
|
+
drawings = 0;
|
|
578
|
+
missed = [];
|
|
579
|
+
titled = false;
|
|
580
|
+
const body = (await blocks(paper, {})).join("");
|
|
581
|
+
const title = document.title || "Document";
|
|
582
|
+
const text = (s) => new TextEncoder().encode(s);
|
|
583
|
+
const files = {
|
|
584
|
+
"[Content_Types].xml":
|
|
585
|
+
text(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
586
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Default Extension="png" ContentType="image/png"/><Default Extension="jpeg" ContentType="image/jpeg"/><Default Extension="gif" ContentType="image/gif"/><Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/><Override PartName="/word/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.styles+xml"/><Override PartName="/word/numbering.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.numbering+xml"/><Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/></Types>`),
|
|
587
|
+
"_rels/.rels":
|
|
588
|
+
text(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
589
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="${R}/officeDocument" Target="word/document.xml"/><Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/></Relationships>`),
|
|
590
|
+
"docProps/core.xml":
|
|
591
|
+
text(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
592
|
+
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"><dc:title>${xml(title)}</dc:title><dcterms:created xsi:type="dcterms:W3CDTF">${new Date().toISOString().slice(0, 19)}Z</dcterms:created></cp:coreProperties>`),
|
|
593
|
+
"word/_rels/document.xml.rels":
|
|
594
|
+
text(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
595
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="${R}/styles" Target="styles.xml"/><Relationship Id="rId2" Type="${R}/numbering" Target="numbering.xml"/>${rels
|
|
596
|
+
.map(
|
|
597
|
+
(one) =>
|
|
598
|
+
`<Relationship Id="${one.id}" Type="${one.type}" Target="${xml(one.target)}"${one.external ? ' TargetMode="External"' : ""}/>`,
|
|
599
|
+
)
|
|
600
|
+
.join("")}</Relationships>`),
|
|
601
|
+
"word/styles.xml": text(STYLES),
|
|
602
|
+
"word/numbering.xml": text(numbering()),
|
|
603
|
+
"word/document.xml":
|
|
604
|
+
text(`<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
605
|
+
<w:document xmlns:w="${W}" xmlns:r="${R}" xmlns:wp="http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing" xmlns:a="http://schemas.openxmlformats.org/drawingml/2006/main" xmlns:pic="http://schemas.openxmlformats.org/drawingml/2006/picture"><w:body>${body}<w:sectPr><w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440" w:header="708" w:footer="708" w:gutter="0"/></w:sectPr></w:body></w:document>`),
|
|
606
|
+
};
|
|
607
|
+
for (const one of media) files[`word/media/${one.name}`] = one.bytes;
|
|
608
|
+
return { bytes: zip(files), missed: [...missed] };
|
|
609
|
+
};
|
|
610
|
+
|
|
611
|
+
// Export › Word file
|
|
612
|
+
document.querySelector("[data-docx]")?.addEventListener("click", async () => {
|
|
613
|
+
const { bytes, missed: left } = await window.shell.docx();
|
|
614
|
+
const url = URL.createObjectURL(
|
|
615
|
+
new Blob([bytes], {
|
|
616
|
+
type: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
617
|
+
}),
|
|
618
|
+
);
|
|
619
|
+
const name = `${(document.title || "document").replace(/[\\/:*?"<>|]+/g, "-")}.docx`;
|
|
620
|
+
const a = Object.assign(document.createElement("a"), {
|
|
621
|
+
href: url,
|
|
622
|
+
download: name,
|
|
623
|
+
});
|
|
624
|
+
document.body.append(a);
|
|
625
|
+
a.click();
|
|
626
|
+
a.remove();
|
|
627
|
+
setTimeout(() => URL.revokeObjectURL(url), 1000);
|
|
628
|
+
if (left.length)
|
|
629
|
+
alert(`Pictures not put in the Word file:\n${left.join("\n")}`);
|
|
630
|
+
});
|
|
631
|
+
})();
|