@tangle-network/agent-app 0.43.56 → 0.43.57
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/dist/DesignCanvas-UNL34LS2.js +12 -0
- package/dist/DesignCanvasEditor-FJIVYOA2.js +13 -0
- package/dist/IntakeInterview-3ZIQMZS6.js +8 -0
- package/dist/InvitationsPanel-CJ7DR4F7.js +8 -0
- package/dist/MembersPanel-BB4OD2KB.js +8 -0
- package/dist/{TimelineEditor-I35SJJAJ.js → TimelineEditor-2ECKSU6P.js} +3 -3
- package/dist/{VaultPane-DrQ_tH6c.d.ts → VaultPane-BDP6SB_K.d.ts} +1 -0
- package/dist/{access-BZ81Btt5.d.ts → access-490O6aOU.d.ts} +16 -1
- package/dist/app-auth/index.d.ts +6 -1
- package/dist/app-auth/index.js +3 -3
- package/dist/app-auth/index.js.map +1 -1
- package/dist/{apply-Gk4raT2j.d.ts → apply-6wlMOLf8.d.ts} +30 -1
- package/dist/assets/index.d.ts +41 -0
- package/dist/assets/index.js +1 -1
- package/dist/assistant/index.d.ts +26 -6
- package/dist/assistant/index.js +9 -9
- package/dist/assistant/index.js.map +1 -1
- package/dist/{attachment-validation-D0csjCvz.d.ts → attachment-validation-tPRp-PP1.d.ts} +7 -1
- package/dist/{auth-DuptSkWh.d.ts → auth-_FU8w01b.d.ts} +4 -1
- package/dist/billing/index.d.ts +8 -0
- package/dist/billing/index.js +1 -1
- package/dist/brand/index.d.ts +2 -0
- package/dist/brand/index.js.map +1 -1
- package/dist/brand-extraction/index.d.ts +3 -0
- package/dist/brand-extraction/index.js.map +1 -1
- package/dist/catalog/index.d.ts +2 -0
- package/dist/catalog/index.js +1 -1
- package/dist/chat-routes/index.d.ts +41 -10
- package/dist/chat-routes/index.js +15 -15
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/chat-store/index.d.ts +18 -4
- package/dist/chat-store/index.js +4 -4
- package/dist/chat-store/index.js.map +1 -1
- package/dist/{chunk-H5FM75MR.js → chunk-22GLESKE.js} +3 -3
- package/dist/chunk-22GLESKE.js.map +1 -0
- package/dist/{chunk-WEBBJBDH.js → chunk-2DRYTJHI.js} +1 -1
- package/dist/chunk-2DRYTJHI.js.map +1 -0
- package/dist/{chunk-7EVZUIHW.js → chunk-3EJ6SFJI.js} +1 -1
- package/dist/chunk-3EJ6SFJI.js.map +1 -0
- package/dist/{chunk-HRJJH3RX.js → chunk-3OFQFW5K.js} +4 -4
- package/dist/chunk-3OFQFW5K.js.map +1 -0
- package/dist/{chunk-7SUPA2YT.js → chunk-4KKXZQPT.js} +2 -2
- package/dist/chunk-4KKXZQPT.js.map +1 -0
- package/dist/{chunk-5PTGEJZL.js → chunk-5SD6HT2V.js} +1 -1
- package/dist/chunk-5SD6HT2V.js.map +1 -0
- package/dist/{chunk-7C64WKAH.js → chunk-5SXS3YAB.js} +2 -2
- package/dist/{chunk-L2TG5DBW.js → chunk-5TESA4SN.js} +1 -1
- package/dist/chunk-5TESA4SN.js.map +1 -0
- package/dist/{chunk-63CE7FEZ.js → chunk-6XIAPIW6.js} +1 -1
- package/dist/chunk-6XIAPIW6.js.map +1 -0
- package/dist/{chunk-MFRCM32T.js → chunk-7CTIUCQ4.js} +1 -1
- package/dist/chunk-7CTIUCQ4.js.map +1 -0
- package/dist/{chunk-6KVH5SP5.js → chunk-7YW6EBDX.js} +2 -2
- package/dist/chunk-7YW6EBDX.js.map +1 -0
- package/dist/{chunk-ZXNXAQAH.js → chunk-AP2UXKUK.js} +1 -1
- package/dist/chunk-AP2UXKUK.js.map +1 -0
- package/dist/{chunk-K63OWUDJ.js → chunk-BPSV5AA2.js} +2 -2
- package/dist/chunk-BPSV5AA2.js.map +1 -0
- package/dist/{chunk-MFI4ZM7W.js → chunk-BT4QFCTB.js} +1 -1
- package/dist/chunk-BT4QFCTB.js.map +1 -0
- package/dist/{chunk-ZYBWGSAZ.js → chunk-BVKQKGRK.js} +1 -1
- package/dist/chunk-BVKQKGRK.js.map +1 -0
- package/dist/{chunk-RVUISYUE.js → chunk-C3NSB66C.js} +7 -7
- package/dist/chunk-C3NSB66C.js.map +1 -0
- package/dist/{chunk-BQV42AFK.js → chunk-CCVWR33L.js} +1 -1
- package/dist/chunk-CCVWR33L.js.map +1 -0
- package/dist/{chunk-MCJSS6SM.js → chunk-CQZSAR77.js} +1 -1
- package/dist/chunk-CQZSAR77.js.map +1 -0
- package/dist/{chunk-3SVAA3MA.js → chunk-DJ4VJIH5.js} +1 -1
- package/dist/chunk-DJ4VJIH5.js.map +1 -0
- package/dist/{chunk-XKBIYSSM.js → chunk-DTSPS2HV.js} +3 -3
- package/dist/chunk-DTSPS2HV.js.map +1 -0
- package/dist/{chunk-4QN75UHX.js → chunk-E7N67FYI.js} +8 -8
- package/dist/chunk-E7N67FYI.js.map +1 -0
- package/dist/{chunk-2QI7XV2T.js → chunk-FBVLEGEG.js} +1 -1
- package/dist/{chunk-2QI7XV2T.js.map → chunk-FBVLEGEG.js.map} +1 -1
- package/dist/{chunk-5RMIUJDI.js → chunk-FKCB5FTD.js} +1 -1
- package/dist/chunk-FKCB5FTD.js.map +1 -0
- package/dist/{chunk-EEPJGZJW.js → chunk-GPBRL2XR.js} +1 -1
- package/dist/chunk-GPBRL2XR.js.map +1 -0
- package/dist/{chunk-G3HCU7TA.js → chunk-GXE6KHTS.js} +1 -1
- package/dist/chunk-GXE6KHTS.js.map +1 -0
- package/dist/{chunk-65P3HJY3.js → chunk-HCOROIRT.js} +1 -1
- package/dist/chunk-HCOROIRT.js.map +1 -0
- package/dist/{chunk-54GSK2KK.js → chunk-HYTIHYNV.js} +2 -2
- package/dist/chunk-HYTIHYNV.js.map +1 -0
- package/dist/{chunk-5MG74GVQ.js → chunk-IG46XCZM.js} +3 -3
- package/dist/chunk-IG46XCZM.js.map +1 -0
- package/dist/{chunk-ND3XEGBE.js → chunk-JDEGS53O.js} +1 -1
- package/dist/chunk-JDEGS53O.js.map +1 -0
- package/dist/{chunk-7W5XSTUF.js → chunk-JML7WKWU.js} +1 -1
- package/dist/chunk-JML7WKWU.js.map +1 -0
- package/dist/{chunk-Q4TKVF3L.js → chunk-JTLYXUEV.js} +1 -1
- package/dist/chunk-JTLYXUEV.js.map +1 -0
- package/dist/{chunk-CCRVDLV3.js → chunk-JWBZ74TW.js} +2 -2
- package/dist/chunk-JWBZ74TW.js.map +1 -0
- package/dist/{chunk-ZU5GNSOJ.js → chunk-K3LIPKFV.js} +3 -3
- package/dist/chunk-K3LIPKFV.js.map +1 -0
- package/dist/{chunk-GQRL6VNU.js → chunk-LGBO24FL.js} +1 -1
- package/dist/chunk-LGBO24FL.js.map +1 -0
- package/dist/{chunk-SWUVTGMR.js → chunk-MEUNTJL5.js} +3 -3
- package/dist/{chunk-SWUVTGMR.js.map → chunk-MEUNTJL5.js.map} +1 -1
- package/dist/{chunk-YNALYIQ3.js → chunk-NLSSFLLS.js} +1 -1
- package/dist/chunk-NLSSFLLS.js.map +1 -0
- package/dist/{chunk-HFC4BTWJ.js → chunk-PTUMBMVH.js} +1 -1
- package/dist/chunk-PTUMBMVH.js.map +1 -0
- package/dist/{chunk-R4NOKZMD.js → chunk-ROLFDOMJ.js} +3 -3
- package/dist/{chunk-NYATNLRK.js → chunk-RT5RKAFX.js} +1 -1
- package/dist/chunk-RT5RKAFX.js.map +1 -0
- package/dist/{chunk-GNL3MG5J.js → chunk-S564OFTL.js} +2 -2
- package/dist/{chunk-GUSCYE3S.js → chunk-TQOFR6XY.js} +1 -1
- package/dist/chunk-TQOFR6XY.js.map +1 -0
- package/dist/{chunk-47C6PJCW.js → chunk-TZO3SOUL.js} +1 -1
- package/dist/chunk-TZO3SOUL.js.map +1 -0
- package/dist/{chunk-YGUNTIT5.js → chunk-UKTYKG57.js} +1 -1
- package/dist/chunk-UKTYKG57.js.map +1 -0
- package/dist/{chunk-23LUHHHN.js → chunk-VCOD5AOR.js} +1 -1
- package/dist/chunk-VCOD5AOR.js.map +1 -0
- package/dist/{chunk-5I72FRZY.js → chunk-VOJB7MVG.js} +2 -2
- package/dist/{chunk-XWJXZ6WE.js → chunk-VTWKH4YU.js} +2 -2
- package/dist/chunk-VTWKH4YU.js.map +1 -0
- package/dist/{chunk-4K7BZYO7.js → chunk-VWAEWOWK.js} +3 -3
- package/dist/chunk-VWAEWOWK.js.map +1 -0
- package/dist/{chunk-M2EBUDZ7.js → chunk-VWOPOLVO.js} +2 -2
- package/dist/chunk-VWOPOLVO.js.map +1 -0
- package/dist/{chunk-BN2USWIR.js → chunk-VYECQAGM.js} +2 -2
- package/dist/chunk-VYECQAGM.js.map +1 -0
- package/dist/{chunk-SIXYZ2FB.js → chunk-YJMCRXQQ.js} +1 -1
- package/dist/chunk-YJMCRXQQ.js.map +1 -0
- package/dist/{chunk-PJC4NXPA.js → chunk-YKBDH2UY.js} +3 -3
- package/dist/chunk-YKBDH2UY.js.map +1 -0
- package/dist/{chunk-XAWFPMAR.js → chunk-YKOM4ZLQ.js} +1 -1
- package/dist/chunk-YKOM4ZLQ.js.map +1 -0
- package/dist/{chunk-TKVJE63N.js → chunk-YRMLYFVR.js} +3 -3
- package/dist/chunk-YRMLYFVR.js.map +1 -0
- package/dist/{chunk-USYXHDKE.js → chunk-YZ6KQULN.js} +2 -2
- package/dist/{command-stack-B_-WNfZU.d.ts → command-stack-TNPWJCzW.d.ts} +14 -2
- package/dist/{completion-Kaqp_tWk.d.ts → completion-CvcNUz07.d.ts} +1 -1
- package/dist/config/index.d.ts +1 -1
- package/dist/{contract-KfqJh_au.d.ts → contract-TjB68uqA.d.ts} +17 -0
- package/dist/design-canvas/drizzle.d.ts +10 -2
- package/dist/design-canvas/drizzle.js.map +1 -1
- package/dist/design-canvas/index.d.ts +29 -11
- package/dist/design-canvas/index.js +1070 -17
- package/dist/design-canvas/index.js.map +1 -1
- package/dist/design-canvas-react/engine.d.ts +42 -6
- package/dist/design-canvas-react/engine.js +4 -4
- package/dist/design-canvas-react/index.d.ts +12 -7
- package/dist/design-canvas-react/index.js +11 -11
- package/dist/design-canvas-react/index.js.map +1 -1
- package/dist/design-canvas-react/lazy.d.ts +4 -4
- package/dist/design-canvas-react/lazy.js +1 -1
- package/dist/durable-chat/index.d.ts +46 -1
- package/dist/durable-chat/index.js +3 -3
- package/dist/eval/index.d.ts +1 -1
- package/dist/{export-presets-DAsU5Wal.d.ts → export-presets-mgVulRaV.d.ts} +10 -1
- package/dist/{flow-types-Cb_AblZs.d.ts → flow-types-CqomVAUN.d.ts} +1 -0
- package/dist/harness/index.d.ts +5 -0
- package/dist/harness/index.js +1 -1
- package/dist/index.d.ts +10 -18
- package/dist/index.js +30 -231
- package/dist/intakes/api.d.ts +3 -2
- package/dist/intakes/api.js +3 -3
- package/dist/intakes/api.js.map +1 -1
- package/dist/intakes/drizzle.d.ts +10 -2
- package/dist/intakes/drizzle.js +3 -3
- package/dist/intakes/drizzle.js.map +1 -1
- package/dist/intakes/index.d.ts +2 -2
- package/dist/intakes/index.js +2 -2
- package/dist/intakes-react/index.d.ts +2 -1
- package/dist/intakes-react/index.js +2 -2
- package/dist/intakes-react/lazy.d.ts +2 -1
- package/dist/intakes-react/lazy.js +1 -1
- package/dist/intakes-react/lazy.js.map +1 -1
- package/dist/integrations/index.d.ts +4 -0
- package/dist/integrations/index.js +1 -1
- package/dist/interactions/index.d.ts +13 -2
- package/dist/interactions/index.js +2 -2
- package/dist/{invitations-CZ5G2tIn.d.ts → invitations-DoYMvXea.d.ts} +11 -1
- package/dist/{invitations-schema-DaMO4hZE.d.ts → invitations-schema-_fI7HkJP.d.ts} +5 -1
- package/dist/knowledge/index.d.ts +2 -0
- package/dist/knowledge/index.js +1 -1
- package/dist/knowledge-loop/index.d.ts +4 -1
- package/dist/knowledge-loop/index.js +1 -1
- package/dist/{lazy-C7LiVeFt.d.ts → lazy-DrkVyDpe.d.ts} +2 -2
- package/dist/{mcp-BHfIoGLW.d.ts → mcp-BjPqQ-3C.d.ts} +4 -2
- package/dist/{mcp-rpc-DLw_r9PQ.d.ts → mcp-rpc-BfUOsalw.d.ts} +3 -0
- package/dist/missions/index.d.ts +25 -0
- package/dist/missions/index.js +1 -1
- package/dist/{model-Bzi5i63l.d.ts → model-C8U0HZxL.d.ts} +1 -0
- package/dist/{model-dF2h4xT9.d.ts → model-DmdkIteM.d.ts} +19 -0
- package/dist/{model-BBwyOuWG.d.ts → model-DnfO_PdJ.d.ts} +25 -1
- package/dist/model-resolution/index.d.ts +10 -0
- package/dist/model-resolution/index.js.map +1 -1
- package/dist/{operations-DLIvvD00.d.ts → operations-ByJ6yXOW.d.ts} +20 -2
- package/dist/{parts-1sRYSPjF.d.ts → parts-n9XUon0a.d.ts} +29 -1
- package/dist/plans/index.d.ts +6 -0
- package/dist/plans/index.js +1 -1
- package/dist/platform/index.d.ts +24 -2
- package/dist/platform/index.js +3 -3
- package/dist/platform/index.js.map +1 -1
- package/dist/preflight/index.d.ts +3 -0
- package/dist/preflight/index.js +1 -1
- package/dist/preset-cloudflare/index.d.ts +4 -1
- package/dist/preset-cloudflare/index.js +2 -2
- package/dist/redact/index.d.ts +5 -0
- package/dist/redact/index.js +1 -1
- package/dist/{roles-BC1n4t37.d.ts → roles-B4nU3jDi.d.ts} +12 -0
- package/dist/run/index.d.ts +3 -0
- package/dist/run/index.js +1 -1
- package/dist/run/index.js.map +1 -1
- package/dist/runtime/index.d.ts +11 -4
- package/dist/runtime/index.js +4 -4
- package/dist/sandbox/index.d.ts +96 -3
- package/dist/sandbox/index.js +6 -6
- package/dist/{sandbox-terminal-BIIC__CP.d.ts → sandbox-terminal-ChNEdHF8.d.ts} +5 -0
- package/dist/sequences/drizzle.d.ts +11 -1
- package/dist/sequences/drizzle.js +1 -1
- package/dist/sequences/drizzle.js.map +1 -1
- package/dist/sequences/index.d.ts +32 -7
- package/dist/sequences/index.js +1562 -47
- package/dist/sequences/index.js.map +1 -1
- package/dist/sequences-react/index.d.ts +32 -2
- package/dist/sequences-react/index.js +3 -3
- package/dist/sequences-react/index.js.map +1 -1
- package/dist/{sso-CNOsARMJ.d.ts → sso-Cm00S61u.d.ts} +13 -0
- package/dist/{stale-turn-lock-C8Na1cFZ.d.ts → stale-turn-lock-DucQzvXu.d.ts} +2 -0
- package/dist/store/index.d.ts +5 -0
- package/dist/store/index.js.map +1 -1
- package/dist/{store-gckrNq-g.d.ts → store-B7LLlk9p.d.ts} +22 -1
- package/dist/{store-CFyxgTlD.d.ts → store-CqfDtnPQ.d.ts} +8 -2
- package/dist/stream/index.d.ts +10 -4
- package/dist/stream/index.js +4 -4
- package/dist/{stream-normalizer-DWvtmY6F.d.ts → stream-normalizer-C3M--r6y.d.ts} +17 -1
- package/dist/studio/index.d.ts +37 -0
- package/dist/studio/index.js +1 -1
- package/dist/studio-react/index.d.ts +3 -0
- package/dist/studio-react/index.js +1 -1
- package/dist/studio-react/index.js.map +1 -1
- package/dist/tangle/index.d.ts +3 -0
- package/dist/tangle/index.js +1 -1
- package/dist/teams/drizzle.d.ts +8 -4
- package/dist/teams/drizzle.js +1 -1
- package/dist/teams/drizzle.js.map +1 -1
- package/dist/teams/index.d.ts +4 -2
- package/dist/teams/index.js +3 -3
- package/dist/teams/invitations-api.d.ts +10 -4
- package/dist/teams/invitations-api.js +4 -4
- package/dist/teams/invitations-api.js.map +1 -1
- package/dist/teams/members-api.d.ts +4 -2
- package/dist/teams/members-api.js +3 -3
- package/dist/teams/resend.d.ts +5 -4
- package/dist/teams/resend.js +1 -1
- package/dist/teams/resend.js.map +1 -1
- package/dist/teams-react/index.d.ts +7 -2
- package/dist/teams-react/index.js +3 -3
- package/dist/teams-react/lazy.d.ts +5 -2
- package/dist/teams-react/lazy.js +2 -2
- package/dist/teams-react/lazy.js.map +1 -1
- package/dist/theme/index.d.ts +2 -0
- package/dist/theme/index.js +1 -1
- package/dist/theme/tailwind-preset.d.ts +1 -0
- package/dist/theme/tailwind-preset.js.map +1 -1
- package/dist/theme-contract/cli.js +1 -1
- package/dist/theme-contract/index.d.ts +3 -0
- package/dist/theme-contract/index.js +1 -1
- package/dist/tools/index.d.ts +13 -6
- package/dist/tools/index.js +3 -3
- package/dist/trace/index.d.ts +6 -1
- package/dist/trace/index.js +2 -2
- package/dist/{turn-buffer-C9mEgoop.d.ts → turn-buffer-DGnAPKwa.d.ts} +6 -0
- package/dist/turn-stream/index.d.ts +28 -2
- package/dist/turn-stream/index.js +1 -1
- package/dist/turn-stream/index.js.map +1 -1
- package/dist/{types-BEOvc_ue.d.ts → types-CBRyqijY.d.ts} +10 -0
- package/dist/vault/index.d.ts +1 -1
- package/dist/vault/lazy.d.ts +3 -2
- package/dist/vault/lazy.js.map +1 -1
- package/dist/web/index.d.ts +4 -0
- package/dist/web/index.js +1 -1
- package/dist/web-react/index.d.ts +51 -8
- package/dist/web-react/index.js +9 -9
- package/dist/web-react/terminal.d.ts +2 -2
- package/dist/web-react/terminal.js +1 -1
- package/package.json +3 -2
- package/dist/DesignCanvas-5DJAFDNC.js +0 -12
- package/dist/DesignCanvasEditor-XUJZARSN.js +0 -13
- package/dist/IntakeInterview-WHVKMNQW.js +0 -8
- package/dist/InvitationsPanel-XUUWZBPV.js +0 -8
- package/dist/MembersPanel-FOVHWUQ3.js +0 -8
- package/dist/chunk-23LUHHHN.js.map +0 -1
- package/dist/chunk-3SVAA3MA.js.map +0 -1
- package/dist/chunk-47C6PJCW.js.map +0 -1
- package/dist/chunk-4K7BZYO7.js.map +0 -1
- package/dist/chunk-4QN75UHX.js.map +0 -1
- package/dist/chunk-54GSK2KK.js.map +0 -1
- package/dist/chunk-5MG74GVQ.js.map +0 -1
- package/dist/chunk-5PTGEJZL.js.map +0 -1
- package/dist/chunk-5RMIUJDI.js.map +0 -1
- package/dist/chunk-63CE7FEZ.js.map +0 -1
- package/dist/chunk-65P3HJY3.js.map +0 -1
- package/dist/chunk-6KVH5SP5.js.map +0 -1
- package/dist/chunk-7EVZUIHW.js.map +0 -1
- package/dist/chunk-7SUPA2YT.js.map +0 -1
- package/dist/chunk-7W5XSTUF.js.map +0 -1
- package/dist/chunk-BN2USWIR.js.map +0 -1
- package/dist/chunk-BQV42AFK.js.map +0 -1
- package/dist/chunk-CCRVDLV3.js.map +0 -1
- package/dist/chunk-EEPJGZJW.js.map +0 -1
- package/dist/chunk-G3HCU7TA.js.map +0 -1
- package/dist/chunk-GQRL6VNU.js.map +0 -1
- package/dist/chunk-GUSCYE3S.js.map +0 -1
- package/dist/chunk-H5FM75MR.js.map +0 -1
- package/dist/chunk-HFC4BTWJ.js.map +0 -1
- package/dist/chunk-HRJJH3RX.js.map +0 -1
- package/dist/chunk-K63OWUDJ.js.map +0 -1
- package/dist/chunk-L2TG5DBW.js.map +0 -1
- package/dist/chunk-M2EBUDZ7.js.map +0 -1
- package/dist/chunk-MCJSS6SM.js.map +0 -1
- package/dist/chunk-MFI4ZM7W.js.map +0 -1
- package/dist/chunk-MFRCM32T.js.map +0 -1
- package/dist/chunk-ND3XEGBE.js.map +0 -1
- package/dist/chunk-NEOV2NQ3.js +0 -1617
- package/dist/chunk-NEOV2NQ3.js.map +0 -1
- package/dist/chunk-NYATNLRK.js.map +0 -1
- package/dist/chunk-PJC4NXPA.js.map +0 -1
- package/dist/chunk-Q4TKVF3L.js.map +0 -1
- package/dist/chunk-RVUISYUE.js.map +0 -1
- package/dist/chunk-SIXYZ2FB.js.map +0 -1
- package/dist/chunk-TKVJE63N.js.map +0 -1
- package/dist/chunk-WEBBJBDH.js.map +0 -1
- package/dist/chunk-WFMUDX5J.js +0 -1092
- package/dist/chunk-WFMUDX5J.js.map +0 -1
- package/dist/chunk-XAWFPMAR.js.map +0 -1
- package/dist/chunk-XKBIYSSM.js.map +0 -1
- package/dist/chunk-XWJXZ6WE.js.map +0 -1
- package/dist/chunk-YGUNTIT5.js.map +0 -1
- package/dist/chunk-YNALYIQ3.js.map +0 -1
- package/dist/chunk-ZU5GNSOJ.js.map +0 -1
- package/dist/chunk-ZXNXAQAH.js.map +0 -1
- package/dist/chunk-ZYBWGSAZ.js.map +0 -1
- /package/dist/{DesignCanvas-5DJAFDNC.js.map → DesignCanvas-UNL34LS2.js.map} +0 -0
- /package/dist/{DesignCanvasEditor-XUJZARSN.js.map → DesignCanvasEditor-FJIVYOA2.js.map} +0 -0
- /package/dist/{IntakeInterview-WHVKMNQW.js.map → IntakeInterview-3ZIQMZS6.js.map} +0 -0
- /package/dist/{InvitationsPanel-XUUWZBPV.js.map → InvitationsPanel-CJ7DR4F7.js.map} +0 -0
- /package/dist/{MembersPanel-FOVHWUQ3.js.map → MembersPanel-BB4OD2KB.js.map} +0 -0
- /package/dist/{TimelineEditor-I35SJJAJ.js.map → TimelineEditor-2ECKSU6P.js.map} +0 -0
- /package/dist/{chunk-7C64WKAH.js.map → chunk-5SXS3YAB.js.map} +0 -0
- /package/dist/{chunk-R4NOKZMD.js.map → chunk-ROLFDOMJ.js.map} +0 -0
- /package/dist/{chunk-GNL3MG5J.js.map → chunk-S564OFTL.js.map} +0 -0
- /package/dist/{chunk-5I72FRZY.js.map → chunk-VOJB7MVG.js.map} +0 -0
- /package/dist/{chunk-USYXHDKE.js.map → chunk-YZ6KQULN.js.map} +0 -0
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/design-canvas/model.ts","../src/design-canvas/validate.ts","../src/design-canvas/apply.ts","../src/design-canvas/export-presets.ts"],"sourcesContent":["/**\n * Design-canvas scene model — the product-agnostic document behind the visual\n * asset editor. A document is an ordered list of pages; a page is an ordered\n * list of elements (index order IS z-order, bottom→top); every element is a\n * typed node with a closed attribute vocabulary. The whole document\n * serializes as one JSON value and persists atomically with a revision\n * counter (see ./store) — every canvas action is a durable operation against\n * this schema, which is what makes the canvas automatable: agents and\n * external data sources mutate the same document the editor renders.\n *\n * Units are CSS pixels throughout; `settings.dpi` carries the print\n * conversion factor for bleed/trim-aware exports. Angles are degrees.\n * Nothing here touches Konva, React, or a database.\n */\n\nimport { assertMediaUrl } from '../web'\n\nexport const SCENE_SCHEMA_VERSION = 1\n\nexport interface SceneDocument {\n schemaVersion: typeof SCENE_SCHEMA_VERSION\n title: string\n pages: ScenePage[]\n settings: SceneSettings\n metadata: Record<string, unknown>\n}\n\nexport interface SceneSettings {\n /** Print conversion factor for mm↔px math at export; 96 = CSS default. */\n dpi: number\n}\n\n/** Per-side bleed extents in px, drawn OUTSIDE the page bounds. Trim = the\n * page rect itself; exports may include or exclude the bleed area. */\nexport interface PageBleed {\n top: number\n right: number\n bottom: number\n left: number\n}\n\n/** Saved ruler guides, in page coordinates. */\nexport interface PageGuides {\n vertical: number[]\n horizontal: number[]\n}\n\nexport interface ScenePage {\n id: string\n name: string\n width: number\n height: number\n /** Page background fill (color string); elements paint over it. */\n background: string\n bleed: PageBleed | null\n guides: PageGuides\n elements: SceneElement[]\n}\n\n// ---------------------------------------------------------------------------\n// Elements\n// ---------------------------------------------------------------------------\n\n/** Attributes every element carries. `x`/`y` are the element's top-left in\n * page coordinates (for `group`, children are relative to the group origin).\n * `slot` names a template binding point — `apply_data` targets it. */\nexport interface SceneElementBase {\n id: string\n name: string\n x: number\n y: number\n /** Degrees, clockwise, about the element's top-left origin (Konva default). */\n rotation: number\n /** 0..1 */\n opacity: number\n locked: boolean\n visible: boolean\n slot?: string\n}\n\nexport interface RectElement extends SceneElementBase {\n kind: 'rect'\n width: number\n height: number\n fill: string\n stroke?: string\n strokeWidth?: number\n cornerRadius?: number\n}\n\nexport interface EllipseElement extends SceneElementBase {\n kind: 'ellipse'\n width: number\n height: number\n fill: string\n stroke?: string\n strokeWidth?: number\n}\n\nexport interface LineElement extends SceneElementBase {\n kind: 'line'\n /** Flat [x0, y0, x1, y1, ...] relative to (x, y); ≥ 2 points. */\n points: number[]\n stroke: string\n strokeWidth: number\n dash?: number[]\n}\n\nexport interface TextElement extends SceneElementBase {\n kind: 'text'\n text: string\n /** Wrap width; height derives from content. */\n width: number\n fontFamily: string\n fontSize: number\n fontStyle: 'normal' | 'bold' | 'italic' | 'bold italic'\n fill: string\n align: 'left' | 'center' | 'right'\n lineHeight: number\n letterSpacing: number\n}\n\nexport interface ImageElement extends SceneElementBase {\n kind: 'image'\n width: number\n height: number\n /** http(s) or rooted /api/ path — same boundary rule as sequences media. */\n src: string\n /** How the source maps into the frame; 'fill' stretches, 'cover' crops. */\n fit: 'fill' | 'cover' | 'contain'\n}\n\n/** Video placed on a canvas renders and exports as its poster frame — motion\n * belongs to the sequences surface; this keeps video assets placeable in\n * static layouts (e.g. a thumbnail mock) without a playback engine. */\nexport interface VideoElement extends SceneElementBase {\n kind: 'video'\n width: number\n height: number\n src: string\n posterSrc?: string\n}\n\nexport interface GroupElement extends SceneElementBase {\n kind: 'group'\n children: SceneElement[]\n}\n\nexport type SceneElement =\n | RectElement\n | EllipseElement\n | LineElement\n | TextElement\n | ImageElement\n | VideoElement\n | GroupElement\n\nexport type SceneElementKind = SceneElement['kind']\n\nexport const SCENE_ELEMENT_KINDS: readonly SceneElementKind[] = [\n 'rect', 'ellipse', 'line', 'text', 'image', 'video', 'group',\n] as const\n\n// ---------------------------------------------------------------------------\n// Geometry\n// ---------------------------------------------------------------------------\n\nexport interface Bounds {\n x: number\n y: number\n width: number\n height: number\n}\n\n/** Unrotated local extent of an element (line/group derive from content). */\nexport function elementExtent(element: SceneElement): { width: number; height: number } {\n switch (element.kind) {\n case 'rect':\n case 'ellipse':\n case 'image':\n case 'video':\n return { width: element.width, height: element.height }\n case 'text':\n // Height derives from content at render time; the model exposes a\n // deterministic estimate so layout math (snapping, describe) never\n // depends on a canvas context: lines × fontSize × lineHeight.\n return {\n width: element.width,\n height: estimateTextHeight(element),\n }\n case 'line': {\n let maxX = 0\n let maxY = 0\n for (let i = 0; i < element.points.length; i += 2) {\n maxX = Math.max(maxX, Math.abs(element.points[i]!))\n maxY = Math.max(maxY, Math.abs(element.points[i + 1]!))\n }\n return { width: maxX, height: maxY }\n }\n case 'group': {\n // minX/minY track the negative-space corner: a rotated child whose AABB\n // extends left/above the group origin contributes negative values, so\n // they must seed from the first child, not 0 (0 would clip them).\n let minX = 0, minY = 0, maxX = 0, maxY = 0\n for (const child of element.children) {\n const aabb = elementAabb(child)\n minX = Math.min(minX, aabb.x)\n minY = Math.min(minY, aabb.y)\n maxX = Math.max(maxX, aabb.x + aabb.width)\n maxY = Math.max(maxY, aabb.y + aabb.height)\n }\n return { width: maxX - minX, height: maxY - minY }\n }\n }\n}\n\nexport function estimateTextHeight(element: Pick<TextElement, 'text' | 'fontSize' | 'lineHeight'>): number {\n const lines = element.text.length === 0 ? 1 : element.text.split('\\n').length\n return lines * element.fontSize * element.lineHeight\n}\n\n/** Axis-aligned bounding box in the parent's coordinate space, accounting for\n * rotation about the element's top-left origin. */\nexport function elementAabb(element: SceneElement): Bounds {\n const { width, height } = elementExtent(element)\n if (element.rotation % 360 === 0) {\n return { x: element.x, y: element.y, width, height }\n }\n const rad = (element.rotation * Math.PI) / 180\n const cos = Math.cos(rad)\n const sin = Math.sin(rad)\n const corners: Array<[number, number]> = [\n [0, 0],\n [width * cos, width * sin],\n [-height * sin, height * cos],\n [width * cos - height * sin, width * sin + height * cos],\n ]\n let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity\n for (const [cx, cy] of corners) {\n minX = Math.min(minX, cx); minY = Math.min(minY, cy)\n maxX = Math.max(maxX, cx); maxY = Math.max(maxY, cy)\n }\n return { x: element.x + minX, y: element.y + minY, width: maxX - minX, height: maxY - minY }\n}\n\nexport function boundsIntersect(a: Bounds, b: Bounds): boolean {\n return a.x < b.x + b.width && a.x + a.width > b.x && a.y < b.y + b.height && a.y + a.height > b.y\n}\n\n// ---------------------------------------------------------------------------\n// Lookup + traversal\n// ---------------------------------------------------------------------------\n\nexport function requirePage(document: SceneDocument, pageId: string): ScenePage {\n const page = document.pages.find((candidate) => candidate.id === pageId)\n if (!page) throw new Error(`page ${pageId} not found in document`)\n return page\n}\n\n/** Depth-first search across a page including group children. Returns the\n * element and the array that owns it (page.elements or a group's children),\n * so callers can splice in place. */\nexport function findElement(page: ScenePage, elementId: string): { element: SceneElement; owner: SceneElement[]; index: number } | null {\n const stack: SceneElement[][] = [page.elements]\n while (stack.length > 0) {\n const owner = stack.pop()!\n for (let index = 0; index < owner.length; index += 1) {\n const element = owner[index]!\n if (element.id === elementId) return { element, owner, index }\n if (element.kind === 'group') stack.push(element.children)\n }\n }\n return null\n}\n\nexport function requireElement(page: ScenePage, elementId: string): { element: SceneElement; owner: SceneElement[]; index: number } {\n const found = findElement(page, elementId)\n if (!found) throw new Error(`element ${elementId} not found on page ${page.id}`)\n return found\n}\n\n/** All slot names declared across the document — the template's fillable\n * surface. Duplicate slot names are a validation error (see ./validate). */\nexport function collectSlots(document: SceneDocument): Map<string, { pageId: string; elementId: string; kind: SceneElementKind }> {\n const slots = new Map<string, { pageId: string; elementId: string; kind: SceneElementKind }>()\n for (const page of document.pages) {\n const stack = [...page.elements]\n while (stack.length > 0) {\n const element = stack.pop()!\n if (element.slot) {\n if (slots.has(element.slot)) throw new Error(`duplicate slot name \"${element.slot}\"`)\n slots.set(element.slot, { pageId: page.id, elementId: element.id, kind: element.kind })\n }\n if (element.kind === 'group') stack.push(...element.children)\n }\n }\n return slots\n}\n\n// ---------------------------------------------------------------------------\n// Construction\n// ---------------------------------------------------------------------------\n\nexport interface NewPageOptions {\n name?: string\n width?: number\n height?: number\n background?: string\n}\n\nexport function createEmptyDocument(title: string, page?: NewPageOptions): SceneDocument {\n return {\n schemaVersion: SCENE_SCHEMA_VERSION,\n title,\n pages: [createPage(page ?? {}, 'page-1')],\n settings: { dpi: 96 },\n metadata: {},\n }\n}\n\nexport function createPage(options: NewPageOptions, id: string): ScenePage {\n const width = options.width ?? 1080\n const height = options.height ?? 1080\n assertPositiveFinite(width, 'page width')\n assertPositiveFinite(height, 'page height')\n return {\n id,\n name: options.name ?? 'Page',\n width,\n height,\n background: options.background ?? '#ffffff',\n bleed: null,\n guides: { vertical: [], horizontal: [] },\n elements: [],\n }\n}\n\nexport function assertPositiveFinite(value: number, label: string): void {\n if (!Number.isFinite(value) || value <= 0) throw new Error(`${label} must be a positive finite number`)\n}\n\nexport function assertFinite(value: number, label: string): void {\n if (!Number.isFinite(value)) throw new Error(`${label} must be a finite number`)\n}\n\n/** Valid hex lengths are exactly 3 (rgb), 4 (rgba), 6 (rrggbb), or 8\n * (rrggbbaa) digits — `{3,8}` wrongly admitted 5- and 7-digit junk that no\n * renderer accepts. */\nconst HEX_COLOR = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/\nconst RGB_COLOR = /^rgba?\\(\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*,\\s*(\\d{1,3})\\s*(?:,\\s*(?:0|1|0?\\.\\d+)\\s*)?\\)$/\n\nexport function assertColor(value: string, label: string): void {\n if (value === 'transparent' || HEX_COLOR.test(value)) return\n const rgb = RGB_COLOR.exec(value)\n if (rgb && rgbChannelsInRange(rgb)) return\n throw new Error(`${label} must be a hex/rgb(a) color or 'transparent', got \"${value}\"`)\n}\n\n/** Each of the three rgb channels must be 0..255 — the pattern alone admits\n * values like `rgb(999,0,0)` which clamp or render wrong. */\nfunction rgbChannelsInRange(match: RegExpExecArray): boolean {\n for (let i = 1; i <= 3; i += 1) {\n const channel = Number(match[i])\n if (channel > 255) return false\n }\n return true\n}\n\n/** Media boundary rule shared with sequences: remote http(s) or a rooted\n * /api/ path — never sandbox-local files or data:/blob: blobs. Delegates to the\n * canonical `assertMediaUrl` boundary in `../web`; the two surfaces share ONE\n * rule (trims, named-rejects local/inline schemes) so they cannot drift. */\nexport function assertSceneMediaSrc(value: string, label: string): void {\n assertMediaUrl(value, label)\n}\n","/**\n * Pre-write validation for scene operations. Every rule runs against a\n * `SceneDocument` snapshot BEFORE any mutation, so a rejected batch leaves no\n * partial state. Batch errors carry the shape `operation N (type): reason` —\n * precise enough for an LLM planner to repair the offending operation and\n * resubmit.\n *\n * Validation is static: a batch is checked against the document as given, so\n * an operation may not reference entities created by an earlier operation in\n * the same batch. Dispatchers that chain operations must refresh the document\n * between applications and validate per-operation.\n *\n * Slot typing contract (for apply_data bindings):\n * text elements → value is any string\n * image elements → value must pass assertSceneMediaSrc (src rewrite)\n * video elements → value must pass assertSceneMediaSrc (src rewrite)\n * rect elements → value must pass assertColor (fill recolor)\n * ellipse elements → value must pass assertColor (fill recolor)\n * line elements → value must pass assertColor (stroke recolor)\n * group elements → value must pass assertColor (fill/stroke passed down to children)\n */\n\nimport {\n assertColor,\n assertFinite,\n assertPositiveFinite,\n assertSceneMediaSrc,\n collectSlots,\n findElement,\n requireElement,\n requirePage,\n} from './model'\nimport type { SceneDocument, SceneElement, SceneElementKind } from './model'\nimport type {\n AddElementOperation,\n ApplyDataOperation,\n BindSlotOperation,\n DeleteElementOperation,\n DeletePageOperation,\n DuplicatePageOperation,\n GroupElementsOperation,\n ReorderElementOperation,\n ReorderPageOperation,\n SceneAttrsPatch,\n SceneOperation,\n SetAttrsOperation,\n SetDocumentTitleOperation,\n SetPageGuidesOperation,\n SetPagePropsOperation,\n UngroupElementOperation,\n} from './operations'\n\n// ---------------------------------------------------------------------------\n// Public entry\n// ---------------------------------------------------------------------------\n\nexport function validateSceneOperations(document: SceneDocument, operations: SceneOperation[]): void {\n operations.forEach((operation, index) => {\n try {\n validateSceneOperation(document, operation)\n } catch (error) {\n const reason = error instanceof Error ? error.message : String(error)\n throw new Error(`operation ${index + 1} (${operation.type}): ${reason}`)\n }\n })\n}\n\nexport function validateSceneOperation(document: SceneDocument, operation: SceneOperation): void {\n switch (operation.type) {\n case 'add_element':\n return validateAddElement(document, operation)\n case 'set_attrs':\n return validateSetAttrs(document, operation)\n case 'reorder_element':\n return validateReorderElement(document, operation)\n case 'delete_element':\n return validateDeleteElement(document, operation)\n case 'group_elements':\n return validateGroupElements(document, operation)\n case 'ungroup_element':\n return validateUngroupElement(document, operation)\n case 'add_page':\n return validateAddPage(operation)\n case 'duplicate_page':\n return validateDuplicatePage(document, operation)\n case 'delete_page':\n return validateDeletePage(document, operation)\n case 'reorder_page':\n return validateReorderPage(document, operation)\n case 'set_page_props':\n return validateSetPageProps(document, operation)\n case 'set_page_guides':\n return validateSetPageGuides(document, operation)\n case 'bind_slot':\n return validateBindSlot(document, operation)\n case 'apply_data':\n return validateApplyData(document, operation)\n case 'set_document_title':\n return validateSetDocumentTitle(operation)\n default: {\n const unknown = operation as { type?: unknown }\n throw new Error(`unsupported operation type ${JSON.stringify(unknown.type)}`)\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Per-operation validators\n// ---------------------------------------------------------------------------\n\nfunction validateAddElement(document: SceneDocument, op: AddElementOperation): void {\n const page = requirePage(document, op.pageId)\n assertUniqueIdDocumentWide(document, op.element.id)\n if (op.parentGroupId !== undefined) {\n const { element: parent } = requireElement(page, op.parentGroupId)\n if (parent.kind !== 'group') {\n throw new Error(`parentGroupId \"${op.parentGroupId}\" is a ${parent.kind}, not a group`)\n }\n }\n if (op.index !== undefined) {\n const owner = op.parentGroupId\n ? (() => {\n const { element: g } = requireElement(page, op.parentGroupId)\n return (g as { kind: 'group'; children: SceneElement[] }).children\n })()\n : page.elements\n if (op.index < 0 || op.index > owner.length) {\n throw new Error(`index ${op.index} out of range (owner has ${owner.length} elements)`)\n }\n }\n validateElementAttrs(op.element.kind, op.element as unknown as SceneAttrsPatch, true)\n}\n\nfunction validateSetAttrs(document: SceneDocument, op: SetAttrsOperation): void {\n const page = requirePage(document, op.pageId)\n const { element } = requireElement(page, op.elementId)\n // A patch that ONLY unlocks is always permitted even on a locked element.\n const isUnlockOnly = Object.keys(op.attrs).length === 1 && op.attrs.locked === false\n if (element.locked && !isUnlockOnly) {\n throw new Error(`element \"${op.elementId}\" is locked; unlock it first (pass attrs: {locked: false}) before making other changes`)\n }\n validateElementAttrs(element.kind, op.attrs, false)\n}\n\nfunction validateReorderElement(document: SceneDocument, op: ReorderElementOperation): void {\n const page = requirePage(document, op.pageId)\n const { element, owner } = requireElement(page, op.elementId)\n if (element.locked) {\n throw new Error(`element \"${op.elementId}\" is locked; unlock it before reordering`)\n }\n // toIndex is the insertion point after splice-remove; owner.length-1 is valid\n // (element becomes last). owner.length itself is out-of-range post-remove.\n if (op.toIndex < 0 || op.toIndex >= owner.length) {\n throw new Error(`toIndex ${op.toIndex} out of range (owner has ${owner.length} elements)`)\n }\n}\n\nfunction validateDeleteElement(document: SceneDocument, op: DeleteElementOperation): void {\n const page = requirePage(document, op.pageId)\n const { element } = requireElement(page, op.elementId)\n if (element.locked) {\n throw new Error(`element \"${op.elementId}\" is locked; unlock it before deleting`)\n }\n}\n\nfunction validateGroupElements(document: SceneDocument, op: GroupElementsOperation): void {\n if (op.elementIds.length < 2) {\n throw new Error(`group_elements requires ≥ 2 element ids (got ${op.elementIds.length})`)\n }\n assertUniqueIdDocumentWide(document, op.groupId)\n const page = requirePage(document, op.pageId)\n // Resolve all targets and check siblings + locked\n const owners = op.elementIds.map((id) => {\n const { element, owner } = requireElement(page, id)\n if (element.locked) throw new Error(`element \"${id}\" is locked; unlock before grouping`)\n return owner\n })\n // All must share the exact same owner array reference (sibling check)\n const firstOwner = owners[0]\n for (let i = 1; i < owners.length; i++) {\n if (owners[i] !== firstOwner) {\n throw new Error(`elements are not siblings — they must all share the same parent (page root or one group)`)\n }\n }\n}\n\nfunction validateUngroupElement(document: SceneDocument, op: UngroupElementOperation): void {\n const page = requirePage(document, op.pageId)\n const { element } = requireElement(page, op.groupId)\n if (element.kind !== 'group') {\n throw new Error(`element \"${op.groupId}\" is a ${element.kind}, not a group`)\n }\n}\n\nfunction validateAddPage(op: { options?: { width?: number; height?: number; background?: string } }): void {\n const opts = op.options\n if (!opts) return\n if (opts.width !== undefined) assertPositiveFinite(opts.width, 'page width')\n if (opts.height !== undefined) assertPositiveFinite(opts.height, 'page height')\n if (opts.background !== undefined) assertColor(opts.background, 'page background')\n}\n\nfunction validateDuplicatePage(document: SceneDocument, op: DuplicatePageOperation): void {\n requirePage(document, op.sourcePageId)\n const existing = document.pages.find((p) => p.id === op.pageId)\n if (existing) throw new Error(`pageId \"${op.pageId}\" already exists in the document`)\n}\n\nfunction validateDeletePage(document: SceneDocument, op: DeletePageOperation): void {\n requirePage(document, op.pageId)\n if (document.pages.length === 1) {\n throw new Error('cannot delete the last remaining page')\n }\n}\n\nfunction validateReorderPage(document: SceneDocument, op: ReorderPageOperation): void {\n requirePage(document, op.pageId)\n if (op.toIndex < 0 || op.toIndex >= document.pages.length) {\n throw new Error(`toIndex ${op.toIndex} out of range (document has ${document.pages.length} pages)`)\n }\n}\n\nfunction validateSetPageProps(document: SceneDocument, op: SetPagePropsOperation): void {\n requirePage(document, op.pageId)\n if (op.width !== undefined) assertPositiveFinite(op.width, 'page width')\n if (op.height !== undefined) assertPositiveFinite(op.height, 'page height')\n if (op.background !== undefined) assertColor(op.background, 'page background')\n if (op.bleed != null) {\n assertNonNegativeFinite(op.bleed.top, 'bleed.top')\n assertNonNegativeFinite(op.bleed.right, 'bleed.right')\n assertNonNegativeFinite(op.bleed.bottom, 'bleed.bottom')\n assertNonNegativeFinite(op.bleed.left, 'bleed.left')\n }\n}\n\nfunction validateSetPageGuides(document: SceneDocument, op: SetPageGuidesOperation): void {\n requirePage(document, op.pageId)\n for (const pos of op.guides.vertical) {\n if (!Number.isFinite(pos)) throw new Error(`guide position ${pos} is not finite`)\n }\n for (const pos of op.guides.horizontal) {\n if (!Number.isFinite(pos)) throw new Error(`guide position ${pos} is not finite`)\n }\n}\n\nfunction validateBindSlot(document: SceneDocument, op: BindSlotOperation): void {\n const page = requirePage(document, op.pageId)\n requireElement(page, op.elementId)\n if (op.slot === null) return\n // Slot names must be unique document-wide; a re-bind of the SAME element to\n // its own current slot name is fine (idempotent) — collectSlots throws on\n // duplicates owned by DIFFERENT elements, so we check manually.\n for (const p of document.pages) {\n const stack = [...p.elements]\n while (stack.length > 0) {\n const el = stack.pop()!\n if (el.slot === op.slot && el.id !== op.elementId) {\n throw new Error(`slot \"${op.slot}\" is already bound to element \"${el.id}\" on page \"${p.id}\"`)\n }\n if (el.kind === 'group') stack.push(...el.children)\n }\n }\n}\n\nfunction validateApplyData(document: SceneDocument, op: ApplyDataOperation): void {\n // Build the slot map; collectSlots throws on internal duplicates\n const slots = collectSlots(document)\n for (const [slotName, value] of Object.entries(op.bindings)) {\n const slot = slots.get(slotName)\n if (!slot) {\n throw new Error(`slot \"${slotName}\" does not exist in the document`)\n }\n validateSlotValue(slotName, slot.kind, value)\n }\n}\n\nfunction validateSetDocumentTitle(op: SetDocumentTitleOperation): void {\n if (op.title.trim().length === 0) throw new Error('title must be non-empty')\n}\n\n// ---------------------------------------------------------------------------\n// Element attr validation\n// ---------------------------------------------------------------------------\n\n/** Attributes present on EVERY element kind (SceneElementBase minus id/kind). */\nconst BASE_ATTRS = new Set([\n 'name', 'x', 'y', 'rotation', 'opacity', 'locked', 'visible', 'slot',\n])\n\n/** Per-kind allowed attributes (beyond base). */\nconst KIND_ATTRS: Record<SceneElementKind, Set<string>> = {\n rect: new Set(['width', 'height', 'fill', 'stroke', 'strokeWidth', 'cornerRadius']),\n ellipse: new Set(['width', 'height', 'fill', 'stroke', 'strokeWidth']),\n line: new Set(['points', 'stroke', 'strokeWidth', 'dash']),\n text: new Set(['text', 'width', 'fontFamily', 'fontSize', 'fontStyle', 'fill', 'align', 'lineHeight', 'letterSpacing']),\n image: new Set(['width', 'height', 'src', 'fit']),\n video: new Set(['width', 'height', 'src', 'posterSrc']),\n group: new Set([]),\n}\n\nconst FONT_STYLES = new Set(['normal', 'bold', 'italic', 'bold italic'])\nconst ALIGN_VALUES = new Set(['left', 'center', 'right'])\nconst FIT_VALUES = new Set(['fill', 'cover', 'contain'])\n\n/**\n * Validates attribute patches (or full element attrs for add_element).\n * `isConstruction` = true when validating a new element (required fields\n * must be present); false for a partial set_attrs patch.\n */\nfunction validateElementAttrs(\n kind: SceneElementKind,\n attrs: SceneAttrsPatch,\n isConstruction: boolean,\n): void {\n const allowed = KIND_ATTRS[kind]\n\n // Foreign attribute check: reject any key that is neither base nor kind-specific.\n for (const key of Object.keys(attrs)) {\n if (key === 'id' || key === 'kind' || key === 'children') continue\n if (!BASE_ATTRS.has(key) && !allowed.has(key)) {\n throw new Error(`attribute \"${key}\" is not valid for a ${kind} element`)\n }\n }\n\n // Base attribute validation\n if (attrs.opacity !== undefined) {\n if (typeof attrs.opacity !== 'number' || !Number.isFinite(attrs.opacity) || attrs.opacity < 0 || attrs.opacity > 1) {\n throw new Error('opacity must be a number in [0, 1]')\n }\n }\n if (attrs.x !== undefined) assertFinite(attrs.x, 'x')\n if (attrs.y !== undefined) assertFinite(attrs.y, 'y')\n if (attrs.rotation !== undefined) assertFinite(attrs.rotation, 'rotation')\n\n // Kind-specific attribute validation\n switch (kind) {\n case 'rect':\n case 'ellipse':\n case 'image':\n case 'video':\n if (attrs.width !== undefined) assertPositiveFinite(attrs.width, 'width')\n if (attrs.height !== undefined) assertPositiveFinite(attrs.height, 'height')\n break\n case 'text':\n if (attrs.width !== undefined) assertPositiveFinite(attrs.width, 'width')\n if (attrs.fontSize !== undefined) assertPositiveFinite(attrs.fontSize, 'fontSize')\n if (attrs.lineHeight !== undefined) {\n if (typeof attrs.lineHeight !== 'number' || !Number.isFinite(attrs.lineHeight) || attrs.lineHeight <= 0) {\n throw new Error('lineHeight must be a positive finite number')\n }\n }\n if (attrs.fontStyle !== undefined && !FONT_STYLES.has(attrs.fontStyle)) {\n throw new Error(`fontStyle must be one of: ${[...FONT_STYLES].join(', ')}`)\n }\n if (attrs.align !== undefined && !ALIGN_VALUES.has(attrs.align)) {\n throw new Error(`align must be one of: ${[...ALIGN_VALUES].join(', ')}`)\n }\n break\n case 'line':\n if (attrs.points !== undefined) {\n if (!Array.isArray(attrs.points) || attrs.points.length < 4 || attrs.points.length % 2 !== 0) {\n throw new Error('points must be an even-length array with at least 4 numbers (2 points)')\n }\n for (let i = 0; i < attrs.points.length; i++) {\n if (!Number.isFinite(attrs.points[i])) {\n throw new Error(`points[${i}] is not finite`)\n }\n }\n }\n if (attrs.strokeWidth !== undefined) assertPositiveFinite(attrs.strokeWidth, 'strokeWidth')\n break\n case 'group':\n // Group geometry derives from children; no width/height attrs.\n break\n }\n\n // Color validation for fill/stroke\n if (attrs.fill !== undefined) assertColor(attrs.fill, 'fill')\n if (attrs.stroke !== undefined) assertColor(attrs.stroke, 'stroke')\n if (attrs.strokeWidth !== undefined && kind !== 'line') {\n assertPositiveFinite(attrs.strokeWidth, 'strokeWidth')\n }\n\n // Image/video src\n if (attrs.src !== undefined) assertSceneMediaSrc(attrs.src, 'src')\n if ((attrs as { posterSrc?: string }).posterSrc !== undefined) {\n assertSceneMediaSrc((attrs as { posterSrc: string }).posterSrc, 'posterSrc')\n }\n\n // fit\n if (attrs.fit !== undefined && !FIT_VALUES.has(attrs.fit)) {\n throw new Error(`fit must be one of: ${[...FIT_VALUES].join(', ')}`)\n }\n\n // Construction-time required-field checks\n if (isConstruction) {\n validateRequiredConstructionAttrs(kind, attrs)\n }\n}\n\nfunction validateRequiredConstructionAttrs(kind: SceneElementKind, attrs: SceneAttrsPatch): void {\n switch (kind) {\n case 'rect':\n requireAttrPresent(attrs, 'width', kind)\n requireAttrPresent(attrs, 'height', kind)\n requireAttrPresent(attrs, 'fill', kind)\n break\n case 'ellipse':\n requireAttrPresent(attrs, 'width', kind)\n requireAttrPresent(attrs, 'height', kind)\n requireAttrPresent(attrs, 'fill', kind)\n break\n case 'line':\n requireAttrPresent(attrs, 'points', kind)\n requireAttrPresent(attrs, 'stroke', kind)\n requireAttrPresent(attrs, 'strokeWidth', kind)\n break\n case 'text':\n requireAttrPresent(attrs, 'text', kind)\n requireAttrPresent(attrs, 'width', kind)\n requireAttrPresent(attrs, 'fontFamily', kind)\n requireAttrPresent(attrs, 'fontSize', kind)\n requireAttrPresent(attrs, 'fontStyle', kind)\n requireAttrPresent(attrs, 'fill', kind)\n requireAttrPresent(attrs, 'align', kind)\n requireAttrPresent(attrs, 'lineHeight', kind)\n requireAttrPresent(attrs, 'letterSpacing', kind)\n break\n case 'image':\n requireAttrPresent(attrs, 'width', kind)\n requireAttrPresent(attrs, 'height', kind)\n requireAttrPresent(attrs, 'src', kind)\n requireAttrPresent(attrs, 'fit', kind)\n break\n case 'video':\n requireAttrPresent(attrs, 'width', kind)\n requireAttrPresent(attrs, 'height', kind)\n requireAttrPresent(attrs, 'src', kind)\n break\n case 'group':\n // Groups are created empty; children are added separately.\n break\n }\n}\n\nfunction requireAttrPresent(attrs: SceneAttrsPatch, key: string, kind: SceneElementKind): void {\n if ((attrs as Record<string, unknown>)[key] === undefined) {\n throw new Error(`${key} is required when constructing a ${kind} element`)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Slot value typing (apply_data)\n// ---------------------------------------------------------------------------\n\n/**\n * Validates that a slot binding value matches the slot element's kind.\n * text → any string; image/video → media src; rect/ellipse/line/group → color.\n */\nexport function validateSlotValue(slotName: string, elementKind: SceneElementKind, value: string): void {\n switch (elementKind) {\n case 'text':\n // Any string is valid for a text slot.\n return\n case 'image':\n case 'video':\n try {\n assertSceneMediaSrc(value, `slot \"${slotName}\" value`)\n } catch (e) {\n throw new Error(\n `slot \"${slotName}\" is bound to a ${elementKind} element — value must be an http(s) URL or a rooted /api/ path (got \"${value}\")`,\n )\n }\n return\n case 'rect':\n case 'ellipse':\n case 'line':\n case 'group':\n try {\n assertColor(value, `slot \"${slotName}\" value`)\n } catch {\n throw new Error(\n `slot \"${slotName}\" is bound to a ${elementKind} element — value must be a color (hex/rgb(a)/transparent) for fill/stroke recolor (got \"${value}\")`,\n )\n }\n return\n }\n}\n\n// ---------------------------------------------------------------------------\n// Document-wide uniqueness\n// ---------------------------------------------------------------------------\n\nfunction assertUniqueIdDocumentWide(document: SceneDocument, id: string): void {\n for (const page of document.pages) {\n if (page.id === id) throw new Error(`id \"${id}\" is already used by a page`)\n const stack = [...page.elements]\n while (stack.length > 0) {\n const el = stack.pop()!\n if (el.id === id) throw new Error(`id \"${id}\" is already used by element on page \"${page.id}\"`)\n if (el.kind === 'group') stack.push(...el.children)\n }\n }\n}\n\n// ---------------------------------------------------------------------------\n// Internal guards\n// ---------------------------------------------------------------------------\n\nfunction assertNonNegativeFinite(value: number, label: string): void {\n if (!Number.isFinite(value) || value < 0) {\n throw new Error(`${label} must be a non-negative finite number`)\n }\n}\n","/**\n * Pure application of validated scene operations to a SceneDocument.\n * `applySceneOperations` deep-clones the document then mutates the clone,\n * returning the new document and per-op results. It never touches the store,\n * a clock, or a PRNG — all id generation goes through the caller-supplied\n * `mintId` option so callers control determinism (counter in tests,\n * crypto.randomUUID in production).\n *\n * Rotation semantics for group/ungroup:\n * Group: children retain their absolute rotation. The group origin is the\n * min-x, min-y corner of the AABB union of all member elements. Children\n * are rebased so their position in group-local space equals\n * (element.x - group.x, element.y - group.y). A rotated child appears at\n * the same absolute page position after grouping because the group's additive\n * translation preserves the child's own-origin transform.\n * Ungroup: inverse — child.x += group.x, child.y += group.y; rotation stays.\n * This is consistent with Konva's transform-about-own-origin model without\n * needing a full matrix concatenation for the common case.\n *\n * `storeApplyScenePlan` layers store I/O on top: getDocument → validate →\n * apply → saveDocument(expectedRev) → recordDecision, retrying once on\n * stale-rev by refetch + revalidate + reapply.\n */\n\nimport {\n assertColor,\n assertSceneMediaSrc,\n collectSlots,\n createPage,\n elementAabb,\n requireElement,\n requirePage,\n} from './model'\nimport type {\n GroupElement,\n NewPageOptions,\n SceneDocument,\n SceneElement,\n ScenePage,\n} from './model'\nimport type {\n AddElementOperation,\n AddPageOperation,\n ApplyDataOperation,\n BindSlotOperation,\n DeleteElementOperation,\n DeletePageOperation,\n DuplicatePageOperation,\n GroupElementsOperation,\n ReorderElementOperation,\n ReorderPageOperation,\n SceneOperation,\n ScenePlan,\n SetAttrsOperation,\n SetDocumentTitleOperation,\n SetPageGuidesOperation,\n SetPagePropsOperation,\n UngroupElementOperation,\n} from './operations'\nimport type { NewSceneDecision, SceneDocumentRecord, SceneStore } from './store'\nimport { validateSceneOperations } from './validate'\n\n// ---------------------------------------------------------------------------\n// Result types\n// ---------------------------------------------------------------------------\n\nexport type SceneApplyResult =\n | { kind: 'element'; pageId: string; element: SceneElement }\n | { kind: 'page'; page: ScenePage }\n | { kind: 'document' }\n\n// ---------------------------------------------------------------------------\n// Options\n// ---------------------------------------------------------------------------\n\nexport interface ApplySceneOptions {\n /**\n * Provides fresh ids when duplicate_page re-mints element ids. Never called\n * by any other operation. Counter-based in tests; crypto.randomUUID in\n * production (wrapped so `mintId()` has no arguments, matching this signature).\n */\n mintId: () => string\n}\n\n// ---------------------------------------------------------------------------\n// Public: pure apply\n// ---------------------------------------------------------------------------\n\n/** Full form: returns the new document AND per-op results. `mintId` must be\n * provided when any operation in the list may be `duplicate_page` (which\n * re-mints element ids); for all other operation types it is never called. */\nexport function applySceneOperations(\n document: SceneDocument,\n operations: SceneOperation[],\n options: ApplySceneOptions,\n): { document: SceneDocument; results: SceneApplyResult[] }\n\n/** Convenience 2-arg form: returns the new document directly. Uses a\n * monotonic counter as the mintId so `duplicate_page` never collides within\n * a single call (suitable for editor-local optimistic state; server will\n * re-mint on persist). */\nexport function applySceneOperations(\n document: SceneDocument,\n operations: SceneOperation[],\n): SceneDocument\n\nexport function applySceneOperations(\n document: SceneDocument,\n operations: SceneOperation[],\n options?: ApplySceneOptions,\n): { document: SceneDocument; results: SceneApplyResult[] } | SceneDocument {\n const opts = options ?? { mintId: makeCounter() }\n const doc = deepCloneDocument(document)\n const results: SceneApplyResult[] = []\n for (const operation of operations) {\n results.push(applyOneOperation(doc, operation, opts))\n }\n return options !== undefined ? { document: doc, results } : doc\n}\n\n/** Apply a single operation to a document and return the new document.\n * Equivalent to `applySceneOperations(doc, [op])` but returns `SceneDocument`\n * directly — the common case in editor commands and tests. */\nexport function applySceneOperation(\n document: SceneDocument,\n operation: SceneOperation,\n): SceneDocument {\n return applySceneOperations(document, [operation])\n}\n\nfunction makeCounter(): () => string {\n let n = 0\n return () => `minted-${(n += 1)}`\n}\n\n// ---------------------------------------------------------------------------\n// Public: store-integrated helper\n// ---------------------------------------------------------------------------\n\n/** Detects the stale-revision sentinel thrown by drizzle-store. The store\n * throws a plain Error whose message contains \"stale rev\" — no custom class\n * is needed because the store contract documents exactly this phrase. */\nfunction isStaleRevError(err: unknown): boolean {\n return err instanceof Error && /stale rev/i.test(err.message)\n}\n\nexport async function storeApplyScenePlan(\n store: SceneStore,\n plan: ScenePlan,\n opts: { actorKind: NewSceneDecision['kind']; mintId: () => string },\n): Promise<{ record: SceneDocumentRecord; results: SceneApplyResult[] }> {\n let { document, rev } = await store.getDocument()\n\n validateSceneOperations(document, plan.operations)\n let applied = applySceneOperations(document, plan.operations, { mintId: opts.mintId })\n\n let record: SceneDocumentRecord\n try {\n record = await store.saveDocument(applied.document, rev)\n } catch (firstError) {\n // Only retry for stale-rev conflicts. Any other error (network, constraint,\n // permissions) is a hard failure — retrying would risk a double-apply if\n // the first save actually succeeded but the acknowledgement was lost.\n if (!isStaleRevError(firstError)) throw firstError\n const refreshed = await store.getDocument()\n validateSceneOperations(refreshed.document, plan.operations)\n applied = applySceneOperations(refreshed.document, plan.operations, { mintId: opts.mintId })\n try {\n record = await store.saveDocument(applied.document, refreshed.rev)\n } catch (secondError) {\n const reason = secondError instanceof Error ? secondError.message : String(secondError)\n throw new Error(`storeApplyScenePlan: stale rev persists after retry — ${reason}`)\n }\n }\n\n const opTypeCounts: Record<string, number> = {}\n for (const op of plan.operations) {\n opTypeCounts[op.type] = (opTypeCounts[op.type] ?? 0) + 1\n }\n\n await store.recordDecision({\n kind: opts.actorKind,\n instruction: plan.summary,\n metadata: { opTypeCounts, operationCount: plan.operations.length },\n })\n\n return { record, results: applied.results }\n}\n\n// ---------------------------------------------------------------------------\n// Per-operation dispatch (mutates cloned doc in-place)\n// ---------------------------------------------------------------------------\n\nfunction applyOneOperation(\n doc: SceneDocument,\n operation: SceneOperation,\n options: ApplySceneOptions,\n): SceneApplyResult {\n switch (operation.type) {\n case 'add_element':\n return applyAddElement(doc, operation)\n case 'set_attrs':\n return applySetAttrs(doc, operation)\n case 'reorder_element':\n return applyReorderElement(doc, operation)\n case 'delete_element':\n return applyDeleteElement(doc, operation)\n case 'group_elements':\n return applyGroupElements(doc, operation)\n case 'ungroup_element':\n return applyUngroupElement(doc, operation)\n case 'add_page':\n return applyAddPage(doc, operation)\n case 'duplicate_page':\n return applyDuplicatePage(doc, operation, options)\n case 'delete_page':\n return applyDeletePage(doc, operation)\n case 'reorder_page':\n return applyReorderPage(doc, operation)\n case 'set_page_props':\n return applySetPageProps(doc, operation)\n case 'set_page_guides':\n return applySetPageGuides(doc, operation)\n case 'bind_slot':\n return applyBindSlot(doc, operation)\n case 'apply_data':\n return applyApplyData(doc, operation)\n case 'set_document_title':\n return applySetDocumentTitle(doc, operation)\n }\n}\n\n// ---------------------------------------------------------------------------\n// Operation implementations (all mutate the deep-cloned doc)\n// ---------------------------------------------------------------------------\n\nfunction applyAddElement(doc: SceneDocument, op: AddElementOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const owner: SceneElement[] = op.parentGroupId !== undefined\n ? (() => {\n const { element: g } = requireElement(page, op.parentGroupId)\n return (g as GroupElement).children\n })()\n : page.elements\n const index = op.index !== undefined ? op.index : owner.length\n owner.splice(index, 0, op.element)\n return { kind: 'element', pageId: op.pageId, element: op.element }\n}\n\nfunction applySetAttrs(doc: SceneDocument, op: SetAttrsOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const { element, owner, index } = requireElement(page, op.elementId)\n const patched = { ...element, ...op.attrs } as SceneElement\n owner[index] = patched\n return { kind: 'element', pageId: op.pageId, element: patched }\n}\n\nfunction applyReorderElement(doc: SceneDocument, op: ReorderElementOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const { element, owner, index } = requireElement(page, op.elementId)\n owner.splice(index, 1)\n owner.splice(op.toIndex, 0, element)\n return { kind: 'element', pageId: op.pageId, element }\n}\n\nfunction applyDeleteElement(doc: SceneDocument, op: DeleteElementOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const { element, owner, index } = requireElement(page, op.elementId)\n owner.splice(index, 1)\n return { kind: 'element', pageId: op.pageId, element }\n}\n\nfunction applyGroupElements(doc: SceneDocument, op: GroupElementsOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const members = op.elementIds.map((id) => requireElement(page, id))\n\n // Group origin = min AABB corner of all members in the owner coordinate space.\n let minX = Infinity, minY = Infinity\n for (const { element } of members) {\n const aabb = elementAabb(element)\n if (aabb.x < minX) minX = aabb.x\n if (aabb.y < minY) minY = aabb.y\n }\n\n const owner = members[0]!.owner\n // Sort ascending by index to preserve z-order in children array.\n const sortedByIndex = [...members].sort((a, b) => a.index - b.index)\n\n // Children retain rotation; x/y rebased to group-local space.\n const children: SceneElement[] = sortedByIndex.map(({ element }) => ({\n ...element,\n x: element.x - minX,\n y: element.y - minY,\n }))\n\n // Remove in reverse order to avoid index drift.\n for (const { index } of [...sortedByIndex].reverse()) {\n owner.splice(index, 1)\n }\n\n // Group inserted at the slot vacated by the bottommost former member.\n const insertAt = sortedByIndex[0]!.index\n\n const group: GroupElement = {\n id: op.groupId,\n kind: 'group',\n name: op.name ?? 'Group',\n x: minX,\n y: minY,\n rotation: 0,\n opacity: 1,\n locked: false,\n visible: true,\n children,\n }\n\n owner.splice(insertAt, 0, group)\n return { kind: 'element', pageId: op.pageId, element: group }\n}\n\nfunction applyUngroupElement(doc: SceneDocument, op: UngroupElementOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const { element: groupEl, owner, index: groupIndex } = requireElement(page, op.groupId)\n const group = groupEl as GroupElement\n\n // Re-absolutize: child.x/y += group.x/y; rotation unchanged.\n const promoted: SceneElement[] = group.children.map((child) => ({\n ...child,\n x: child.x + group.x,\n y: child.y + group.y,\n }))\n\n owner.splice(groupIndex, 1, ...promoted)\n return { kind: 'element', pageId: op.pageId, element: groupEl }\n}\n\nfunction applyAddPage(doc: SceneDocument, op: AddPageOperation): SceneApplyResult {\n const opts: NewPageOptions = op.options ?? {}\n const page = createPage(opts, op.pageId)\n const index = op.index !== undefined ? op.index : doc.pages.length\n doc.pages.splice(index, 0, page)\n return { kind: 'page', page }\n}\n\nfunction applyDuplicatePage(\n doc: SceneDocument,\n op: DuplicatePageOperation,\n options: ApplySceneOptions,\n): SceneApplyResult {\n const source = requirePage(doc, op.sourcePageId)\n const copy: ScenePage = JSON.parse(JSON.stringify(source)) as ScenePage\n copy.id = op.pageId\n // Re-mint all element ids so the copy has no id collisions with the source.\n remintElementIds(copy.elements, options.mintId)\n doc.pages.push(copy)\n return { kind: 'page', page: copy }\n}\n\nfunction remintElementIds(elements: SceneElement[], mintId: () => string): void {\n for (let i = 0; i < elements.length; i++) {\n const el = elements[i]!\n const newEl: SceneElement = { ...el, id: mintId() }\n if (newEl.kind === 'group') {\n remintElementIds((newEl as GroupElement).children, mintId)\n }\n elements[i] = newEl\n }\n}\n\nfunction applyDeletePage(doc: SceneDocument, op: DeletePageOperation): SceneApplyResult {\n if (doc.pages.length <= 1) throw new Error('delete_page: cannot delete the last page')\n const index = doc.pages.findIndex((p) => p.id === op.pageId)\n if (index < 0) throw new Error(`page ${op.pageId} not found`)\n const [page] = doc.pages.splice(index, 1) as [ScenePage]\n return { kind: 'page', page }\n}\n\nfunction applyReorderPage(doc: SceneDocument, op: ReorderPageOperation): SceneApplyResult {\n const index = doc.pages.findIndex((p) => p.id === op.pageId)\n if (index < 0) throw new Error(`page ${op.pageId} not found`)\n const [page] = doc.pages.splice(index, 1) as [ScenePage]\n doc.pages.splice(op.toIndex, 0, page)\n return { kind: 'page', page }\n}\n\nfunction applySetPageProps(doc: SceneDocument, op: SetPagePropsOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n if (op.name !== undefined) page.name = op.name\n if (op.width !== undefined) page.width = op.width\n if (op.height !== undefined) page.height = op.height\n if (op.background !== undefined) page.background = op.background\n if (op.bleed !== undefined) page.bleed = op.bleed\n return { kind: 'page', page }\n}\n\nfunction applySetPageGuides(doc: SceneDocument, op: SetPageGuidesOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n page.guides = op.guides\n return { kind: 'page', page }\n}\n\nfunction applyBindSlot(doc: SceneDocument, op: BindSlotOperation): SceneApplyResult {\n const page = requirePage(doc, op.pageId)\n const { element, owner, index } = requireElement(page, op.elementId)\n const patched: SceneElement = op.slot === null ? omitSlot(element) : { ...element, slot: op.slot }\n owner[index] = patched\n return { kind: 'element', pageId: op.pageId, element: patched }\n}\n\nfunction omitSlot(element: SceneElement): SceneElement {\n const { slot: _slot, ...rest } = element as SceneElement & { slot?: string }\n return rest as SceneElement\n}\n\nfunction applyApplyData(doc: SceneDocument, op: ApplyDataOperation): SceneApplyResult {\n const slots = collectSlots(doc)\n for (const [slotName, value] of Object.entries(op.bindings)) {\n const slot = slots.get(slotName)\n if (!slot) throw new Error(`slot \"${slotName}\" not found in document`)\n const page = requirePage(doc, slot.pageId)\n const { element, owner, index } = requireElement(page, slot.elementId)\n owner[index] = applySlotValue(element, value)\n }\n return { kind: 'document' }\n}\n\nfunction applySlotValue(element: SceneElement, value: string): SceneElement {\n switch (element.kind) {\n case 'text':\n return { ...element, text: value }\n case 'image':\n case 'video':\n assertSceneMediaSrc(value, 'slot value')\n return { ...element, src: value }\n case 'rect':\n case 'ellipse':\n assertColor(value, 'slot value')\n return { ...element, fill: value }\n case 'line':\n assertColor(value, 'slot value')\n return { ...element, stroke: value }\n case 'group':\n // Group recolor: propagate fill/stroke to children that carry the property.\n assertColor(value, 'slot value')\n return recolorGroupChildren(element as GroupElement, value)\n }\n}\n\nfunction recolorGroupChildren(group: GroupElement, color: string): GroupElement {\n const children = group.children.map((child): SceneElement => {\n switch (child.kind) {\n case 'rect':\n case 'ellipse':\n return { ...child, fill: color }\n case 'line':\n return { ...child, stroke: color }\n case 'text':\n return { ...child, fill: color }\n case 'image':\n case 'video':\n return child\n case 'group':\n return recolorGroupChildren(child, color)\n }\n })\n return { ...group, children }\n}\n\nfunction applySetDocumentTitle(doc: SceneDocument, op: SetDocumentTitleOperation): SceneApplyResult {\n doc.title = op.title\n return { kind: 'document' }\n}\n\n// ---------------------------------------------------------------------------\n// Deep clone\n// ---------------------------------------------------------------------------\n\nfunction deepCloneDocument(document: SceneDocument): SceneDocument {\n return JSON.parse(JSON.stringify(document)) as SceneDocument\n}\n","/**\n * Page size presets, channel-size output presets, and export math for the\n * design canvas.\n *\n * Size presets (SizePreset / SIZE_PRESETS) describe page dimensions for the\n * new-page dialog. Export presets (ExportPreset / EXPORT_PRESETS) pin pixel\n * ratio, output dimensions, bleed, and format for the export dialog and the\n * MCP export tool. Channel presets (ChannelPreset / CHANNEL_PRESETS) are the\n * fixed output resolutions the export UI offers; `scaleForPreset` + the\n * letterbox helper derive Konva stage parameters from them.\n *\n * All dimensions are CSS pixels at 96 DPI unless noted otherwise.\n */\n\nimport type { Bounds, PageBleed, ScenePage } from './model'\n\nexport interface SizePreset {\n id: string\n label: string\n category: 'social' | 'print' | 'presentation' | 'custom'\n width: number\n height: number\n}\n\nexport const SIZE_PRESETS: readonly SizePreset[] = [\n // Social\n { id: 'instagram-square', label: 'Instagram — Square', category: 'social', width: 1080, height: 1080 },\n { id: 'instagram-portrait', label: 'Instagram — Portrait', category: 'social', width: 1080, height: 1350 },\n { id: 'instagram-story', label: 'Instagram Story', category: 'social', width: 1080, height: 1920 },\n { id: 'twitter-post', label: 'X / Twitter Post', category: 'social', width: 1200, height: 675 },\n { id: 'linkedin-post', label: 'LinkedIn Post', category: 'social', width: 1200, height: 627 },\n { id: 'facebook-post', label: 'Facebook Post', category: 'social', width: 1200, height: 630 },\n { id: 'youtube-thumbnail', label: 'YouTube Thumbnail', category: 'social', width: 1280, height: 720 },\n { id: 'og-image', label: 'Open Graph Image', category: 'social', width: 1200, height: 630 },\n // Presentation\n { id: 'slide-16-9', label: 'Slide — 16:9', category: 'presentation', width: 1920, height: 1080 },\n { id: 'slide-4-3', label: 'Slide — 4:3', category: 'presentation', width: 1024, height: 768 },\n // Print (96 DPI px equivalents of A4, Letter — products may scale at export)\n { id: 'a4-landscape', label: 'A4 Landscape', category: 'print', width: 1123, height: 794 },\n { id: 'a4-portrait', label: 'A4 Portrait', category: 'print', width: 794, height: 1123 },\n { id: 'us-letter-landscape', label: 'US Letter Landscape', category: 'print', width: 1100, height: 850 },\n { id: 'us-letter-portrait', label: 'US Letter Portrait', category: 'print', width: 850, height: 1100 },\n] as const\n\nexport function findPreset(id: string): SizePreset | null {\n return SIZE_PRESETS.find((p) => p.id === id) ?? null\n}\n\n/** Match a (width, height) pair against the preset table. Returns the first\n * exact match or null — used to drive the dropdown selection indicator. */\nexport function matchPreset(width: number, height: number): SizePreset | null {\n return SIZE_PRESETS.find((p) => p.width === width && p.height === height) ?? null\n}\n\n// ---------------------------------------------------------------------------\n// Export quality presets\n// ---------------------------------------------------------------------------\n\nexport type ExportFormat = 'png' | 'jpeg'\n\n/**\n * Export quality preset — pins pixel density, optional output dimensions,\n * bleed inclusion, and raster format so callers pass a preset id rather than\n * a full parameter bag.\n *\n * `outputWidth` / `outputHeight`: when non-null, the pixel ratio is derived\n * from the crop rect width (see `scaleForPreset`) so the output is exactly\n * this many pixels wide. When null, `pixelRatio` applies directly.\n */\nexport interface ExportPreset {\n name: string\n /** Target pixels-per-document-px for stage.toDataURL. */\n pixelRatio: number\n outputWidth: number | null\n outputHeight: number | null\n /** Whether bleed margins are included in the crop rect. */\n includeBleed: boolean\n format: ExportFormat\n}\n\nexport const EXPORT_PRESETS: Record<string, ExportPreset> = {\n 'instagram-square': {\n name: 'Instagram square (1080×1080)',\n pixelRatio: 1,\n outputWidth: 1080,\n outputHeight: 1080,\n includeBleed: false,\n format: 'jpeg',\n },\n 'instagram-portrait': {\n name: 'Instagram portrait (1080×1350)',\n pixelRatio: 1,\n outputWidth: 1080,\n outputHeight: 1350,\n includeBleed: false,\n format: 'jpeg',\n },\n 'twitter-card': {\n name: 'Twitter/X card (1200×675)',\n pixelRatio: 1,\n outputWidth: 1200,\n outputHeight: 675,\n includeBleed: false,\n format: 'jpeg',\n },\n 'og-image': {\n name: 'OG image (1200×630)',\n pixelRatio: 1,\n outputWidth: 1200,\n outputHeight: 630,\n includeBleed: false,\n format: 'png',\n },\n 'print-a4': {\n name: 'Print A4 (300 dpi)',\n pixelRatio: 3.125,\n outputWidth: null,\n outputHeight: null,\n includeBleed: true,\n format: 'png',\n },\n 'screen-2x': {\n name: 'Screen @2×',\n pixelRatio: 2,\n outputWidth: null,\n outputHeight: null,\n includeBleed: false,\n format: 'png',\n },\n} as const\n\n// ---------------------------------------------------------------------------\n// Crop + scale math (pure, no canvas context)\n// ---------------------------------------------------------------------------\n\nexport interface ExportCropRect {\n /** Page-coordinate origin. Negative when bleed extends outside page bounds. */\n x: number\n y: number\n width: number\n height: number\n}\n\n/**\n * Crop rectangle in page coordinates for a given page, optionally expanded to\n * include bleed margins. Bleed extends OUTSIDE the page bounds, so the origin\n * goes negative when bleed is included.\n *\n * When `includeBleed` is true but `page.bleed` is null, the page rect is\n * returned unchanged — the caller must not assume symmetric expansion.\n */\nexport function bleedAwareExportBounds(page: ScenePage, includeBleed: boolean): ExportCropRect {\n if (!includeBleed || page.bleed === null) {\n return { x: 0, y: 0, width: page.width, height: page.height }\n }\n const bleed: PageBleed = page.bleed\n return {\n x: -bleed.left,\n y: -bleed.top,\n width: page.width + bleed.left + bleed.right,\n height: page.height + bleed.top + bleed.bottom,\n }\n}\n\n/**\n * Pixel ratio for stage.toDataURL given a crop rect and an export preset.\n *\n * When the preset pins `outputWidth`, the ratio is derived from the crop rect\n * width so the final raster is exactly that many pixels wide. When there is no\n * output pin, the preset's declared `pixelRatio` is returned directly.\n *\n * The crop rect must already account for bleed inclusion before this call.\n */\nexport function scaleForPreset(preset: ExportPreset, cropRect: ExportCropRect): number {\n if (preset.outputWidth !== null) {\n if (cropRect.width <= 0) {\n throw new Error(`export crop width must be positive, got ${cropRect.width}`)\n }\n return preset.outputWidth / cropRect.width\n }\n return preset.pixelRatio\n}\n\n// ---------------------------------------------------------------------------\n// Channel-size presets (fixed output resolutions for the export UI)\n// ---------------------------------------------------------------------------\n\nexport interface ChannelPreset {\n id: string\n label: string\n width: number\n height: number\n}\n\n/**\n * Fixed output resolution presets for the channel/platform export dialog.\n * Width × height are OUTPUT pixels (the raster the export produces), not page\n * CSS px — they describe the target delivery format, not the canvas layout.\n * The a4_print_2480x3508 preset corresponds to A4 at 300 dpi.\n */\nexport const CHANNEL_PRESETS: readonly ChannelPreset[] = [\n { id: 'square_1080', label: 'Square (1080×1080)', width: 1080, height: 1080 },\n { id: 'portrait_1080x1350', label: 'Portrait (1080×1350)', width: 1080, height: 1350 },\n { id: 'story_1080x1920', label: 'Story (1080×1920)', width: 1080, height: 1920 },\n { id: 'landscape_1200x628', label: 'Landscape (1200×628)', width: 1200, height: 628 },\n { id: 'wide_1920x1080', label: 'Wide (1920×1080)', width: 1920, height: 1080 },\n { id: 'og_1200x630', label: 'Open Graph (1200×630)', width: 1200, height: 630 },\n { id: 'a4_print_2480x3508', label: 'A4 Print (2480×3508 · 300 dpi)', width: 2480, height: 3508 },\n] as const\n\nexport type ChannelPresetId = (typeof CHANNEL_PRESETS)[number]['id']\n\n/** Throws when the id is unknown — callers should only pass ids sourced from\n * `CHANNEL_PRESETS`. */\nexport function requireChannelPreset(id: string): ChannelPreset {\n const found = CHANNEL_PRESETS.find((p) => p.id === id)\n if (!found) {\n throw new Error(\n `unknown channel preset \"${id}\" — valid ids: ${CHANNEL_PRESETS.map((p) => p.id).join(', ')}`,\n )\n }\n return found\n}\n\n// ---------------------------------------------------------------------------\n// Letterbox scale math (page → channel preset, centered contain)\n// ---------------------------------------------------------------------------\n\nexport interface ChannelScaleResult {\n /**\n * Konva stage pixelRatio: the stage logical size stays at page dimensions;\n * the backing canvas renders at `pixelRatio × page` px. Setting Konva's\n * pixelRatio to this value yields an output canvas exactly\n * `channelPreset.width × channelPreset.height` pixels.\n */\n pixelRatio: number\n /**\n * Horizontal letterbox offset in PAGE-coordinate px. Add to the stage x\n * translation so the page is centered horizontally in the output frame.\n * Zero when the page fills the full width after scaling.\n */\n offsetX: number\n /**\n * Vertical letterbox offset in PAGE-coordinate px. Add to the stage y\n * translation. Zero when the page fills the full height.\n */\n offsetY: number\n fit: 'contain'\n}\n\n/**\n * Computes the Konva stage parameters to render `page` centered inside\n * `channelPreset` without cropping (contain / letterbox).\n *\n * Exact-ratio fast path: when page and preset share the same aspect ratio\n * (within 1e-9 floating-point tolerance) both offsets are 0 and pixelRatio\n * is exact.\n *\n * Example — 1080×1080 page into 1920×1080 preset:\n * scaleX = 1920/1080 ≈ 1.7778, scaleY = 1080/1080 = 1.0\n * pixelRatio = 1.0, rendered page = 1080×1080 px\n * offsetX = (1920 − 1080) / 2 / 1.0 = 420 page-px, offsetY = 0\n */\nexport function scalePageForChannelPreset(\n page: Pick<ScenePage, 'width' | 'height'>,\n channelPreset: ChannelPreset,\n): ChannelScaleResult {\n if (page.width <= 0 || page.height <= 0) {\n throw new Error(`page dimensions must be positive; got ${page.width}×${page.height}`)\n }\n\n const scaleX = channelPreset.width / page.width\n const scaleY = channelPreset.height / page.height\n const pixelRatio = Math.min(scaleX, scaleY)\n\n const renderedW = page.width * pixelRatio\n const renderedH = page.height * pixelRatio\n\n // Back-project padding into page coordinates so the Konva offset is in the\n // stage's own coordinate space\n const offsetX = ((channelPreset.width - renderedW) / 2) / pixelRatio\n const offsetY = ((channelPreset.height - renderedH) / 2) / pixelRatio\n\n return { pixelRatio, offsetX, offsetY, fit: 'contain' }\n}\n\n// ---------------------------------------------------------------------------\n// Bleed-aware trim bounds (no-arg bleed variant for templates/channel exports)\n// ---------------------------------------------------------------------------\n\n/**\n * Export rectangle in page coordinates that includes bleed margins when\n * present. When `page.bleed` is null, returns the trim rectangle (origin 0,0;\n * size = page dimensions).\n *\n * The bleed rectangle extends OUTSIDE the page: x and y are negative (bleed\n * bleeds off the left/top edge), width and height exceed the page by the\n * combined bleed on each axis. Pass into Konva's clip/export bounds to\n * include the bleed zone in the render.\n *\n * Named `bleedAwareExportRect` to avoid collision with the two-arg\n * `bleedAwareExportBounds(page, includeBleed)` above.\n */\nexport function bleedAwareExportRect(page: Pick<ScenePage, 'width' | 'height' | 'bleed'>): Bounds {\n if (page.bleed === null) {\n return { x: 0, y: 0, width: page.width, height: page.height }\n }\n const bleed: PageBleed = page.bleed\n return {\n x: -bleed.left,\n y: -bleed.top,\n width: page.width + bleed.left + bleed.right,\n height: page.height + bleed.top + bleed.bottom,\n }\n}\n"],"mappings":";;;;;AAiBO,IAAM,uBAAuB;AA8I7B,IAAM,sBAAmD;AAAA,EAC9D;AAAA,EAAQ;AAAA,EAAW;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAS;AAAA,EAAS;AACvD;AAcO,SAAS,cAAc,SAA0D;AACtF,UAAQ,QAAQ,MAAM;AAAA,IACpB,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO,EAAE,OAAO,QAAQ,OAAO,QAAQ,QAAQ,OAAO;AAAA,IACxD,KAAK;AAIH,aAAO;AAAA,QACL,OAAO,QAAQ;AAAA,QACf,QAAQ,mBAAmB,OAAO;AAAA,MACpC;AAAA,IACF,KAAK,QAAQ;AACX,UAAI,OAAO;AACX,UAAI,OAAO;AACX,eAAS,IAAI,GAAG,IAAI,QAAQ,OAAO,QAAQ,KAAK,GAAG;AACjD,eAAO,KAAK,IAAI,MAAM,KAAK,IAAI,QAAQ,OAAO,CAAC,CAAE,CAAC;AAClD,eAAO,KAAK,IAAI,MAAM,KAAK,IAAI,QAAQ,OAAO,IAAI,CAAC,CAAE,CAAC;AAAA,MACxD;AACA,aAAO,EAAE,OAAO,MAAM,QAAQ,KAAK;AAAA,IACrC;AAAA,IACA,KAAK,SAAS;AAIZ,UAAI,OAAO,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO;AACzC,iBAAW,SAAS,QAAQ,UAAU;AACpC,cAAM,OAAO,YAAY,KAAK;AAC9B,eAAO,KAAK,IAAI,MAAM,KAAK,CAAC;AAC5B,eAAO,KAAK,IAAI,MAAM,KAAK,CAAC;AAC5B,eAAO,KAAK,IAAI,MAAM,KAAK,IAAI,KAAK,KAAK;AACzC,eAAO,KAAK,IAAI,MAAM,KAAK,IAAI,KAAK,MAAM;AAAA,MAC5C;AACA,aAAO,EAAE,OAAO,OAAO,MAAM,QAAQ,OAAO,KAAK;AAAA,IACnD;AAAA,EACF;AACF;AAEO,SAAS,mBAAmB,SAAwE;AACzG,QAAM,QAAQ,QAAQ,KAAK,WAAW,IAAI,IAAI,QAAQ,KAAK,MAAM,IAAI,EAAE;AACvE,SAAO,QAAQ,QAAQ,WAAW,QAAQ;AAC5C;AAIO,SAAS,YAAY,SAA+B;AACzD,QAAM,EAAE,OAAO,OAAO,IAAI,cAAc,OAAO;AAC/C,MAAI,QAAQ,WAAW,QAAQ,GAAG;AAChC,WAAO,EAAE,GAAG,QAAQ,GAAG,GAAG,QAAQ,GAAG,OAAO,OAAO;AAAA,EACrD;AACA,QAAM,MAAO,QAAQ,WAAW,KAAK,KAAM;AAC3C,QAAM,MAAM,KAAK,IAAI,GAAG;AACxB,QAAM,MAAM,KAAK,IAAI,GAAG;AACxB,QAAM,UAAmC;AAAA,IACvC,CAAC,GAAG,CAAC;AAAA,IACL,CAAC,QAAQ,KAAK,QAAQ,GAAG;AAAA,IACzB,CAAC,CAAC,SAAS,KAAK,SAAS,GAAG;AAAA,IAC5B,CAAC,QAAQ,MAAM,SAAS,KAAK,QAAQ,MAAM,SAAS,GAAG;AAAA,EACzD;AACA,MAAI,OAAO,UAAU,OAAO,UAAU,OAAO,WAAW,OAAO;AAC/D,aAAW,CAAC,IAAI,EAAE,KAAK,SAAS;AAC9B,WAAO,KAAK,IAAI,MAAM,EAAE;AAAG,WAAO,KAAK,IAAI,MAAM,EAAE;AACnD,WAAO,KAAK,IAAI,MAAM,EAAE;AAAG,WAAO,KAAK,IAAI,MAAM,EAAE;AAAA,EACrD;AACA,SAAO,EAAE,GAAG,QAAQ,IAAI,MAAM,GAAG,QAAQ,IAAI,MAAM,OAAO,OAAO,MAAM,QAAQ,OAAO,KAAK;AAC7F;AAEO,SAAS,gBAAgB,GAAW,GAAoB;AAC7D,SAAO,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE;AAClG;AAMO,SAAS,YAAY,UAAyB,QAA2B;AAC9E,QAAM,OAAO,SAAS,MAAM,KAAK,CAAC,cAAc,UAAU,OAAO,MAAM;AACvE,MAAI,CAAC,KAAM,OAAM,IAAI,MAAM,QAAQ,MAAM,wBAAwB;AACjE,SAAO;AACT;AAKO,SAAS,YAAY,MAAiB,WAA2F;AACtI,QAAM,QAA0B,CAAC,KAAK,QAAQ;AAC9C,SAAO,MAAM,SAAS,GAAG;AACvB,UAAM,QAAQ,MAAM,IAAI;AACxB,aAAS,QAAQ,GAAG,QAAQ,MAAM,QAAQ,SAAS,GAAG;AACpD,YAAM,UAAU,MAAM,KAAK;AAC3B,UAAI,QAAQ,OAAO,UAAW,QAAO,EAAE,SAAS,OAAO,MAAM;AAC7D,UAAI,QAAQ,SAAS,QAAS,OAAM,KAAK,QAAQ,QAAQ;AAAA,IAC3D;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,eAAe,MAAiB,WAAoF;AAClI,QAAM,QAAQ,YAAY,MAAM,SAAS;AACzC,MAAI,CAAC,MAAO,OAAM,IAAI,MAAM,WAAW,SAAS,sBAAsB,KAAK,EAAE,EAAE;AAC/E,SAAO;AACT;AAIO,SAAS,aAAa,UAAqG;AAChI,QAAM,QAAQ,oBAAI,IAA2E;AAC7F,aAAW,QAAQ,SAAS,OAAO;AACjC,UAAM,QAAQ,CAAC,GAAG,KAAK,QAAQ;AAC/B,WAAO,MAAM,SAAS,GAAG;AACvB,YAAM,UAAU,MAAM,IAAI;AAC1B,UAAI,QAAQ,MAAM;AAChB,YAAI,MAAM,IAAI,QAAQ,IAAI,EAAG,OAAM,IAAI,MAAM,wBAAwB,QAAQ,IAAI,GAAG;AACpF,cAAM,IAAI,QAAQ,MAAM,EAAE,QAAQ,KAAK,IAAI,WAAW,QAAQ,IAAI,MAAM,QAAQ,KAAK,CAAC;AAAA,MACxF;AACA,UAAI,QAAQ,SAAS,QAAS,OAAM,KAAK,GAAG,QAAQ,QAAQ;AAAA,IAC9D;AAAA,EACF;AACA,SAAO;AACT;AAaO,SAAS,oBAAoB,OAAe,MAAsC;AACvF,SAAO;AAAA,IACL,eAAe;AAAA,IACf;AAAA,IACA,OAAO,CAAC,WAAW,QAAQ,CAAC,GAAG,QAAQ,CAAC;AAAA,IACxC,UAAU,EAAE,KAAK,GAAG;AAAA,IACpB,UAAU,CAAC;AAAA,EACb;AACF;AAEO,SAAS,WAAW,SAAyB,IAAuB;AACzE,QAAM,QAAQ,QAAQ,SAAS;AAC/B,QAAM,SAAS,QAAQ,UAAU;AACjC,uBAAqB,OAAO,YAAY;AACxC,uBAAqB,QAAQ,aAAa;AAC1C,SAAO;AAAA,IACL;AAAA,IACA,MAAM,QAAQ,QAAQ;AAAA,IACtB;AAAA,IACA;AAAA,IACA,YAAY,QAAQ,cAAc;AAAA,IAClC,OAAO;AAAA,IACP,QAAQ,EAAE,UAAU,CAAC,GAAG,YAAY,CAAC,EAAE;AAAA,IACvC,UAAU,CAAC;AAAA,EACb;AACF;AAEO,SAAS,qBAAqB,OAAe,OAAqB;AACvE,MAAI,CAAC,OAAO,SAAS,KAAK,KAAK,SAAS,EAAG,OAAM,IAAI,MAAM,GAAG,KAAK,mCAAmC;AACxG;AAEO,SAAS,aAAa,OAAe,OAAqB;AAC/D,MAAI,CAAC,OAAO,SAAS,KAAK,EAAG,OAAM,IAAI,MAAM,GAAG,KAAK,0BAA0B;AACjF;AAKA,IAAM,YAAY;AAClB,IAAM,YAAY;AAEX,SAAS,YAAY,OAAe,OAAqB;AAC9D,MAAI,UAAU,iBAAiB,UAAU,KAAK,KAAK,EAAG;AACtD,QAAM,MAAM,UAAU,KAAK,KAAK;AAChC,MAAI,OAAO,mBAAmB,GAAG,EAAG;AACpC,QAAM,IAAI,MAAM,GAAG,KAAK,sDAAsD,KAAK,GAAG;AACxF;AAIA,SAAS,mBAAmB,OAAiC;AAC3D,WAAS,IAAI,GAAG,KAAK,GAAG,KAAK,GAAG;AAC9B,UAAM,UAAU,OAAO,MAAM,CAAC,CAAC;AAC/B,QAAI,UAAU,IAAK,QAAO;AAAA,EAC5B;AACA,SAAO;AACT;AAMO,SAAS,oBAAoB,OAAe,OAAqB;AACtE,iBAAe,OAAO,KAAK;AAC7B;;;AC9TO,SAAS,wBAAwB,UAAyB,YAAoC;AACnG,aAAW,QAAQ,CAAC,WAAW,UAAU;AACvC,QAAI;AACF,6BAAuB,UAAU,SAAS;AAAA,IAC5C,SAAS,OAAO;AACd,YAAM,SAAS,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AACpE,YAAM,IAAI,MAAM,aAAa,QAAQ,CAAC,KAAK,UAAU,IAAI,MAAM,MAAM,EAAE;AAAA,IACzE;AAAA,EACF,CAAC;AACH;AAEO,SAAS,uBAAuB,UAAyB,WAAiC;AAC/F,UAAQ,UAAU,MAAM;AAAA,IACtB,KAAK;AACH,aAAO,mBAAmB,UAAU,SAAS;AAAA,IAC/C,KAAK;AACH,aAAO,iBAAiB,UAAU,SAAS;AAAA,IAC7C,KAAK;AACH,aAAO,uBAAuB,UAAU,SAAS;AAAA,IACnD,KAAK;AACH,aAAO,sBAAsB,UAAU,SAAS;AAAA,IAClD,KAAK;AACH,aAAO,sBAAsB,UAAU,SAAS;AAAA,IAClD,KAAK;AACH,aAAO,uBAAuB,UAAU,SAAS;AAAA,IACnD,KAAK;AACH,aAAO,gBAAgB,SAAS;AAAA,IAClC,KAAK;AACH,aAAO,sBAAsB,UAAU,SAAS;AAAA,IAClD,KAAK;AACH,aAAO,mBAAmB,UAAU,SAAS;AAAA,IAC/C,KAAK;AACH,aAAO,oBAAoB,UAAU,SAAS;AAAA,IAChD,KAAK;AACH,aAAO,qBAAqB,UAAU,SAAS;AAAA,IACjD,KAAK;AACH,aAAO,sBAAsB,UAAU,SAAS;AAAA,IAClD,KAAK;AACH,aAAO,iBAAiB,UAAU,SAAS;AAAA,IAC7C,KAAK;AACH,aAAO,kBAAkB,UAAU,SAAS;AAAA,IAC9C,KAAK;AACH,aAAO,yBAAyB,SAAS;AAAA,IAC3C,SAAS;AACP,YAAM,UAAU;AAChB,YAAM,IAAI,MAAM,8BAA8B,KAAK,UAAU,QAAQ,IAAI,CAAC,EAAE;AAAA,IAC9E;AAAA,EACF;AACF;AAMA,SAAS,mBAAmB,UAAyB,IAA+B;AAClF,QAAM,OAAO,YAAY,UAAU,GAAG,MAAM;AAC5C,6BAA2B,UAAU,GAAG,QAAQ,EAAE;AAClD,MAAI,GAAG,kBAAkB,QAAW;AAClC,UAAM,EAAE,SAAS,OAAO,IAAI,eAAe,MAAM,GAAG,aAAa;AACjE,QAAI,OAAO,SAAS,SAAS;AAC3B,YAAM,IAAI,MAAM,kBAAkB,GAAG,aAAa,UAAU,OAAO,IAAI,eAAe;AAAA,IACxF;AAAA,EACF;AACA,MAAI,GAAG,UAAU,QAAW;AAC1B,UAAM,QAAQ,GAAG,iBACZ,MAAM;AACL,YAAM,EAAE,SAAS,EAAE,IAAI,eAAe,MAAM,GAAG,aAAa;AAC5D,aAAQ,EAAkD;AAAA,IAC5D,GAAG,IACH,KAAK;AACT,QAAI,GAAG,QAAQ,KAAK,GAAG,QAAQ,MAAM,QAAQ;AAC3C,YAAM,IAAI,MAAM,SAAS,GAAG,KAAK,4BAA4B,MAAM,MAAM,YAAY;AAAA,IACvF;AAAA,EACF;AACA,uBAAqB,GAAG,QAAQ,MAAM,GAAG,SAAuC,IAAI;AACtF;AAEA,SAAS,iBAAiB,UAAyB,IAA6B;AAC9E,QAAM,OAAO,YAAY,UAAU,GAAG,MAAM;AAC5C,QAAM,EAAE,QAAQ,IAAI,eAAe,MAAM,GAAG,SAAS;AAErD,QAAM,eAAe,OAAO,KAAK,GAAG,KAAK,EAAE,WAAW,KAAK,GAAG,MAAM,WAAW;AAC/E,MAAI,QAAQ,UAAU,CAAC,cAAc;AACnC,UAAM,IAAI,MAAM,YAAY,GAAG,SAAS,wFAAwF;AAAA,EAClI;AACA,uBAAqB,QAAQ,MAAM,GAAG,OAAO,KAAK;AACpD;AAEA,SAAS,uBAAuB,UAAyB,IAAmC;AAC1F,QAAM,OAAO,YAAY,UAAU,GAAG,MAAM;AAC5C,QAAM,EAAE,SAAS,MAAM,IAAI,eAAe,MAAM,GAAG,SAAS;AAC5D,MAAI,QAAQ,QAAQ;AAClB,UAAM,IAAI,MAAM,YAAY,GAAG,SAAS,0CAA0C;AAAA,EACpF;AAGA,MAAI,GAAG,UAAU,KAAK,GAAG,WAAW,MAAM,QAAQ;AAChD,UAAM,IAAI,MAAM,WAAW,GAAG,OAAO,4BAA4B,MAAM,MAAM,YAAY;AAAA,EAC3F;AACF;AAEA,SAAS,sBAAsB,UAAyB,IAAkC;AACxF,QAAM,OAAO,YAAY,UAAU,GAAG,MAAM;AAC5C,QAAM,EAAE,QAAQ,IAAI,eAAe,MAAM,GAAG,SAAS;AACrD,MAAI,QAAQ,QAAQ;AAClB,UAAM,IAAI,MAAM,YAAY,GAAG,SAAS,wCAAwC;AAAA,EAClF;AACF;AAEA,SAAS,sBAAsB,UAAyB,IAAkC;AACxF,MAAI,GAAG,WAAW,SAAS,GAAG;AAC5B,UAAM,IAAI,MAAM,qDAAgD,GAAG,WAAW,MAAM,GAAG;AAAA,EACzF;AACA,6BAA2B,UAAU,GAAG,OAAO;AAC/C,QAAM,OAAO,YAAY,UAAU,GAAG,MAAM;AAE5C,QAAM,SAAS,GAAG,WAAW,IAAI,CAAC,OAAO;AACvC,UAAM,EAAE,SAAS,MAAM,IAAI,eAAe,MAAM,EAAE;AAClD,QAAI,QAAQ,OAAQ,OAAM,IAAI,MAAM,YAAY,EAAE,qCAAqC;AACvF,WAAO;AAAA,EACT,CAAC;AAED,QAAM,aAAa,OAAO,CAAC;AAC3B,WAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,QAAI,OAAO,CAAC,MAAM,YAAY;AAC5B,YAAM,IAAI,MAAM,+FAA0F;AAAA,IAC5G;AAAA,EACF;AACF;AAEA,SAAS,uBAAuB,UAAyB,IAAmC;AAC1F,QAAM,OAAO,YAAY,UAAU,GAAG,MAAM;AAC5C,QAAM,EAAE,QAAQ,IAAI,eAAe,MAAM,GAAG,OAAO;AACnD,MAAI,QAAQ,SAAS,SAAS;AAC5B,UAAM,IAAI,MAAM,YAAY,GAAG,OAAO,UAAU,QAAQ,IAAI,eAAe;AAAA,EAC7E;AACF;AAEA,SAAS,gBAAgB,IAAkF;AACzG,QAAM,OAAO,GAAG;AAChB,MAAI,CAAC,KAAM;AACX,MAAI,KAAK,UAAU,OAAW,sBAAqB,KAAK,OAAO,YAAY;AAC3E,MAAI,KAAK,WAAW,OAAW,sBAAqB,KAAK,QAAQ,aAAa;AAC9E,MAAI,KAAK,eAAe,OAAW,aAAY,KAAK,YAAY,iBAAiB;AACnF;AAEA,SAAS,sBAAsB,UAAyB,IAAkC;AACxF,cAAY,UAAU,GAAG,YAAY;AACrC,QAAM,WAAW,SAAS,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM;AAC9D,MAAI,SAAU,OAAM,IAAI,MAAM,WAAW,GAAG,MAAM,kCAAkC;AACtF;AAEA,SAAS,mBAAmB,UAAyB,IAA+B;AAClF,cAAY,UAAU,GAAG,MAAM;AAC/B,MAAI,SAAS,MAAM,WAAW,GAAG;AAC/B,UAAM,IAAI,MAAM,uCAAuC;AAAA,EACzD;AACF;AAEA,SAAS,oBAAoB,UAAyB,IAAgC;AACpF,cAAY,UAAU,GAAG,MAAM;AAC/B,MAAI,GAAG,UAAU,KAAK,GAAG,WAAW,SAAS,MAAM,QAAQ;AACzD,UAAM,IAAI,MAAM,WAAW,GAAG,OAAO,+BAA+B,SAAS,MAAM,MAAM,SAAS;AAAA,EACpG;AACF;AAEA,SAAS,qBAAqB,UAAyB,IAAiC;AACtF,cAAY,UAAU,GAAG,MAAM;AAC/B,MAAI,GAAG,UAAU,OAAW,sBAAqB,GAAG,OAAO,YAAY;AACvE,MAAI,GAAG,WAAW,OAAW,sBAAqB,GAAG,QAAQ,aAAa;AAC1E,MAAI,GAAG,eAAe,OAAW,aAAY,GAAG,YAAY,iBAAiB;AAC7E,MAAI,GAAG,SAAS,MAAM;AACpB,4BAAwB,GAAG,MAAM,KAAK,WAAW;AACjD,4BAAwB,GAAG,MAAM,OAAO,aAAa;AACrD,4BAAwB,GAAG,MAAM,QAAQ,cAAc;AACvD,4BAAwB,GAAG,MAAM,MAAM,YAAY;AAAA,EACrD;AACF;AAEA,SAAS,sBAAsB,UAAyB,IAAkC;AACxF,cAAY,UAAU,GAAG,MAAM;AAC/B,aAAW,OAAO,GAAG,OAAO,UAAU;AACpC,QAAI,CAAC,OAAO,SAAS,GAAG,EAAG,OAAM,IAAI,MAAM,kBAAkB,GAAG,gBAAgB;AAAA,EAClF;AACA,aAAW,OAAO,GAAG,OAAO,YAAY;AACtC,QAAI,CAAC,OAAO,SAAS,GAAG,EAAG,OAAM,IAAI,MAAM,kBAAkB,GAAG,gBAAgB;AAAA,EAClF;AACF;AAEA,SAAS,iBAAiB,UAAyB,IAA6B;AAC9E,QAAM,OAAO,YAAY,UAAU,GAAG,MAAM;AAC5C,iBAAe,MAAM,GAAG,SAAS;AACjC,MAAI,GAAG,SAAS,KAAM;AAItB,aAAW,KAAK,SAAS,OAAO;AAC9B,UAAM,QAAQ,CAAC,GAAG,EAAE,QAAQ;AAC5B,WAAO,MAAM,SAAS,GAAG;AACvB,YAAM,KAAK,MAAM,IAAI;AACrB,UAAI,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW;AACjD,cAAM,IAAI,MAAM,SAAS,GAAG,IAAI,kCAAkC,GAAG,EAAE,cAAc,EAAE,EAAE,GAAG;AAAA,MAC9F;AACA,UAAI,GAAG,SAAS,QAAS,OAAM,KAAK,GAAG,GAAG,QAAQ;AAAA,IACpD;AAAA,EACF;AACF;AAEA,SAAS,kBAAkB,UAAyB,IAA8B;AAEhF,QAAM,QAAQ,aAAa,QAAQ;AACnC,aAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,GAAG,QAAQ,GAAG;AAC3D,UAAM,OAAO,MAAM,IAAI,QAAQ;AAC/B,QAAI,CAAC,MAAM;AACT,YAAM,IAAI,MAAM,SAAS,QAAQ,kCAAkC;AAAA,IACrE;AACA,sBAAkB,UAAU,KAAK,MAAM,KAAK;AAAA,EAC9C;AACF;AAEA,SAAS,yBAAyB,IAAqC;AACrE,MAAI,GAAG,MAAM,KAAK,EAAE,WAAW,EAAG,OAAM,IAAI,MAAM,yBAAyB;AAC7E;AAOA,IAAM,aAAa,oBAAI,IAAI;AAAA,EACzB;AAAA,EAAQ;AAAA,EAAK;AAAA,EAAK;AAAA,EAAY;AAAA,EAAW;AAAA,EAAU;AAAA,EAAW;AAChE,CAAC;AAGD,IAAM,aAAoD;AAAA,EACxD,MAAS,oBAAI,IAAI,CAAC,SAAS,UAAU,QAAQ,UAAU,eAAe,cAAc,CAAC;AAAA,EACrF,SAAS,oBAAI,IAAI,CAAC,SAAS,UAAU,QAAQ,UAAU,aAAa,CAAC;AAAA,EACrE,MAAS,oBAAI,IAAI,CAAC,UAAU,UAAU,eAAe,MAAM,CAAC;AAAA,EAC5D,MAAS,oBAAI,IAAI,CAAC,QAAQ,SAAS,cAAc,YAAY,aAAa,QAAQ,SAAS,cAAc,eAAe,CAAC;AAAA,EACzH,OAAS,oBAAI,IAAI,CAAC,SAAS,UAAU,OAAO,KAAK,CAAC;AAAA,EAClD,OAAS,oBAAI,IAAI,CAAC,SAAS,UAAU,OAAO,WAAW,CAAC;AAAA,EACxD,OAAS,oBAAI,IAAI,CAAC,CAAC;AACrB;AAEA,IAAM,cAAc,oBAAI,IAAI,CAAC,UAAU,QAAQ,UAAU,aAAa,CAAC;AACvE,IAAM,eAAe,oBAAI,IAAI,CAAC,QAAQ,UAAU,OAAO,CAAC;AACxD,IAAM,aAAa,oBAAI,IAAI,CAAC,QAAQ,SAAS,SAAS,CAAC;AAOvD,SAAS,qBACP,MACA,OACA,gBACM;AACN,QAAM,UAAU,WAAW,IAAI;AAG/B,aAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,QAAI,QAAQ,QAAQ,QAAQ,UAAU,QAAQ,WAAY;AAC1D,QAAI,CAAC,WAAW,IAAI,GAAG,KAAK,CAAC,QAAQ,IAAI,GAAG,GAAG;AAC7C,YAAM,IAAI,MAAM,cAAc,GAAG,wBAAwB,IAAI,UAAU;AAAA,IACzE;AAAA,EACF;AAGA,MAAI,MAAM,YAAY,QAAW;AAC/B,QAAI,OAAO,MAAM,YAAY,YAAY,CAAC,OAAO,SAAS,MAAM,OAAO,KAAK,MAAM,UAAU,KAAK,MAAM,UAAU,GAAG;AAClH,YAAM,IAAI,MAAM,oCAAoC;AAAA,IACtD;AAAA,EACF;AACA,MAAI,MAAM,MAAM,OAAW,cAAa,MAAM,GAAG,GAAG;AACpD,MAAI,MAAM,MAAM,OAAW,cAAa,MAAM,GAAG,GAAG;AACpD,MAAI,MAAM,aAAa,OAAW,cAAa,MAAM,UAAU,UAAU;AAGzE,UAAQ,MAAM;AAAA,IACZ,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,UAAI,MAAM,UAAU,OAAW,sBAAqB,MAAM,OAAO,OAAO;AACxE,UAAI,MAAM,WAAW,OAAW,sBAAqB,MAAM,QAAQ,QAAQ;AAC3E;AAAA,IACF,KAAK;AACH,UAAI,MAAM,UAAU,OAAW,sBAAqB,MAAM,OAAO,OAAO;AACxE,UAAI,MAAM,aAAa,OAAW,sBAAqB,MAAM,UAAU,UAAU;AACjF,UAAI,MAAM,eAAe,QAAW;AAClC,YAAI,OAAO,MAAM,eAAe,YAAY,CAAC,OAAO,SAAS,MAAM,UAAU,KAAK,MAAM,cAAc,GAAG;AACvG,gBAAM,IAAI,MAAM,6CAA6C;AAAA,QAC/D;AAAA,MACF;AACA,UAAI,MAAM,cAAc,UAAa,CAAC,YAAY,IAAI,MAAM,SAAS,GAAG;AACtE,cAAM,IAAI,MAAM,6BAA6B,CAAC,GAAG,WAAW,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,MAC5E;AACA,UAAI,MAAM,UAAU,UAAa,CAAC,aAAa,IAAI,MAAM,KAAK,GAAG;AAC/D,cAAM,IAAI,MAAM,yBAAyB,CAAC,GAAG,YAAY,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,MACzE;AACA;AAAA,IACF,KAAK;AACH,UAAI,MAAM,WAAW,QAAW;AAC9B,YAAI,CAAC,MAAM,QAAQ,MAAM,MAAM,KAAK,MAAM,OAAO,SAAS,KAAK,MAAM,OAAO,SAAS,MAAM,GAAG;AAC5F,gBAAM,IAAI,MAAM,wEAAwE;AAAA,QAC1F;AACA,iBAAS,IAAI,GAAG,IAAI,MAAM,OAAO,QAAQ,KAAK;AAC5C,cAAI,CAAC,OAAO,SAAS,MAAM,OAAO,CAAC,CAAC,GAAG;AACrC,kBAAM,IAAI,MAAM,UAAU,CAAC,iBAAiB;AAAA,UAC9C;AAAA,QACF;AAAA,MACF;AACA,UAAI,MAAM,gBAAgB,OAAW,sBAAqB,MAAM,aAAa,aAAa;AAC1F;AAAA,IACF,KAAK;AAEH;AAAA,EACJ;AAGA,MAAI,MAAM,SAAS,OAAW,aAAY,MAAM,MAAM,MAAM;AAC5D,MAAI,MAAM,WAAW,OAAW,aAAY,MAAM,QAAQ,QAAQ;AAClE,MAAI,MAAM,gBAAgB,UAAa,SAAS,QAAQ;AACtD,yBAAqB,MAAM,aAAa,aAAa;AAAA,EACvD;AAGA,MAAI,MAAM,QAAQ,OAAW,qBAAoB,MAAM,KAAK,KAAK;AACjE,MAAK,MAAiC,cAAc,QAAW;AAC7D,wBAAqB,MAAgC,WAAW,WAAW;AAAA,EAC7E;AAGA,MAAI,MAAM,QAAQ,UAAa,CAAC,WAAW,IAAI,MAAM,GAAG,GAAG;AACzD,UAAM,IAAI,MAAM,uBAAuB,CAAC,GAAG,UAAU,EAAE,KAAK,IAAI,CAAC,EAAE;AAAA,EACrE;AAGA,MAAI,gBAAgB;AAClB,sCAAkC,MAAM,KAAK;AAAA,EAC/C;AACF;AAEA,SAAS,kCAAkC,MAAwB,OAA8B;AAC/F,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,yBAAmB,OAAO,SAAS,IAAI;AACvC,yBAAmB,OAAO,UAAU,IAAI;AACxC,yBAAmB,OAAO,QAAQ,IAAI;AACtC;AAAA,IACF,KAAK;AACH,yBAAmB,OAAO,SAAS,IAAI;AACvC,yBAAmB,OAAO,UAAU,IAAI;AACxC,yBAAmB,OAAO,QAAQ,IAAI;AACtC;AAAA,IACF,KAAK;AACH,yBAAmB,OAAO,UAAU,IAAI;AACxC,yBAAmB,OAAO,UAAU,IAAI;AACxC,yBAAmB,OAAO,eAAe,IAAI;AAC7C;AAAA,IACF,KAAK;AACH,yBAAmB,OAAO,QAAQ,IAAI;AACtC,yBAAmB,OAAO,SAAS,IAAI;AACvC,yBAAmB,OAAO,cAAc,IAAI;AAC5C,yBAAmB,OAAO,YAAY,IAAI;AAC1C,yBAAmB,OAAO,aAAa,IAAI;AAC3C,yBAAmB,OAAO,QAAQ,IAAI;AACtC,yBAAmB,OAAO,SAAS,IAAI;AACvC,yBAAmB,OAAO,cAAc,IAAI;AAC5C,yBAAmB,OAAO,iBAAiB,IAAI;AAC/C;AAAA,IACF,KAAK;AACH,yBAAmB,OAAO,SAAS,IAAI;AACvC,yBAAmB,OAAO,UAAU,IAAI;AACxC,yBAAmB,OAAO,OAAO,IAAI;AACrC,yBAAmB,OAAO,OAAO,IAAI;AACrC;AAAA,IACF,KAAK;AACH,yBAAmB,OAAO,SAAS,IAAI;AACvC,yBAAmB,OAAO,UAAU,IAAI;AACxC,yBAAmB,OAAO,OAAO,IAAI;AACrC;AAAA,IACF,KAAK;AAEH;AAAA,EACJ;AACF;AAEA,SAAS,mBAAmB,OAAwB,KAAa,MAA8B;AAC7F,MAAK,MAAkC,GAAG,MAAM,QAAW;AACzD,UAAM,IAAI,MAAM,GAAG,GAAG,oCAAoC,IAAI,UAAU;AAAA,EAC1E;AACF;AAUO,SAAS,kBAAkB,UAAkB,aAA+B,OAAqB;AACtG,UAAQ,aAAa;AAAA,IACnB,KAAK;AAEH;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AACH,UAAI;AACF,4BAAoB,OAAO,SAAS,QAAQ,SAAS;AAAA,MACvD,SAAS,GAAG;AACV,cAAM,IAAI;AAAA,UACR,SAAS,QAAQ,mBAAmB,WAAW,6EAAwE,KAAK;AAAA,QAC9H;AAAA,MACF;AACA;AAAA,IACF,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,UAAI;AACF,oBAAY,OAAO,SAAS,QAAQ,SAAS;AAAA,MAC/C,QAAQ;AACN,cAAM,IAAI;AAAA,UACR,SAAS,QAAQ,mBAAmB,WAAW,gGAA2F,KAAK;AAAA,QACjJ;AAAA,MACF;AACA;AAAA,EACJ;AACF;AAMA,SAAS,2BAA2B,UAAyB,IAAkB;AAC7E,aAAW,QAAQ,SAAS,OAAO;AACjC,QAAI,KAAK,OAAO,GAAI,OAAM,IAAI,MAAM,OAAO,EAAE,6BAA6B;AAC1E,UAAM,QAAQ,CAAC,GAAG,KAAK,QAAQ;AAC/B,WAAO,MAAM,SAAS,GAAG;AACvB,YAAM,KAAK,MAAM,IAAI;AACrB,UAAI,GAAG,OAAO,GAAI,OAAM,IAAI,MAAM,OAAO,EAAE,yCAAyC,KAAK,EAAE,GAAG;AAC9F,UAAI,GAAG,SAAS,QAAS,OAAM,KAAK,GAAG,GAAG,QAAQ;AAAA,IACpD;AAAA,EACF;AACF;AAMA,SAAS,wBAAwB,OAAe,OAAqB;AACnE,MAAI,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GAAG;AACxC,UAAM,IAAI,MAAM,GAAG,KAAK,uCAAuC;AAAA,EACjE;AACF;;;ACvZO,SAAS,qBACd,UACA,YACA,SAC0E;AAC1E,QAAM,OAAO,WAAW,EAAE,QAAQ,YAAY,EAAE;AAChD,QAAM,MAAM,kBAAkB,QAAQ;AACtC,QAAM,UAA8B,CAAC;AACrC,aAAW,aAAa,YAAY;AAClC,YAAQ,KAAK,kBAAkB,KAAK,WAAW,IAAI,CAAC;AAAA,EACtD;AACA,SAAO,YAAY,SAAY,EAAE,UAAU,KAAK,QAAQ,IAAI;AAC9D;AAKO,SAAS,oBACd,UACA,WACe;AACf,SAAO,qBAAqB,UAAU,CAAC,SAAS,CAAC;AACnD;AAEA,SAAS,cAA4B;AACnC,MAAI,IAAI;AACR,SAAO,MAAM,UAAW,KAAK,CAAE;AACjC;AASA,SAAS,gBAAgB,KAAuB;AAC9C,SAAO,eAAe,SAAS,aAAa,KAAK,IAAI,OAAO;AAC9D;AAEA,eAAsB,oBACpB,OACA,MACA,MACuE;AACvE,MAAI,EAAE,UAAU,IAAI,IAAI,MAAM,MAAM,YAAY;AAEhD,0BAAwB,UAAU,KAAK,UAAU;AACjD,MAAI,UAAU,qBAAqB,UAAU,KAAK,YAAY,EAAE,QAAQ,KAAK,OAAO,CAAC;AAErF,MAAI;AACJ,MAAI;AACF,aAAS,MAAM,MAAM,aAAa,QAAQ,UAAU,GAAG;AAAA,EACzD,SAAS,YAAY;AAInB,QAAI,CAAC,gBAAgB,UAAU,EAAG,OAAM;AACxC,UAAM,YAAY,MAAM,MAAM,YAAY;AAC1C,4BAAwB,UAAU,UAAU,KAAK,UAAU;AAC3D,cAAU,qBAAqB,UAAU,UAAU,KAAK,YAAY,EAAE,QAAQ,KAAK,OAAO,CAAC;AAC3F,QAAI;AACF,eAAS,MAAM,MAAM,aAAa,QAAQ,UAAU,UAAU,GAAG;AAAA,IACnE,SAAS,aAAa;AACpB,YAAM,SAAS,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW;AACtF,YAAM,IAAI,MAAM,8DAAyD,MAAM,EAAE;AAAA,IACnF;AAAA,EACF;AAEA,QAAM,eAAuC,CAAC;AAC9C,aAAW,MAAM,KAAK,YAAY;AAChC,iBAAa,GAAG,IAAI,KAAK,aAAa,GAAG,IAAI,KAAK,KAAK;AAAA,EACzD;AAEA,QAAM,MAAM,eAAe;AAAA,IACzB,MAAM,KAAK;AAAA,IACX,aAAa,KAAK;AAAA,IAClB,UAAU,EAAE,cAAc,gBAAgB,KAAK,WAAW,OAAO;AAAA,EACnE,CAAC;AAED,SAAO,EAAE,QAAQ,SAAS,QAAQ,QAAQ;AAC5C;AAMA,SAAS,kBACP,KACA,WACA,SACkB;AAClB,UAAQ,UAAU,MAAM;AAAA,IACtB,KAAK;AACH,aAAO,gBAAgB,KAAK,SAAS;AAAA,IACvC,KAAK;AACH,aAAO,cAAc,KAAK,SAAS;AAAA,IACrC,KAAK;AACH,aAAO,oBAAoB,KAAK,SAAS;AAAA,IAC3C,KAAK;AACH,aAAO,mBAAmB,KAAK,SAAS;AAAA,IAC1C,KAAK;AACH,aAAO,mBAAmB,KAAK,SAAS;AAAA,IAC1C,KAAK;AACH,aAAO,oBAAoB,KAAK,SAAS;AAAA,IAC3C,KAAK;AACH,aAAO,aAAa,KAAK,SAAS;AAAA,IACpC,KAAK;AACH,aAAO,mBAAmB,KAAK,WAAW,OAAO;AAAA,IACnD,KAAK;AACH,aAAO,gBAAgB,KAAK,SAAS;AAAA,IACvC,KAAK;AACH,aAAO,iBAAiB,KAAK,SAAS;AAAA,IACxC,KAAK;AACH,aAAO,kBAAkB,KAAK,SAAS;AAAA,IACzC,KAAK;AACH,aAAO,mBAAmB,KAAK,SAAS;AAAA,IAC1C,KAAK;AACH,aAAO,cAAc,KAAK,SAAS;AAAA,IACrC,KAAK;AACH,aAAO,eAAe,KAAK,SAAS;AAAA,IACtC,KAAK;AACH,aAAO,sBAAsB,KAAK,SAAS;AAAA,EAC/C;AACF;AAMA,SAAS,gBAAgB,KAAoB,IAA2C;AACtF,QAAM,OAAO,YAAY,KAAK,GAAG,MAAM;AACvC,QAAM,QAAwB,GAAG,kBAAkB,UAC9C,MAAM;AACL,UAAM,EAAE,SAAS,EAAE,IAAI,eAAe,MAAM,GAAG,aAAa;AAC5D,WAAQ,EAAmB;AAAA,EAC7B,GAAG,IACH,KAAK;AACT,QAAM,QAAQ,GAAG,UAAU,SAAY,GAAG,QAAQ,MAAM;AACxD,QAAM,OAAO,OAAO,GAAG,GAAG,OAAO;AACjC,SAAO,EAAE,MAAM,WAAW,QAAQ,GAAG,QAAQ,SAAS,GAAG,QAAQ;AACnE;AAEA,SAAS,cAAc,KAAoB,IAAyC;AAClF,QAAM,OAAO,YAAY,KAAK,GAAG,MAAM;AACvC,QAAM,EAAE,SAAS,OAAO,MAAM,IAAI,eAAe,MAAM,GAAG,SAAS;AACnE,QAAM,UAAU,EAAE,GAAG,SAAS,GAAG,GAAG,MAAM;AAC1C,QAAM,KAAK,IAAI;AACf,SAAO,EAAE,MAAM,WAAW,QAAQ,GAAG,QAAQ,SAAS,QAAQ;AAChE;AAEA,SAAS,oBAAoB,KAAoB,IAA+C;AAC9F,QAAM,OAAO,YAAY,KAAK,GAAG,MAAM;AACvC,QAAM,EAAE,SAAS,OAAO,MAAM,IAAI,eAAe,MAAM,GAAG,SAAS;AACnE,QAAM,OAAO,OAAO,CAAC;AACrB,QAAM,OAAO,GAAG,SAAS,GAAG,OAAO;AACnC,SAAO,EAAE,MAAM,WAAW,QAAQ,GAAG,QAAQ,QAAQ;AACvD;AAEA,SAAS,mBAAmB,KAAoB,IAA8C;AAC5F,QAAM,OAAO,YAAY,KAAK,GAAG,MAAM;AACvC,QAAM,EAAE,SAAS,OAAO,MAAM,IAAI,eAAe,MAAM,GAAG,SAAS;AACnE,QAAM,OAAO,OAAO,CAAC;AACrB,SAAO,EAAE,MAAM,WAAW,QAAQ,GAAG,QAAQ,QAAQ;AACvD;AAEA,SAAS,mBAAmB,KAAoB,IAA8C;AAC5F,QAAM,OAAO,YAAY,KAAK,GAAG,MAAM;AACvC,QAAM,UAAU,GAAG,WAAW,IAAI,CAAC,OAAO,eAAe,MAAM,EAAE,CAAC;AAGlE,MAAI,OAAO,UAAU,OAAO;AAC5B,aAAW,EAAE,QAAQ,KAAK,SAAS;AACjC,UAAM,OAAO,YAAY,OAAO;AAChC,QAAI,KAAK,IAAI,KAAM,QAAO,KAAK;AAC/B,QAAI,KAAK,IAAI,KAAM,QAAO,KAAK;AAAA,EACjC;AAEA,QAAM,QAAQ,QAAQ,CAAC,EAAG;AAE1B,QAAM,gBAAgB,CAAC,GAAG,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAGnE,QAAM,WAA2B,cAAc,IAAI,CAAC,EAAE,QAAQ,OAAO;AAAA,IACnE,GAAG;AAAA,IACH,GAAG,QAAQ,IAAI;AAAA,IACf,GAAG,QAAQ,IAAI;AAAA,EACjB,EAAE;AAGF,aAAW,EAAE,MAAM,KAAK,CAAC,GAAG,aAAa,EAAE,QAAQ,GAAG;AACpD,UAAM,OAAO,OAAO,CAAC;AAAA,EACvB;AAGA,QAAM,WAAW,cAAc,CAAC,EAAG;AAEnC,QAAM,QAAsB;AAAA,IAC1B,IAAI,GAAG;AAAA,IACP,MAAM;AAAA,IACN,MAAM,GAAG,QAAQ;AAAA,IACjB,GAAG;AAAA,IACH,GAAG;AAAA,IACH,UAAU;AAAA,IACV,SAAS;AAAA,IACT,QAAQ;AAAA,IACR,SAAS;AAAA,IACT;AAAA,EACF;AAEA,QAAM,OAAO,UAAU,GAAG,KAAK;AAC/B,SAAO,EAAE,MAAM,WAAW,QAAQ,GAAG,QAAQ,SAAS,MAAM;AAC9D;AAEA,SAAS,oBAAoB,KAAoB,IAA+C;AAC9F,QAAM,OAAO,YAAY,KAAK,GAAG,MAAM;AACvC,QAAM,EAAE,SAAS,SAAS,OAAO,OAAO,WAAW,IAAI,eAAe,MAAM,GAAG,OAAO;AACtF,QAAM,QAAQ;AAGd,QAAM,WAA2B,MAAM,SAAS,IAAI,CAAC,WAAW;AAAA,IAC9D,GAAG;AAAA,IACH,GAAG,MAAM,IAAI,MAAM;AAAA,IACnB,GAAG,MAAM,IAAI,MAAM;AAAA,EACrB,EAAE;AAEF,QAAM,OAAO,YAAY,GAAG,GAAG,QAAQ;AACvC,SAAO,EAAE,MAAM,WAAW,QAAQ,GAAG,QAAQ,SAAS,QAAQ;AAChE;AAEA,SAAS,aAAa,KAAoB,IAAwC;AAChF,QAAM,OAAuB,GAAG,WAAW,CAAC;AAC5C,QAAM,OAAO,WAAW,MAAM,GAAG,MAAM;AACvC,QAAM,QAAQ,GAAG,UAAU,SAAY,GAAG,QAAQ,IAAI,MAAM;AAC5D,MAAI,MAAM,OAAO,OAAO,GAAG,IAAI;AAC/B,SAAO,EAAE,MAAM,QAAQ,KAAK;AAC9B;AAEA,SAAS,mBACP,KACA,IACA,SACkB;AAClB,QAAM,SAAS,YAAY,KAAK,GAAG,YAAY;AAC/C,QAAM,OAAkB,KAAK,MAAM,KAAK,UAAU,MAAM,CAAC;AACzD,OAAK,KAAK,GAAG;AAEb,mBAAiB,KAAK,UAAU,QAAQ,MAAM;AAC9C,MAAI,MAAM,KAAK,IAAI;AACnB,SAAO,EAAE,MAAM,QAAQ,MAAM,KAAK;AACpC;AAEA,SAAS,iBAAiB,UAA0B,QAA4B;AAC9E,WAAS,IAAI,GAAG,IAAI,SAAS,QAAQ,KAAK;AACxC,UAAM,KAAK,SAAS,CAAC;AACrB,UAAM,QAAsB,EAAE,GAAG,IAAI,IAAI,OAAO,EAAE;AAClD,QAAI,MAAM,SAAS,SAAS;AAC1B,uBAAkB,MAAuB,UAAU,MAAM;AAAA,IAC3D;AACA,aAAS,CAAC,IAAI;AAAA,EAChB;AACF;AAEA,SAAS,gBAAgB,KAAoB,IAA2C;AACtF,MAAI,IAAI,MAAM,UAAU,EAAG,OAAM,IAAI,MAAM,0CAA0C;AACrF,QAAM,QAAQ,IAAI,MAAM,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM;AAC3D,MAAI,QAAQ,EAAG,OAAM,IAAI,MAAM,QAAQ,GAAG,MAAM,YAAY;AAC5D,QAAM,CAAC,IAAI,IAAI,IAAI,MAAM,OAAO,OAAO,CAAC;AACxC,SAAO,EAAE,MAAM,QAAQ,KAAK;AAC9B;AAEA,SAAS,iBAAiB,KAAoB,IAA4C;AACxF,QAAM,QAAQ,IAAI,MAAM,UAAU,CAAC,MAAM,EAAE,OAAO,GAAG,MAAM;AAC3D,MAAI,QAAQ,EAAG,OAAM,IAAI,MAAM,QAAQ,GAAG,MAAM,YAAY;AAC5D,QAAM,CAAC,IAAI,IAAI,IAAI,MAAM,OAAO,OAAO,CAAC;AACxC,MAAI,MAAM,OAAO,GAAG,SAAS,GAAG,IAAI;AACpC,SAAO,EAAE,MAAM,QAAQ,KAAK;AAC9B;AAEA,SAAS,kBAAkB,KAAoB,IAA6C;AAC1F,QAAM,OAAO,YAAY,KAAK,GAAG,MAAM;AACvC,MAAI,GAAG,SAAS,OAAW,MAAK,OAAO,GAAG;AAC1C,MAAI,GAAG,UAAU,OAAW,MAAK,QAAQ,GAAG;AAC5C,MAAI,GAAG,WAAW,OAAW,MAAK,SAAS,GAAG;AAC9C,MAAI,GAAG,eAAe,OAAW,MAAK,aAAa,GAAG;AACtD,MAAI,GAAG,UAAU,OAAW,MAAK,QAAQ,GAAG;AAC5C,SAAO,EAAE,MAAM,QAAQ,KAAK;AAC9B;AAEA,SAAS,mBAAmB,KAAoB,IAA8C;AAC5F,QAAM,OAAO,YAAY,KAAK,GAAG,MAAM;AACvC,OAAK,SAAS,GAAG;AACjB,SAAO,EAAE,MAAM,QAAQ,KAAK;AAC9B;AAEA,SAAS,cAAc,KAAoB,IAAyC;AAClF,QAAM,OAAO,YAAY,KAAK,GAAG,MAAM;AACvC,QAAM,EAAE,SAAS,OAAO,MAAM,IAAI,eAAe,MAAM,GAAG,SAAS;AACnE,QAAM,UAAwB,GAAG,SAAS,OAAO,SAAS,OAAO,IAAI,EAAE,GAAG,SAAS,MAAM,GAAG,KAAK;AACjG,QAAM,KAAK,IAAI;AACf,SAAO,EAAE,MAAM,WAAW,QAAQ,GAAG,QAAQ,SAAS,QAAQ;AAChE;AAEA,SAAS,SAAS,SAAqC;AACrD,QAAM,EAAE,MAAM,OAAO,GAAG,KAAK,IAAI;AACjC,SAAO;AACT;AAEA,SAAS,eAAe,KAAoB,IAA0C;AACpF,QAAM,QAAQ,aAAa,GAAG;AAC9B,aAAW,CAAC,UAAU,KAAK,KAAK,OAAO,QAAQ,GAAG,QAAQ,GAAG;AAC3D,UAAM,OAAO,MAAM,IAAI,QAAQ;AAC/B,QAAI,CAAC,KAAM,OAAM,IAAI,MAAM,SAAS,QAAQ,yBAAyB;AACrE,UAAM,OAAO,YAAY,KAAK,KAAK,MAAM;AACzC,UAAM,EAAE,SAAS,OAAO,MAAM,IAAI,eAAe,MAAM,KAAK,SAAS;AACrE,UAAM,KAAK,IAAI,eAAe,SAAS,KAAK;AAAA,EAC9C;AACA,SAAO,EAAE,MAAM,WAAW;AAC5B;AAEA,SAAS,eAAe,SAAuB,OAA6B;AAC1E,UAAQ,QAAQ,MAAM;AAAA,IACpB,KAAK;AACH,aAAO,EAAE,GAAG,SAAS,MAAM,MAAM;AAAA,IACnC,KAAK;AAAA,IACL,KAAK;AACH,0BAAoB,OAAO,YAAY;AACvC,aAAO,EAAE,GAAG,SAAS,KAAK,MAAM;AAAA,IAClC,KAAK;AAAA,IACL,KAAK;AACH,kBAAY,OAAO,YAAY;AAC/B,aAAO,EAAE,GAAG,SAAS,MAAM,MAAM;AAAA,IACnC,KAAK;AACH,kBAAY,OAAO,YAAY;AAC/B,aAAO,EAAE,GAAG,SAAS,QAAQ,MAAM;AAAA,IACrC,KAAK;AAEH,kBAAY,OAAO,YAAY;AAC/B,aAAO,qBAAqB,SAAyB,KAAK;AAAA,EAC9D;AACF;AAEA,SAAS,qBAAqB,OAAqB,OAA6B;AAC9E,QAAM,WAAW,MAAM,SAAS,IAAI,CAAC,UAAwB;AAC3D,YAAQ,MAAM,MAAM;AAAA,MAClB,KAAK;AAAA,MACL,KAAK;AACH,eAAO,EAAE,GAAG,OAAO,MAAM,MAAM;AAAA,MACjC,KAAK;AACH,eAAO,EAAE,GAAG,OAAO,QAAQ,MAAM;AAAA,MACnC,KAAK;AACH,eAAO,EAAE,GAAG,OAAO,MAAM,MAAM;AAAA,MACjC,KAAK;AAAA,MACL,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO,qBAAqB,OAAO,KAAK;AAAA,IAC5C;AAAA,EACF,CAAC;AACD,SAAO,EAAE,GAAG,OAAO,SAAS;AAC9B;AAEA,SAAS,sBAAsB,KAAoB,IAAiD;AAClG,MAAI,QAAQ,GAAG;AACf,SAAO,EAAE,MAAM,WAAW;AAC5B;AAMA,SAAS,kBAAkB,UAAwC;AACjE,SAAO,KAAK,MAAM,KAAK,UAAU,QAAQ,CAAC;AAC5C;;;ACvcO,IAAM,eAAsC;AAAA;AAAA,EAEjD,EAAE,IAAI,oBAAoB,OAAO,2BAAsB,UAAU,UAAU,OAAO,MAAM,QAAQ,KAAK;AAAA,EACrG,EAAE,IAAI,sBAAsB,OAAO,6BAAwB,UAAU,UAAU,OAAO,MAAM,QAAQ,KAAK;AAAA,EACzG,EAAE,IAAI,mBAAmB,OAAO,mBAAmB,UAAU,UAAU,OAAO,MAAM,QAAQ,KAAK;AAAA,EACjG,EAAE,IAAI,gBAAgB,OAAO,oBAAoB,UAAU,UAAU,OAAO,MAAM,QAAQ,IAAI;AAAA,EAC9F,EAAE,IAAI,iBAAiB,OAAO,iBAAiB,UAAU,UAAU,OAAO,MAAM,QAAQ,IAAI;AAAA,EAC5F,EAAE,IAAI,iBAAiB,OAAO,iBAAiB,UAAU,UAAU,OAAO,MAAM,QAAQ,IAAI;AAAA,EAC5F,EAAE,IAAI,qBAAqB,OAAO,qBAAqB,UAAU,UAAU,OAAO,MAAM,QAAQ,IAAI;AAAA,EACpG,EAAE,IAAI,YAAY,OAAO,oBAAoB,UAAU,UAAU,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAE1F,EAAE,IAAI,cAAc,OAAO,qBAAgB,UAAU,gBAAgB,OAAO,MAAM,QAAQ,KAAK;AAAA,EAC/F,EAAE,IAAI,aAAa,OAAO,oBAAe,UAAU,gBAAgB,OAAO,MAAM,QAAQ,IAAI;AAAA;AAAA,EAE5F,EAAE,IAAI,gBAAgB,OAAO,gBAAgB,UAAU,SAAS,OAAO,MAAM,QAAQ,IAAI;AAAA,EACzF,EAAE,IAAI,eAAe,OAAO,eAAe,UAAU,SAAS,OAAO,KAAK,QAAQ,KAAK;AAAA,EACvF,EAAE,IAAI,uBAAuB,OAAO,uBAAuB,UAAU,SAAS,OAAO,MAAM,QAAQ,IAAI;AAAA,EACvG,EAAE,IAAI,sBAAsB,OAAO,sBAAsB,UAAU,SAAS,OAAO,KAAK,QAAQ,KAAK;AACvG;AAEO,SAAS,WAAW,IAA+B;AACxD,SAAO,aAAa,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK;AAClD;AAIO,SAAS,YAAY,OAAe,QAAmC;AAC5E,SAAO,aAAa,KAAK,CAAC,MAAM,EAAE,UAAU,SAAS,EAAE,WAAW,MAAM,KAAK;AAC/E;AA4BO,IAAM,iBAA+C;AAAA,EAC1D,oBAAoB;AAAA,IAClB,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,cAAc;AAAA,IACd,cAAc;AAAA,IACd,QAAQ;AAAA,EACV;AAAA,EACA,sBAAsB;AAAA,IACpB,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,cAAc;AAAA,IACd,cAAc;AAAA,IACd,QAAQ;AAAA,EACV;AAAA,EACA,gBAAgB;AAAA,IACd,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,cAAc;AAAA,IACd,cAAc;AAAA,IACd,QAAQ;AAAA,EACV;AAAA,EACA,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,cAAc;AAAA,IACd,cAAc;AAAA,IACd,QAAQ;AAAA,EACV;AAAA,EACA,YAAY;AAAA,IACV,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,cAAc;AAAA,IACd,cAAc;AAAA,IACd,QAAQ;AAAA,EACV;AAAA,EACA,aAAa;AAAA,IACX,MAAM;AAAA,IACN,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,cAAc;AAAA,IACd,cAAc;AAAA,IACd,QAAQ;AAAA,EACV;AACF;AAsBO,SAAS,uBAAuB,MAAiB,cAAuC;AAC7F,MAAI,CAAC,gBAAgB,KAAK,UAAU,MAAM;AACxC,WAAO,EAAE,GAAG,GAAG,GAAG,GAAG,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO;AAAA,EAC9D;AACA,QAAM,QAAmB,KAAK;AAC9B,SAAO;AAAA,IACL,GAAG,CAAC,MAAM;AAAA,IACV,GAAG,CAAC,MAAM;AAAA,IACV,OAAO,KAAK,QAAQ,MAAM,OAAO,MAAM;AAAA,IACvC,QAAQ,KAAK,SAAS,MAAM,MAAM,MAAM;AAAA,EAC1C;AACF;AAWO,SAAS,eAAe,QAAsB,UAAkC;AACrF,MAAI,OAAO,gBAAgB,MAAM;AAC/B,QAAI,SAAS,SAAS,GAAG;AACvB,YAAM,IAAI,MAAM,2CAA2C,SAAS,KAAK,EAAE;AAAA,IAC7E;AACA,WAAO,OAAO,cAAc,SAAS;AAAA,EACvC;AACA,SAAO,OAAO;AAChB;AAmBO,IAAM,kBAA4C;AAAA,EACvD,EAAE,IAAI,eAAsB,OAAO,yBAAoC,OAAO,MAAM,QAAQ,KAAK;AAAA,EACjG,EAAE,IAAI,sBAAsB,OAAO,2BAAoC,OAAO,MAAM,QAAQ,KAAK;AAAA,EACjG,EAAE,IAAI,mBAAsB,OAAO,wBAAoC,OAAO,MAAM,QAAQ,KAAK;AAAA,EACjG,EAAE,IAAI,sBAAsB,OAAO,2BAAoC,OAAO,MAAM,QAAQ,IAAK;AAAA,EACjG,EAAE,IAAI,kBAAsB,OAAO,uBAAoC,OAAO,MAAM,QAAQ,KAAK;AAAA,EACjG,EAAE,IAAI,eAAsB,OAAO,4BAAoC,OAAO,MAAM,QAAQ,IAAK;AAAA,EACjG,EAAE,IAAI,sBAAsB,OAAO,wCAAmC,OAAO,MAAM,QAAQ,KAAK;AAClG;AAMO,SAAS,qBAAqB,IAA2B;AAC9D,QAAM,QAAQ,gBAAgB,KAAK,CAAC,MAAM,EAAE,OAAO,EAAE;AACrD,MAAI,CAAC,OAAO;AACV,UAAM,IAAI;AAAA,MACR,2BAA2B,EAAE,uBAAkB,gBAAgB,IAAI,CAAC,MAAM,EAAE,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA,IAC5F;AAAA,EACF;AACA,SAAO;AACT;AAyCO,SAAS,0BACd,MACA,eACoB;AACpB,MAAI,KAAK,SAAS,KAAK,KAAK,UAAU,GAAG;AACvC,UAAM,IAAI,MAAM,yCAAyC,KAAK,KAAK,OAAI,KAAK,MAAM,EAAE;AAAA,EACtF;AAEA,QAAM,SAAS,cAAc,QAAQ,KAAK;AAC1C,QAAM,SAAS,cAAc,SAAS,KAAK;AAC3C,QAAM,aAAa,KAAK,IAAI,QAAQ,MAAM;AAE1C,QAAM,YAAY,KAAK,QAAQ;AAC/B,QAAM,YAAY,KAAK,SAAS;AAIhC,QAAM,WAAY,cAAc,QAAQ,aAAa,IAAK;AAC1D,QAAM,WAAY,cAAc,SAAS,aAAa,IAAK;AAE3D,SAAO,EAAE,YAAY,SAAS,SAAS,KAAK,UAAU;AACxD;AAmBO,SAAS,qBAAqB,MAA6D;AAChG,MAAI,KAAK,UAAU,MAAM;AACvB,WAAO,EAAE,GAAG,GAAG,GAAG,GAAG,OAAO,KAAK,OAAO,QAAQ,KAAK,OAAO;AAAA,EAC9D;AACA,QAAM,QAAmB,KAAK;AAC9B,SAAO;AAAA,IACL,GAAG,CAAC,MAAM;AAAA,IACV,GAAG,CAAC,MAAM;AAAA,IACV,OAAO,KAAK,QAAQ,MAAM,OAAO,MAAM;AAAA,IACvC,QAAQ,KAAK,SAAS,MAAM,MAAM,MAAM;AAAA,EAC1C;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/harness/index.ts"],"sourcesContent":["/**\n * Coding-agent harness selection — taxonomy, coercion, and the session-lock invariant.\n *\n * A \"harness\" is the coding-agent CLI a sandbox drives (opencode / codex /\n * claude-code / …). The shell governs WHICH harness a chat session uses and\n * enforces that a session is LOCKED to the harness it started with — the model\n * may change mid-session, the harness may not (swapping it mid-session would\n * orphan the session's running agent state). Every product otherwise hand-rolls\n * this and hard-codes a single harness; this is the one place the rule lives.\n *\n * Substrate-free: the harness list mirrors the sandbox SDK's `BackendType` as a\n * plain string union (no sandbox dependency). The consumer owns storage — which\n * harness a workspace defaults to, which one a session locked — and maps the\n * resolved value onto the SDK's `backend.type`.\n *\n * Harness↔model COMPATIBILITY (which models a harness can run, snapping) is NOT defined here — it\n * comes from `@tangle-network/agent-interface`, the single source of truth shared with the\n * sandbox-ui pickers and the cli-bridge backends. This module owns the harness TAXONOMY + the\n * session lock.\n */\n\nimport {\n harnessSupportsModel,\n modelProvider,\n preferredHarnessForModel,\n snapHarnessToModel as aiSnapHarnessToModel,\n snapModelToHarness as aiSnapModelToHarness,\n type HarnessType,\n} from '@tangle-network/agent-interface'\n\n/** The known coding-agent backends. Mirrors `@tangle-network/sandbox`'s\n * `BackendType`; kept structural so this module needs no sandbox dependency. */\nexport const KNOWN_HARNESSES = [\n 'opencode',\n 'claude-code',\n 'nanoclaw',\n 'kimi-code',\n 'codex',\n 'amp',\n 'factory-droids',\n 'pi',\n 'hermes',\n 'forge',\n 'openclaw',\n 'acp',\n 'cursor',\n 'cli-base',\n] as const\n\nexport type Harness = (typeof KNOWN_HARNESSES)[number]\n\nexport const DEFAULT_HARNESS: Harness = 'opencode'\n\nconst HARNESS_SET: ReadonlySet<string> = new Set(KNOWN_HARNESSES)\n\nexport function isHarness(value: unknown): value is Harness {\n return typeof value === 'string' && HARNESS_SET.has(value)\n}\n\n/** Coerce an arbitrary value to a known harness, falling back (default `opencode`). */\nexport function coerceHarness(value: unknown, fallback: Harness = DEFAULT_HARNESS): Harness {\n return isHarness(value) ? value : fallback\n}\n\nexport interface ResolveSessionHarnessInput {\n /** The harness already locked to this session (recorded at its first turn). */\n sessionHarness?: unknown\n /** The harness requested now — a new session's choice, or a turn's attempt to switch. */\n requested?: unknown\n /** The workspace's default harness, used only when starting a fresh session. */\n workspaceDefault?: unknown\n /** Final fallback when nothing else resolves (default `opencode`). */\n fallback?: Harness\n}\n\nexport interface ResolvedSessionHarness {\n /** The harness to actually run — the locked one when the session already has it. */\n harness: Harness\n /** True when the session already had a locked harness (this turn did not pick it). */\n locked: boolean\n /** True when `requested` differs from the locked harness — a forbidden mid-session\n * swap the caller should reject or warn on. The lock always wins regardless. */\n swapAttempted: boolean\n}\n\n/**\n * Resolve the harness for a turn, enforcing the session lock.\n *\n * - **Session already started** (`sessionHarness` is a known harness): that harness\n * wins (`locked: true`); a differing `requested` sets `swapAttempted` so the caller\n * can reject the swap. The model is a separate per-turn concern and is unaffected.\n * - **Fresh session**: pick `requested → workspaceDefault → fallback`. The caller\n * persists the result as the session's lock for every subsequent turn.\n */\nexport function resolveSessionHarness(input: ResolveSessionHarnessInput = {}): ResolvedSessionHarness {\n const fallback = input.fallback ?? DEFAULT_HARNESS\n if (isHarness(input.sessionHarness)) {\n const locked = input.sessionHarness\n const swapAttempted = isHarness(input.requested) && input.requested !== locked\n return { harness: locked, locked: true, swapAttempted }\n }\n const harness = coerceHarness(input.requested, coerceHarness(input.workspaceDefault, fallback))\n return { harness, locked: false, swapAttempted: false }\n}\n\n/**\n * Harness ↔ model compatibility + snapping — delegated to `@tangle-network/agent-interface`.\n *\n * agent-app's `Harness` taxonomy is a superset of agent-interface's `HarnessType` (it carries\n * `forge`/`cursor`, which agent-interface doesn't list). Those extra runners have no provider lock\n * there, so they resolve as router-backed (any model) — the correct behavior — which makes the\n * `as HarnessType` casts safe. The snap helpers only ever return a vendor-locked harness or\n * `opencode`, all of which are valid `Harness` values.\n */\n\nexport { modelProvider }\n\n/** Provider-less ids (sentinels like \"default\", or a session's own config) are\n * compatible everywhere — every harness honors its own configuration. */\nexport function isModelCompatibleWithHarness(harness: Harness, modelId: string): boolean {\n return harnessSupportsModel(harness as HarnessType, modelId)\n}\n\n/** Keep `modelId` when the harness can run it; else the harness's best compatible\n * catalog id (preferred patterns in order, highest version). When nothing in the\n * catalog fits, return the original so the caller sees the incompatibility. */\nexport function snapModelToHarness(harness: Harness, modelId: string, canonicalIds: readonly string[]): string {\n return aiSnapModelToHarness(harness as HarnessType, modelId, canonicalIds)\n}\n\n/** Keep the harness when it can run `modelId`; else the model's native harness\n * (anthropic → claude-code, openai → codex, moonshot → kimi-code), falling back to opencode. */\nexport function snapHarnessToModel(harness: Harness, modelId: string): Harness {\n return aiSnapHarnessToModel(harness as HarnessType, modelId) as Harness\n}\n\n/** Fail-loud server guard: throw when a harness is asked to run a model it can't.\n * Call before dispatching a sandbox turn so a bypassed UI can't reach the sidecar\n * with an incompatible pair. */\nexport function assertHarnessModelCompatible(harness: Harness, modelId: string): void {\n if (!isModelCompatibleWithHarness(harness, modelId)) {\n const provider = modelProvider(modelId)\n const native = preferredHarnessForModel(modelId)\n throw new Error(\n `Harness \"${harness}\" cannot run model \"${modelId}\" (provider \"${provider}\"). ` +\n `Use ${native ?? 'a router-backed harness (opencode)'} or an allowed model.`,\n )\n }\n}\n"],"mappings":";AAqBA;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA,sBAAsB;AAAA,EACtB,sBAAsB;AAAA,OAEjB;AAIA,IAAM,kBAAkB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;AAIO,IAAM,kBAA2B;AAExC,IAAM,cAAmC,IAAI,IAAI,eAAe;AAEzD,SAAS,UAAU,OAAkC;AAC1D,SAAO,OAAO,UAAU,YAAY,YAAY,IAAI,KAAK;AAC3D;AAGO,SAAS,cAAc,OAAgB,WAAoB,iBAA0B;AAC1F,SAAO,UAAU,KAAK,IAAI,QAAQ;AACpC;AAgCO,SAAS,sBAAsB,QAAoC,CAAC,GAA2B;AACpG,QAAM,WAAW,MAAM,YAAY;AACnC,MAAI,UAAU,MAAM,cAAc,GAAG;AACnC,UAAM,SAAS,MAAM;AACrB,UAAM,gBAAgB,UAAU,MAAM,SAAS,KAAK,MAAM,cAAc;AACxE,WAAO,EAAE,SAAS,QAAQ,QAAQ,MAAM,cAAc;AAAA,EACxD;AACA,QAAM,UAAU,cAAc,MAAM,WAAW,cAAc,MAAM,kBAAkB,QAAQ,CAAC;AAC9F,SAAO,EAAE,SAAS,QAAQ,OAAO,eAAe,MAAM;AACxD;AAgBO,SAAS,6BAA6B,SAAkB,SAA0B;AACvF,SAAO,qBAAqB,SAAwB,OAAO;AAC7D;AAKO,SAAS,mBAAmB,SAAkB,SAAiB,cAAyC;AAC7G,SAAO,qBAAqB,SAAwB,SAAS,YAAY;AAC3E;AAIO,SAAS,mBAAmB,SAAkB,SAA0B;AAC7E,SAAO,qBAAqB,SAAwB,OAAO;AAC7D;AAKO,SAAS,6BAA6B,SAAkB,SAAuB;AACpF,MAAI,CAAC,6BAA6B,SAAS,OAAO,GAAG;AACnD,UAAM,WAAW,cAAc,OAAO;AACtC,UAAM,SAAS,yBAAyB,OAAO;AAC/C,UAAM,IAAI;AAAA,MACR,YAAY,OAAO,uBAAuB,OAAO,gBAAgB,QAAQ,WAChE,UAAU,oCAAoC;AAAA,IACzD;AAAA,EACF;AACF;","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/studio/generation.ts"],"sourcesContent":["// Minimal structural view of a connected integration — `isDestinationConnected`\n// only reads the connection status and the provider/connector identifiers, so\n// the logic layer declares its own shape instead of depending on the\n// design-system package. A sandbox-ui `IntegrationConnection` is assignable to\n// this; `connectorId` is optional because the engine type leaves it undefined\n// for connections established without a named connector.\nexport interface StudioIntegrationConnection {\n status: string\n providerId: string\n connectorId?: string\n}\n\nexport type GenerationType = 'image' | 'video' | 'speech' | 'avatar' | 'transcription'\n\nexport type GenerationStatus = 'pending' | 'running' | 'succeeded' | 'failed'\n\nexport type MediaModelStatus = 'available' | 'limited' | 'unavailable'\n\nexport interface Generation {\n id: string\n type: string\n prompt: string\n result: string | null\n model: string | null\n cost: number | null\n createdAt: Date | null\n metadata: Record<string, unknown> | null\n}\n\nexport interface MediaModelOption {\n id: string\n name: string\n provider?: string\n type: GenerationType\n status: MediaModelStatus\n reason?: string\n}\n\nexport interface MediaModelCatalogResponse {\n defaults: Record<GenerationType, string>\n models: Record<GenerationType, MediaModelOption[]>\n error?: string\n}\n\nexport interface PublishPackage {\n caption: string\n description: string\n mentions: string[]\n destinations: string[]\n cadence: string\n workflowDraft: boolean\n evalContract: {\n artifactType: string\n deterministicChecks: string[]\n }\n}\n\nexport interface PublishDestination {\n id: string\n label: string\n providerIds: string[]\n fields: string\n}\n\n// Order drives the type filter tabs and the composer segmented control\nexport const GENERATION_TYPES: readonly GenerationType[] = ['image', 'video', 'avatar', 'speech', 'transcription']\n\nexport function isGenerationType(value: string): value is GenerationType {\n return (GENERATION_TYPES as readonly string[]).includes(value)\n}\n\nexport const DESTINATIONS: PublishDestination[] = [\n { id: 'instagram', label: 'Instagram', providerIds: ['instagram'], fields: 'Caption, hashtags, crop' },\n { id: 'tiktok', label: 'TikTok', providerIds: ['tiktok'], fields: 'Caption, audio note, vertical video' },\n { id: 'youtube-shorts', label: 'YouTube Shorts', providerIds: ['youtube'], fields: 'Title, description, vertical video' },\n { id: 'linkedin', label: 'LinkedIn', providerIds: ['linkedin'], fields: 'Caption, link, creator/company page' },\n { id: 'x', label: 'X', providerIds: ['twitter'], fields: 'Short copy, mentions, thread option' },\n]\n\nexport const CADENCES = ['Manual approval', 'Publish now', 'Daily creative drop', 'Weekly series']\n\nexport const MIN_IMAGE_COUNT = 1\nexport const MAX_IMAGE_COUNT = 4\n\nexport function relativeTime(date: Date | null): string {\n if (!date) return ''\n const now = Date.now()\n const diff = now - new Date(date).getTime()\n const minutes = Math.floor(diff / 60000)\n if (minutes < 1) return 'just now'\n if (minutes < 60) return `${minutes}m ago`\n const hours = Math.floor(minutes / 60)\n if (hours < 24) return `${hours}h ago`\n const days = Math.floor(hours / 24)\n if (days < 7) return `${days}d ago`\n return new Date(date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })\n}\n\nexport function outputPathFor(type: GenerationType): string {\n if (type === 'image') return 'generated/images'\n if (type === 'video') return 'generated/videos'\n if (type === 'avatar') return 'generated/avatars'\n if (type === 'speech') return 'generated/audio'\n return 'generated/transcripts'\n}\n\nexport function generationVaultPath(generation: Generation): string | null {\n const value = generation.metadata?.vaultPath\n return typeof value === 'string' && value.trim() ? value.trim() : null\n}\n\nexport function buildPublishPackage({\n caption,\n postDescription,\n mentions,\n cadence,\n destinations,\n}: {\n caption: string\n postDescription: string\n mentions: string\n cadence: string\n destinations: string[]\n}): PublishPackage | null {\n const trimmedCaption = caption.trim()\n const trimmedDescription = postDescription.trim()\n const parsedMentions = mentions.split(',').map((item) => item.trim()).filter(Boolean)\n const selectedDestinations = destinations.filter(Boolean)\n const trimmedCadence = cadence.trim() || 'Manual approval'\n const hasPublishContent = Boolean(\n trimmedCaption\n || trimmedDescription\n || parsedMentions.length > 0\n || selectedDestinations.length > 0\n || trimmedCadence !== 'Manual approval',\n )\n if (!hasPublishContent) return null\n\n return {\n caption: trimmedCaption,\n description: trimmedDescription,\n mentions: parsedMentions,\n destinations: selectedDestinations,\n cadence: trimmedCadence,\n workflowDraft: trimmedCadence !== 'Manual approval',\n evalContract: {\n artifactType: 'publish_package',\n deterministicChecks: ['has_asset', 'has_destination', 'has_caption_or_description', 'has_cadence'],\n },\n }\n}\n\nexport function isPublishPackage(value: unknown): value is { caption?: string; description?: string; mentions?: string[]; destinations?: string[]; cadence?: string } {\n if (!value || typeof value !== 'object') return false\n const publishPackage = value as {\n caption?: unknown\n description?: unknown\n mentions?: unknown\n destinations?: unknown\n cadence?: unknown\n }\n const destinations = Array.isArray(publishPackage.destinations) ? publishPackage.destinations.filter(Boolean) : []\n const mentions = Array.isArray(publishPackage.mentions) ? publishPackage.mentions.filter(Boolean) : []\n return Boolean(\n destinations.length > 0\n || mentions.length > 0\n || (typeof publishPackage.caption === 'string' && publishPackage.caption.trim())\n || (typeof publishPackage.description === 'string' && publishPackage.description.trim())\n || (typeof publishPackage.cadence === 'string' && publishPackage.cadence.trim() && publishPackage.cadence.trim() !== 'Manual approval'),\n )\n}\n\nexport function isDestinationConnected(\n destination: PublishDestination,\n connections: StudioIntegrationConnection[],\n): boolean {\n return connections.some((connection) => connection.status === 'connected'\n && (destination.providerIds.includes(connection.providerId)\n || (connection.connectorId !== undefined && destination.providerIds.includes(connection.connectorId))))\n}\n\nexport function selectedModelsWithDefaults(\n current: Partial<Record<GenerationType, string>>,\n catalog: MediaModelCatalogResponse,\n): Partial<Record<GenerationType, string>> {\n const next = { ...current }\n for (const key of GENERATION_TYPES) {\n const models = catalog.models[key] ?? []\n const currentOption = models.find((model) => model.id === next[key])\n // Reset when: no selection, selection not in catalog, or selection is unavailable.\n // This ensures the Generate button is never stuck disabled when routeable\n // models exist but the stored default isn't one of them.\n if (!next[key] || !currentOption || currentOption.status === 'unavailable') {\n next[key] = preferredModelId(key, catalog) ?? ''\n }\n }\n return next\n}\n\nexport function preferredModelId(type: GenerationType, catalog: MediaModelCatalogResponse | null): string | undefined {\n if (!catalog) return undefined\n const models = catalog.models[type] ?? []\n const preferred = catalog.defaults[type]\n return models.find((model) => model.id === preferred)?.id\n ?? models.find((model) => model.status !== 'unavailable')?.id\n ?? models[0]?.id\n}\n\nexport function modelMessage(model: MediaModelOption | undefined, loading: boolean, count: number): string | null {\n if (loading) return 'Loading media models...'\n if (count === 0) return 'No models are available for this media type.'\n if (!model) return 'Select a model.'\n if (model.status === 'unavailable') return model.reason ?? 'This model is not configured.'\n if (model.status === 'limited') return model.reason ? `Limited: ${model.reason}` : 'Limited availability.'\n return null\n}\n\nexport interface GenerationRequestFields {\n workspaceId: string\n clientRequestId: string\n type: GenerationType\n model: string\n prompt: string\n negativePrompt: string\n outputPath: string\n publishPackage: PublishPackage | null\n image: { size: string; quality: string; count: number }\n video: { duration: string; resolution: string; aspectRatio: string; referenceImageUrl: string }\n speech: { voice: string }\n avatar: { audioUrl: string; imageUrl: string; avatarId: string }\n transcription: { audioUrl: string; language: string; responseFormat: string; temperature: string }\n}\n\n// image.count must already be normalized — it is also the optimistic-card count on the caller side\nexport function buildGenerationRequestBody(fields: GenerationRequestFields): Record<string, unknown> {\n const body: Record<string, unknown> = {\n workspaceId: fields.workspaceId,\n clientRequestId: fields.clientRequestId,\n type: fields.type,\n model: fields.model,\n prompt: fields.prompt.trim(),\n negativePrompt: fields.negativePrompt.trim() || undefined,\n outputPath: fields.outputPath.trim() || undefined,\n }\n if (fields.publishPackage) body.publishPackage = fields.publishPackage\n if (fields.type === 'image') Object.assign(body, {\n size: fields.image.size,\n quality: fields.image.quality,\n n: fields.image.count,\n })\n if (fields.type === 'video') {\n const duration = Number(fields.video.duration)\n Object.assign(body, {\n // omit (let the API default) rather than serialize NaN → null on bad input\n duration: Number.isFinite(duration) ? duration : undefined,\n resolution: fields.video.resolution,\n aspectRatio: fields.video.aspectRatio.trim() || undefined,\n referenceImageUrl: fields.video.referenceImageUrl.trim() || undefined,\n })\n }\n if (fields.type === 'speech') Object.assign(body, { voice: fields.speech.voice })\n if (fields.type === 'avatar') Object.assign(body, {\n audioUrl: fields.avatar.audioUrl.trim(),\n imageUrl: fields.avatar.imageUrl.trim() || undefined,\n avatarId: fields.avatar.avatarId.trim() || undefined,\n })\n if (fields.type === 'transcription') {\n const temperature = Number(fields.transcription.temperature)\n Object.assign(body, {\n audioUrl: fields.transcription.audioUrl.trim(),\n language: fields.transcription.language.trim() || undefined,\n responseFormat: fields.transcription.responseFormat,\n // omit (let the API default) rather than serialize NaN → null on bad input\n temperature: Number.isFinite(temperature) ? temperature : undefined,\n })\n }\n return body\n}\n\nexport function generationStatus(generation: Generation): GenerationStatus {\n const metadata = generation.metadata ?? {}\n const status = typeof metadata.generationStatus === 'string' ? metadata.generationStatus : ''\n if (status === 'pending' || status === 'running' || status === 'failed' || status === 'succeeded') return status\n return generation.result ? 'succeeded' : 'pending'\n}\n\nexport function generationError(generation: Generation): string | null {\n const metadata = generation.metadata ?? {}\n if (typeof metadata.providerError === 'string' && metadata.providerError.trim()) {\n return userSafeGenerationMessage(metadata.providerError)\n }\n if (typeof metadata.storageError === 'string' && metadata.storageError.trim()) {\n return metadata.storageError\n }\n return null\n}\n\nfunction generationClientRequestId(generation: Generation): string | null {\n const metadata = generation.metadata ?? {}\n return typeof metadata.clientRequestId === 'string' && metadata.clientRequestId.trim()\n ? metadata.clientRequestId\n : null\n}\n\nfunction generationBatchSlotKey(generation: Generation): string | null {\n const metadata = generation.metadata ?? {}\n const batchId = typeof metadata.batchId === 'string' && metadata.batchId.trim() ? metadata.batchId : null\n return batchId && typeof metadata.outputIndex === 'number'\n ? `${batchId}:${metadata.outputIndex}`\n : null\n}\n\nexport function generationMergeKey(generation: Generation): string | null {\n return generationBatchSlotKey(generation) ?? generationClientRequestId(generation)\n}\n\nexport function mergeLiveGeneration(current: Generation[], generation: Generation): Generation[] {\n const mergeKey = generationMergeKey(generation)\n const existingIndex = current.findIndex((item) => (\n item.id === generation.id\n || (mergeKey && generationMergeKey(item) === mergeKey)\n ))\n if (existingIndex === -1) return [generation, ...current]\n\n const next = [...current]\n next[existingIndex] = generation\n return next\n}\n\n// Overlay in-flight `live` generations on the loader's rows: each live row leads\n// (prefer the matching loader row by merge key / id so it carries the freshest\n// server state), then the remaining loader rows that no live row already\n// represents — deduped by BOTH id and merge key so a server row and its\n// optimistic twin never both appear. Returns `loader` unchanged when nothing is\n// live. Drives the canvas, library, and polling off one list.\nexport function mergeLoaderAndLive(loader: Generation[], live: Generation[]): Generation[] {\n if (live.length === 0) return loader\n const leading = live.map((generation) => {\n const mergeKey = generationMergeKey(generation)\n return mergeKey\n ? loader.find((gen) => generationMergeKey(gen) === mergeKey) ?? generation\n : loader.find((gen) => gen.id === generation.id) ?? generation\n })\n const leadingIds = new Set(leading.map((gen) => gen.id))\n const leadingMergeKeys = new Set(leading\n .map((gen) => generationMergeKey(gen))\n .filter((id): id is string => Boolean(id)))\n return [\n ...leading,\n ...loader.filter((gen) => (\n !leadingIds.has(gen.id)\n && !leadingMergeKeys.has(generationMergeKey(gen) ?? '')\n )),\n ]\n}\n\nexport function isLocalGeneration(generation: Generation): boolean {\n return generation.id.startsWith('local-')\n}\n\nfunction generationOutputIndex(generation: Generation): number {\n const value = generation.metadata?.outputIndex\n return typeof value === 'number' ? value : 0\n}\n\n// The most-recent run: all generations sharing the leading item's clientRequestId\n// (a multi-image batch), ordered by output slot. Falls back to the single leading\n// item when no request id is present. Drives the result canvas.\nexport function latestBatchOf(generations: Generation[]): Generation[] {\n const first = generations[0]\n if (!first) return []\n const key = generationClientRequestId(first)\n const batch = key\n ? generations.filter((generation) => generationClientRequestId(generation) === key)\n : [first]\n return [...batch].sort((a, b) => generationOutputIndex(a) - generationOutputIndex(b))\n}\n\nexport function userSafeGenerationMessage(message?: string): string {\n if (!message) return 'Generation failed'\n if (/Tangle API key is invalid or expired/i.test(message)) return message\n if (/(api[_ -]?key|secret|token|credential|env|configured|configuration)/i.test(message)) {\n return 'Generation failed'\n }\n return message\n}\n\nexport function optimisticGeneration({\n type,\n prompt,\n model,\n clientRequestId,\n outputIndex,\n outputCount,\n}: {\n type: GenerationType\n prompt: string\n model?: string\n clientRequestId: string\n outputIndex?: number\n outputCount?: number\n}): Generation {\n const batchId = outputIndex == null ? undefined : clientRequestId\n return {\n id: outputIndex == null ? `local-${clientRequestId}` : `local-${clientRequestId}-${outputIndex}`,\n type,\n prompt,\n result: null,\n model: model ?? null,\n cost: null,\n createdAt: new Date(),\n metadata: {\n generationStatus: 'pending',\n provider: type,\n clientRequestId,\n batchId,\n outputIndex,\n outputCount,\n },\n }\n}\n\nexport function failedOptimisticGeneration(generation: Generation): Generation {\n return {\n ...generation,\n metadata: {\n ...(generation.metadata ?? {}),\n generationStatus: 'failed',\n providerError: 'Generation failed',\n },\n }\n}\n\nexport function normalizeImageCount(value: unknown): number {\n const numeric = typeof value === 'number' ? value : Number(value)\n if (!Number.isFinite(numeric)) return MIN_IMAGE_COUNT\n return Math.min(Math.max(Math.trunc(numeric), MIN_IMAGE_COUNT), MAX_IMAGE_COUNT)\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/tools/errors.ts","../src/tools/openai.ts","../src/tools/registry.ts","../src/tools/dispatch.ts","../src/tools/runtime.ts"],"sourcesContent":["/** A correctable bad-input error a tool handler throws; the HTTP layer maps it\n * to a 4xx with the code, the runtime layer to a failed tool_result. So the\n * agent learns the call failed and can correct, instead of a silent success. */\nexport class ToolInputError extends Error {\n constructor(\n public code: string,\n message: string,\n public status = 400,\n ) {\n super(message)\n this.name = 'ToolInputError'\n }\n}\n","import type { AppToolTaxonomy, BuildAppToolsOptions } from './types'\n\n/** The four canonical app-tool names. Stable identifiers the model calls in\n * both the sandbox (MCP server name) and runtime (function-tool name) paths. */\nexport const APP_TOOL_NAMES = ['submit_proposal', 'schedule_followup', 'render_ui', 'add_citation'] as const\nexport type AppToolName = (typeof APP_TOOL_NAMES)[number]\n\nconst NAME_SET = new Set<string>(APP_TOOL_NAMES)\nexport function isAppToolName(name: string): name is AppToolName {\n return NAME_SET.has(name)\n}\n\n/** A minimal OpenAI Chat Completions function-tool shape — structurally\n * compatible with `@tangle-network/agent-runtime`'s `OpenAIChatTool` without\n * importing it (keeps this package runtime-free). */\nexport interface OpenAIFunctionTool {\n type: 'function'\n function: {\n name: string\n description: string\n parameters: Record<string, unknown>\n }\n}\n\n/**\n * Build the four app tools in OpenAI function-tool shape. `submit_proposal`'s\n * `type` enum is the product's {@link AppToolTaxonomy.proposalTypes}; the\n * model-facing descriptions and the follow-up priority enum default to the\n * Tangle reference vocabulary and can be retuned via {@link BuildAppToolsOptions}\n * (the tool names + JSON-Schema shapes stay fixed — they are mechanism). Pass\n * the result to the agent-runtime backend's `tools`.\n */\nexport function buildAppToolOpenAITools(\n taxonomy: AppToolTaxonomy,\n opts?: BuildAppToolsOptions,\n): OpenAIFunctionTool[] {\n const d = opts?.descriptions\n const priorityValues = opts?.priorityValues ?? ['low', 'medium', 'high']\n const custom: OpenAIFunctionTool[] = (opts?.customTools ?? []).map((t) => ({\n type: 'function',\n function: { name: t.name, description: t.description, parameters: t.parameters },\n }))\n return [\n {\n type: 'function',\n function: {\n name: 'submit_proposal',\n description:\n d?.submit_proposal ??\n 'Route a regulated or state-changing action to a human for approval (a recommendation, contacting/soliciting a contact, outreach, a record/account change, scheduling). Queues it for a named certified human to approve before it executes.',\n parameters: {\n type: 'object',\n properties: {\n type: { type: 'string', enum: [...taxonomy.proposalTypes] },\n title: { type: 'string', description: 'Short label for the approval queue.' },\n description: { type: 'string', description: 'The full drafted message/recommendation, with sources.' },\n },\n required: ['type', 'title'],\n },\n },\n },\n {\n type: 'function',\n function: {\n name: 'schedule_followup',\n description:\n d?.schedule_followup ??\n 'Register a dated cadence step (a reminder, chase, or check-in) on the follow-up calendar. Executes immediately.',\n parameters: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n dueDate: { type: 'string', description: 'ISO date YYYY-MM-DD.' },\n priority: { type: 'string', enum: [...priorityValues] },\n },\n required: ['title', 'dueDate'],\n },\n },\n },\n {\n type: 'function',\n function: {\n name: 'render_ui',\n description:\n d?.render_ui ??\n 'Show a generated view live in the workspace. Validates the OpenUI JSON and persists the artifact. Executes immediately.',\n parameters: {\n type: 'object',\n properties: {\n title: { type: 'string' },\n schema: { type: 'object', description: 'The OpenUI JSON object.' },\n },\n required: ['title', 'schema'],\n },\n },\n },\n {\n type: 'function',\n function: {\n name: 'add_citation',\n description:\n d?.add_citation ??\n 'Anchor a grounding reference: the exact quote from a file backing a figure or claim. Verifies the quote appears in the file. Executes immediately.',\n parameters: {\n type: 'object',\n properties: {\n path: { type: 'string', description: 'The vault file path.' },\n quote: { type: 'string', description: 'The exact text from it.' },\n },\n required: ['path', 'quote'],\n },\n },\n },\n ...custom,\n ]\n}\n","import { isAppToolName } from './openai'\nimport type { AppToolContext } from './types'\nimport type { OpenAIFunctionTool } from './openai'\n\n/**\n * A product-defined app tool — the open registration seam.\n *\n * The four built-ins (`submit_proposal`/`schedule_followup`/`render_ui`/\n * `add_citation`) are mechanism and stay hard-typed. This is how a product adds\n * a fifth+ tool (e.g. gtm-agent's `set_config`) WITHOUT forking the shell: the\n * `name` + JSON-Schema `parameters` are what the model sees, and `execute` is\n * dispatched through the SAME validation/outcome path as the built-ins — a\n * thrown {@link ToolInputError} becomes a correctable 4xx, any other throw an\n * internal error, and a tool call never silently \"succeeds\" without its effect.\n */\nexport interface AppToolDefinition<Args = Record<string, unknown>> {\n /** Stable identifier the model calls (and the MCP server name). Must not\n * collide with a built-in app tool. */\n name: string\n /** Model-facing description. */\n description: string\n /** JSON-Schema for the parameters (the OpenAI `function.parameters` object). */\n parameters: Record<string, unknown>\n /** Default route path the per-turn MCP server / HTTP handler is mounted at.\n * Overridable per call via `paths`/`buildHttpMcpServer`. */\n path?: string\n /** Fulfil the call; the return value is the tool result the model sees.\n * `ctx` is the trusted per-turn identity (never from tool args). */\n execute: (args: Args, ctx: AppToolContext) => Promise<unknown> | unknown\n}\n\n/**\n * Validate + brand a product tool definition. Throws when the name is empty or\n * collides with a built-in (those are reserved mechanism). Identity otherwise —\n * call it at module scope so a bad definition fails at boot, not first use.\n */\nexport function defineAppTool<Args = Record<string, unknown>>(def: AppToolDefinition<Args>): AppToolDefinition<Args> {\n const name = def.name?.trim()\n if (!name) throw new Error('defineAppTool: name is required')\n if (isAppToolName(name)) throw new Error(`defineAppTool: \"${name}\" is a built-in app tool — choose a different name`)\n if (typeof def.execute !== 'function') throw new Error(`defineAppTool: \"${name}\" needs an execute() handler`)\n return def\n}\n\n/** The OpenAI function-tool def for a custom tool — appended to the built-ins by\n * `buildAppToolOpenAITools`. */\nexport function customToolToOpenAI(def: AppToolDefinition): OpenAIFunctionTool {\n return { type: 'function', function: { name: def.name, description: def.description, parameters: def.parameters } }\n}\n\n/** Find a registered custom tool by the name the model called. */\nexport function findCustomTool(\n name: string,\n tools: readonly AppToolDefinition[] | undefined,\n): AppToolDefinition | undefined {\n return tools?.find((t) => t.name === name)\n}\n","import { ToolInputError } from './errors'\nimport { isAppToolName } from './openai'\nimport { findCustomTool, type AppToolDefinition } from './registry'\nimport type {\n AppToolContext,\n AppToolHandlers,\n AppToolOutcome,\n AppToolProducedEvent,\n AppToolTaxonomy,\n} from './types'\n\nexport interface DispatchOptions {\n handlers: AppToolHandlers\n taxonomy: AppToolTaxonomy\n /** Product-registered tools beyond the four built-ins. A called name that is\n * not a built-in is dispatched to the matching {@link AppToolDefinition.execute}\n * through this same validation/outcome path. */\n customTools?: readonly AppToolDefinition[]\n /** Per-call approval policy. When provided it OVERRIDES the static\n * `taxonomy.regulatedTypes` membership check, so products can gate by\n * cost threshold, environment, or first-use instead of always/never.\n * Fail-closed: a predicate that throws counts as \"approval required\". */\n needsApproval?: (type: string, args: { title: string; description: string | null }, ctx: AppToolContext) => boolean | Promise<boolean>\n /** Called at the real side-effect site for proposals (proposal_created) and\n * generated views (artifact) so a consumer's completion oracle credits\n * persisted state. Omit when produced state isn't tracked. */\n onProduced?: (event: AppToolProducedEvent) => void\n}\n\n/**\n * The ONE place an app-tool call is validated, dispatched to the product's\n * handler, and turned into an {@link AppToolOutcome} + produced events. Shared\n * by the HTTP route layer and the agent-runtime executor so both paths apply\n * identical validation and identical side effects. A {@link ToolInputError}\n * (bad input the agent can correct) and any other throw both become\n * `{ ok: false }` — a tool call never silently \"succeeds\" without its effect.\n */\nexport async function dispatchAppTool(\n toolName: string,\n rawArgs: Record<string, unknown>,\n ctx: AppToolContext,\n opts: DispatchOptions,\n): Promise<AppToolOutcome> {\n try {\n if (!isAppToolName(toolName)) {\n const custom = findCustomTool(toolName, opts.customTools)\n if (!custom) return { ok: false, code: 'unknown_tool', message: `${toolName} is not an app tool.` }\n // Custom tools own their own arg validation (their execute throws\n // ToolInputError for correctable input); the outer try/catch maps it.\n const result = await custom.execute(rawArgs, ctx)\n return { ok: true, result }\n }\n\n if (toolName === 'submit_proposal') {\n const type = String(rawArgs.type ?? '').trim()\n const title = String(rawArgs.title ?? '').trim()\n if (!type || !opts.taxonomy.proposalTypes.includes(type)) {\n return { ok: false, code: 'invalid_type', message: `type must be one of: ${opts.taxonomy.proposalTypes.join(', ')}.` }\n }\n if (!title) return { ok: false, code: 'missing_title', message: 'title is required.' }\n const description = rawArgs.description == null ? null : String(rawArgs.description)\n // Approval policy runs BEFORE the handler so the decision can gate the\n // side effect itself, not merely re-label it afterwards.\n let regulated = opts.taxonomy.regulatedTypes.includes(type)\n if (opts.needsApproval) {\n try {\n regulated = await opts.needsApproval(type, { title, description }, ctx)\n } catch {\n regulated = true // fail-closed: a broken policy means approval required\n }\n }\n const r = await opts.handlers.submitProposal({ type, title, description, regulated }, ctx)\n // Pass the handler's result through: products with immediate-execute\n // proposal types return status 'executed' plus their own fields\n // (e.g. datasetId) — the model must see what actually happened, not a\n // hard-coded \"queued for approval\".\n const { proposalId, deduped, status, ...extra } = r\n const effectiveStatus = status ?? 'queued_for_approval'\n opts.onProduced?.({\n type: 'proposal_created',\n proposalId,\n title,\n status: effectiveStatus === 'executed' ? 'executed' : 'pending',\n content: description ?? undefined,\n })\n return { ok: true, result: { ...extra, status: effectiveStatus, proposalId, deduped, regulated } }\n }\n\n if (toolName === 'schedule_followup') {\n const r = await opts.handlers.scheduleFollowup(\n { title: String(rawArgs.title ?? ''), dueDate: String(rawArgs.dueDate ?? ''), priority: rawArgs.priority as string | undefined },\n ctx,\n )\n return { ok: true, result: { followupId: r.id, dueDate: r.dueDate, deduped: r.deduped } }\n }\n\n if (toolName === 'render_ui') {\n const r = await opts.handlers.renderUi({ title: String(rawArgs.title ?? ''), schema: rawArgs.schema }, ctx)\n opts.onProduced?.({ type: 'artifact', path: r.path, content: r.content })\n return { ok: true, result: { path: r.path } }\n }\n\n // add_citation\n const r = await opts.handlers.addCitation(\n { path: String(rawArgs.path ?? ''), quote: String(rawArgs.quote ?? ''), label: rawArgs.label as string | undefined },\n ctx,\n )\n return { ok: true, result: { citationId: r.citationId, path: r.path } }\n } catch (err) {\n if (err instanceof ToolInputError) return { ok: false, code: err.code, message: err.message, status: err.status }\n return { ok: false, code: 'app_tool_error', message: err instanceof Error ? err.message : String(err), status: 500 }\n }\n}\n\n/** HTTP status for a failed outcome — the handler's `ToolInputError.status`\n * when present, else 400 for a validation reject. */\nexport function outcomeStatus(outcome: Extract<AppToolOutcome, { ok: false }>): number {\n return outcome.status ?? 400\n}\n","import { dispatchAppTool, type DispatchOptions } from './dispatch'\nimport type { AppToolContext, AppToolOutcome } from './types'\n\n/** Executes an app-tool call the model emits on the agent-runtime chat path.\n * Plug into `runChatThroughRuntime({ appToolExecutor })` (or any loop that\n * dispatches function tool_calls). */\nexport type AppToolRuntimeExecutor = (call: {\n toolName: string\n args: Record<string, unknown>\n}) => Promise<AppToolOutcome>\n\nexport interface RuntimeExecutorOptions extends DispatchOptions {\n /** The trusted per-turn context — supplied directly (not from headers), since\n * the runtime path has no HTTP request. */\n ctx: AppToolContext\n}\n\n/**\n * Build the runtime executor for one turn. The agent-runtime backend must also\n * advertise the tools (`buildAppToolOpenAITools(taxonomy)` on the backend's\n * `tools`) for the model to call them; this executor fulfils each call against\n * the product's handlers and emits produced events via `opts.onProduced`.\n */\nexport function createAppToolRuntimeExecutor(opts: RuntimeExecutorOptions): AppToolRuntimeExecutor {\n return ({ toolName, args }) => dispatchAppTool(toolName, args, opts.ctx, opts)\n}\n"],"mappings":"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","names":["r"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/intakes/model.ts"],"sourcesContent":["/**\n * Pure intake MODEL — the question-graph and the answer/completion algebra a\n * one-question-at-a-time interview runs on. Zero dependencies: no drizzle, no\n * env, no react, no I/O. The DB layer (`./intakes/drizzle`), the handlers\n * (`./intakes/api`) and the React surface (`./intakes-react`) all build on\n * these functions; this leaf imports nothing back, so a consumer can pull just\n * the intake math.\n *\n * An intake is an ordered list of questions, optionally branching: a question\n * can declare `next` as a function of the answers so far, so the next prompt\n * depends on what was said (e.g. \"do you have a website?\" → no → skip the URL\n * question). Traversal is pure and deterministic — `nextQuestion(graph, answers)`\n * folds the answers into the single question to ask next (or null = done), and\n * `isComplete` is true exactly when every REQUIRED reachable question has a\n * valid answer.\n *\n * Answers are a flat `Record<questionId, value>`. Each question declares an\n * answer `type` that `validateAnswer` checks against — the same validation the\n * UI runs before advancing and the store runs before persisting, so an invalid\n * answer can never enter the payload.\n */\n\n/** The kinds of answer a question accepts. */\nexport type IntakeAnswerType =\n | 'text'\n | 'long-text'\n | 'single-select'\n | 'multi-select'\n | 'boolean'\n | 'number'\n | 'url'\n | 'email'\n\n/** A selectable option for single/multi-select questions. */\nexport interface IntakeOption {\n value: string\n label: string\n}\n\n/** A flat map of answers keyed by question id. */\nexport type IntakeAnswers = Record<string, IntakeAnswerValue>\n\n/** Any value an answer can hold; the type is validated per-question. */\nexport type IntakeAnswerValue = string | string[] | number | boolean | null\n\n/**\n * One question in the graph. `next` (optional) makes the graph branch: given\n * the answers so far it returns the id of the question to ask next, or null to\n * end the interview early. With no `next`, traversal falls through to the next\n * question in declaration order.\n */\nexport interface IntakeQuestion {\n id: string\n /** The prompt the interviewer asks. */\n prompt: string\n type: IntakeAnswerType\n /** Required questions gate completion; optional ones may be skipped. */\n required?: boolean\n /** Help text shown under the prompt. */\n help?: string\n /** Options for single/multi-select questions. */\n options?: IntakeOption[]\n /** Min length (text) or min value (number); inclusive. */\n min?: number\n /** Max length (text) or max value (number); inclusive. */\n max?: number\n /**\n * Branch override: given the answers so far, the id of the next question\n * (or null to end early). Omit for linear flow (next in declaration order).\n */\n next?(answers: IntakeAnswers): string | null\n}\n\n/** The intake definition: an ordered, addressable set of questions. */\nexport interface IntakeGraph {\n /** Stable id for the intake definition (e.g. 'user-onboarding-v1'). */\n id: string\n /** Human title shown at the top of the interview. */\n title: string\n /** Optional one-line description. */\n description?: string\n /** Questions in declaration (default traversal) order. */\n questions: IntakeQuestion[]\n}\n\n/** The reason an answer failed validation. */\nexport type AnswerRejectionReason =\n | 'required'\n | 'wrong-type'\n | 'too-short'\n | 'too-long'\n | 'too-small'\n | 'too-large'\n | 'not-an-option'\n | 'invalid-url'\n | 'invalid-email'\n | 'unknown-question'\n\nexport interface AnswerValidationResult {\n ok: boolean\n reason?: AnswerRejectionReason\n}\n\nconst URL_PATTERN = /^https?:\\/\\/[^\\s/$.?#].[^\\s]*$/i\nconst EMAIL_PATTERN = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/\n\n/** Look up a question by id, or null if the graph has none. */\nexport function getQuestion(graph: IntakeGraph, questionId: string): IntakeQuestion | null {\n return graph.questions.find((q) => q.id === questionId) ?? null\n}\n\n/** True when an answer value is present (not null/undefined/empty). */\nexport function hasAnswer(value: IntakeAnswerValue | undefined): boolean {\n if (value == null) return false\n if (typeof value === 'string') return value.trim().length > 0\n if (Array.isArray(value)) return value.length > 0\n return true\n}\n\n/**\n * Validate one answer against its question. Pure: no I/O, no graph mutation.\n * An empty value is rejected as `required` only when the question is required;\n * an empty value for an optional question is OK (the user skipped it).\n */\nexport function validateAnswer(\n question: IntakeQuestion,\n value: IntakeAnswerValue | undefined,\n): AnswerValidationResult {\n if (!hasAnswer(value)) {\n return question.required ? { ok: false, reason: 'required' } : { ok: true }\n }\n\n switch (question.type) {\n case 'text':\n case 'long-text': {\n if (typeof value !== 'string') return { ok: false, reason: 'wrong-type' }\n const len = value.trim().length\n if (question.min != null && len < question.min) return { ok: false, reason: 'too-short' }\n if (question.max != null && len > question.max) return { ok: false, reason: 'too-long' }\n return { ok: true }\n }\n case 'url': {\n if (typeof value !== 'string') return { ok: false, reason: 'wrong-type' }\n return URL_PATTERN.test(value.trim()) ? { ok: true } : { ok: false, reason: 'invalid-url' }\n }\n case 'email': {\n if (typeof value !== 'string') return { ok: false, reason: 'wrong-type' }\n return EMAIL_PATTERN.test(value.trim()) ? { ok: true } : { ok: false, reason: 'invalid-email' }\n }\n case 'number': {\n if (typeof value !== 'number' || !Number.isFinite(value)) return { ok: false, reason: 'wrong-type' }\n if (question.min != null && value < question.min) return { ok: false, reason: 'too-small' }\n if (question.max != null && value > question.max) return { ok: false, reason: 'too-large' }\n return { ok: true }\n }\n case 'boolean': {\n return typeof value === 'boolean' ? { ok: true } : { ok: false, reason: 'wrong-type' }\n }\n case 'single-select': {\n if (typeof value !== 'string') return { ok: false, reason: 'wrong-type' }\n return optionExists(question, value) ? { ok: true } : { ok: false, reason: 'not-an-option' }\n }\n case 'multi-select': {\n if (!Array.isArray(value)) return { ok: false, reason: 'wrong-type' }\n if (!value.every((v) => typeof v === 'string' && optionExists(question, v))) {\n return { ok: false, reason: 'not-an-option' }\n }\n if (question.min != null && value.length < question.min) return { ok: false, reason: 'too-short' }\n if (question.max != null && value.length > question.max) return { ok: false, reason: 'too-long' }\n return { ok: true }\n }\n default:\n return { ok: false, reason: 'wrong-type' }\n }\n}\n\n/**\n * The single question to ask next given the answers so far, or null when the\n * interview is done. Walks the graph from the first question, following each\n * question's `next` branch (or declaration order) and stopping at the first\n * reachable question that has no valid answer yet. Deterministic and pure.\n *\n * A `next` that points at a missing id, or returns null, ends the walk — so a\n * malformed graph terminates rather than looping. A visited-set guards against\n * a cyclic `next`.\n */\nexport function nextQuestion(graph: IntakeGraph, answers: IntakeAnswers): IntakeQuestion | null {\n if (graph.questions.length === 0) return null\n const visited = new Set<string>()\n let current: IntakeQuestion | null = graph.questions[0] ?? null\n\n while (current) {\n if (visited.has(current.id)) return null\n visited.add(current.id)\n\n const answer = answers[current.id]\n const validity = validateAnswer(current, answer)\n if (!validity.ok || (current.required && !hasAnswer(answer))) {\n return current\n }\n\n current = advance(graph, current, answers)\n }\n\n return null\n}\n\n/**\n * True when every REQUIRED question reachable under the current answers has a\n * valid answer. Pure: it replays the same traversal `nextQuestion` uses and is\n * complete exactly when that traversal has no question left to ask.\n */\nexport function isComplete(graph: IntakeGraph, answers: IntakeAnswers): boolean {\n return nextQuestion(graph, answers) === null\n}\n\n/**\n * Progress as answered-vs-total over the REACHABLE required questions under the\n * current answers — what a progress bar renders. Optional questions don't count\n * toward the denominator (they never block completion).\n */\nexport function intakeProgress(graph: IntakeGraph, answers: IntakeAnswers): { answered: number; total: number } {\n const reachable = reachableQuestions(graph, answers)\n const required = reachable.filter((q) => q.required)\n const answered = required.filter((q) => validateAnswer(q, answers[q.id]).ok && hasAnswer(answers[q.id])).length\n return { answered, total: required.length }\n}\n\n/** The questions reachable under the current answers, in traversal order. */\nexport function reachableQuestions(graph: IntakeGraph, answers: IntakeAnswers): IntakeQuestion[] {\n if (graph.questions.length === 0) return []\n const visited = new Set<string>()\n const out: IntakeQuestion[] = []\n let current: IntakeQuestion | null = graph.questions[0] ?? null\n\n while (current) {\n if (visited.has(current.id)) break\n visited.add(current.id)\n out.push(current)\n current = advance(graph, current, answers)\n }\n return out\n}\n\nfunction advance(graph: IntakeGraph, current: IntakeQuestion, answers: IntakeAnswers): IntakeQuestion | null {\n if (current.next) {\n const nextId = current.next(answers)\n return nextId == null ? null : getQuestion(graph, nextId)\n }\n const index = graph.questions.indexOf(current)\n return graph.questions[index + 1] ?? null\n}\n\nfunction optionExists(question: IntakeQuestion, value: string): boolean {\n return (question.options ?? []).some((option) => option.value === value)\n}\n"],"mappings":";AAuGA,IAAM,cAAc;AACpB,IAAM,gBAAgB;AAGf,SAAS,YAAY,OAAoB,YAA2C;AACzF,SAAO,MAAM,UAAU,KAAK,CAAC,MAAM,EAAE,OAAO,UAAU,KAAK;AAC7D;AAGO,SAAS,UAAU,OAA+C;AACvE,MAAI,SAAS,KAAM,QAAO;AAC1B,MAAI,OAAO,UAAU,SAAU,QAAO,MAAM,KAAK,EAAE,SAAS;AAC5D,MAAI,MAAM,QAAQ,KAAK,EAAG,QAAO,MAAM,SAAS;AAChD,SAAO;AACT;AAOO,SAAS,eACd,UACA,OACwB;AACxB,MAAI,CAAC,UAAU,KAAK,GAAG;AACrB,WAAO,SAAS,WAAW,EAAE,IAAI,OAAO,QAAQ,WAAW,IAAI,EAAE,IAAI,KAAK;AAAA,EAC5E;AAEA,UAAQ,SAAS,MAAM;AAAA,IACrB,KAAK;AAAA,IACL,KAAK,aAAa;AAChB,UAAI,OAAO,UAAU,SAAU,QAAO,EAAE,IAAI,OAAO,QAAQ,aAAa;AACxE,YAAM,MAAM,MAAM,KAAK,EAAE;AACzB,UAAI,SAAS,OAAO,QAAQ,MAAM,SAAS,IAAK,QAAO,EAAE,IAAI,OAAO,QAAQ,YAAY;AACxF,UAAI,SAAS,OAAO,QAAQ,MAAM,SAAS,IAAK,QAAO,EAAE,IAAI,OAAO,QAAQ,WAAW;AACvF,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AAAA,IACA,KAAK,OAAO;AACV,UAAI,OAAO,UAAU,SAAU,QAAO,EAAE,IAAI,OAAO,QAAQ,aAAa;AACxE,aAAO,YAAY,KAAK,MAAM,KAAK,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,IAAI,OAAO,QAAQ,cAAc;AAAA,IAC5F;AAAA,IACA,KAAK,SAAS;AACZ,UAAI,OAAO,UAAU,SAAU,QAAO,EAAE,IAAI,OAAO,QAAQ,aAAa;AACxE,aAAO,cAAc,KAAK,MAAM,KAAK,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,IAAI,OAAO,QAAQ,gBAAgB;AAAA,IAChG;AAAA,IACA,KAAK,UAAU;AACb,UAAI,OAAO,UAAU,YAAY,CAAC,OAAO,SAAS,KAAK,EAAG,QAAO,EAAE,IAAI,OAAO,QAAQ,aAAa;AACnG,UAAI,SAAS,OAAO,QAAQ,QAAQ,SAAS,IAAK,QAAO,EAAE,IAAI,OAAO,QAAQ,YAAY;AAC1F,UAAI,SAAS,OAAO,QAAQ,QAAQ,SAAS,IAAK,QAAO,EAAE,IAAI,OAAO,QAAQ,YAAY;AAC1F,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AAAA,IACA,KAAK,WAAW;AACd,aAAO,OAAO,UAAU,YAAY,EAAE,IAAI,KAAK,IAAI,EAAE,IAAI,OAAO,QAAQ,aAAa;AAAA,IACvF;AAAA,IACA,KAAK,iBAAiB;AACpB,UAAI,OAAO,UAAU,SAAU,QAAO,EAAE,IAAI,OAAO,QAAQ,aAAa;AACxE,aAAO,aAAa,UAAU,KAAK,IAAI,EAAE,IAAI,KAAK,IAAI,EAAE,IAAI,OAAO,QAAQ,gBAAgB;AAAA,IAC7F;AAAA,IACA,KAAK,gBAAgB;AACnB,UAAI,CAAC,MAAM,QAAQ,KAAK,EAAG,QAAO,EAAE,IAAI,OAAO,QAAQ,aAAa;AACpE,UAAI,CAAC,MAAM,MAAM,CAAC,MAAM,OAAO,MAAM,YAAY,aAAa,UAAU,CAAC,CAAC,GAAG;AAC3E,eAAO,EAAE,IAAI,OAAO,QAAQ,gBAAgB;AAAA,MAC9C;AACA,UAAI,SAAS,OAAO,QAAQ,MAAM,SAAS,SAAS,IAAK,QAAO,EAAE,IAAI,OAAO,QAAQ,YAAY;AACjG,UAAI,SAAS,OAAO,QAAQ,MAAM,SAAS,SAAS,IAAK,QAAO,EAAE,IAAI,OAAO,QAAQ,WAAW;AAChG,aAAO,EAAE,IAAI,KAAK;AAAA,IACpB;AAAA,IACA;AACE,aAAO,EAAE,IAAI,OAAO,QAAQ,aAAa;AAAA,EAC7C;AACF;AAYO,SAAS,aAAa,OAAoB,SAA+C;AAC9F,MAAI,MAAM,UAAU,WAAW,EAAG,QAAO;AACzC,QAAM,UAAU,oBAAI,IAAY;AAChC,MAAI,UAAiC,MAAM,UAAU,CAAC,KAAK;AAE3D,SAAO,SAAS;AACd,QAAI,QAAQ,IAAI,QAAQ,EAAE,EAAG,QAAO;AACpC,YAAQ,IAAI,QAAQ,EAAE;AAEtB,UAAM,SAAS,QAAQ,QAAQ,EAAE;AACjC,UAAM,WAAW,eAAe,SAAS,MAAM;AAC/C,QAAI,CAAC,SAAS,MAAO,QAAQ,YAAY,CAAC,UAAU,MAAM,GAAI;AAC5D,aAAO;AAAA,IACT;AAEA,cAAU,QAAQ,OAAO,SAAS,OAAO;AAAA,EAC3C;AAEA,SAAO;AACT;AAOO,SAAS,WAAW,OAAoB,SAAiC;AAC9E,SAAO,aAAa,OAAO,OAAO,MAAM;AAC1C;AAOO,SAAS,eAAe,OAAoB,SAA6D;AAC9G,QAAM,YAAY,mBAAmB,OAAO,OAAO;AACnD,QAAM,WAAW,UAAU,OAAO,CAAC,MAAM,EAAE,QAAQ;AACnD,QAAM,WAAW,SAAS,OAAO,CAAC,MAAM,eAAe,GAAG,QAAQ,EAAE,EAAE,CAAC,EAAE,MAAM,UAAU,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE;AACzG,SAAO,EAAE,UAAU,OAAO,SAAS,OAAO;AAC5C;AAGO,SAAS,mBAAmB,OAAoB,SAA0C;AAC/F,MAAI,MAAM,UAAU,WAAW,EAAG,QAAO,CAAC;AAC1C,QAAM,UAAU,oBAAI,IAAY;AAChC,QAAM,MAAwB,CAAC;AAC/B,MAAI,UAAiC,MAAM,UAAU,CAAC,KAAK;AAE3D,SAAO,SAAS;AACd,QAAI,QAAQ,IAAI,QAAQ,EAAE,EAAG;AAC7B,YAAQ,IAAI,QAAQ,EAAE;AACtB,QAAI,KAAK,OAAO;AAChB,cAAU,QAAQ,OAAO,SAAS,OAAO;AAAA,EAC3C;AACA,SAAO;AACT;AAEA,SAAS,QAAQ,OAAoB,SAAyB,SAA+C;AAC3G,MAAI,QAAQ,MAAM;AAChB,UAAM,SAAS,QAAQ,KAAK,OAAO;AACnC,WAAO,UAAU,OAAO,OAAO,YAAY,OAAO,MAAM;AAAA,EAC1D;AACA,QAAM,QAAQ,MAAM,UAAU,QAAQ,OAAO;AAC7C,SAAO,MAAM,UAAU,QAAQ,CAAC,KAAK;AACvC;AAEA,SAAS,aAAa,UAA0B,OAAwB;AACtE,UAAQ,SAAS,WAAW,CAAC,GAAG,KAAK,CAAC,WAAW,OAAO,UAAU,KAAK;AACzE;","names":[]}
|