Skip to content

Commit 369eb09

Browse files
committed
authflow: render the live MEMCODE matrix wordmark on the CLI login callback
Replace the static image with the marketing site's MatrixGlyphWordmark canvas animation (static lit-glyph base + katakana rain, composited then clipped to the letterforms), ported to dependency-free vanilla JS so the self-contained callback page needs no assets. Restores the original heading/help text with proper vertical rhythm; HTML-escapes the error.
1 parent 3526812 commit 369eb09

1 file changed

Lines changed: 63 additions & 17 deletions

File tree

internal/authflow/authflow.go

Lines changed: 63 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -254,12 +254,51 @@ func StripGlobalEnvToken() (bool, error) {
254254
return true, nil
255255
}
256256

257-
// callbackWordmark is the hosted memcode wordmark (the matrix glyph mark from
258-
// the marketing site). Referenced by absolute URL rather than embedded: the
259-
// browser rendering this page just completed OAuth against memcode.ai, so it
260-
// has connectivity, and a 300KB PNG has no business inflating the binary. If
261-
// the fetch fails the alt text still reads "memcode".
262-
const callbackWordmark = "https://memcode.ai/logo-wordmark.png"
257+
// callbackScript is the MEMCODE matrix wordmark, ported verbatim from the
258+
// marketing site's MatrixGlyphWordmark canvas component to dependency-free JS:
259+
// a static lit-glyph base plus katakana rain, both composited then clipped to
260+
// the word with destination-in, animated on requestAnimationFrame.
261+
const callbackScript = `<script>(function(){
262+
var G='ハケモサナアウキニツヲヤカコレミネテヒイオリ0123456789=<>+*|/\\'.split('');
263+
var LIT=['#e0d8ff','#d2c6ff','#c4b6ff','#b9aaff'];
264+
function pg(){return G[(Math.random()*G.length)|0];}
265+
function pl(){return LIT[(Math.random()*LIT.length)|0];}
266+
var canvas=document.getElementById('mark');
267+
if(!canvas||!canvas.getContext){return;}
268+
var ctx=canvas.getContext('2d');
269+
var baseCell=11,baseCells=[],lastW=0,lastH=0,fc=14,columns=[];
270+
function setup(w,h){
271+
lastW=w;lastH=h;baseCells=[];
272+
for(var y=baseCell/2;y<h;y+=baseCell){for(var x=baseCell/2;x<w;x+=baseCell){baseCells.push({x:x,y:y,glyph:pg(),color:pl()});}}
273+
var cell=w<640?10:fc,cols=Math.ceil(w/cell);columns=[];
274+
for(var i=0;i<cols;i++){columns.push({x:i*cell+cell/2,y:Math.random()*h,speed:0.15+Math.random()*0.3,len:4+((Math.random()*8)|0)});}
275+
}
276+
function draw(){
277+
var cssW=canvas.clientWidth,cssH=canvas.clientHeight;
278+
if(cssW===0||cssH===0){return;}
279+
if(cssW!==lastW||cssH!==lastH){setup(cssW,cssH);}
280+
var dpr=window.devicePixelRatio||1;
281+
if(canvas.width!==cssW*dpr||canvas.height!==cssH*dpr){canvas.width=cssW*dpr;canvas.height=cssH*dpr;}
282+
var lay=document.createElement('canvas');lay.width=cssW*dpr;lay.height=cssH*dpr;
283+
var lx=lay.getContext('2d');lx.setTransform(dpr,0,0,dpr,0,0);lx.clearRect(0,0,cssW,cssH);
284+
lx.textAlign='center';lx.textBaseline='middle';
285+
lx.font='700 '+(baseCell*1.05)+"px 'SF Mono',Menlo,Monaco,monospace";
286+
for(var k=0;k<baseCells.length;k++){var c=baseCells[k];lx.globalAlpha=0.55;lx.fillStyle=c.color;lx.fillText(c.glyph,c.x,c.y);}
287+
var cell=cssW<640?10:fc,alpha=cssW<640?0.27:0.19;
288+
lx.font='400 '+(cell*0.86)+"px 'SF Mono',Menlo,Monaco,monospace";
289+
for(var i=0;i<columns.length;i++){var col=columns[i];
290+
for(var j=0;j<col.len;j++){var yy=col.y-j*cell;if(yy<-cell||yy>cssH+cell){continue;}var tail=j/col.len;lx.globalAlpha=(1-tail)*(alpha+Math.random()*0.13);lx.fillStyle='#6a5da6';lx.fillText(pg(),col.x,yy);}
291+
col.y+=col.speed;if(col.y-col.len*cell>cssH){col.y=-Math.random()*cssH*0.5;col.speed=0.15+Math.random()*0.3;col.len=4+((Math.random()*8)|0);}
292+
}
293+
lx.globalAlpha=1;lx.globalCompositeOperation='destination-in';
294+
var word='MEMCODE',fs=cssH*0.92;lx.font='900 '+fs+"px 'Arial Black',Arial,sans-serif";
295+
while(lx.measureText(word).width>cssW*0.94&&fs>6){fs-=1;lx.font='900 '+fs+"px 'Arial Black',Arial,sans-serif";}
296+
lx.fillStyle='#fff';lx.fillText(word,cssW/2,cssH/2);
297+
ctx.setTransform(1,0,0,1,0,0);ctx.clearRect(0,0,canvas.width,canvas.height);ctx.drawImage(lay,0,0);
298+
}
299+
(function loop(){draw();requestAnimationFrame(loop);})();
300+
if(typeof ResizeObserver!=='undefined'){new ResizeObserver(function(){lastW=0;}).observe(canvas);}
301+
})();</script>`
263302

