@tokenoftrust/storefront-runner 1.4.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (474) hide show
  1. package/apps/storefront/astro.config.mjs +17 -1
  2. package/apps/storefront/env.d.ts +54 -5
  3. package/apps/storefront/integrations/materialize-guard.mjs +54 -0
  4. package/apps/storefront/migrations-apps/0002_good_switch.sql +26 -0
  5. package/apps/storefront/migrations-apps/0003_mute_marauders.sql +12 -0
  6. package/apps/storefront/migrations-apps/0004_deep_morph.sql +36 -0
  7. package/apps/storefront/migrations-apps/0005_common_mystique.sql +23 -0
  8. package/apps/storefront/migrations-apps/0006_broad_microchip.sql +16 -0
  9. package/apps/storefront/migrations-apps/0007_brave_outlaw_kid.sql +15 -0
  10. package/apps/storefront/migrations-apps/0008_blue_dazzler.sql +11 -0
  11. package/apps/storefront/migrations-apps/0009_bored_shinobi_shaw.sql +15 -0
  12. package/apps/storefront/migrations-apps/0010_ws4_verified_provider_measurement.sql +35 -0
  13. package/apps/storefront/migrations-apps/meta/0002_snapshot.json +1160 -0
  14. package/apps/storefront/migrations-apps/meta/0003_snapshot.json +1235 -0
  15. package/apps/storefront/migrations-apps/meta/0004_snapshot.json +1474 -0
  16. package/apps/storefront/migrations-apps/meta/0005_snapshot.json +1617 -0
  17. package/apps/storefront/migrations-apps/meta/0006_snapshot.json +1716 -0
  18. package/apps/storefront/migrations-apps/meta/0007_snapshot.json +1812 -0
  19. package/apps/storefront/migrations-apps/meta/0008_snapshot.json +1881 -0
  20. package/apps/storefront/migrations-apps/meta/0009_snapshot.json +1979 -0
  21. package/apps/storefront/migrations-apps/meta/_journal.json +56 -0
  22. package/apps/storefront/public/apps/tot.demo.analytics/0.1.0/entry.js +42 -0
  23. package/apps/storefront/public/js/dashboard-team.js +38 -2
  24. package/apps/storefront/public/platform/amplitude-http/v1/entry.mjs +19 -0
  25. package/apps/storefront/public/platform/google-measurement/v1/entry.mjs +19 -0
  26. package/apps/storefront/public/shared/commerce-chrome.css +20 -2
  27. package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +68 -0
  28. package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +111 -0
  29. package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +2 -0
  30. package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +247 -0
  31. package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +73 -0
  32. package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +79 -0
  33. package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +10982 -0
  34. package/apps/storefront/scripts/import-v84-through-ws2.ts +38 -0
  35. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +2 -0
  36. package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +4 -0
  37. package/apps/storefront/scripts/install-v84-native-through-ws1.ts +185 -0
  38. package/apps/storefront/scripts/prove-clone-isolation.ts +506 -0
  39. package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +148 -0
  40. package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +35 -0
  41. package/apps/storefront/scripts/replay-v84-clean-clone.ts +214 -0
  42. package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +18 -0
  43. package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +18 -0
  44. package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +18 -0
  45. package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +17 -0
  46. package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +15 -0
  47. package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +21 -0
  48. package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +42 -0
  49. package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +145 -0
  50. package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +18 -0
  51. package/apps/storefront/src/components/Img.astro +1 -1
  52. package/apps/storefront/src/components/Seo.astro +65 -1
  53. package/apps/storefront/src/components/admin/AdminBlogDetailRail.astro +878 -0
  54. package/apps/storefront/src/components/admin/AdminBlogTab.astro +1435 -0
  55. package/apps/storefront/src/components/admin/AdminCustomersTab.astro +3 -7
  56. package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +105 -0
  57. package/apps/storefront/src/components/admin/AdminPublishTab.astro +1980 -2564
  58. package/apps/storefront/src/components/admin/AdminSettingsTab.astro +2 -2
  59. package/apps/storefront/src/components/admin/AdminSubscriptionsTab.astro +1 -3
  60. package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +5 -5
  61. package/apps/storefront/src/components/blog/BlogPaywall.astro +87 -0
  62. package/apps/storefront/src/components/blog/PostAccessBadge.astro +43 -0
  63. package/apps/storefront/src/components/blog/PostGrid.astro +57 -0
  64. package/apps/storefront/src/components/blog/ReactionBar.astro +214 -0
  65. package/apps/storefront/src/components/chrome/NavDropdown.astro +12 -0
  66. package/apps/storefront/src/components/chrome/SiteFooter.astro +7 -2
  67. package/apps/storefront/src/components/chrome/SiteHeader.astro +46 -10
  68. package/apps/storefront/src/components/compliance/AdultSignatureNotice.astro +1 -1
  69. package/apps/storefront/src/components/compliance/ComplianceNotice.astro +1 -2
  70. package/apps/storefront/src/components/compliance/ExciseTaxNotice.astro +1 -2
  71. package/apps/storefront/src/components/compliance/PactActNotice.astro +1 -1
  72. package/apps/storefront/src/components/compliance/Prop65Warning.astro +1 -2
  73. package/apps/storefront/src/components/compliance/PurchaseLimitNotice.astro +2 -3
  74. package/apps/storefront/src/components/compliance/ShippingRestrictionNotice.astro +2 -2
  75. package/apps/storefront/src/components/compliance/StateEligibilityNotice.astro +2 -3
  76. package/apps/storefront/src/components/content/TrustStrip.astro +4 -4
  77. package/apps/storefront/src/components/islands/MobileNav.tsx +26 -1
  78. package/apps/storefront/src/components/islands/VariantSelector.tsx +27 -2
  79. package/apps/storefront/src/components/islands/useCheckoutHandoffCorrelation.ts +75 -0
  80. package/apps/storefront/src/components/membership/IllustrativeLabel.astro +1 -2
  81. package/apps/storefront/src/components/membership/TierCards.astro +2 -2
  82. package/apps/storefront/src/components/membership/TierComparisonTable.astro +2 -2
  83. package/apps/storefront/src/components/subscription/SavingsExplainer.astro +2 -2
  84. package/apps/storefront/src/config/adminTenantLookup.ts +35 -0
  85. package/apps/storefront/src/config/devTenantSeed.ts +9 -9
  86. package/apps/storefront/src/config/resolver.ts +4 -4
  87. package/apps/storefront/src/layouts/Layout.astro +146 -21
  88. package/apps/storefront/src/lib/activity/alerts.ts +17 -19
  89. package/apps/storefront/src/lib/activity/candidateSubmitLease.ts +87 -0
  90. package/apps/storefront/src/lib/activity/changeActorAttribution.ts +89 -9
  91. package/apps/storefront/src/lib/activity/deployVersion.ts +20 -27
  92. package/apps/storefront/src/lib/activity/ingest.ts +8 -8
  93. package/apps/storefront/src/lib/activity/ingestAuth.ts +1 -1
  94. package/apps/storefront/src/lib/activity/killSwitch.ts +6 -6
  95. package/apps/storefront/src/lib/activity/query.ts +9 -9
  96. package/apps/storefront/src/lib/activity/recordActivity.ts +11 -15
  97. package/apps/storefront/src/lib/activity/store.ts +4 -6
  98. package/apps/storefront/src/lib/activity/uiActor.ts +12 -12
  99. package/apps/storefront/src/lib/activity/workerCommit.ts +6 -6
  100. package/apps/storefront/src/lib/admin/adminShell.ts +292 -0
  101. package/apps/storefront/src/lib/admin/ai/approvalMode.ts +2 -3
  102. package/apps/storefront/src/lib/admin/ai/audit.ts +3 -5
  103. package/apps/storefront/src/lib/admin/checkoutStyleInput.ts +7 -7
  104. package/apps/storefront/src/lib/admin/checkoutStyleTarget.ts +3 -3
  105. package/apps/storefront/src/lib/admin/envelope.ts +1 -2
  106. package/apps/storefront/src/lib/admin/orderClient.ts +3 -5
  107. package/apps/storefront/src/lib/admin/services/orders.ts +17 -19
  108. package/apps/storefront/src/lib/admin/viewmodels/aiWorkflows.ts +4 -6
  109. package/apps/storefront/src/lib/admin/viewmodels/settings.ts +1 -1
  110. package/apps/storefront/src/lib/apps/adminService.ts +14 -4
  111. package/apps/storefront/src/lib/apps/adminSession.ts +54 -45
  112. package/apps/storefront/src/lib/apps/apiAuth.ts +2 -2
  113. package/apps/storefront/src/lib/apps/apiRoute.ts +2 -2
  114. package/apps/storefront/src/lib/apps/appVersionStore.ts +281 -0
  115. package/apps/storefront/src/lib/apps/catalogMapper.ts +1 -1
  116. package/apps/storefront/src/lib/apps/consentConfigStore.ts +228 -0
  117. package/apps/storefront/src/lib/apps/credentials.ts +2 -2
  118. package/apps/storefront/src/lib/apps/gatewayKeys.ts +4 -4
  119. package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +447 -0
  120. package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +367 -0
  121. package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +315 -0
  122. package/apps/storefront/src/lib/apps/healthAggregate.ts +5 -5
  123. package/apps/storefront/src/lib/apps/integrationLifecycle.ts +614 -0
  124. package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +125 -0
  125. package/apps/storefront/src/lib/apps/integrationReadModel.ts +563 -0
  126. package/apps/storefront/src/lib/apps/integrationsTab.ts +547 -0
  127. package/apps/storefront/src/lib/apps/integrationsView.ts +106 -0
  128. package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +599 -0
  129. package/apps/storefront/src/lib/apps/orders/attributionService.ts +2 -2
  130. package/apps/storefront/src/lib/apps/orders/customerHash.ts +1 -1
  131. package/apps/storefront/src/lib/apps/orders/foxyOrderClient.ts +12 -12
  132. package/apps/storefront/src/lib/apps/orders/idempotency.ts +7 -7
  133. package/apps/storefront/src/lib/apps/orders/orderConversionD1.ts +150 -0
  134. package/apps/storefront/src/lib/apps/orders/orderForwardReceiver.ts +9 -1
  135. package/apps/storefront/src/lib/apps/orders/orderMapper.ts +2 -2
  136. package/apps/storefront/src/lib/apps/orders/ordersStore.ts +7 -7
  137. package/apps/storefront/src/lib/apps/orders/verifiedProviderMeasurement.ts +117 -0
  138. package/apps/storefront/src/lib/apps/registryService.ts +270 -16
  139. package/apps/storefront/src/lib/apps/scopes.ts +2 -2
  140. package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +170 -0
  141. package/apps/storefront/src/lib/apps/tokenIssuer.ts +4 -4
  142. package/apps/storefront/src/lib/apps/tokenVerifier.ts +6 -6
  143. package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +16 -0
  144. package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +105 -0
  145. package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +177 -0
  146. package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +251 -0
  147. package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +241 -0
  148. package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +211 -0
  149. package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +409 -0
  150. package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +368 -0
  151. package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +188 -0
  152. package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +291 -0
  153. package/apps/storefront/src/lib/apps/widgets/eligibility.ts +2 -2
  154. package/apps/storefront/src/lib/apps/widgets/frameProps.ts +3 -3
  155. package/apps/storefront/src/lib/apps/widgets/launchToken.ts +4 -4
  156. package/apps/storefront/src/lib/apps/widgets/placements.ts +2 -2
  157. package/apps/storefront/src/lib/apps/widgets/renderSlot.ts +1 -1
  158. package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +95 -0
  159. package/apps/storefront/src/lib/auth/adminApiGuard.ts +66 -0
  160. package/apps/storefront/src/lib/auth/adminEntry.ts +11 -11
  161. package/apps/storefront/src/lib/auth/adminLanding.ts +88 -0
  162. package/apps/storefront/src/lib/auth/identityToken.ts +6 -14
  163. package/apps/storefront/src/lib/auth/loginGate.ts +75 -13
  164. package/apps/storefront/src/lib/auth/mcpClientAssertion.ts +2 -4
  165. package/apps/storefront/src/lib/auth/operatorSession.ts +113 -0
  166. package/apps/storefront/src/lib/auth/session.ts +7 -8
  167. package/apps/storefront/src/lib/auth/totAccessClient.ts +8 -18
  168. package/apps/storefront/src/lib/basePath.ts +9 -2
  169. package/apps/storefront/src/lib/blog/access.ts +29 -0
  170. package/apps/storefront/src/lib/blog/collection.ts +400 -0
  171. package/apps/storefront/src/lib/blog/markdown.ts +174 -0
  172. package/apps/storefront/src/lib/blog/provider.ts +62 -3
  173. package/apps/storefront/src/lib/blog/reactions.ts +87 -0
  174. package/apps/storefront/src/lib/blog/rss.ts +92 -0
  175. package/apps/storefront/src/lib/blog/teaser.ts +83 -0
  176. package/apps/storefront/src/lib/blog/types.ts +34 -0
  177. package/apps/storefront/src/lib/blog/viewCounts.ts +243 -0
  178. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome-assignments.example.json +6 -0
  179. package/apps/storefront/src/lib/chrome/examples/tokenoftrust.chrome.example.json +79 -0
  180. package/apps/storefront/src/lib/chrome/model.ts +48 -253
  181. package/apps/storefront/src/lib/chrome/navCurrent.ts +29 -0
  182. package/apps/storefront/src/lib/chrome/parseConfig.ts +18 -32
  183. package/apps/storefront/src/lib/chrome/variant.ts +39 -0
  184. package/apps/storefront/src/lib/commerce/checkoutHandoffCorrelation.ts +105 -0
  185. package/apps/storefront/src/lib/compliance/tamper-sentinel-boot.ts +152 -0
  186. package/apps/storefront/src/lib/compliance/tamper-sentinel.ts +327 -0
  187. package/apps/storefront/src/lib/content/index.ts +5 -5
  188. package/apps/storefront/src/lib/content-edit/applyDiff.ts +1 -1
  189. package/apps/storefront/src/lib/content-edit/route.ts +1 -1
  190. package/apps/storefront/src/lib/d1/catalog.ts +14 -2
  191. package/apps/storefront/src/lib/d1/schema-apps.ts +372 -17
  192. package/apps/storefront/src/lib/dev/activityIngestToken.ts +2 -4
  193. package/apps/storefront/src/lib/dev/activityStore.ts +3 -5
  194. package/apps/storefront/src/lib/dev/apiBase.ts +1 -4
  195. package/apps/storefront/src/lib/dev/cockpit/ask.ts +49 -0
  196. package/apps/storefront/src/lib/dev/cockpit/clickToSource.ts +71 -0
  197. package/apps/storefront/src/lib/dev/cockpit/copyCommand.ts +227 -0
  198. package/apps/storefront/src/lib/dev/cockpit/files.ts +249 -0
  199. package/apps/storefront/src/lib/dev/cockpit/hostedActivity.ts +500 -0
  200. package/apps/storefront/src/lib/dev/cockpit/infoPopover.ts +12 -0
  201. package/apps/storefront/src/lib/dev/cockpit/mcpCopy.ts +34 -0
  202. package/apps/storefront/src/lib/dev/cockpit/monitor.ts +107 -0
  203. package/apps/storefront/src/lib/dev/cockpit/stepper.ts +116 -0
  204. package/apps/storefront/src/lib/dev/cockpit/themeToggle.ts +23 -0
  205. package/apps/storefront/src/lib/dev/cockpitStore.ts +1 -9
  206. package/apps/storefront/src/lib/dev/devDraft.ts +1 -1
  207. package/apps/storefront/src/lib/dev/previewStatus.ts +9 -21
  208. package/apps/storefront/src/lib/dev/rendezvousBroker.ts +9 -24
  209. package/apps/storefront/src/lib/dev/runtimeStore.ts +1 -1
  210. package/apps/storefront/src/lib/edgeCache.ts +34 -5
  211. package/apps/storefront/src/lib/email/candidateConflictEmail.ts +80 -0
  212. package/apps/storefront/src/lib/http/fetchTransport.ts +63 -0
  213. package/apps/storefront/src/lib/i18n.ts +25 -0
  214. package/apps/storefront/src/lib/integration/browserEnvironment.ts +137 -0
  215. package/apps/storefront/src/lib/integration/managedInstallDeclarations.ts +72 -0
  216. package/apps/storefront/src/lib/integration/nonceStamp.ts +49 -0
  217. package/apps/storefront/src/lib/integration/tenantDeclarations.ts +116 -0
  218. package/apps/storefront/src/lib/jsonld.ts +118 -0
  219. package/apps/storefront/src/lib/membership/eligibility.ts +1 -1
  220. package/apps/storefront/src/lib/membership/model.ts +1 -2
  221. package/apps/storefront/src/lib/monitoring/manifest.ts +11 -11
  222. package/apps/storefront/src/lib/newsletter/confirmationEmail.ts +30 -0
  223. package/apps/storefront/src/lib/newsletter/consent.ts +23 -0
  224. package/apps/storefront/src/lib/newsletter/digest.ts +117 -0
  225. package/apps/storefront/src/lib/newsletter/resendClient.ts +79 -0
  226. package/apps/storefront/src/lib/newsletter/send.ts +265 -0
  227. package/apps/storefront/src/lib/newsletter/subscribers.ts +156 -0
  228. package/apps/storefront/src/lib/previewReload.ts +57 -1
  229. package/apps/storefront/src/lib/publish/adminPublishTab.ts +4625 -0
  230. package/apps/storefront/src/lib/publish/apex-readiness.ts +41 -18
  231. package/apps/storefront/src/lib/publish/buildStream.ts +269 -0
  232. package/apps/storefront/src/lib/publish/collapsePersistence.ts +72 -0
  233. package/apps/storefront/src/lib/publish/dispatchHealth.ts +5 -7
  234. package/apps/storefront/src/lib/publish/domainState.ts +22 -13
  235. package/apps/storefront/src/lib/publish/lifecycleState.ts +369 -0
  236. package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +29 -0
  237. package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +78 -0
  238. package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +62 -0
  239. package/apps/storefront/src/lib/publish/mergeRowFindings.ts +124 -0
  240. package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +75 -0
  241. package/apps/storefront/src/lib/publish/panelPolling.ts +78 -0
  242. package/apps/storefront/src/lib/publish/publish-guard.ts +88 -0
  243. package/apps/storefront/src/lib/publish/publishFormat.ts +165 -0
  244. package/apps/storefront/src/lib/publish/publishHero.ts +177 -0
  245. package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +66 -0
  246. package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +141 -0
  247. package/apps/storefront/src/lib/publish/reviewRelease/index.ts +41 -0
  248. package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +47 -0
  249. package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +42 -0
  250. package/apps/storefront/src/lib/publish/reviewRelease/types.ts +59 -0
  251. package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +440 -0
  252. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +161 -0
  253. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +325 -0
  254. package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +241 -0
  255. package/apps/storefront/src/lib/publish/shipWorkspace.ts +121 -51
  256. package/apps/storefront/src/lib/publish/sseRoute.ts +119 -0
  257. package/apps/storefront/src/lib/publish/stageBundleUpload.ts +215 -0
  258. package/apps/storefront/src/lib/publish/streamLogConsole.ts +142 -0
  259. package/apps/storefront/src/lib/publish/versionNumber.ts +52 -0
  260. package/apps/storefront/src/lib/rate/kvWindow.ts +53 -0
  261. package/apps/storefront/src/lib/rawChrome.ts +125 -47
  262. package/apps/storefront/src/lib/rawMarketingHtml.ts +74 -3
  263. package/apps/storefront/src/lib/search/index.ts +2 -2
  264. package/apps/storefront/src/lib/seo/alternates.ts +42 -0
  265. package/apps/storefront/src/lib/seo/meta.ts +65 -0
  266. package/apps/storefront/src/lib/social/golive.ts +212 -0
  267. package/apps/storefront/src/lib/social/notify.ts +32 -0
  268. package/apps/storefront/src/lib/social/tokenStore.ts +171 -0
  269. package/apps/storefront/src/lib/storyblok/content-model.ts +7 -33
  270. package/apps/storefront/src/lib/storyblok/provider.ts +168 -60
  271. package/apps/storefront/src/lib/subscription/model.ts +4 -4
  272. package/apps/storefront/src/lib/the-build/provider.ts +32 -0
  273. package/apps/storefront/src/lib/the-build/readingTime.ts +35 -0
  274. package/apps/storefront/src/lib/the-build/toc.ts +95 -0
  275. package/apps/storefront/src/lib/tot/ToTClient.ts +1 -1
  276. package/apps/storefront/src/lib/tot/d1Client.ts +2 -2
  277. package/apps/storefront/src/lib/tot/totClientInterface.ts +3 -4
  278. package/apps/storefront/src/lib/webhooks/deliveryStore.ts +16 -17
  279. package/apps/storefront/src/middleware/index.ts +385 -52
  280. package/apps/storefront/src/pages/[...slug].astro +9 -4
  281. package/apps/storefront/src/pages/admin/review-release.astro +265 -0
  282. package/apps/storefront/src/pages/admin/select.astro +96 -0
  283. package/apps/storefront/src/pages/admin.astro +25 -1313
  284. package/apps/storefront/src/pages/api/activity.ts +8 -8
  285. package/apps/storefront/src/pages/api/admin/activity-alerts.ts +5 -20
  286. package/apps/storefront/src/pages/api/admin/orders/[id].ts +7 -2
  287. package/apps/storefront/src/pages/api/admin/orders.ts +8 -2
  288. package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +174 -0
  289. package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +96 -0
  290. package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +118 -0
  291. package/apps/storefront/src/pages/api/apps/admin/integrations.ts +89 -0
  292. package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +85 -0
  293. package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +74 -2
  294. package/apps/storefront/src/pages/api/blog/[collection]/[slug]/react.ts +104 -0
  295. package/apps/storefront/src/pages/api/commerce/checkout-handoff.ts +77 -0
  296. package/apps/storefront/src/pages/api/compliance/tamper.ts +88 -0
  297. package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +108 -0
  298. package/apps/storefront/src/pages/api/newsletter/[collection]/subscribe.ts +146 -0
  299. package/apps/storefront/src/pages/api/newsletter/confirm.ts +75 -0
  300. package/apps/storefront/src/pages/api/newsletter/unsubscribe.ts +90 -0
  301. package/apps/storefront/src/pages/api/publish/stage-bundle.ts +70 -0
  302. package/apps/storefront/src/pages/api/rum/vitals.ts +20 -2
  303. package/apps/storefront/src/pages/blog/[slug].astro +99 -19
  304. package/apps/storefront/src/pages/blog/author/[author].astro +140 -0
  305. package/apps/storefront/src/pages/blog/category/[category].astro +67 -0
  306. package/apps/storefront/src/pages/blog/index.astro +4 -2
  307. package/apps/storefront/src/pages/blog/tag/[tag].astro +65 -0
  308. package/apps/storefront/src/pages/cockpit.astro +77 -1353
  309. package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +15 -11
  310. package/apps/storefront/src/pages/index.astro +10 -5
  311. package/apps/storefront/src/pages/llms.txt.ts +1 -1
  312. package/apps/storefront/src/pages/products/[handle].astro +7 -6
  313. package/apps/storefront/src/pages/search.astro +1 -1
  314. package/apps/storefront/src/pages/sitemap.xml.ts +21 -1
  315. package/apps/storefront/src/pages/style-guide/[tenant]/[theme].astro +31 -27
  316. package/apps/storefront/src/pages/style-guide/[tenant]/chrome/[theme].astro +1 -1
  317. package/apps/storefront/src/pages/tenants/[id]/[...path].ts +38 -2
  318. package/apps/storefront/src/pages/the-build/[slug].astro +129 -0
  319. package/apps/storefront/src/pages/the-build/index.astro +146 -0
  320. package/apps/storefront/src/pages/the-build/rss.xml.ts +33 -0
  321. package/apps/storefront/src/styles/admin.css +1106 -0
  322. package/apps/storefront/tsconfig.json +6 -1
  323. package/package.json +1 -1
  324. package/packages/public-runtime/package.json +3 -1
  325. package/packages/public-runtime/schemas/managed-analytics/v1/adapter-definition.schema.json +427 -0
  326. package/packages/public-runtime/schemas/managed-analytics/v1/create-integration-install-request.schema.json +161 -0
  327. package/packages/public-runtime/schemas/managed-analytics/v1/create-migration-draft-request.schema.json +157 -0
  328. package/packages/public-runtime/schemas/managed-analytics/v1/delete-integration-install-request.schema.json +84 -0
  329. package/packages/public-runtime/schemas/managed-analytics/v1/delete-migration-draft-request.schema.json +84 -0
  330. package/packages/public-runtime/schemas/managed-analytics/v1/error-envelope.schema.json +180 -0
  331. package/packages/public-runtime/schemas/managed-analytics/v1/get-adapter-definition-request.schema.json +52 -0
  332. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-install-request.schema.json +52 -0
  333. package/packages/public-runtime/schemas/managed-analytics/v1/get-integration-revision-request.schema.json +79 -0
  334. package/packages/public-runtime/schemas/managed-analytics/v1/get-migration-draft-request.schema.json +52 -0
  335. package/packages/public-runtime/schemas/managed-analytics/v1/integration-install.schema.json +167 -0
  336. package/packages/public-runtime/schemas/managed-analytics/v1/integration-revision.schema.json +208 -0
  337. package/packages/public-runtime/schemas/managed-analytics/v1/list-adapter-definitions-request.schema.json +53 -0
  338. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-installs-request.schema.json +53 -0
  339. package/packages/public-runtime/schemas/managed-analytics/v1/list-integration-revisions-request.schema.json +82 -0
  340. package/packages/public-runtime/schemas/managed-analytics/v1/migration-draft.schema.json +218 -0
  341. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-request.schema.json +52 -0
  342. package/packages/public-runtime/schemas/managed-analytics/v1/mutation-success-envelope.schema.json +49 -0
  343. package/packages/public-runtime/schemas/managed-analytics/v1/read-request.schema.json +20 -0
  344. package/packages/public-runtime/schemas/managed-analytics/v1/read-success-envelope.schema.json +45 -0
  345. package/packages/public-runtime/schemas/managed-analytics/v1/revise-integration-install-request.schema.json +156 -0
  346. package/packages/public-runtime/schemas/managed-analytics/v1/set-integration-install-status-request.schema.json +92 -0
  347. package/packages/public-runtime/schemas/managed-analytics/v1/update-migration-draft-request.schema.json +184 -0
  348. package/packages/public-runtime/scripts/generate-managed-analytics-api-v1-schemas.mjs +13 -0
  349. package/packages/public-runtime/src/activity/catalog.ts +89 -33
  350. package/packages/public-runtime/src/activity/event.ts +6 -6
  351. package/packages/public-runtime/src/activity/index.ts +2 -2
  352. package/packages/public-runtime/src/activity/redaction.ts +4 -4
  353. package/packages/public-runtime/src/candidate-index.ts +42 -8
  354. package/packages/public-runtime/src/catalog-d1.ts +1 -1
  355. package/packages/public-runtime/src/checkout-orders.ts +2 -2
  356. package/packages/public-runtime/src/checkout-style-publish.ts +2 -2
  357. package/packages/public-runtime/src/checkout-style.ts +7 -7
  358. package/packages/public-runtime/src/checkout.ts +3 -3
  359. package/packages/public-runtime/src/chrome.ts +777 -0
  360. package/packages/public-runtime/src/compliance-obligations.ts +310 -0
  361. package/packages/public-runtime/src/csp.ts +99 -0
  362. package/packages/public-runtime/src/customization-preview.ts +25 -2
  363. package/packages/public-runtime/src/customization-runtime.ts +156 -3
  364. package/packages/public-runtime/src/customization-versioning.ts +10 -0
  365. package/packages/public-runtime/src/destination-webhook/contract.ts +353 -0
  366. package/packages/public-runtime/src/embed-catalog.d.mts +9 -0
  367. package/packages/public-runtime/src/embed-catalog.mjs +40 -0
  368. package/packages/public-runtime/src/evidence.ts +288 -0
  369. package/packages/public-runtime/src/gtm-migration/bounds.ts +138 -0
  370. package/packages/public-runtime/src/gtm-migration/disposition-rules.ts +361 -0
  371. package/packages/public-runtime/src/gtm-migration/dispositions.ts +181 -0
  372. package/packages/public-runtime/src/gtm-migration/draft-generator.ts +400 -0
  373. package/packages/public-runtime/src/gtm-migration/graph.ts +468 -0
  374. package/packages/public-runtime/src/gtm-migration/index.ts +95 -0
  375. package/packages/public-runtime/src/gtm-migration/parse.ts +410 -0
  376. package/packages/public-runtime/src/gtm-migration/references.ts +266 -0
  377. package/packages/public-runtime/src/gtm-migration/types.ts +86 -0
  378. package/packages/public-runtime/src/index.ts +17 -1
  379. package/packages/public-runtime/src/integration/assurance-decision.ts +259 -0
  380. package/packages/public-runtime/src/integration/assurance.ts +228 -0
  381. package/packages/public-runtime/src/integration/candidate-evidence.ts +377 -0
  382. package/packages/public-runtime/src/integration/commerce-broker.ts +497 -0
  383. package/packages/public-runtime/src/integration/consent-broker.ts +337 -0
  384. package/packages/public-runtime/src/integration/consent-cmp-demand.ts +115 -0
  385. package/packages/public-runtime/src/integration/consent-config.ts +260 -0
  386. package/packages/public-runtime/src/integration/consent-preview.ts +125 -0
  387. package/packages/public-runtime/src/integration/correlation-token.ts +444 -0
  388. package/packages/public-runtime/src/integration/csp-compiler.ts +267 -0
  389. package/packages/public-runtime/src/integration/csp.ts +105 -0
  390. package/packages/public-runtime/src/integration/digest.ts +43 -0
  391. package/packages/public-runtime/src/integration/environment.ts +228 -0
  392. package/packages/public-runtime/src/integration/events-catalog.ts +186 -0
  393. package/packages/public-runtime/src/integration/events.ts +701 -0
  394. package/packages/public-runtime/src/integration/g2-action-boundary.ts +106 -0
  395. package/packages/public-runtime/src/integration/index.ts +90 -0
  396. package/packages/public-runtime/src/integration/interim-bridge.ts +138 -0
  397. package/packages/public-runtime/src/integration/lifecycle-boot.ts +317 -0
  398. package/packages/public-runtime/src/integration/lifecycle-runtime.ts +393 -0
  399. package/packages/public-runtime/src/integration/lifecycle.ts +94 -0
  400. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.d.mts +2 -0
  401. package/packages/public-runtime/src/integration/managed-analytics-api-v1-safety.mjs +10 -0
  402. package/packages/public-runtime/src/integration/managed-analytics-api-v1.schemas.ts +382 -0
  403. package/packages/public-runtime/src/integration/managed-analytics-api-v1.ts +684 -0
  404. package/packages/public-runtime/src/integration/managed-analytics-bridge.ts +383 -0
  405. package/packages/public-runtime/src/integration/managed-analytics-value-safety.ts +14 -0
  406. package/packages/public-runtime/src/integration/managed-analytics.ts +277 -0
  407. package/packages/public-runtime/src/integration/manifest.ts +470 -0
  408. package/packages/public-runtime/src/integration/native-amplitude.ts +362 -0
  409. package/packages/public-runtime/src/integration/native-ga4.ts +352 -0
  410. package/packages/public-runtime/src/integration/observability.ts +451 -0
  411. package/packages/public-runtime/src/integration/order-conversion.ts +0 -0
  412. package/packages/public-runtime/src/integration/phases.ts +182 -0
  413. package/packages/public-runtime/src/integration/plan.ts +112 -0
  414. package/packages/public-runtime/src/integration/platform-adapter-runtime.ts +146 -0
  415. package/packages/public-runtime/src/integration/platform-adapters.ts +671 -0
  416. package/packages/public-runtime/src/integration/promotion-gate.ts +451 -0
  417. package/packages/public-runtime/src/integration/provider-checkout-extension.ts +425 -0
  418. package/packages/public-runtime/src/integration/receipt-measurement.ts +370 -0
  419. package/packages/public-runtime/src/integration/regulated-parent-cert.ts +311 -0
  420. package/packages/public-runtime/src/integration/render-phases.ts +267 -0
  421. package/packages/public-runtime/src/integration/resolve.ts +465 -0
  422. package/packages/public-runtime/src/integration/result.ts +239 -0
  423. package/packages/public-runtime/src/integration/secrets.ts +92 -0
  424. package/packages/public-runtime/src/integration/tamper-evidence.ts +205 -0
  425. package/packages/public-runtime/src/integration/tamper-recovery.ts +127 -0
  426. package/packages/public-runtime/src/integration/version.ts +92 -0
  427. package/packages/public-runtime/src/integration-capabilities.ts +520 -0
  428. package/packages/public-runtime/src/membership.ts +2 -2
  429. package/packages/public-runtime/src/raw-html-policy.ts +2 -4
  430. package/packages/public-runtime/src/regulated-commerce/broker/consent-dedupe.ts +144 -0
  431. package/packages/public-runtime/src/regulated-commerce/correlation/contract.ts +177 -0
  432. package/packages/public-runtime/src/regulated-commerce/diagnostics/local-dispatch.ts +24 -0
  433. package/packages/public-runtime/src/regulated-commerce/diagnostics/read-model.ts +69 -0
  434. package/packages/public-runtime/src/regulated-commerce/dispatch/core.ts +134 -0
  435. package/packages/public-runtime/src/regulated-commerce/events/contract.ts +113 -0
  436. package/packages/public-runtime/src/regulated-commerce/ledger/core.ts +81 -0
  437. package/packages/public-runtime/src/regulated-commerce/mappings/ga4-ads.ts +87 -0
  438. package/packages/public-runtime/src/regulated-commerce/provider-order/bridge.ts +52 -0
  439. package/packages/public-runtime/src/regulated-commerce/purchase-truth/contract.ts +252 -0
  440. package/packages/public-runtime/src/regulated-commerce/sources/accepted-browser-operation.ts +67 -0
  441. package/packages/public-runtime/src/regulated-commerce/testing/fake-destination.ts +93 -0
  442. package/packages/public-runtime/src/static-bundle.ts +357 -0
  443. package/packages/public-runtime/src/tenant-assets.ts +143 -4
  444. package/packages/public-runtime/src/tenant.ts +81 -17
  445. package/packages/public-runtime/tests/fixtures/integration/browser-environment.example.json +8 -0
  446. package/packages/public-runtime/tests/fixtures/integration/commerce-event.example.json +44 -0
  447. package/packages/public-runtime/tests/fixtures/integration/commerce-order-event.example.json +44 -0
  448. package/packages/public-runtime/tests/fixtures/integration/compliance-assurance-record.example.json +50 -0
  449. package/packages/public-runtime/tests/fixtures/integration/integration-manifest-v2.example.json +120 -0
  450. package/packages/public-runtime/tests/fixtures/integration/managed-analytics-v1/fake-adapter-catalog.json +35 -0
  451. package/packages/public-runtime/tests/fixtures/integration/tenant-action-registry.example.json +11 -0
  452. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/README.md +22 -0
  453. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/entry.js +30 -0
  454. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/manifest.json +75 -0
  455. package/packages/public-runtime/tests/fixtures/integration/tenant-local-example/public-config.json +3 -0
  456. package/packages/public-runtime/tests/fixtures/regulated-commerce/fake-destinations.ts +38 -0
  457. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.provenance.json +29 -0
  458. package/packages/public-runtime/tests/gtm-migration/fixtures/gtm-sample-container-v84.redacted.json +3397 -0
  459. package/scripts/build/copy-tenant-assets.mjs +14 -4
  460. package/scripts/dev/ai-edit.mjs +1 -1
  461. package/scripts/dev/checkout-watch.mjs +2 -2
  462. package/scripts/dev/file-browser.mjs +4 -6
  463. package/scripts/dev/file-writer.mjs +3 -3
  464. package/scripts/dev/git-status.mjs +2 -2
  465. package/scripts/dev/integration-guardrails.mjs +498 -0
  466. package/scripts/dev/locate-handler.mjs +3 -3
  467. package/scripts/dev/port-check.mjs +3 -3
  468. package/scripts/dev/publish.mjs +5 -9
  469. package/scripts/dev/safe-path.mjs +2 -2
  470. package/scripts/dev/shot.mjs +1 -1
  471. package/scripts/dev/tenant-source-root.mjs +1 -1
  472. package/scripts/dev/unified-diff.mjs +8 -3
  473. package/scripts/tot-dev.mjs +8 -11
  474. package/apps/storefront/src/lib/dev/vcBinding.ts +0 -80
