@echomem/mcp 1.4.6 → 1.4.8

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.
@@ -8,10 +8,10 @@
8
8
  --ink:#1b1c1f; --muted:#8b8d94; --line:#ececef;
9
9
  --mono:"IBM Plex Mono","SFMono-Regular",ui-monospace,Menlo,monospace;
10
10
  --sans:"Avenir Next","Helvetica Neue","Segoe UI",system-ui,-apple-system,sans-serif;
11
- --blue:#2f6df0; --red:#e8473f; --amber:#f0a83f;
11
+ --blue:#1a3a8f; --red:#C7372F; --amber:#f0a83f;
12
12
  }
13
13
  *{margin:0;padding:0;box-sizing:border-box;}
14
- html,body{background:#eef0f3;}
14
+ html,body{background:#FAF7F0;}
15
15
  body{display:flex;justify-content:center;align-items:center;height:100vh;overflow:hidden;
16
16
  font-family:var(--sans);color:var(--ink);}
17
17
 
@@ -38,7 +38,9 @@
38
38
  .v7-head{right:56px;top:48px;width:410px;text-align:right;}
39
39
  .v7-eyebrow{font-family:var(--mono);font-size:12px;font-weight:800;letter-spacing:.16em;text-transform:uppercase;color:#9aa0aa;}
40
40
  .v7-title{font-size:34px;font-weight:800;letter-spacing:-.02em;line-height:1.05;margin-top:8px;}
41
- .v7-personaName{font-size:64px;font-weight:800;letter-spacing:-.035em;line-height:.9;margin-top:8px;text-transform:lowercase;}
41
+ /* Persona is identity flavor (a badge), not the page headline — one display-size element per
42
+ screen, and that's "Here's what you built." */
43
+ .v7-personaName{font-size:46px;font-weight:800;letter-spacing:-.03em;line-height:.92;margin-top:8px;text-transform:lowercase;}
42
44
  .v7-rank{margin-top:12px;font-family:var(--mono);font-size:12px;font-weight:800;letter-spacing:.16em;text-transform:uppercase;color:#8d929c;}
43
45
  .v7-combo{display:flex;gap:8px;flex-wrap:wrap;margin-top:16px;}
44
46
  .v7-combo .ct{font-family:var(--mono);font-size:11px;letter-spacing:.02em;color:#3a3c43;border:1px solid var(--line);
@@ -59,6 +61,9 @@
59
61
  .v7-stat .v{font-size:23px;font-weight:800;letter-spacing:-.01em;line-height:1;}
60
62
  .v7-stat .k{font-family:var(--mono);font-size:11px;font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:#9296a0;margin-top:4px;}
61
63
  .v7-band{left:90px;right:90px;bottom:250px;text-align:center;}
64
+ /* Echo delivers the verdict: the mascot makes the band a character speaking, not a floating stat. */
65
+ .v7-echo{width:56px;height:56px;object-fit:contain;margin:10px auto 2px;display:block;
66
+ filter:drop-shadow(0 12px 20px rgba(26,58,143,.22));}
62
67
  .v7-band .eyebrow{font-family:var(--mono);font-size:12px;font-weight:800;letter-spacing:.2em;text-transform:uppercase;color:#a4a7af;}
63
68
  .v7-band .bignum{font-size:64px;font-weight:800;letter-spacing:-.03em;line-height:.9;margin-top:10px;color:var(--red);
64
69
  min-height:64px;transition:opacity .52s ease,transform .52s ease;}
@@ -75,7 +80,7 @@
75
80
  .v7-band .say b{color:var(--ink);font-weight:700;}
76
81
  .v7-ctaWrap{left:0;right:0;bottom:112px;text-align:center;}
77
82
  .v7-ctaWrap .pre{font-family:var(--mono);font-size:12px;font-weight:800;letter-spacing:.06em;text-transform:uppercase;color:#9296a0;margin-bottom:15px;}
78
- .v7-cta{display:inline-flex;align-items:center;gap:11px;background:#1a3a8f;color:#fff;border:0;border-radius:15px;
83
+ .v7-cta{display:inline-flex;align-items:center;gap:11px;background:#1a3a8f;color:#fff;border:0;border-radius:999px;
79
84
  font-family:var(--mono);font-size:17px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;padding:21px 48px;
80
85
  box-shadow:0 20px 46px rgba(26,58,143,.32);cursor:pointer;transition:background .18s ease,transform .18s ease;}
81
86
  .v7-cta:hover,.v7-cta:focus-visible{background:#223f9b;transform:translateY(-2px);outline:none;}
@@ -155,7 +160,7 @@
155
160
 
156
161
  /* setup flow CTA: hidden in share-card export, visible only in ?setup=1 mode */
157
162
  .setup-connect{display:none;position:absolute;z-index:20;right:46px;top:930px;width:238px;min-height:58px;
158
- border:1px solid #1a3a8f;border-radius:12px;background:#1a3a8f;color:#fff;align-items:center;justify-content:center;
163
+ border:1px solid #1a3a8f;border-radius:999px;background:#1a3a8f;color:#fff;align-items:center;justify-content:center;
159
164
  font-family:var(--mono);font-size:13px;font-weight:800;letter-spacing:.08em;text-transform:uppercase;
160
165
  box-shadow:0 16px 36px rgba(20,24,40,.18);cursor:pointer;}
161
166
  .setup-connect:hover{background:#223f9b;}
@@ -227,7 +232,7 @@
227
232
  body.setup-mode .counterfactual,
228
233
  body.setup-mode .setup-connect{display:none;}
229
234
  body.setup-mode .v7-persona{left:clamp(48px,5vw,86px);top:clamp(42px,5vh,72px);width:min(360px,32vw);}
230
- body.setup-mode .v7-personaName{font-size:clamp(54px,5.3vw,86px);}
235
+ body.setup-mode .v7-personaName{font-size:clamp(38px,3.4vw,54px);}
231
236
  body.setup-mode .v7-head{right:clamp(48px,5vw,86px);top:clamp(42px,5vh,72px);width:min(520px,38vw);}
232
237
  body.setup-mode .v7-head .v7-eyebrow{font-size:clamp(10px,.72vw,12px);}
233
238
  body.setup-mode .v7-title{font-size:clamp(28px,2.1vw,40px);}
@@ -235,7 +240,10 @@
235
240
  body.setup-mode .v7-stat .v{font-size:clamp(20px,1.45vw,28px);}
236
241
  body.setup-mode .v7-stat .k{font-size:clamp(9px,.72vw,11px);}
237
242
  body.setup-mode .v7-band{left:clamp(48px,5vw,86px);right:auto;bottom:clamp(82px,8vh,130px);
238
- width:min(570px,38vw);text-align:left;}
243
+ width:min(640px,42vw);text-align:left;display:grid;grid-template-columns:72px minmax(0,1fr);
244
+ column-gap:16px;align-items:start;}
245
+ body.setup-mode .v7-echo{width:72px;height:72px;margin:2px 0 0;}
246
+ body.setup-mode .v7-bandCopy{min-width:0;}
239
247
  body.setup-mode .v7-band .eyebrow{display:none;}
240
248
  body.setup-mode .v7-band .bignum{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;
241
249
  margin-top:0;min-height:0;line-height:.96;}
@@ -250,7 +258,7 @@
250
258
  body.setup-mode .v7-ctaWrap .micro{max-width:430px;margin-left:auto;}
251
259
  body.setup-mode .v7-foot{left:clamp(48px,5vw,86px);right:clamp(48px,5vw,86px);}
252
260
  @media (max-height:820px){
253
- body.setup-mode .v7-personaName{font-size:clamp(44px,6vh,62px);}
261
+ body.setup-mode .v7-personaName{font-size:clamp(32px,4.4vh,46px);}
254
262
  body.setup-mode .v7-title{font-size:clamp(30px,5vh,44px);}
255
263
  body.setup-mode .v7-band{bottom:58px;width:min(500px,40vw);}
256
264
  body.setup-mode .v7-ctaWrap{bottom:58px;width:min(390px,32vw);}
@@ -323,8 +331,11 @@
323
331
  </div>
324
332
  <div class="v7-z v7-band">
325
333
  <div class="eyebrow">What we found</div>
334
+ <img class="v7-echo" src="echo-face-cutout.png" alt="" />
335
+ <div class="v7-bandCopy">
326
336
  <div class="bignum" id="v7-metric"><span id="v7-metric-value">{{dirtyPct}}%</span><span id="v7-metric-label">repeated reads</span><button class="v7-infoBtn" id="v7-info-btn" type="button" aria-label="How we read your city" aria-expanded="false" aria-controls="v7-info-sheet">i</button></div>
327
337
  <div class="say" id="v7-metric-say">Your agents re-load context they've already seen — unchanged files, the same rules, stale maps — every session. That's context rot. It's <b>how coding agents work today</b>, and it's the haze on your city.</div>
338
+ </div>
328
339
  </div>
329
340
  <div class="v7-z v7-ctaWrap">
330
341
  <div class="pre">Ready when you are</div>
@@ -896,17 +907,19 @@ function addCube(o){
896
907
  }
897
908
  // repo summary as an upright billboard above each bar — white text, ALWAYS faces the camera (never skewed).
898
909
  // name = small tag just above the bar top; tokens + $ fly higher above it, clear of the cube.
899
- function addTopLabel(o){
910
+ function addTopLabel(o, numbers=true, lift=0){
900
911
  const PW=320, PH=280;
901
912
  const cv=document.createElement('canvas'); cv.width=PW; cv.height=PH;
902
913
  const g=cv.getContext('2d'); g.textAlign='center';
903
914
  g.shadowColor='rgba(0,0,0,0.34)'; g.shadowBlur=9; g.shadowOffsetY=1; // subtle contrast over light areas
904
- // tokens — the headline, flies highest
905
- g.fillStyle='#ffffff'; g.font=`800 ${Math.round(PW*0.19)}px "IBM Plex Mono", monospace`;
906
- g.fillText(fmtTok(o.tokens), PW/2, PH*0.18);
907
- // dollars — just under the tokens, also up high
908
- g.fillStyle='rgba(255,255,255,0.92)'; g.font=`700 ${Math.round(PW*0.11)}px "IBM Plex Mono", monospace`;
909
- g.fillText('$'+Math.round(o.cost*(setupMode?1:COST_SCALE)).toLocaleString('en-US'), PW/2, PH*0.40);
915
+ if(numbers){
916
+ // tokens the headline, flies highest
917
+ g.fillStyle='#ffffff'; g.font=`800 ${Math.round(PW*0.19)}px "IBM Plex Mono", monospace`;
918
+ g.fillText(fmtTok(o.tokens), PW/2, PH*0.18);
919
+ // dollars just under the tokens, also up high
920
+ g.fillStyle='rgba(255,255,255,0.92)'; g.font=`700 ${Math.round(PW*0.11)}px "IBM Plex Mono", monospace`;
921
+ g.fillText('$'+Math.round(o.cost*(setupMode?1:COST_SCALE)).toLocaleString('en-US'), PW/2, PH*0.40);
922
+ }
910
923
  // name — small tag sitting just above the bar top
911
924
  g.fillStyle='rgba(255,255,255,0.95)'; g.font=`600 ${Math.round(PW*0.075)}px "IBM Plex Mono", monospace`;
912
925
  const fw=g.measureText(o.name).width, maxw=PW*0.94;
@@ -915,7 +928,7 @@ function addTopLabel(o){
915
928
  const sprite=new THREE.Sprite(new THREE.SpriteMaterial({map:tex, transparent:true, toneMapped:false, depthWrite:false}));
916
929
  const wWorld=Math.min(Math.max(o.w*0.95, 42), 128), hWorld=wWorld*PH/PW; // scales with bar size, clamped legible
917
930
  sprite.scale.set(wWorld, hWorld, 1);
918
- sprite.position.set(o.x, TRAY_TOP + o.h - 3 + 6 + 0.36*hWorld, o.z); // name tags the top; tokens/$ fly higher
931
+ sprite.position.set(o.x, TRAY_TOP + o.h - 3 + 6 + 0.36*hWorld + lift, o.z); // name tags the top; tokens/$ fly higher; lift staggers neighbors
919
932
  scene.add(sprite);
920
933
  }
921
934
  // fine ivory grain — textures EVERY face (incl. the curved side walls), so the tray isn't flat plastic
@@ -962,7 +975,12 @@ tray.position.set(CX, TRAY_TOP/2, CZ); tray.castShadow=true; tray.receiveShadow=
962
975
  plane.rotation.x=-Math.PI/2; plane.position.set(CX, TRAY_TOP+0.6, CZ); scene.add(plane);
963
976
  })();
964
977
 
965
- all.forEach(o=>{ o.mesh=addCube(o); o.mesh.userData=o; if(o.w>=LABEL_MIN_BASE) addTopLabel(o); });
978
+ // De-crowd the skyline: only the top token movers fly their numbers; every other building keeps just
979
+ // its name tag (hover still shows the full repo detail card). Kills the mid-air label collisions.
980
+ const NUMBERED_LABELS=4;
981
+ const numberedOrder=all.slice().sort((a,b)=>(b.tokens||0)-(a.tokens||0)).slice(0,NUMBERED_LABELS);
982
+ const numberedLift=new Map(numberedOrder.map((o,i)=>[o.name, (i%2)*16])); // alternate heights so neighbors' numbers never collide
983
+ all.forEach(o=>{ o.mesh=addCube(o); o.mesh.userData=o; if(o.w>=LABEL_MIN_BASE) addTopLabel(o, numberedLift.has(o.name), numberedLift.get(o.name)||0); });
966
984
 
967
985
  // bottom-right standing "signpost": tray summary — total tokens + how much is input
968
986
  let signpostCanvas=null, signpostTexture=null;
Binary file