@tokenoftrust/storefront-runner 2.0.2 → 2.0.3
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/package.json +1 -1
- 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/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/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,2983 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
/**
|
|
3
|
-
* Publish tab — the go-live surface (handoffs 2026-08-05-admin-golive-surface
|
|
4
|
-
* + 2026-08-05-domain-connect-ux). Replaces the terminal runbook for a
|
|
5
|
-
* permissioned operator:
|
|
6
|
-
*
|
|
7
|
-
* - OPEN CHANGE QUEUE from GET /api/changes ({ current, changes }) with
|
|
8
|
-
* per-change reconcile/compliance state, preview link, head sha, and age.
|
|
9
|
-
* - ACCEPT TO PREVIEW (POST /api/changes/integrate — the accept-to-preview
|
|
10
|
-
* split; advances the shared `preview` aggregate, does NOT go live) and
|
|
11
|
-
* REJECT (with a note) per change. Going live is the aggregate card's
|
|
12
|
-
* exact-digest PUBLISH (POST /api/changes/ship), never a per-change accept.
|
|
13
|
-
* - LIVE STATE: the current live version (live-channel pointer) + the
|
|
14
|
-
* static-republish dispatch status for static-publish tenants (fired /
|
|
15
|
-
* skipped / sink-unbound notice).
|
|
16
|
-
* - DOMAIN section (static-publish tenants): apex connect + rollback via the
|
|
17
|
-
* www-domain dispatch endpoints — typed-confirmation connect (owner-only),
|
|
18
|
-
* always-visible rollback with the captured-records timestamp, in-flight
|
|
19
|
-
* run + legible failures.
|
|
20
|
-
*
|
|
21
|
-
* PERMISSIONS (server-authoritative; mirrored here for UX only):
|
|
22
|
-
* - Ship actions require an owner/admin session (`resolveOwnerSession`'s
|
|
23
|
-
* OWNER_CAPABILITIES) — others see the queue read-only with a sign-in note.
|
|
24
|
-
* - Non-premium tenants see the go-live control as a PAYWALL UPSELL — never
|
|
25
|
-
* hidden, never "coming soon" (recorded product direction). The
|
|
26
|
-
* entitlement signal is the existing premium model: a custom-domain
|
|
27
|
-
* tenant is premium/pilot; a `<label>.tokenoftrust.store` self-serve store
|
|
28
|
-
* is not. TODO(paywall workstream): replace with the real billing
|
|
29
|
-
* entitlement when one exists.
|
|
30
|
-
*/
|
|
31
|
-
import { readViewerSession } from "@/lib/auth/route";
|
|
32
|
-
import { withBase } from "@/lib/basePath";
|
|
33
|
-
import { readEnv } from "@/lib/env";
|
|
34
|
-
import { resolveViewerShipCapability } from "@/lib/change";
|
|
35
|
-
import { resolveLiveVersion } from "@/lib/change/views/live-version";
|
|
36
|
-
import { isPublicSiteCurrent, staticLiveManifestUrl } from "@/lib/change/views/live-digest";
|
|
37
|
-
import { isHostedStoreDomain, isInternalTestTenant } from "@tot/public-runtime";
|
|
38
|
-
import { mintPreviewShareToken } from "@/lib/preview/previewShareLink";
|
|
39
|
-
|
|
40
|
-
const { envelope } = Astro.props;
|
|
41
|
-
const { tenant, basePath } = Astro.locals;
|
|
42
|
-
|
|
43
|
-
// Viewer capability, mirrored from the same session model the API routes gate
|
|
44
|
-
// on (resolveOwnerSession admits capability owner|admin). UX-only: the server
|
|
45
|
-
// re-authorizes every action. Checked against `envelope.targetTenant`, not the
|
|
46
|
-
// routed `tenant.appDomain` — a ToT-staff viewer operating a store other than
|
|
47
|
-
// the one that served this admin route must be checked against the tenant
|
|
48
|
-
// they SELECTED, else a real grant on the selected tenant reads as "none".
|
|
49
|
-
const viewerSession = await readViewerSession(Astro);
|
|
50
|
-
// Session capability counts only when the session's resource IS the selected tenant.
|
|
51
|
-
const sessionCapability =
|
|
52
|
-
viewerSession && viewerSession.resource === envelope.targetTenant
|
|
53
|
-
? viewerSession.capability
|
|
54
|
-
: undefined;
|
|
55
|
-
const viewerCapability =
|
|
56
|
-
Astro.locals.viewer?.capability ?? sessionCapability ?? "none";
|
|
57
|
-
// A signed-in DEVELOPER (not owner/admin) may hold an explicit, owner-revocable
|
|
58
|
-
// ship-on-behalf grant. Resolve it LIVE from the MCP subject-capability store —
|
|
59
|
-
// NEVER the coarse role, NEVER a client header — so a ship-granted developer sees
|
|
60
|
-
// the queue + Go-live/Reject. This MIRRORS the server (resolveShipSession /
|
|
61
|
-
// effectiveShipCapability); the server re-authorizes every action. UX-only.
|
|
62
|
-
let viewerCanShip = viewerCapability === "owner" || viewerCapability === "admin";
|
|
63
|
-
if (!viewerCanShip && Astro.locals.viewer?.email) {
|
|
64
|
-
const shipGrant = await resolveViewerShipCapability(
|
|
65
|
-
Astro.locals.viewer.email,
|
|
66
|
-
envelope.targetTenant,
|
|
67
|
-
);
|
|
68
|
-
if (shipGrant === "ship-on-behalf") viewerCanShip = true;
|
|
69
|
-
}
|
|
70
|
-
const viewerIsOwner = viewerCapability === "owner";
|
|
71
|
-
// Apex/domain cutover is granted to owner OR admin (decision: apex-cutover =
|
|
72
|
-
// owner+admin). This MIRRORS the server, whose `apexCutover` = `isOwner` over
|
|
73
|
-
// OWNER_CAPABILITIES {owner, admin} (see change/route.ts decideIsOwner +
|
|
74
|
-
// change/shipCapabilities.ts) — this UI gate was wrongly owner-only, stricter
|
|
75
|
-
// than the gate the server actually enforces. UX-only: the server re-authorizes.
|
|
76
|
-
const viewerCanApexCutover = viewerCapability === "owner" || viewerCapability === "admin";
|
|
77
|
-
|
|
78
|
-
// Entitlement: the existing premium signal (custom-domain tenant = premium).
|
|
79
|
-
// TODO(paywall workstream): swap for the real billing entitlement.
|
|
80
|
-
const goLiveEntitled = !isHostedStoreDomain(tenant.appDomain);
|
|
81
|
-
const internalTestTenant = isInternalTestTenant(tenant);
|
|
82
|
-
|
|
83
|
-
const staticPublishEnabled = Boolean(
|
|
84
|
-
tenant.staticPublish && tenant.staticPublish.enabled !== false,
|
|
85
|
-
);
|
|
86
|
-
// Presence-only signal for the dispatch sink (never the value).
|
|
87
|
-
const dispatchConfigured = Boolean((await readEnv("WWW_PUBLISH_DISPATCH_TOKEN"))?.trim());
|
|
88
|
-
|
|
89
|
-
// "What does the public site actually serve right now?" — the read-back half of
|
|
90
|
-
// the static-publish seam (resolveLiveVersion's staticLiveDigest), computed here
|
|
91
|
-
// so it's ALWAYS visible rather than only living in GET /api/changes JSON. This is
|
|
92
|
-
// the exact gap that can let tokenoftrust.com's published public site
|
|
93
|
-
// sit two days stale behind several accepted-but-unpublished changes with nothing
|
|
94
|
-
// in the console calling it out — going live and staying live for the PUBLIC
|
|
95
|
-
// static target are two different facts, and this card makes the second one
|
|
96
|
-
// honest instead of assumed. Best-effort: resolveLiveVersion never throws.
|
|
97
|
-
// BUDGETED: resolveLiveVersion chains several TENANT_CACHE reads (resolvePreviewTenant,
|
|
98
|
-
// resolveChannelVersion, readSiteVersion) that are pathologically slow + variable on the
|
|
99
|
-
// preview worker (measured 2-10s EACH, spiking together — a KV/storage-latency issue, not
|
|
100
|
-
// this code). It runs in blocking SSR frontmatter, so it gated the whole admin document
|
|
101
|
-
// stream. It's best-effort display chrome ("what does live/ serve right now"), and the
|
|
102
|
-
// client-side GET /api/changes already returns `current`, so cap it: on timeout the card
|
|
103
|
-
// renders from the client fetch instead of blocking first paint.
|
|
104
|
-
const liveVersion = staticPublishEnabled
|
|
105
|
-
? await Promise.race([
|
|
106
|
-
resolveLiveVersion(tenant.appDomain),
|
|
107
|
-
new Promise<null>((resolve) => setTimeout(() => resolve(null), 1500)),
|
|
108
|
-
])
|
|
109
|
-
: null;
|
|
110
|
-
const publicSiteDigest = liveVersion?.staticLiveDigest ?? null;
|
|
111
|
-
// The actual verdict: does the public target's manifest sha match what was last
|
|
112
|
-
// promoted to "live" here? Both are real tenant Gitea shas — a direct,
|
|
113
|
-
// honest comparison, not a heuristic. See liveDigest.ts's isPublicSiteCurrent.
|
|
114
|
-
const publicSiteVerdict = publicSiteDigest
|
|
115
|
-
? isPublicSiteCurrent(liveVersion?.versionId ?? null, publicSiteDigest)
|
|
116
|
-
: null;
|
|
117
|
-
|
|
118
|
-
const changesEndpoint = withBase(basePath || "", "/api/changes");
|
|
119
|
-
// The branch-integration lifecycle SPLIT of accept from publish:
|
|
120
|
-
// - integrateEndpoint = "accept = integrate into the protected preview
|
|
121
|
-
// aggregate" (POST /api/changes/integrate → TenantIntegrationQueue.enqueue).
|
|
122
|
-
// This is the candidate-queue "Accept to preview" action — it advances the
|
|
123
|
-
// shared aggregate, it does NOT go live. Returns an IntegrateOutcome
|
|
124
|
-
// (queueState/runState/statusMessage), never a bare merged:true.
|
|
125
|
-
// - shipEndpoint = the exact-digest go-live (POST /api/changes/ship →
|
|
126
|
-
// AggregateShipOrchestrator.plan/ship). The aggregate card's "Publish"
|
|
127
|
-
// action: it ships the reviewed pinned SHA + digest, double-gated
|
|
128
|
-
// (paywall + human confirm). The read-only plan is served on GET /api/changes
|
|
129
|
-
// (`plan`), so the confirm surface never has to POST to preview it.
|
|
130
|
-
const integrateEndpoint = withBase(basePath || "", "/api/changes/integrate");
|
|
131
|
-
const shipEndpoint = withBase(basePath || "", "/api/changes/ship");
|
|
132
|
-
// Build-on-demand for a synthetic not-built PR row: materialize the PR's
|
|
133
|
-
// hosted preview (INERT — flips no live channel). Gated server-side by buildGate
|
|
134
|
-
// (the ship-on-behalf floor); the endpoint resolves the head sha via forge PR-read
|
|
135
|
-
// when the row supplies only the PR number.
|
|
136
|
-
const buildEndpoint = withBase(basePath || "", "/api/preview/build");
|
|
137
|
-
// The ship-operations workspace woven into this tab: versions/promotion
|
|
138
|
-
// history, per-candidate diff, and rollback (through the ONE rollback
|
|
139
|
-
// orchestrator). Read endpoints mirror the change-queue's auth/scope.
|
|
140
|
-
const versionsEndpoint = withBase(basePath || "", "/api/changes/versions");
|
|
141
|
-
const rollbackEndpoint = withBase(basePath || "", "/api/changes/rollback");
|
|
142
|
-
// Branch retention: GET dry-run classification / POST guarded delete.
|
|
143
|
-
const branchesEndpoint = withBase(basePath || "", "/api/changes/branches");
|
|
144
|
-
// The client builds `${diffEndpointBase}/<changeId>/diff` per candidate.
|
|
145
|
-
const diffEndpointBase = changesEndpoint;
|
|
146
|
-
// The live store's root on THIS surface (basePath-aware): where the published
|
|
147
|
-
// digest actually serves. Rendered into "already live" copy + the publish
|
|
148
|
-
// ceremony's "View your live store" CTA.
|
|
149
|
-
const liveHref = withBase(basePath || "", "/");
|
|
150
|
-
|
|
151
|
-
// LIVE-SITE ENTRY POINTS — surfaced explicitly so "where does my store actually
|
|
152
|
-
// serve?" is never a mystery (it was). Two public URLs, always shown + clickable:
|
|
153
|
-
// 1. Hosted store — THIS worker at the request host + tenant base path
|
|
154
|
-
// (e.g. storefront.tokenoftrust.store/tokenoftrust.com/).
|
|
155
|
-
// 2. Public site — the static target's canonical URL, derived from the SAME
|
|
156
|
-
// manifest URL the freshness read-back probes (WWW_PUBLISH_VERIFY_URL or
|
|
157
|
-
// https://<canonicalDomain>/manifest.json), minus the /manifest.json tail
|
|
158
|
-
// (e.g. published.tokenoftrust.com). Only for static-publish tenants; a
|
|
159
|
-
// hosted-only tenant has no separate public site. Computed PURELY (no probe)
|
|
160
|
-
// so the URL shows even when the freshness probe above timed out.
|
|
161
|
-
const requestHost = Astro.request.headers.get("host") ?? Astro.url.host;
|
|
162
|
-
const liveStoreUrl = `${Astro.url.protocol}//${requestHost}${liveHref}`;
|
|
163
|
-
const publicManifestUrl = staticPublishEnabled
|
|
164
|
-
? staticLiveManifestUrl({
|
|
165
|
-
canonicalDomain: tenant.canonicalDomain,
|
|
166
|
-
// Per-tenant override first (never bleeds across stores), then the global env
|
|
167
|
-
// escape hatch, then the canonical-apex default — mirrors resolveLiveVersion.
|
|
168
|
-
explicitUrl: tenant.staticPublish?.verifyUrl ?? (await readEnv("WWW_PUBLISH_VERIFY_URL")) ?? null,
|
|
169
|
-
})
|
|
170
|
-
: null;
|
|
171
|
-
const publicSiteUrl = publicManifestUrl
|
|
172
|
-
? publicManifestUrl.replace(/\/manifest\.json$/, "/")
|
|
173
|
-
: null;
|
|
174
|
-
const stripScheme = (u: string): string => u.replace(/^https?:\/\//, "");
|
|
175
|
-
|
|
176
|
-
// r08 — a signed, tenant-scoped, expiring `?__preview` capability link an owner can
|
|
177
|
-
// copy and hand to a non-technical stakeholder (view-only, no sign-in required).
|
|
178
|
-
// Minted server-side so the signing secret never reaches the browser (mirrors
|
|
179
|
-
// preview/dashboard.astro). Signs the tenant_id claim: the shared-preview
|
|
180
|
-
// middleware verifies against `tenant.tenant_id` and the candidate renderer accepts
|
|
181
|
-
// tenant_id too, so ONE token authorizes BOTH the shared next-release preview AND
|
|
182
|
-
// per-candidate `preview/pr/<N>` links. Only minted for a ship-capable viewer, and
|
|
183
|
-
// absent when signing isn't configured — the copy buttons then stay hidden rather
|
|
184
|
-
// than offering a dead link. TTL 24h so a link copied late in a long session still
|
|
185
|
-
// works; a reload re-mints a fresh one.
|
|
186
|
-
const previewShareToken = viewerCanShip ? await mintPreviewShareToken(tenant.tenant_id) : null;
|
|
187
|
-
|
|
188
|
-
const domainStatusEndpoint = withBase(basePath || "", "/api/domain/status");
|
|
189
|
-
const domainDispatchEndpoint = withBase(basePath || "", "/api/domain/dispatch");
|
|
190
|
-
// The machine-verifiable apex-cutover readiness gate — the SAME server gate the
|
|
191
|
-
// `tot go-live` CLI reads, so the admin display and the CLI never diverge.
|
|
192
|
-
const domainReadinessEndpoint = withBase(basePath || "", "/api/domain/readiness");
|
|
193
|
-
---
|
|
194
|
-
|
|
195
|
-
<!-- agent-hook:publish-tab -->
|
|
196
|
-
<section
|
|
197
|
-
id="publish"
|
|
198
|
-
class="band publish-tab"
|
|
199
|
-
aria-labelledby="admin-tab-publish publish-title"
|
|
200
|
-
data-admin-panel
|
|
201
|
-
data-agent-scope="publish"
|
|
202
|
-
role="tabpanel"
|
|
203
|
-
data-changes-endpoint={changesEndpoint}
|
|
204
|
-
data-integrate-endpoint={integrateEndpoint}
|
|
205
|
-
data-ship-endpoint={shipEndpoint}
|
|
206
|
-
data-build-endpoint={buildEndpoint}
|
|
207
|
-
data-versions-endpoint={versionsEndpoint}
|
|
208
|
-
data-rollback-endpoint={rollbackEndpoint}
|
|
209
|
-
data-branches-endpoint={branchesEndpoint}
|
|
210
|
-
data-diff-endpoint-base={diffEndpointBase}
|
|
211
|
-
data-domain-status-endpoint={domainStatusEndpoint}
|
|
212
|
-
data-domain-dispatch-endpoint={domainDispatchEndpoint}
|
|
213
|
-
data-domain-readiness-endpoint={domainReadinessEndpoint}
|
|
214
|
-
data-repo={tenant.appDomain}
|
|
215
|
-
data-app-domain={tenant.appDomain}
|
|
216
|
-
data-canonical-domain={tenant.canonicalDomain ?? ""}
|
|
217
|
-
data-preview-share-token={previewShareToken ?? ""}
|
|
218
|
-
data-live-href={liveHref}
|
|
219
|
-
data-can-ship={viewerCanShip ? "true" : "false"}
|
|
220
|
-
data-is-owner={viewerIsOwner ? "true" : "false"}
|
|
221
|
-
data-entitled={goLiveEntitled ? "true" : "false"}
|
|
222
|
-
data-static-publish={staticPublishEnabled ? "true" : "false"}
|
|
223
|
-
data-dispatch-configured={dispatchConfigured ? "true" : "false"}
|
|
224
|
-
data-public-site-host={publicSiteUrl ? stripScheme(publicSiteUrl).replace(/\/$/, "") : ""}
|
|
225
|
-
>
|
|
226
|
-
<div class="section-title">
|
|
227
|
-
<div>
|
|
228
|
-
<h2 id="publish-title">Publish</h2>
|
|
229
|
-
<p>Review what's changed, then publish your next release when you're ready.</p>
|
|
230
|
-
</div>
|
|
231
|
-
<div class="actions">
|
|
232
|
-
<button class="btn" type="button" data-publish-refresh>
|
|
233
|
-
<svg><use href="#i-refresh"></use></svg>Refresh
|
|
234
|
-
</button>
|
|
235
|
-
</div>
|
|
236
|
-
</div>
|
|
237
|
-
|
|
238
|
-
{internalTestTenant && (
|
|
239
|
-
<div class="ship-gate amber">
|
|
240
|
-
<div class="ship-gate-main">
|
|
241
|
-
<span class="seal"><svg><use href="#i-alert"></use></svg></span>
|
|
242
|
-
<div>
|
|
243
|
-
<b>Internal test store</b>
|
|
244
|
-
<span>This store exists for testing only and is blocked from going live.</span>
|
|
245
|
-
</div>
|
|
246
|
-
</div>
|
|
247
|
-
</div>
|
|
248
|
-
)}
|
|
249
|
-
|
|
250
|
-
{!goLiveEntitled && (
|
|
251
|
-
<div class="ship-gate amber" data-publish-paywall>
|
|
252
|
-
<div class="ship-gate-main">
|
|
253
|
-
<span class="seal"><svg><use href="#i-card"></use></svg></span>
|
|
254
|
-
<div>
|
|
255
|
-
<b>Publishing to your live store is part of the paid plan</b>
|
|
256
|
-
<span>
|
|
257
|
-
Previewing and reviewing changes stays free. Upgrade to take changes live
|
|
258
|
-
and publish your public site.
|
|
259
|
-
</span>
|
|
260
|
-
</div>
|
|
261
|
-
</div>
|
|
262
|
-
<a class="btn primary" href="mailto:success@tokenoftrust.com?subject=Upgrade%20store%20publishing">
|
|
263
|
-
Upgrade to publish
|
|
264
|
-
</a>
|
|
265
|
-
</div>
|
|
266
|
-
)}
|
|
267
|
-
|
|
268
|
-
{goLiveEntitled && !viewerCanShip && (
|
|
269
|
-
<div class="ship-gate amber">
|
|
270
|
-
<div class="ship-gate-main">
|
|
271
|
-
<span class="seal"><svg><use href="#i-shield"></use></svg></span>
|
|
272
|
-
<div>
|
|
273
|
-
<b>Read-only view</b>
|
|
274
|
-
<span>Going live requires an owner or admin sign-in for this store.</span>
|
|
275
|
-
</div>
|
|
276
|
-
</div>
|
|
277
|
-
</div>
|
|
278
|
-
)}
|
|
279
|
-
|
|
280
|
-
<!-- State-driven "next action" hero (r01) — the SINGLE top-of-tab state
|
|
281
|
-
element. ONE sentence + ONE primary CTA, derived by lib/publish/publishHero.ts
|
|
282
|
-
from the SAME /api/changes read the queue + aggregate already fetch (no extra
|
|
283
|
-
read). It ABSORBS the two former standalone banners: the merge-doctor health
|
|
284
|
-
summary (B1) and the platform-fault "Report to support" escalation (B3) are
|
|
285
|
-
folded into the hero's state model, expressed through this one element — a
|
|
286
|
-
merge conflict/platform blocker turns the hero red/urgent instead of rendering
|
|
287
|
-
a separate banner. Rendered by renderHero() in adminPublishTab.ts; starts
|
|
288
|
-
hidden until the first /api/changes read resolves. The r03 stale-public-site
|
|
289
|
-
alarm sits alongside it in this region (a distinct top-level signal that
|
|
290
|
-
deep-links into Live Site), so there is ONE hero/alert region, not floating
|
|
291
|
-
banners. Urgency is never color-only: the tone drives the icon + an explicit
|
|
292
|
-
"Action needed" label. -->
|
|
293
|
-
<div class="publish-hero-region" data-publish-hero-region>
|
|
294
|
-
<div class="publish-hero" data-publish-hero data-tone="calm" hidden role="status" aria-live="polite">
|
|
295
|
-
<div class="publish-hero-main">
|
|
296
|
-
<span class="publish-hero-icon" data-publish-hero-icon aria-hidden="true">
|
|
297
|
-
<svg><use href="#i-check"></use></svg>
|
|
298
|
-
</span>
|
|
299
|
-
<div class="publish-hero-copy">
|
|
300
|
-
<span class="publish-hero-kicker" data-publish-hero-kicker hidden>Action needed</span>
|
|
301
|
-
<b class="publish-hero-headline" data-publish-hero-headline></b>
|
|
302
|
-
<span class="publish-hero-detail" data-publish-hero-detail></span>
|
|
303
|
-
</div>
|
|
304
|
-
</div>
|
|
305
|
-
<div class="publish-hero-actions">
|
|
306
|
-
<button class="btn primary publish-hero-cta" type="button" data-publish-hero-cta hidden></button>
|
|
307
|
-
<a class="btn primary publish-hero-cta" data-publish-hero-cta-link hidden href="mailto:success@tokenoftrust.com"></a>
|
|
308
|
-
</div>
|
|
309
|
-
</div>
|
|
310
|
-
|
|
311
|
-
<!-- r03: stale-public-site alarm, LIFTED to top-level. The "public site is
|
|
312
|
-
behind the live pointer" verdict (resolveLiveVersion / isPublicSiteCurrent)
|
|
313
|
-
used to sit buried inside the collapsible Live Site section; when the public
|
|
314
|
-
target is behind, surface it here as a top-level amber alarm that deep-links
|
|
315
|
-
into Live Site (where the full breakdown stays nested). SSR-driven from the
|
|
316
|
-
same publicSiteVerdict the Live Site chip reads — shown only for a
|
|
317
|
-
static-publish tenant whose public target is confirmed behind. -->
|
|
318
|
-
{staticPublishEnabled && publicSiteVerdict && publicSiteVerdict.status === "behind" && (
|
|
319
|
-
<div class="risk-alert amber publish-stale-site" data-publish-stale-site role="status" aria-live="polite">
|
|
320
|
-
<div class="risk-alert-main">
|
|
321
|
-
<svg><use href="#i-alert"></use></svg>
|
|
322
|
-
<div>
|
|
323
|
-
<b>Public site is behind — re-publish to catch it up</b>
|
|
324
|
-
<span>
|
|
325
|
-
Your public site is still serving an older release
|
|
326
|
-
(<code class="publish-mono">{publicSiteVerdict.publishedSha.slice(0, 12)}</code>)
|
|
327
|
-
while your live pointer has moved on to
|
|
328
|
-
(<code class="publish-mono">{publicSiteVerdict.livePointerSha.slice(0, 12)}</code>).
|
|
329
|
-
Re-publish, or open the publishing details for the full breakdown.
|
|
330
|
-
</span>
|
|
331
|
-
</div>
|
|
332
|
-
</div>
|
|
333
|
-
<a class="btn" href="#publish-live-site" data-publish-stale-site-link>Open publishing details</a>
|
|
334
|
-
</div>
|
|
335
|
-
)}
|
|
336
|
-
</div>
|
|
337
|
-
|
|
338
|
-
<div class="grid cols-3">
|
|
339
|
-
<div class="metric">
|
|
340
|
-
<span>Live version</span>
|
|
341
|
-
<strong>
|
|
342
|
-
<!-- The human version label ("v42") links straight to the live store; the
|
|
343
|
-
raw SHA is demoted to the Technical details disclosure below. -->
|
|
344
|
-
<a class="live-version-link" data-publish-live-version-link href={liveHref}
|
|
345
|
-
target="_blank" rel="noopener" hidden>—</a>
|
|
346
|
-
<span data-publish-live-version-none>—</span>
|
|
347
|
-
</strong>
|
|
348
|
-
<small data-publish-live-source>What shoppers see right now</small>
|
|
349
|
-
<details class="metric-tech" data-publish-live-tech hidden>
|
|
350
|
-
<summary>Technical details</summary>
|
|
351
|
-
<span class="publish-mono subtle" data-publish-live-sha>—</span>
|
|
352
|
-
</details>
|
|
353
|
-
</div>
|
|
354
|
-
<div class="metric">
|
|
355
|
-
<span>Published</span>
|
|
356
|
-
<strong data-publish-live-at>—</strong>
|
|
357
|
-
<small>When the live version last changed</small>
|
|
358
|
-
<small data-publish-live-shipped-by></small>
|
|
359
|
-
</div>
|
|
360
|
-
<div class="metric">
|
|
361
|
-
<span>Open changes</span>
|
|
362
|
-
<strong data-publish-queue-count>—</strong>
|
|
363
|
-
<small>Waiting for review or go-live</small>
|
|
364
|
-
</div>
|
|
365
|
-
</div>
|
|
366
|
-
|
|
367
|
-
{staticPublishEnabled && !dispatchConfigured && (
|
|
368
|
-
<div class="risk-alert" data-publish-sink-unbound>
|
|
369
|
-
<div class="risk-alert-main">
|
|
370
|
-
<svg><use href="#i-alert"></use></svg>
|
|
371
|
-
<div>
|
|
372
|
-
<b>Automatic public-site republish is not yet enabled on this environment</b>
|
|
373
|
-
<span>
|
|
374
|
-
Going live updates the hosted store immediately; the public-site republish
|
|
375
|
-
job is dispatched once publishing is enabled here.
|
|
376
|
-
</span>
|
|
377
|
-
</div>
|
|
378
|
-
</div>
|
|
379
|
-
</div>
|
|
380
|
-
)}
|
|
381
|
-
|
|
382
|
-
<div class="panel" id="publish-queue">
|
|
383
|
-
<details class="publish-queue-details" open>
|
|
384
|
-
<summary class="panel-head">
|
|
385
|
-
<h3><svg><use href="#i-export"></use></svg>What needs your approval?</h3>
|
|
386
|
-
<span class="chip" data-publish-queue-chip>Loading</span>
|
|
387
|
-
</summary>
|
|
388
|
-
<div class="panel-body grid">
|
|
389
|
-
<p class="note publish-status" data-publish-status role="status" aria-live="polite"></p>
|
|
390
|
-
<!-- rux1: forge-listing degraded strip. Shown ONLY when GET /api/changes
|
|
391
|
-
reports `degraded` (the open-PR listing failed) — distinct from the
|
|
392
|
-
genuine empty queue, so a forge blip never reads as "No open changes". -->
|
|
393
|
-
<div class="risk-alert" data-publish-degraded hidden role="alert">
|
|
394
|
-
<div class="risk-alert-main">
|
|
395
|
-
<svg><use href="#i-alert"></use></svg>
|
|
396
|
-
<div>
|
|
397
|
-
<b>Some open changes may not be shown</b>
|
|
398
|
-
<span>
|
|
399
|
-
We couldn't reach the pull-request list, so open changes that haven't been
|
|
400
|
-
built yet may be missing from the queue below. Built candidates are
|
|
401
|
-
unaffected. Refresh to try again.
|
|
402
|
-
</span>
|
|
403
|
-
</div>
|
|
404
|
-
</div>
|
|
405
|
-
</div>
|
|
406
|
-
<p class="note publish-pr-callout" data-publish-pr-callout hidden role="status" aria-live="polite"></p>
|
|
407
|
-
<div class="table-wrap">
|
|
408
|
-
<table class="publish-table">
|
|
409
|
-
<thead>
|
|
410
|
-
<tr>
|
|
411
|
-
<th>Change</th>
|
|
412
|
-
<th>Status</th>
|
|
413
|
-
<th>Checks</th>
|
|
414
|
-
<th>Head</th>
|
|
415
|
-
<th>Updated</th>
|
|
416
|
-
<th class="action">Actions</th>
|
|
417
|
-
</tr>
|
|
418
|
-
</thead>
|
|
419
|
-
<tbody data-publish-queue-body>
|
|
420
|
-
<tr data-publish-placeholder>
|
|
421
|
-
<td colspan="6"><span class="subtle">Loading the change queue…</span></td>
|
|
422
|
-
</tr>
|
|
423
|
-
</tbody>
|
|
424
|
-
</table>
|
|
425
|
-
</div>
|
|
426
|
-
{staticPublishEnabled && (
|
|
427
|
-
<p class="note" data-publish-dispatch-status>
|
|
428
|
-
{dispatchConfigured
|
|
429
|
-
? "Public-site republish: dispatched automatically when a change goes live."
|
|
430
|
-
: "Public-site republish: not yet enabled on this environment."}
|
|
431
|
-
</p>
|
|
432
|
-
)}
|
|
433
|
-
</div>
|
|
434
|
-
</details>
|
|
435
|
-
</div>
|
|
436
|
-
|
|
437
|
-
<!-- "See all" history: changes that LEFT the queue by being rejected. Rejecting
|
|
438
|
-
is otherwise a black hole — this collapsed panel keeps them visible (and their
|
|
439
|
-
preview reachable) so a reject is recoverable-from-history. Collapsed by
|
|
440
|
-
default (a reference, not a work surface); the whole panel is hidden by the
|
|
441
|
-
client when there are none, so it never adds empty chrome. Excludes shipped
|
|
442
|
-
(merged) changes — those live in the Live Site / versions panel. -->
|
|
443
|
-
<div class="panel" id="publish-closed" data-publish-closed-panel hidden>
|
|
444
|
-
<details class="closed-changes-details">
|
|
445
|
-
<summary class="panel-head">
|
|
446
|
-
<h3><svg><use href="#i-undo"></use></svg>Closed & rejected changes</h3>
|
|
447
|
-
<span class="chip" data-publish-closed-chip>0</span>
|
|
448
|
-
</summary>
|
|
449
|
-
<div class="panel-body grid">
|
|
450
|
-
<p class="note subtle">
|
|
451
|
-
Changes you rejected. They're not in your next release and won't go live —
|
|
452
|
-
kept here so nothing you closed silently disappears. Open a preview to review
|
|
453
|
-
what it was.
|
|
454
|
-
</p>
|
|
455
|
-
<div class="table-wrap">
|
|
456
|
-
<table class="publish-table">
|
|
457
|
-
<thead>
|
|
458
|
-
<tr>
|
|
459
|
-
<th>Change</th>
|
|
460
|
-
<th>Closed</th>
|
|
461
|
-
<th>Head</th>
|
|
462
|
-
</tr>
|
|
463
|
-
</thead>
|
|
464
|
-
<tbody data-publish-closed-body></tbody>
|
|
465
|
-
</table>
|
|
466
|
-
</div>
|
|
467
|
-
</div>
|
|
468
|
-
</details>
|
|
469
|
-
</div>
|
|
470
|
-
|
|
471
|
-
<!-- b11: the AGGREGATE card — the protected `preview` aggregate the accepted
|
|
472
|
-
candidates integrate into, its combined evidence (the last integration
|
|
473
|
-
run's honest state), and the exact-digest Publish (b09/b10 ship the
|
|
474
|
-
reviewed pinned SHA + digest, never a rebuild). -->
|
|
475
|
-
<div class="panel agg-console" id="publish-next-release" data-publish-aggregate>
|
|
476
|
-
<details class="agg-console-details" open>
|
|
477
|
-
<summary class="panel-head">
|
|
478
|
-
<h3><svg><use href="#i-box"></use></svg>Next release</h3>
|
|
479
|
-
<!-- r11 — the human release name (aggregate.name), shown beside the heading.
|
|
480
|
-
Client-populated by renderAggregate; hidden (falls back to just "Next
|
|
481
|
-
release") when the release has no name. -->
|
|
482
|
-
<span class="agg-release-name" data-aggregate-name hidden></span>
|
|
483
|
-
<!-- One-click hop to the current preview build. Shown only when a
|
|
484
|
-
genuinely-built revision exists (renderAggregate toggles it via
|
|
485
|
-
classifyBuildLink), so it can never lead to a 404. The click is
|
|
486
|
-
stop-propagated in adminPublishTab.ts so opening the preview never
|
|
487
|
-
also toggles this collapsible. -->
|
|
488
|
-
<a class="btn btn-compact" data-aggregate-open-preview target="_blank" rel="noopener" hidden>Open preview</a>
|
|
489
|
-
<!-- r08 — copy a shareable, view-only preview link for the next release to hand
|
|
490
|
-
to a non-technical stakeholder (no sign-in). Unhidden by the client only
|
|
491
|
-
when a share token was minted; the click is stop-propagated so copying
|
|
492
|
-
never also toggles this collapsible. -->
|
|
493
|
-
<button class="btn btn-compact" type="button" data-aggregate-copy-preview hidden>
|
|
494
|
-
<svg><use href="#i-copy"></use></svg><span data-copy-preview-label>Copy preview link</span>
|
|
495
|
-
</button>
|
|
496
|
-
<span class="chip" data-aggregate-chip>Loading</span>
|
|
497
|
-
</summary>
|
|
498
|
-
<div class="panel-body grid">
|
|
499
|
-
<p class="note publish-status" data-aggregate-status role="status" aria-live="polite"></p>
|
|
500
|
-
|
|
501
|
-
<!-- Keep the primary action beside the release status. The detail table can
|
|
502
|
-
be long; publishing must never be buried beneath it. -->
|
|
503
|
-
<div class="check-row agg-launch">
|
|
504
|
-
<div>
|
|
505
|
-
<b>
|
|
506
|
-
{staticPublishEnabled
|
|
507
|
-
? "Publish this release to the hosted and public sites"
|
|
508
|
-
: "Publish this release to your live store"}
|
|
509
|
-
</b>
|
|
510
|
-
<p data-aggregate-publish-detail>
|
|
511
|
-
{publicSiteUrl ? (
|
|
512
|
-
<>
|
|
513
|
-
Publishes the exact build you reviewed to the hosted store, then
|
|
514
|
-
automatically republishes <code>{stripScheme(publicSiteUrl).replace(/\/$/, "")}</code>.
|
|
515
|
-
</>
|
|
516
|
-
) : (
|
|
517
|
-
<>Publishes the exact build you reviewed — no rebuild.</>
|
|
518
|
-
)}
|
|
519
|
-
</p>
|
|
520
|
-
</div>
|
|
521
|
-
<button class="btn primary agg-publish-btn" type="button" data-aggregate-publish disabled>
|
|
522
|
-
<svg><use href="#i-export"></use></svg>
|
|
523
|
-
{staticPublishEnabled ? "Publish to both live sites" : "Publish live"}
|
|
524
|
-
</button>
|
|
525
|
-
</div>
|
|
526
|
-
|
|
527
|
-
<!-- Only visible after an actually failed aggregate rebuild. -->
|
|
528
|
-
<div class="check-row" data-aggregate-retry-row hidden>
|
|
529
|
-
<div>
|
|
530
|
-
<b>Retry the build</b>
|
|
531
|
-
<p>
|
|
532
|
-
Your change was added, but rebuilding the preview failed. Retry rebuilds it
|
|
533
|
-
and re-runs your store's checks — nothing new is added.
|
|
534
|
-
</p>
|
|
535
|
-
</div>
|
|
536
|
-
<button class="btn" type="button" data-aggregate-retry-build>
|
|
537
|
-
<svg><use href="#i-refresh"></use></svg>Retry build
|
|
538
|
-
</button>
|
|
539
|
-
</div>
|
|
540
|
-
|
|
541
|
-
<!-- AI-assembled release summary — a plain-language "what am I about to
|
|
542
|
-
publish?" ahead of the technical trust chain. Rendered ONLY when the
|
|
543
|
-
server supplies data.releaseSummary; otherwise it stays hidden and the
|
|
544
|
-
panel degrades to today's behavior (graceful fallback). -->
|
|
545
|
-
<div class="agg-summary" data-aggregate-summary hidden>
|
|
546
|
-
<div class="agg-summary-head">
|
|
547
|
-
<span class="agg-summary-glyph" aria-hidden="true">
|
|
548
|
-
<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2l1.9 4.7L18.5 8.6l-4.6 1.9L12 15.2l-1.9-4.7L5.5 8.6l4.6-1.9L12 2z"></path><path d="M18.5 14l.9 2.2 2.3.9-2.3.9-.9 2.2-.9-2.2-2.3-.9 2.3-.9.9-2.2z"></path></svg>
|
|
549
|
-
</span>
|
|
550
|
-
<span class="agg-summary-label">What’s in this release</span>
|
|
551
|
-
<span class="agg-ai-chip">
|
|
552
|
-
<svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M12 2l1.9 4.7L18.5 8.6l-4.6 1.9L12 15.2l-1.9-4.7L5.5 8.6l4.6-1.9L12 2z"></path></svg>
|
|
553
|
-
AI summary
|
|
554
|
-
</span>
|
|
555
|
-
</div>
|
|
556
|
-
<p class="agg-summary-lead" data-aggregate-summary-lead></p>
|
|
557
|
-
<ul class="agg-summary-points" data-aggregate-summary-points></ul>
|
|
558
|
-
</div>
|
|
559
|
-
|
|
560
|
-
<!-- Chain of custody: the build that was verified → its combined evidence →
|
|
561
|
-
the digest that ships. Collapsed by default — this is verification
|
|
562
|
-
detail for a developer, not something a merchant needs to read to
|
|
563
|
-
decide whether to publish; the plain-language summary above and the
|
|
564
|
-
Publish button below are the actual decision surface. -->
|
|
565
|
-
<details class="agg-chain-details">
|
|
566
|
-
<summary>Technical details</summary>
|
|
567
|
-
<div class="agg-chain">
|
|
568
|
-
<div class="agg-stop">
|
|
569
|
-
<span class="agg-stop-label">Aggregate build</span>
|
|
570
|
-
<span class="agg-stop-val"><strong class="publish-mono" data-aggregate-sha>—</strong></span>
|
|
571
|
-
<p class="agg-stop-cap" data-aggregate-run-link>The current green preview build for this store.</p>
|
|
572
|
-
</div>
|
|
573
|
-
<div class="agg-link" aria-hidden="true">
|
|
574
|
-
<span class="agg-flowlabel">verified by</span>
|
|
575
|
-
<span class="agg-arrow"><svg viewBox="0 0 34 10" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M1 5h29m0 0-4-4m4 4-4 4"></path></svg></span>
|
|
576
|
-
</div>
|
|
577
|
-
<div class="agg-stop">
|
|
578
|
-
<span class="agg-stop-label">Combined evidence</span>
|
|
579
|
-
<span class="agg-stop-val"><strong data-aggregate-evidence>—</strong></span>
|
|
580
|
-
<p class="agg-stop-cap" data-aggregate-evidence-detail>The last integration run's verdict.</p>
|
|
581
|
-
</div>
|
|
582
|
-
<div class="agg-link" aria-hidden="true">
|
|
583
|
-
<span class="agg-flowlabel">produces</span>
|
|
584
|
-
<span class="agg-arrow"><svg viewBox="0 0 34 10" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M1 5h29m0 0-4-4m4 4-4 4"></path></svg></span>
|
|
585
|
-
</div>
|
|
586
|
-
<div class="agg-stop">
|
|
587
|
-
<span class="agg-stop-label">Reviewed digest</span>
|
|
588
|
-
<span class="agg-stop-val"><strong class="publish-mono agg-digest" data-aggregate-digest>—</strong></span>
|
|
589
|
-
<p class="agg-stop-cap">The content digest that will be published — exactly what you reviewed.</p>
|
|
590
|
-
</div>
|
|
591
|
-
</div>
|
|
592
|
-
</details>
|
|
593
|
-
|
|
594
|
-
<!-- Included PRs — the batch this aggregate composes (b01 includedPrs). u14:
|
|
595
|
-
the "missing middle" — this is the STAGED delta vs live. The title + note
|
|
596
|
-
narrate how far ahead of the live site the staged preview is, and each row
|
|
597
|
-
carries an Undo (per-change revert) to roll the staged preview backward. -->
|
|
598
|
-
<div class="aggregate-prs agg-staged" data-aggregate-prs-wrap hidden>
|
|
599
|
-
<div class="agg-staged-head">
|
|
600
|
-
<b data-aggregate-prs-title>Included changes</b>
|
|
601
|
-
<!-- Scope-of-change risk measure across the whole staged batch (total
|
|
602
|
-
files + lines). Populated by renderAggregate from data.stagedScope;
|
|
603
|
-
hidden when no staged change carries size stats. -->
|
|
604
|
-
<span class="agg-scope" data-aggregate-scope hidden></span>
|
|
605
|
-
</div>
|
|
606
|
-
<!-- Staged → live delta strip: staged aggregate sha ahead of the live
|
|
607
|
-
pointer. Populated by renderAggregate; hidden when nothing is ahead. -->
|
|
608
|
-
<div class="agg-delta-strip" data-aggregate-delta-strip hidden>
|
|
609
|
-
<span class="agg-delta-node">
|
|
610
|
-
<span class="agg-stop-label">Staged preview</span>
|
|
611
|
-
<span class="sha-token" data-agg-delta-staged>—</span>
|
|
612
|
-
</span>
|
|
613
|
-
<span class="agg-delta-mid" aria-hidden="true">
|
|
614
|
-
<span class="agg-delta-count" data-agg-delta-count></span>
|
|
615
|
-
<svg viewBox="0 0 26 10" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M1 5h21m0 0-4-4m4 4-4 4"></path></svg>
|
|
616
|
-
</span>
|
|
617
|
-
<span class="agg-delta-node">
|
|
618
|
-
<span class="agg-stop-label">Live site</span>
|
|
619
|
-
<span class="sha-token ghost" data-agg-delta-live>—</span>
|
|
620
|
-
</span>
|
|
621
|
-
</div>
|
|
622
|
-
<p class="note" data-aggregate-delta-note hidden></p>
|
|
623
|
-
<!-- ONE table, one row per change — merges what used to be two separate
|
|
624
|
-
"Included changes" lists (this staged-delta section + the Publish
|
|
625
|
-
plan's own duplicate) into a single source of truth. Populated by
|
|
626
|
-
renderAggregate, enriched with the plan's per-change title/description/
|
|
627
|
-
file+line counts when available. -->
|
|
628
|
-
<div class="table-wrap">
|
|
629
|
-
<table class="publish-table agg-included-table">
|
|
630
|
-
<thead>
|
|
631
|
-
<tr>
|
|
632
|
-
<th>Change</th>
|
|
633
|
-
<th>Head</th>
|
|
634
|
-
<th>Files</th>
|
|
635
|
-
<th>Lines</th>
|
|
636
|
-
<th>Added</th>
|
|
637
|
-
<th class="action">Actions</th>
|
|
638
|
-
</tr>
|
|
639
|
-
</thead>
|
|
640
|
-
<tbody data-aggregate-prs></tbody>
|
|
641
|
-
</table>
|
|
642
|
-
</div>
|
|
643
|
-
</div>
|
|
644
|
-
|
|
645
|
-
<!-- The exact-digest Publish plan (b09 plan()): pinned SHA + digest +
|
|
646
|
-
rollback target + paywall verdict + run link. Shown inline (from
|
|
647
|
-
GET /api/changes `plan`) so the confirm never POSTs to preview.
|
|
648
|
-
Publish is refused until the plan is `ok`. Its own included-changes
|
|
649
|
-
list was merged into the single table above — see data-aggregate-prs. -->
|
|
650
|
-
<div class="aggregate-plan agg-manifest" data-aggregate-plan hidden>
|
|
651
|
-
<div class="aggregate-plan-head agg-manifest-head">
|
|
652
|
-
<b><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 11l3 3L22 4"></path><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"></path></svg>Publish plan</b>
|
|
653
|
-
<span class="chip" data-aggregate-plan-chip></span>
|
|
654
|
-
</div>
|
|
655
|
-
<details class="agg-chain-details">
|
|
656
|
-
<summary>Technical details</summary>
|
|
657
|
-
<dl class="aggregate-plan-grid" data-aggregate-plan-grid></dl>
|
|
658
|
-
</details>
|
|
659
|
-
|
|
660
|
-
<p class="note" data-aggregate-plan-note></p>
|
|
661
|
-
</div>
|
|
662
|
-
|
|
663
|
-
</div>
|
|
664
|
-
</details>
|
|
665
|
-
</div>
|
|
666
|
-
|
|
667
|
-
<!-- Branch retention panel removed entirely — old candidate branches now
|
|
668
|
-
clean themselves up automatically (auto-swept after each successful
|
|
669
|
-
accept/publish, see adminPublishTab.ts autoSweepBranches). There is
|
|
670
|
-
nothing here for anyone to look at or act on day-to-day, so there is no
|
|
671
|
-
panel at all, not even a collapsed one. -->
|
|
672
|
-
|
|
673
|
-
<!-- Publish next release — the collapsed destination/status panel below the
|
|
674
|
-
staged release. Its summary answers the operational question first: either
|
|
675
|
-
live already matches the staged release, or there is an action to advance it.
|
|
676
|
-
The disclosure contains destination links, freshness, rollback, and domain
|
|
677
|
-
controls for operators who need the detail. -->
|
|
678
|
-
<div class="panel" id="publish-live-site" data-publish-live-site>
|
|
679
|
-
<details class="live-site-details">
|
|
680
|
-
<summary class="panel-head">
|
|
681
|
-
<h3><svg><use href="#i-export"></use></svg>Publish next release</h3>
|
|
682
|
-
<span class="chip" data-live-release-state>Checking release…</span>
|
|
683
|
-
<button
|
|
684
|
-
class="btn primary btn-compact live-release-publish"
|
|
685
|
-
type="button"
|
|
686
|
-
data-live-release-publish
|
|
687
|
-
hidden
|
|
688
|
-
>Publish next release</button>
|
|
689
|
-
</summary>
|
|
690
|
-
<div class="panel-body grid">
|
|
691
|
-
<ul class="live-site-links">
|
|
692
|
-
<li>
|
|
693
|
-
<span class="live-site-link-label">Hosted store</span>
|
|
694
|
-
<a class="live-site-link-url publish-mono" href={liveHref} target="_blank" rel="noopener">{stripScheme(liveStoreUrl)}</a>
|
|
695
|
-
<span class="live-site-link-note">Updates immediately when you publish the reviewed release above.</span>
|
|
696
|
-
</li>
|
|
697
|
-
{publicSiteUrl && (
|
|
698
|
-
<li>
|
|
699
|
-
<span class="live-site-link-label">Public site</span>
|
|
700
|
-
<a class="live-site-link-url publish-mono" href={publicSiteUrl} target="_blank" rel="noopener">{stripScheme(publicSiteUrl)}</a>
|
|
701
|
-
<span class="live-site-link-note">Republishes automatically after the hosted release is published.</span>
|
|
702
|
-
</li>
|
|
703
|
-
)}
|
|
704
|
-
</ul>
|
|
705
|
-
|
|
706
|
-
<!-- Public-site freshness read-back (static-publish seam's staticLiveDigest):
|
|
707
|
-
what the external target (CloudFront/S3) is HONESTLY observed serving
|
|
708
|
-
right now, vs the control-plane's live pointer — the two can diverge on a
|
|
709
|
-
stale/failed republish. Answers "did going live actually reach the public
|
|
710
|
-
site?" without a manual curl. -->
|
|
711
|
-
{staticPublishEnabled && publicSiteDigest && publicSiteVerdict && (
|
|
712
|
-
<div
|
|
713
|
-
class={`risk-alert ${publicSiteVerdict.status === "current" ? "public-site-ok" : "amber"}`}
|
|
714
|
-
data-publish-public-site
|
|
715
|
-
>
|
|
716
|
-
<div class="risk-alert-main">
|
|
717
|
-
<svg><use href={publicSiteVerdict.status === "current" ? "#i-check" : "#i-alert"}></use></svg>
|
|
718
|
-
<div>
|
|
719
|
-
{publicSiteVerdict.status === "current" ? (
|
|
720
|
-
<>
|
|
721
|
-
<b>Public site matches the currently published release</b>
|
|
722
|
-
<span>
|
|
723
|
-
<code>{stripScheme(publicSiteUrl ?? tenant.appDomain).replace(/\/$/, "")}</code> is serving
|
|
724
|
-
(<code class="publish-mono">{liveVersion?.versionId?.slice(0, 12)}</code>)
|
|
725
|
-
— the same release as the hosted store. Staged preview changes are
|
|
726
|
-
a separate next release: use <b>Publish to both live sites</b> above
|
|
727
|
-
to advance the hosted store and republish this public site.
|
|
728
|
-
</span>
|
|
729
|
-
</>
|
|
730
|
-
) : publicSiteVerdict.status === "behind" ? (
|
|
731
|
-
<>
|
|
732
|
-
<b>Public site is behind the hosted store's live pointer</b>
|
|
733
|
-
<span>
|
|
734
|
-
<code>{tenant.appDomain}</code>'s public target is still serving{" "}
|
|
735
|
-
<code class="publish-mono">{publicSiteVerdict.publishedSha.slice(0, 12)}</code>, but
|
|
736
|
-
the hosted store's live pointer has since moved to{" "}
|
|
737
|
-
<code class="publish-mono">{publicSiteVerdict.livePointerSha.slice(0, 12)}</code>.
|
|
738
|
-
The republish job hasn't caught up yet (or a go-live happened before it was enabled) —
|
|
739
|
-
check the workflow run, or trigger a fresh go-live to re-dispatch it.
|
|
740
|
-
</span>
|
|
741
|
-
</>
|
|
742
|
-
) : (
|
|
743
|
-
<>
|
|
744
|
-
<b>Couldn't verify the public site is current</b>
|
|
745
|
-
<span>
|
|
746
|
-
Checked <code class="publish-mono">{publicSiteDigest.url}</code>: {publicSiteVerdict.reason}.
|
|
747
|
-
This does NOT mean the hosted store's own "Live version" above is wrong, only that the
|
|
748
|
-
separate public static target couldn't be confirmed current.
|
|
749
|
-
</span>
|
|
750
|
-
</>
|
|
751
|
-
)}
|
|
752
|
-
</div>
|
|
753
|
-
</div>
|
|
754
|
-
</div>
|
|
755
|
-
)}
|
|
756
|
-
|
|
757
|
-
<!-- Rollback options — CONTAINED within Live Site as a subsection (was a sibling
|
|
758
|
-
panel). The full published-version history + one-click rollback through the
|
|
759
|
-
single rollback orchestrator; collapsed by default (most people never open
|
|
760
|
-
it — the live version is already the metric up top). Keeps its
|
|
761
|
-
data-publish-versions + data-versions-* hooks verbatim. -->
|
|
762
|
-
<div class="live-site-subsection" data-publish-versions>
|
|
763
|
-
<details class="versions-details">
|
|
764
|
-
<summary class="live-site-subhead">
|
|
765
|
-
<h4><svg><use href="#i-refresh"></use></svg>Rollback options</h4>
|
|
766
|
-
<span class="chip" data-versions-chip>Loading</span>
|
|
767
|
-
</summary>
|
|
768
|
-
<div class="grid">
|
|
769
|
-
<p class="note publish-status" data-versions-status role="status" aria-live="polite"></p>
|
|
770
|
-
<p class="note" data-versions-rollback-note hidden></p>
|
|
771
|
-
<!-- u14: orients the owner between the staged aggregate above and live versions
|
|
772
|
-
below — "N staged change(s) not yet published; Publish live creates the next
|
|
773
|
-
version here." Owned by renderAggregate (its own element, so its timing never
|
|
774
|
-
races renderVersions). -->
|
|
775
|
-
<p class="note" data-versions-orientation hidden></p>
|
|
776
|
-
<div class="table-wrap">
|
|
777
|
-
<table class="publish-table versions-table">
|
|
778
|
-
<thead>
|
|
779
|
-
<tr>
|
|
780
|
-
<th>Version</th>
|
|
781
|
-
<th>Digest</th>
|
|
782
|
-
<th>Promoted</th>
|
|
783
|
-
<th>Preview</th>
|
|
784
|
-
<th>State</th>
|
|
785
|
-
<th class="action">Actions</th>
|
|
786
|
-
</tr>
|
|
787
|
-
</thead>
|
|
788
|
-
<tbody data-versions-body>
|
|
789
|
-
<tr data-versions-placeholder>
|
|
790
|
-
<td colspan="6"><span class="subtle">Loading published versions…</span></td>
|
|
791
|
-
</tr>
|
|
792
|
-
</tbody>
|
|
793
|
-
</table>
|
|
794
|
-
</div>
|
|
795
|
-
</div>
|
|
796
|
-
</details>
|
|
797
|
-
</div>
|
|
798
|
-
|
|
799
|
-
<!-- Public domain (apex connect / rollback) — CONTAINED within Live Site as a
|
|
800
|
-
subsection. Keeps its data-publish-domain + data-domain-* hooks verbatim so
|
|
801
|
-
the client controller is unchanged; only the wrapper/heading are lighter to
|
|
802
|
-
read as a nested section rather than a sibling panel. -->
|
|
803
|
-
{staticPublishEnabled && (
|
|
804
|
-
<div class="live-site-subsection" data-publish-domain>
|
|
805
|
-
<div class="live-site-subhead">
|
|
806
|
-
<h4><svg><use href="#i-shield"></use></svg>Public domain</h4>
|
|
807
|
-
<span class="chip" data-domain-state-chip>Loading</span>
|
|
808
|
-
</div>
|
|
809
|
-
<div class="grid">
|
|
810
|
-
<div class="grid cols-3">
|
|
811
|
-
<div class="field">
|
|
812
|
-
<label>Serving from</label>
|
|
813
|
-
<strong data-domain-state>—</strong>
|
|
814
|
-
<p data-domain-since>Current host for {tenant.appDomain}</p>
|
|
815
|
-
</div>
|
|
816
|
-
<div class="field">
|
|
817
|
-
<label>Rollback records</label>
|
|
818
|
-
<strong data-domain-capture>None captured</strong>
|
|
819
|
-
<p>DNS records captured before connect — rollback restores these.</p>
|
|
820
|
-
</div>
|
|
821
|
-
<div class="field">
|
|
822
|
-
<label>Last run</label>
|
|
823
|
-
<strong data-domain-run>None</strong>
|
|
824
|
-
<p data-domain-run-detail>Connect and rollback runs appear here.</p>
|
|
825
|
-
</div>
|
|
826
|
-
</div>
|
|
827
|
-
|
|
828
|
-
<p class="note publish-status" data-domain-status role="status" aria-live="polite"></p>
|
|
829
|
-
|
|
830
|
-
<!-- u9: apex-cutover readiness — the itemized preconditions the server gate
|
|
831
|
-
requires before a connect is permitted. Populated from
|
|
832
|
-
/api/domain/readiness (the SAME gate `tot go-live` reads). -->
|
|
833
|
-
<div class="domain-readiness" data-domain-readiness hidden>
|
|
834
|
-
<div class="domain-readiness-head">
|
|
835
|
-
<b>Cutover readiness</b>
|
|
836
|
-
<span class="chip" data-domain-readiness-chip>Checking…</span>
|
|
837
|
-
</div>
|
|
838
|
-
<ul class="domain-readiness-list" data-domain-readiness-list></ul>
|
|
839
|
-
<p class="note" data-domain-readiness-note></p>
|
|
840
|
-
</div>
|
|
841
|
-
|
|
842
|
-
<div class="check-row">
|
|
843
|
-
<div>
|
|
844
|
-
<b>Connect {tenant.appDomain} to the storefront</b>
|
|
845
|
-
<p>
|
|
846
|
-
Updates two DNS names — <code>{tenant.appDomain}</code> and <code>www.{tenant.appDomain}</code> —
|
|
847
|
-
to serve the published site. Takes effect in about a minute. Type the
|
|
848
|
-
domain to confirm.
|
|
849
|
-
</p>
|
|
850
|
-
{!viewerCanApexCutover && (
|
|
851
|
-
<p><span class="chip amber">Owner/admin only</span> Connecting the domain requires an owner or admin sign-in.</p>
|
|
852
|
-
)}
|
|
853
|
-
</div>
|
|
854
|
-
<div class="domain-connect-controls">
|
|
855
|
-
<input
|
|
856
|
-
type="text"
|
|
857
|
-
placeholder={tenant.appDomain}
|
|
858
|
-
autocomplete="off"
|
|
859
|
-
spellcheck="false"
|
|
860
|
-
aria-label="Type the domain to confirm"
|
|
861
|
-
data-domain-confirm
|
|
862
|
-
disabled={!viewerCanApexCutover}
|
|
863
|
-
/>
|
|
864
|
-
<label class="domain-rehearsal">
|
|
865
|
-
<input type="checkbox" data-domain-rehearsal disabled={!viewerCanApexCutover} />
|
|
866
|
-
Rehearsal (dry run, no DNS change)
|
|
867
|
-
</label>
|
|
868
|
-
<button
|
|
869
|
-
class="btn primary"
|
|
870
|
-
type="button"
|
|
871
|
-
data-domain-connect
|
|
872
|
-
data-owner-locked={!viewerCanApexCutover ? "true" : undefined}
|
|
873
|
-
disabled={!viewerCanApexCutover}
|
|
874
|
-
>
|
|
875
|
-
<svg><use href="#i-check"></use></svg>Connect domain
|
|
876
|
-
</button>
|
|
877
|
-
</div>
|
|
878
|
-
</div>
|
|
879
|
-
|
|
880
|
-
<div class="check-row">
|
|
881
|
-
<div>
|
|
882
|
-
<b>Roll back to the previous host</b>
|
|
883
|
-
<p data-domain-rollback-detail>
|
|
884
|
-
Restores the DNS records captured before connect. Available once records
|
|
885
|
-
have been captured.
|
|
886
|
-
</p>
|
|
887
|
-
</div>
|
|
888
|
-
<button class="btn" type="button" data-domain-rollback disabled={!viewerCanShip}>
|
|
889
|
-
<svg><use href="#i-refresh"></use></svg>Roll back
|
|
890
|
-
</button>
|
|
891
|
-
</div>
|
|
892
|
-
</div>
|
|
893
|
-
</div>
|
|
894
|
-
)}
|
|
895
|
-
</div>
|
|
896
|
-
</details>
|
|
897
|
-
</div>
|
|
898
|
-
|
|
899
|
-
<!-- Accept-to-preview LIFTOFF — the plan confirm + live integration flight +
|
|
900
|
-
outcome ceremony. One dialog, three phases (plan → flight → outcome).
|
|
901
|
-
The flight's stage rail advances ONLY on real signals (the in-flight POST
|
|
902
|
-
plus GET /api/changes polling — includedPrs / activeRun / aggregateSha);
|
|
903
|
-
nothing is simulated. Replaces the old window.confirm() plan popup. -->
|
|
904
|
-
<div class="liftoff-backdrop" data-liftoff hidden>
|
|
905
|
-
<div class="liftoff-card" role="dialog" aria-modal="true" aria-labelledby="liftoff-title" tabindex="-1">
|
|
906
|
-
<div class="liftoff-phase" data-liftoff-plan>
|
|
907
|
-
<p class="liftoff-kicker">Add to release</p>
|
|
908
|
-
<h3 class="liftoff-title" id="liftoff-title" data-liftoff-title>Add to your next release</h3>
|
|
909
|
-
<dl class="liftoff-grid" data-liftoff-grid></dl>
|
|
910
|
-
<p class="note">
|
|
911
|
-
Adds this change to your next release, then rebuilds the preview and re-runs your
|
|
912
|
-
store's checks. <b>Nothing goes live</b> — publishing your release stays a
|
|
913
|
-
separate, explicit step.
|
|
914
|
-
</p>
|
|
915
|
-
<div class="liftoff-actions">
|
|
916
|
-
<button class="btn" type="button" data-liftoff-cancel>Cancel</button>
|
|
917
|
-
<button class="btn primary" type="button" data-liftoff-proceed>
|
|
918
|
-
<svg><use href="#i-check"></use></svg>Add to release
|
|
919
|
-
</button>
|
|
920
|
-
</div>
|
|
921
|
-
</div>
|
|
922
|
-
|
|
923
|
-
<div class="liftoff-phase" data-liftoff-flight hidden>
|
|
924
|
-
<p class="liftoff-kicker">Adding to your release</p>
|
|
925
|
-
<h3 class="liftoff-title" data-liftoff-flight-title>Adding…</h3>
|
|
926
|
-
<ol class="liftoff-stages">
|
|
927
|
-
<li data-liftoff-stage="merge">
|
|
928
|
-
<span class="liftoff-dot"></span>
|
|
929
|
-
<div><b>Add</b><span>add this change to your next release</span></div>
|
|
930
|
-
</li>
|
|
931
|
-
<li data-liftoff-stage="build">
|
|
932
|
-
<span class="liftoff-dot"></span>
|
|
933
|
-
<div><b>Build</b><span>rebuild the preview with your change in it</span></div>
|
|
934
|
-
</li>
|
|
935
|
-
<li data-liftoff-stage="evidence">
|
|
936
|
-
<span class="liftoff-dot"></span>
|
|
937
|
-
<div><b>Check</b><span>re-run your store's checks on the preview</span></div>
|
|
938
|
-
</li>
|
|
939
|
-
<li data-liftoff-stage="green">
|
|
940
|
-
<span class="liftoff-dot"></span>
|
|
941
|
-
<div><b>Ready</b><span>your next release is ready to review and publish</span></div>
|
|
942
|
-
</li>
|
|
943
|
-
</ol>
|
|
944
|
-
<p class="liftoff-live" data-liftoff-live role="status" aria-live="polite">Working…</p>
|
|
945
|
-
<div class="stream-log" data-liftoff-log role="log" aria-live="polite"></div>
|
|
946
|
-
<div class="liftoff-meta">
|
|
947
|
-
<span class="publish-mono" data-liftoff-elapsed>0:00</span>
|
|
948
|
-
<button class="btn" type="button" data-liftoff-hide>Continue in background</button>
|
|
949
|
-
</div>
|
|
950
|
-
</div>
|
|
951
|
-
|
|
952
|
-
<div class="liftoff-phase liftoff-outcome-phase" data-liftoff-outcome hidden>
|
|
953
|
-
<div class="liftoff-seal" data-liftoff-seal aria-hidden="true"></div>
|
|
954
|
-
<h3 class="liftoff-title" data-liftoff-outcome-title></h3>
|
|
955
|
-
<p class="note" data-liftoff-outcome-detail></p>
|
|
956
|
-
<!-- Merge-conflict recovery (shown ONLY for reason "merge_failed") — decision
|
|
957
|
-
`automate-first-conflict-recovery` (u4): the refusal LEADS with one primary
|
|
958
|
-
action ("Update this change automatically" → /resolve strategy:"merge",
|
|
959
|
-
then auto-re-integrate). The three option cards appear only when diff3 hits
|
|
960
|
-
a genuine same-line overlap (RefreshResult.unresolved); the old terminal +
|
|
961
|
-
AI-agent guidance is demoted VERBATIM into the collapsed "Do it manually
|
|
962
|
-
instead" expert section (nothing removed). Decision logic lives in the
|
|
963
|
-
tested `resolveConflict.ts`; this glue mirrors it (verified on preview). -->
|
|
964
|
-
<div class="liftoff-conflict" data-liftoff-conflict hidden>
|
|
965
|
-
<p class="note liftoff-conflict-summary" data-conflict-summary hidden></p>
|
|
966
|
-
|
|
967
|
-
<!-- PRIMARY ACTION — always first when a candidate is refused not-mergeable. -->
|
|
968
|
-
<div class="liftoff-conflict-primary" data-conflict-primary>
|
|
969
|
-
<p class="note" data-conflict-primary-note>
|
|
970
|
-
We can rebuild your change on top of the store's current version and
|
|
971
|
-
re-integrate it for you — no terminal, no re-submit.
|
|
972
|
-
</p>
|
|
973
|
-
<button class="btn primary" type="button" data-conflict-auto>Update this change automatically</button>
|
|
974
|
-
<p class="note liftoff-conflict-primary-status" data-conflict-primary-status role="status" aria-live="polite" hidden></p>
|
|
975
|
-
</div>
|
|
976
|
-
|
|
977
|
-
<!-- OPTION CARDS — revealed only on a genuine same-line overlap diff3 can't
|
|
978
|
-
safely auto-pick. Static nodes mirror CONFLICT_OPTION_CARDS. -->
|
|
979
|
-
<div class="liftoff-conflict-cards" data-conflict-cards hidden>
|
|
980
|
-
<p class="note" data-conflict-cards-intro>
|
|
981
|
-
Some of the same lines were changed on both sides, so there's no single safe
|
|
982
|
-
answer. Pick how to resolve the overlap:
|
|
983
|
-
</p>
|
|
984
|
-
<ul class="liftoff-conflict-files publish-mono" data-conflict-files></ul>
|
|
985
|
-
<div class="liftoff-conflict-card-grid" role="group" aria-label="Resolve the overlap">
|
|
986
|
-
<button class="liftoff-option-card" type="button" data-conflict-card data-strategy="merge" data-recommended="1">
|
|
987
|
-
<b>Combine both</b>
|
|
988
|
-
<span>Recommended — weave both sets of edits together</span>
|
|
989
|
-
</button>
|
|
990
|
-
<button class="liftoff-option-card" type="button" data-conflict-card data-strategy="ours">
|
|
991
|
-
<b>Keep my change</b>
|
|
992
|
-
<span>Use your edits on the conflicting lines</span>
|
|
993
|
-
</button>
|
|
994
|
-
<button class="liftoff-option-card" type="button" data-conflict-card data-strategy="theirs">
|
|
995
|
-
<b>Keep the store's current version</b>
|
|
996
|
-
<span>Use the store's edits on the conflicting lines</span>
|
|
997
|
-
</button>
|
|
998
|
-
</div>
|
|
999
|
-
<p class="note liftoff-conflict-cards-status" data-conflict-cards-status role="status" aria-live="polite" hidden></p>
|
|
1000
|
-
</div>
|
|
1001
|
-
|
|
1002
|
-
<!-- EXPERT FALLBACK — the ORIGINAL terminal + AI-agent guidance, verbatim,
|
|
1003
|
-
collapsed. Nothing deleted; just demoted from primary to fallback. -->
|
|
1004
|
-
<details class="liftoff-conflict-expert" data-conflict-expert>
|
|
1005
|
-
<summary>Do it manually instead</summary>
|
|
1006
|
-
<p class="note" data-conflict-intro>
|
|
1007
|
-
From the store checkout that submitted this change, bring it up to date and
|
|
1008
|
-
re-submit — the open PR updates in place. Once the new build is ready,
|
|
1009
|
-
finish here with <b>Add to release</b>, or from the terminal with
|
|
1010
|
-
<code>tot accept --tenant <store> --pr <N></code>.
|
|
1011
|
-
Check <code>git status</code> first — abort any half-finished rebase from an
|
|
1012
|
-
earlier attempt. Then <code>tot pr</code>: this change should be marked
|
|
1013
|
-
<code>(active)</code>, proving you're in the right checkout and branch.
|
|
1014
|
-
If git reports an authentication failure, run <code>tot login</code> and retry.
|
|
1015
|
-
</p>
|
|
1016
|
-
<pre class="liftoff-commands publish-mono" data-conflict-commands></pre>
|
|
1017
|
-
<div class="liftoff-conflict-actions">
|
|
1018
|
-
<button class="btn" type="button" data-conflict-copy-prompt>Copy prompt for your AI agent</button>
|
|
1019
|
-
<button class="btn" type="button" data-conflict-copy>Copy commands</button>
|
|
1020
|
-
</div>
|
|
1021
|
-
<p class="liftoff-conflict-raw publish-mono" data-conflict-raw></p>
|
|
1022
|
-
</details>
|
|
1023
|
-
</div>
|
|
1024
|
-
<div class="liftoff-actions">
|
|
1025
|
-
<a class="btn" data-liftoff-review target="_blank" rel="noopener" hidden>Review the preview build</a>
|
|
1026
|
-
<!-- Fail-visible next step: when the merge LANDED but the preview build
|
|
1027
|
-
failed, this offers Rebuild (not a dead "did not land"); on an
|
|
1028
|
-
indeterminate outcome it offers Re-check. Hidden on success/conflict
|
|
1029
|
-
(conflict recovery owns its own primary action). -->
|
|
1030
|
-
<button class="btn primary" type="button" data-liftoff-rebuild hidden>Rebuild the preview build</button>
|
|
1031
|
-
<button class="btn primary" type="button" data-liftoff-done>Done</button>
|
|
1032
|
-
</div>
|
|
1033
|
-
</div>
|
|
1034
|
-
|
|
1035
|
-
<div class="liftoff-confetti" data-liftoff-confetti aria-hidden="true"></div>
|
|
1036
|
-
</div>
|
|
1037
|
-
</div>
|
|
1038
|
-
|
|
1039
|
-
<!-- Publish-live ceremony — the go-live counterpart of the accept liftoff
|
|
1040
|
-
(same shell/styles). Plan states the exact digest + no-rebuild contract;
|
|
1041
|
-
flight is a single honest in-flight state (one POST, no staged theater);
|
|
1042
|
-
outcome celebrates with a real "View your live store" link. Replaces the
|
|
1043
|
-
raw window.confirm() publish popup. -->
|
|
1044
|
-
<div class="liftoff-backdrop" data-shipoff hidden>
|
|
1045
|
-
<div class="liftoff-card" role="dialog" aria-modal="true" aria-labelledby="shipoff-title" tabindex="-1">
|
|
1046
|
-
<div class="liftoff-phase" data-shipoff-plan>
|
|
1047
|
-
<p class="liftoff-kicker">Publish live</p>
|
|
1048
|
-
<h3 class="liftoff-title" id="shipoff-title">Publish the reviewed build to your live store</h3>
|
|
1049
|
-
<dl class="liftoff-grid" data-shipoff-grid></dl>
|
|
1050
|
-
<p class="note">
|
|
1051
|
-
Publishes this <b>exact reviewed digest</b> — no rebuild, nothing rides along.
|
|
1052
|
-
Your live store updates in about a second; rolling back re-publishes the prior
|
|
1053
|
-
version from the archive.
|
|
1054
|
-
</p>
|
|
1055
|
-
<div class="liftoff-actions">
|
|
1056
|
-
<button class="btn" type="button" data-shipoff-cancel>Cancel</button>
|
|
1057
|
-
<button class="btn primary" type="button" data-shipoff-proceed>
|
|
1058
|
-
<svg><use href="#i-export"></use></svg>Publish live
|
|
1059
|
-
</button>
|
|
1060
|
-
</div>
|
|
1061
|
-
</div>
|
|
1062
|
-
|
|
1063
|
-
<div class="liftoff-phase" data-shipoff-flight hidden>
|
|
1064
|
-
<p class="liftoff-kicker">Publishing</p>
|
|
1065
|
-
<h3 class="liftoff-title">Promoting the reviewed digest…</h3>
|
|
1066
|
-
<p class="liftoff-live" data-shipoff-live role="status" aria-live="polite">
|
|
1067
|
-
Pinning the exact reviewed build to your live channel…
|
|
1068
|
-
</p>
|
|
1069
|
-
<div class="stream-log" data-shipoff-log role="log" aria-live="polite"></div>
|
|
1070
|
-
<div class="liftoff-meta">
|
|
1071
|
-
<span class="publish-mono" data-shipoff-elapsed>0:00</span>
|
|
1072
|
-
<button class="btn" type="button" data-shipoff-hide>Continue in background</button>
|
|
1073
|
-
</div>
|
|
1074
|
-
</div>
|
|
1075
|
-
|
|
1076
|
-
<div class="liftoff-phase liftoff-outcome-phase" data-shipoff-outcome hidden>
|
|
1077
|
-
<div class="liftoff-seal" data-shipoff-seal aria-hidden="true"></div>
|
|
1078
|
-
<h3 class="liftoff-title" data-shipoff-outcome-title></h3>
|
|
1079
|
-
<p class="note" data-shipoff-outcome-detail></p>
|
|
1080
|
-
<!-- Failure detail + "Copy for your AI agent" — shown ONLY when the publish did
|
|
1081
|
-
not complete. Mirrors the build-flight failure affordance. -->
|
|
1082
|
-
<div class="liftoff-conflict" data-shipoff-failure hidden>
|
|
1083
|
-
<pre class="liftoff-commands publish-mono" data-shipoff-failure-detail></pre>
|
|
1084
|
-
<div class="liftoff-conflict-actions">
|
|
1085
|
-
<button class="btn" type="button" data-shipoff-copy>Copy for your AI agent</button>
|
|
1086
|
-
</div>
|
|
1087
|
-
</div>
|
|
1088
|
-
<div class="liftoff-actions">
|
|
1089
|
-
<a class="btn primary" data-shipoff-view target="_blank" rel="noopener" hidden>View live</a>
|
|
1090
|
-
<!-- r10 — right at the post-publish moment: jump straight to the rollback
|
|
1091
|
-
row for the version this release just replaced, pre-targeted, so a
|
|
1092
|
-
publish that looked wrong is one click from being undone. Reuses the
|
|
1093
|
-
existing Live Site rollback action; shown only on a successful ship. -->
|
|
1094
|
-
<button class="btn" type="button" data-shipoff-undo hidden>
|
|
1095
|
-
<svg><use href="#i-undo"></use></svg>Undo this release
|
|
1096
|
-
</button>
|
|
1097
|
-
<button class="btn" type="button" data-shipoff-done>Done</button>
|
|
1098
|
-
</div>
|
|
1099
|
-
</div>
|
|
1100
|
-
|
|
1101
|
-
<div class="liftoff-confetti" data-shipoff-confetti aria-hidden="true"></div>
|
|
1102
|
-
</div>
|
|
1103
|
-
</div>
|
|
1104
|
-
|
|
1105
|
-
<!-- Build/check flight — the real-time counterpart of the accept liftoff for the
|
|
1106
|
-
"Build preview" / "Run checks" action. `POST /api/preview/build` streams its
|
|
1107
|
-
lifecycle phases over SSE (Accept: text/event-stream); this dialog reflects them
|
|
1108
|
-
live on the SAME stage-rail + "Continue in background" pattern as the liftoff.
|
|
1109
|
-
On a FAILURE the outcome shows the full detail the build reported plus a "Copy
|
|
1110
|
-
for your AI agent" button (mirrors the merge-conflict copy affordance). Progress
|
|
1111
|
-
reduction + copy assembly live in the tested buildStream.ts; this glue mirrors
|
|
1112
|
-
it (verified on preview). -->
|
|
1113
|
-
<div class="liftoff-backdrop" data-buildoff hidden>
|
|
1114
|
-
<div class="liftoff-card" role="dialog" aria-modal="true" aria-labelledby="buildoff-title" tabindex="-1">
|
|
1115
|
-
<div class="liftoff-phase" data-buildoff-flight>
|
|
1116
|
-
<p class="liftoff-kicker">Building preview</p>
|
|
1117
|
-
<h3 class="liftoff-title" id="buildoff-title" data-buildoff-flight-title>Building…</h3>
|
|
1118
|
-
<ol class="liftoff-stages">
|
|
1119
|
-
<li data-buildoff-stage="resolve">
|
|
1120
|
-
<span class="liftoff-dot"></span>
|
|
1121
|
-
<div><b>Locate commit</b><span>find the pull request's latest commit</span></div>
|
|
1122
|
-
</li>
|
|
1123
|
-
<li data-buildoff-stage="read">
|
|
1124
|
-
<span class="liftoff-dot"></span>
|
|
1125
|
-
<div><b>Read & validate</b><span>read your store's files and check them</span></div>
|
|
1126
|
-
</li>
|
|
1127
|
-
<li data-buildoff-stage="publish">
|
|
1128
|
-
<span class="liftoff-dot"></span>
|
|
1129
|
-
<div><b>Publish version</b><span>materialize this version's hosted preview</span></div>
|
|
1130
|
-
</li>
|
|
1131
|
-
<li data-buildoff-stage="check">
|
|
1132
|
-
<span class="liftoff-dot"></span>
|
|
1133
|
-
<div><b>Run checks</b><span>compute the evidence for this version</span></div>
|
|
1134
|
-
</li>
|
|
1135
|
-
</ol>
|
|
1136
|
-
<p class="liftoff-live" data-buildoff-live role="status" aria-live="polite">Starting…</p>
|
|
1137
|
-
<div class="stream-log" data-buildoff-log role="log" aria-live="polite"></div>
|
|
1138
|
-
<div class="liftoff-meta">
|
|
1139
|
-
<span class="publish-mono" data-buildoff-elapsed>0:00</span>
|
|
1140
|
-
<button class="btn" type="button" data-buildoff-hide>Continue in background</button>
|
|
1141
|
-
</div>
|
|
1142
|
-
</div>
|
|
1143
|
-
|
|
1144
|
-
<div class="liftoff-phase liftoff-outcome-phase" data-buildoff-outcome hidden>
|
|
1145
|
-
<div class="liftoff-seal" data-buildoff-seal aria-hidden="true"></div>
|
|
1146
|
-
<h3 class="liftoff-title" data-buildoff-outcome-title></h3>
|
|
1147
|
-
<p class="note" data-buildoff-outcome-detail></p>
|
|
1148
|
-
<!-- Failure detail + "Copy for your AI agent" — shown ONLY when the build did
|
|
1149
|
-
not succeed. The detail is the honest reason the build reported (errors +
|
|
1150
|
-
blocking findings); the copy button hands a framed brief to a coding agent. -->
|
|
1151
|
-
<div class="liftoff-conflict" data-buildoff-failure hidden>
|
|
1152
|
-
<pre class="liftoff-commands publish-mono" data-buildoff-failure-detail></pre>
|
|
1153
|
-
<div class="liftoff-conflict-actions">
|
|
1154
|
-
<button class="btn" type="button" data-buildoff-copy>Copy for your AI agent</button>
|
|
1155
|
-
</div>
|
|
1156
|
-
</div>
|
|
1157
|
-
<div class="liftoff-actions">
|
|
1158
|
-
<a class="btn" data-buildoff-review target="_blank" rel="noopener" hidden>Open the preview</a>
|
|
1159
|
-
<button class="btn primary" type="button" data-buildoff-done>Done</button>
|
|
1160
|
-
</div>
|
|
1161
|
-
</div>
|
|
1162
|
-
</div>
|
|
1163
|
-
</div>
|
|
1164
|
-
|
|
1165
|
-
<!-- Retry-build flight — the real-time counterpart for the aggregate "Retry build"
|
|
1166
|
-
action (`POST /api/changes/rebuild`), which re-runs ONLY the aggregate rebuild +
|
|
1167
|
-
combined evidence against the last merged commit (no new merge). It streams its
|
|
1168
|
-
reading/publishing/checking phases over SSE into the SAME shared scrolling
|
|
1169
|
-
live-log console; "Continue in background" hides it while the stream keeps
|
|
1170
|
-
running, and on a FAILURE the outcome shows the honest detail + a "Copy for your
|
|
1171
|
-
AI agent" button — the same affordances as the build/ship flights. -->
|
|
1172
|
-
<div class="liftoff-backdrop" data-rebuildoff hidden>
|
|
1173
|
-
<div class="liftoff-card" role="dialog" aria-modal="true" aria-labelledby="rebuildoff-title" tabindex="-1">
|
|
1174
|
-
<div class="liftoff-phase" data-rebuildoff-flight>
|
|
1175
|
-
<p class="liftoff-kicker">Retrying the build</p>
|
|
1176
|
-
<h3 class="liftoff-title" id="rebuildoff-title">Rebuilding your next release preview…</h3>
|
|
1177
|
-
<p class="liftoff-live" data-rebuildoff-live role="status" aria-live="polite">Starting…</p>
|
|
1178
|
-
<div class="stream-log" data-rebuildoff-log role="log" aria-live="polite"></div>
|
|
1179
|
-
<div class="liftoff-meta">
|
|
1180
|
-
<span class="publish-mono" data-rebuildoff-elapsed>0:00</span>
|
|
1181
|
-
<button class="btn" type="button" data-rebuildoff-hide>Continue in background</button>
|
|
1182
|
-
</div>
|
|
1183
|
-
</div>
|
|
1184
|
-
|
|
1185
|
-
<div class="liftoff-phase liftoff-outcome-phase" data-rebuildoff-outcome hidden>
|
|
1186
|
-
<div class="liftoff-seal" data-rebuildoff-seal aria-hidden="true"></div>
|
|
1187
|
-
<h3 class="liftoff-title" data-rebuildoff-outcome-title></h3>
|
|
1188
|
-
<p class="note" data-rebuildoff-outcome-detail></p>
|
|
1189
|
-
<div class="liftoff-conflict" data-rebuildoff-failure hidden>
|
|
1190
|
-
<pre class="liftoff-commands publish-mono" data-rebuildoff-failure-detail></pre>
|
|
1191
|
-
<div class="liftoff-conflict-actions">
|
|
1192
|
-
<button class="btn" type="button" data-rebuildoff-copy>Copy for your AI agent</button>
|
|
1193
|
-
</div>
|
|
1194
|
-
</div>
|
|
1195
|
-
<div class="liftoff-actions">
|
|
1196
|
-
<button class="btn primary" type="button" data-rebuildoff-done>Done</button>
|
|
1197
|
-
</div>
|
|
1198
|
-
</div>
|
|
1199
|
-
</div>
|
|
1200
|
-
</div>
|
|
1201
|
-
</section>
|
|
1202
|
-
|
|
1203
|
-
<script>
|
|
1204
|
-
import { initAdminPublishTab } from "@/lib/publish/adminPublishTab";
|
|
1205
|
-
initAdminPublishTab();
|
|
1206
|
-
</script>
|
|
1207
|
-
|
|
1208
|
-
<style>
|
|
1209
|
-
.publish-table {
|
|
1210
|
-
min-width: 900px;
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
/* r06 — responsive change queue. On a narrow screen the min-width:900px queue
|
|
1214
|
-
table forces horizontal scroll, unusable on a phone. Below ~700px, restack the
|
|
1215
|
-
existing rows into labeled cards — CSS-only, no DOM/JS change: the table, rows
|
|
1216
|
-
and cells become blocks, the visual header is hidden (kept for assistive tech),
|
|
1217
|
-
and each cell gains its column label via a ::before drawn from the fixed thead
|
|
1218
|
-
order. Scoped to #publish-queue so the aggregate/versions tables are untouched.
|
|
1219
|
-
Full-width colspan rows (empty state, sign-in notice, the inline review pane)
|
|
1220
|
-
stay unlabeled and full width. */
|
|
1221
|
-
@media (max-width: 700px) {
|
|
1222
|
-
#publish-queue .publish-table,
|
|
1223
|
-
#publish-queue .publish-table tbody,
|
|
1224
|
-
#publish-queue .publish-table tr,
|
|
1225
|
-
#publish-queue .publish-table td {
|
|
1226
|
-
display: block;
|
|
1227
|
-
min-width: 0;
|
|
1228
|
-
width: auto;
|
|
1229
|
-
}
|
|
1230
|
-
#publish-queue .publish-table thead {
|
|
1231
|
-
position: absolute;
|
|
1232
|
-
width: 1px;
|
|
1233
|
-
height: 1px;
|
|
1234
|
-
overflow: hidden;
|
|
1235
|
-
clip: rect(0 0 0 0);
|
|
1236
|
-
clip-path: inset(50%);
|
|
1237
|
-
white-space: nowrap;
|
|
1238
|
-
}
|
|
1239
|
-
#publish-queue .publish-table tbody tr {
|
|
1240
|
-
margin: 0 0 12px;
|
|
1241
|
-
padding: 10px 12px;
|
|
1242
|
-
border: 1px solid var(--admin-line);
|
|
1243
|
-
border-radius: var(--admin-radius-sm);
|
|
1244
|
-
background: var(--admin-surface, #fff);
|
|
1245
|
-
}
|
|
1246
|
-
#publish-queue .publish-table tbody td {
|
|
1247
|
-
padding: 4px 0;
|
|
1248
|
-
text-align: left;
|
|
1249
|
-
border: 0;
|
|
1250
|
-
}
|
|
1251
|
-
#publish-queue .publish-table tbody td:not([colspan])::before {
|
|
1252
|
-
display: block;
|
|
1253
|
-
margin-bottom: 2px;
|
|
1254
|
-
font-size: 11px;
|
|
1255
|
-
font-weight: 600;
|
|
1256
|
-
text-transform: uppercase;
|
|
1257
|
-
letter-spacing: 0.04em;
|
|
1258
|
-
opacity: 0.6;
|
|
1259
|
-
}
|
|
1260
|
-
#publish-queue .publish-table tbody td:not([colspan]):nth-of-type(1)::before {
|
|
1261
|
-
content: "Change";
|
|
1262
|
-
}
|
|
1263
|
-
#publish-queue .publish-table tbody td:not([colspan]):nth-of-type(2)::before {
|
|
1264
|
-
content: "Status";
|
|
1265
|
-
}
|
|
1266
|
-
#publish-queue .publish-table tbody td:not([colspan]):nth-of-type(3)::before {
|
|
1267
|
-
content: "Checks";
|
|
1268
|
-
}
|
|
1269
|
-
#publish-queue .publish-table tbody td:not([colspan]):nth-of-type(4)::before {
|
|
1270
|
-
content: "Head";
|
|
1271
|
-
}
|
|
1272
|
-
#publish-queue .publish-table tbody td:not([colspan]):nth-of-type(5)::before {
|
|
1273
|
-
content: "Updated";
|
|
1274
|
-
}
|
|
1275
|
-
#publish-queue .publish-table tbody td:not([colspan]):nth-of-type(6)::before {
|
|
1276
|
-
content: "Actions";
|
|
1277
|
-
}
|
|
1278
|
-
}
|
|
1279
|
-
|
|
1280
|
-
/* Collapsible panels (What needs your approval, Next release, Live Site): the
|
|
1281
|
-
panel-head IS the summary. Kill the native marker, add a rotating chevron, and
|
|
1282
|
-
make the whole head a click target. Mirrors the Rollback options (.versions-details). */
|
|
1283
|
-
.publish-queue-details > summary.panel-head,
|
|
1284
|
-
.closed-changes-details > summary.panel-head,
|
|
1285
|
-
.agg-console-details > summary.panel-head,
|
|
1286
|
-
.live-site-details > summary.panel-head,
|
|
1287
|
-
.versions-details > summary.live-site-subhead {
|
|
1288
|
-
cursor: pointer;
|
|
1289
|
-
list-style: none;
|
|
1290
|
-
user-select: none;
|
|
1291
|
-
}
|
|
1292
|
-
.publish-queue-details > summary.panel-head::-webkit-details-marker,
|
|
1293
|
-
.closed-changes-details > summary.panel-head::-webkit-details-marker,
|
|
1294
|
-
.agg-console-details > summary.panel-head::-webkit-details-marker,
|
|
1295
|
-
.live-site-details > summary.panel-head::-webkit-details-marker,
|
|
1296
|
-
.versions-details > summary.live-site-subhead::-webkit-details-marker {
|
|
1297
|
-
display: none;
|
|
1298
|
-
}
|
|
1299
|
-
.publish-queue-details > summary.panel-head h3::before,
|
|
1300
|
-
.closed-changes-details > summary.panel-head h3::before,
|
|
1301
|
-
.agg-console-details > summary.panel-head h3::before,
|
|
1302
|
-
.live-site-details > summary.panel-head h3::before,
|
|
1303
|
-
.versions-details > summary.live-site-subhead h4::before {
|
|
1304
|
-
content: "";
|
|
1305
|
-
width: 7px;
|
|
1306
|
-
height: 7px;
|
|
1307
|
-
margin-right: 2px;
|
|
1308
|
-
border-right: 2px solid currentColor;
|
|
1309
|
-
border-bottom: 2px solid currentColor;
|
|
1310
|
-
transform: rotate(-45deg);
|
|
1311
|
-
transition: transform 0.15s ease;
|
|
1312
|
-
opacity: 0.6;
|
|
1313
|
-
}
|
|
1314
|
-
.publish-queue-details[open] > summary.panel-head h3::before,
|
|
1315
|
-
.closed-changes-details[open] > summary.panel-head h3::before,
|
|
1316
|
-
.agg-console-details[open] > summary.panel-head h3::before,
|
|
1317
|
-
.live-site-details[open] > summary.panel-head h3::before,
|
|
1318
|
-
.versions-details[open] > summary.live-site-subhead h4::before {
|
|
1319
|
-
transform: rotate(45deg);
|
|
1320
|
-
}
|
|
1321
|
-
|
|
1322
|
-
/* The collapsed release summary owns one mutually exclusive state affordance:
|
|
1323
|
-
a green current-state chip or the action that advances live. Both classes set
|
|
1324
|
-
display, so explicitly preserve the semantic hidden state. */
|
|
1325
|
-
[data-live-release-state][hidden],
|
|
1326
|
-
.live-release-publish[hidden] {
|
|
1327
|
-
display: none;
|
|
1328
|
-
}
|
|
1329
|
-
|
|
1330
|
-
/* Live Site — the two clickable public entry points, previously undiscoverable. */
|
|
1331
|
-
.live-site-links {
|
|
1332
|
-
list-style: none;
|
|
1333
|
-
margin: 0;
|
|
1334
|
-
padding: 0;
|
|
1335
|
-
display: grid;
|
|
1336
|
-
gap: 12px;
|
|
1337
|
-
}
|
|
1338
|
-
.live-site-links > li {
|
|
1339
|
-
display: grid;
|
|
1340
|
-
grid-template-columns: minmax(96px, auto) 1fr;
|
|
1341
|
-
align-items: baseline;
|
|
1342
|
-
gap: 4px 12px;
|
|
1343
|
-
}
|
|
1344
|
-
.live-site-link-label {
|
|
1345
|
-
font-weight: 600;
|
|
1346
|
-
font-size: 13px;
|
|
1347
|
-
color: var(--admin-fg, inherit);
|
|
1348
|
-
}
|
|
1349
|
-
.live-site-link-url {
|
|
1350
|
-
word-break: break-all;
|
|
1351
|
-
font-size: 13px;
|
|
1352
|
-
}
|
|
1353
|
-
.live-site-link-note {
|
|
1354
|
-
grid-column: 2;
|
|
1355
|
-
font-size: 12px;
|
|
1356
|
-
opacity: 0.7;
|
|
1357
|
-
}
|
|
1358
|
-
|
|
1359
|
-
/* r13 — Rollback + Public domain nested INSIDE Live Site read as a subsection,
|
|
1360
|
-
not a sibling panel: a single left rule + generous spacing rather than a boxed
|
|
1361
|
-
card, so the Live Site panel doesn't stack boxes-in-boxes. */
|
|
1362
|
-
.live-site-subsection {
|
|
1363
|
-
border-left: 2px solid var(--admin-line);
|
|
1364
|
-
padding-left: 16px;
|
|
1365
|
-
margin-top: 18px;
|
|
1366
|
-
display: grid;
|
|
1367
|
-
gap: 12px;
|
|
1368
|
-
}
|
|
1369
|
-
/* r13 — the Public-domain status trio (Serving from / Rollback records / Last run)
|
|
1370
|
-
were full `.field` cards (border + fill) nested inside the Live Site panel — the
|
|
1371
|
-
genuine boxes-in-boxes. Flatten them to plain label/value blocks (hooks kept);
|
|
1372
|
-
the subsection's left rule + grid gap carry the separation instead. */
|
|
1373
|
-
.live-site-subsection .field {
|
|
1374
|
-
border: 0;
|
|
1375
|
-
background: transparent;
|
|
1376
|
-
padding: 0;
|
|
1377
|
-
}
|
|
1378
|
-
.live-site-subhead {
|
|
1379
|
-
display: flex;
|
|
1380
|
-
align-items: center;
|
|
1381
|
-
justify-content: space-between;
|
|
1382
|
-
gap: 12px;
|
|
1383
|
-
}
|
|
1384
|
-
.live-site-subhead h4 {
|
|
1385
|
-
margin: 0;
|
|
1386
|
-
display: flex;
|
|
1387
|
-
align-items: center;
|
|
1388
|
-
gap: 8px;
|
|
1389
|
-
font-size: 14px;
|
|
1390
|
-
}
|
|
1391
|
-
|
|
1392
|
-
.publish-mono {
|
|
1393
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
1394
|
-
font-size: 12px;
|
|
1395
|
-
}
|
|
1396
|
-
|
|
1397
|
-
/* r11 — the human release name beside the "Next release" heading. */
|
|
1398
|
-
.agg-release-name {
|
|
1399
|
-
font-size: 13px;
|
|
1400
|
-
font-weight: 600;
|
|
1401
|
-
color: var(--admin-muted);
|
|
1402
|
-
}
|
|
1403
|
-
.agg-release-name[hidden] {
|
|
1404
|
-
display: none;
|
|
1405
|
-
}
|
|
1406
|
-
|
|
1407
|
-
/* r02 — the humanized "Live version" metric: "v42" as a link to the live store,
|
|
1408
|
-
with the raw SHA tucked into a "Technical details" disclosure. */
|
|
1409
|
-
.live-version-link {
|
|
1410
|
-
color: inherit;
|
|
1411
|
-
text-decoration: none;
|
|
1412
|
-
border-bottom: 1px solid color-mix(in srgb, currentColor 35%, transparent);
|
|
1413
|
-
}
|
|
1414
|
-
.live-version-link:hover {
|
|
1415
|
-
border-bottom-color: currentColor;
|
|
1416
|
-
}
|
|
1417
|
-
.metric-tech,
|
|
1418
|
-
.ver-tech {
|
|
1419
|
-
margin-top: 4px;
|
|
1420
|
-
font-size: 12px;
|
|
1421
|
-
}
|
|
1422
|
-
.metric-tech > summary,
|
|
1423
|
-
.ver-tech > summary {
|
|
1424
|
-
cursor: pointer;
|
|
1425
|
-
color: var(--muted, #64716d);
|
|
1426
|
-
font-size: 11px;
|
|
1427
|
-
list-style: none;
|
|
1428
|
-
}
|
|
1429
|
-
.metric-tech > summary::-webkit-details-marker,
|
|
1430
|
-
.ver-tech > summary::-webkit-details-marker {
|
|
1431
|
-
display: none;
|
|
1432
|
-
}
|
|
1433
|
-
.ver-tech {
|
|
1434
|
-
margin-top: 6px;
|
|
1435
|
-
}
|
|
1436
|
-
|
|
1437
|
-
/* r08 — the "Copy preview link" affordance (icon + text, never color-only). */
|
|
1438
|
-
.publish-copy-preview {
|
|
1439
|
-
display: inline-flex;
|
|
1440
|
-
align-items: center;
|
|
1441
|
-
gap: 5px;
|
|
1442
|
-
}
|
|
1443
|
-
.publish-copy-preview svg {
|
|
1444
|
-
width: 14px;
|
|
1445
|
-
height: 14px;
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
.publish-actions {
|
|
1449
|
-
white-space: nowrap;
|
|
1450
|
-
}
|
|
1451
|
-
|
|
1452
|
-
.publish-actions .btn {
|
|
1453
|
-
margin-left: 6px;
|
|
1454
|
-
}
|
|
1455
|
-
|
|
1456
|
-
/* The per-row orphan "Delete this branch" action — a deliberate, destructive,
|
|
1457
|
-
one-at-a-time HUMAN delete, styled distinct from the neutral "Clean up" and the
|
|
1458
|
-
bulk "Sweep dead branches" so it never reads as a routine cleanup. */
|
|
1459
|
-
.btn.branch-delete-orphan {
|
|
1460
|
-
border-color: var(--admin-danger, #b3261e);
|
|
1461
|
-
color: var(--admin-danger, #b3261e);
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
.btn.branch-delete-orphan:not(:disabled):hover {
|
|
1465
|
-
background: var(--admin-danger, #b3261e);
|
|
1466
|
-
color: #fff;
|
|
1467
|
-
}
|
|
1468
|
-
|
|
1469
|
-
.publish-status {
|
|
1470
|
-
min-height: 0;
|
|
1471
|
-
}
|
|
1472
|
-
|
|
1473
|
-
.publish-status:empty {
|
|
1474
|
-
display: none;
|
|
1475
|
-
}
|
|
1476
|
-
|
|
1477
|
-
.publish-status[data-state="ok"] {
|
|
1478
|
-
color: var(--admin-green);
|
|
1479
|
-
}
|
|
1480
|
-
|
|
1481
|
-
.publish-status[data-state="err"] {
|
|
1482
|
-
color: var(--admin-red);
|
|
1483
|
-
}
|
|
1484
|
-
|
|
1485
|
-
/* Public-site status card, verified-current tone — .risk-alert defaults to an
|
|
1486
|
-
amber/warning look (admin.astro), wrong for "this is fine, confirmed fresh". */
|
|
1487
|
-
.risk-alert.public-site-ok {
|
|
1488
|
-
border-color: var(--admin-green);
|
|
1489
|
-
background: var(--admin-green-soft);
|
|
1490
|
-
}
|
|
1491
|
-
|
|
1492
|
-
.risk-alert.public-site-ok svg {
|
|
1493
|
-
color: var(--admin-green);
|
|
1494
|
-
}
|
|
1495
|
-
|
|
1496
|
-
/* ── State-driven hero (r01) ─────────────────────────────────────────────
|
|
1497
|
-
The single top-of-tab state element. One row: an icon whose tone conveys
|
|
1498
|
-
urgency (never color alone — a "Action needed" kicker rides the two blocker
|
|
1499
|
-
tones), the one-sentence state + supporting detail, and one primary CTA laid
|
|
1500
|
-
out to the trailing edge (like the paywall gate). Tone is set by
|
|
1501
|
-
renderHero() via [data-tone]. */
|
|
1502
|
-
.publish-hero-region {
|
|
1503
|
-
display: grid;
|
|
1504
|
-
gap: 12px;
|
|
1505
|
-
}
|
|
1506
|
-
.publish-hero {
|
|
1507
|
-
display: flex;
|
|
1508
|
-
align-items: center;
|
|
1509
|
-
justify-content: space-between;
|
|
1510
|
-
gap: 16px;
|
|
1511
|
-
flex-wrap: wrap;
|
|
1512
|
-
padding: 14px 16px;
|
|
1513
|
-
border: 1px solid var(--admin-line-strong);
|
|
1514
|
-
border-left-width: 4px;
|
|
1515
|
-
border-radius: var(--admin-radius, 12px);
|
|
1516
|
-
background: var(--admin-surface);
|
|
1517
|
-
}
|
|
1518
|
-
.publish-hero[hidden] {
|
|
1519
|
-
display: none;
|
|
1520
|
-
}
|
|
1521
|
-
.publish-hero-main {
|
|
1522
|
-
display: flex;
|
|
1523
|
-
align-items: center;
|
|
1524
|
-
gap: 13px;
|
|
1525
|
-
min-width: 0;
|
|
1526
|
-
}
|
|
1527
|
-
.publish-hero-icon {
|
|
1528
|
-
flex: none;
|
|
1529
|
-
width: 34px;
|
|
1530
|
-
height: 34px;
|
|
1531
|
-
display: grid;
|
|
1532
|
-
place-items: center;
|
|
1533
|
-
border-radius: 9px;
|
|
1534
|
-
background: var(--admin-surface-soft);
|
|
1535
|
-
}
|
|
1536
|
-
.publish-hero-icon svg {
|
|
1537
|
-
width: 19px;
|
|
1538
|
-
height: 19px;
|
|
1539
|
-
}
|
|
1540
|
-
.publish-hero-copy {
|
|
1541
|
-
display: grid;
|
|
1542
|
-
gap: 2px;
|
|
1543
|
-
min-width: 0;
|
|
1544
|
-
}
|
|
1545
|
-
.publish-hero-kicker {
|
|
1546
|
-
font-size: 10.5px;
|
|
1547
|
-
font-weight: 760;
|
|
1548
|
-
letter-spacing: 0.08em;
|
|
1549
|
-
text-transform: uppercase;
|
|
1550
|
-
color: var(--admin-red);
|
|
1551
|
-
}
|
|
1552
|
-
.publish-hero-kicker[hidden] {
|
|
1553
|
-
display: none;
|
|
1554
|
-
}
|
|
1555
|
-
.publish-hero-headline {
|
|
1556
|
-
font-size: 15.5px;
|
|
1557
|
-
line-height: 1.3;
|
|
1558
|
-
color: var(--admin-ink);
|
|
1559
|
-
}
|
|
1560
|
-
.publish-hero-detail {
|
|
1561
|
-
font-size: 13px;
|
|
1562
|
-
color: var(--admin-muted);
|
|
1563
|
-
line-height: 1.45;
|
|
1564
|
-
}
|
|
1565
|
-
.publish-hero-detail:empty {
|
|
1566
|
-
display: none;
|
|
1567
|
-
}
|
|
1568
|
-
.publish-hero-actions {
|
|
1569
|
-
flex: none;
|
|
1570
|
-
display: flex;
|
|
1571
|
-
gap: 8px;
|
|
1572
|
-
}
|
|
1573
|
-
.publish-hero-cta[hidden] {
|
|
1574
|
-
display: none;
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
/* Tones — the accent rail + icon colour. Blocker/warn borrow the admin
|
|
1578
|
-
red/amber tokens; ready/attention the teal; calm stays neutral. */
|
|
1579
|
-
.publish-hero[data-tone="blocker"] {
|
|
1580
|
-
border-left-color: var(--admin-red);
|
|
1581
|
-
background: var(--admin-red-soft);
|
|
1582
|
-
}
|
|
1583
|
-
.publish-hero[data-tone="blocker"] .publish-hero-icon svg {
|
|
1584
|
-
color: var(--admin-red);
|
|
1585
|
-
}
|
|
1586
|
-
.publish-hero[data-tone="warn"] {
|
|
1587
|
-
border-left-color: var(--admin-amber);
|
|
1588
|
-
background: var(--admin-surface-soft);
|
|
1589
|
-
}
|
|
1590
|
-
.publish-hero[data-tone="warn"] .publish-hero-icon svg {
|
|
1591
|
-
color: var(--admin-amber);
|
|
1592
|
-
}
|
|
1593
|
-
.publish-hero[data-tone="attention"] {
|
|
1594
|
-
border-left-color: var(--admin-teal, #0f9d8f);
|
|
1595
|
-
}
|
|
1596
|
-
.publish-hero[data-tone="attention"] .publish-hero-icon svg {
|
|
1597
|
-
color: var(--admin-teal, #0f9d8f);
|
|
1598
|
-
}
|
|
1599
|
-
.publish-hero[data-tone="ready"] {
|
|
1600
|
-
border-left-color: var(--admin-green);
|
|
1601
|
-
background: var(--admin-green-soft);
|
|
1602
|
-
}
|
|
1603
|
-
.publish-hero[data-tone="ready"] .publish-hero-icon svg {
|
|
1604
|
-
color: var(--admin-green);
|
|
1605
|
-
}
|
|
1606
|
-
.publish-hero[data-tone="calm"] {
|
|
1607
|
-
border-left-color: var(--admin-line-strong);
|
|
1608
|
-
}
|
|
1609
|
-
.publish-hero[data-tone="calm"] .publish-hero-icon svg {
|
|
1610
|
-
color: var(--admin-green);
|
|
1611
|
-
}
|
|
1612
|
-
|
|
1613
|
-
/* r03 stale-public-site alarm — lays its "Open Live Site" deep-link to the
|
|
1614
|
-
trailing edge, like the paywall gate. Inherits the amber .risk-alert look. */
|
|
1615
|
-
.risk-alert.publish-stale-site {
|
|
1616
|
-
display: flex;
|
|
1617
|
-
align-items: center;
|
|
1618
|
-
justify-content: space-between;
|
|
1619
|
-
gap: 12px;
|
|
1620
|
-
flex-wrap: wrap;
|
|
1621
|
-
}
|
|
1622
|
-
.risk-alert.publish-stale-site .btn {
|
|
1623
|
-
flex: none;
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
/* Per-row finding: the ownership-tag chip leads the row's action cell (what's
|
|
1627
|
-
wrong, in operator words), and the mapped action button gets a subtle ring so
|
|
1628
|
-
the operator sees which button resolves it. Tone (red/amber/neutral) rides the
|
|
1629
|
-
shared .chip convention; primed emphasis is added to the EXISTING button. */
|
|
1630
|
-
.publish-actions .publish-finding-chip {
|
|
1631
|
-
vertical-align: middle;
|
|
1632
|
-
}
|
|
1633
|
-
|
|
1634
|
-
.publish-actions .btn.publish-cta-primed {
|
|
1635
|
-
box-shadow: 0 0 0 2px var(--admin-amber);
|
|
1636
|
-
}
|
|
1637
|
-
|
|
1638
|
-
/* ?pr=N deep link: "not in the queue" callout + row flash */
|
|
1639
|
-
.publish-pr-callout {
|
|
1640
|
-
border: 1px solid var(--admin-line-strong);
|
|
1641
|
-
border-left: 3px solid var(--admin-amber);
|
|
1642
|
-
background: var(--admin-surface-soft);
|
|
1643
|
-
padding: 8px 12px;
|
|
1644
|
-
border-radius: var(--admin-radius-sm);
|
|
1645
|
-
color: var(--admin-ink);
|
|
1646
|
-
}
|
|
1647
|
-
|
|
1648
|
-
@keyframes publish-row-flash {
|
|
1649
|
-
0%,
|
|
1650
|
-
25% {
|
|
1651
|
-
background-color: var(--admin-teal-soft);
|
|
1652
|
-
}
|
|
1653
|
-
100% {
|
|
1654
|
-
background-color: transparent;
|
|
1655
|
-
}
|
|
1656
|
-
}
|
|
1657
|
-
|
|
1658
|
-
.publish-row-highlight > td {
|
|
1659
|
-
animation: publish-row-flash 2.4s ease-out;
|
|
1660
|
-
}
|
|
1661
|
-
|
|
1662
|
-
@media (prefers-reduced-motion: reduce) {
|
|
1663
|
-
.publish-row-highlight > td {
|
|
1664
|
-
animation: none;
|
|
1665
|
-
background-color: var(--admin-teal-soft);
|
|
1666
|
-
}
|
|
1667
|
-
}
|
|
1668
|
-
|
|
1669
|
-
.publish-error-detail {
|
|
1670
|
-
color: var(--admin-red);
|
|
1671
|
-
font-size: 12px;
|
|
1672
|
-
}
|
|
1673
|
-
|
|
1674
|
-
/* ── Accept-to-preview LIFTOFF ─────────────────────────────────────────── */
|
|
1675
|
-
.liftoff-backdrop {
|
|
1676
|
-
position: fixed;
|
|
1677
|
-
inset: 0;
|
|
1678
|
-
z-index: 60;
|
|
1679
|
-
display: grid;
|
|
1680
|
-
place-items: center;
|
|
1681
|
-
padding: 24px;
|
|
1682
|
-
background: color-mix(in srgb, var(--admin-ink) 32%, transparent);
|
|
1683
|
-
backdrop-filter: blur(6px);
|
|
1684
|
-
-webkit-backdrop-filter: blur(6px);
|
|
1685
|
-
}
|
|
1686
|
-
|
|
1687
|
-
/* `display: grid` above beats the UA's [hidden] rule — restate it, or the
|
|
1688
|
-
invisible backdrop click-blocks the entire page (caught by live test). */
|
|
1689
|
-
.liftoff-backdrop[hidden] {
|
|
1690
|
-
display: none;
|
|
1691
|
-
}
|
|
1692
|
-
|
|
1693
|
-
.liftoff-card {
|
|
1694
|
-
position: relative;
|
|
1695
|
-
width: min(520px, 100%);
|
|
1696
|
-
max-height: min(84vh, 640px);
|
|
1697
|
-
overflow: auto;
|
|
1698
|
-
border: 1px solid var(--admin-line-strong);
|
|
1699
|
-
border-radius: var(--admin-radius, 12px);
|
|
1700
|
-
background: var(--admin-surface);
|
|
1701
|
-
box-shadow: 0 24px 80px rgba(8, 32, 30, 0.35);
|
|
1702
|
-
padding: 24px 26px;
|
|
1703
|
-
animation: liftoff-in 0.22s ease-out;
|
|
1704
|
-
}
|
|
1705
|
-
|
|
1706
|
-
@keyframes liftoff-in {
|
|
1707
|
-
from { transform: translateY(10px) scale(0.985); opacity: 0; }
|
|
1708
|
-
to { transform: none; opacity: 1; }
|
|
1709
|
-
}
|
|
1710
|
-
|
|
1711
|
-
/* In-flight shimmer: a live gradient beam along the card's top edge. */
|
|
1712
|
-
.liftoff-card::before {
|
|
1713
|
-
content: "";
|
|
1714
|
-
position: absolute;
|
|
1715
|
-
inset: 0 0 auto 0;
|
|
1716
|
-
height: 3px;
|
|
1717
|
-
border-radius: inherit;
|
|
1718
|
-
opacity: 0;
|
|
1719
|
-
background: linear-gradient(90deg, transparent, var(--admin-teal, #0f9d8f), #f4b942, var(--admin-teal, #0f9d8f), transparent);
|
|
1720
|
-
background-size: 220% 100%;
|
|
1721
|
-
transition: opacity 0.3s ease;
|
|
1722
|
-
}
|
|
1723
|
-
|
|
1724
|
-
.liftoff-card[data-mode="flight"]::before {
|
|
1725
|
-
opacity: 1;
|
|
1726
|
-
animation: liftoff-beam 1.6s linear infinite;
|
|
1727
|
-
}
|
|
1728
|
-
|
|
1729
|
-
@keyframes liftoff-beam {
|
|
1730
|
-
from { background-position: 200% 0; }
|
|
1731
|
-
to { background-position: -20% 0; }
|
|
1732
|
-
}
|
|
1733
|
-
|
|
1734
|
-
.liftoff-kicker {
|
|
1735
|
-
margin: 0 0 2px;
|
|
1736
|
-
font-size: 11px;
|
|
1737
|
-
font-weight: 700;
|
|
1738
|
-
letter-spacing: 0.08em;
|
|
1739
|
-
text-transform: uppercase;
|
|
1740
|
-
color: var(--admin-teal, #0f9d8f);
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
.liftoff-title {
|
|
1744
|
-
margin: 0 0 14px;
|
|
1745
|
-
font-size: 19px;
|
|
1746
|
-
line-height: 1.25;
|
|
1747
|
-
}
|
|
1748
|
-
|
|
1749
|
-
.liftoff-grid {
|
|
1750
|
-
display: grid;
|
|
1751
|
-
grid-template-columns: auto 1fr;
|
|
1752
|
-
gap: 6px 16px;
|
|
1753
|
-
margin: 0 0 12px;
|
|
1754
|
-
padding: 12px 14px;
|
|
1755
|
-
border: 1px solid var(--admin-line-strong);
|
|
1756
|
-
border-radius: var(--admin-radius-sm);
|
|
1757
|
-
background: var(--admin-surface-soft);
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
.liftoff-grid dt {
|
|
1761
|
-
color: var(--admin-muted);
|
|
1762
|
-
font-size: 12px;
|
|
1763
|
-
}
|
|
1764
|
-
|
|
1765
|
-
.liftoff-grid dd {
|
|
1766
|
-
margin: 0;
|
|
1767
|
-
font-size: 13px;
|
|
1768
|
-
}
|
|
1769
|
-
|
|
1770
|
-
.liftoff-actions {
|
|
1771
|
-
display: flex;
|
|
1772
|
-
justify-content: flex-end;
|
|
1773
|
-
gap: 8px;
|
|
1774
|
-
margin-top: 16px;
|
|
1775
|
-
}
|
|
1776
|
-
|
|
1777
|
-
/* Merge-conflict guidance (outcome phase, reason "merge_failed" only) —
|
|
1778
|
-
directions, not mood: the terminal recovery front and center, the forge's
|
|
1779
|
-
raw refusal kept small underneath for support/diagnosis. Left-aligned
|
|
1780
|
-
inside the centered outcome phase: commands and prose read as
|
|
1781
|
-
instructions, not ceremony. */
|
|
1782
|
-
.liftoff-conflict {
|
|
1783
|
-
margin-top: 10px;
|
|
1784
|
-
text-align: left;
|
|
1785
|
-
}
|
|
1786
|
-
|
|
1787
|
-
.liftoff-commands {
|
|
1788
|
-
margin: 10px 0 8px;
|
|
1789
|
-
padding: 10px 12px;
|
|
1790
|
-
border: 1px solid var(--admin-line-strong);
|
|
1791
|
-
border-radius: var(--admin-radius-sm);
|
|
1792
|
-
background: var(--admin-surface-soft);
|
|
1793
|
-
font-size: 12px;
|
|
1794
|
-
line-height: 1.55;
|
|
1795
|
-
overflow-x: auto;
|
|
1796
|
-
white-space: pre;
|
|
1797
|
-
}
|
|
1798
|
-
|
|
1799
|
-
.liftoff-conflict-actions {
|
|
1800
|
-
display: flex;
|
|
1801
|
-
justify-content: flex-end;
|
|
1802
|
-
gap: 8px;
|
|
1803
|
-
flex-wrap: wrap;
|
|
1804
|
-
}
|
|
1805
|
-
|
|
1806
|
-
.liftoff-conflict-raw {
|
|
1807
|
-
margin: 10px 0 0;
|
|
1808
|
-
font-size: 11px;
|
|
1809
|
-
color: var(--admin-muted);
|
|
1810
|
-
overflow-wrap: anywhere;
|
|
1811
|
-
}
|
|
1812
|
-
|
|
1813
|
-
/* Automate-first recovery: the primary action leads, option cards appear
|
|
1814
|
-
only on a genuine overlap, the terminal guidance is a collapsed fallback. */
|
|
1815
|
-
.liftoff-conflict-summary {
|
|
1816
|
-
margin: 0 0 10px;
|
|
1817
|
-
}
|
|
1818
|
-
.liftoff-conflict-primary {
|
|
1819
|
-
display: flex;
|
|
1820
|
-
flex-direction: column;
|
|
1821
|
-
align-items: flex-start;
|
|
1822
|
-
gap: 8px;
|
|
1823
|
-
margin-bottom: 12px;
|
|
1824
|
-
}
|
|
1825
|
-
.liftoff-conflict-primary .btn.primary {
|
|
1826
|
-
align-self: flex-start;
|
|
1827
|
-
}
|
|
1828
|
-
.liftoff-conflict-primary-status[data-state="err"],
|
|
1829
|
-
.liftoff-conflict-cards-status[data-state="err"] {
|
|
1830
|
-
color: var(--admin-danger, #b3261e);
|
|
1831
|
-
}
|
|
1832
|
-
.liftoff-conflict-cards {
|
|
1833
|
-
margin: 4px 0 12px;
|
|
1834
|
-
padding: 12px;
|
|
1835
|
-
border: 1px solid var(--admin-line-strong);
|
|
1836
|
-
border-radius: var(--admin-radius-sm);
|
|
1837
|
-
background: var(--admin-surface-soft);
|
|
1838
|
-
}
|
|
1839
|
-
.liftoff-conflict-files {
|
|
1840
|
-
list-style: none;
|
|
1841
|
-
margin: 8px 0 12px;
|
|
1842
|
-
padding: 8px 10px;
|
|
1843
|
-
border-radius: var(--admin-radius-sm);
|
|
1844
|
-
background: var(--admin-surface, #fff);
|
|
1845
|
-
font-size: 12px;
|
|
1846
|
-
line-height: 1.6;
|
|
1847
|
-
overflow-x: auto;
|
|
1848
|
-
}
|
|
1849
|
-
.liftoff-conflict-card-grid {
|
|
1850
|
-
display: grid;
|
|
1851
|
-
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
|
|
1852
|
-
gap: 8px;
|
|
1853
|
-
}
|
|
1854
|
-
.liftoff-option-card {
|
|
1855
|
-
display: flex;
|
|
1856
|
-
flex-direction: column;
|
|
1857
|
-
gap: 4px;
|
|
1858
|
-
text-align: left;
|
|
1859
|
-
padding: 12px;
|
|
1860
|
-
border: 1px solid var(--admin-line-strong);
|
|
1861
|
-
border-radius: var(--admin-radius-sm);
|
|
1862
|
-
background: var(--admin-surface, #fff);
|
|
1863
|
-
cursor: pointer;
|
|
1864
|
-
font: inherit;
|
|
1865
|
-
color: inherit;
|
|
1866
|
-
}
|
|
1867
|
-
.liftoff-option-card:hover:not(:disabled) {
|
|
1868
|
-
border-color: var(--admin-accent, #0f9d8f);
|
|
1869
|
-
}
|
|
1870
|
-
.liftoff-option-card:disabled {
|
|
1871
|
-
opacity: 0.55;
|
|
1872
|
-
cursor: default;
|
|
1873
|
-
}
|
|
1874
|
-
.liftoff-option-card[data-recommended="1"] {
|
|
1875
|
-
border-color: var(--admin-accent, #0f9d8f);
|
|
1876
|
-
box-shadow: inset 0 0 0 1px var(--admin-accent, #0f9d8f);
|
|
1877
|
-
}
|
|
1878
|
-
.liftoff-option-card b {
|
|
1879
|
-
font-size: 13px;
|
|
1880
|
-
}
|
|
1881
|
-
.liftoff-option-card span {
|
|
1882
|
-
font-size: 11px;
|
|
1883
|
-
color: var(--admin-muted);
|
|
1884
|
-
}
|
|
1885
|
-
.liftoff-conflict-expert {
|
|
1886
|
-
margin-top: 10px;
|
|
1887
|
-
border-top: 1px solid var(--admin-line, #e5e5e5);
|
|
1888
|
-
padding-top: 8px;
|
|
1889
|
-
}
|
|
1890
|
-
.liftoff-conflict-expert > summary {
|
|
1891
|
-
cursor: pointer;
|
|
1892
|
-
font-size: 12px;
|
|
1893
|
-
color: var(--admin-muted);
|
|
1894
|
-
user-select: none;
|
|
1895
|
-
}
|
|
1896
|
-
|
|
1897
|
-
.liftoff-stages {
|
|
1898
|
-
list-style: none;
|
|
1899
|
-
margin: 6px 0 4px;
|
|
1900
|
-
padding: 0;
|
|
1901
|
-
display: grid;
|
|
1902
|
-
gap: 0;
|
|
1903
|
-
}
|
|
1904
|
-
|
|
1905
|
-
.liftoff-stages li {
|
|
1906
|
-
position: relative;
|
|
1907
|
-
display: flex;
|
|
1908
|
-
gap: 12px;
|
|
1909
|
-
align-items: flex-start;
|
|
1910
|
-
padding: 9px 0;
|
|
1911
|
-
}
|
|
1912
|
-
|
|
1913
|
-
/* Connecting rail between the dots. */
|
|
1914
|
-
.liftoff-stages li + li::before {
|
|
1915
|
-
content: "";
|
|
1916
|
-
position: absolute;
|
|
1917
|
-
left: 10px;
|
|
1918
|
-
top: -8px;
|
|
1919
|
-
height: 16px;
|
|
1920
|
-
width: 2px;
|
|
1921
|
-
background: var(--admin-line-strong);
|
|
1922
|
-
}
|
|
1923
|
-
|
|
1924
|
-
.liftoff-stages li > div {
|
|
1925
|
-
display: grid;
|
|
1926
|
-
gap: 1px;
|
|
1927
|
-
}
|
|
1928
|
-
|
|
1929
|
-
.liftoff-stages b {
|
|
1930
|
-
font-size: 13px;
|
|
1931
|
-
}
|
|
1932
|
-
|
|
1933
|
-
.liftoff-stages span:not(.liftoff-dot) {
|
|
1934
|
-
font-size: 12px;
|
|
1935
|
-
color: var(--admin-muted);
|
|
1936
|
-
}
|
|
1937
|
-
|
|
1938
|
-
.liftoff-dot {
|
|
1939
|
-
flex: none;
|
|
1940
|
-
width: 21px;
|
|
1941
|
-
height: 21px;
|
|
1942
|
-
margin-top: 1px;
|
|
1943
|
-
border-radius: 50%;
|
|
1944
|
-
border: 2px solid var(--admin-line-strong);
|
|
1945
|
-
display: grid;
|
|
1946
|
-
place-items: center;
|
|
1947
|
-
font-size: 12px;
|
|
1948
|
-
font-weight: 700;
|
|
1949
|
-
color: #fff;
|
|
1950
|
-
background: transparent;
|
|
1951
|
-
transition: all 0.25s ease;
|
|
1952
|
-
}
|
|
1953
|
-
|
|
1954
|
-
.liftoff-stages li[data-state="active"] .liftoff-dot {
|
|
1955
|
-
border-color: var(--admin-teal, #0f9d8f);
|
|
1956
|
-
box-shadow: 0 0 0 0 color-mix(in srgb, var(--admin-teal, #0f9d8f) 45%, transparent);
|
|
1957
|
-
animation: liftoff-pulse 1.3s ease-out infinite;
|
|
1958
|
-
}
|
|
1959
|
-
|
|
1960
|
-
.liftoff-stages li[data-state="active"] b {
|
|
1961
|
-
color: var(--admin-teal, #0f9d8f);
|
|
1962
|
-
}
|
|
1963
|
-
|
|
1964
|
-
@keyframes liftoff-pulse {
|
|
1965
|
-
from { box-shadow: 0 0 0 0 color-mix(in srgb, var(--admin-teal, #0f9d8f) 45%, transparent); }
|
|
1966
|
-
to { box-shadow: 0 0 0 10px transparent; }
|
|
1967
|
-
}
|
|
1968
|
-
|
|
1969
|
-
.liftoff-stages li[data-state="done"] .liftoff-dot {
|
|
1970
|
-
border-color: var(--admin-green);
|
|
1971
|
-
background: var(--admin-green);
|
|
1972
|
-
}
|
|
1973
|
-
|
|
1974
|
-
.liftoff-stages li[data-state="done"] .liftoff-dot::after {
|
|
1975
|
-
content: "✓";
|
|
1976
|
-
}
|
|
1977
|
-
|
|
1978
|
-
.liftoff-stages li[data-state="fail"] .liftoff-dot {
|
|
1979
|
-
border-color: var(--admin-red);
|
|
1980
|
-
background: var(--admin-red);
|
|
1981
|
-
}
|
|
1982
|
-
|
|
1983
|
-
.liftoff-stages li[data-state="fail"] .liftoff-dot::after {
|
|
1984
|
-
content: "✕";
|
|
1985
|
-
}
|
|
1986
|
-
|
|
1987
|
-
.liftoff-live {
|
|
1988
|
-
margin: 8px 0 0;
|
|
1989
|
-
min-height: 18px;
|
|
1990
|
-
font-size: 13px;
|
|
1991
|
-
color: var(--admin-ink);
|
|
1992
|
-
}
|
|
1993
|
-
|
|
1994
|
-
/* The shared scrolling live-log console — a "CI build log" window reused across
|
|
1995
|
-
the build / integrate / rebuild / ship flight dialogs. Fixed height, scrolls,
|
|
1996
|
-
newest line auto-scrolled into view by the client (see makeLogConsole). */
|
|
1997
|
-
.stream-log {
|
|
1998
|
-
margin: 10px 0 0;
|
|
1999
|
-
max-height: 150px;
|
|
2000
|
-
overflow-y: auto;
|
|
2001
|
-
padding: 8px 10px;
|
|
2002
|
-
border: 1px solid var(--admin-border, rgba(0, 0, 0, 0.12));
|
|
2003
|
-
border-radius: 8px;
|
|
2004
|
-
background: var(--admin-code-bg, rgba(0, 0, 0, 0.04));
|
|
2005
|
-
font-family: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
|
|
2006
|
-
font-size: 12px;
|
|
2007
|
-
line-height: 1.5;
|
|
2008
|
-
color: var(--admin-ink);
|
|
2009
|
-
-webkit-overflow-scrolling: touch;
|
|
2010
|
-
}
|
|
2011
|
-
.stream-log:empty {
|
|
2012
|
-
display: none;
|
|
2013
|
-
}
|
|
2014
|
-
.stream-log-line {
|
|
2015
|
-
white-space: pre-wrap;
|
|
2016
|
-
word-break: break-word;
|
|
2017
|
-
}
|
|
2018
|
-
|
|
2019
|
-
.liftoff-meta {
|
|
2020
|
-
display: flex;
|
|
2021
|
-
justify-content: space-between;
|
|
2022
|
-
align-items: center;
|
|
2023
|
-
margin-top: 14px;
|
|
2024
|
-
color: var(--admin-muted);
|
|
2025
|
-
}
|
|
2026
|
-
|
|
2027
|
-
.liftoff-outcome-phase {
|
|
2028
|
-
text-align: center;
|
|
2029
|
-
padding-top: 6px;
|
|
2030
|
-
}
|
|
2031
|
-
|
|
2032
|
-
.liftoff-outcome-phase .liftoff-actions {
|
|
2033
|
-
justify-content: center;
|
|
2034
|
-
}
|
|
2035
|
-
|
|
2036
|
-
.liftoff-seal {
|
|
2037
|
-
width: 56px;
|
|
2038
|
-
height: 56px;
|
|
2039
|
-
margin: 4px auto 12px;
|
|
2040
|
-
border-radius: 50%;
|
|
2041
|
-
display: grid;
|
|
2042
|
-
place-items: center;
|
|
2043
|
-
font-size: 28px;
|
|
2044
|
-
font-weight: 700;
|
|
2045
|
-
color: #fff;
|
|
2046
|
-
}
|
|
2047
|
-
|
|
2048
|
-
.liftoff-seal[data-state="ok"] {
|
|
2049
|
-
background: var(--admin-green);
|
|
2050
|
-
animation: liftoff-seal-pop 0.45s cubic-bezier(0.2, 1.6, 0.4, 1);
|
|
2051
|
-
}
|
|
2052
|
-
|
|
2053
|
-
.liftoff-seal[data-state="ok"]::after {
|
|
2054
|
-
content: "✓";
|
|
2055
|
-
}
|
|
2056
|
-
|
|
2057
|
-
.liftoff-seal[data-state="err"] {
|
|
2058
|
-
background: var(--admin-red);
|
|
2059
|
-
}
|
|
2060
|
-
|
|
2061
|
-
.liftoff-seal[data-state="err"]::after {
|
|
2062
|
-
content: "✕";
|
|
2063
|
-
}
|
|
2064
|
-
|
|
2065
|
-
@keyframes liftoff-seal-pop {
|
|
2066
|
-
from { transform: scale(0.4); opacity: 0; }
|
|
2067
|
-
60% { transform: scale(1.12); }
|
|
2068
|
-
to { transform: none; opacity: 1; }
|
|
2069
|
-
}
|
|
2070
|
-
|
|
2071
|
-
.liftoff-confetti {
|
|
2072
|
-
pointer-events: none;
|
|
2073
|
-
position: absolute;
|
|
2074
|
-
inset: 0;
|
|
2075
|
-
overflow: hidden;
|
|
2076
|
-
border-radius: inherit;
|
|
2077
|
-
}
|
|
2078
|
-
|
|
2079
|
-
.liftoff-confetti i {
|
|
2080
|
-
position: absolute;
|
|
2081
|
-
top: -12px;
|
|
2082
|
-
width: 7px;
|
|
2083
|
-
height: 11px;
|
|
2084
|
-
border-radius: 2px;
|
|
2085
|
-
opacity: 0;
|
|
2086
|
-
animation-name: liftoff-confetti-fall;
|
|
2087
|
-
animation-timing-function: ease-in;
|
|
2088
|
-
animation-fill-mode: forwards;
|
|
2089
|
-
}
|
|
2090
|
-
|
|
2091
|
-
@keyframes liftoff-confetti-fall {
|
|
2092
|
-
0% { opacity: 1; transform: translate(0, 0) rotate(0deg); }
|
|
2093
|
-
100% {
|
|
2094
|
-
opacity: 0.9;
|
|
2095
|
-
transform: translate(var(--liftoff-drift, 0px), 660px) rotate(var(--liftoff-spin, 240deg));
|
|
2096
|
-
}
|
|
2097
|
-
}
|
|
2098
|
-
|
|
2099
|
-
@media (prefers-reduced-motion: reduce) {
|
|
2100
|
-
.liftoff-card,
|
|
2101
|
-
.liftoff-card[data-mode="flight"]::before,
|
|
2102
|
-
.liftoff-stages li[data-state="active"] .liftoff-dot,
|
|
2103
|
-
.liftoff-seal[data-state="ok"] {
|
|
2104
|
-
animation: none;
|
|
2105
|
-
}
|
|
2106
|
-
}
|
|
2107
|
-
|
|
2108
|
-
.domain-connect-controls {
|
|
2109
|
-
display: grid;
|
|
2110
|
-
gap: 8px;
|
|
2111
|
-
justify-items: end;
|
|
2112
|
-
min-width: 220px;
|
|
2113
|
-
}
|
|
2114
|
-
|
|
2115
|
-
.domain-connect-controls input[type="text"] {
|
|
2116
|
-
width: 100%;
|
|
2117
|
-
min-height: 36px;
|
|
2118
|
-
padding: 0 10px;
|
|
2119
|
-
border: 1px solid var(--admin-line-strong);
|
|
2120
|
-
border-radius: var(--admin-radius-sm);
|
|
2121
|
-
background: var(--admin-surface);
|
|
2122
|
-
color: var(--admin-ink);
|
|
2123
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
2124
|
-
font-size: 12px;
|
|
2125
|
-
}
|
|
2126
|
-
|
|
2127
|
-
.domain-rehearsal {
|
|
2128
|
-
display: inline-flex;
|
|
2129
|
-
align-items: center;
|
|
2130
|
-
gap: 6px;
|
|
2131
|
-
color: var(--admin-muted);
|
|
2132
|
-
font-size: 12px;
|
|
2133
|
-
}
|
|
2134
|
-
|
|
2135
|
-
@media (max-width: 720px) {
|
|
2136
|
-
.domain-connect-controls {
|
|
2137
|
-
justify-items: stretch;
|
|
2138
|
-
min-width: 0;
|
|
2139
|
-
width: 100%;
|
|
2140
|
-
}
|
|
2141
|
-
}
|
|
2142
|
-
|
|
2143
|
-
/* Apex-cutover readiness itemization */
|
|
2144
|
-
.domain-readiness {
|
|
2145
|
-
margin: 0.75rem 0;
|
|
2146
|
-
padding: 0.75rem 1rem;
|
|
2147
|
-
border: 1px solid var(--hairline, rgba(0, 0, 0, 0.12));
|
|
2148
|
-
border-radius: 0.5rem;
|
|
2149
|
-
}
|
|
2150
|
-
.domain-readiness-head {
|
|
2151
|
-
display: flex;
|
|
2152
|
-
align-items: center;
|
|
2153
|
-
gap: 0.5rem;
|
|
2154
|
-
margin-bottom: 0.5rem;
|
|
2155
|
-
}
|
|
2156
|
-
.domain-readiness-list {
|
|
2157
|
-
list-style: none;
|
|
2158
|
-
margin: 0;
|
|
2159
|
-
padding: 0;
|
|
2160
|
-
display: grid;
|
|
2161
|
-
gap: 0.25rem;
|
|
2162
|
-
}
|
|
2163
|
-
.domain-readiness-list li {
|
|
2164
|
-
display: grid;
|
|
2165
|
-
gap: 0.1rem;
|
|
2166
|
-
font-size: 0.9em;
|
|
2167
|
-
}
|
|
2168
|
-
.domain-readiness-item-head {
|
|
2169
|
-
display: flex;
|
|
2170
|
-
align-items: baseline;
|
|
2171
|
-
gap: 0.4rem;
|
|
2172
|
-
}
|
|
2173
|
-
.domain-readiness-mark {
|
|
2174
|
-
font-weight: 700;
|
|
2175
|
-
width: 1em;
|
|
2176
|
-
flex: 0 0 auto;
|
|
2177
|
-
}
|
|
2178
|
-
.domain-readiness-list li.ok .domain-readiness-mark {
|
|
2179
|
-
color: var(--good, #1a7f37);
|
|
2180
|
-
}
|
|
2181
|
-
.domain-readiness-list li.blocked .domain-readiness-mark {
|
|
2182
|
-
color: var(--bad, #b42318);
|
|
2183
|
-
}
|
|
2184
|
-
/* detail + remedy sit on their OWN lines, indented under the label (past the
|
|
2185
|
-
mark) so neither runs together with the bold label above it. */
|
|
2186
|
-
.domain-readiness-detail,
|
|
2187
|
-
.domain-readiness-remedy {
|
|
2188
|
-
margin: 0;
|
|
2189
|
-
padding-left: 1.4em;
|
|
2190
|
-
}
|
|
2191
|
-
.domain-readiness-detail {
|
|
2192
|
-
opacity: 0.75;
|
|
2193
|
-
}
|
|
2194
|
-
.domain-readiness-remedy {
|
|
2195
|
-
color: var(--accent, #0b6bcb);
|
|
2196
|
-
opacity: 0.95;
|
|
2197
|
-
}
|
|
2198
|
-
|
|
2199
|
-
/* Aggregate card + ship plan */
|
|
2200
|
-
.aggregate-prs {
|
|
2201
|
-
display: grid;
|
|
2202
|
-
gap: 6px;
|
|
2203
|
-
}
|
|
2204
|
-
|
|
2205
|
-
.aggregate-prs-list {
|
|
2206
|
-
list-style: none;
|
|
2207
|
-
margin: 0;
|
|
2208
|
-
padding: 0;
|
|
2209
|
-
display: grid;
|
|
2210
|
-
gap: 4px;
|
|
2211
|
-
}
|
|
2212
|
-
|
|
2213
|
-
.aggregate-prs-list li {
|
|
2214
|
-
display: flex;
|
|
2215
|
-
align-items: center;
|
|
2216
|
-
gap: 8px;
|
|
2217
|
-
font-size: 13px;
|
|
2218
|
-
}
|
|
2219
|
-
|
|
2220
|
-
/* Provenance ("integrated <age> by <email>") sits after the sha, subtle. */
|
|
2221
|
-
.aggregate-pr-meta {
|
|
2222
|
-
font-size: 12px;
|
|
2223
|
-
}
|
|
2224
|
-
|
|
2225
|
-
/* The per-change Undo (revert) button is pushed to the row's trailing edge
|
|
2226
|
-
and kept compact so the batch reads as a list, not a stack of buttons. */
|
|
2227
|
-
.aggregate-prs-list .btn-compact {
|
|
2228
|
-
margin-left: auto;
|
|
2229
|
-
padding: 2px 10px;
|
|
2230
|
-
font-size: 12px;
|
|
2231
|
-
}
|
|
2232
|
-
|
|
2233
|
-
.aggregate-plan {
|
|
2234
|
-
padding: 0.75rem 1rem;
|
|
2235
|
-
border: 1px solid var(--hairline, rgba(0, 0, 0, 0.12));
|
|
2236
|
-
border-radius: 0.5rem;
|
|
2237
|
-
display: grid;
|
|
2238
|
-
gap: 8px;
|
|
2239
|
-
}
|
|
2240
|
-
|
|
2241
|
-
.aggregate-plan-head {
|
|
2242
|
-
display: flex;
|
|
2243
|
-
align-items: center;
|
|
2244
|
-
gap: 0.5rem;
|
|
2245
|
-
}
|
|
2246
|
-
|
|
2247
|
-
.aggregate-plan-grid {
|
|
2248
|
-
display: grid;
|
|
2249
|
-
grid-template-columns: max-content 1fr;
|
|
2250
|
-
gap: 4px 16px;
|
|
2251
|
-
margin: 0;
|
|
2252
|
-
}
|
|
2253
|
-
|
|
2254
|
-
.aggregate-plan-grid dt {
|
|
2255
|
-
color: var(--admin-muted);
|
|
2256
|
-
font-size: 12px;
|
|
2257
|
-
}
|
|
2258
|
-
|
|
2259
|
-
.aggregate-plan-grid dd {
|
|
2260
|
-
margin: 0;
|
|
2261
|
-
font-size: 13px;
|
|
2262
|
-
overflow-wrap: anywhere;
|
|
2263
|
-
}
|
|
2264
|
-
|
|
2265
|
-
/* ── Preview-aggregate console — the go-live surface ─────────────────────
|
|
2266
|
-
Everything here is scoped under [data-publish-aggregate] / .agg-* so it
|
|
2267
|
-
never leaks into the other panels (candidate queue, versions, branches)
|
|
2268
|
-
that share .panel / .field / .check-row / .chip. */
|
|
2269
|
-
.agg-console .panel-body {
|
|
2270
|
-
gap: 16px;
|
|
2271
|
-
}
|
|
2272
|
-
|
|
2273
|
-
[data-publish-aggregate] .sha-token {
|
|
2274
|
-
display: inline-flex;
|
|
2275
|
-
align-items: center;
|
|
2276
|
-
padding: 2px 8px;
|
|
2277
|
-
border: 1px solid var(--admin-line-strong);
|
|
2278
|
-
border-radius: 5px;
|
|
2279
|
-
background: var(--admin-surface-soft);
|
|
2280
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
2281
|
-
font-size: 12.5px;
|
|
2282
|
-
color: var(--admin-ink);
|
|
2283
|
-
}
|
|
2284
|
-
[data-publish-aggregate] .sha-token.ghost {
|
|
2285
|
-
background: transparent;
|
|
2286
|
-
color: var(--admin-muted);
|
|
2287
|
-
border-style: dashed;
|
|
2288
|
-
}
|
|
2289
|
-
|
|
2290
|
-
/* AI-assembled release summary */
|
|
2291
|
-
.agg-summary {
|
|
2292
|
-
display: grid;
|
|
2293
|
-
gap: 11px;
|
|
2294
|
-
padding: 15px 16px;
|
|
2295
|
-
border: 1px solid var(--admin-line);
|
|
2296
|
-
border-radius: var(--admin-radius);
|
|
2297
|
-
background:
|
|
2298
|
-
linear-gradient(180deg, var(--admin-teal-soft), transparent 62%),
|
|
2299
|
-
var(--admin-surface);
|
|
2300
|
-
}
|
|
2301
|
-
.agg-summary[hidden] {
|
|
2302
|
-
display: none;
|
|
2303
|
-
}
|
|
2304
|
-
.agg-summary-head {
|
|
2305
|
-
display: flex;
|
|
2306
|
-
align-items: center;
|
|
2307
|
-
gap: 10px;
|
|
2308
|
-
}
|
|
2309
|
-
.agg-summary-glyph {
|
|
2310
|
-
flex: none;
|
|
2311
|
-
width: 26px;
|
|
2312
|
-
height: 26px;
|
|
2313
|
-
display: grid;
|
|
2314
|
-
place-items: center;
|
|
2315
|
-
border-radius: 7px;
|
|
2316
|
-
background: var(--admin-surface);
|
|
2317
|
-
color: var(--admin-teal-dark);
|
|
2318
|
-
border: 1px solid color-mix(in srgb, var(--admin-teal) 30%, transparent);
|
|
2319
|
-
}
|
|
2320
|
-
/* The sparkle glyphs are filled shapes; opt out of the global stroked-svg rule. */
|
|
2321
|
-
.agg-summary-glyph svg,
|
|
2322
|
-
.agg-ai-chip svg {
|
|
2323
|
-
fill: currentColor;
|
|
2324
|
-
stroke: none;
|
|
2325
|
-
}
|
|
2326
|
-
.agg-summary-glyph svg {
|
|
2327
|
-
width: 15px;
|
|
2328
|
-
height: 15px;
|
|
2329
|
-
}
|
|
2330
|
-
.agg-summary-label {
|
|
2331
|
-
flex: 1;
|
|
2332
|
-
font-size: 11px;
|
|
2333
|
-
font-weight: 760;
|
|
2334
|
-
letter-spacing: 0.08em;
|
|
2335
|
-
text-transform: uppercase;
|
|
2336
|
-
color: var(--admin-muted);
|
|
2337
|
-
}
|
|
2338
|
-
.agg-ai-chip {
|
|
2339
|
-
display: inline-flex;
|
|
2340
|
-
align-items: center;
|
|
2341
|
-
gap: 5px;
|
|
2342
|
-
padding: 3px 9px;
|
|
2343
|
-
border-radius: 999px;
|
|
2344
|
-
background: var(--admin-teal-soft);
|
|
2345
|
-
color: #007973;
|
|
2346
|
-
font-size: 10px;
|
|
2347
|
-
font-weight: 760;
|
|
2348
|
-
letter-spacing: 0.06em;
|
|
2349
|
-
text-transform: uppercase;
|
|
2350
|
-
}
|
|
2351
|
-
.agg-ai-chip svg {
|
|
2352
|
-
width: 10px;
|
|
2353
|
-
height: 10px;
|
|
2354
|
-
}
|
|
2355
|
-
.agg-summary-lead {
|
|
2356
|
-
margin: 0;
|
|
2357
|
-
font-size: 15px;
|
|
2358
|
-
line-height: 1.55;
|
|
2359
|
-
color: var(--admin-ink);
|
|
2360
|
-
max-width: 68ch;
|
|
2361
|
-
}
|
|
2362
|
-
.agg-summary-points {
|
|
2363
|
-
list-style: none;
|
|
2364
|
-
margin: 0;
|
|
2365
|
-
padding: 0;
|
|
2366
|
-
display: grid;
|
|
2367
|
-
gap: 7px;
|
|
2368
|
-
}
|
|
2369
|
-
.agg-summary-points[hidden] {
|
|
2370
|
-
display: none;
|
|
2371
|
-
}
|
|
2372
|
-
.agg-summary-points li {
|
|
2373
|
-
display: flex;
|
|
2374
|
-
align-items: flex-start;
|
|
2375
|
-
gap: 9px;
|
|
2376
|
-
font-size: 13px;
|
|
2377
|
-
color: var(--admin-muted);
|
|
2378
|
-
line-height: 1.45;
|
|
2379
|
-
}
|
|
2380
|
-
.agg-summary-points .agg-tick {
|
|
2381
|
-
flex: none;
|
|
2382
|
-
margin-top: 1px;
|
|
2383
|
-
width: 16px;
|
|
2384
|
-
height: 16px;
|
|
2385
|
-
border-radius: 50%;
|
|
2386
|
-
display: grid;
|
|
2387
|
-
place-items: center;
|
|
2388
|
-
background: var(--admin-green-soft);
|
|
2389
|
-
color: var(--admin-green);
|
|
2390
|
-
}
|
|
2391
|
-
.agg-summary-points .agg-tick svg {
|
|
2392
|
-
width: 10px;
|
|
2393
|
-
height: 10px;
|
|
2394
|
-
}
|
|
2395
|
-
.agg-summary-points b {
|
|
2396
|
-
color: var(--admin-ink);
|
|
2397
|
-
font-weight: 640;
|
|
2398
|
-
}
|
|
2399
|
-
|
|
2400
|
-
/* Chain of custody: build → evidence → digest */
|
|
2401
|
-
.agg-chain {
|
|
2402
|
-
display: grid;
|
|
2403
|
-
grid-template-columns: 1fr auto 1fr auto 1fr;
|
|
2404
|
-
align-items: stretch;
|
|
2405
|
-
padding: 16px;
|
|
2406
|
-
border: 1px solid var(--admin-line);
|
|
2407
|
-
border-radius: var(--admin-radius);
|
|
2408
|
-
background: var(--admin-surface-soft);
|
|
2409
|
-
}
|
|
2410
|
-
.agg-stop {
|
|
2411
|
-
display: flex;
|
|
2412
|
-
flex-direction: column;
|
|
2413
|
-
gap: 7px;
|
|
2414
|
-
padding: 2px 12px;
|
|
2415
|
-
min-width: 0;
|
|
2416
|
-
}
|
|
2417
|
-
.agg-stop:first-child {
|
|
2418
|
-
padding-left: 2px;
|
|
2419
|
-
}
|
|
2420
|
-
.agg-stop:last-child {
|
|
2421
|
-
padding-right: 2px;
|
|
2422
|
-
}
|
|
2423
|
-
.agg-stop-label {
|
|
2424
|
-
font-size: 10.5px;
|
|
2425
|
-
font-weight: 760;
|
|
2426
|
-
letter-spacing: 0.08em;
|
|
2427
|
-
text-transform: uppercase;
|
|
2428
|
-
color: var(--admin-muted);
|
|
2429
|
-
}
|
|
2430
|
-
.agg-stop-val {
|
|
2431
|
-
display: flex;
|
|
2432
|
-
align-items: center;
|
|
2433
|
-
gap: 8px;
|
|
2434
|
-
font-size: 15px;
|
|
2435
|
-
font-weight: 680;
|
|
2436
|
-
}
|
|
2437
|
-
.agg-stop-val strong {
|
|
2438
|
-
font-size: 15px;
|
|
2439
|
-
}
|
|
2440
|
-
.agg-stop-cap {
|
|
2441
|
-
margin: 0;
|
|
2442
|
-
font-size: 12px;
|
|
2443
|
-
color: var(--admin-muted);
|
|
2444
|
-
line-height: 1.45;
|
|
2445
|
-
}
|
|
2446
|
-
.agg-stop-cap a {
|
|
2447
|
-
white-space: nowrap;
|
|
2448
|
-
}
|
|
2449
|
-
.agg-digest {
|
|
2450
|
-
padding: 2px 8px;
|
|
2451
|
-
border: 1px solid color-mix(in srgb, var(--admin-teal) 55%, var(--admin-line-strong));
|
|
2452
|
-
border-radius: 5px;
|
|
2453
|
-
background: var(--admin-teal-soft);
|
|
2454
|
-
color: #007973;
|
|
2455
|
-
}
|
|
2456
|
-
[data-publish-aggregate] [data-aggregate-evidence][data-state="passed"] {
|
|
2457
|
-
color: var(--admin-green);
|
|
2458
|
-
}
|
|
2459
|
-
[data-publish-aggregate] [data-aggregate-evidence][data-state="failed"] {
|
|
2460
|
-
color: var(--admin-red);
|
|
2461
|
-
}
|
|
2462
|
-
.agg-link {
|
|
2463
|
-
display: flex;
|
|
2464
|
-
flex-direction: column;
|
|
2465
|
-
align-items: center;
|
|
2466
|
-
justify-content: center;
|
|
2467
|
-
gap: 6px;
|
|
2468
|
-
padding: 0 2px;
|
|
2469
|
-
align-self: center;
|
|
2470
|
-
}
|
|
2471
|
-
.agg-flowlabel {
|
|
2472
|
-
font-size: 9px;
|
|
2473
|
-
font-weight: 720;
|
|
2474
|
-
letter-spacing: 0.06em;
|
|
2475
|
-
text-transform: uppercase;
|
|
2476
|
-
color: var(--admin-muted-2);
|
|
2477
|
-
}
|
|
2478
|
-
.agg-arrow {
|
|
2479
|
-
display: grid;
|
|
2480
|
-
place-items: center;
|
|
2481
|
-
color: var(--admin-teal);
|
|
2482
|
-
}
|
|
2483
|
-
.agg-arrow svg {
|
|
2484
|
-
width: 32px;
|
|
2485
|
-
height: 10px;
|
|
2486
|
-
}
|
|
2487
|
-
|
|
2488
|
-
/* Staged → live delta strip */
|
|
2489
|
-
.agg-delta-strip {
|
|
2490
|
-
display: flex;
|
|
2491
|
-
align-items: center;
|
|
2492
|
-
gap: 12px;
|
|
2493
|
-
flex-wrap: wrap;
|
|
2494
|
-
padding: 10px 13px;
|
|
2495
|
-
border: 1px solid var(--admin-line);
|
|
2496
|
-
border-radius: var(--admin-radius-sm);
|
|
2497
|
-
background: var(--admin-surface-soft);
|
|
2498
|
-
}
|
|
2499
|
-
.agg-delta-strip[hidden] {
|
|
2500
|
-
display: none;
|
|
2501
|
-
}
|
|
2502
|
-
.agg-delta-node {
|
|
2503
|
-
display: flex;
|
|
2504
|
-
flex-direction: column;
|
|
2505
|
-
gap: 3px;
|
|
2506
|
-
}
|
|
2507
|
-
.agg-delta-mid {
|
|
2508
|
-
display: flex;
|
|
2509
|
-
align-items: center;
|
|
2510
|
-
gap: 8px;
|
|
2511
|
-
color: var(--admin-teal);
|
|
2512
|
-
}
|
|
2513
|
-
.agg-delta-mid svg {
|
|
2514
|
-
width: 24px;
|
|
2515
|
-
height: 10px;
|
|
2516
|
-
}
|
|
2517
|
-
.agg-delta-count {
|
|
2518
|
-
padding: 2px 9px;
|
|
2519
|
-
border-radius: 999px;
|
|
2520
|
-
background: var(--admin-teal-soft);
|
|
2521
|
-
color: #007973;
|
|
2522
|
-
font-size: 11.5px;
|
|
2523
|
-
font-weight: 720;
|
|
2524
|
-
}
|
|
2525
|
-
|
|
2526
|
-
/* Publish plan → launch manifest */
|
|
2527
|
-
[data-publish-aggregate] .agg-manifest {
|
|
2528
|
-
display: block;
|
|
2529
|
-
padding: 0;
|
|
2530
|
-
overflow: hidden;
|
|
2531
|
-
background:
|
|
2532
|
-
linear-gradient(180deg, var(--admin-surface-tint), transparent 46px),
|
|
2533
|
-
var(--admin-surface);
|
|
2534
|
-
}
|
|
2535
|
-
.agg-manifest-head {
|
|
2536
|
-
padding: 12px 15px;
|
|
2537
|
-
border-bottom: 1px dashed var(--admin-line-strong);
|
|
2538
|
-
}
|
|
2539
|
-
.agg-manifest-head b {
|
|
2540
|
-
display: flex;
|
|
2541
|
-
align-items: center;
|
|
2542
|
-
gap: 8px;
|
|
2543
|
-
font-size: 13px;
|
|
2544
|
-
}
|
|
2545
|
-
.agg-manifest-head b svg {
|
|
2546
|
-
width: 15px;
|
|
2547
|
-
height: 15px;
|
|
2548
|
-
color: var(--admin-teal);
|
|
2549
|
-
}
|
|
2550
|
-
[data-publish-aggregate] .agg-manifest .aggregate-plan-grid {
|
|
2551
|
-
padding: 8px 15px 12px;
|
|
2552
|
-
}
|
|
2553
|
-
[data-publish-aggregate] .agg-manifest > .note {
|
|
2554
|
-
margin: 0;
|
|
2555
|
-
padding: 11px 15px;
|
|
2556
|
-
border-top: 1px dashed var(--admin-line-strong);
|
|
2557
|
-
background: var(--admin-surface-soft);
|
|
2558
|
-
}
|
|
2559
|
-
|
|
2560
|
-
/* Openable "Included changes" — reused by the Published Versions "What
|
|
2561
|
-
shipped" per-version disclosure (.ver-changes below); the staged-delta
|
|
2562
|
-
table above no longer uses the <details> wrap itself, only the row/diff
|
|
2563
|
-
classes it shares with changeSummaryItem. */
|
|
2564
|
-
.agg-included-details > summary {
|
|
2565
|
-
list-style: none;
|
|
2566
|
-
cursor: pointer;
|
|
2567
|
-
display: flex;
|
|
2568
|
-
align-items: center;
|
|
2569
|
-
gap: 10px;
|
|
2570
|
-
padding: 11px 15px;
|
|
2571
|
-
font-size: 12px;
|
|
2572
|
-
color: var(--admin-muted);
|
|
2573
|
-
}
|
|
2574
|
-
.agg-included-details > summary::-webkit-details-marker {
|
|
2575
|
-
display: none;
|
|
2576
|
-
}
|
|
2577
|
-
.agg-included-details > summary::marker {
|
|
2578
|
-
content: "";
|
|
2579
|
-
}
|
|
2580
|
-
.agg-included-details > summary:focus-visible {
|
|
2581
|
-
outline: 2px solid var(--admin-teal);
|
|
2582
|
-
outline-offset: -2px;
|
|
2583
|
-
}
|
|
2584
|
-
.agg-included-lead {
|
|
2585
|
-
font-size: 11px;
|
|
2586
|
-
font-weight: 740;
|
|
2587
|
-
letter-spacing: 0.03em;
|
|
2588
|
-
text-transform: uppercase;
|
|
2589
|
-
color: var(--admin-ink);
|
|
2590
|
-
}
|
|
2591
|
-
.agg-included-badge {
|
|
2592
|
-
padding: 1px 8px;
|
|
2593
|
-
border-radius: 999px;
|
|
2594
|
-
background: var(--admin-surface-tint);
|
|
2595
|
-
color: #3f4d49;
|
|
2596
|
-
font-size: 11.5px;
|
|
2597
|
-
font-weight: 720;
|
|
2598
|
-
}
|
|
2599
|
-
.agg-chev {
|
|
2600
|
-
margin-left: auto;
|
|
2601
|
-
display: inline-flex;
|
|
2602
|
-
color: var(--admin-muted-2);
|
|
2603
|
-
transition: transform 0.18s ease;
|
|
2604
|
-
}
|
|
2605
|
-
.agg-chev svg {
|
|
2606
|
-
width: 14px;
|
|
2607
|
-
height: 14px;
|
|
2608
|
-
}
|
|
2609
|
-
.agg-included-details[open] > summary .agg-chev {
|
|
2610
|
-
transform: rotate(90deg);
|
|
2611
|
-
}
|
|
2612
|
-
@media (prefers-reduced-motion: reduce) {
|
|
2613
|
-
.agg-chev {
|
|
2614
|
-
transition: none;
|
|
2615
|
-
}
|
|
2616
|
-
}
|
|
2617
|
-
.agg-included-list {
|
|
2618
|
-
list-style: none;
|
|
2619
|
-
margin: 0;
|
|
2620
|
-
padding: 0 15px 14px;
|
|
2621
|
-
display: grid;
|
|
2622
|
-
gap: 8px;
|
|
2623
|
-
}
|
|
2624
|
-
.agg-included-item {
|
|
2625
|
-
display: grid;
|
|
2626
|
-
gap: 5px;
|
|
2627
|
-
padding: 11px 12px;
|
|
2628
|
-
border: 1px solid var(--admin-line);
|
|
2629
|
-
border-radius: var(--admin-radius-sm);
|
|
2630
|
-
background: var(--admin-surface-soft);
|
|
2631
|
-
}
|
|
2632
|
-
.agg-included-top {
|
|
2633
|
-
display: flex;
|
|
2634
|
-
align-items: center;
|
|
2635
|
-
gap: 10px;
|
|
2636
|
-
flex-wrap: wrap;
|
|
2637
|
-
}
|
|
2638
|
-
.agg-included-pr {
|
|
2639
|
-
font-weight: 720;
|
|
2640
|
-
font-size: 13px;
|
|
2641
|
-
}
|
|
2642
|
-
.agg-included-title {
|
|
2643
|
-
font-weight: 600;
|
|
2644
|
-
font-size: 13.5px;
|
|
2645
|
-
}
|
|
2646
|
-
.agg-included-desc {
|
|
2647
|
-
margin: 0;
|
|
2648
|
-
color: var(--admin-muted);
|
|
2649
|
-
font-size: 12.5px;
|
|
2650
|
-
line-height: 1.5;
|
|
2651
|
-
}
|
|
2652
|
-
.agg-included-meta {
|
|
2653
|
-
display: flex;
|
|
2654
|
-
align-items: center;
|
|
2655
|
-
gap: 6px;
|
|
2656
|
-
flex-wrap: wrap;
|
|
2657
|
-
color: var(--admin-muted-2);
|
|
2658
|
-
font-size: 11.5px;
|
|
2659
|
-
}
|
|
2660
|
-
.agg-included-meta > span + span::before {
|
|
2661
|
-
content: "·";
|
|
2662
|
-
padding-right: 6px;
|
|
2663
|
-
color: var(--admin-line-strong);
|
|
2664
|
-
}
|
|
2665
|
-
.agg-included-meta .sha-token {
|
|
2666
|
-
font-size: 11px;
|
|
2667
|
-
padding: 1px 6px;
|
|
2668
|
-
}
|
|
2669
|
-
|
|
2670
|
-
/* Published-versions "what shipped": the release summary lead + openable
|
|
2671
|
-
per-change list, inline in the Version cell. Reuses the .agg-included-*
|
|
2672
|
-
item markup; only the container chrome is retuned for a table cell. */
|
|
2673
|
-
.ver-summary {
|
|
2674
|
-
margin: 6px 0 0;
|
|
2675
|
-
color: var(--admin-muted);
|
|
2676
|
-
font-size: 12.5px;
|
|
2677
|
-
line-height: 1.5;
|
|
2678
|
-
max-width: 48ch;
|
|
2679
|
-
}
|
|
2680
|
-
.ver-changes {
|
|
2681
|
-
margin-top: 8px;
|
|
2682
|
-
border: 1px solid var(--admin-line);
|
|
2683
|
-
border-radius: var(--admin-radius-sm);
|
|
2684
|
-
background: var(--admin-surface-soft);
|
|
2685
|
-
}
|
|
2686
|
-
.ver-changes > summary {
|
|
2687
|
-
padding: 8px 10px;
|
|
2688
|
-
}
|
|
2689
|
-
.ver-changes .agg-included-list {
|
|
2690
|
-
padding: 0 10px 10px;
|
|
2691
|
-
}
|
|
2692
|
-
|
|
2693
|
-
/* Branch-retention "what this branch changes": the PR title + one-line
|
|
2694
|
-
description + scope meta, inline under the branch ref. */
|
|
2695
|
-
.branch-pr-title {
|
|
2696
|
-
margin: 6px 0 0;
|
|
2697
|
-
font-weight: 600;
|
|
2698
|
-
font-size: 13px;
|
|
2699
|
-
color: var(--admin-ink);
|
|
2700
|
-
max-width: 46ch;
|
|
2701
|
-
}
|
|
2702
|
-
.branch-pr-desc {
|
|
2703
|
-
margin: 3px 0 0;
|
|
2704
|
-
color: var(--admin-muted);
|
|
2705
|
-
font-size: 12.5px;
|
|
2706
|
-
line-height: 1.5;
|
|
2707
|
-
max-width: 46ch;
|
|
2708
|
-
}
|
|
2709
|
-
.branch-pr-meta {
|
|
2710
|
-
margin-top: 5px;
|
|
2711
|
-
}
|
|
2712
|
-
.branch-pr-ai {
|
|
2713
|
-
color: var(--admin-teal-dark);
|
|
2714
|
-
font-weight: 640;
|
|
2715
|
-
}
|
|
2716
|
-
|
|
2717
|
-
/* Scope-of-change: lines added/removed (green/red), + the top-level readout */
|
|
2718
|
-
.agg-lines {
|
|
2719
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
2720
|
-
font-variant-numeric: tabular-nums;
|
|
2721
|
-
white-space: nowrap;
|
|
2722
|
-
}
|
|
2723
|
-
.agg-add {
|
|
2724
|
-
color: var(--admin-green);
|
|
2725
|
-
}
|
|
2726
|
-
.agg-del {
|
|
2727
|
-
color: var(--admin-red);
|
|
2728
|
-
}
|
|
2729
|
-
|
|
2730
|
-
/* "View diff" disclosure for a trivial change (the diff IS its summary). */
|
|
2731
|
-
.agg-diff {
|
|
2732
|
-
margin-top: 2px;
|
|
2733
|
-
}
|
|
2734
|
-
.agg-diff > summary {
|
|
2735
|
-
cursor: pointer;
|
|
2736
|
-
font-size: 11.5px;
|
|
2737
|
-
font-weight: 640;
|
|
2738
|
-
color: var(--admin-teal-dark);
|
|
2739
|
-
width: max-content;
|
|
2740
|
-
}
|
|
2741
|
-
.agg-diff[open] > summary {
|
|
2742
|
-
margin-bottom: 6px;
|
|
2743
|
-
}
|
|
2744
|
-
.agg-diff-file {
|
|
2745
|
-
margin-top: 6px;
|
|
2746
|
-
}
|
|
2747
|
-
.agg-diff-path {
|
|
2748
|
-
font-size: 11px;
|
|
2749
|
-
color: var(--admin-muted);
|
|
2750
|
-
margin-bottom: 3px;
|
|
2751
|
-
}
|
|
2752
|
-
.agg-diff-patch {
|
|
2753
|
-
margin: 0;
|
|
2754
|
-
padding: 8px 10px;
|
|
2755
|
-
border: 1px solid var(--admin-line);
|
|
2756
|
-
border-radius: var(--admin-radius-sm);
|
|
2757
|
-
background: var(--admin-surface);
|
|
2758
|
-
font-size: 11.5px;
|
|
2759
|
-
line-height: 1.5;
|
|
2760
|
-
overflow-x: auto;
|
|
2761
|
-
white-space: pre;
|
|
2762
|
-
}
|
|
2763
|
-
.agg-diff-add {
|
|
2764
|
-
color: var(--admin-green);
|
|
2765
|
-
background: var(--admin-green-soft);
|
|
2766
|
-
}
|
|
2767
|
-
.agg-diff-del {
|
|
2768
|
-
color: var(--admin-red);
|
|
2769
|
-
background: var(--admin-red-soft);
|
|
2770
|
-
}
|
|
2771
|
-
.agg-diff-hunk {
|
|
2772
|
-
color: var(--admin-muted-2);
|
|
2773
|
-
}
|
|
2774
|
-
|
|
2775
|
-
.agg-staged-head {
|
|
2776
|
-
display: flex;
|
|
2777
|
-
align-items: baseline;
|
|
2778
|
-
justify-content: space-between;
|
|
2779
|
-
gap: 12px;
|
|
2780
|
-
flex-wrap: wrap;
|
|
2781
|
-
}
|
|
2782
|
-
.agg-scope {
|
|
2783
|
-
display: inline-flex;
|
|
2784
|
-
align-items: center;
|
|
2785
|
-
gap: 8px;
|
|
2786
|
-
padding: 3px 10px;
|
|
2787
|
-
border: 1px solid var(--admin-line);
|
|
2788
|
-
border-radius: 999px;
|
|
2789
|
-
background: var(--admin-surface-soft);
|
|
2790
|
-
font-size: 11.5px;
|
|
2791
|
-
color: var(--admin-ink);
|
|
2792
|
-
}
|
|
2793
|
-
.agg-scope[hidden] {
|
|
2794
|
-
display: none;
|
|
2795
|
-
}
|
|
2796
|
-
.agg-scope > span + span::before {
|
|
2797
|
-
content: "·";
|
|
2798
|
-
padding-right: 8px;
|
|
2799
|
-
color: var(--admin-line-strong);
|
|
2800
|
-
}
|
|
2801
|
-
.agg-scope-label {
|
|
2802
|
-
font-size: 10px;
|
|
2803
|
-
font-weight: 760;
|
|
2804
|
-
letter-spacing: 0.08em;
|
|
2805
|
-
text-transform: uppercase;
|
|
2806
|
-
color: var(--admin-muted);
|
|
2807
|
-
}
|
|
2808
|
-
.agg-scope-partial {
|
|
2809
|
-
color: var(--admin-muted-2);
|
|
2810
|
-
font-style: italic;
|
|
2811
|
-
cursor: help;
|
|
2812
|
-
}
|
|
2813
|
-
|
|
2814
|
-
/* Publish action — stack the copy above a full-width, weighted CTA */
|
|
2815
|
-
[data-publish-aggregate] .agg-launch {
|
|
2816
|
-
display: grid;
|
|
2817
|
-
grid-template-columns: 1fr;
|
|
2818
|
-
gap: 12px;
|
|
2819
|
-
padding: 14px 0 2px;
|
|
2820
|
-
}
|
|
2821
|
-
/* Extra selector weight so the gradient/shadow beat the .btn.primary base. */
|
|
2822
|
-
[data-publish-aggregate] .btn.agg-publish-btn {
|
|
2823
|
-
width: 100%;
|
|
2824
|
-
justify-content: center;
|
|
2825
|
-
min-height: 50px;
|
|
2826
|
-
gap: 9px;
|
|
2827
|
-
font-size: 15px;
|
|
2828
|
-
border-radius: var(--admin-radius-sm);
|
|
2829
|
-
background: linear-gradient(178deg, var(--admin-teal), var(--admin-teal-dark));
|
|
2830
|
-
box-shadow: 0 8px 22px color-mix(in srgb, var(--admin-teal) 26%, transparent);
|
|
2831
|
-
}
|
|
2832
|
-
.agg-publish-btn svg {
|
|
2833
|
-
width: 18px;
|
|
2834
|
-
height: 18px;
|
|
2835
|
-
}
|
|
2836
|
-
[data-publish-aggregate] .btn.agg-publish-btn:not(:disabled):hover {
|
|
2837
|
-
filter: brightness(1.05);
|
|
2838
|
-
}
|
|
2839
|
-
[data-publish-aggregate] .btn.agg-publish-btn:disabled {
|
|
2840
|
-
background: var(--admin-surface-tint);
|
|
2841
|
-
border-color: var(--admin-line-strong);
|
|
2842
|
-
color: var(--admin-muted);
|
|
2843
|
-
box-shadow: none;
|
|
2844
|
-
cursor: not-allowed;
|
|
2845
|
-
}
|
|
2846
|
-
|
|
2847
|
-
@media (max-width: 760px) {
|
|
2848
|
-
.agg-chain {
|
|
2849
|
-
grid-template-columns: 1fr;
|
|
2850
|
-
gap: 4px;
|
|
2851
|
-
}
|
|
2852
|
-
.agg-link {
|
|
2853
|
-
flex-direction: row;
|
|
2854
|
-
gap: 10px;
|
|
2855
|
-
padding: 4px 12px;
|
|
2856
|
-
}
|
|
2857
|
-
.agg-link .agg-arrow svg {
|
|
2858
|
-
transform: rotate(90deg);
|
|
2859
|
-
}
|
|
2860
|
-
.agg-stop {
|
|
2861
|
-
padding: 8px 2px;
|
|
2862
|
-
}
|
|
2863
|
-
}
|
|
2864
|
-
|
|
2865
|
-
/* Versions + diff */
|
|
2866
|
-
.versions-table {
|
|
2867
|
-
min-width: 780px;
|
|
2868
|
-
}
|
|
2869
|
-
|
|
2870
|
-
/* Unified review pane */
|
|
2871
|
-
.publish-review-cell {
|
|
2872
|
-
padding: 12px 14px;
|
|
2873
|
-
background: var(--admin-surface-soft);
|
|
2874
|
-
}
|
|
2875
|
-
|
|
2876
|
-
.review-pane {
|
|
2877
|
-
display: grid;
|
|
2878
|
-
grid-template-columns: minmax(0, 1.3fr) minmax(0, 1fr);
|
|
2879
|
-
gap: 16px;
|
|
2880
|
-
align-items: start;
|
|
2881
|
-
}
|
|
2882
|
-
|
|
2883
|
-
@media (max-width: 900px) {
|
|
2884
|
-
.review-pane {
|
|
2885
|
-
grid-template-columns: 1fr;
|
|
2886
|
-
}
|
|
2887
|
-
}
|
|
2888
|
-
|
|
2889
|
-
.review-preview {
|
|
2890
|
-
display: grid;
|
|
2891
|
-
gap: 8px;
|
|
2892
|
-
min-width: 0;
|
|
2893
|
-
}
|
|
2894
|
-
|
|
2895
|
-
.review-pane-head {
|
|
2896
|
-
display: flex;
|
|
2897
|
-
align-items: center;
|
|
2898
|
-
justify-content: space-between;
|
|
2899
|
-
gap: 10px;
|
|
2900
|
-
}
|
|
2901
|
-
|
|
2902
|
-
.review-frame {
|
|
2903
|
-
width: 100%;
|
|
2904
|
-
height: 420px;
|
|
2905
|
-
border: 1px solid var(--admin-line-strong);
|
|
2906
|
-
border-radius: var(--admin-radius-sm);
|
|
2907
|
-
background: var(--admin-surface);
|
|
2908
|
-
}
|
|
2909
|
-
|
|
2910
|
-
.review-detail {
|
|
2911
|
-
display: grid;
|
|
2912
|
-
gap: 14px;
|
|
2913
|
-
min-width: 0;
|
|
2914
|
-
}
|
|
2915
|
-
|
|
2916
|
-
.review-section {
|
|
2917
|
-
display: grid;
|
|
2918
|
-
gap: 6px;
|
|
2919
|
-
}
|
|
2920
|
-
|
|
2921
|
-
.review-chips {
|
|
2922
|
-
display: flex;
|
|
2923
|
-
flex-wrap: wrap;
|
|
2924
|
-
gap: 6px;
|
|
2925
|
-
}
|
|
2926
|
-
|
|
2927
|
-
.review-actions {
|
|
2928
|
-
display: flex;
|
|
2929
|
-
flex-wrap: wrap;
|
|
2930
|
-
gap: 8px;
|
|
2931
|
-
}
|
|
2932
|
-
|
|
2933
|
-
.publish-diff-head {
|
|
2934
|
-
display: flex;
|
|
2935
|
-
flex-wrap: wrap;
|
|
2936
|
-
align-items: center;
|
|
2937
|
-
gap: 10px;
|
|
2938
|
-
margin-bottom: 8px;
|
|
2939
|
-
}
|
|
2940
|
-
|
|
2941
|
-
.publish-diff-blocked {
|
|
2942
|
-
color: var(--admin-amber);
|
|
2943
|
-
}
|
|
2944
|
-
|
|
2945
|
-
.publish-diff-list {
|
|
2946
|
-
list-style: none;
|
|
2947
|
-
margin: 0;
|
|
2948
|
-
padding: 0;
|
|
2949
|
-
display: grid;
|
|
2950
|
-
gap: 3px;
|
|
2951
|
-
}
|
|
2952
|
-
|
|
2953
|
-
.publish-diff-list li {
|
|
2954
|
-
display: flex;
|
|
2955
|
-
align-items: baseline;
|
|
2956
|
-
gap: 8px;
|
|
2957
|
-
font-size: 12px;
|
|
2958
|
-
}
|
|
2959
|
-
|
|
2960
|
-
.publish-diff-mark {
|
|
2961
|
-
width: 1em;
|
|
2962
|
-
font-weight: 700;
|
|
2963
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
2964
|
-
}
|
|
2965
|
-
|
|
2966
|
-
.publish-diff-added .publish-diff-mark {
|
|
2967
|
-
color: var(--admin-green);
|
|
2968
|
-
}
|
|
2969
|
-
|
|
2970
|
-
.publish-diff-changed .publish-diff-mark {
|
|
2971
|
-
color: var(--admin-blue);
|
|
2972
|
-
}
|
|
2973
|
-
|
|
2974
|
-
.publish-diff-removed .publish-diff-mark {
|
|
2975
|
-
color: var(--admin-red);
|
|
2976
|
-
}
|
|
2977
|
-
|
|
2978
|
-
.publish-diff-list code {
|
|
2979
|
-
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
2980
|
-
font-size: 12px;
|
|
2981
|
-
overflow-wrap: anywhere;
|
|
2982
|
-
}
|
|
2983
|
-
</style>
|