@@ -27,7 +27,6 @@ import { readViewerSession } from "@/lib/auth/route";
27
27
  import { staticTenants } from "@/config/tenants";
28
28
  import { resolvePreviewTenant } from "@/config/previewTenantLookup";
29
29
  import { readTenantPreviewStatus, isStoreNotSetUp } from "@/lib/dev/previewStatus";
30
- import { ensureCockpitVcBinding } from "@/lib/dev/vcBinding";
31
30
  import { resolveDevApiBase } from "@/lib/dev/apiBase";
32
31
  import { resolveCockpitStoreResource } from "@/lib/dev/cockpitStore";
33
32
  import { normalizeEmailHint, redactEmailForHint } from "@/lib/privacy/emailHint";
@@ -143,9 +142,19 @@ const canSetupStore = hasStoreAccess && !noResolvableStore && !storeNotSetUp;
143
142
  // off the hot render path; the authority-seam binding + backfill remain the real
144
143
  // guarantee. Gated on canSetupStore (entitled + resolvable + provisioned), the exact
145
144
  // condition under which we render a runnable command.
145
+ //
146
+ // The binding itself is control-plane (authors a forge/MCP binding under the app
147
+ // credential), so this renderer page reaches it only over the network — POST
148
+ // /api/dev/ensure-vc-binding — never by static import (renderer-carve moat). The
149
+ // endpoint re-derives the developer from the forwarded session cookie.
146
150
  if (canSetupStore && session && !Astro.cookies.has("tot_vc_bound")) {
147
- const vcBind = await ensureCockpitVcBinding(session.email);
148
- if (vcBind.ok) {
151
+ const vcBind = await fetch(new URL(`${basePath}/api/dev/ensure-vc-binding`, url.origin), {
152
+ method: "POST",
153
+ headers: { cookie: Astro.request.headers.get("cookie") ?? "" },
154
+ })
155
+ .then((r) => (r.ok ? r.json() : null))
156
+ .catch(() => null);
157
+ if (vcBind?.ok) {
149
158
  // Throttle only on a real success — a fail-open miss retries on the next load.
150
159
  Astro.cookies.set("tot_vc_bound", "1", {
151
160
  path: "/",
@@ -192,1082 +201,19 @@ const isLocalDevLoop = import.meta.env.DEV;
192
201
  const hostedActivity = !isLocalDevLoop && canSetupStore;
193
202
 
194
203
  // cp-env (cloud-dev-cockpit Phase 0): the file-tree/viewer/diff + click-to-source
195
- // cockpit scripts below (filesScript, c8Script) are ENVIRONMENT-AWARE — same client
196
- // JS, different endpoint base. Local: the astro-dev-only /__tot/* plugins. Hosted: new
204
+ // cockpit modules (lib/dev/cockpit/files.ts, clickToSource.ts) are ENVIRONMENT-AWARE
205
+ // — same client JS, different endpoint base. Local: the astro-dev-only /__tot/* plugins. Hosted: new
197
206
  // /api/dev/* Astro API routes, landing incrementally (cp-tree/cp-file/cp-write/cp-diff/
198
207
  // cp-locate — separate cards). Resolving it here, unconditionally, means those cards
199
208
  // don't each have to invent the same env check; on isLocalDevLoop this is a no-op
200
209
  // (resolves to the unchanged /__tot base). See decision architecture-environment-aware-cockpit.
201
210
  const apiBase = resolveDevApiBase(isLocalDevLoop, basePath);
202
211
 
203
- // Live loop Monitor (C1) client. Polls the dev server's /__tot/dev-loop-status
204
- // and renders the loopbar: server health, watched file, and the save→reload
205
- // timeline. Kept as a static string rendered via set:html (raw, un-escaped —
206
- // the JS comparison operators must not be HTML-encoded) inside an is:inline
207
- // nonce'd tag, gated on isLocalDevLoop so it's absent from the production build. Polling
208
- // (not the WS custom event) keeps this a plain inline script with no Vite-module
209
- // coupling, and it survives a full page reload — the server holds loopState, so
210
- // the bar re-hydrates to the true state on every load. All dynamic values go
211
- // through textContent, never innerHTML.
212
- const monitorScript = `
213
- (function () {
214
- var bar = document.getElementById("loopbar");
215
- if (!bar) return;
216
- var elStatus = document.getElementById("lbStatus");
217
- var elWatch = document.getElementById("lbWatch");
218
- var elLast = document.getElementById("lbLast");
219
- var elServer = document.getElementById("lbServer");
220
- var elUrl = document.getElementById("lbUrl");
221
- var elFile = document.getElementById("lbFile");
222
- var elSave = document.getElementById("lbSave");
223
- var elReload = document.getElementById("lbReload");
224
- var head = document.getElementById("lbHead");
225
-
226
- if (head) head.addEventListener("click", function () { bar.classList.toggle("open"); });
227
-
228
- function rel(ts) {
229
- var d = Math.max(0, Math.round((Date.now() - Number(ts)) / 1000));
230
- return d < 2 ? "just now" : d < 60 ? d + "s ago" : Math.floor(d / 60) + "m ago";
231
- }
232
- function setTs(el, ts, emptyText) {
233
- if (ts) {
234
- el.setAttribute("data-ts", String(ts));
235
- el.classList.remove("dim");
236
- el.textContent = rel(ts);
237
- } else {
238
- el.removeAttribute("data-ts");
239
- el.classList.add("dim");
240
- el.textContent = emptyText;
241
- }
242
- }
243
- setInterval(function () {
244
- [elSave, elReload].forEach(function (el) {
245
- var ts = el.getAttribute("data-ts");
246
- if (ts) el.textContent = rel(ts);
247
- });
248
- }, 1000);
249
-
250
- function render(s) {
251
- bar.classList.add("live");
252
- var port = s.port || null;
253
- var url = port ? ("localhost:" + port + (s.tenant ? "/" + s.tenant + "/" : "/")) : "—";
254
- elServer.textContent = s.server ? ("running" + (port ? " · port " + port : "")) : "not running";
255
- elServer.classList.toggle("dim", !s.server);
256
- elUrl.textContent = url;
257
- elUrl.classList.toggle("dim", !port);
258
- elFile.textContent = s.watchedFile || "content/*.html";
259
- elFile.classList.toggle("dim", !s.watchedFile);
260
- elWatch.textContent = s.watchedFile || "your content";
261
- setTs(elSave, s.lastSaveAt, "no saves yet");
262
- setTs(elReload, s.lastReloadAt, "waiting for a save");
263
-
264
- var saved = s.lastSaveAt || 0;
265
- var reloaded = s.lastReloadAt || 0;
266
- var state, status, last;
267
- if (!saved) {
268
- state = "running"; status = "Running"; last = "reloads on save";
269
- } else if (reloaded >= saved) {
270
- state = "running"; status = "Running"; last = "reloaded";
271
- } else if (Date.now() - saved < 3000) {
272
- state = "reloading"; status = "Reloading…"; last = "change detected";
273
- } else {
274
- state = "stale"; status = "Stale"; last = "save not reflected — restart?";
275
- }
276
- bar.setAttribute("data-state", state);
277
- elStatus.textContent = status;
278
- elLast.textContent = last;
279
- // Broadcast the snapshot so other consumers (U2 stepper, U3 Ask) share this
280
- // one poll instead of each opening their own. window.__totLoop holds the
281
- // latest for listeners that attach late.
282
- window.__totLoop = s;
283
- window.dispatchEvent(new CustomEvent("tot:loop-state", { detail: s }));
284
- }
285
- function offline() {
286
- bar.classList.add("live");
287
- bar.setAttribute("data-state", "stale");
288
- elStatus.textContent = "Offline";
289
- elLast.textContent = "dev server not reachable";
290
- elServer.textContent = "not reachable";
291
- var off = { server: false, offline: true };
292
- window.__totLoop = off;
293
- window.dispatchEvent(new CustomEvent("tot:loop-state", { detail: off }));
294
- }
295
- function poll() {
296
- fetch("/__tot/dev-loop-status", { cache: "no-store" })
297
- .then(function (r) { return r.ok ? r.json() : Promise.reject(); })
298
- .then(render)
299
- .catch(offline);
300
- }
301
- poll();
302
- setInterval(poll, 1000);
303
- })();
304
- `;
305
-
306
- // Progressive stepper + staged flow (U2). Drives Run → Edit → What's next with
307
- // progressive disclosure: only the current stage shows, completed stages collapse
308
- // to a done-bar, and the flow AUTO-ADVANCES on C1's REAL loop signal (never a
309
- // timer) — Step 1 completes when the local server is live, Step 2 when a real
310
- // save→reload is observed since the edit step began. Consumes the shared
311
- // tot:loop-state event from monitorScript (one poll for the whole page).
312
- //
313
- // Progress is persisted in sessionStorage. This is REQUIRED, not a nicety: saving
314
- // a watched file triggers Vite's global full-reload, which reloads THIS /dev tab
315
- // too — resetting in-memory state and re-baselining the very save that should
316
- // advance Edit→Next. Persisting done-flags + current stage + the edit baseline
317
- // (the lastSaveAt watermark captured when the edit step began, BEFORE the
318
- // qualifying save) lets the flow survive that reload and credit the save.
319
- //
320
- // Dev-only, rendered via set:html inside an isLocalDevLoop-gated is:inline tag. All dynamic
321
- // values via textContent.
322
- const stepperScript = `
323
- (function () {
324
- var stepper = document.getElementById("stepper");
325
- if (!stepper) return;
326
- var stages = { run: document.getElementById("stage-run"), edit: document.getElementById("stage-edit"), next: document.getElementById("stage-next") };
327
- var bars = { run: document.getElementById("bar-run"), edit: document.getElementById("bar-edit") };
328
- var celRun = document.getElementById("cel-run");
329
- var celEdit = document.getElementById("cel-edit");
330
- var toNext = document.getElementById("toNext");
331
- var detect = document.getElementById("detect");
332
- var detectTxt = document.getElementById("detectTxt");
333
- var detectEdit = document.getElementById("detect-edit");
334
- var detectEditTxt = document.getElementById("detectEditTxt");
335
- var barRunMeta = document.getElementById("bar-run-meta");
336
- var barEditMeta = document.getElementById("bar-edit-meta");
337
- var toast = document.getElementById("toast"), toastMsg = document.getElementById("toastMsg"), tT;
338
- var reduce = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
339
-
340
- var SS = window.sessionStorage;
341
- function load(k, d) { try { var v = SS.getItem(k); return v === null ? d : JSON.parse(v); } catch (e) { return d; } }
342
- function put(k, v) { try { SS.setItem(k, JSON.stringify(v)); } catch (e) {} }
343
-
344
- function ping(m, once) {
345
- if (once && load(once, false)) return;
346
- if (once) put(once, true);
347
- if (!toast || !toastMsg) return;
348
- toastMsg.textContent = m; toast.classList.add("show");
349
- clearTimeout(tT); tT = setTimeout(function () { toast.classList.remove("show"); }, 1900);
350
- }
351
-
352
- var state = {
353
- unlocked: load("tot.unlocked", { run: true, edit: false, next: false }),
354
- done: load("tot.done", { run: false, edit: false }),
355
- current: load("tot.current", "run"),
356
- };
357
- var editBase = load("tot.editBase", null);
358
- function persist() { put("tot.unlocked", state.unlocked); put("tot.done", state.done); put("tot.current", state.current); }
359
-
360
- function render() {
361
- Array.prototype.forEach.call(stepper.querySelectorAll(".snode"), function (n) {
362
- var s = n.getAttribute("data-go");
363
- n.setAttribute("data-st", state.current === s ? "active" : state.done[s] ? "done" : state.unlocked[s] ? "done" : "locked");
364
- });
365
- Object.keys(stages).forEach(function (s) { if (stages[s]) stages[s].hidden = state.current !== s; });
366
- if (bars.run) bars.run.hidden = !(state.done.run && state.current !== "run");
367
- if (bars.edit) bars.edit.hidden = !(state.done.edit && state.current !== "edit");
368
- // Contextual celebrate banners reflect achieved state (survive reloads).
369
- if (celRun && state.done.run) celRun.hidden = false;
370
- if (celEdit && state.done.edit) celEdit.hidden = false;
371
- if (toNext) toNext.disabled = !state.unlocked.next;
372
- }
373
- function goTo(s) {
374
- if (!state.unlocked[s]) return;
375
- state.current = s; persist(); render();
376
- window.scrollTo({ top: 0, behavior: reduce ? "auto" : "smooth" });
377
- }
378
-
379
- function onSignal(s) {
380
- if (!s || s.offline) return;
381
-
382
- // Step 1 — local server is live.
383
- if (s.server && !state.done.run) {
384
- state.done.run = true; state.unlocked.edit = true;
385
- if (editBase === null) { editBase = s.lastSaveAt || 0; put("tot.editBase", editBase); }
386
- persist();
387
- if (barRunMeta && s.port) barRunMeta.textContent = "localhost:" + s.port + (s.tenant ? "/" + s.tenant + "/" : "/");
388
- if (detect) detect.setAttribute("data-listening", "false");
389
- if (detectTxt) detectTxt.textContent = "Your store is live on this machine.";
390
- if (state.current === "run") { goTo("edit"); } else { render(); }
391
- ping("Your store is live", "tot.pinged.run");
392
- }
393
-
394
- // Step 2 — a NEW save (since the edit step began) has been reflected by a reload.
395
- if (state.done.run && !state.done.edit && editBase !== null) {
396
- var newSave = (s.lastSaveAt || 0) > editBase;
397
- var reflected = (s.lastSaveAt || 0) > 0 && (s.lastReloadAt || 0) >= (s.lastSaveAt || 0);
398
- if (newSave && !reflected && detectEditTxt) {
399
- detectEditTxt.textContent = "Change detected — reloading your store…";
400
- }
401
- if (newSave && reflected) {
402
- state.done.edit = true; state.unlocked.next = true; persist();
403
- if (barEditMeta && s.watchedFile) barEditMeta.textContent = s.watchedFile;
404
- if (detectEdit) detectEdit.setAttribute("data-listening", "false");
405
- if (detectEditTxt) detectEditTxt.textContent = "Saved — your store reloaded. That's the loop.";
406
- if (state.current === "edit") { goTo("next"); } else { render(); }
407
- ping("Saved — your page reloaded", "tot.pinged.edit");
408
- }
409
- }
410
- }
411
-
412
- render();
413
- Array.prototype.forEach.call(document.querySelectorAll("[data-go]"), function (el) {
414
- el.addEventListener("click", function () { goTo(el.getAttribute("data-go")); });
415
- });
416
- window.addEventListener("tot:loop-state", function (e) { onSignal(e.detail); });
417
- if (window.__totLoop) onSignal(window.__totLoop);
418
- })();
419
- `;
420
-
421
- // "Stuck? Ask" recovery affordance (U3). A quiet FAB that opens a scoped Q&A for
422
- // "why isn't the loop working" — NOT general AI editing. Static answers ship as
423
- // markup; the two stateful answers (is it running? / save-vs-reload) update from
424
- // the shared tot:loop-state signal + window.__totLoop (C1/U2 — no second poll).
425
- // "Reconnect preview" reloads the page, re-establishing the HMR connection and
426
- // re-reading loop state (recovers a stale client); a fully-down server is handled
427
- // by the answer text (run tot start). Dev-only, set:html in an isLocalDevLoop-gated tag.
428
- const askScript = `
429
- (function () {
430
- var fab = document.getElementById("askFab");
431
- var panel = document.getElementById("askPanel");
432
- if (!fab || !panel) return;
433
- var ansRunning = document.getElementById("ans-running");
434
- var ansChange = document.getElementById("ans-change");
435
- var restart = document.getElementById("restartBtn");
436
-
437
- fab.addEventListener("click", function (e) { e.stopPropagation(); panel.classList.toggle("open"); });
438
- document.addEventListener("click", function (e) {
439
- if (!panel.contains(e.target) && e.target !== fab && !fab.contains(e.target)) panel.classList.remove("open");
440
- });
441
- if (restart) restart.addEventListener("click", function () { location.reload(); });
442
-
443
- function rel(ts) {
444
- var d = Math.max(0, Math.round((Date.now() - Number(ts)) / 1000));
445
- return d < 2 ? "just now" : d < 60 ? d + "s ago" : Math.floor(d / 60) + "m ago";
446
- }
447
- function update(s) {
448
- if (!s) return;
449
- if (ansRunning) {
450
- ansRunning.textContent = (s.offline || !s.server)
451
- ? "Your dev server isn't reachable right now — run tot start in your terminal, then reload this page."
452
- : "Yes — your server is running" + (s.port ? " on port " + s.port : "") + ". This page is connected to it.";
453
- }
454
- if (ansChange) {
455
- if (!s.lastSaveAt) {
456
- ansChange.textContent = "No saves seen yet. Edit a watched file (like content/home.html) and save — it'll show up here.";
457
- } else if ((s.lastReloadAt || 0) >= s.lastSaveAt) {
458
- ansChange.textContent = "Working: your last save (" + rel(s.lastSaveAt) + ") triggered a reload (" + rel(s.lastReloadAt) + "). If the page didn't visibly change, make sure you edited the file the page renders from.";
459
- } else {
460
- ansChange.textContent = "Your save (" + rel(s.lastSaveAt) + ") hasn't been reflected by a reload yet — the preview may be stale. Try Reconnect preview below.";
461
- }
462
- }
463
- }
464
- window.addEventListener("tot:loop-state", function (e) { update(e.detail); });
465
- if (window.__totLoop) update(window.__totLoop);
466
- })();
467
- `;
468
-
469
- // C5 · Files tab — in-browser file tree + LIVE EDITOR. Dev-only, rendered via
470
- // set:html inside an isLocalDevLoop-gated is:inline nonce'd tag. Reads /__tot/tree +
471
- // /__tot/file and writes /__tot/write (dev-plugins/dev-file-browser + dev-write).
472
- // Writable files (content HTML + theme.json) become an editable textarea with
473
- // ~600ms debounce autosave + Cmd/Ctrl+S; the save carries the C6 baseVersion so a
474
- // concurrent $EDITOR/git change is caught (409) instead of clobbered. Dynamic
475
- // values go through .value / textContent (never innerHTML) per the C1 decision.
476
- const filesScript = `
477
- (function () {
478
- var apiBase = ${JSON.stringify(apiBase)};
479
- var isDev = ${JSON.stringify(isLocalDevLoop)};
480
- var offlineMsg = isDev ? "Could not reach the dev server." : "Not available on the hosted panel yet \\u2014 check back soon.";
481
- var treeEl = document.getElementById("filesTree");
482
- var viewEl = document.getElementById("filesView");
483
- var pathEl = document.getElementById("filesPath");
484
- var editbar = document.getElementById("filesEditbar");
485
- var statusEl = document.getElementById("filesStatus");
486
- var conflictEl = document.getElementById("filesConflict");
487
- var reloadBtn = document.getElementById("filesReload");
488
- var forceBtn = document.getElementById("filesForce");
489
- var diffBtn = document.getElementById("filesDiffBtn");
490
- var diffEl = document.getElementById("filesDiff");
491
- var publishBtn = document.getElementById("filesPublishBtn");
492
- if (!treeEl || !viewEl) return;
493
- var selected = null;
494
- var cur = null;
495
- var saveTimer = null;
496
- var diffOn = false;
497
- var rowByPath = {}; // path -> tree row element, for programmatic open (C8 jump)
498
-
499
- function setStatus(txt, cls) {
500
- if (!statusEl) return;
501
- statusEl.textContent = txt || "";
502
- statusEl.className = "df-status" + (cls ? " " + cls : "");
503
- }
504
- function showConflict(show) { if (conflictEl) conflictEl.hidden = !show; }
505
- function showBar(show) { if (editbar) editbar.hidden = !show; }
506
-
507
- function openFile(path, row, afterLoad) {
508
- if (selected) selected.classList.remove("sel");
509
- selected = row; row.classList.add("sel");
510
- if (pathEl) pathEl.textContent = path;
511
- showConflict(false); showBar(true);
512
- setDiffMode(false); if (diffBtn) diffBtn.hidden = true;
513
- if (publishBtn) publishBtn.hidden = true;
514
- viewEl.readOnly = true;
515
- setStatus("Loading\\u2026");
516
- fetch(apiBase + "/file?path=" + encodeURIComponent(path))
517
- .then(function (r) { return r.json(); })
518
- .then(function (d) {
519
- if (!d || !d.ok) { viewEl.value = ""; cur = null; setStatus((d && d.message) || "Could not open that file.", "warn"); return; }
520
- if (d.binary) { viewEl.value = "(binary file \\u2014 " + (d.size || 0) + " bytes)"; cur = null; setStatus("Binary \\u2014 not editable"); return; }
521
- if (d.tooBig) { viewEl.value = "(file too large to show \\u2014 " + (d.size || 0) + " bytes)"; cur = null; setStatus("Too large \\u2014 not editable"); return; }
522
- viewEl.value = d.content == null ? "" : d.content;
523
- cur = { path: path, baseVersion: (typeof d.mtimeMs === "number" ? d.mtimeMs : null), writable: !!d.writable };
524
- viewEl.readOnly = !cur.writable;
525
- if (diffBtn) diffBtn.hidden = false;
526
- if (publishBtn) publishBtn.hidden = !cur.writable;
527
- setStatus(cur.writable ? "Editable \\u2014 edits save + reload your store" : "Read-only");
528
- if (typeof afterLoad === "function") afterLoad();
529
- })
530
- .catch(function () { viewEl.value = ""; cur = null; setStatus(offlineMsg, "warn"); });
531
- }
532
-
533
- // Place the textarea caret at a 1-based line:col and scroll it into view. Used by
534
- // the C8 click-to-source jump; a textarea has no CodeMirror EditorView, so we map
535
- // (line,col) to a character offset + setSelectionRange, then center by scrollTop.
536
- function placeCursor(line, col) {
537
- var text = viewEl.value;
538
- var lines = text.split("\\n");
539
- var ln = Math.min(Math.max(1, line || 1), lines.length);
540
- var offset = 0;
541
- for (var i = 0; i < ln - 1; i++) offset += lines[i].length + 1; // +1 for the newline
542
- offset += Math.min(Math.max(1, col || 1), (lines[ln - 1] || "").length + 1) - 1;
543
- viewEl.focus();
544
- try { viewEl.setSelectionRange(offset, offset); } catch (e) { /* not focusable yet */ }
545
- // Center the target line: approximate line height from the rendered content.
546
- var lh = viewEl.scrollHeight / Math.max(1, lines.length);
547
- viewEl.scrollTop = Math.max(0, (ln - 1) * lh - viewEl.clientHeight / 2);
548
- }
549
-
550
- // C8 capstone entry point: open <path> and drop the caret at line:col. Invoked by
551
- // the cockpit's origin-checked tot:locate listener when a click lands in the
552
- // live-preview iframe. Finds the file's tree row (built by renderNodes); if the
553
- // tree hasn't loaded yet, no-ops gracefully.
554
- window.__totOpenFileAt = function (path, line, col) {
555
- var row = rowByPath[path];
556
- if (!row) return false;
557
- if (typeof row.scrollIntoView === "function") row.scrollIntoView({ block: "nearest" });
558
- openFile(path, row, function () { placeCursor(line, col); });
559
- return true;
560
- };
561
-
562
- function doSave(force) {
563
- if (!cur || !cur.writable) return;
564
- setStatus("Saving\\u2026");
565
- var body = { path: cur.path, content: viewEl.value };
566
- if (!force && typeof cur.baseVersion === "number") body.baseVersion = cur.baseVersion;
567
- fetch(apiBase + "/write", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) })
568
- .then(function (r) { return r.json().then(function (j) { return { s: r.status, j: j }; }); })
569
- .then(function (res) {
570
- if (res.s === 200 && res.j && res.j.ok) { cur.baseVersion = res.j.mtimeMs; showConflict(false); setStatus("Saved \\u2713", "ok"); return; }
571
- if (res.s === 409) { showConflict(true); setStatus("Changed on disk since you opened it", "warn"); return; }
572
- if (res.s === 403) { setStatus("This file isn't editable.", "warn"); return; }
573
- setStatus((res.j && res.j.message) || "Save failed", "warn");
574
- })
575
- .catch(function () { setStatus(isDev ? "Save failed \\u2014 dev server unreachable" : offlineMsg, "warn"); });
576
- }
577
-
578
- viewEl.addEventListener("input", function () {
579
- if (!cur || !cur.writable) return;
580
- setStatus("Editing\\u2026");
581
- if (saveTimer) clearTimeout(saveTimer);
582
- saveTimer = setTimeout(function () { doSave(false); }, 600);
583
- });
584
- viewEl.addEventListener("keydown", function (e) {
585
- if ((e.metaKey || e.ctrlKey) && (e.key === "s" || e.key === "S")) {
586
- e.preventDefault();
587
- if (saveTimer) clearTimeout(saveTimer);
588
- doSave(false);
589
- }
590
- });
591
- if (reloadBtn) reloadBtn.addEventListener("click", function () { if (cur && selected) openFile(cur.path, selected); });
592
- if (forceBtn) forceBtn.addEventListener("click", function () { showConflict(false); doSave(true); });
593
-
594
- function setDiffMode(on) {
595
- diffOn = on;
596
- if (diffEl) diffEl.hidden = !on;
597
- viewEl.hidden = on;
598
- if (diffBtn) diffBtn.textContent = on ? "Edit" : "Diff";
599
- }
600
- function renderDiffText(t) { if (diffEl) diffEl.textContent = t; }
601
- function lineDiff(before, after) {
602
- var a = before.split("\\n"), b = after.split("\\n");
603
- var m = a.length, n = b.length;
604
- if (m * n > 4000000) return null;
605
- var dp = []; for (var i = 0; i <= m; i++) { dp.push(new Array(n + 1).fill(0)); }
606
- for (var i = m - 1; i >= 0; i--) for (var j = n - 1; j >= 0; j--) dp[i][j] = a[i] === b[j] ? dp[i + 1][j + 1] + 1 : (dp[i + 1][j] >= dp[i][j + 1] ? dp[i + 1][j] : dp[i][j + 1]);
607
- var out = [], x = 0, y = 0;
608
- while (x < m && y < n) {
609
- if (a[x] === b[y]) { out.push([" ", a[x]]); x++; y++; }
610
- else if (dp[x + 1][y] >= dp[x][y + 1]) { out.push(["-", a[x]]); x++; }
611
- else { out.push(["+", b[y]]); y++; }
612
- }
613
- while (x < m) { out.push(["-", a[x]]); x++; }
614
- while (y < n) { out.push(["+", b[y]]); y++; }
615
- return out;
616
- }
617
- function renderDiff(before, after) {
618
- if (!diffEl) return;
619
- diffEl.textContent = "";
620
- var rows = lineDiff(before, after);
621
- if (!rows) { renderDiffText("(file too large to diff)"); return; }
622
- var frag = document.createDocumentFragment();
623
- rows.forEach(function (row) {
624
- var ln = document.createElement("span");
625
- ln.className = "dl " + (row[0] === "+" ? "add" : row[0] === "-" ? "del" : "ctx");
626
- ln.textContent = row[0] + " " + row[1];
627
- frag.appendChild(ln);
628
- });
629
- diffEl.appendChild(frag);
630
- }
631
- function doDiff() {
632
- if (!cur) return;
633
- if (diffOn) { setDiffMode(false); setStatus(cur.writable ? "Editable" : "Read-only"); return; }
634
- setStatus("Loading diff\\u2026");
635
- fetch(apiBase + "/diff?path=" + encodeURIComponent(cur.path))
636
- .then(function (r) { return r.json(); })
637
- .then(function (d) {
638
- if (!d || !d.ok) { setStatus((d && d.message) || "Could not diff.", "warn"); return; }
639
- if (!d.shown) { renderDiffText("(diff not shown \\u2014 " + (d.reason || "") + ")"); setDiffMode(true); setStatus(d.status); return; }
640
- renderDiff(d.before || "", d.after || "");
641
- setDiffMode(true);
642
- setStatus(d.status === "unchanged" ? "No changes vs your last commit" : (d.status + " vs HEAD"));
643
- })
644
- .catch(function () { setStatus(offlineMsg, "warn"); });
645
- }
646
- if (diffBtn) diffBtn.addEventListener("click", doDiff);
647
-
648
- // cp-publish: draft -> PR-backed candidate for review (unit s6, milestone
649
- // "PR-Backed Hosted Review Loop") — never straight to live, and no longer
650
- // straight to the test channel either; it opens/updates a pull request an
651
- // owner reviews and accepts. Confirm-before-submit per the card.
652
- function doPublish() {
653
- if (!cur || !cur.writable || !publishBtn) return;
654
- if (!window.confirm("Submit your saved edit for review? (Opens/updates a pull request \\u2014 not your live site.)")) return;
655
- var prevLabel = publishBtn.textContent;
656
- publishBtn.disabled = true;
657
- setStatus("Submitting\\u2026");
658
- fetch(apiBase + "/publish", { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify({ path: cur.path }) })
659
- .then(function (r) { return r.json().then(function (j) { return { s: r.status, j: j }; }); })
660
- .then(function (res) {
661
- if (res.s === 200 && res.j && res.j.ok) { setStatus("Submitted for review \\u2713" + (res.j.prNumber ? " (PR #" + res.j.prNumber + ")" : ""), "ok"); return; }
662
- if (res.s === 409 && res.j && res.j.status === "no_draft") { setStatus("No unpublished edit to submit.", "warn"); return; }
663
- setStatus((res.j && res.j.message) || "Submit failed", "warn");
664
- })
665
- .catch(function () { setStatus(offlineMsg, "warn"); })
666
- .then(function () { publishBtn.disabled = false; publishBtn.textContent = prevLabel; });
667
- }
668
- if (publishBtn) publishBtn.addEventListener("click", doPublish);
669
-
670
- function renderNodes(nodes, parentUl, depth) {
671
- nodes.forEach(function (n) {
672
- var li = document.createElement("li");
673
- li.className = "ft-" + n.type;
674
- var row = document.createElement("div");
675
- row.className = "ft-row";
676
- row.style.paddingLeft = (depth * 12 + 10) + "px";
677
- var icon = document.createElement("span");
678
- icon.className = "ft-ic";
679
- icon.textContent = n.type === "dir" ? "\\uD83D\\uDCC1" : "\\uD83D\\uDCC4";
680
- var label = document.createElement("span");
681
- label.className = "ft-name";
682
- label.textContent = n.name;
683
- row.appendChild(icon); row.appendChild(label); li.appendChild(row);
684
- if (n.type === "dir") {
685
- var childUl = document.createElement("ul");
686
- childUl.className = "ft-children";
687
- renderNodes(n.children || [], childUl, depth + 1);
688
- li.appendChild(childUl);
689
- } else {
690
- row.tabIndex = 0;
691
- rowByPath[n.path] = row; // for the C8 programmatic jump
692
- row.addEventListener("click", function () { openFile(n.path, row); });
693
- row.addEventListener("keydown", function (e) { if (e.key === "Enter") { e.preventDefault(); openFile(n.path, row); } });
694
- }
695
- parentUl.appendChild(li);
696
- });
697
- }
698
-
699
- function load() {
700
- fetch(apiBase + "/tree")
701
- .then(function (r) { return r.json(); })
702
- .then(function (d) {
703
- treeEl.textContent = "";
704
- rowByPath = {}; // rebuild the path→row index for this fresh tree
705
- if (!d || !d.ok) { treeEl.textContent = (d && d.message) || "Could not load your files."; return; }
706
- var ul = document.createElement("ul");
707
- ul.className = "ft-root";
708
- renderNodes(d.tree || [], ul, 0);
709
- treeEl.appendChild(ul);
710
- })
711
- .catch(function () { treeEl.textContent = offlineMsg; });
712
- }
713
- load();
714
- })();
715
- `;
716
-
717
- // C8 · Click-to-source capstone — the cockpit side. Runs the live-preview iframe
718
- // (mini + full-screen dialog) and LISTENS for the framed picker's tot:locate
719
- // postMessage. The picker (ai-widget-client.js, auto-injected into the same-origin
720
- // preview) resolves a click to {path,line,col} via /__tot/locate, then — because it
721
- // is framed — postMessages the result here instead of showing its own toast. We
722
- // verify event.origin === our origin, then hand off to the Files panel's
723
- // window.__totOpenFileAt to open the source + drop the caret. Only content-region
724
- // hits ever arrive (chrome/refused clicks stay silent in the frame), per decision
725
- // click-to-source-anchors-at-content-region.
726
- const c8Script = `
727
- (function () {
728
- var isDev = ${JSON.stringify(isLocalDevLoop)};
729
- var hostedAppDomain = ${JSON.stringify(appDomain)};
730
- var dlg = document.getElementById("previewDialog");
731
- var expandBtn = document.getElementById("previewExpand");
732
- var closeBtn = document.getElementById("previewClose");
733
- var dlgFrame = document.getElementById("previewDialogFrame");
734
- var miniFrame = document.getElementById("previewFrame");
735
- var urlEl = document.getElementById("previewUrl");
736
-
737
- if (miniFrame) {
738
- if (isDev) {
739
- // Point the preview at the DEV tenant (TOT_DEV_TENANT), read from the server
740
- // via /__tot/dev-loop-status — NOT the SSR appDomain (workerd can't see
741
- // TOT_DEV_*). Same-origin, so the auto-injected picker can postMessage back.
742
- fetch("/__tot/dev-loop-status")
743
- .then(function (r) { return r.json(); })
744
- .then(function (s) {
745
- var tenant = s && s.tenant;
746
- if (!tenant) return;
747
- var src = "/" + tenant + "/";
748
- miniFrame.src = src;
749
- if (urlEl) urlEl.textContent = "localhost preview · " + tenant;
750
- })
751
- .catch(function () { /* dev server unreachable — preview stays blank */ });
752
- } else if (hostedAppDomain) {
753
- // Hosted: the SSR-resolved tenant IS the signed-in developer's own store
754
- // (cp-env's capability gate already scoped it) — same-origin, so a cp-write
755
- // save + this reload shows the developer's own draft overlay via the
756
- // middleware's tot_dev_draft cookie check.
757
- miniFrame.src = "/" + hostedAppDomain + "/";
758
- if (urlEl) urlEl.textContent = "your store · " + hostedAppDomain;
759
- }
760
- }
761
-
762
- if (expandBtn && dlg) {
763
- expandBtn.addEventListener("click", function () {
764
- if (dlgFrame && !dlgFrame.getAttribute("src") && miniFrame) dlgFrame.src = miniFrame.getAttribute("src") || miniFrame.src;
765
- if (typeof dlg.showModal === "function") dlg.showModal(); else dlg.setAttribute("open", "");
766
- });
767
- }
768
- if (closeBtn && dlg) closeBtn.addEventListener("click", function () { dlg.close ? dlg.close() : dlg.removeAttribute("open"); });
769
- if (dlg) dlg.addEventListener("click", function (e) { if (e.target === dlg) { dlg.close ? dlg.close() : dlg.removeAttribute("open"); } });
770
-
771
- // The one privileged inbound message: a source location from OUR OWN origin's
772
- // framed preview. Reject any other origin, shape, or type outright.
773
- window.addEventListener("message", function (e) {
774
- if (e.origin !== window.location.origin) return;
775
- var d = e.data;
776
- if (!d || d.type !== "tot:locate" || typeof d.path !== "string") return;
777
- if (typeof window.__totOpenFileAt === "function") {
778
- var opened = window.__totOpenFileAt(d.path, d.line, d.col);
779
- // If the preview was expanded full-screen, close it so the editor + caret are visible.
780
- if (opened && dlg && dlg.open) { dlg.close ? dlg.close() : dlg.removeAttribute("open"); }
781
- }
782
- });
783
- })();
784
- `;
785
-
786
- // Hosted /dev activity log (hosted-dev-activity-bridge). Polls the NEW
787
- // GET /api/dev/activity — a translated, file-level feed of what the local dev
788
- // loop reported — and renders it newest-first. Also drives the "your edit
789
- // landed" celebration: the first poll primes a baseline (sessionStorage, same
790
- // survive-a-reload rationale as the local stepper's editBase), and any LATER
791
- // event past that baseline triggers the banner + a lightweight confetti burst.
792
- // POLL (not SSE): Workers are stateless per request, so a redeploy mid-session
793
- // needs no reconnect logic here. Rendered only when hostedActivity (never
794
- // alongside the isLocalDevLoop-gated local scripts above).
795
- const hostedActivityScript = `
796
- (function () {
797
- var root = document.getElementById("hostedActivity");
798
- if (!root) return;
799
-
800
- // The request's tenant prefix ("" host-routed, "/<tenant>" path-routed). Every
801
- // /api/dev/* call below carries it so the activity feed + the CLI sign-in-code mint
802
- // run in THIS store's scope (locals.tenant on the API request) — the same store this
803
- // /<tenant>/cockpit rendered for, never the session's first membership.
804
- var basePath = ${JSON.stringify(basePath)};
805
-
806
- // ?reset — a TRUE reset for re-testing the onboarding loop: clear THIS browser's
807
- // stepper progress AND the server-side KV (live-runtime record + activity buffer),
808
- // then reload clean. Clearing the server too means a stopped local loop stops reading
809
- // as "up" immediately instead of lingering until the runtime TTL/live-window expires.
810
- try {
811
- if (/[?&]reset\\b/.test(location.search)) {
812
- ["tot.hosted.steps", "tot.hosted.current", "tot.hosted.editBase"].forEach(function (k) {
813
- try { window.sessionStorage.removeItem(k); } catch (e) {}
814
- });
815
- var reloaded = false;
816
- var doneReset = function () { if (reloaded) return; reloaded = true; location.replace(location.pathname); };
817
- setTimeout(doneReset, 2000); // fallback so a hung request still lands us fresh
818
- try { fetch(basePath + "/api/dev/activity", { method: "DELETE", cache: "no-store" }).then(doneReset, doneReset); }
819
- catch (e) { doneReset(); }
820
- return; // the post-clear reload re-runs this script against a fresh, empty state
821
- }
822
- } catch (e) {}
823
-
824
- var tableEl = document.getElementById("haTable");
825
- var bodyEl = document.getElementById("haBody");
826
- var emptyEl = document.getElementById("haEmpty");
827
-
828
- // Bridge strip — the rail's power source, fed by the SAME poll's \`runtime\` object
829
- // (no second poller). \`runtime\` is { cliVersion, runnerVersion, url, aliveAt, live }
830
- // reported by the developer's local \`tot\` CLI; absent until its data layer lands,
831
- // so every read here is defensive (missing runtime -> the strip stays hidden).
832
- var bridge = document.getElementById("bridge");
833
- var bUrl = document.getElementById("bUrl");
834
- var bGo = document.getElementById("bGo");
835
- var bKicker = document.getElementById("bKicker");
836
- var bHint = document.getElementById("bHint");
837
- var bDiag = document.getElementById("bDiag");
838
- var bTxt = document.getElementById("bTxt");
839
- var bObstacle = document.getElementById("bObstacle");
840
- var bObTitle = document.getElementById("bObTitle");
841
- var bObMsg = document.getElementById("bObMsg");
842
- var bObCmd = document.getElementById("bObCmd");
843
- var bObNote = document.getElementById("bObNote");
844
- var doneRunMeta = document.getElementById("doneRunMeta");
845
- function updateBridge(rt, obstacle) {
846
- if (!bridge) return;
847
- var live = !!(rt && rt.live);
848
- if (live) sawLive = true;
849
- // Obstacle lane (v2 §6): a failed \`tot start\` is the ONE reason the strip
850
- // shows BEFORE the loop was ever live this page-view — the developer needs the
851
- // fix, not a silent "watching…". The server returns \`obstacle\` only when there
852
- // is no live heartbeat, so a live loop supersedes it. Fill from server copy.
853
- if (obstacle && !live) {
854
- bridge.hidden = false;
855
- bridge.setAttribute("data-live", "obstacle");
856
- if (bTxt) bTxt.hidden = true;
857
- if (bDiag) bDiag.hidden = true;
858
- if (bGo) bGo.hidden = true;
859
- if (bHint) bHint.hidden = true;
860
- if (bObstacle) bObstacle.hidden = false;
861
- if (bObTitle) bObTitle.textContent = obstacle.title || "Setup hit a snag";
862
- if (bObMsg) bObMsg.textContent = obstacle.message || "";
863
- if (bObCmd) {
864
- if (obstacle.fixCommand) { bObCmd.textContent = obstacle.fixCommand; bObCmd.hidden = false; }
865
- else bObCmd.hidden = true;
866
- }
867
- if (bObNote) bObNote.textContent = obstacle.fixNote || "";
868
- return;
869
- }
870
- // Back to the normal live/quiet strip: restore the pieces the obstacle state hid.
871
- if (bObstacle) bObstacle.hidden = true;
872
- if (bTxt) bTxt.hidden = false;
873
- if (!rt || !rt.url) { bridge.hidden = true; return; }
874
- // On FIRST arrival, a stale runtime record (offline, never seen live this
875
- // page-view — e.g. left over from a prior run, still within the heartbeat TTL)
876
- // must NOT show — let Step 1 "Get your store running" do the work. Only surface
877
- // the strip once it's live, or after it's gone quiet HAVING been live this page-view.
878
- if (!live && !sawLive) { bridge.hidden = true; return; }
879
- bridge.hidden = false;
880
- bridge.setAttribute("data-live", live ? "1" : "0");
881
- if (bUrl) { bUrl.textContent = rt.url; bUrl.href = rt.url; }
882
- // No point offering "Open your store" to a server that stopped answering.
883
- if (bGo) { bGo.href = rt.url; bGo.hidden = !live; }
884
- if (bKicker) bKicker.textContent = live ? "Live on your machine" : "Went quiet";
885
- if (bHint) bHint.hidden = live; // recovery hint only while offline
886
- if (doneRunMeta) doneRunMeta.textContent = rt.url; // the collapsed Step-1 row's meta
887
- if (bDiag) {
888
- if (rt.cliVersion) {
889
- bDiag.textContent = "tot CLI v" + rt.cliVersion + (rt.runnerVersion ? " \\u00b7 runner v" + rt.runnerVersion : "");
890
- bDiag.hidden = false;
891
- } else {
892
- bDiag.hidden = true;
893
- }
894
- }
895
- updateEditHint(rt); // suggest the exact "open this file" command on Step 2
896
- }
897
-
898
- // Item 6 — once the CLI reports the developer's $EDITOR, show the EXACT command to
899
- // open the first file to edit (e.g. \`code content/home.html\`); otherwise a generic
900
- // \`$EDITOR content/home.html\` plus a nudge to set $EDITOR.
901
- var editOpenCmd = document.getElementById("editOpenCmd");
902
- var editOpenHint = document.getElementById("editOpenHint");
903
- function updateEditHint(rt) {
904
- if (!editOpenCmd) return;
905
- var editor = rt && typeof rt.editor === "string" ? rt.editor : "";
906
- var cwd = rt && typeof rt.cwd === "string" ? rt.cwd : "";
907
- // Prefer the FULL path (works from any directory) when the CLI reports the
908
- // project dir; else a project-relative path with a "run it from there" note.
909
- var file = cwd ? (cwd.replace(/\\/+$/, "") + "/content/home.html") : "content/home.html";
910
- editOpenCmd.textContent = (editor || "$EDITOR") + " " + file;
911
- if (editOpenHint) {
912
- editOpenHint.textContent = editor
913
- ? (cwd
914
- ? "Run that anywhere to open the file — then change a line and save."
915
- : "Run that from your project directory — then change a line and save.")
916
- : "Tip: set $EDITOR (e.g. \`export EDITOR=\\"code\\"\`) and we'll show the exact open command here.";
917
- }
918
- }
919
-
920
- var SS = window.sessionStorage;
921
- function load(k, d) { try { var v = SS.getItem(k); return v === null ? d : JSON.parse(v); } catch (e) { return d; } }
922
- function put(k, v) { try { SS.setItem(k, JSON.stringify(v)); } catch (e) {} }
923
-
924
- var baseline = load("tot.hosted.editBase", null); // null = not yet primed by a first poll
925
- var expanded = false; // activity list: last 3 by default, expand to all
926
- // Have we seen the store go live during THIS PAGE VIEW? In-memory ONLY (not
927
- // persisted): a fresh land or a reload starts false, so a stale/offline record can
928
- // never greet the developer with "went quiet" — Step 1 owns that moment. The
929
- // recovery ("went quiet") state shows only after we actually watched it live → drop.
930
- var sawLive = false;
931
-
932
- // Guided accordion. Each step opens with its content; when its activity is detected
933
- // it animates CLOSED to a reopenable done-bar, and a single "Next Up" panel at the
934
- // bottom expands to tease the following step with ONE CTA that opens it.
935
- // steps = completion flags {run, edit}
936
- // current = the OPEN step key, or null between steps (Next Up showing)
937
- // Both persisted so a reload restores the exact place in the flow.
938
- var steps = load("tot.hosted.steps", { run: false, edit: false });
939
- // Live-driven flow. current is the EXPLICITLY-opened step ("run" when Step 1 was
940
- // reopened, "edit"/"next" for those) or null — the default view, where Step 1's
941
- // open/minimized state is driven by whether localhost is LIVE. NOT persisted: a
942
- // refresh lands in this default view (progress in steps still persists).
943
- var current = null;
944
- var liveNow = false; // is localhost live right now? set each poll from runtime.live
945
- var bodies = {
946
- run: document.getElementById("stage-run"),
947
- edit: document.getElementById("hosted-stage-edit"),
948
- next: document.getElementById("hosted-stage-next"),
949
- };
950
- // Rail station elements — the reskin's DOM (applyView targets these).
951
- var stEls = {
952
- run: document.getElementById("st-run"),
953
- edit: document.getElementById("st-edit"),
954
- next: document.getElementById("st-next"),
955
- };
956
- var segRun = document.getElementById("seg-run");
957
- var segEdit = document.getElementById("seg-edit");
958
- var doneRunEl = document.getElementById("done-run");
959
- var doneEditEl = document.getElementById("done-edit");
960
- var doneEditMeta = document.getElementById("doneEditMeta");
961
- var lockEditEl = document.getElementById("lock-edit");
962
- var lockNextEl = document.getElementById("lock-next");
963
- var teaseEditEl = document.getElementById("tease-edit");
964
- var teaseNextEl = document.getElementById("tease-next");
965
- var quietNoteEl = document.getElementById("quietNote");
966
-
967
- // Persisted state mutations funnel through here so the sessionStorage write can
968
- // never be forgotten at a call site (progress in steps + the edit baseline are
969
- // the only things that survive a reload).
970
- function completeStep(key) {
971
- if (steps[key]) return false; // already done — no-op
972
- steps[key] = true; put("tot.hosted.steps", steps);
973
- return true; // "just completed" — caller may celebrate
974
- }
975
- function setBaseline(v) { baseline = v; put("tot.hosted.editBase", baseline); }
976
-
977
- // ============================================================================
978
- // STATE → VIEW → DOM. The flow's behaviour is split into three layers so the
979
- // rules live in ONE readable place and every render goes through one path:
980
- // 1. derive — computeView(): pure, reads state (steps/current/liveNow),
981
- // returns a view model. EVERY visibility/emphasis rule is here.
982
- // 2. apply — applyView(): the ONLY code that mutates the rail DOM (station
983
- // statuses, segment lit/flow, step bodies, done rows, locked
984
- // labels, teasers, quiet note, activity).
985
- // 3. renderSteps(): apply(compute()). Call after ANY state change — never poke
986
- // an element directly from a handler or the poll.
987
- // The signal-rail reskin kept layer 1's RULES verbatim (only added derived
988
- // fields) and rewrote applyView() + the markup/CSS it targets.
989
- // ============================================================================
990
-
991
- // --- derive ---------------------------------------------------------------
992
- function unlocked(step) {
993
- return step === "run" || (step === "edit" && steps.run) || (step === "next" && steps.edit);
994
- }
995
- // Step 1 is OPEN while you still need to get the store running (localhost not live) or
996
- // when you explicitly reopened it; it MINIMIZES to a done row once localhost is live —
997
- // in opposition to revealing the Step-2 teaser.
998
- function step1Open() {
999
- return current === "run" || (current === null && !(steps.run && liveNow));
1000
- }
1001
- // The complete view model — no DOM access, so the rules can be read top to bottom:
1002
- // open which step BODY is expanded {run, edit, next}
1003
- // seg which rail segment is LIT {run, edit}
1004
- // flow do lit segments flow downward (live) boolean
1005
- // stRun/Edit/Next each station's status "open" | "done" | "next" | "locked"
1006
- // teaseEdit/Next is that step's forward teaser shown (live-gated)
1007
- // doneRun/Edit is that step's collapsed done row shown
1008
- // lockEdit/Next is that step's locked label shown
1009
- // quietNote the Step-1 "went quiet" note (re-inflated after a drop)
1010
- // showActivity is "Your activity" visible (NEVER while Step 1 is open)
1011
- function computeView() {
1012
- var s1 = step1Open();
1013
- // Forward teasers show ONLY while localhost is live — a forward CTA only makes
1014
- // sense while the loop is alive. When the loop goes quiet Step 1 re-inflates to own
1015
- // that moment, so the teaser retracts. Either is also hidden while the step it
1016
- // teases is already open.
1017
- var teaseEdit = steps.run && !steps.edit && liveNow && current !== "edit";
1018
- var teaseNext = steps.edit && liveNow && current !== "next";
1019
- return {
1020
- open: { run: s1, edit: current === "edit", next: current === "next" },
1021
- seg: { run: steps.run, edit: steps.edit },
1022
- flow: liveNow,
1023
- stRun: s1 ? "open" : "done",
1024
- stEdit: steps.edit ? "done" : (current === "edit") ? "open" : teaseEdit ? "next" : "locked",
1025
- stNext: (current === "next") ? "open" : teaseNext ? "next" : "locked",
1026
- teaseEdit: teaseEdit,
1027
- teaseNext: teaseNext,
1028
- doneRun: !s1 && steps.run,
1029
- doneEdit: steps.edit && current !== "edit",
1030
- lockEdit: !steps.run,
1031
- lockNext: !steps.edit,
1032
- quietNote: s1 && steps.run && !liveNow && sawLive,
1033
- showActivity: !s1 && steps.run,
1034
- };
1035
- }
1036
-
1037
- // --- apply (targets the rail DOM; the rules live in computeView above) -----
1038
- function applyView(v) {
1039
- if (stEls.run) stEls.run.setAttribute("data-st", v.stRun);
1040
- if (stEls.edit) stEls.edit.setAttribute("data-st", v.stEdit);
1041
- if (stEls.next) stEls.next.setAttribute("data-st", v.stNext);
1042
- if (segRun) { segRun.setAttribute("data-lit", v.seg.run ? "1" : "0"); segRun.setAttribute("data-flow", v.flow ? "1" : "0"); }
1043
- if (segEdit) { segEdit.setAttribute("data-lit", v.seg.edit ? "1" : "0"); segEdit.setAttribute("data-flow", v.flow ? "1" : "0"); }
1044
- if (bodies.run) bodies.run.hidden = !v.open.run;
1045
- if (bodies.edit) bodies.edit.hidden = !v.open.edit;
1046
- if (bodies.next) bodies.next.hidden = !v.open.next;
1047
- if (doneRunEl) doneRunEl.hidden = !v.doneRun;
1048
- if (doneEditEl) doneEditEl.hidden = !v.doneEdit;
1049
- if (lockEditEl) lockEditEl.hidden = !v.lockEdit;
1050
- if (lockNextEl) lockNextEl.hidden = !v.lockNext;
1051
- if (teaseEditEl) teaseEditEl.hidden = !v.teaseEdit;
1052
- if (teaseNextEl) teaseNextEl.hidden = !v.teaseNext;
1053
- if (quietNoteEl) quietNoteEl.hidden = !v.quietNote;
1054
- if (root) root.hidden = !v.showActivity;
1055
- }
1056
- // Single render entry point — derive, then apply. (Named renderSteps so the
1057
- // existing call sites keep working; distinct from the activity render(events).)
1058
- function renderSteps() { applyView(computeView()); }
1059
-
1060
- function expandStage(el) {
1061
- if (!el) return;
1062
- el.hidden = false;
1063
- el.classList.add("expanding");
1064
- setTimeout(function () { if (el && el.classList) el.classList.remove("expanding"); }, 420);
1065
- }
1066
-
1067
- // Open a step (from a teaser CTA or a collapsed done row) — animates it in.
1068
- function goTo(step) {
1069
- if (!unlocked(step)) return;
1070
- current = step;
1071
- renderSteps();
1072
- expandStage(bodies[step]);
1073
- window.scrollTo({ top: 0, behavior: "smooth" });
1074
- }
1075
-
1076
- // Rail click wiring: done rows reopen their step; teaser CTAs open the teased step.
1077
- if (doneRunEl) doneRunEl.addEventListener("click", function () { goTo("run"); });
1078
- if (doneEditEl) doneEditEl.addEventListener("click", function () { goTo("edit"); });
1079
- var ctaEdit = document.getElementById("cta-edit");
1080
- if (ctaEdit) ctaEdit.addEventListener("click", function () { goTo("edit"); });
1081
- var ctaNext = document.getElementById("cta-next");
1082
- if (ctaNext) ctaNext.addEventListener("click", function () { goTo("next"); });
1083
-
1084
- // Absolute clock time with seconds (e.g. "2:57:14 PM") — a fixed point, not a
1085
- // churning duration. Full date+time stays on the hover title (render()).
1086
- function rel(ts) {
1087
- try {
1088
- return new Date(Number(ts)).toLocaleTimeString([], { hour: "numeric", minute: "2-digit", second: "2-digit" });
1089
- } catch (x) {
1090
- return "";
1091
- }
1092
- }
1093
-
1094
- // Toast — reuses the shared #toast element (also driven by the copy script).
1095
- var toastEl = document.getElementById("toast");
1096
- var toastMsgEl = document.getElementById("toastMsg");
1097
- var toastT;
1098
- function toast(msg) {
1099
- if (!toastEl || !toastMsgEl) return;
1100
- toastMsgEl.textContent = msg;
1101
- toastEl.classList.add("show");
1102
- clearTimeout(toastT);
1103
- toastT = setTimeout(function () { toastEl.classList.remove("show"); }, 2600);
1104
- }
1105
-
1106
- // Celebration — replaces the full-screen confetti. Stamps the Step-2 node (spark
1107
- // burst + stamp, via the .celebrating class) and shows a toast. Reduced-motion
1108
- // disables the burst/stamp in CSS; the toast still shows.
1109
- function celebrate(file) {
1110
- var st = stEls.edit;
1111
- if (st) {
1112
- st.classList.remove("celebrating");
1113
- void st.offsetWidth; // restart the burst animation
1114
- st.classList.add("celebrating");
1115
- setTimeout(function () { if (st && st.classList) st.classList.remove("celebrating"); }, 1100);
1116
- }
1117
- if (doneEditMeta && file) doneEditMeta.textContent = file;
1118
- toast("Saved " + (file || "your file") + " \\u2014 your store reloaded. That's the loop.");
1119
- }
1120
-
1121
- var toggleEl = document.getElementById("haToggle");
1122
- var VISIBLE = 3; // show the latest 3 by default; expand for the rest
1123
- if (toggleEl) {
1124
- toggleEl.addEventListener("click", function () { expanded = !expanded; render(lastEvents); });
1125
- }
1126
- var lastEvents = [];
1127
-
1128
- function render(events) {
1129
- lastEvents = events;
1130
- // Item 3 — drop the "up"/"running locally" pings; the live widget at the top
1131
- // already conveys that. Only real FILE activity belongs in the table, and each
1132
- // field is its own <td> so time / action / file never run together.
1133
- var real = (events || []).filter(function (e) { return e && e.event !== "up"; });
1134
- Array.prototype.forEach.call(bodyEl.querySelectorAll(".ha-row"), function (n) { n.remove(); });
1135
- if (!real.length) {
1136
- emptyEl.hidden = false;
1137
- if (tableEl) tableEl.hidden = true;
1138
- if (toggleEl) toggleEl.hidden = true;
1139
- return;
1140
- }
1141
- emptyEl.hidden = true;
1142
- if (tableEl) tableEl.hidden = false;
1143
- var newestFirst = real.slice().reverse();
1144
- var shown = expanded ? newestFirst : newestFirst.slice(0, VISIBLE);
1145
- shown.forEach(function (e, i) {
1146
- var tr = document.createElement("tr");
1147
- tr.className = i === 0 ? "ha-row is-new" : "ha-row"; // accent the newest row
1148
- var when = document.createElement("td");
1149
- when.className = "ha-when";
1150
- when.textContent = rel(e.at);
1151
- try { when.title = new Date(e.at).toLocaleString(); } catch (x) {} // exact time on hover
1152
- var desc = document.createElement("td");
1153
- desc.className = "ha-desc";
1154
- desc.textContent = e.description;
1155
- var file = document.createElement("td");
1156
- file.className = "ha-file";
1157
- file.textContent = e.file || ""; // the concrete file name, e.g. content/home.html
1158
- tr.appendChild(when); tr.appendChild(desc); tr.appendChild(file);
1159
- bodyEl.appendChild(tr);
1160
- });
1161
- if (toggleEl) {
1162
- if (real.length > VISIBLE) {
1163
- toggleEl.hidden = false;
1164
- toggleEl.textContent = expanded ? "Show fewer" : "Show all " + real.length;
1165
- } else {
1166
- toggleEl.hidden = true;
1167
- }
1168
- }
1169
- }
1170
-
1171
- // Adaptive poll cadence + visibility pause (2026-07-15 KV-quota incident: a
1172
- // single backgrounded tab, polling flat-out every 3s with no pause, burned
1173
- // through the ENTIRE account-wide daily KV read quota by itself and took
1174
- // down real customer traffic with 500s). Two rules:
1175
- // 1. HIDDEN tab → don't poll at all. A backgrounded/minimized tab can't be
1176
- // "watching" the loop; resume (with an immediate catch-up poll) the
1177
- // instant it becomes visible again.
1178
- // 2. VISIBLE but the loop is already fully celebrated (steps.run AND
1179
- // steps.edit) → the high-stakes "is it working yet" moment has passed,
1180
- // so back off to a slow cadence. Still visible AND still mid-loop →
1181
- // keep the fast cadence (this is the one case where sub-second-feeling
1182
- // responsiveness genuinely matters — a real developer staring at the
1183
- // screen waiting to see their save land).
1184
- var FAST_MS = 3000; // actively watching for the loop to prove itself
1185
- var SLOW_MS = 20000; // loop already celebrated — just an occasional glance
1186
- var pollTimer = null;
1187
-
1188
- function nextDelay() {
1189
- return (steps.run && steps.edit) ? SLOW_MS : FAST_MS;
1190
- }
1191
-
1192
- function scheduleNext() {
1193
- if (pollTimer) { clearTimeout(pollTimer); pollTimer = null; }
1194
- if (document.hidden) return; // stay fully paused; visibilitychange resumes us
1195
- pollTimer = setTimeout(poll, nextDelay());
1196
- }
1197
-
1198
- function poll() {
1199
- fetch(basePath + "/api/dev/activity", { cache: "no-store" })
1200
- .then(function (r) { return r.ok ? r.json() : Promise.reject(); })
1201
- .then(function (d) {
1202
- var events = (d && d.events) || [];
1203
- var rt = d && d.runtime;
1204
- var isLive = !!(rt && rt.live);
1205
- liveNow = isLive; // drives Step 1 minimize + the Step-2 teaser visibility below
1206
- updateBridge(rt, d && d.obstacle); // same poll drives the bridge strip (+ obstacle lane)
1207
- render(events);
1208
- // b1 fix — Step-2 completion must key off the SAME events the feed shows:
1209
- // REAL file edits (event !== "up"), NOT the single newest buffer entry. The
1210
- // local loop interleaves recurring "up" liveness pings into this one ring
1211
- // buffer, so the newest event is frequently an "up" ping even right after a
1212
- // save. The old gate read events[last]: when an "up" ping landed AFTER the
1213
- // edit it (1) made lastIsEdit false so completeStep("edit") never fired, and
1214
- // (2) still advanced the baseline watermark past the edit's timestamp —
1215
- // permanently stranding Step 2 even though the save rendered in "Your
1216
- // activity". Filter to real edits (mirroring render()'s filter) and advance
1217
- // the watermark only from real edits, so a liveness ping can neither satisfy
1218
- // nor poison the gate.
1219
- var realEdits = events.filter(function (e) { return e && e.event !== "up"; });
1220
- var lastEdit = realEdits.length ? realEdits[realEdits.length - 1] : null;
1221
- var latestEdit = lastEdit ? lastEdit.at : 0;
1222
- // Step 1 "Get running" completes once localhost reports LIVE (or any activity
1223
- // arrives). Once complete, renderSteps() minimizes Step 1 and reveals the Step-2 nudge.
1224
- if (isLive || events.length) completeStep("run");
1225
- if (baseline === null) {
1226
- setBaseline(latestEdit); // first poll primes the watermark from real edits — no edit inferred yet
1227
- } else if (latestEdit > baseline) {
1228
- setBaseline(latestEdit);
1229
- // A real FILE edit landed after the watermark → Step 2 completes (once).
1230
- // completeStep flips + persists once, returning true on that first poll only.
1231
- if (completeStep("edit")) {
1232
- celebrate(lastEdit && lastEdit.file);
1233
- if (current === "edit") current = null; // minimize Step 2 → reveal Step-3 teaser
1234
- }
1235
- }
1236
- // ALWAYS re-render so a localhost drop hides the Step-2 nudge + reopens Step 1.
1237
- renderSteps();
1238
- })
1239
- .catch(function () {})
1240
- .then(scheduleNext); // reschedule (or not, if now hidden) regardless of outcome
1241
- }
1242
-
1243
- document.addEventListener("visibilitychange", function () {
1244
- if (document.hidden) {
1245
- if (pollTimer) { clearTimeout(pollTimer); pollTimer = null; }
1246
- } else {
1247
- poll(); // catch up immediately; poll() reschedules itself at the end
1248
- }
1249
- });
1250
-
1251
- // Item 7/10 — Step 3 "Submit for preview" opens the "coming soon" dialog (the
1252
- // compliance-reviewed preview pipeline isn't wired into this panel yet).
1253
- var submitBtn = document.getElementById("submitPreview");
1254
- var comingSoon = document.getElementById("comingSoon");
1255
- if (submitBtn && comingSoon) {
1256
- submitBtn.addEventListener("click", function () {
1257
- if (typeof comingSoon.showModal === "function") comingSoon.showModal();
1258
- else comingSoon.setAttribute("open", "");
1259
- });
1260
- var csClose = document.getElementById("comingSoonClose");
1261
- if (csClose) csClose.addEventListener("click", function () {
1262
- if (typeof comingSoon.close === "function") comingSoon.close();
1263
- comingSoon.removeAttribute("open");
1264
- });
1265
- }
1266
-
1267
- renderSteps();
1268
- if (!document.hidden) poll(); // if the tab starts hidden, visibilitychange kicks it off later
1269
- })();
1270
- `;
212
+ // Dev-only client widgets (Monitor, Stepper, Ask, Files, click-to-source) and
213
+ // the hosted activity rail live in typed apps/storefront/src/lib/dev/cockpit/*
214
+ // modules, imported from processed <script> tags below (ADR 0013) instead of
215
+ // being hand-mirrored here as raw strings. apiBase/isLocalDevLoop/appDomain/
216
+ // basePath cross into those modules via data-cockpit-* attrs on <body>.
1271
217
  ---
1272
218
 
1273
219
  <html lang="en">
@@ -1595,6 +541,8 @@ const hostedActivityScript = `
1595
541
 
1596
542
  /* ---- Step 2 "lay it beside your editor" tip ---- */
1597
543
  .tip-lay { margin: 1rem 0 0; font-size: .9rem; color: var(--muted); line-height: 1.5; }
544
+ .tip-branch { margin: .5rem 0 0; font-size: .9rem; color: var(--muted); line-height: 1.5; }
545
+ .tip-branch a { color: inherit; text-decoration: underline; }
1598
546
 
1599
547
  /* ---- Guided accordion: a step body animates IN when opened (collapse is instant
1600
548
  + synchronous so the flow can't get stuck). Shared with isLocalDevLoop —
@@ -2016,7 +964,12 @@ const hostedActivityScript = `
2016
964
  [hidden] { display: none !important; }
2017
965
  </style>
2018
966
  </head>
2019
- <body>
967
+ <body
968
+ data-cockpit-api-base={apiBase}
969
+ data-cockpit-is-dev={isLocalDevLoop ? "true" : "false"}
970
+ data-cockpit-app-domain={appDomain}
971
+ data-cockpit-base-path={basePath}
972
+ >
2020
973
  <div class="wrap">
2021
974
  <header class="topbar">
2022
975
  <div class="brand">
@@ -2260,7 +1213,8 @@ const hostedActivityScript = `
2260
1213
  <span class="cmd" id="editOpenCmd">$EDITOR content/home.html</span></div>
2261
1214
  </div>
2262
1215
  <p class="cmd-note" id="editOpenHint">Tip: set <code>$EDITOR</code> and we'll show the exact open command here.</p>
2263
- <p class="tip-lay">Lay this cockpit <em>beside</em> your editor and your running store — hot reload is on, so every save reloads the store and lands in <strong>Your activity</strong> here, live.</p>
1216
+ <p class="tip-lay">Lay this cockpit <em>beside</em> your editor (or an AI coding agent like Claude — same idea) and your running store — hot reload is on, so every save reloads the store and lands in <strong>Your activity</strong> here, live. Keep the terminal running <code>tot start</code>/<code>tot dev</code> open too, watching for build errors — need the command line for something else? Open a new terminal instead of closing that one.</p>
1217
+ <p class="tip-branch">No need to think about git branches for this — just keep editing and running <code>tot preview</code>; it updates the same change every time. <a href="https://github.com/tokenoftrust/storefront/blob/feature/8500-storefront-integration/docs/kb/develop-ship-and-connect-domain.md#branching-is-an-advanced-feature--you-almost-never-need-it" target="_blank" rel="noopener">Working on two things at once? Read this first →</a></p>
2264
1218
  </div>
2265
1219
  </section>
2266
1220
  </div>
@@ -2534,291 +1488,61 @@ const hostedActivityScript = `
2534
1488
  </div>
2535
1489
  )}
2536
1490
 
2537
- <script nonce={cspNonce} is:inline>
2538
- // Theme toggle: flip data-theme on <html>, seeded from the OS preference on first
2539
- // click, and PERSISTED to localStorage so a reload stays on the manual choice (the
2540
- // head seed script above reads this same key before first paint). Clearing storage
2541
- // falls back to prefers-color-scheme again, same as a first-ever visit.
2542
- (function () {
2543
- var root = document.documentElement;
2544
- var btn = document.getElementById("themeBtn");
2545
- if (!btn) return;
2546
- var KEY = "tot.theme";
2547
- function store(v) { try { localStorage.setItem(KEY, v); } catch (e) {} }
2548
- btn.addEventListener("click", function () {
2549
- var cur = root.getAttribute("data-theme");
2550
- var next = cur === "dark" ? "light" : cur === "light" ? "dark"
2551
- : (window.matchMedia && window.matchMedia("(prefers-color-scheme: dark)").matches ? "light" : "dark");
2552
- root.setAttribute("data-theme", next);
2553
- store(next);
2554
- });
2555
- })();
1491
+ <script>
1492
+ import { initThemeToggle } from "@/lib/dev/cockpit/themeToggle";
1493
+ initThemeToggle();
2556
1494
  </script>
2557
1495
 
2558
- <script nonce={cspNonce} is:inline>
2559
- // Copy the "connect the MCP" command (d3-unhold-dev-step0-connect-mcp). Static
2560
- // text (no minting), so unlike #copyIcon this button is visible from load.
2561
- (function () {
2562
- var copyIcon = document.getElementById("mcpCopyIcon");
2563
- var cmdText = document.getElementById("mcpCmdText");
2564
- var toast = document.getElementById("toast");
2565
- var toastMsg = document.getElementById("toastMsg");
2566
- if (!copyIcon || !cmdText) return;
2567
- var tT;
2568
- function ping(m) {
2569
- if (!toast || !toastMsg) return;
2570
- toastMsg.textContent = m; toast.classList.add("show");
2571
- clearTimeout(tT); tT = setTimeout(function () { toast.classList.remove("show"); }, 1900);
2572
- }
2573
- copyIcon.addEventListener("click", function () {
2574
- var text = cmdText.textContent;
2575
- var done = navigator.clipboard
2576
- ? navigator.clipboard.writeText(text).then(function () { return true; }).catch(function () { return false; })
2577
- : Promise.resolve(false);
2578
- done.then(function (ok) {
2579
- if (ok) {
2580
- copyIcon.setAttribute("data-copied", "1");
2581
- setTimeout(function () { copyIcon.removeAttribute("data-copied"); }, 1600);
2582
- ping("Copied");
2583
- } else {
2584
- ping("Select the command above to copy");
2585
- }
2586
- });
2587
- });
2588
- })();
1496
+ <script>
1497
+ import { initMcpCopy } from "@/lib/dev/cockpit/mcpCopy";
1498
+ initMcpCopy();
2589
1499
  </script>
2590
1500
 
2591
- <script nonce={cspNonce} is:inline>
2592
- // Info popover: toggle on the ⓘ button, dismiss on outside click.
2593
- (function () {
2594
- var info1 = document.getElementById("info1");
2595
- if (!info1) return;
2596
- var openBtn = info1.querySelector(".info-btn");
2597
- openBtn.addEventListener("click", function (e) { e.stopPropagation(); info1.classList.toggle("open"); });
2598
- document.addEventListener("click", function (e) { if (!info1.contains(e.target)) info1.classList.remove("open"); });
2599
- })();
1501
+ <script>
1502
+ import { initInfoPopover } from "@/lib/dev/cockpit/infoPopover";
1503
+ initInfoPopover();
2600
1504
  </script>
2601
1505
 
2602
- <script nonce={cspNonce} is:inline>
2603
- // Copy the one command. Two behaviours:
2604
- // · fastpath (signed-in dev): mint a single-use `tot login --code … && tot start` from
2605
- // /api/dev/cli-signin-code, reveal it (textContent, never innerHTML — it carries a
2606
- // live code), and copy the whole thing.
2607
- // · otherwise: copy the generic install-and-run command directly.
2608
- (function () {
2609
- var btn = document.getElementById("copyCmd");
2610
- var textEl = document.getElementById("cli-cmd-text");
2611
- var note = document.getElementById("cli-cmd-note");
2612
- var toast = document.getElementById("toast");
2613
- var toastMsg = document.getElementById("toastMsg");
2614
- var copyIcon = document.getElementById("copyIcon");
2615
- if (!btn || !textEl) return;
2616
- var generic = btn.getAttribute("data-generic") || "";
2617
- var fastpath = btn.getAttribute("data-fastpath") === "1";
2618
- // The request's tenant prefix ("" host-routed, "/<tenant>" path-routed). Carried
2619
- // as a data-attr because `basePath` is otherwise defined only inside the SEPARATE
2620
- // hostedActivity IIFE — referencing it here (the cli-signin-code / -status / -approve
2621
- // fetches below) was a ReferenceError that the mint's catch surfaced as the opaque
2622
- // "Couldn't reach the sign-in service" with no request ever sent.
2623
- var basePath = btn.getAttribute("data-base-path") || "";
2624
- var tT;
2625
- function ping(m) {
2626
- if (!toast) return;
2627
- toastMsg.textContent = m; toast.classList.add("show");
2628
- clearTimeout(tT); tT = setTimeout(function () { toast.classList.remove("show"); }, 1900);
2629
- }
2630
- function copy(text) {
2631
- if (navigator.clipboard) return navigator.clipboard.writeText(text).then(function () { return true; }).catch(function () { return false; });
2632
- return Promise.resolve(false);
2633
- }
2634
- function label(s) { btn.childNodes[btn.childNodes.length - 1].textContent = s; }
2635
-
2636
- // Standard copy-icon affordance on the terminal box. Once the command is
2637
- // revealed, this is the obvious, always-there way to (re)copy it — the
2638
- // icon→check swap is its own inline confirmation, so copying no longer
2639
- // depends on the transient toast being noticed.
2640
- var iconT;
2641
- function flashCopied() {
2642
- if (!copyIcon) return;
2643
- copyIcon.setAttribute("data-copied", "1");
2644
- copyIcon.setAttribute("aria-label", "Copied");
2645
- copyIcon.setAttribute("title", "Copied");
2646
- clearTimeout(iconT);
2647
- iconT = setTimeout(function () {
2648
- copyIcon.removeAttribute("data-copied");
2649
- copyIcon.setAttribute("aria-label", "Copy the command");
2650
- copyIcon.setAttribute("title", "Copy the command");
2651
- }, 1600);
2652
- }
2653
- function revealIcon() {
2654
- if (copyIcon) copyIcon.hidden = false;
2655
- }
2656
- if (copyIcon) {
2657
- copyIcon.addEventListener("click", function () {
2658
- copy(textEl.textContent).then(function (ok) {
2659
- if (ok) { flashCopied(); ping("Copied — paste & press Enter"); }
2660
- else ping("Select the command above to copy");
2661
- });
2662
- });
2663
- }
2664
-
2665
- // ── Rendezvous approval: after a command is generated, the terminal ATTACHES
2666
- // its own PKCE key and prints a fingerprint; the developer confirms it HERE. This
2667
- // is the load-bearing anti-interception step — approving the wrong fingerprint is
2668
- // how a hijack is caught, so the confirm is an explicit, human decision.
2669
- var rzvRoot = document.getElementById("rzvApprove");
2670
- var rzvWait = document.getElementById("rzvWait");
2671
- var rzvConfirm = document.getElementById("rzvConfirm");
2672
- var rzvFp = document.getElementById("rzvFp");
2673
- var rzvApproveBtn = document.getElementById("rzvApproveBtn");
2674
- var rzvDenyBtn = document.getElementById("rzvDenyBtn");
2675
- var rzvDone = document.getElementById("rzvDone");
2676
- var rzvTimer = null, rzvCode = null, rzvDecided = false;
2677
- function rzvShow(el) { if (el) el.hidden = false; }
2678
- function rzvHide(el) { if (el) el.hidden = true; }
2679
- // Once the terminal has ATTACHED, the command has plainly been pasted and
2680
- // run — hide the copy affordances so the approve prompt is the page's
2681
- // single focus (DZ 2026-08-18). An EXPIRED sign-in restores the generate
2682
- // button (a fresh mint is needed); the stale command's copy icon stays gone.
2683
- function rzvHideCopyCtas() {
2684
- if (btn) btn.hidden = true;
2685
- if (copyIcon) copyIcon.hidden = true;
2686
- }
2687
- function rzvRestoreGenerate() {
2688
- if (btn) btn.hidden = false;
2689
- }
2690
- function rzvStopPoll() { if (rzvTimer) { clearTimeout(rzvTimer); rzvTimer = null; } }
2691
- function rzvFinish(msg) {
2692
- rzvStopPoll(); rzvDecided = true;
2693
- rzvHide(rzvWait); rzvHide(rzvConfirm);
2694
- if (rzvDone) { rzvDone.textContent = msg; rzvDone.hidden = false; }
2695
- }
2696
- async function rzvPoll() {
2697
- if (rzvDecided) return;
2698
- var r, b;
2699
- try {
2700
- r = await fetch(basePath + "/api/dev/cli-signin-status", {
2701
- method: "POST", headers: { "content-type": "application/json" },
2702
- body: JSON.stringify({ rendezvousCode: rzvCode }),
2703
- });
2704
- b = await r.json().catch(function () { return {}; });
2705
- } catch (e) { rzvTimer = setTimeout(rzvPoll, 2800); return; }
2706
- if (!r || !r.ok) { rzvTimer = setTimeout(rzvPoll, 3200); return; }
2707
- if (b.state === "attached" && b.fingerprint) {
2708
- rzvHideCopyCtas();
2709
- rzvHide(rzvWait); rzvShow(rzvConfirm);
2710
- if (rzvFp) { rzvFp.textContent = b.fingerprint; rzvFp.setAttribute("data-fp", b.fingerprint); }
2711
- return; // stop polling — wait for the human decision
2712
- }
2713
- if (b.state === "approved") { rzvHideCopyCtas(); rzvFinish("Signed in ✓ — your terminal is good to go."); return; }
2714
- if (b.state === "denied") { rzvHideCopyCtas(); rzvFinish("Rejected — that terminal wasn’t approved. You’re safe; generate a fresh command to try again."); return; }
2715
- if (b.state === "expired") { rzvRestoreGenerate(); rzvFinish("This sign-in expired. Generate a fresh setup command above."); return; }
2716
- rzvTimer = setTimeout(rzvPoll, 2500); // pending → keep watching
2717
- }
2718
- function rzvStart(code) {
2719
- rzvCode = code; rzvDecided = false;
2720
- if (rzvApproveBtn) rzvApproveBtn.disabled = false;
2721
- if (rzvDenyBtn) rzvDenyBtn.disabled = false;
2722
- rzvShow(rzvRoot); rzvShow(rzvWait); rzvHide(rzvConfirm); rzvHide(rzvDone);
2723
- rzvStopPoll(); rzvPoll();
2724
- }
2725
- async function rzvDecide(decision) {
2726
- if (rzvDecided) return;
2727
- var fp = rzvFp ? (rzvFp.getAttribute("data-fp") || "") : "";
2728
- if (rzvApproveBtn) rzvApproveBtn.disabled = true;
2729
- if (rzvDenyBtn) rzvDenyBtn.disabled = true;
2730
- var r;
2731
- try {
2732
- r = await fetch(basePath + "/api/dev/cli-signin-approve", {
2733
- method: "POST", headers: { "content-type": "application/json" },
2734
- body: JSON.stringify({ rendezvousCode: rzvCode, fingerprint: fp, decision: decision }),
2735
- });
2736
- } catch (e) {
2737
- if (rzvApproveBtn) rzvApproveBtn.disabled = false;
2738
- if (rzvDenyBtn) rzvDenyBtn.disabled = false;
2739
- return;
2740
- }
2741
- if (decision === "deny") { rzvFinish("Rejected — that terminal wasn’t approved."); return; }
2742
- if (r.ok) { rzvFinish("Signed in ✓ — your terminal is good to go."); return; }
2743
- if (rzvApproveBtn) rzvApproveBtn.disabled = false;
2744
- if (rzvDenyBtn) rzvDenyBtn.disabled = false;
2745
- var m = r.status === 403 ? "That didn’t match your identity — generate a fresh command and try again."
2746
- : r.status === 409 ? "That code didn’t match a waiting terminal — re-check it, or generate a fresh command."
2747
- : "Couldn’t complete approval — try again.";
2748
- if (rzvDone) { rzvDone.textContent = m; rzvDone.hidden = false; }
2749
- }
2750
- if (rzvApproveBtn) rzvApproveBtn.addEventListener("click", function () { rzvDecide("approve"); });
2751
- if (rzvDenyBtn) rzvDenyBtn.addEventListener("click", function () { rzvDecide("deny"); });
2752
-
2753
- if (!fastpath) {
2754
- btn.addEventListener("click", function () {
2755
- copy(generic).then(function (ok) {
2756
- label(ok ? " Copied — paste & press Enter" : " Copy the command");
2757
- ping(ok ? "Copied the command" : "Select the command to copy");
2758
- setTimeout(function () { label(" Copy the command"); }, 2600);
2759
- });
2760
- });
2761
- return;
2762
- }
2763
-
2764
- var busy = false;
2765
- btn.addEventListener("click", async function () {
2766
- if (busy) return;
2767
- busy = true;
2768
- label(" …"); btn.disabled = true;
2769
- var res, body;
2770
- try {
2771
- res = await fetch(basePath + "/api/dev/cli-signin-code", {
2772
- method: "POST",
2773
- headers: { "content-type": "application/json" },
2774
- body: "{}",
2775
- });
2776
- body = await res.json().catch(function () { return {}; });
2777
- } catch (err) {
2778
- if (note) note.textContent = "Couldn’t reach the sign-in service — try again in a moment.";
2779
- label(" Copy the command"); btn.disabled = false; busy = false;
2780
- return;
2781
- }
2782
- if (!res.ok || !body.command) {
2783
- var byStatus = {
2784
- 401: "Your session expired — reload the page and sign in again.",
2785
- 403: "This account doesn’t have developer access to a store yet.",
2786
- 409: "Your store isn’t set up yet — reload this page for its status.",
2787
- 502: "The sign-in service is having a moment — try again.",
2788
- 503: "Terminal sign-in isn’t configured on this environment.",
2789
- };
2790
- if (note) note.textContent = byStatus[res.status] || "Couldn’t mint a sign-in command — try again.";
2791
- label(" Copy the command"); btn.disabled = false; busy = false;
2792
- return;
2793
- }
2794
- // Reveal the real command (carries the non-secret rendezvous handle) as a pure
2795
- // text node, then copy it.
2796
- textEl.textContent = body.command;
2797
- textEl.classList.remove("is-placeholder");
2798
- revealIcon();
2799
- // Start watching for the terminal to attach so we can prompt for approval.
2800
- if (body.rendezvousCode) rzvStart(body.rendezvousCode);
2801
- var ok = await copy(body.command);
2802
- if (ok) flashCopied();
2803
- label(ok ? " Copied ✓" : " Select & copy");
2804
- ping(ok ? "Copied — paste & press Enter" : "Select the command above to copy");
2805
- if (note) {
2806
- note.textContent = ok
2807
- ? "Copied. Paste into your terminal and press Enter — it signs you in, runs your store, and opens your browser. Single-use, short-lived code."
2808
- : "Select the command above and copy it, then paste into your terminal and press Enter. Single-use, short-lived code.";
2809
- }
2810
- btn.disabled = false;
2811
- setTimeout(function () { label(" Copy the command"); }, 4000);
2812
- busy = false;
2813
- });
2814
- })();
1506
+ <script>
1507
+ import { initCopyCommand } from "@/lib/dev/cockpit/copyCommand";
1508
+ initCopyCommand();
2815
1509
  </script>
2816
1510
 
2817
- {hostedActivity && <script nonce={cspNonce} is:inline set:html={hostedActivityScript} />}
2818
- {isLocalDevLoop && <script nonce={cspNonce} is:inline set:html={stepperScript} />}
2819
- {isLocalDevLoop && <script nonce={cspNonce} is:inline set:html={askScript} />}
2820
- {isLocalDevLoop && <script nonce={cspNonce} is:inline set:html={monitorScript} />}
2821
- {isLocalDevLoop && <script nonce={cspNonce} is:inline set:html={filesScript} />}
2822
- {isLocalDevLoop && <script nonce={cspNonce} is:inline set:html={c8Script} />}
1511
+ {hostedActivity && (
1512
+ <script>
1513
+ import { initHostedActivity } from "@/lib/dev/cockpit/hostedActivity";
1514
+ initHostedActivity();
1515
+ </script>
1516
+ )}
1517
+ {isLocalDevLoop && (
1518
+ <script>
1519
+ import { initStepper } from "@/lib/dev/cockpit/stepper";
1520
+ initStepper();
1521
+ </script>
1522
+ )}
1523
+ {isLocalDevLoop && (
1524
+ <script>
1525
+ import { initAsk } from "@/lib/dev/cockpit/ask";
1526
+ initAsk();
1527
+ </script>
1528
+ )}
1529
+ {isLocalDevLoop && (
1530
+ <script>
1531
+ import { initMonitor } from "@/lib/dev/cockpit/monitor";
1532
+ initMonitor();
1533
+ </script>
1534
+ )}
1535
+ {isLocalDevLoop && (
1536
+ <script>
1537
+ import { initFiles } from "@/lib/dev/cockpit/files";
1538
+ initFiles();
1539
+ </script>
1540
+ )}
1541
+ {isLocalDevLoop && (
1542
+ <script>
1543
+ import { initClickToSource } from "@/lib/dev/cockpit/clickToSource";
1544
+ initClickToSource();
1545
+ </script>
1546
+ )}
2823
1547
  </body>
2824
1548
  </html>