264303
func writeCallbackPage(w http.ResponseWriter, ok bool, errMsg string) {
265304
w.Header().Set("Content-Type", "text/html; charset=utf-8")
@@ -269,30 +308,37 @@ func writeCallbackPage(w http.ResponseWriter, ok bool, errMsg string) {
269308

270309
var inner string
271310
if ok {
272-
inner = `<h1>Authenticated</h1><p class="sub">You can close this tab and return to your terminal.</p>`
311+
inner = `<h2>Authenticated</h2><p class="sub">You can close this tab and return to your terminal.</p>`
273312
} else {
274313
inner = fmt.Sprintf(
275-
`<h1>Authentication failed</h1><p class="err">%s</p><p class="sub">Close this tab and try again.</p>`,
314+
`<h2>Authentication failed</h2><p class="err">%s</p><p class="sub">Close this tab and try again.</p>`,
276315
html.EscapeString(errMsg),
277316
)
278317
}
279318

319+
// The MEMCODE wordmark is the SAME live canvas animation the marketing site
320+
// runs (a static glyph base with matrix rain sweeping through, both clipped
321+
// to the letterforms) — ported to vanilla JS so the self-contained page
322+
// needs no assets. Generous vertical rhythm below it: wordmark, heading, and
323+
// help line each get their own breathing room.
280324
page := `<!doctype html><html lang="en"><head><meta charset="utf-8">` +
281325
`<meta name="viewport" content="width=device-width,initial-scale=1"><title>memcode</title><style>` +
282326
`html,body{height:100%}` +
283327
`body{margin:0;background:#0a0a0a;color:#fafafa;` +
284-
`font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;` +
328+
`font-family:system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;` +
285329
`display:flex;align-items:center;justify-content:center}` +
286-
`.wrap{text-align:center;padding:24px;animation:fade .5s ease both}` +
287-
`.mark{width:200px;max-width:56vw;height:auto;display:block;margin:0 auto 40px;opacity:.95}` +
288-
`h1{margin:0 0 10px;font-size:28px;font-weight:600;letter-spacing:-.02em}` +
289-
`.sub{margin:0;color:#8a8a8a;font-size:15px}` +
290-
`.err{margin:0 0 6px;color:#f87171;font-size:15px}` +
291-
`@keyframes fade{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}` +
330+
`.wrap{text-align:center;padding:40px;animation:fade .6s ease both}` +
331+
`.mark{width:560px;max-width:82vw;height:150px;display:block;margin:0 auto 44px}` +
332+
`h2{margin:0 0 14px;font-size:24px;font-weight:600;letter-spacing:-.01em}` +
333+
`.sub{margin:0;color:#888;font-size:15px;line-height:1.5}` +
334+
`.err{margin:0 0 10px;color:#f87171;font-size:15px}` +
335+
`@keyframes fade{from{opacity:0}to{opacity:1}}` +
292336
`</style></head><body><div class="wrap">` +
293-
`<img class="mark" src="` + callbackWordmark + `" alt="memcode">` +
337+
`<canvas id="mark" class="mark"></canvas>` +
294338
inner +
295-
`</div></body></html>`
339+
`</div>` +
340+
callbackScript +
341+
`</body></html>`
296342

297343
io.WriteString(w, page)
298344
}

0 commit comments

Comments
 (0)