@echomem/mcp 1.4.43 → 1.4.44
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/README.md +25 -28
- package/dist/config-files.js +63 -0
- package/dist/context-analysis/claude-native-canonical.js +2 -2
- package/dist/context-analysis/vendored-canonical.js +2 -2
- package/dist/context-analysis/workspace-report.js +3 -3
- package/dist/hud/hooks.js +43 -31
- package/dist/index.js +15 -79
- package/dist/local-jsonl.js +87 -0
- package/dist/migrate.js +6 -4
- package/dist/onboarding-stats.js +16 -0
- package/dist/save-checkpoint-hook.js +1 -1
- package/dist/setup-page/client-core.js +18 -372
- package/dist/setup-page/client-extraction.js +33 -188
- package/dist/setup-page/client-lifecycle.js +31 -101
- package/dist/setup-page/client.js +0 -2
- package/dist/setup-page/styles-extraction.js +1 -31
- package/dist/setup-page/styles-foundation.js +0 -89
- package/dist/setup-page/styles-mvp.js +10 -155
- package/dist/setup-page/styles-website-alignment.js +0 -204
- package/dist/setup-page/styles.js +0 -4
- package/dist/setup-page.js +4 -4
- package/dist/setup-preview.js +4 -212
- package/dist/setup.js +314 -667
- package/dist/v1-contract.js +0 -8
- package/package.json +9 -7
- package/dist/city/README.md +0 -9
- package/dist/city/echo-ai-city-only.html +0 -2232
- package/dist/city/echo-extraction-plate.html +0 -330
- package/dist/city/echo-face-cutout.png +0 -0
- package/dist/city/personality_stickers/bossy.png +0 -0
- package/dist/city/personality_stickers/ghosty.png +0 -0
- package/dist/city/personality_stickers/loopy.png +0 -0
- package/dist/city/personality_stickers/lusty.png +0 -0
- package/dist/city/personality_stickers/maxxy.png +0 -0
- package/dist/city/personality_stickers/tabby.png +0 -0
- package/dist/city/vendor/OrbitControls.js +0 -1417
- package/dist/city/vendor/RoundedBoxGeometry.js +0 -155
- package/dist/city/vendor/echo_general-file-21.riv +0 -0
- package/dist/city/vendor/rive.js +0 -8139
- package/dist/city/vendor/rive.wasm +0 -0
- package/dist/city/vendor/three.module.min.js +0 -6
- package/dist/forensics.js +0 -1531
- package/dist/report.js +0 -721
- package/dist/setup-page/client-report-audit.js +0 -819
- package/dist/setup-page/client-report-city.js +0 -356
- package/dist/setup-page/client-report.js +0 -6
- package/dist/setup-page/styles-city-report.js +0 -880
- package/dist/setup-page/styles-context-audit.js +0 -470
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
// Generated by splitting the previous inline setup page. Keep this source readable: it is emitted into one browser IIFE.
|
|
2
|
-
export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ----------
|
|
3
|
-
function
|
|
4
|
-
setScanMode(false);
|
|
5
|
-
setCityMode(false);
|
|
2
|
+
export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- local-history permission and setup routing ---------- */
|
|
3
|
+
function renderLocalHistoryConsent() {
|
|
6
4
|
setExtractMode(false);
|
|
7
5
|
setReadyMode(true);
|
|
8
6
|
setHead("Local file access", "Consent");
|
|
@@ -14,15 +12,15 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
|
|
|
14
12
|
'<img class="mvpConsentEcho" src="/hud-assets/echo-face-cutout.png" alt="" />' +
|
|
15
13
|
'<div class="mvpConsentPitch">' +
|
|
16
14
|
'<h2 id="mvpConsentTitle">Start with the context you’ve already built.</h2>' +
|
|
17
|
-
'<p class="mvpConsentIntro">
|
|
15
|
+
'<p class="mvpConsentIntro">Choose which local coding conversations to bring into EchoMem.</p>' +
|
|
18
16
|
'</div>' +
|
|
19
17
|
'</div>' +
|
|
20
|
-
'<p class="mvpConsentTrustLine">Your history stays on
|
|
21
|
-
'<button type="button" class="primary mvpPrimary" id="
|
|
18
|
+
'<p class="mvpConsentTrustLine">Your history stays on this computer until you choose what to import. ' + dataHandlingLink("Data handling") + '</p>' +
|
|
19
|
+
'<button type="button" class="primary mvpPrimary" id="allowLocalHistory">Continue <span aria-hidden="true">→</span></button>' +
|
|
22
20
|
'<p class="consentFine" id="consentStatus" role="status" aria-live="polite"></p>' +
|
|
23
21
|
'</section>';
|
|
24
|
-
var allow = document.getElementById("
|
|
25
|
-
if (allow) allow.onclick =
|
|
22
|
+
var allow = document.getElementById("allowLocalHistory");
|
|
23
|
+
if (allow) allow.onclick = allowLocalHistory;
|
|
26
24
|
}
|
|
27
25
|
function setConsentStatus(message, tone) {
|
|
28
26
|
var node = document.getElementById("consentStatus");
|
|
@@ -31,18 +29,26 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
|
|
|
31
29
|
node.classList.toggle("is-required", tone === "required");
|
|
32
30
|
}
|
|
33
31
|
}
|
|
34
|
-
|
|
35
|
-
|
|
32
|
+
function continueAfterLocalHistoryConsent() {
|
|
33
|
+
if (connected) {
|
|
34
|
+
try { void refreshBillingStatus(); } catch (_) {}
|
|
35
|
+
void waitForStats();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
rememberSetupPlanChoice("");
|
|
39
|
+
renderLocalLogin("");
|
|
40
|
+
}
|
|
41
|
+
async function allowLocalHistory() {
|
|
42
|
+
var allow = document.getElementById("allowLocalHistory");
|
|
36
43
|
if (allow) allow.disabled = true;
|
|
37
|
-
setConsentStatus("
|
|
44
|
+
setConsentStatus("Saving your choice...");
|
|
38
45
|
try {
|
|
39
|
-
await postJson("/
|
|
46
|
+
await postJson("/local-history-consent", { allowed: true }, 5000);
|
|
40
47
|
localHistoryConsentGranted = true;
|
|
41
|
-
|
|
48
|
+
continueAfterLocalHistoryConsent();
|
|
42
49
|
} catch (error) {
|
|
43
50
|
// The localhost bridge may have accepted consent even if the browser loses the POST
|
|
44
|
-
// response while it swaps connections. Recover from the server's authoritative state
|
|
45
|
-
// instead of leaving the user forever on "Starting local scan...".
|
|
51
|
+
// response while it swaps connections. Recover from the server's authoritative state.
|
|
46
52
|
try {
|
|
47
53
|
var configResponse = await fetchWithTimeout(
|
|
48
54
|
"/config?nonce=" + encodeURIComponent(nonce),
|
|
@@ -52,80 +58,14 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
|
|
|
52
58
|
var config = configResponse.ok ? await configResponse.json() : null;
|
|
53
59
|
if (config && config.consentGranted === true) {
|
|
54
60
|
localHistoryConsentGranted = true;
|
|
55
|
-
|
|
61
|
+
continueAfterLocalHistoryConsent();
|
|
56
62
|
return;
|
|
57
63
|
}
|
|
58
64
|
} catch (_) {}
|
|
59
|
-
|
|
65
|
+
if (allow) allow.disabled = false;
|
|
66
|
+
setConsentStatus(error && error.message ? error.message : "Could not save local-history access.", "required");
|
|
60
67
|
}
|
|
61
68
|
}
|
|
62
|
-
async function showReport() {
|
|
63
|
-
if (reportPollStarted) return;
|
|
64
|
-
reportPollStarted = true;
|
|
65
|
-
if (!report) renderLoading();
|
|
66
|
-
try {
|
|
67
|
-
var fails = 0;
|
|
68
|
-
for (;;) {
|
|
69
|
-
if (decisionMade) return;
|
|
70
|
-
var res;
|
|
71
|
-
try {
|
|
72
|
-
res = await fetchWithTimeout("/report?nonce=" + encodeURIComponent(nonce), { credentials: "omit", cache: "no-store" }, 12000);
|
|
73
|
-
} catch (_) {
|
|
74
|
-
// Only a RUN of consecutive failures is a real bridge problem. A long scan that keeps
|
|
75
|
-
// answering valid 202 envelopes must never trip this.
|
|
76
|
-
fails++;
|
|
77
|
-
if (fails >= 8) { renderBridgeIssue(); return; }
|
|
78
|
-
await sleep(700);
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
if (res.status === 403) {
|
|
82
|
-
renderReportIssue("SETUP_SESSION_INVALID", "This setup link is missing or has an invalid local session nonce.");
|
|
83
|
-
return;
|
|
84
|
-
}
|
|
85
|
-
var envelope;
|
|
86
|
-
try {
|
|
87
|
-
envelope = await res.json();
|
|
88
|
-
} catch (_) {
|
|
89
|
-
renderReportIssue("REPORT_RESPONSE_INVALID", "The local bridge returned a report response that could not be decoded.");
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
try {
|
|
93
|
-
validateProductionReportEnvelope(envelope);
|
|
94
|
-
} catch (_) {
|
|
95
|
-
renderReportIssue("REPORT_SCHEMA_INVALID", "The local report failed its provenance or ledger consistency checks.");
|
|
96
|
-
return;
|
|
97
|
-
}
|
|
98
|
-
if (res.status === 202 && envelope.kind === "scanning") {
|
|
99
|
-
fails = 0;
|
|
100
|
-
reportEnvelope = envelope;
|
|
101
|
-
scheduleReportToCity();
|
|
102
|
-
renderLoading();
|
|
103
|
-
await sleep(700);
|
|
104
|
-
continue;
|
|
105
|
-
}
|
|
106
|
-
if (res.status === 200 && envelope.kind === "empty") {
|
|
107
|
-
renderEmptyReport();
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
if (res.status === 200 && envelope.kind === "ready") {
|
|
111
|
-
reportEnvelope = envelope;
|
|
112
|
-
report = envelope.report;
|
|
113
|
-
try {
|
|
114
|
-
renderForensicReport();
|
|
115
|
-
} catch (_) {
|
|
116
|
-
renderReportIssue("RENDER_FAILED", "Your validated local report could not be rendered safely.");
|
|
117
|
-
}
|
|
118
|
-
return;
|
|
119
|
-
}
|
|
120
|
-
if (envelope.kind === "failed") {
|
|
121
|
-
renderReportIssue(envelope.error.code, envelope.error.message);
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
renderReportIssue("REPORT_STATE_MISMATCH", "The local bridge returned a report state that did not match its HTTP status.");
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
} finally { reportPollStarted = false; }
|
|
128
|
-
}
|
|
129
69
|
|
|
130
70
|
window.addEventListener("message", function (event) {
|
|
131
71
|
if (event.origin !== location.origin) return;
|
|
@@ -140,27 +80,22 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
|
|
|
140
80
|
rememberSetupPlanChoice("");
|
|
141
81
|
try { void refreshBillingStatus(); } catch (_) {}
|
|
142
82
|
void waitForStats();
|
|
143
|
-
} else if (data.type === "echomem:city-ready" && data.nonce === nonce) {
|
|
144
|
-
scheduleReportToCity();
|
|
145
|
-
} else if (data.type === "echomem:connect-click") {
|
|
146
|
-
onConnectClick();
|
|
147
83
|
}
|
|
148
84
|
});
|
|
149
85
|
async function init() {
|
|
150
86
|
if (!nonce) throw new Error("Missing setup nonce. Re-run echomem-mcp init.");
|
|
151
|
-
// Older builds cached a report under the nonce. It is never an authority now; the current
|
|
152
|
-
// server envelope and scan identity are the only inputs allowed to unlock personalized UI.
|
|
153
|
-
try { sessionStorage.removeItem("echomem:setup-report:" + nonce); } catch (_) {}
|
|
154
87
|
var configLoaded = false;
|
|
155
88
|
var consentRequired = true;
|
|
156
89
|
var consentGranted = false;
|
|
157
|
-
// Learn the connection state and server-side consent gate before routing.
|
|
158
90
|
try {
|
|
159
91
|
var cfg = await getJson("/config");
|
|
160
92
|
configLoaded = true;
|
|
161
93
|
connected = !!cfg.connected;
|
|
162
94
|
setupFlow = cfg.flow === "login" ? "login" : "onboarding";
|
|
163
95
|
workspacePath = cfg.workspacePath || workspacePath;
|
|
96
|
+
setupPlatform = cfg.platform || setupPlatform;
|
|
97
|
+
canOpenClaudeDesktop = !!(cfg.capabilities && cfg.capabilities.openClaudeDesktop);
|
|
98
|
+
canOpenAgentSessions = !!(cfg.capabilities && cfg.capabilities.openAgentSessions);
|
|
164
99
|
consentRequired = cfg.consentRequired !== false;
|
|
165
100
|
consentGranted = cfg.consentGranted === true;
|
|
166
101
|
localHistoryConsentGranted = consentGranted;
|
|
@@ -173,32 +108,27 @@ export const SETUP_PAGE_CLIENT_LIFECYCLE = String.raw ` /* ---------- scan-
|
|
|
173
108
|
if (!connected) rememberSetupPlanChoice("");
|
|
174
109
|
if (params.get("connected") === "1") {
|
|
175
110
|
if (!configLoaded || (consentRequired && !consentGranted)) {
|
|
176
|
-
|
|
111
|
+
renderLocalHistoryConsent();
|
|
177
112
|
setConsentStatus("Local history access is required before you can connect EchoMem or continue setup.", "required");
|
|
178
113
|
return;
|
|
179
114
|
}
|
|
180
115
|
connected = true;
|
|
181
116
|
if (notifyOpenerConnected()) {
|
|
182
|
-
// Popup flow: hand the main tab back control, then this popup closes.
|
|
183
117
|
setHead("EchoMem connected", "Done");
|
|
184
118
|
app.className = "notice";
|
|
185
119
|
app.innerHTML = '<h2 style="color:#203820">This device is connected</h2><p style="margin-top:10px">Returning to the local setup page...</p>';
|
|
186
|
-
// Fallback: if window.close() was blocked (auth opened as a tab, or the browser refused),
|
|
187
|
-
// this window would otherwise hang on "Returning..." forever. After a beat, take over here
|
|
188
|
-
// so the user always lands on the dashboard instead of a dead page.
|
|
189
120
|
setTimeout(function () { if (!window.closed) void waitForStats(); }, 1500);
|
|
190
121
|
} else {
|
|
191
|
-
// Same-tab fallback (popup was blocked): go straight to the extraction dashboard.
|
|
192
122
|
try { void refreshBillingStatus(); } catch (_) {}
|
|
193
123
|
void waitForStats();
|
|
194
124
|
}
|
|
195
125
|
return;
|
|
196
126
|
}
|
|
197
127
|
if (consentGranted) {
|
|
198
|
-
|
|
128
|
+
continueAfterLocalHistoryConsent();
|
|
199
129
|
return;
|
|
200
130
|
}
|
|
201
|
-
|
|
131
|
+
renderLocalHistoryConsent();
|
|
202
132
|
}
|
|
203
133
|
__ECHO_SETUP_STARTUP__;
|
|
204
134
|
`;
|
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { SETUP_PAGE_CLIENT_CORE } from "./client-core.js";
|
|
2
|
-
import { SETUP_PAGE_CLIENT_REPORT } from "./client-report.js";
|
|
3
2
|
import { SETUP_PAGE_CLIENT_EXTRACTION } from "./client-extraction.js";
|
|
4
3
|
import { SETUP_PAGE_CLIENT_LIFECYCLE } from "./client-lifecycle.js";
|
|
5
4
|
export function renderSetupPageClient(previewState, startupScript) {
|
|
6
5
|
const body = [
|
|
7
6
|
SETUP_PAGE_CLIENT_CORE,
|
|
8
|
-
SETUP_PAGE_CLIENT_REPORT,
|
|
9
7
|
SETUP_PAGE_CLIENT_EXTRACTION,
|
|
10
8
|
SETUP_PAGE_CLIENT_LIFECYCLE,
|
|
11
9
|
].join("\n")
|
|
@@ -598,13 +598,8 @@ export const SETUP_PAGE_STYLES_EXTRACTION = String.raw ` /* ---- dashboard (p
|
|
|
598
598
|
.consentActions .primary { width: 100%; }
|
|
599
599
|
.localAuthActions .primary,
|
|
600
600
|
.localAuthForm > .primary { width: 100%; }
|
|
601
|
-
.cityHero { min-height: auto; }
|
|
602
|
-
.cityShell {
|
|
603
|
-
width: 100vw;
|
|
604
|
-
height: 100vh;
|
|
605
|
-
}
|
|
606
601
|
}
|
|
607
|
-
/* extraction dashboard
|
|
602
|
+
/* extraction dashboard */
|
|
608
603
|
.exHero { display: grid; grid-template-columns: minmax(0, 1fr) minmax(300px, 0.95fr); gap: clamp(20px, 3vw, 40px); align-items: center; margin-top: 8px; }
|
|
609
604
|
/* Centered composition: mascot on top, then everything stacked and centered on one axis. */
|
|
610
605
|
.readyDecision {
|
|
@@ -674,7 +669,6 @@ export const SETUP_PAGE_STYLES_EXTRACTION = String.raw ` /* ---- dashboard (p
|
|
|
674
669
|
justify-items: stretch;
|
|
675
670
|
margin: 0 auto;
|
|
676
671
|
}
|
|
677
|
-
.exHeroResult .exPlate { display: none; }
|
|
678
672
|
.exHeroResult .exCopy { width: 100%; margin: 0 auto; }
|
|
679
673
|
.exCopy h2 { max-width: 620px; margin: 8px 0 0; font-size: clamp(34px, 4.8vw, 58px); line-height: 1.01; }
|
|
680
674
|
.exCopy .exSub { margin: 14px 0 0; max-width: 590px; font-size: clamp(15px, 1.3vw, 17px); line-height: 1.6; color: var(--echo-ink-mute); }
|
|
@@ -896,19 +890,6 @@ export const SETUP_PAGE_STYLES_EXTRACTION = String.raw ` /* ---- dashboard (p
|
|
|
896
890
|
font-size: 12px;
|
|
897
891
|
line-height: 1.35;
|
|
898
892
|
}
|
|
899
|
-
.exPlate { width: 100%; }
|
|
900
|
-
/* WebGL clay city (echo-extraction-plate.html) — the only plate; no SVG layer */
|
|
901
|
-
.plateStack { position: relative; width: 100%; aspect-ratio: 640 / 470; }
|
|
902
|
-
.plateStack .plateGl { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; background: transparent; }
|
|
903
|
-
.exHeroActive {
|
|
904
|
-
grid-template-columns: minmax(0, 640px);
|
|
905
|
-
justify-content: center;
|
|
906
|
-
justify-items: stretch;
|
|
907
|
-
width: 100%;
|
|
908
|
-
max-width: min(920px, calc(100vw - clamp(36px, 8vw, 120px)));
|
|
909
|
-
margin: 0 auto;
|
|
910
|
-
}
|
|
911
|
-
.exHeroActive .exPlate { display: none; }
|
|
912
893
|
.exHeroActive .exCopy { width: 100%; }
|
|
913
894
|
.workbench { display: grid; gap: 14px; max-width: 640px; }
|
|
914
895
|
.workbench h2 {
|
|
@@ -2538,15 +2519,4 @@ export const SETUP_PAGE_STYLES_EXTRACTION = String.raw ` /* ---- dashboard (p
|
|
|
2538
2519
|
.sessionPickerFoot button { width: 100%; }
|
|
2539
2520
|
}
|
|
2540
2521
|
|
|
2541
|
-
/* ===== workspace readout dashboard (Part 2 — replaces explainPage) ===== */
|
|
2542
|
-
@keyframes rdoRise { from { opacity: 0; translate: 0 14px; } to { opacity: 1; translate: 0 0; } }
|
|
2543
|
-
@keyframes rdoGrow { from { transform: scaleY(0); } to { transform: scaleY(1); } }
|
|
2544
|
-
@keyframes rdoDraw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
|
|
2545
|
-
@keyframes rdoWipe { from { clip-path: inset(0 100% 0 0); } to { clip-path: inset(0 0 0 0); } }
|
|
2546
|
-
/* Pull the report up toward the city figure — the canvas bottom fades out now,
|
|
2547
|
-
so the document can tuck into that empty band without touching the board. */
|
|
2548
|
-
body.cityMode .detailsWrap { background: var(--echo-paper-canvas); margin-top: clamp(-120px, -7vw, -56px); }
|
|
2549
|
-
|
|
2550
|
-
/* Context audit — a printed report sheet on the desk: serif document type, tight rhythm. */
|
|
2551
|
-
.context-audit {
|
|
2552
2522
|
`;
|
|
@@ -105,89 +105,6 @@ export const SETUP_PAGE_STYLES_FOUNDATION = String.raw ` :root {
|
|
|
105
105
|
font-size: 16px;
|
|
106
106
|
text-align: center;
|
|
107
107
|
}
|
|
108
|
-
body.scanMode {
|
|
109
|
-
background: var(--echo-paper-canvas);
|
|
110
|
-
}
|
|
111
|
-
body.scanMode main {
|
|
112
|
-
width: 100%;
|
|
113
|
-
min-height: 100vh;
|
|
114
|
-
margin: 0;
|
|
115
|
-
padding: clamp(32px, 7vw, 96px);
|
|
116
|
-
display: grid;
|
|
117
|
-
place-items: center;
|
|
118
|
-
}
|
|
119
|
-
body.scanMode main > header {
|
|
120
|
-
display: none;
|
|
121
|
-
}
|
|
122
|
-
body.scanMode #app {
|
|
123
|
-
width: min(900px, 100%);
|
|
124
|
-
min-height: min(62vh, 620px);
|
|
125
|
-
display: grid;
|
|
126
|
-
place-items: center;
|
|
127
|
-
}
|
|
128
|
-
.scanLoader {
|
|
129
|
-
width: min(720px, 100%);
|
|
130
|
-
border-top: 1px solid #c9cbc6;
|
|
131
|
-
border-right: 1px solid #c9cbc6;
|
|
132
|
-
border-bottom: 1px solid #c9cbc6;
|
|
133
|
-
border-left: 5px solid var(--echo-ink-primary);
|
|
134
|
-
border-radius: 2px;
|
|
135
|
-
background: var(--echo-paper-white);
|
|
136
|
-
padding: clamp(30px, 6vw, 58px);
|
|
137
|
-
text-align: left;
|
|
138
|
-
}
|
|
139
|
-
.scanEyebrow {
|
|
140
|
-
margin: 0 0 22px;
|
|
141
|
-
color: var(--echo-ink-faint);
|
|
142
|
-
font-family: var(--echo-font-mono);
|
|
143
|
-
font-size: 14px;
|
|
144
|
-
font-weight: 700;
|
|
145
|
-
letter-spacing: 0.18em;
|
|
146
|
-
text-transform: uppercase;
|
|
147
|
-
}
|
|
148
|
-
.scanLoader h2 {
|
|
149
|
-
margin: 0;
|
|
150
|
-
color: var(--echo-ink-text);
|
|
151
|
-
font-size: clamp(42px, 5vw, 72px);
|
|
152
|
-
line-height: 1;
|
|
153
|
-
}
|
|
154
|
-
.scanProgress {
|
|
155
|
-
width: min(520px, 100%);
|
|
156
|
-
height: 6px;
|
|
157
|
-
margin: 38px 0 0;
|
|
158
|
-
overflow: hidden;
|
|
159
|
-
border-radius: 0;
|
|
160
|
-
background: #e2e3e9;
|
|
161
|
-
}
|
|
162
|
-
.scanProgressFill {
|
|
163
|
-
display: block;
|
|
164
|
-
height: 100%;
|
|
165
|
-
min-width: 10px;
|
|
166
|
-
border-radius: 0;
|
|
167
|
-
background: var(--echo-ink-primary);
|
|
168
|
-
/* Matched to the 700ms report poll so the fill glides between updates instead of
|
|
169
|
-
stepping and then sitting still for the rest of the interval. */
|
|
170
|
-
transition: width 700ms linear;
|
|
171
|
-
}
|
|
172
|
-
.scanCaption {
|
|
173
|
-
margin: 20px 0 0;
|
|
174
|
-
color: var(--echo-ink-faint);
|
|
175
|
-
font-family: var(--echo-font-mono);
|
|
176
|
-
font-size: 16px;
|
|
177
|
-
font-weight: 600;
|
|
178
|
-
line-height: 1.45;
|
|
179
|
-
}
|
|
180
|
-
body.cityMode {
|
|
181
|
-
background: var(--echo-paper-canvas);
|
|
182
|
-
}
|
|
183
|
-
body.cityMode main {
|
|
184
|
-
width: 100%;
|
|
185
|
-
padding: 0;
|
|
186
|
-
margin: 0;
|
|
187
|
-
}
|
|
188
|
-
body.cityMode main > header {
|
|
189
|
-
display: none;
|
|
190
|
-
}
|
|
191
108
|
body.extractMode main > header {
|
|
192
109
|
display: none;
|
|
193
110
|
}
|
|
@@ -210,12 +127,6 @@ export const SETUP_PAGE_STYLES_FOUNDATION = String.raw ` :root {
|
|
|
210
127
|
body.readyMode {
|
|
211
128
|
background: #f5f3ed;
|
|
212
129
|
}
|
|
213
|
-
body.cityMode #app {
|
|
214
|
-
display: block;
|
|
215
|
-
min-height: 100vh;
|
|
216
|
-
color: var(--echo-ink-text);
|
|
217
|
-
text-align: left;
|
|
218
|
-
}
|
|
219
130
|
body.extractMode #app {
|
|
220
131
|
width: 100%;
|
|
221
132
|
display: grid;
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
// Final MVP onboarding layer. It deliberately mirrors the public Echo pricing,
|
|
2
|
-
// login, and
|
|
2
|
+
// login, plan, and import surfaces instead of introducing another local-only design system.
|
|
3
3
|
export const SETUP_PAGE_STYLES_MVP = String.raw `
|
|
4
4
|
body.readyMode,
|
|
5
|
-
body.scanMode,
|
|
6
5
|
body.extractMode {
|
|
7
6
|
background:
|
|
8
7
|
radial-gradient(circle, rgba(26,58,143,0.12) 0 1.2px, transparent 1.5px) 0 0 / 22px 22px,
|
|
9
8
|
var(--echo-paper-canvas);
|
|
10
9
|
}
|
|
11
|
-
body.readyMode main,
|
|
12
|
-
body.scanMode main,
|
|
10
|
+
body.readyMode main main,
|
|
13
11
|
body.extractMode main {
|
|
14
12
|
width: 100%;
|
|
15
13
|
min-height: 100vh;
|
|
@@ -19,19 +17,17 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
|
|
|
19
17
|
}
|
|
20
18
|
main > header { display: none; }
|
|
21
19
|
#app.state { color: transparent; }
|
|
22
|
-
body.readyMode #app,
|
|
23
|
-
body.scanMode #app,
|
|
20
|
+
body.readyMode #app #app,
|
|
24
21
|
body.extractMode #app {
|
|
25
22
|
width: 100%;
|
|
26
23
|
min-height: calc(100vh - clamp(56px, 10vw, 128px));
|
|
27
24
|
}
|
|
28
|
-
body.readyMode #app:has(> .setupJourney),
|
|
29
|
-
body.scanMode #app:has(> .setupJourney),
|
|
25
|
+
body.readyMode #app:has(> .setupJourney) #app:has(> .setupJourney),
|
|
30
26
|
body.extractMode #app:has(> .setupJourney) {
|
|
31
27
|
padding-top: 0 !important;
|
|
32
28
|
}
|
|
33
29
|
|
|
34
|
-
/* One calm map across
|
|
30
|
+
/* One calm map across access, account/Stripe, import, and first value. */
|
|
35
31
|
.setupJourney {
|
|
36
32
|
width: min(760px, 100%);
|
|
37
33
|
flex: 0 0 auto;
|
|
@@ -40,7 +36,7 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
|
|
|
40
36
|
}
|
|
41
37
|
.setupJourney ol {
|
|
42
38
|
display: grid;
|
|
43
|
-
grid-template-columns: repeat(
|
|
39
|
+
grid-template-columns: repeat(4, minmax(0, 1fr));
|
|
44
40
|
gap: 0;
|
|
45
41
|
margin: 0;
|
|
46
42
|
padding: 0;
|
|
@@ -99,11 +95,6 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
|
|
|
99
95
|
box-shadow: 0 0 0 4px rgba(26,58,143,0.09);
|
|
100
96
|
color: white;
|
|
101
97
|
}
|
|
102
|
-
body.cityMode .setupJourney {
|
|
103
|
-
margin-top: 12px;
|
|
104
|
-
margin-bottom: 8px;
|
|
105
|
-
padding-inline: 18px;
|
|
106
|
-
}
|
|
107
98
|
#setupJourneySlot {
|
|
108
99
|
width: 100%;
|
|
109
100
|
flex: 0 0 auto;
|
|
@@ -151,21 +142,6 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
|
|
|
151
142
|
text-align: center;
|
|
152
143
|
}
|
|
153
144
|
.mvpConsentTrustLine strong { color: var(--echo-ink-text); }
|
|
154
|
-
.mvpReportPreview { overflow: hidden; border: 1px solid rgba(26,58,143,0.18); border-radius: 18px; background: rgba(255,255,255,0.84); box-shadow: 0 16px 36px -26px rgba(26,58,143,0.42); }
|
|
155
|
-
.mvpReportPreview header { display: flex; align-items: center; gap: 13px; border-bottom: 1px solid rgba(26,58,143,0.10); background: color-mix(in srgb, var(--echo-paper-white) 66%, var(--echo-note-blue) 34%); padding: 13px 15px; }
|
|
156
|
-
.mvpReportPreview header span,
|
|
157
|
-
.mvpReportPreview header small,
|
|
158
|
-
.mvpReportPreview header strong,
|
|
159
|
-
.mvpPreviewRow span,
|
|
160
|
-
.mvpPreviewRow small,
|
|
161
|
-
.mvpPreviewRow strong { display: block; }
|
|
162
|
-
.mvpReportPreview header small { color: var(--echo-ink-primary); font: 800 9px/1 var(--echo-font-mono); letter-spacing: 0.1em; text-transform: uppercase; }
|
|
163
|
-
.mvpReportPreview header strong { margin-top: 5px; color: var(--echo-ink-text); font-size: 15px; }
|
|
164
|
-
.mvpPreviewRow { display: grid; grid-template-columns: 22px minmax(0, 1fr); gap: 10px; align-items: start; padding: 11px 15px; }
|
|
165
|
-
.mvpPreviewRow + .mvpPreviewRow { border-top: 1px solid rgba(26,58,143,0.08); }
|
|
166
|
-
.mvpPreviewRow .setupGlyph { width: 19px; height: 19px; color: #2c8f68; }
|
|
167
|
-
.mvpPreviewRow strong { color: var(--echo-ink-text); font-size: 13px; }
|
|
168
|
-
.mvpPreviewRow small { margin-top: 2px; color: var(--echo-ink-mute); font-size: 11px; line-height: 1.35; }
|
|
169
145
|
.mvpTrustCard { display: grid; grid-template-columns: 38px minmax(0, 1fr) auto; gap: 12px; align-items: center; border: 1px solid rgba(44,143,104,0.28); border-radius: 15px; background: color-mix(in srgb, var(--echo-paper-white) 62%, var(--echo-note-mint) 38%); padding: 13px 15px; }
|
|
170
146
|
.mvpTrustIcon { width: 38px; height: 38px; display: grid; place-items: center; border-radius: 50%; background: rgba(44,143,104,0.13); color: #257b59; }
|
|
171
147
|
.mvpTrustIcon .setupGlyph { width: 21px; height: 21px; }
|
|
@@ -537,125 +513,6 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
|
|
|
537
513
|
font-weight: 750;
|
|
538
514
|
}
|
|
539
515
|
|
|
540
|
-
/* Reading --------------------------------------------------------------- */
|
|
541
|
-
.scanLoading {
|
|
542
|
-
display: flex !important;
|
|
543
|
-
flex-direction: column;
|
|
544
|
-
align-items: center;
|
|
545
|
-
justify-content: flex-start;
|
|
546
|
-
}
|
|
547
|
-
.scanLoading .mvpReading {
|
|
548
|
-
margin-block: auto;
|
|
549
|
-
}
|
|
550
|
-
.mvpReading {
|
|
551
|
-
display: flex;
|
|
552
|
-
width: min(460px, 100%);
|
|
553
|
-
flex-direction: column;
|
|
554
|
-
align-items: center;
|
|
555
|
-
color: var(--echo-ink-text);
|
|
556
|
-
text-align: center;
|
|
557
|
-
}
|
|
558
|
-
.mvpReadingMascot {
|
|
559
|
-
display: block;
|
|
560
|
-
width: 150px;
|
|
561
|
-
height: 150px;
|
|
562
|
-
margin: 0 auto 18px;
|
|
563
|
-
object-fit: contain;
|
|
564
|
-
filter: none;
|
|
565
|
-
}
|
|
566
|
-
.mvpReading h2 {
|
|
567
|
-
margin: 0;
|
|
568
|
-
color: var(--echo-ink-primary);
|
|
569
|
-
font: 650 38px/1.05 var(--echo-font-script), cursive;
|
|
570
|
-
}
|
|
571
|
-
.mvpReadingCount {
|
|
572
|
-
min-height: 18px;
|
|
573
|
-
margin: 13px 0 0;
|
|
574
|
-
color: var(--echo-ink-mute);
|
|
575
|
-
font: 650 13px/1.4 var(--echo-font-mono);
|
|
576
|
-
}
|
|
577
|
-
.mvpReadingCount[hidden] {
|
|
578
|
-
display: block;
|
|
579
|
-
visibility: hidden;
|
|
580
|
-
}
|
|
581
|
-
.mvpReadingEta {
|
|
582
|
-
min-height: 17px;
|
|
583
|
-
margin: 5px 0 0;
|
|
584
|
-
color: var(--echo-ink-faint);
|
|
585
|
-
font: 600 12px/1.4 var(--echo-font-mono);
|
|
586
|
-
font-variant-numeric: tabular-nums;
|
|
587
|
-
}
|
|
588
|
-
.mvpReadingProgress {
|
|
589
|
-
width: min(260px, 100%);
|
|
590
|
-
height: 3px;
|
|
591
|
-
margin: 8px 0 0;
|
|
592
|
-
background: rgba(26,58,143,0.12);
|
|
593
|
-
}
|
|
594
|
-
.discoveryLoader {
|
|
595
|
-
width: 100%;
|
|
596
|
-
min-height: min(62vh, 620px);
|
|
597
|
-
display: grid;
|
|
598
|
-
place-items: center;
|
|
599
|
-
}
|
|
600
|
-
.discoverySpinner span {
|
|
601
|
-
display: block;
|
|
602
|
-
width: 26px;
|
|
603
|
-
height: 26px;
|
|
604
|
-
border: 3px solid rgba(26,58,143,0.16);
|
|
605
|
-
border-top-color: var(--echo-ink-primary);
|
|
606
|
-
border-radius: 50%;
|
|
607
|
-
animation: localAuthSpin 700ms linear infinite;
|
|
608
|
-
}
|
|
609
|
-
.extractionStartStage {
|
|
610
|
-
display: flex !important;
|
|
611
|
-
flex-direction: column;
|
|
612
|
-
align-items: center;
|
|
613
|
-
justify-content: flex-start;
|
|
614
|
-
}
|
|
615
|
-
.extractionStartStage .discoverySpinner {
|
|
616
|
-
display: grid;
|
|
617
|
-
flex: 1 1 auto;
|
|
618
|
-
place-items: center;
|
|
619
|
-
}
|
|
620
|
-
.reportMessageStage {
|
|
621
|
-
display: flex !important;
|
|
622
|
-
justify-content: center;
|
|
623
|
-
color: var(--echo-ink-text);
|
|
624
|
-
}
|
|
625
|
-
.reportMessage {
|
|
626
|
-
width: min(620px, 100%);
|
|
627
|
-
padding-top: clamp(48px, 10vh, 112px);
|
|
628
|
-
}
|
|
629
|
-
.reportMessage h2 {
|
|
630
|
-
margin: 0;
|
|
631
|
-
font-size: clamp(42px, 5vw, 58px);
|
|
632
|
-
font-weight: 650;
|
|
633
|
-
line-height: 1;
|
|
634
|
-
letter-spacing: -0.045em;
|
|
635
|
-
}
|
|
636
|
-
.reportMessage p {
|
|
637
|
-
margin: 16px 0 0;
|
|
638
|
-
color: var(--echo-ink-mute);
|
|
639
|
-
font-size: 16px;
|
|
640
|
-
line-height: 1.55;
|
|
641
|
-
}
|
|
642
|
-
.reportMessage code {
|
|
643
|
-
color: var(--echo-ink-primary);
|
|
644
|
-
font-family: var(--echo-font-mono);
|
|
645
|
-
}
|
|
646
|
-
.reportTechnical {
|
|
647
|
-
width: fit-content;
|
|
648
|
-
margin-top: 22px;
|
|
649
|
-
color: var(--echo-ink-faint);
|
|
650
|
-
font: 650 11px/1.5 var(--echo-font-mono);
|
|
651
|
-
}
|
|
652
|
-
.reportTechnical summary {
|
|
653
|
-
cursor: pointer;
|
|
654
|
-
text-decoration: underline;
|
|
655
|
-
text-underline-offset: 3px;
|
|
656
|
-
}
|
|
657
|
-
.reportTechnical code { display: block; margin-top: 8px; }
|
|
658
|
-
|
|
659
516
|
/* Pricing copied from the public Echo habitat -------------------------- */
|
|
660
517
|
.planGateStage {
|
|
661
518
|
display: flex !important;
|
|
@@ -1575,7 +1432,7 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
|
|
|
1575
1432
|
box-shadow: 0 6px 0 color-mix(in srgb, var(--echo-ink-primary) 77%, black);
|
|
1576
1433
|
}
|
|
1577
1434
|
|
|
1578
|
-
/* Extraction
|
|
1435
|
+
/* Extraction ------------------------------------------------------------- */
|
|
1579
1436
|
.exHero {
|
|
1580
1437
|
width: min(1180px, 100%);
|
|
1581
1438
|
min-height: calc(100vh - clamp(56px, 10vw, 128px));
|
|
@@ -1585,7 +1442,8 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
|
|
|
1585
1442
|
box-shadow: none;
|
|
1586
1443
|
}
|
|
1587
1444
|
.exHero.exHeroActive {
|
|
1588
|
-
|
|
1445
|
+
width: min(720px, 100%);
|
|
1446
|
+
grid-template-columns: minmax(0, 1fr);
|
|
1589
1447
|
align-items: center;
|
|
1590
1448
|
}
|
|
1591
1449
|
.exHeroActive #exText { padding: clamp(20px, 4vw, 44px); }
|
|
@@ -1610,7 +1468,6 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
|
|
|
1610
1468
|
-webkit-text-fill-color: var(--echo-ink-primary);
|
|
1611
1469
|
font-weight: 650;
|
|
1612
1470
|
}
|
|
1613
|
-
.exHeroActive .exPlate { min-height: min(72vh, 720px); }
|
|
1614
1471
|
.exHeroActive .exMeter {
|
|
1615
1472
|
margin-top: 28px;
|
|
1616
1473
|
border: 0;
|
|
@@ -1735,11 +1592,9 @@ export const SETUP_PAGE_STYLES_MVP = String.raw `
|
|
|
1735
1592
|
.checkoutCommitment,
|
|
1736
1593
|
.checkoutActions { grid-template-columns: 1fr; }
|
|
1737
1594
|
.exHero.exHeroActive { grid-template-columns: 1fr; }
|
|
1738
|
-
|
|
1739
|
-
}
|
|
1595
|
+
}
|
|
1740
1596
|
@media (max-width: 560px) {
|
|
1741
1597
|
body.readyMode main,
|
|
1742
|
-
body.scanMode main,
|
|
1743
1598
|
body.extractMode main { padding: 22px 14px 38px; }
|
|
1744
1599
|
.setupJourney { margin-bottom: 24px; }
|
|
1745
1600
|
.mvpConsentFooter { align-items: stretch; flex-direction: column; }
|