@ashlr/hub 3.11.0 → 3.11.2
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/CHANGELOG.md +96 -0
- package/dist/authority-surface.json +12 -7
- package/dist/build-identity.json +2 -2
- package/dist/core/universe/builtins/preparation/manifest.json +1 -1
- package/dist/core/universe/builtins/preparation/preparation-bridge.mjs +5 -4
- package/dist/core/universe/builtins/preparation/preparation-verification-fixtures.mjs +4 -3
- package/dist/core/universe/campaign.js +9 -1
- package/dist/core/universe/campaign.js.map +1 -1
- package/dist/core/universe/runner.d.ts +3 -0
- package/dist/core/universe/runner.js +7 -3
- package/dist/core/universe/runner.js.map +1 -1
- package/dist/core/verse/compaction-point.d.ts +55 -0
- package/dist/core/verse/compaction-point.js +75 -0
- package/dist/core/verse/compaction-point.js.map +1 -0
- package/dist/core/verse/context-math.d.ts +1 -38
- package/dist/core/verse/context-math.js +5 -59
- package/dist/core/verse/context-math.js.map +1 -1
- package/dist/core/web/public/next/assets/{ActionMenu-CQvNJvHY.js → ActionMenu-_DhtX2gO.js} +2 -2
- package/dist/core/web/public/next/assets/{ActionMenu-CQvNJvHY.js.map → ActionMenu-_DhtX2gO.js.map} +1 -1
- package/dist/core/web/public/next/assets/Advanced-CAAfaBke.js +2 -0
- package/dist/core/web/public/next/assets/{Advanced-DEM93sKI.js.map → Advanced-CAAfaBke.js.map} +1 -1
- package/dist/core/web/public/next/assets/App-BAywyjE8.js +7 -0
- package/dist/core/web/public/next/assets/{App-CimXmK0L.js.map → App-BAywyjE8.js.map} +1 -1
- package/dist/core/web/public/next/assets/AppsSection-CUypKeog.js +2 -0
- package/dist/core/web/public/next/assets/{AppsSection-D6U-dSj6.js.map → AppsSection-CUypKeog.js.map} +1 -1
- package/dist/core/web/public/next/assets/{AreaTrend-LD9lCPkT.js → AreaTrend-CblDqC8Q.js} +2 -2
- package/dist/core/web/public/next/assets/{AreaTrend-LD9lCPkT.js.map → AreaTrend-CblDqC8Q.js.map} +1 -1
- package/dist/core/web/public/next/assets/AutonomySection-D3d8lYO3.js +2 -0
- package/dist/core/web/public/next/assets/{AutonomySection-3V9pUyxm.js.map → AutonomySection-D3d8lYO3.js.map} +1 -1
- package/dist/core/web/public/next/assets/BranchBar-DU0jAV1E.js +2 -0
- package/dist/core/web/public/next/assets/{BranchBar-U8UdpfEB.js.map → BranchBar-DU0jAV1E.js.map} +1 -1
- package/dist/core/web/public/next/assets/{BudgetControl-DOTlMSmW.js → BudgetControl-Mm1ZL4u6.js} +2 -2
- package/dist/core/web/public/next/assets/{BudgetControl-DOTlMSmW.js.map → BudgetControl-Mm1ZL4u6.js.map} +1 -1
- package/dist/core/web/public/next/assets/{BurnDown-x4PWH9JF.js → BurnDown-C1fgcFqo.js} +2 -2
- package/dist/core/web/public/next/assets/{BurnDown-x4PWH9JF.js.map → BurnDown-C1fgcFqo.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Button-DYv_KKxr.js → Button-DqgLUZvd.js} +2 -2
- package/dist/core/web/public/next/assets/{Button-DYv_KKxr.js.map → Button-DqgLUZvd.js.map} +1 -1
- package/dist/core/web/public/next/assets/CapacityStrip-nWFHvqFB.js +2 -0
- package/dist/core/web/public/next/assets/CapacityStrip-nWFHvqFB.js.map +1 -0
- package/dist/core/web/public/next/assets/ChatSection-DUZNpiVr.js +3 -0
- package/dist/core/web/public/next/assets/ChatSection-DUZNpiVr.js.map +1 -0
- package/dist/core/web/public/next/assets/{CloudBudgetForm-DJeJvBEu.js → CloudBudgetForm-2OW38o_C.js} +2 -2
- package/dist/core/web/public/next/assets/{CloudBudgetForm-DJeJvBEu.js.map → CloudBudgetForm-2OW38o_C.js.map} +1 -1
- package/dist/core/web/public/next/assets/CommandPalette-C5w2o1-r.js +2 -0
- package/dist/core/web/public/next/assets/{CommandPalette-FiD4N8XD.js.map → CommandPalette-C5w2o1-r.js.map} +1 -1
- package/dist/core/web/public/next/assets/CommandSection-Bx2knNqY.js +2 -0
- package/dist/core/web/public/next/assets/{CommandSection-DN8KwH-X.js.map → CommandSection-Bx2knNqY.js.map} +1 -1
- package/dist/core/web/public/next/assets/ConfirmDialog-B7ZwCQOX.js +2 -0
- package/dist/core/web/public/next/assets/ConfirmDialog-B7ZwCQOX.js.map +1 -0
- package/dist/core/web/public/next/assets/ConfirmDialog-Dx15x0Ho.css +1 -0
- package/dist/core/web/public/next/assets/Dialog-BpgcEW6b.js +2 -0
- package/dist/core/web/public/next/assets/Dialog-BpgcEW6b.js.map +1 -0
- package/dist/core/web/public/next/assets/Dialog-tQ8kFulD.css +1 -0
- package/dist/core/web/public/next/assets/{DiffPane-DLJ3gGGK.js → DiffPane-DpqRUtCx.js} +2 -2
- package/dist/core/web/public/next/assets/{DiffPane-DLJ3gGGK.js.map → DiffPane-DpqRUtCx.js.map} +1 -1
- package/dist/core/web/public/next/assets/{DiffViewer-B7NWPjIO.js → DiffViewer-CoV5-nUf.js} +2 -2
- package/dist/core/web/public/next/assets/{DiffViewer-B7NWPjIO.js.map → DiffViewer-CoV5-nUf.js.map} +1 -1
- package/dist/core/web/public/next/assets/DockHost-C0Pq7Jak.js +2 -0
- package/dist/core/web/public/next/assets/{DockHost-DvR-hPhZ.js.map → DockHost-C0Pq7Jak.js.map} +1 -1
- package/dist/core/web/public/next/assets/{EmptyState-DhNgDDrF.js → EmptyState-DYW29ncw.js} +2 -2
- package/dist/core/web/public/next/assets/{EmptyState-DhNgDDrF.js.map → EmptyState-DYW29ncw.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Epistemic-uARPnx9x.js → Epistemic-DVx6oHzp.js} +2 -2
- package/dist/core/web/public/next/assets/{Epistemic-uARPnx9x.js.map → Epistemic-DVx6oHzp.js.map} +1 -1
- package/dist/core/web/public/next/assets/FleetSection-DSETviPK.js +3 -0
- package/dist/core/web/public/next/assets/{FleetSection-D_zRBCub.js.map → FleetSection-DSETviPK.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Funnel-CL8uHONq.js → Funnel-DxiGqJrt.js} +2 -2
- package/dist/core/web/public/next/assets/{Funnel-CL8uHONq.js.map → Funnel-DxiGqJrt.js.map} +1 -1
- package/dist/core/web/public/next/assets/GearTray-C3I89sIt.js +2 -0
- package/dist/core/web/public/next/assets/{GearTray-C7k4l_BW.js.map → GearTray-C3I89sIt.js.map} +1 -1
- package/dist/core/web/public/next/assets/{GrowthSection-DwdNSGj8.js → GrowthSection-C3m2bPEz.js} +2 -2
- package/dist/core/web/public/next/assets/{GrowthSection-DwdNSGj8.js.map → GrowthSection-C3m2bPEz.js.map} +1 -1
- package/dist/core/web/public/next/assets/HandoffDialog-Bjb96a0N.js +2 -0
- package/dist/core/web/public/next/assets/{HandoffDialog-FPVOqOMN.js.map → HandoffDialog-Bjb96a0N.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Input-B9-VLcHS.js → Input-ekTehVaH.js} +2 -2
- package/dist/core/web/public/next/assets/{Input-B9-VLcHS.js.map → Input-ekTehVaH.js.map} +1 -1
- package/dist/core/web/public/next/assets/LeaderCard-85VBbbaZ.js +2 -0
- package/dist/core/web/public/next/assets/{LeaderCard-4R0BhDzT.js.map → LeaderCard-85VBbbaZ.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MatrixHeatmap-DiuushcS.js → MatrixHeatmap-Bo8j0QPC.js} +2 -2
- package/dist/core/web/public/next/assets/{MatrixHeatmap-DiuushcS.js.map → MatrixHeatmap-Bo8j0QPC.js.map} +1 -1
- package/dist/core/web/public/next/assets/McpSection-DvHFaVyp.js +2 -0
- package/dist/core/web/public/next/assets/{McpSection-CJ__OJKl.js.map → McpSection-DvHFaVyp.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MessageMarkdown-qWKyJvAQ.js → MessageMarkdown-fsY_ltVa.js} +3 -3
- package/dist/core/web/public/next/assets/{MessageMarkdown-qWKyJvAQ.js.map → MessageMarkdown-fsY_ltVa.js.map} +1 -1
- package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-x05TrsrW.js → MessageMarkdownRenderer-DDdtu82l.js} +2 -2
- package/dist/core/web/public/next/assets/{MessageMarkdownRenderer-x05TrsrW.js.map → MessageMarkdownRenderer-DDdtu82l.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Meter-D-hncpkO.js → Meter-CeGDXdDM.js} +2 -2
- package/dist/core/web/public/next/assets/{Meter-D-hncpkO.js.map → Meter-CeGDXdDM.js.map} +1 -1
- package/dist/core/web/public/next/assets/MindSection-CDVhHvXl.js +2 -0
- package/dist/core/web/public/next/assets/{MindSection-BjXHkQPS.js.map → MindSection-CDVhHvXl.js.map} +1 -1
- package/dist/core/web/public/next/assets/MutationTokenDialog-CrfJ0PdD.css +1 -0
- package/dist/core/web/public/next/assets/MutationTokenDialog-DTQzEOHR.js +2 -0
- package/dist/core/web/public/next/assets/MutationTokenDialog-DTQzEOHR.js.map +1 -0
- package/dist/core/web/public/next/assets/NeedsYouDrawer-6AuN0BF3.js +2 -0
- package/dist/core/web/public/next/assets/{NeedsYouDrawer-rSd1ZHAR.js.map → NeedsYouDrawer-6AuN0BF3.js.map} +1 -1
- package/dist/core/web/public/next/assets/{NeedsYouList-CG-nB1tr.js → NeedsYouList-ChnRmbSk.js} +2 -2
- package/dist/core/web/public/next/assets/{NeedsYouList-CG-nB1tr.js.map → NeedsYouList-ChnRmbSk.js.map} +1 -1
- package/dist/core/web/public/next/assets/NewChatDialog-B06E7sqg.js +3 -0
- package/dist/core/web/public/next/assets/{NewChatDialog-BeDQ1eW3.js.map → NewChatDialog-B06E7sqg.js.map} +1 -1
- package/dist/core/web/public/next/assets/OnboardingFlow-C2M3zov9.js +2 -0
- package/dist/core/web/public/next/assets/{OnboardingFlow-CkTZtt-m.js.map → OnboardingFlow-C2M3zov9.js.map} +1 -1
- package/dist/core/web/public/next/assets/PreviewPane-Bd6HsX1k.js +2 -0
- package/dist/core/web/public/next/assets/{PreviewPane-kzbbSK4u.js.map → PreviewPane-Bd6HsX1k.js.map} +1 -1
- package/dist/core/web/public/next/assets/ProviderLogo-Bz7KR5j3.js +2 -0
- package/dist/core/web/public/next/assets/ProviderLogo-Bz7KR5j3.js.map +1 -0
- package/dist/core/web/public/next/assets/{RailStatus-D-t2gPae.js → RailStatus-CctCuuLy.js} +2 -2
- package/dist/core/web/public/next/assets/{RailStatus-D-t2gPae.js.map → RailStatus-CctCuuLy.js.map} +1 -1
- package/dist/core/web/public/next/assets/{RefreshIndicator-BLnr9IOM.js → RefreshIndicator-CKY5BGdD.js} +2 -2
- package/dist/core/web/public/next/assets/{RefreshIndicator-BLnr9IOM.js.map → RefreshIndicator-CKY5BGdD.js.map} +1 -1
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-CM_8pJs6.css → ResourcePoolConsoleApp-BX9JLkT4.css} +1 -1
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BeG0gBif.js → ResourcePoolConsoleApp-CspIsW_G.js} +5 -5
- package/dist/core/web/public/next/assets/{ResourcePoolConsoleApp-BeG0gBif.js.map → ResourcePoolConsoleApp-CspIsW_G.js.map} +1 -1
- package/dist/core/web/public/next/assets/ResourcesChrome-B7fHZ6zX.js +3 -0
- package/dist/core/web/public/next/assets/ResourcesChrome-B7fHZ6zX.js.map +1 -0
- package/dist/core/web/public/next/assets/ResourcesChrome-uZEqC2EM.css +1 -0
- package/dist/core/web/public/next/assets/ResourcesDrawer-B7-N9n7-.js +2 -0
- package/dist/core/web/public/next/assets/ResourcesDrawer-B7-N9n7-.js.map +1 -0
- package/dist/core/web/public/next/assets/RouteErrorBoundary-C_B11J0s.js +2 -0
- package/dist/core/web/public/next/assets/RouteErrorBoundary-C_B11J0s.js.map +1 -0
- package/dist/core/web/public/next/assets/RouteErrorBoundary-DdxhTdZ6.css +1 -0
- package/dist/core/web/public/next/assets/{RunInCloudAction-C3LBp7C4.js → RunInCloudAction-_juw4EXd.js} +2 -2
- package/dist/core/web/public/next/assets/{RunInCloudAction-C3LBp7C4.js.map → RunInCloudAction-_juw4EXd.js.map} +1 -1
- package/dist/core/web/public/next/assets/SeatSelector-D_YXvkPy.js +3 -0
- package/dist/core/web/public/next/assets/{SeatSelector-DpvvbjNm.js.map → SeatSelector-D_YXvkPy.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Segmented-BL40udoO.js → Segmented-DnKP6rr7.js} +2 -2
- package/dist/core/web/public/next/assets/{Segmented-BL40udoO.js.map → Segmented-DnKP6rr7.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Select-FWLnAw_j.js → Select-DlbUupTX.js} +2 -2
- package/dist/core/web/public/next/assets/{Select-FWLnAw_j.js.map → Select-DlbUupTX.js.map} +1 -1
- package/dist/core/web/public/next/assets/SessionGate-DnuTZACL.js +2 -0
- package/dist/core/web/public/next/assets/SessionGate-DnuTZACL.js.map +1 -0
- package/dist/core/web/public/next/assets/SessionGate-DvMpXNpJ.css +1 -0
- package/dist/core/web/public/next/assets/SessionInsightChip-C33-k-Q5.js +2 -0
- package/dist/core/web/public/next/assets/{SessionInsightChip-CWye87Ju.js.map → SessionInsightChip-C33-k-Q5.js.map} +1 -1
- package/dist/core/web/public/next/assets/{SessionSearch-YvCHFUuN.js → SessionSearch-DhhR9Csg.js} +2 -2
- package/dist/core/web/public/next/assets/{SessionSearch-YvCHFUuN.js.map → SessionSearch-DhhR9Csg.js.map} +1 -1
- package/dist/core/web/public/next/assets/SettingsSection-C0xQvCz3.js +2 -0
- package/dist/core/web/public/next/assets/SettingsSection-C0xQvCz3.js.map +1 -0
- package/dist/core/web/public/next/assets/Sheet-BvNYOYv5.js +2 -0
- package/dist/core/web/public/next/assets/{Sheet-DZKcQXQg.js.map → Sheet-BvNYOYv5.js.map} +1 -1
- package/dist/core/web/public/next/assets/ShortcutsOverlay-1nUBVO-Z.js +2 -0
- package/dist/core/web/public/next/assets/{ShortcutsOverlay-BpDimdPq.js.map → ShortcutsOverlay-1nUBVO-Z.js.map} +1 -1
- package/dist/core/web/public/next/assets/Sidebar-YrVvvBNM.js +3 -0
- package/dist/core/web/public/next/assets/{Sidebar-BoBzBEFX.js.map → Sidebar-YrVvvBNM.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Slider-DUMo9HDD.js → Slider-B2c-dbQb.js} +2 -2
- package/dist/core/web/public/next/assets/{Slider-DUMo9HDD.js.map → Slider-B2c-dbQb.js.map} +1 -1
- package/dist/core/web/public/next/assets/{StatusBadge-BwnBNFQr.js → StatusBadge-Q9YYkijx.js} +2 -2
- package/dist/core/web/public/next/assets/{StatusBadge-BwnBNFQr.js.map → StatusBadge-Q9YYkijx.js.map} +1 -1
- package/dist/core/web/public/next/assets/{StepBand-PDXMIlRk.js → StepBand-Ci-C0GUG.js} +2 -2
- package/dist/core/web/public/next/assets/{StepBand-PDXMIlRk.js.map → StepBand-Ci-C0GUG.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Surface-B3TZVoER.js → Surface-DBovMq4P.js} +2 -2
- package/dist/core/web/public/next/assets/{Surface-B3TZVoER.js.map → Surface-DBovMq4P.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Swimlane-DR4OgON_.js → Swimlane-Cdc4TqDe.js} +2 -2
- package/dist/core/web/public/next/assets/{Swimlane-DR4OgON_.js.map → Swimlane-Cdc4TqDe.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Switch-CcR5Llk5.js → Switch-CUrvzO2k.js} +2 -2
- package/dist/core/web/public/next/assets/{Switch-CcR5Llk5.js.map → Switch-CUrvzO2k.js.map} +1 -1
- package/dist/core/web/public/next/assets/{Tag-_-K1h7Hk.js → Tag-Bv8u6APt.js} +2 -2
- package/dist/core/web/public/next/assets/{Tag-_-K1h7Hk.js.map → Tag-Bv8u6APt.js.map} +1 -1
- package/dist/core/web/public/next/assets/TerminalPane-BE4Xlqi5.js +7 -0
- package/dist/core/web/public/next/assets/{TerminalPane-_1aH4ndN.js.map → TerminalPane-BE4Xlqi5.js.map} +1 -1
- package/dist/core/web/public/next/assets/UniverseConsoleApp-BGwejf0m.js +2 -0
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-BUsq-inZ.js.map → UniverseConsoleApp-BGwejf0m.js.map} +1 -1
- package/dist/core/web/public/next/assets/{UniverseConsoleApp-Dh7A-hLD.css → UniverseConsoleApp-DfuT-wFw.css} +1 -1
- package/dist/core/web/public/next/assets/{UniverseView-Dogy_dsB.js → UniverseView-CgKa-WXC.js} +4 -4
- package/dist/core/web/public/next/assets/{UniverseView-Dogy_dsB.js.map → UniverseView-CgKa-WXC.js.map} +1 -1
- package/dist/core/web/public/next/assets/UsageSection-CazsHm1f.js +2 -0
- package/dist/core/web/public/next/assets/{UsageSection-DiX_dTyo.js.map → UsageSection-CazsHm1f.js.map} +1 -1
- package/dist/core/web/public/next/assets/VerseConsoleApp-CTF3Shs0.js +3 -0
- package/dist/core/web/public/next/assets/VerseConsoleApp-CTF3Shs0.js.map +1 -0
- package/dist/core/web/public/next/assets/Workspace-CgdyXiEV.js +10 -0
- package/dist/core/web/public/next/assets/Workspace-CgdyXiEV.js.map +1 -0
- package/dist/core/web/public/next/assets/Workspace-Dwri2jFJ.css +1 -0
- package/dist/core/web/public/next/assets/actions-MtxBGS5B.js +2 -0
- package/dist/core/web/public/next/assets/{actions-s2lfm_5k.js.map → actions-MtxBGS5B.js.map} +1 -1
- package/dist/core/web/public/next/assets/budget-queries-CP7hgR-p.js +2 -0
- package/dist/core/web/public/next/assets/{budget-queries-wUshcovN.js.map → budget-queries-CP7hgR-p.js.map} +1 -1
- package/dist/core/web/public/next/assets/{capacity-strip-model-Bj_9ztzs.js → capacity-strip-model-fHfu5cph.js} +2 -2
- package/dist/core/web/public/next/assets/{capacity-strip-model-Bj_9ztzs.js.map → capacity-strip-model-fHfu5cph.js.map} +1 -1
- package/dist/core/web/public/next/assets/{charts-CMSgAUKn.js → charts-Bis9WSiS.js} +2 -2
- package/dist/core/web/public/next/assets/{charts-CMSgAUKn.js.map → charts-Bis9WSiS.js.map} +1 -1
- package/dist/core/web/public/next/assets/cloud-queries-BsOepFz6.js +2 -0
- package/dist/core/web/public/next/assets/{cloud-queries-CnV1yaNG.js.map → cloud-queries-BsOepFz6.js.map} +1 -1
- package/dist/core/web/public/next/assets/{cloud.module-CiwatLj3.js → cloud.module-DoFnr8ZH.js} +2 -2
- package/dist/core/web/public/next/assets/{cloud.module-CiwatLj3.js.map → cloud.module-DoFnr8ZH.js.map} +1 -1
- package/dist/core/web/public/next/assets/{command-bus-DqimKCUY.js → command-bus-CMvbYP5K.js} +2 -2
- package/dist/core/web/public/next/assets/{command-bus-DqimKCUY.js.map → command-bus-CMvbYP5K.js.map} +1 -1
- package/dist/core/web/public/next/assets/command-catalog-Bb_PxC5w.js +2 -0
- package/dist/core/web/public/next/assets/command-catalog-Bb_PxC5w.js.map +1 -0
- package/dist/core/web/public/next/assets/compaction-point-VL9oegWS.js +2 -0
- package/dist/core/web/public/next/assets/compaction-point-VL9oegWS.js.map +1 -0
- package/dist/core/web/public/next/assets/{composer-keys-DBRymxQn.js → composer-keys-su0g1h9o.js} +2 -2
- package/dist/core/web/public/next/assets/{composer-keys-DBRymxQn.js.map → composer-keys-su0g1h9o.js.map} +1 -1
- package/dist/core/web/public/next/assets/context-model-Ci3Ch9t5.js +2 -0
- package/dist/core/web/public/next/assets/{context-model-Dwd49-dp.js.map → context-model-Ci3Ch9t5.js.map} +1 -1
- package/dist/core/web/public/next/assets/context-model-CjiJTnf1.js +2 -0
- package/dist/core/web/public/next/assets/{context-model-MT17V86_.js.map → context-model-CjiJTnf1.js.map} +1 -1
- package/dist/core/web/public/next/assets/context-queries-BDXT6LQ3.js +2 -0
- package/dist/core/web/public/next/assets/{context-queries-xVKIeynP.js.map → context-queries-BDXT6LQ3.js.map} +1 -1
- package/dist/core/web/public/next/assets/control-queries-Dv-h1Clu.js +2 -0
- package/dist/core/web/public/next/assets/{control-queries-DOZR-D8G.js.map → control-queries-Dv-h1Clu.js.map} +1 -1
- package/dist/core/web/public/next/assets/{dock-icons-8EtARwUm.js → dock-icons-t--Eugrd.js} +2 -2
- package/dist/core/web/public/next/assets/{dock-icons-8EtARwUm.js.map → dock-icons-t--Eugrd.js.map} +1 -1
- package/dist/core/web/public/next/assets/{dock-store-BA1cHOYA.js → dock-store-qIu7kSQE.js} +2 -2
- package/dist/core/web/public/next/assets/{dock-store-BA1cHOYA.js.map → dock-store-qIu7kSQE.js.map} +1 -1
- package/dist/core/web/public/next/assets/{fleet-model-CNe8Wh-s.js → fleet-model-IRbwfhkR.js} +2 -2
- package/dist/core/web/public/next/assets/{fleet-model-CNe8Wh-s.js.map → fleet-model-IRbwfhkR.js.map} +1 -1
- package/dist/core/web/public/next/assets/fleet-queries-7FVNByX1.js +2 -0
- package/dist/core/web/public/next/assets/{fleet-queries-BSdRlUwJ.js.map → fleet-queries-7FVNByX1.js.map} +1 -1
- package/dist/core/web/public/next/assets/focus-trap-QqQxQdvH.js +2 -0
- package/dist/core/web/public/next/assets/focus-trap-QqQxQdvH.js.map +1 -0
- package/dist/core/web/public/next/assets/{format-B8DAVcn2.js → format-CaMmVQCn.js} +2 -2
- package/dist/core/web/public/next/assets/{format-B8DAVcn2.js.map → format-CaMmVQCn.js.map} +1 -1
- package/dist/core/web/public/next/assets/git-model-B4S_GUNA.js +5 -0
- package/dist/core/web/public/next/assets/{git-model-CivuBY_D.js.map → git-model-B4S_GUNA.js.map} +1 -1
- package/dist/core/web/public/next/assets/global-BkNl4e6E.js +2 -0
- package/dist/core/web/public/next/assets/global-BkNl4e6E.js.map +1 -0
- package/dist/core/web/public/next/assets/global-BlI2BYa1.css +1 -0
- package/dist/core/web/public/next/assets/guarded-action-DHiL8tY5.js +3 -0
- package/dist/core/web/public/next/assets/guarded-action-DHiL8tY5.js.map +1 -0
- package/dist/core/web/public/next/assets/guarded-runners-DmGEkM2P.js +2 -0
- package/dist/core/web/public/next/assets/guarded-runners-DmGEkM2P.js.map +1 -0
- package/dist/core/web/public/next/assets/icon-base-NtFmP2yB.js +2 -0
- package/dist/core/web/public/next/assets/icon-base-NtFmP2yB.js.map +1 -0
- package/dist/core/web/public/next/assets/icons-C5EFt_Ts.js +2 -0
- package/dist/core/web/public/next/assets/icons-C5EFt_Ts.js.map +1 -0
- package/dist/core/web/public/next/assets/{index-oAAq8XJa.js → index-DbDw9UAs.js} +3 -3
- package/dist/core/web/public/next/assets/{index-oAAq8XJa.js.map → index-DbDw9UAs.js.map} +1 -1
- package/dist/core/web/public/next/assets/{live-model-DCw_IFRI.js → live-model-DEwrl7_A.js} +2 -2
- package/dist/core/web/public/next/assets/{live-model-DCw_IFRI.js.map → live-model-DEwrl7_A.js.map} +1 -1
- package/dist/core/web/public/next/assets/{mind.module-vnuJcwvD.js → mind.module-CLhqMQak.js} +2 -2
- package/dist/core/web/public/next/assets/{mind.module-vnuJcwvD.js.map → mind.module-CLhqMQak.js.map} +1 -1
- package/dist/core/web/public/next/assets/mutations-Kvzsg-iK.js +2 -0
- package/dist/core/web/public/next/assets/{mutations-Dpsql5t7.js.map → mutations-Kvzsg-iK.js.map} +1 -1
- package/dist/core/web/public/next/assets/nav-C0IunEYF.js +2 -0
- package/dist/core/web/public/next/assets/nav-C0IunEYF.js.map +1 -0
- package/dist/core/web/public/next/assets/overnight-queries-C_AfcL7q.js +2 -0
- package/dist/core/web/public/next/assets/{overnight-queries-X-44Gsw5.js.map → overnight-queries-C_AfcL7q.js.map} +1 -1
- package/dist/core/web/public/next/assets/preloaded-CctOitLu.js +2 -0
- package/dist/core/web/public/next/assets/preloaded-CctOitLu.js.map +1 -0
- package/dist/core/web/public/next/assets/primitives-CbAGCNKt.js +1 -0
- package/dist/core/web/public/next/assets/{queries-BA4rNGD-.js → queries-THD05gHP.js} +2 -2
- package/dist/core/web/public/next/assets/{queries-BA4rNGD-.js.map → queries-THD05gHP.js.map} +1 -1
- package/dist/core/web/public/next/assets/reasoning-pref-BVPu0SME.js +2 -0
- package/dist/core/web/public/next/assets/{reasoning-pref-B9-DJUXP.js.map → reasoning-pref-BVPu0SME.js.map} +1 -1
- package/dist/core/web/public/next/assets/reveal-anchor-wCT5b4m3.js +2 -0
- package/dist/core/web/public/next/assets/reveal-anchor-wCT5b4m3.js.map +1 -0
- package/dist/core/web/public/next/assets/seat-subscription-CR_VylsK.js +2 -0
- package/dist/core/web/public/next/assets/{seat-subscription-CH6fW4fJ.js.map → seat-subscription-CR_VylsK.js.map} +1 -1
- package/dist/core/web/public/next/assets/{section-visibility-qFqc_d4Z.js → section-visibility-B0xXUQJ0.js} +2 -2
- package/dist/core/web/public/next/assets/{section-visibility-qFqc_d4Z.js.map → section-visibility-B0xXUQJ0.js.map} +1 -1
- package/dist/core/web/public/next/assets/{sidebar-model-M1GaQU5l.js → sidebar-model-C5wVXGpb.js} +2 -2
- package/dist/core/web/public/next/assets/{sidebar-model-M1GaQU5l.js.map → sidebar-model-C5wVXGpb.js.map} +1 -1
- package/dist/core/web/public/next/assets/slots-DDd8cEoh.js +3 -0
- package/dist/core/web/public/next/assets/{slots-Ds9ZIqUL.js.map → slots-DDd8cEoh.js.map} +1 -1
- package/dist/core/web/public/next/assets/surface-data-BFxAlefI.js +2 -0
- package/dist/core/web/public/next/assets/{surface-data-J52iIGMD.js.map → surface-data-BFxAlefI.js.map} +1 -1
- package/dist/core/web/public/next/assets/surface-prefetch-CHusyO9q.js +2 -0
- package/dist/core/web/public/next/assets/{surface-prefetch-CgZ-LEJK.js.map → surface-prefetch-CHusyO9q.js.map} +1 -1
- package/dist/core/web/public/next/assets/{tasks-model-ent8f0jZ.js → tasks-model-DvkWveXs.js} +2 -2
- package/dist/core/web/public/next/assets/{tasks-model-ent8f0jZ.js.map → tasks-model-DvkWveXs.js.map} +1 -1
- package/dist/core/web/public/next/assets/{terminal-icons-DJTK7QpR.js → terminal-icons-Dp6pJgV2.js} +2 -2
- package/dist/core/web/public/next/assets/{terminal-icons-DJTK7QpR.js.map → terminal-icons-Dp6pJgV2.js.map} +1 -1
- package/dist/core/web/public/next/assets/{usage-queries-Bn8ccqyN.js → usage-queries-CUTmnegA.js} +2 -2
- package/dist/core/web/public/next/assets/{usage-queries-Bn8ccqyN.js.map → usage-queries-CUTmnegA.js.map} +1 -1
- package/dist/core/web/public/next/assets/use-guarded-action-UCX2u1t1.js +2 -0
- package/dist/core/web/public/next/assets/{use-guarded-action-C2HlSzkz.js.map → use-guarded-action-UCX2u1t1.js.map} +1 -1
- package/dist/core/web/public/next/assets/{use-ticker-BNc1H4ZC.js → use-ticker-OXAjob3y.js} +2 -2
- package/dist/core/web/public/next/assets/{use-ticker-BNc1H4ZC.js.map → use-ticker-OXAjob3y.js.map} +1 -1
- package/dist/core/web/public/next/assets/use-token-gate-CDDRAoKH.js +2 -0
- package/dist/core/web/public/next/assets/{use-token-gate-B3IJL6bf.js.map → use-token-gate-CDDRAoKH.js.map} +1 -1
- package/dist/core/web/public/next/assets/useChartWidth-BW6SwYIK.css +1 -0
- package/dist/core/web/public/next/assets/useChartWidth-D9M7kUw7.js +2 -0
- package/dist/core/web/public/next/assets/{useChartWidth-lZ9PrQn0.js.map → useChartWidth-D9M7kUw7.js.map} +1 -1
- package/dist/core/web/public/next/assets/{useSeatsRefresh-CG4jczeL.js → useSeatsRefresh-B2dCVcj9.js} +2 -2
- package/dist/core/web/public/next/assets/{useSeatsRefresh-CG4jczeL.js.map → useSeatsRefresh-B2dCVcj9.js.map} +1 -1
- package/dist/core/web/public/next/assets/useVerseTranscript-BL3CljSI.js +2 -0
- package/dist/core/web/public/next/assets/useVerseTranscript-BL3CljSI.js.map +1 -0
- package/dist/core/web/public/next/assets/verse-icons-DASWdYuE.js +2 -0
- package/dist/core/web/public/next/assets/verse-icons-DASWdYuE.js.map +1 -0
- package/dist/core/web/public/next/assets/verse-model-BQ0Y2CSr.js +2 -0
- package/dist/core/web/public/next/assets/verse-model-BQ0Y2CSr.js.map +1 -0
- package/dist/core/web/public/next/assets/verse-store-b_Lek_c5.js +3 -0
- package/dist/core/web/public/next/assets/verse-store-b_Lek_c5.js.map +1 -0
- package/dist/core/web/public/next/assets/verse-ui-store-D7YXi63q.js +3 -0
- package/dist/core/web/public/next/assets/{verse-ui-store-zafyzXKD.js.map → verse-ui-store-D7YXi63q.js.map} +1 -1
- package/dist/core/web/public/next/assets/{viewport-BEqGUoTA.js → viewport-BYGH5EgA.js} +2 -2
- package/dist/core/web/public/next/assets/{viewport-BEqGUoTA.js.map → viewport-BYGH5EgA.js.map} +1 -1
- package/dist/core/web/public/next/assets/warmup-AsqwWCUi.js +3 -0
- package/dist/core/web/public/next/assets/{warmup-BR6pxAKs.js.map → warmup-AsqwWCUi.js.map} +1 -1
- package/dist/core/web/public/next/assets/{workspace-model-D_tWD5Wt.js → workspace-model-CXorJr0G.js} +2 -2
- package/dist/core/web/public/next/assets/{workspace-model-D_tWD5Wt.js.map → workspace-model-CXorJr0G.js.map} +1 -1
- package/dist/core/web/public/next/index.html +1 -1
- package/dist/release-dependency-inventory.json +1 -1
- package/package.json +5 -2
- package/dist/cli/activation.d.ts +0 -41
- package/dist/cli/activation.js +0 -485
- package/dist/cli/activation.js.map +0 -1
- package/dist/core/fleet/generated-repair-resolution.d.ts +0 -36
- package/dist/core/fleet/generated-repair-resolution.js +0 -245
- package/dist/core/fleet/generated-repair-resolution.js.map +0 -1
- package/dist/core/local-runtime/llama/port.d.ts +0 -17
- package/dist/core/local-runtime/llama/port.js +0 -47
- package/dist/core/local-runtime/llama/port.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-host.d.ts +0 -54
- package/dist/core/local-runtime/llama/proxy-host.js +0 -159
- package/dist/core/local-runtime/llama/proxy-host.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-process.d.ts +0 -148
- package/dist/core/local-runtime/llama/proxy-process.js +0 -250
- package/dist/core/local-runtime/llama/proxy-process.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-record.d.ts +0 -40
- package/dist/core/local-runtime/llama/proxy-record.js +0 -133
- package/dist/core/local-runtime/llama/proxy-record.js.map +0 -1
- package/dist/core/local-runtime/llama/proxy-supervisor.d.ts +0 -118
- package/dist/core/local-runtime/llama/proxy-supervisor.js +0 -511
- package/dist/core/local-runtime/llama/proxy-supervisor.js.map +0 -1
- package/dist/core/web/public/next/assets/Advanced-DEM93sKI.js +0 -2
- package/dist/core/web/public/next/assets/App-CimXmK0L.js +0 -7
- package/dist/core/web/public/next/assets/AppsSection-D6U-dSj6.js +0 -2
- package/dist/core/web/public/next/assets/AutonomySection-3V9pUyxm.js +0 -2
- package/dist/core/web/public/next/assets/BranchBar-U8UdpfEB.js +0 -2
- package/dist/core/web/public/next/assets/CapacityStrip-C667yQe8.js +0 -2
- package/dist/core/web/public/next/assets/CapacityStrip-C667yQe8.js.map +0 -1
- package/dist/core/web/public/next/assets/ChatSection-CCcIPpOq.js +0 -3
- package/dist/core/web/public/next/assets/ChatSection-CCcIPpOq.js.map +0 -1
- package/dist/core/web/public/next/assets/CommandPalette-FiD4N8XD.js +0 -2
- package/dist/core/web/public/next/assets/CommandSection-DN8KwH-X.js +0 -2
- package/dist/core/web/public/next/assets/DockHost-DvR-hPhZ.js +0 -2
- package/dist/core/web/public/next/assets/FleetSection-D_zRBCub.js +0 -3
- package/dist/core/web/public/next/assets/GearTray-C7k4l_BW.js +0 -2
- package/dist/core/web/public/next/assets/HandoffDialog-FPVOqOMN.js +0 -2
- package/dist/core/web/public/next/assets/LeaderCard-4R0BhDzT.js +0 -2
- package/dist/core/web/public/next/assets/McpSection-CJ__OJKl.js +0 -2
- package/dist/core/web/public/next/assets/MindSection-BjXHkQPS.js +0 -2
- package/dist/core/web/public/next/assets/MutationTokenDialog-Cum_DqQZ.css +0 -1
- package/dist/core/web/public/next/assets/MutationTokenDialog-SkRxqV_I.js +0 -2
- package/dist/core/web/public/next/assets/MutationTokenDialog-SkRxqV_I.js.map +0 -1
- package/dist/core/web/public/next/assets/NeedsYouDrawer-rSd1ZHAR.js +0 -2
- package/dist/core/web/public/next/assets/NewChatDialog-BeDQ1eW3.js +0 -3
- package/dist/core/web/public/next/assets/OnboardingFlow-CkTZtt-m.js +0 -2
- package/dist/core/web/public/next/assets/PreviewPane-kzbbSK4u.js +0 -2
- package/dist/core/web/public/next/assets/ResourcesChrome-B3Z3O98t.css +0 -1
- package/dist/core/web/public/next/assets/ResourcesChrome-BzV6bkIa.js +0 -3
- package/dist/core/web/public/next/assets/ResourcesChrome-BzV6bkIa.js.map +0 -1
- package/dist/core/web/public/next/assets/ResourcesDrawer-o4ILHdep.js +0 -2
- package/dist/core/web/public/next/assets/ResourcesDrawer-o4ILHdep.js.map +0 -1
- package/dist/core/web/public/next/assets/RouteErrorBoundary-6RMd_aZg.css +0 -1
- package/dist/core/web/public/next/assets/RouteErrorBoundary-C5SxA2Dg.js +0 -2
- package/dist/core/web/public/next/assets/RouteErrorBoundary-C5SxA2Dg.js.map +0 -1
- package/dist/core/web/public/next/assets/SeatSelector-DpvvbjNm.js +0 -3
- package/dist/core/web/public/next/assets/SessionInsightChip-CWye87Ju.js +0 -2
- package/dist/core/web/public/next/assets/SettingsSection-Dr18upeE.js +0 -2
- package/dist/core/web/public/next/assets/SettingsSection-Dr18upeE.js.map +0 -1
- package/dist/core/web/public/next/assets/Sheet-DZKcQXQg.js +0 -2
- package/dist/core/web/public/next/assets/ShortcutsOverlay-BpDimdPq.js +0 -2
- package/dist/core/web/public/next/assets/Sidebar-BoBzBEFX.js +0 -3
- package/dist/core/web/public/next/assets/TerminalPane-_1aH4ndN.js +0 -7
- package/dist/core/web/public/next/assets/UniverseConsoleApp-BUsq-inZ.js +0 -2
- package/dist/core/web/public/next/assets/UsageSection-DiX_dTyo.js +0 -2
- package/dist/core/web/public/next/assets/VerseConsoleApp-WVeyJnW7.js +0 -3
- package/dist/core/web/public/next/assets/VerseConsoleApp-WVeyJnW7.js.map +0 -1
- package/dist/core/web/public/next/assets/Workspace-BYW9dm90.css +0 -1
- package/dist/core/web/public/next/assets/Workspace-BbxZHwQd.js +0 -10
- package/dist/core/web/public/next/assets/Workspace-BbxZHwQd.js.map +0 -1
- package/dist/core/web/public/next/assets/actions-s2lfm_5k.js +0 -2
- package/dist/core/web/public/next/assets/budget-queries-wUshcovN.js +0 -2
- package/dist/core/web/public/next/assets/cloud-queries-CnV1yaNG.js +0 -2
- package/dist/core/web/public/next/assets/command-catalog-BdtDH93o.js +0 -2
- package/dist/core/web/public/next/assets/command-catalog-BdtDH93o.js.map +0 -1
- package/dist/core/web/public/next/assets/context-model-Dwd49-dp.js +0 -2
- package/dist/core/web/public/next/assets/context-model-MT17V86_.js +0 -2
- package/dist/core/web/public/next/assets/context-queries-xVKIeynP.js +0 -2
- package/dist/core/web/public/next/assets/control-queries-DOZR-D8G.js +0 -2
- package/dist/core/web/public/next/assets/dock-panes-UKS-mXhr.js +0 -2
- package/dist/core/web/public/next/assets/dock-panes-UKS-mXhr.js.map +0 -1
- package/dist/core/web/public/next/assets/fleet-queries-BSdRlUwJ.js +0 -2
- package/dist/core/web/public/next/assets/git-model-CivuBY_D.js +0 -5
- package/dist/core/web/public/next/assets/global-Di2PUgj4.js +0 -2
- package/dist/core/web/public/next/assets/global-Di2PUgj4.js.map +0 -1
- package/dist/core/web/public/next/assets/global-_tOCFt9y.css +0 -1
- package/dist/core/web/public/next/assets/guarded-action-CvOHcqyH.js +0 -2
- package/dist/core/web/public/next/assets/guarded-action-CvOHcqyH.js.map +0 -1
- package/dist/core/web/public/next/assets/icons-B_yuAgdN.js +0 -2
- package/dist/core/web/public/next/assets/icons-B_yuAgdN.js.map +0 -1
- package/dist/core/web/public/next/assets/mutations-Dpsql5t7.js +0 -2
- package/dist/core/web/public/next/assets/overnight-queries-X-44Gsw5.js +0 -2
- package/dist/core/web/public/next/assets/primitives-BDIcUvob.js +0 -1
- package/dist/core/web/public/next/assets/reasoning-pref-B9-DJUXP.js +0 -2
- package/dist/core/web/public/next/assets/seat-subscription-CH6fW4fJ.js +0 -2
- package/dist/core/web/public/next/assets/slots-Ds9ZIqUL.js +0 -3
- package/dist/core/web/public/next/assets/surface-data-J52iIGMD.js +0 -2
- package/dist/core/web/public/next/assets/surface-prefetch-CgZ-LEJK.js +0 -2
- package/dist/core/web/public/next/assets/use-guarded-action-C2HlSzkz.js +0 -2
- package/dist/core/web/public/next/assets/use-token-gate-B3IJL6bf.js +0 -2
- package/dist/core/web/public/next/assets/useActivity-Bk3XgXjL.js +0 -2
- package/dist/core/web/public/next/assets/useActivity-Bk3XgXjL.js.map +0 -1
- package/dist/core/web/public/next/assets/useChartWidth-BAtVgn2n.css +0 -1
- package/dist/core/web/public/next/assets/useChartWidth-lZ9PrQn0.js +0 -2
- package/dist/core/web/public/next/assets/verse-model-D7NZQFAR.js +0 -2
- package/dist/core/web/public/next/assets/verse-model-D7NZQFAR.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-queries-DvIJcn6Z.js +0 -2
- package/dist/core/web/public/next/assets/verse-queries-DvIJcn6Z.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-store-DjBWj04q.js +0 -3
- package/dist/core/web/public/next/assets/verse-store-DjBWj04q.js.map +0 -1
- package/dist/core/web/public/next/assets/verse-ui-store-zafyzXKD.js +0 -3
- package/dist/core/web/public/next/assets/warmup-BR6pxAKs.js +0 -3
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"charts-CMSgAUKn.js","names":[],"sources":["../../../../../../src/web-ui/components/charts/ChartContainer.module.css","../../../../../../src/web-ui/components/charts/ChartContainer.tsx","../../../../../../src/web-ui/components/charts/Legend.module.css","../../../../../../src/web-ui/components/charts/Legend.tsx","../../../../../../src/web-ui/components/charts/LineChart.module.css","../../../../../../src/web-ui/components/charts/LineChart.tsx","../../../../../../src/web-ui/components/charts/BarChart.module.css","../../../../../../src/web-ui/components/charts/BarChart.tsx"],"sourcesContent":[".figure {\n margin: 0;\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-lg);\n background: var(--bg-surface);\n padding: var(--space-4);\n /* Fixed heights must include the axis-label band — this container instead\n grows with its content so a chart's x-axis labels never get clipped or\n forced into a nested scroll (dataviz anti-patterns). */\n}\n\n.caption {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: var(--space-3);\n margin-bottom: var(--space-3);\n}\n\n.title {\n font-size: var(--text-sm-size);\n font-weight: var(--font-weight-semibold);\n color: var(--text-primary);\n}\n\n.description {\n font-size: var(--text-xs-size);\n color: var(--text-tertiary);\n}\n\n.plot {\n width: 100%;\n overflow-x: auto;\n}\n\n.tableDisclosure {\n margin-top: var(--space-3);\n border-top: 1px solid var(--border-subtle);\n padding-top: var(--space-2);\n}\n\n.tableDisclosure > summary {\n cursor: pointer;\n font-size: var(--text-xs-size);\n color: var(--text-secondary);\n list-style: none;\n}\n\n.tableDisclosure > summary::-webkit-details-marker {\n display: none;\n}\n\n.tableDisclosure > summary::before {\n content: '▸ ';\n}\n\n.tableDisclosure[open] > summary::before {\n content: '▾ ';\n}\n\n.tableBody {\n margin-top: var(--space-2);\n}\n\n.caveat {\n margin: 0 0 var(--space-3);\n padding: var(--space-2) var(--space-3);\n border-radius: var(--radius-md);\n background: var(--status-unknown-bg);\n color: var(--status-unknown-fg);\n font-size: var(--text-xs-size);\n line-height: var(--text-sm-line);\n}\n\n.empty {\n color: var(--text-tertiary);\n font-size: var(--text-sm-size);\n padding: var(--space-6) 0;\n text-align: center;\n}\n","/**\n * components/charts/ChartContainer.tsx — the figure every chart mounts\n * inside: title/description, the plot itself, an optional caveat banner\n * (epistemic-honesty-adjacent: for a chart built on data with a known,\n * disclosed limitation — see ProductionView's judge-verdict caveat), an\n * optional table-view twin (the WCAG-clean accessibility equivalent every\n * chart should ship per the dataviz skill), and an empty state that never\n * lies about missing data.\n */\nimport type { ReactNode } from 'react';\nimport './chart-tokens.css';\nimport styles from './ChartContainer.module.css';\n\nexport interface ChartContainerProps {\n title: string;\n description?: string;\n /** A short, honest note about a known limitation of the data behind this\n * chart (e.g. \"counts may include X, not yet separable\"). Never omit a\n * caveat to make a chart look cleaner than the data actually is. */\n caveat?: string;\n /** The chart itself (SVG + legend). */\n children: ReactNode;\n /** The accessibility twin — same data as a real HTML table. Omit only\n * when the chart already IS a table. */\n table?: ReactNode;\n /** When true, renders the empty state instead of `children`. */\n empty?: boolean;\n emptyMessage?: string;\n}\n\nexport function ChartContainer({\n title,\n description,\n caveat,\n children,\n table,\n empty,\n emptyMessage = 'No data for this window.',\n}: ChartContainerProps) {\n return (\n <figure className={styles.figure}>\n <figcaption className={styles.caption}>\n <span className={styles.title}>{title}</span>\n {description ? <span className={styles.description}>{description}</span> : null}\n </figcaption>\n {caveat ? (\n <p className={styles.caveat} role=\"note\">\n {caveat}\n </p>\n ) : null}\n {empty ? (\n <p className={styles.empty}>{emptyMessage}</p>\n ) : (\n <div className={styles.plot}>{children}</div>\n )}\n {table ? (\n <details className={styles.tableDisclosure}>\n <summary>View as table</summary>\n <div className={styles.tableBody}>{table}</div>\n </details>\n ) : null}\n </figure>\n );\n}\n",".legend {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-1) var(--space-4);\n margin: var(--space-2) 0 0;\n padding: 0;\n list-style: none;\n}\n\n.item {\n display: inline-flex;\n align-items: center;\n gap: var(--space-2);\n font-size: var(--text-xs-size);\n color: var(--text-secondary);\n}\n\n.swatch {\n width: 10px;\n height: 10px;\n border-radius: var(--radius-full);\n flex: none;\n}\n\n.swatchLine {\n width: 14px;\n height: 2px;\n border-radius: var(--radius-full);\n flex: none;\n}\n","/**\n * components/charts/Legend.tsx — the dependable identity channel. A legend\n * is always present for two or more series (never rely on color-matching\n * alone); a single series needs no legend box because the chart's own title\n * already names it (see marks-and-anatomy.md \"Labels & legend\").\n */\nimport styles from './Legend.module.css';\n\nexport interface LegendItem {\n label: string;\n color: string;\n /** 'line' for line charts (a short stroke key), 'swatch' for bar/area. */\n kind?: 'line' | 'swatch';\n}\n\nexport function Legend({ items }: { items: LegendItem[] }) {\n if (items.length < 2) return null;\n return (\n <ul className={styles.legend}>\n {items.map((it) => (\n <li key={it.label} className={styles.item}>\n <span\n className={it.kind === 'line' ? styles.swatchLine : styles.swatch}\n style={{ background: it.color }}\n aria-hidden=\"true\"\n />\n {it.label}\n </li>\n ))}\n </ul>\n );\n}\n",".svg {\n display: block;\n width: 100%;\n height: auto;\n}\n\n.gridline {\n stroke: var(--chart-grid);\n stroke-width: 1;\n}\n\n.axis {\n stroke: var(--chart-axis);\n stroke-width: 1;\n}\n\n.axisLabel {\n font-size: var(--text-xs-size);\n fill: var(--text-tertiary);\n font-family: var(--font-ui);\n}\n\n.line {\n fill: none;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n\n.area {\n opacity: 0.1;\n stroke: none;\n}\n\n.marker {\n stroke: var(--chart-surface);\n stroke-width: 2;\n}\n\n.endLabel {\n font-size: var(--text-xs-size);\n font-family: var(--font-ui);\n font-weight: var(--font-weight-medium);\n}\n\n.crosshairLine {\n stroke: var(--chart-axis);\n stroke-width: 1;\n pointer-events: none;\n}\n\n.overlay {\n fill: transparent;\n cursor: crosshair;\n}\n\n.wrap {\n position: relative;\n}\n\n.tooltip {\n position: absolute;\n pointer-events: none;\n background: var(--chart-tooltip-bg);\n border: 1px solid var(--chart-tooltip-border);\n border-radius: var(--radius-sm);\n box-shadow: var(--shadow-md);\n padding: var(--space-2);\n font-size: var(--text-xs-size);\n color: var(--text-primary);\n white-space: nowrap;\n z-index: var(--z-dropdown);\n transform: translate(-50%, -100%);\n margin-top: -10px;\n}\n\n.tooltipDate {\n color: var(--text-tertiary);\n margin-bottom: var(--space-1);\n}\n\n.tooltipRow {\n display: flex;\n align-items: center;\n gap: var(--space-1);\n}\n\n.tooltipKey {\n width: 12px;\n height: 2px;\n flex: none;\n}\n\n.tooltipValue {\n font-weight: var(--font-weight-semibold);\n font-variant-numeric: tabular-nums;\n}\n\n.tooltipGap {\n color: var(--text-tertiary);\n font-style: italic;\n}\n","/**\n * components/charts/LineChart.tsx — trend over time. A gap (`null` y) is\n * split into a separate path segment so it renders as a visible break in\n * the line, never as a straight line through a missing value and never as\n * a silent zero. Single series gets an optional soft area fill; two-plus\n * series always render a legend (never color-matching alone).\n */\nimport { useRef, useState } from 'react';\nimport type { Series } from './types.js';\nimport { seriesColor, CHART_GRID, CHART_AXIS } from './colors.js';\nimport { Legend } from './Legend.js';\nimport {\n MIN_TIME_SPAN_MS,\n allIntegers,\n axisTicks,\n dodgeLabels,\n ensureSpan,\n isTimeAxis,\n labelCharPx,\n layoutAxisLabels,\n xKeeper,\n} from './chart-math.js';\nimport { timeLabelLadder } from './format.js';\nimport { useTextScale } from './useTextScale.js';\nimport './chart-tokens.css';\nimport styles from './LineChart.module.css';\n\nconst VBOX_W = 640;\n/** Minimum left gutter; it widens when the y tick labels need more (see padL). */\nconst PAD_L = 44;\nconst PAD_R = 12;\nconst PAD_T = 12;\nconst PAD_B = 24;\nconst TICKS_Y = 4;\n/* Direct end-of-line labels need a gutter of their own. They are drawn in USER\n units — the viewBox scales the font along with the geometry, so a reserve\n measured in user units holds at every rendered width — and `--text-xs-size`\n resolves to 12 user units here, where the UI sans averages a shade over 6\n units per character at medium weight. 6.4 rounds that up so the reserve errs\n wide rather than clipping. Without the reserve the label was drawn at\n `VBOX_W - PAD_R + 4` with only PAD_R (12u) of room and the svg's own\n `overflow: hidden` cropped it to its first glyph — \"Estimated spend\"\n rendered as a lone \"E\".\n Every text measure here (END_LABEL_CH, END_LABEL_GAP_Y, the axis label\n advance) is for 12 px text and is multiplied by the operator's Display\n size (useTextScale): --text-xs-size is 12px × --ui-text-scale, so at\n XLarge the same label is 15 units tall and 25% wider. END_LABEL_MAX is a\n budget of plot width, not a text measure, and stays as it is. */\nconst END_LABEL_CH = 6.4;\nconst END_LABEL_GAP = 4;\n/* Past this the gutter would cost more plot than the label is worth, so the\n label is dropped rather than shrinking the chart around it. Nothing is lost:\n Legend.tsx deliberately renders nothing below two series because the panel\n heading already names a lone series, and two-plus series always have the\n legend regardless. */\nconst END_LABEL_MAX = 150;\n/** Line height of a direct end label at 12 px (user units): two closer than this overlap. */\nconst END_LABEL_GAP_Y = 14;\n\ninterface Run {\n x: number;\n y: number;\n}\n\nfunction splitRuns(points: Series['points']): Run[][] {\n const runs: Run[][] = [];\n let current: Run[] = [];\n for (const p of points) {\n if (p.y === null) {\n if (current.length) runs.push(current);\n current = [];\n continue;\n }\n current.push({ x: p.x, y: p.y });\n }\n if (current.length) runs.push(current);\n return runs;\n}\n\nexport function LineChart({\n series: seriesProp,\n height = 200,\n area = false,\n formatX: formatXProp,\n formatY: formatYProp,\n ariaLabel,\n}: {\n series: Series[];\n height?: number;\n area?: boolean;\n formatX?: (x: number) => string;\n formatY?: (y: number) => string;\n ariaLabel: string;\n}) {\n const svgRef = useRef<SVGSVGElement>(null);\n const textScale = useTextScale();\n /* Axis labels' estimated advance per character, at the size they render. */\n const axisLabelCh = labelCharPx(textScale);\n const [hoverX, setHoverX] = useState<number | null>(null);\n const [tooltipPos, setTooltipPos] = useState<{ left: number; top: number } | null>(null);\n\n const formatX = formatXProp ?? ((x: number) => String(x));\n const formatY = formatYProp ?? ((y: number) => String(y));\n /* On a time axis a 0 / NaN / pre-2000 x is a null timestamp that leaked\n through, not a moment: it would start the axis at \"Dec 31\" 1969. Such\n points are left out of the domain AND the drawing. */\n const keepX = xKeeper(seriesProp.flatMap((s) => s.points.map((p) => p.x)));\n const series = seriesProp.map((s) => ({ ...s, points: s.points.filter((p) => keepX(p.x)) }));\n const allX = series.flatMap((s) => s.points.map((p) => p.x));\n const knownY = series.flatMap((s) => s.points.map((p) => p.y)).filter((y): y is number => y !== null && Number.isFinite(y));\n if (allX.length === 0) {\n return <p>No data.</p>;\n }\n const rawXMin = Math.min(...allX);\n const rawXMax = Math.max(...allX);\n const timeAxis = isTimeAxis(allX);\n // A burst of points seconds apart is widened to MIN_TIME_SPAN_MS around\n // itself instead of stretched edge to edge under identical labels.\n const [xMin, xMax] = timeAxis && rawXMax > rawXMin ? ensureSpan(rawXMin, rawXMax, MIN_TIME_SPAN_MS) : [rawXMin, rawXMax];\n const xRange = xMax - xMin || 1;\n\n // Nice ticks whose labels are exact: integer data get integer ticks, and a\n // caller's formatter never prints a rounded label on a precise tick.\n const yAxis = axisTicks(Math.min(0, ...knownY), Math.max(0, ...knownY), {\n count: TICKS_Y,\n integer: knownY.length > 0 && allIntegers(knownY),\n format: formatYProp,\n });\n const yTicks = yAxis.ticks;\n const yMin = yTicks[0]!;\n const yMax = yTicks[yTicks.length - 1]!;\n const yRange = yMax - yMin || 1;\n const plotH = height - PAD_T - PAD_B;\n const yScale = (y: number) => PAD_T + plotH - ((y - yMin) / yRange) * plotH;\n /* The y labels hang right-aligned 6 units left of the plot, and the svg\n clips at its edge: the gutter grows to fit the widest one (+2 of air) —\n 44 still fits five 12 px characters, a larger Display size fewer. */\n const padL = Math.max(PAD_L, Math.ceil(Math.max(0, ...yAxis.labels.map((l) => l.length)) * axisLabelCh) + 8);\n\n /* Decided before the x scale, because the reserved gutter is what plotW is\n measured against — and the gridlines and x-axis stop at the same edge.\n End labels are dodged vertically so two lines ending on one value never\n print their names over each other; if they cannot all fit, the legend\n (always present for 2+ series) names them instead. */\n const wantEndLabels = series.length >= 1 && series.length <= 4;\n const endLabelW = wantEndLabels\n ? Math.max(...series.map((s) => s.label.length)) * END_LABEL_CH * textScale\n : 0;\n const endPoints = series.flatMap((s) => {\n for (let i = s.points.length - 1; i >= 0; i--) {\n const y = s.points[i]!.y;\n if (y !== null && Number.isFinite(y)) return [{ key: s.id, y: yScale(y) }];\n }\n return [];\n });\n const dodged = wantEndLabels && endLabelW <= END_LABEL_MAX ? dodgeLabels(endPoints, END_LABEL_GAP_Y * textScale, PAD_T, PAD_T + plotH) : null;\n const showEndLabels = dodged !== null && endPoints.length > 0;\n const padR = PAD_R + (showEndLabels ? END_LABEL_GAP + endLabelW : 0);\n const showLegend = series.length >= 2;\n\n const plotW = VBOX_W - padL - padR;\n const xScale = (x: number) => padL + ((x - xMin) / xRange) * plotW;\n\n // Nearest-x lookup across a reference axis (the union of all distinct x\n // values, since series may not share every point).\n const xAxis = Array.from(new Set(allX)).sort((a, b) => a - b);\n\n /* First and last x labels, collision-free: the end outranks the start;\n on a time axis both walk one detail ladder (the caller's format → a\n shorter date/time; clock time only inside one day) until they fit. */\n const xEnds = xAxis.length > 1 ? [xAxis[0]!, xAxis[xAxis.length - 1]!] : [];\n const ladder = timeAxis ? timeLabelLadder(rawXMin, rawXMax, formatXProp, xEnds) : [formatX];\n const xLabels = layoutAxisLabels(\n xEnds.map((x, i) => {\n const px = xScale(x);\n const edge = i === 0 ? px <= padL + 0.5 : px >= VBOX_W - padR - 0.5;\n return {\n key: i === 0 ? 'start' : 'end',\n x: px,\n anchor: edge ? (i === 0 ? 'start' as const : 'end' as const) : 'middle' as const,\n priority: i === 0 ? 2 : 3,\n variants: ladder.map((f) => f(x)),\n };\n }),\n { min: padL, max: VBOX_W - padR, charPx: axisLabelCh },\n );\n\n function nearestX(clientX: number): number | null {\n const svg = svgRef.current;\n if (!svg) return null;\n const rect = svg.getBoundingClientRect();\n const svgX = ((clientX - rect.left) / rect.width) * VBOX_W;\n const dataX = xMin + ((svgX - padL) / plotW) * xRange;\n let nearest = xAxis[0];\n let best = Infinity;\n for (const x of xAxis) {\n const d = Math.abs(x - dataX);\n if (d < best) {\n best = d;\n nearest = x;\n }\n }\n return nearest;\n }\n\n function handleMove(e: React.PointerEvent<SVGSVGElement>) {\n const x = nearestX(e.clientX);\n if (x === null) return;\n setHoverX(x);\n const svg = svgRef.current;\n if (svg) {\n const rect = svg.getBoundingClientRect();\n const px = (xScale(x) / VBOX_W) * rect.width;\n setTooltipPos({ left: px, top: 0 });\n }\n }\n\n const hoverPoints =\n hoverX !== null\n ? series.map((s) => ({\n series: s,\n point: s.points.find((p) => p.x === hoverX) ?? null,\n }))\n : null;\n\n return (\n <div className={styles.wrap}>\n <svg\n ref={svgRef}\n className={styles.svg}\n viewBox={`0 0 ${VBOX_W} ${height}`}\n role=\"img\"\n aria-label={ariaLabel}\n onPointerMove={handleMove}\n onPointerLeave={() => setHoverX(null)}\n >\n {yTicks.map((t, i) => (\n <g key={i}>\n <line\n x1={padL}\n x2={VBOX_W - padR}\n y1={yScale(t)}\n y2={yScale(t)}\n className={styles.gridline}\n stroke={CHART_GRID}\n />\n <text x={padL - 6} y={yScale(t)} dy=\"0.32em\" textAnchor=\"end\" className={styles.axisLabel}>\n {yAxis.labels[i]}\n </text>\n </g>\n ))}\n <line\n x1={padL}\n x2={VBOX_W - padR}\n y1={height - PAD_B}\n y2={height - PAD_B}\n className={styles.axis}\n stroke={CHART_AXIS}\n />\n {xLabels.map((l) => (\n <text key={l.key} data-axis-label={l.key} x={l.x} y={height - 6} textAnchor={l.anchor} className={styles.axisLabel}>\n {l.text}\n </text>\n ))}\n\n {series.map((s, si) => {\n const color = seriesColor(si);\n const runs = splitRuns(s.points);\n return (\n <g key={s.id}>\n {area && series.length === 1\n ? runs.map((run, ri) => {\n const d =\n run.map((p, j) => `${j === 0 ? 'M' : 'L'}${xScale(p.x)},${yScale(p.y)}`).join(' ') +\n ` L${xScale(run[run.length - 1].x)},${yScale(yMin)} L${xScale(run[0].x)},${yScale(yMin)} Z`;\n return <path key={ri} d={d} fill={color} className={styles.area} />;\n })\n : null}\n {runs.map((run, ri) => (\n <path\n key={ri}\n d={run.map((p, j) => `${j === 0 ? 'M' : 'L'}${xScale(p.x)},${yScale(p.y)}`).join(' ')}\n stroke={color}\n className={styles.line}\n />\n ))}\n {showEndLabels && runs.length ? (\n (() => {\n const lastRun = runs[runs.length - 1];\n const last = lastRun[lastRun.length - 1];\n return (\n <text\n data-end-label={s.id}\n x={xScale(last.x) + END_LABEL_GAP}\n y={dodged?.get(s.id) ?? yScale(last.y)}\n dy=\"0.32em\"\n className={styles.endLabel}\n fill={color}\n >\n {s.label}\n </text>\n );\n })()\n ) : null}\n </g>\n );\n })}\n\n {hoverX !== null ? (\n <line\n x1={xScale(hoverX)}\n x2={xScale(hoverX)}\n y1={PAD_T}\n y2={height - PAD_B}\n className={styles.crosshairLine}\n />\n ) : null}\n {hoverX !== null\n ? series.map((s, si) => {\n const p = s.points.find((pt) => pt.x === hoverX);\n if (!p || p.y === null) return null;\n return (\n <circle\n key={s.id}\n cx={xScale(p.x)}\n cy={yScale(p.y)}\n r={4}\n fill={seriesColor(si)}\n className={styles.marker}\n />\n );\n })\n : null}\n\n <rect\n x={padL}\n y={PAD_T}\n width={plotW}\n height={plotH}\n className={styles.overlay}\n onFocus={() => setHoverX(xAxis[xAxis.length - 1] ?? null)}\n />\n </svg>\n {hoverPoints && tooltipPos ? (\n <div className={styles.tooltip} style={{ left: tooltipPos.left, top: tooltipPos.top }}>\n <div className={styles.tooltipDate}>{formatX(hoverX as number)}</div>\n {hoverPoints.map(({ series: s, point }, i) => (\n <div className={styles.tooltipRow} key={s.id}>\n <span className={styles.tooltipKey} style={{ background: seriesColor(i) }} aria-hidden=\"true\" />\n {point && point.y !== null ? (\n <>\n <span className={styles.tooltipValue}>{formatY(point.y)}</span>\n {showLegend ? <span>{s.label}</span> : null}\n </>\n ) : (\n <span className={styles.tooltipGap}>no data{showLegend ? ` — ${s.label}` : ''}</span>\n )}\n </div>\n ))}\n </div>\n ) : null}\n {showLegend ? (\n <Legend items={series.map((s, i) => ({ label: s.label, color: seriesColor(i), kind: 'line' }))} />\n ) : null}\n </div>\n );\n}\n",".svg {\n display: block;\n overflow: visible;\n}\n\n.bar {\n transition: opacity var(--duration-fast) var(--ease-standard);\n}\n\n.bar:hover,\n.bar:focus-visible {\n opacity: 0.82;\n}\n\n.baseline {\n stroke: var(--chart-axis);\n stroke-width: 1;\n}\n\n.gridline {\n stroke: var(--chart-grid);\n stroke-width: 1;\n}\n\n.axisLabel {\n font-size: 10px;\n fill: var(--text-tertiary);\n font-family: var(--font-ui);\n}\n\n.valueLabel {\n font-size: 10px;\n fill: var(--text-secondary);\n font-family: var(--font-ui);\n font-variant-numeric: tabular-nums;\n}\n\n.categoryLabel {\n font-size: 10px;\n fill: var(--text-secondary);\n font-family: var(--font-ui);\n}\n\n.hitArea {\n fill: transparent;\n cursor: pointer;\n}\n\n.tooltip {\n position: absolute;\n pointer-events: none;\n background: var(--chart-tooltip-bg);\n border: 1px solid var(--chart-tooltip-border);\n border-radius: var(--radius-sm);\n box-shadow: var(--shadow-md);\n padding: var(--space-1) var(--space-2);\n font-size: var(--text-xs-size);\n color: var(--text-primary);\n white-space: nowrap;\n z-index: var(--z-dropdown);\n}\n\n.tooltipValue {\n font-weight: var(--font-weight-semibold);\n font-variant-numeric: tabular-nums;\n}\n\n.tooltipLabel {\n color: var(--text-tertiary);\n margin-left: var(--space-1);\n}\n\n.wrap {\n position: relative;\n}\n","/**\n * components/charts/BarChart.tsx — categorical magnitude comparison.\n * Single hue by default (nominal categories never get a value-ramp — see\n * dataviz anti-patterns \"a value-ramp on nominal categories\"). Vertical for\n * a handful of short-named categories, horizontal for many or long-named\n * ones (dataviz choosing-a-form.md). Null values render as an explicit\n * \"no data\" dash, never a silent zero-height bar indistinguishable from a\n * real zero.\n */\nimport { useId, useState } from 'react';\nimport type { CategoricalDatum } from './types.js';\nimport { CHART_SEQUENTIAL, CHART_AXIS, CHART_GRID } from './colors.js';\nimport './chart-tokens.css';\nimport styles from './BarChart.module.css';\n\nconst BAR_MAX = 24;\nconst GAP = 10;\n\nexport function BarChart({\n data,\n height = 160,\n orientation = 'vertical',\n color = CHART_SEQUENTIAL,\n formatValue = (v: number) => String(v),\n ariaLabel,\n}: {\n data: CategoricalDatum[];\n height?: number;\n orientation?: 'vertical' | 'horizontal';\n color?: string;\n formatValue?: (v: number) => string;\n ariaLabel: string;\n}) {\n const uid = useId();\n const [hover, setHover] = useState<number | null>(null);\n\n const known = data.map((d) => d.value).filter((v): v is number => v !== null);\n const maxVal = Math.max(0, ...known, 0.0001);\n const minVal = Math.min(0, ...known);\n const range = maxVal - minVal || 1;\n\n if (orientation === 'horizontal') {\n const LABEL_W = 120;\n const PAD_R = 48;\n const rowH = Math.min(BAR_MAX, Math.max(14, Math.floor((height - GAP) / data.length) - GAP));\n const plotW = 420;\n const svgH = data.length * (rowH + GAP) + GAP;\n const svgW = LABEL_W + plotW + PAD_R;\n\n return (\n <div className={styles.wrap}>\n <svg\n className={styles.svg}\n width={svgW}\n height={svgH}\n viewBox={`0 0 ${svgW} ${svgH}`}\n role=\"img\"\n aria-label={ariaLabel}\n >\n <line x1={LABEL_W} y1={0} x2={LABEL_W} y2={svgH} className={styles.baseline} stroke={CHART_AXIS} />\n {data.map((d, i) => {\n const y = GAP + i * (rowH + GAP);\n const barW = d.value !== null ? ((d.value - minVal) / range) * plotW : 0;\n return (\n <g key={d.label + i}>\n <text\n x={LABEL_W - 8}\n y={y + rowH / 2}\n dy=\"0.32em\"\n textAnchor=\"end\"\n className={styles.categoryLabel}\n >\n {d.label.length > 22 ? `${d.label.slice(0, 21)}…` : d.label}\n </text>\n {d.value === null ? (\n <line\n x1={LABEL_W + 4}\n y1={y + rowH / 2}\n x2={LABEL_W + 14}\n y2={y + rowH / 2}\n stroke={CHART_GRID}\n strokeWidth={2}\n strokeDasharray=\"2 2\"\n />\n ) : (\n <>\n <rect\n x={LABEL_W}\n y={y}\n width={Math.max(2, barW)}\n height={rowH}\n rx={4}\n fill={color}\n className={styles.bar}\n />\n <text\n x={LABEL_W + Math.max(2, barW) + 6}\n y={y + rowH / 2}\n dy=\"0.32em\"\n className={styles.valueLabel}\n >\n {formatValue(d.value)}\n </text>\n </>\n )}\n <rect\n className={styles.hitArea}\n x={0}\n y={y - GAP / 2}\n width={svgW}\n height={rowH + GAP}\n tabIndex={0}\n role=\"img\"\n aria-label={`${d.label}: ${d.value === null ? 'no data' : formatValue(d.value)}`}\n onPointerEnter={() => setHover(i)}\n onPointerLeave={() => setHover((h) => (h === i ? null : h))}\n onFocus={() => setHover(i)}\n onBlur={() => setHover((h) => (h === i ? null : h))}\n />\n </g>\n );\n })}\n </svg>\n {hover !== null ? (\n <div\n className={styles.tooltip}\n style={{ left: LABEL_W + 8, top: GAP + hover * (rowH + GAP) - 4 }}\n >\n <span className={styles.tooltipValue}>\n {data[hover].value === null ? 'no data' : formatValue(data[hover].value as number)}\n </span>\n <span className={styles.tooltipLabel}>{data[hover].label}</span>\n </div>\n ) : null}\n </div>\n );\n }\n\n // Vertical orientation.\n const barSlot = data.length ? Math.max(BAR_MAX, 0) : BAR_MAX;\n const barW = Math.min(BAR_MAX, barSlot);\n const PAD_T = 20;\n const PAD_B = 30;\n const plotH = height;\n const svgH = plotH + PAD_T + PAD_B;\n const svgW = data.length * (barW + GAP) + GAP;\n const zeroY = PAD_T + plotH - ((0 - minVal) / range) * plotH;\n\n return (\n <div className={styles.wrap}>\n <svg\n className={styles.svg}\n width={svgW}\n height={svgH}\n viewBox={`0 0 ${svgW} ${svgH}`}\n role=\"img\"\n aria-label={ariaLabel}\n >\n <line x1={0} y1={zeroY} x2={svgW} y2={zeroY} className={styles.baseline} stroke={CHART_AXIS} />\n {data.map((d, i) => {\n const x = GAP + i * (barW + GAP);\n const barH = d.value !== null ? (Math.abs(d.value - minVal) / range) * plotH : 0;\n const top = d.value !== null && d.value >= 0 ? zeroY - barH : zeroY;\n return (\n <g key={d.label + i}>\n {d.value === null ? (\n <line\n x1={x + barW / 2 - 5}\n y1={zeroY}\n x2={x + barW / 2 + 5}\n y2={zeroY}\n stroke={CHART_GRID}\n strokeWidth={2}\n strokeDasharray=\"2 2\"\n />\n ) : (\n <>\n <rect\n x={x}\n y={top}\n width={barW}\n height={Math.max(2, barH)}\n rx={4}\n fill={color}\n className={styles.bar}\n />\n <text x={x + barW / 2} y={top - 4} textAnchor=\"middle\" className={styles.valueLabel}>\n {formatValue(d.value)}\n </text>\n </>\n )}\n <text\n x={x + barW / 2}\n y={svgH - 8}\n textAnchor=\"middle\"\n className={styles.axisLabel}\n >\n {d.label.length > 10 ? `${d.label.slice(0, 9)}…` : d.label}\n </text>\n <rect\n className={styles.hitArea}\n x={x - GAP / 2}\n y={0}\n width={barW + GAP}\n height={svgH}\n tabIndex={0}\n role=\"img\"\n aria-label={`${d.label}: ${d.value === null ? 'no data' : formatValue(d.value)}`}\n onPointerEnter={() => setHover(i)}\n onPointerLeave={() => setHover((h) => (h === i ? null : h))}\n onFocus={() => setHover(i)}\n onBlur={() => setHover((h) => (h === i ? null : h))}\n />\n </g>\n );\n })}\n </svg>\n {hover !== null ? (\n <div\n className={styles.tooltip}\n style={{ left: GAP + hover * (barW + GAP), top: 0 }}\n id={`${uid}-tooltip`}\n >\n <span className={styles.tooltipValue}>\n {data[hover].value === null ? 'no data' : formatValue(data[hover].value as number)}\n </span>\n <span className={styles.tooltipLabel}>{data[hover].label}</span>\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"6tBC8BA,SAAgB,EAAe,CAC7B,QACA,cACA,SACA,WACA,QACA,QACA,eAAe,4BACO,CACtB,OACE,EAAA,EAAA,KAAA,CAAC,SAAD,CAAQ,UAAW,EAAO,OAA1B,SAAA,EACE,EAAA,EAAA,KAAA,CAAC,aAAD,CAAY,UAAW,EAAO,QAA9B,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAO,MAAQ,SAAA,CAAY,CAAA,EAC3C,GAAc,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAO,YAAc,SAAA,CAAkB,CAAA,EAAI,IACjE,IACX,GACC,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAW,EAAO,OAAQ,KAAK,OAC/B,SAAA,CACA,CAAA,EACD,KACH,GACC,EAAA,EAAA,IAAA,CAAC,IAAD,CAAG,UAAW,EAAO,MAAQ,SAAA,CAAgB,CAAA,GAE7C,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAO,KAAO,UAAc,CAAA,EAE7C,GACC,EAAA,EAAA,KAAA,CAAC,UAAD,CAAS,UAAW,EAAO,gBAA3B,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,UAAD,CAAA,SAAS,eAAsB,CAAA,GAC/B,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAO,UAAY,SAAA,CAAW,CAAA,CACvC,CACP,CAAA,EAAA,IACE,GAEZ,oHEhDA,SAAgB,EAAO,CAAE,SAAkC,CAEzD,OADI,EAAM,OAAS,EAAU,MAE3B,EAAA,EAAA,IAAA,CAAC,KAAD,CAAI,UAAW,EAAO,OACnB,SAAA,EAAM,IAAK,IACV,EAAA,EAAA,KAAA,CAAC,KAAD,CAAmB,UAAW,EAAO,KAArC,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,UAAW,EAAG,OAAS,OAAS,EAAO,WAAa,EAAO,OAC3D,MAAO,CAAE,WAAY,EAAG,KAAM,EAC9B,cAAY,MACb,CAAA,EACA,EAAG,KACF,CAPK,EAAA,EAAG,KAOR,CACL,CACC,CAAA,CAER,ogBEJM,EAAS,IAET,GAAQ,GACR,GAAQ,GACR,EAAQ,GACR,EAAQ,GACR,GAAU,EAeV,GAAe,IACf,EAAgB,EAMhB,GAAgB,IAEhB,GAAkB,GAOxB,SAAS,GAAU,EAAmC,CACpD,IAAM,EAAgB,CAAC,EACnB,EAAiB,CAAC,EACtB,IAAK,IAAM,KAAK,EAAQ,CACtB,GAAI,EAAE,IAAM,KAAM,CACZ,EAAQ,QAAQ,EAAK,KAAK,CAAO,EACrC,EAAU,CAAC,EACX,QACF,CACA,EAAQ,KAAK,CAAE,EAAG,EAAE,EAAG,EAAG,EAAE,CAAE,CAAC,CACjC,CAEA,OADI,EAAQ,QAAQ,EAAK,KAAK,CAAO,EAC9B,CACT,CAEA,SAAgB,EAAU,CACxB,OAAQ,EACR,SAAS,IACT,OAAO,GACP,QAAS,EACT,QAAS,EACT,aAQC,CACD,IAAM,GAAA,EAAS,EAAA,OAAA,CAAsB,IAAI,EACnC,EAAY,EAAa,EAEzB,EAAc,EAAY,CAAS,EACnC,CAAC,EAAQ,IAAA,EAAa,EAAA,SAAA,CAAwB,IAAI,EAClD,CAAC,EAAY,KAAA,EAAiB,EAAA,SAAA,CAA+C,IAAI,EAEjF,EAAU,IAAiB,GAAc,OAAO,CAAC,GACjD,GAAU,IAAiB,GAAc,OAAO,CAAC,GAIjD,GAAQ,EAAQ,EAAW,QAAS,GAAM,EAAE,OAAO,IAAK,GAAM,EAAE,CAAC,CAAC,CAAC,EACnE,EAAS,EAAW,IAAK,IAAO,CAAE,GAAG,EAAG,OAAQ,EAAE,OAAO,OAAQ,GAAM,GAAM,EAAE,CAAC,CAAC,CAAE,EAAE,EACrF,EAAO,EAAO,QAAS,GAAM,EAAE,OAAO,IAAK,GAAM,EAAE,CAAC,CAAC,EACrD,EAAS,EAAO,QAAS,GAAM,EAAE,OAAO,IAAK,GAAM,EAAE,CAAC,CAAC,CAAC,CAAC,OAAQ,GAAmB,IAAM,MAAQ,OAAO,SAAS,CAAC,CAAC,EAC1H,GAAI,EAAK,SAAW,EAClB,OAAO,EAAA,EAAA,IAAA,CAAC,IAAD,CAAA,SAAG,UAAW,CAAA,EAEvB,IAAM,EAAU,KAAK,IAAI,GAAG,CAAI,EAC1B,EAAU,KAAK,IAAI,GAAG,CAAI,EAC1B,GAAW,EAAW,CAAI,EAG1B,CAAC,EAAM,IAAQ,IAAY,EAAU,EAAU,EAAW,EAAS,EAAS,CAAgB,EAAI,CAAC,EAAS,CAAO,EACjH,EAAS,GAAO,GAAQ,EAIxB,EAAQ,EAAU,KAAK,IAAI,EAAG,GAAG,CAAM,EAAG,KAAK,IAAI,EAAG,GAAG,CAAM,EAAG,CACtE,MAAO,GACP,QAAS,EAAO,OAAS,GAAK,EAAY,CAAM,EAChD,OAAQ,CACV,CAAC,EACK,EAAS,EAAM,MACf,EAAO,EAAO,GAEd,GADO,EAAO,EAAO,OAAS,GACd,GAAQ,EACxB,EAAQ,EAAS,EAAQ,EACzB,EAAU,GAAc,EAAQ,GAAU,EAAI,GAAQ,GAAU,EAIhE,EAAO,KAAK,IAAI,GAAO,KAAK,KAAK,KAAK,IAAI,EAAG,GAAG,EAAM,OAAO,IAAK,GAAM,EAAE,MAAM,CAAC,EAAI,CAAW,EAAI,CAAC,EAOrG,EAAgB,EAAO,QAAU,GAAK,EAAO,QAAU,EACvD,GAAY,EACd,KAAK,IAAI,GAAG,EAAO,IAAK,GAAM,EAAE,MAAM,MAAM,CAAC,EAAI,GAAe,EAChE,EACE,GAAY,EAAO,QAAS,GAAM,CACtC,IAAK,IAAI,EAAI,EAAE,OAAO,OAAS,EAAG,GAAK,EAAG,IAAK,CAC7C,IAAM,EAAI,EAAE,OAAO,EAAE,CAAE,EACvB,GAAI,IAAM,MAAQ,OAAO,SAAS,CAAC,EAAG,MAAO,CAAC,CAAE,IAAK,EAAE,GAAI,EAAG,EAAO,CAAC,CAAE,CAAC,CAC3E,CACA,MAAO,CAAC,CACV,CAAC,EACK,GAAS,GAAiB,IAAa,GAAgB,EAAY,GAAW,GAAkB,EAAW,EAAO,EAAQ,CAAK,EAAI,KACnI,GAAgB,KAAW,MAAQ,GAAU,OAAS,EACtD,EAAO,IAAS,GAAgB,EAAgB,GAAY,GAC5D,EAAa,EAAO,QAAU,EAE9B,EAAQ,EAAS,EAAO,EACxB,EAAU,GAAc,GAAS,EAAI,GAAQ,EAAU,EAIvD,EAAQ,MAAM,KAAK,IAAI,IAAI,CAAI,CAAC,CAAC,CAAC,MAAM,EAAG,IAAM,EAAI,CAAC,EAKtD,GAAQ,EAAM,OAAS,EAAI,CAAC,EAAM,GAAK,EAAM,EAAM,OAAS,EAAG,EAAI,CAAC,EACpE,GAAS,GAAW,EAAgB,EAAS,EAAS,EAAa,EAAK,EAAI,CAAC,CAAO,EACpF,GAAU,EACd,GAAM,KAAK,EAAG,IAAM,CAClB,IAAM,EAAK,EAAO,CAAC,EACb,EAAO,IAAM,EAAI,GAAM,EAAO,GAAM,GAAM,EAAS,EAAO,GAChE,MAAO,CACL,IAAK,IAAM,EAAI,QAAU,MACzB,EAAG,EACH,OAAQ,EAAQ,IAAM,EAAI,QAAmB,MAAkB,SAC/D,SAAU,IAAM,EAAI,EAAI,EACxB,SAAU,GAAO,IAAK,GAAM,EAAE,CAAC,CAAC,CAClC,CACF,CAAC,EACD,CAAE,IAAK,EAAM,IAAK,EAAS,EAAM,OAAQ,CAAY,CACvD,EAEA,SAAS,GAAS,EAAgC,CAChD,IAAM,EAAM,EAAO,QACnB,GAAI,CAAC,EAAK,OAAO,KACjB,IAAM,EAAO,EAAI,sBAAsB,EACjC,GAAS,EAAU,EAAK,MAAQ,EAAK,MAAS,EAC9C,EAAQ,GAAS,EAAO,GAAQ,EAAS,EAC3C,EAAU,EAAM,GAChB,EAAO,IACX,IAAK,IAAM,KAAK,EAAO,CACrB,IAAM,EAAI,KAAK,IAAI,EAAI,CAAK,EACxB,EAAI,IACN,EAAO,EACP,EAAU,EAEd,CACA,OAAO,CACT,CAEA,SAAS,GAAW,EAAsC,CACxD,IAAM,EAAI,GAAS,EAAE,OAAO,EAC5B,GAAI,IAAM,KAAM,OAChB,EAAU,CAAC,EACX,IAAM,EAAM,EAAO,QACnB,GAAI,EAAK,CACP,IAAM,EAAO,EAAI,sBAAsB,EACjC,EAAM,EAAO,CAAC,EAAI,EAAU,EAAK,MACvC,GAAc,CAAE,KAAM,EAAI,IAAK,CAAE,CAAC,CACpC,CACF,CAEA,IAAM,GACJ,IAAW,KAKP,KAJA,EAAO,IAAK,IAAO,CACjB,OAAQ,EACR,MAAO,EAAE,OAAO,KAAM,GAAM,EAAE,IAAM,CAAM,GAAK,IACjD,EAAE,EAGR,OACE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAO,KAAvB,SAAA,EACE,EAAA,EAAA,KAAA,CAAC,MAAD,CACE,IAAK,EACL,UAAW,EAAO,IAClB,QAAS,OAAO,EAAO,GAAG,IAC1B,KAAK,MACL,aAAY,EACZ,cAAe,GACf,mBAAsB,EAAU,IAAI,EAPtC,SAAA,CASG,EAAO,KAAK,EAAG,KACd,EAAA,EAAA,KAAA,CAAC,IAAD,CAAA,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,GAAI,EACJ,GAAI,EAAS,EACb,GAAI,EAAO,CAAC,EACZ,GAAI,EAAO,CAAC,EACZ,UAAW,EAAO,SAClB,OAAQ,CACT,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,EAAG,EAAO,EAAG,EAAG,EAAO,CAAC,EAAG,GAAG,SAAS,WAAW,MAAM,UAAW,EAAO,UAC7E,SAAA,EAAM,OAAO,EACV,CAAA,CACL,CAAA,EAZK,CAYL,CACJ,GACD,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,GAAI,EACJ,GAAI,EAAS,EACb,GAAI,EAAS,EACb,GAAI,EAAS,EACb,UAAW,EAAO,KAClB,OAAQ,CACT,CAAA,EACA,GAAQ,IAAK,IACZ,EAAA,EAAA,IAAA,CAAC,OAAD,CAAkB,kBAAiB,EAAE,IAAK,EAAG,EAAE,EAAG,EAAG,EAAS,EAAG,WAAY,EAAE,OAAQ,UAAW,EAAO,UACtG,SAAA,EAAE,IACC,EAFK,EAAE,GAEP,CACP,EAEA,EAAO,KAAK,EAAG,IAAO,CACrB,IAAM,EAAQ,EAAY,CAAE,EACtB,EAAO,GAAU,EAAE,MAAM,EAC/B,OACE,EAAA,EAAA,KAAA,CAAC,IAAD,CAAA,SAAA,CACG,GAAQ,EAAO,SAAW,EACvB,EAAK,KAAK,EAAK,IAAO,CACpB,IAAM,EACJ,EAAI,KAAK,EAAG,IAAM,GAAG,IAAM,EAAI,IAAM,MAAM,EAAO,EAAE,CAAC,EAAE,GAAG,EAAO,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EACjF,KAAK,EAAO,EAAI,EAAI,OAAS,EAAE,CAAC,CAAC,EAAE,GAAG,EAAO,CAAI,EAAE,IAAI,EAAO,EAAI,EAAE,CAAC,CAAC,EAAE,GAAG,EAAO,CAAI,EAAE,IAC1F,OAAO,EAAA,EAAA,IAAA,CAAC,OAAD,CAAkB,IAAG,KAAM,EAAO,UAAW,EAAO,IAAO,EAAhD,CAAgD,CACpE,CAAC,EACD,KACH,EAAK,KAAK,EAAK,KACd,EAAA,EAAA,IAAA,CAAC,OAAD,CAEE,EAAG,EAAI,KAAK,EAAG,IAAM,GAAG,IAAM,EAAI,IAAM,MAAM,EAAO,EAAE,CAAC,EAAE,GAAG,EAAO,EAAE,CAAC,GAAG,CAAC,CAAC,KAAK,GAAG,EACpF,OAAQ,EACR,UAAW,EAAO,IACnB,EAJM,CAIN,CACF,EACA,IAAiB,EAAK,YACd,CACL,IAAM,EAAU,EAAK,EAAK,OAAS,GAC7B,EAAO,EAAQ,EAAQ,OAAS,GACtC,OACE,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,iBAAgB,EAAE,GAClB,EAAG,EAAO,EAAK,CAAC,EAAI,EACpB,EAAG,IAAQ,IAAI,EAAE,EAAE,GAAK,EAAO,EAAK,CAAC,EACrC,GAAG,SACH,UAAW,EAAO,SAClB,KAAM,EAEL,SAAA,EAAE,KACC,CAAA,CAEV,EAAA,CAAG,EACD,IACH,CAAA,EAnCK,EAAE,EAmCP,CAEP,CAAC,EAEA,IAAW,KAQR,MAPF,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,GAAI,EAAO,CAAM,EACjB,GAAI,EAAO,CAAM,EACjB,GAAI,EACJ,GAAI,EAAS,EACb,UAAW,EAAO,aACnB,CAAA,EAEF,IAAW,KAeR,KAdA,EAAO,KAAK,EAAG,IAAO,CACpB,IAAM,EAAI,EAAE,OAAO,KAAM,GAAO,EAAG,IAAM,CAAM,EAE/C,MADI,CAAC,GAAK,EAAE,IAAM,KAAa,MAE7B,EAAA,EAAA,IAAA,CAAC,SAAD,CAEE,GAAI,EAAO,EAAE,CAAC,EACd,GAAI,EAAO,EAAE,CAAC,EACd,EAAG,EACH,KAAM,EAAY,CAAE,EACpB,UAAW,EAAO,MACnB,EANM,EAAE,EAMR,CAEL,CAAC,GAGL,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,EAAG,EACH,EAAG,EACH,MAAO,EACP,OAAQ,EACR,UAAW,EAAO,QAClB,YAAe,EAAU,EAAM,EAAM,OAAS,IAAM,IAAI,CACzD,CAAA,CACE,IACJ,IAAe,GACd,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAO,QAAS,MAAO,CAAE,KAAM,EAAW,KAAM,IAAK,EAAW,GAAI,EAApF,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,MAAD,CAAK,UAAW,EAAO,YAAc,SAAA,EAAQ,CAAgB,CAAO,CAAA,EACnE,GAAY,KAAK,CAAE,OAAQ,EAAG,SAAS,KACtC,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAO,WAAvB,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAO,WAAY,MAAO,CAAE,WAAY,EAAY,CAAC,CAAE,EAAG,cAAY,MAAQ,CAAA,EAC9F,GAAS,EAAM,IAAM,MACpB,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAO,aAAe,SAAA,GAAQ,EAAM,CAAC,CAAQ,CAAA,EAC7D,GAAa,EAAA,EAAA,IAAA,CAAC,OAAD,CAAA,SAAO,EAAE,KAAY,CAAA,EAAI,IACvC,CAAA,CAAA,GAEF,EAAA,EAAA,KAAA,CAAC,OAAD,CAAM,UAAW,EAAO,WAAxB,SAAA,CAAoC,UAAQ,EAAa,MAAM,EAAE,QAAU,EAAS,CAEnF,CAAA,CAAA,CAVmC,EAAA,EAAE,EAUrC,CACN,CACE,CACH,CAAA,EAAA,KACH,GACC,EAAA,EAAA,IAAA,CAAC,EAAD,CAAQ,MAAO,EAAO,KAAK,EAAG,KAAO,CAAE,MAAO,EAAE,MAAO,MAAO,EAAY,CAAC,EAAG,KAAM,MAAO,EAAE,CAAI,CAAA,EAC/F,IACD,GAET,+WE/VM,EAAU,GACV,EAAM,GAEZ,SAAgB,EAAS,CACvB,OACA,SAAS,IACT,cAAc,WACd,QAAQ,EACR,cAAe,GAAc,OAAO,CAAC,EACrC,aAQC,CACD,IAAM,GAAA,EAAM,EAAA,MAAA,CAAM,EACZ,CAAC,EAAO,IAAA,EAAY,EAAA,SAAA,CAAwB,IAAI,EAEhD,EAAQ,EAAK,IAAK,GAAM,EAAE,KAAK,CAAC,CAAC,OAAQ,GAAmB,IAAM,IAAI,EACtE,EAAS,KAAK,IAAI,EAAG,GAAG,EAAO,IAAM,EACrC,EAAS,KAAK,IAAI,EAAG,GAAG,CAAK,EAC7B,EAAQ,EAAS,GAAU,EAEjC,GAAI,IAAgB,aAAc,CAChC,IAEM,EAAO,KAAK,IAAI,EAAS,KAAK,IAAI,GAAI,KAAK,OAAO,EAAS,GAAO,EAAK,MAAM,EAAI,CAAG,CAAC,EAErF,EAAO,EAAK,QAAU,EAAO,GAAO,EAG1C,OACE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAO,KAAvB,SAAA,EACE,EAAA,EAAA,KAAA,CAAC,MAAD,CACE,UAAW,EAAO,IAClB,MAAO,IACP,OAAQ,EACR,QAAS,WAAe,IACxB,KAAK,MACL,aAAY,EANd,SAAA,EAQE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,GAAI,IAAS,GAAI,EAAG,GAAI,IAAS,GAAI,EAAM,UAAW,EAAO,SAAU,OAAQ,CAAa,CAAA,EACjG,EAAK,KAAK,EAAG,IAAM,CAClB,IAAM,EAAI,EAAM,GAAK,EAAO,GACtB,EAAO,EAAE,QAAU,KAA8C,GAArC,EAAE,MAAQ,GAAU,EAAS,IAC/D,OACE,EAAA,EAAA,KAAA,CAAC,IAAD,CAAA,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,EAAG,IACH,EAAG,EAAI,EAAO,EACd,GAAG,SACH,WAAW,MACX,UAAW,EAAO,cAEjB,SAAA,EAAE,MAAM,OAAS,GAAK,GAAG,EAAE,MAAM,MAAM,EAAG,EAAE,EAAE,GAAK,EAAE,KAClD,CAAA,EACL,EAAE,QAAU,MACX,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,GAAI,IACJ,GAAI,EAAI,EAAO,EACf,GAAI,IACJ,GAAI,EAAI,EAAO,EACf,OAAQ,EACR,YAAa,EACb,gBAAgB,KACjB,CAAA,GAED,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,EAAG,IACA,IACH,MAAO,KAAK,IAAI,EAAG,CAAI,EACvB,OAAQ,EACR,GAAI,EACJ,KAAM,EACN,UAAW,EAAO,GACnB,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,EAAG,IAAU,KAAK,IAAI,EAAG,CAAI,EAAI,EACjC,EAAG,EAAI,EAAO,EACd,GAAG,SACH,UAAW,EAAO,WAEjB,SAAA,EAAY,EAAE,KAAK,CAChB,CAAA,CACN,CAAA,CAAA,GAEJ,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,UAAW,EAAO,QAClB,EAAG,EACH,EAAG,EAAI,EAAM,EACb,MAAO,IACP,OAAQ,EAAO,EACf,SAAU,EACV,KAAK,MACL,aAAY,GAAG,EAAE,MAAM,IAAI,EAAE,QAAU,KAAO,UAAY,EAAY,EAAE,KAAK,IAC7E,mBAAsB,EAAS,CAAC,EAChC,mBAAsB,EAAU,GAAO,IAAM,EAAI,KAAO,CAAE,EAC1D,YAAe,EAAS,CAAC,EACzB,WAAc,EAAU,GAAO,IAAM,EAAI,KAAO,CAAE,CACnD,CAAA,CACA,CAAA,EAvDK,EAAE,MAAQ,CAuDf,CAEP,CAAC,CACE,CACJ,CAAA,EAAA,IAAU,KAUP,MATF,EAAA,EAAA,KAAA,CAAC,MAAD,CACE,UAAW,EAAO,QAClB,MAAO,CAAE,KAAM,IAAa,IAAK,EAAM,GAAS,EAAO,GAAO,CAAE,EAFlE,SAAA,EAIE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAO,aACrB,SAAA,EAAK,EAAM,CAAC,QAAU,KAAO,UAAY,EAAY,EAAK,EAAM,CAAC,KAAe,CAC7E,CAAA,GACN,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAO,aAAe,SAAA,EAAK,EAAM,CAAC,KAAY,CAAA,CAC5D,CACH,CAAA,CACD,GAET,CAGA,IAAM,EAAU,EAAK,OAAS,KAAK,IAAI,EAAS,CAAC,EAAI,EAC/C,EAAO,KAAK,IAAI,EAAS,CAAO,EAGhC,EAAQ,EACR,EAAO,EAAQ,GAAQ,GACvB,EAAO,EAAK,QAAU,EAAO,GAAO,EACpC,EAAQ,GAAQ,GAAU,EAAI,GAAU,EAAS,EAEvD,OACE,EAAA,EAAA,KAAA,CAAC,MAAD,CAAK,UAAW,EAAO,KAAvB,SAAA,EACE,EAAA,EAAA,KAAA,CAAC,MAAD,CACE,UAAW,EAAO,IAClB,MAAO,EACP,OAAQ,EACR,QAAS,OAAO,EAAK,GAAG,IACxB,KAAK,MACL,aAAY,EANd,SAAA,EAQE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,GAAI,EAAG,GAAI,EAAO,GAAI,EAAM,GAAI,EAAO,UAAW,EAAO,SAAU,OAAQ,CAAa,CAAA,EAC7F,EAAK,KAAK,EAAG,IAAM,CAClB,IAAM,EAAI,EAAM,GAAK,EAAO,GACtB,EAAO,EAAE,QAAU,KAAsD,EAA9C,KAAK,IAAI,EAAE,MAAQ,CAAM,EAAI,EAAS,EACjE,EAAM,EAAE,QAAU,MAAQ,EAAE,OAAS,EAAI,EAAQ,EAAO,EAC9D,OACE,EAAA,EAAA,KAAA,CAAC,IAAD,CAAA,SAAA,CACG,EAAE,QAAU,MACX,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,GAAI,EAAI,EAAO,EAAI,EACnB,GAAI,EACJ,GAAI,EAAI,EAAO,EAAI,EACnB,GAAI,EACJ,OAAQ,EACR,YAAa,EACb,gBAAgB,KACjB,CAAA,GAED,EAAA,EAAA,KAAA,CAAA,EAAA,SAAA,CAAA,SAAA,EACE,EAAA,EAAA,IAAA,CAAC,OAAD,CACK,IACH,EAAG,EACH,MAAO,EACP,OAAQ,KAAK,IAAI,EAAG,CAAI,EACxB,GAAI,EACJ,KAAM,EACN,UAAW,EAAO,GACnB,CAAA,GACD,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,EAAG,EAAI,EAAO,EAAG,EAAG,EAAM,EAAG,WAAW,SAAS,UAAW,EAAO,WACtE,SAAA,EAAY,EAAE,KAAK,CAChB,CAAA,CACN,CAAA,CAAA,GAEJ,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,EAAG,EAAI,EAAO,EACd,EAAG,EAAO,EACV,WAAW,SACX,UAAW,EAAO,UAEjB,SAAA,EAAE,MAAM,OAAS,GAAK,GAAG,EAAE,MAAM,MAAM,EAAG,CAAC,EAAE,GAAK,EAAE,KACjD,CAAA,GACN,EAAA,EAAA,IAAA,CAAC,OAAD,CACE,UAAW,EAAO,QAClB,EAAG,EAAI,EAAM,EACb,EAAG,EACH,MAAO,EAAO,EACd,OAAQ,EACR,SAAU,EACV,KAAK,MACL,aAAY,GAAG,EAAE,MAAM,IAAI,EAAE,QAAU,KAAO,UAAY,EAAY,EAAE,KAAK,IAC7E,mBAAsB,EAAS,CAAC,EAChC,mBAAsB,EAAU,GAAO,IAAM,EAAI,KAAO,CAAE,EAC1D,YAAe,EAAS,CAAC,EACzB,WAAc,EAAU,GAAO,IAAM,EAAI,KAAO,CAAE,CACnD,CAAA,CACA,CAAA,EAjDK,EAAE,MAAQ,CAiDf,CAEP,CAAC,CACE,CACJ,CAAA,EAAA,IAAU,KAWP,MAVF,EAAA,EAAA,KAAA,CAAC,MAAD,CACE,UAAW,EAAO,QAClB,MAAO,CAAE,KAAM,EAAM,GAAS,EAAO,GAAM,IAAK,CAAE,EAClD,GAAI,GAAG,EAAI,UAHb,SAAA,EAKE,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAO,aACrB,SAAA,EAAK,EAAM,CAAC,QAAU,KAAO,UAAY,EAAY,EAAK,EAAM,CAAC,KAAe,CAC7E,CAAA,GACN,EAAA,EAAA,IAAA,CAAC,OAAD,CAAM,UAAW,EAAO,aAAe,SAAA,EAAK,EAAM,CAAC,KAAY,CAAA,CAC5D,CACH,CAAA,CACD,GAET"}
|
|
1
|
+
{"version":3,"file":"charts-Bis9WSiS.js","names":[],"sources":["../../../../../../src/web-ui/components/charts/ChartContainer.module.css","../../../../../../src/web-ui/components/charts/ChartContainer.tsx","../../../../../../src/web-ui/components/charts/Legend.module.css","../../../../../../src/web-ui/components/charts/Legend.tsx","../../../../../../src/web-ui/components/charts/LineChart.module.css","../../../../../../src/web-ui/components/charts/LineChart.tsx","../../../../../../src/web-ui/components/charts/BarChart.module.css","../../../../../../src/web-ui/components/charts/BarChart.tsx"],"sourcesContent":[".figure {\n margin: 0;\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-lg);\n background: var(--bg-surface);\n padding: var(--space-4);\n /* Fixed heights must include the axis-label band — this container instead\n grows with its content so a chart's x-axis labels never get clipped or\n forced into a nested scroll (dataviz anti-patterns). */\n}\n\n.caption {\n display: flex;\n align-items: baseline;\n justify-content: space-between;\n gap: var(--space-3);\n margin-bottom: var(--space-3);\n}\n\n.title {\n font-size: var(--text-sm-size);\n font-weight: var(--font-weight-semibold);\n color: var(--text-primary);\n}\n\n.description {\n font-size: var(--text-xs-size);\n color: var(--text-tertiary);\n}\n\n.plot {\n width: 100%;\n overflow-x: auto;\n}\n\n.tableDisclosure {\n margin-top: var(--space-3);\n border-top: 1px solid var(--border-subtle);\n padding-top: var(--space-2);\n}\n\n.tableDisclosure > summary {\n cursor: pointer;\n font-size: var(--text-xs-size);\n color: var(--text-secondary);\n list-style: none;\n}\n\n.tableDisclosure > summary::-webkit-details-marker {\n display: none;\n}\n\n.tableDisclosure > summary::before {\n content: '▸ ';\n}\n\n.tableDisclosure[open] > summary::before {\n content: '▾ ';\n}\n\n.tableBody {\n margin-top: var(--space-2);\n}\n\n.caveat {\n margin: 0 0 var(--space-3);\n padding: var(--space-2) var(--space-3);\n border-radius: var(--radius-md);\n background: var(--status-unknown-bg);\n color: var(--status-unknown-fg);\n font-size: var(--text-xs-size);\n line-height: var(--text-sm-line);\n}\n\n.empty {\n color: var(--text-tertiary);\n font-size: var(--text-sm-size);\n padding: var(--space-6) 0;\n text-align: center;\n}\n","/**\n * components/charts/ChartContainer.tsx — the figure every chart mounts\n * inside: title/description, the plot itself, an optional caveat banner\n * (epistemic-honesty-adjacent: for a chart built on data with a known,\n * disclosed limitation — see ProductionView's judge-verdict caveat), an\n * optional table-view twin (the WCAG-clean accessibility equivalent every\n * chart should ship per the dataviz skill), and an empty state that never\n * lies about missing data.\n */\nimport type { ReactNode } from 'react';\nimport './chart-tokens.css';\nimport styles from './ChartContainer.module.css';\n\nexport interface ChartContainerProps {\n title: string;\n description?: string;\n /** A short, honest note about a known limitation of the data behind this\n * chart (e.g. \"counts may include X, not yet separable\"). Never omit a\n * caveat to make a chart look cleaner than the data actually is. */\n caveat?: string;\n /** The chart itself (SVG + legend). */\n children: ReactNode;\n /** The accessibility twin — same data as a real HTML table. Omit only\n * when the chart already IS a table. */\n table?: ReactNode;\n /** When true, renders the empty state instead of `children`. */\n empty?: boolean;\n emptyMessage?: string;\n}\n\nexport function ChartContainer({\n title,\n description,\n caveat,\n children,\n table,\n empty,\n emptyMessage = 'No data for this window.',\n}: ChartContainerProps) {\n return (\n <figure className={styles.figure}>\n <figcaption className={styles.caption}>\n <span className={styles.title}>{title}</span>\n {description ? <span className={styles.description}>{description}</span> : null}\n </figcaption>\n {caveat ? (\n <p className={styles.caveat} role=\"note\">\n {caveat}\n </p>\n ) : null}\n {empty ? (\n <p className={styles.empty}>{emptyMessage}</p>\n ) : (\n <div className={styles.plot}>{children}</div>\n )}\n {table ? (\n <details className={styles.tableDisclosure}>\n <summary>View as table</summary>\n <div className={styles.tableBody}>{table}</div>\n </details>\n ) : null}\n </figure>\n );\n}\n",".legend {\n display: flex;\n flex-wrap: wrap;\n gap: var(--space-1) var(--space-4);\n margin: var(--space-2) 0 0;\n padding: 0;\n list-style: none;\n}\n\n.item {\n display: inline-flex;\n align-items: center;\n gap: var(--space-2);\n font-size: var(--text-xs-size);\n color: var(--text-secondary);\n}\n\n.swatch {\n width: 10px;\n height: 10px;\n border-radius: var(--radius-full);\n flex: none;\n}\n\n.swatchLine {\n width: 14px;\n height: 2px;\n border-radius: var(--radius-full);\n flex: none;\n}\n","/**\n * components/charts/Legend.tsx — the dependable identity channel. A legend\n * is always present for two or more series (never rely on color-matching\n * alone); a single series needs no legend box because the chart's own title\n * already names it (see marks-and-anatomy.md \"Labels & legend\").\n */\nimport styles from './Legend.module.css';\n\nexport interface LegendItem {\n label: string;\n color: string;\n /** 'line' for line charts (a short stroke key), 'swatch' for bar/area. */\n kind?: 'line' | 'swatch';\n}\n\nexport function Legend({ items }: { items: LegendItem[] }) {\n if (items.length < 2) return null;\n return (\n <ul className={styles.legend}>\n {items.map((it) => (\n <li key={it.label} className={styles.item}>\n <span\n className={it.kind === 'line' ? styles.swatchLine : styles.swatch}\n style={{ background: it.color }}\n aria-hidden=\"true\"\n />\n {it.label}\n </li>\n ))}\n </ul>\n );\n}\n",".svg {\n display: block;\n width: 100%;\n height: auto;\n}\n\n.gridline {\n stroke: var(--chart-grid);\n stroke-width: 1;\n}\n\n.axis {\n stroke: var(--chart-axis);\n stroke-width: 1;\n}\n\n.axisLabel {\n font-size: var(--text-xs-size);\n fill: var(--text-tertiary);\n font-family: var(--font-ui);\n}\n\n.line {\n fill: none;\n stroke-width: 2;\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n\n.area {\n opacity: 0.1;\n stroke: none;\n}\n\n.marker {\n stroke: var(--chart-surface);\n stroke-width: 2;\n}\n\n.endLabel {\n font-size: var(--text-xs-size);\n font-family: var(--font-ui);\n font-weight: var(--font-weight-medium);\n}\n\n.crosshairLine {\n stroke: var(--chart-axis);\n stroke-width: 1;\n pointer-events: none;\n}\n\n.overlay {\n fill: transparent;\n cursor: crosshair;\n}\n\n.wrap {\n position: relative;\n}\n\n.tooltip {\n position: absolute;\n pointer-events: none;\n background: var(--chart-tooltip-bg);\n border: 1px solid var(--chart-tooltip-border);\n border-radius: var(--radius-sm);\n box-shadow: var(--shadow-md);\n padding: var(--space-2);\n font-size: var(--text-xs-size);\n color: var(--text-primary);\n white-space: nowrap;\n z-index: var(--z-dropdown);\n transform: translate(-50%, -100%);\n margin-top: -10px;\n}\n\n.tooltipDate {\n color: var(--text-tertiary);\n margin-bottom: var(--space-1);\n}\n\n.tooltipRow {\n display: flex;\n align-items: center;\n gap: var(--space-1);\n}\n\n.tooltipKey {\n width: 12px;\n height: 2px;\n flex: none;\n}\n\n.tooltipValue {\n font-weight: var(--font-weight-semibold);\n font-variant-numeric: tabular-nums;\n}\n\n.tooltipGap {\n color: var(--text-tertiary);\n font-style: italic;\n}\n","/**\n * components/charts/LineChart.tsx — trend over time. A gap (`null` y) is\n * split into a separate path segment so it renders as a visible break in\n * the line, never as a straight line through a missing value and never as\n * a silent zero. Single series gets an optional soft area fill; two-plus\n * series always render a legend (never color-matching alone).\n */\nimport { useRef, useState } from 'react';\nimport type { Series } from './types.js';\nimport { seriesColor, CHART_GRID, CHART_AXIS } from './colors.js';\nimport { Legend } from './Legend.js';\nimport {\n MIN_TIME_SPAN_MS,\n allIntegers,\n axisTicks,\n dodgeLabels,\n ensureSpan,\n isTimeAxis,\n labelCharPx,\n layoutAxisLabels,\n xKeeper,\n} from './chart-math.js';\nimport { timeLabelLadder } from './format.js';\nimport { useTextScale } from './useTextScale.js';\nimport './chart-tokens.css';\nimport styles from './LineChart.module.css';\n\nconst VBOX_W = 640;\n/** Minimum left gutter; it widens when the y tick labels need more (see padL). */\nconst PAD_L = 44;\nconst PAD_R = 12;\nconst PAD_T = 12;\nconst PAD_B = 24;\nconst TICKS_Y = 4;\n/* Direct end-of-line labels need a gutter of their own. They are drawn in USER\n units — the viewBox scales the font along with the geometry, so a reserve\n measured in user units holds at every rendered width — and `--text-xs-size`\n resolves to 12 user units here, where the UI sans averages a shade over 6\n units per character at medium weight. 6.4 rounds that up so the reserve errs\n wide rather than clipping. Without the reserve the label was drawn at\n `VBOX_W - PAD_R + 4` with only PAD_R (12u) of room and the svg's own\n `overflow: hidden` cropped it to its first glyph — \"Estimated spend\"\n rendered as a lone \"E\".\n Every text measure here (END_LABEL_CH, END_LABEL_GAP_Y, the axis label\n advance) is for 12 px text and is multiplied by the operator's Display\n size (useTextScale): --text-xs-size is 12px × --ui-text-scale, so at\n XLarge the same label is 15 units tall and 25% wider. END_LABEL_MAX is a\n budget of plot width, not a text measure, and stays as it is. */\nconst END_LABEL_CH = 6.4;\nconst END_LABEL_GAP = 4;\n/* Past this the gutter would cost more plot than the label is worth, so the\n label is dropped rather than shrinking the chart around it. Nothing is lost:\n Legend.tsx deliberately renders nothing below two series because the panel\n heading already names a lone series, and two-plus series always have the\n legend regardless. */\nconst END_LABEL_MAX = 150;\n/** Line height of a direct end label at 12 px (user units): two closer than this overlap. */\nconst END_LABEL_GAP_Y = 14;\n\ninterface Run {\n x: number;\n y: number;\n}\n\nfunction splitRuns(points: Series['points']): Run[][] {\n const runs: Run[][] = [];\n let current: Run[] = [];\n for (const p of points) {\n if (p.y === null) {\n if (current.length) runs.push(current);\n current = [];\n continue;\n }\n current.push({ x: p.x, y: p.y });\n }\n if (current.length) runs.push(current);\n return runs;\n}\n\nexport function LineChart({\n series: seriesProp,\n height = 200,\n area = false,\n formatX: formatXProp,\n formatY: formatYProp,\n ariaLabel,\n}: {\n series: Series[];\n height?: number;\n area?: boolean;\n formatX?: (x: number) => string;\n formatY?: (y: number) => string;\n ariaLabel: string;\n}) {\n const svgRef = useRef<SVGSVGElement>(null);\n const textScale = useTextScale();\n /* Axis labels' estimated advance per character, at the size they render. */\n const axisLabelCh = labelCharPx(textScale);\n const [hoverX, setHoverX] = useState<number | null>(null);\n const [tooltipPos, setTooltipPos] = useState<{ left: number; top: number } | null>(null);\n\n const formatX = formatXProp ?? ((x: number) => String(x));\n const formatY = formatYProp ?? ((y: number) => String(y));\n /* On a time axis a 0 / NaN / pre-2000 x is a null timestamp that leaked\n through, not a moment: it would start the axis at \"Dec 31\" 1969. Such\n points are left out of the domain AND the drawing. */\n const keepX = xKeeper(seriesProp.flatMap((s) => s.points.map((p) => p.x)));\n const series = seriesProp.map((s) => ({ ...s, points: s.points.filter((p) => keepX(p.x)) }));\n const allX = series.flatMap((s) => s.points.map((p) => p.x));\n const knownY = series.flatMap((s) => s.points.map((p) => p.y)).filter((y): y is number => y !== null && Number.isFinite(y));\n if (allX.length === 0) {\n return <p>No data.</p>;\n }\n const rawXMin = Math.min(...allX);\n const rawXMax = Math.max(...allX);\n const timeAxis = isTimeAxis(allX);\n // A burst of points seconds apart is widened to MIN_TIME_SPAN_MS around\n // itself instead of stretched edge to edge under identical labels.\n const [xMin, xMax] = timeAxis && rawXMax > rawXMin ? ensureSpan(rawXMin, rawXMax, MIN_TIME_SPAN_MS) : [rawXMin, rawXMax];\n const xRange = xMax - xMin || 1;\n\n // Nice ticks whose labels are exact: integer data get integer ticks, and a\n // caller's formatter never prints a rounded label on a precise tick.\n const yAxis = axisTicks(Math.min(0, ...knownY), Math.max(0, ...knownY), {\n count: TICKS_Y,\n integer: knownY.length > 0 && allIntegers(knownY),\n format: formatYProp,\n });\n const yTicks = yAxis.ticks;\n const yMin = yTicks[0]!;\n const yMax = yTicks[yTicks.length - 1]!;\n const yRange = yMax - yMin || 1;\n const plotH = height - PAD_T - PAD_B;\n const yScale = (y: number) => PAD_T + plotH - ((y - yMin) / yRange) * plotH;\n /* The y labels hang right-aligned 6 units left of the plot, and the svg\n clips at its edge: the gutter grows to fit the widest one (+2 of air) —\n 44 still fits five 12 px characters, a larger Display size fewer. */\n const padL = Math.max(PAD_L, Math.ceil(Math.max(0, ...yAxis.labels.map((l) => l.length)) * axisLabelCh) + 8);\n\n /* Decided before the x scale, because the reserved gutter is what plotW is\n measured against — and the gridlines and x-axis stop at the same edge.\n End labels are dodged vertically so two lines ending on one value never\n print their names over each other; if they cannot all fit, the legend\n (always present for 2+ series) names them instead. */\n const wantEndLabels = series.length >= 1 && series.length <= 4;\n const endLabelW = wantEndLabels\n ? Math.max(...series.map((s) => s.label.length)) * END_LABEL_CH * textScale\n : 0;\n const endPoints = series.flatMap((s) => {\n for (let i = s.points.length - 1; i >= 0; i--) {\n const y = s.points[i]!.y;\n if (y !== null && Number.isFinite(y)) return [{ key: s.id, y: yScale(y) }];\n }\n return [];\n });\n const dodged = wantEndLabels && endLabelW <= END_LABEL_MAX ? dodgeLabels(endPoints, END_LABEL_GAP_Y * textScale, PAD_T, PAD_T + plotH) : null;\n const showEndLabels = dodged !== null && endPoints.length > 0;\n const padR = PAD_R + (showEndLabels ? END_LABEL_GAP + endLabelW : 0);\n const showLegend = series.length >= 2;\n\n const plotW = VBOX_W - padL - padR;\n const xScale = (x: number) => padL + ((x - xMin) / xRange) * plotW;\n\n // Nearest-x lookup across a reference axis (the union of all distinct x\n // values, since series may not share every point).\n const xAxis = Array.from(new Set(allX)).sort((a, b) => a - b);\n\n /* First and last x labels, collision-free: the end outranks the start;\n on a time axis both walk one detail ladder (the caller's format → a\n shorter date/time; clock time only inside one day) until they fit. */\n const xEnds = xAxis.length > 1 ? [xAxis[0]!, xAxis[xAxis.length - 1]!] : [];\n const ladder = timeAxis ? timeLabelLadder(rawXMin, rawXMax, formatXProp, xEnds) : [formatX];\n const xLabels = layoutAxisLabels(\n xEnds.map((x, i) => {\n const px = xScale(x);\n const edge = i === 0 ? px <= padL + 0.5 : px >= VBOX_W - padR - 0.5;\n return {\n key: i === 0 ? 'start' : 'end',\n x: px,\n anchor: edge ? (i === 0 ? 'start' as const : 'end' as const) : 'middle' as const,\n priority: i === 0 ? 2 : 3,\n variants: ladder.map((f) => f(x)),\n };\n }),\n { min: padL, max: VBOX_W - padR, charPx: axisLabelCh },\n );\n\n function nearestX(clientX: number): number | null {\n const svg = svgRef.current;\n if (!svg) return null;\n const rect = svg.getBoundingClientRect();\n const svgX = ((clientX - rect.left) / rect.width) * VBOX_W;\n const dataX = xMin + ((svgX - padL) / plotW) * xRange;\n let nearest = xAxis[0];\n let best = Infinity;\n for (const x of xAxis) {\n const d = Math.abs(x - dataX);\n if (d < best) {\n best = d;\n nearest = x;\n }\n }\n return nearest;\n }\n\n function handleMove(e: React.PointerEvent<SVGSVGElement>) {\n const x = nearestX(e.clientX);\n if (x === null) return;\n setHoverX(x);\n const svg = svgRef.current;\n if (svg) {\n const rect = svg.getBoundingClientRect();\n const px = (xScale(x) / VBOX_W) * rect.width;\n setTooltipPos({ left: px, top: 0 });\n }\n }\n\n const hoverPoints =\n hoverX !== null\n ? series.map((s) => ({\n series: s,\n point: s.points.find((p) => p.x === hoverX) ?? null,\n }))\n : null;\n\n return (\n <div className={styles.wrap}>\n <svg\n ref={svgRef}\n className={styles.svg}\n viewBox={`0 0 ${VBOX_W} ${height}`}\n role=\"img\"\n aria-label={ariaLabel}\n onPointerMove={handleMove}\n onPointerLeave={() => setHoverX(null)}\n >\n {yTicks.map((t, i) => (\n <g key={i}>\n <line\n x1={padL}\n x2={VBOX_W - padR}\n y1={yScale(t)}\n y2={yScale(t)}\n className={styles.gridline}\n stroke={CHART_GRID}\n />\n <text x={padL - 6} y={yScale(t)} dy=\"0.32em\" textAnchor=\"end\" className={styles.axisLabel}>\n {yAxis.labels[i]}\n </text>\n </g>\n ))}\n <line\n x1={padL}\n x2={VBOX_W - padR}\n y1={height - PAD_B}\n y2={height - PAD_B}\n className={styles.axis}\n stroke={CHART_AXIS}\n />\n {xLabels.map((l) => (\n <text key={l.key} data-axis-label={l.key} x={l.x} y={height - 6} textAnchor={l.anchor} className={styles.axisLabel}>\n {l.text}\n </text>\n ))}\n\n {series.map((s, si) => {\n const color = seriesColor(si);\n const runs = splitRuns(s.points);\n return (\n <g key={s.id}>\n {area && series.length === 1\n ? runs.map((run, ri) => {\n const d =\n run.map((p, j) => `${j === 0 ? 'M' : 'L'}${xScale(p.x)},${yScale(p.y)}`).join(' ') +\n ` L${xScale(run[run.length - 1].x)},${yScale(yMin)} L${xScale(run[0].x)},${yScale(yMin)} Z`;\n return <path key={ri} d={d} fill={color} className={styles.area} />;\n })\n : null}\n {runs.map((run, ri) => (\n <path\n key={ri}\n d={run.map((p, j) => `${j === 0 ? 'M' : 'L'}${xScale(p.x)},${yScale(p.y)}`).join(' ')}\n stroke={color}\n className={styles.line}\n />\n ))}\n {showEndLabels && runs.length ? (\n (() => {\n const lastRun = runs[runs.length - 1];\n const last = lastRun[lastRun.length - 1];\n return (\n <text\n data-end-label={s.id}\n x={xScale(last.x) + END_LABEL_GAP}\n y={dodged?.get(s.id) ?? yScale(last.y)}\n dy=\"0.32em\"\n className={styles.endLabel}\n fill={color}\n >\n {s.label}\n </text>\n );\n })()\n ) : null}\n </g>\n );\n })}\n\n {hoverX !== null ? (\n <line\n x1={xScale(hoverX)}\n x2={xScale(hoverX)}\n y1={PAD_T}\n y2={height - PAD_B}\n className={styles.crosshairLine}\n />\n ) : null}\n {hoverX !== null\n ? series.map((s, si) => {\n const p = s.points.find((pt) => pt.x === hoverX);\n if (!p || p.y === null) return null;\n return (\n <circle\n key={s.id}\n cx={xScale(p.x)}\n cy={yScale(p.y)}\n r={4}\n fill={seriesColor(si)}\n className={styles.marker}\n />\n );\n })\n : null}\n\n <rect\n x={padL}\n y={PAD_T}\n width={plotW}\n height={plotH}\n className={styles.overlay}\n onFocus={() => setHoverX(xAxis[xAxis.length - 1] ?? null)}\n />\n </svg>\n {hoverPoints && tooltipPos ? (\n <div className={styles.tooltip} style={{ left: tooltipPos.left, top: tooltipPos.top }}>\n <div className={styles.tooltipDate}>{formatX(hoverX as number)}</div>\n {hoverPoints.map(({ series: s, point }, i) => (\n <div className={styles.tooltipRow} key={s.id}>\n <span className={styles.tooltipKey} style={{ background: seriesColor(i) }} aria-hidden=\"true\" />\n {point && point.y !== null ? (\n <>\n <span className={styles.tooltipValue}>{formatY(point.y)}</span>\n {showLegend ? <span>{s.label}</span> : null}\n </>\n ) : (\n <span className={styles.tooltipGap}>no data{showLegend ? ` — ${s.label}` : ''}</span>\n )}\n </div>\n ))}\n </div>\n ) : null}\n {showLegend ? (\n <Legend items={series.map((s, i) => ({ label: s.label, color: seriesColor(i), kind: 'line' }))} />\n ) : null}\n </div>\n );\n}\n",".svg {\n display: block;\n overflow: visible;\n}\n\n.bar {\n transition: opacity var(--duration-fast) var(--ease-standard);\n}\n\n.bar:hover,\n.bar:focus-visible {\n opacity: 0.82;\n}\n\n.baseline {\n stroke: var(--chart-axis);\n stroke-width: 1;\n}\n\n.gridline {\n stroke: var(--chart-grid);\n stroke-width: 1;\n}\n\n.axisLabel {\n font-size: 10px;\n fill: var(--text-tertiary);\n font-family: var(--font-ui);\n}\n\n.valueLabel {\n font-size: 10px;\n fill: var(--text-secondary);\n font-family: var(--font-ui);\n font-variant-numeric: tabular-nums;\n}\n\n.categoryLabel {\n font-size: 10px;\n fill: var(--text-secondary);\n font-family: var(--font-ui);\n}\n\n.hitArea {\n fill: transparent;\n cursor: pointer;\n}\n\n.tooltip {\n position: absolute;\n pointer-events: none;\n background: var(--chart-tooltip-bg);\n border: 1px solid var(--chart-tooltip-border);\n border-radius: var(--radius-sm);\n box-shadow: var(--shadow-md);\n padding: var(--space-1) var(--space-2);\n font-size: var(--text-xs-size);\n color: var(--text-primary);\n white-space: nowrap;\n z-index: var(--z-dropdown);\n}\n\n.tooltipValue {\n font-weight: var(--font-weight-semibold);\n font-variant-numeric: tabular-nums;\n}\n\n.tooltipLabel {\n color: var(--text-tertiary);\n margin-left: var(--space-1);\n}\n\n.wrap {\n position: relative;\n}\n","/**\n * components/charts/BarChart.tsx — categorical magnitude comparison.\n * Single hue by default (nominal categories never get a value-ramp — see\n * dataviz anti-patterns \"a value-ramp on nominal categories\"). Vertical for\n * a handful of short-named categories, horizontal for many or long-named\n * ones (dataviz choosing-a-form.md). Null values render as an explicit\n * \"no data\" dash, never a silent zero-height bar indistinguishable from a\n * real zero.\n */\nimport { useId, useState } from 'react';\nimport type { CategoricalDatum } from './types.js';\nimport { CHART_SEQUENTIAL, CHART_AXIS, CHART_GRID } from './colors.js';\nimport './chart-tokens.css';\nimport styles from './BarChart.module.css';\n\nconst BAR_MAX = 24;\nconst GAP = 10;\n\nexport function BarChart({\n data,\n height = 160,\n orientation = 'vertical',\n color = CHART_SEQUENTIAL,\n formatValue = (v: number) => String(v),\n ariaLabel,\n}: {\n data: CategoricalDatum[];\n height?: number;\n orientation?: 'vertical' | 'horizontal';\n color?: string;\n formatValue?: (v: number) => string;\n ariaLabel: string;\n}) {\n const uid = useId();\n const [hover, setHover] = useState<number | null>(null);\n\n const known = data.map((d) => d.value).filter((v): v is number => v !== null);\n const maxVal = Math.max(0, ...known, 0.0001);\n const minVal = Math.min(0, ...known);\n const range = maxVal - minVal || 1;\n\n if (orientation === 'horizontal') {\n const LABEL_W = 120;\n const PAD_R = 48;\n const rowH = Math.min(BAR_MAX, Math.max(14, Math.floor((height - GAP) / data.length) - GAP));\n const plotW = 420;\n const svgH = data.length * (rowH + GAP) + GAP;\n const svgW = LABEL_W + plotW + PAD_R;\n\n return (\n <div className={styles.wrap}>\n <svg\n className={styles.svg}\n width={svgW}\n height={svgH}\n viewBox={`0 0 ${svgW} ${svgH}`}\n role=\"img\"\n aria-label={ariaLabel}\n >\n <line x1={LABEL_W} y1={0} x2={LABEL_W} y2={svgH} className={styles.baseline} stroke={CHART_AXIS} />\n {data.map((d, i) => {\n const y = GAP + i * (rowH + GAP);\n const barW = d.value !== null ? ((d.value - minVal) / range) * plotW : 0;\n return (\n <g key={d.label + i}>\n <text\n x={LABEL_W - 8}\n y={y + rowH / 2}\n dy=\"0.32em\"\n textAnchor=\"end\"\n className={styles.categoryLabel}\n >\n {d.label.length > 22 ? `${d.label.slice(0, 21)}…` : d.label}\n </text>\n {d.value === null ? (\n <line\n x1={LABEL_W + 4}\n y1={y + rowH / 2}\n x2={LABEL_W + 14}\n y2={y + rowH / 2}\n stroke={CHART_GRID}\n strokeWidth={2}\n strokeDasharray=\"2 2\"\n />\n ) : (\n <>\n <rect\n x={LABEL_W}\n y={y}\n width={Math.max(2, barW)}\n height={rowH}\n rx={4}\n fill={color}\n className={styles.bar}\n />\n <text\n x={LABEL_W + Math.max(2, barW) + 6}\n y={y + rowH / 2}\n dy=\"0.32em\"\n className={styles.valueLabel}\n >\n {formatValue(d.value)}\n </text>\n </>\n )}\n <rect\n className={styles.hitArea}\n x={0}\n y={y - GAP / 2}\n width={svgW}\n height={rowH + GAP}\n tabIndex={0}\n role=\"img\"\n aria-label={`${d.label}: ${d.value === null ? 'no data' : formatValue(d.value)}`}\n onPointerEnter={() => setHover(i)}\n onPointerLeave={() => setHover((h) => (h === i ? null : h))}\n onFocus={() => setHover(i)}\n onBlur={() => setHover((h) => (h === i ? null : h))}\n />\n </g>\n );\n })}\n </svg>\n {hover !== null ? (\n <div\n className={styles.tooltip}\n style={{ left: LABEL_W + 8, top: GAP + hover * (rowH + GAP) - 4 }}\n >\n <span className={styles.tooltipValue}>\n {data[hover].value === null ? 'no data' : formatValue(data[hover].value as number)}\n </span>\n <span className={styles.tooltipLabel}>{data[hover].label}</span>\n </div>\n ) : null}\n </div>\n );\n }\n\n // Vertical orientation.\n const barSlot = data.length ? Math.max(BAR_MAX, 0) : BAR_MAX;\n const barW = Math.min(BAR_MAX, barSlot);\n const PAD_T = 20;\n const PAD_B = 30;\n const plotH = height;\n const svgH = plotH + PAD_T + PAD_B;\n const svgW = data.length * (barW + GAP) + GAP;\n const zeroY = PAD_T + plotH - ((0 - minVal) / range) * plotH;\n\n return (\n <div className={styles.wrap}>\n <svg\n className={styles.svg}\n width={svgW}\n height={svgH}\n viewBox={`0 0 ${svgW} ${svgH}`}\n role=\"img\"\n aria-label={ariaLabel}\n >\n <line x1={0} y1={zeroY} x2={svgW} y2={zeroY} className={styles.baseline} stroke={CHART_AXIS} />\n {data.map((d, i) => {\n const x = GAP + i * (barW + GAP);\n const barH = d.value !== null ? (Math.abs(d.value - minVal) / range) * plotH : 0;\n const top = d.value !== null && d.value >= 0 ? zeroY - barH : zeroY;\n return (\n <g key={d.label + i}>\n {d.value === null ? (\n <line\n x1={x + barW / 2 - 5}\n y1={zeroY}\n x2={x + barW / 2 + 5}\n y2={zeroY}\n stroke={CHART_GRID}\n strokeWidth={2}\n strokeDasharray=\"2 2\"\n />\n ) : (\n <>\n <rect\n x={x}\n y={top}\n width={barW}\n height={Math.max(2, barH)}\n rx={4}\n fill={color}\n className={styles.bar}\n />\n <text x={x + barW / 2} y={top - 4} textAnchor=\"middle\" className={styles.valueLabel}>\n {formatValue(d.value)}\n </text>\n </>\n )}\n <text\n x={x + barW / 2}\n y={svgH - 8}\n textAnchor=\"middle\"\n className={styles.axisLabel}\n >\n {d.label.length > 10 ? `${d.label.slice(0, 9)}…` : d.label}\n </text>\n <rect\n className={styles.hitArea}\n x={x - GAP / 2}\n y={0}\n width={barW + GAP}\n height={svgH}\n tabIndex={0}\n role=\"img\"\n aria-label={`${d.label}: ${d.value === null ? 'no data' : formatValue(d.value)}`}\n onPointerEnter={() => setHover(i)}\n onPointerLeave={() => setHover((h) => (h === i ? null : h))}\n onFocus={() => setHover(i)}\n onBlur={() => setHover((h) => (h === i ? null : h))}\n />\n </g>\n );\n })}\n </svg>\n {hover !== null ? (\n <div\n className={styles.tooltip}\n style={{ left: GAP + hover * (barW + GAP), top: 0 }}\n id={`${uid}-tooltip`}\n >\n <span className={styles.tooltipValue}>\n {data[hover].value === null ? 'no data' : formatValue(data[hover].value as number)}\n </span>\n <span className={styles.tooltipLabel}>{data[hover].label}</span>\n </div>\n ) : null}\n </div>\n );\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{D as e,F as t,U as n,c as r,l as i,o as a}from"./global-BkNl4e6E.js";import{F as o}from"./VerseConsoleApp-CTF3Shs0.js";import{c as s,d as c,l,o as u,r as d,s as f,u as p}from"./types-D5XOdyw8.js";var m=`verse-cloud`,h=3e4,g=class extends Error{constructor(){super(`Unlock actions with the mutation token first.`),this.name=`CloudLockedError`}},_=class extends Error{failure;constructor(e,t){super(e),this.failure=t,this.name=`CloudLaunchRefusedError`}};function v(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function y(e){if(!v(e))return null;let{seat:t,budget:n,tasks:r,backlog:i}=e;if(!v(t)||typeof t.ready!=`boolean`||!v(n)||!v(n.budget)||!v(n.canLaunch)||!v(n.canSelfImprove))return null;for(let e of[`creditsTotalUsd`,`estimatedSpentUsd`,`estimatedRemainingUsd`,`sessionsToday`,`selfImproveToday`,`running`])if(typeof n[e]!=`number`||!Number.isFinite(n[e]))return null;return!Array.isArray(r)||!r.every(e=>v(e)&&typeof e.id==`string`&&typeof e.state==`string`)||!v(i)||!Array.isArray(i.items)?null:e}function b(e){return e instanceof a?e.status===404?`The cloud lane is not in this build yet.`:e.status===503?`The cloud lane failed to load on the server.`:`The cloud lane answered HTTP ${e.status}.`:`The cloud lane could not be reached.`}var x={key:m,fetch:async e=>{try{let t=y(await r(l,e));return t===null?{value:null,available:!0,reason:`The cloud lane answered in a shape this version does not recognise, so nothing is shown rather than guessed.`}:{value:t,available:!0,reason:null}}catch(e){if(e instanceof a&&e.status===401||e instanceof DOMException&&e.name===`AbortError`)throw e;return{value:null,available:!1,reason:b(e)}}}};async function S(n,r){let a=e();if(!a)throw new g;let o=await i(n,r,a);return t(),o}function C(){n(m),o()}async function w(e){try{let t=await S(s,e);if(!v(t)||t.ok!==!0||!v(t.task))throw new _(v(t)&&typeof t.error==`string`&&t.error?t.error:`The cloud session was not started, and the server sent no reason.`,v(t)&&typeof t.failure==`string`?t.failure:null);return t}finally{C()}}async function T(e){let t=await S(u,e);return n(m),t}async function E(e={count:1}){try{let t=await S(f,e);return{launched:v(t)&&Array.isArray(t.launched)?t.launched:[],skipped:v(t)&&Array.isArray(t.skipped)?t.skipped:[]}}finally{C()}}async function D(){try{return await S(p,{})}finally{C()}}async function O(e){if(!d.test(e))throw Error(`That task id is not one Verse issued, so nothing was sent.`);try{return await S(`${c}/${e}/dismiss`,{})}finally{C()}}export{D as a,w as i,x as n,E as o,O as r,T as s,h as t};
|
|
2
|
+
//# sourceMappingURL=cloud-queries-BsOepFz6.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud-queries-CnV1yaNG.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/cloud/cloud-queries.ts"],"sourcesContent":["/**\n * routes/verse/cloud/cloud-queries.ts — the one read and the writes behind\n * the cloud lane's UI (3.11 unit C3; server: core/cloud/cloud-api.ts, C2).\n *\n * GET /api/verse/cloud CloudOverviewResponse\n * POST /api/verse/cloud/launch CloudLaunchRequest → CloudLaunchResponse\n * POST /api/verse/cloud/budget CloudBudgetUpdate\n * POST /api/verse/cloud/refresh (re-read tasks from GitHub)\n * POST /api/verse/cloud/improve CloudImproveRequest → CloudImproveResponse\n * POST /api/verse/cloud/tasks/<id>/dismiss (mark closed; never touches GitHub)\n *\n * WHY the read is OPTIONAL (the surface-data.ts rule): the routes land in a\n * parallel unit, and a server without them answers 404. That is \"this build\n * has no cloud lane yet\" — one card's designed state — never a broken\n * Command. 401 and aborts still propagate: an expired read session is the\n * whole app's state.\n *\n * LAZY ONLY. Everything that imports this file (Command's card, the Usage\n * panel, the Fleet chip, the composer's \"Run in cloud\") is itself behind an\n * import(); nothing cloud-related may cost chat first-paint bytes.\n *\n * Writes pull the held mutation token (CloudLockedError without one — the\n * callers route every write through a token gate that opens the unlock\n * dialog first), then invalidate the overview so every cloud view moves\n * together, and nudge the shell's activity poll (Needs-you carries cloud\n * items).\n */\nimport {\n CLOUD_TASK_ID_PATTERN,\n VERSE_CLOUD_BUDGET_PATH,\n VERSE_CLOUD_IMPROVE_PATH,\n VERSE_CLOUD_LAUNCH_PATH,\n VERSE_CLOUD_PATH,\n VERSE_CLOUD_REFRESH_PATH,\n VERSE_CLOUD_TASKS_PATH,\n type CloudBudgetUpdate,\n type CloudImproveRequest,\n type CloudImproveResponse,\n type CloudLaunchFailureCode,\n type CloudLaunchRequest,\n type CloudLaunchResponse,\n type CloudOverviewResponse,\n} from '../../../../core/cloud/types.js';\nimport { ApiError, apiGet, apiPost } from '../../../data/client.js';\nimport { getMutationToken, touchMutationHold } from '../../../data/auth-store.js';\nimport { invalidate } from '../../../data/cache.js';\nimport type { QueryDef } from '../../../data/queries.js';\nimport { refreshActivity } from '../shell/useActivity.js';\nimport type { OptionalRead } from '../command/surface-data.js';\n\nexport const CLOUD_KEY = 'verse-cloud';\n\n/** The card, the chip and the panel all poll at this pace while visible (SPEC 3.11 C3). */\nexport const CLOUD_POLL_MS = 30_000;\n\nexport class CloudLockedError extends Error {\n constructor() {\n super('Unlock actions with the mutation token first.');\n this.name = 'CloudLockedError';\n }\n}\n\n/**\n * The server answered a launch with `ok: false` — a budget gate, a missing\n * seat, a CLI that printed no session. `message` is the server's own\n * sentence; `failure` the machine code for tests and chips.\n */\nexport class CloudLaunchRefusedError extends Error {\n constructor(message: string, public readonly failure: CloudLaunchFailureCode | null) {\n super(message);\n this.name = 'CloudLaunchRefusedError';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * Just enough structure that no render can crash on `undefined.map` or read\n * a budget field that is not there. Field-level honesty (null = unknown) is\n * the model's job.\n */\nexport function narrowCloudOverview(raw: unknown): CloudOverviewResponse | null {\n if (!isRecord(raw)) return null;\n const { seat, budget, tasks, backlog } = raw;\n if (!isRecord(seat) || typeof seat['ready'] !== 'boolean') return null;\n if (!isRecord(budget) || !isRecord(budget['budget']) || !isRecord(budget['canLaunch']) || !isRecord(budget['canSelfImprove'])) return null;\n for (const key of ['creditsTotalUsd', 'estimatedSpentUsd', 'estimatedRemainingUsd', 'sessionsToday', 'selfImproveToday', 'running']) {\n if (typeof budget[key] !== 'number' || !Number.isFinite(budget[key])) return null;\n }\n if (!Array.isArray(tasks) || !tasks.every((t) => isRecord(t) && typeof t['id'] === 'string' && typeof t['state'] === 'string')) return null;\n if (!isRecord(backlog) || !Array.isArray(backlog['items'])) return null;\n return raw as unknown as CloudOverviewResponse;\n}\n\n/** Operator words for a cloud read that did not answer — never a path, never a trace. */\nfunction absence(err: unknown): string {\n if (err instanceof ApiError) {\n if (err.status === 404) return 'The cloud lane is not in this build yet.';\n if (err.status === 503) return 'The cloud lane failed to load on the server.';\n return `The cloud lane answered HTTP ${err.status}.`;\n }\n return 'The cloud lane could not be reached.';\n}\n\nexport const cloudQuery: QueryDef<OptionalRead<CloudOverviewResponse>> = {\n key: CLOUD_KEY,\n fetch: async (signal) => {\n try {\n const value = narrowCloudOverview(await apiGet<unknown>(VERSE_CLOUD_PATH, signal));\n if (value === null) {\n return { value: null, available: true, reason: 'The cloud lane answered in a shape this version does not recognise, so nothing is shown rather than guessed.' };\n }\n return { value, available: true, reason: null };\n } catch (err) {\n if (err instanceof ApiError && err.status === 401) throw err;\n if (err instanceof DOMException && err.name === 'AbortError') throw err;\n return { value: null, available: false, reason: absence(err) };\n }\n },\n};\n\nasync function post<T>(path: string, body: unknown): Promise<T> {\n const token = getMutationToken();\n if (!token) throw new CloudLockedError();\n const result = await apiPost<T>(path, body, token);\n touchMutationHold();\n return result;\n}\n\nfunction settled(): void {\n invalidate(CLOUD_KEY);\n void refreshActivity();\n}\n\n/**\n * Launch one cloud session. Resolves only on `ok: true` with a task; a\n * refusal the server words in a 200 body is thrown as CloudLaunchRefusedError\n * so every caller shows it the same way as a 4xx refusal (whose sentence is\n * `ApiError.detail`). Invalidates either way — a failed launch is still a\n * recorded task.\n */\nexport async function launchCloudTask(request: CloudLaunchRequest): Promise<CloudLaunchResponse & { task: NonNullable<CloudLaunchResponse['task']> }> {\n try {\n const response = await post<CloudLaunchResponse>(VERSE_CLOUD_LAUNCH_PATH, request);\n if (!isRecord(response) || response.ok !== true || !isRecord(response.task)) {\n const error = isRecord(response) && typeof response.error === 'string' && response.error ? response.error : 'The cloud session was not started, and the server sent no reason.';\n const failure = isRecord(response) && typeof response.failure === 'string' ? response.failure : null;\n throw new CloudLaunchRefusedError(error, failure);\n }\n return response as CloudLaunchResponse & { task: NonNullable<CloudLaunchResponse['task']> };\n } finally {\n settled();\n }\n}\n\n/** One budget change (any subset; the server validates and clamps). */\nexport async function updateCloudBudget(update: CloudBudgetUpdate): Promise<unknown> {\n const result = await post<unknown>(VERSE_CLOUD_BUDGET_PATH, update);\n invalidate(CLOUD_KEY);\n return result;\n}\n\n/** Launch up to `count` backlog items now (the \"Improve Verse\" button). */\nexport async function runCloudImprove(request: CloudImproveRequest = { count: 1 }): Promise<CloudImproveResponse> {\n try {\n const result = await post<unknown>(VERSE_CLOUD_IMPROVE_PATH, request);\n const launched = isRecord(result) && Array.isArray(result['launched']) ? (result['launched'] as CloudImproveResponse['launched']) : [];\n const skipped = isRecord(result) && Array.isArray(result['skipped']) ? (result['skipped'] as CloudImproveResponse['skipped']) : [];\n return { launched, skipped };\n } finally {\n settled();\n }\n}\n\n/** Ask the server to re-read every open task from GitHub now. */\nexport async function refreshCloudTasks(): Promise<unknown> {\n try {\n return await post<unknown>(VERSE_CLOUD_REFRESH_PATH, {});\n } finally {\n settled();\n }\n}\n\n/**\n * Stop tracking one task (it becomes `closed`, \"Dismissed in Verse.\"). The id\n * is re-checked against the contract's pattern: it came from the server, but\n * it is spliced into a path the mutation token is sent to.\n */\nexport async function dismissCloudTask(taskId: string): Promise<unknown> {\n if (!CLOUD_TASK_ID_PATTERN.test(taskId)) throw new Error('That task id is not one Verse issued, so nothing was sent.');\n try {\n return await post<unknown>(`${VERSE_CLOUD_TASKS_PATH}/${taskId}/dismiss`, {});\n } finally {\n settled();\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cloud-queries-BsOepFz6.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/cloud/cloud-queries.ts"],"sourcesContent":["/**\n * routes/verse/cloud/cloud-queries.ts — the one read and the writes behind\n * the cloud lane's UI (3.11 unit C3; server: core/cloud/cloud-api.ts, C2).\n *\n * GET /api/verse/cloud CloudOverviewResponse\n * POST /api/verse/cloud/launch CloudLaunchRequest → CloudLaunchResponse\n * POST /api/verse/cloud/budget CloudBudgetUpdate\n * POST /api/verse/cloud/refresh (re-read tasks from GitHub)\n * POST /api/verse/cloud/improve CloudImproveRequest → CloudImproveResponse\n * POST /api/verse/cloud/tasks/<id>/dismiss (mark closed; never touches GitHub)\n *\n * WHY the read is OPTIONAL (the surface-data.ts rule): the routes land in a\n * parallel unit, and a server without them answers 404. That is \"this build\n * has no cloud lane yet\" — one card's designed state — never a broken\n * Command. 401 and aborts still propagate: an expired read session is the\n * whole app's state.\n *\n * LAZY ONLY. Everything that imports this file (Command's card, the Usage\n * panel, the Fleet chip, the composer's \"Run in cloud\") is itself behind an\n * import(); nothing cloud-related may cost chat first-paint bytes.\n *\n * Writes pull the held mutation token (CloudLockedError without one — the\n * callers route every write through a token gate that opens the unlock\n * dialog first), then invalidate the overview so every cloud view moves\n * together, and nudge the shell's activity poll (Needs-you carries cloud\n * items).\n */\nimport {\n CLOUD_TASK_ID_PATTERN,\n VERSE_CLOUD_BUDGET_PATH,\n VERSE_CLOUD_IMPROVE_PATH,\n VERSE_CLOUD_LAUNCH_PATH,\n VERSE_CLOUD_PATH,\n VERSE_CLOUD_REFRESH_PATH,\n VERSE_CLOUD_TASKS_PATH,\n type CloudBudgetUpdate,\n type CloudImproveRequest,\n type CloudImproveResponse,\n type CloudLaunchFailureCode,\n type CloudLaunchRequest,\n type CloudLaunchResponse,\n type CloudOverviewResponse,\n} from '../../../../core/cloud/types.js';\nimport { ApiError, apiGet, apiPost } from '../../../data/client.js';\nimport { getMutationToken, touchMutationHold } from '../../../data/auth-store.js';\nimport { invalidate } from '../../../data/cache.js';\nimport type { QueryDef } from '../../../data/queries.js';\nimport { refreshActivity } from '../shell/useActivity.js';\nimport type { OptionalRead } from '../command/surface-data.js';\n\nexport const CLOUD_KEY = 'verse-cloud';\n\n/** The card, the chip and the panel all poll at this pace while visible (SPEC 3.11 C3). */\nexport const CLOUD_POLL_MS = 30_000;\n\nexport class CloudLockedError extends Error {\n constructor() {\n super('Unlock actions with the mutation token first.');\n this.name = 'CloudLockedError';\n }\n}\n\n/**\n * The server answered a launch with `ok: false` — a budget gate, a missing\n * seat, a CLI that printed no session. `message` is the server's own\n * sentence; `failure` the machine code for tests and chips.\n */\nexport class CloudLaunchRefusedError extends Error {\n constructor(message: string, public readonly failure: CloudLaunchFailureCode | null) {\n super(message);\n this.name = 'CloudLaunchRefusedError';\n }\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === 'object' && !Array.isArray(value);\n}\n\n/**\n * Just enough structure that no render can crash on `undefined.map` or read\n * a budget field that is not there. Field-level honesty (null = unknown) is\n * the model's job.\n */\nexport function narrowCloudOverview(raw: unknown): CloudOverviewResponse | null {\n if (!isRecord(raw)) return null;\n const { seat, budget, tasks, backlog } = raw;\n if (!isRecord(seat) || typeof seat['ready'] !== 'boolean') return null;\n if (!isRecord(budget) || !isRecord(budget['budget']) || !isRecord(budget['canLaunch']) || !isRecord(budget['canSelfImprove'])) return null;\n for (const key of ['creditsTotalUsd', 'estimatedSpentUsd', 'estimatedRemainingUsd', 'sessionsToday', 'selfImproveToday', 'running']) {\n if (typeof budget[key] !== 'number' || !Number.isFinite(budget[key])) return null;\n }\n if (!Array.isArray(tasks) || !tasks.every((t) => isRecord(t) && typeof t['id'] === 'string' && typeof t['state'] === 'string')) return null;\n if (!isRecord(backlog) || !Array.isArray(backlog['items'])) return null;\n return raw as unknown as CloudOverviewResponse;\n}\n\n/** Operator words for a cloud read that did not answer — never a path, never a trace. */\nfunction absence(err: unknown): string {\n if (err instanceof ApiError) {\n if (err.status === 404) return 'The cloud lane is not in this build yet.';\n if (err.status === 503) return 'The cloud lane failed to load on the server.';\n return `The cloud lane answered HTTP ${err.status}.`;\n }\n return 'The cloud lane could not be reached.';\n}\n\nexport const cloudQuery: QueryDef<OptionalRead<CloudOverviewResponse>> = {\n key: CLOUD_KEY,\n fetch: async (signal) => {\n try {\n const value = narrowCloudOverview(await apiGet<unknown>(VERSE_CLOUD_PATH, signal));\n if (value === null) {\n return { value: null, available: true, reason: 'The cloud lane answered in a shape this version does not recognise, so nothing is shown rather than guessed.' };\n }\n return { value, available: true, reason: null };\n } catch (err) {\n if (err instanceof ApiError && err.status === 401) throw err;\n if (err instanceof DOMException && err.name === 'AbortError') throw err;\n return { value: null, available: false, reason: absence(err) };\n }\n },\n};\n\nasync function post<T>(path: string, body: unknown): Promise<T> {\n const token = getMutationToken();\n if (!token) throw new CloudLockedError();\n const result = await apiPost<T>(path, body, token);\n touchMutationHold();\n return result;\n}\n\nfunction settled(): void {\n invalidate(CLOUD_KEY);\n void refreshActivity();\n}\n\n/**\n * Launch one cloud session. Resolves only on `ok: true` with a task; a\n * refusal the server words in a 200 body is thrown as CloudLaunchRefusedError\n * so every caller shows it the same way as a 4xx refusal (whose sentence is\n * `ApiError.detail`). Invalidates either way — a failed launch is still a\n * recorded task.\n */\nexport async function launchCloudTask(request: CloudLaunchRequest): Promise<CloudLaunchResponse & { task: NonNullable<CloudLaunchResponse['task']> }> {\n try {\n const response = await post<CloudLaunchResponse>(VERSE_CLOUD_LAUNCH_PATH, request);\n if (!isRecord(response) || response.ok !== true || !isRecord(response.task)) {\n const error = isRecord(response) && typeof response.error === 'string' && response.error ? response.error : 'The cloud session was not started, and the server sent no reason.';\n const failure = isRecord(response) && typeof response.failure === 'string' ? response.failure : null;\n throw new CloudLaunchRefusedError(error, failure);\n }\n return response as CloudLaunchResponse & { task: NonNullable<CloudLaunchResponse['task']> };\n } finally {\n settled();\n }\n}\n\n/** One budget change (any subset; the server validates and clamps). */\nexport async function updateCloudBudget(update: CloudBudgetUpdate): Promise<unknown> {\n const result = await post<unknown>(VERSE_CLOUD_BUDGET_PATH, update);\n invalidate(CLOUD_KEY);\n return result;\n}\n\n/** Launch up to `count` backlog items now (the \"Improve Verse\" button). */\nexport async function runCloudImprove(request: CloudImproveRequest = { count: 1 }): Promise<CloudImproveResponse> {\n try {\n const result = await post<unknown>(VERSE_CLOUD_IMPROVE_PATH, request);\n const launched = isRecord(result) && Array.isArray(result['launched']) ? (result['launched'] as CloudImproveResponse['launched']) : [];\n const skipped = isRecord(result) && Array.isArray(result['skipped']) ? (result['skipped'] as CloudImproveResponse['skipped']) : [];\n return { launched, skipped };\n } finally {\n settled();\n }\n}\n\n/** Ask the server to re-read every open task from GitHub now. */\nexport async function refreshCloudTasks(): Promise<unknown> {\n try {\n return await post<unknown>(VERSE_CLOUD_REFRESH_PATH, {});\n } finally {\n settled();\n }\n}\n\n/**\n * Stop tracking one task (it becomes `closed`, \"Dismissed in Verse.\"). The id\n * is re-checked against the contract's pattern: it came from the server, but\n * it is spliced into a path the mutation token is sent to.\n */\nexport async function dismissCloudTask(taskId: string): Promise<unknown> {\n if (!CLOUD_TASK_ID_PATTERN.test(taskId)) throw new Error('That task id is not one Verse issued, so nothing was sent.');\n try {\n return await post<unknown>(`${VERSE_CLOUD_TASKS_PATH}/${taskId}/dismiss`, {});\n } finally {\n settled();\n }\n}\n"],"mappings":"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"}
|
package/dist/core/web/public/next/assets/{cloud.module-CiwatLj3.js → cloud.module-DoFnr8ZH.js}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{
|
|
2
|
-
//# sourceMappingURL=cloud.module-
|
|
1
|
+
import{E as e}from"./VerseConsoleApp-CTF3Shs0.js";import{v as t,y as n}from"./format-CaMmVQCn.js";import{v as r}from"./context-model-Ci3Ch9t5.js";import{a as i,i as a,n as o}from"./types-D5XOdyw8.js";var s=i.selfImprove.repo,c=/^[A-Za-z0-9-]+\/[A-Za-z0-9._-]+$/,l=/^[A-Za-z0-9._/+-]{1,200}$/;function u(e){return!l.test(e)||e.startsWith(`-`)||e.startsWith(`/`)||e.endsWith(`/`)||e.endsWith(`.`)||e.endsWith(`.lock`)?!1:!e.includes(`..`)&&!e.includes(`//`)}function d(e,t){if(typeof e!=`string`||e.length>2048)return null;try{let n=new URL(e);return n.protocol===`https:`&&n.hostname===t?n.toString():null}catch{return null}}function f(e){if(!Number.isFinite(e))return`—`;let t=Math.abs(e)<.005?0:e,n=Number.isInteger(t)?String(Math.abs(t)):Math.abs(t).toFixed(2);return`${t<0?`−`:``}$${n}`}function p(t){let n=t.creditsTotalUsd,r=t.estimatedRemainingUsd,i=t.budget.selfImprove.reserveUsd,a=n>0?n:null,o=a===null?null:(n-Math.max(0,r))/n*100,s=r<=0?`danger`:r<i?`warning`:`accent`,c=r<=0?`The estimate says the credits are spent. Check the real balance on claude.ai and adjust the budget.`:r<i?`Under the ${f(i)} reserve, so Verse stops launching self-improvement tasks.`:null;return{value:Math.max(0,r),max:a,text:`${f(Math.max(0,r))} of ${f(n)} · estimate`,usedText:o===null?`no credit total set`:`${e(o)} used`,tone:s,warning:c}}function m(e){let t=new Date(e);return t.setHours(24,0,0,0),t.getTime()}function h(e,t=Date.now()){let r=n(new Date(m(t)).toISOString(),t),i=e.running===1?`1 running`:`${e.running} running`;return`${e.sessionsToday} of ${e.budget.maxSessionsPerDay} sessions today · ${i}${r?` · resets ${r}`:``}`}function g(e){let t=e.budget.selfImprove;return t.enabled?`${e.selfImproveToday} of ${t.maxPerDay} self-improvement launches today, on ${t.repo}. Stops under a ${f(t.reserveUsd)} estimated balance.`:`Self-improvement is off. Verse launches cloud tasks only when you ask.`}function _(e,n){return e.ok?null:e.reason?t(e.reason):n}var v=`The Claude seat isn't set up on this Mac.`;function y(e){return e.ready?null:e.reason?t(e.reason):v}function b(e,t){return y(e)??_(t.canLaunch,`The cloud budget does not allow another launch right now.`)}var x={queued:`Queued`,launching:`Launching`,running:`Running`,"pr-open":`PR open`,merged:`Merged`,closed:`Closed`,failed:`Failed`,expired:`Expired`},S={queued:`info`,launching:`info`,running:`running`,"pr-open":`warning`,merged:`success`,closed:`neutral`,failed:`danger`,expired:`unknown`};function C(e){return e.state===`pr-open`&&e.pr?.draft?`Draft PR`:x[e.state]??e.state}function w(e){return a.includes(e)}function T(e){return e===`queued`||e===`launching`||e===`running`}var E=864e5;function D(e,t=Date.now()){let n=e=>{let n=Date.parse(e.updatedAt);return Number.isFinite(n)&&t-n<E},r=e.filter(e=>!w(e.state)||n(e)).sort((e,t)=>(Date.parse(t.createdAt)||0)-(Date.parse(e.createdAt)||0));return{shown:r.slice(0,6),hidden:Math.max(0,r.length-6)}}function O(e){return e.filter(e=>T(e.state)).length}function k(e,t=Date.now()){let n=e.state===`queued`||e.state===`launching`?e.createdAt:e.launchedAt??e.createdAt,i=r(n,t),a=e.state===`queued`?`queued`:e.state===`failed`?`tried`:`started`;return`${e.repo} from ${e.baseBranch}${i?` · ${a} ${i}`:``}`}function A(e,n=Date.now()){return e.report?.summary&&(e.state===`pr-open`||e.state===`merged`)?t(e.report.summary,n):e.stateReason?t(e.stateReason,n):null}function j(e){return e.state!==`merged`&&e.state!==`closed`}var M=new Intl.NumberFormat(`en-US`);function N(e){let t={},n=e.repo.trim(),r=e.baseBranch.trim(),i=e.prompt.trim();return n?c.test(n)||(t.repo=`Use the GitHub owner/name form, like ashlrai/ashlr-hub.`):t.repo=`Enter the GitHub repository as owner/name.`,r&&!u(r)&&(t.baseBranch=`That isn't a branch name git accepts.`),i?i.length>2e4&&(t.prompt=`The task is ${M.format(i.length)} characters; the limit is ${M.format(o)}.`):t.prompt=`Describe the task for the cloud session.`,Object.keys(t).length>0?{ok:!1,errors:t}:{ok:!0,request:{repo:n,...r?{baseBranch:r}:{},prompt:i}}}var P=[{field:`total`,label:`Credits on the account`,hint:`What claude.ai says you have.`,money:!0,integer:!1,min:0,max:1e5},{field:`spent`,label:`Already spent`,hint:`Spent before Verse tracked it, or a correction after checking.`,money:!0,integer:!1,min:-1e5,max:1e5},{field:`perSession`,label:`Estimate per session`,hint:`Flat cost Verse counts for each launch.`,money:!0,integer:!1,min:.01,max:1e3},{field:`maxPerDay`,label:`Sessions per day`,hint:`From every origin, counted per local day.`,money:!1,integer:!0,min:0,max:500},{field:`maxConcurrent`,label:`Running at once`,hint:`Launching or running together.`,money:!1,integer:!0,min:1,max:50},{field:`selfImproveMax`,label:`Self-improvement per day`,hint:`Launches Verse may make on its own.`,money:!1,integer:!0,min:0,max:100},{field:`reserve`,label:`Reserve`,hint:`Self-improvement stops under this estimated balance.`,money:!0,integer:!1,min:0,max:1e5}];function F(e){return String(Number.isInteger(e)?e:Math.round(e*100)/100)}function I(e){return{total:F(e.creditsTotalUsd),spent:F(e.creditsSpentAdjustmentUsd),perSession:F(e.estimatedCostPerSessionUsd),maxPerDay:F(e.maxSessionsPerDay),maxConcurrent:F(e.maxConcurrent),selfImprove:e.selfImprove.enabled,selfImproveMax:F(e.selfImprove.maxPerDay),reserve:F(e.selfImprove.reserveUsd)}}function L(e,t){let n={},r={};for(let t of P){let i=e[t.field].trim().replace(/^\$/,``),a=i===``?NaN:Number(i);Number.isFinite(a)?t.integer&&!Number.isInteger(a)?n[t.field]=`Enter a whole number.`:a<t.min||a>t.max?n[t.field]=t.money?`Between ${f(t.min)} and ${f(t.max)}.`:`Between ${M.format(t.min)} and ${M.format(t.max)}.`:r[t.field]=a:n[t.field]=`Enter a number.`}if(Object.keys(n).length>0)return{ok:!1,errors:n};let i={},a=(e,t)=>Math.abs(e-t)<.005;a(r.total,t.creditsTotalUsd)||(i.creditsTotalUsd=r.total),a(r.spent,t.creditsSpentAdjustmentUsd)||(i.creditsSpentAdjustmentUsd=r.spent),a(r.perSession,t.estimatedCostPerSessionUsd)||(i.estimatedCostPerSessionUsd=r.perSession),a(r.maxPerDay,t.maxSessionsPerDay)||(i.maxSessionsPerDay=r.maxPerDay),a(r.maxConcurrent,t.maxConcurrent)||(i.maxConcurrent=r.maxConcurrent);let o={};return e.selfImprove!==t.selfImprove.enabled&&(o.enabled=e.selfImprove),a(r.selfImproveMax,t.selfImprove.maxPerDay)||(o.maxPerDay=r.selfImproveMax),a(r.reserve,t.selfImprove.reserveUsd)||(o.reserveUsd=r.reserve),Object.keys(o).length>0&&(i.selfImprove=o),{ok:!0,update:i,changed:Object.keys(i).length>0}}function R(e){return e.overview?y(e.overview.seat)||(e.repo?_(e.overview.budget.canLaunch,`The cloud budget does not allow another launch right now.`)||(e.prompt.trim()?e.prompt.trim().length>2e4?`The message is over ${M.format(o)} characters — trim it to run it in the cloud.`:null:`Type the task in the message box first.`):e.rootsLoading?`Reading this chat's project…`:`This chat's project has no GitHub origin, so a cloud session has nothing to clone.`):e.overviewReason??`The cloud lane did not answer.`}var z={body:`_body_1uchw_10`,actionsRow:`_actionsRow_1uchw_17`,headActions:`_headActions_1uchw_24`,meterBlock:`_meterBlock_1uchw_31`,estimateNote:`_estimateNote_1uchw_37`,muted:`_muted_1uchw_38`,facts:`_facts_1uchw_46`,link:`_link_1uchw_54`,linkButton:`_linkButton_1uchw_67`,toggleRow:`_toggleRow_1uchw_94`,toggleText:`_toggleText_1uchw_101`,tasks:`_tasks_1uchw_108`,task:`_task_1uchw_108`,taskChip:`_taskChip_1uchw_128`,taskMain:`_taskMain_1uchw_132`,taskTitle:`_taskTitle_1uchw_139`,taskLinks:`_taskLinks_1uchw_148`,notice:`_notice_1uchw_157`,noticeRow:`_noticeRow_1uchw_187`,popover:`_popover_1uchw_197`,popoverTitle:`_popoverTitle_1uchw_212`,form:`_form_1uchw_222`,fieldGrid:`_fieldGrid_1uchw_228`,textareaField:`_textareaField_1uchw_234`,label:`_label_1uchw_240`,textarea:`_textarea_1uchw_234`,fieldError:`_fieldError_1uchw_269`,formActions:`_formActions_1uchw_276`,dialogWidth:`_dialogWidth_1uchw_283`,laneChip:`_laneChip_1uchw_289`,sheetAction:`_sheetAction_1uchw_295`};export{N as S,g as _,I as a,A as b,D as c,_ as d,O as f,y as g,d as h,S as i,p as l,R as m,P as n,L as o,b as p,s as r,j as s,z as t,f as u,h as v,k as x,C as y};
|
|
2
|
+
//# sourceMappingURL=cloud.module-DoFnr8ZH.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cloud.module-CiwatLj3.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/cloud/cloud-model.ts","../../../../../../src/web-ui/routes/verse/cloud/cloud.module.css"],"sourcesContent":["/**\n * routes/verse/cloud/cloud-model.ts — the cloud lane's words and decisions as\n * pure functions (3.11 unit C3), so the card, the Usage panel, the Fleet chip\n * and the composer action say the same thing and the copy is tested as a\n * table. No React here.\n *\n * Copy rules (DESIGN §13.8, and the cloud contract's own):\n * - spend is ALWAYS an estimate and says so — Claude does not expose the\n * credit balance, so every dollar figure carries \"estimate\" beside it;\n * - server prose (gate reasons, state reasons, report summaries) goes\n * through `tidyProse`, so an ISO instant reads as local time;\n * - times are local and relative (\"started 5m ago\"), never ISO;\n * - a state is always a WORD beside any colour.\n */\nimport {\n CLOUD_PROMPT_MAX_CHARS,\n CLOUD_TERMINAL_STATES,\n DEFAULT_CLOUD_BUDGET,\n type CloudBudgetUpdate,\n type CloudBudgetV1,\n type CloudBudgetView,\n type CloudSeatStatus,\n type CloudTaskState,\n type CloudTaskV1,\n} from '../../../../core/cloud/types.js';\nimport { describeResetAt } from '../../../../core/verse/seat-readiness.js';\nimport type { Tone } from '../../../components/primitives/StatusBadge.js';\nimport type { MeterTone } from '../../../components/primitives/Meter.js';\nimport { tidyProse } from '../autonomy/format.js';\nimport { relativePhrase } from '../context/context-model.js';\nimport { usedPercentText } from '../percent-text.js';\n\n/** The repo a new task targets unless the operator names another (the self-improvement default). */\nexport const CLOUD_DEFAULT_REPO = DEFAULT_CLOUD_BUDGET.selfImprove.repo;\n\n/** Same rule the service enforces (core/cloud/service.ts), checked here so the dialog can say so first. */\nexport const CLOUD_REPO_PATTERN = /^[A-Za-z0-9-]+\\/[A-Za-z0-9._-]+$/;\n\n/**\n * A branch name git would accept, conservatively: no spaces or control\n * characters, no `..`, no leading `-` or `/`, no trailing `/` or `.lock`.\n * The server re-checks it against origin; this only catches typing slips.\n */\nconst BRANCH_CHARS = /^[A-Za-z0-9._/+-]{1,200}$/;\nexport function isBranchName(value: string): boolean {\n if (!BRANCH_CHARS.test(value)) return false;\n if (value.startsWith('-') || value.startsWith('/') || value.endsWith('/') || value.endsWith('.') || value.endsWith('.lock')) return false;\n return !value.includes('..') && !value.includes('//');\n}\n\n/** Only these hosts become links: the ids came from a CLI's stdout and gh's JSON. */\nexport function safeHref(url: string | null | undefined, host: 'claude.ai' | 'github.com'): string | null {\n if (typeof url !== 'string' || url.length > 2_048) return null;\n try {\n const parsed = new URL(url);\n return parsed.protocol === 'https:' && parsed.hostname === host ? parsed.toString() : null;\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Money and the credits meter\n// ---------------------------------------------------------------------------\n\n/** \"$250\", \"$12.50\" — whole dollars stay whole; never \"-$0\". */\nexport function formatDollars(value: number): string {\n if (!Number.isFinite(value)) return '—';\n const v = Math.abs(value) < 0.005 ? 0 : value;\n const text = Number.isInteger(v) ? String(Math.abs(v)) : Math.abs(v).toFixed(2);\n return `${v < 0 ? '−' : ''}$${text}`;\n}\n\nexport interface CreditsMeterView {\n /** Remaining, clamped at 0 for the bar (the text says when it went below). */\n value: number;\n max: number | null;\n /** \"$212 of $250 · estimate\" */\n text: string;\n /** \"15% used\" — the one percent rule. */\n usedText: string;\n tone: MeterTone;\n /** A plain sentence when the estimate is at or under the self-improvement reserve. */\n warning: string | null;\n}\n\nexport function creditsMeter(view: CloudBudgetView): CreditsMeterView {\n const total = view.creditsTotalUsd;\n const remaining = view.estimatedRemainingUsd;\n const reserve = view.budget.selfImprove.reserveUsd;\n const max = total > 0 ? total : null;\n const used = max === null ? null : ((total - Math.max(0, remaining)) / total) * 100;\n const tone: MeterTone = remaining <= 0 ? 'danger' : remaining < reserve ? 'warning' : 'accent';\n const warning = remaining <= 0\n ? 'The estimate says the credits are spent. Check the real balance on claude.ai and adjust the budget.'\n : remaining < reserve\n ? `Under the ${formatDollars(reserve)} reserve, so Verse stops launching self-improvement tasks.`\n : null;\n return {\n value: Math.max(0, remaining),\n max,\n text: `${formatDollars(Math.max(0, remaining))} of ${formatDollars(total)} · estimate`,\n usedText: used === null ? 'no credit total set' : `${usedPercentText(used)} used`,\n tone,\n warning,\n };\n}\n\n/** The next local midnight — when the per-day caps start counting again. */\nexport function nextLocalMidnight(now: number): number {\n const d = new Date(now);\n d.setHours(24, 0, 0, 0);\n return d.getTime();\n}\n\n/** \"3 of 20 sessions today · 1 running\" plus when the daily count resets. */\nexport function sessionsLine(view: CloudBudgetView, now: number = Date.now()): string {\n const reset = describeResetAt(new Date(nextLocalMidnight(now)).toISOString(), now);\n const running = view.running === 1 ? '1 running' : `${view.running} running`;\n return `${view.sessionsToday} of ${view.budget.maxSessionsPerDay} sessions today · ${running}${reset ? ` · resets ${reset}` : ''}`;\n}\n\nexport function selfImproveLine(view: CloudBudgetView): string {\n const si = view.budget.selfImprove;\n if (!si.enabled) return 'Self-improvement is off. Verse launches cloud tasks only when you ask.';\n return `${view.selfImproveToday} of ${si.maxPerDay} self-improvement launches today, on ${si.repo}. Stops under a ${formatDollars(si.reserveUsd)} estimated balance.`;\n}\n\n/** A gate's reason, fit to print (null when the gate is open). */\nexport function gateText(gate: { ok: boolean; reason: string | null }, fallback: string): string | null {\n if (gate.ok) return null;\n return gate.reason ? tidyProse(gate.reason) : fallback;\n}\n\nexport const SEAT_NOT_READY = \"The Claude seat isn't set up on this Mac.\";\n\nexport function seatText(seat: CloudSeatStatus): string | null {\n if (seat.ready) return null;\n return seat.reason ? tidyProse(seat.reason) : SEAT_NOT_READY;\n}\n\n/**\n * Why a launch button is disabled right now, or null when it may be pressed.\n * Seat first (nothing launches without it), then the budget gate.\n */\nexport function launchBlock(seat: CloudSeatStatus, budget: CloudBudgetView): string | null {\n return seatText(seat) ?? gateText(budget.canLaunch, 'The cloud budget does not allow another launch right now.');\n}\n\n// ---------------------------------------------------------------------------\n// Tasks\n// ---------------------------------------------------------------------------\n\nexport const STATE_WORD: Record<CloudTaskState, string> = {\n queued: 'Queued',\n launching: 'Launching',\n running: 'Running',\n 'pr-open': 'PR open',\n merged: 'Merged',\n closed: 'Closed',\n failed: 'Failed',\n expired: 'Expired',\n};\n\nexport const STATE_TONE: Record<CloudTaskState, Tone> = {\n queued: 'info',\n launching: 'info',\n running: 'running',\n 'pr-open': 'warning',\n merged: 'success',\n closed: 'neutral',\n failed: 'danger',\n expired: 'unknown',\n};\n\n/** A draft PR reads \"Draft PR\", not \"PR open\": the session may still be pushing. */\nexport function stateWord(task: Pick<CloudTaskV1, 'state' | 'pr'>): string {\n if (task.state === 'pr-open' && task.pr?.draft) return 'Draft PR';\n return STATE_WORD[task.state] ?? task.state;\n}\n\nexport function isTerminal(state: CloudTaskState): boolean {\n return CLOUD_TERMINAL_STATES.includes(state);\n}\n\n/** In flight: counted by the Fleet chip and the \"running\" line. */\nexport function isInFlight(state: CloudTaskState): boolean {\n return state === 'queued' || state === 'launching' || state === 'running';\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n/** How many tasks the card lists before \"N more on GitHub\". */\nexport const CARD_TASK_LIMIT = 6;\n\n/**\n * What the card lists: everything still moving or waiting on review, then\n * anything that ended in the last 24 h (a failure you have not seen yet),\n * newest first. Older finished tasks live in `ashlr cloud list --all`.\n */\nexport function cardTasks(tasks: readonly CloudTaskV1[], now: number = Date.now()): { shown: CloudTaskV1[]; hidden: number } {\n const recent = (t: CloudTaskV1) => {\n const at = Date.parse(t.updatedAt);\n return Number.isFinite(at) && now - at < DAY_MS;\n };\n const listed = tasks\n .filter((t) => !isTerminal(t.state) || recent(t))\n .sort((a, b) => (Date.parse(b.createdAt) || 0) - (Date.parse(a.createdAt) || 0));\n return { shown: listed.slice(0, CARD_TASK_LIMIT), hidden: Math.max(0, listed.length - CARD_TASK_LIMIT) };\n}\n\nexport function inFlightCount(tasks: readonly CloudTaskV1[]): number {\n return tasks.filter((t) => isInFlight(t.state)).length;\n}\n\n/** \"ashlrai/ashlr-hub from master · started 5m ago\" */\nexport function taskMeta(task: CloudTaskV1, now: number = Date.now()): string {\n const when = task.state === 'queued' || task.state === 'launching' ? task.createdAt : task.launchedAt ?? task.createdAt;\n const phrase = relativePhrase(when, now);\n const verb = task.state === 'queued' ? 'queued' : task.state === 'failed' ? 'tried' : 'started';\n return `${task.repo} from ${task.baseBranch}${phrase ? ` · ${verb} ${phrase}` : ''}`;\n}\n\n/** The one line under a task's title: the report for a PR, else the state's reason. */\nexport function taskDetail(task: CloudTaskV1, now: number = Date.now()): string | null {\n if (task.report?.summary && (task.state === 'pr-open' || task.state === 'merged')) return tidyProse(task.report.summary, now);\n return task.stateReason ? tidyProse(task.stateReason, now) : null;\n}\n\n/** Dismiss is offered for anything Verse is still tracking; merged and closed tasks are already done. */\nexport function canDismiss(task: Pick<CloudTaskV1, 'state'>): boolean {\n return task.state !== 'merged' && task.state !== 'closed';\n}\n\n// ---------------------------------------------------------------------------\n// The launch form\n// ---------------------------------------------------------------------------\n\nexport interface LaunchDraft {\n repo: string;\n baseBranch: string;\n prompt: string;\n}\n\nexport type LaunchErrors = Partial<Record<keyof LaunchDraft, string>>;\n\nexport type LaunchValidation =\n | { ok: true; request: { repo: string; baseBranch?: string; prompt: string } }\n | { ok: false; errors: LaunchErrors };\n\nconst COUNT = new Intl.NumberFormat('en-US');\n\nexport function validateLaunch(draft: LaunchDraft): LaunchValidation {\n const errors: LaunchErrors = {};\n const repo = draft.repo.trim();\n const base = draft.baseBranch.trim();\n const prompt = draft.prompt.trim();\n if (!repo) errors.repo = 'Enter the GitHub repository as owner/name.';\n else if (!CLOUD_REPO_PATTERN.test(repo)) errors.repo = 'Use the GitHub owner/name form, like ashlrai/ashlr-hub.';\n if (base && !isBranchName(base)) errors.baseBranch = \"That isn't a branch name git accepts.\";\n if (!prompt) errors.prompt = 'Describe the task for the cloud session.';\n else if (prompt.length > CLOUD_PROMPT_MAX_CHARS) {\n errors.prompt = `The task is ${COUNT.format(prompt.length)} characters; the limit is ${COUNT.format(CLOUD_PROMPT_MAX_CHARS)}.`;\n }\n if (Object.keys(errors).length > 0) return { ok: false, errors };\n return { ok: true, request: { repo, ...(base ? { baseBranch: base } : {}), prompt } };\n}\n\n// ---------------------------------------------------------------------------\n// The budget form (Edit budget popover and the Usage panel share it)\n// ---------------------------------------------------------------------------\n\nexport interface BudgetForm {\n total: string;\n spent: string;\n perSession: string;\n maxPerDay: string;\n maxConcurrent: string;\n selfImprove: boolean;\n selfImproveMax: string;\n reserve: string;\n}\n\nexport type BudgetFormField = Exclude<keyof BudgetForm, 'selfImprove'>;\nexport type BudgetErrors = Partial<Record<BudgetFormField, string>>;\n\n/** Field → label and bounds. Bounds are the form's sanity check; the server clamps as well. */\nexport const BUDGET_FIELDS: ReadonlyArray<{\n field: BudgetFormField;\n label: string;\n hint: string;\n money: boolean;\n integer: boolean;\n min: number;\n max: number;\n}> = [\n { field: 'total', label: 'Credits on the account', hint: 'What claude.ai says you have.', money: true, integer: false, min: 0, max: 100_000 },\n { field: 'spent', label: 'Already spent', hint: 'Spent before Verse tracked it, or a correction after checking.', money: true, integer: false, min: -100_000, max: 100_000 },\n { field: 'perSession', label: 'Estimate per session', hint: 'Flat cost Verse counts for each launch.', money: true, integer: false, min: 0.01, max: 1_000 },\n { field: 'maxPerDay', label: 'Sessions per day', hint: 'From every origin, counted per local day.', money: false, integer: true, min: 0, max: 500 },\n { field: 'maxConcurrent', label: 'Running at once', hint: 'Launching or running together.', money: false, integer: true, min: 1, max: 50 },\n { field: 'selfImproveMax', label: 'Self-improvement per day', hint: 'Launches Verse may make on its own.', money: false, integer: true, min: 0, max: 100 },\n { field: 'reserve', label: 'Reserve', hint: 'Self-improvement stops under this estimated balance.', money: true, integer: false, min: 0, max: 100_000 },\n];\n\nfunction numText(n: number): string {\n return Number.isInteger(n) ? String(n) : String(Math.round(n * 100) / 100);\n}\n\nexport function budgetFormFrom(b: CloudBudgetV1): BudgetForm {\n return {\n total: numText(b.creditsTotalUsd),\n spent: numText(b.creditsSpentAdjustmentUsd),\n perSession: numText(b.estimatedCostPerSessionUsd),\n maxPerDay: numText(b.maxSessionsPerDay),\n maxConcurrent: numText(b.maxConcurrent),\n selfImprove: b.selfImprove.enabled,\n selfImproveMax: numText(b.selfImprove.maxPerDay),\n reserve: numText(b.selfImprove.reserveUsd),\n };\n}\n\nexport type BudgetPatch = { ok: true; update: CloudBudgetUpdate; changed: boolean } | { ok: false; errors: BudgetErrors };\n\n/**\n * The form as ONE update carrying only what changed — so two people editing\n * different fields never overwrite each other's numbers with stale ones.\n */\nexport function budgetPatch(form: BudgetForm, current: CloudBudgetV1): BudgetPatch {\n const errors: BudgetErrors = {};\n const values: Partial<Record<BudgetFormField, number>> = {};\n for (const spec of BUDGET_FIELDS) {\n const raw = form[spec.field].trim().replace(/^\\$/, '');\n const n = raw === '' ? Number.NaN : Number(raw);\n if (!Number.isFinite(n)) errors[spec.field] = 'Enter a number.';\n else if (spec.integer && !Number.isInteger(n)) errors[spec.field] = 'Enter a whole number.';\n else if (n < spec.min || n > spec.max) {\n errors[spec.field] = spec.money\n ? `Between ${formatDollars(spec.min)} and ${formatDollars(spec.max)}.`\n : `Between ${COUNT.format(spec.min)} and ${COUNT.format(spec.max)}.`;\n } else values[spec.field] = n;\n }\n if (Object.keys(errors).length > 0) return { ok: false, errors };\n const update: CloudBudgetUpdate = {};\n const same = (a: number, b: number) => Math.abs(a - b) < 0.005;\n if (!same(values.total!, current.creditsTotalUsd)) update.creditsTotalUsd = values.total!;\n if (!same(values.spent!, current.creditsSpentAdjustmentUsd)) update.creditsSpentAdjustmentUsd = values.spent!;\n if (!same(values.perSession!, current.estimatedCostPerSessionUsd)) update.estimatedCostPerSessionUsd = values.perSession!;\n if (!same(values.maxPerDay!, current.maxSessionsPerDay)) update.maxSessionsPerDay = values.maxPerDay!;\n if (!same(values.maxConcurrent!, current.maxConcurrent)) update.maxConcurrent = values.maxConcurrent!;\n const selfImprove: NonNullable<CloudBudgetUpdate['selfImprove']> = {};\n if (form.selfImprove !== current.selfImprove.enabled) selfImprove.enabled = form.selfImprove;\n if (!same(values.selfImproveMax!, current.selfImprove.maxPerDay)) selfImprove.maxPerDay = values.selfImproveMax!;\n if (!same(values.reserve!, current.selfImprove.reserveUsd)) selfImprove.reserveUsd = values.reserve!;\n if (Object.keys(selfImprove).length > 0) update.selfImprove = selfImprove;\n return { ok: true, update, changed: Object.keys(update).length > 0 };\n}\n\n// ---------------------------------------------------------------------------\n// The composer's \"Run in cloud\"\n// ---------------------------------------------------------------------------\n\nexport interface RunInCloudInputs {\n /** The cloud overview, or null when the route did not answer. */\n overview: { seat: CloudSeatStatus; budget: CloudBudgetView } | null;\n /** Why the overview is missing, when it is. */\n overviewReason: string | null;\n /** The chat project's GitHub `owner/name`, or null when it has none. */\n repo: string | null;\n /** True while the chat's roots have not been read yet. */\n rootsLoading: boolean;\n prompt: string;\n}\n\n/** Null when \"Run in cloud\" may be pressed; otherwise the tooltip saying why not. */\nexport function runInCloudBlock(inputs: RunInCloudInputs): string | null {\n if (!inputs.overview) return inputs.overviewReason ?? 'The cloud lane did not answer.';\n const seat = seatText(inputs.overview.seat);\n if (seat) return seat;\n if (!inputs.repo) {\n return inputs.rootsLoading\n ? \"Reading this chat's project…\"\n : \"This chat's project has no GitHub origin, so a cloud session has nothing to clone.\";\n }\n const gate = gateText(inputs.overview.budget.canLaunch, 'The cloud budget does not allow another launch right now.');\n if (gate) return gate;\n if (!inputs.prompt.trim()) return 'Type the task in the message box first.';\n if (inputs.prompt.trim().length > CLOUD_PROMPT_MAX_CHARS) {\n return `The message is over ${COUNT.format(CLOUD_PROMPT_MAX_CHARS)} characters — trim it to run it in the cloud.`;\n }\n return null;\n}\n","/*\n * routes/verse/cloud/cloud.module.css — the cloud lane's card, dialog,\n * popover, Usage panel, Fleet chip and composer action (3.11 unit C3).\n * Tokens only (style-scan-310): no raw hex colour, no px font size, and\n * every colour follows the theme. Nothing is ellipsized: long titles and\n * reasons wrap at word boundaries (only an unbreakable run, like a URL, may\n * break inside itself).\n */\n\n.body {\n display: flex;\n flex-direction: column;\n gap: var(--space-3);\n min-width: 0;\n}\n\n.actionsRow {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-2);\n}\n\n.headActions {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: var(--space-2);\n}\n\n.meterBlock {\n display: flex;\n flex-direction: column;\n gap: var(--space-1);\n}\n\n.estimateNote,\n.muted {\n margin: 0;\n color: var(--text-tertiary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n overflow-wrap: break-word;\n}\n\n.facts {\n margin: 0;\n color: var(--text-secondary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n overflow-wrap: break-word;\n}\n\n.link {\n color: var(--text-link);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n text-decoration: none;\n white-space: nowrap;\n}\n\n.link:hover {\n text-decoration: underline;\n}\n\n.link:focus-visible,\n.linkButton:focus-visible {\n outline: 2px solid var(--border-focus);\n outline-offset: 2px;\n}\n\n.linkButton {\n padding: 0;\n border: 0;\n background: none;\n color: var(--text-link);\n font: inherit;\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n cursor: pointer;\n white-space: nowrap;\n}\n\n.linkButton:hover {\n text-decoration: underline;\n}\n\n.linkButton:disabled {\n color: var(--text-disabled);\n cursor: default;\n text-decoration: none;\n}\n\n.toggleRow {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-2) var(--space-3);\n}\n\n.toggleText {\n flex: 1 1 16rem;\n min-width: 0;\n}\n\n/* ---- tasks ------------------------------------------------------------ */\n\n.tasks {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.task {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr);\n gap: var(--space-1) var(--space-3);\n padding: var(--space-2) 0;\n border-top: 1px solid var(--border-subtle);\n}\n\n.task:first-child {\n border-top: 0;\n}\n\n.taskChip {\n padding-top: 1px;\n}\n\n.taskMain {\n display: flex;\n flex-direction: column;\n gap: var(--space-1);\n min-width: 0;\n}\n\n.taskTitle {\n margin: 0;\n color: var(--text-primary);\n font-size: var(--text-sm-size);\n line-height: var(--text-sm-line);\n font-weight: var(--font-weight-medium);\n overflow-wrap: break-word;\n}\n\n.taskLinks {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-1) var(--space-3);\n}\n\n/* ---- notices (the NoticeSlot tones, inside a card) --------------------- */\n\n.notice {\n margin: 0;\n padding: var(--space-2) var(--space-3);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-md);\n background: var(--bg-surface-raised);\n color: var(--text-secondary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n overflow-wrap: break-word;\n}\n\n.notice[data-tone='warning'] {\n border-color: var(--status-warning-solid);\n background: var(--status-warning-bg);\n color: var(--status-warning-fg);\n}\n\n.notice[data-tone='danger'] {\n border-color: var(--status-danger-solid);\n background: var(--status-danger-bg);\n color: var(--status-danger-fg);\n}\n\n.notice[data-tone='success'] {\n border-color: var(--status-success-solid);\n background: var(--status-success-bg);\n color: var(--status-success-fg);\n}\n\n.noticeRow {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n justify-content: space-between;\n gap: var(--space-1) var(--space-3);\n}\n\n/* ---- the Edit budget popover ------------------------------------------ */\n\n.popover {\n position: absolute;\n top: calc(100% + var(--space-2));\n right: 0;\n z-index: var(--z-dropdown);\n width: min(26rem, calc(100vw - var(--space-8)));\n max-height: min(34rem, 70vh);\n overflow-y: auto;\n padding: var(--space-4);\n border: 1px solid var(--border-default);\n border-radius: var(--radius-lg);\n background: var(--bg-surface-raised);\n box-shadow: var(--shadow-menu);\n}\n\n.popoverTitle {\n margin: 0 0 var(--space-3);\n color: var(--text-primary);\n font-size: var(--text-sm-size);\n line-height: var(--text-sm-line);\n font-weight: var(--font-weight-semibold);\n}\n\n/* ---- forms (launch dialog, budget) ------------------------------------ */\n\n.form {\n display: flex;\n flex-direction: column;\n gap: var(--space-3);\n}\n\n.fieldGrid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));\n gap: var(--space-3);\n}\n\n.textareaField {\n display: flex;\n flex-direction: column;\n gap: var(--space-1);\n}\n\n.label {\n color: var(--text-secondary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n font-weight: var(--font-weight-medium);\n}\n\n.textarea {\n min-height: 8rem;\n padding: var(--space-2) var(--space-3);\n border: 1px solid var(--border-default);\n border-radius: var(--radius-md);\n background: var(--bg-input);\n color: var(--text-primary);\n font: inherit;\n font-size: var(--text-sm-size);\n line-height: var(--text-sm-line);\n resize: vertical;\n}\n\n.textarea:focus-visible {\n outline: 2px solid var(--border-focus);\n outline-offset: 0;\n}\n\n.textarea[aria-invalid='true'] {\n border-color: var(--status-danger-solid);\n}\n\n.fieldError {\n margin: 0;\n color: var(--status-danger-fg);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n}\n\n.formActions {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: var(--space-2);\n}\n\n.dialogWidth {\n width: min(34rem, calc(100vw - var(--space-8)));\n}\n\n/* ---- the Fleet chip ---------------------------------------------------- */\n\n.laneChip[data-active='true'] {\n border-color: var(--status-running-solid);\n}\n\n/* ---- the composer's \"Run in cloud\" ------------------------------------- */\n\n.sheetAction {\n display: flex;\n flex-direction: column;\n gap: var(--space-2);\n padding-top: var(--space-3);\n border-top: 1px solid var(--border-subtle);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cloud.module-DoFnr8ZH.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/cloud/cloud-model.ts","../../../../../../src/web-ui/routes/verse/cloud/cloud.module.css"],"sourcesContent":["/**\n * routes/verse/cloud/cloud-model.ts — the cloud lane's words and decisions as\n * pure functions (3.11 unit C3), so the card, the Usage panel, the Fleet chip\n * and the composer action say the same thing and the copy is tested as a\n * table. No React here.\n *\n * Copy rules (DESIGN §13.8, and the cloud contract's own):\n * - spend is ALWAYS an estimate and says so — Claude does not expose the\n * credit balance, so every dollar figure carries \"estimate\" beside it;\n * - server prose (gate reasons, state reasons, report summaries) goes\n * through `tidyProse`, so an ISO instant reads as local time;\n * - times are local and relative (\"started 5m ago\"), never ISO;\n * - a state is always a WORD beside any colour.\n */\nimport {\n CLOUD_PROMPT_MAX_CHARS,\n CLOUD_TERMINAL_STATES,\n DEFAULT_CLOUD_BUDGET,\n type CloudBudgetUpdate,\n type CloudBudgetV1,\n type CloudBudgetView,\n type CloudSeatStatus,\n type CloudTaskState,\n type CloudTaskV1,\n} from '../../../../core/cloud/types.js';\nimport { describeResetAt } from '../../../../core/verse/seat-readiness.js';\nimport type { Tone } from '../../../components/primitives/StatusBadge.js';\nimport type { MeterTone } from '../../../components/primitives/Meter.js';\nimport { tidyProse } from '../autonomy/format.js';\nimport { relativePhrase } from '../context/context-model.js';\nimport { usedPercentText } from '../percent-text.js';\n\n/** The repo a new task targets unless the operator names another (the self-improvement default). */\nexport const CLOUD_DEFAULT_REPO = DEFAULT_CLOUD_BUDGET.selfImprove.repo;\n\n/** Same rule the service enforces (core/cloud/service.ts), checked here so the dialog can say so first. */\nexport const CLOUD_REPO_PATTERN = /^[A-Za-z0-9-]+\\/[A-Za-z0-9._-]+$/;\n\n/**\n * A branch name git would accept, conservatively: no spaces or control\n * characters, no `..`, no leading `-` or `/`, no trailing `/` or `.lock`.\n * The server re-checks it against origin; this only catches typing slips.\n */\nconst BRANCH_CHARS = /^[A-Za-z0-9._/+-]{1,200}$/;\nexport function isBranchName(value: string): boolean {\n if (!BRANCH_CHARS.test(value)) return false;\n if (value.startsWith('-') || value.startsWith('/') || value.endsWith('/') || value.endsWith('.') || value.endsWith('.lock')) return false;\n return !value.includes('..') && !value.includes('//');\n}\n\n/** Only these hosts become links: the ids came from a CLI's stdout and gh's JSON. */\nexport function safeHref(url: string | null | undefined, host: 'claude.ai' | 'github.com'): string | null {\n if (typeof url !== 'string' || url.length > 2_048) return null;\n try {\n const parsed = new URL(url);\n return parsed.protocol === 'https:' && parsed.hostname === host ? parsed.toString() : null;\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Money and the credits meter\n// ---------------------------------------------------------------------------\n\n/** \"$250\", \"$12.50\" — whole dollars stay whole; never \"-$0\". */\nexport function formatDollars(value: number): string {\n if (!Number.isFinite(value)) return '—';\n const v = Math.abs(value) < 0.005 ? 0 : value;\n const text = Number.isInteger(v) ? String(Math.abs(v)) : Math.abs(v).toFixed(2);\n return `${v < 0 ? '−' : ''}$${text}`;\n}\n\nexport interface CreditsMeterView {\n /** Remaining, clamped at 0 for the bar (the text says when it went below). */\n value: number;\n max: number | null;\n /** \"$212 of $250 · estimate\" */\n text: string;\n /** \"15% used\" — the one percent rule. */\n usedText: string;\n tone: MeterTone;\n /** A plain sentence when the estimate is at or under the self-improvement reserve. */\n warning: string | null;\n}\n\nexport function creditsMeter(view: CloudBudgetView): CreditsMeterView {\n const total = view.creditsTotalUsd;\n const remaining = view.estimatedRemainingUsd;\n const reserve = view.budget.selfImprove.reserveUsd;\n const max = total > 0 ? total : null;\n const used = max === null ? null : ((total - Math.max(0, remaining)) / total) * 100;\n const tone: MeterTone = remaining <= 0 ? 'danger' : remaining < reserve ? 'warning' : 'accent';\n const warning = remaining <= 0\n ? 'The estimate says the credits are spent. Check the real balance on claude.ai and adjust the budget.'\n : remaining < reserve\n ? `Under the ${formatDollars(reserve)} reserve, so Verse stops launching self-improvement tasks.`\n : null;\n return {\n value: Math.max(0, remaining),\n max,\n text: `${formatDollars(Math.max(0, remaining))} of ${formatDollars(total)} · estimate`,\n usedText: used === null ? 'no credit total set' : `${usedPercentText(used)} used`,\n tone,\n warning,\n };\n}\n\n/** The next local midnight — when the per-day caps start counting again. */\nexport function nextLocalMidnight(now: number): number {\n const d = new Date(now);\n d.setHours(24, 0, 0, 0);\n return d.getTime();\n}\n\n/** \"3 of 20 sessions today · 1 running\" plus when the daily count resets. */\nexport function sessionsLine(view: CloudBudgetView, now: number = Date.now()): string {\n const reset = describeResetAt(new Date(nextLocalMidnight(now)).toISOString(), now);\n const running = view.running === 1 ? '1 running' : `${view.running} running`;\n return `${view.sessionsToday} of ${view.budget.maxSessionsPerDay} sessions today · ${running}${reset ? ` · resets ${reset}` : ''}`;\n}\n\nexport function selfImproveLine(view: CloudBudgetView): string {\n const si = view.budget.selfImprove;\n if (!si.enabled) return 'Self-improvement is off. Verse launches cloud tasks only when you ask.';\n return `${view.selfImproveToday} of ${si.maxPerDay} self-improvement launches today, on ${si.repo}. Stops under a ${formatDollars(si.reserveUsd)} estimated balance.`;\n}\n\n/** A gate's reason, fit to print (null when the gate is open). */\nexport function gateText(gate: { ok: boolean; reason: string | null }, fallback: string): string | null {\n if (gate.ok) return null;\n return gate.reason ? tidyProse(gate.reason) : fallback;\n}\n\nexport const SEAT_NOT_READY = \"The Claude seat isn't set up on this Mac.\";\n\nexport function seatText(seat: CloudSeatStatus): string | null {\n if (seat.ready) return null;\n return seat.reason ? tidyProse(seat.reason) : SEAT_NOT_READY;\n}\n\n/**\n * Why a launch button is disabled right now, or null when it may be pressed.\n * Seat first (nothing launches without it), then the budget gate.\n */\nexport function launchBlock(seat: CloudSeatStatus, budget: CloudBudgetView): string | null {\n return seatText(seat) ?? gateText(budget.canLaunch, 'The cloud budget does not allow another launch right now.');\n}\n\n// ---------------------------------------------------------------------------\n// Tasks\n// ---------------------------------------------------------------------------\n\nexport const STATE_WORD: Record<CloudTaskState, string> = {\n queued: 'Queued',\n launching: 'Launching',\n running: 'Running',\n 'pr-open': 'PR open',\n merged: 'Merged',\n closed: 'Closed',\n failed: 'Failed',\n expired: 'Expired',\n};\n\nexport const STATE_TONE: Record<CloudTaskState, Tone> = {\n queued: 'info',\n launching: 'info',\n running: 'running',\n 'pr-open': 'warning',\n merged: 'success',\n closed: 'neutral',\n failed: 'danger',\n expired: 'unknown',\n};\n\n/** A draft PR reads \"Draft PR\", not \"PR open\": the session may still be pushing. */\nexport function stateWord(task: Pick<CloudTaskV1, 'state' | 'pr'>): string {\n if (task.state === 'pr-open' && task.pr?.draft) return 'Draft PR';\n return STATE_WORD[task.state] ?? task.state;\n}\n\nexport function isTerminal(state: CloudTaskState): boolean {\n return CLOUD_TERMINAL_STATES.includes(state);\n}\n\n/** In flight: counted by the Fleet chip and the \"running\" line. */\nexport function isInFlight(state: CloudTaskState): boolean {\n return state === 'queued' || state === 'launching' || state === 'running';\n}\n\nconst DAY_MS = 24 * 60 * 60 * 1000;\n/** How many tasks the card lists before \"N more on GitHub\". */\nexport const CARD_TASK_LIMIT = 6;\n\n/**\n * What the card lists: everything still moving or waiting on review, then\n * anything that ended in the last 24 h (a failure you have not seen yet),\n * newest first. Older finished tasks live in `ashlr cloud list --all`.\n */\nexport function cardTasks(tasks: readonly CloudTaskV1[], now: number = Date.now()): { shown: CloudTaskV1[]; hidden: number } {\n const recent = (t: CloudTaskV1) => {\n const at = Date.parse(t.updatedAt);\n return Number.isFinite(at) && now - at < DAY_MS;\n };\n const listed = tasks\n .filter((t) => !isTerminal(t.state) || recent(t))\n .sort((a, b) => (Date.parse(b.createdAt) || 0) - (Date.parse(a.createdAt) || 0));\n return { shown: listed.slice(0, CARD_TASK_LIMIT), hidden: Math.max(0, listed.length - CARD_TASK_LIMIT) };\n}\n\nexport function inFlightCount(tasks: readonly CloudTaskV1[]): number {\n return tasks.filter((t) => isInFlight(t.state)).length;\n}\n\n/** \"ashlrai/ashlr-hub from master · started 5m ago\" */\nexport function taskMeta(task: CloudTaskV1, now: number = Date.now()): string {\n const when = task.state === 'queued' || task.state === 'launching' ? task.createdAt : task.launchedAt ?? task.createdAt;\n const phrase = relativePhrase(when, now);\n const verb = task.state === 'queued' ? 'queued' : task.state === 'failed' ? 'tried' : 'started';\n return `${task.repo} from ${task.baseBranch}${phrase ? ` · ${verb} ${phrase}` : ''}`;\n}\n\n/** The one line under a task's title: the report for a PR, else the state's reason. */\nexport function taskDetail(task: CloudTaskV1, now: number = Date.now()): string | null {\n if (task.report?.summary && (task.state === 'pr-open' || task.state === 'merged')) return tidyProse(task.report.summary, now);\n return task.stateReason ? tidyProse(task.stateReason, now) : null;\n}\n\n/** Dismiss is offered for anything Verse is still tracking; merged and closed tasks are already done. */\nexport function canDismiss(task: Pick<CloudTaskV1, 'state'>): boolean {\n return task.state !== 'merged' && task.state !== 'closed';\n}\n\n// ---------------------------------------------------------------------------\n// The launch form\n// ---------------------------------------------------------------------------\n\nexport interface LaunchDraft {\n repo: string;\n baseBranch: string;\n prompt: string;\n}\n\nexport type LaunchErrors = Partial<Record<keyof LaunchDraft, string>>;\n\nexport type LaunchValidation =\n | { ok: true; request: { repo: string; baseBranch?: string; prompt: string } }\n | { ok: false; errors: LaunchErrors };\n\nconst COUNT = new Intl.NumberFormat('en-US');\n\nexport function validateLaunch(draft: LaunchDraft): LaunchValidation {\n const errors: LaunchErrors = {};\n const repo = draft.repo.trim();\n const base = draft.baseBranch.trim();\n const prompt = draft.prompt.trim();\n if (!repo) errors.repo = 'Enter the GitHub repository as owner/name.';\n else if (!CLOUD_REPO_PATTERN.test(repo)) errors.repo = 'Use the GitHub owner/name form, like ashlrai/ashlr-hub.';\n if (base && !isBranchName(base)) errors.baseBranch = \"That isn't a branch name git accepts.\";\n if (!prompt) errors.prompt = 'Describe the task for the cloud session.';\n else if (prompt.length > CLOUD_PROMPT_MAX_CHARS) {\n errors.prompt = `The task is ${COUNT.format(prompt.length)} characters; the limit is ${COUNT.format(CLOUD_PROMPT_MAX_CHARS)}.`;\n }\n if (Object.keys(errors).length > 0) return { ok: false, errors };\n return { ok: true, request: { repo, ...(base ? { baseBranch: base } : {}), prompt } };\n}\n\n// ---------------------------------------------------------------------------\n// The budget form (Edit budget popover and the Usage panel share it)\n// ---------------------------------------------------------------------------\n\nexport interface BudgetForm {\n total: string;\n spent: string;\n perSession: string;\n maxPerDay: string;\n maxConcurrent: string;\n selfImprove: boolean;\n selfImproveMax: string;\n reserve: string;\n}\n\nexport type BudgetFormField = Exclude<keyof BudgetForm, 'selfImprove'>;\nexport type BudgetErrors = Partial<Record<BudgetFormField, string>>;\n\n/** Field → label and bounds. Bounds are the form's sanity check; the server clamps as well. */\nexport const BUDGET_FIELDS: ReadonlyArray<{\n field: BudgetFormField;\n label: string;\n hint: string;\n money: boolean;\n integer: boolean;\n min: number;\n max: number;\n}> = [\n { field: 'total', label: 'Credits on the account', hint: 'What claude.ai says you have.', money: true, integer: false, min: 0, max: 100_000 },\n { field: 'spent', label: 'Already spent', hint: 'Spent before Verse tracked it, or a correction after checking.', money: true, integer: false, min: -100_000, max: 100_000 },\n { field: 'perSession', label: 'Estimate per session', hint: 'Flat cost Verse counts for each launch.', money: true, integer: false, min: 0.01, max: 1_000 },\n { field: 'maxPerDay', label: 'Sessions per day', hint: 'From every origin, counted per local day.', money: false, integer: true, min: 0, max: 500 },\n { field: 'maxConcurrent', label: 'Running at once', hint: 'Launching or running together.', money: false, integer: true, min: 1, max: 50 },\n { field: 'selfImproveMax', label: 'Self-improvement per day', hint: 'Launches Verse may make on its own.', money: false, integer: true, min: 0, max: 100 },\n { field: 'reserve', label: 'Reserve', hint: 'Self-improvement stops under this estimated balance.', money: true, integer: false, min: 0, max: 100_000 },\n];\n\nfunction numText(n: number): string {\n return Number.isInteger(n) ? String(n) : String(Math.round(n * 100) / 100);\n}\n\nexport function budgetFormFrom(b: CloudBudgetV1): BudgetForm {\n return {\n total: numText(b.creditsTotalUsd),\n spent: numText(b.creditsSpentAdjustmentUsd),\n perSession: numText(b.estimatedCostPerSessionUsd),\n maxPerDay: numText(b.maxSessionsPerDay),\n maxConcurrent: numText(b.maxConcurrent),\n selfImprove: b.selfImprove.enabled,\n selfImproveMax: numText(b.selfImprove.maxPerDay),\n reserve: numText(b.selfImprove.reserveUsd),\n };\n}\n\nexport type BudgetPatch = { ok: true; update: CloudBudgetUpdate; changed: boolean } | { ok: false; errors: BudgetErrors };\n\n/**\n * The form as ONE update carrying only what changed — so two people editing\n * different fields never overwrite each other's numbers with stale ones.\n */\nexport function budgetPatch(form: BudgetForm, current: CloudBudgetV1): BudgetPatch {\n const errors: BudgetErrors = {};\n const values: Partial<Record<BudgetFormField, number>> = {};\n for (const spec of BUDGET_FIELDS) {\n const raw = form[spec.field].trim().replace(/^\\$/, '');\n const n = raw === '' ? Number.NaN : Number(raw);\n if (!Number.isFinite(n)) errors[spec.field] = 'Enter a number.';\n else if (spec.integer && !Number.isInteger(n)) errors[spec.field] = 'Enter a whole number.';\n else if (n < spec.min || n > spec.max) {\n errors[spec.field] = spec.money\n ? `Between ${formatDollars(spec.min)} and ${formatDollars(spec.max)}.`\n : `Between ${COUNT.format(spec.min)} and ${COUNT.format(spec.max)}.`;\n } else values[spec.field] = n;\n }\n if (Object.keys(errors).length > 0) return { ok: false, errors };\n const update: CloudBudgetUpdate = {};\n const same = (a: number, b: number) => Math.abs(a - b) < 0.005;\n if (!same(values.total!, current.creditsTotalUsd)) update.creditsTotalUsd = values.total!;\n if (!same(values.spent!, current.creditsSpentAdjustmentUsd)) update.creditsSpentAdjustmentUsd = values.spent!;\n if (!same(values.perSession!, current.estimatedCostPerSessionUsd)) update.estimatedCostPerSessionUsd = values.perSession!;\n if (!same(values.maxPerDay!, current.maxSessionsPerDay)) update.maxSessionsPerDay = values.maxPerDay!;\n if (!same(values.maxConcurrent!, current.maxConcurrent)) update.maxConcurrent = values.maxConcurrent!;\n const selfImprove: NonNullable<CloudBudgetUpdate['selfImprove']> = {};\n if (form.selfImprove !== current.selfImprove.enabled) selfImprove.enabled = form.selfImprove;\n if (!same(values.selfImproveMax!, current.selfImprove.maxPerDay)) selfImprove.maxPerDay = values.selfImproveMax!;\n if (!same(values.reserve!, current.selfImprove.reserveUsd)) selfImprove.reserveUsd = values.reserve!;\n if (Object.keys(selfImprove).length > 0) update.selfImprove = selfImprove;\n return { ok: true, update, changed: Object.keys(update).length > 0 };\n}\n\n// ---------------------------------------------------------------------------\n// The composer's \"Run in cloud\"\n// ---------------------------------------------------------------------------\n\nexport interface RunInCloudInputs {\n /** The cloud overview, or null when the route did not answer. */\n overview: { seat: CloudSeatStatus; budget: CloudBudgetView } | null;\n /** Why the overview is missing, when it is. */\n overviewReason: string | null;\n /** The chat project's GitHub `owner/name`, or null when it has none. */\n repo: string | null;\n /** True while the chat's roots have not been read yet. */\n rootsLoading: boolean;\n prompt: string;\n}\n\n/** Null when \"Run in cloud\" may be pressed; otherwise the tooltip saying why not. */\nexport function runInCloudBlock(inputs: RunInCloudInputs): string | null {\n if (!inputs.overview) return inputs.overviewReason ?? 'The cloud lane did not answer.';\n const seat = seatText(inputs.overview.seat);\n if (seat) return seat;\n if (!inputs.repo) {\n return inputs.rootsLoading\n ? \"Reading this chat's project…\"\n : \"This chat's project has no GitHub origin, so a cloud session has nothing to clone.\";\n }\n const gate = gateText(inputs.overview.budget.canLaunch, 'The cloud budget does not allow another launch right now.');\n if (gate) return gate;\n if (!inputs.prompt.trim()) return 'Type the task in the message box first.';\n if (inputs.prompt.trim().length > CLOUD_PROMPT_MAX_CHARS) {\n return `The message is over ${COUNT.format(CLOUD_PROMPT_MAX_CHARS)} characters — trim it to run it in the cloud.`;\n }\n return null;\n}\n","/*\n * routes/verse/cloud/cloud.module.css — the cloud lane's card, dialog,\n * popover, Usage panel, Fleet chip and composer action (3.11 unit C3).\n * Tokens only (style-scan-310): no raw hex colour, no px font size, and\n * every colour follows the theme. Nothing is ellipsized: long titles and\n * reasons wrap at word boundaries (only an unbreakable run, like a URL, may\n * break inside itself).\n */\n\n.body {\n display: flex;\n flex-direction: column;\n gap: var(--space-3);\n min-width: 0;\n}\n\n.actionsRow {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-2);\n}\n\n.headActions {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: var(--space-2);\n}\n\n.meterBlock {\n display: flex;\n flex-direction: column;\n gap: var(--space-1);\n}\n\n.estimateNote,\n.muted {\n margin: 0;\n color: var(--text-tertiary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n overflow-wrap: break-word;\n}\n\n.facts {\n margin: 0;\n color: var(--text-secondary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n overflow-wrap: break-word;\n}\n\n.link {\n color: var(--text-link);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n text-decoration: none;\n white-space: nowrap;\n}\n\n.link:hover {\n text-decoration: underline;\n}\n\n.link:focus-visible,\n.linkButton:focus-visible {\n outline: 2px solid var(--border-focus);\n outline-offset: 2px;\n}\n\n.linkButton {\n padding: 0;\n border: 0;\n background: none;\n color: var(--text-link);\n font: inherit;\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n cursor: pointer;\n white-space: nowrap;\n}\n\n.linkButton:hover {\n text-decoration: underline;\n}\n\n.linkButton:disabled {\n color: var(--text-disabled);\n cursor: default;\n text-decoration: none;\n}\n\n.toggleRow {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-2) var(--space-3);\n}\n\n.toggleText {\n flex: 1 1 16rem;\n min-width: 0;\n}\n\n/* ---- tasks ------------------------------------------------------------ */\n\n.tasks {\n display: flex;\n flex-direction: column;\n margin: 0;\n padding: 0;\n list-style: none;\n}\n\n.task {\n display: grid;\n grid-template-columns: auto minmax(0, 1fr);\n gap: var(--space-1) var(--space-3);\n padding: var(--space-2) 0;\n border-top: 1px solid var(--border-subtle);\n}\n\n.task:first-child {\n border-top: 0;\n}\n\n.taskChip {\n padding-top: 1px;\n}\n\n.taskMain {\n display: flex;\n flex-direction: column;\n gap: var(--space-1);\n min-width: 0;\n}\n\n.taskTitle {\n margin: 0;\n color: var(--text-primary);\n font-size: var(--text-sm-size);\n line-height: var(--text-sm-line);\n font-weight: var(--font-weight-medium);\n overflow-wrap: break-word;\n}\n\n.taskLinks {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--space-1) var(--space-3);\n}\n\n/* ---- notices (the NoticeSlot tones, inside a card) --------------------- */\n\n.notice {\n margin: 0;\n padding: var(--space-2) var(--space-3);\n border: 1px solid var(--border-subtle);\n border-radius: var(--radius-md);\n background: var(--bg-surface-raised);\n color: var(--text-secondary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n overflow-wrap: break-word;\n}\n\n.notice[data-tone='warning'] {\n border-color: var(--status-warning-solid);\n background: var(--status-warning-bg);\n color: var(--status-warning-fg);\n}\n\n.notice[data-tone='danger'] {\n border-color: var(--status-danger-solid);\n background: var(--status-danger-bg);\n color: var(--status-danger-fg);\n}\n\n.notice[data-tone='success'] {\n border-color: var(--status-success-solid);\n background: var(--status-success-bg);\n color: var(--status-success-fg);\n}\n\n.noticeRow {\n display: flex;\n flex-wrap: wrap;\n align-items: baseline;\n justify-content: space-between;\n gap: var(--space-1) var(--space-3);\n}\n\n/* ---- the Edit budget popover ------------------------------------------ */\n\n.popover {\n position: absolute;\n top: calc(100% + var(--space-2));\n right: 0;\n z-index: var(--z-dropdown);\n width: min(26rem, calc(100vw - var(--space-8)));\n max-height: min(34rem, 70vh);\n overflow-y: auto;\n padding: var(--space-4);\n border: 1px solid var(--border-default);\n border-radius: var(--radius-lg);\n background: var(--bg-surface-raised);\n box-shadow: var(--shadow-menu);\n}\n\n.popoverTitle {\n margin: 0 0 var(--space-3);\n color: var(--text-primary);\n font-size: var(--text-sm-size);\n line-height: var(--text-sm-line);\n font-weight: var(--font-weight-semibold);\n}\n\n/* ---- forms (launch dialog, budget) ------------------------------------ */\n\n.form {\n display: flex;\n flex-direction: column;\n gap: var(--space-3);\n}\n\n.fieldGrid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));\n gap: var(--space-3);\n}\n\n.textareaField {\n display: flex;\n flex-direction: column;\n gap: var(--space-1);\n}\n\n.label {\n color: var(--text-secondary);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n font-weight: var(--font-weight-medium);\n}\n\n.textarea {\n min-height: 8rem;\n padding: var(--space-2) var(--space-3);\n border: 1px solid var(--border-default);\n border-radius: var(--radius-md);\n background: var(--bg-input);\n color: var(--text-primary);\n font: inherit;\n font-size: var(--text-sm-size);\n line-height: var(--text-sm-line);\n resize: vertical;\n}\n\n.textarea:focus-visible {\n outline: 2px solid var(--border-focus);\n outline-offset: 0;\n}\n\n.textarea[aria-invalid='true'] {\n border-color: var(--status-danger-solid);\n}\n\n.fieldError {\n margin: 0;\n color: var(--status-danger-fg);\n font-size: var(--text-xs-size);\n line-height: var(--text-xs-line);\n}\n\n.formActions {\n display: flex;\n flex-wrap: wrap;\n justify-content: flex-end;\n gap: var(--space-2);\n}\n\n.dialogWidth {\n width: min(34rem, calc(100vw - var(--space-8)));\n}\n\n/* ---- the Fleet chip ---------------------------------------------------- */\n\n.laneChip[data-active='true'] {\n border-color: var(--status-running-solid);\n}\n\n/* ---- the composer's \"Run in cloud\" ------------------------------------- */\n\n.sheetAction {\n display: flex;\n flex-direction: column;\n gap: var(--space-2);\n padding-top: var(--space-3);\n border-top: 1px solid var(--border-subtle);\n}\n"],"mappings":"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"}
|
package/dist/core/web/public/next/assets/{command-bus-DqimKCUY.js → command-bus-CMvbYP5K.js}
RENAMED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as e,s as t}from"./index-
|
|
2
|
-
//# sourceMappingURL=command-bus-
|
|
1
|
+
import{a as e,s as t}from"./index-DbDw9UAs.js";var n=t(e(),1),r=new Map,i=null,a=4e3;function o(e){if(!i||i.id!==e)return;if(Date.now()>i.until){i=null;return}let{invocation:t}=i;i=null,queueMicrotask(()=>{c(e,t)})}function s(e,t){let n=r.get(e)??[];return n.push(t),r.set(e,n),o(e),()=>{let n=r.get(e);if(!n)return;let i=n.lastIndexOf(t);i>=0&&n.splice(i,1),n.length===0&&r.delete(e)}}function c(e,t={}){let n=r.get(e);if(!n)return!1;for(let r of[...n].reverse())try{if(r(t)!==!1)return!0}catch(t){return console.error(`[verse] command ${e} failed`,t),!0}return!1}function l(e,t={},n=a){return c(e,t)?!0:(i={id:e,invocation:t,until:Date.now()+n},!1)}function u(e,t,r=!0){let i=(0,n.useRef)(t);i.current=t,(0,n.useEffect)(()=>{if(r)return s(e,e=>i.current(e))},[e,r])}export{u as a,l as i,s as n,c as r,a as t};
|
|
2
|
+
//# sourceMappingURL=command-bus-CMvbYP5K.js.map
|
package/dist/core/web/public/next/assets/{command-bus-DqimKCUY.js.map → command-bus-CMvbYP5K.js.map}
RENAMED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"command-bus-
|
|
1
|
+
{"version":3,"file":"command-bus-CMvbYP5K.js","names":[],"sources":["../../../../../../src/web-ui/routes/verse/shell/command-bus.ts"],"sourcesContent":["/**\n * routes/verse/shell/command-bus.ts — how a catalog command reaches the unit\n * that owns its behaviour (unit C1; the keys and titles are C0's\n * command-catalog.ts).\n *\n * The catalog is one table; the behaviour behind it is spread over units:\n * the shell runs the global commands, the chat (C2) runs `dock.*` and\n * `chat.*`, the composer (C3) runs `composer.*`. The palette, the desktop\n * menu bridge and the shell's key handler all call `runCommand(id)`; the\n * owning unit registers a handler for the ids it serves:\n *\n * useCommandHandler('dock.terminal', () => openDockPane('terminal'));\n *\n * A handler may return `false` to decline (e.g. a composer command with no\n * chat open), and the most recently registered handler for an id is asked\n * first — a mounted chat outranks nothing, a focused one outranks a hidden\n * one because it registers last.\n *\n * DELIVERY LATER. A chat command run from another surface (⌘K \"Terminal\"\n * while on Fleet) has no handler until Chat mounts. The shell switches to\n * Chat and parks the command with `runCommandWhenReady`; the first handler to\n * register for that id within the window receives it. Parked commands expire\n * — a command that runs 20 s after the operator asked for it is a surprise,\n * not a feature.\n *\n * Framework-free except the one hook at the bottom.\n */\nimport { useEffect, useRef } from 'react';\n\n/** What a palette argument (Tab) filled in, or what a caller passes along. */\nexport interface CommandArgument {\n kind: 'seat' | 'project' | 'section' | 'session';\n id: string;\n label: string;\n}\n\nexport interface CommandInvocation {\n argument?: CommandArgument;\n /** Where it came from — handlers may word a toast differently for a key press. */\n via?: 'palette' | 'key' | 'menu' | 'button';\n}\n\n/** Return false to decline (the next handler is asked); anything else = handled. */\nexport type CommandHandler = (invocation: CommandInvocation) => void | boolean;\n\nconst handlers = new Map<string, CommandHandler[]>();\n\ninterface Parked {\n id: string;\n invocation: CommandInvocation;\n until: number;\n}\n\nlet parked: Parked | null = null;\n\nexport const PARKED_COMMAND_TTL_MS = 4_000;\n\nfunction deliverParked(id: string): void {\n if (!parked || parked.id !== id) return;\n if (Date.now() > parked.until) {\n parked = null;\n return;\n }\n const { invocation } = parked;\n parked = null;\n // After the registering effect has finished: the handler's own component\n // is mounted, but its sibling effects may not have run yet.\n queueMicrotask(() => {\n runCommand(id, invocation);\n });\n}\n\n/** Register `handler` for command `id`. Returns the unregister function. */\nexport function registerCommandHandler(id: string, handler: CommandHandler): () => void {\n const list = handlers.get(id) ?? [];\n list.push(handler);\n handlers.set(id, list);\n deliverParked(id);\n return () => {\n const current = handlers.get(id);\n if (!current) return;\n const index = current.lastIndexOf(handler);\n if (index >= 0) current.splice(index, 1);\n if (current.length === 0) handlers.delete(id);\n };\n}\n\nexport function hasCommandHandler(id: string): boolean {\n return (handlers.get(id)?.length ?? 0) > 0;\n}\n\n/** Run `id` now. True when a handler took it. */\nexport function runCommand(id: string, invocation: CommandInvocation = {}): boolean {\n const list = handlers.get(id);\n if (!list) return false;\n for (const handler of [...list].reverse()) {\n try {\n if (handler(invocation) !== false) return true;\n } catch (err) {\n // A broken handler must not take the palette or the key handler down.\n console.error(`[verse] command ${id} failed`, err);\n return true;\n }\n }\n return false;\n}\n\n/** Run `id` now if someone serves it, else hold it for the next handler to register (within the TTL). */\nexport function runCommandWhenReady(id: string, invocation: CommandInvocation = {}, ttlMs: number = PARKED_COMMAND_TTL_MS): boolean {\n if (runCommand(id, invocation)) return true;\n parked = { id, invocation, until: Date.now() + ttlMs };\n return false;\n}\n\n/** Test hygiene. */\nexport function resetCommandBus(): void {\n handlers.clear();\n parked = null;\n}\n\n/**\n * Serve command `id` while mounted (and `enabled`). The latest `handler` is\n * always the one called, so an inline closure does not re-register each render.\n */\nexport function useCommandHandler(id: string, handler: CommandHandler, enabled = true): void {\n const ref = useRef(handler);\n ref.current = handler;\n useEffect(() => {\n if (!enabled) return;\n return registerCommandHandler(id, (invocation) => ref.current(invocation));\n }, [id, enabled]);\n}\n"],"mappings":"8DA6CM,EAAW,IAAI,IAQjB,EAAwB,KAEf,EAAwB,IAErC,SAAS,EAAc,EAAkB,CACvC,GAAI,CAAC,GAAU,EAAO,KAAO,EAAI,OACjC,GAAI,KAAK,IAAI,EAAI,EAAO,MAAO,CAC7B,EAAS,KACT,MACF,CACA,GAAM,CAAE,cAAe,EACvB,EAAS,KAGT,mBAAqB,CACnB,EAAW,EAAI,CAAU,CAC3B,CAAC,CACH,CAGA,SAAgB,EAAuB,EAAY,EAAqC,CACtF,IAAM,EAAO,EAAS,IAAI,CAAE,GAAK,CAAC,EAIlC,OAHA,EAAK,KAAK,CAAO,EACjB,EAAS,IAAI,EAAI,CAAI,EACrB,EAAc,CAAE,MACH,CACX,IAAM,EAAU,EAAS,IAAI,CAAE,EAC/B,GAAI,CAAC,EAAS,OACd,IAAM,EAAQ,EAAQ,YAAY,CAAO,EACrC,GAAS,GAAG,EAAQ,OAAO,EAAO,CAAC,EACnC,EAAQ,SAAW,GAAG,EAAS,OAAO,CAAE,CAC9C,CACF,CAOA,SAAgB,EAAW,EAAY,EAAgC,CAAC,EAAY,CAClF,IAAM,EAAO,EAAS,IAAI,CAAE,EAC5B,GAAI,CAAC,EAAM,MAAO,GAClB,IAAK,IAAM,IAAW,CAAC,GAAG,CAAI,CAAC,CAAC,QAAQ,EACtC,GAAI,CACF,GAAI,EAAQ,CAAU,IAAM,GAAO,MAAO,EAC5C,OAAS,EAAK,CAGZ,OADA,QAAQ,MAAM,mBAAmB,EAAG,SAAU,CAAG,EAC1C,EACT,CAEF,MAAO,EACT,CAGA,SAAgB,EAAoB,EAAY,EAAgC,CAAC,EAAG,EAAgB,EAAgC,CAGlI,OAFI,EAAW,EAAI,CAAU,EAAU,IACvC,EAAS,CAAE,KAAI,aAAY,MAAO,KAAK,IAAI,EAAI,CAAM,EAC9C,GACT,CAYA,SAAgB,EAAkB,EAAY,EAAyB,EAAU,GAAY,CAC3F,IAAM,GAAA,EAAM,EAAA,OAAA,CAAO,CAAO,EAC1B,EAAI,QAAU,GACd,EAAA,EAAA,UAAA,KAAgB,CACT,KACL,OAAO,EAAuB,EAAK,GAAe,EAAI,QAAQ,CAAU,CAAC,CAC3E,EAAG,CAAC,EAAI,CAAO,CAAC,CAClB"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
var e=`ashlr:command`,t={"open-settings":`section.settings`,"toggle-theme":`appearance.toggle-theme`,"open-needs-you":`needs-you.open`,"new-chat":`chat.new`,"focus-composer":`app.summon`},n=/^open-session:([A-Za-z0-9._-]{1,128})$/;function r(e){if(typeof e!=`string`)return null;if(Object.prototype.hasOwnProperty.call(t,e)){let n=e;return{kind:`command`,name:n,commandId:t[n]}}let r=n.exec(e);return r?{kind:`open-session`,sessionId:r[1]}:null}var i=[{id:`needs-you`,label:`Needs you`},{id:`chats`,label:`Chats`},{id:`actions`,label:`Actions`},{id:`go-to`,label:`Go to`},{id:`seats-apps`,label:`Seats & Apps`},{id:`projects`,label:`Projects`}],a={">":`actions`,"#":`chats`},o=[`Surfaces`,`Navigation`,`Chat`,`Dock`,`Composer`,`Needs you`,`Charts`,`App`],s={confirm:{title:`Stop every running chat?`,body:`Each running turn is cancelled. Queued follow-ups are held, not sent.`,confirmLabel:`Stop chats`,destructive:!0},token:!0},c={confirm:{title:`Stop the fleet?`,body:`Autonomous work halts within one tick and stays stopped until you resume it. Chats keep running.`,confirmLabel:`Stop fleet`,destructive:!0},token:!0},l=(e,t,n)=>({id:`surface.${t}`,title:`Go to ${n}`,scope:`global`,keys:[{key:String(e),mod:!0}],group:`go-to`,section:`Surfaces`,keywords:[n.toLowerCase()]}),u=[l(1,`command`,`Command`),l(2,`fleet`,`Fleet`),l(3,`growth`,`Growth`),l(4,`mind`,`Mind`),l(5,`chat`,`Chat`),{id:`section.settings`,title:`Open Settings`,scope:`global`,keys:[{key:`,`,mod:!0}],group:`go-to`,section:`Surfaces`,keywords:[`preferences`,`appearance`],native:{kind:`menu`,desktopCommand:`open-settings`,accelerator:`CmdOrCtrl+,`}},{id:`section.apps`,title:`Open Apps & Accounts`,scope:`global`,keys:[],group:`go-to`,section:`Surfaces`,keywords:[`mcp`,`accounts`,`seats`,`integrations`,`ollama`]},{id:`section.usage`,title:`Open Usage`,scope:`global`,keys:[],group:`go-to`,section:`Surfaces`,keywords:[`capacity`,`spend`,`limits`]},{id:`palette.open`,title:`Command palette`,scope:`global`,keys:[{key:`k`,mod:!0}],group:null,section:`Navigation`},{id:`needs-you.open`,title:`Open Needs you`,scope:`global`,keys:[{key:`j`,mod:!0}],group:`actions`,section:`Navigation`,keywords:[`approvals`,`inbox`,`triage`]},{id:`shortcuts.open`,title:`Keyboard shortcuts`,scope:`global`,keys:[{key:`/`,mod:!0}],group:`actions`,section:`Navigation`,keywords:[`keys`,`help`]},{id:`resources.toggle`,title:`Show resources`,scope:`global`,keys:[{key:`.`,mod:!0}],group:`actions`,section:`Navigation`,keywords:[`accounts`,`seats`,`capacity`,`credits`,`cloud`,`local models`]},{id:`resources.bar.toggle`,title:`Show or hide the resource bar`,scope:`global`,keys:[],group:`actions`,section:`Navigation`,keywords:[`battery`,`usage bars`,`accounts`,`capacity`,`rail`,`resources`]},{id:`history.back`,title:`Back`,scope:`global`,keys:[{key:`[`,mod:!0}],group:null,section:`Navigation`,note:`through surfaces and chats`},{id:`history.forward`,title:`Forward`,scope:`global`,keys:[{key:`]`,mod:!0}],group:null,section:`Navigation`,note:`through surfaces and chats`},{id:`chat.recent-next`,title:`Next recent chat`,scope:`global`,keys:[{key:`tab`,ctrl:!0}],group:null,section:`Navigation`},{id:`chat.recent-prev`,title:`Previous recent chat`,scope:`global`,keys:[{key:`tab`,ctrl:!0,shift:!0}],group:null,section:`Navigation`},{id:`rail.toggle-labels`,title:`Show or hide rail labels`,scope:`global`,keys:[{key:`\\`,mod:!0,shift:!0}],group:`actions`,section:`Navigation`},{id:`chat.new`,title:`New chat`,scope:`global`,keys:[{key:`n`,mod:!0}],group:`actions`,section:`Chat`},{id:`chat.new-on`,title:`New chat on…`,scope:`global`,keys:[],group:`actions`,section:`Chat`,argument:{kind:`seat`,prompt:`Seat`},keywords:[`seat`,`account`,`engine`]},{id:`chat.sidebar`,title:`Show or hide the chat list`,scope:`chat`,keys:[{key:`b`,mod:!0}],group:`actions`,section:`Chat`},{id:`chat.find`,title:`Find in chat`,scope:`chat`,keys:[{key:`f`,mod:!0}],group:`actions`,section:`Chat`,keywords:[`search`]},{id:`chat.turn-prev`,title:`Previous turn`,scope:`chat`,keys:[{key:`arrowup`,alt:!0}],group:null,section:`Chat`},{id:`chat.turn-next`,title:`Next turn`,scope:`chat`,keys:[{key:`arrowdown`,alt:!0}],group:null,section:`Chat`},{id:`chats.stop-all`,title:`Stop running chats…`,scope:`global`,keys:[],group:`actions`,section:`Chat`,guard:s,keywords:[`cancel`,`halt`]},{id:`git.create-pr`,title:`Create pull request…`,scope:`chat`,keys:[],group:`actions`,section:`Chat`,keywords:[`pr`,`github`,`git`,`open pull request`]},{id:`git.merge-pr`,title:`Merge pull request…`,scope:`chat`,keys:[],group:`actions`,section:`Chat`,keywords:[`pr`,`github`,`git`,`land`,`ship`]},{id:`dock.toggle`,title:`Show or hide the dock`,scope:`chat`,keys:[{key:`\\`,mod:!0}],group:`actions`,section:`Dock`},{id:`dock.terminal`,title:`Terminal`,scope:`chat`,keys:[{key:"`",ctrl:!0}],group:`actions`,section:`Dock`,keywords:[`shell`,`console`]},{id:`dock.terminal-new`,title:`New terminal tab`,scope:`chat`,keys:[{key:"`",ctrl:!0,shift:!0}],group:`actions`,section:`Dock`},{id:`dock.preview`,title:`Preview`,scope:`chat`,keys:[{key:`b`,mod:!0,shift:!0}],group:`actions`,section:`Dock`,keywords:[`browser`,`dev server`]},{id:`dock.diff`,title:`Review changes`,scope:`chat`,keys:[{key:`d`,mod:!0,shift:!0}],group:`actions`,section:`Dock`,keywords:[`diff`,`git`]},{id:`composer.permission`,title:`Permission mode…`,scope:`chat`,keys:[{key:`m`,mod:!0,shift:!0}],group:`actions`,section:`Composer`,keywords:[`plan`,`bypass`,`auto`]},{id:`composer.model`,title:`Model…`,scope:`chat`,keys:[{key:`i`,mod:!0,shift:!0}],group:`actions`,section:`Composer`},{id:`composer.effort`,title:`Effort…`,scope:`chat`,keys:[{key:`e`,mod:!0,shift:!0}],group:`actions`,section:`Composer`,keywords:[`reasoning`,`thinking`]},{id:`composer.attach`,title:`Attach files…`,scope:`chat`,keys:[{key:`u`,mod:!0}],group:`actions`,section:`Composer`,keywords:[`upload`,`image`]},{id:`composer.send`,title:`Send`,scope:`composer`,keys:[{key:`enter`}],group:null,section:`Composer`,note:`queues while a turn runs (up to 3)`},{id:`composer.stop-and-send`,title:`Stop and send`,scope:`composer`,keys:[{key:`enter`,mod:!0,shift:!0}],group:null,section:`Composer`},{id:`composer.stop`,title:`Stop the running turn`,scope:`composer`,keys:[{key:`escape`}],group:null,section:`Composer`,note:`only from an empty composer with no overlay open`},{id:`drawer.next`,title:`Next item`,scope:`drawer`,keys:[{key:`j`}],group:null,section:`Needs you`},{id:`drawer.prev`,title:`Previous item`,scope:`drawer`,keys:[{key:`k`}],group:null,section:`Needs you`},{id:`drawer.open`,title:`Open item`,scope:`drawer`,keys:[{key:`enter`}],group:null,section:`Needs you`},{id:`drawer.approve`,title:`Approve`,scope:`drawer`,keys:[{key:`a`}],group:null,section:`Needs you`,note:`confirms, then asks for the token`},{id:`drawer.reject`,title:`Reject`,scope:`drawer`,keys:[{key:`r`}],group:null,section:`Needs you`,note:`confirms, then asks for the token`},{id:`drawer.veto`,title:`Veto`,scope:`drawer`,keys:[{key:`v`}],group:null,section:`Needs you`,note:`confirms, then asks for the token`},{id:`drawer.done`,title:`Mark done`,scope:`drawer`,keys:[{key:`e`}],group:null,section:`Needs you`},{id:`drawer.split-prev`,title:`Previous split`,scope:`drawer`,keys:[{key:`h`}],group:null,section:`Needs you`},{id:`drawer.split-next`,title:`Next split`,scope:`drawer`,keys:[{key:`l`}],group:null,section:`Needs you`},{id:`chart.table`,title:`Show as a table`,scope:`chart`,keys:[{key:`t`}],group:null,section:`Charts`,note:`when a chart card has focus`},{id:`appearance.toggle-theme`,title:`Toggle light / dark`,scope:`global`,keys:[{key:`l`,mod:!0,shift:!0}],group:`actions`,section:`App`,keywords:[`theme`,`dark mode`],native:{kind:`menu`,desktopCommand:`toggle-theme`,accelerator:`Shift+CmdOrCtrl+L`}},{id:`app.summon`,title:`Show Verse and focus the composer`,scope:`global`,keys:[{key:`space`,ctrl:!0,alt:!0}],group:null,section:`App`,note:`system-wide; turn on in Settings ▸ Desktop`,native:{kind:`global-hotkey`,accelerator:`Control+Alt+Space`}},{id:`fleet.stop`,title:`Stop the fleet…`,scope:`global`,keys:[],group:`actions`,section:`App`,guard:c,keywords:[`kill`,`halt`,`autonomy`]}],d=new Map(u.map(e=>[e.id,e]));function f(e){return d.get(e)??null}function p(e){return u.filter(t=>t.group===e)}function m(){let e=u.filter(e=>e.keys.length>0);return o.map(t=>({section:t,commands:e.filter(e=>e.section===t)})).filter(e=>e.commands.length>0)}function h(){if(typeof navigator>`u`)return`mac`;let e=navigator,t=e.userAgentData?.platform??e.platform??``;return/mac|iphone|ipad|ipod/i.test(t)?`mac`:`other`}var g={Backslash:`\\`,Backquote:"`",BracketLeft:`[`,BracketRight:`]`,Slash:`/`,Comma:`,`,Period:`.`,Semicolon:`;`,Quote:`'`,Minus:`-`,Equal:`=`,Space:`space`},_={" ":`space`,spacebar:`space`,esc:`escape`,return:`enter`,up:`arrowup`,down:`arrowdown`,left:`arrowleft`,right:`arrowright`};function v(e){let t=e.key??``;if(/^[a-z]$/i.test(t))return t.toLowerCase();let n=e.code??``;if(/^Digit[0-9]$/.test(n))return n.slice(5);if(g[n])return g[n];if(/^Key[A-Z]$/.test(n))return n.slice(3).toLowerCase();let r=t.toLowerCase();return _[r]??r}function y(e,t,n=h()){if(n===`mac`){if(!!t.mod!==e.metaKey||!!t.ctrl!==e.ctrlKey)return!1}else if(e.metaKey||!!(t.mod||t.ctrl)!==e.ctrlKey)return!1;return!!t.shift!==e.shiftKey||!!t.alt!==e.altKey?!1:v(e)===t.key}function b(e,t,n=h()){for(let r of t)for(let t of u)if(t.scope===r&&t.native?.kind!==`global-hotkey`&&t.keys.some(t=>y(e,t,n)))return t;return null}var x={enter:`↩`,escape:`Esc`,tab:`⇥`,space:`Space`,arrowup:`↑`,arrowdown:`↓`,arrowleft:`←`,arrowright:`→`,backspace:`⌫`},S={enter:`Enter`,escape:`Esc`,tab:`Tab`,space:`Space`,arrowup:`↑`,arrowdown:`↓`,arrowleft:`←`,arrowright:`→`,backspace:`Backspace`};function C(e,t=h()){let n=e.key.length===1?e.key.toUpperCase():(t===`mac`?x:S)[e.key]??e.key;return t===`mac`?`${e.ctrl?`⌃`:``}${e.alt?`⌥`:``}${e.shift?`⇧`:``}${e.mod?`⌘`:``}${n}`:[e.mod||e.ctrl?`Ctrl`:``,e.alt?`Alt`:``,e.shift?`Shift`:``,n].filter(Boolean).join(`+`)}export{h as a,C as c,r as d,m as f,y as i,b as l,a as n,v as o,e as r,f as s,i as t,p as u};
|
|
2
|
+
//# sourceMappingURL=command-catalog-Bb_PxC5w.js.map
|