@tokenoftrust/storefront-runner 2.0.2 → 2.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/apps/storefront/.astro/content.d.ts +159 -0
- package/apps/storefront/.astro/integrations/_astrojs_cloudflare/cloudflare.d.ts +1 -0
- package/apps/storefront/.astro/types.d.ts +3 -0
- package/apps/storefront/node_modules/.bin/acorn +43 -0
- package/apps/storefront/node_modules/.bin/astro +43 -0
- package/apps/storefront/node_modules/.bin/astro-check +43 -0
- package/apps/storefront/node_modules/.bin/browserslist +43 -0
- package/apps/storefront/node_modules/.bin/cf-wrangler +43 -0
- package/apps/storefront/node_modules/.bin/depcruise +43 -0
- package/apps/storefront/node_modules/.bin/depcruise-baseline +43 -0
- package/apps/storefront/node_modules/.bin/depcruise-fmt +43 -0
- package/apps/storefront/node_modules/.bin/depcruise-wrap-stream-in-html +43 -0
- package/apps/storefront/node_modules/.bin/dependency-cruise +43 -0
- package/apps/storefront/node_modules/.bin/dependency-cruiser +43 -0
- package/apps/storefront/node_modules/.bin/drizzle-kit +43 -0
- package/apps/storefront/node_modules/.bin/esbuild +43 -0
- package/apps/storefront/node_modules/.bin/jiti +43 -0
- package/apps/storefront/node_modules/.bin/playwright +43 -0
- package/apps/storefront/node_modules/.bin/playwright-core +43 -0
- package/apps/storefront/node_modules/.bin/prettier +43 -0
- package/apps/storefront/node_modules/.bin/rollup +43 -0
- package/apps/storefront/node_modules/.bin/tsc +43 -0
- package/apps/storefront/node_modules/.bin/tsserver +43 -0
- package/apps/storefront/node_modules/.bin/tsx +43 -0
- package/apps/storefront/node_modules/.bin/vite +43 -0
- package/apps/storefront/node_modules/.bin/vitest +43 -0
- package/apps/storefront/node_modules/.bin/workerd +43 -0
- package/apps/storefront/node_modules/.bin/wrangler +43 -0
- package/apps/storefront/node_modules/.bin/wrangler2 +43 -0
- package/apps/storefront/node_modules/.bin/yaml +43 -0
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_client__js.js +58 -0
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_client__js.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_n_@preact_signals.js +741 -0
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_n_@preact_signals.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_server__js.js +2549 -0
- package/apps/storefront/node_modules/.vite/deps/@astrojs_preact_server__js.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/_metadata.json +150 -0
- package/apps/storefront/node_modules/.vite/deps/astro-BIHuWFxm.js +434 -0
- package/apps/storefront/node_modules/.vite/deps/astro-BIHuWFxm.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/astro_n_aria-query.js +6390 -0
- package/apps/storefront/node_modules/.vite/deps/astro_n_aria-query.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/astro_n_axobject-query.js +2705 -0
- package/apps/storefront/node_modules/.vite/deps/astro_n_axobject-query.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/astro_n_html-escaper.js +65 -0
- package/apps/storefront/node_modules/.vite/deps/astro_n_html-escaper.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js +455 -0
- package/apps/storefront/node_modules/.vite/deps/astro_runtime_client_dev-toolbar_entrypoint__js.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/audit-DKDpcdEI.js +1535 -0
- package/apps/storefront/node_modules/.vite/deps/audit-DKDpcdEI.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/highlight-B9UoX1wa.js +48 -0
- package/apps/storefront/node_modules/.vite/deps/highlight-B9UoX1wa.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/icons-BWi2wI9n.js +42 -0
- package/apps/storefront/node_modules/.vite/deps/icons-BWi2wI9n.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/package.json +3 -0
- package/apps/storefront/node_modules/.vite/deps/preact.js +310 -0
- package/apps/storefront/node_modules/.vite/deps/preact.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/preact_compat.js +420 -0
- package/apps/storefront/node_modules/.vite/deps/preact_compat.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/preact_compat_jsx-runtime.js +4 -0
- package/apps/storefront/node_modules/.vite/deps/preact_debug.js +236 -0
- package/apps/storefront/node_modules/.vite/deps/preact_debug.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/preact_devtools.js +12 -0
- package/apps/storefront/node_modules/.vite/deps/preact_devtools.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/preact_hooks.js +192 -0
- package/apps/storefront/node_modules/.vite/deps/preact_hooks.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/preact_jsx-dev-runtime.js +3 -0
- package/apps/storefront/node_modules/.vite/deps/preact_jsx-runtime.js +92 -0
- package/apps/storefront/node_modules/.vite/deps/preact_jsx-runtime.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/preact_test-utils.js +59 -0
- package/apps/storefront/node_modules/.vite/deps/preact_test-utils.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/rolldown-runtime-BPOCksWG.js +24 -0
- package/apps/storefront/node_modules/.vite/deps/settings-BVDV0Roj.js +189 -0
- package/apps/storefront/node_modules/.vite/deps/settings-BVDV0Roj.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/static-html-DveYqwMo.js +13 -0
- package/apps/storefront/node_modules/.vite/deps/static-html-DveYqwMo.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/toolbar-B3VIHCXg.js +458 -0
- package/apps/storefront/node_modules/.vite/deps/toolbar-B3VIHCXg.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/ui-library-BK6ricUd.js +1016 -0
- package/apps/storefront/node_modules/.vite/deps/ui-library-BK6ricUd.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/window-BvxDenKN.js +32 -0
- package/apps/storefront/node_modules/.vite/deps/window-BvxDenKN.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps/xray-YFiRUU0Z.js +113 -0
- package/apps/storefront/node_modules/.vite/deps/xray-YFiRUU0Z.js.map +1 -0
- package/apps/storefront/node_modules/.vite/deps_ssr/_metadata.json +8 -0
- package/apps/storefront/node_modules/.vite/deps_ssr/package.json +3 -0
- package/apps/storefront/src/layouts/Layout.astro +1 -1
- package/apps/storefront/src/lib/apps/registryAccess.ts +45 -0
- package/apps/storefront/src/lib/auth/adminEntry.ts +4 -4
- package/apps/storefront/src/lib/shared/capability.ts +68 -0
- package/apps/storefront/src/lib/shared/developerInvites.ts +1250 -0
- package/apps/storefront/src/lib/shared/invitePreflight.ts +123 -0
- package/apps/storefront/src/lib/webhooks/deliveryEngine.ts +38 -4
- package/apps/storefront/src/lib/webhooks/emit.ts +1 -1
- package/apps/storefront/src/lib/webhooks/getDispatcher.ts +1 -1
- package/apps/storefront/src/pages/api/auth/magic-exchange.ts +1 -1
- package/apps/storefront/src/pages/index.astro +1 -1
- package/apps/storefront/src/pages/products/[handle].astro +1 -1
- package/package.json +1 -1
- package/packages/public-runtime/node_modules/.bin/tsc +43 -0
- package/packages/public-runtime/node_modules/.bin/tsserver +43 -0
- package/packages/public-runtime/node_modules/.bin/vitest +43 -0
- package/pnpm-workspace.yaml +3 -0
- package/scripts/lib/tenant-dirs.mjs +80 -0
- package/scripts/tenant/lint-embeds.mjs +180 -0
- package/scripts/tenant/materialize-tenant-content.mjs +533 -0
- package/scripts/tenant/validate.mjs +837 -0
- package/apps/storefront/scripts/build-v84-clean-clone-standalone.mjs +0 -68
- package/apps/storefront/scripts/deny-v84-clean-clone-standalone-capabilities.mjs +0 -111
- package/apps/storefront/scripts/deny-v84-dogfood-capabilities.mjs +0 -2
- package/apps/storefront/scripts/deny-v84-lifecycle-capabilities.mjs +0 -247
- package/apps/storefront/scripts/dogfood-v84-nonportable-routes.ts +0 -73
- package/apps/storefront/scripts/fixed-v84-native-install-producer.ts +0 -79
- package/apps/storefront/scripts/generated/v84-clean-clone-replay.bundle.mjs +0 -10982
- package/apps/storefront/scripts/import-v84-through-ws2.ts +0 -38
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling-dependency.ts +0 -2
- package/apps/storefront/scripts/initialize-v84-lifecycle-tooling.ts +0 -4
- package/apps/storefront/scripts/install-v84-native-through-ws1.ts +0 -185
- package/apps/storefront/scripts/prove-clone-isolation.ts +0 -506
- package/apps/storefront/scripts/replay-v84-clean-clone-pass.ts +0 -148
- package/apps/storefront/scripts/replay-v84-clean-clone-standalone.ts +0 -35
- package/apps/storefront/scripts/replay-v84-clean-clone.ts +0 -214
- package/apps/storefront/scripts/run-fixed-v84-native-install-producer.mjs +0 -18
- package/apps/storefront/scripts/run-v84-clone-isolation-proof.mjs +0 -18
- package/apps/storefront/scripts/run-v84-clone-lifecycle-proof.mjs +0 -18
- package/apps/storefront/scripts/run-v84-import-through-ws2-proof.mjs +0 -17
- package/apps/storefront/scripts/run-v84-nonportable-route-dogfood.mjs +0 -15
- package/apps/storefront/scripts/sanitize-v84-proof-environment.mjs +0 -21
- package/apps/storefront/scripts/v84CleanCloneGraphPin.ts +0 -42
- package/apps/storefront/scripts/v84CleanCloneSourceGraph.ts +0 -145
- package/apps/storefront/scripts/validate-v84-clone-lifecycle.ts +0 -18
- package/apps/storefront/src/components/admin/AdminAIWorkflowsTab.astro +0 -531
- package/apps/storefront/src/components/admin/AdminBlogDetailRail.astro +0 -878
- package/apps/storefront/src/components/admin/AdminBlogTab.astro +0 -1435
- package/apps/storefront/src/components/admin/AdminCheckoutSettingsPanel.astro +0 -226
- package/apps/storefront/src/components/admin/AdminCustomersTab.astro +0 -593
- package/apps/storefront/src/components/admin/AdminFulfillmentTab.astro +0 -465
- package/apps/storefront/src/components/admin/AdminIntegrationsTab.astro +0 -105
- package/apps/storefront/src/components/admin/AdminProductsTab.astro +0 -901
- package/apps/storefront/src/components/admin/AdminPublishTab.astro +0 -2983
- package/apps/storefront/src/components/admin/AdminReportingTab.astro +0 -435
- package/apps/storefront/src/components/admin/AdminSettingsTab.astro +0 -782
- package/apps/storefront/src/components/admin/AdminSubscriptionsTab.astro +0 -611
- package/apps/storefront/src/components/admin/orders/OrdersWorkspace.astro +0 -1223
- package/apps/storefront/src/lib/apps/adminService.ts +0 -116
- package/apps/storefront/src/lib/apps/adminSession.ts +0 -214
- package/apps/storefront/src/lib/apps/apiAuth.ts +0 -91
- package/apps/storefront/src/lib/apps/apiRoute.ts +0 -35
- package/apps/storefront/src/lib/apps/catalogMapper.ts +0 -39
- package/apps/storefront/src/lib/apps/consentConfigStore.ts +0 -228
- package/apps/storefront/src/lib/apps/gtmMigrationInstall.ts +0 -447
- package/apps/storefront/src/lib/apps/gtmMigrationReport.ts +0 -367
- package/apps/storefront/src/lib/apps/gtmMigrationTab.ts +0 -315
- package/apps/storefront/src/lib/apps/healthAggregate.ts +0 -66
- package/apps/storefront/src/lib/apps/integrationLifecycle.ts +0 -614
- package/apps/storefront/src/lib/apps/integrationLifecycleController.ts +0 -125
- package/apps/storefront/src/lib/apps/integrationReadModel.ts +0 -563
- package/apps/storefront/src/lib/apps/integrationsTab.ts +0 -547
- package/apps/storefront/src/lib/apps/integrationsView.ts +0 -106
- package/apps/storefront/src/lib/apps/managedAnalyticsService.ts +0 -599
- package/apps/storefront/src/lib/apps/tenantIntegrationMaterialization.ts +0 -170
- package/apps/storefront/src/lib/apps/tokenIssuer.ts +0 -121
- package/apps/storefront/src/lib/apps/tokenVerifier.ts +0 -148
- package/apps/storefront/src/lib/apps/v84CleanCloneReplayContext.ts +0 -16
- package/apps/storefront/src/lib/apps/v84CloneLifecycleCacheEvidence.ts +0 -105
- package/apps/storefront/src/lib/apps/v84CloneLifecycleEvidence.ts +0 -177
- package/apps/storefront/src/lib/apps/v84CloneLifecycleMaterialization.ts +0 -251
- package/apps/storefront/src/lib/apps/v84CloneLifecycleSemantics.ts +0 -241
- package/apps/storefront/src/lib/apps/v84CloneMigrationEvidence.ts +0 -211
- package/apps/storefront/src/lib/apps/v84CloneNativeInstallEvidence.ts +0 -409
- package/apps/storefront/src/lib/apps/v84NonportableCodeCoaching.ts +0 -368
- package/apps/storefront/src/lib/apps/v84NonportableRouteContract.ts +0 -188
- package/apps/storefront/src/lib/apps/v84NonportableRouteDogfood.ts +0 -291
- package/apps/storefront/src/lib/apps/ws1ManagedAnalyticsClient.ts +0 -95
- package/apps/storefront/src/lib/content-edit/applyDiff.ts +0 -91
- package/apps/storefront/src/lib/content-edit/client.ts +0 -286
- package/apps/storefront/src/lib/content-edit/index.ts +0 -14
- package/apps/storefront/src/lib/content-edit/route.ts +0 -25
- package/apps/storefront/src/lib/content-edit/types.ts +0 -19
- package/apps/storefront/src/lib/dev/cockpitStore.ts +0 -57
- package/apps/storefront/src/lib/publish/adminPublishTab.ts +0 -4625
- package/apps/storefront/src/lib/publish/apex-readiness.ts +0 -360
- package/apps/storefront/src/lib/publish/buildStream.ts +0 -269
- package/apps/storefront/src/lib/publish/collapsePersistence.ts +0 -72
- package/apps/storefront/src/lib/publish/dispatchHealth.ts +0 -267
- package/apps/storefront/src/lib/publish/domainState.ts +0 -360
- package/apps/storefront/src/lib/publish/lifecycleState.ts +0 -369
- package/apps/storefront/src/lib/publish/liveReleaseSummary.ts +0 -29
- package/apps/storefront/src/lib/publish/mergeDoctorOnFailure.ts +0 -78
- package/apps/storefront/src/lib/publish/mergeHealthBanner.ts +0 -62
- package/apps/storefront/src/lib/publish/mergeRowFindings.ts +0 -124
- package/apps/storefront/src/lib/publish/mergeSupportEscalation.ts +0 -75
- package/apps/storefront/src/lib/publish/panelPolling.ts +0 -78
- package/apps/storefront/src/lib/publish/publish-guard.ts +0 -88
- package/apps/storefront/src/lib/publish/publishFormat.ts +0 -165
- package/apps/storefront/src/lib/publish/publishHero.ts +0 -177
- package/apps/storefront/src/lib/publish/reviewRelease/changeState.ts +0 -66
- package/apps/storefront/src/lib/publish/reviewRelease/copy.ts +0 -141
- package/apps/storefront/src/lib/publish/reviewRelease/index.ts +0 -41
- package/apps/storefront/src/lib/publish/reviewRelease/permissions.ts +0 -47
- package/apps/storefront/src/lib/publish/reviewRelease/releaseState.ts +0 -42
- package/apps/storefront/src/lib/publish/reviewRelease/types.ts +0 -59
- package/apps/storefront/src/lib/publish/reviewRelease/viewModel.ts +0 -440
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/actions.ts +0 -161
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/controller.ts +0 -325
- package/apps/storefront/src/lib/publish/reviewReleaseWorkspace/render.ts +0 -241
- package/apps/storefront/src/lib/publish/shipWorkspace.ts +0 -439
- package/apps/storefront/src/lib/publish/sseRoute.ts +0 -119
- package/apps/storefront/src/lib/publish/stageBundleUpload.ts +0 -215
- package/apps/storefront/src/lib/publish/streamLogConsole.ts +0 -142
- package/apps/storefront/src/lib/publish/versionNumber.ts +0 -52
- package/apps/storefront/src/pages/admin/ops-timeline.astro +0 -420
- package/apps/storefront/src/pages/admin/review-release.astro +0 -265
- package/apps/storefront/src/pages/admin/select.astro +0 -96
- package/apps/storefront/src/pages/admin.astro +0 -570
- package/apps/storefront/src/pages/api/apps/admin/credentials/rotate.ts +0 -86
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration-install.ts +0 -174
- package/apps/storefront/src/pages/api/apps/admin/gtm-migration.ts +0 -96
- package/apps/storefront/src/pages/api/apps/admin/health.ts +0 -44
- package/apps/storefront/src/pages/api/apps/admin/install.ts +0 -125
- package/apps/storefront/src/pages/api/apps/admin/integration-lifecycle.ts +0 -118
- package/apps/storefront/src/pages/api/apps/admin/integrations.ts +0 -89
- package/apps/storefront/src/pages/api/apps/admin/list.ts +0 -26
- package/apps/storefront/src/pages/api/apps/admin/platform-adapters.ts +0 -85
- package/apps/storefront/src/pages/api/apps/admin/resume.ts +0 -79
- package/apps/storefront/src/pages/api/apps/admin/suspend.ts +0 -80
- package/apps/storefront/src/pages/api/apps/admin/uninstall.ts +0 -98
- package/apps/storefront/src/pages/api/apps/admin/update.ts +0 -126
- package/apps/storefront/src/pages/api/apps/admin/webhooks/deliveries/[deliveryId]/replay.ts +0 -70
- package/apps/storefront/src/pages/api/apps/admin/webhooks/deliveries.ts +0 -51
- package/apps/storefront/src/pages/api/apps/internal/order-forward.ts +0 -255
- package/apps/storefront/src/pages/api/apps/oauth/token.ts +0 -87
- package/apps/storefront/src/pages/api/apps/v1/attribution.ts +0 -162
- package/apps/storefront/src/pages/api/apps/v1/catalog/products/[handle].ts +0 -39
- package/apps/storefront/src/pages/api/apps/v1/catalog/products.ts +0 -49
- package/apps/storefront/src/pages/api/apps/v1/health.ts +0 -32
- package/apps/storefront/src/pages/api/apps/v1/inventory.ts +0 -59
- package/apps/storefront/src/pages/api/apps/v1/orders/[id].ts +0 -48
- package/apps/storefront/src/pages/api/apps/v1/orders.ts +0 -73
- package/apps/storefront/src/pages/api/apps/v1/reports.ts +0 -21
- package/apps/storefront/src/pages/api/apps/v1/webhooks/deliveries/[deliveryId]/replay.ts +0 -74
- package/apps/storefront/src/pages/api/apps/v1/webhooks/deliveries.ts +0 -47
- package/apps/storefront/src/pages/api/cache-purge.ts +0 -55
- package/apps/storefront/src/pages/api/compliance/tamper.ts +0 -88
- package/apps/storefront/src/pages/api/content-edit/ai-edit.ts +0 -164
- package/apps/storefront/src/pages/api/dashboard/enter-vendor.ts +0 -154
- package/apps/storefront/src/pages/api/dashboard/vendor-search.ts +0 -120
- package/apps/storefront/src/pages/api/integrations/v1/[operation].ts +0 -108
- package/apps/storefront/src/pages/api/publish/stage-bundle.ts +0 -70
- package/apps/storefront/src/pages/api/test/dev-session.ts +0 -133
- package/apps/storefront/src/pages/cockpit.astro +0 -1548
- package/apps/storefront/src/pages/dashboard/[appDomain]/apps/index.astro +0 -119
- package/apps/storefront/src/pages/dashboard/[appDomain]/index.astro +0 -105
- package/apps/storefront/src/pages/dashboard/[appDomain]/team.astro +0 -177
- package/apps/storefront/src/pages/dashboard/index.astro +0 -160
|
@@ -1,1548 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
/**
|
|
3
|
-
* /cockpit — the developer's first-run shell (what a granted developer lands on after
|
|
4
|
-
* sign-in). Renamed from /dev 2026-07-20; middleware 302-redirects old /dev links here.
|
|
5
|
-
*
|
|
6
|
-
* Deliberately NOT the operator/reconcile control plane, and — post U1 (strip-to-shell) —
|
|
7
|
-
* deliberately NOT the old marketing home either. This is the guided-cockpit's Step 1: a
|
|
8
|
-
* single dominant action (copy the one command that gets your store running) plus the
|
|
9
|
-
* developer's own tenant identity and an ⓘ explainer. The staged flow, live Monitor,
|
|
10
|
-
* in-browser file tree/diff/editor, "Stuck? Ask", and the "coming soon" shelf land on top
|
|
11
|
-
* of this shell in later chunks (U2/U3/U4, C1+) — this file is the frame they mount into.
|
|
12
|
-
*
|
|
13
|
-
* Brand: a STANDALONE document (bypasses Layout.astro) that ports the canonical /dev cockpit
|
|
14
|
-
* mockup's visual system — the tokenoftrust tenant theme (navy ink #0A2540 + trust teal
|
|
15
|
-
* #0E9F8E on cool-white #F7F9FC), Plus Jakarta Sans / Inter / JetBrains Mono, rounded cards —
|
|
16
|
-
* now with full dark-mode (system + manual toggle) and reduced-motion support. Same CSP as
|
|
17
|
-
* every storefront response; the shared typefaces are self-hosted same-origin (@fontsource,
|
|
18
|
-
* imported via `@/styles/fonts.css`), and inline style/script carry this request's nonce.
|
|
19
|
-
*
|
|
20
|
-
* The one command honours the shipped browserless-signin fastpath: a developer who reached
|
|
21
|
-
* this page from their magic link already has a session here, so the Copy button mints a
|
|
22
|
-
* single-use `tot login --code … && tot start` command (via /api/dev/cli-signin-code) and
|
|
23
|
-
* copies the whole thing — no browser sign-in in the terminal. Everyone else copies the
|
|
24
|
-
* generic `npm i -g @tokenoftrust/cli && tot start`.
|
|
25
|
-
*/
|
|
26
|
-
import { readViewerSession } from "@/lib/auth/route";
|
|
27
|
-
import { staticTenants } from "@/config/tenants";
|
|
28
|
-
import { resolvePreviewTenant } from "@/config/previewTenantLookup";
|
|
29
|
-
import { readTenantPreviewStatus, isStoreNotSetUp } from "@/lib/dev/previewStatus";
|
|
30
|
-
import { resolveDevApiBase } from "@/lib/dev/apiBase";
|
|
31
|
-
import { resolveCockpitStoreResource } from "@/lib/dev/cockpitStore";
|
|
32
|
-
import { normalizeEmailHint, redactEmailForHint } from "@/lib/privacy/emailHint";
|
|
33
|
-
// Self-hosted web fonts (audit finding 6) — same-origin @fontsource, replacing
|
|
34
|
-
// the render-blocking fonts.googleapis.com stylesheet. Covers Plus Jakarta Sans,
|
|
35
|
-
// Inter, and JetBrains Mono used by this page's inline styles.
|
|
36
|
-
import "@/styles/fonts.css";
|
|
37
|
-
|
|
38
|
-
export const prerender = false;
|
|
39
|
-
const cspNonce = Astro.locals.cspNonce;
|
|
40
|
-
const url = new URL(Astro.request.url);
|
|
41
|
-
// The tenant prefix ("" host-routed, "/<tenant>" path-routed) this cockpit is served
|
|
42
|
-
// under — set by the middleware. Every self-link + same-store API call below carries
|
|
43
|
-
// it so a `/<tenant>/cockpit` invite keeps its store across the login round-trip and
|
|
44
|
-
// the command mint (see resolveCockpitStoreResource / the basePath-scoped fetches).
|
|
45
|
-
const basePath = Astro.locals.basePath ?? "";
|
|
46
|
-
const isCredentialRecovery = url.searchParams.get("recover") === "cli";
|
|
47
|
-
const legacyRecoveryEmail = (url.searchParams.get("email") ?? "").trim();
|
|
48
|
-
const recoveryEmailHint =
|
|
49
|
-
normalizeEmailHint(url.searchParams.get("email_hint")) ??
|
|
50
|
-
redactEmailForHint(legacyRecoveryEmail) ??
|
|
51
|
-
"";
|
|
52
|
-
if (isCredentialRecovery && url.searchParams.has("email")) {
|
|
53
|
-
const canonical = new URLSearchParams(url.searchParams);
|
|
54
|
-
canonical.delete("email");
|
|
55
|
-
if (recoveryEmailHint) canonical.set("email_hint", recoveryEmailHint);
|
|
56
|
-
return Astro.redirect(
|
|
57
|
-
`${basePath}/cockpit${canonical.toString() ? `?${canonical.toString()}` : ""}`,
|
|
58
|
-
302,
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
const cockpitNext = `${basePath}/cockpit${isCredentialRecovery ? "?recover=cli" : ""}`;
|
|
62
|
-
const loginParams = new URLSearchParams({ next: cockpitNext });
|
|
63
|
-
if (recoveryEmailHint) loginParams.set("email_hint", recoveryEmailHint);
|
|
64
|
-
const loginHref = `/auth/login?${loginParams.toString()}`;
|
|
65
|
-
|
|
66
|
-
const session = await readViewerSession({ cookies: Astro.cookies });
|
|
67
|
-
const signedIn = !!session;
|
|
68
|
-
// d1 (board: d1-unmask-invite-email) — the FULL invite address to show its OWNER in the
|
|
69
|
-
// recovery notice, never a redacted hint. Prefer the validated session email; else a full
|
|
70
|
-
// `?email=` if one ever survives to render. Empty when only a redacted `email_hint` is on
|
|
71
|
-
// hand (the common case — the CLI masks the address upstream before it reaches this page),
|
|
72
|
-
// and the notice then falls back to neutral, non-redacted copy rather than re-showing the
|
|
73
|
-
// owner a masked form of their own address. redactEmailForHint/emailHint.ts stay intact for
|
|
74
|
-
// every other caller (e.g. the /auth/login hint below) — we simply stop masking HERE.
|
|
75
|
-
const recoveryEmailFull =
|
|
76
|
-
session?.email ||
|
|
77
|
-
(/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(legacyRecoveryEmail) ? legacyRecoveryEmail.toLowerCase() : "");
|
|
78
|
-
// The fastpath (browserless terminal sign-in) is gated behind the developer's own session +
|
|
79
|
-
// capability — the same gate /api/dev/cli-signin-code enforces. Mirror it here so a
|
|
80
|
-
// logged-out / no-capability visitor gets the generic command, never a broken mint.
|
|
81
|
-
const hasStoreAccess = signedIn && !!session?.capability && session.capability !== "none";
|
|
82
|
-
|
|
83
|
-
// Resolve which store this cockpit is for. The URL wins when a `/<tenant>/cockpit`
|
|
84
|
-
// request named a specific store the signed-in developer can access (the trusted
|
|
85
|
-
// host/path seam on `locals.tenant`, membership-checked) — this is the regression
|
|
86
|
-
// fix: an invite to another store than the session's first membership no longer
|
|
87
|
-
// "loses" that store. Otherwise: the developer's own first store (generic host), or
|
|
88
|
-
// — for an ANONYMOUS viewer only — the path store's branding, else `?tenant=`. Never
|
|
89
|
-
// a signed-in developer's store overridden by caller-supplied query input.
|
|
90
|
-
// See resolveCockpitStoreResource; the same resolver gates the mint + /api/dev/*.
|
|
91
|
-
const wanted = resolveCockpitStoreResource(Astro, session);
|
|
92
|
-
const tenant =
|
|
93
|
-
(wanted
|
|
94
|
-
? staticTenants.find((t) =>
|
|
95
|
-
[t.appDomain, t.tenant_id, t.canonicalDomain, ...t.domains].some(
|
|
96
|
-
(d) => d?.toLowerCase() === wanted,
|
|
97
|
-
),
|
|
98
|
-
)
|
|
99
|
-
: null) ??
|
|
100
|
-
staticTenants.find((t) => t.tenant_id === "tokenoftrust") ??
|
|
101
|
-
staticTenants[0];
|
|
102
|
-
|
|
103
|
-
const appDomain = tenant?.appDomain ?? "tokenoftrust.com";
|
|
104
|
-
|
|
105
|
-
// fix-staff-cockpit-403: `hasStoreAccess` above only proves the session carries SOME
|
|
106
|
-
// eligible capability — the federated sign-in path stamps capability:"member"
|
|
107
|
-
// unconditionally (auth/magic-exchange), so a ToT-staff viewer admitted by the
|
|
108
|
-
// loginGate staff bypass (no real store membership) passes it too, even on the home
|
|
109
|
-
// host where `wanted` never names a real store. Every /api/dev/* route instead gates
|
|
110
|
-
// on this SAME tenant actually RESOLVING (resolvePreviewTenant — see
|
|
111
|
-
// lib/dev/hostedCockpit.ts, api/dev/activity.ts) — mirror that here so we never mint a
|
|
112
|
-
// setup command or start polling for a viewer who'd just 403-loop against it.
|
|
113
|
-
const resolvedStoreTenant = hasStoreAccess && wanted ? await resolvePreviewTenant(wanted) : null;
|
|
114
|
-
// Capability-eligible, but no store resolves for this viewer/host (the staff/home-host
|
|
115
|
-
// case above). Gets its own calm "pick a store" state instead of the full flow.
|
|
116
|
-
const noResolvableStore = hasStoreAccess && !resolvedStoreTenant;
|
|
117
|
-
|
|
118
|
-
// STORE-NOT-SET-UP (#8425 invite-gated-auto-build): a developer WITH access + a
|
|
119
|
-
// resolvable store, but whose forge repo isn't provisioned yet — the invite landed
|
|
120
|
-
// before (or without) the store being set up. Offering the fastpath here would hand
|
|
121
|
-
// them a `tot login --code … && tot start` that dies at checkout on "Repo … is not
|
|
122
|
-
// provisioned" (James's first-run failure). Ask the credential-free MCP preview-status
|
|
123
|
-
// seam; only a DEFINITIVE not-provisioned flips the state (an unconfigured / can't-answer
|
|
124
|
-
// seam keeps the normal fastpath, with the CLI's own clean "not set up yet" error as the
|
|
125
|
-
// backstop — so no regression on local/dev or a transient MCP blip). This is a DISTINCT
|
|
126
|
-
// axis from noResolvableStore/noStoreAccess and gets its own honest notice below.
|
|
127
|
-
const storeNotSetUp =
|
|
128
|
-
hasStoreAccess && !noResolvableStore && isStoreNotSetUp(await readTenantPreviewStatus(appDomain));
|
|
129
|
-
|
|
130
|
-
// The gate for rendering the setup-command + activity-poll flow: eligible AND a real
|
|
131
|
-
// store to point it at AND that store is actually provisioned. Use this (never the raw
|
|
132
|
-
// hasStoreAccess) for anything that mints a command or starts the activity poll.
|
|
133
|
-
const canSetupStore = hasStoreAccess && !noResolvableStore && !storeNotSetUp;
|
|
134
|
-
|
|
135
|
-
// vc-app-binding safety-net (08-18, §5): a BROWSER-FIRST developer reached this page
|
|
136
|
-
// via the storefront's own sign-in (a tot_session cookie) without ever crossing an
|
|
137
|
-
// MCP binding seam (rendezvous approve / native OAuth), so `appForCaller` doesn't
|
|
138
|
-
// resolve them and the `tot start` command we're about to hand them would 403 with
|
|
139
|
-
// "No version-control app is bound." Ensure the binding now — on-behalf of the
|
|
140
|
-
// verified session identity, via the app's private_key_jwt channel. Best-effort +
|
|
141
|
-
// fail-open (never blocks render), throttled to once per browser session so it stays
|
|
142
|
-
// off the hot render path; the authority-seam binding + backfill remain the real
|
|
143
|
-
// guarantee. Gated on canSetupStore (entitled + resolvable + provisioned), the exact
|
|
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.
|
|
150
|
-
if (canSetupStore && session && !Astro.cookies.has("tot_vc_bound")) {
|
|
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) {
|
|
158
|
-
// Throttle only on a real success — a fail-open miss retries on the next load.
|
|
159
|
-
Astro.cookies.set("tot_vc_bound", "1", {
|
|
160
|
-
path: "/",
|
|
161
|
-
httpOnly: true,
|
|
162
|
-
sameSite: "lax",
|
|
163
|
-
secure: true,
|
|
164
|
-
maxAge: 3600, // re-check hourly so a lost/revoked binding self-heals
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
// Signed in, but either no developer capability on any store OR (staff bypass) no store
|
|
170
|
-
// resolves for this viewer/host — an honest recovery state (not a 401): show what to
|
|
171
|
-
// do, not the fastpath. NOTE: storeNotSetUp is deliberately NOT folded in here — it is
|
|
172
|
-
// its own state with its own notice, so this stays computed from resolvable-store only.
|
|
173
|
-
const noStoreAccess = signedIn && (!hasStoreAccess || noResolvableStore);
|
|
174
|
-
// The generic command shown (and copied) when we can't mint a personalised one.
|
|
175
|
-
// Pinned to the EXACT CLI version this build was cut alongside (`@tokenoftrust/cli@<version>`,
|
|
176
|
-
// astro.config Vite define) so the paste installs precisely the CLI this cockpit expects —
|
|
177
|
-
// never a floating dist-tag the terminal resolves. Used in the "prefer the steps" hint +
|
|
178
|
-
// (env-derived) the minted fastpath command.
|
|
179
|
-
const cliInstallSpec = __CLI_INSTALL_SPEC__;
|
|
180
|
-
|
|
181
|
-
// isLocalDevLoop === "the STOREFRONT app itself is running under `astro dev`" (a
|
|
182
|
-
// storefront engineer's local rig) — NOT the invited developer's dev loop (that's
|
|
183
|
-
// `tot dev`, the runner, which excludes this page entirely). It gates ONLY the
|
|
184
|
-
// local astro-dev cockpit — the Monitor/loopbar, in-browser Files editor, live
|
|
185
|
-
// preview + click-to-source, and the local stepper — all of which read the
|
|
186
|
-
// /__tot/* dev-plugins that exist ONLY under `astro dev`. import.meta.env.DEV is
|
|
187
|
-
// statically replaced with false in the production Cloudflare Worker build, so all
|
|
188
|
-
// of that is tree-shaken away there. Nothing that should reach a real developer on
|
|
189
|
-
// the deployed panel may hang off this flag — hosted product surface is either
|
|
190
|
-
// unconditional (see the "coming soon" shelf) or gated on `hostedActivity` below.
|
|
191
|
-
const isLocalDevLoop = import.meta.env.DEV;
|
|
192
|
-
|
|
193
|
-
// hostedActivity === the deployed hosted panel's activity-fed cockpit — the actual
|
|
194
|
-
// product surface the invited developer sees. It renders on the Worker (never under
|
|
195
|
-
// `astro dev`) for a signed-in developer with capability, and reflects what they're
|
|
196
|
-
// doing in their LOCAL dev loop via the activity bridge (POST /api/dev/activity →
|
|
197
|
-
// polled here). This is the "magic": the developer watches their own local rig work
|
|
198
|
-
// on our hosted surface. It is NOT a port of the local-only Monitor/Files/preview
|
|
199
|
-
// panels (those depend on /__tot/* endpoints that exist only under `astro dev`;
|
|
200
|
-
// bringing those hosted is cp-tree/cp-file/cp-preview, tracked separately).
|
|
201
|
-
const hostedActivity = !isLocalDevLoop && canSetupStore;
|
|
202
|
-
|
|
203
|
-
// cp-env (cloud-dev-cockpit Phase 0): the file-tree/viewer/diff + click-to-source
|
|
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
|
|
206
|
-
// /api/dev/* Astro API routes, landing incrementally (cp-tree/cp-file/cp-write/cp-diff/
|
|
207
|
-
// cp-locate — separate cards). Resolving it here, unconditionally, means those cards
|
|
208
|
-
// don't each have to invent the same env check; on isLocalDevLoop this is a no-op
|
|
209
|
-
// (resolves to the unchanged /__tot base). See decision architecture-environment-aware-cockpit.
|
|
210
|
-
const apiBase = resolveDevApiBase(isLocalDevLoop, basePath);
|
|
211
|
-
|
|
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>.
|
|
217
|
-
---
|
|
218
|
-
|
|
219
|
-
<html lang="en">
|
|
220
|
-
<head>
|
|
221
|
-
<meta charset="utf-8" />
|
|
222
|
-
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
223
|
-
<meta name="robots" content="noindex, nofollow" />
|
|
224
|
-
<title>Developer Cockpit — {appDomain}</title>
|
|
225
|
-
{/* cockpit-theme-persist: seed data-theme from the STORED choice before the stylesheet/
|
|
226
|
-
style below ever paint, so a reload of a manually-toggled theme doesn't flash the OS
|
|
227
|
-
default first. Runs before the theme toggle's own script (end of body) exists — it
|
|
228
|
-
only ever READS here; the toggle is what writes. No stored choice → leave the
|
|
229
|
-
attribute unset, so the `@media (prefers-color-scheme)` block keeps deciding. */}
|
|
230
|
-
<script nonce={cspNonce} is:inline>
|
|
231
|
-
(function () {
|
|
232
|
-
try {
|
|
233
|
-
var v = localStorage.getItem("tot.theme");
|
|
234
|
-
if (v === "dark" || v === "light") document.documentElement.setAttribute("data-theme", v);
|
|
235
|
-
} catch (e) {}
|
|
236
|
-
})();
|
|
237
|
-
</script>
|
|
238
|
-
{/* Web fonts are self-hosted via `@/styles/fonts.css` (imported above). */}
|
|
239
|
-
<style nonce={cspNonce}>
|
|
240
|
-
:root {
|
|
241
|
-
--bg: #f7f9fc; --bg-2: #eef3f9; --surface: #ffffff;
|
|
242
|
-
--ink: #0a2540; --ink-2: #33415b; --muted: #55637a; --faint: #8593a8;
|
|
243
|
-
--accent: #0e9f8e; --accent-ink: #0b7a6d; --accent-wash: #e7f6f4; --accent-line: #b8e6df;
|
|
244
|
-
--border: #e3e8ef; --border-2: #eef2f7;
|
|
245
|
-
--warn: #c23b3b; --warn-wash: #fbecec; --warn-line: #f0cccc;
|
|
246
|
-
--amber: #b7791f; --amber-wash: #fdf6e7; --amber-line: #f0dcae;
|
|
247
|
-
|
|
248
|
-
--term: #0a1a2f; --term-2: #071426; --term-fg: #dbe6f2; --term-dim: #5f7189;
|
|
249
|
-
--term-prompt: #2dd4bf; --term-ok: #34d399; --term-line: #14263f;
|
|
250
|
-
|
|
251
|
-
--r-sm: 6px; --r-md: 10px; --r-lg: 16px; --r-xl: 22px; --r-full: 999px;
|
|
252
|
-
--sh-sm: 0 1px 2px rgba(10,37,64,.06), 0 2px 6px -2px rgba(10,37,64,.08);
|
|
253
|
-
--sh-md: 0 8px 28px -10px rgba(10,37,64,.16);
|
|
254
|
-
--sh-lg: 0 28px 64px -26px rgba(10,37,64,.28);
|
|
255
|
-
|
|
256
|
-
/* Guided-flow motion tokens (Steps 1–3 choreography). One place to retune
|
|
257
|
-
the whole open/collapse/reveal feel — a future reskin changes the motion
|
|
258
|
-
here, not rule-by-rule. Theme-independent, so declared once. */
|
|
259
|
-
--flow-ease: cubic-bezier(.2, .9, .3, 1);
|
|
260
|
-
--flow-dur: .42s;
|
|
261
|
-
--flow-rise: 8px; /* how far a newly-revealed block settles in from */
|
|
262
|
-
|
|
263
|
-
--display: "Plus Jakarta Sans", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
264
|
-
--body: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
|
|
265
|
-
--mono: "JetBrains Mono", ui-monospace, "SF Mono", "Cascadia Code", Menlo, monospace;
|
|
266
|
-
color-scheme: light;
|
|
267
|
-
}
|
|
268
|
-
@media (prefers-color-scheme: dark) {
|
|
269
|
-
:root {
|
|
270
|
-
--bg: #071320; --bg-2: #0a1a2b; --surface: #0e2134;
|
|
271
|
-
--ink: #eaf1f8; --ink-2: #bdccdc; --muted: #90a3b7; --faint: #6c8098;
|
|
272
|
-
--accent: #2fd4bf; --accent-ink: #7fe7da; --accent-wash: #0f2e30; --accent-line: #1e514b;
|
|
273
|
-
--border: #1b3348; --border-2: #16293c;
|
|
274
|
-
--warn: #f2857f; --warn-wash: #2a1414; --warn-line: #5a2626;
|
|
275
|
-
--amber: #f0c56b; --amber-wash: #2a2110; --amber-line: #5a481f;
|
|
276
|
-
color-scheme: dark;
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
:root[data-theme="dark"] {
|
|
280
|
-
--bg: #071320; --bg-2: #0a1a2b; --surface: #0e2134;
|
|
281
|
-
--ink: #eaf1f8; --ink-2: #bdccdc; --muted: #90a3b7; --faint: #6c8098;
|
|
282
|
-
--accent: #2fd4bf; --accent-ink: #7fe7da; --accent-wash: #0f2e30; --accent-line: #1e514b;
|
|
283
|
-
--border: #1b3348; --border-2: #16293c;
|
|
284
|
-
--warn: #f2857f; --warn-wash: #2a1414; --warn-line: #5a2626;
|
|
285
|
-
--amber: #f0c56b; --amber-wash: #2a2110; --amber-line: #5a481f;
|
|
286
|
-
color-scheme: dark;
|
|
287
|
-
}
|
|
288
|
-
:root[data-theme="light"] {
|
|
289
|
-
--bg: #f7f9fc; --bg-2: #eef3f9; --surface: #ffffff;
|
|
290
|
-
--ink: #0a2540; --ink-2: #33415b; --muted: #55637a; --faint: #8593a8;
|
|
291
|
-
--accent: #0e9f8e; --accent-ink: #0b7a6d; --accent-wash: #e7f6f4; --accent-line: #b8e6df;
|
|
292
|
-
--border: #e3e8ef; --border-2: #eef2f7;
|
|
293
|
-
--warn: #c23b3b; --warn-wash: #fbecec; --warn-line: #f0cccc;
|
|
294
|
-
--amber: #b7791f; --amber-wash: #fdf6e7; --amber-line: #f0dcae;
|
|
295
|
-
color-scheme: light;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
* { box-sizing: border-box; }
|
|
299
|
-
html { -webkit-text-size-adjust: 100%; }
|
|
300
|
-
body {
|
|
301
|
-
margin: 0;
|
|
302
|
-
background:
|
|
303
|
-
radial-gradient(50rem 28rem at 100% -8%, var(--accent-wash) 0%, transparent 55%),
|
|
304
|
-
var(--bg);
|
|
305
|
-
background-repeat: no-repeat;
|
|
306
|
-
color: var(--ink-2); font-family: var(--body); font-size: 16px; line-height: 1.6;
|
|
307
|
-
letter-spacing: -.011em; -webkit-font-smoothing: antialiased;
|
|
308
|
-
padding: 0 1.25rem 6rem;
|
|
309
|
-
}
|
|
310
|
-
a { color: var(--accent-ink); }
|
|
311
|
-
a:focus-visible, button:focus-visible, summary:focus-visible {
|
|
312
|
-
outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px;
|
|
313
|
-
}
|
|
314
|
-
.wrap { max-width: 58rem; margin: 0 auto; }
|
|
315
|
-
|
|
316
|
-
/* ---- top bar (tenant identity) ---- */
|
|
317
|
-
.topbar {
|
|
318
|
-
display: flex; align-items: center; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
|
|
319
|
-
padding: 1.15rem 0 1rem; border-bottom: 1px solid var(--border); margin-bottom: 1.8rem;
|
|
320
|
-
}
|
|
321
|
-
.brand { display: flex; align-items: center; gap: .65rem; }
|
|
322
|
-
.brand .mark {
|
|
323
|
-
width: 30px; height: 30px; border-radius: 9px;
|
|
324
|
-
background: linear-gradient(150deg, var(--accent), var(--accent-ink));
|
|
325
|
-
display: grid; place-items: center; color: #fff; flex: none;
|
|
326
|
-
box-shadow: 0 3px 10px -3px rgba(14,159,142,.6);
|
|
327
|
-
}
|
|
328
|
-
.brand .mark svg { width: 17px; height: 17px; }
|
|
329
|
-
.brand .who { display: flex; flex-direction: column; line-height: 1.15; }
|
|
330
|
-
.brand .dom { font-family: var(--display); font-weight: 700; color: var(--ink); font-size: .98rem; letter-spacing: -.01em; }
|
|
331
|
-
.brand .sub { font-family: var(--mono); font-size: .7rem; color: var(--faint); text-transform: uppercase; letter-spacing: .12em; }
|
|
332
|
-
.topbar .right { display: flex; align-items: center; gap: .9rem; font-family: var(--mono); font-size: .78rem; color: var(--muted); }
|
|
333
|
-
.cockpit-lede { margin: -0.6rem 0 1.6rem; font-family: var(--body); font-size: .92rem; color: var(--muted); max-width: 60ch; }
|
|
334
|
-
.cockpit-lede strong { color: var(--ink); font-weight: 600; }
|
|
335
|
-
.topbar .right .sess { display: inline-flex; align-items: center; gap: .45rem; }
|
|
336
|
-
.topbar .right .sess::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 3px var(--accent-wash); }
|
|
337
|
-
.themetog {
|
|
338
|
-
font-family: var(--mono); font-size: .72rem; background: var(--surface); color: var(--muted);
|
|
339
|
-
border: 1px solid var(--border); border-radius: var(--r-full); padding: .3rem .7rem; cursor: pointer;
|
|
340
|
-
}
|
|
341
|
-
.themetog:hover { color: var(--ink); border-color: var(--accent-line); }
|
|
342
|
-
|
|
343
|
-
/* ---- recovery notice (signed out / no store access) ---- */
|
|
344
|
-
.notice {
|
|
345
|
-
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
|
|
346
|
-
box-shadow: var(--sh-sm); padding: 1.15rem 1.4rem; margin: 0 0 1.6rem;
|
|
347
|
-
display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .7rem 1.4rem;
|
|
348
|
-
}
|
|
349
|
-
.notice p { margin: 0; color: var(--ink-2); font-size: .95rem; }
|
|
350
|
-
.notice a.action {
|
|
351
|
-
flex: none; display: inline-flex; align-items: center; gap: .4rem; background: var(--accent);
|
|
352
|
-
color: #fff; text-decoration: none; font: 700 .92rem/1 var(--display); padding: .66rem 1.15rem;
|
|
353
|
-
border-radius: var(--r-full); transition: background .18s ease, transform .12s ease;
|
|
354
|
-
}
|
|
355
|
-
.notice a.action:hover { background: var(--accent-ink); transform: translateY(-1px); }
|
|
356
|
-
|
|
357
|
-
/* ---- hosted activity ledger (hosted-dev-activity-bridge) — reskinned to the
|
|
358
|
-
signal-rail "activity" look: a compact 3-col table (time | action | file)
|
|
359
|
-
with the newest row accented (.is-new). ---- */
|
|
360
|
-
.hosted-activity {
|
|
361
|
-
margin-top: .4rem; background: var(--surface); border: 1px solid var(--border);
|
|
362
|
-
border-radius: var(--r-lg); box-shadow: var(--sh-sm); padding: 1rem 1.2rem .9rem; margin-bottom: 1.6rem;
|
|
363
|
-
}
|
|
364
|
-
.ha-head { display: flex; align-items: baseline; gap: .6rem; flex-wrap: wrap; margin-bottom: .5rem; }
|
|
365
|
-
.ha-head h2 { font-family: var(--display); font-size: .95rem; font-weight: 700; color: var(--ink); margin: 0; }
|
|
366
|
-
.ha-sub { font-size: .78rem; color: var(--muted); }
|
|
367
|
-
.ha-sub code { font-family: var(--mono); }
|
|
368
|
-
/* Activity TABLE — three real columns (time | action | file) with row spacing,
|
|
369
|
-
so the fields are always visually separated and never run together. */
|
|
370
|
-
.ha-table { width: 100%; border-collapse: separate; border-spacing: 0 .4rem; margin: 0; }
|
|
371
|
-
.ha-row > td {
|
|
372
|
-
padding: .55rem .8rem; background: var(--bg-2); font-size: .85rem; color: var(--ink-2);
|
|
373
|
-
vertical-align: baseline;
|
|
374
|
-
}
|
|
375
|
-
.ha-row > td:first-child { border-radius: var(--r-md) 0 0 var(--r-md); }
|
|
376
|
-
.ha-row > td:last-child { border-radius: 0 var(--r-md) var(--r-md) 0; }
|
|
377
|
-
.ha-row.is-new > td { background: var(--accent-wash); }
|
|
378
|
-
td.ha-when { font-family: var(--mono); font-size: .74rem; color: var(--faint); width: 6.8rem; white-space: nowrap; font-variant-numeric: tabular-nums; }
|
|
379
|
-
td.ha-desc { font-weight: 600; white-space: nowrap; width: 1%; padding-right: 1.1rem; }
|
|
380
|
-
td.ha-file { font-family: var(--mono); font-size: .74rem; color: var(--muted); max-width: 0; width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
381
|
-
.ha-toggle {
|
|
382
|
-
margin-top: .5rem; background: none; border: none; padding: .3rem .1rem; cursor: pointer;
|
|
383
|
-
font: 600 .8rem var(--body); color: var(--accent-ink); text-decoration: underline;
|
|
384
|
-
}
|
|
385
|
-
.ha-toggle:hover { color: var(--accent); }
|
|
386
|
-
/* Encouragement banner shown atop Step 2 when you arrive. */
|
|
387
|
-
.ha-encourage {
|
|
388
|
-
display: flex; align-items: center; gap: .6rem; margin: 0 0 1rem;
|
|
389
|
-
padding: .7rem .9rem; border-radius: var(--r-md);
|
|
390
|
-
background: var(--accent-wash); border: 1px solid var(--accent-line); color: var(--accent-ink);
|
|
391
|
-
font-weight: 600; font-size: .92rem;
|
|
392
|
-
}
|
|
393
|
-
.ha-encourage .chk { flex: none; }
|
|
394
|
-
.ha-empty { margin: .2rem 0 .3rem; font-size: .84rem; color: var(--muted); }
|
|
395
|
-
.ha-empty code { font-family: var(--mono); }
|
|
396
|
-
|
|
397
|
-
/* ---- bridge strip (replaces the big live-dev-link card): the rail's power
|
|
398
|
-
source, slimmed to a strip. Fed by the SAME activity poll's runtime. ---- */
|
|
399
|
-
.bridge {
|
|
400
|
-
display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
|
|
401
|
-
border: 1px solid var(--accent-line); background: var(--surface);
|
|
402
|
-
border-radius: var(--r-lg); padding: .7rem 1rem; margin-bottom: 1.4rem;
|
|
403
|
-
box-shadow: var(--sh-sm);
|
|
404
|
-
}
|
|
405
|
-
.bridge[data-live="1"] { border-color: var(--accent); animation: bridgeGlow 2.6s ease-in-out infinite; }
|
|
406
|
-
@keyframes bridgeGlow {
|
|
407
|
-
0%, 100% { box-shadow: 0 0 0 1px var(--accent-line), 0 0 12px -4px rgba(14,159,142,.3); }
|
|
408
|
-
50% { box-shadow: 0 0 0 2px var(--accent), 0 0 22px -2px rgba(14,159,142,.45); }
|
|
409
|
-
}
|
|
410
|
-
.bridge[data-live="0"] { border-color: var(--amber-line); background: var(--amber-wash); }
|
|
411
|
-
.beacon { width: 10px; height: 10px; border-radius: 50%; flex: none; background: var(--accent); }
|
|
412
|
-
.bridge[data-live="1"] .beacon { animation: beat 1.6s ease-in-out infinite; }
|
|
413
|
-
.bridge[data-live="0"] .beacon { background: var(--amber); }
|
|
414
|
-
.b-txt { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
|
|
415
|
-
.b-kicker { font-family: var(--mono); font-size: .66rem; text-transform: uppercase; letter-spacing: .13em; color: var(--accent-ink); }
|
|
416
|
-
.bridge[data-live="0"] .b-kicker { color: var(--amber); }
|
|
417
|
-
.b-url { font-family: var(--mono); font-size: .9rem; font-weight: 600; color: var(--accent-ink); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
418
|
-
.b-url:hover { text-decoration: underline; }
|
|
419
|
-
.bridge[data-live="0"] .b-url { color: var(--muted); }
|
|
420
|
-
.b-diag { margin-left: auto; font-family: var(--mono); font-size: .68rem; color: var(--faint); white-space: nowrap; }
|
|
421
|
-
.b-go {
|
|
422
|
-
flex: none; display: inline-flex; align-items: center; gap: .35rem;
|
|
423
|
-
font: 700 .8rem/1 var(--display); background: var(--accent); color: #fff; text-decoration: none;
|
|
424
|
-
padding: .5rem .95rem; border-radius: var(--r-full); transition: background .12s, transform .12s;
|
|
425
|
-
}
|
|
426
|
-
.b-go:hover { background: var(--accent-ink); transform: translateY(-1px); }
|
|
427
|
-
.b-hint { flex-basis: 100%; margin: 0; font-size: .8rem; color: var(--amber); }
|
|
428
|
-
.b-hint code { font-family: var(--mono); }
|
|
429
|
-
/* Obstacle lane (v2 §6): a failed `tot start` — an ERROR, so it uses the
|
|
430
|
-
existing --warn (red) token triple (NO new palette values), which also sets
|
|
431
|
-
it apart from the amber "went quiet". Content is a stacked block, so the
|
|
432
|
-
beacon aligns to the top rather than centering on a tall card. */
|
|
433
|
-
.bridge[data-live="obstacle"] { align-items: flex-start; border-color: var(--warn-line); background: var(--warn-wash); }
|
|
434
|
-
.bridge[data-live="obstacle"] .beacon { background: var(--warn); }
|
|
435
|
-
.b-obstacle { flex: 1 1 100%; display: flex; flex-direction: column; gap: .3rem; min-width: 0; }
|
|
436
|
-
.b-ob-title { color: var(--warn); }
|
|
437
|
-
.b-ob-title::before { content: "\26A0 "; } /* ⚠ warning glyph */
|
|
438
|
-
.b-ob-msg { margin: 0; font-size: .9rem; font-weight: 600; color: var(--ink); }
|
|
439
|
-
.b-ob-fix { margin: 0; font-size: .82rem; color: var(--muted); line-height: 1.5; }
|
|
440
|
-
.b-ob-lead { color: var(--warn); font-weight: 700; }
|
|
441
|
-
.b-ob-cmd { font-family: var(--mono); font-size: .8rem; background: var(--bg-2); color: var(--ink); padding: .1rem .35rem; border-radius: var(--r-sm); }
|
|
442
|
-
/* These three set their own `display`, which (author > UA) would beat the UA
|
|
443
|
-
[hidden]{display:none} — so the script's `.hidden` toggles silently no-op on
|
|
444
|
-
them. Re-assert [hidden] at higher specificity so hidden means hidden: the
|
|
445
|
-
obstacle block and the live-strip text swap cleanly, AND the quiet-state CTA
|
|
446
|
-
(b-go) actually hides. */
|
|
447
|
-
.b-txt[hidden], .b-go[hidden], .b-obstacle[hidden] { display: none; }
|
|
448
|
-
@media (prefers-reduced-motion: reduce) { .bridge[data-live="1"], .beacon, .b-go { animation: none !important; transition: none; } }
|
|
449
|
-
@media (max-width: 34rem) { .b-diag { display: none; } }
|
|
450
|
-
|
|
451
|
-
/* ---- the signal rail: breadcrumb + done-bars merged into stations on one
|
|
452
|
-
spine. NO single .rail wrapper — each station is a top-level element and
|
|
453
|
-
carries its own liveness flow via data-attributes on its .seg. ---- */
|
|
454
|
-
.station { display: grid; grid-template-columns: 2.4rem 1fr; column-gap: 1rem; position: relative; }
|
|
455
|
-
.station .nodecol { display: flex; flex-direction: column; align-items: center; }
|
|
456
|
-
.node {
|
|
457
|
-
width: 2.1rem; height: 2.1rem; border-radius: 50%; flex: none; z-index: 1;
|
|
458
|
-
display: grid; place-items: center; position: relative;
|
|
459
|
-
font-family: var(--mono); font-weight: 600; font-size: .85rem;
|
|
460
|
-
background: var(--surface); border: 1.5px solid var(--border); color: var(--faint);
|
|
461
|
-
transition: background .3s, border-color .3s, color .3s;
|
|
462
|
-
}
|
|
463
|
-
.node svg { width: 15px; height: 15px; display: none; }
|
|
464
|
-
.station[data-st="done"] .node { background: var(--accent); border-color: var(--accent); color: #fff; }
|
|
465
|
-
.station[data-st="done"] .node svg { display: block; }
|
|
466
|
-
.station[data-st="done"] .node .num { display: none; }
|
|
467
|
-
.station[data-st="open"] .node { border-color: var(--accent); color: var(--accent-ink); box-shadow: 0 0 0 4px var(--accent-wash); }
|
|
468
|
-
.station[data-st="next"] .node { border-color: var(--accent-line); color: var(--accent-ink); background: var(--accent-wash); animation: nodeBreathe 1.8s ease-in-out infinite; }
|
|
469
|
-
@keyframes nodeBreathe {
|
|
470
|
-
0%, 100% { box-shadow: 0 0 0 3px var(--accent-wash); }
|
|
471
|
-
50% { box-shadow: 0 0 0 8px var(--accent-wash); }
|
|
472
|
-
}
|
|
473
|
-
.station[data-st="locked"] .node { opacity: .55; }
|
|
474
|
-
/* the segment of rail below each node — liveness flows down each lit segment */
|
|
475
|
-
.seg { flex: 1; width: 2px; min-height: 1.2rem; background: var(--border); margin: 4px 0; border-radius: 1px; }
|
|
476
|
-
#st-next .seg { visibility: hidden; } /* last station: no line below */
|
|
477
|
-
.seg[data-lit="1"] { background: var(--accent-line); }
|
|
478
|
-
.seg[data-lit="1"][data-flow="1"] {
|
|
479
|
-
background: repeating-linear-gradient(180deg, var(--accent) 0 5px, var(--accent-line) 5px 11px);
|
|
480
|
-
animation: flow 1.1s linear infinite;
|
|
481
|
-
}
|
|
482
|
-
@keyframes flow { to { background-position: 0 11px; } }
|
|
483
|
-
.seg[data-lit="1"][data-flow="0"] {
|
|
484
|
-
background: repeating-linear-gradient(180deg, var(--faint) 0 4px, transparent 4px 10px);
|
|
485
|
-
opacity: .5;
|
|
486
|
-
}
|
|
487
|
-
.s-body { padding-bottom: 1.5rem; min-width: 0; }
|
|
488
|
-
|
|
489
|
-
/* spark burst on celebration — radiates from the node itself */
|
|
490
|
-
.spark { position: absolute; left: 50%; top: 50%; width: 6px; height: 6px; border-radius: 2px; pointer-events: none; opacity: 0; }
|
|
491
|
-
.station.celebrating .spark { animation: sparkOut .9s ease-out forwards; }
|
|
492
|
-
@keyframes sparkOut {
|
|
493
|
-
0% { opacity: 1; transform: translate(-50%, -50%); }
|
|
494
|
-
100% { opacity: 0; transform: translate(calc(-50% + var(--dx) * 34px), calc(-50% + var(--dy) * 34px)) rotate(140deg); }
|
|
495
|
-
}
|
|
496
|
-
.station.celebrating .node { animation: stamp .5s cubic-bezier(.2,.9,.3,1.4); }
|
|
497
|
-
@keyframes stamp { 0% { transform: scale(.7); } 60% { transform: scale(1.18); } 100% { transform: scale(1); } }
|
|
498
|
-
|
|
499
|
-
/* collapsed done row — lives IN sequence on the rail (replaces the done-bars) */
|
|
500
|
-
.s-done {
|
|
501
|
-
display: flex; align-items: baseline; gap: .6rem; width: 100%; text-align: left;
|
|
502
|
-
background: none; border: none; border-radius: var(--r-sm); padding: .35rem .4rem .35rem 0; cursor: pointer;
|
|
503
|
-
font-family: var(--body);
|
|
504
|
-
}
|
|
505
|
-
.s-done .lab { font-family: var(--display); font-weight: 600; color: var(--ink-2); font-size: .92rem; }
|
|
506
|
-
.s-done .meta { font-family: var(--mono); font-size: .74rem; color: var(--faint); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
507
|
-
.s-done .reopen { margin-left: auto; flex: none; font-family: var(--mono); font-size: .7rem; color: var(--accent-ink); opacity: 0; transition: opacity .12s; }
|
|
508
|
-
.s-done:hover .reopen, .s-done:focus-visible .reopen { opacity: 1; }
|
|
509
|
-
.s-done:hover .lab { color: var(--ink); }
|
|
510
|
-
|
|
511
|
-
/* locked label */
|
|
512
|
-
.s-locked { padding: .38rem 0; font-family: var(--display); font-weight: 600; font-size: .92rem; color: var(--faint); }
|
|
513
|
-
.s-locked .sub { display: block; font: 400 .76rem/1.4 var(--body); color: var(--faint); }
|
|
514
|
-
|
|
515
|
-
/* next-up teaser — sits at its own station on the rail */
|
|
516
|
-
.s-tease {
|
|
517
|
-
background: var(--accent-wash); border: 1px solid var(--accent-line); border-radius: var(--r-lg);
|
|
518
|
-
padding: 1rem 1.15rem; box-shadow: var(--sh-sm); animation: flowRise var(--flow-dur) var(--flow-ease);
|
|
519
|
-
}
|
|
520
|
-
.t-kick { font-family: var(--mono); font-size: .66rem; text-transform: uppercase; letter-spacing: .13em; color: var(--accent-ink); }
|
|
521
|
-
.s-tease h3 { font-family: var(--display); font-size: 1.1rem; font-weight: 700; color: var(--ink); margin: .3rem 0 .25rem; letter-spacing: -.02em; }
|
|
522
|
-
.s-tease p { font-size: .88rem; color: var(--ink-2); margin: 0 0 .9rem; max-width: 46ch; }
|
|
523
|
-
.s-tease p code { font-family: var(--mono); font-size: .86em; background: var(--bg-2); border-radius: var(--r-sm); padding: .06rem .3rem; color: var(--ink); }
|
|
524
|
-
.t-cta {
|
|
525
|
-
display: inline-flex; align-items: center; gap: .5rem;
|
|
526
|
-
font: 700 .88rem/1 var(--display); background: var(--accent); color: #fff; border: none;
|
|
527
|
-
padding: .66rem 1.15rem; border-radius: var(--r-full); cursor: pointer;
|
|
528
|
-
box-shadow: 0 8px 22px -10px rgba(14,159,142,.8); transition: background .12s, transform .12s;
|
|
529
|
-
animation: ctaPulse 1.8s ease-in-out infinite;
|
|
530
|
-
}
|
|
531
|
-
.t-cta:hover { background: var(--accent-ink); transform: translateY(-1px); }
|
|
532
|
-
|
|
533
|
-
/* the Step-1 "went quiet" note, ported from the mockup (sits above the re-inflated Step 1) */
|
|
534
|
-
.quiet-note { display: flex; gap: .5rem; align-items: baseline; margin: 0 0 1rem; padding: .6rem .8rem; border-radius: var(--r-md); background: var(--amber-wash); border: 1px solid var(--amber-line); color: var(--amber); font-size: .86rem; }
|
|
535
|
-
.quiet-note code { font-family: var(--mono); }
|
|
536
|
-
|
|
537
|
-
@media (prefers-reduced-motion: reduce) {
|
|
538
|
-
.station[data-st="next"] .node, .seg[data-lit="1"][data-flow="1"],
|
|
539
|
-
.t-cta, .s-tease, .station.celebrating .node, .station.celebrating .spark { animation: none !important; }
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
/* ---- Step 2 "lay it beside your editor" tip ---- */
|
|
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; }
|
|
546
|
-
|
|
547
|
-
/* ---- Guided accordion: a step body animates IN when opened (collapse is instant
|
|
548
|
-
+ synchronous so the flow can't get stuck). Shared with isLocalDevLoop —
|
|
549
|
-
left on its original timing so that variant is untouched. ---- */
|
|
550
|
-
.stage.expanding { animation: stageExpand .42s ease; }
|
|
551
|
-
@keyframes stageExpand {
|
|
552
|
-
from { opacity: 0; transform: translateY(-8px); }
|
|
553
|
-
to { opacity: 1; transform: none; }
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
/* Hosted guided-flow reveal (flowRise): the activity ledger settles into place
|
|
557
|
-
with the same motion a step body uses to open — so moving between Steps 1–3
|
|
558
|
-
reads as a single, deliberate transition. Scoped to the hosted id, so the
|
|
559
|
-
shared isLocalDevLoop variant is left untouched. */
|
|
560
|
-
#hostedActivity {
|
|
561
|
-
animation: flowRise var(--flow-dur) var(--flow-ease);
|
|
562
|
-
}
|
|
563
|
-
@keyframes flowRise {
|
|
564
|
-
from { opacity: 0; transform: translateY(calc(var(--flow-rise) * -1)); }
|
|
565
|
-
to { opacity: 1; transform: none; }
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
/* CTA pulse — used by the rail teaser CTAs (.t-cta). */
|
|
569
|
-
@keyframes ctaPulse {
|
|
570
|
-
0%, 100% { box-shadow: 0 0 0 0 rgba(14,159,142,.5); }
|
|
571
|
-
50% { box-shadow: 0 0 0 8px rgba(14,159,142,0); }
|
|
572
|
-
}
|
|
573
|
-
@media (prefers-reduced-motion: reduce) {
|
|
574
|
-
.stage.expanding, #hostedActivity { animation: none; }
|
|
575
|
-
}
|
|
576
|
-
|
|
577
|
-
/* ---- "coming soon" dialog (Step 3 "Submit for preview") ---- */
|
|
578
|
-
.cs-dialog {
|
|
579
|
-
border: 1px solid var(--border); border-radius: var(--r-xl); padding: 0;
|
|
580
|
-
max-width: 30rem; width: calc(100% - 2rem);
|
|
581
|
-
background: var(--surface); color: var(--ink);
|
|
582
|
-
box-shadow: var(--sh-lg, 0 24px 60px -20px rgba(10,37,64,.5));
|
|
583
|
-
}
|
|
584
|
-
.cs-dialog::backdrop { background: rgba(10,37,64,.42); }
|
|
585
|
-
.cs-body { padding: 1.8rem 1.7rem; text-align: center; }
|
|
586
|
-
.cs-emoji { font-size: 2rem; display: block; margin-bottom: .5rem; }
|
|
587
|
-
.cs-body h2 { font-family: var(--display); font-size: 1.15rem; font-weight: 700; margin: 0 0 .5rem; color: var(--ink); }
|
|
588
|
-
.cs-body p { font-size: .92rem; color: var(--muted); line-height: 1.55; margin: 0 0 1.3rem; }
|
|
589
|
-
|
|
590
|
-
/* ---- Make an Edit (G2 — experimental, collapsed in-browser editor) ---- */
|
|
591
|
-
.exp-badge {
|
|
592
|
-
align-self: center; font-family: var(--mono); font-size: .6rem; font-weight: 500;
|
|
593
|
-
text-transform: uppercase; letter-spacing: .12em; color: var(--amber);
|
|
594
|
-
background: var(--amber-wash); border: 1px solid var(--amber-line);
|
|
595
|
-
border-radius: var(--r-full); padding: .18rem .55rem;
|
|
596
|
-
}
|
|
597
|
-
.reveal-btn {
|
|
598
|
-
margin-top: 1.1rem; display: inline-flex; align-items: center; gap: .5rem;
|
|
599
|
-
font: 700 .9rem/1 var(--display); background: var(--accent); color: #fff; border: none;
|
|
600
|
-
padding: .72rem 1.25rem; border-radius: var(--r-full); cursor: pointer;
|
|
601
|
-
box-shadow: 0 8px 22px -10px rgba(14,159,142,.8); transition: background .12s, transform .12s;
|
|
602
|
-
}
|
|
603
|
-
.reveal-btn:hover { background: var(--accent-ink); transform: translateY(-1px); }
|
|
604
|
-
.reveal-btn svg { width: 15px; height: 15px; }
|
|
605
|
-
@media (prefers-reduced-motion: reduce) { .reveal-btn:hover { transform: none; } }
|
|
606
|
-
|
|
607
|
-
/* ---- stage card ---- */
|
|
608
|
-
.stage {
|
|
609
|
-
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-xl);
|
|
610
|
-
box-shadow: var(--sh-md); overflow: hidden; position: relative;
|
|
611
|
-
}
|
|
612
|
-
.stage::before { content: ""; position: absolute; inset: 0 0 auto 0; height: 3px; background: linear-gradient(90deg, var(--accent), var(--accent-line)); }
|
|
613
|
-
.stage-body { padding: 1.6rem 1.6rem 1.7rem; }
|
|
614
|
-
@media (max-width: 34rem) { .stage-body { padding: 1.2rem 1.1rem 1.3rem; } }
|
|
615
|
-
.kicker {
|
|
616
|
-
font-family: var(--mono); font-size: .72rem; font-weight: 500; text-transform: uppercase;
|
|
617
|
-
letter-spacing: .15em; color: var(--accent-ink); display: inline-flex; align-items: center; gap: .5rem; margin: 0 0 .5rem;
|
|
618
|
-
}
|
|
619
|
-
.stage h1 {
|
|
620
|
-
font-family: var(--display); font-weight: 800; color: var(--ink); letter-spacing: -.03em;
|
|
621
|
-
font-size: clamp(1.5rem, 3.4vw, 1.95rem); margin: 0; text-wrap: balance;
|
|
622
|
-
display: flex; align-items: center; gap: .55rem; flex-wrap: wrap;
|
|
623
|
-
}
|
|
624
|
-
.stage .goal { margin: .55rem 0 1.3rem; color: var(--muted); max-width: 48ch; }
|
|
625
|
-
|
|
626
|
-
/* info icon + popover */
|
|
627
|
-
.info { position: relative; display: inline-flex; }
|
|
628
|
-
.info-btn {
|
|
629
|
-
width: 1.4rem; height: 1.4rem; border-radius: 50%; border: 1.5px solid var(--border);
|
|
630
|
-
background: var(--surface); color: var(--muted); font-family: var(--display); font-weight: 700;
|
|
631
|
-
font-size: .82rem; cursor: pointer; display: grid; place-items: center; line-height: 1;
|
|
632
|
-
}
|
|
633
|
-
.info-btn:hover { border-color: var(--accent-line); color: var(--accent-ink); }
|
|
634
|
-
.info-pop {
|
|
635
|
-
position: absolute; top: calc(100% + .5rem); left: 0; width: min(21rem, 78vw); z-index: 30;
|
|
636
|
-
background: var(--ink); color: #e7eef6; font-size: .82rem; line-height: 1.55;
|
|
637
|
-
border-radius: var(--r-md); padding: .85rem 1rem; box-shadow: var(--sh-lg);
|
|
638
|
-
opacity: 0; visibility: hidden; transform: translateY(-4px); transition: opacity .16s, transform .16s, visibility .16s;
|
|
639
|
-
}
|
|
640
|
-
.info-pop code { font-family: var(--mono); font-size: .78rem; color: var(--term-prompt); }
|
|
641
|
-
.info.open .info-pop { opacity: 1; visibility: visible; transform: translateY(0); }
|
|
642
|
-
@media (prefers-color-scheme: dark) { .info-pop { background: #04101c; } }
|
|
643
|
-
:root[data-theme="dark"] .info-pop { background: #04101c; }
|
|
644
|
-
:root[data-theme="light"] .info-pop { background: var(--ink); }
|
|
645
|
-
|
|
646
|
-
/* ---- terminal ---- */
|
|
647
|
-
.term {
|
|
648
|
-
background: linear-gradient(180deg, var(--term) 0%, var(--term-2) 100%);
|
|
649
|
-
border: 1px solid var(--term-line); border-radius: var(--r-lg); box-shadow: var(--sh-md);
|
|
650
|
-
overflow: hidden; font-family: var(--mono); font-size: .82rem; line-height: 1.7;
|
|
651
|
-
}
|
|
652
|
-
.term-bar { display: flex; align-items: center; gap: .5rem; padding: .6rem .85rem; border-bottom: 1px solid var(--term-line); background: rgba(255,255,255,.02); }
|
|
653
|
-
.term-bar .dots { display: flex; gap: .4rem; }
|
|
654
|
-
.term-bar .dot { width: 11px; height: 11px; border-radius: 50%; }
|
|
655
|
-
.dot.r { background: #ff5f56; } .dot.y { background: #ffbd2e; } .dot.g { background: #27c93f; }
|
|
656
|
-
.term-bar .ttl { margin-left: .4rem; font-size: .72rem; color: var(--term-dim); }
|
|
657
|
-
/* Standard "copy this code block" affordance, docked in the terminal chrome.
|
|
658
|
-
Revealed once the command is minted; its icon→check swap is the obvious,
|
|
659
|
-
inline copy confirmation (no reliance on the transient toast). */
|
|
660
|
-
.term-copy {
|
|
661
|
-
margin-left: auto; display: inline-flex; align-items: center; justify-content: center;
|
|
662
|
-
width: 30px; height: 26px; padding: 0; border-radius: var(--r-sm); cursor: pointer;
|
|
663
|
-
background: rgba(255,255,255,.04); border: 1px solid var(--term-line);
|
|
664
|
-
color: var(--term-dim); transition: color .12s, background .12s, border-color .12s;
|
|
665
|
-
}
|
|
666
|
-
.term-copy:hover { color: var(--term-fg); background: rgba(255,255,255,.09); border-color: var(--term-dim); }
|
|
667
|
-
.term-copy svg { width: 15px; height: 15px; }
|
|
668
|
-
.term-copy .ic-check { display: none; }
|
|
669
|
-
.term-copy[data-copied="1"] { color: var(--term-ok); border-color: var(--term-ok); }
|
|
670
|
-
.term-copy[data-copied="1"] .ic-copy { display: none; }
|
|
671
|
-
.term-copy[data-copied="1"] .ic-check { display: block; }
|
|
672
|
-
/* Rendezvous approval widget — the confirm-your-terminal step after pasting. */
|
|
673
|
-
.rzv { margin-top: 14px; }
|
|
674
|
-
.rzv[hidden] { display: none; }
|
|
675
|
-
.rzv-wait { display: flex; align-items: center; gap: 10px; font-size: .92rem; opacity: .8; }
|
|
676
|
-
.rzv-confirm { border: 1px solid var(--term-ok, #0e9f8e); border-radius: 12px; padding: 16px 18px; background: color-mix(in srgb, var(--term-ok, #0e9f8e) 8%, transparent); }
|
|
677
|
-
.rzv-q { margin: 0 0 6px; font-weight: 600; }
|
|
678
|
-
.rzv-fp { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 1.75rem; letter-spacing: .12em; text-align: center; margin: 8px 0 16px; }
|
|
679
|
-
.rzv-actions { display: flex; gap: 10px; flex-wrap: wrap; }
|
|
680
|
-
.rzv-actions .cta { margin: 0; }
|
|
681
|
-
.rzv-deny { background: none; border: 1px solid currentColor; opacity: .7; border-radius: 8px; padding: 8px 14px; cursor: pointer; color: inherit; font: inherit; }
|
|
682
|
-
.rzv-deny:hover { opacity: 1; }
|
|
683
|
-
.rzv-deny[disabled], .rzv-actions .cta[disabled] { opacity: .5; cursor: default; }
|
|
684
|
-
.rzv-done { margin-top: 12px; font-weight: 600; }
|
|
685
|
-
.term-body { padding: 1rem 1.05rem 1.1rem; color: var(--term-fg); white-space: pre-wrap; word-break: break-word; }
|
|
686
|
-
/* block, not inline — Astro trims the whitespace text node between the spans, so the
|
|
687
|
-
line break must be structural or the comment + command collapse onto one line. */
|
|
688
|
-
.term-body .c { display: block; color: var(--term-dim); }
|
|
689
|
-
.term-body .cmd { display: block; color: #fff; }
|
|
690
|
-
|
|
691
|
-
.cta {
|
|
692
|
-
display: inline-flex; align-items: center; justify-content: center; gap: .55rem;
|
|
693
|
-
font-family: var(--display); font-weight: 700; font-size: .95rem; background: var(--accent); color: #fff;
|
|
694
|
-
border: none; padding: .82rem 1.3rem; border-radius: var(--r-full); cursor: pointer;
|
|
695
|
-
box-shadow: 0 8px 22px -10px rgba(14,159,142,.8); transition: transform .12s, background .12s; width: 100%;
|
|
696
|
-
}
|
|
697
|
-
.cta:hover { transform: translateY(-1px); background: var(--accent-ink); }
|
|
698
|
-
.cta:disabled { opacity: .7; cursor: default; transform: none; }
|
|
699
|
-
.cta svg { width: 16px; height: 16px; }
|
|
700
|
-
.cmd-actions { margin-top: .85rem; }
|
|
701
|
-
.cmd-note { margin: .8rem 0 0; font: 500 .8rem/1.5 var(--mono); color: var(--muted); }
|
|
702
|
-
.cmd-note em { color: var(--ink-2); font-style: normal; font-weight: 600; }
|
|
703
|
-
.cmd-steps { margin: 1rem 0 0; font-size: .85rem; color: var(--muted); }
|
|
704
|
-
.cmd-steps code { font-family: var(--mono); font-size: .86em; background: var(--bg-2); border-radius: var(--r-sm); padding: .08rem .34rem; color: var(--ink); }
|
|
705
|
-
.cmd-steps a { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 2px; }
|
|
706
|
-
|
|
707
|
-
/* ---- live loop status bar (the Monitor — C1) ---- */
|
|
708
|
-
.loopbar {
|
|
709
|
-
margin-top: 1.4rem; background: var(--surface); border: 1px solid var(--border);
|
|
710
|
-
border-radius: var(--r-lg); box-shadow: var(--sh-sm); overflow: hidden;
|
|
711
|
-
max-height: 0; opacity: 0; transition: max-height .4s ease, opacity .3s ease, margin .3s;
|
|
712
|
-
}
|
|
713
|
-
.loopbar.live { max-height: 40rem; opacity: 1; }
|
|
714
|
-
.lb-head { display: flex; align-items: center; gap: .8rem; padding: .8rem 1.1rem; cursor: pointer; }
|
|
715
|
-
.lb-head .pill {
|
|
716
|
-
display: inline-flex; align-items: center; gap: .5rem; font-family: var(--mono); font-size: .74rem;
|
|
717
|
-
padding: .32rem .7rem; border-radius: var(--r-full); border: 1px solid var(--accent-line);
|
|
718
|
-
background: var(--accent-wash); color: var(--accent-ink); white-space: nowrap;
|
|
719
|
-
}
|
|
720
|
-
.lb-head .pill .beat { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
|
|
721
|
-
.loopbar[data-state="running"] .pill .beat { animation: beat 1.6s ease-in-out infinite; }
|
|
722
|
-
.loopbar[data-state="reloading"] .pill .beat { animation: beat .5s ease-in-out infinite; }
|
|
723
|
-
.loopbar[data-state="stale"] .pill { border-color: var(--amber-line); background: var(--amber-wash); color: var(--amber); }
|
|
724
|
-
.loopbar[data-state="stale"] .pill .beat { background: var(--amber); }
|
|
725
|
-
@keyframes beat { 0%,100% { box-shadow: 0 0 0 0 rgba(14,159,142,.5); } 50% { box-shadow: 0 0 0 5px rgba(14,159,142,0); } }
|
|
726
|
-
.lb-head .lu { font-family: var(--mono); font-size: .76rem; color: var(--muted); }
|
|
727
|
-
.lb-head .lu b { color: var(--ink); font-weight: 500; }
|
|
728
|
-
.lb-head .chev { margin-left: auto; color: var(--faint); transition: transform .18s; }
|
|
729
|
-
.loopbar.open .lb-head .chev { transform: rotate(90deg); }
|
|
730
|
-
.lb-detail { display: none; padding: 0 1.1rem; }
|
|
731
|
-
.loopbar.open .lb-detail { display: block; padding-bottom: .9rem; }
|
|
732
|
-
.lb-row { display: grid; grid-template-columns: 1rem 8rem 1fr; gap: .6rem; align-items: baseline; padding: .5rem 0; border-top: 1px solid var(--border-2); }
|
|
733
|
-
.lb-row .ic { color: var(--accent); font-size: .78rem; }
|
|
734
|
-
.lb-row .k { font-size: .8rem; color: var(--muted); }
|
|
735
|
-
.lb-row .v { font-family: var(--mono); font-size: .8rem; color: var(--ink); font-variant-numeric: tabular-nums; word-break: break-all; }
|
|
736
|
-
.lb-row .v.dim { color: var(--faint); }
|
|
737
|
-
@media (prefers-reduced-motion: reduce) { .loopbar .pill .beat { animation: none !important; } }
|
|
738
|
-
|
|
739
|
-
/* ---- stepper (U2) ---- */
|
|
740
|
-
.stepper { display: flex; align-items: stretch; gap: 0; margin-bottom: 2rem; }
|
|
741
|
-
.snode {
|
|
742
|
-
flex: 1; display: flex; align-items: center; gap: .6rem; background: none; border: none;
|
|
743
|
-
font-family: var(--body); text-align: left; cursor: default; padding: .2rem 0; position: relative; color: var(--faint);
|
|
744
|
-
}
|
|
745
|
-
.snode .dot {
|
|
746
|
-
width: 2rem; height: 2rem; border-radius: 50%; flex: none; display: grid; place-items: center;
|
|
747
|
-
font-family: var(--mono); font-weight: 600; font-size: .85rem;
|
|
748
|
-
background: var(--surface); border: 1.5px solid var(--border); color: var(--faint);
|
|
749
|
-
transition: background .25s ease, border-color .25s ease, color .25s ease;
|
|
750
|
-
}
|
|
751
|
-
.snode .lab { display: flex; flex-direction: column; line-height: 1.2; }
|
|
752
|
-
.snode .lab .t { font-family: var(--display); font-weight: 600; font-size: .9rem; color: var(--muted); transition: color .25s; }
|
|
753
|
-
.snode .lab .s { font-size: .72rem; color: var(--faint); }
|
|
754
|
-
.snode .line { position: absolute; top: 1rem; left: 2.6rem; right: .4rem; height: 1.5px; background: var(--border); z-index: -1; }
|
|
755
|
-
.snode:last-child .line { display: none; }
|
|
756
|
-
.snode[data-st="active"] .dot { background: var(--accent); border-color: var(--accent); color: #fff; box-shadow: 0 0 0 4px var(--accent-wash); }
|
|
757
|
-
.snode[data-st="active"] .lab .t { color: var(--ink); }
|
|
758
|
-
.snode[data-st="done"] { cursor: pointer; }
|
|
759
|
-
.snode[data-st="done"] .dot { background: var(--accent-wash); border-color: var(--accent-line); color: var(--accent-ink); }
|
|
760
|
-
.snode[data-st="done"] .line { background: var(--accent-line); }
|
|
761
|
-
.snode[data-st="done"] .lab .t { color: var(--ink-2); }
|
|
762
|
-
.snode[data-st="done"]:hover .dot { border-color: var(--accent); }
|
|
763
|
-
.snode[data-st="locked"] { opacity: .65; }
|
|
764
|
-
|
|
765
|
-
/* ---- collapsed step bar (U2) ---- */
|
|
766
|
-
.done-bar {
|
|
767
|
-
display: flex; align-items: center; gap: .7rem; width: 100%; text-align: left;
|
|
768
|
-
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-md);
|
|
769
|
-
padding: .8rem 1rem; margin-bottom: 1rem; cursor: pointer; font-family: var(--body);
|
|
770
|
-
transition: border-color .12s;
|
|
771
|
-
}
|
|
772
|
-
.done-bar:hover { border-color: var(--accent-line); }
|
|
773
|
-
.done-bar .chk { width: 1.4rem; height: 1.4rem; border-radius: 50%; background: var(--accent-wash); border: 1px solid var(--accent-line); color: var(--accent-ink); display: grid; place-items: center; font-size: .8rem; flex: none; }
|
|
774
|
-
.done-bar .lab { font-family: var(--display); font-weight: 600; color: var(--ink-2); font-size: .9rem; }
|
|
775
|
-
.done-bar .meta { font-family: var(--mono); font-size: .75rem; color: var(--faint); }
|
|
776
|
-
.done-bar .reopen { margin-left: auto; font-family: var(--mono); font-size: .72rem; color: var(--accent-ink); }
|
|
777
|
-
|
|
778
|
-
/* ---- waiting / detect panel (U2) ---- */
|
|
779
|
-
.detect {
|
|
780
|
-
margin-top: 1.1rem; border: 1px dashed var(--border); border-radius: var(--r-md);
|
|
781
|
-
padding: .95rem 1.05rem; display: flex; align-items: center; gap: .8rem; background: var(--bg-2);
|
|
782
|
-
}
|
|
783
|
-
.detect .radar { width: 12px; height: 12px; border-radius: 50%; background: var(--faint); flex: none; position: relative; }
|
|
784
|
-
.detect .radar::after { content: ""; position: absolute; inset: -5px; border-radius: 50%; border: 1.5px solid var(--faint); opacity: 0; }
|
|
785
|
-
.detect[data-listening="true"] .radar { background: var(--accent); }
|
|
786
|
-
.detect[data-listening="true"] .radar::after { border-color: var(--accent); animation: ping 1.6s ease-out infinite; }
|
|
787
|
-
@keyframes ping { 0% { transform: scale(.6); opacity: .7; } 100% { transform: scale(1.8); opacity: 0; } }
|
|
788
|
-
.detect .txt { font-size: .86rem; color: var(--muted); flex: 1; }
|
|
789
|
-
.detect .txt b { color: var(--ink-2); font-weight: 600; }
|
|
790
|
-
|
|
791
|
-
/* ---- celebrate banner (U2) ---- */
|
|
792
|
-
.celebrate {
|
|
793
|
-
display: flex; align-items: center; gap: .7rem; background: var(--accent-wash);
|
|
794
|
-
border: 1px solid var(--accent-line); color: var(--accent-ink); border-radius: var(--r-md);
|
|
795
|
-
padding: .8rem 1rem; margin-bottom: 1.2rem; font-weight: 500; font-size: .92rem;
|
|
796
|
-
}
|
|
797
|
-
.celebrate .chk {
|
|
798
|
-
width: 1.5rem; height: 1.5rem; border-radius: 50%; background: var(--accent); color: #fff; flex: none;
|
|
799
|
-
display: grid; place-items: center; font-size: .85rem;
|
|
800
|
-
}
|
|
801
|
-
.pop-in { animation: popin .4s cubic-bezier(.2,.9,.3,1.3); }
|
|
802
|
-
@keyframes popin { 0% { transform: scale(.9); opacity: 0; } 100% { transform: scale(1); opacity: 1; } }
|
|
803
|
-
|
|
804
|
-
/* ---- stage nav buttons (U2) ---- */
|
|
805
|
-
.btnrow { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 1.3rem; align-items: center; }
|
|
806
|
-
.btn {
|
|
807
|
-
font-family: var(--display); font-weight: 600; font-size: .85rem; background: var(--surface); color: var(--ink-2);
|
|
808
|
-
cursor: pointer; border: 1px solid var(--border); border-radius: var(--r-full); padding: .6rem 1rem;
|
|
809
|
-
display: inline-flex; align-items: center; gap: .45rem; transition: border-color .12s, color .12s, background .12s;
|
|
810
|
-
}
|
|
811
|
-
.btn:hover { border-color: var(--accent-line); color: var(--ink); }
|
|
812
|
-
.btn.ghost { background: transparent; }
|
|
813
|
-
.btn:disabled { opacity: .5; cursor: not-allowed; }
|
|
814
|
-
.btn svg { width: 15px; height: 15px; }
|
|
815
|
-
.navspace { margin-left: auto; }
|
|
816
|
-
|
|
817
|
-
/* ---- future / roadmap note (U2) ---- */
|
|
818
|
-
.future {
|
|
819
|
-
margin-top: 1.2rem; border: 1px dashed var(--accent-line); background: var(--accent-wash);
|
|
820
|
-
border-radius: var(--r-md); padding: .85rem 1rem; font-size: .84rem; color: var(--accent-ink);
|
|
821
|
-
display: flex; gap: .6rem; align-items: flex-start;
|
|
822
|
-
}
|
|
823
|
-
.future b { font-weight: 700; }
|
|
824
|
-
.future .tag { font-family: var(--mono); font-size: .64rem; text-transform: uppercase; letter-spacing: .1em; border: 1px solid var(--accent-line); border-radius: var(--r-full); padding: .1rem .45rem; flex: none; }
|
|
825
|
-
|
|
826
|
-
/* C8 · live-preview iframe — click an element here to jump to its source */
|
|
827
|
-
.preview-embed { margin-top: 1.1rem; border: 1px solid var(--border); border-radius: var(--r-lg); overflow: hidden; background: var(--surface); }
|
|
828
|
-
.pe-bar { display: flex; align-items: center; gap: .5rem; padding: .5rem .7rem; border-bottom: 1px solid var(--border-2); background: var(--bg-2); }
|
|
829
|
-
.pe-bar .dots { display: flex; gap: .35rem; }
|
|
830
|
-
.pe-bar .dot { width: 10px; height: 10px; border-radius: 50%; }
|
|
831
|
-
.pe-url { flex: 1; font-family: var(--mono); font-size: .72rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
832
|
-
.pe-expand { font-family: var(--display); font-weight: 600; font-size: .74rem; color: var(--ink-2); background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-full); padding: .3rem .7rem; cursor: pointer; }
|
|
833
|
-
.pe-expand:hover { border-color: var(--accent-line); color: var(--ink); }
|
|
834
|
-
.pe-frame { width: 100%; height: 320px; border: 0; background: #fff; display: block; }
|
|
835
|
-
.pe-hint { padding: .5rem .8rem; font-size: .78rem; color: var(--muted); border-top: 1px solid var(--border-2); background: var(--bg-2); }
|
|
836
|
-
.pe-hint b { color: var(--ink-2); font-weight: 600; }
|
|
837
|
-
dialog.pe-dialog { width: min(94vw, 1200px); height: min(90vh, 820px); max-width: none; max-height: none; padding: 0; border: 1px solid var(--border); border-radius: var(--r-lg); box-shadow: var(--sh-lg); background: var(--surface); }
|
|
838
|
-
dialog.pe-dialog[open] { display: flex; flex-direction: column; }
|
|
839
|
-
dialog.pe-dialog::backdrop { background: rgba(10, 37, 64, .55); }
|
|
840
|
-
.ped-frame { flex: 1; width: 100%; border: 0; background: #fff; }
|
|
841
|
-
|
|
842
|
-
/* C3 · Files tab — in-browser file tree + read-only viewer */
|
|
843
|
-
.devfiles { margin-top: 1.1rem; border: 1px solid var(--border); border-radius: var(--r-lg); overflow: hidden; background: var(--surface); }
|
|
844
|
-
.df-head { display: flex; align-items: baseline; justify-content: space-between; gap: .75rem; padding: .55rem .85rem; border-bottom: 1px solid var(--border-2); background: var(--bg-2); }
|
|
845
|
-
.df-title { font-weight: 700; font-size: .82rem; color: var(--ink); }
|
|
846
|
-
.df-path { font-family: var(--mono); font-size: .72rem; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
847
|
-
.df-body { display: grid; grid-template-columns: minmax(160px, 240px) 1fr; min-height: 260px; max-height: 420px; }
|
|
848
|
-
.df-tree { overflow: auto; border-right: 1px solid var(--border-2); padding: .35rem 0; font-size: .8rem; }
|
|
849
|
-
.df-main { display: flex; flex-direction: column; min-width: 0; background: var(--term); }
|
|
850
|
-
.df-editbar { flex: none; display: flex; gap: .7rem; align-items: center; min-height: 30px; padding: .35rem .8rem; border-bottom: 1px solid var(--term-line); background: var(--term-2); font-size: .72rem; color: var(--term-dim); }
|
|
851
|
-
.df-status { font-family: var(--mono); }
|
|
852
|
-
.df-status.ok { color: var(--term-ok); }
|
|
853
|
-
.df-status.warn { color: #f6b0b0; }
|
|
854
|
-
.df-conflict { display: inline-flex; gap: .4rem; align-items: center; color: #f6b0b0; }
|
|
855
|
-
.df-mini { font-family: var(--mono); font-size: .7rem; padding: .1rem .45rem; border-radius: var(--r-sm); border: 1px solid var(--term-line); background: transparent; color: var(--term-fg); cursor: pointer; }
|
|
856
|
-
.df-mini:hover { background: var(--term-line); }
|
|
857
|
-
.df-view { flex: 1 1 auto; width: 100%; box-sizing: border-box; display: block; margin: 0; border: 0; resize: none; outline: none; overflow: auto; padding: .75rem .9rem; font-family: var(--mono); font-size: .76rem; line-height: 1.55; color: var(--term-fg); background: var(--term); white-space: pre; tab-size: 2; }
|
|
858
|
-
.df-view[readonly] { opacity: .9; }
|
|
859
|
-
.df-diffbtn { margin-left: auto; }
|
|
860
|
-
.df-publishbtn { border-color: var(--accent); color: var(--accent-ink); font-weight: 600; }
|
|
861
|
-
.df-publishbtn:hover { background: var(--accent-wash); }
|
|
862
|
-
.df-publishbtn:disabled { opacity: .6; cursor: default; }
|
|
863
|
-
.df-diff { flex: 1 1 auto; margin: 0; overflow: auto; padding: .5rem 0; font-family: var(--mono); font-size: .76rem; line-height: 1.5; background: var(--term); color: var(--term-fg); }
|
|
864
|
-
.df-diff .dl { display: block; padding: 0 .9rem; white-space: pre; }
|
|
865
|
-
.df-diff .add { background: rgba(52, 211, 153, .16); color: #bff2e0; }
|
|
866
|
-
.df-diff .del { background: rgba(246, 176, 176, .15); color: #f6c2c2; }
|
|
867
|
-
.df-diff .ctx { color: var(--term-dim); }
|
|
868
|
-
.ft-root, .ft-children { list-style: none; margin: 0; padding: 0; }
|
|
869
|
-
.ft-row { display: flex; align-items: center; gap: .4rem; padding: .18rem .5rem .18rem 0; cursor: default; border-radius: var(--r-sm); }
|
|
870
|
-
.ft-file .ft-row { cursor: pointer; }
|
|
871
|
-
.ft-file .ft-row:hover { background: var(--accent-wash); }
|
|
872
|
-
.ft-row.sel { background: var(--accent-wash); color: var(--accent-ink); font-weight: 600; }
|
|
873
|
-
.ft-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
874
|
-
.ft-ic { font-size: .78rem; flex: none; opacity: .8; }
|
|
875
|
-
.ft-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
876
|
-
.future-note { margin-top: .8rem; font-size: .8rem; color: var(--muted); display: flex; gap: .5rem; align-items: baseline; }
|
|
877
|
-
.future-note .tag { font-family: var(--mono); font-size: .64rem; text-transform: uppercase; letter-spacing: .1em; border: 1px solid var(--accent-line); border-radius: var(--r-full); padding: .1rem .45rem; flex: none; color: var(--accent-ink); }
|
|
878
|
-
@media (max-width: 560px) { .df-body { grid-template-columns: 1fr; } .df-view { max-height: 220px; } }
|
|
879
|
-
@media (prefers-reduced-motion: reduce) {
|
|
880
|
-
.detect[data-listening="true"] .radar::after, .pop-in { animation: none !important; }
|
|
881
|
-
}
|
|
882
|
-
|
|
883
|
-
/* ---- Ask (quiet loop-recovery help, U3) ---- */
|
|
884
|
-
.ask-fab {
|
|
885
|
-
position: fixed; right: 1.4rem; bottom: 1.4rem; z-index: 50;
|
|
886
|
-
display: inline-flex; align-items: center; gap: .5rem;
|
|
887
|
-
background: var(--surface); color: var(--ink-2); border: 1px solid var(--border);
|
|
888
|
-
border-radius: var(--r-full); padding: .6rem .95rem; cursor: pointer; box-shadow: var(--sh-md);
|
|
889
|
-
font-family: var(--body); font-size: .85rem; transition: border-color .12s, color .12s;
|
|
890
|
-
}
|
|
891
|
-
.ask-fab:hover { border-color: var(--accent-line); color: var(--ink); }
|
|
892
|
-
.ask-fab .u { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--accent-line); }
|
|
893
|
-
.ask-fab svg { width: 16px; height: 16px; color: var(--accent); }
|
|
894
|
-
.ask-panel {
|
|
895
|
-
position: fixed; right: 1.4rem; bottom: 4.6rem; z-index: 51; width: min(23rem, 90vw);
|
|
896
|
-
background: var(--surface); border: 1px solid var(--border); border-radius: var(--r-lg);
|
|
897
|
-
box-shadow: var(--sh-lg); overflow: hidden; transform-origin: bottom right;
|
|
898
|
-
opacity: 0; visibility: hidden; transform: translateY(8px) scale(.98); transition: opacity .16s, transform .16s, visibility .16s;
|
|
899
|
-
}
|
|
900
|
-
.ask-panel.open { opacity: 1; visibility: visible; transform: translateY(0) scale(1); }
|
|
901
|
-
.ask-head { padding: .95rem 1.1rem; border-bottom: 1px solid var(--border-2); }
|
|
902
|
-
.ask-head strong { font-family: var(--display); color: var(--ink); font-size: .98rem; display: block; }
|
|
903
|
-
.ask-head span { font-size: .8rem; color: var(--muted); }
|
|
904
|
-
.qa { padding: .3rem .5rem .6rem; max-height: 60vh; overflow-y: auto; }
|
|
905
|
-
.qa details { border-bottom: 1px solid var(--border-2); }
|
|
906
|
-
.qa details:last-child { border-bottom: none; }
|
|
907
|
-
.qa summary { list-style: none; cursor: pointer; padding: .7rem .6rem; display: flex; align-items: center; gap: .6rem; font-size: .88rem; color: var(--ink); }
|
|
908
|
-
.qa summary::-webkit-details-marker { display: none; }
|
|
909
|
-
.qa summary .q { font-family: var(--mono); color: var(--accent); font-size: .78rem; flex: none; }
|
|
910
|
-
.qa summary .chev { margin-left: auto; color: var(--faint); transition: transform .18s; }
|
|
911
|
-
.qa details[open] summary .chev { transform: rotate(90deg); }
|
|
912
|
-
.qa .ans { padding: 0 .6rem .85rem 1.9rem; color: var(--muted); font-size: .84rem; }
|
|
913
|
-
.qa .ans code { font-family: var(--mono); font-size: .8rem; color: var(--ink-2); background: var(--bg-2); padding: .05em .4em; border-radius: 5px; }
|
|
914
|
-
.qa .ans b { color: var(--ink-2); font-weight: 600; }
|
|
915
|
-
.ask-foot { padding: .7rem 1.1rem; border-top: 1px solid var(--border-2); font-family: var(--mono); font-size: .68rem; color: var(--faint); }
|
|
916
|
-
@media (prefers-reduced-motion: reduce) { .ask-panel { transition: none; } }
|
|
917
|
-
|
|
918
|
-
/* ---- connect the MCP (d3-unhold-dev-step0-connect-mcp) ---- */
|
|
919
|
-
#stage-mcp { margin-top: 2rem; }
|
|
920
|
-
/* disable ligatures so `--transport` renders as literal characters, not a glyph */
|
|
921
|
-
#mcpCmdText { font-variant-ligatures: none; font-feature-settings: "liga" 0, "calt" 0; }
|
|
922
|
-
|
|
923
|
-
/* ---- what's next : coming soon shelf (U4) ---- */
|
|
924
|
-
.next { margin-top: 3.4rem; padding-top: 2rem; border-top: 1px dashed var(--border); }
|
|
925
|
-
.next h3 { font-family: var(--display); font-weight: 700; color: var(--ink-2); font-size: 1.1rem; margin: 0 0 .3rem; letter-spacing: -.02em; }
|
|
926
|
-
.next .sub { margin: 0 0 1.2rem; font-size: .88rem; color: var(--muted); max-width: 52ch; }
|
|
927
|
-
.nextgrid { display: grid; gap: .7rem; }
|
|
928
|
-
@media (min-width: 40rem) { .nextgrid { grid-template-columns: 1fr 1fr; } }
|
|
929
|
-
@media (min-width: 60rem) { .nextgrid { grid-template-columns: repeat(4, 1fr); } }
|
|
930
|
-
.nextcard {
|
|
931
|
-
display: flex; flex-direction: column; gap: .3rem; background: transparent;
|
|
932
|
-
border: 1px solid var(--border); border-radius: var(--r-md); padding: .9rem 1rem; opacity: .78;
|
|
933
|
-
}
|
|
934
|
-
.nextcard .path { font-family: var(--mono); font-size: .8rem; color: var(--muted); }
|
|
935
|
-
.nextcard .d { font-size: .8rem; color: var(--faint); }
|
|
936
|
-
.nextcard .soon { align-self: flex-start; margin-top: .3rem; font-family: var(--mono); font-size: .64rem; text-transform: uppercase; letter-spacing: .1em; color: var(--faint); border: 1px solid var(--border); border-radius: var(--r-full); padding: .1rem .5rem; }
|
|
937
|
-
|
|
938
|
-
/* ---- footer ---- */
|
|
939
|
-
.foot {
|
|
940
|
-
margin-top: 2.8rem; padding-top: 1.2rem; border-top: 1px solid var(--border);
|
|
941
|
-
display: flex; flex-wrap: wrap; gap: 1rem 1.4rem; align-items: center;
|
|
942
|
-
font-family: var(--mono); font-size: .78rem; color: var(--muted);
|
|
943
|
-
}
|
|
944
|
-
.foot .sp { margin-left: auto; }
|
|
945
|
-
.foot a { color: var(--muted); text-decoration: none; }
|
|
946
|
-
.foot a:hover { color: var(--accent-ink); }
|
|
947
|
-
|
|
948
|
-
/* ---- toast ---- */
|
|
949
|
-
.toast {
|
|
950
|
-
position: fixed; left: 50%; bottom: 1.6rem; transform: translateX(-50%) translateY(160%);
|
|
951
|
-
/* Terminal palette (dark bg + light fg in BOTH themes) — a plain var(--ink)
|
|
952
|
-
background went white-on-white in dark mode. */
|
|
953
|
-
background: var(--term); color: var(--term-fg); border: 1px solid var(--term-line);
|
|
954
|
-
font-family: var(--mono); font-size: .8rem;
|
|
955
|
-
padding: .7rem 1.1rem; border-radius: var(--r-full); box-shadow: var(--sh-lg);
|
|
956
|
-
transition: transform .28s cubic-bezier(.2,.9,.3,1.2); z-index: 60; pointer-events: none;
|
|
957
|
-
display: inline-flex; align-items: center; gap: .5rem;
|
|
958
|
-
}
|
|
959
|
-
.toast.show { transform: translateX(-50%) translateY(0); }
|
|
960
|
-
.toast .ok { color: var(--term-ok); }
|
|
961
|
-
@media (prefers-reduced-motion: reduce) {
|
|
962
|
-
.cta, .toast, .notice a.action, .info-pop { transition: none; }
|
|
963
|
-
}
|
|
964
|
-
[hidden] { display: none !important; }
|
|
965
|
-
</style>
|
|
966
|
-
</head>
|
|
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
|
-
>
|
|
973
|
-
<div class="wrap">
|
|
974
|
-
<header class="topbar">
|
|
975
|
-
<div class="brand">
|
|
976
|
-
<span class="mark" aria-hidden="true">
|
|
977
|
-
<svg viewBox="0 0 24 24" fill="none"><path d="M12 2.5 20 6v6c0 4.6-3.2 7.9-8 9.5-4.8-1.6-8-4.9-8-9.5V6l8-3.5Z" stroke="currentColor" stroke-width="2" stroke-linejoin="round"/><path d="m8.5 12 2.4 2.4L15.8 9.5" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
978
|
-
</span>
|
|
979
|
-
<span class="who"><span class="dom">{appDomain}</span><span class="sub">Developer Cockpit</span></span>
|
|
980
|
-
</div>
|
|
981
|
-
<div class="right">
|
|
982
|
-
{session?.email ? <span class="sess">{session.email}</span> : null}
|
|
983
|
-
<button class="themetog" id="themeBtn" type="button">◐ Theme</button>
|
|
984
|
-
</div>
|
|
985
|
-
</header>
|
|
986
|
-
|
|
987
|
-
{/* U1 anchor — name the surface so a developer knows which page is "home". */}
|
|
988
|
-
<p class="cockpit-lede">This is your <strong>Developer Cockpit</strong> — your home base while your store spins up.</p>
|
|
989
|
-
|
|
990
|
-
{signedIn === false && (
|
|
991
|
-
<div class="notice">
|
|
992
|
-
{/* d1 (board: d1-unmask-invite-email) — the invitee owns this address (the
|
|
993
|
-
link was sent to it), so masking it back to them is pointless friction.
|
|
994
|
-
Show the FULL invite email when we actually have it; NEVER show the redacted
|
|
995
|
-
hint to its owner. This branch is signed-OUT, so there's no session.email to
|
|
996
|
-
read; the recovery link arrives carrying only a redacted `email_hint` (the
|
|
997
|
-
full address is masked UPSTREAM by the CLI before it ever reaches this page —
|
|
998
|
-
see report), so the full email is genuinely unavailable here and we fall back
|
|
999
|
-
to neutral copy rather than re-displaying the redacted form. `recoveryEmailFull`
|
|
1000
|
-
lights up automatically the moment a full address is available. */}
|
|
1001
|
-
<p>{isCredentialRecovery
|
|
1002
|
-
? recoveryEmailFull
|
|
1003
|
-
? <>Sign in with your invite email, <strong>{recoveryEmailFull}</strong>, to generate a fresh setup command for this machine.</>
|
|
1004
|
-
: <>Sign in with the same email that accepted the invite to generate a fresh setup command for this machine.</>
|
|
1005
|
-
: <>Sign in with the email your invite came to — then this page shows your store and your one command.</>}</p>
|
|
1006
|
-
<a class="action" href={loginHref}>Sign in</a>
|
|
1007
|
-
</div>
|
|
1008
|
-
)}
|
|
1009
|
-
|
|
1010
|
-
{noStoreAccess && (
|
|
1011
|
-
<div class="notice">
|
|
1012
|
-
{noResolvableStore
|
|
1013
|
-
? <>
|
|
1014
|
-
<p>You're signed in{session?.email ? <>{" "}as <strong>{session.email}</strong></> : null}, but this page doesn't have a store to set up yet. Pick one from your dashboard to get its setup command.</p>
|
|
1015
|
-
<a class="action" href="/dashboard">Pick a store</a>
|
|
1016
|
-
</>
|
|
1017
|
-
: <>
|
|
1018
|
-
<p>You're signed in{session?.email ? <>{" "}as <strong>{session.email}</strong></> : null}, but this account isn't linked to a store yet. Ask whoever sent your invite to grant developer access — then reload this page.</p>
|
|
1019
|
-
<a class="action" href="/auth/logout">Switch account</a>
|
|
1020
|
-
</>}
|
|
1021
|
-
</div>
|
|
1022
|
-
)}
|
|
1023
|
-
|
|
1024
|
-
{/* STORE-NOT-SET-UP notice — a real, signed-in developer whose store's repo isn't
|
|
1025
|
-
provisioned yet. We deliberately withhold the setup command here (canSetupStore
|
|
1026
|
-
is false, so the mint fastpath + activity rail below don't render) rather than
|
|
1027
|
-
hand out a `tot start` that would die at checkout. Honest, low-friction copy:
|
|
1028
|
-
nothing for them to do but wait + reload. */}
|
|
1029
|
-
{storeNotSetUp && (
|
|
1030
|
-
<div class="notice">
|
|
1031
|
-
<p>You're all set{session?.email ? <>{" "}as <strong>{session.email}</strong></> : null} — but <strong>{appDomain}</strong>'s store is still being set up. Your one-paste setup command appears here the moment it's ready. Nothing to do on your end; check back shortly, or ask your Token of Trust contact if it's urgent.</p>
|
|
1032
|
-
<a class="action" href="/cockpit">Reload</a>
|
|
1033
|
-
</div>
|
|
1034
|
-
)}
|
|
1035
|
-
|
|
1036
|
-
{/* Bridge strip (replaces the big live-dev-link card) — the rail's power source,
|
|
1037
|
-
fed by the activity poll's `runtime`. Hidden until the store has been seen
|
|
1038
|
-
live this page-view (sawLive); localhost is the developer's OWN machine, so
|
|
1039
|
-
clicking is a plain navigation. Pulses teal while live; turns amber with a
|
|
1040
|
-
recovery hint when the loop goes quiet (data-live="0"). */}
|
|
1041
|
-
{hostedActivity && (
|
|
1042
|
-
<section class="bridge" id="bridge" data-live="1" hidden>
|
|
1043
|
-
<span class="beacon" aria-hidden="true"></span>
|
|
1044
|
-
<div class="b-txt" id="bTxt">
|
|
1045
|
-
<span class="b-kicker" id="bKicker">Live on your machine</span>
|
|
1046
|
-
<a class="b-url" id="bUrl" href="#" target="_blank" rel="noreferrer">http://localhost/</a>
|
|
1047
|
-
</div>
|
|
1048
|
-
<span class="b-diag" id="bDiag" hidden></span>
|
|
1049
|
-
<a class="b-go" id="bGo" href="#" target="_blank" rel="noreferrer">Open your store ↗</a>
|
|
1050
|
-
<p class="b-hint" id="bHint" hidden>Your store went quiet. Run <code>tot start</code> in your project to bring it back — Step 1 has the command.</p>
|
|
1051
|
-
{/* Obstacle lane (v2 §6): a failed `tot start` (wrong Node, missing pnpm,
|
|
1052
|
-
install/clone error) beacons why via the setup command's activity token;
|
|
1053
|
-
the strip becomes an actionable fix card. Text is set via textContent from
|
|
1054
|
-
the SERVER-composed remediation copy (obstacleStore) — never innerHTML. */}
|
|
1055
|
-
<div class="b-obstacle" id="bObstacle" hidden>
|
|
1056
|
-
<span class="b-kicker b-ob-title" id="bObTitle">Setup hit a snag</span>
|
|
1057
|
-
<p class="b-ob-msg" id="bObMsg"></p>
|
|
1058
|
-
<p class="b-ob-fix"><span class="b-ob-lead">Fix:</span> <code class="b-ob-cmd" id="bObCmd" hidden></code> <span id="bObNote"></span></p>
|
|
1059
|
-
</div>
|
|
1060
|
-
</section>
|
|
1061
|
-
)}
|
|
1062
|
-
|
|
1063
|
-
{isLocalDevLoop && (
|
|
1064
|
-
<nav class="stepper" id="stepper" aria-label="Setup progress">
|
|
1065
|
-
<button class="snode" type="button" data-go="run" data-st="active">
|
|
1066
|
-
<span class="dot">1</span><span class="lab"><span class="t">Get running</span><span class="s">on your machine</span></span><span class="line"></span>
|
|
1067
|
-
</button>
|
|
1068
|
-
<button class="snode" type="button" data-go="edit" data-st="locked">
|
|
1069
|
-
<span class="dot">2</span><span class="lab"><span class="t">Make an edit</span><span class="s">see it reload</span></span><span class="line"></span>
|
|
1070
|
-
</button>
|
|
1071
|
-
<button class="snode" type="button" data-go="next" data-st="locked">
|
|
1072
|
-
<span class="dot">3</span><span class="lab"><span class="t">What's next</span><span class="s">coming soon</span></span>
|
|
1073
|
-
</button>
|
|
1074
|
-
</nav>
|
|
1075
|
-
)}
|
|
1076
|
-
|
|
1077
|
-
{isLocalDevLoop && (
|
|
1078
|
-
<button class="done-bar" id="bar-run" type="button" data-go="run" hidden>
|
|
1079
|
-
<span class="chk">✓</span><span class="lab">Store running</span><span class="meta" id="bar-run-meta">on your machine</span><span class="reopen">reopen</span>
|
|
1080
|
-
</button>
|
|
1081
|
-
)}
|
|
1082
|
-
{isLocalDevLoop && (
|
|
1083
|
-
<button class="done-bar" id="bar-edit" type="button" data-go="edit" hidden>
|
|
1084
|
-
<span class="chk">✓</span><span class="lab">First edit landed</span><span class="meta" id="bar-edit-meta">content/home.html</span><span class="reopen">reopen</span>
|
|
1085
|
-
</button>
|
|
1086
|
-
)}
|
|
1087
|
-
|
|
1088
|
-
{/* Station 1 — SHARED across all variants. #stage-run stays rendered exactly
|
|
1089
|
-
once with its inner content unchanged; class:list gives non-hosted variants
|
|
1090
|
-
neutral wrappers (.strun has no CSS), while hosted gets the rail station grid
|
|
1091
|
-
+ node column + collapsible done row. */}
|
|
1092
|
-
<section id="st-run" class:list={["strun", { station: hostedActivity }]} data-st="open">
|
|
1093
|
-
{hostedActivity && (
|
|
1094
|
-
<div class="nodecol">
|
|
1095
|
-
<span class="node" aria-hidden="true"><span class="num">1</span><svg viewBox="0 0 24 24" fill="none"><path d="m5 12.5 4.5 4.5L19 7.5" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/></svg><span class="spark" style="--dx:1;--dy:-.4;background:#0e9f8e"></span><span class="spark" style="--dx:-1;--dy:-.7;background:#2fd4bf"></span><span class="spark" style="--dx:.6;--dy:1;background:#f0c56b"></span><span class="spark" style="--dx:-.7;--dy:.8;background:#0e9f8e"></span><span class="spark" style="--dx:0;--dy:-1.1;background:#2fd4bf"></span><span class="spark" style="--dx:1.1;--dy:.5;background:#b7791f"></span></span>
|
|
1096
|
-
<span class="seg" id="seg-run" data-lit="0" data-flow="0"></span>
|
|
1097
|
-
</div>
|
|
1098
|
-
)}
|
|
1099
|
-
<div class:list={{ "s-body": hostedActivity }}>
|
|
1100
|
-
{hostedActivity && (
|
|
1101
|
-
<button class="s-done" id="done-run" type="button" hidden>
|
|
1102
|
-
<span class="lab">Store running</span><span class="meta" id="doneRunMeta"></span><span class="reopen">Reopen ↓</span>
|
|
1103
|
-
</button>
|
|
1104
|
-
)}
|
|
1105
|
-
{hostedActivity && (
|
|
1106
|
-
<p class="quiet-note" id="quietNote" hidden><span aria-hidden="true">◍</span><span>Your store went quiet — it stopped answering from your machine. <code>tot start</code> brings it back.</span></p>
|
|
1107
|
-
)}
|
|
1108
|
-
<section class="stage" id="stage-run">
|
|
1109
|
-
<div class="stage-body">
|
|
1110
|
-
<span class="kicker">Step 1</span>
|
|
1111
|
-
<h1>Get your store running.
|
|
1112
|
-
<span class="info" id="info1">
|
|
1113
|
-
<button class="info-btn" type="button" aria-label="What does this command do?">i</button>
|
|
1114
|
-
<span class="info-pop" role="tooltip">Installs the <code>tot</code> command, signs your terminal in with a single-use code, checks out your store into a new <code>{appDomain}</code> folder, runs it, and opens your browser. No Docker, no browser sign-in, nothing to provision. From then on it's just <code>tot start</code>.</span>
|
|
1115
|
-
</span>
|
|
1116
|
-
</h1>
|
|
1117
|
-
<p class="goal">{canSetupStore
|
|
1118
|
-
? isCredentialRecovery
|
|
1119
|
-
? <>Generate a fresh personal setup command, then paste it into your terminal and press Enter.</>
|
|
1120
|
-
: <>Generate your personal setup command, then paste it into your terminal and press Enter.</>
|
|
1121
|
-
: storeNotSetUp
|
|
1122
|
-
? <>We're finishing setting up <strong>{appDomain}</strong>. Your one-paste setup command appears here the moment it's ready — check back shortly.</>
|
|
1123
|
-
: <>Open the invite link we emailed you to unlock your personal setup command.</>}</p>
|
|
1124
|
-
|
|
1125
|
-
<div class="term">
|
|
1126
|
-
<div class="term-bar"><span class="dots"><span class="dot r"></span><span class="dot y"></span><span class="dot g"></span></span><span class="ttl">your terminal</span>{canSetupStore && (
|
|
1127
|
-
<button class="term-copy" id="copyIcon" type="button" aria-label="Copy the command" title="Copy the command" hidden>
|
|
1128
|
-
<svg class="ic-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
|
1129
|
-
<svg class="ic-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
|
|
1130
|
-
</button>
|
|
1131
|
-
)}</div>
|
|
1132
|
-
<div class="term-body"><span class="c"># your personal, single-use setup command</span>
|
|
1133
|
-
<span class="cmd is-placeholder" id="cli-cmd-text">{canSetupStore ? (isCredentialRecovery ? "→ click “Generate a fresh setup command” below" : "→ click “Generate my setup command” below") : storeNotSetUp ? "→ available once your store finishes setting up" : "→ available once you’re signed in via your invite"}</span></div>
|
|
1134
|
-
</div>
|
|
1135
|
-
|
|
1136
|
-
{canSetupStore && (
|
|
1137
|
-
<div class="cmd-actions">
|
|
1138
|
-
<button class="cta" id="copyCmd" type="button" data-fastpath="1" data-base-path={basePath}>
|
|
1139
|
-
<svg viewBox="0 0 24 24" fill="none"><path d="M12 5v14M5 12h14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/></svg>
|
|
1140
|
-
{isCredentialRecovery ? "Generate a fresh setup command" : "Generate my setup command"}
|
|
1141
|
-
</button>
|
|
1142
|
-
</div>
|
|
1143
|
-
)}
|
|
1144
|
-
|
|
1145
|
-
{/* Rendezvous approval — after you paste the command, your terminal connects here and
|
|
1146
|
-
you confirm the code it printed matches (the load-bearing security step: it's how
|
|
1147
|
-
you prove it's YOUR terminal, not an interceptor's). Hidden until a command is
|
|
1148
|
-
generated; the client script drives the wait → confirm → done states. */}
|
|
1149
|
-
{canSetupStore && (
|
|
1150
|
-
<div class="rzv" id="rzvApprove" hidden aria-live="polite">
|
|
1151
|
-
<div class="rzv-wait" id="rzvWait" hidden>
|
|
1152
|
-
<span class="radar" aria-hidden="true"></span>
|
|
1153
|
-
<span>Waiting for your terminal to connect — run the pasted command, then come back here.</span>
|
|
1154
|
-
</div>
|
|
1155
|
-
<div class="rzv-confirm" id="rzvConfirm" hidden>
|
|
1156
|
-
<p class="rzv-q">Your terminal is asking to sign in. Confirm the code it printed matches:</p>
|
|
1157
|
-
<p class="rzv-fp" id="rzvFp">····-····</p>
|
|
1158
|
-
<div class="rzv-actions">
|
|
1159
|
-
<button class="cta" id="rzvApproveBtn" type="button">Yes — that's my terminal, approve</button>
|
|
1160
|
-
<button class="rzv-deny" id="rzvDenyBtn" type="button">Not me — reject</button>
|
|
1161
|
-
</div>
|
|
1162
|
-
</div>
|
|
1163
|
-
<p class="rzv-done" id="rzvDone" hidden></p>
|
|
1164
|
-
</div>
|
|
1165
|
-
)}
|
|
1166
|
-
<p class="cmd-note" id="cli-cmd-note">{canSetupStore
|
|
1167
|
-
? <>This mints a <em>single-use, short-lived</em> sign-in code, shows the full command here, and copies it to your clipboard. Paste it <em>from the folder where you keep projects</em> — it creates a <code>{appDomain}</code> folder there with your store's code, signs you in, runs your store, and opens your browser. Nothing to retype.</>
|
|
1168
|
-
: storeNotSetUp
|
|
1169
|
-
? <>Nothing to do on your end — a Token of Trust reviewer is finishing your store's first setup. Once it's ready, reload and this page hands you the one-paste command.</>
|
|
1170
|
-
: <>You'll need <em>Node.js 22.12+ (24 LTS recommended) and your invite</em>. Sign in through your invite link and this page hands you a one-paste setup command — no Docker, nothing to provision.</>}</p>
|
|
1171
|
-
|
|
1172
|
-
<p class="cmd-steps">Prefer the steps? <code>npm i -g {cliInstallSpec}</code>, then <code>tot clone {appDomain}</code>, <code>cd {appDomain}</code>, <code>tot dev</code>. New to Node? Get Node 24 (LTS) at <a href="https://nodejs.org">nodejs.org</a> — it includes <code>npm</code>.</p>
|
|
1173
|
-
|
|
1174
|
-
{isLocalDevLoop && (
|
|
1175
|
-
<div class="detect" id="detect" data-listening="true">
|
|
1176
|
-
<span class="radar" aria-hidden="true"></span>
|
|
1177
|
-
<span class="txt" id="detectTxt">Watching for your store to come online on this machine…</span>
|
|
1178
|
-
</div>
|
|
1179
|
-
)}
|
|
1180
|
-
</div>
|
|
1181
|
-
</section>
|
|
1182
|
-
</div>
|
|
1183
|
-
</section>
|
|
1184
|
-
|
|
1185
|
-
{/* Station 2 — hosted-only. Locked label → live-gated teaser → open step body →
|
|
1186
|
-
collapsed done row, all on the rail. */}
|
|
1187
|
-
{hostedActivity && (
|
|
1188
|
-
<section class="station" id="st-edit" data-st="locked">
|
|
1189
|
-
<div class="nodecol">
|
|
1190
|
-
<span class="node" aria-hidden="true"><span class="num">2</span><svg viewBox="0 0 24 24" fill="none"><path d="m5 12.5 4.5 4.5L19 7.5" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/></svg><span class="spark" style="--dx:1;--dy:-.4;background:#0e9f8e"></span><span class="spark" style="--dx:-1;--dy:-.7;background:#2fd4bf"></span><span class="spark" style="--dx:.6;--dy:1;background:#f0c56b"></span><span class="spark" style="--dx:-.7;--dy:.8;background:#0e9f8e"></span><span class="spark" style="--dx:0;--dy:-1.1;background:#2fd4bf"></span><span class="spark" style="--dx:1.1;--dy:.5;background:#b7791f"></span></span>
|
|
1191
|
-
<span class="seg" id="seg-edit" data-lit="0" data-flow="0"></span>
|
|
1192
|
-
</div>
|
|
1193
|
-
<div class="s-body">
|
|
1194
|
-
<div class="s-locked" id="lock-edit">Change one line <span class="sub">unlocks when your store is running</span></div>
|
|
1195
|
-
<button class="s-done" id="done-edit" type="button" hidden>
|
|
1196
|
-
<span class="lab">First edit landed</span><span class="meta" id="doneEditMeta">content/home.html</span><span class="reopen">Reopen ↓</span>
|
|
1197
|
-
</button>
|
|
1198
|
-
<div class="s-tease" id="tease-edit" hidden>
|
|
1199
|
-
<span class="t-kick">Next up · Step 2</span>
|
|
1200
|
-
<h3>Change one line.</h3>
|
|
1201
|
-
<p>Open <code>content/home.html</code>, edit anything, save. Your store reloads itself and the save shows up here.</p>
|
|
1202
|
-
<button class="t-cta" id="cta-edit" type="button">Make your first edit →</button>
|
|
1203
|
-
</div>
|
|
1204
|
-
<section class="stage" id="hosted-stage-edit" hidden>
|
|
1205
|
-
<div class="stage-body">
|
|
1206
|
-
<p class="ha-encourage"><span class="chk">✓</span><span>You're running locally — nice. One more step: make an edit and watch it land here.</span></p>
|
|
1207
|
-
<span class="kicker">Step 2</span>
|
|
1208
|
-
<h1>Change one line.</h1>
|
|
1209
|
-
<p class="goal">Open <code>content/home.html</code> in your editor, change one line, and save. Your store reloads on its own, and the save shows up in <strong>Your activity</strong> below within a couple of seconds.</p>
|
|
1210
|
-
<div class="term">
|
|
1211
|
-
<div class="term-bar"><span class="dots"><span class="dot r"></span><span class="dot y"></span><span class="dot g"></span></span><span class="ttl">open the file</span></div>
|
|
1212
|
-
<div class="term-body"><span class="c"># open your homepage content to edit</span>
|
|
1213
|
-
<span class="cmd" id="editOpenCmd">$EDITOR content/home.html</span></div>
|
|
1214
|
-
</div>
|
|
1215
|
-
<p class="cmd-note" id="editOpenHint">Tip: set <code>$EDITOR</code> and we'll show the exact open command here.</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>
|
|
1218
|
-
</div>
|
|
1219
|
-
</section>
|
|
1220
|
-
</div>
|
|
1221
|
-
</section>
|
|
1222
|
-
)}
|
|
1223
|
-
|
|
1224
|
-
{/* Station 3 — hosted-only. Its .seg is the last on the rail (hidden via CSS). */}
|
|
1225
|
-
{hostedActivity && (
|
|
1226
|
-
<section class="station" id="st-next" data-st="locked">
|
|
1227
|
-
<div class="nodecol">
|
|
1228
|
-
<span class="node" aria-hidden="true"><span class="num">3</span><svg viewBox="0 0 24 24" fill="none"><path d="m5 12.5 4.5 4.5L19 7.5" stroke="currentColor" stroke-width="2.6" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
|
|
1229
|
-
<span class="seg"></span>
|
|
1230
|
-
</div>
|
|
1231
|
-
<div class="s-body">
|
|
1232
|
-
<div class="s-locked" id="lock-next">Send it for review <span class="sub">unlocks after your first edit</span></div>
|
|
1233
|
-
<div class="s-tease" id="tease-next" hidden>
|
|
1234
|
-
<span class="t-kick">Next up · Step 3</span>
|
|
1235
|
-
<h3>Send your change for review.</h3>
|
|
1236
|
-
<p>Bundle what you've edited into a compliance-reviewed preview build.</p>
|
|
1237
|
-
<button class="t-cta" id="cta-next" type="button">Go to Step 3 →</button>
|
|
1238
|
-
</div>
|
|
1239
|
-
<section class="stage" id="hosted-stage-next" hidden>
|
|
1240
|
-
<div class="stage-body">
|
|
1241
|
-
<span class="kicker">Step 3</span>
|
|
1242
|
-
<h1>Submit your change for preview.</h1>
|
|
1243
|
-
<p class="goal">You've got the loop: edit locally, see it reflected here. When a change is ready, <code>tot preview</code> bundles it into a compliance-reviewed preview build; once that preview looks right, <code>tot ship</code> promotes it live.</p>
|
|
1244
|
-
<div class="term">
|
|
1245
|
-
<div class="term-bar"><span class="dots"><span class="dot r"></span><span class="dot y"></span><span class="dot g"></span></span><span class="ttl">preview</span></div>
|
|
1246
|
-
<div class="term-body"><span class="c"># bundle your local changes into a reviewable preview</span>
|
|
1247
|
-
<span class="cmd">tot preview</span></div>
|
|
1248
|
-
</div>
|
|
1249
|
-
<div class="cmd-actions">
|
|
1250
|
-
<button class="cta" id="submitPreview" type="button">
|
|
1251
|
-
<svg viewBox="0 0 24 24" fill="none"><path d="M5 12h14M13 6l6 6-6 6" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>
|
|
1252
|
-
Submit for preview
|
|
1253
|
-
</button>
|
|
1254
|
-
</div>
|
|
1255
|
-
<p class="cmd-note">This packages your changes and kicks off a compliance-reviewed preview build. Once it reconciles cleanly, <code>tot ship</code> promotes it live — it always shows you the diff and asks for a confirm first.</p>
|
|
1256
|
-
</div>
|
|
1257
|
-
</section>
|
|
1258
|
-
</div>
|
|
1259
|
-
</section>
|
|
1260
|
-
)}
|
|
1261
|
-
|
|
1262
|
-
{/* Activity ledger — positioned AFTER the stations. Keeps the ids the activity
|
|
1263
|
-
renderer targets (#haTable/#haBody/#haEmpty/#haToggle). Celebration is now the
|
|
1264
|
-
Step-2 node stamp + toast, so the old inline banner is gone. */}
|
|
1265
|
-
{hostedActivity && (
|
|
1266
|
-
<section class="hosted-activity" id="hostedActivity" hidden>
|
|
1267
|
-
<div class="ha-head">
|
|
1268
|
-
<h2>Your activity</h2>
|
|
1269
|
-
<span class="ha-sub">reported by <code>tot dev</code> on your machine</span>
|
|
1270
|
-
</div>
|
|
1271
|
-
<p class="ha-empty" id="haEmpty">No saves yet — that's Step 2. Edit a file locally and it appears here.</p>
|
|
1272
|
-
<table class="ha-table" id="haTable" hidden>
|
|
1273
|
-
<tbody id="haBody"></tbody>
|
|
1274
|
-
</table>
|
|
1275
|
-
<button class="ha-toggle" id="haToggle" type="button" hidden></button>
|
|
1276
|
-
</section>
|
|
1277
|
-
)}
|
|
1278
|
-
|
|
1279
|
-
{isLocalDevLoop && (
|
|
1280
|
-
<section class="stage" id="stage-edit" hidden>
|
|
1281
|
-
<div class="stage-body">
|
|
1282
|
-
<div class="celebrate pop-in" id="cel-run" hidden>
|
|
1283
|
-
<span class="chk">✓</span><span>You're live — your store is running on this machine. Now let's change something.</span>
|
|
1284
|
-
</div>
|
|
1285
|
-
<span class="kicker">Step 2</span>
|
|
1286
|
-
<h1>Make your first edit.</h1>
|
|
1287
|
-
<p class="goal">Open a content file in your editor, change one line, and save. Your store reloads on its own — watch the loop react below.</p>
|
|
1288
|
-
|
|
1289
|
-
<div class="term">
|
|
1290
|
-
<div class="term-bar"><span class="dots"><span class="dot r"></span><span class="dot y"></span><span class="dot g"></span></span><span class="ttl">your editor</span></div>
|
|
1291
|
-
<div class="term-body"><span class="c"># open your store's homepage source, edit the headline, save</span>
|
|
1292
|
-
<span class="cmd">content/home.html</span></div>
|
|
1293
|
-
</div>
|
|
1294
|
-
|
|
1295
|
-
<div class="detect" id="detect-edit" data-listening="true">
|
|
1296
|
-
<span class="radar" aria-hidden="true"></span>
|
|
1297
|
-
<span class="txt" id="detectEditTxt">Waiting for your first save… save a watched file and it shows up here instantly.</span>
|
|
1298
|
-
</div>
|
|
1299
|
-
|
|
1300
|
-
<div class="preview-embed" id="previewEmbed">
|
|
1301
|
-
<div class="pe-bar">
|
|
1302
|
-
<span class="dots"><span class="dot r"></span><span class="dot y"></span><span class="dot g"></span></span>
|
|
1303
|
-
<span class="pe-url" id="previewUrl">localhost preview</span>
|
|
1304
|
-
<button type="button" class="pe-expand" id="previewExpand">⤢ Full screen</button>
|
|
1305
|
-
</div>
|
|
1306
|
-
{/* src is set client-side from /__tot/dev-loop-status (the DEV tenant) —
|
|
1307
|
-
NOT the SSR appDomain, which runs in workerd and can't see TOT_DEV_TENANT. */}
|
|
1308
|
-
<iframe class="pe-frame" id="previewFrame" title="live preview" loading="lazy"></iframe>
|
|
1309
|
-
<p class="pe-hint">Your running store. Open the <b>✦</b> widget inside it, hit <b>◎ Locate</b>, then click any element — its source file opens below with the cursor on that line.</p>
|
|
1310
|
-
</div>
|
|
1311
|
-
|
|
1312
|
-
<div class="devfiles" id="devfiles">
|
|
1313
|
-
<div class="df-head">
|
|
1314
|
-
<span class="df-title">Your store's files</span>
|
|
1315
|
-
<span class="df-path" id="filesPath">select a file to view</span>
|
|
1316
|
-
</div>
|
|
1317
|
-
<div class="df-body">
|
|
1318
|
-
<div class="df-tree" id="filesTree">Loading your files…</div>
|
|
1319
|
-
<div class="df-main">
|
|
1320
|
-
<div class="df-editbar" id="filesEditbar" hidden>
|
|
1321
|
-
<span class="df-status" id="filesStatus"></span>
|
|
1322
|
-
<span class="df-conflict" id="filesConflict" hidden>Changed on disk.
|
|
1323
|
-
<button type="button" class="df-mini" id="filesReload">Reload</button>
|
|
1324
|
-
<button type="button" class="df-mini" id="filesForce">Overwrite</button>
|
|
1325
|
-
</span>
|
|
1326
|
-
<button type="button" class="df-mini df-diffbtn" id="filesDiffBtn" hidden>Diff</button>
|
|
1327
|
-
<button type="button" class="df-mini df-publishbtn" id="filesPublishBtn" hidden>Publish</button>
|
|
1328
|
-
</div>
|
|
1329
|
-
<textarea class="df-view" id="filesView" readonly spellcheck="false" wrap="off">Pick a file on the left to view its source.</textarea>
|
|
1330
|
-
<pre class="df-diff" id="filesDiff" hidden></pre>
|
|
1331
|
-
</div>
|
|
1332
|
-
</div>
|
|
1333
|
-
</div>
|
|
1334
|
-
<p class="future-note">
|
|
1335
|
-
<span class="tag">Soon</span> Live editing is on. A visual diff and click-an-element-to-jump-to-source layer onto this panel next.
|
|
1336
|
-
</p>
|
|
1337
|
-
|
|
1338
|
-
<div class="btnrow">
|
|
1339
|
-
<button class="btn ghost" id="backToRun" type="button" data-go="run">← Back</button>
|
|
1340
|
-
<button class="btn ghost navspace" id="toNext" type="button" data-go="next" disabled>Next: what's next →</button>
|
|
1341
|
-
</div>
|
|
1342
|
-
</div>
|
|
1343
|
-
</section>
|
|
1344
|
-
)}
|
|
1345
|
-
|
|
1346
|
-
{isLocalDevLoop && (
|
|
1347
|
-
<section class="stage" id="stage-next" hidden>
|
|
1348
|
-
<div class="stage-body">
|
|
1349
|
-
<div class="celebrate pop-in" id="cel-edit" hidden>
|
|
1350
|
-
<span class="chk">✓</span><span>You changed a file and saw it on your site. That's the loop — you're in control.</span>
|
|
1351
|
-
</div>
|
|
1352
|
-
<span class="kicker">You're in control</span>
|
|
1353
|
-
<h1>You've got the loop.</h1>
|
|
1354
|
-
<p class="goal">Edit, save, see it — with no terminal round-trip. Previews, publish checks, AI assistance, and diagnostics open up from here soon.</p>
|
|
1355
|
-
<div class="btnrow">
|
|
1356
|
-
<button class="btn ghost" id="backToEdit" type="button" data-go="edit">← Back to editing</button>
|
|
1357
|
-
</div>
|
|
1358
|
-
</div>
|
|
1359
|
-
</section>
|
|
1360
|
-
)}
|
|
1361
|
-
|
|
1362
|
-
{isLocalDevLoop && (
|
|
1363
|
-
<div class="loopbar" id="loopbar" data-state="running" aria-live="polite">
|
|
1364
|
-
<div class="lb-head" id="lbHead">
|
|
1365
|
-
<span class="pill"><span class="beat"></span><span id="lbStatus">Connecting…</span></span>
|
|
1366
|
-
<span class="lu">watching <b id="lbWatch">your content</b> · <span id="lbLast">connecting…</span></span>
|
|
1367
|
-
<span class="chev">›</span>
|
|
1368
|
-
</div>
|
|
1369
|
-
<div class="lb-detail">
|
|
1370
|
-
<div class="lb-row"><span class="ic">✓</span><span class="k">Local server</span><span class="v dim" id="lbServer">—</span></div>
|
|
1371
|
-
<div class="lb-row"><span class="ic">↗</span><span class="k">Current URL</span><span class="v dim" id="lbUrl">—</span></div>
|
|
1372
|
-
<div class="lb-row"><span class="ic">✎</span><span class="k">Watched file</span><span class="v dim" id="lbFile">content/*.html</span></div>
|
|
1373
|
-
<div class="lb-row"><span class="ic">◷</span><span class="k">Last save seen</span><span class="v dim" id="lbSave" data-ts="">no saves yet</span></div>
|
|
1374
|
-
<div class="lb-row"><span class="ic">⟳</span><span class="k">Last reload sent</span><span class="v dim" id="lbReload" data-ts="">waiting for a save</span></div>
|
|
1375
|
-
<div class="lb-row"><span class="ic">≡</span><span class="k">Source</span><span class="v dim">your working tree — not published</span></div>
|
|
1376
|
-
</div>
|
|
1377
|
-
</div>
|
|
1378
|
-
)}
|
|
1379
|
-
|
|
1380
|
-
{/* d3-unhold-dev-step0-connect-mcp: un-held now that MCP access follows the tot20
|
|
1381
|
-
developer grant (federated sign-in / 8425 Stage B is live) — an invited developer
|
|
1382
|
-
already has what this needs, so it's no longer aspirational. Copy/command carried
|
|
1383
|
-
over from the pre-U1-strip /dev (dev.astro @ 110f73e); the "FUNCTIONALLY PENDING"
|
|
1384
|
-
caveat from e58eab5 is dropped since the prerequisite is now real. Static, renders
|
|
1385
|
-
unconditionally like the "next" teaser grid below — not gated on capability. */}
|
|
1386
|
-
<section class="stage" id="stage-mcp">
|
|
1387
|
-
<div class="stage-body">
|
|
1388
|
-
<span class="kicker">Connect the MCP</span>
|
|
1389
|
-
<h1>Bring your AI assistant into the loop.</h1>
|
|
1390
|
-
<p class="goal">The Regulated Storefront is a native AI platform — connecting the Token of Trust MCP lets Claude, Cursor, or any MCP-speaking assistant work your store directly. It works with every major AI platform. On Claude:</p>
|
|
1391
|
-
<div class="term">
|
|
1392
|
-
<div class="term-bar">
|
|
1393
|
-
<span class="dots"><span class="dot r"></span><span class="dot y"></span><span class="dot g"></span></span><span class="ttl">Claude Code</span>
|
|
1394
|
-
<button class="term-copy" id="mcpCopyIcon" type="button" aria-label="Copy the command" title="Copy the command">
|
|
1395
|
-
<svg class="ic-copy" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>
|
|
1396
|
-
<svg class="ic-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5"/></svg>
|
|
1397
|
-
</button>
|
|
1398
|
-
</div>
|
|
1399
|
-
<div class="term-body"><span class="c"># add the Token of Trust MCP</span>
|
|
1400
|
-
<span class="cmd" id="mcpCmdText">claude mcp add --transport http tot https://mcp.tokenoftrust.com/mcp</span>
|
|
1401
|
-
<span class="c"> </span>
|
|
1402
|
-
<span class="c"># then run /mcp in Claude and sign in with your invite email</span></div>
|
|
1403
|
-
</div>
|
|
1404
|
-
<p class="cmd-note">Other assistants: point them at <code>https://mcp.tokenoftrust.com/mcp</code>. Sign in with the email your invite came to — your access follows your developer grant on this store.</p>
|
|
1405
|
-
</div>
|
|
1406
|
-
</section>
|
|
1407
|
-
|
|
1408
|
-
{/* Static "coming soon" cards — pure /dev rendering with no dependency on the
|
|
1409
|
-
isLocalDevLoop-only /__tot/* endpoints and no need for the activity bridge, so
|
|
1410
|
-
it renders unconditionally on the deployed hosted panel (and locally). Not
|
|
1411
|
-
gated on capability either — it's a teaser of what's next. */}
|
|
1412
|
-
<section class="next">
|
|
1413
|
-
<h3>The rest of the workflow</h3>
|
|
1414
|
-
<p class="sub">Previews, publish checks, AI assistance, and diagnostics. These open up once you've got the loop — coming soon.</p>
|
|
1415
|
-
<div class="nextgrid">
|
|
1416
|
-
<div class="nextcard"><span class="path">Share a preview</span><span class="d">Put a branch or PR up at a shareable URL.</span><span class="soon">Coming soon</span></div>
|
|
1417
|
-
<div class="nextcard"><span class="path">Publish readiness</span><span class="d">What happens if you publish today — checks & blockers.</span><span class="soon">Coming soon</span></div>
|
|
1418
|
-
<div class="nextcard"><span class="path">Troubleshooting</span><span class="d">Doctor, logs, server health, and auth checks.</span><span class="soon">Coming soon</span></div>
|
|
1419
|
-
</div>
|
|
1420
|
-
</section>
|
|
1421
|
-
|
|
1422
|
-
<footer class="foot">
|
|
1423
|
-
<span>{session?.email ? <>Signed in as {session.email}</> : <>Not signed in</>}</span>
|
|
1424
|
-
<span class="sp"></span>
|
|
1425
|
-
{signedIn ? <a href="/auth/logout">Sign out</a> : null}
|
|
1426
|
-
</footer>
|
|
1427
|
-
</div>
|
|
1428
|
-
|
|
1429
|
-
<div class="toast" id="toast"><span class="ok">✓</span><span id="toastMsg">Copied</span></div>
|
|
1430
|
-
|
|
1431
|
-
{/* Item 7/10 — Step 3's "Submit for preview" CTA opens this "coming soon" dialog;
|
|
1432
|
-
the compliance-reviewed preview pipeline isn't wired into this panel yet. */}
|
|
1433
|
-
{hostedActivity && (
|
|
1434
|
-
<dialog class="cs-dialog" id="comingSoon" aria-label="Submit for preview — coming soon">
|
|
1435
|
-
<div class="cs-body">
|
|
1436
|
-
<span class="cs-emoji" aria-hidden="true">🚧</span>
|
|
1437
|
-
<h2>Preview submissions are coming soon</h2>
|
|
1438
|
-
<p>Submitting a change for a compliance-reviewed preview build isn't wired into this panel yet. For now, keep iterating locally — your saves land in <strong>Your activity</strong> above in real time.</p>
|
|
1439
|
-
<button type="button" class="cta" id="comingSoonClose">Got it</button>
|
|
1440
|
-
</div>
|
|
1441
|
-
</dialog>
|
|
1442
|
-
)}
|
|
1443
|
-
|
|
1444
|
-
{isLocalDevLoop && (
|
|
1445
|
-
<dialog class="pe-dialog" id="previewDialog" aria-label={`${appDomain} — full-screen preview`}>
|
|
1446
|
-
<div class="pe-bar">
|
|
1447
|
-
<span class="pe-url">{isLocalDevLoop ? "localhost preview" : "your store"} · {appDomain}</span>
|
|
1448
|
-
<button type="button" class="pe-expand" id="previewClose">✕ Close</button>
|
|
1449
|
-
</div>
|
|
1450
|
-
<iframe class="ped-frame" id="previewDialogFrame" title={`${appDomain} — full-screen preview`}></iframe>
|
|
1451
|
-
</dialog>
|
|
1452
|
-
)}
|
|
1453
|
-
|
|
1454
|
-
{isLocalDevLoop && (
|
|
1455
|
-
<button class="ask-fab" id="askFab" type="button">
|
|
1456
|
-
<svg viewBox="0 0 24 24" fill="none"><path d="M4 5.5A1.5 1.5 0 0 1 5.5 4h13A1.5 1.5 0 0 1 20 5.5v9a1.5 1.5 0 0 1-1.5 1.5H9l-4 3.5v-3.5H5.5A1.5 1.5 0 0 1 4 14.5v-9Z" stroke="currentColor" stroke-width="1.8" stroke-linejoin="round"/></svg>
|
|
1457
|
-
<span class="u">Stuck?</span> Ask
|
|
1458
|
-
</button>
|
|
1459
|
-
)}
|
|
1460
|
-
{isLocalDevLoop && (
|
|
1461
|
-
<div class="ask-panel" id="askPanel" role="dialog" aria-label="Help with the local loop">
|
|
1462
|
-
<div class="ask-head"><strong>Ask about the loop</strong><span>Quick answers for "why isn't this working."</span></div>
|
|
1463
|
-
<div class="qa">
|
|
1464
|
-
<details>
|
|
1465
|
-
<summary><span class="q">?</span>Is my local site running?<span class="chev">›</span></summary>
|
|
1466
|
-
<div class="ans"><span id="ans-running">Checking…</span></div>
|
|
1467
|
-
</details>
|
|
1468
|
-
<details>
|
|
1469
|
-
<summary><span class="q">?</span>Why didn't my change show up?<span class="chev">›</span></summary>
|
|
1470
|
-
<div class="ans"><span id="ans-change">Save a watched file and I'll compare when it was seen vs when your store reloaded.</span></div>
|
|
1471
|
-
</details>
|
|
1472
|
-
<details>
|
|
1473
|
-
<summary><span class="q">?</span>Did the watcher see my save?<span class="chev">›</span></summary>
|
|
1474
|
-
<div class="ans"><span id="ans-watcher">The Monitor's <b>Last save seen</b> updates the instant a watched file changes. If it doesn't move after you save, the watcher missed it — usually after a rename or delete. Restart to re-scan.</span></div>
|
|
1475
|
-
</details>
|
|
1476
|
-
<details>
|
|
1477
|
-
<summary><span class="q">?</span>Am I editing the rendered source?<span class="chev">›</span></summary>
|
|
1478
|
-
<div class="ans">Yes — you're editing your <b>working tree</b>, the same file the page renders from. Nothing publishes; only your local preview sees it.</div>
|
|
1479
|
-
</details>
|
|
1480
|
-
<details>
|
|
1481
|
-
<summary><span class="q">↻</span>Restart the local preview.<span class="chev">›</span></summary>
|
|
1482
|
-
<div class="ans">Reconnects this page to the dev server and re-reads loop state — fixes a preview that's gone stale. If the server is fully down, run <code>tot start</code> in your terminal.
|
|
1483
|
-
<div style="margin-top:.6rem"><button class="btn primary" id="restartBtn" type="button" style="background:var(--accent);color:#fff;border-color:transparent">Reconnect preview</button></div>
|
|
1484
|
-
</div>
|
|
1485
|
-
</details>
|
|
1486
|
-
</div>
|
|
1487
|
-
<div class="ask-foot">Scoped to the local loop.</div>
|
|
1488
|
-
</div>
|
|
1489
|
-
)}
|
|
1490
|
-
|
|
1491
|
-
<script>
|
|
1492
|
-
import { initThemeToggle } from "@/lib/dev/cockpit/themeToggle";
|
|
1493
|
-
initThemeToggle();
|
|
1494
|
-
</script>
|
|
1495
|
-
|
|
1496
|
-
<script>
|
|
1497
|
-
import { initMcpCopy } from "@/lib/dev/cockpit/mcpCopy";
|
|
1498
|
-
initMcpCopy();
|
|
1499
|
-
</script>
|
|
1500
|
-
|
|
1501
|
-
<script>
|
|
1502
|
-
import { initInfoPopover } from "@/lib/dev/cockpit/infoPopover";
|
|
1503
|
-
initInfoPopover();
|
|
1504
|
-
</script>
|
|
1505
|
-
|
|
1506
|
-
<script>
|
|
1507
|
-
import { initCopyCommand } from "@/lib/dev/cockpit/copyCommand";
|
|
1508
|
-
initCopyCommand();
|
|
1509
|
-
</script>
|
|
1510
|
-
|
|
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
|
-
)}
|
|
1547
|
-
</body>
|
|
1548
|
-
</html>
|