| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"> | |
| <meta name="description" content="Offline Preact and WebGL2 architecture atlas for DeepSeek V4.1 Flash: causal encoder-decoder, CSA2, Engram, DSpark, native FP4 and supplied NVFP4 recipe, byte-proportional solids and local Safetensors auditing."> | |
| <meta name="color-scheme" content="dark"> | |
| <meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; img-src data: blob:; connect-src 'none'; base-uri 'none'; form-action 'none';"> | |
| <title>DeepSeek V4.1 Flash - Tensor Atlas v4</title> | |
| <style> | |
| :root { | |
| color-scheme: dark; | |
| --bg: #070c15; | |
| --panel: #0c1320; | |
| --raised: #121d2b; | |
| --line: #203044; | |
| --ink: #eaf0fb; | |
| --muted: #8f9db3; | |
| --faint: #52637a; | |
| --blue: #6394ff; | |
| --cyan: #55d7c1; | |
| --mono: ui-monospace,SFMono-Regular,Consolas,'Liberation Mono',monospace; | |
| --sans: Inter,-apple-system,BlinkMacSystemFont,'Segoe UI',sans-serif; | |
| --serif: 'Iowan Old Style','Palatino Linotype',Palatino,Georgia,serif | |
| } | |
| * { | |
| box-sizing: border-box | |
| } | |
| html,body,#root { | |
| margin: 0; | |
| width: 100%; | |
| height: 100%; | |
| overflow: hidden; | |
| background: var(--bg) | |
| } | |
| body { | |
| font-family: var(--sans); | |
| font-size: calc(13px * var(--font-scale,1)); | |
| color: var(--ink); | |
| -webkit-font-smoothing: antialiased | |
| } | |
| button,input,select { | |
| font: inherit | |
| } | |
| button { | |
| cursor: pointer | |
| } | |
| button,a,input,select { | |
| -webkit-tap-highlight-color: transparent | |
| } | |
| button { | |
| color: inherit | |
| } | |
| button:focus-visible,a:focus-visible,select:focus-visible,input:focus-visible,summary:focus-visible { | |
| outline: 2px solid var(--cyan); | |
| outline-offset: 3px | |
| } | |
| button:disabled { | |
| opacity: .6; | |
| cursor: wait | |
| } | |
| a { | |
| color: #93b5ff | |
| } | |
| p { | |
| line-height: 1.7 | |
| } | |
| b,strong { | |
| font-weight: 600 | |
| } | |
| button { | |
| border: 0; | |
| background: none | |
| } | |
| select { | |
| background: #111c2a; | |
| border: 1px solid var(--line); | |
| color: var(--ink); | |
| border-radius: 5px; | |
| padding: 7px 8px; | |
| max-width: 100% | |
| } | |
| code,pre { | |
| font-family: var(--mono) | |
| } | |
| code { | |
| font-size: .91em | |
| } | |
| small { | |
| font-size: calc(11px * var(--font-scale,1)) | |
| } | |
| h1,h2,h3,p { | |
| margin: 0 | |
| } | |
| h3 { | |
| font-weight: 600 | |
| } | |
| input[type=range] { | |
| accent-color: var(--cyan) | |
| } | |
| input[type=number] { | |
| appearance: textfield; | |
| -moz-appearance: textfield | |
| } | |
| input[type=number]::-webkit-inner-spin-button { | |
| appearance: none | |
| } | |
| ::-webkit-scrollbar { | |
| width: 5px; | |
| height: 5px | |
| } | |
| ::-webkit-scrollbar-thumb { | |
| background: #28384e; | |
| border-radius: 5px | |
| } | |
| ::-webkit-scrollbar-track { | |
| background: transparent | |
| } | |
| ::selection { | |
| background: #40648a; | |
| color: white | |
| } | |
| .app-shell { | |
| height: 100vh; | |
| height: 100dvh; | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden | |
| } | |
| .topbar { | |
| height: 60px; | |
| flex-shrink: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 0 23px; | |
| border-bottom: 1px solid var(--line); | |
| background: #0a111b; | |
| gap: 12px; | |
| z-index: 20 | |
| } | |
| .brand { | |
| display: flex; | |
| align-items: center; | |
| gap: 13px; | |
| text-decoration: none; | |
| color: #c9d6ed; | |
| font-family: var(--mono); | |
| font-size: calc(11px * var(--font-scale,1)); | |
| letter-spacing: 1.6px; | |
| white-space: nowrap | |
| } | |
| .brand b { | |
| color: var(--faint); | |
| font-size: calc(10px * var(--font-scale,1)); | |
| letter-spacing: 2px; | |
| margin-left: 14px; | |
| padding-left: 16px; | |
| border-left: 1px solid #314057 | |
| } | |
| .brand-mark { | |
| display: flex; | |
| align-items: center; | |
| gap: 3px; | |
| transform: skewY(-13deg) | |
| } | |
| .brand-mark i { | |
| width: 4px; | |
| display: block; | |
| background: #7399ff; | |
| height: 19px | |
| } | |
| .brand-mark i:first-child { | |
| height: 10px | |
| } | |
| .brand-mark i:last-child { | |
| height: 14px; | |
| background: #54d7c0 | |
| } | |
| .topbar-center { | |
| font-family: var(--mono); | |
| font-size: calc(10px * var(--font-scale,1)); | |
| letter-spacing: 1.8px; | |
| color: #8499b5 | |
| } | |
| .topbar-center span { | |
| color: #435571; | |
| padding: 0 15px | |
| } | |
| .header-actions { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px | |
| } | |
| .offline-badge { | |
| font-family: var(--mono); | |
| font-size: calc(9px * var(--font-scale,1)); | |
| letter-spacing: 1px; | |
| color: #889eac; | |
| margin-right: 13px | |
| } | |
| .offline-badge i,.source-status i,.inspector-foot>span i { | |
| display: inline-block; | |
| width: 5px; | |
| height: 5px; | |
| border-radius: 50%; | |
| background: #55d7c1; | |
| box-shadow: 0 0 8px #55d7c144; | |
| margin-right: 7px | |
| } | |
| .icon-button { | |
| width: 32px; | |
| height: 32px; | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| border: 1px solid #233147; | |
| background: #101b2a88; | |
| border-radius: 7px; | |
| color: #92a7c0; | |
| flex-shrink: 0; | |
| transition: background .15s,border-color .15s | |
| } | |
| .icon-button:hover,.icon-button.active { | |
| color: #c8dcff; | |
| background: #273e6044; | |
| border-color: #486b96 | |
| } | |
| .workspace { | |
| flex: 1; | |
| min-height: 0; | |
| display: flex | |
| } | |
| .layer-rail { | |
| width: 53px; | |
| flex-shrink: 0; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: stretch; | |
| background: #0b121d; | |
| border-right: 1px solid var(--line); | |
| padding: 12px 7px 9px; | |
| gap: 4px | |
| } | |
| .rail-caption { | |
| text-align: center; | |
| letter-spacing: 1.6px; | |
| font-family: var(--mono); | |
| font-size: calc(8px * var(--font-scale,1)); | |
| color: var(--faint); | |
| padding: 4px 0 | |
| } | |
| .rail-special { | |
| height: 26px; | |
| flex-shrink: 0; | |
| color: #8aa6d6; | |
| border: 1px solid #2c3c57; | |
| border-radius: 5px; | |
| font-family: var(--mono); | |
| font-size: calc(11px * var(--font-scale,1)) | |
| } | |
| .rail-special:hover,.rail-special.selected { | |
| background: #28375a | |
| } | |
| .rail-layers { | |
| display: flex; | |
| flex-direction: column; | |
| flex: 1; | |
| min-height: 0; | |
| gap: 2px | |
| } | |
| .rail-layer { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 3px; | |
| flex: 1; | |
| min-height: 5px; | |
| width: 100%; | |
| padding: 0 3px 0 2px; | |
| position: relative; | |
| border-radius: 3px; | |
| border: 1px solid transparent | |
| } | |
| .rail-layer span { | |
| font-family: var(--mono); | |
| font-size: calc(8px * var(--font-scale,1)); | |
| color: #6d83a2; | |
| line-height: 1 | |
| } | |
| .rail-layer i { | |
| display: block; | |
| width: 11px; | |
| height: 65%; | |
| min-height: 2px; | |
| background: var(--layer-color); | |
| opacity: .45; | |
| transition: width .18s | |
| } | |
| .rail-layer:hover { | |
| background: #1b3047 | |
| } | |
| .rail-layer.selected { | |
| background: #23334b; | |
| border-color: var(--layer-color) | |
| } | |
| .rail-layer.selected i { | |
| opacity: 1; | |
| width: 14px | |
| } | |
| .rail-layer:nth-child(20) { | |
| margin-bottom: 8px | |
| } | |
| .rail-layer:nth-child(20):after { | |
| content: ''; | |
| position: absolute; | |
| left: -4px; | |
| right: -4px; | |
| bottom: -7px; | |
| border-bottom: 1px solid #314560 | |
| } | |
| .main { | |
| flex: 1; | |
| min-width: 0; | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden | |
| } | |
| .title-strip { | |
| padding: 23px 27px 20px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 16px; | |
| flex-shrink: 0 | |
| } | |
| .eyebrow { | |
| font-family: var(--mono); | |
| font-size: calc(8.5px * var(--font-scale,1)); | |
| letter-spacing: 2.1px; | |
| color: #6b8bb5; | |
| margin-bottom: 9px | |
| } | |
| h1 { | |
| font-family: var(--serif); | |
| font-size: calc(32px * var(--font-scale,1)); | |
| font-weight: 400; | |
| letter-spacing: -1px; | |
| line-height: 1.1; | |
| white-space: nowrap | |
| } | |
| h1 em { | |
| font-weight: 400 | |
| } | |
| h1 .title-dot { | |
| color: var(--cyan); | |
| font-size: calc(36px * var(--font-scale,1)) | |
| } | |
| .title-stats { | |
| display: flex; | |
| gap: 28px; | |
| text-align: right | |
| } | |
| .title-stats b { | |
| font: calc(17px * var(--font-scale,1)) var(--mono); | |
| color: #bbd0ea; | |
| display: block | |
| } | |
| .title-stats small { | |
| display: block; | |
| font: calc(7.5px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: .8px; | |
| color: #6a7b94; | |
| margin-top: 8px | |
| } | |
| .view-toolbar { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 15px; | |
| border-top: 1px solid #182539; | |
| border-bottom: 1px solid #243047; | |
| background: #0b131f88; | |
| padding: 9px 25px; | |
| flex-shrink: 0; | |
| min-height: 65px | |
| } | |
| .view-tabs { | |
| display: flex; | |
| gap: 2px | |
| } | |
| .view-tabs button { | |
| height: 35px; | |
| padding: 0 11px; | |
| border: 1px solid transparent; | |
| color: #7e91ab; | |
| font-size: calc(11px * var(--font-scale,1)); | |
| border-radius: 5px; | |
| white-space: nowrap | |
| } | |
| .view-tabs button small { | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| color: #4e6789; | |
| margin-right: 7px | |
| } | |
| .view-tabs button.active { | |
| color: #d5e6ff; | |
| background: #18263d; | |
| border-color: #284164 | |
| } | |
| .view-tabs button.active small { | |
| color: #8babef | |
| } | |
| .view-tabs button:hover:not(.active) { | |
| color: #b5d0f0 | |
| } | |
| .precision-toggle { | |
| display: flex; | |
| border: 1px solid #2d3e58; | |
| border-radius: 6px; | |
| overflow: hidden | |
| } | |
| .precision-toggle button { | |
| padding: 7px 13px; | |
| color: #718298; | |
| min-width: 88px; | |
| position: relative; | |
| border-right: 1px solid #293851; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 4px; | |
| align-items: flex-start; | |
| text-align: left; | |
| transition: background .2s | |
| } | |
| .precision-toggle button:last-child { | |
| border: 0 | |
| } | |
| .precision-toggle button b { | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: 1.2px | |
| } | |
| .precision-toggle button span { | |
| font: calc(10px * var(--font-scale,1)) var(--mono); | |
| font-variant-numeric: tabular-nums | |
| } | |
| .precision-toggle button.active { | |
| background: #17263b; | |
| color: var(--precision); | |
| box-shadow: inset 0 -2px var(--precision) | |
| } | |
| .precision-toggle button:hover { | |
| background: #152338 | |
| } | |
| .world { | |
| position: relative; | |
| flex: 1; | |
| min-height: 150px; | |
| overflow: hidden; | |
| background: radial-gradient(ellipse at 49% 37%,#16273b80,transparent 70%),linear-gradient(#080e18,#09111c); | |
| isolation: isolate | |
| } | |
| .world:after { | |
| content: ''; | |
| position: absolute; | |
| inset: 0; | |
| box-shadow: inset 0 0 85px #05081066; | |
| pointer-events: none; | |
| z-index: 1 | |
| } | |
| .world canvas { | |
| position: absolute; | |
| inset: 0; | |
| display: block; | |
| width: 100%; | |
| height: 100%; | |
| touch-action: none; | |
| outline: none | |
| } | |
| .world-labels { | |
| position: absolute; | |
| inset: 0; | |
| pointer-events: none; | |
| overflow: hidden; | |
| z-index: 3 | |
| } | |
| .world-label { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| text-align: center; | |
| white-space: nowrap; | |
| pointer-events: none; | |
| font-family: var(--mono); | |
| text-shadow: 0 1px 5px #040810,0 0 12px #050912; | |
| line-height: 1.45; | |
| padding: 3px 6px; | |
| border-radius: 3px; | |
| background: #09111c55 | |
| } | |
| .world-label b { | |
| display: block; | |
| font-size: calc(9px * var(--font-scale,1)); | |
| font-weight: 500; | |
| letter-spacing: .6px | |
| } | |
| .world-label span { | |
| display: block; | |
| font-size: calc(8px * var(--font-scale,1)); | |
| color: #829bb7 | |
| } | |
| .scene-heading { | |
| position: absolute; | |
| left: 25px; | |
| top: 20px; | |
| display: flex; | |
| gap: 12px; | |
| pointer-events: none; | |
| z-index: 4; | |
| max-width: calc(100% - 90px) | |
| } | |
| .scene-index { | |
| font: calc(10px * var(--font-scale,1)) var(--mono); | |
| color: #4b678d; | |
| padding-top: 3px | |
| } | |
| .scene-heading b { | |
| display: block; | |
| font-size: calc(12px * var(--font-scale,1)); | |
| font-weight: 500; | |
| letter-spacing: .2px; | |
| color: #bbcbe0 | |
| } | |
| .scene-heading small { | |
| display: block; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| color: #637a97; | |
| margin-top: 5px | |
| } | |
| .scene-tools { | |
| position: absolute; | |
| top: 19px; | |
| right: 19px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 6px; | |
| z-index: 5 | |
| } | |
| .scene-tools .icon-button { | |
| width: 30px; | |
| height: 30px; | |
| background: #0c1729bd; | |
| backdrop-filter: blur(6px) | |
| } | |
| .scene-scale { | |
| position: absolute; | |
| bottom: 19px; | |
| left: 25px; | |
| display: flex; | |
| align-items: center; | |
| gap: 9px; | |
| pointer-events: none; | |
| z-index: 4; | |
| color: #99afca; | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| line-height: 1.6 | |
| } | |
| .scene-scale em { | |
| display: block; | |
| font-size: calc(8px * var(--font-scale,1)); | |
| font-style: normal; | |
| color: #576f8f | |
| } | |
| .scale-glyph { | |
| display: block; | |
| width: 13px; | |
| height: 13px; | |
| border: 1px solid #677b96; | |
| transform: rotate(-20deg) skewY(20deg); | |
| position: relative | |
| } | |
| .scale-glyph:after { | |
| content: ''; | |
| border: 1px solid #4f6686; | |
| position: absolute; | |
| left: 4px; | |
| top: -5px; | |
| width: 100%; | |
| height: 100% | |
| } | |
| .scene-legend { | |
| position: absolute; | |
| bottom: 22px; | |
| right: 21px; | |
| display: flex; | |
| gap: 12px; | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| color: #8293ac; | |
| z-index: 4; | |
| pointer-events: none | |
| } | |
| .scene-legend span { | |
| display: flex; | |
| align-items: center; | |
| gap: 4px | |
| } | |
| .scene-legend i { | |
| width: 4px; | |
| height: 4px; | |
| border-radius: 1px | |
| } | |
| .format-notice { | |
| position: absolute; | |
| left: 25px; | |
| bottom: 62px; | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| color: #799a9e; | |
| background: #10232bea; | |
| border: 1px solid #2c4956; | |
| border-radius: 4px; | |
| padding: 8px 11px; | |
| z-index: 4 | |
| } | |
| .format-notice b { | |
| color: var(--cyan); | |
| margin-right: 5px | |
| } | |
| .scene-tip { | |
| position: absolute; | |
| z-index: 10; | |
| display: none; | |
| pointer-events: none; | |
| padding: 12px 14px; | |
| background: #0d1929f2; | |
| border: 1px solid #3a5579; | |
| border-radius: 7px; | |
| max-width: 280px; | |
| box-shadow: 0 6px 35px #0006; | |
| backdrop-filter: blur(12px) | |
| } | |
| .scene-tip b { | |
| display: block; | |
| color: #d0dff2; | |
| font-size: calc(12px * var(--font-scale,1)); | |
| font-weight: 500 | |
| } | |
| .scene-tip span { | |
| display: block; | |
| font: calc(9px * var(--font-scale,1))/1.7 var(--mono); | |
| color: #93adc9; | |
| margin-top: 4px | |
| } | |
| .graphics-error { | |
| position: absolute; | |
| left: 10%; | |
| right: 10%; | |
| top: 25%; | |
| padding: 25px; | |
| background: #0b1528f5; | |
| border: 1px solid #645941; | |
| border-radius: 12px; | |
| z-index: 10 | |
| } | |
| .graphics-error h2 { | |
| font-family: var(--serif); | |
| font-size: calc(24px * var(--font-scale,1)) | |
| } | |
| .graphics-error p { | |
| margin-top: 12px; | |
| color: #b0bac9; | |
| font-size: calc(12px * var(--font-scale,1)) | |
| } | |
| .transport { | |
| flex-shrink: 0; | |
| padding: 19px 25px 12px; | |
| background: #0b1320; | |
| border-top: 1px solid #293a50; | |
| z-index: 10 | |
| } | |
| .story-text { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 15px | |
| } | |
| .story-text>div { | |
| display: flex; | |
| gap: 18px; | |
| align-items: center | |
| } | |
| .story-phase { | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: 1px; | |
| min-width: 100px | |
| } | |
| .story-text>div>b { | |
| font-family: var(--serif); | |
| font-weight: 400; | |
| font-size: calc(19px * var(--font-scale,1)); | |
| color: #d0dcea | |
| } | |
| .story-number { | |
| font: calc(19px * var(--font-scale,1)) var(--mono); | |
| min-width: 90px; | |
| text-align: right | |
| } | |
| .story-number small { | |
| display: block; | |
| color: #617891; | |
| font: calc(7px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: .8px; | |
| margin-top: 4px | |
| } | |
| .story-description { | |
| font-size: calc(10px * var(--font-scale,1)); | |
| color: #7e94af; | |
| margin: 9px 0 13px; | |
| line-height: 1.5; | |
| min-height: 15px; | |
| padding-right: 50px | |
| } | |
| .transport-row { | |
| display: flex; | |
| align-items: center; | |
| gap: 13px | |
| } | |
| .play-button { | |
| width: 34px; | |
| height: 34px; | |
| border: 1px solid #304c72; | |
| border-radius: 50%; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: #91b4fa; | |
| background: #182944; | |
| flex-shrink: 0 | |
| } | |
| .play-button:hover { | |
| background: #233e65 | |
| } | |
| .timeline { | |
| flex: 1; | |
| min-width: 0; | |
| position: relative | |
| } | |
| .phase-buttons { | |
| display: flex; | |
| gap: 2px; | |
| width: 100%; | |
| position: relative | |
| } | |
| .phase-buttons button { | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| text-align: left; | |
| padding: 0 0 6px 4px; | |
| white-space: nowrap; | |
| color: #61758f | |
| } | |
| .phase-buttons button.active { | |
| color: var(--phase) | |
| } | |
| .timeline input { | |
| display: block; | |
| width: 100%; | |
| margin: 0; | |
| height: 11px; | |
| background: none; | |
| -webkit-appearance: none; | |
| appearance: none; | |
| cursor: pointer | |
| } | |
| .timeline input::-webkit-slider-runnable-track { | |
| height: 2px; | |
| border-radius: 2px; | |
| background: linear-gradient(to right,#729dea var(--progress),#2a3c56 var(--progress)) | |
| } | |
| .timeline input::-webkit-slider-thumb { | |
| appearance: none; | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 50%; | |
| background: #b7d5ff; | |
| margin-top: -3px; | |
| box-shadow: 0 0 0 3px #6e9bee16 | |
| } | |
| .timeline input::-moz-range-track { | |
| height: 2px; | |
| background: #2a3c56 | |
| } | |
| .timeline input::-moz-range-thumb { | |
| width: 8px; | |
| height: 8px; | |
| border: 0; | |
| background: #b7d5ff | |
| } | |
| .speed-select { | |
| font: calc(10px * var(--font-scale,1)) var(--mono); | |
| width: 58px; | |
| padding: 7px 4px; | |
| background: transparent; | |
| border-color: #293e59 | |
| } | |
| .orbit-button { | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| color: #6f8daf; | |
| border: 1px solid #2c425d; | |
| padding: 8px 10px; | |
| border-radius: 4px | |
| } | |
| .orbit-button.active { | |
| background: #213b5c; | |
| color: #abd0ff | |
| } | |
| .transport-bottom { | |
| display: flex; | |
| justify-content: space-between; | |
| gap: 10px; | |
| padding-top: 13px; | |
| font: calc(7.3px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: .65px; | |
| color: #506780 | |
| } | |
| .inspector { | |
| width: 376px; | |
| flex-shrink: 0; | |
| display: flex; | |
| flex-direction: column; | |
| min-height: 0; | |
| border-left: 1px solid var(--line); | |
| background: var(--panel); | |
| z-index: 15 | |
| } | |
| .inspector-top { | |
| height: 54px; | |
| flex-shrink: 0; | |
| border-bottom: 1px solid var(--line); | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 0 22px | |
| } | |
| .panel-tabs { | |
| display: flex; | |
| align-items: center; | |
| gap: 21px; | |
| height: 100% | |
| } | |
| .panel-tabs button { | |
| height: 100%; | |
| font-size: calc(11px * var(--font-scale,1)); | |
| color: #647c98; | |
| padding: 0; | |
| position: relative | |
| } | |
| .panel-tabs button.active { | |
| color: #c9daef | |
| } | |
| .panel-tabs button.active:after { | |
| content: ''; | |
| position: absolute; | |
| left: 0; | |
| right: 0; | |
| height: 2px; | |
| bottom: -1px; | |
| background: #7da6ff | |
| } | |
| .inspector-nav { | |
| height: 41px; | |
| flex-shrink: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 0 22px; | |
| border-bottom: 1px solid #1b2a3e; | |
| color: #6c84a5; | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: .25px | |
| } | |
| .inspector-nav>div { | |
| display: flex; | |
| gap: 4px | |
| } | |
| .inspector-nav button { | |
| width: 22px; | |
| height: 23px; | |
| background: #142238; | |
| border-radius: 3px; | |
| color: #a4b4ce | |
| } | |
| .inspector-scroll { | |
| flex: 1; | |
| overflow-y: auto; | |
| min-height: 0; | |
| padding: 25px 23px 24px; | |
| overscroll-behavior: contain | |
| } | |
| .inspector-foot { | |
| height: 37px; | |
| border-top: 1px solid var(--line); | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| padding: 0 20px; | |
| flex-shrink: 0; | |
| font: calc(7px * var(--font-scale,1)) var(--mono); | |
| color: #607c9c; | |
| letter-spacing: .7px | |
| } | |
| .inspector-foot>span i { | |
| width: 4px; | |
| height: 4px; | |
| background: #ba975e; | |
| box-shadow: none; | |
| margin-right: 5px | |
| } | |
| .inspector-foot button { | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| color: #a1b1c8; | |
| padding: 4px | |
| } | |
| .intro-kicker,.inspect-path { | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: 1.5px; | |
| color: #698cb8; | |
| margin-bottom: 15px | |
| } | |
| .inspect-path span { | |
| color: #485b76; | |
| margin-left: 5px | |
| } | |
| .editorial { | |
| font-family: var(--serif); | |
| font-weight: 400; | |
| font-size: calc(31px * var(--font-scale,1)); | |
| letter-spacing: -.9px; | |
| line-height: 1.12; | |
| color: #e0e9f8 | |
| } | |
| .editorial em { | |
| color: #91b6ef; | |
| font-weight: 400 | |
| } | |
| .lede { | |
| font-size: calc(12px * var(--font-scale,1)); | |
| line-height: 1.75; | |
| color: #90a2ba; | |
| margin-top: 12px; | |
| margin-bottom: 18px | |
| } | |
| .stat-grid { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| border: 1px solid #243449; | |
| border-radius: 6px; | |
| overflow: hidden; | |
| margin: 22px 0 17px | |
| } | |
| .stat-grid>div { | |
| padding: 14px; | |
| background: #0d1725 | |
| } | |
| .stat-grid>div:nth-child(odd) { | |
| border-right: 1px solid #243449 | |
| } | |
| .stat-grid>div:nth-child(-n+2) { | |
| border-bottom: 1px solid #243449 | |
| } | |
| .stat-grid small { | |
| display: block; | |
| font: calc(7px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: .6px; | |
| color: #7990af; | |
| white-space: nowrap | |
| } | |
| .stat-grid strong { | |
| display: block; | |
| font: calc(27px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: -1px; | |
| margin-top: 8px; | |
| color: #d1e3ff | |
| } | |
| .stat-grid strong span { | |
| font: calc(10px * var(--font-scale,1)) var(--mono); | |
| color: #7395c0; | |
| margin-left: 3px; | |
| letter-spacing: 0 | |
| } | |
| .note { | |
| border-left: 2px solid var(--note); | |
| background: #16223577; | |
| padding: 12px 13px; | |
| margin: 14px 0; | |
| color: #91a4be; | |
| font-size: calc(10.5px * var(--font-scale,1)); | |
| line-height: 1.8 | |
| } | |
| .note strong { | |
| color: #c2d5ed | |
| } | |
| .section { | |
| margin-top: 26px | |
| } | |
| .section-head { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| gap: 5px; | |
| border-bottom: 1px solid #26384f; | |
| padding-bottom: 9px; | |
| margin-bottom: 10px | |
| } | |
| .section-head h3 { | |
| font-size: calc(11px * var(--font-scale,1)); | |
| color: #bdcce0; | |
| letter-spacing: .05px | |
| } | |
| .section-head>span { | |
| font: calc(7px * var(--font-scale,1)) var(--mono); | |
| color: #607b9c; | |
| letter-spacing: .5px; | |
| text-align: right | |
| } | |
| .fact { | |
| display: flex; | |
| align-items: baseline; | |
| justify-content: space-between; | |
| gap: 15px; | |
| padding: 8px 0; | |
| border-bottom: 1px solid #19283b; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| line-height: 1.5 | |
| } | |
| .fact>span { | |
| color: #8196b1 | |
| } | |
| .fact>b { | |
| font: calc(9px * var(--font-scale,1))/1.5 var(--mono); | |
| color: #c4d6f0; | |
| text-align: right; | |
| overflow-wrap: anywhere; | |
| max-width: 60%; | |
| font-variant-numeric: tabular-nums | |
| } | |
| .fine { | |
| font-size: calc(10px * var(--font-scale,1)); | |
| line-height: 1.8; | |
| color: #758da9; | |
| margin: 12px 0 | |
| } | |
| .fine code { | |
| color: #acc4e2; | |
| background: #1a2b42; | |
| padding: 1px 3px; | |
| border-radius: 2px; | |
| overflow-wrap: anywhere | |
| } | |
| .mode-legend { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 4px; | |
| margin: 12px 0 | |
| } | |
| .mode-key { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| background: #1724366b; | |
| border: 1px solid #23344b; | |
| border-radius: 4px; | |
| padding: 9px; | |
| color: #8eaccb; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| text-align: left | |
| } | |
| .mode-key i { | |
| width: 5px; | |
| height: 5px; | |
| border-radius: 1px | |
| } | |
| .mode-key b { | |
| font: calc(12px * var(--font-scale,1)) var(--mono); | |
| color: #c1d0e4 | |
| } | |
| .mode-key span { | |
| font-size: calc(9px * var(--font-scale,1)) | |
| } | |
| .mode-key:hover { | |
| background: #243753 | |
| } | |
| .feature-link { | |
| display: flex; | |
| align-items: center; | |
| width: 100%; | |
| gap: 11px; | |
| text-align: left; | |
| border-bottom: 1px solid #1b2c40; | |
| padding: 13px 0 | |
| } | |
| .feature-icon { | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| width: 30px; | |
| height: 31px; | |
| border: 1px solid #30415b; | |
| border-radius: 4px; | |
| font: calc(14px * var(--font-scale,1)) var(--mono); | |
| background: #142135; | |
| flex-shrink: 0 | |
| } | |
| .feature-link>span:nth-child(2) { | |
| flex: 1 | |
| } | |
| .feature-link b { | |
| display: block; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| color: #b5cbe4; | |
| font-weight: 500 | |
| } | |
| .feature-link small { | |
| display: block; | |
| font-size: calc(9px * var(--font-scale,1)); | |
| color: #637d9c; | |
| margin-top: 6px; | |
| line-height: 1.5 | |
| } | |
| .feature-link:hover b { | |
| color: #ebf3ff | |
| } | |
| .feature-link>svg { | |
| color: #546e92; | |
| flex-shrink: 0 | |
| } | |
| .text-link { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 8px; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| color: #8daff0; | |
| padding: 7px 0; | |
| line-height: 1.5 | |
| } | |
| .text-link:hover { | |
| color: #bbd8ff | |
| } | |
| .outline-btn { | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| gap: 8px; | |
| padding: 11px 13px; | |
| background: #132136; | |
| border: 1px solid #314967; | |
| border-radius: 5px; | |
| color: #a9c2e3; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| line-height: 1.4 | |
| } | |
| .outline-btn:hover { | |
| border-color: #5378a6; | |
| background: #1c304b | |
| } | |
| .full-width { | |
| width: 100%; | |
| margin-top: 10px | |
| } | |
| .mobile-only,.mobile-inspect { | |
| display: none | |
| } | |
| .chip { | |
| text-transform: uppercase; | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: .8px; | |
| display: inline-block; | |
| border: 1px solid; | |
| border-radius: 4px; | |
| padding: 5px 7px; | |
| white-space: nowrap | |
| } | |
| .module-title { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 8px; | |
| margin-bottom: 20px | |
| } | |
| .module-title h2 { | |
| font: calc(26px * var(--font-scale,1)) var(--serif); | |
| letter-spacing: -.6px | |
| } | |
| .module-metrics { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| border-bottom: 1px solid #253950; | |
| padding-bottom: 14px; | |
| gap: 10px | |
| } | |
| .module-metrics small { | |
| display: block; | |
| font: calc(7px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: .7px; | |
| color: #6d85a7; | |
| margin-bottom: 8px | |
| } | |
| .module-metrics b { | |
| font: calc(20px * var(--font-scale,1)) var(--mono); | |
| color: #b9cfea | |
| } | |
| .filter-row { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| margin-bottom: 12px | |
| } | |
| .filter-row label { | |
| font-size: calc(10px * var(--font-scale,1)); | |
| color: #6c84a1; | |
| display: flex; | |
| align-items: center; | |
| gap: 9px | |
| } | |
| .filter-row select { | |
| font-size: calc(9px * var(--font-scale,1)); | |
| padding: 5px | |
| } | |
| .tiny-btn { | |
| color: #9ebadd; | |
| font-size: calc(9px * var(--font-scale,1)); | |
| border: 1px solid #284364; | |
| border-radius: 3px; | |
| padding: 4px 8px | |
| } | |
| .tensor-row { | |
| padding: 9px 0; | |
| border-bottom: 1px solid #1a2b40 | |
| } | |
| .tensor-row summary { | |
| display: flex; | |
| align-items: flex-start; | |
| gap: 7px; | |
| list-style: none; | |
| cursor: pointer | |
| } | |
| .tensor-row summary::-webkit-details-marker { | |
| display: none | |
| } | |
| .tensor-dot { | |
| width: 4px; | |
| height: 4px; | |
| display: block; | |
| border-radius: 1px; | |
| margin-top: 3px; | |
| flex-shrink: 0 | |
| } | |
| .tensor-body { | |
| flex: 1; | |
| min-width: 0 | |
| } | |
| .tensor-body b { | |
| display: block; | |
| font: calc(9px * var(--font-scale,1))/1.5 var(--mono); | |
| color: #b8c9de; | |
| overflow-wrap: anywhere | |
| } | |
| .tensor-body small { | |
| display: block; | |
| font: calc(8px * var(--font-scale,1))/1.6 var(--mono); | |
| color: #758caa; | |
| overflow-wrap: anywhere; | |
| margin-top: 3px | |
| } | |
| .tensor-body i { | |
| display: block; | |
| font: calc(7px * var(--font-scale,1))/1.6 var(--mono); | |
| font-style: normal; | |
| color: #586e8b; | |
| margin-top: 3px | |
| } | |
| .tensor-row summary>strong { | |
| font: calc(8px * var(--font-scale,1))/1.5 var(--mono); | |
| color: #8daee4; | |
| white-space: nowrap; | |
| padding-top: 1px | |
| } | |
| .tensor-row[open] { | |
| background: #142236; | |
| border-radius: 5px; | |
| padding: 9px 7px; | |
| margin: 0 -7px; | |
| border-bottom-color: #345071 | |
| } | |
| .tensor-row p { | |
| font-size: calc(10px * var(--font-scale,1)); | |
| line-height: 1.8; | |
| color: #8ca5c5; | |
| margin: 9px 0 | |
| } | |
| .tensor-row code { | |
| font-size: calc(8px * var(--font-scale,1)); | |
| color: #a8c7f5 | |
| } | |
| .ledger-bar { | |
| margin: 13px 0 | |
| } | |
| .ledger-bar>div { | |
| display: flex; | |
| justify-content: space-between; | |
| font-size: calc(9px * var(--font-scale,1)); | |
| color: #8ea4c0; | |
| gap: 8px | |
| } | |
| .ledger-bar b { | |
| font: calc(9px * var(--font-scale,1)) var(--mono) | |
| } | |
| .ledger-bar>span { | |
| display: block; | |
| height: 3px; | |
| background: #192a40; | |
| margin-top: 7px | |
| } | |
| .ledger-bar>span>i { | |
| display: block; | |
| height: 100%; | |
| transition: width .4s | |
| } | |
| .storage-total { | |
| border-top: 1px solid #2b425d; | |
| border-bottom: 1px solid #2b425d; | |
| padding: 20px 0; | |
| margin: 22px 0 | |
| } | |
| .storage-total small { | |
| display: block; | |
| font: calc(7.5px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: .9px; | |
| color: #6c93b9 | |
| } | |
| .storage-total strong { | |
| display: block; | |
| font: calc(38px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: -1.5px; | |
| color: #c2d9ff; | |
| margin: 11px 0 9px | |
| } | |
| .storage-total span { | |
| font-size: calc(9px * var(--font-scale,1)); | |
| color: #6b849f | |
| } | |
| .storage-total em { | |
| font: calc(14px * var(--font-scale,1)) var(--mono); | |
| color: #8cb3c6; | |
| letter-spacing: 0 | |
| } | |
| .cache-total strong { | |
| color: #e4bc7e | |
| } | |
| .category-row { | |
| display: block; | |
| text-align: left; | |
| width: 100%; | |
| padding: 11px 0; | |
| border-bottom: 1px solid #1c2c41 | |
| } | |
| .category-row>div { | |
| display: flex; | |
| align-items: center; | |
| gap: 7px; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| color: #b4c9e0 | |
| } | |
| .category-row>div>i { | |
| display: block; | |
| width: 4px; | |
| height: 4px; | |
| border-radius: 1px | |
| } | |
| .category-row>div>span { | |
| flex: 1 | |
| } | |
| .category-row b { | |
| font: calc(9px * var(--font-scale,1)) var(--mono) | |
| } | |
| .mini-track { | |
| display: block; | |
| height: 2px; | |
| background: #17283e; | |
| margin: 9px 0 7px | |
| } | |
| .mini-track>i { | |
| display: block; | |
| height: 100%; | |
| transition: width .4s | |
| } | |
| .category-row>small { | |
| display: flex; | |
| justify-content: space-between; | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| color: #607a9a | |
| } | |
| .category-row:hover,.category-row.active { | |
| background: #192a4199; | |
| border-radius: 4px; | |
| padding-left: 7px; | |
| padding-right: 7px | |
| } | |
| .equation { | |
| padding: 13px 14px; | |
| background: #0a1220; | |
| border: 1px solid #21364f; | |
| border-radius: 5px | |
| } | |
| .equation>span { | |
| font: calc(7px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: .8px; | |
| color: #6385ae; | |
| display: block; | |
| margin: 3px 0 8px | |
| } | |
| .equation>code { | |
| font-size: calc(10px * var(--font-scale,1)); | |
| display: block; | |
| color: #bbd2ee; | |
| margin-bottom: 16px; | |
| white-space: nowrap | |
| } | |
| .equation>code:last-child { | |
| margin-bottom: 2px | |
| } | |
| .assumptions { | |
| border-top: 1px solid #2b3e57; | |
| margin: 20px 0; | |
| padding-top: 14px; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| color: #97b0cc; | |
| line-height: 1.8 | |
| } | |
| .assumptions summary { | |
| cursor: pointer | |
| } | |
| .assumptions p { | |
| color: #6d85a2; | |
| margin-top: 12px | |
| } | |
| .range-title { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 12px; | |
| color: #93aac6; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| margin: 15px 0 | |
| } | |
| .range-title input { | |
| width: 112px; | |
| font: calc(12px * var(--font-scale,1)) var(--mono); | |
| padding: 7px 9px; | |
| color: #d7e5f7; | |
| background: #101e30; | |
| border: 1px solid #304763; | |
| border-radius: 4px; | |
| text-align: right | |
| } | |
| .range-title output { | |
| color: #c8dfed; | |
| font: calc(15px * var(--font-scale,1)) var(--mono) | |
| } | |
| .slider { | |
| width: 100%; | |
| height: 20px; | |
| display: block; | |
| margin: 6px 0 0; | |
| cursor: pointer | |
| } | |
| .range-ends { | |
| display: flex; | |
| justify-content: space-between; | |
| color: #536d8d; | |
| font: calc(8px * var(--font-scale,1)) var(--mono) | |
| } | |
| .preset-row { | |
| display: flex; | |
| gap: 6px; | |
| margin: 16px 0 10px | |
| } | |
| .preset-row button { | |
| flex: 1; | |
| background: #14243a; | |
| border: 1px solid #293f5a; | |
| border-radius: 4px; | |
| padding: 7px; | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| color: #7da0c4 | |
| } | |
| .preset-row button.active { | |
| border-color: #53868e; | |
| color: #97dbd2; | |
| background: #19313c | |
| } | |
| .field-label { | |
| display: block; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| color: #829bb9; | |
| margin: 20px 0 | |
| } | |
| .field-label select { | |
| display: block; | |
| width: 100%; | |
| margin-top: 9px; | |
| font-size: calc(11px * var(--font-scale,1)); | |
| padding: 10px | |
| } | |
| .benchmark-chart { | |
| margin-top: 22px | |
| } | |
| .benchmark-row { | |
| padding: 10px 0 | |
| } | |
| .benchmark-row>div:first-child { | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| color: #879ebc; | |
| font-size: calc(10px * var(--font-scale,1)) | |
| } | |
| .benchmark-row b { | |
| font: calc(11px * var(--font-scale,1)) var(--mono); | |
| color: #9cb4d4 | |
| } | |
| .bench-track { | |
| height: 4px; | |
| background: #16263b; | |
| border-radius: 2px; | |
| margin-top: 9px; | |
| overflow: hidden | |
| } | |
| .bench-track i { | |
| height: 100%; | |
| display: block; | |
| border-radius: 2px; | |
| transition: width .45s | |
| } | |
| .benchmark-row.featured { | |
| border: 1px solid #2b555b; | |
| border-radius: 6px; | |
| padding: 14px 11px; | |
| margin: 7px -11px 4px; | |
| background: #12282e88 | |
| } | |
| .benchmark-row.featured span,.benchmark-row.featured b { | |
| color: var(--cyan) | |
| } | |
| .small-code { | |
| background: #0a1423; | |
| padding: 14px; | |
| border: 1px solid #223951; | |
| border-radius: 5px; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| color: #a2c6e6; | |
| line-height: 1.9; | |
| overflow: auto | |
| } | |
| .source-status { | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| color: #81a5ab; | |
| letter-spacing: .7px; | |
| padding: 12px; | |
| border: 1px solid #22414a; | |
| border-radius: 4px; | |
| background: #102329 | |
| } | |
| .source-row { | |
| display: flex; | |
| gap: 12px; | |
| padding: 14px 0; | |
| border-bottom: 1px solid #1d3047 | |
| } | |
| .source-row>span { | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| color: #4a678b; | |
| padding-top: 3px | |
| } | |
| .source-row>div { | |
| flex: 1 | |
| } | |
| .source-row a { | |
| font-size: calc(11px * var(--font-scale,1)); | |
| color: #b1caff; | |
| text-decoration: none | |
| } | |
| .source-row a:hover { | |
| text-decoration: underline | |
| } | |
| .source-row a span { | |
| font: calc(10px * var(--font-scale,1)) var(--mono); | |
| color: #587aa6; | |
| margin-left: 5px | |
| } | |
| .source-row p { | |
| font-size: calc(10px * var(--font-scale,1)); | |
| color: #758da9; | |
| line-height: 1.8; | |
| margin-top: 7px | |
| } | |
| .action-grid { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 7px | |
| } | |
| .modal-backdrop { | |
| position: fixed; | |
| inset: 0; | |
| background: #020711c7; | |
| backdrop-filter: blur(10px); | |
| display: flex; | |
| justify-content: center; | |
| align-items: center; | |
| z-index: 100; | |
| padding: 30px | |
| } | |
| .modal { | |
| width: 640px; | |
| max-width: 100%; | |
| max-height: 86vh; | |
| background: #0d1727; | |
| border: 1px solid #344c6b; | |
| border-radius: 12px; | |
| box-shadow: 0 25px 120px #0009; | |
| display: flex; | |
| flex-direction: column; | |
| overflow: hidden | |
| } | |
| .modal.wide { | |
| width: 760px | |
| } | |
| .modal-head { | |
| padding: 23px 27px 20px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 20px; | |
| border-bottom: 1px solid #283e5a; | |
| flex-shrink: 0 | |
| } | |
| .modal-head h2 { | |
| font: calc(27px * var(--font-scale,1)) var(--serif); | |
| font-weight: 400; | |
| color: #d5e3f5; | |
| letter-spacing: -.5px | |
| } | |
| .modal-head .eyebrow { | |
| font-size: calc(8px * var(--font-scale,1)) | |
| } | |
| .modal-body { | |
| padding: 22px 28px 27px; | |
| overflow: auto | |
| } | |
| .modal-body>p { | |
| font-size: calc(12px * var(--font-scale,1)); | |
| color: #8da5c3; | |
| line-height: 1.85; | |
| margin: 12px 0 | |
| } | |
| .modal-body>h3 { | |
| font-size: calc(14px * var(--font-scale,1)); | |
| color: #c1d9ef; | |
| margin: 20px 0 10px | |
| } | |
| .help-grid { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 15px 20px; | |
| margin: 24px 0 | |
| } | |
| .help-grid>div { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 7px | |
| } | |
| .help-grid kbd { | |
| font: calc(10px * var(--font-scale,1)) var(--mono); | |
| color: #acc5e6 | |
| } | |
| .help-grid span { | |
| font-size: calc(11px * var(--font-scale,1)); | |
| color: #6984a7 | |
| } | |
| .config-switch { | |
| display: flex; | |
| gap: 8px | |
| } | |
| .config-switch button { | |
| font-size: calc(11px * var(--font-scale,1)); | |
| border: 1px solid #2d4769; | |
| border-radius: 4px; | |
| padding: 9px 12px; | |
| color: #86a6d0 | |
| } | |
| .config-switch button.active { | |
| background: #213b5b; | |
| color: #b8d4fc; | |
| border-color: #527ba9 | |
| } | |
| .config-code { | |
| font: calc(11px * var(--font-scale,1))/1.7 var(--mono); | |
| padding: 19px; | |
| color: #a5c5e6; | |
| background: #081321; | |
| border: 1px solid #223c59; | |
| border-radius: 5px; | |
| max-height: 49vh; | |
| overflow: auto; | |
| tab-size: 2 | |
| } | |
| .audit-picker { | |
| border: 1px dashed #3c688b; | |
| background: #112339; | |
| width: 100%; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| padding: 26px 15px; | |
| gap: 13px; | |
| border-radius: 7px; | |
| color: #93b8ec; | |
| margin: 22px 0 | |
| } | |
| .audit-picker:hover { | |
| background: #173049; | |
| border-color: #6797c5 | |
| } | |
| .audit-picker b { | |
| font-size: calc(13px * var(--font-scale,1)) | |
| } | |
| .audit-picker small { | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| color: #5d829e | |
| } | |
| .error-note { | |
| border: 1px solid #8b544a; | |
| background: #37201b; | |
| color: #d5ab94; | |
| padding: 14px; | |
| border-radius: 5px; | |
| font-size: calc(12px * var(--font-scale,1)); | |
| line-height: 1.7 | |
| } | |
| .audit-groups { | |
| max-height: 180px; | |
| overflow: auto; | |
| margin-top: 18px; | |
| border: 1px solid #223c55; | |
| border-radius: 5px; | |
| padding: 0 13px | |
| } | |
| .license-text { | |
| font: calc(9px * var(--font-scale,1))/1.7 var(--mono); | |
| color: #6785a5; | |
| max-height: 250px; | |
| overflow: auto; | |
| white-space: pre-wrap; | |
| background: #091320; | |
| border-radius: 4px; | |
| padding: 16px | |
| } | |
| .toast { | |
| position: fixed; | |
| bottom: 22px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| max-width: 90vw; | |
| background: #173445f5; | |
| border: 1px solid #426775; | |
| border-radius: 7px; | |
| padding: 13px 19px; | |
| color: #c5e1e7; | |
| font-size: calc(11px * var(--font-scale,1)); | |
| box-shadow: 0 10px 40px #0009; | |
| z-index: 120; | |
| text-align: center | |
| } | |
| .sheet-scrim { | |
| display: none | |
| } | |
| @media(min-width: 1650px) { | |
| .inspector { | |
| width:406px | |
| } | |
| .inspector-scroll { | |
| padding: 29px | |
| } | |
| .topbar { | |
| height: 65px | |
| } | |
| .title-strip { | |
| padding: 25px 32px | |
| } | |
| .title-strip h1 { | |
| font-size: calc(37px * var(--font-scale,1)) | |
| } | |
| .title-stats { | |
| gap: 40px | |
| } | |
| .view-toolbar { | |
| padding-left: 31px; | |
| padding-right: 31px | |
| } | |
| .scene-heading { | |
| left: 31px | |
| } | |
| .transport { | |
| padding: 23px 31px 15px | |
| } | |
| .section-head h3 { | |
| font-size: calc(12px * var(--font-scale,1)) | |
| } | |
| .fact { | |
| font-size: calc(11px * var(--font-scale,1)) | |
| } | |
| .fact>b { | |
| font-size: calc(10px * var(--font-scale,1)) | |
| } | |
| .fine { | |
| font-size: calc(11px * var(--font-scale,1)) | |
| } | |
| .note { | |
| font-size: calc(11px * var(--font-scale,1)) | |
| } | |
| .lede { | |
| font-size: calc(13px * var(--font-scale,1)) | |
| } | |
| .editorial { | |
| font-size: calc(34px * var(--font-scale,1)) | |
| } | |
| } | |
| @media(max-width: 1270px) { | |
| .topbar-center { | |
| display:none | |
| } | |
| .title-stats { | |
| display: none | |
| } | |
| .title-strip { | |
| padding: 21px | |
| } | |
| .view-toolbar { | |
| padding: 9px 13px; | |
| gap: 8px | |
| } | |
| .view-tabs button { | |
| padding: 0 9px; | |
| font-size: calc(10px * var(--font-scale,1)) | |
| } | |
| .view-tabs button small { | |
| display: none | |
| } | |
| .precision-toggle button { | |
| min-width: 77px; | |
| padding: 7px 10px | |
| } | |
| .inspector { | |
| width: 348px | |
| } | |
| .inspector-scroll { | |
| padding: 22px 20px | |
| } | |
| .story-text>div { | |
| gap: 10px | |
| } | |
| .story-phase { | |
| min-width: 85px; | |
| font-size: calc(8px * var(--font-scale,1)) | |
| } | |
| .story-text>div>b { | |
| font-size: calc(17px * var(--font-scale,1)) | |
| } | |
| .transport { | |
| padding-left: 20px; | |
| padding-right: 20px | |
| } | |
| .transport-bottom { | |
| font-size: calc(6.5px * var(--font-scale,1)) | |
| } | |
| .transport-bottom>span:last-child { | |
| display: none | |
| } | |
| .story-description { | |
| font-size: calc(9px * var(--font-scale,1)) | |
| } | |
| .scene-legend { | |
| gap: 8px; | |
| font-size: calc(7px * var(--font-scale,1)) | |
| } | |
| .scene-scale { | |
| font-size: calc(8px * var(--font-scale,1)) | |
| } | |
| .scene-scale em { | |
| font-size: calc(7px * var(--font-scale,1)) | |
| } | |
| .scene-heading { | |
| left: 20px | |
| } | |
| .scene-heading small { | |
| font-size: calc(9px * var(--font-scale,1)) | |
| } | |
| } | |
| @media(max-width: 1040px) and (min-width:901px) { | |
| .view-toolbar { | |
| flex-direction:column; | |
| align-items: stretch; | |
| gap: 7px; | |
| padding: 7px 16px | |
| } | |
| .view-tabs { | |
| justify-content: space-between | |
| } | |
| .view-tabs button { | |
| flex: 1; | |
| height: 26px | |
| } | |
| .precision-toggle button { | |
| flex: 1; | |
| flex-direction: row; | |
| justify-content: space-between; | |
| align-items: center; | |
| padding: 7px 10px | |
| } | |
| .precision-toggle button span { | |
| font-size: calc(8px * var(--font-scale,1)) | |
| } | |
| .title-strip h1 { | |
| font-size: calc(29px * var(--font-scale,1)) | |
| } | |
| .scene-legend { | |
| display: none | |
| } | |
| .story-text>div { | |
| flex-direction: column; | |
| align-items: flex-start; | |
| gap: 6px | |
| } | |
| .story-description { | |
| padding-right: 0 | |
| } | |
| .orbit-button { | |
| display: none | |
| } | |
| } | |
| @media(max-width: 900px) { | |
| .topbar { | |
| height:53px; | |
| padding: 0 17px | |
| } | |
| .brand b { | |
| font-size: calc(8px * var(--font-scale,1)); | |
| margin-left: 9px; | |
| padding-left: 11px; | |
| letter-spacing: 1.2px | |
| } | |
| .offline-badge { | |
| font-size: calc(7px * var(--font-scale,1)); | |
| letter-spacing: .5px; | |
| margin-right: 4px | |
| } | |
| .header-actions { | |
| gap: 5px | |
| } | |
| .title-strip { | |
| padding: 17px 22px | |
| } | |
| .title-strip h1 { | |
| font-size: calc(30px * var(--font-scale,1)) | |
| } | |
| .title-stats { | |
| display: none | |
| } | |
| .mobile-inspect { | |
| display: inline-flex; | |
| font-size: calc(9px * var(--font-scale,1)); | |
| padding: 8px 10px | |
| } | |
| .inspector { | |
| position: fixed; | |
| right: 0; | |
| top: 53px; | |
| bottom: 0; | |
| width: 390px; | |
| max-width: calc(100vw - 40px); | |
| z-index: 70; | |
| transform: translateX(101%); | |
| transition: transform .25s; | |
| box-shadow: -15px 0 70px #0009 | |
| } | |
| .inspector.sheet-open { | |
| transform: translateX(0) | |
| } | |
| .sheet-scrim { | |
| display: block; | |
| position: fixed; | |
| inset: 53px 0 0; | |
| background: #03081099; | |
| backdrop-filter: blur(3px); | |
| z-index: 65 | |
| } | |
| .mobile-only { | |
| display: inline-flex | |
| } | |
| .inspector-top { | |
| padding-right: 12px | |
| } | |
| .view-toolbar { | |
| padding-left: 20px; | |
| padding-right: 20px | |
| } | |
| .transport { | |
| padding: 15px 20px 10px | |
| } | |
| .story-text>div>b { | |
| font-size: calc(18px * var(--font-scale,1)) | |
| } | |
| .scene-scale { | |
| bottom: 19px | |
| } | |
| .world-label b { | |
| font-size: calc(9px * var(--font-scale,1)) | |
| } | |
| .world-label span { | |
| font-size: calc(7.5px * var(--font-scale,1)) | |
| } | |
| .layer-rail { | |
| width: 46px; | |
| padding: 9px 5px | |
| } | |
| .rail-layer i { | |
| width: 9px | |
| } | |
| .rail-layer span { | |
| font-size: calc(7px * var(--font-scale,1)) | |
| } | |
| } | |
| @media(max-width: 580px) { | |
| .brand { | |
| font-size:calc(10px * var(--font-scale,1)); | |
| gap: 9px | |
| } | |
| .brand b { | |
| display: none | |
| } | |
| .topbar { | |
| padding: 0 13px; | |
| height: 49px | |
| } | |
| .offline-badge { | |
| font-size: calc(6px * var(--font-scale,1)) | |
| } | |
| .header-actions .icon-button { | |
| width: 28px; | |
| height: 28px | |
| } | |
| .title-strip { | |
| padding: 16px 14px 14px; | |
| gap: 8px | |
| } | |
| .eyebrow { | |
| font-size: calc(6px * var(--font-scale,1)); | |
| letter-spacing: 1.25px; | |
| margin-bottom: 7px | |
| } | |
| .title-strip h1 { | |
| font-size: calc(24px * var(--font-scale,1)); | |
| letter-spacing: -.8px | |
| } | |
| .title-strip h1 .title-dot { | |
| font-size: calc(25px * var(--font-scale,1)) | |
| } | |
| .mobile-inspect { | |
| font-size: 0; | |
| padding: 7px; | |
| gap: 0; | |
| width: 29px; | |
| height: 29px | |
| } | |
| .layer-rail { | |
| width: 32px; | |
| padding: 6px 3px; | |
| gap: 2px | |
| } | |
| .rail-layer { | |
| padding: 0 2px | |
| } | |
| .rail-layer span { | |
| font-size: calc(6px * var(--font-scale,1)) | |
| } | |
| .rail-layer i { | |
| width: 5px | |
| } | |
| .rail-layer.selected i { | |
| width: 6px | |
| } | |
| .rail-caption { | |
| font-size: calc(6px * var(--font-scale,1)); | |
| padding: 3px 0 | |
| } | |
| .rail-special { | |
| font-size: calc(8px * var(--font-scale,1)); | |
| height: 20px | |
| } | |
| .rail-layers { | |
| gap: 1px | |
| } | |
| .view-toolbar { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: stretch; | |
| gap: 8px; | |
| padding: 7px 13px 10px | |
| } | |
| .view-tabs { | |
| justify-content: space-between; | |
| gap: 6px | |
| } | |
| .view-tabs button { | |
| flex: 1; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| height: 27px; | |
| padding: 0 7px | |
| } | |
| .view-tabs button small { | |
| display: inline; | |
| font-size: calc(7px * var(--font-scale,1)); | |
| margin-right: 6px | |
| } | |
| .precision-toggle button { | |
| min-width: 0; | |
| flex: 1; | |
| padding: 7px 9px; | |
| gap: 4px | |
| } | |
| .precision-toggle button b { | |
| font-size: calc(8px * var(--font-scale,1)); | |
| letter-spacing: .9px | |
| } | |
| .precision-toggle button span { | |
| font-size: calc(8px * var(--font-scale,1)) | |
| } | |
| .world { | |
| min-height: 160px | |
| } | |
| .scene-heading { | |
| left: 12px; | |
| top: 15px; | |
| gap: 7px; | |
| max-width: calc(100% - 55px) | |
| } | |
| .scene-heading b { | |
| font-size: calc(10px * var(--font-scale,1)); | |
| line-height: 1.4 | |
| } | |
| .scene-heading small { | |
| font-size: calc(8px * var(--font-scale,1)); | |
| line-height: 1.6; | |
| margin-top: 4px | |
| } | |
| .scene-index { | |
| font-size: calc(8px * var(--font-scale,1)) | |
| } | |
| .scene-tools { | |
| right: 9px; | |
| top: 13px; | |
| gap: 5px | |
| } | |
| .scene-tools .icon-button { | |
| width: 25px; | |
| height: 25px; | |
| border-radius: 5px | |
| } | |
| .scene-tools svg { | |
| width: 14px; | |
| height: 14px | |
| } | |
| .scene-tools .icon-button:last-child { | |
| display: none | |
| } | |
| .scene-legend { | |
| display: none | |
| } | |
| .scene-scale { | |
| left: 15px; | |
| bottom: 13px; | |
| font-size: calc(7px * var(--font-scale,1)); | |
| gap: 7px | |
| } | |
| .scene-scale em { | |
| font-size: calc(6px * var(--font-scale,1)) | |
| } | |
| .scale-glyph { | |
| width: 10px; | |
| height: 10px | |
| } | |
| .format-notice { | |
| font-size: calc(7px * var(--font-scale,1)); | |
| left: 12px; | |
| bottom: 49px; | |
| right: 12px; | |
| padding: 7px 8px | |
| } | |
| .scene-tip { | |
| display: none!important | |
| } | |
| .world-label { | |
| padding: 2px 3px | |
| } | |
| .world-label b { | |
| font-size: calc(7px * var(--font-scale,1)); | |
| letter-spacing: .1px | |
| } | |
| .world-label span { | |
| font-size: calc(6px * var(--font-scale,1)) | |
| } | |
| .transport { | |
| padding: 13px 13px 9px | |
| } | |
| .story-text>div { | |
| flex-direction: column; | |
| align-items: flex-start; | |
| gap: 6px | |
| } | |
| .story-phase { | |
| font-size: calc(7px * var(--font-scale,1)) | |
| } | |
| .story-text>div>b { | |
| font-size: calc(15px * var(--font-scale,1)); | |
| line-height: 1.2; | |
| max-width: 210px | |
| } | |
| .story-number { | |
| font-size: calc(15px * var(--font-scale,1)); | |
| min-width: 62px | |
| } | |
| .story-number small { | |
| font-size: calc(5px * var(--font-scale,1)); | |
| letter-spacing: .3px | |
| } | |
| .story-description { | |
| display: none | |
| } | |
| .transport-row { | |
| gap: 8px; | |
| margin-top: 14px | |
| } | |
| .play-button { | |
| width: 29px; | |
| height: 29px | |
| } | |
| .play-button svg { | |
| width: 14px | |
| } | |
| .phase-buttons button { | |
| font-size: calc(6.3px * var(--font-scale,1)); | |
| white-space: nowrap; | |
| padding-left: 0 | |
| } | |
| .speed-select { | |
| width: 41px; | |
| font-size: calc(8px * var(--font-scale,1)); | |
| padding: 6px 0 | |
| } | |
| .orbit-button { | |
| display: none | |
| } | |
| .transport-bottom { | |
| font-size: calc(5.5px * var(--font-scale,1)); | |
| letter-spacing: .35px; | |
| padding-top: 10px | |
| } | |
| .inspector { | |
| top: 49px; | |
| width: 368px; | |
| max-width: calc(100vw - 22px) | |
| } | |
| .sheet-scrim { | |
| top: 49px | |
| } | |
| .inspector-scroll { | |
| padding: 22px | |
| } | |
| .inspector-top { | |
| padding-left: 22px | |
| } | |
| .panel-tabs { | |
| gap: 22px | |
| } | |
| .panel-tabs button { | |
| font-size: calc(11px * var(--font-scale,1)) | |
| } | |
| .modal-backdrop { | |
| padding: 12px | |
| } | |
| .modal { | |
| max-height: 92vh | |
| } | |
| .modal-head { | |
| padding: 18px | |
| } | |
| .modal-head h2 { | |
| font-size: calc(23px * var(--font-scale,1)) | |
| } | |
| .modal-body { | |
| padding: 15px 18px 22px | |
| } | |
| .help-grid { | |
| gap: 18px 10px | |
| } | |
| .help-grid kbd { | |
| font-size: calc(9px * var(--font-scale,1)) | |
| } | |
| .help-grid span { | |
| font-size: calc(10px * var(--font-scale,1)) | |
| } | |
| .modal-body>p { | |
| font-size: calc(11px * var(--font-scale,1)) | |
| } | |
| .audit-picker small { | |
| font-size: calc(6.5px * var(--font-scale,1)) | |
| } | |
| .graphics-error { | |
| top: 20%; | |
| left: 5%; | |
| right: 5%; | |
| padding: 18px | |
| } | |
| .graphics-error h2 { | |
| font-size: calc(20px * var(--font-scale,1)) | |
| } | |
| .graphics-error p { | |
| font-size: calc(10px * var(--font-scale,1)) | |
| } | |
| } | |
| @media(prefers-reduced-motion:reduce) { | |
| *,*:before,*:after { | |
| transition: none!important; | |
| scroll-behavior: auto!important | |
| } | |
| } | |
| .world-label b { | |
| font-size: calc(10px * var(--font-scale,1)) | |
| } | |
| .world-label span { | |
| font-size: calc(8px * var(--font-scale,1)) | |
| } | |
| .draft-diagram { | |
| position: absolute; | |
| left: 23px; | |
| bottom: 64px; | |
| z-index: 5; | |
| width: 320px; | |
| background: #111b2aef; | |
| border: 1px solid #51415e; | |
| border-radius: 7px; | |
| padding: 13px 14px; | |
| box-shadow: 0 12px 45px #0007; | |
| backdrop-filter: blur(7px) | |
| } | |
| .draft-diagram>div:first-child { | |
| display: flex; | |
| justify-content: space-between; | |
| gap: 8px; | |
| align-items: center | |
| } | |
| .draft-diagram>div>b { | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| color: #d6a2df; | |
| letter-spacing: .6px | |
| } | |
| .draft-diagram>div>small { | |
| font: calc(6.5px * var(--font-scale,1)) var(--mono); | |
| color: #7c7790 | |
| } | |
| .draft-cells { | |
| display: flex; | |
| gap: 5px; | |
| margin-top: 13px | |
| } | |
| .draft-cell { | |
| flex: 1; | |
| text-align: center; | |
| padding: 7px 2px; | |
| background: #1e2338; | |
| border: 1px solid #393953; | |
| border-radius: 3px; | |
| transition: background .4s,border-color .4s | |
| } | |
| .draft-cell small { | |
| display: block; | |
| font: calc(7px * var(--font-scale,1)) var(--mono); | |
| color: #777e9b | |
| } | |
| .draft-cell b { | |
| display: block; | |
| font: calc(14px * var(--font-scale,1)) var(--mono); | |
| color: #bcb9d7; | |
| margin: 7px 0 | |
| } | |
| .draft-cell span { | |
| font: calc(6px * var(--font-scale,1)) var(--mono); | |
| text-transform: uppercase; | |
| letter-spacing: .2px; | |
| color: #a59fbe | |
| } | |
| .draft-cell.accepted { | |
| background: #13322f; | |
| border-color: #386858 | |
| } | |
| .draft-cell.accepted b,.draft-cell.accepted span { | |
| color: #7be0bc | |
| } | |
| .draft-cell.corrected { | |
| background: #302a24; | |
| border-color: #765e39 | |
| } | |
| .draft-cell.corrected b,.draft-cell.corrected span { | |
| color: #e4bc7e | |
| } | |
| .draft-cell.redraft { | |
| opacity: .55 | |
| } | |
| .draft-diagram p { | |
| font: calc(7px * var(--font-scale,1))/1.5 var(--mono); | |
| color: #68768e; | |
| margin-top: 11px | |
| } | |
| @media(max-width: 580px) { | |
| .world-label b { | |
| font-size:calc(7px * var(--font-scale,1)) | |
| } | |
| .world-label span { | |
| font-size: calc(6px * var(--font-scale,1)) | |
| } | |
| .draft-diagram { | |
| width: auto; | |
| left: 10px; | |
| right: 10px; | |
| bottom: 42px; | |
| padding: 9px | |
| } | |
| .draft-diagram>div>b { | |
| font-size: calc(6.5px * var(--font-scale,1)) | |
| } | |
| .draft-diagram>div>small { | |
| font-size: calc(5.5px * var(--font-scale,1)) | |
| } | |
| .draft-cells { | |
| margin-top: 7px | |
| } | |
| .draft-cell b { | |
| font-size: calc(11px * var(--font-scale,1)); | |
| margin: 5px 0 | |
| } | |
| .draft-cell span { | |
| font-size: calc(5px * var(--font-scale,1)) | |
| } | |
| .draft-diagram p { | |
| font-size: calc(5.7px * var(--font-scale,1)); | |
| margin-top: 7px | |
| } | |
| .format-notice { | |
| bottom: 44px | |
| } | |
| .draft-diagram~.format-notice { | |
| display: none | |
| } | |
| } | |
| /* Revision 2: readable tensor dissection and persistent explanations. */ | |
| .inspector { | |
| width: 410px | |
| } | |
| .inspector-scroll { | |
| padding: 22px 23px | |
| } | |
| .panel-tabs { | |
| gap: 18px | |
| } | |
| .panel-tabs button { | |
| font-size: calc(11px * var(--font-scale,1)) | |
| } | |
| .note { | |
| font-size: calc(12px * var(--font-scale,1)); | |
| line-height: 1.8 | |
| } | |
| .fact { | |
| font-size: calc(12px * var(--font-scale,1)) | |
| } | |
| .section-head h3 { | |
| font-size: calc(13px * var(--font-scale,1)) | |
| } | |
| .fine { | |
| font-size: calc(11px * var(--font-scale,1)); | |
| line-height: 1.7 | |
| } | |
| .scene-tip { | |
| max-width: 330px; | |
| width: 330px; | |
| padding: 13px 15px; | |
| pointer-events: none; | |
| z-index: 20 | |
| } | |
| .scene-tip b { | |
| font-size: calc(13px * var(--font-scale,1)); | |
| line-height: 1.5 | |
| } | |
| .scene-tip span { | |
| font-size: calc(10px * var(--font-scale,1)); | |
| line-height: 1.5 | |
| } | |
| .scene-tip p { | |
| font-size: calc(11.5px * var(--font-scale,1)); | |
| line-height: 1.65; | |
| color: #afc1d8; | |
| margin-top: 8px; | |
| padding-top: 8px; | |
| border-top: 1px solid #ffffff15 | |
| } | |
| .world-labels { | |
| z-index: 4 | |
| } | |
| .tensor-leaders { | |
| position: absolute; | |
| inset: 0; | |
| width: 100%; | |
| height: 100%; | |
| pointer-events: none; | |
| overflow: visible | |
| } | |
| .tensor-leaders path { | |
| fill: none; | |
| stroke-width: .9 | |
| } | |
| .tensor-label { | |
| position: absolute; | |
| text-align: left; | |
| white-space: nowrap; | |
| padding: 2px 5px 2px 7px; | |
| border-radius: 3px; | |
| border-left: 2px solid var(--tensor); | |
| background: #0a1421d9; | |
| pointer-events: auto; | |
| cursor: pointer; | |
| overflow: hidden; | |
| box-shadow: 0 2px 8px #03070d66; | |
| transition: background .12s | |
| } | |
| .tensor-label b { | |
| display: block; | |
| font: calc(10.5px * var(--font-scale,1)) var(--mono); | |
| font-weight: 500; | |
| color: var(--tensor); | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| line-height: calc(15px * var(--font-scale,1)) | |
| } | |
| .tensor-label span { | |
| display: block; | |
| font: calc(8.5px * var(--font-scale,1)) var(--mono); | |
| line-height: calc(12px * var(--font-scale,1)); | |
| color: #91a6bd | |
| } | |
| .tensor-label.compact { | |
| padding-top: 1px; | |
| padding-bottom: 0 | |
| } | |
| .tensor-label.compact span { | |
| display: none | |
| } | |
| .tensor-label.compact b { | |
| font-size: calc(9.5px * var(--font-scale,1)); | |
| line-height: calc(15px * var(--font-scale,1)) | |
| } | |
| .tensor-label:hover,.tensor-label.hot { | |
| background: #20354ef5; | |
| outline: 1px solid var(--tensor); | |
| border-left-width: 3px | |
| } | |
| .tensor-label.hot b { | |
| color: #f0f5ff | |
| } | |
| .tensor-label:focus-visible { | |
| outline: 2px solid #c5e2ff | |
| } | |
| .expansion-tools { | |
| position: absolute; | |
| left: 22px; | |
| right: 60px; | |
| top: 54px; | |
| z-index: 9; | |
| display: flex; | |
| align-items: center; | |
| gap: 7px; | |
| flex-wrap: wrap | |
| } | |
| .expansion-switch { | |
| display: flex; | |
| border: 1px solid #2c435c; | |
| border-radius: 5px; | |
| overflow: hidden; | |
| background: #0c1829ed | |
| } | |
| .expansion-switch button { | |
| padding: 6px 9px; | |
| color: #8ca3bd; | |
| font: calc(10px * var(--font-scale,1)) var(--mono); | |
| white-space: nowrap | |
| } | |
| .expansion-switch button.active { | |
| background: #203950; | |
| color: #c7e8ff | |
| } | |
| .expansion-tools>.tool-btn { | |
| font: calc(10px * var(--font-scale,1)) var(--mono); | |
| border: 1px solid #2c435c; | |
| border-radius: 5px; | |
| padding: 6px 9px; | |
| background: #0c1829ed; | |
| color: #8fa9c6; | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 5px | |
| } | |
| .expansion-tools>.tool-btn:hover { | |
| color: white; | |
| border-color: #7197c3 | |
| } | |
| .expansion-caption { | |
| position: absolute; | |
| bottom: 42px; | |
| left: 22px; | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| color: #718ba8; | |
| z-index: 5; | |
| pointer-events: none; | |
| max-width: 80%; | |
| line-height: 1.4 | |
| } | |
| .world.exploded .scene-scale { | |
| bottom: 14px | |
| } | |
| .world.exploded .scene-legend { | |
| display: none | |
| } | |
| .world.exploded .format-notice { | |
| display: none | |
| } | |
| .world.exploded .draft-diagram { | |
| display: none | |
| } | |
| .world.exploded .scene-heading small { | |
| display: none | |
| } | |
| .world.exploded .scene-heading { | |
| max-width: calc(100% - 65px) | |
| } | |
| .layer-story h3 { | |
| font-family: var(--serif); | |
| font-size: calc(20px * var(--font-scale,1)); | |
| font-weight: 400; | |
| line-height: 1.25; | |
| color: #dde8fa; | |
| margin: 6px 0 11px | |
| } | |
| .layer-story p { | |
| font-size: calc(12px * var(--font-scale,1)); | |
| line-height: 1.8; | |
| color: #a9b9ce; | |
| margin: 0 0 10px | |
| } | |
| .layer-story h3:not(:first-child) { | |
| margin-top: 21px | |
| } | |
| .guide-copy { | |
| font-size: calc(12px * var(--font-scale,1)); | |
| line-height: 1.8; | |
| color: #a9bbd2; | |
| margin-bottom: 15px | |
| } | |
| .ownership-row { | |
| display: flex; | |
| align-items: center; | |
| gap: 9px; | |
| padding: 9px 0; | |
| border-bottom: 1px solid #1c2a3c | |
| } | |
| .ownership-row>i { | |
| width: 7px; | |
| height: 7px; | |
| border: 1px solid var(--own); | |
| border-radius: 50%; | |
| flex-shrink: 0 | |
| } | |
| .ownership-row>i.own { | |
| background: var(--own) | |
| } | |
| .ownership-row>i.off { | |
| border-color: #3d4b5c | |
| } | |
| .ownership-row>div { | |
| flex: 1; | |
| min-width: 0 | |
| } | |
| .ownership-row b { | |
| display: block; | |
| color: #cad7e9; | |
| font-size: calc(11.5px * var(--font-scale,1)); | |
| font-weight: 500 | |
| } | |
| .ownership-row small { | |
| display: block; | |
| color: #7f95ae; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| line-height: 1.5; | |
| margin-top: 3px | |
| } | |
| .ownership-row>span { | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| white-space: nowrap; | |
| align-self: flex-start; | |
| margin-top: 3px | |
| } | |
| .tensor-walk { | |
| display: flex; | |
| align-items: center; | |
| gap: 9px; | |
| padding: 11px 0 15px; | |
| border-bottom: 1px solid #253448; | |
| margin-bottom: 17px | |
| } | |
| .tensor-walk>button { | |
| padding: 7px; | |
| border: 1px solid #2a3d55; | |
| background: #142234; | |
| border-radius: 5px; | |
| display: flex | |
| } | |
| .tensor-walk>span { | |
| flex: 1; | |
| font-size: calc(11px * var(--font-scale,1)); | |
| color: #b0c5de | |
| } | |
| .tensor-walk small { | |
| display: block; | |
| font-size: calc(9px * var(--font-scale,1)); | |
| color: #748da9; | |
| margin-top: 4px | |
| } | |
| .tensor-insight { | |
| padding: 16px; | |
| background: linear-gradient(135deg,#18283bb8,#0d1828); | |
| border: 1px solid #2c415e; | |
| border-left: 3px solid var(--tensor); | |
| border-radius: 5px; | |
| margin: 5px 0 20px; | |
| overflow-wrap: anywhere | |
| } | |
| .insight-kicker { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 6px; | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: 1px; | |
| color: var(--tensor); | |
| margin-bottom: 12px | |
| } | |
| .insight-kicker .tiny-btn { | |
| font-size: calc(9px * var(--font-scale,1)); | |
| letter-spacing: 0 | |
| } | |
| .tensor-insight h3 { | |
| font-family: var(--serif); | |
| font-size: calc(23px * var(--font-scale,1)); | |
| line-height: 1.2; | |
| font-weight: 400; | |
| color: #f2f7ff; | |
| margin-bottom: 9px | |
| } | |
| .insight-name { | |
| display: block; | |
| color: var(--tensor); | |
| font-size: calc(10px * var(--font-scale,1)); | |
| line-height: 1.6; | |
| margin-bottom: 11px | |
| } | |
| .tensor-insight>p { | |
| font-size: calc(12px * var(--font-scale,1)); | |
| line-height: 1.8; | |
| color: #c0d0e4; | |
| margin-bottom: 12px | |
| } | |
| .tensor-equation,.expert-equation { | |
| font: calc(11px * var(--font-scale,1))/1.9 var(--mono); | |
| padding: 12px 13px; | |
| border: 1px solid #2a415c; | |
| background: #090f1b88; | |
| border-radius: 4px; | |
| color: #a8d4df; | |
| margin: 12px 0; | |
| overflow-wrap: anywhere | |
| } | |
| .caution-small { | |
| font-size: calc(10px * var(--font-scale,1)); | |
| line-height: 1.7; | |
| color: #c5b597; | |
| background: #5b492a22; | |
| border-left: 2px solid #9d8253; | |
| padding: 8px 10px; | |
| margin: 10px 0 14px | |
| } | |
| .tensor-numbers { | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| padding: 12px 0; | |
| border-top: 1px solid #314054 | |
| } | |
| .tensor-numbers>div { | |
| display: flex; | |
| justify-content: space-between; | |
| gap: 9px; | |
| align-items: flex-start | |
| } | |
| .tensor-numbers small { | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| color: #7c94b2; | |
| flex-shrink: 0 | |
| } | |
| .tensor-numbers strong { | |
| font: calc(10px * var(--font-scale,1)) var(--mono); | |
| color: #c9d8ea; | |
| text-align: right | |
| } | |
| .account-details { | |
| border-top: 1px solid #2a4059; | |
| padding: 10px 0 | |
| } | |
| .account-details summary { | |
| cursor: pointer; | |
| color: var(--tensor); | |
| font-size: calc(12px * var(--font-scale,1)) | |
| } | |
| .account-details summary span { | |
| display: block; | |
| color: #7e96b2; | |
| font-size: calc(9px * var(--font-scale,1)); | |
| margin: 4px 0 0 13px | |
| } | |
| .account-details .fact { | |
| font-size: calc(10px * var(--font-scale,1)) | |
| } | |
| .insight-actions { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 7px; | |
| padding-top: 8px; | |
| border-top: 1px solid #2a4059 | |
| } | |
| .insight-actions a { | |
| font-size: calc(10px * var(--font-scale,1)); | |
| text-decoration: none; | |
| white-space: nowrap | |
| } | |
| .insight-actions .text-link { | |
| font-size: calc(10px * var(--font-scale,1)); | |
| margin: 0 | |
| } | |
| .weight-button { | |
| width: 100%; | |
| display: flex; | |
| align-items: center; | |
| gap: 9px; | |
| padding: 9px 5px; | |
| text-align: left; | |
| border: 0; | |
| border-bottom: 1px solid #1b2a3d; | |
| background: transparent | |
| } | |
| .weight-button>i { | |
| width: 5px; | |
| height: 5px; | |
| border-radius: 1px; | |
| background: var(--tensor); | |
| flex-shrink: 0 | |
| } | |
| .weight-button>span { | |
| flex: 1; | |
| min-width: 0 | |
| } | |
| .weight-button b { | |
| display: block; | |
| font: calc(10px * var(--font-scale,1))/1.55 var(--mono); | |
| color: #b7c9df; | |
| overflow-wrap: anywhere | |
| } | |
| .weight-button small { | |
| display: block; | |
| font: calc(9px * var(--font-scale,1))/1.6 var(--mono); | |
| color: #7994b5 | |
| } | |
| .weight-button em { | |
| display: block; | |
| font: calc(8px * var(--font-scale,1))/1.6 var(--mono); | |
| font-style: normal; | |
| color: #68819d | |
| } | |
| .weight-button>strong { | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| color: var(--tensor); | |
| white-space: nowrap | |
| } | |
| .weight-button:hover,.weight-button.hot { | |
| background: #20324970; | |
| border-left: 2px solid var(--tensor) | |
| } | |
| @media(max-width: 1270px) { | |
| .inspector { | |
| width:365px | |
| } | |
| .inspector-scroll { | |
| padding: 20px 19px | |
| } | |
| .panel-tabs { | |
| gap: 14px | |
| } | |
| .expansion-switch button,.expansion-tools>.tool-btn { | |
| font-size: calc(9px * var(--font-scale,1)); | |
| padding: 6px 7px | |
| } | |
| .tensor-label b { | |
| font-size: calc(10px * var(--font-scale,1)) | |
| } | |
| } | |
| @media(max-width: 1100px) { | |
| .inspector { | |
| position:fixed; | |
| right: 0; | |
| top: 53px; | |
| bottom: 0; | |
| width: 410px; | |
| max-width: calc(100vw - 35px); | |
| z-index: 70; | |
| transform: translateX(101%); | |
| transition: transform .25s; | |
| box-shadow: -15px 0 70px #0009 | |
| } | |
| .inspector.sheet-open { | |
| transform: translateX(0) | |
| } | |
| .sheet-scrim { | |
| display: block; | |
| position: fixed; | |
| inset: 53px 0 0; | |
| background: #03081099; | |
| backdrop-filter: blur(3px); | |
| z-index: 65 | |
| } | |
| .mobile-inspect { | |
| display: inline-flex; | |
| font-size: calc(10px * var(--font-scale,1)); | |
| padding: 8px 10px | |
| } | |
| .mobile-only { | |
| display: inline-flex | |
| } | |
| .inspector-top { | |
| padding-right: 12px | |
| } | |
| .panel-tabs { | |
| gap: 16px | |
| } | |
| .inspector-scroll { | |
| padding: 22px | |
| } | |
| } | |
| @media(max-width: 580px) { | |
| .tensor-label b,.tensor-label.compact b { | |
| font-size:calc(8px * var(--font-scale,1)); | |
| line-height: calc(13px * var(--font-scale,1)) | |
| } | |
| .tensor-label { | |
| padding-left: 4px | |
| } | |
| .tensor-label span { | |
| font-size: calc(7px * var(--font-scale,1)); | |
| line-height: calc(10px * var(--font-scale,1)) | |
| } | |
| .expansion-tools { | |
| left: 10px; | |
| right: 43px; | |
| top: 45px; | |
| gap: 5px | |
| } | |
| .expansion-switch button,.expansion-tools>.tool-btn { | |
| font-size: calc(8px * var(--font-scale,1)); | |
| padding: 5px 6px | |
| } | |
| .expansion-tools>.fit-text { | |
| display: none | |
| } | |
| .expansion-caption { | |
| left: 10px; | |
| bottom: 36px; | |
| font-size: calc(7px * var(--font-scale,1)); | |
| max-width: 92% | |
| } | |
| .tensor-insight h3 { | |
| font-size: calc(23px * var(--font-scale,1)) | |
| } | |
| .tensor-insight>p { | |
| font-size: calc(12px * var(--font-scale,1)) | |
| } | |
| .inspector { | |
| top: 49px | |
| } | |
| .inspector-scroll { | |
| padding: 20px | |
| } | |
| .mobile-inspect { | |
| font-size: 0; | |
| padding: 7px; | |
| width: 29px; | |
| height: 29px | |
| } | |
| .scene-tip { | |
| display: none!important | |
| } | |
| .story-description { | |
| font-size: calc(10px * var(--font-scale,1)) | |
| } | |
| .tensor-label.compact { | |
| padding-top: 0 | |
| } | |
| } | |
| .expansion-caption { | |
| bottom: 16px; | |
| left: auto; | |
| right: 22px; | |
| text-align: right; | |
| max-width: 65% | |
| } | |
| @media(max-width: 580px) { | |
| .expansion-caption { | |
| display:none | |
| } | |
| .world.exploded .scene-scale { | |
| bottom: 9px | |
| } | |
| .world.exploded .scene-scale em { | |
| display: none | |
| } | |
| } | |
| /* Revision 3: one inventory, reversible decks, uncluttered panels, scalable text. */ | |
| .workspace { | |
| position: relative | |
| } | |
| .main { | |
| min-height: 0 | |
| } | |
| .topbar { | |
| min-height: 56px; | |
| height: auto; | |
| padding-top: 10px; | |
| padding-bottom: 10px | |
| } | |
| .header-actions { | |
| gap: 7px | |
| } | |
| .font-controls { | |
| display: flex; | |
| align-items: center; | |
| border: 1px solid #2b3f59; | |
| border-radius: 6px; | |
| background: #102032; | |
| flex-shrink: 0 | |
| } | |
| .font-controls button { | |
| min-width: 29px; | |
| height: 32px; | |
| padding: 0 5px; | |
| font: 16px var(--mono); | |
| color: #c4d4e8 | |
| } | |
| .font-controls .font-reset { | |
| min-width: 44px; | |
| font: 10px var(--mono); | |
| color: #8faccc | |
| } | |
| .font-controls button:hover { | |
| background: #233951 | |
| } | |
| .font-controls button:disabled { | |
| opacity: .35; | |
| cursor: default | |
| } | |
| .architecture-controls { | |
| flex-shrink: 0; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 10px; | |
| padding: 9px 22px; | |
| border-bottom: 1px solid #25384c; | |
| background: #0b1521 | |
| } | |
| .deck-switch,.activity-switch { | |
| display: flex; | |
| gap: 2px; | |
| align-items: center | |
| } | |
| .deck-switch button,.activity-switch button { | |
| font-size: calc(11px * var(--font-scale)); | |
| min-height: 32px; | |
| padding: 6px 10px; | |
| border-radius: 5px; | |
| color: #8aa2be; | |
| border: 1px solid transparent; | |
| white-space: nowrap | |
| } | |
| .deck-switch button.active { | |
| color: #c5e1ff; | |
| background: #1b2e45; | |
| border-color: #355371 | |
| } | |
| .activity-switch button.active { | |
| color: #9ce2cf; | |
| background: #142e2d; | |
| border-color: #2a544b | |
| } | |
| .activity-switch button:last-child.active { | |
| color: #e0beef; | |
| background: #32223b; | |
| border-color: #5a4268 | |
| } | |
| .architecture-controls small { | |
| font: calc(8px * var(--font-scale)) var(--mono); | |
| margin-left: 5px; | |
| opacity: .65 | |
| } | |
| .same-weights { | |
| font: calc(9px * var(--font-scale)) var(--mono); | |
| color: #65809d; | |
| white-space: nowrap | |
| } | |
| .inspector-dock { | |
| width: var(--panel-width,390px); | |
| flex: 0 0 auto; | |
| min-width: 0; | |
| overflow: hidden; | |
| transition: width .32s cubic-bezier(.2,.8,.2,1); | |
| z-index: 15 | |
| } | |
| .inspector-dock.collapsed { | |
| width: 0 | |
| } | |
| .inspector-dock .inspector { | |
| width: var(--panel-width,390px); | |
| height: 100%; | |
| position: relative; | |
| inset: auto; | |
| transform: none; | |
| box-shadow: none; | |
| transition: transform .32s; | |
| max-width: none | |
| } | |
| .inspector-dock.collapsed .inspector { | |
| transform: translateX(100%) | |
| } | |
| .inspector-top { | |
| padding: 0 14px; | |
| gap: 8px; | |
| min-height: 54px; | |
| height: auto | |
| } | |
| .panel-tabs { | |
| gap: 14px; | |
| min-width: 0; | |
| overflow-x: auto; | |
| scrollbar-width: thin; | |
| flex: 1 | |
| } | |
| .panel-tabs button { | |
| min-height: 52px; | |
| flex-shrink: 0 | |
| } | |
| .panel-collapse { | |
| width: 31px; | |
| height: 31px | |
| } | |
| .inspector-scroll { | |
| padding: 22px; | |
| overscroll-behavior: contain; | |
| scrollbar-gutter: stable | |
| } | |
| .inspector-nav { | |
| min-height: 36px; | |
| height: auto | |
| } | |
| .inspector-nav>span { | |
| min-width: 0; | |
| white-space: normal; | |
| overflow-wrap: anywhere | |
| } | |
| .inspector-reopen { | |
| position: absolute; | |
| z-index: 30; | |
| right: 0; | |
| top: 46%; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 9px; | |
| align-items: center; | |
| padding: 15px 8px; | |
| border: 1px solid #34536d; | |
| border-right: 0; | |
| border-radius: 9px 0 0 9px; | |
| background: #102032e8; | |
| color: #b5d0e7; | |
| backdrop-filter: blur(8px); | |
| box-shadow: -4px 0 20px #0003 | |
| } | |
| .inspector-reopen span { | |
| font: 10px var(--mono); | |
| writing-mode: vertical-rl; | |
| letter-spacing: .6px | |
| } | |
| .inspector-reopen:hover { | |
| background: #27405be8; | |
| color: white | |
| } | |
| .inspector.blank-right,.inspector.blank-right * { | |
| cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M4 16H27M18 7L27 16L18 25' stroke='%23050912' stroke-width='6' fill='none' stroke-linejoin='round'/%3E%3Cpath d='M4 16H27M18 7L27 16L18 25' stroke='%239de8e1' stroke-width='3' fill='none' stroke-linejoin='round'/%3E%3C/svg%3E") 26 16,e-resize | |
| } | |
| .transport.blank-down,.transport.blank-down * { | |
| cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='32' height='32' viewBox='0 0 32 32'%3E%3Cpath d='M16 4V27M7 18L16 27L25 18' stroke='%23050912' stroke-width='6' fill='none' stroke-linejoin='round'/%3E%3Cpath d='M16 4V27M7 18L16 27L25 18' stroke='%239de8e1' stroke-width='3' fill='none' stroke-linejoin='round'/%3E%3C/svg%3E") 16 26,s-resize | |
| } | |
| .transport-shell { | |
| display: grid; | |
| grid-template-rows: 1fr; | |
| flex-shrink: 0; | |
| transition: grid-template-rows .3s ease; | |
| min-height: 0 | |
| } | |
| .transport-shell.collapsed { | |
| grid-template-rows: 0fr | |
| } | |
| .transport { | |
| position: relative; | |
| min-height: 0; | |
| overflow: hidden; | |
| padding: 16px 23px 10px; | |
| transition: padding .3s,border-width .3s | |
| } | |
| .transport-shell.collapsed .transport { | |
| padding-top: 0; | |
| padding-bottom: 0; | |
| border-top-width: 0 | |
| } | |
| .timeline-collapse { | |
| position: absolute; | |
| top: 5px; | |
| right: 8px; | |
| width: 30px; | |
| height: 25px; | |
| border: 1px solid #263d53; | |
| border-radius: 5px; | |
| background: #112132; | |
| color: #91b2d1; | |
| display: grid; | |
| place-items: center | |
| } | |
| .story-text { | |
| padding-right: 23px | |
| } | |
| .story-text>div { | |
| min-width: 0 | |
| } | |
| .story-text>div>b { | |
| white-space: normal | |
| } | |
| .story-description { | |
| padding-right: 5px; | |
| line-height: 1.6 | |
| } | |
| .transport-row { | |
| min-width: 0 | |
| } | |
| .timeline { | |
| min-width: 0 | |
| } | |
| .phase-buttons { | |
| gap: 3px | |
| } | |
| .phase-buttons button { | |
| white-space: normal; | |
| min-height: 30px; | |
| font-size: calc(10px * var(--font-scale)); | |
| padding: 3px | |
| } | |
| .transport-bottom { | |
| gap: 10px; | |
| flex-wrap: wrap | |
| } | |
| .timeline-dock { | |
| flex-shrink: 0; | |
| min-height: 30px; | |
| padding: 0 17px; | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 10px; | |
| border-top: 1px solid #203146; | |
| background: #0c1623; | |
| z-index: 16; | |
| padding-bottom: env(safe-area-inset-bottom) | |
| } | |
| .timeline-dock>button { | |
| display: flex; | |
| align-items: center; | |
| gap: 7px; | |
| color: #8dabc7; | |
| font: calc(9px * var(--font-scale)) var(--mono); | |
| min-height: 29px; | |
| white-space: nowrap | |
| } | |
| .timeline-dock.mini { | |
| min-height: 40px | |
| } | |
| .timeline-dock.mini>button { | |
| min-height: 40px | |
| } | |
| .mini-playback { | |
| display: flex; | |
| align-items: center; | |
| gap: 7px | |
| } | |
| .mini-playback>button { | |
| display: grid; | |
| place-items: center; | |
| color: #acdfce; | |
| min-width: 32px; | |
| min-height: 34px | |
| } | |
| .mini-playback select { | |
| font-size: calc(10px * var(--font-scale)); | |
| padding: 4px 7px; | |
| min-height: 30px | |
| } | |
| .weight-state { | |
| font: calc(8px * var(--font-scale)) var(--mono); | |
| color: #667f9d; | |
| text-align: right | |
| } | |
| .note,.fine,.lede,.fact,.guide-copy,.layer-story,.tensor-insight,.source-row,.help-grid { | |
| overflow-wrap: anywhere | |
| } | |
| .fact { | |
| flex-wrap: wrap | |
| } | |
| .fact>b { | |
| text-align: right; | |
| max-width: 100% | |
| } | |
| .section-head { | |
| flex-wrap: wrap; | |
| gap: 6px | |
| } | |
| .tensor-label.compact b { | |
| line-height: 1.3 | |
| } | |
| .module-title { | |
| flex-wrap: wrap | |
| } | |
| .inspector-foot { | |
| flex-wrap: wrap; | |
| gap: 6px; | |
| min-height: 34px; | |
| height: auto | |
| } | |
| .modal { | |
| max-height: calc(100dvh - 32px) | |
| } | |
| .modal-body { | |
| overscroll-behavior: contain | |
| } | |
| .world-label { | |
| max-width: calc(100% - 12px) | |
| } | |
| .title-strip h1 { | |
| white-space: normal | |
| } | |
| .story-number { | |
| flex-shrink: 0 | |
| } | |
| .scene-heading { | |
| right: 58px; | |
| max-width: none | |
| } | |
| .scene-heading b,.scene-heading small { | |
| line-height: 1.4 | |
| } | |
| .title-strip { | |
| padding-top: 18px; | |
| padding-bottom: 17px | |
| } | |
| .topbar-center { | |
| font-size: calc(10px * var(--font-scale)) | |
| } | |
| @media(max-width: 1550px) { | |
| .offline-badge { | |
| display:none | |
| } | |
| .same-weights { | |
| display: none | |
| } | |
| .title-stats { | |
| display: none | |
| } | |
| .brand b { | |
| display: none | |
| } | |
| .inspector-dock { | |
| --panel-width: clamp(350px,calc(370px * var(--font-scale)),460px) | |
| } | |
| } | |
| @media(max-width: 1100px) { | |
| .inspector-dock,.inspector-dock.collapsed { | |
| width:0; | |
| overflow: visible | |
| } | |
| .inspector-dock .inspector { | |
| position: fixed; | |
| top: 56px; | |
| right: 0; | |
| bottom: 0; | |
| height: auto; | |
| max-width: calc(100vw - 20px); | |
| width: min(var(--panel-width,410px),calc(100vw - 20px)); | |
| transform: translateX(105%); | |
| z-index: 70; | |
| box-shadow: -15px 0 70px #0009; | |
| padding-bottom: env(safe-area-inset-bottom) | |
| } | |
| .inspector-dock .inspector.sheet-open { | |
| transform: translateX(0) | |
| } | |
| .inspector-dock { | |
| z-index: 70 | |
| } | |
| .sheet-scrim { | |
| inset: 56px 0 0; | |
| backdrop-filter: none | |
| } | |
| .inspector-reopen { | |
| padding: 12px 6px | |
| } | |
| .topbar { | |
| height: 56px; | |
| min-height: 56px; | |
| padding-top: 0; | |
| padding-bottom: 0 | |
| } | |
| .topbar-center { | |
| display: none | |
| } | |
| .architecture-controls { | |
| padding-left: 18px; | |
| padding-right: 18px | |
| } | |
| .same-weights { | |
| display: block | |
| } | |
| .mobile-inspect { | |
| display: none | |
| } | |
| .topbar .brand { | |
| font-size: 11px | |
| } | |
| .header-actions { | |
| gap: 5px | |
| } | |
| .title-strip { | |
| padding: 13px 20px | |
| } | |
| .world { | |
| min-height: 160px | |
| } | |
| } | |
| @media(max-width: 760px) { | |
| .same-weights { | |
| display:none | |
| } | |
| .architecture-controls { | |
| gap: 5px; | |
| padding: 7px 10px | |
| } | |
| .architecture-controls button { | |
| padding: 5px 7px | |
| } | |
| .architecture-controls small { | |
| display: none | |
| } | |
| .scene-legend { | |
| display: none | |
| } | |
| .weight-state { | |
| display: none | |
| } | |
| .title-strip h1 { | |
| font-size: calc(26px * var(--font-scale)); | |
| line-height: 1.13 | |
| } | |
| .title-strip .eyebrow { | |
| font-size: calc(8px * var(--font-scale)); | |
| letter-spacing: 1px | |
| } | |
| .title-strip { | |
| padding: 13px 14px | |
| } | |
| .view-toolbar { | |
| min-height: 0; | |
| padding: 7px 10px; | |
| flex-wrap: wrap | |
| } | |
| .view-tabs { | |
| flex: 1; | |
| justify-content: space-between | |
| } | |
| .view-tabs button { | |
| font-size: calc(11px * var(--font-scale)); | |
| padding: 4px 8px; | |
| min-height: 32px; | |
| height: auto | |
| } | |
| .precision-toggle { | |
| margin-left: auto | |
| } | |
| .precision-toggle button { | |
| min-width: 65px; | |
| padding: 6px 8px | |
| } | |
| .precision-toggle button b { | |
| font-size: calc(9px * var(--font-scale)) | |
| } | |
| .precision-toggle button span { | |
| font-size: calc(9px * var(--font-scale)) | |
| } | |
| .story-text>div { | |
| align-items: flex-start; | |
| flex-direction: column; | |
| gap: 5px | |
| } | |
| .story-phase { | |
| font-size: calc(10px * var(--font-scale)) | |
| } | |
| .story-text>div>b { | |
| font-size: calc(18px * var(--font-scale)) | |
| } | |
| .story-number { | |
| font-size: calc(16px * var(--font-scale)) | |
| } | |
| .story-description { | |
| font-size: calc(11px * var(--font-scale)); | |
| margin-top: 8px | |
| } | |
| .transport { | |
| padding: 12px 14px 9px | |
| } | |
| .transport-bottom { | |
| display: none | |
| } | |
| .transport-row { | |
| gap: 7px | |
| } | |
| .orbit-button { | |
| display: none | |
| } | |
| .timeline-dock { | |
| padding-left: 12px; | |
| padding-right: 12px | |
| } | |
| .scene-tools .icon-button { | |
| width: 34px; | |
| height: 34px | |
| } | |
| .expansion-tools { | |
| right: 48px | |
| } | |
| .expansion-switch button,.expansion-tools>.tool-btn { | |
| min-height: 32px; | |
| font-size: calc(10px * var(--font-scale)); | |
| padding: 6px | |
| } | |
| .inspector-top { | |
| min-height: 54px | |
| } | |
| .panel-tabs { | |
| gap: 13px | |
| } | |
| .inspector-scroll { | |
| padding: 20px | |
| } | |
| .panel-tabs button { | |
| font-size: calc(11px * var(--font-scale)); | |
| min-height: 52px | |
| } | |
| .inspector-foot { | |
| padding: 9px 17px | |
| } | |
| .inspector-nav { | |
| padding: 9px 16px | |
| } | |
| .inspector-reopen { | |
| top: 43% | |
| } | |
| .inspector-reopen span { | |
| display: none | |
| } | |
| } | |
| @media(max-width: 480px) { | |
| .topbar { | |
| padding:0 10px | |
| } | |
| .topbar .brand { | |
| letter-spacing: 1px; | |
| font-size: 10px; | |
| gap: 7px | |
| } | |
| .brand-mark { | |
| display: none | |
| } | |
| .header-actions { | |
| gap: 4px | |
| } | |
| .header-actions .icon-button { | |
| width: 30px; | |
| height: 34px | |
| } | |
| .font-controls button { | |
| min-width: 27px; | |
| height: 34px | |
| } | |
| .font-controls .font-reset { | |
| min-width: 39px | |
| } | |
| .header-actions>button[aria-label="View sources"] { | |
| display: none | |
| } | |
| .layer-rail { | |
| width: 35px; | |
| padding: 6px 3px | |
| } | |
| .rail-layer span { | |
| font-size: 8px | |
| } | |
| .layer-rail .rail-special { | |
| min-height: 28px | |
| } | |
| .rail-layers { | |
| overflow-y: auto; | |
| scrollbar-width: none | |
| } | |
| .rail-layer { | |
| min-height: 23px | |
| } | |
| .rail-caption { | |
| font-size: 7px | |
| } | |
| .view-toolbar { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: stretch; | |
| gap: 6px | |
| } | |
| .view-tabs { | |
| width: 100% | |
| } | |
| .view-tabs button { | |
| flex: 1 | |
| } | |
| .precision-toggle { | |
| margin: 0 | |
| } | |
| .precision-toggle button { | |
| flex: 1; | |
| align-items: center; | |
| flex-direction: row; | |
| justify-content: space-between; | |
| padding: 7px 8px; | |
| min-width: 0 | |
| } | |
| .architecture-controls { | |
| gap: 4px; | |
| padding: 6px 7px | |
| } | |
| .deck-switch,.activity-switch { | |
| gap: 0 | |
| } | |
| .architecture-controls button { | |
| font-size: calc(10px * var(--font-scale)); | |
| padding: 6px | |
| } | |
| .scene-heading { | |
| top: 14px; | |
| left: 13px; | |
| right: 48px | |
| } | |
| .scene-heading b { | |
| font-size: calc(11px * var(--font-scale)) | |
| } | |
| .scene-heading small { | |
| font-size: calc(9px * var(--font-scale)) | |
| } | |
| .scene-tools { | |
| right: 7px; | |
| top: 12px; | |
| gap: 5px | |
| } | |
| .scene-scale { | |
| left: 12px; | |
| bottom: 12px; | |
| font-size: calc(8px * var(--font-scale)) | |
| } | |
| .scene-scale em { | |
| font-size: calc(7px * var(--font-scale)) | |
| } | |
| .expansion-tools { | |
| top: 46px; | |
| left: 10px; | |
| gap: 4px | |
| } | |
| .expansion-tools>.tool-btn { | |
| font-size: calc(9px * var(--font-scale)) | |
| } | |
| .expansion-switch button { | |
| font-size: calc(9px * var(--font-scale)) | |
| } | |
| .play-button { | |
| width: 34px; | |
| height: 34px | |
| } | |
| .speed-select { | |
| width: 48px; | |
| font-size: calc(9px * var(--font-scale)); | |
| padding: 4px | |
| } | |
| .timeline input { | |
| height: 22px | |
| } | |
| .phase-buttons button { | |
| font-size: calc(9px * var(--font-scale)); | |
| min-height: 26px | |
| } | |
| .story-number { | |
| display: none | |
| } | |
| .transport .story-description { | |
| max-height: 5.1em; | |
| overflow-y: auto; | |
| scrollbar-width: thin | |
| } | |
| .timeline-dock>button { | |
| font-size: calc(9px * var(--font-scale)) | |
| } | |
| .mini-playback { | |
| gap: 3px | |
| } | |
| .mini-playback select { | |
| max-width: 120px | |
| } | |
| .world-label b { | |
| font-size: calc(8px * var(--font-scale)) | |
| } | |
| .world-label span { | |
| font-size: calc(7px * var(--font-scale)) | |
| } | |
| } | |
| @media(max-height: 600px) and (max-width:1100px) { | |
| .title-strip { | |
| display:none | |
| } | |
| .view-toolbar { | |
| flex-direction: row; | |
| flex-wrap: nowrap; | |
| padding: 4px 10px | |
| } | |
| .view-tabs,.precision-toggle { | |
| width: auto; | |
| flex: 1 | |
| } | |
| .architecture-controls { | |
| padding: 3px 10px | |
| } | |
| .topbar { | |
| height: 46px; | |
| min-height: 46px | |
| } | |
| .inspector-dock .inspector { | |
| top: 46px | |
| } | |
| .sheet-scrim { | |
| top: 46px | |
| } | |
| .rail-layer { | |
| min-height: 12px | |
| } | |
| .transport .story-description { | |
| display: none | |
| } | |
| .world { | |
| min-height: 130px | |
| } | |
| .scene-tools { | |
| gap: 3px | |
| } | |
| .scene-tools .icon-button { | |
| height: 29px; | |
| width: 29px | |
| } | |
| .timeline-dock { | |
| min-height: 30px | |
| } | |
| .timeline-dock.mini { | |
| min-height: 34px | |
| } | |
| } | |
| @media(prefers-reduced-motion:reduce) { | |
| .inspector-dock,.inspector-dock .inspector,.transport-shell,.transport { | |
| transition: none | |
| } | |
| } | |
| .inspector-reopen { | |
| top: 60% | |
| } | |
| @media(max-width: 480px) { | |
| .inspector-reopen { | |
| top:59% | |
| } | |
| .scene-tools .icon-button:last-child { | |
| display: none | |
| } | |
| .timeline-dock.mini>button { | |
| min-height: 40px | |
| } | |
| .inspector-scroll p { | |
| font-size: calc(12px * var(--font-scale)); | |
| line-height: 1.8 | |
| } | |
| .tensor-label.compact b { | |
| font-size: calc(8px * var(--font-scale)); | |
| line-height: 1.3 | |
| } | |
| .tensor-label { | |
| padding-top: 2px; | |
| padding-bottom: 2px | |
| } | |
| .layer-rail button { | |
| touch-action: manipulation | |
| } | |
| .scene-tools { | |
| top: 12px | |
| } | |
| .transport-shell:not(.collapsed) .transport { | |
| max-height: 245px; | |
| overflow: auto | |
| } | |
| .transport-shell:not(.collapsed) { | |
| flex-shrink: 1 | |
| } | |
| } | |
| @media(max-width: 1300px) { | |
| .same-weights { | |
| display:none | |
| } | |
| } | |
| @media(max-height: 600px) { | |
| .architecture-controls small { | |
| display:none | |
| } | |
| } | |
| /* v4: the masthead lives in the existing top header; no separate title strip. */ | |
| .topbar { | |
| height: auto; | |
| min-height: 66px; | |
| padding: 9px 22px; | |
| gap: 14px; | |
| flex-wrap: wrap; | |
| align-content: center | |
| } | |
| .topbar .header-brand { | |
| flex: 0 1 auto; | |
| min-width: 0; | |
| max-width: 100%; | |
| gap: 13px; | |
| letter-spacing: 0; | |
| white-space: normal | |
| } | |
| .header-wordmark { | |
| min-width: 0 | |
| } | |
| .header-wordmark .eyebrow { | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: 1.6px; | |
| margin: 0 0 4px; | |
| color: #8da9cc; | |
| line-height: 1.3 | |
| } | |
| .header-wordmark h1 { | |
| font-family: var(--serif); | |
| font-size: calc(27px * var(--font-scale,1)); | |
| font-weight: 400; | |
| letter-spacing: -.65px; | |
| line-height: 1.06; | |
| white-space: normal; | |
| color: #eaf0fb | |
| } | |
| .header-wordmark h1 .title-dot { | |
| font-size: 1em | |
| } | |
| .header-summary { | |
| display: flex; | |
| gap: 12px; | |
| align-items: center; | |
| color: #8199b6; | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: 1px | |
| } | |
| .header-summary i { | |
| font-style: normal; | |
| color: #405b7c | |
| } | |
| .topbar .header-actions { | |
| margin-left: auto; | |
| flex: 0 0 auto; | |
| gap: 7px | |
| } | |
| .decode-path { | |
| display: flex; | |
| align-items: center; | |
| justify-content: space-between; | |
| gap: 5px; | |
| padding: 13px 9px; | |
| border: 1px solid #294057; | |
| border-radius: 7px; | |
| background: #0c1d2a; | |
| margin: 17px 0; | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| line-height: 1.55 | |
| } | |
| .decode-path span { | |
| text-align: center; | |
| white-space: nowrap | |
| } | |
| .decode-path small { | |
| display: block; | |
| font-size: calc(8px * var(--font-scale,1)); | |
| color: #8b9fb8 | |
| } | |
| .decode-path span:nth-of-type(2) { | |
| color: #81b2ff | |
| } | |
| .decode-path span:nth-of-type(3) { | |
| color: #68dfbb | |
| } | |
| .decode-path i { | |
| font-style: normal; | |
| color: #a5bbd8 | |
| } | |
| .layer-execution { | |
| margin: 13px 0; | |
| padding: 10px 12px; | |
| border-left: 2px solid #6dd5bb; | |
| background: #6dd5bb09; | |
| color: #a6b9ce; | |
| font-size: calc(11px * var(--font-scale,1)) | |
| } | |
| .layer-execution b { | |
| color: #bbf1df | |
| } | |
| .phase-loop { | |
| border: 1px solid #2c425d; | |
| border-radius: 4px; | |
| padding: 7px; | |
| color: #7e94b0; | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| flex-shrink: 0; | |
| max-width: 76px; | |
| line-height: 1.35 | |
| } | |
| .phase-loop.active { | |
| border-color: #426e67; | |
| color: #86d8c5; | |
| background: #12392f55 | |
| } | |
| @media(max-width: 1270px) { | |
| .header-summary { | |
| display:none | |
| } | |
| } | |
| @media(max-width: 1100px) { | |
| .inspector-dock .inspector { | |
| top:var(--header-height,66px) | |
| } | |
| .sheet-scrim { | |
| top: var(--header-height,66px) | |
| } | |
| .topbar { | |
| min-height: 64px; | |
| height: auto; | |
| padding: 8px 17px | |
| } | |
| } | |
| @media(max-width: 640px) { | |
| .topbar { | |
| min-height:60px; | |
| height: auto; | |
| padding: 8px 12px; | |
| column-gap: 10px; | |
| row-gap: 8px | |
| } | |
| .topbar .header-brand { | |
| gap: 8px | |
| } | |
| .header-wordmark .eyebrow { | |
| font-size: calc(6.7px * var(--font-scale,1)); | |
| letter-spacing: .65px; | |
| margin-bottom: 4px | |
| } | |
| .header-wordmark h1 { | |
| font-size: calc(20px * var(--font-scale,1)); | |
| letter-spacing: -.5px | |
| } | |
| .topbar .header-actions { | |
| gap: 4px | |
| } | |
| .topbar .brand-mark { | |
| display: none | |
| } | |
| .topbar .offline-badge { | |
| display: none | |
| } | |
| .header-actions>button[aria-label="View sources"] { | |
| display: none | |
| } | |
| .header-actions .icon-button { | |
| width: 28px; | |
| height: 32px | |
| } | |
| .font-controls button { | |
| min-width: 24px; | |
| height: 30px | |
| } | |
| .font-controls .font-reset { | |
| min-width: 36px | |
| } | |
| .phase-loop { | |
| padding: 5px; | |
| font-size: calc(8px * var(--font-scale,1)); | |
| max-width: 54px | |
| } | |
| .transport-row { | |
| gap: 6px | |
| } | |
| .decode-path { | |
| flex-wrap: wrap; | |
| row-gap: 8px | |
| } | |
| .header-wordmark h1 { | |
| white-space: normal | |
| } | |
| } | |
| @media(max-width: 360px) { | |
| .topbar .header-actions { | |
| width:100%; | |
| justify-content: flex-end | |
| } | |
| .topbar .header-brand { | |
| width: 100% | |
| } | |
| } | |
| @media(max-height: 600px) { | |
| .topbar { | |
| min-height:50px; | |
| height: auto; | |
| padding-top: 6px; | |
| padding-bottom: 6px | |
| } | |
| .header-wordmark h1 { | |
| font-size: calc(21px * var(--font-scale,1)) | |
| } | |
| .header-wordmark .eyebrow { | |
| margin-bottom: 2px | |
| } | |
| .inspector-dock .inspector { | |
| top: var(--header-height,50px) | |
| } | |
| } | |
| /* v5: view and precision controls share the masthead, not the canvas column. | |
| A single scrollable navigation row keeps all labels at their chosen text size | |
| on narrow screens. The existing header ResizeObserver positions the drawer. */ | |
| .topbar { | |
| display: grid; | |
| grid-template-columns: max-content minmax(0,1fr) max-content; | |
| grid-template-areas: "brand navigation actions"; | |
| height: auto; | |
| min-height: 66px; | |
| padding: 8px 18px; | |
| gap: 8px 14px; | |
| align-items: center; | |
| justify-content: normal | |
| } | |
| .topbar .header-brand { | |
| grid-area: brand; | |
| width: auto; | |
| max-width: 100%; | |
| min-width: 0 | |
| } | |
| .topbar .header-actions { | |
| grid-area: actions; | |
| width: auto; | |
| min-width: 0; | |
| margin-left: 0; | |
| justify-content: flex-end | |
| } | |
| .header-navigation { | |
| grid-area: navigation; | |
| display: flex; | |
| align-items: center; | |
| gap: 14px; | |
| min-width: 0; | |
| max-width: 100%; | |
| overflow-x: auto; | |
| overflow-y: hidden; | |
| padding: 4px; | |
| overscroll-behavior-x: contain; | |
| scroll-padding-inline: 5px; | |
| scrollbar-width: thin; | |
| scrollbar-color: #38536e transparent; | |
| -webkit-overflow-scrolling: touch | |
| } | |
| .header-navigation::-webkit-scrollbar { | |
| height: 3px | |
| } | |
| .header-navigation::-webkit-scrollbar-thumb { | |
| background: #38536e; | |
| border-radius: 3px | |
| } | |
| .header-navigation .view-tabs { | |
| flex: 0 0 auto; | |
| gap: 2px; | |
| justify-content: flex-start; | |
| width: auto; | |
| margin-left: auto | |
| } | |
| .header-navigation .view-tabs button { | |
| flex: none; | |
| height: 36px; | |
| min-height: 36px; | |
| padding: 0 9px; | |
| font-size: calc(11px * var(--font-scale,1)); | |
| line-height: 1.3; | |
| white-space: nowrap; | |
| touch-action: manipulation | |
| } | |
| .header-navigation .view-tabs button small { | |
| display: inline; | |
| font: calc(8px * var(--font-scale,1)) var(--mono); | |
| margin-right: 6px | |
| } | |
| .header-navigation .precision-toggle { | |
| flex: 0 0 auto; | |
| margin: 0 auto 0 0; | |
| align-self: center | |
| } | |
| .header-navigation .precision-toggle button { | |
| flex: none; | |
| min-width: 74px; | |
| flex-direction: column; | |
| align-items: flex-start; | |
| justify-content: center; | |
| gap: 3px; | |
| padding: 6px 10px; | |
| white-space: nowrap; | |
| touch-action: manipulation | |
| } | |
| .header-navigation .precision-toggle button b { | |
| display: block; | |
| font: calc(9px * var(--font-scale,1)) var(--mono); | |
| letter-spacing: .8px | |
| } | |
| .header-navigation .precision-toggle button span { | |
| display: block; | |
| font: calc(10px * var(--font-scale,1)) var(--mono); | |
| font-variant-numeric: tabular-nums | |
| } | |
| .header-navigation button:focus-visible { | |
| outline-offset: 1px | |
| } | |
| @media(max-width: 1700px) { | |
| .topbar .offline-badge { | |
| display:none | |
| } | |
| } | |
| @media(max-width: 1100px) { | |
| .topbar { | |
| grid-template-columns:minmax(0,1fr) max-content; | |
| grid-template-areas: "brand actions" "navigation navigation"; | |
| gap: 3px 12px; | |
| padding: 7px 16px 3px; | |
| min-height: 0 | |
| } | |
| .header-navigation { | |
| gap: 18px; | |
| padding: 4px 2px 5px | |
| } | |
| .header-navigation .view-tabs { | |
| margin-left: 0 | |
| } | |
| .header-navigation .precision-toggle { | |
| margin-left: auto; | |
| margin-right: 0 | |
| } | |
| } | |
| @media(max-width: 640px) { | |
| .topbar { | |
| padding:7px 10px 2px; | |
| gap: 3px 8px | |
| } | |
| .topbar .header-brand { | |
| width: auto; | |
| gap: 8px | |
| } | |
| .topbar .header-actions { | |
| width: auto | |
| } | |
| .header-navigation { | |
| gap: 12px; | |
| padding: 3px 2px 5px | |
| } | |
| .header-navigation .view-tabs button { | |
| min-height: 38px; | |
| height: 38px; | |
| padding: 0 8px | |
| } | |
| .header-navigation .view-tabs button small { | |
| margin-right: 4px | |
| } | |
| .header-navigation .precision-toggle button { | |
| min-width: 74px; | |
| padding: 5px 9px | |
| } | |
| } | |
| @media(max-height: 600px) { | |
| .topbar { | |
| padding-top:4px; | |
| padding-bottom: 2px; | |
| row-gap: 2px; | |
| min-height: 0 | |
| } | |
| .header-navigation { | |
| padding-top: 2px; | |
| padding-bottom: 3px | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="root"></div> | |
| <noscript> | |
| <div style="padding:40px;color:#d7e6ff;font-family:monospace">DeepSeek Model Atlas requires JavaScript. Everything needed is already embedded in this file. Enable JavaScript and reload.</div> | |
| </noscript> | |
| <script> | |
| "use strict"; | |
| ( () => { | |
| const Preact = ( () => { | |
| // Preact, MIT License | |
| var n, l, u, i, t, o, r = {}, f = [], e = /acit|ex(?:s|g|n|p|$)|rph|grid|ows|mnc|ntw|ine[ch]|zoo|^ord|itera/i; | |
| function c(e, n) { | |
| for (var t in n) | |
| e[t] = n[t]; | |
| return e | |
| } | |
| function s(e) { | |
| var n = e.parentNode; | |
| n && n.removeChild(e) | |
| } | |
| function a(e, n, t) { | |
| var _, l, o, r = arguments, i = {}; | |
| for (o in n) | |
| "key" == o ? _ = n[o] : "ref" == o ? l = n[o] : i[o] = n[o]; | |
| if (arguments.length > 3) | |
| for (t = [t], | |
| o = 3; o < arguments.length; o++) | |
| t.push(r[o]); | |
| if (null != t && (i.children = t), | |
| "function" == typeof e && null != e.defaultProps) | |
| for (o in e.defaultProps) | |
| void 0 === i[o] && (i[o] = e.defaultProps[o]); | |
| return v(e, i, _, l, null) | |
| } | |
| function v(e, t, _, l, o) { | |
| var r = { | |
| type: e, | |
| props: t, | |
| key: _, | |
| ref: l, | |
| __k: null, | |
| __: null, | |
| __b: 0, | |
| __e: null, | |
| __d: void 0, | |
| __c: null, | |
| __h: null, | |
| constructor: void 0, | |
| __v: null == o ? ++n.__v : o | |
| }; | |
| return null != n.vnode && n.vnode(r), | |
| r | |
| } | |
| function h() { | |
| return { | |
| current: null | |
| } | |
| } | |
| function y(e) { | |
| return e.children | |
| } | |
| function p(e, n) { | |
| this.props = e, | |
| this.context = n | |
| } | |
| function d(e, n) { | |
| if (null == n) | |
| return e.__ ? d(e.__, e.__.__k.indexOf(e) + 1) : null; | |
| for (var t; n < e.__k.length; n++) | |
| if (null != (t = e.__k[n]) && null != t.__e) | |
| return t.__e; | |
| return "function" == typeof e.type ? d(e) : null | |
| } | |
| function _(e) { | |
| var n, t; | |
| if (null != (e = e.__) && null != e.__c) { | |
| for (e.__e = e.__c.base = null, | |
| n = 0; n < e.__k.length; n++) | |
| if (null != (t = e.__k[n]) && null != t.__e) { | |
| e.__e = e.__c.base = t.__e; | |
| break | |
| } | |
| return _(e) | |
| } | |
| } | |
| function k(e) { | |
| (!e.__d && (e.__d = !0) && u.push(e) && !b.__r++ || t !== n.debounceRendering) && ((t = n.debounceRendering) || i)(b) | |
| } | |
| function b() { | |
| for (var e; b.__r = u.length; ) | |
| e = u.sort(function(e, n) { | |
| return e.__v.__b - n.__v.__b | |
| }), | |
| u = [], | |
| e.some(function(e) { | |
| var n, t, l, o, r, i; | |
| e.__d && (r = (o = (n = e).__v).__e, | |
| (i = n.__P) && (t = [], | |
| (l = c({}, o)).__v = o.__v + 1, | |
| I(i, o, l, n.__n, void 0 !== i.ownerSVGElement, null != o.__h ? [r] : null, t, null == r ? d(o) : r, o.__h), | |
| T(t, o), | |
| o.__e != r && _(o))) | |
| }) | |
| } | |
| function m(e, n, t, _, l, o, i, u, s, c) { | |
| var p, a, h, m, k, b, C, P = _ && _.__k || f, S = P.length; | |
| for (t.__k = [], | |
| p = 0; p < n.length; p++) | |
| if (null != (m = t.__k[p] = null == (m = n[p]) || "boolean" == typeof m ? null : "string" == typeof m || "number" == typeof m || "bigint" == typeof m ? v(null, m, null, null, m) : Array.isArray(m) ? v(y, { | |
| children: m | |
| }, null, null, null) : m.__b > 0 ? v(m.type, m.props, m.key, null, m.__v) : m)) { | |
| if (m.__ = t, | |
| m.__b = t.__b + 1, | |
| null === (h = P[p]) || h && m.key == h.key && m.type === h.type) | |
| P[p] = void 0; | |
| else | |
| for (a = 0; a < S; a++) { | |
| if ((h = P[a]) && m.key == h.key && m.type === h.type) { | |
| P[a] = void 0; | |
| break | |
| } | |
| h = null | |
| } | |
| I(e, m, h = h || r, l, o, i, u, s, c), | |
| k = m.__e, | |
| (a = m.ref) && h.ref != a && (C || (C = []), | |
| h.ref && C.push(h.ref, null, m), | |
| C.push(a, m.__c || k, m)), | |
| null != k ? (null == b && (b = k), | |
| "function" == typeof m.type && null != m.__k && m.__k === h.__k ? m.__d = s = g(m, s, e) : s = x(e, m, h, P, k, s), | |
| c || "option" !== t.type ? "function" == typeof t.type && (t.__d = s) : e.value = "") : s && h.__e == s && s.parentNode != e && (s = d(h)) | |
| } | |
| for (t.__e = b, | |
| p = S; p--; ) | |
| null != P[p] && ("function" == typeof t.type && null != P[p].__e && P[p].__e == t.__d && (t.__d = d(_, p + 1)), | |
| L(P[p], P[p])); | |
| if (C) | |
| for (p = 0; p < C.length; p++) | |
| z(C[p], C[++p], C[++p]) | |
| } | |
| function g(e, n, t) { | |
| var _, l; | |
| for (_ = 0; _ < e.__k.length; _++) | |
| (l = e.__k[_]) && (l.__ = e, | |
| n = "function" == typeof l.type ? g(l, n, t) : x(t, l, l, e.__k, l.__e, n)); | |
| return n | |
| } | |
| function w(e, n) { | |
| return n = n || [], | |
| null == e || "boolean" == typeof e || (Array.isArray(e) ? e.some(function(e) { | |
| w(e, n) | |
| }) : n.push(e)), | |
| n | |
| } | |
| function x(e, n, t, _, l, o) { | |
| var r, i, u; | |
| if (void 0 !== n.__d) | |
| r = n.__d, | |
| n.__d = void 0; | |
| else if (null == t || l != o || null == l.parentNode) | |
| e: if (null == o || o.parentNode !== e) | |
| e.appendChild(l), | |
| r = null; | |
| else { | |
| for (i = o, | |
| u = 0; (i = i.nextSibling) && u < _.length; u += 2) | |
| if (i == l) | |
| break e; | |
| e.insertBefore(l, o), | |
| r = o | |
| } | |
| return void 0 !== r ? r : l.nextSibling | |
| } | |
| function A(e, n, t, _, l) { | |
| var o; | |
| for (o in t) | |
| "children" === o || "key" === o || o in n || C(e, o, null, t[o], _); | |
| for (o in n) | |
| l && "function" != typeof n[o] || "children" === o || "key" === o || "value" === o || "checked" === o || t[o] === n[o] || C(e, o, n[o], t[o], _) | |
| } | |
| function P(n, t, _) { | |
| "-" === t[0] ? n.setProperty(t, _) : n[t] = null == _ ? "" : "number" != typeof _ || e.test(t) ? _ : _ + "px" | |
| } | |
| function C(e, n, t, _, l) { | |
| var o; | |
| e: if ("style" === n) | |
| if ("string" == typeof t) | |
| e.style.cssText = t; | |
| else { | |
| if ("string" == typeof _ && (e.style.cssText = _ = ""), | |
| _) | |
| for (n in _) | |
| t && n in t || P(e.style, n, ""); | |
| if (t) | |
| for (n in t) | |
| _ && t[n] === _[n] || P(e.style, n, t[n]) | |
| } | |
| else if ("o" === n[0] && "n" === n[1]) | |
| o = n !== (n = n.replace(/Capture$/, "")), | |
| n = n.toLowerCase() in e ? n.toLowerCase().slice(2) : n.slice(2), | |
| e.l || (e.l = {}), | |
| e.l[n + o] = t, | |
| t ? _ || e.addEventListener(n, o ? H : $, o) : e.removeEventListener(n, o ? H : $, o); | |
| else if ("dangerouslySetInnerHTML" !== n) { | |
| if (l) | |
| n = n.replace(/xlink[H:h]/, "h").replace(/sName$/, "s"); | |
| else if ("href" !== n && "list" !== n && "form" !== n && "tabIndex" !== n && "download" !== n && n in e) | |
| try { | |
| e[n] = null == t ? "" : t; | |
| break e | |
| } catch (e) {} | |
| "function" == typeof t || (null != t && (!1 !== t || "a" === n[0] && "r" === n[1]) ? e.setAttribute(n, t) : e.removeAttribute(n)) | |
| } | |
| } | |
| function $(e) { | |
| this.l[e.type + !1](n.event ? n.event(e) : e) | |
| } | |
| function H(e) { | |
| this.l[e.type + !0](n.event ? n.event(e) : e) | |
| } | |
| function I(e, t, _, l, o, r, i, u, s) { | |
| var f, a, d, h, v, k, g, b, C, x, P, S = t.type; | |
| if (void 0 !== t.constructor) | |
| return null; | |
| null != _.__h && (s = _.__h, | |
| u = t.__e = _.__e, | |
| t.__h = null, | |
| r = [u]), | |
| (f = n.__b) && f(t); | |
| try { | |
| e: if ("function" == typeof S) { | |
| if (b = t.props, | |
| C = (f = S.contextType) && l[f.__c], | |
| x = f ? C ? C.props.value : f.__ : l, | |
| _.__c ? g = (a = t.__c = _.__c).__ = a.__E : ("prototype" in S && S.prototype.render ? t.__c = a = new S(b,x) : (t.__c = a = new p(b,x), | |
| a.constructor = S, | |
| a.render = M), | |
| C && C.sub(a), | |
| a.props = b, | |
| a.state || (a.state = {}), | |
| a.context = x, | |
| a.__n = l, | |
| d = a.__d = !0, | |
| a.__h = []), | |
| null == a.__s && (a.__s = a.state), | |
| null != S.getDerivedStateFromProps && (a.__s == a.state && (a.__s = c({}, a.__s)), | |
| c(a.__s, S.getDerivedStateFromProps(b, a.__s))), | |
| h = a.props, | |
| v = a.state, | |
| d) | |
| null == S.getDerivedStateFromProps && null != a.componentWillMount && a.componentWillMount(), | |
| null != a.componentDidMount && a.__h.push(a.componentDidMount); | |
| else { | |
| if (null == S.getDerivedStateFromProps && b !== h && null != a.componentWillReceiveProps && a.componentWillReceiveProps(b, x), | |
| !a.__e && null != a.shouldComponentUpdate && !1 === a.shouldComponentUpdate(b, a.__s, x) || t.__v === _.__v) { | |
| a.props = b, | |
| a.state = a.__s, | |
| t.__v !== _.__v && (a.__d = !1), | |
| a.__v = t, | |
| t.__e = _.__e, | |
| t.__k = _.__k, | |
| t.__k.forEach(function(e) { | |
| e && (e.__ = t) | |
| }), | |
| a.__h.length && i.push(a); | |
| break e | |
| } | |
| null != a.componentWillUpdate && a.componentWillUpdate(b, a.__s, x), | |
| null != a.componentDidUpdate && a.__h.push(function() { | |
| a.componentDidUpdate(h, v, k) | |
| }) | |
| } | |
| a.context = x, | |
| a.props = b, | |
| a.state = a.__s, | |
| (f = n.__r) && f(t), | |
| a.__d = !1, | |
| a.__v = t, | |
| a.__P = e, | |
| f = a.render(a.props, a.state, a.context), | |
| a.state = a.__s, | |
| null != a.getChildContext && (l = c(c({}, l), a.getChildContext())), | |
| d || null == a.getSnapshotBeforeUpdate || (k = a.getSnapshotBeforeUpdate(h, v)), | |
| P = null != f && f.type === y && null == f.key ? f.props.children : f, | |
| m(e, Array.isArray(P) ? P : [P], t, _, l, o, r, i, u, s), | |
| a.base = t.__e, | |
| t.__h = null, | |
| a.__h.length && i.push(a), | |
| g && (a.__E = a.__ = null), | |
| a.__e = !1 | |
| } else | |
| null == r && t.__v === _.__v ? (t.__k = _.__k, | |
| t.__e = _.__e) : t.__e = j(_.__e, t, _, l, o, r, i, s); | |
| (f = n.diffed) && f(t) | |
| } catch (e) { | |
| t.__v = null, | |
| (s || null != r) && (t.__e = u, | |
| t.__h = !!s, | |
| r[r.indexOf(u)] = null), | |
| n.__e(e, t, _) | |
| } | |
| } | |
| function T(e, t) { | |
| n.__c && n.__c(t, e), | |
| e.some(function(t) { | |
| try { | |
| e = t.__h, | |
| t.__h = [], | |
| e.some(function(e) { | |
| e.call(t) | |
| }) | |
| } catch (e) { | |
| n.__e(e, t.__v) | |
| } | |
| }) | |
| } | |
| function j(e, n, t, _, l, o, i, u) { | |
| var c, p, a, d, h = t.props, v = n.props, y = n.type, k = 0; | |
| if ("svg" === y && (l = !0), | |
| null != o) | |
| for (; k < o.length; k++) | |
| if ((c = o[k]) && (c === e || (y ? c.localName == y : 3 == c.nodeType))) { | |
| e = c, | |
| o[k] = null; | |
| break | |
| } | |
| if (null == e) { | |
| if (null === y) | |
| return document.createTextNode(v); | |
| e = l ? document.createElementNS("http://www.w3.org/2000/svg", y) : document.createElement(y, v.is && v), | |
| o = null, | |
| u = !1 | |
| } | |
| if (null === y) | |
| h === v || u && e.data === v || (e.data = v); | |
| else { | |
| if (o = o && f.slice.call(e.childNodes), | |
| p = (h = t.props || r).dangerouslySetInnerHTML, | |
| a = v.dangerouslySetInnerHTML, | |
| !u) { | |
| if (null != o) | |
| for (h = {}, | |
| d = 0; d < e.attributes.length; d++) | |
| h[e.attributes[d].name] = e.attributes[d].value; | |
| (a || p) && (a && (p && a.__html == p.__html || a.__html === e.innerHTML) || (e.innerHTML = a && a.__html || "")) | |
| } | |
| if (A(e, v, h, l, u), | |
| a) | |
| n.__k = []; | |
| else if (k = n.props.children, | |
| m(e, Array.isArray(k) ? k : [k], n, t, _, l && "foreignObject" !== y, o, i, e.firstChild, u), | |
| null != o) | |
| for (k = o.length; k--; ) | |
| null != o[k] && s(o[k]); | |
| u || ("value" in v && void 0 !== (k = v.value) && (k !== e.value || "progress" === y && !k) && C(e, "value", k, h.value, !1), | |
| "checked" in v && void 0 !== (k = v.checked) && k !== e.checked && C(e, "checked", k, h.checked, !1)) | |
| } | |
| return e | |
| } | |
| function z(e, t, _) { | |
| try { | |
| "function" == typeof e ? e(t) : e.current = t | |
| } catch (e) { | |
| n.__e(e, _) | |
| } | |
| } | |
| function L(e, t, _) { | |
| var l, o, r; | |
| if (n.unmount && n.unmount(e), | |
| (l = e.ref) && (l.current && l.current !== e.__e || z(l, null, t)), | |
| _ || "function" == typeof e.type || (_ = null != (o = e.__e)), | |
| e.__e = e.__d = void 0, | |
| null != (l = e.__c)) { | |
| if (l.componentWillUnmount) | |
| try { | |
| l.componentWillUnmount() | |
| } catch (e) { | |
| n.__e(e, t) | |
| } | |
| l.base = l.__P = null | |
| } | |
| if (l = e.__k) | |
| for (r = 0; r < l.length; r++) | |
| l[r] && L(l[r], t, _); | |
| null != o && s(o) | |
| } | |
| function M(e, n, t) { | |
| return this.constructor(e, t) | |
| } | |
| function N(e, t, _) { | |
| var l, o, i; | |
| n.__ && n.__(e, t), | |
| o = (l = "function" == typeof _) ? null : _ && _.__k || t.__k, | |
| i = [], | |
| I(t, e = (!l && _ || t).__k = a(y, null, [e]), o || r, r, void 0 !== t.ownerSVGElement, !l && _ ? [_] : o ? null : t.firstChild ? f.slice.call(t.childNodes) : null, i, !l && _ ? _ : o ? o.__e : t.firstChild, l), | |
| T(i, e) | |
| } | |
| function O(e, n) { | |
| N(e, n, O) | |
| } | |
| function S(e, n, t) { | |
| var _, l, o, r = arguments, i = c({}, e.props); | |
| for (o in n) | |
| "key" == o ? _ = n[o] : "ref" == o ? l = n[o] : i[o] = n[o]; | |
| if (arguments.length > 3) | |
| for (t = [t], | |
| o = 3; o < arguments.length; o++) | |
| t.push(r[o]); | |
| return null != t && (i.children = t), | |
| v(e.type, i, _ || e.key, l || e.ref, null) | |
| } | |
| function q(e, n) { | |
| var t = { | |
| __c: n = "__cC" + o++, | |
| __: e, | |
| Consumer: function(e, n) { | |
| return e.children(n) | |
| }, | |
| Provider: function(e) { | |
| var t, _; | |
| return this.getChildContext || (t = [], | |
| (_ = {})[n] = this, | |
| this.getChildContext = function() { | |
| return _ | |
| } | |
| , | |
| this.shouldComponentUpdate = function(e) { | |
| this.props.value !== e.value && t.some(k) | |
| } | |
| , | |
| this.sub = function(e) { | |
| t.push(e); | |
| var n = e.componentWillUnmount; | |
| e.componentWillUnmount = function() { | |
| t.splice(t.indexOf(e), 1), | |
| n && n.call(e) | |
| } | |
| } | |
| ), | |
| e.children | |
| } | |
| }; | |
| return t.Provider.__ = t.Consumer.contextType = t | |
| } | |
| n = { | |
| __e: function(e, n) { | |
| for (var t, _, l; n = n.__; ) | |
| if ((t = n.__c) && !t.__) | |
| try { | |
| if ((_ = t.constructor) && null != _.getDerivedStateFromError && (t.setState(_.getDerivedStateFromError(e)), | |
| l = t.__d), | |
| null != t.componentDidCatch && (t.componentDidCatch(e), | |
| l = t.__d), | |
| l) | |
| return t.__E = t | |
| } catch (n) { | |
| e = n | |
| } | |
| throw e | |
| }, | |
| __v: 0 | |
| }, | |
| l = function(e) { | |
| return null != e && void 0 === e.constructor | |
| } | |
| , | |
| p.prototype.setState = function(e, n) { | |
| var t; | |
| t = null != this.__s && this.__s !== this.state ? this.__s : this.__s = c({}, this.state), | |
| "function" == typeof e && (e = e(c({}, t), this.props)), | |
| e && c(t, e), | |
| null != e && this.__v && (n && this.__h.push(n), | |
| k(this)) | |
| } | |
| , | |
| p.prototype.forceUpdate = function(e) { | |
| this.__v && (this.__e = !0, | |
| e && this.__h.push(e), | |
| k(this)) | |
| } | |
| , | |
| p.prototype.render = y, | |
| u = [], | |
| i = "function" == typeof Promise ? Promise.prototype.then.bind(Promise.resolve()) : setTimeout, | |
| b.__r = 0, | |
| o = 0; | |
| return { | |
| render: N, | |
| hydrate: O, | |
| createElement: a, | |
| h: a, | |
| Fragment: y, | |
| createRef: h, | |
| isValidElement: l, | |
| Component: p, | |
| cloneElement: S, | |
| createContext: q, | |
| toChildArray: w, | |
| options: n | |
| }; | |
| } | |
| )(); | |
| const htm = ( () => { | |
| // HTM, Apache License | |
| var n = function(t, s, r, e) { | |
| var u; | |
| s[0] = 0; | |
| for (var h = 1; h < s.length; h++) { | |
| var p = s[h++] | |
| , a = s[h] ? (s[0] |= p ? 1 : 2, | |
| r[s[h++]]) : s[++h]; | |
| 3 === p ? e[0] = a : 4 === p ? e[1] = Object.assign(e[1] || {}, a) : 5 === p ? (e[1] = e[1] || {})[s[++h]] = a : 6 === p ? e[1][s[++h]] += a + "" : p ? (u = t.apply(a, n(t, a, r, ["", null])), | |
| e.push(u), | |
| a[0] ? s[0] |= 2 : (s[h - 2] = 0, | |
| s[h] = u)) : e.push(a) | |
| } | |
| return e | |
| } | |
| , t = new Map; | |
| return function(s) { | |
| var r = t.get(this); | |
| return r || (r = new Map, | |
| t.set(this, r)), | |
| (r = n(this, r.get(s) || (r.set(s, r = function(n) { | |
| for (var t, s, r = 1, e = "", u = "", h = [0], p = function(n) { | |
| 1 === r && (n || (e = e.replace(/^\s*\n\s*|\s*\n\s*$/g, ""))) ? h.push(0, n, e) : 3 === r && (n || e) ? (h.push(3, n, e), | |
| r = 2) : 2 === r && "..." === e && n ? h.push(4, n, 0) : 2 === r && e && !n ? h.push(5, 0, !0, e) : r >= 5 && ((e || !n && 5 === r) && (h.push(r, 0, e, s), | |
| r = 6), | |
| n && (h.push(r, n, 0, s), | |
| r = 6)), | |
| e = "" | |
| }, a = 0; a < n.length; a++) { | |
| a && (1 === r && p(), | |
| p(a)); | |
| for (var l = 0; l < n[a].length; l++) | |
| t = n[a][l], | |
| 1 === r ? "<" === t ? (p(), | |
| h = [h], | |
| r = 3) : e += t : 4 === r ? "--" === e && ">" === t ? (r = 1, | |
| e = "") : e = t + e[0] : u ? t === u ? u = "" : e += t : '"' === t || "'" === t ? u = t : ">" === t ? (p(), | |
| r = 1) : r && ("=" === t ? (r = 5, | |
| s = e, | |
| e = "") : "/" === t && (r < 5 || ">" === n[a][l + 1]) ? (p(), | |
| 3 === r && (h = h[0]), | |
| r = h, | |
| (h = h[0]).push(2, 0, r), | |
| r = 0) : " " === t || "\t" === t || "\n" === t || "\r" === t ? (p(), | |
| r = 2) : e += t), | |
| 3 === r && "!--" === e && (r = 4, | |
| h = h[0]) | |
| } | |
| return p(), | |
| h | |
| }(s)), | |
| r), arguments, [])).length > 1 ? r : r[0] | |
| } | |
| } | |
| )(); | |
| const MIT_LICENSE = "MIT License\nCopyright (c) 2015-present Jason Miller\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE."; | |
| const APACHE_LICENSE = "\n Apache License\n Version 2.0, January 2004\n http://www.apache.org/licenses/\n\n TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n 1. Definitions.\n\n \"License\" shall mean the terms and conditions for use, reproduction,\n and distribution as defined by Sections 1 through 9 of this document.\n\n \"Licensor\" shall mean the copyright owner or entity authorized by\n the copyright owner that is granting the License.\n\n \"Legal Entity\" shall mean the union of the acting entity and all\n other entities that control, are controlled by, or are under common\n control with that entity. For the purposes of this definition,\n \"control\" means (i) the power, direct or indirect, to cause the\n direction or management of such entity, whether by contract or\n otherwise, or (ii) ownership of fifty percent (50%) or more of the\n outstanding shares, or (iii) beneficial ownership of such entity.\n\n \"You\" (or \"Your\") shall mean an individual or Legal Entity\n exercising permissions granted by this License.\n\n \"Source\" form shall mean the preferred form for making modifications,\n including but not limited to software source code, documentation\n source, and configuration files.\n\n \"Object\" form shall mean any form resulting from mechanical\n transformation or translation of a Source form, including but\n not limited to compiled object code, generated documentation,\n and conversions to other media types.\n\n \"Work\" shall mean the work of authorship, whether in Source or\n Object form, made available under the License, as indicated by a\n copyright notice that is included in or attached to the work\n (an example is provided in the Appendix below).\n\n \"Derivative Works\" shall mean any work, whether in Source or Object\n form, that is based on (or derived from) the Work and for which the\n editorial revisions, annotations, elaborations, or other modifications\n represent, as a whole, an original work of authorship. For the purposes\n of this License, Derivative Works shall not include works that remain\n separable from, or merely link (or bind by name) to the interfaces of,\n the Work and Derivative Works thereof.\n\n \"Contribution\" shall mean any work of authorship, including\n the original version of the Work and any modifications or additions\n to that Work or Derivative Works thereof, that is intentionally\n submitted to Licensor for inclusion in the Work by the copyright owner\n or by an individual or Legal Entity authorized to submit on behalf of\n the copyright owner. For the purposes of this definition, \"submitted\"\n means any form of electronic, verbal, or written communication sent\n to the Licensor or its representatives, including but not limited to\n communication on electronic mailing lists, source code control systems,\n and issue tracking systems that are managed by, or on behalf of, the\n Licensor for the purpose of discussing and improving the Work, but\n excluding communication that is conspicuously marked or otherwise\n designated in writing by the copyright owner as \"Not a Contribution.\"\n\n \"Contributor\" shall mean Licensor and any individual or Legal Entity\n on behalf of whom a Contribution has been received by Licensor and\n subsequently incorporated within the Work.\n\n 2. Grant of Copyright License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n copyright license to reproduce, prepare Derivative Works of,\n publicly display, publicly perform, sublicense, and distribute the\n Work and such Derivative Works in Source or Object form.\n\n 3. Grant of Patent License. Subject to the terms and conditions of\n this License, each Contributor hereby grants to You a perpetual,\n worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n (except as stated in this section) patent license to make, have made,\n use, offer to sell, sell, import, and otherwise transfer the Work,\n where such license applies only to those patent claims licensable\n by such Contributor that are necessarily infringed by their\n Contribution(s) alone or by combination of their Contribution(s)\n with the Work to which such Contribution(s) was submitted. If You\n institute patent litigation against any entity (including a\n cross-claim or counterclaim in a lawsuit) alleging that the Work\n or a Contribution incorporated within the Work constitutes direct\n or contributory patent infringement, then any patent licenses\n granted to You under this License for that Work shall terminate\n as of the date such litigation is filed.\n\n 4. Redistribution. You may reproduce and distribute copies of the\n Work or Derivative Works thereof in any medium, with or without\n modifications, and in Source or Object form, provided that You\n meet the following conditions:\n\n (a) You must give any other recipients of the Work or\n Derivative Works a copy of this License; and\n\n (b) You must cause any modified files to carry prominent notices\n stating that You changed the files; and\n\n (c) You must retain, in the Source form of any Derivative Works\n that You distribute, all copyright, patent, trademark, and\n attribution notices from the Source form of the Work,\n excluding those notices that do not pertain to any part of\n the Derivative Works; and\n\n (d) If the Work includes a \"NOTICE\" text file as part of its\n distribution, then any Derivative Works that You distribute must\n include a readable copy of the attribution notices contained\n within such NOTICE file, excluding those notices that do not\n pertain to any part of the Derivative Works, in at least one\n of the following places: within a NOTICE text file distributed\n as part of the Derivative Works; within the Source form or\n documentation, if provided along with the Derivative Works; or,\n within a display generated by the Derivative Works, if and\n wherever such third-party notices normally appear. The contents\n of the NOTICE file are for informational purposes only and\n do not modify the License. You may add Your own attribution\n notices within Derivative Works that You distribute, alongside\n or as an addendum to the NOTICE text from the Work, provided\n that such additional attribution notices cannot be construed\n as modifying the License.\n\n You may add Your own copyright statement to Your modifications and\n may provide additional or different license terms and conditions\n for use, reproduction, or distribution of Your modifications, or\n for any such Derivative Works as a whole, provided Your use,\n reproduction, and distribution of the Work otherwise complies with\n the conditions stated in this License.\n\n 5. Submission of Contributions. Unless You explicitly state otherwise,\n any Contribution intentionally submitted for inclusion in the Work\n by You to the Licensor shall be under the terms and conditions of\n this License, without any additional terms or conditions.\n Notwithstanding the above, nothing herein shall supersede or modify\n the terms of any separate license agreement you may have executed\n with Licensor regarding such Contributions.\n\n 6. Trademarks. This License does not grant permission to use the trade\n names, trademarks, service marks, or product names of the Licensor,\n except as required for reasonable and customary use in describing the\n origin of the Work and reproducing the content of the NOTICE file.\n\n 7. Disclaimer of Warranty. Unless required by applicable law or\n agreed to in writing, Licensor provides the Work (and each\n Contributor provides its Contributions) on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n implied, including, without limitation, any warranties or conditions\n of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n PARTICULAR PURPOSE. You are solely responsible for determining the\n appropriateness of using or redistributing the Work and assume any\n risks associated with Your exercise of permissions under this License.\n\n 8. Limitation of Liability. In no event and under no legal theory,\n whether in tort (including negligence), contract, or otherwise,\n unless required by applicable law (such as deliberate and grossly\n negligent acts) or agreed to in writing, shall any Contributor be\n liable to You for damages, including any direct, indirect, special,\n incidental, or consequential damages of any character arising as a\n result of this License or out of the use or inability to use the\n Work (including but not limited to damages for loss of goodwill,\n work stoppage, computer failure or malfunction, or any and all\n other commercial damages or losses), even if such Contributor\n has been advised of the possibility of such damages.\n\n 9. Accepting Warranty or Additional Liability. While redistributing\n the Work or Derivative Works thereof, You may choose to offer,\n and charge a fee for, acceptance of support, warranty, indemnity,\n or other liability obligations and/or rights consistent with this\n License. However, in accepting such obligations, You may act only\n on Your own behalf and on Your sole responsibility, not on behalf\n of any other Contributor, and only if You agree to indemnify,\n defend, and hold each Contributor harmless for any liability\n incurred by, or claims asserted against, such Contributor by reason\n of your accepting any such warranty or additional liability.\n\n END OF TERMS AND CONDITIONS\n\n APPENDIX: How to apply the Apache License to your work.\n\n To apply the Apache License to your work, attach the following\n boilerplate notice, with the fields enclosed by brackets \"[]\"\n replaced with your own identifying information. (Don't include\n the brackets!) The text should be enclosed in the appropriate\n comment syntax for the file format. We also recommend that a\n file or class name and description of purpose be included on the\n same \"printed page\" as the copyright notice for easier\n identification within third-party archives.\n\n Copyright [yyyy] [name of copyright owner]\n\n Licensed under the Apache License, Version 2.0 (the \"License\");\n you may not use this file except in compliance with the License.\n You may obtain a copy of the License at\n\n http://www.apache.org/licenses/LICENSE-2.0\n\n Unless required by applicable law or agreed to in writing, software\n distributed under the License is distributed on an \"AS IS\" BASIS,\n WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n See the License for the specific language governing permissions and\n limitations under the License.\n"; | |
| // ---------- data.js ---------- | |
| /* DeepSeek V4.1 Flash Atlas. Model facts from the supplied configs and | |
| DeepSeek's reference source; byte totals are schema-derived, not shard-audited. */ | |
| const CFG = { | |
| architectures: ['DeepseekV41ForCausalLM'], | |
| model_type: 'deepseek_v41', | |
| dtype: 'bfloat16', | |
| transformers_version: '5.6.0', | |
| bos_token_id: 0, | |
| eos_token_id: 1, | |
| pad_token_id: 2, | |
| image_token_id: 129264, | |
| quantization_config: { | |
| quant_method: 'fp8', | |
| activation_scheme: 'dynamic', | |
| weight_block_size: [32, 32], | |
| scale_fmt: 'ue8m0', | |
| expert_dtype: 'fp4' | |
| }, | |
| text_config: { | |
| model_type: 'deepseek_v41_text', | |
| vocab_size: 129280, | |
| hidden_size: 5120, | |
| moe_intermediate_size: 2304, | |
| num_hidden_layers: 40, | |
| num_attention_heads: 64, | |
| num_key_value_heads: 1, | |
| head_dim: 512, | |
| qk_rope_head_dim: 64, | |
| q_lora_rank: 1280, | |
| o_lora_rank: 1024, | |
| o_groups: 8, | |
| hidden_act: 'silu', | |
| swiglu_limit: 10, | |
| rms_norm_eps: 1e-20, | |
| attention_bias: false, | |
| attention_dropout: 0, | |
| initializer_range: .02, | |
| use_cache: true, | |
| tie_word_embeddings: false, | |
| max_position_embeddings: 1048576, | |
| rope_theta: 10000, | |
| rope_scaling: { | |
| rope_type: 'yarn', | |
| factor: 16, | |
| beta_fast: 32, | |
| beta_slow: 1, | |
| original_max_position_embeddings: 65536 | |
| }, | |
| n_routed_experts: 384, | |
| n_shared_experts: 1, | |
| num_experts_per_tok: 6, | |
| scoring_func: 'sqrtsoftplus', | |
| topk_method: 'noaux_tc', | |
| norm_topk_prob: true, | |
| routed_scaling_factor: 1.5, | |
| sliding_window: 128, | |
| compress_ratios: [0, 0, ...Array(18).fill(2), ...Array(20).fill(1), 0, 0, 0], | |
| compress_rope_theta: 160000, | |
| kv_source_layer_ids: [2, 8, 14, 20], | |
| index_source_layer_ids: [2, 8, 14, 20, 24, 28, 32, 36], | |
| index_n_heads: 32, | |
| index_head_dim: 128, | |
| index_topk: 512, | |
| candidate_source_layer_id: 20, | |
| candidate_topk_blocks: 2048, | |
| candidate_block_size: 8, | |
| hc_mult: 4, | |
| hc_sinkhorn_iters: 20, | |
| hc_eps: 1e-6, | |
| engram_layer_ids: [1, 14], | |
| engram_num_embeddings: [384006168, 384016682], | |
| engram_max_ngram_size: 4, | |
| engram_vocab_size: 16000000, | |
| engram_n_heads: 8, | |
| engram_head_dim: 256, | |
| engram_pad_token_id: 2, | |
| engram_compressed_vocab_size: 99092, | |
| num_nextn_predict_layers: 3, | |
| dspark_block_size: 5, | |
| dspark_noise_token_id: 128799, | |
| dspark_target_layer_ids: [37, 38, 39], | |
| dspark_markov_rank: 256, | |
| dspark_n_routed_experts: 128, | |
| dspark_num_experts_per_tok: 3 | |
| }, | |
| vision_config: { | |
| model_type: 'deepseek_v41_vision', | |
| num_hidden_layers: 32, | |
| hidden_size: 1024, | |
| num_attention_heads: 16, | |
| intermediate_size: 2816, | |
| patch_size: 14, | |
| rope_theta: 10000, | |
| downsample_ratio: 3, | |
| max_image_tokens: 1024, | |
| min_pixels: 295936, | |
| max_wh_ratio: null | |
| } | |
| }; | |
| const NV_CONFIG = JSON.parse(JSON.stringify(CFG)); | |
| Object.assign(NV_CONFIG.quantization_config, { | |
| config_groups: { | |
| group_0: { | |
| input_activations: { | |
| dynamic: false, | |
| group_size: 16, | |
| num_bits: 4, | |
| type: 'float' | |
| }, | |
| targets: ['Linear'], | |
| weights: { | |
| dynamic: false, | |
| group_size: 16, | |
| num_bits: 4, | |
| type: 'float' | |
| } | |
| } | |
| }, | |
| group_size: 16, | |
| ignore: ['*.attn.*', '*.ffn.shared_experts.*', 'head', 'mtp.*'], | |
| kv_cache_quant_algo: null, | |
| moe_quant_algo: 'NVFP4', | |
| producer: { | |
| name: 'modelopt', | |
| version: 'dsv4-nvfp4-experts' | |
| }, | |
| quant_algo: 'MIXED_PRECISION', | |
| quantized_layers: Object.fromEntries(Array.from({ | |
| length: 40 | |
| }, (_, i) => ['layers.' + i + '.ffn.experts', { | |
| group_size: 16, | |
| quant_algo: 'NVFP4' | |
| }])) | |
| }); | |
| const CT = CFG.text_config; | |
| const COL = { | |
| blue: '#638bff', | |
| enc: '#5ca7ff', | |
| dec: '#55d7c1', | |
| engram: '#b99bff', | |
| expert: '#76b9ff', | |
| shared: '#d6e99c', | |
| attn: '#55ddd0', | |
| router: '#f4b765', | |
| mhc: '#e3a8dc', | |
| vision: '#80ceea', | |
| head: '#c4d0ff', | |
| full: '#efbc71', | |
| reindex: '#b798ff', | |
| reuse: '#709bbd', | |
| swa: '#758090', | |
| norm: '#a7b5cc', | |
| muted: '#8390a6' | |
| }; | |
| const clamp = (x, a, b) => Math.max(a, Math.min(b, x)); | |
| const lerp = (a, b, t) => a + (b - a) * t; | |
| const smooth = x => x * x * (3 - 2 * x); | |
| const num = x => Math.round(x).toLocaleString('en-US'); | |
| function fmtP(p) { | |
| return p >= 1e12 ? (p / 1e12).toFixed(3) + 'T' : p >= 1e9 ? (p / 1e9).toFixed(2) + 'B' : p >= 1e6 ? (p / 1e6).toFixed(2) + 'M' : p >= 1e3 ? (p / 1e3).toFixed(1) + 'K' : num(p); | |
| } | |
| function bytes(n, binary=false) { | |
| let b = binary ? 1024 : 1000 | |
| , u = binary ? ['B', 'KiB', 'MiB', 'GiB', 'TiB'] : ['B', 'KB', 'MB', 'GB', 'TB'] | |
| , k = 0; | |
| while (n >= b && k < 4) { | |
| n /= b; | |
| k++; | |
| } | |
| return (k === 0 ? num(n) : n.toFixed(n >= 100 ? 1 : 2)) + ' ' + u[k]; | |
| } | |
| const SOURCE_BASE = 'https://huggingface.co/deepseek-ai/DeepSeek-V4.1-Flash'; | |
| const SOURCES = [['Training schematic: autograd', 'https://docs.pytorch.org/tutorials/beginner/basics/autogradqs_tutorial.html', 'General forward / loss / backward / update explanation only; not the DeepSeek training implementation.'], ['Model card', SOURCE_BASE, 'Architecture, 552B backbone / 196B Engram, 8B / 16B active, 890 B/token, training and reported benchmarks.'], ['Released config', SOURCE_BASE + '/blob/main/config.json', 'The full user-provided config is embedded; layer IDs are zero-based.'], ['Reference model.py', SOURCE_BASE + '/blob/main/inference/model.py', 'Tensor shapes, mixed storage, CSA2 sharing, Engram and DSpark. Tensor names and operations rechecked against the reference for this revision.'], ['Weight conversion', SOURCE_BASE + '/blob/main/inference/convert.py', 'Packed I8 expert tensors contain two FP4 values per byte. wo_a is FP8 on disk, dequantized to BF16 by the converter.'], ['Vision implementation', SOURCE_BASE + '/blob/main/inference/vision.py', '32-layer ViT, bias-bearing QKV/O, gated MLP and 9,216-channel aligner input.'], ['Technical report', SOURCE_BASE + '/blob/main/DeepSeek_V41_Tech_Report.pdf', 'Primary report supplied by the user. The PDF download could not be retrieved in this build; architecture details were cross-checked against the model card and code, not an inspected PDF.'], ['Repository files', SOURCE_BASE + '/tree/main', 'The retrieved upstream listing reports about 510 GB. That rounded repository label is not an exact tensor-byte audit.']]; | |
| const MODE_INFO = { | |
| native: { | |
| label: 'Native FP4', | |
| short: 'NATIVE', | |
| color: COL.enc, | |
| note: 'Upstream mixed checkpoint' | |
| }, | |
| nvfp4: { | |
| label: 'NVFP4 recipe', | |
| short: 'NVFP4', | |
| color: COL.dec, | |
| note: 'Supplied routed-expert conversion' | |
| }, | |
| bf16: { | |
| label: 'BF16 reference', | |
| short: 'BF16', | |
| color: COL.engram, | |
| note: 'Hypothetical unquantized reference' | |
| } | |
| }; | |
| function modeFor(i) { | |
| return i < 2 ? 'swa' : CT.kv_source_layer_ids.includes(i) ? 'full' : CT.index_source_layer_ids.includes(i) ? 'reindex' : 'reuse'; | |
| } | |
| function ownerFor(i) { | |
| if (i < 2) | |
| return null; | |
| return [...CT.kv_source_layer_ids].reverse().find(x => x <= i); | |
| } | |
| function indexOwnerFor(i) { | |
| if (i < 2) | |
| return null; | |
| return [...CT.index_source_layer_ids].reverse().find(x => x <= i); | |
| } | |
| function W(name, shape, format, cat, count=1, note='') { | |
| const p = shape.reduce( (a, b) => a * b, 1) * count; | |
| return { | |
| name, | |
| shape, | |
| format, | |
| cat, | |
| count, | |
| p, | |
| note | |
| }; | |
| } | |
| function wBytes(w, mode='native') { | |
| const p = w.p | |
| , rows = w.shape[0] | |
| , cols = w.shape.length > 1 ? w.shape.slice(1).reduce( (a, b) => a * b, 1) : 1; | |
| if (mode === 'bf16') | |
| return p * 2; | |
| if (mode === 'nvfp4' && w.cat === 'expert' && w.convert !== false) | |
| return p / 2 + rows * Math.ceil(cols / 16) * w.count + 8 * w.count; | |
| if (w.format === 'fp4') | |
| return p / 2 + rows * Math.ceil(cols / 32) * w.count; | |
| if (w.format === 'fp8row') | |
| return p + rows * Math.ceil(cols / 32) * w.count; | |
| if (w.format === 'fp8') | |
| return p + Math.ceil(rows / 32) * Math.ceil(cols / 32) * w.count; | |
| return p * (w.format === 'f32' ? 4 : w.format === 'i64' ? 8 : 2); | |
| } | |
| function wFormat(w, mode) { | |
| if (mode === 'bf16') | |
| return 'BF16 ref.'; | |
| if (mode === 'nvfp4' && w.cat === 'expert' && w.convert !== false) | |
| return 'NVFP4 / 16'; | |
| return { | |
| fp4: 'FP4 / 32', | |
| fp8: 'FP8 / 32\u00d732', | |
| fp8row: 'FP8 / 32', | |
| bf16: 'BF16', | |
| f32: 'F32', | |
| i64: 'I64' | |
| }[w.format]; | |
| } | |
| const sumP = ws => ws.reduce( (a, w) => a + w.p, 0); | |
| const sumB = (ws, m) => ws.reduce( (a, w) => a + wBytes(w, m), 0); | |
| function attentionWeights(i) { | |
| const a = [W('attn.wq_a.weight', [1280, 5120], 'fp8', 'attn', 1, 'Low-rank query input: 5,120 to 1,280.'), W('attn.q_norm.weight', [1280], 'bf16', 'norm'), W('attn.wq_b.weight', [32768, 1280], 'fp8', 'attn', 1, '1,280 to 64 query heads of 512 channels.'), W('attn.wkv.weight', [512, 5120], 'fp8', 'attn', 1, 'Layer-local sliding-window KV projection, even in Reuse mode.'), W('attn.kv_norm.weight', [512], 'bf16', 'norm'), W('attn.wo_a.weight', [8192, 4096], 'fp8', 'attn', 1, 'Eight groups, each 4,096 to 1,024. Stored FP8; the reference converter expands this tensor to BF16. This atlas measures disk payload, not converted runtime tensors.'), W('attn.wo_b.weight', [5120, 8192], 'fp8', 'attn'), W('attn.attn_sink', [64], 'f32', 'attn')]; | |
| if (CT.kv_source_layer_ids.includes(i)) { | |
| a.push(W('attn.compressor.wkv.weight', [512, 5120], 'bf16', 'attn', 1, 'Checkpoint dtype modeled as BF16. Ratio-2 pooling uses F32 arithmetic in the reference runtime. Exact stored dtype requires the shard header.')); | |
| if (i < 20) | |
| a.push(W('attn.compressor.wgate.weight', [512, 5120], 'bf16', 'attn')); | |
| a.push(W('attn.compressor.norm.weight', [512], 'bf16', 'norm')); | |
| } | |
| if (CT.index_source_layer_ids.includes(i)) { | |
| a.push(W('attn.indexer.wq_b.weight', [4096, 1280], 'fp8', 'attn'), W('attn.indexer.weights_proj.weight', [32, 5120], 'bf16', 'attn')); | |
| if (CT.kv_source_layer_ids.includes(i)) | |
| a.push(W('attn.indexer.wk.weight', [128, 512], 'bf16', 'attn'), W('attn.indexer.k_norm.weight', [128], 'bf16', 'norm')); | |
| } | |
| return a; | |
| } | |
| function layerWeights(i, isDraft=false) { | |
| const n = isDraft ? 128 : 384 | |
| , a = attentionWeights(i); | |
| a.push(W('ffn.gate.weight', [n, 5120], 'bf16', 'router', 1, 'sqrt(softplus) scoring. Correction biases affect selection, not mixture weights. Normalize the winning scores, then multiply by 1.5.'), W('ffn.gate.bias', [n], 'f32', 'router'), W('ffn.gate.bias_vl', [n], 'f32', 'router', 1, 'Separate routing correction for image-span tokens.')); | |
| for (const [name,shape] of [['w1', [2304, 5120]], ['w2', [5120, 2304]], ['w3', [2304, 5120]]]) { | |
| let e = W('ffn.experts.' + name + '.weight', shape, 'fp4', 'expert', n, 'Three SwiGLU matrices per expert: gate, down, up. Packed FP4 in the upstream release; NOT an FP8-to-FP4 shrink.'); | |
| e.convert = !isDraft; | |
| a.push(e); | |
| a.push(W('ffn.shared_experts.' + name + '.weight', shape, 'fp8', 'shared', 1, 'Always-on shared expert, same intermediate width as a routed expert. Not converted by the supplied NVFP4 recipe.')); | |
| } | |
| a.push(W('attn_norm.weight', [5120], 'bf16', 'norm'), W('ffn_norm.weight', [5120], 'bf16', 'norm')); | |
| for (const s of ['attn', 'ffn']) | |
| a.push(W('hc_' + s + '_fn', [24, 20480], 'f32', 'mhc', 1, 'Single-pass mHC: four residual streams; 24 mixing coefficients from a 20,480-dimensional concatenation.'), W('hc_' + s + '_base', [24], 'f32', 'mhc'), W('hc_' + s + '_scale', [3], 'f32', 'mhc')); | |
| if (isDraft && i === 40) | |
| a.push(W('main_proj.weight', [5120, 15360], 'fp8', 'attn', 1, 'Concatenates features from target layers 37, 38, 39.'), W('main_norm.weight', [5120], 'bf16', 'norm')); | |
| if (isDraft && i === 42) | |
| a.push(W('norm.weight', [5120], 'bf16', 'norm'), W('markov_head.embed.weight', [129280, 256], 'bf16', 'head'), W('markov_head.head.weight', [129280, 256], 'bf16', 'head'), W('confidence_head.proj.weight', [1, 5376], 'bf16', 'head')); | |
| return a; | |
| } | |
| const LAYERS = Array.from({ | |
| length: 40 | |
| }, (_, i) => ({ | |
| id: 'L' + i, | |
| index: i, | |
| label: 'Layer ' + String(i).padStart(2, '0'), | |
| part: i < 20 ? 'encoder' : 'decoder', | |
| mode: modeFor(i), | |
| owner: ownerFor(i), | |
| indexOwner: indexOwnerFor(i), | |
| ratio: CT.compress_ratios[i], | |
| ws: layerWeights(i) | |
| })); | |
| const ENGRAM = CT.engram_layer_ids.map( (i, k) => ({ | |
| id: 'E' + i, | |
| index: i, | |
| label: 'Engram @ L' + String(i).padStart(2, '0'), | |
| ws: [W('layers.' + i + '.engram.embed.weight', [CT.engram_num_embeddings[k], 256], 'fp8row', 'engram', 1, 'FP8 table with one E8M0 scale per 32 values. The hash-head count is already reflected in the total rows; multiplying by eight again would overcount.'), W('layers.' + i + '.engram.wkv.weight', [25600, 6144], 'fp8', 'engram', 1, '24 lookup vectors of 256 channels project to four keys plus one value at hidden width 5,120.'), W('layers.' + i + '.engram.q_weight', [4, 5120], 'bf16', 'norm'), W('layers.' + i + '.engram.k_weight', [4, 5120], 'bf16', 'norm')] | |
| })); | |
| const DRAFT = Array.from({ | |
| length: 3 | |
| }, (_, i) => ({ | |
| id: 'D' + i, | |
| index: i + 40, | |
| label: 'DSpark stage ' + (i + 1), | |
| ws: layerWeights(i + 40, true) | |
| })); | |
| const EMBED = { | |
| id: 'embed', | |
| label: 'Text embedding', | |
| ws: [W('embed.weight', [129280, 5120], 'bf16', 'head', 1, '129,280 vocabulary entries, each 5,120-dimensional. Untied from the main output head.')] | |
| }; | |
| const HEAD = { | |
| id: 'head', | |
| label: 'Output head', | |
| ws: [W('norm.weight', [5120], 'bf16', 'norm'), W('head.weight', [129280, 5120], 'bf16', 'head', 1, 'Untied output vocabulary projection. BF16 on disk even though the reference runtime stores this head in F32.')] | |
| }; | |
| function visionLayerWeights(i) { | |
| return [W('vision.blocks.' + i + '.norm1.weight', [1024], 'f32', 'norm'), W('vision.blocks.' + i + '.norm2.weight', [1024], 'f32', 'norm'), W('vision.blocks.' + i + '.attn.wqkv.weight', [3072, 1024], 'bf16', 'vision'), W('vision.blocks.' + i + '.attn.wqkv.bias', [3072], 'bf16', 'vision'), W('vision.blocks.' + i + '.attn.wo.weight', [1024, 1024], 'bf16', 'vision'), W('vision.blocks.' + i + '.attn.wo.bias', [1024], 'bf16', 'vision'), W('vision.blocks.' + i + '.mlp.w1.weight', [5632, 1024], 'bf16', 'vision'), W('vision.blocks.' + i + '.mlp.w2.weight', [1024, 2816], 'bf16', 'vision')]; | |
| } | |
| const VISION = { | |
| id: 'vision', | |
| label: 'DeepSeek-ViT', | |
| ws: [W('vision.patch_embed.proj.weight', [1024, 588], 'bf16', 'vision'), W('vision.patch_embed.proj.bias', [1024], 'bf16', 'vision'), ...Array.from({ | |
| length: 32 | |
| }, (_, i) => visionLayerWeights(i)).flat(), W('vision.norm.weight', [1024], 'f32', 'norm')] | |
| }; | |
| const ALIGNER = { | |
| id: 'aligner', | |
| label: 'Vision projector', | |
| ws: [W('aligner.w1.weight', [5120, 9216], 'bf16', 'vision'), W('aligner.w1.bias', [5120], 'bf16', 'vision'), W('aligner.w2.weight', [5120, 5120], 'bf16', 'vision'), W('aligner.w2.bias', [5120], 'bf16', 'vision'), W('image_start', [5120], 'bf16', 'vision'), W('image_end', [5120], 'bf16', 'vision'), W('image_newline', [5120], 'bf16', 'vision')] | |
| }; | |
| const MODULES = [EMBED, ...LAYERS, HEAD, ...ENGRAM, ...DRAFT, VISION, ALIGNER]; | |
| const ALL_W = MODULES.flatMap(m => m.ws); | |
| const TOTALS = Object.fromEntries(['native', 'nvfp4', 'bf16'].map(m => [m, sumB(ALL_W, m)])); | |
| const TOTAL_P = sumP(ALL_W); | |
| const MOE_EXPERT_P = 3 * 5120 * 2304; | |
| const MOE_TOTAL_P = MOE_EXPERT_P * 384; | |
| const NV_DELTA = TOTALS.nvfp4 - TOTALS.native; | |
| const CATEGORIES = [['routed', 'Routed experts', COL.expert, LAYERS.flatMap(l => l.ws.filter(w => w.cat === 'expert'))], ['engram', 'Engram memory', COL.engram, ENGRAM.flatMap(x => x.ws)], ['attention', 'Attention + indexers', COL.attn, LAYERS.flatMap(l => l.ws.filter(w => w.cat === 'attn'))], ['shared', 'Shared experts', COL.shared, LAYERS.flatMap(l => l.ws.filter(w => w.cat === 'shared'))], ['other', 'Routers, norms, mHC', COL.mhc, LAYERS.flatMap(l => l.ws.filter(w => !['expert', 'attn', 'shared'].includes(w.cat)))], ['vocab', 'Embedding + head', COL.head, [...EMBED.ws, ...HEAD.ws]], ['dspark', 'DSpark', COL.router, DRAFT.flatMap(x => x.ws)], ['vision', 'Vision + projector', COL.vision, [...VISION.ws, ...ALIGNER.ws]]]; | |
| const EXP = { | |
| overview: { | |
| title: 'A different kind of Transformer.', | |
| body: 'Twenty causal encoder layers build the global memory. Twenty decoder layers read it. The asymmetric path targets input-heavy agent workloads: 8B active parameters per input token and 16B per output token, as reported by DeepSeek. Engram and the three DSpark stages are separate from the 552B backbone figure.' | |
| }, | |
| full: { | |
| title: 'Full does not mean dense attention.', | |
| body: 'This layer owns compressed global KV, creates the indexer keys and computes a fresh Top-512 selection. Every layer still has its own query projection and a local 128-token sliding window. Full refers to which cache/index operations are computed, not to attending every global position.' | |
| }, | |
| reindex: { | |
| title: 'New questions. The same memory.', | |
| body: 'The decoder reuses L20\'s global KV and indexer keys, but this layer scores with its own indexer queries. It selects Top-512 positions from the candidate pool created at L20. No new global KV bank is stored here.' | |
| }, | |
| reuse: { | |
| title: 'Reuse the memory and the lookup.', | |
| body: 'This layer reads the latest Full layer\'s main KV and the most recent indexing layer\'s Top-512 positions. It has no fresh sparse indexer. Its main queries, output projection, MoE and local sliding-window KV remain layer-specific.' | |
| }, | |
| swa: { | |
| title: 'Local attention only.', | |
| body: 'Layers 0 and 1 have compression ratio zero. They attend within a 128-token causal sliding window and do not own global KV or a global sparse indexer. They still contain the full 384-routed-expert MoE; L1 also receives Engram memory.' | |
| }, | |
| engram: { | |
| title: 'Memory is not more per-token compute.', | |
| body: 'Two token-addressed tables at layers 1 and 14 store 196.614B table values. N-grams of lengths 2, 3 and 4, with eight hash heads each, fetch 24 rows per module. The lookup is then gated into the residual stream. Image tokens are masked out of Engram in the reference code. The supplied NVFP4 conversion leaves these tables in FP8.' | |
| }, | |
| vision: { | |
| title: 'Images join the token stream.', | |
| body: 'The 32-layer, width-1,024 ViT uses 16 attention heads and 14-pixel patches. A 3 by 3 pixel-unshuffle concatenates nine patch features into 9,216 channels; two MLP layers map them into width 5,120. The config caps visual embeddings at 1,024 per image. The image animation is illustrative, not an inference run.' | |
| }, | |
| dspark: { | |
| title: 'Draft together. Verify with the backbone.', | |
| body: 'Three SWA-only draft stages use 128 routed experts with top-3 selection, plus a shared expert. Features come from target layers 37, 38 and 39. Block size is five; the last stage has a rank-256 Markov head and a confidence head. Confidence and acceptance shown here are synthetic examples, never benchmark measurements. The supplied recipe ignores mtp.*; ignored tensors retain their original mixed dtypes, not necessarily BF16.' | |
| }, | |
| mhc: { | |
| title: 'Four residual streams, one pass.', | |
| body: 'Single-Pass mHC carries four 5,120-dimensional residual copies. Its input/output/residual mixing coefficients are computed for the next sublayer; Sinkhorn normalization uses 20 iterations. The four light trails depict those streams, not four copies of the weight tensors.' | |
| }, | |
| native: { | |
| title: 'The original is already four-bit.', | |
| body: 'The upstream quant_method says fp8, but expert_dtype says fp4. Expert payload is two E2M1 values per byte plus one E8M0 scale per 32 values: 0.53125 bytes per logical parameter. Dense linears largely use FP8 with 32 by 32 scales. Engram uses row-wise FP8 with 32-value scale groups.' | |
| }, | |
| nvfp4: { | |
| title: 'A format conversion, not a 2x shrink.', | |
| body: 'Your explicit layer map converts only backbone routed experts. NVFP4 uses one E4M3 scale per 16 values, so modeled expert storage is 0.5625 bytes per parameter, plus small tensor-level scales. That is 18/17, or 5.88%, larger than the native expert layout before auxiliary overhead. Attention, shared experts, head and mtp.* are not re-quantized. Engram stays FP8. This is not the separate LibertAI variant that quantizes Engram.' | |
| }, | |
| cache: { | |
| title: '890 bytes, not 890 per layer.', | |
| body: 'The card reports 890 bytes per original token for the entire global cache. Three encoder owners compress 2:1; the decoder owner keeps 1:1 positions. Main KV uses 512 FP4 channels plus 32 scale bytes = 288 bytes per stored position. A 128-channel FP4 index key with four scale bytes adds 68. (3/2 + 1) times (288 + 68) = 890. This reconstruction excludes bounded SWA state, working buffers, allocator alignment and weights.' | |
| } | |
| }; | |
| const BENCH = [['GPQA Diamond', 'Reasoning', [93.4, 94.1, 92.9, 88.1, 92.4, 89.9, 90.9]], ['Terminal-Bench 2.1', 'Agentic', [89.1, 88.8, 88.3, 88.2, 87.9, 82.7, 90.6]], ['Terminal-Bench 3.0', 'Agentic', [43.3, 34.4, 17.7, 28.3, 11.8, 7.6, 30]], ['Terminal-Bench 4.0', 'Agentic', [51.8, 39.9, 12.6, 37.9, 12.4, 7, 31.2]], ['DeepSWE v1.1', 'Agentic', [74, 73, 67.5, 66.9, 62.7, 54.4, 74.2]], ['ProgramBench', 'Agentic', [37, 23, 17.5, 19, 15.5, null, 20.3]], ['NL2Repo-Bench', 'Agentic', [75.3, 56.8, 58, 58, 61.5, 54.2, 64]], ['CyberGym', 'Agentic', [null, 84.5, 80, 84.5, 83.3, 76.7, 88.1]], ['SEC-Bench Pro', 'Agentic', [null, 74.3, null, null, 56.4, 30.9, 62.8]], ['ExploitGym', 'Agentic', [22.1, 33.7, null, 15, 5.4, 1.8, 15.3]], ['HLE with tools', 'Agentic', [63.6, null, 59.8, 62.5, 60, 51.5, 63.9]], ['AutomationBench', 'Agentic', [50.3, 45.8, 46.7, 48.8, 43.2, 37.7, 54.8]], ['Agent\'s Last Exam', 'Agentic', [28.6, 26.7, 27.6, 28.5, 25.7, 25.2, 31.8]], ['Chartography with tools', 'Visual', [84, 79.9, 68.1, null, null, null, 78.9]], ['BabyVision with tools', 'Visual', [94.1, 88.9, 85.7, null, null, null, 89.6]], ['ZeroBench-main (Pass@5)', 'Visual', [52, 53, 41, null, null, null, 49]]]; | |
| const BENCH_MODELS = ['Opus-5.0', 'GPT-5.6 Sol', 'K3', 'GLM-5.3', 'DS V4 Pro', 'DS V4 Flash', 'DS V4.1 Flash']; | |
| function pickExperts(seed, n=384, k=6) { | |
| let x = (seed + 1) * 2654435761 >>> 0; | |
| const s = new Set; | |
| while (s.size < k) { | |
| x = (Math.imul(x, 1664525) + 1013904223) >>> 0; | |
| s.add(x % n); | |
| } | |
| return [...s].sort( (a, b) => a - b); | |
| } | |
| const PHASES = [{ | |
| name: 'Prefill', | |
| label: 'Prefill*', | |
| from: 0, | |
| to: 6, | |
| active: '8B', | |
| color: COL.enc, | |
| caption: 'Optimized prefill: prepare the prompt cache.', | |
| desc: 'Optimized CED schedule from the model card: run the full prompt through L00-L19 and project decoder global KV from its output. The minimal reference code instead loops over all 40 layers.' | |
| }, { | |
| name: 'SWA replay', | |
| from: 6, | |
| to: 9, | |
| active: '128 tokens', | |
| color: COL.full, | |
| caption: 'Rebuild the small, local window.', | |
| desc: 'A bounded suffix replay rebuilds local decoder SWA state; this is not another full-prompt pass. Window truncation makes this an approximation, not exact replay of unlimited preceding computation.' | |
| }, { | |
| name: 'Decode', | |
| from: 9, | |
| to: 16, | |
| active: '16B', | |
| color: COL.dec, | |
| caption: 'One decode pass. All 40 layers, in order.', | |
| desc: 'L00-L19 then L20-L39 then the output head. Both groups run attention and MoE using their own weights. The boundary changes global-cache ownership, not the execution order.' | |
| }, { | |
| name: 'DSpark', | |
| from: 16, | |
| to: 22, | |
| active: '5-token block', | |
| color: COL.mhc, | |
| caption: 'Propose a block. Verify against the backbone.', | |
| desc: 'Three draft stages propose five tokens. Acceptance colors in the token diagram are illustrative; no throughput or quality is being simulated.' | |
| }]; | |
| const TRAIN_PHASES = [{ | |
| name: 'Forward', | |
| from: 0, | |
| to: 8, | |
| active: 'Read W', | |
| color: COL.enc, | |
| caption: 'Use the weights. Predict the next tokens.', | |
| desc: 'Conceptual training pass: known tokens feed the causal backbone. Attention and routed experts build predictions; token positions can be processed in parallel with a causal mask.' | |
| }, { | |
| name: 'Loss', | |
| from: 8, | |
| to: 11, | |
| active: 'Compare', | |
| color: COL.engram, | |
| caption: 'Compare predictions with the target tokens.', | |
| desc: 'The loss measures prediction error. This is a teaching schematic, not a reproduction of DeepSeek’s complete training recipe or its losses.' | |
| }, { | |
| name: 'Backward', | |
| from: 11, | |
| to: 18, | |
| active: 'Gradients', | |
| color: COL.mhc, | |
| caption: 'Trace the error back through the computation.', | |
| desc: 'Gradients follow the operations used in the forward pass. The animation traces layer order, not every expert being active. Extra training memory is not included in weight solids.' | |
| }, { | |
| name: 'Update', | |
| from: 18, | |
| to: 22, | |
| active: 'Update W', | |
| color: COL.full, | |
| caption: 'Update trainable values. Keep the same tensor shapes.', | |
| desc: 'An optimizer updates trainable weights. The glow symbolizes a value update, not added bytes. Frozen components and the exact optimizer are recipe-dependent; this demo changes no actual weights.' | |
| }]; | |
| function phasesFor(mode) { | |
| return mode === 'training' ? TRAIN_PHASES : PHASES; | |
| } | |
| function phaseAt(t, mode='inference') { | |
| const ps = phasesFor(mode); | |
| return ps.find(p => t >= p.from && t < p.to) || ps[0]; | |
| } | |
| // ---------- explanations.js ---------- | |
| // Shapes below are [output, input]. Geometry measures modeled stored payload, | |
| // never activation size or time. See the original reference and audit notes. | |
| const TC = { | |
| q: '#8caaff', | |
| local: '#ffc477', | |
| kv: '#61dccb', | |
| index: '#f5a078', | |
| out: '#b2bfff', | |
| expert: '#66deb0', | |
| shared: '#eee081', | |
| router: '#f494be', | |
| mhc: '#c9a3fa', | |
| norm: '#90a9b5', | |
| engram: '#d7b57b', | |
| vision: '#62d5d0', | |
| head: '#a5b9eb' | |
| }; | |
| function tensorKind(w) { | |
| const n = w.name; | |
| if (n.includes('engram')) | |
| return w.cat === 'norm' ? 'norm' : 'engram'; | |
| if (n.includes('indexer')) | |
| return 'index'; | |
| if (n.includes('compressor')) | |
| return 'kv'; | |
| if (n.includes('wq_')) | |
| return 'q'; | |
| if (n.includes('wo_')) | |
| return 'out'; | |
| if (n === 'attn.wkv.weight' || n === 'attn.kv_norm.weight') | |
| return 'local'; | |
| if (w.cat === 'attn') | |
| return 'local'; | |
| return TC[w.cat] ? w.cat : 'head'; | |
| } | |
| function tensorColor(w) { | |
| return TC[tensorKind(w)] || COL.attn; | |
| } | |
| function tensorShort(w) { | |
| return w.name.replace(/^vision\.blocks\.\*\./, 'ViT.').replace(/^vision\.blocks\.\d+\./, 'ViT.').replace(/^layers\.\d+\./, '').replace(/\.weight$/, '').replace(/^ffn\.experts\./, 'experts.').replace(/^ffn\.shared_experts\./, 'shared.'); | |
| } | |
| function weightParts(w, mode) { | |
| const rows = w.shape[0] | |
| , cols = w.shape.slice(1).reduce( (a, b) => a * b, 1) | |
| , p = w.p; | |
| let data = 0 | |
| , scales = 0 | |
| , aux = 0; | |
| if (mode === 'bf16') | |
| data = p * 2; | |
| else if (mode === 'nvfp4' && w.cat === 'expert' && w.convert !== false) { | |
| data = p / 2; | |
| scales = rows * Math.ceil(cols / 16) * w.count; | |
| aux = 8 * w.count; | |
| } else if (w.format === 'fp4') { | |
| data = p / 2; | |
| scales = rows * Math.ceil(cols / 32) * w.count; | |
| } else if (w.format === 'fp8row') { | |
| data = p; | |
| scales = rows * Math.ceil(cols / 32) * w.count; | |
| } else if (w.format === 'fp8') { | |
| data = p; | |
| scales = Math.ceil(rows / 32) * Math.ceil(cols / 32) * w.count; | |
| } else | |
| data = p * (w.format === 'f32' ? 4 : w.format === 'i64' ? 8 : 2); | |
| return { | |
| data, | |
| scales, | |
| aux, | |
| total: data + scales + aux | |
| }; | |
| } | |
| function displayWeights(m) { | |
| if (m.id !== 'vision') | |
| return m.ws; | |
| const groups = new Map; | |
| for (const w of m.ws) { | |
| let key = w.name.replace(/blocks\.\d+\./, 'blocks.*.'); | |
| if (groups.has(key)) { | |
| let g = groups.get(key); | |
| g.count += w.count; | |
| g.p += w.p; | |
| } else | |
| groups.set(key, { | |
| ...w, | |
| name: key | |
| }); | |
| } | |
| return [...groups.values()]; | |
| } | |
| function findWeight(m, name) { | |
| return displayWeights(m).find(w => w.name === name) || m.ws.find(w => w.name === name); | |
| } | |
| function orderWeights(m) { | |
| const ws = displayWeights(m); | |
| const rank = w => { | |
| let n = w.name; | |
| if (n === 'attn_norm.weight') | |
| return 0; | |
| if (n === 'main_proj.weight') | |
| return 1; | |
| if (n === 'main_norm.weight') | |
| return 2; | |
| if (n.includes('hc_attn')) | |
| return 16; | |
| if (n.includes('wq_a')) | |
| return 3; | |
| if (n === 'attn.q_norm.weight') | |
| return 4; | |
| if (n === 'attn.wq_b.weight') | |
| return 5; | |
| if (n === 'attn.wkv.weight') | |
| return 6; | |
| if (n === 'attn.kv_norm.weight') | |
| return 7; | |
| if (n.includes('compressor')) | |
| return 8; | |
| if (n.includes('indexer')) | |
| return 9; | |
| if (n.includes('attn_sink')) | |
| return 10; | |
| if (n.includes('wo_a')) | |
| return 11; | |
| if (n.includes('wo_b')) | |
| return 12; | |
| if (n === 'ffn_norm.weight') | |
| return 20; | |
| if (n === 'ffn.gate.weight') | |
| return 21; | |
| if (n === 'ffn.gate.bias') | |
| return 22; | |
| if (n === 'ffn.gate.bias_vl') | |
| return 23; | |
| if (n.includes('experts.w1')) | |
| return n.includes('shared') ? 30 : 25; | |
| if (n.includes('experts.w3')) | |
| return n.includes('shared') ? 31 : 26; | |
| if (n.includes('experts.w2')) | |
| return n.includes('shared') ? 32 : 27; | |
| if (n.includes('hc_ffn')) | |
| return 34; | |
| return 40; | |
| } | |
| ; | |
| return [...ws].sort( (a, b) => rank(a) - rank(b)); | |
| } | |
| function isAttentionTensor(w) { | |
| return w.name.startsWith('attn') || w.name.startsWith('hc_attn') || w.name.startsWith('main_'); | |
| } | |
| function tensorInfo(w, m) { | |
| const n = w.name | |
| , has = s => n.includes(s) | |
| , draft = m && m.id.startsWith('D') | |
| , k = draft ? 3 : 6 | |
| , N = draft ? 128 : 384; | |
| let title = tensorShort(w) | |
| , job = 'A learned tensor in this module.' | |
| , flow = '' | |
| , why = '' | |
| , caution = ''; | |
| if (has('indexer.wq_b')) { | |
| title = 'Ask where to look'; | |
| job = 'Builds 32 small indexer queries. These rank global memory positions before the larger attention heads read them.'; | |
| flow = '1,280 latent channels \u2192 32 \u00d7 128 indexer queries'; | |
| why = m && m.mode === 'reindex' ? 'This Reindex layer asks a new question of L20\u2019s existing keys. It searches the candidate pool selected at L20, not the entire context.' : 'The selector scores visible global positions and keeps up to 512. Full mode creates both the keys and the selection; Reuse layers do not own this matrix.'; | |
| } else if (has('indexer.weights_proj')) { | |
| title = 'Combine the indexer heads'; | |
| job = 'Produces one score weight for each of the 32 indexer heads, letting their evidence contribute differently to the final position score.'; | |
| flow = '5,120 hidden channels \u2192 32 head weights'; | |
| why = 'These are weights for combining indexer scores, not another bank of 32 KV heads. The resulting Top-K indices are runtime integers, not learned model parameters.'; | |
| } else if (has('indexer.wk')) { | |
| title = 'Give each memory entry a search key'; | |
| job = 'Projects a compressed 512-channel KV latent into a smaller, 128-channel indexer key.'; | |
| flow = '512 main-KV channels \u2192 128 index-key channels'; | |
| why = 'Only Full layers own this projection. Their index keys are cached and reused together with the main KV entries. Searching a small key is cheaper than reading the full attention representation.'; | |
| } else if (has('indexer.k_norm')) { | |
| title = 'Normalize the index keys'; | |
| job = 'Rescales each 128-channel indexer key before it is used to rank memory positions.'; | |
| flow = '128-channel key \u2192 RMSNorm \u2192 searchable key'; | |
| why = 'A normalization vector has one scale per channel. It is not a 128 \u00d7 128 matrix, so its stored volume is much smaller than a projection.'; | |
| } else if (has('compressor.wgate')) { | |
| title = 'Learn how two tokens become one'; | |
| job = 'Produces channel-wise scores for pooling the two tokens in an encoder compression group. A softmax chooses their relative contribution.'; | |
| flow = 'Two token states \u2192 pooling scores \u2192 one weighted KV entry'; | |
| why = 'The value projection supplies what is stored; this gate chooses the mixture. Only the ratio-2 encoder owners need a pooling gate. L20 uses ratio 1 and has no wgate tensor.'; | |
| caution = 'The reference runtime promotes ratio-2 pooling weights to F32. The atlas models the checkpoint payload as BF16 until a shard header confirms it.'; | |
| } else if (has('compressor.wkv')) { | |
| title = m && m.index === 20 ? 'Make the decoder\u2019s shared memory' : 'Write the compressed global memory'; | |
| job = m && m.index === 20 ? 'Projects final encoder states into the one global KV bank read by all 20 decoder layers. It is not the layer-local SWA projection.' : 'Projects hidden states to a 512-channel latent. This encoder owner combines pairs of latents into compressed global entries.'; | |
| flow = m && m.index === 20 ? 'Encoder output \u2192 5,120 to 512 \u2192 decoder global KV' : '5,120 hidden channels \u2192 512 KV channels \u2192 2:1 pooling'; | |
| why = 'Separating this global bank from every layer\u2019s local KV is the important distinction. Later layers borrow the cached activations, not this layer\u2019s query or feed-forward weights.'; | |
| caution = 'Checkpoint dtype is modeled as BF16; runtime promotion and actual on-disk dtype are different questions.'; | |
| } else if (has('compressor.norm')) { | |
| title = 'Normalize a completed memory entry'; | |
| job = 'Applies RMS normalization to the compressed KV latent after pooling, before the attention path uses its positional representation.'; | |
| flow = 'One pooled 512-vector \u2192 normalized KV latent'; | |
| why = 'There is one learned scale per channel. This tensor exists at KV owners, not at every layer that reads their memory.'; | |
| } else if (has('attn.wq_a')) { | |
| title = 'Compress the question'; | |
| job = 'Maps the token\u2019s 5,120-channel state into a 1,280-channel query bottleneck. This is the first half of the low-rank query path.'; | |
| flow = '5,120 hidden channels \u2192 1,280 query channels'; | |
| why = 'The next matrix expands this compact representation into all 64 query heads. Even a Reuse layer needs its own questions: sharing memory does not mean sharing query weights.'; | |
| } else if (n === 'attn.q_norm.weight') { | |
| title = 'Normalize the query bottleneck'; | |
| job = 'Normalizes the 1,280-channel vector between the two query projections.'; | |
| flow = 'wq_a output \u2192 RMSNorm \u2192 wq_b input'; | |
| why = 'This is a learned vector, not another large matrix. The network can adjust the scale of each bottleneck channel without adding many weights.'; | |
| } else if (n === 'attn.wq_b.weight') { | |
| title = 'Form all 64 attention queries'; | |
| job = 'Expands the normalized bottleneck into 64 heads of 512 channels each. Each head asks a different question of the available KV entries.'; | |
| flow = '1,280 \u2192 32,768 channels = 64 heads \u00d7 512'; | |
| why = 'The config reserves 64 channels per head for rotary position information. This main-query path is distinct from the much smaller indexer query path.'; | |
| } else if (n === 'attn.wkv.weight') { | |
| title = 'Remember the recent 128 tokens'; | |
| job = 'Projects the current layer\u2019s hidden state into its local 512-channel KV latent. Every backbone layer owns this projection, including Reuse layers.'; | |
| flow = '5,120 \u2192 512 local KV channels \u2192 a 128-token ring buffer'; | |
| why = 'Local SWA memory is not the shared global cache. Sharing the global bank does not remove these local weights or the need to rebuild local states during bounded replay.'; | |
| } else if (n === 'attn.kv_norm.weight') { | |
| title = 'Normalize the local memory'; | |
| job = 'Rescales the layer-local KV latent before it enters sliding-window attention.'; | |
| flow = 'Local 512-vector \u2192 RMSNorm \u2192 local attention memory'; | |
| why = 'Its 512 learned scales are stored weights. The ring buffer holding 128 token vectors is a separate runtime allocation, not part of this solid.'; | |
| } else if (has('attn_sink')) { | |
| title = 'A learned attention sink'; | |
| job = 'Supplies one scalar per attention head to the attention normalization. It lets a head allocate probability mass without forcing all of it onto visible token entries.'; | |
| flow = '64 learned scalars \u2192 attention normalization'; | |
| why = 'An attention sink is neither a vocabulary token nor a KV vector. Only 64 F32 values are stored here, so the byte-proportional solid is genuinely tiny.'; | |
| } else if (has('wo_a')) { | |
| title = 'Compress head outputs within groups'; | |
| job = 'Splits 64 attention heads into eight groups. Each group compresses eight 512-channel head outputs into 1,024 channels.'; | |
| flow = '8 \u00d7 (4,096 \u2192 1,024) \u2192 8,192 output channels'; | |
| why = 'This is block-grouped, not a fully connected 32,768 \u2192 8,192 projection. The [8,192, 4,096] storage shape stacks the eight group matrices.'; | |
| caution = 'The converter expands stored FP8 wo_a to BF16 for the reference grouped operation. Geometry measures the modeled disk payload, not that runtime expansion.'; | |
| } else if (has('wo_b')) { | |
| title = 'Return attention to the hidden width'; | |
| job = 'Combines the eight compressed output groups into one 5,120-channel attention result.'; | |
| flow = '8,192 grouped channels \u2192 5,120 hidden channels'; | |
| why = 'Together, wo_a and wo_b replace one large dense output projection. The result is mixed back into the residual streams by mHC.'; | |
| } else if (n === 'ffn.gate.weight') { | |
| title = 'Route the token to specialists'; | |
| job = 'Produces ' + N + ' expert scores from the current hidden state. The router chooses ' + k + ' specialists for this token.'; | |
| flow = '5,120 \u2192 ' + N + ' scores \u2192 Top-' + k + ' expert IDs'; | |
| why = 'Scores use sqrt(softplus). Correction biases change the selected IDs, but the mixture uses the original selected scores, normalized and scaled by 1.5. The displayed route is synthetic, not a model execution.'; | |
| } else if (n === 'ffn.gate.bias' || n === 'ffn.gate.bias_vl') { | |
| title = has('bias_vl') ? 'Balance image-token routing' : 'Balance text-token routing'; | |
| job = 'Adds a correction to the expert scores used for selection, encouraging balanced use of the routed experts.'; | |
| flow = N + ' correction values \u2192 selection scores only'; | |
| why = has('bias_vl') ? 'The reference uses a separate correction for tokens in image spans. Balancing all tokens together would not necessarily balance each modality.' : 'These corrections are not the mixture weights returned by the router. They steer which experts win; the unbiased scores determine the weighted combination.'; | |
| } else if (has('experts.w')) { | |
| const gate = has('.w1.') | |
| , up = has('.w3.') | |
| , shared = has('shared_experts'); | |
| title = (shared ? 'Shared expert: ' : w.count === 1 ? 'Routed expert: ' : 'Routed expert bank: ') + (gate ? 'gate projection' : up ? 'up projection' : 'down projection'); | |
| job = gate ? 'Creates the branch passed through SiLU. Its elementwise product with the up branch forms the gated hidden features.' : up ? 'Creates the content branch of SwiGLU. It is multiplied elementwise by the activated gate branch.' : 'Maps the 2,304-channel gated features back to the 5,120-channel model width.'; | |
| flow = gate ? 'x \u2192 W1 \u2192 SiLU \u2192 gate' : up ? 'x \u2192 W3 \u2192 content' : 'SiLU(W1 x) \u2299 (W3 x) \u2192 W2 \u2192 output'; | |
| why = shared ? 'There is one always-on shared expert, with the same width as a routed specialist. The supplied NVFP4 recipe excludes it; its source FP8 format is retained.' : w.count === 1 ? 'This is one specialist\u2019s matrix. Its three projections are counted once; the remaining specialists stay in the bank. Only ' + k + ' routed experts run per token, but all ' + N + ' are stored.' : 'This descriptor holds ' + w.count + ' separate matrices, not one matrix with ' + w.count + ' times the width. Only ' + k + ' experts run for an illustrative token, but all ' + N + ' must still be stored. Switch to Experts to split the bank into individuals.'; | |
| if (draft && !shared) | |
| caution = 'The supplied recipe ignores mtp.*. These draft experts retain native FP4 rather than changing to NVFP4.'; | |
| } else if (has('hc_')) { | |
| const stage = has('hc_attn') ? 'attention' : 'feed-forward'; | |
| title = has('_fn') ? 'Predict residual-stream mixing' : has('_base') ? 'Baseline residual mixing' : 'Scale the three mixing branches'; | |
| job = has('_fn') ? 'Reads four 5,120-channel residual streams and predicts 24 coefficients for input mixing, residual mixing and output distribution.' : has('_base') ? 'Adds a learned baseline to the 24 predicted mixing coefficients.' : 'Controls the scale of the three coefficient groups before they become mixing weights.'; | |
| flow = has('_fn') ? '20,480 \u2192 24 = 4 input + 16 residual + 4 output' : has('_base') ? '24 learned offsets \u2192 mixing coefficients' : '3 learned scales \u2192 input / residual / output mixing'; | |
| why = 'These belong to the ' + stage + ' sublayer\u2019s Single-Pass mHC path. The reference passes predicted coefficients to the next sublayer; Sinkhorn normalization balances the 4 \u00d7 4 residual-mixing matrix. Four activation streams do not mean four copies of all model weights.'; | |
| } else if (has('engram.embed')) { | |
| title = 'Look up learned n-gram memory'; | |
| job = 'Stores token-addressed memory. N-grams of lengths 2, 3 and 4 each use eight hash heads, retrieving 24 rows per module.'; | |
| flow = 'Recent token IDs \u2192 24 hash addresses \u2192 24 \u00d7 256 = 6,144 channels'; | |
| why = 'The total row count already includes the hash tables. A lookup reads a few rows instead of multiplying through the entire table. Sparse access reduces per-token work, not the checkpoint\u2019s storage requirement.'; | |
| caution = 'The config and reference do not justify claiming the entire table can never reside in GPU memory. Placement and offload depend on the serving system.'; | |
| } else if (has('engram.wkv')) { | |
| title = 'Turn retrievals into gated memory'; | |
| job = 'Projects the 6,144 retrieved channels into four keys and one shared value, each 5,120-dimensional.'; | |
| flow = '6,144 \u2192 (4 keys + 1 value) \u00d7 5,120 = 25,600'; | |
| why = 'Each residual stream compares itself with its key. The resulting gates decide how much of the shared retrieved value is added. This is why the released combined projection is larger than two simple 6,144 \u2192 5,120 matrices.'; | |
| } else if (has('engram.q_weight') || has('engram.k_weight')) { | |
| title = 'Normalize the memory-match gate'; | |
| job = 'Supplies channel scales for normalized matching between the four residual streams and the four retrieved keys.'; | |
| flow = '4 streams \u00d7 5,120 channels \u2192 key/query matching'; | |
| why = 'The lookup is conditional twice: token IDs choose rows, and the current hidden state decides how much of the retrieved information to use.'; | |
| } else if (n === 'main_proj.weight') { | |
| title = 'Feed backbone features to the drafter'; | |
| job = 'Combines hidden features from backbone layers 37, 38 and 39 into the first DSpark stage\u2019s input width.'; | |
| flow = '3 \u00d7 5,120 = 15,360 \u2192 5,120 draft channels'; | |
| why = 'The drafter reuses late backbone features rather than starting from a separate language model. This projection belongs to the first draft stage only.'; | |
| } else if (has('markov_head.embed')) { | |
| title = 'Represent the previous draft token'; | |
| job = 'Looks up a 256-channel representation for a token used by the draft block\u2019s low-rank Markov dependency.'; | |
| flow = '129,280 possible IDs \u2192 256-channel token representation'; | |
| why = 'Semi-autoregressive drafting proposes a block, but neighboring draft tokens should not be treated as completely independent. This lookup is separate from the main 5,120-channel token embedding.'; | |
| } else if (has('markov_head.head')) { | |
| title = 'Score a Markov correction'; | |
| job = 'Maps the 256-channel Markov representation back to vocabulary scores inside the draft mechanism.'; | |
| flow = '256 Markov channels \u2192 129,280 scores'; | |
| why = 'The pair of vocabulary-shaped rank-256 tensors is not a guessed 5,120 \u2192 256 down-projection. These dimensions follow the reference draft-head structure.'; | |
| } else if (has('confidence_head')) { | |
| title = 'Estimate whether a draft will survive'; | |
| job = 'Projects a combined 5,376-channel feature into a scalar confidence for a proposed position.'; | |
| flow = '5,120 draft channels + 256 Markov channels \u2192 1 confidence'; | |
| why = 'Confidence can guide how many draft positions to verify. It does not replace backbone verification, and the animated confidence values are examples rather than measured acceptance rates.'; | |
| } else if (has('patch_embed')) { | |
| title = has('bias') ? 'Offset the patch embedding' : 'Encode one image patch'; | |
| job = has('bias') ? 'Adds one learned offset per patch-embedding output channel.' : 'Maps a 14 \u00d7 14 RGB patch into the vision encoder\u2019s 1,024-channel hidden representation.'; | |
| flow = has('bias') ? '1,024 offsets + projected patch features' : '14 \u00d7 14 \u00d7 3 = 588 \u2192 1,024 channels'; | |
| why = 'Image patches enter the vision Transformer first. The later projector, not this patch embedding, maps them into the language model\u2019s width.'; | |
| } else if (n.startsWith('aligner.') && has('.bias')) { | |
| title = 'Offset the projected visual features'; | |
| job = 'Adds a learned bias to each of the 5,120 output channels after this projector matrix is applied.'; | |
| flow = '5,120 learned offsets + projected visual vector'; | |
| why = 'A bias is a vector, not a second copy of the projection matrix. It contributes only two modeled bytes per channel in native BF16 storage.'; | |
| } else if (has('aligner.w1')) { | |
| title = 'Compress nine neighboring patch features'; | |
| job = 'Maps the 9,216 channels formed by 3 \u00d7 3 pixel-unshuffle into the language hidden width.'; | |
| flow = '9 \u00d7 1,024 = 9,216 \u2192 5,120'; | |
| why = 'Pixel-unshuffle reorganizes neighboring features; it is not a learned 9-times-larger vision encoder. It reduces the number of image tokens the language backbone must process.'; | |
| } else if (has('aligner.w2')) { | |
| title = 'Finish the vision-to-language projection'; | |
| job = 'Applies the second projector layer at the language model\u2019s hidden width.'; | |
| flow = '5,120 \u2192 5,120 visual embedding'; | |
| why = 'Text embeddings and projected visual embeddings can now share the backbone\u2019s input sequence.'; | |
| } else if (n.startsWith('image_')) { | |
| title = 'A learned image-boundary embedding'; | |
| job = 'Marks a structural boundary in the serialized image representation.'; | |
| flow = 'Image boundary marker \u2192 5,120-channel vector'; | |
| why = 'These are small learned vectors alongside the projector, not additional copies of the vision encoder.'; | |
| } else if (has('vision.blocks') && has('.bias')) { | |
| title = has('wqkv') ? 'Offsets for vision queries, keys and values' : 'Offset the vision attention output'; | |
| job = 'Adds one learned bias per output channel after the corresponding vision attention projection.'; | |
| flow = has('wqkv') ? '3,072 offsets for fused Q, K and V' : '1,024 offsets for the attention output'; | |
| why = w.count > 1 ? 'This bank counts the corresponding bias vectors in all 32 vision blocks. They are distinct learned vectors, not shared offsets.' : 'A bias changes the offset of the projected representation; it is not another matrix multiplication.'; | |
| } else if (has('vision.blocks')) { | |
| title = has('wqkv') ? 'Vision queries, keys and values' : has('.wo.') ? 'Vision attention output' : has('.mlp.w1') ? 'Fused vision SwiGLU gate and up' : has('.mlp.w2') ? 'Vision SwiGLU down-projection' : 'Vision normalization'; | |
| job = has('wqkv') ? 'Builds the vision block\u2019s queries, keys and values in one fused projection.' : has('.wo.') ? 'Returns the attended vision features to the 1,024-channel residual stream.' : has('.mlp.w1') ? 'Produces two 2,816-channel branches for the vision block\u2019s gated feed-forward operation.' : has('.mlp.w2') ? 'Returns gated 2,816-channel features to width 1,024.' : 'Normalizes the vision block\u2019s hidden representation.'; | |
| flow = has('wqkv') ? '1,024 \u2192 3 \u00d7 1,024' : has('.mlp.w1') ? '1,024 \u2192 5,632 = 2 \u00d7 2,816' : has('.mlp.w2') ? '2,816 \u2192 1,024' : '1,024-channel vision path'; | |
| why = w.count > 1 ? 'The scene groups the same tensor role across all 32 vision blocks. The copies are counted in its volume; it does not imply weight sharing between blocks.' : 'One tensor in one of 32 vision blocks. Vision attention uses 16 heads and 2D rotary position information.'; | |
| } else if (n === 'embed.weight') { | |
| title = 'Look up the input token'; | |
| job = 'Stores one 5,120-channel vector for each of the 129,280 vocabulary entries.'; | |
| flow = 'Token ID \u2192 one row of [129,280, 5,120]'; | |
| why = 'An ordinary token lookup reads one row. Counting every embedding parameter as active arithmetic for every token would be misleading. The output head is untied and stored separately.'; | |
| } else if (n === 'head.weight') { | |
| title = 'Choose scores for the next token'; | |
| job = 'Projects the final 5,120-channel representation into a score for each vocabulary entry.'; | |
| flow = '5,120 hidden channels \u2192 129,280 logits'; | |
| why = 'Unlike a lookup, this is a vocabulary projection. It is untied from the input embedding. It is used when producing output scores, not for the encoder-only bulk prefill path.'; | |
| caution = 'The atlas models BF16 checkpoint storage. The reference runtime\u2019s F32 head expansion is not extra on-disk weight data.'; | |
| } else if (has('norm') || w.cat === 'norm') { | |
| title = 'Normalize the sublayer input'; | |
| job = 'Applies a learned scale per channel after RMS normalization.'; | |
| flow = num(w.p / w.count) + ' channels \u2192 normalized channels'; | |
| why = 'A normalization vector is much smaller than a square projection. Its small scene marker is a picking aid; the stored-byte solid has not been inflated.'; | |
| } else { | |
| job = w.note || job; | |
| why = 'The inspector reports this tensor\u2019s modeled storage, not the size of the activations passing through it.'; | |
| } | |
| return { | |
| title, | |
| job, | |
| flow, | |
| why, | |
| caution, | |
| source: n.startsWith('vision') || n.startsWith('aligner') ? 'vision' : 'model' | |
| }; | |
| } | |
| function layerStory(m) { | |
| if (!m.mode) | |
| return null; | |
| let t, body, detail; | |
| if (m.mode === 'swa') { | |
| t = 'Local context, then specialists.'; | |
| body = 'Layer ' + m.index + ' has no global attention branch. It reads its own 128-token sliding window, then routes the result through six of 384 experts and the always-on shared expert.'; | |
| detail = 'No compressor or global indexer matrices should appear here. The query path, output path, router, shared expert, normalizations and mHC weights are still present.'; | |
| } else if (m.mode === 'full' && m.index === 20) { | |
| t = 'The decoder\u2019s memory starts here.'; | |
| body = 'L20 is the decoder\u2019s only global-KV owner. Under CED, its global projection reads the final encoder representation, while local sliding-window KV remains layer-specific.'; | |
| detail = 'It also makes a fresh sparse selection and a candidate pool of at most 2,048 blocks \u00d7 8 positions. Later decoder selectors search this pool; all 20 decoder layers read the same global bank.'; | |
| } else if (m.mode === 'full') { | |
| t = 'Write memory once. Share it upward.'; | |
| body = 'L' + m.index + ' owns an encoder global bank. It projects and pools pairs of token states, constructs indexer keys, and selects up to 512 global positions for attention.'; | |
| detail = 'Layers ' + m.index + '\u2013' + (m.index + 5) + ' share those cached entries and the selection. Each layer still computes its own main queries and local KV. Full describes the set of operations performed, not dense global attention.'; | |
| } else if (m.mode === 'reindex') { | |
| t = 'Search the same memory with a new query.'; | |
| body = 'L' + m.index + ' borrows the main KV and index keys from L20, but owns indexer query weights. It recomputes which 512 entries to read inside L20\u2019s candidate pool.'; | |
| detail = 'Its next three decoder layers reuse this selection. No new global bank is written here; that is why the compressor and index-key projection are absent from this layer\u2019s weights.'; | |
| } else { | |
| t = 'New computation, reused global memory.'; | |
| body = 'L' + m.index + ' borrows global KV from L' + m.owner + ' and the selected positions from L' + m.indexOwner + '. It has neither a global compressor nor a fresh indexer.'; | |
| detail = 'Reuse does not skip this layer. Its own queries read those borrowed entries alongside its own 128-token local window. Output projections, routed experts, shared expert and residual mixing all still run.'; | |
| } | |
| return { | |
| title: t, | |
| body, | |
| detail | |
| }; | |
| } | |
| // ---------- fallback.js ---------- | |
| /* CPU projection fallback: same geometry, perspective and byte scale as WebGL. | |
| This also supports locked-down enterprise browsers with WebGL disabled. */ | |
| class CanvasRenderer { | |
| constructor(canvas) { | |
| this.canvas = canvas; | |
| this.ctx = canvas.getContext('2d', { | |
| alpha: true | |
| }); | |
| if (!this.ctx) | |
| throw Error('Neither WebGL2 nor Canvas2D is available in this browser.'); | |
| this.software = true; | |
| } | |
| resize(w, h) { | |
| this.dpr = Math.min(devicePixelRatio || 1, 1.6); | |
| this.w = w; | |
| this.h = h; | |
| this.canvas.width = Math.max(1, Math.round(w * this.dpr)); | |
| this.canvas.height = Math.max(1, Math.round(h * this.dpr)); | |
| } | |
| render(boxes, lines, points, vp, eye) { | |
| const ctx = this.ctx | |
| , dpr = this.dpr | |
| , W = this.w | |
| , H = this.h; | |
| ctx.setTransform(dpr, 0, 0, dpr, 0, 0); | |
| ctx.clearRect(0, 0, W, H); | |
| const project = p => { | |
| let x = p[0] | |
| , y = p[1] | |
| , z = p[2] | |
| , w = vp[3] * x + vp[7] * y + vp[11] * z + vp[15]; | |
| if (w <= .01) | |
| return null; | |
| return [(vp[0] * x + vp[4] * y + vp[8] * z + vp[12]) / w * W / 2 + W / 2, H / 2 - (vp[1] * x + vp[5] * y + vp[9] * z + vp[13]) / w * H / 2, w]; | |
| } | |
| , css = (c, a=1) => 'rgba(' + c.map(v => Math.round(clamp(v, 0, 1) * 255)).join(',') + ',' + a + ')'; | |
| ctx.lineWidth = .8; | |
| ctx.lineJoin = 'round'; | |
| ctx.lineCap = 'round'; | |
| // Flow links sit behind weight solids; they do not represent stored mass. | |
| for (let i = 0; i < lines.length; i += 14) { | |
| let a = project(lines.slice(i, i + 3)) | |
| , b = project(lines.slice(i + 7, i + 10)); | |
| if (!a || !b) | |
| continue; | |
| ctx.strokeStyle = css(lines.slice(i + 3, i + 6), lines[i + 6]); | |
| ctx.beginPath(); | |
| ctx.moveTo(a[0], a[1]); | |
| ctx.lineTo(b[0], b[1]); | |
| ctx.stroke(); | |
| } | |
| const F = [{ | |
| n: [0, 1, 0], | |
| c: [3, 2, 6, 7], | |
| light: .82 | |
| }, { | |
| n: [0, -1, 0], | |
| c: [0, 4, 5, 1], | |
| light: .34 | |
| }, { | |
| n: [1, 0, 0], | |
| c: [1, 5, 6, 2], | |
| light: .42 | |
| }, { | |
| n: [-1, 0, 0], | |
| c: [0, 3, 7, 4], | |
| light: .60 | |
| }, { | |
| n: [0, 0, 1], | |
| c: [4, 7, 6, 5], | |
| light: .64 | |
| }, { | |
| n: [0, 0, -1], | |
| c: [0, 1, 2, 3], | |
| light: .38 | |
| }]; | |
| const signs = [[-1, -1, -1], [1, -1, -1], [1, 1, -1], [-1, 1, -1], [-1, -1, 1], [1, -1, 1], [1, 1, 1], [-1, 1, 1]] | |
| , polys = []; | |
| for (const box of boxes) { | |
| let corners = signs.map(s => box.pos.map( (v, i) => v + s[i] * box.size[i] / 2)) | |
| , screen = corners.map(project) | |
| , view = V3.sub(eye, box.pos); | |
| if (screen.some(p => !p)) | |
| continue; | |
| for (const f of F) { | |
| if (V3.dot(f.n, view) <= 0) | |
| continue; | |
| let coords = f.c.map(i => screen[i]) | |
| , depth = f.c.reduce( (a, i) => a + screen[i][2], 0) / 4; | |
| let color = box.color.map(v => v * (f.light + (box.glow || 0) * .55)); | |
| polys.push({ | |
| coords, | |
| depth, | |
| color, | |
| edge: box.color, | |
| style: box.style, | |
| top: f.n[1] !== 0 | |
| }); | |
| } | |
| } | |
| polys.sort( (a, b) => b.depth - a.depth); | |
| for (const p of polys) { | |
| let c = p.coords; | |
| if (c.every(v => v[0] < -20) || c.every(v => v[0] > W + 20) || c.every(v => v[1] < -20) || c.every(v => v[1] > H + 20)) | |
| continue; | |
| let xmin = Math.min(...c.map(v => v[0])) | |
| , xmax = Math.max(...c.map(v => v[0])) | |
| , ymin = Math.min(...c.map(v => v[1])) | |
| , ymax = Math.max(...c.map(v => v[1])); | |
| let grad = ctx.createLinearGradient(xmin, ymin, xmax, ymax + .1); | |
| grad.addColorStop(0, css(p.color.map(v => v * 1.15))); | |
| grad.addColorStop(1, css(p.color.map(v => v * .83))); | |
| ctx.fillStyle = grad; | |
| ctx.strokeStyle = css(p.edge, .63); | |
| ctx.lineWidth = .55; | |
| ctx.beginPath(); | |
| ctx.moveTo(c[0][0], c[0][1]); | |
| for (let j = 1; j < 4; j++) | |
| ctx.lineTo(c[j][0], c[j][1]); | |
| ctx.closePath(); | |
| ctx.fill(); | |
| ctx.stroke(); | |
| if (p.style === 2 && !p.top && xmax - xmin > 25 && ymax - ymin > 20) { | |
| ctx.save(); | |
| ctx.clip(); | |
| ctx.strokeStyle = css(p.edge, .22); | |
| ctx.lineWidth = .6; | |
| for (let k = 1; k < 24; k++) { | |
| let t = k / 24; | |
| ctx.beginPath(); | |
| ctx.moveTo(lerp(c[0][0], c[1][0], t), lerp(c[0][1], c[1][1], t)); | |
| ctx.lineTo(lerp(c[3][0], c[2][0], t), lerp(c[3][1], c[2][1], t)); | |
| ctx.stroke(); | |
| } | |
| ctx.restore(); | |
| } | |
| } | |
| ctx.globalCompositeOperation = 'lighter'; | |
| for (let i = 0; i < points.length; i += 8) { | |
| let p = project(points.slice(i, i + 3)); | |
| if (!p || p[0] < 0 || p[0] > W || p[1] < 0 || p[1] > H) | |
| continue; | |
| let r = points[i + 7] * .5 | |
| , c = points.slice(i + 3, i + 6) | |
| , a = points[i + 6]; | |
| if (r < 2.2) { | |
| ctx.fillStyle = css(c, a * .65); | |
| ctx.beginPath(); | |
| ctx.arc(p[0], p[1], r * .6, 0, Math.PI * 2); | |
| ctx.fill(); | |
| } else { | |
| let gr = ctx.createRadialGradient(p[0], p[1], 0, p[0], p[1], r); | |
| gr.addColorStop(0, css(c, a)); | |
| gr.addColorStop(.32, css(c, a * .55)); | |
| gr.addColorStop(1, css(c, 0)); | |
| ctx.fillStyle = gr; | |
| ctx.fillRect(p[0] - r, p[1] - r, r * 2, r * 2); | |
| } | |
| } | |
| ctx.globalCompositeOperation = 'source-over'; | |
| } | |
| dispose() {} | |
| } | |
| // ---------- engine.js ---------- | |
| /* Small, self-contained instanced WebGL2 renderer. There are no CDN requests. | |
| A camera-only zoom changes projection, never the byte-to-volume conversion. */ | |
| const VOLUME_UNIT = 1e9; | |
| const V3 = { | |
| add: (a, b) => a.map( (v, i) => v + b[i]), | |
| sub: (a, b) => a.map( (v, i) => v - b[i]), | |
| scale: (a, s) => a.map(v => v * s), | |
| dot: (a, b) => a.reduce( (s, x, i) => s + x * b[i], 0), | |
| cross: (a, b) => [a[1] * b[2] - a[2] * b[1], a[2] * b[0] - a[0] * b[2], a[0] * b[1] - a[1] * b[0]], | |
| norm: a => { | |
| let l = Math.hypot(...a) || 1; | |
| return a.map(v => v / l); | |
| } | |
| }; | |
| function matMul(a, b) { | |
| let o = new Float32Array(16); | |
| for (let c = 0; c < 4; c++) | |
| for (let r = 0; r < 4; r++) | |
| for (let k = 0; k < 4; k++) | |
| o[c * 4 + r] += a[k * 4 + r] * b[c * 4 + k]; | |
| return o; | |
| } | |
| function perspective(f, aspect, n, far) { | |
| let s = 1 / Math.tan(f / 2); | |
| return new Float32Array([s / aspect, 0, 0, 0, 0, s, 0, 0, 0, 0, (far + n) / (n - far), -1, 0, 0, 2 * far * n / (n - far), 0]); | |
| } | |
| function lookAt(eye, target) { | |
| const z = V3.norm(V3.sub(eye, target)) | |
| , x = V3.norm(V3.cross([0, 1, 0], z)) | |
| , y = V3.cross(z, x); | |
| return new Float32Array([x[0], y[0], z[0], 0, x[1], y[1], z[1], 0, x[2], y[2], z[2], 0, -V3.dot(x, eye), -V3.dot(y, eye), -V3.dot(z, eye), 1]); | |
| } | |
| const rgb = h => [1, 3, 5].map(i => parseInt(h.slice(i, i + 2), 16) / 255); | |
| const mixCol = (a, b, t) => a.map( (x, i) => lerp(x, b[i], t)); | |
| function cubic(a, b, c, d, t) { | |
| const u = 1 - t; | |
| return a.map( (x, i) => u * u * u * x + 3 * u * u * t * b[i] + 3 * u * t * t * c[i] + t * t * t * d[i]); | |
| } | |
| const VS = `#version 300 es | |
| precision highp float; | |
| layout(location=0) in vec3 position;layout(location=1) in vec3 normal; | |
| layout(location=2) in vec3 offset;layout(location=3) in vec3 size; | |
| layout(location=4) in vec3 color;layout(location=5) in float glow;layout(location=6) in float style; | |
| uniform mat4 vp;uniform vec3 eye; | |
| out vec3 vN;out vec3 vColor;out vec3 vLocal;out vec3 vWorld;out float vGlow;out float vStyle; | |
| void main(){vN=normal;vColor=color;vLocal=position;vGlow=glow;vStyle=style;vWorld=offset+position*size;gl_Position=vp*vec4(vWorld,1.);}`; | |
| const FS = `#version 300 es | |
| precision highp float; | |
| in vec3 vN;in vec3 vColor;in vec3 vLocal;in vec3 vWorld;in float vGlow;in float vStyle; | |
| uniform vec3 eye;out vec4 outColor; | |
| void main(){vec3 n=normalize(vN);vec3 light=normalize(vec3(-.4,.8,.7));float diffuse=max(dot(n,light),0.); | |
| vec3 viewDir=normalize(eye-vWorld);float rim=pow(1.-max(dot(n,viewDir),0.),3.); | |
| vec2 uv=abs(n.y)>.5?vLocal.xz:abs(n.x)>.5?vLocal.zy:vLocal.xy; | |
| vec2 border=.5-abs(uv);float ed=1.-smoothstep(0.,max(fwidth(uv.x),fwidth(uv.y))*1.3+.004,min(border.x,border.y)); | |
| float sheen=pow(max(dot(reflect(-light,n),viewDir),0.),40.); | |
| vec3 c=vColor*(.37+.55*diffuse)+vColor*(vGlow*.65+rim*.12)+vec3(.21)*sheen; | |
| c=mix(c,vColor*(1.05+vGlow*.7),ed*.72); | |
| if(vStyle>1.5&&vStyle<2.5){float stripe=step(.96,fract((uv.x+.5)*24.));c+=vColor*stripe*.10;} | |
| if(vStyle>2.5&&vStyle<3.5){float stripe=step(.93,fract((uv.y+.5)*16.));c+=vColor*stripe*.10;} | |
| outColor=vec4(c,1.);}`; | |
| const LINE_VS = `#version 300 es | |
| precision highp float;layout(location=0) in vec3 p;layout(location=1) in vec4 c;uniform mat4 vp;out vec4 vc;void main(){gl_Position=vp*vec4(p,1.);vc=c;}`; | |
| const LINE_FS = `#version 300 es | |
| precision highp float;in vec4 vc;out vec4 outColor;void main(){outColor=vc;}`; | |
| const POINT_VS = `#version 300 es | |
| precision highp float;layout(location=0) in vec3 p;layout(location=1) in vec4 c;layout(location=2) in float size;uniform mat4 vp;uniform float dpr;out vec4 vc;void main(){gl_Position=vp*vec4(p,1.);gl_PointSize=size*dpr;vc=c;}`; | |
| const POINT_FS = `#version 300 es | |
| precision highp float;in vec4 vc;out vec4 outColor;void main(){float r=length(gl_PointCoord-.5)*2.;if(r>1.)discard;float a=exp(-r*r*5.)*(1.-smoothstep(.65,1.,r));outColor=vec4(vc.rgb,vc.a*a);}`; | |
| class GLRenderer { | |
| constructor(canvas) { | |
| this.canvas = canvas; | |
| const gl = this.gl = canvas.getContext('webgl2', { | |
| antialias: true, | |
| alpha: true, | |
| preserveDrawingBuffer: true, | |
| powerPreference: 'high-performance' | |
| }); | |
| if (!gl) | |
| throw Error('WebGL 2 is unavailable. Enable hardware acceleration or try a recent desktop browser.'); | |
| this.resources = []; | |
| this.cubeP = this.program(VS, FS); | |
| this.lineP = this.program(LINE_VS, LINE_FS); | |
| this.pointP = this.program(POINT_VS, POINT_FS); | |
| const verts = []; | |
| const faces = [[[1, 0, 0], [[.5, -.5, -.5], [.5, .5, -.5], [.5, .5, .5], [.5, -.5, .5]]], [[-1, 0, 0], [[-.5, -.5, .5], [-.5, .5, .5], [-.5, .5, -.5], [-.5, -.5, -.5]]], [[0, 1, 0], [[-.5, .5, .5], [.5, .5, .5], [.5, .5, -.5], [-.5, .5, -.5]]], [[0, -1, 0], [[-.5, -.5, -.5], [.5, -.5, -.5], [.5, -.5, .5], [-.5, -.5, .5]]], [[0, 0, 1], [[.5, -.5, .5], [.5, .5, .5], [-.5, .5, .5], [-.5, -.5, .5]]], [[0, 0, -1], [[-.5, -.5, -.5], [-.5, .5, -.5], [.5, .5, -.5], [.5, -.5, -.5]]]]; | |
| faces.forEach( ([n,vs]) => [0, 1, 2, 0, 2, 3].forEach(i => verts.push(...vs[i], ...n))); | |
| this.cubeVAO = gl.createVertexArray(); | |
| gl.bindVertexArray(this.cubeVAO); | |
| this.geo = this.buffer(); | |
| gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(verts), gl.STATIC_DRAW); | |
| this.attr(0, 3, 24, 0); | |
| this.attr(1, 3, 24, 12); | |
| this.instances = this.buffer(); | |
| for (const [loc,n,off] of [[2, 3, 0], [3, 3, 12], [4, 3, 24], [5, 1, 36], [6, 1, 40]]) { | |
| this.attr(loc, n, 44, off); | |
| gl.vertexAttribDivisor(loc, 1); | |
| } | |
| this.lineVAO = gl.createVertexArray(); | |
| gl.bindVertexArray(this.lineVAO); | |
| this.lines = this.buffer(); | |
| this.attr(0, 3, 28, 0); | |
| this.attr(1, 4, 28, 12); | |
| this.pointVAO = gl.createVertexArray(); | |
| gl.bindVertexArray(this.pointVAO); | |
| this.points = this.buffer(); | |
| this.attr(0, 3, 32, 0); | |
| this.attr(1, 4, 32, 12); | |
| this.attr(2, 1, 32, 28); | |
| gl.bindVertexArray(null); | |
| } | |
| buffer() { | |
| const b = this.gl.createBuffer(); | |
| this.gl.bindBuffer(this.gl.ARRAY_BUFFER, b); | |
| this.resources.push(b); | |
| return b; | |
| } | |
| attr(i, n, stride, offset) { | |
| const g = this.gl; | |
| g.enableVertexAttribArray(i); | |
| g.vertexAttribPointer(i, n, g.FLOAT, false, stride, offset); | |
| } | |
| program(v, f) { | |
| const g = this.gl; | |
| let p = g.createProgram(); | |
| for (const [type,s] of [[g.VERTEX_SHADER, v], [g.FRAGMENT_SHADER, f]]) { | |
| let sh = g.createShader(type); | |
| g.shaderSource(sh, s); | |
| g.compileShader(sh); | |
| if (!g.getShaderParameter(sh, g.COMPILE_STATUS)) | |
| throw Error(g.getShaderInfoLog(sh)); | |
| g.attachShader(p, sh); | |
| g.deleteShader(sh); | |
| } | |
| g.linkProgram(p); | |
| if (!g.getProgramParameter(p, g.LINK_STATUS)) | |
| throw Error(g.getProgramInfoLog(p)); | |
| return p; | |
| } | |
| resize(w, h) { | |
| this.dpr = Math.min(devicePixelRatio || 1, 2); | |
| this.canvas.width = Math.max(1, Math.round(w * this.dpr)); | |
| this.canvas.height = Math.max(1, Math.round(h * this.dpr)); | |
| this.gl.viewport(0, 0, this.canvas.width, this.canvas.height); | |
| } | |
| render(boxes, lines, points, vp, eye) { | |
| const g = this.gl; | |
| g.clearColor(0, 0, 0, 0); | |
| g.clear(g.COLOR_BUFFER_BIT | g.DEPTH_BUFFER_BIT); | |
| g.enable(g.DEPTH_TEST); | |
| g.disable(g.CULL_FACE); | |
| g.disable(g.BLEND); | |
| g.depthMask(true); | |
| const data = new Float32Array(boxes.length * 11); | |
| boxes.forEach( (b, i) => data.set([...b.pos, ...b.size, ...b.color, b.glow || 0, b.style || 0], i * 11)); | |
| g.useProgram(this.cubeP); | |
| g.uniformMatrix4fv(g.getUniformLocation(this.cubeP, 'vp'), false, vp); | |
| g.uniform3fv(g.getUniformLocation(this.cubeP, 'eye'), eye); | |
| g.bindVertexArray(this.cubeVAO); | |
| g.bindBuffer(g.ARRAY_BUFFER, this.instances); | |
| g.bufferData(g.ARRAY_BUFFER, data, g.DYNAMIC_DRAW); | |
| g.drawArraysInstanced(g.TRIANGLES, 0, 36, boxes.length); | |
| g.enable(g.BLEND); | |
| g.blendFunc(g.SRC_ALPHA, g.ONE_MINUS_SRC_ALPHA); | |
| g.depthMask(false); | |
| if (lines.length) { | |
| g.useProgram(this.lineP); | |
| g.uniformMatrix4fv(g.getUniformLocation(this.lineP, 'vp'), false, vp); | |
| g.bindVertexArray(this.lineVAO); | |
| g.bindBuffer(g.ARRAY_BUFFER, this.lines); | |
| g.bufferData(g.ARRAY_BUFFER, new Float32Array(lines), g.DYNAMIC_DRAW); | |
| g.drawArrays(g.LINES, 0, lines.length / 7); | |
| } | |
| if (points.length) { | |
| g.blendFunc(g.SRC_ALPHA, g.ONE); | |
| g.useProgram(this.pointP); | |
| g.uniformMatrix4fv(g.getUniformLocation(this.pointP, 'vp'), false, vp); | |
| g.uniform1f(g.getUniformLocation(this.pointP, 'dpr'), this.dpr); | |
| g.bindVertexArray(this.pointVAO); | |
| g.bindBuffer(g.ARRAY_BUFFER, this.points); | |
| g.bufferData(g.ARRAY_BUFFER, new Float32Array(points), g.DYNAMIC_DRAW); | |
| g.drawArrays(g.POINTS, 0, points.length / 8); | |
| } | |
| g.depthMask(true); | |
| g.bindVertexArray(null); | |
| } | |
| dispose() { | |
| const g = this.gl; | |
| this.resources.forEach(b => g.deleteBuffer(b)); | |
| [this.cubeP, this.lineP, this.pointP].forEach(p => g.deleteProgram(p)); | |
| [this.cubeVAO, this.lineVAO, this.pointVAO].forEach(v => g.deleteVertexArray(v)); | |
| } | |
| } | |
| class AtlasEngine { | |
| constructor(canvas, labelRoot, tip, callbacks, state) { | |
| this.canvas = canvas; | |
| this.labelRoot = labelRoot; | |
| this.tip = tip; | |
| this.cb = callbacks; | |
| this.state = state; | |
| try { | |
| this.renderer = new GLRenderer(canvas); | |
| } catch (error) { | |
| this.renderer = new CanvasRenderer(canvas); | |
| this.fallbackReason = error.message; | |
| } | |
| this.items = new Map; | |
| this.expands = new Map; | |
| this.time = state.time || 0; | |
| this.clock = this.time; | |
| this.last = performance.now(); | |
| this.labelPool = []; | |
| this.hover = null; | |
| this.lastTick = 0; | |
| this.phaseIndex = 0; | |
| this.frameNo = 0; | |
| this.deck = state.layout === 'split' ? 1 : 0; | |
| this.reduced = matchMedia('(prefers-reduced-motion: reduce)').matches; | |
| this.cam = { | |
| theta: .34, | |
| phi: 1.11, | |
| dist: 49, | |
| target: [-3, 7.8, 0] | |
| }; | |
| this.to = { | |
| ...this.cam, | |
| target: [...this.cam.target] | |
| }; | |
| this.lastView = state.view; | |
| this.fov = 42 * Math.PI / 180; | |
| this.disposers = []; | |
| this.pointers = new Map; | |
| this.mx = -1; | |
| this.my = -1; | |
| this.bind(); | |
| this.ro = new ResizeObserver( () => this.resize()); | |
| this.ro.observe(canvas.parentElement); | |
| this.resize(); | |
| this.reset(true); | |
| this.loop = this.loop.bind(this); | |
| this.raf = requestAnimationFrame(this.loop); | |
| } | |
| resize() { | |
| const r = this.canvas.parentElement.getBoundingClientRect(); | |
| this.w = Math.max(1, r.width); | |
| this.h = Math.max(1, r.height); | |
| this.renderer.resize(this.w, this.h); | |
| if (this.layoutCache) | |
| this.layoutCache.clear(); | |
| if (this.state.selected && this.state.view === 'architecture') | |
| this.focus(this.state.selected); | |
| else if (this.to) | |
| this.reset(); | |
| } | |
| sync(s) { | |
| const old = this.state; | |
| this.state = s; | |
| if (old.view !== s.view) { | |
| this.lastView = s.view; | |
| this.items.clear(); | |
| this.expands.clear(); | |
| this.hover = null; | |
| this.reset(); | |
| } | |
| if (old.fontScale !== s.fontScale && this.layoutCache) | |
| this.layoutCache.clear(); | |
| if (s.view === 'architecture' && s.selected && (old.selected !== s.selected || old.detailLayout !== s.detailLayout || old.expertView !== s.expertView || old.precision !== s.precision || old.layout !== s.layout || old.fontScale !== s.fontScale)) | |
| this.focus(s.selected); | |
| if (s.view === 'architecture' && ((old.selected && !s.selected) || old.layout !== s.layout || old.fontScale !== s.fontScale) && !s.selected) | |
| this.reset(); | |
| } | |
| reset(instant=false) { | |
| const view = this.state.view | |
| , wide = this.w / Math.max(1, this.h) | |
| , f = Math.tan(this.fov / 2) | |
| , reserve = Math.max(1.1, this.h / Math.max(150, this.h - 115)) | |
| , split = this.state.layout === 'split'; | |
| let dest = view === 'storage' ? { | |
| theta: .37, | |
| phi: 1.12, | |
| dist: wide < 1 ? 58 : 40, | |
| target: [-1.7, 2.2, 0] | |
| } : view === 'cache' ? { | |
| theta: .1, | |
| phi: 1.3, | |
| dist: wide < 1 ? 16 : 11.6, | |
| target: [0, .25, 0] | |
| } : { | |
| theta: .30, | |
| phi: 1.25, | |
| dist: Math.max((split ? 25 : 35) / (2 * f) * reserve, (split ? 27 : 20) / (2 * f * wide) * 1.12), | |
| target: split ? [-2.4, 8.5, 0] : [-1.8, 14.7, 0] | |
| }; | |
| dest.dist = clamp(dest.dist, 10, 160); | |
| this.to = dest; | |
| if (instant) | |
| this.cam = { | |
| ...dest, | |
| target: [...dest.target] | |
| }; | |
| } | |
| focus(id) { | |
| const m = MODULES.find(x => x.id === id); | |
| if (!m) | |
| return; | |
| const p = this.modulePos(m, this.state.layout === 'split' ? 1 : 0) | |
| , plan = this.tensorLayout(m, this.state.precision) | |
| , aspect = this.w / Math.max(1, this.h) | |
| , f = Math.tan(this.fov / 2) | |
| , reserve = this.h / Math.max(150, this.h - 155) | |
| , floor = this.state.detailLayout === 'floor' && !plan.grid; | |
| let height = floor ? plan.height * .9 + 3 : plan.height + 1 | |
| , width = plan.grid ? plan.width + 2 : floor ? plan.width + 2 : plan.width + 7; | |
| let dist = Math.max(height / (2 * f) * reserve, width / (2 * f * aspect) * 1.08); | |
| this.to = { | |
| theta: floor ? .16 : .035, | |
| phi: floor ? .30 : 1.43, | |
| dist: clamp(dist, 9, 150), | |
| target: [p[0], p[1] + (floor ? 0 : 1), p[2] + (floor ? 0 : 2.8)] | |
| }; | |
| } | |
| seek(t) { | |
| this.time = clamp(t, 0, 21.999); | |
| this.clock = this.time; | |
| this.cb.tick(this.time); | |
| } | |
| modulePos(m, deck=this.deck) { | |
| // The same module IDs travel between layer-order and functional layouts. | |
| // Horizontal separation happens first, so the two decks never pass through each other. | |
| let tower, split; | |
| if (m.id === 'embed') { | |
| tower = [0, -1.3, 0]; | |
| split = [-4.8, -1.3, 0]; | |
| } else if (m.id === 'head') { | |
| tower = [0, 27.7, 0]; | |
| split = [5.4, 16.0, 0]; | |
| } else if (m.id === 'vision') { | |
| tower = [-7, -1.15, 1.5]; | |
| split = [-10, -1.1, 1.5]; | |
| } else if (m.id === 'aligner') { | |
| tower = [-3.8, -1.15, 1.5]; | |
| split = [-7, -1.1, 1.5]; | |
| } else if (m.id[0] === 'E') { | |
| tower = [-8.0, m.index * .67, -4.6]; | |
| split = [-12.3, m.index === 1 ? 3.2 : 10.5, -3.5]; | |
| } else if (m.id[0] === 'D') { | |
| tower = [0, 29.1 + (m.index - 40) * .73, 0]; | |
| split = [5.4, 17.6 + (m.index - 40) * .73, 0]; | |
| } else { | |
| tower = [0, m.index * .67, 0]; | |
| split = [m.index < 20 ? -4.8 : 5.4, (m.index % 20) * .75, 0]; | |
| } | |
| const index = m.mode ? m.index % 20 : 0 | |
| , delay = index / 19 * .10 | |
| , u = clamp((deck - delay) / (.9), 0, 1) | |
| , x = smooth(clamp(u / .48, 0, 1)) | |
| , y = smooth(clamp((u - .35) / .65, 0, 1)); | |
| return [lerp(tower[0], split[0], x), lerp(tower[1], split[1], y), lerp(tower[2], split[2], x)]; | |
| } | |
| on(el, type, fn, opts) { | |
| el.addEventListener(type, fn, opts); | |
| this.disposers.push( () => el.removeEventListener(type, fn, opts)); | |
| } | |
| bind() { | |
| let c = this.canvas; | |
| this.on(c, 'contextmenu', e => e.preventDefault()); | |
| this.on(c, 'pointerdown', e => { | |
| e.preventDefault(); | |
| c.focus({ | |
| preventScroll: true | |
| }); | |
| c.setPointerCapture(e.pointerId); | |
| this.pointers.set(e.pointerId, { | |
| x: e.clientX, | |
| y: e.clientY | |
| }); | |
| this.drag = { | |
| x: e.clientX, | |
| y: e.clientY, | |
| startX: e.clientX, | |
| startY: e.clientY, | |
| moved: false, | |
| pan: e.button === 1 || e.button === 2 || e.shiftKey, | |
| touch: e.pointerType === 'touch' | |
| }; | |
| if (this.pointers.size > 1) | |
| this.drag.moved = true; | |
| } | |
| ); | |
| this.on(c, 'pointermove', e => { | |
| let r = c.getBoundingClientRect(); | |
| this.mx = e.clientX - r.left; | |
| this.my = e.clientY - r.top; | |
| if (!this.pointers.has(e.pointerId)) | |
| return; | |
| let previous = [...this.pointers.values()]; | |
| this.pointers.set(e.pointerId, { | |
| x: e.clientX, | |
| y: e.clientY | |
| }); | |
| let current = [...this.pointers.values()]; | |
| if (current.length === 2) { | |
| let pd = Math.hypot(previous[0].x - previous[1].x, previous[0].y - previous[1].y) | |
| , nd = Math.hypot(current[0].x - current[1].x, current[0].y - current[1].y); | |
| if (nd > 1) | |
| this.to.dist = clamp(this.to.dist * pd / nd, 2.5, 200); | |
| this.pan((current[0].x + current[1].x - previous[0].x - previous[1].x) / 2, (current[0].y + current[1].y - previous[0].y - previous[1].y) / 2); | |
| if (this.drag) | |
| this.drag.moved = true; | |
| return; | |
| } | |
| if (!this.drag) | |
| return; | |
| const d = this.drag | |
| , dx = e.clientX - d.x | |
| , dy = e.clientY - d.y; | |
| if (Math.hypot(e.clientX - d.startX, e.clientY - d.startY) > 4) | |
| d.moved = true; | |
| if (d.pan) | |
| this.pan(dx, dy); | |
| else { | |
| this.to.theta -= dx * .005; | |
| this.to.phi = clamp(this.to.phi - dy * .005, .18, 2.86); | |
| } | |
| d.x = e.clientX; | |
| d.y = e.clientY; | |
| } | |
| ); | |
| const end = e => { | |
| let d = this.drag; | |
| this.pointers.delete(e.pointerId); | |
| if (this.pointers.size === 0) { | |
| if (d && !d.moved && !d.pan && e.type !== 'pointercancel') { | |
| let r = c.getBoundingClientRect(); | |
| const hit = this.pick(e.clientX - r.left, e.clientY - r.top); | |
| if (hit) { | |
| this.cb.select(hit.meta.module || null, hit.meta.tensor || null, hit.meta.expert); | |
| } else | |
| this.cb.select(null); | |
| } | |
| this.drag = null; | |
| } else if (d) { | |
| let p = [...this.pointers.values()][0]; | |
| d.x = p.x; | |
| d.y = p.y; | |
| d.moved = true; | |
| } | |
| } | |
| ; | |
| this.on(c, 'pointerup', end); | |
| this.on(c, 'pointercancel', end); | |
| this.on(c, 'lostpointercapture', () => { | |
| if (!this.pointers.size) | |
| this.drag = null; | |
| } | |
| ); | |
| this.on(c, 'pointerleave', () => { | |
| if (!this.drag) { | |
| this.mx = -1; | |
| this.my = -1; | |
| this.hover = null; | |
| this.tip.style.display = 'none'; | |
| } | |
| } | |
| ); | |
| this.on(c, 'wheel', e => { | |
| e.preventDefault(); | |
| this.to.dist = clamp(this.to.dist * Math.exp(clamp(e.deltaY, -200, 200) * .0013), 2.5, 200); | |
| } | |
| , { | |
| passive: false | |
| }); | |
| this.on(c, 'dblclick', () => this.reset()); | |
| this.on(c, 'webglcontextlost', e => { | |
| e.preventDefault(); | |
| cancelAnimationFrame(this.raf); | |
| this.cb.error('The graphics context was lost. Reload this file to restore the view.'); | |
| } | |
| ); | |
| } | |
| pan(dx, dy) { | |
| const s = this.to.dist * 2 * Math.tan(this.fov / 2) / Math.max(1, this.h) | |
| , th = this.to.theta | |
| , ph = this.to.phi; | |
| const right = [Math.cos(th), 0, -Math.sin(th)] | |
| , up = [-Math.cos(ph) * Math.sin(th), Math.sin(ph), -Math.cos(ph) * Math.cos(th)]; | |
| this.to.target = this.to.target.map( (v, i) => v - dx * s * right[i] + dy * s * up[i]); | |
| } | |
| camera(dt) { | |
| let a = this.reduced ? 1 : 1 - Math.exp(-dt * 9) | |
| , c = this.cam | |
| , t = this.to; | |
| if (this.state.orbit && !this.drag && this.state.playing) | |
| t.theta += dt * .06; | |
| for (const k of ['theta', 'phi', 'dist']) | |
| c[k] = lerp(c[k], t[k], a); | |
| c.target = c.target.map( (v, i) => lerp(v, t.target[i], a)); | |
| const off = [c.dist * Math.sin(c.phi) * Math.sin(c.theta), c.dist * Math.cos(c.phi), c.dist * Math.sin(c.phi) * Math.cos(c.theta)]; | |
| this.eye = V3.add(c.target, off); | |
| this.forward = V3.norm(V3.sub(c.target, this.eye)); | |
| this.right = V3.norm(V3.cross(this.forward, [0, 1, 0])); | |
| this.up = V3.cross(this.right, this.forward); | |
| this.vp = matMul(perspective(this.fov, this.w / this.h, .03, 500), lookAt(this.eye, c.target)); | |
| } | |
| project(p) { | |
| let m = this.vp | |
| , x = p[0] | |
| , y = p[1] | |
| , z = p[2] | |
| , w = m[3] * x + m[7] * y + m[11] * z + m[15]; | |
| if (w <= 0) | |
| return null; | |
| let sx = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w | |
| , sy = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w; | |
| return [(sx + 1) * this.w * .5, (1 - sy) * this.h * .5, w]; | |
| } | |
| pick(x, y) { | |
| if (!this.boxes || !this.eye) | |
| return null; | |
| let yy = (1 - 2 * y / this.h) * Math.tan(this.fov / 2) | |
| , xx = (2 * x / this.w - 1) * this.w / this.h * Math.tan(this.fov / 2); | |
| const d = V3.norm(V3.add(this.forward, V3.add(V3.scale(this.right, xx), V3.scale(this.up, yy)))); | |
| let best = Infinity | |
| , hit = null; | |
| for (const b of this.boxes) { | |
| if (!b.meta || b.meta.noPick) | |
| continue; | |
| let mn = 0 | |
| , mx = Infinity; | |
| for (let k = 0; k < 3; k++) { | |
| let half = b.size[k] / 2; | |
| if (Math.abs(d[k]) < 1e-9) { | |
| if (this.eye[k] < b.pos[k] - half || this.eye[k] > b.pos[k] + half) { | |
| mx = -1; | |
| break; | |
| } | |
| continue; | |
| } | |
| let a = (b.pos[k] - half - this.eye[k]) / d[k] | |
| , v = (b.pos[k] + half - this.eye[k]) / d[k]; | |
| mn = Math.max(mn, Math.min(a, v)); | |
| mx = Math.min(mx, Math.max(a, v)); | |
| } | |
| if (mx >= mn && mn < best) { | |
| best = mn; | |
| hit = b; | |
| } | |
| } | |
| let close = null | |
| , near = 8; | |
| for (const b of this.boxes) { | |
| if (!b.meta || !b.meta.weight || b.meta.module !== this.state.selected) | |
| continue; | |
| const p = this.project(b.pos); | |
| if (!p) | |
| continue; | |
| const d = Math.hypot(p[0] - x, p[1] - y); | |
| if (d < near) { | |
| near = d; | |
| close = b; | |
| } | |
| } | |
| return close || hit; | |
| } | |
| emit(id, pos, byteCount, color, meta, opt={}) { | |
| const targetSide = Math.cbrt(Math.max(0, byteCount) / VOLUME_UNIT) | |
| , sx = opt.sx || targetSide | |
| , sz = opt.sz || targetSide; | |
| let rec = this.items.get(id); | |
| if (!rec || rec.lastUsed < this.frameNo - 1) { | |
| let from = opt.from; | |
| rec = { | |
| pos: from ? [...from.pos] : [...pos], | |
| bytes: byteCount, | |
| sx: from ? from.sx : sx, | |
| sz: from ? from.sz : sz | |
| }; | |
| this.items.set(id, rec); | |
| } | |
| rec.lastUsed = this.frameNo; | |
| const a = this.reduced ? 1 : this.damp; | |
| rec.pos = rec.pos.map( (v, i) => lerp(v, pos[i], a)); | |
| rec.bytes = lerp(rec.bytes, byteCount, a); | |
| rec.sx = lerp(rec.sx, sx, a); | |
| rec.sz = lerp(rec.sz, sz, a); | |
| let sy = rec.bytes / VOLUME_UNIT / (rec.sx * rec.sz); | |
| const dim = opt.dim || 1 | |
| , co = (typeof color === 'string' ? rgb(color) : color).map(v => v * dim); | |
| const b = { | |
| id, | |
| pos: [...rec.pos], | |
| size: [rec.sx, sy, rec.sz], | |
| color: co, | |
| glow: opt.glow || 0, | |
| style: opt.style || 0, | |
| meta, | |
| bytes: rec.bytes | |
| }; | |
| if (this.hover && this.hover.id === id) | |
| b.glow += .3; | |
| this.boxes.push(b); | |
| return b; | |
| } | |
| line(a, b, color, alpha=.4) { | |
| const c = typeof color === 'string' ? rgb(color) : color; | |
| this.lines.push(...a, ...c, alpha, ...b, ...c, alpha); | |
| } | |
| point(p, color, size=8, alpha=1) { | |
| let c = typeof color === 'string' ? rgb(color) : color; | |
| this.points.push(...p, ...c, alpha, size); | |
| } | |
| curve(a, d, color, alpha=.4, phase=0, bend=1.6, particles=1) { | |
| let b = [a[0] + bend, a[1], a[2] + .3] | |
| , c = [d[0] - bend, d[1], d[2] + .3] | |
| , prev = a; | |
| for (let k = 1; k <= 48; k++) { | |
| let p = cubic(a, b, c, d, k / 48); | |
| this.line(prev, p, color, alpha); | |
| prev = p; | |
| } | |
| for (let n = 0; n < particles; n++) { | |
| let t = ((this.clock * .19 + phase + n / particles) % 1 + 1) % 1; | |
| this.point(cubic(a, b, c, d, t), color, 9, alpha * 2); | |
| } | |
| } | |
| label(pos, title, sub='', color=COL.muted, priority=0) { | |
| this.labels.push({ | |
| pos, | |
| title, | |
| sub, | |
| color, | |
| priority | |
| }); | |
| } | |
| layerHeat(i) { | |
| let p = phaseAt(this.time, this.state.activity) | |
| , t = (this.time - p.from) / (p.to - p.from); | |
| if (this.state.activity === 'training') { | |
| if (p.name === 'Forward') | |
| return Math.exp(-Math.pow((i / 40 - t) * 7, 2)) * .8; | |
| if (p.name === 'Backward') | |
| return Math.exp(-Math.pow((i / 40 - (1 - t)) * 7, 2)) * .8; | |
| if (p.name === 'Update') | |
| return .25 + .18 * Math.sin(t * 6.28 + i * .18); | |
| return i > 36 ? .35 : .05; | |
| } | |
| if (p.name === 'Prefill') | |
| return i < 20 ? Math.exp(-Math.pow((i / 20 - t) * 7, 2)) * .7 : 0; | |
| if (p.name === 'SWA replay') | |
| return i >= 20 ? .22 + .12 * Math.sin(i * .5 + this.clock * 4) : .03; | |
| if (p.name === 'Decode') | |
| return Math.exp(-Math.pow((i / 40 - (t * 2 % 1)) * 8, 2)) * .7; | |
| return i >= 37 ? .3 : 0; | |
| } | |
| tensorLayout(m, mode) { | |
| const state = this.state | |
| , fs = state.fontScale || 1 | |
| , key = [m.id, mode, state.detailLayout, state.expertView, fs, Math.round(this.w), Math.round(this.h)].join('|'); | |
| this.layoutCache = this.layoutCache || new Map; | |
| if (this.layoutCache.has(key)) | |
| return this.layoutCache.get(key); | |
| const ws = orderWeights(m) | |
| , layer = !!m.mode || m.id.startsWith('D'); | |
| const entries = ws.map( (w, index) => { | |
| const vol = wBytes(w, mode) / VOLUME_UNIT | |
| , a = w.shape.length > 1 ? clamp(Math.sqrt(w.shape[w.shape.length - 1] / w.shape[0]), .65, 1.7) : 1 | |
| , side = Math.cbrt(vol) | |
| , sx = side * a * 1.15 | |
| , sz = side / a | |
| , sy = vol / (sx * sz); | |
| return { | |
| w, | |
| index, | |
| sx, | |
| sy, | |
| sz, | |
| lane: layer ? (isAttentionTensor(w) ? 0 : 1) : index % 2, | |
| rel: [0, 0, 0] | |
| }; | |
| } | |
| ); | |
| let height = 0 | |
| , width = 10 | |
| , headings = []; | |
| if (state.detailLayout === 'floor') { | |
| const cols = layer ? (this.w < 600 ? 2 : 4) : Math.min(3, entries.length) | |
| , rows = Math.ceil(entries.length / cols) | |
| , cellZ = Math.max(2.8, ...entries.map(e => e.sz + .8)) | |
| , cellX = Math.max(3.6, ...entries.map(e => e.sx + .8), cellZ * (rows / cols) * (this.w / this.h) * 1.26); | |
| entries.forEach( (e, i) => { | |
| e.rel = [(i % cols - (cols - 1) / 2) * cellX, e.sy / 2, (Math.floor(i / cols) - (rows - 1) / 2) * cellZ]; | |
| } | |
| ); | |
| height = rows * cellZ; | |
| width = cols * cellX; | |
| headings = [{ | |
| pos: [0, 1, -rows * cellZ / 2 - 1], | |
| text: 'INDIVIDUAL STORED TENSORS', | |
| sub: 'Each solid is a named tensor or an explicitly counted bank.' | |
| }]; | |
| } else { | |
| const laneGap = layer ? 7.4 : Math.max(6, ...entries.map(e => e.sx + e.sz + 1)); | |
| for (let lane = 0; lane < 2; lane++) { | |
| let list = entries.filter(e => e.lane === lane) | |
| , total = list.reduce( (a, e) => a + Math.max(e.sy + .24, .59 * fs), 0); | |
| height = Math.max(height, total); | |
| let y = total / 2; | |
| for (const e of list) { | |
| const step = Math.max(e.sy + .24, .59 * fs); | |
| e.rel = [(lane - .5) * laneGap, y - step / 2, 2.8]; | |
| y -= step; | |
| } | |
| } | |
| for (let lane = 0; lane < 2; lane++) { | |
| const list = entries.filter(e => e.lane === lane); | |
| if (list.length) | |
| headings.push({ | |
| pos: [(lane - .5) * laneGap, height / 2 + .65, 2.8], | |
| text: layer ? (lane === 0 ? '01 / ATTENTION + RESIDUAL' : '02 / EXPERTS + RESIDUAL') : (lane === 0 ? 'STORED WEIGHTS' : 'SUPPORTING TENSORS'), | |
| sub: list.length + ' separately modeled tensors' | |
| }); | |
| } | |
| width = laneGap + 5; | |
| height += 1.6; | |
| } | |
| const grid = state.expertView && layer; | |
| if (grid) { | |
| const n = m.id.startsWith('D') ? 128 : 384 | |
| , ex = ws.filter(w => w.cat === 'expert') | |
| , expB = sumB(ex, mode) / n | |
| , side = Math.cbrt(expB / VOLUME_UNIT) | |
| , spacing = side + .105 | |
| , cols = n === 128 ? 16 : 24 | |
| , rows = n / cols; | |
| let others = entries.filter(e => e.w.cat !== 'expert'); | |
| let maxY = 0; | |
| [0, 1].forEach(lane => { | |
| let list = others.filter(e => e.lane === lane) | |
| , total = list.length * .59 * fs; | |
| maxY = Math.max(maxY, total); | |
| list.forEach( (e, i) => { | |
| e.rel = [lane === 0 ? -7.0 : -4.2, total / 2 - (i + .5) * .59 * fs, 2.8]; | |
| } | |
| ); | |
| } | |
| ); | |
| const center = [3.4, 0, 2.8]; | |
| height = Math.max(maxY + 2, rows * spacing + 4.8); | |
| width = 22; | |
| headings = [{ | |
| pos: [-5.6, maxY / 2 + .8, 2.8], | |
| text: 'LAYER-LOCAL TENSORS', | |
| sub: 'Hover or click a marker for the exact weight.' | |
| }, { | |
| pos: [center[0], rows * spacing / 2 + .65, 2.8], | |
| text: n + ' EXPERTS / TOP-' + (n === 128 ? 3 : 6), | |
| sub: 'Each cube holds gate, up and down. Click to split one.' | |
| }]; | |
| const plan = { | |
| entries, | |
| height, | |
| width, | |
| headings, | |
| grid: { | |
| n, | |
| ex, | |
| side, | |
| spacing, | |
| cols, | |
| rows, | |
| center, | |
| expB | |
| }, | |
| layer | |
| }; | |
| this.layoutCache.set(key, plan); | |
| return plan; | |
| } | |
| const plan = { | |
| entries, | |
| height, | |
| width, | |
| headings, | |
| grid: null, | |
| layer | |
| }; | |
| this.layoutCache.set(key, plan); | |
| return plan; | |
| } | |
| stackKey(m) { | |
| if (m.id === 'embed') | |
| return { | |
| chain: 0, | |
| index: -1 | |
| }; | |
| if (m.mode) | |
| return { | |
| chain: m.index < 20 ? 0 : 1, | |
| index: m.index | |
| }; | |
| if (m.id === 'head') | |
| return { | |
| chain: 1, | |
| index: 40 | |
| }; | |
| if (m.id.startsWith('D')) | |
| return { | |
| chain: 1, | |
| index: m.index + 1 | |
| }; | |
| return null; | |
| } | |
| viewPos(m) { | |
| let p = this.modulePos(m) | |
| , mk = this.stackKey(m); | |
| for (const [id,e] of this.expands) { | |
| if (e < .002 || m.id === id) | |
| continue; | |
| const other = MODULES.find(x => x.id === id); | |
| if (!other) | |
| continue; | |
| const ok = this.stackKey(other) | |
| , plan = this.tensorLayout(other, this.state.precision); | |
| if (mk && ok) { | |
| const together = mk.chain === ok.chain ? 1 : 1 - this.deck; | |
| p[1] += (mk.index < ok.index ? -1 : 1) * (plan.height / 2 + 1.25) * e * together; | |
| if (mk.chain !== ok.chain) | |
| p[0] += (mk.chain === 0 ? -1 : 1) * 4.5 * e * this.deck; | |
| } | |
| if (m.id.startsWith('E') && ok) { | |
| p[0] -= 3.5 * e; | |
| p[2] -= 2.5 * e; | |
| } | |
| } | |
| return p; | |
| } | |
| architecture() { | |
| const s = this.state | |
| , mode = s.precision | |
| , selection = s.selected; | |
| for (const m of MODULES) { | |
| let x = this.expands.get(m.id) || 0 | |
| , d = selection === m.id ? 1 : 0; | |
| x = this.reduced ? d : lerp(x, d, this.damp * .65); | |
| if (Math.abs(x - d) < .0008) | |
| x = d; | |
| this.expands.set(m.id, x); | |
| } | |
| this.framePositions = new Map; | |
| for (const m of MODULES) { | |
| const p = this.viewPos(m); | |
| this.framePositions.set(m.id, p); | |
| const b = sumB(m.ws, mode) | |
| , col = m.part === 'encoder' ? COL.enc : m.part === 'decoder' ? COL.dec : m.id[0] === 'E' ? TC.engram : m.id[0] === 'D' ? TC.mhc : m.id === 'vision' || m.id === 'aligner' ? TC.vision : COL.head; | |
| const isLayer = !!m.mode || m.id[0] === 'D' | |
| , x = this.expands.get(m.id) || 0; | |
| if (x > .0001) { | |
| this.detailed(m, p, x, mode); | |
| continue; | |
| } | |
| let dim = selection ? .20 : 1; | |
| const ph = phaseAt(this.time, this.state.activity).name; | |
| if (!selection && ph === 'Prefill' && (m.part === 'decoder' || m.id === 'head' || m.id[0] === 'D')) | |
| dim = .32; | |
| const f = Math.sqrt(b / sumB(m.ws, 'native')); | |
| let opt = isLayer ? { | |
| sx: 4 * f, | |
| sz: 3.5 * f, | |
| glow: m.index < 40 ? this.layerHeat(m.index) : ph === 'DSpark' ? .42 : 0, | |
| dim | |
| } : m.id[0] === 'E' ? { | |
| glow: .02, | |
| style: 2, | |
| dim | |
| } : ['embed', 'head'].includes(m.id) ? { | |
| sx: 4 * f, | |
| sz: 3.5 * f, | |
| dim | |
| } : m.id === 'vision' ? { | |
| sx: 1.25, | |
| sz: .8, | |
| dim, | |
| style: 3 | |
| } : m.id === 'aligner' ? { | |
| sx: .65, | |
| sz: .65, | |
| dim | |
| } : { | |
| dim | |
| }; | |
| const ob = this.emit(m.id, p, b, col, { | |
| module: m.id, | |
| label: m.label, | |
| bytes: b | |
| }, opt); | |
| if (isLayer) { | |
| const c = COL[m.mode] || TC.mhc; | |
| this.line([ob.pos[0] - ob.size[0] / 2, ob.pos[1] + ob.size[1] / 2, ob.pos[2] + ob.size[2] / 2], [ob.pos[0] + ob.size[0] / 2, ob.pos[1] + ob.size[1] / 2, ob.pos[2] + ob.size[2] / 2], c, selection ? .17 : .8); | |
| if (!selection && (s.labels || m.id[0] === 'D')) | |
| this.label([ob.pos[0] + ob.size[0] / 2 + .35, ob.pos[1], ob.pos[2]], m.id[0] === 'D' ? 'D' + (m.index - 39) : String(m.index).padStart(2, '0'), s.labels ? m.mode || 'draft' : '', c, 1); | |
| } else if (!selection) { | |
| if (m.id[0] === 'E') | |
| this.label([p[0] - ob.size[0] / 2 - 1.3, p[1], p[2] + 1], m.label, bytes(b, s.binary), col, 6); | |
| else | |
| this.label([p[0], p[1] + ob.size[1] / 2 + .5, p[2]], m.label, bytes(b, s.binary), col, 2); | |
| } | |
| } | |
| if (selection) | |
| return; | |
| const pos = id => this.framePositions.get(id) | |
| , split = this.deck > .85; | |
| if (split) { | |
| this.label(V3.add(pos('L19'), [-.4, 1.5, 2]), 'ENCODER / L00-L19', 'First 20 layers / runs in decode', COL.enc, 8); | |
| this.label(V3.add(pos('L36'), [4.2, 0, 1]), 'DECODER / L20-L39', 'Next 20 layers / runs in decode', COL.dec, 8); | |
| } else { | |
| this.label(V3.add(pos('D2'), [0, 1, 0]), 'ONE CHECKPOINT / L00-L39', 'Ordered layers, not physical shard offsets.', COL.muted, 9); | |
| } | |
| const boundary = V3.add(pos('L20'), [-3.2, 0, 1.3]); | |
| this.label(boundary, 'L19 -> L20', phaseAt(this.time, s.activity).name === 'Prefill' ? 'Optimized prefill: prepare decoder KV' : 'Forward pass continues. No model swap.', COL.full, 7); | |
| // Each layer gets a small address label; the side rail remains the accessible list. | |
| if (!this.state.labels) | |
| for (const m of LAYERS) { | |
| const p = pos(m.id); | |
| this.label(V3.add(p, [2.5, 0, 1.9]), 'L' + String(m.index).padStart(2, '0'), '', m.index < 20 ? COL.enc : COL.dec, 1); | |
| } | |
| if (!s.links) | |
| return; | |
| this.curve(pos('vision'), pos('aligner'), TC.vision, .5, .4, .4); | |
| this.curve(pos('aligner'), pos('embed'), TC.vision, .5, .2, .6); | |
| for (const e of ENGRAM) { | |
| const p = pos(e.id) | |
| , lp = pos('L' + e.index); | |
| this.curve(V3.add(p, [2.3, 0, 0]), V3.add(lp, [-2, 0, 0]), TC.engram, .4, e.index * .03, 2, 2); | |
| } | |
| this.curve(V3.add(pos('L19'), [-2, .3, 0]), V3.add(pos('L20'), [-2, 0, 0]), COL.full, .42, .3, split ? 7 : 3, 2); | |
| if (split) { | |
| const a = V3.add(pos('L19'), [0, 0, 2.15]) | |
| , b = V3.add(pos('L20'), [0, 0, 2.15]) | |
| , v = V3.norm(V3.sub(b, a)) | |
| , side = V3.norm(V3.cross(v, [0, 0, 1])); | |
| this.line(a, b, COL.dec, .55); | |
| const tip = a.map( (x, i) => lerp(x, b[i], .56)); | |
| this.line(tip, tip.map( (x, i) => x - v[i] * .65 + side[i] * .28), COL.dec, .8); | |
| this.line(tip, tip.map( (x, i) => x - v[i] * .65 - side[i] * .28), COL.dec, .8); | |
| } | |
| CT.kv_source_layer_ids.forEach( (id, k) => { | |
| const p = pos('L' + id) | |
| , cp = [p[0] + 3.8, p[1] + .2, -2.6] | |
| , n = id < 20 ? Math.floor(s.context / 2) : s.context | |
| , kv = n * 356; | |
| this.emit('cache:' + id, cp, kv, COL.full, { | |
| module: 'L' + id, | |
| label: 'L' + id + ' global KV activations', | |
| bytes: kv, | |
| noPick: true | |
| }, { | |
| glow: .14 | |
| }); | |
| this.curve([p[0] + 2, p[1], 0], cp, COL.full, .24, k * .15, .65); | |
| } | |
| ); | |
| const ph = phaseAt(this.time, s.activity) | |
| , progress = (this.time - ph.from) / (ph.to - ph.from) | |
| , train = s.activity === 'training'; | |
| let ids = ph.name === 'Prefill' ? Array.from({ | |
| length: 20 | |
| }, (_, i) => 'L' + i) : ph.name === 'SWA replay' ? Array.from({ | |
| length: 20 | |
| }, (_, i) => 'L' + (20 + i)) : ['embed', ...LAYERS.map(m => m.id), 'head']; | |
| if (train && ph.name === 'Backward') | |
| ids = ids.slice().reverse(); | |
| if (['Prefill', 'SWA replay', 'Decode', 'Forward', 'Backward'].includes(ph.name)) { | |
| for (let lane = 0; lane < 4; lane++) { | |
| const offset = [-.7 + lane * .46, 0, 2.15]; | |
| for (let i = 1; i < ids.length; i++) | |
| this.line(V3.add(pos(ids[i - 1]), offset), V3.add(pos(ids[i]), offset), ph.color, .16); | |
| const frac = train ? clamp(progress + lane * .009, 0, .999) : ((progress * (ph.name === 'Decode' ? 2 : 1) + lane * .045) % 1) | |
| , at = frac * (ids.length - 1) | |
| , lo = Math.floor(at) | |
| , hi = Math.min(lo + 1, ids.length - 1) | |
| , a = pos(ids[lo]) | |
| , b = pos(ids[hi]); | |
| this.point(a.map( (v, k) => lerp(v, b[k], at - lo) + offset[k]), ph.color, 9, .9); | |
| } | |
| } | |
| if (train) { | |
| const hp = pos('head'); | |
| if (ph.name === 'Loss') | |
| this.label(V3.add(hp, [4, .6, 0]), 'PREDICTION vs TARGET', 'Loss (schematic)', COL.engram, 10); | |
| if (ph.name === 'Backward') | |
| this.label(V3.add(pos('L20'), [4, 0, 0]), 'GRADIENTS', 'Backward path, not reverse inference.', COL.mhc, 10); | |
| if (ph.name === 'Update') | |
| this.label(V3.add(pos('L20'), [4, 0, 0]), 'UPDATE VALUES', 'Shapes and modeled bytes stay fixed.', COL.full, 10); | |
| } | |
| if (!train && ph.name === 'DSpark') | |
| for (let i = 0; i < 3; i++) | |
| this.curve(pos('L' + (37 + i)), pos('D' + i), TC.mhc, .55, i * .3, 3, 2); | |
| } | |
| detailed(m, p, t, mode) { | |
| const plan = this.tensorLayout(m, mode) | |
| , ws = plan.entries | |
| , total = sumB(m.ws, mode) | |
| , f = Math.sqrt(total / sumB(m.ws, 'native')) | |
| , layer = plan.layer; | |
| let sx0 = layer || ['embed', 'head'].includes(m.id) ? 4 * f : m.id === 'vision' ? 1.25 : m.id === 'aligner' ? .65 : Math.cbrt(total / VOLUME_UNIT) | |
| , sz0 = layer || ['embed', 'head'].includes(m.id) ? 3.5 * f : m.id === 'vision' ? .8 : m.id === 'aligner' ? .65 : sx0; | |
| const height0 = total / VOLUME_UNIT / (sx0 * sz0); | |
| let startY = p[1] - height0 / 2; | |
| const selected = m.id === this.state.selected | |
| , focusName = this.state.hoverTensor || this.state.tensor; | |
| for (const e of ws) { | |
| const w = e.w; | |
| if (plan.grid && w.cat === 'expert') | |
| continue; | |
| const b = wBytes(w, mode) | |
| , sliceH = b / VOLUME_UNIT / (sx0 * sz0) | |
| , from = { | |
| pos: [p[0], startY + sliceH / 2, p[2]], | |
| sx: sx0, | |
| sz: sz0 | |
| }; | |
| startY += sliceH; | |
| const local = clamp(t * 1.16 - e.index / ws.length * .16, 0, 1) | |
| , a = smooth(local) | |
| , dest = V3.add(p, e.rel) | |
| , pos = from.pos.map( (v, i) => lerp(v, dest[i], a)) | |
| , sx = Math.exp(lerp(Math.log(sx0), Math.log(e.sx), a)) | |
| , sz = Math.exp(lerp(Math.log(sz0), Math.log(e.sz), a)); | |
| const hot = selected && focusName === w.name | |
| , ob = this.emit(m.id + ':w:' + w.name, pos, b, tensorColor(w), { | |
| module: m.id, | |
| tensor: w.name, | |
| label: tensorInfo(w, m).title, | |
| bytes: b, | |
| weight: w | |
| }, { | |
| sx, | |
| sz, | |
| from, | |
| glow: hot ? .75 : w.cat === 'shared' ? .17 : .03, | |
| dim: selected ? 1 : .35 | |
| }); | |
| const tiny = w.p < 100000; | |
| const sc = this.project(ob.pos); | |
| if (selected && t > .4 && sc) { | |
| const pixels = Math.max(...ob.size) * this.h / (2 * Math.tan(this.fov / 2) * sc[2]); | |
| if (pixels < 6) | |
| this.point(ob.pos, tensorColor(w), hot ? 9 : 6, hot ? 1 : .8); | |
| } | |
| if (selected && t > .57 && (!plan.grid || hot)) { | |
| this.detailLabels.push({ | |
| m, | |
| w, | |
| ob, | |
| lane: e.lane, | |
| order: e.index, | |
| tiny, | |
| hot, | |
| layout: this.state.detailLayout | |
| }); | |
| } | |
| } | |
| if (plan.grid) { | |
| const g = plan.grid | |
| , top = new Set(pickExperts(m.index * 1000 + Math.floor(this.clock * .65), g.n, g.n === 128 ? 3 : 6)) | |
| , center = V3.add(p, g.center) | |
| , exParts = g.ex.map(w => ({ | |
| w, | |
| b: wBytes(w, mode) / g.n | |
| })); | |
| let expertFocus = this.state.expert; | |
| for (let i = 0; i < g.n; i++) { | |
| const dest = [center[0] + (i % g.cols - (g.cols - 1) / 2) * g.spacing, center[1] + ((g.rows - 1) / 2 - Math.floor(i / g.cols)) * g.spacing, center[2]] | |
| , a = smooth(t) | |
| , origin = [p[0], startY + (i + .5) * g.expB / VOLUME_UNIT / (sx0 * sz0), p[2]] | |
| , pos = origin.map( (v, k) => lerp(v, dest[k], a)); | |
| if (selected && expertFocus === i) { | |
| let sum = 0; | |
| for (let j = 0; j < exParts.length; j++) { | |
| let item = exParts[j] | |
| , w = { | |
| ...item.w, | |
| count: 1, | |
| p: item.w.p / g.n | |
| } | |
| , h = item.b / VOLUME_UNIT / (g.side * g.side) | |
| , from = { | |
| pos: [pos[0], pos[1] - g.side / 2 + sum + h / 2, pos[2]], | |
| sx: g.side, | |
| sz: g.side | |
| }; | |
| sum += h; | |
| const side = Math.cbrt(item.b / VOLUME_UNIT) | |
| , d = [center[0] + (j - 1) * 1.3, center[1] - g.rows * g.spacing / 2 - 1.4, center[2] + .3] | |
| , ob = this.emit(m.id + ':ex:' + i + ':' + w.name, d, item.b, tensorColor(w), { | |
| module: m.id, | |
| expert: i, | |
| tensor: w.name, | |
| label: 'Expert ' + i + ' / ' + tensorInfo(w, m).title, | |
| bytes: item.b, | |
| weight: w | |
| }, { | |
| sx: side * 1.2, | |
| sz: side / 1.2, | |
| from, | |
| glow: .5 | |
| }); | |
| if (t > .65) | |
| this.label([ob.pos[0], ob.pos[1] - .42, ob.pos[2]], w.name.includes('.w1.') ? 'W1 / gate' : w.name.includes('.w3.') ? 'W3 / up' : 'W2 / down', bytes(item.b, this.state.binary), tensorColor(w), 8); | |
| } | |
| const ring = g.side * .62; | |
| this.line([dest[0] - ring, dest[1] - ring, dest[2]], [dest[0] + ring, dest[1] - ring, dest[2]], TC.expert, .5); | |
| continue; | |
| } | |
| const f0 = { | |
| pos: origin, | |
| sx: sx0, | |
| sz: sz0 | |
| } | |
| , ss = Math.exp(lerp(Math.log(sx0), Math.log(g.side), a)) | |
| , zz = Math.exp(lerp(Math.log(sz0), Math.log(g.side), a)); | |
| const ob = this.emit(m.id + ':ex:' + i, pos, g.expB, top.has(i) ? TC.shared : TC.expert, { | |
| module: m.id, | |
| expert: i, | |
| label: 'Expert ' + String(i).padStart(3, '0'), | |
| bytes: g.expB, | |
| active: top.has(i) | |
| }, { | |
| sx: ss, | |
| sz: zz, | |
| from: f0, | |
| glow: top.has(i) ? .65 : 0, | |
| dim: top.has(i) ? 1 : .6 | |
| }); | |
| if (selected && t > .85 && top.has(i) && this.state.links) { | |
| const source = V3.add(p, [-4.2, 3.4, 2.8]); | |
| this.line(source, ob.pos, TC.router, .16); | |
| this.point(ob.pos, TC.shared, 6, .8); | |
| } | |
| } | |
| } | |
| if (selected && t > .6) { | |
| if (this.w >= 600) | |
| for (const h of plan.headings) | |
| this.label(V3.add(p, h.pos), h.text, h.sub, plan.grid ? TC.expert : COL.muted, 9); | |
| if (this.state.links && !plan.grid && this.state.detailLayout === 'column') { | |
| // Only genuine data dependencies are animated. Parameter ordering is not an execution trace. | |
| const get = n => this.boxes.find(b => b.id === m.id + ':w:' + n); | |
| const pairs = [['attn.wq_a.weight', 'attn.q_norm.weight'], ['attn.q_norm.weight', 'attn.wq_b.weight'], ['attn.wo_a.weight', 'attn.wo_b.weight'], ['ffn.gate.weight', 'ffn.experts.w1.weight'], ['ffn.gate.weight', 'ffn.experts.w3.weight'], ['ffn.experts.w1.weight', 'ffn.experts.w2.weight'], ['ffn.experts.w3.weight', 'ffn.experts.w2.weight']]; | |
| for (let i = 0; i < pairs.length; i++) { | |
| const a = get(pairs[i][0]) | |
| , b = get(pairs[i][1]); | |
| if (a && b) { | |
| let p1 = V3.add(a.pos, [0, 0, a.size[2] / 2 + .12]) | |
| , p2 = V3.add(b.pos, [0, 0, b.size[2] / 2 + .12]); | |
| this.line(p1, p2, TC.q, .16); | |
| this.point(p1.map( (v, k) => lerp(v, p2[k], (this.clock * .4 + i * .23) % 1)), TC.q, 6, .7); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| commitDetailLabels() { | |
| this.detailPool = this.detailPool || []; | |
| this.detailLines = this.detailLines || []; | |
| if (!this.leaderSvg) { | |
| this.leaderSvg = document.createElementNS('http://www.w3.org/2000/svg', 'svg'); | |
| this.leaderSvg.classList.add('tensor-leaders'); | |
| this.labelRoot.prepend(this.leaderSvg); | |
| } | |
| const fs = this.state.fontScale || 1 | |
| , small = this.w < 600 | |
| , grid = this.state.expertView | |
| , floor = this.state.detailLayout === 'floor' | |
| , raw = this.detailLabels.filter(l => this.project(l.ob.pos)); | |
| let queue = []; | |
| const tools = document.querySelector('.expansion-tools') | |
| , world = this.canvas.getBoundingClientRect() | |
| , toolsBottom = tools ? tools.getBoundingClientRect().bottom - world.top : 80; | |
| const top = Math.max(100, toolsBottom + 32) | |
| , bottom = this.h - 43 | |
| , available = Math.max(30, bottom - top); | |
| if (!floor && !grid) { | |
| for (let lane = 0; lane < 2; lane++) { | |
| let list = raw.filter(l => l.lane === lane).map(l => ({ | |
| ...l, | |
| sc: this.project(l.ob.pos) | |
| })).sort( (a, b) => a.sc[1] - b.sc[1]); | |
| const compactHeight = Math.ceil((small ? 13 : 15) * fs) + 4 | |
| , fullHeight = Math.ceil(28 * fs) + 5; | |
| const compact = list.reduce( (a, l) => a + (l.tiny ? compactHeight : fullHeight) + 3, 0) > available; | |
| // Do not shrink fonts to squeeze in more labels. Subsample only labels when necessary; | |
| // all weight solids and the full inspector inventory remain available for picking. | |
| const cap = Math.max(1, Math.floor(available / (compactHeight + 3))); | |
| if (compact && list.length > cap) { | |
| let keep = new Set(); | |
| list.forEach( (l, i) => { | |
| if (l.hot) | |
| keep.add(i); | |
| } | |
| ); | |
| for (let k = 0; keep.size < cap && k < cap; k++) | |
| keep.add(Math.round(k * (list.length - 1) / Math.max(1, cap - 1))); | |
| list = list.filter( (l, i) => keep.has(i)); | |
| } | |
| for (const l of list) { | |
| l.h = compact || l.tiny ? compactHeight : fullHeight; | |
| l.compact = compact || l.tiny; | |
| } | |
| let previous = top; | |
| for (const l of list) { | |
| l.y = Math.max(previous, l.sc[1] - l.h / 2); | |
| previous = l.y + l.h + 3; | |
| } | |
| if (list.length) { | |
| const excess = Math.max(0, list[list.length - 1].y + list[list.length - 1].h - bottom); | |
| for (let i = list.length - 1; i >= 0; i--) { | |
| list[i].y -= excess; | |
| if (i < list.length - 1) | |
| list[i].y = Math.min(list[i].y, list[i + 1].y - list[i].h - 3); | |
| } | |
| if (list[0].y < top) { | |
| const add = top - list[0].y; | |
| list.forEach(l => l.y += add); | |
| } | |
| } | |
| const width = small ? Math.min(150 * fs, this.w * .34) : Math.min(270 * fs, this.w * .27); | |
| for (const l of list) { | |
| const px = l.ob.size[0] * this.h / (2 * Math.tan(this.fov / 2) * l.sc[2]); | |
| l.width = width; | |
| l.x = lane === 0 ? l.sc[0] - px / 2 - width - 18 : l.sc[0] + px / 2 + 18; | |
| l.x = clamp(l.x, 8, this.w - width - 8); | |
| queue.push(l); | |
| } | |
| } | |
| } else { | |
| const sorted = raw.map(l => ({ | |
| ...l, | |
| sc: this.project(l.ob.pos) | |
| })).sort( (a, b) => Number(b.hot) - Number(a.hot) || a.sc[1] - b.sc[1]) | |
| , placed = []; | |
| const cols = small ? 2 : 4 | |
| , width = floor && !grid ? Math.min(235 * fs, this.w / cols * .82) : Math.min(195 * fs, this.w * (small ? .32 : .22)) | |
| , h = Math.ceil((small ? 14 : 28) * fs) + 5; | |
| for (const l of sorted) { | |
| if (l.sc[0] < 0 || l.sc[0] > this.w || l.sc[1] < top - 20 || l.sc[1] > bottom) | |
| continue; | |
| let x = clamp(l.sc[0] - width / 2, 7, this.w - width - 7) | |
| , y = l.sc[1] + 10; | |
| for (let j = 0; j < 20 && placed.some(r => x < r.x + r.width + 3 && x + width + 3 > r.x && y < r.y + r.h + 3 && y + h + 3 > r.y); j++) | |
| y += h + 3; | |
| if (y + h > bottom) | |
| continue; | |
| const q = { | |
| ...l, | |
| x, | |
| y, | |
| width, | |
| h, | |
| compact: small | |
| }; | |
| placed.push(q); | |
| queue.push(q); | |
| } | |
| } | |
| let count = 0; | |
| for (const l of queue) { | |
| let node = this.detailPool[count] | |
| , line = this.detailLines[count]; | |
| if (!node) { | |
| node = document.createElement('button'); | |
| node.type = 'button'; | |
| node.className = 'tensor-label'; | |
| const a = document.createElement('b') | |
| , b = document.createElement('span'); | |
| node.append(a, b); | |
| this.labelRoot.append(node); | |
| node.addEventListener('pointerenter', () => { | |
| this.labelHover = true; | |
| const d = node._datum; | |
| this.cb.hover && this.cb.hover(d.w.name); | |
| } | |
| ); | |
| node.addEventListener('pointerleave', () => { | |
| this.labelHover = false; | |
| this.cb.hover && this.cb.hover(null); | |
| } | |
| ); | |
| node.addEventListener('click', e => { | |
| e.stopPropagation(); | |
| const d = node._datum; | |
| this.cb.select(d.m.id, d.w.name, null); | |
| } | |
| ); | |
| this.detailPool.push(node); | |
| line = document.createElementNS('http://www.w3.org/2000/svg', 'path'); | |
| this.leaderSvg.append(line); | |
| this.detailLines.push(line); | |
| } | |
| node._datum = l; | |
| node.children[0].textContent = tensorShort(l.w) + (l.w.count > 1 ? ' \u00d7' + l.w.count : ''); | |
| node.children[1].textContent = bytes(wBytes(l.w, this.state.precision), this.state.binary) + ' / ' + wFormat(l.w, this.state.precision); | |
| node.title = tensorInfo(l.w, l.m).title + '\n[' + l.w.shape.join(', ') + ']\nClick to pin the explanation'; | |
| node.className = 'tensor-label' + (l.compact ? ' compact' : '') + (l.hot ? ' hot' : ''); | |
| node.style.cssText = 'display:block;left:' + l.x + 'px;top:' + l.y + 'px;width:' + l.width + 'px;height:' + l.h + 'px;--tensor:' + tensorColor(l.w); | |
| node.setAttribute('aria-label', l.w.name + ', ' + node.children[1].textContent + ', inspect'); | |
| const endX = l.sc[0] < l.x ? l.x : l.sc[0] > l.x + l.width ? l.x + l.width : clamp(l.sc[0], l.x, l.x + l.width) | |
| , endY = l.y + l.h / 2; | |
| line.setAttribute('d', 'M' + l.sc[0] + ',' + l.sc[1] + ' L' + (l.sc[0] + endX) / 2 + ',' + endY + ' L' + endX + ',' + endY); | |
| line.setAttribute('stroke', tensorColor(l.w)); | |
| line.setAttribute('opacity', l.hot ? '.85' : '.32'); | |
| line.style.display = 'block'; | |
| count++; | |
| } | |
| for (let i = count; i < this.detailPool.length; i++) { | |
| this.detailPool[i].style.display = 'none'; | |
| this.detailLines[i].style.display = 'none'; | |
| } | |
| } | |
| focusTensor(name) { | |
| const ob = this.boxes.find(b => b.meta && b.meta.module === this.state.selected && b.meta.tensor === name); | |
| if (!ob) | |
| return; | |
| this.to.target = [...ob.pos]; | |
| this.to.dist = Math.max(3.8, Math.max(...ob.size) * 8); | |
| this.to.theta = .35; | |
| this.to.phi = 1.1; | |
| } | |
| diagnostics() { | |
| const model = new Map; | |
| let maxError = 0; | |
| for (const b of this.boxes) { | |
| const v = b.size.reduce( (a, n) => a * n, 1) * VOLUME_UNIT; | |
| maxError = Math.max(maxError, Math.abs(v - b.bytes) / Math.max(1, b.bytes)); | |
| if (!b.meta || b.id.startsWith('cache:') || b.id.startsWith('kv:')) | |
| continue; | |
| let id = b.meta.module; | |
| if (MODULES.some(m => m.id === id)) | |
| model.set(id, (model.get(id) || 0) + b.bytes); | |
| } | |
| return { | |
| precision: this.state.precision, | |
| selected: this.state.selected, | |
| solidCount: this.boxes.length, | |
| maxRelativeVolumeError: maxError, | |
| modules: [...model].map( ([id,actual]) => ({ | |
| id, | |
| actual, | |
| expected: sumB(MODULES.find(m => m.id === id).ws, this.state.precision) | |
| })), | |
| visibleTensorLabels: (this.detailPool || []).filter(n => n.style.display !== 'none').length | |
| }; | |
| } | |
| storage() { | |
| let s = this.state | |
| , rSide = Math.cbrt(sumB(CATEGORIES[0][3], s.precision) / VOLUME_UNIT) | |
| , eSide = Math.cbrt(sumB(CATEGORIES[1][3], s.precision) / VOLUME_UNIT) | |
| , coords = [[-2.6 - rSide / 2, 0, 0], [-1.4 + eSide / 2, 0, 0], [-1.4 + eSide + 2.4, 0, -2.1], [-1.4 + eSide + 5.4, 0, -2.1], [-1.4 + eSide + 5.4, 0, 2], [-1.4 + eSide + 2.4, 0, 2], [-4, 0, Math.max(rSide, eSide) / 2 + 2.4], [.6, 0, Math.max(rSide, eSide) / 2 + 2.4]]; | |
| for (let i = 0; i < CATEGORIES.length; i++) { | |
| let[id,name,col,ws] = CATEGORIES[i] | |
| , b = sumB(ws, s.precision) | |
| , side = Math.cbrt(b / VOLUME_UNIT) | |
| , p = [coords[i][0], side / 2, coords[i][2]]; | |
| let ob = this.emit('category:' + id, p, b, col, { | |
| module: 'cat:' + id, | |
| label: name, | |
| bytes: b | |
| }, { | |
| style: id === 'engram' ? 2 : 0, | |
| glow: id === 'routed' ? .06 : 0 | |
| }); | |
| this.label([p[0], ob.size[1] + .6, p[2]], name, bytes(b, s.binary) + ' / ' + (b / TOTALS[s.precision] * 100).toFixed(1) + '%', col, 3); | |
| } | |
| for (let i = -16; i <= 14; i += 2) { | |
| this.line([i, -.02, -6], [i, -.02, 9], '#38516a', .14); | |
| this.line([-16, -.02, i / 2], [14, -.02, i / 2], '#38516a', .14); | |
| } | |
| this.label([-2, Math.max(10, rSide + 1.5), -3], 'THE CHECKPOINT, BY VOLUME', 'One cubic world unit = one decimal GB. No category is enlarged.', COL.muted, 8); | |
| } | |
| cache() { | |
| const s = this.state | |
| , ids = [2, 8, 14, 20]; | |
| ids.forEach( (id, i) => { | |
| let n = id < 20 ? Math.floor(s.context / 2) : s.context | |
| , b = n * 356 | |
| , p = [(i - 1.5) * 1.45, 1.55, 0] | |
| , side = Math.cbrt(b / VOLUME_UNIT); | |
| this.emit('kv:' + id, p, b, i === 3 ? COL.dec : COL.full, { | |
| module: 'L' + id, | |
| label: 'L' + id + ' global KV + index keys', | |
| bytes: b | |
| }, { | |
| glow: .12 | |
| }); | |
| this.label([p[0], 2.0 + side / 2, 0], 'L' + String(id).padStart(2, '0'), (id < 20 ? '2:1 / ' : '1:1 / ') + bytes(b, s.binary), i === 3 ? COL.dec : COL.full, 5); | |
| if (s.links) | |
| this.curve([p[0], 1.2, 0], [0, -.2, 0], i === 3 ? COL.dec : COL.full, .3, i * .18, .6, 2); | |
| } | |
| ); | |
| const selected = pickExperts(Math.floor(this.clock * .4), 2048, 512) | |
| , set = new Set(selected); | |
| for (let i = 0; i < 2048; i++) { | |
| let x = (i % 64 - 31.5) * .077 | |
| , y = -.5 - Math.floor(i / 64) * .052; | |
| this.point([x, y, 0], set.has(i) ? COL.dec : '#4a6280', set.has(i) ? 3.3 : 2, set.has(i) ? .72 : .22); | |
| } | |
| this.label([0, -2.53, 0], '2,048 CANDIDATE BLOCKS x 8', 'A schematic pool; the true cap is 16,384 candidate positions.', COL.muted, 7); | |
| this.label([0, -3.23, 0], 'TOP-512 POSITIONS PER QUERY', '512 illuminated sample marks illustrate sparse selection.', COL.dec, 6); | |
| this.label([0, 3.25, 0], 'FOUR SHARED GLOBAL BANKS', '890 B / original token across the model, not per layer.', COL.full, 8); | |
| } | |
| commitLabels() { | |
| let placed = [] | |
| , use = 0; | |
| const q = this.labels.map(l => ({ | |
| ...l, | |
| screen: this.project(l.pos) | |
| })).filter(l => l.screen && l.screen[0] > -60 && l.screen[0] < this.w + 60 && l.screen[1] > 15 && l.screen[1] < this.h - 30).sort( (a, b) => b.priority - a.priority); | |
| for (const l of q) { | |
| let[x,y] = l.screen; | |
| const fs = this.state.fontScale || 1; | |
| let width = Math.min(420, Math.max(l.title.length * 6.7, l.sub.length * 5.25) * fs + 16) | |
| , h = (l.sub ? 33 : 18) * fs; | |
| if (x - width / 2 < 8 || x + width / 2 > this.w - 8) | |
| continue; | |
| let rect = { | |
| x: x - width / 2, | |
| y: y - h / 2, | |
| w: width, | |
| h | |
| }; | |
| if (placed.some(r => rect.x < r.x + r.w + 7 && rect.x + rect.w + 7 > r.x && rect.y < r.y + r.h + 4 && rect.y + rect.h + 4 > r.y)) | |
| continue; | |
| if (this.state.view === 'architecture' && y < 95 && x < 430) | |
| continue; | |
| placed.push(rect); | |
| let node = this.labelPool[use++]; | |
| if (!node) { | |
| node = document.createElement('div'); | |
| node.className = 'world-label'; | |
| let a = document.createElement('b') | |
| , b = document.createElement('span'); | |
| node.append(a, b); | |
| this.labelRoot.append(node); | |
| this.labelPool.push(node); | |
| } | |
| if (node.children[0].textContent !== l.title) | |
| node.children[0].textContent = l.title; | |
| if (node.children[1].textContent !== l.sub) | |
| node.children[1].textContent = l.sub; | |
| node.children[0].style.color = l.color; | |
| node.style.transform = `translate(${x}px,${y}px) translate(-50%,-50%)`; | |
| node.style.display = 'block'; | |
| } | |
| for (let i = use; i < this.labelPool.length; i++) | |
| this.labelPool[i].style.display = 'none'; | |
| } | |
| updateTooltip() { | |
| if (this.labelHover) { | |
| this.tip.style.display = 'none'; | |
| return; | |
| } | |
| const h = !this.drag && this.mx >= 0 ? this.pick(this.mx, this.my) : null; | |
| this.hover = h; | |
| const name = h && h.meta.weight ? h.meta.tensor : null; | |
| if (name !== this.lastHoverName) { | |
| this.lastHoverName = name; | |
| this.cb.hover && this.cb.hover(name); | |
| } | |
| if (!h) { | |
| this.tip.style.display = 'none'; | |
| this.canvas.style.cursor = this.drag ? 'grabbing' : 'grab'; | |
| return; | |
| } | |
| this.canvas.style.cursor = 'pointer'; | |
| const m = h.meta | |
| , mod = MODULES.find(x => x.id === m.module) | |
| , info = m.weight ? tensorInfo(m.weight, mod) : mod ? moduleExplanation(mod) : null; | |
| this.tip.style.display = 'block'; | |
| this.tip.children[0].textContent = m.weight ? info.title : m.label; | |
| this.tip.children[0].style.color = m.weight ? tensorColor(m.weight) : '#d6e8ff'; | |
| this.tip.children[1].textContent = (m.weight ? '[' + m.weight.shape.map(num).join(', ') + '] / ' : '') + bytes(m.bytes, this.state.binary) + (m.expert !== undefined ? ' / expert ' + m.expert : ' / click to pin'); | |
| this.tip.children[2].textContent = m.weight ? info.job : m.expert !== undefined ? 'One specialist holds three matrices: gate, up and down. Click to separate them. Routing is illustrative; light changes, volume does not.' : info ? info.body : ''; | |
| const width = Math.min(330, this.w - 16) | |
| , height = this.tip.offsetHeight || 170; | |
| this.tip.style.width = width + 'px'; | |
| this.tip.style.left = clamp(this.mx + 17, 8, this.w - width - 8) + 'px'; | |
| this.tip.style.top = clamp(this.my + 18, 8, this.h - height - 8) + 'px'; | |
| } | |
| loop(now) { | |
| if (document.hidden) { | |
| this.last = now; | |
| this.raf = requestAnimationFrame(this.loop); | |
| return; | |
| } | |
| if (this.w < 600 && this.lastRendered && now - this.lastRendered < 32) { | |
| this.raf = requestAnimationFrame(this.loop); | |
| return; | |
| } | |
| this.lastRendered = now; | |
| this.frameNo++; | |
| let dt = Math.min(.05, Math.max(.001, (now - this.last) / 1000)); | |
| this.last = now; | |
| this.damp = 1 - Math.exp(-dt * 10); | |
| if (this.state.playing && !document.hidden) { | |
| this.clock += dt * this.state.speed; | |
| const phase = phaseAt(this.time, this.state.activity) | |
| , next = this.time + dt * this.state.speed; | |
| this.time = this.state.loopPhase ? phase.from + (next - phase.from) % (phase.to - phase.from) : next % 22; | |
| } | |
| const targetDeck = this.state.layout === 'split' ? 1 : 0; | |
| this.deck = this.reduced ? targetDeck : clamp(this.deck + Math.sign(targetDeck - this.deck) * Math.min(Math.abs(targetDeck - this.deck), dt / 1.35), 0, 1); | |
| this.camera(dt); | |
| this.boxes = []; | |
| this.lines = []; | |
| this.points = []; | |
| this.labels = []; | |
| this.detailLabels = []; | |
| if (this.state.view === 'storage') | |
| this.storage(); | |
| else if (this.state.view === 'cache') | |
| this.cache(); | |
| else | |
| this.architecture(); | |
| this.updateTooltip(); | |
| this.renderer.render(this.boxes, this.lines, this.points, this.vp, this.eye); | |
| this.commitLabels(); | |
| this.commitDetailLabels(); | |
| if (now - this.lastTick > 140) { | |
| this.cb.tick(this.time); | |
| this.lastTick = now; | |
| } | |
| this.raf = requestAnimationFrame(this.loop); | |
| } | |
| snapshot() { | |
| return this.canvas.toDataURL('image/png'); | |
| } | |
| dispose() { | |
| cancelAnimationFrame(this.raf); | |
| this.ro.disconnect(); | |
| this.disposers.forEach(f => f()); | |
| this.renderer.dispose(); | |
| this.labelRoot.replaceChildren(); | |
| } | |
| } | |
| // ---------- app.js ---------- | |
| const {h, render, Component, createRef} = Preact; | |
| const html = htm.bind(h); | |
| const ICONS = { | |
| play: 'M8 5L19 12L8 19Z', | |
| pause: 'M8 5V19M16 5V19', | |
| reset: 'M3 10A9 9 0 1 1 5 19M3 4V10H9', | |
| expand: 'M4 9V4H9M15 4H20V9M20 15V20H15M9 20H4V15', | |
| close: 'M6 6L18 18M18 6L6 18', | |
| help: 'M9 8A3 3 0 1 1 14 10C12 11 12 12 12 14M12 17V18', | |
| link: 'M9 15L15 9M7 14L5 16A3 3 0 0 0 9 20L12 17M12 7L15 4A3 3 0 0 1 19 8L17 10', | |
| labels: 'M4 5H20M12 5V20M8 20H16', | |
| download: 'M12 3V16M7 11L12 16L17 11M4 17V21H20V17', | |
| camera: 'M4 7H8L10 4H15L17 7H21V20H3V7ZM16 13A4 4 0 1 1 8 13A4 4 0 1 1 16 13', | |
| arrow: 'M6 12H18M13 7L18 12L13 17', | |
| chevron: 'M8 5L15 12L8 19', | |
| menu: 'M4 6H20M4 12H20M4 18H20', | |
| up: 'M6 15L12 9L18 15', | |
| down: 'M6 9L12 15L18 9' | |
| }; | |
| function Icon({name, size=18}) { | |
| return html`<svg width=${size} height=${size} viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d=${ICONS[name] || ICONS.help}/></svg>`; | |
| } | |
| function Section({title, children, tag}) { | |
| return html`<section class="section"><div class="section-head"><h3>${title}</h3>${tag && html`<span>${tag}</span>`}</div>${children}</section>`; | |
| } | |
| function Fact({label, value, color}) { | |
| return html`<div class="fact"><span>${label}</span><b style=${color ? { | |
| color | |
| } : null}>${value}</b></div>`; | |
| } | |
| function Note({children, color=COL.enc}) { | |
| return html`<div class="note" style=${{ | |
| '--note': color | |
| }}>${children}</div>`; | |
| } | |
| function Chip({children, color=COL.enc}) { | |
| return html`<span class="chip" style=${{ | |
| color, | |
| borderColor: color + '55', | |
| background: color + '0c' | |
| }}>${children}</span>`; | |
| } | |
| function safeDownload(name, content, type='application/json') { | |
| const blob = content instanceof Blob ? content : new Blob([content],{ | |
| type | |
| }) | |
| , url = URL.createObjectURL(blob) | |
| , a = document.createElement('a'); | |
| a.href = url; | |
| a.download = name; | |
| a.click(); | |
| setTimeout( () => URL.revokeObjectURL(url), 2000); | |
| } | |
| function moduleExplanation(m) { | |
| if (!m) | |
| return EXP.overview; | |
| if (m.mode) | |
| return EXP[m.mode]; | |
| if (m.id[0] === 'E') | |
| return EXP.engram; | |
| if (m.id[0] === 'D') | |
| return EXP.dspark; | |
| if (m.id === 'vision' || m.id === 'aligner') | |
| return EXP.vision; | |
| return m.id === 'head' ? { | |
| title: 'The untied output vocabulary.', | |
| body: 'The 5,120-dimensional representation projects to 129,280 logits. The output weights are separate from the input embedding. Disk storage is BF16 in this estimate; reference-runtime F32 expansion is deliberately not charged to the checkpoint.' | |
| } : { | |
| title: 'A shared text-and-image stream.', | |
| body: 'A 129,280 by 5,120 BF16 table supplies text embeddings. The vision projector supplies vectors of the same width. Each token travels through four mHC residual streams; those streams do not multiply the stored embedding weights.' | |
| }; | |
| } | |
| function describeFormat(w) { | |
| return w.note || ({ | |
| fp4: 'Packed E2M1, two logical values per byte, with one E8M0 scale for every 32 values in the native layout.', | |
| fp8: 'FP8 weights with one one-byte E8M0 scale per 32 by 32 weight block.', | |
| fp8row: 'FP8 data with one one-byte scale per group of 32 values in each row.', | |
| bf16: 'Two bytes per logical value in the native storage model.', | |
| f32: 'Four bytes per logical value in the native storage model.' | |
| }[w.format] || 'See the embedded configuration and reference implementation.'); | |
| } | |
| function resolveAuditModule(name) { | |
| let n = name.replace(/^(?:model\.|language_model\.|text_model\.)+/, ''); | |
| let a = n.match(/^layers\.(\d+)\.engram\./); | |
| if (a) | |
| return 'E' + a[1]; | |
| a = n.match(/^layers\.(\d+)\./); | |
| if (a) | |
| return +a[1] < 40 ? 'L' + a[1] : 'D' + (+a[1] - 40); | |
| a = n.match(/^mtp\.(?:layers\.)?(\d+)\./); | |
| if (a) | |
| return 'D' + a[1]; | |
| if (/^(mtp\.|dspark\.)/.test(n)) | |
| return 'DSpark unassigned'; | |
| if (/^(embed\.|embed_tokens\.)/.test(n)) | |
| return 'embed'; | |
| if (/^(head\.|lm_head\.|norm\.)/.test(n)) | |
| return 'head'; | |
| if (/^(vision\.|vision_model\.|vision_encoder\.)/.test(n)) | |
| return 'vision'; | |
| if (/^(aligner\.|projector\.|image_)/.test(n)) | |
| return 'aligner'; | |
| return 'Unmapped'; | |
| } | |
| async function auditSafetensors(file) { | |
| if (file.size < 10) | |
| throw Error(file.name + ': too short for a Safetensors header.'); | |
| const lead = await file.slice(0, 8).arrayBuffer() | |
| , n = new DataView(lead).getBigUint64(0, true); | |
| if (n < 2n || n > 67108864n || Number(n) > file.size - 8) | |
| throw Error(file.name + ': invalid or unusually large header (limit 64 MiB).'); | |
| let header; | |
| try { | |
| header = JSON.parse(await file.slice(8, 8 + Number(n)).text()); | |
| } catch (e) { | |
| throw Error(file.name + ': header is not valid JSON.'); | |
| } | |
| const base = 8 + Number(n) | |
| , intervals = [] | |
| , groups = Object.create(null) | |
| , dtypes = Object.create(null) | |
| , tensors = []; | |
| let payload = 0; | |
| for (const [name,w] of Object.entries(header)) { | |
| if (name === '__metadata__') | |
| continue; | |
| if (!w || !Array.isArray(w.data_offsets) || w.data_offsets.length !== 2 || !Array.isArray(w.shape) || typeof w.dtype !== 'string') | |
| throw Error('Invalid tensor descriptor: ' + name); | |
| const [start,end] = w.data_offsets; | |
| if (!Number.isSafeInteger(start) || !Number.isSafeInteger(end) || start < 0 || end < start || base + end > file.size) | |
| throw Error('Out-of-bounds tensor: ' + name); | |
| const b = end - start; | |
| intervals.push([start, end]); | |
| payload += b; | |
| const group = resolveAuditModule(name); | |
| groups[group] = (groups[group] || 0) + b; | |
| dtypes[w.dtype] = (dtypes[w.dtype] || 0) + b; | |
| tensors.push({ | |
| name, | |
| dtype: w.dtype, | |
| shape: w.shape, | |
| bytes: b, | |
| module: group | |
| }); | |
| } | |
| intervals.sort( (a, b) => a[0] - b[0]); | |
| let end = 0; | |
| for (const r of intervals) { | |
| if (r[0] < end && r[1] > r[0]) | |
| throw Error(file.name + ': overlapping tensor ranges.'); | |
| end = Math.max(end, r[1]); | |
| } | |
| return { | |
| name: file.name, | |
| fileBytes: file.size, | |
| headerBytes: base, | |
| payloadBytes: payload, | |
| otherBytes: file.size - base - payload, | |
| groups, | |
| dtypes, | |
| tensors | |
| }; | |
| } | |
| function fontPreference() { | |
| try { | |
| const n = Number(localStorage.getItem('deepseek-atlas-font')); | |
| return Number.isFinite(n) && n >= .85 && n <= 1.5 ? n : 1; | |
| } catch (_) { | |
| return 1; | |
| } | |
| } | |
| // Only actual blank surface collapses a panel. Text, controls, tabs and selection are protected. | |
| function isBlankPanelPoint(e) { | |
| const root = e.currentTarget | |
| , el = e.target; | |
| if (!(el instanceof Element) || el.closest('button,a,input,select,textarea,summary,label,[role="button"],[role="tab"],[role="tablist"],[contenteditable="true"],.panel-tabs,.inspector-nav,.inspector-top,.inspector-foot,.timeline,.transport-row,.world-labels,svg')) | |
| return false; | |
| const edge = root.getBoundingClientRect(); | |
| if (e.clientX > edge.right - 13) | |
| return false; | |
| // never hijack the scrollbar | |
| const tree = document.createTreeWalker(el, NodeFilter.SHOW_TEXT); | |
| let text; | |
| while ((text = tree.nextNode())) { | |
| if (!text.textContent.trim()) | |
| continue; | |
| const r = document.createRange(); | |
| r.selectNodeContents(text); | |
| for (const b of r.getClientRects()) | |
| if (e.clientX >= b.left - 3 && e.clientX <= b.right + 3 && e.clientY >= b.top - 2 && e.clientY <= b.bottom + 2) | |
| return false; | |
| } | |
| return true; | |
| } | |
| class App extends Component { | |
| constructor() { | |
| super(); | |
| this.state = { | |
| layout: 'split', | |
| activity: 'inference', | |
| loopPhase: true, | |
| panelHidden: false, | |
| timelineHidden: innerWidth < 700 || innerHeight < 600, | |
| fontScale: fontPreference(), | |
| mobile: innerWidth <= 1100, | |
| view: 'architecture', | |
| precision: 'native', | |
| selected: null, | |
| tensor: null, | |
| hoverTensor: null, | |
| expert: null, | |
| detailLayout: 'column', | |
| expertView: false, | |
| tab: 'inspect', | |
| playing: !matchMedia('(prefers-reduced-motion: reduce)').matches, | |
| motion: false, | |
| speed: 1, | |
| time: 9, | |
| context: 1048576, | |
| binary: false, | |
| labels: false, | |
| links: true, | |
| orbit: false, | |
| modal: null, | |
| sheet: false, | |
| filter: 'all', | |
| limit: 14, | |
| bench: 1, | |
| effort: 100, | |
| error: null, | |
| audit: null, | |
| auditing: false, | |
| auditError: null, | |
| toast: null | |
| }; | |
| this.header = createRef(); | |
| this.canvas = createRef(); | |
| this.labels = createRef(); | |
| this.tip = createRef(); | |
| this.fileRef = createRef(); | |
| this.panelRef = createRef(); | |
| this.engine = null; | |
| this.key = this.key.bind(this); | |
| this.resizeUI = () => this.patch({ | |
| mobile: innerWidth <= 1100 | |
| }); | |
| this.panelPress = null; | |
| } | |
| componentDidMount() { | |
| if (this.header.current) { | |
| const update = () => this.header.current.parentElement.style.setProperty('--header-height', this.header.current.getBoundingClientRect().height + 'px'); | |
| this.headerObserver = new ResizeObserver(update); | |
| this.headerObserver.observe(this.header.current); | |
| update(); | |
| } | |
| try { | |
| this.engine = new AtlasEngine(this.canvas.current,this.labels.current,this.tip.current,{ | |
| select: (id, cat, expert) => this.select(id, cat, expert), | |
| hover: name => { | |
| if (this.state.hoverTensor !== name) | |
| this.setState({ | |
| hoverTensor: name | |
| }); | |
| } | |
| , | |
| tick: time => { | |
| if (Math.abs(this.state.time - time) > .02) | |
| this.setState({ | |
| time | |
| }); | |
| } | |
| , | |
| error: error => this.setState({ | |
| error | |
| }) | |
| },this.state); | |
| } catch (e) { | |
| console.error(e); | |
| this.setState({ | |
| error: e.message | |
| }); | |
| } | |
| window.addEventListener('keydown', this.key); | |
| window.addEventListener('resize', this.resizeUI); | |
| window.ATLAS_DEBUG = { | |
| app: this, | |
| get engine() { | |
| return window.ATLAS_DEBUG.app.engine; | |
| }, | |
| CFG, | |
| NV_CONFIG, | |
| TOTALS, | |
| TOTAL_P, | |
| MODULES, | |
| CATEGORIES, | |
| wBytes, | |
| sumB | |
| }; | |
| } | |
| componentDidUpdate(prevProps, prevState) { | |
| if (prevState && prevState.fontScale !== this.state.fontScale) { | |
| try { | |
| localStorage.setItem('deepseek-atlas-font', String(this.state.fontScale)); | |
| } catch (_) {} | |
| } | |
| if (this.engine) | |
| this.engine.sync(this.state); | |
| if (prevState && (prevState.selected !== this.state.selected || prevState.view !== this.state.view || prevState.tab !== this.state.tab) && this.panelRef.current) | |
| this.panelRef.current.scrollTop = 0; | |
| if (prevState && prevState.modal !== this.state.modal) { | |
| if (this.state.modal) { | |
| this.modalReturnFocus = document.activeElement; | |
| setTimeout( () => document.querySelector('.modal-head button')?.focus(), 0); | |
| } else if (this.modalReturnFocus && document.contains(this.modalReturnFocus)) | |
| this.modalReturnFocus.focus({ | |
| preventScroll: true | |
| }); | |
| } | |
| } | |
| componentWillUnmount() { | |
| if (this.headerObserver) | |
| this.headerObserver.disconnect(); | |
| if (this.engine) | |
| this.engine.dispose(); | |
| window.removeEventListener('keydown', this.key); | |
| window.removeEventListener('resize', this.resizeUI); | |
| clearTimeout(this.toastTimer); | |
| } | |
| patch(p) { | |
| this.setState(p); | |
| } | |
| changeFont(delta) { | |
| this.patch({ | |
| fontScale: Math.round(clamp(this.state.fontScale + delta, .85, 1.5) * 100) / 100 | |
| }); | |
| } | |
| setLayout(layout) { | |
| this.patch({ | |
| layout, | |
| view: 'architecture' | |
| }); | |
| } | |
| setActivity(activity) { | |
| this.patch({ | |
| activity, | |
| loopPhase: activity === 'inference' | |
| }); | |
| this.seek(activity === 'inference' ? 9 : 0); | |
| } | |
| togglePanel(force) { | |
| const open = force === undefined ? (this.state.mobile ? !this.state.sheet : this.state.panelHidden) : force; | |
| this.patch({ | |
| panelHidden: !open, | |
| sheet: open && this.state.mobile | |
| }); | |
| } | |
| hidePanel() { | |
| this.patch({ | |
| panelHidden: true, | |
| sheet: false | |
| }); | |
| } | |
| blankMove(e, axis) { | |
| if (e.pointerType === 'touch') | |
| return; | |
| const yes = isBlankPanelPoint(e); | |
| e.currentTarget.classList.toggle('blank-' + axis, yes); | |
| } | |
| blankDown(e, axis) { | |
| if (e.pointerType !== 'mouse' || e.button !== 0 || !isBlankPanelPoint(e)) { | |
| this.panelPress = null; | |
| return; | |
| } | |
| this.panelPress = { | |
| axis, | |
| x: e.clientX, | |
| y: e.clientY, | |
| scroll: e.currentTarget.querySelector('.inspector-scroll')?.scrollTop || 0 | |
| }; | |
| } | |
| blankUp(e, axis) { | |
| const p = this.panelPress; | |
| this.panelPress = null; | |
| if (!p || p.axis !== axis || Math.hypot(p.x - e.clientX, p.y - e.clientY) > 4 || !isBlankPanelPoint(e) || getSelection()?.toString()) | |
| return; | |
| if ((e.currentTarget.querySelector('.inspector-scroll')?.scrollTop || 0) !== p.scroll) | |
| return; | |
| if (axis === 'right') | |
| this.hidePanel(); | |
| else | |
| this.patch({ | |
| timelineHidden: true | |
| }); | |
| } | |
| notify(toast) { | |
| clearTimeout(this.toastTimer); | |
| this.setState({ | |
| toast | |
| }); | |
| this.toastTimer = setTimeout( () => this.setState({ | |
| toast: null | |
| }), 3200); | |
| } | |
| setView(view) { | |
| this.patch({ | |
| view, | |
| selected: null, | |
| tensor: null, | |
| hoverTensor: null, | |
| expert: null, | |
| tab: 'inspect', | |
| limit: 100 | |
| }); | |
| } | |
| select(id, tensor=null, expert=null) { | |
| if (!id) { | |
| this.patch({ | |
| selected: null, | |
| tensor: null, | |
| hoverTensor: null, | |
| expert: null, | |
| limit: 100 | |
| }); | |
| return; | |
| } | |
| const same = this.state.selected === id; | |
| if (same && !tensor && (expert === null || expert === undefined) && this.state.view === 'architecture') { | |
| this.patch({ | |
| selected: null, | |
| tensor: null, | |
| hoverTensor: null, | |
| expert: null | |
| }); | |
| return; | |
| } | |
| this.patch({ | |
| selected: id, | |
| tensor, | |
| hoverTensor: null, | |
| expert: expert === undefined ? null : expert, | |
| filter: 'all', | |
| limit: 100, | |
| tab: 'inspect', | |
| sheet: !!tensor && innerWidth < 1100 ? true : this.state.sheet | |
| }); | |
| } | |
| walk(d) { | |
| let i = MODULES.findIndex(m => m.id === this.state.selected); | |
| i = clamp(i + d, 0, MODULES.length - 1); | |
| this.patch({ | |
| selected: MODULES[i].id, | |
| view: 'architecture', | |
| tab: 'inspect', | |
| tensor: null, | |
| expert: null, | |
| filter: 'all', | |
| limit: 14 | |
| }); | |
| } | |
| seek(time) { | |
| if (this.engine) | |
| this.engine.seek(time); | |
| else | |
| this.patch({ | |
| time | |
| }); | |
| } | |
| key(e) { | |
| if (e.ctrlKey || e.metaKey || e.altKey) | |
| return; | |
| const k = e.key.toLowerCase(); | |
| if (k === 'escape') { | |
| if (this.state.modal || this.state.sheet) | |
| this.patch({ | |
| modal: null, | |
| sheet: false | |
| }); | |
| else if (this.state.tensor || this.state.expert !== null) | |
| this.patch({ | |
| tensor: null, | |
| hoverTensor: null, | |
| expert: null | |
| }); | |
| else | |
| this.patch({ | |
| selected: null | |
| }); | |
| return; | |
| } | |
| if (this.state.modal) { | |
| if (k === 'tab') { | |
| let nodes = [...document.querySelectorAll('.modal button:not(:disabled),.modal a,.modal input:not([hidden]),.modal select,.modal summary')].filter(x => x.getClientRects().length) | |
| , first = nodes[0] | |
| , last = nodes[nodes.length - 1]; | |
| if (e.shiftKey && document.activeElement === first) { | |
| e.preventDefault(); | |
| last?.focus(); | |
| } else if (!e.shiftKey && document.activeElement === last) { | |
| e.preventDefault(); | |
| first?.focus(); | |
| } | |
| } | |
| return; | |
| } | |
| if (/input|select|textarea/i.test(e.target.tagName) || e.target.tagName === 'BUTTON' && k === ' ') | |
| return; | |
| if (k === ' ') { | |
| e.preventDefault(); | |
| this.patch({ | |
| playing: !this.state.playing | |
| }); | |
| } | |
| if (k === 'arrowdown') { | |
| e.preventDefault(); | |
| this.walk(1); | |
| } | |
| if (k === 'arrowup') { | |
| e.preventDefault(); | |
| this.walk(-1); | |
| } | |
| if (k === '+' || k === '=') { | |
| e.preventDefault(); | |
| this.changeFont(.1); | |
| } | |
| if (k === '-') { | |
| e.preventDefault(); | |
| this.changeFont(-.1); | |
| } | |
| if (k === 'b') | |
| this.togglePanel(); | |
| if (k === 't') | |
| this.patch({ | |
| timelineHidden: !this.state.timelineHidden | |
| }); | |
| if (k === 'v') | |
| this.setLayout(this.state.layout === 'tower' ? 'split' : 'tower'); | |
| if (k === 'r' && this.engine) { | |
| if (this.state.selected) | |
| this.engine.focus(this.state.selected); | |
| else | |
| this.engine.reset(); | |
| } | |
| if (k === 'f') | |
| this.patch({ | |
| detailLayout: this.state.detailLayout === 'floor' ? 'column' : 'floor' | |
| }); | |
| if (k === 'x') | |
| this.patch({ | |
| expertView: !this.state.expertView, | |
| expert: null | |
| }); | |
| if (k === '[') | |
| this.walkTensor(-1); | |
| if (k === ']') | |
| this.walkTensor(1); | |
| if (k === 'p') { | |
| let keys = ['native', 'nvfp4', 'bf16']; | |
| this.patch({ | |
| precision: keys[(keys.indexOf(this.state.precision) + 1) % 3] | |
| }); | |
| } | |
| if (k === 'l') | |
| this.patch({ | |
| labels: !this.state.labels | |
| }); | |
| if (k === 'k') | |
| this.patch({ | |
| links: !this.state.links | |
| }); | |
| if (['1', '2', '3'].includes(k)) | |
| this.setView(['architecture', 'storage', 'cache'][Number(k) - 1]); | |
| } | |
| exportLedger() { | |
| let mode = this.state.precision; | |
| safeDownload('deepseek-v41-' + mode + '-derived-ledger.json', JSON.stringify({ | |
| title: 'DeepSeek V4.1 Flash - schema-derived payload ledger', | |
| date: '2026-09-10', | |
| precision: mode, | |
| status: 'DERIVED, NOT SHARD-AUDITED', | |
| notes: ['Not device VRAM. Headers, padding and unknown auxiliary buffers are not included.', 'NVFP4 uses the user-supplied backbone-routed-expert-only recipe.', 'NVFP4 tensor-level scale overhead is modeled as 8 bytes per expert matrix; actual exporter metadata can differ.', 'BF16 is a hypothetical two-byte-per-parameter reference, not a published artifact.'], | |
| bytes: TOTALS[mode], | |
| logicalParameters: TOTAL_P, | |
| modules: MODULES.map(m => ({ | |
| id: m.id, | |
| label: m.label, | |
| bytes: sumB(m.ws, mode), | |
| parameters: sumP(m.ws), | |
| weights: m.ws.map(w => ({ | |
| ...w, | |
| storageBytes: wBytes(w, mode), | |
| displayFormat: wFormat(w, mode) | |
| })) | |
| })), | |
| sources: SOURCES | |
| }, null, 2)); | |
| this.notify('Byte ledger exported. Estimates are explicitly labeled.'); | |
| } | |
| snapshot() { | |
| if (!this.engine) | |
| return; | |
| const a = document.createElement('a'); | |
| a.href = this.engine.snapshot(); | |
| a.download = 'deepseek-v41-3d-' + this.state.view + '.png'; | |
| a.click(); | |
| this.notify('3D canvas saved. Interface labels are not part of the canvas.'); | |
| } | |
| async audit(files) { | |
| if (!files.length) | |
| return; | |
| this.patch({ | |
| auditing: true, | |
| auditError: null, | |
| audit: null | |
| }); | |
| try { | |
| let results = []; | |
| for (const file of files) { | |
| if (!file.name.endsWith('.safetensors')) | |
| throw Error('Select .safetensors shards, not the index JSON.'); | |
| results.push(await auditSafetensors(file)); | |
| this.patch({ | |
| auditProgress: results.length + ' / ' + files.length | |
| }); | |
| } | |
| const names = new Set; | |
| for (const r of results) | |
| for (const t of r.tensors) { | |
| if (names.has(t.name)) | |
| throw Error('Duplicate tensor ' + t.name + '. Select one checkpoint, without duplicate shards.'); | |
| names.add(t.name); | |
| } | |
| const groups = Object.create(null) | |
| , dtypes = Object.create(null); | |
| let payload = 0 | |
| , fileBytes = 0 | |
| , headerBytes = 0; | |
| for (const r of results) { | |
| payload += r.payloadBytes; | |
| fileBytes += r.fileBytes; | |
| headerBytes += r.headerBytes; | |
| for (const [k,v] of Object.entries(r.groups)) | |
| groups[k] = (groups[k] || 0) + v; | |
| for (const [k,v] of Object.entries(r.dtypes)) | |
| dtypes[k] = (dtypes[k] || 0) + v; | |
| } | |
| this.patch({ | |
| audit: { | |
| results, | |
| groups, | |
| dtypes, | |
| payload, | |
| fileBytes, | |
| headerBytes, | |
| tensorCount: names.size, | |
| mode: this.state.precision | |
| }, | |
| auditing: false | |
| }); | |
| } catch (e) { | |
| this.patch({ | |
| auditError: e.message, | |
| auditing: false | |
| }); | |
| } | |
| if (this.fileRef.current) | |
| this.fileRef.current.value = ''; | |
| } | |
| renderOverview() { | |
| const s = this.state; | |
| return html`<div> | |
| <div class="intro-kicker">ONE CHECKPOINT / ONE CONTINUOUS FORWARD PASS</div><h2 class="editorial">All 40 layers.<br/><em>Every decode pass.</em></h2><p class="lede">The two groups are consecutive, not alternative. A normal decode pass goes through the encoder and then the decoder.</p> | |
| <div class="decode-path" aria-label="Decode execution order"><span>Embedding</span><i>\u2192</i><span>L00\u2013L19<small>encoder</small></span><i>\u2192</i><span>L20\u2013L39<small>decoder</small></span><i>\u2192</i><span>Head</span></div> | |
| <${Section} title="Same block pattern, different weights"><p class="fine">Both groups use attention, residual mixing and an MoE feed-forward block. The matrices can have matching shapes, but <b>L19 and L20 have their own learned values</b>. The model does not swap checkpoints or skip the second group when generating the next token.</p><p class="fine">The split is a layout for reading the architecture. <b>The actual layer order remains L00 through L39.</b> The tower shows that same sequence vertically; neither layout maps physical shard offsets.</p><button class="text-link" onClick=${ () => this.setLayout(s.layout === 'tower' ? 'split' : 'tower')}>${s.layout === 'tower' ? 'Separate the two layer ranges' : 'Merge into one numbered tower'} <${Icon} name="arrow" size=${14}/></button><//> | |
| <${Section} title="What actually changes at the boundary?"><p class="fine">The decoder's global attention reads a shared bank projected from the final encoder representation. Each decoder layer still computes its own queries, local-window attention and expert output. <b>Sharing cached activations does not mean sharing all weights or skipping layers.</b></p><p class="fine">The published <a href=${SOURCE_BASE + '/blob/main/inference/model.py'} target="_blank" rel="noopener noreferrer">reference forward function</a> iterates over the complete backbone before the output head. Layer cache-owner and index-owner settings are fixed architecture, not switches flipped halfway through a token.</p><//> | |
| <details class="assumptions"><summary>Then why does prefill sometimes show only half?</summary><p>Inference includes prompt preparation as well as token decoding. The model card's optimized CED prefill builds global memory from the encoder and reconstructs bounded local-window state. This avoids a full decoder pass over every cached prompt position; it does not remove the decoder from normal token generation.</p><p>The minimal reference implementation instead runs its complete backbone loop for both prompt and decode inputs. The Prefill* animation illustrates the optimized schedule, not a literal trace of that loop.</p><a href=${SOURCE_BASE + '#introduction'} target="_blank" rel="noopener noreferrer">Read the model-card architecture description</a></details> | |
| <div class="stat-grid"><div><small>BACKBONE</small><strong>552<span>B</span></strong></div><div><small>ENGRAM TABLES</small><strong>196.6<span>B</span></strong></div><div><small>PREFILL / TOKEN</small><strong>8<span>B active</span></strong></div><div><small>DECODE / TOKEN</small><strong>16<span>B active</span></strong></div></div> | |
| <${Note}>The model card describes an optimized prefill: process the full prompt with L00-L19, project decoder global KV, then perform bounded local-window replay. New generated tokens use all 40 backbone layers. That is an execution optimization, not a different set of weights.<//> | |
| <${Section} title="Read the architecture / click to unfold" tag="40 backbone layers"><div class="mode-legend">${[['full', '4', 'Full'], ['reindex', '4', 'Reindex'], ['reuse', '30', 'Reuse'], ['swa', '2', 'SWA only']].map( ([key,n,label]) => html`<button class="mode-key" onClick=${ () => this.select(LAYERS.find(l => l.mode === key).id)}><i style=${{ | |
| background: COL[key] | |
| }}/><b>${n}</b><span>${label}</span></button>`)}</div> | |
| <${Fact} label="Hidden width / mHC streams" value="5,120 / 4"/><${Fact} label="Attention heads / head width" value="64 / 512"/><${Fact} label="Experts at every layer" value="384 routed + 1 shared"/><${Fact} label="Routed experts used / token" value="6 of 384"/><${Fact} label="Local sliding window" value="128 tokens"/><${Fact} label="Global KV owners" value="L02 / L08 / L14 / L20" color=${COL.full}/><p class="fine">Full means new KV and sparse indices, not dense global attention. All layer IDs are zero-based.</p><//> | |
| <button class="outline-btn full-width" onClick=${ () => this.patch({ | |
| tab: 'guide' | |
| })}>Guided tour: Full, Reindex and Reuse <${Icon} name="arrow" size=${14}/></button><${Section} title="Beyond the backbone">${[['E1', 'E', 'Engram conditional memory', 'Two enormous tables. Sparse n-gram lookup.', COL.engram], ['vision', 'V', 'DeepSeek-ViT + projector', '32 vision layers / 3 x 3 unshuffle', COL.vision], ['D0', 'D', 'DSpark speculative draft', 'Three stages / five-token blocks', COL.mhc]].map( ([id,letter,title,sub,c]) => html`<button class="feature-link" onClick=${ () => this.select(id)}><span class="feature-icon" style=${{ | |
| color: c | |
| }}>${letter}</span><span><b>${title}</b><small>${sub}</small></span><${Icon} name="chevron" size=${14}/></button>`)}<//> | |
| <${Section} title="What the volumes mean" tag="Storage, not VRAM"><${Fact} label="Logical values in this inventory" value=${fmtP(TOTAL_P)}/><${Fact} label=${MODE_INFO[s.precision].label + ' payload'} value=${bytes(TOTALS[s.precision], s.binary)} color=${MODE_INFO[s.precision].color}/><p class="fine">Solids use one common scale: 1 cubic world unit = 1 GB. Empty space, paths, particles and labels are schematic. Payloads are derived from shapes and formats, not certified by all shard headers.</p><button class="text-link" onClick=${ () => this.setView('storage')}>Inspect the byte ledger <${Icon} name="arrow" size=${15}/></button><//> | |
| </div>`; | |
| } | |
| walkTensor(d) { | |
| const m = MODULES.find(x => x.id === this.state.selected); | |
| if (!m) | |
| return; | |
| const ws = orderWeights(m); | |
| let i = ws.findIndex(w => w.name === this.state.tensor); | |
| i = i < 0 ? (d > 0 ? 0 : ws.length - 1) : (i + d + ws.length) % ws.length; | |
| this.patch({ | |
| tensor: ws[i].name, | |
| hoverTensor: null, | |
| filter: 'all', | |
| limit: 100 | |
| }); | |
| } | |
| renderInsight(m, w) { | |
| const s = this.state | |
| , single = s.expert !== null && s.expert !== undefined && w.cat === 'expert' | |
| , v = single ? { | |
| ...w, | |
| count: 1, | |
| p: w.p / w.count | |
| } : w | |
| , info = tensorInfo(v, m) | |
| , b = weightParts(v, s.precision) | |
| , pinned = s.tensor === w.name; | |
| return html`<section class="tensor-insight" style=${{ | |
| '--tensor': tensorColor(w) | |
| }} aria-label="Tensor explanation"><div class="insight-kicker"><span>${pinned ? 'PINNED WEIGHT' : 'HOVERED WEIGHT'}${single ? ' / EXPERT ' + s.expert : ''}</span><button class="tiny-btn" onClick=${ () => this.patch({ | |
| tensor: null, | |
| hoverTensor: null | |
| })} title="Unpin tensor">${pinned ? 'Unpin' : 'Clear'}</button></div><h3>${info.title}</h3><code class="insight-name">${w.name}</code><p>${info.job}</p><div class="tensor-equation">${info.flow}</div><p>${info.why}</p>${info.caution && html`<div class="caution-small">${info.caution}</div>`} | |
| <div class="tensor-numbers"><div><small>LOGICAL SHAPE</small><strong>[${w.shape.map(num).join(', ')}]${v.count > 1 ? ' \u00d7 ' + v.count : ''}</strong></div><div><small>MODELED FORMAT</small><strong>${wFormat(w, s.precision)}</strong></div></div> | |
| <details class="account-details"><summary>${bytes(b.total, s.binary)} stored payload <span>Show the arithmetic</span></summary><${Fact} label="Logical parameters" value=${fmtP(v.p)}/><${Fact} label="Packed data" value=${bytes(b.data, s.binary)}/><${Fact} label="Group / block scales" value=${b.scales ? bytes(b.scales, s.binary) : 'None in this model'}/>${b.aux > 0 && html`<${Fact} label="Tensor-level scales (estimate)" value=${num(b.aux) + ' B'}/>`}<${Fact} label="Total" value=${num(b.total) + ' B'}/><p class="fine">Matrix shapes are [output, input]. Volumes include modeled scales. Exact checkpoint dtypes, metadata and padding require a shard-header audit.</p></details> | |
| <div class="insight-actions"><button class="text-link" onClick=${ () => this.engine && this.engine.focusTensor(w.name)}>Zoom to this tensor <${Icon} name="expand" size=${13}/></button><a href=${SOURCE_BASE + '/blob/main/inference/' + (info.source === 'vision' ? 'vision.py' : 'model.py')} target="_blank" rel="noopener noreferrer">Reference code \u2197</a></div> | |
| </section>`; | |
| } | |
| renderModule(m) { | |
| const s = this.state | |
| , ws = displayWeights(m) | |
| , story = layerStory(m) | |
| , exp = moduleExplanation(m) | |
| , isLayer = !!m.mode || m.id.startsWith('D') | |
| , isExpert = s.expert !== null && s.expert !== undefined | |
| , focus = findWeight(m, s.hoverTensor || s.tensor) | |
| , cc = m.part === 'encoder' ? COL.enc : m.part === 'decoder' ? COL.dec : m.id[0] === 'E' ? TC.engram : m.id[0] === 'D' ? TC.mhc : TC.vision; | |
| let weights = orderWeights(m).filter(w => s.filter === 'all' || w.cat === s.filter); | |
| if (isExpert) | |
| weights = ws.filter(w => w.cat === 'expert'); | |
| const cats = [...new Set(ws.map(w => w.cat))]; | |
| return html`<div><div class="inspect-path">${m.part ? m.part.toUpperCase() : 'AUXILIARY MODULE'} <span>/ ${m.id}${isExpert ? ' / expert ' + s.expert : ''}</span></div><div class="module-title"><h2>${m.label}</h2>${m.mode && html`<${Chip} color=${COL[m.mode]}>${m.mode === 'swa' ? 'SWA only' : m.mode}<//>`}</div> | |
| <div class="module-metrics"><div><small>LOGICAL PARAMETERS</small><b>${fmtP(isExpert ? MOE_EXPERT_P : sumP(m.ws))}</b></div><div><small>MODELED PAYLOAD</small><b style=${{ | |
| color: cc | |
| }}>${bytes(isExpert ? sumB(m.ws.filter(w => w.cat === 'expert'), s.precision) / (m.id[0] === 'D' ? 128 : 384) : sumB(m.ws, s.precision), s.binary)}</b></div></div> | |
| <div class="tensor-walk"><button aria-label="Previous tensor" onClick=${ () => this.walkTensor(-1)}><${Icon} name="up" size=${13}/></button><span>${s.tensor ? 'Follow this layer\u2019s weights' : 'Click a labeled weight to explain it'}<small>${ws.length} descriptors / arrows walk every tensor</small></span><button aria-label="Next tensor" onClick=${ () => this.walkTensor(1)}><${Icon} name="down" size=${13}/></button></div> | |
| ${focus && this.renderInsight(m, focus)} | |
| ${m.mode && html`<p class="layer-execution"><b>Decode step ${m.index + 1} of 40.</b> ${m.index === 19 ? 'The next layer is L20; the forward pass continues into the decoder.' : m.index === 20 ? 'Runs after L19, using its own attention and expert weights.' : m.index === 39 ? 'The output head follows this final backbone layer.' : 'Runs in sequence; its cache mode does not skip its computation.'}</p>`} | |
| ${story ? html`<${Section} title="What this layer actually does"><div class="layer-story"><h3>${story.title}</h3><p>${story.body}</p><p>${story.detail}</p></div><//>` : html`<${Note} color=${cc}><strong>${exp.title}</strong> ${exp.body}<//>`} | |
| ${m.mode && html`<${Section} title="Computed here or borrowed?" tag="WEIGHTS VS RUNTIME STATE"><div class="ownership"> | |
| ${[['Main attention queries', 'Own weights', 'All 64 main queries are computed here.', TC.q, 'own'], ['Local SWA KV', 'Own weights', 'Own 128-token window at every layer.', TC.local, 'own'], ['Global main KV', m.owner === null ? 'Absent' : m.owner === m.index ? 'Writes bank' : 'From L' + m.owner, m.owner === null ? 'No global branch at layers 0 and 1.' : m.index === 20 ? 'Projected from final encoder states.' : m.owner === m.index ? 'This owner pools token pairs.' : 'Cached KV activations are shared.', TC.kv, m.owner === null ? 'off' : m.owner === m.index ? 'own' : 'borrow'], ['Indexer keys', m.owner === null ? 'Absent' : m.owner === m.index ? 'Own projection' : 'From L' + m.owner, m.owner === null ? 'There are no global positions to rank.' : m.owner === m.index ? '128-channel keys from main KV.' : 'No indexer key projection in this layer.', TC.index, m.owner === null ? 'off' : m.owner === m.index ? 'own' : 'borrow'], ['Top-512 selection', m.indexOwner === null ? 'Absent' : m.indexOwner === m.index ? 'Recomputed' : 'From L' + m.indexOwner, m.mode === 'reindex' ? 'New query; search L20\u2019s candidate pool.' : m.indexOwner === m.index ? 'Rank visible positions; select a sparse set.' : m.indexOwner === null ? 'Local attention only.' : 'No fresh sparse-index query matrix here.', TC.index, m.indexOwner === null ? 'off' : m.indexOwner === m.index ? 'own' : 'borrow']].map( ([name,status,desc,c,kind]) => html`<div class="ownership-row"><i class=${kind} style=${{ | |
| '--own': c | |
| }}/><div><b>${name}</b><small>${desc}</small></div><span style=${{ | |
| color: kind === 'off' ? '#63768e' : c | |
| }}>${status}</span></div>`)}</div> | |
| ${m.owner !== null && m.owner !== m.index && html`<button class="text-link" onClick=${ () => this.select('L' + m.owner)}>Open the source layer L${m.owner} <${Icon} name="arrow" size=${14}/></button>`} | |
| <p class="fine">Borrowed objects are runtime memory and position indices, not duplicated parameter tensors. Their absence from this layer\u2019s weight list is intentional.</p><//>`} | |
| ${isLayer && html`<${Section} title="Inside one expert" tag=${m.id[0] === 'D' ? '128 / TOP-3' : '384 / TOP-6'}><div class="expert-equation">gate = SiLU(W1 x)<br/>content = W3 x<br/>output = W2(gate \u2299 content)</div><${Fact} label="One expert / logical parameters" value=${fmtP(MOE_EXPERT_P)}/><${Fact} label="Expert width" value="5,120 \u2192 2,304 \u2192 5,120"/><${Fact} label="Three weight matrices / expert" value=${bytes(sumB(m.ws.filter(w => w.cat === 'expert'), s.precision) / (m.id[0] === 'D' ? 128 : 384), s.binary)}/><p class="fine">A bank marked \u00d7${m.id[0] === 'D' ? 128 : 384} contains that many separate matrices. The shared expert is always used; only the selected routed experts are evaluated. Stored volume does not shrink when an expert is idle.</p><button class="outline-btn full-width" onClick=${ () => this.patch({ | |
| expertView: !s.expertView, | |
| expert: null, | |
| tensor: null, | |
| hoverTensor: null | |
| })}>${s.expertView ? 'Return to named tensor banks' : 'Unfold all individual experts'} <${Icon} name="expand" size=${14}/></button><//>`} | |
| ${m.id[0] === 'E' && html`<${Section} title="From token IDs to a gated lookup"><${Fact} label="Orders / hash heads" value="2, 3, 4 / 8 each"/><${Fact} label="Rows fetched per token" value="24 per module"/><${Fact} label="Retrieved channels" value="24 \u00d7 256 = 6,144"/><${Fact} label="Combined key/value projection" value="6,144 \u2192 25,600"/><p class="fine">The projection produces four keys and one value. The residual streams gate how much of that value they accept. Sparse lookup makes the accessed portion small; all table values still contribute to stored bytes.</p><//>`} | |
| ${(m.id === 'vision' || m.id === 'aligner') && html`<${Section} title="Images meet the language model"><div class="expert-equation">14 \u00d7 14 RGB patches<br/>\u2193 32 vision blocks, width 1,024<br/>3 \u00d7 3 unshuffle: 9,216 channels<br/>\u2193 two-layer projector<br/>5,120-channel visual embeddings</div><p class="fine">In the vision scene, the same tensor role across 32 blocks is combined into a bank marked \u00d732. These are 32 separately stored copies, not shared weights.</p><//>`} | |
| <${Section} title=${isExpert ? 'Expert ' + String(s.expert).padStart(3, '0') + ' / three matrices' : 'Every named tensor'} tag=${ws.length + ' DESCRIPTORS'}><div class="filter-row"><label>Show <select aria-label="Tensor category" value=${isExpert ? 'expert' : s.filter} onChange=${e => this.patch({ | |
| filter: e.target.value, | |
| expert: null, | |
| limit: 100 | |
| })}><option value="all">All tensors</option>${cats.map(c => html`<option value=${c}>${c}</option>`)}</select></label>${(s.filter !== 'all' || isExpert) && html`<button class="tiny-btn" onClick=${ () => this.patch({ | |
| filter: 'all', | |
| expert: null, | |
| limit: 100 | |
| })}>Clear</button>`}</div> | |
| ${weights.slice(0, s.limit).map(w => { | |
| const div = isExpert ? w.count : 1 | |
| , hot = (s.hoverTensor || s.tensor) === w.name; | |
| return html`<button key=${w.name} class=${'weight-button' + (hot ? ' hot' : '')} style=${{ | |
| '--tensor': tensorColor(w) | |
| }} aria-pressed=${s.tensor === w.name} title="Pin the tensor explanation and highlight its solid" onMouseEnter=${ () => this.patch({ | |
| hoverTensor: w.name | |
| })} onMouseLeave=${ () => this.patch({ | |
| hoverTensor: null | |
| })} onClick=${ () => { | |
| this.patch({ | |
| tensor: w.name, | |
| hoverTensor: null | |
| }); | |
| if (this.panelRef.current) | |
| this.panelRef.current.scrollTo({ | |
| top: 0, | |
| behavior: matchMedia('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth' | |
| }); | |
| } | |
| }><i/><span><b>${w.name}</b><small>[${w.shape.map(num).join(', ')}]${w.count > 1 && !isExpert ? ' \u00d7 ' + w.count : ''}</small><em>${wFormat(w, s.precision)} / ${fmtP(w.p / div)} parameters</em></span><strong>${bytes(wBytes(w, s.precision) / div, s.binary)}</strong></button>`; | |
| } | |
| )} | |
| ${weights.length > s.limit && html`<button class="outline-btn full-width" onClick=${ () => this.patch({ | |
| limit: s.limit + 40 | |
| })}>Show ${weights.length - s.limit} more tensors</button>`}<p class="fine">Click any row or scene label for the operation, input/output shape, and byte arithmetic. Tiny pick dots and leader lines are annotations, not extra stored mass.</p><//> | |
| <${Section} title="Same weights, different payload">${Object.keys(MODE_INFO).map(mode => { | |
| const b = sumB(m.ws, mode); | |
| return html`<div class="ledger-bar"><div><span>${MODE_INFO[mode].label}</span><b>${bytes(b, s.binary)}</b></div><span><i style=${{ | |
| width: b / sumB(m.ws, 'bf16') * 100 + '%', | |
| background: MODE_INFO[mode].color, | |
| opacity: mode === s.precision ? 1 : .35 | |
| }}/></span></div>`; | |
| } | |
| )}<p class="fine">NVFP4 only changes the explicitly targeted backbone routed experts. Ignored tensors retain their source dtypes. BF16 is a hypothetical reference, not a published checkpoint.</p><//></div>`; | |
| } | |
| renderGuide() { | |
| return html`<div><div class="inspect-path">A READING GUIDE TO THE ANIMATION</div><h2 class="editorial">Follow the data.<br/><em>Read the weights.</em></h2><p class="lede">The solids are learned tensors. The moving lights are explanatory signals. Do not read the size of a weight as its runtime workload.</p> | |
| <${Section} title="Start with a Reuse layer"><p class="guide-copy">Open L03. Its own query matrices ask questions of memory made at L02. It still has local KV, output projections and a full expert bank. The missing compressor and indexer are not a broken expansion: their cached results are borrowed.</p><button class="outline-btn full-width" onClick=${ () => { | |
| this.select('L3'); | |
| this.patch({ | |
| tab: 'inspect' | |
| }); | |
| } | |
| }>Open L03: see what Reuse keeps</button><//> | |
| <${Section} title="Compare a Full layer"><p class="guide-copy">Now open L02. The extra compressor weights create a global memory entry for every pair of tokens. The indexer adds search keys, query projections and score weights. Full means these operations exist here; it does not mean dense global attention.</p><button class="outline-btn full-width" onClick=${ () => { | |
| this.select('L2'); | |
| this.patch({ | |
| tab: 'inspect' | |
| }); | |
| } | |
| }>Open L02: see the extra matrices</button><//> | |
| <${Section} title="Cross the encoder-decoder boundary"><p class="guide-copy">L20\u2019s global KV comes from the encoder output. Bulk prefill can therefore run the first 20 layers and prepare decoder memory without running every decoder layer on every prompt token. A bounded suffix replay reconstructs local SWA state; new decoded tokens traverse both halves.</p><button class="outline-btn full-width" onClick=${ () => { | |
| this.select('L20'); | |
| this.patch({ | |
| tab: 'inspect' | |
| }); | |
| } | |
| }>Open L20: follow the shared memory</button><//> | |
| <${Section} title="Same memory, a different search"><p class="guide-copy">L24 reuses L20\u2019s KV and search keys but has fresh indexer queries. Its search is restricted to the candidate pool selected at L20: up to 2,048 blocks of eight positions, then Top-512. These are activation indices, not more learned weights.</p><button class="outline-btn full-width" onClick=${ () => { | |
| this.select('L24'); | |
| this.patch({ | |
| tab: 'inspect' | |
| }); | |
| } | |
| }>Open L24: compare Reindex</button><//> | |
| <${Section} title="Three distinctions worth keeping"><div class="layer-story"><h3>Stored is not active.</h3><p>Only six routed experts are chosen per backbone token, but all 384 specialists remain in the checkpoint. Highlighting an expert changes its light, not its volume.</p><h3>Weights are not the KV cache.</h3><p>A projection matrix is learned and fixed for inference. It produces token-dependent keys and values that accumulate in runtime memory. Reusing those activations does not imply tied query weights.</p><h3>A format label is not a full precision recipe.</h3><p>The native release already has FP4 experts. NVFP4\u2019s more frequent group scales add modeled bytes here. Attention, shared experts, Engram and draft stages are not all converted just because the checkpoint name changes.</p></div><//> | |
| <${Section} title="A byte-true view, with limits"><p class="guide-copy">Every solid uses 1 cubic world unit per decimal GB of modeled payload. Its three dimensions multiply to that volume, including throughout the morph. Aspect ratio is chosen for readability; it is not the literal matrix shape. Small picking dots, links and labels carry no bytes.</p><p class="guide-copy">The ledger remains schema-derived. A complete set of checkpoint headers is required before calling its totals exact on-disk measurements.</p><button class="text-link" onClick=${ () => this.patch({ | |
| modal: 'audit' | |
| })}>Audit local Safetensors headers <${Icon} name="arrow" size=${14}/></button><//></div>`; | |
| } | |
| renderStorage() { | |
| const s = this.state | |
| , selected = s.selected && s.selected.startsWith('cat:') ? CATEGORIES.find(c => 'cat:' + c[0] === s.selected) : null; | |
| return html`<div><div class="inspect-path">PARAMETERS / FORMATS / BYTES</div><h2 class="editorial">Storage, <em>honestly.</em></h2><p class="lede">Mass is conserved as the architecture unfolds. Compare the same logical weights in different formats.</p><div class="storage-total"><small>${MODE_INFO[s.precision].note.toUpperCase()}</small><strong>${bytes(TOTALS[s.precision], s.binary)}</strong><span>Schema-derived tensor payload estimate</span></div> | |
| <${Note} color=${s.precision === 'nvfp4' ? COL.dec : COL.enc}><strong>${s.precision === 'bf16' ? 'A reference, not a checkpoint.' : EXP[s.precision].title}</strong> ${s.precision === 'bf16' ? 'Every logical value is charged two bytes. This baseline is hypothetical, not an available BF16 repository or a device-memory prediction.' : EXP[s.precision].body}<//> | |
| <${Section} title="Payload by component" tag=${selected ? 'SELECTED: ' + selected[0].toUpperCase() : 'ALL MODULES'}>${CATEGORIES.map( ([id,name,c,ws]) => { | |
| let b = sumB(ws, s.precision) | |
| , active = selected && selected[0] === id; | |
| return html`<button class=${'category-row' + (active ? ' active' : '')} onClick=${ () => this.patch({ | |
| selected: 'cat:' + id | |
| })}><div><i style=${{ | |
| background: c | |
| }}/><span>${name}</span><b>${bytes(b, s.binary)}</b></div><span class="mini-track"><i style=${{ | |
| width: b / TOTALS[s.precision] * 100 + '%', | |
| background: c | |
| }}/></span><small>${fmtP(sumP(ws))} parameters <span>${(b / TOTALS[s.precision] * 100).toFixed(2)}%</span></small></button>`; | |
| } | |
| )}<//> | |
| <${Section} title="Why NVFP4 grows here"><div class="equation"><span>NATIVE</span><code>0.5 + 1/32 = 0.53125 B/p</code><span>NVFP4</span><code>0.5 + 1/16 = 0.56250 B/p</code></div><${Fact} label="Routed expert payload change" value="+5.88%" color=${COL.full}/><${Fact} label="Modeled checkpoint change" value={'+'+bytes(NV_DELTA,s.binary)} color=${COL.full}/><p class="fine">Only 40 backbone expert banks change. Extra per-tensor scaling is estimated at eight bytes per expert matrix. The supplied recipe does not quantize Engram to FP4.</p><//> | |
| <${Section} title="Audit, rather than guess"><p class="fine">The 510 GB upstream listing is consistent with this estimate, but rounded. Inspect local Safetensors headers for exact stored tensor bytes, including scale tensors. No weights are loaded or uploaded.</p><button class="outline-btn full-width" onClick=${ () => this.patch({ | |
| modal: 'audit' | |
| })}><${Icon} name="expand" size=${16}/> Audit local checkpoint headers</button><button class="text-link" onClick=${ () => this.exportLedger()}>Export derived tensor ledger <${Icon} name="download" size=${14}/></button><//> | |
| <details class="assumptions"><summary>Accounting assumptions and limits</summary><p>Safetensors headers, file padding, unknown extra buffers and exporter-specific metadata are not included. The inventory follows the released config and reference shapes. Some checkpoint dtypes, notably compressor weights, require shard-header confirmation.</p><p>FP8 linears include one byte per 32 x 32 scale block. Engram includes one scale byte per 32 values. Expert banks include packed data and group scales. F32 residual mixing and routing correction tensors remain F32 natively.</p><p>The reference converter expands the FP8 grouped output projection <code>wo_a</code> to BF16; the runtime expands the BF16 output head to F32. Neither expansion is counted as disk storage. Serving buffers, tensor parallelism and GPU allocator overhead are different quantities.</p></details></div>`; | |
| } | |
| renderCache() { | |
| const s = this.state | |
| , n = s.context | |
| , global = n * 890; | |
| return html`<div><div class="inspect-path">COMPRESSED SPARSE ATTENTION 2</div><h2 class="editorial">One memory.<br/><em>Many readers.</em></h2><p class="lede">Four global KV owners. Eight indexing layers. Forty local sliding windows.</p><div class="storage-total cache-total"><small>GLOBAL CACHE / ORIGINAL TOKEN</small><strong>890 <em>bytes</em></strong><span>Model-wide, not multiplied by 40 layers</span></div> | |
| <${Section} title="Stretch the context" tag="UP TO 1,048,576"><label class="range-title" for="context-number">Original input tokens <input id="context-number" type="number" min="128" max="1048576" step="128" value=${n} onChange=${e => { | |
| const v = Number(e.target.value); | |
| this.patch({ | |
| context: clamp(Number.isFinite(v) ? Math.round(v) : 128, 128, 1048576) | |
| }); | |
| } | |
| }/></label><input class="slider" aria-label="Context length logarithmic slider" type="range" min="7" max="20" step="0.05" value=${Math.log2(n)} onInput=${e => this.patch({ | |
| context: clamp(Math.round(2 ** Number(e.target.value)), 128, 1048576) | |
| })}/><div class="range-ends"><span>128</span><span>16K</span><span>1M</span></div><div class="preset-row">${[16384, 65536, 262144, 1048576].map(v => html`<button class=${n === v ? 'active' : ''} onClick=${ () => this.patch({ | |
| context: v | |
| })}>${v === 1048576 ? '1M' : v / 1024 + 'K'}</button>`)}</div><${Fact} label="Global cache payload" value=${bytes(global, s.binary)} color=${COL.full}/><${Fact} label="Exact 890 x token estimate" value=${num(global) + ' B'}/><p class="fine">The four solids use integer stored positions. At odd sequence lengths they differ by at most 534 B from the 890 x N asymptotic expression.</p><//> | |
| <${Section} title="Where 890 comes from"><div class="equation"><span>MAIN KV / STORED POSITION</span><code>512 x 0.5 + 512/16 = 288 B</code><span>INDEX K / STORED POSITION</span><code>128 x 0.5 + 128/32 = 68 B</code><span>MODEL / ORIGINAL TOKEN</span><code>(3/2 + 1) x (288 + 68) = 890 B</code></div><p class="fine">Three encoder banks compress pairs of input tokens. The decoder bank keeps one position per input token. Main KV uses E2M1 with an E4M3 scale per 16 channels.</p><//> | |
| <${Section} title="Hierarchical sparse indexer"><${Fact} label="Candidate source" value="Layer 20"/><${Fact} label="Candidate block selection" value="2,048 x 8 positions"/><${Fact} label="Candidate cap" value="16,384 positions"/><${Fact} label="Final sparse selection" value="Top-512 / query"/><${Fact} label="Deeper reindex layers" value="24, 28, 32, 36"/><p class="fine">The point field is a schematic, not a storage mass. Its illuminated marks illustrate a sparse subset, not measured query indices.</p><//><${Note} color=${COL.full}>This is the production cache format described by the model card and reconstructed from code. Fake-quantized reference tensors need not consume 890 bytes per token. Local SWA state, prefill workspace and allocator overhead are excluded.<//></div>`; | |
| } | |
| renderBench() { | |
| const s = this.state | |
| , b = BENCH[s.bench]; | |
| return html`<div><div class="inspect-path">REPORTED EVALUATIONS / NATIVE MODEL</div><h2 class="editorial">Capability,<br/><em>with context.</em></h2><p class="lede">The supplied model-card results at maximum reasoning effort. No synthetic benchmark projections.</p><label class="field-label">Compare a benchmark<select value=${s.bench} onChange=${e => this.patch({ | |
| bench: Number(e.target.value) | |
| })}>${BENCH.map( (r, i) => html`<option value=${i}>${r[0]}</option>`)}</select></label><div class="benchmark-chart">${BENCH_MODELS.map( (name, i) => html`<div class=${'benchmark-row' + (i === 6 ? ' featured' : '')}><div><span>${name}</span><b>${b[2][i] === null ? 'Not reported' : b[2][i].toFixed(1)}</b></div><div class="bench-track"><i style=${{ | |
| width: (b[2][i] || 0) + '%', | |
| background: i === 6 ? COL.dec : COL.enc, | |
| opacity: i === 6 ? 1 : .32 | |
| }}/></div></div>`)}</div><p class="fine">${b[0]} / ${b[1]}. Percentages or the card's 0-100 score; ZeroBench uses Pass@5, ProgramBench Almost@1, DeepSWE resolved rate. Do not average unlike metrics.</p> | |
| <${Section} title="Evaluation conditions"><${Fact} label="Reasoning effort" value="100 / maximum"/><${Fact} label="Sampling" value="T 1.0 / top-p 0.95"/><${Fact} label="Code-agent context" value="1M tokens"/><${Fact} label="Visual-agent context" value="512K tokens"/><p class="fine">Harnesses vary. DeepSWE uses mini-SWE, Terminal-Bench uses DeepSeek Harness Minimal, and several visual-agent evaluations use Claude Code. See the model card for task-specific protocols.</p><${Note} color=${COL.full}>Publisher-reported scores, not independent results generated here. No quality equivalence is claimed for the supplied NVFP4 conversion.<//><//> | |
| <${Section} title="Reasoning is an API setting"><label class="range-title">reasoning_effort <output>${s.effort}</output></label><input class="slider" type="range" min="1" max="100" value=${s.effort} aria-label="Reasoning effort example" onInput=${e => this.patch({ | |
| effort: Number(e.target.value) | |
| })}/><pre class="small-code">${JSON.stringify({ | |
| reasoning_effort: s.effort, | |
| temperature: 1, | |
| top_p: .95 | |
| }, null, 2)}</pre><p class="fine">This changes only the configuration example. It does not predict score, speed or token use. Benchmark bars remain fixed at the published effort-100 results.</p><//></div>`; | |
| } | |
| renderSources() { | |
| return html`<div><div class="inspect-path">PROVENANCE / SEPTEMBER 10, 2026</div><h2 class="editorial">Trace every<br/><em>assumption.</em></h2><p class="lede">The card supplies claims. The config and reference code supply structure. Local headers can supply exact bytes.</p><div class="source-status"><i/> Offline app / no runtime requests</div> | |
| <${Section} title="Primary sources">${SOURCES.map( ([name,url,desc], i) => html`<div class="source-row"><span>${String(i + 1).padStart(2, '0')}</span><div><a href=${url} target="_blank" rel="noopener noreferrer">${name} <${Icon} name="arrow" size=${11}/></a><p>${desc}</p></div></div>`)}<//> | |
| <${Section} title="The supplied NVFP4 recipe"><p class="fine">The second configuration was supplied in this conversation. Its producer identifies <code>modelopt / dsv4-nvfp4-experts</code>. A complete matching checkpoint and shard inventory were not independently retrieved. No repository, final upload size or quality parity is invented.</p><${Fact} label="Explicit conversions" value="40 backbone expert banks"/><${Fact} label="Engram" value="Unchanged / FP8"/><${Fact} label="Ignore semantics" value="Keep original storage"/><${Fact} label="NVFP4 scale groups" value="16 weights"/><//> | |
| <${Section} title="Inspect the inputs"><div class="action-grid"><button class="outline-btn" onClick=${ () => this.patch({ | |
| modal: 'config' | |
| })}>Embedded configs</button><button class="outline-btn" onClick=${ () => this.exportLedger()}>Export byte ledger</button></div><button class="outline-btn full-width" onClick=${ () => this.patch({ | |
| modal: 'audit' | |
| })}>Audit local Safetensors headers</button><//> | |
| <${Section} title="Tower, split and execution"><p class="fine">The tower is a logical layer-order view, not a map of safetensors byte offsets. Splitting moves existing objects; it does not create another model. Both layouts use the exact same tensor inventory.</p><p class="fine">The default Decode animation runs through both groups. Prefill* and SWA replay separately illustrate the model-card CED optimization (encoder-heavy prompt preparation plus bounded replay). The published minimal <code>Transformer.forward</code> loops through all 40 backbone layers; the scene is not a line-by-line trace of that implementation. First-token evaluation and scheduling details depend on the serving engine.</p><p class="fine">Training mode is a general causal language-model forward/loss/backward/update schematic. It does not reproduce the complete DeepSeek training recipe, promise all components are trainable, or model gradients/optimizer memory as checkpoint bytes.</p><//> | |
| <${Section} title="Interpretation rules"><p class="fine">552B is the rounded backbone claim. Engram tables, DSpark and vision are additional inventory. The modeled total is ${fmtP(TOTAL_P)} logical values. Bytes are estimates unless explicitly labeled as a local audit.</p><p class="fine">Routing, flow, drafts, acceptance and timing are explanatory animations. Nothing runs model inference. The timeline is not a latency chart. No weights are bundled in this HTML.</p><p class="fine">The renderer uses volumes, not footprint areas. Illumination does not inflate experts; unfolding conserves modeled weight mass. Zoom does not alter the byte scale. Global KV is a separate runtime quantity.</p><//> | |
| <${Section} title="Implementation"><p class="fine">Preact + HTM, with an embedded, instanced WebGL2 renderer and a Canvas2D software-projection fallback. All code and data live in this file. No CDN, fonts, textures, build tool or server is required.</p><button class="text-link" onClick=${ () => this.patch({ | |
| modal: 'credits' | |
| })}>Credits and third-party licenses <${Icon} name="arrow" size=${14}/></button><//></div>`; | |
| } | |
| renderInspector() { | |
| const s = this.state | |
| , m = MODULES.find(x => x.id === s.selected); | |
| return html`<div class=${'inspector-dock' + (s.panelHidden ? ' collapsed' : '')}><aside id="inspector-panel" inert=${s.mobile ? !s.sheet : s.panelHidden} aria-hidden=${s.mobile ? !s.sheet : s.panelHidden} class=${'inspector' + (s.sheet ? ' sheet-open' : '')} onPointerMove=${e => this.blankMove(e, 'right')} onPointerLeave=${e => e.currentTarget.classList.remove('blank-right')} onPointerDown=${e => this.blankDown(e, 'right')} onPointerUp=${e => this.blankUp(e, 'right')}><div class="inspector-top"><div class="panel-tabs">${[['inspect', 'Inspector'], ['guide', 'Guide'], ['bench', 'Benchmarks'], ['sources', 'Sources']].map( ([tab,label]) => html`<button class=${s.tab === tab ? 'active' : ''} onClick=${ () => this.patch({ | |
| tab | |
| })}>${label}</button>`)}</div><button class="icon-button panel-collapse" aria-label="Hide inspector" title="Hide inspector (B). You can also click blank panel space." onClick=${ () => this.hidePanel()}><${Icon} name="arrow"/></button></div><div class="inspector-nav"><span>${s.tab === 'inspect' ? (m ? m.label : s.view === 'storage' ? 'Checkpoint ledger' : s.view === 'cache' ? 'Global KV cache' : 'Model overview') : s.tab === 'bench' ? 'Model-card comparisons' : 'Evidence and methodology'}</span><div>${s.tab === 'inspect' && s.view === 'architecture' && html`<button aria-label="Previous module" onClick=${ () => this.walk(-1)}><${Icon} name="up" size=${12}/></button><button aria-label="Next module" onClick=${ () => this.walk(1)}><${Icon} name="down" size=${12}/></button>`}${s.selected && html`<button aria-label="Clear selected module" onClick=${ () => { | |
| this.patch({ | |
| selected: null, | |
| tensor: null, | |
| expert: null, | |
| filter: 'all' | |
| }); | |
| this.engine && this.engine.reset(); | |
| } | |
| }><${Icon} name="close" size=${11}/></button>`}</div></div><div class="inspector-scroll" ref=${this.panelRef}>${s.tab === 'guide' ? this.renderGuide() : s.tab === 'bench' ? this.renderBench() : s.tab === 'sources' ? this.renderSources() : s.view === 'storage' ? this.renderStorage() : s.view === 'cache' ? this.renderCache() : m ? this.renderModule(m) : this.renderOverview()}</div><div class="inspector-foot"><span><i/> SCHEMA-DERIVED PAYLOAD</span><button onClick=${ () => this.patch({ | |
| binary: !s.binary | |
| })} title="Toggle decimal and binary byte units">${s.binary ? 'GiB / binary' : 'GB / decimal'}</button></div></aside></div>`; | |
| } | |
| renderModal() { | |
| const s = this.state; | |
| if (!s.modal) | |
| return null; | |
| let title = { | |
| help: 'Explore the model', | |
| config: 'Embedded configurations', | |
| audit: 'Safetensors header audit', | |
| credits: 'Credits & licenses' | |
| }[s.modal]; | |
| return html`<div class="modal-backdrop" onClick=${e => { | |
| if (e.target === e.currentTarget) | |
| this.patch({ | |
| modal: null | |
| }); | |
| } | |
| }><div class=${'modal ' + (s.modal === 'config' ? 'wide' : '')} role="dialog" aria-modal="true" aria-label=${title}><div class="modal-head"><div><div class="eyebrow">DEEPSEEK MODEL ATLAS</div><h2>${title}</h2></div><button class="icon-button" aria-label="Close dialog" onClick=${ () => this.patch({ | |
| modal: null | |
| })}><${Icon} name="close"/></button></div><div class="modal-body"> | |
| ${s.modal === 'help' && html`<p class="lede">A file to keep, inspect and share. Everything needed to render the atlas is embedded.</p><div class="help-grid">${[['V', 'Merge tower / separate encoder and decoder'], ['B / T', 'Hide inspector / animation panel'], ['+ / -', 'Increase / decrease all text'], ['Blank panel background', 'Click the arrow cursor to collapse; text is protected'], ['Drag', 'Orbit the model'], ['Right-drag / Shift-drag', 'Pan in camera space'], ['Wheel / pinch', 'Zoom without changing scale'], ['Click a layer', 'Open named weight tensors'], ['Click a labeled weight', 'Pin its role, shape and bytes'], ['F / X', 'Columns or floor / banks or experts'], ['[ / ]', "Walk the layer's tensor list"], ['Click an expert', 'Split its gate, up and down'], ['Two-finger drag', 'Pan on touch screens'], ['R / double-click', 'Reset the camera'], ['P', 'Cycle native / NVFP4 / BF16'], ['Space', 'Pause or resume the story'], ['Up / Down', 'Previous or next module'], ['L / K', 'Toggle labels / flow paths'], ['1 / 2 / 3', 'Architecture / Storage / KV lab']].map( ([k,v]) => html`<div><kbd>${k}</kbd><span>${v}</span></div>`)}</div><${Note}>The animation starts in Decode and loops that phase. Choose another phase or turn off Loop phase to play the whole schematic story. Tower order is not physical disk order; Split moves the same layer objects. Training mode illustrates gradients and weight updates, not a second checkpoint. Routing and DSpark confidence are synthetic. Solids represent modeled stored bytes, not device utilization, FLOPs, throughput or measured inference time.<//><button class="outline-btn full-width" onClick=${ () => this.patch({ | |
| modal: 'audit' | |
| })}>Check exact bytes from local checkpoint headers</button>`} | |
| ${s.modal === 'config' && html`<div class="config-switch">${['native', 'nvfp4'].map(m => html`<button class=${(s.configMode || 'native') === m ? 'active' : ''} onClick=${ () => this.patch({ | |
| configMode: m | |
| })}>${m === 'native' ? 'Native config' : 'Supplied NVFP4 config'}</button>`)}</div><p class="fine">Full supplied fields. Expanded arrays are shown below. Both recipes have the same underlying architecture.</p><pre class="config-code">${JSON.stringify(s.configMode === 'nvfp4' ? NV_CONFIG : CFG, null, 2)}</pre><button class="outline-btn" onClick=${ () => safeDownload((s.configMode || 'native') + '-config.json', JSON.stringify(s.configMode === 'nvfp4' ? NV_CONFIG : CFG, null, 2))}><${Icon} name="download" size=${16}/> Save config.json</button>`} | |
| ${s.modal === 'audit' && html`<p class="lede">Exact disk measurements, without loading the model.</p><p>Choose local <code>.safetensors</code> shards from a single checkpoint. Only their eight-byte prefixes and JSON headers are read. Tensor byte ranges are validated against file sizes. No data leaves this page.</p><${Note} color=${COL.full}>The result covers only selected files. It does not certify completeness, infer logical FP4 parameters from I8 shapes, or silently replace the estimated 3D scene.<//><input ref=${this.fileRef} type="file" accept=".safetensors" multiple hidden onChange=${e => this.audit([...e.target.files])}/><button class="audit-picker" disabled=${s.auditing} onClick=${ () => this.fileRef.current.click()}><${Icon} name="expand" size=${24}/><b>${s.auditing ? 'Reading headers ' + (s.auditProgress || '') : 'Choose checkpoint shards'}</b><small>Files stay on this device / header limit 64 MiB per shard</small></button>${s.auditError && html`<div class="error-note" role="alert">${s.auditError}</div>`} | |
| ${s.audit && html`<${Section} title="Measured selected files" tag="EXACT DATA_OFFSETS"><${Fact} label="Selected shards / tensors" value=${s.audit.results.length + ' / ' + num(s.audit.tensorCount)}/><${Fact} label="Stored tensor payload" value=${bytes(s.audit.payload, s.binary)} color=${COL.dec}/><${Fact} label="Actual selected file sizes" value=${bytes(s.audit.fileBytes, s.binary)}/><${Fact} label="Headers including prefixes" value=${bytes(s.audit.headerBytes, s.binary)}/><${Fact} label="Other / gaps / padding" value=${num(s.audit.fileBytes - s.audit.payload - s.audit.headerBytes) + ' B'}/><div class="audit-groups">${Object.entries(s.audit.groups).sort( (a, b) => b[1] - a[1]).map( ([k,v]) => html`<${Fact} label=${k} value=${bytes(v, s.binary)}/>`)}</div><p class="fine">Groups use tensor names. Unmapped names still count in the exact total. Scale and auxiliary tensors are measured too. The scene remains the labeled derived model.</p><button class="outline-btn full-width" onClick=${ () => safeDownload('deepseek-exact-selected-shard-audit.json', JSON.stringify({ | |
| scope: 'Selected local files only; completeness not verified', | |
| ...s.audit | |
| }, null, 2))}>Export exact audit JSON</button><//>`}`} | |
| ${s.modal === 'credits' && html`<p>Visualization and WebGL2 engine generated for this single-file atlas. Model data is attributed to DeepSeek-AI's card, configuration and reference implementation. The model card identifies the repository and weights as MIT-licensed.</p><h3>Preact</h3><p>MIT License. Copyright (c) 2015-present Jason Miller. Embedded runtime; source: <a href="https://github.com/preactjs/preact" target="_blank" rel="noopener noreferrer">preactjs/preact</a>.</p><pre class="license-text">${MIT_LICENSE}</pre><h3>HTM</h3><p>Copyright (c) Jason Miller. Licensed under the Apache License, Version 2.0. Embedded runtime; source: <a href="https://github.com/developit/htm" target="_blank" rel="noopener noreferrer">developit/htm</a>.</p><pre class="license-text">${APACHE_LICENSE}</pre>`} | |
| </div></div></div>`; | |
| } | |
| render() { | |
| const s = this.state | |
| , phases = phasesFor(s.activity) | |
| , phase = phaseAt(s.time, s.activity) | |
| , phaseI = phases.indexOf(phase) | |
| , nv = s.precision === 'nvfp4' | |
| , selected = MODULES.find(x => x.id === s.selected); | |
| return html`<div class=${"app-shell" + (s.timelineHidden ? " timeline-hidden" : "")} style=${{ | |
| "--font-scale": s.fontScale, | |
| "--panel-width": Math.min(490, 390 * s.fontScale) + "px" | |
| }}> | |
| <header class="topbar" ref=${this.header}><a class="brand header-brand" href="#" onClick=${e => { | |
| e.preventDefault(); | |
| this.setView('architecture'); | |
| } | |
| } aria-label="DeepSeek V4.1 Flash architecture explorer home"><span class="brand-mark" aria-hidden="true"><i/><i/><i/></span><div class="header-wordmark"><div class="eyebrow">INTERACTIVE ARCHITECTURE EXPLORER</div><h1>DeepSeek <em>V4.1 Flash</em><span class="title-dot">.</span></h1></div></a><nav class="header-navigation" aria-label="View and checkpoint controls"><div class="view-tabs" role="tablist" aria-label="Visualization view">${[['architecture', 'Architecture', '01'], ['storage', 'Storage', '02'], ['cache', 'KV lab', '03']].map( ([v,label,n]) => html`<button role="tab" aria-selected=${s.view === v} class=${s.view === v ? 'active' : ''} onClick=${ () => this.setView(v)}><small>${n}</small>${label}</button>`)}</div><div class="precision-toggle" role="group" aria-label="Checkpoint precision">${Object.entries(MODE_INFO).map( ([mode,inf]) => html`<button aria-pressed=${s.precision === mode} class=${s.precision === mode ? 'active' : ''} style=${{ | |
| '--precision': inf.color | |
| }} onClick=${ () => this.patch({ | |
| precision: mode | |
| })} title=${inf.note}><b>${inf.short}</b><span>${bytes(TOTALS[mode], s.binary)}</span></button>`)}</div></nav><div class="header-actions"><div class="font-controls" role="group" aria-label="Text size"><button aria-label="Decrease font size" title="Smaller text (-)" disabled=${s.fontScale <= .85} onClick=${ () => this.changeFont(-.1)}>-</button><button class="font-reset" aria-label="Reset font size" title="Reset text size" onClick=${ () => this.patch({ | |
| fontScale: 1 | |
| })}>${Math.round(s.fontScale * 100)}%</button><button aria-label="Increase font size" title="Larger text (+)" disabled=${s.fontScale >= 1.5} onClick=${ () => this.changeFont(.1)}>+</button></div><button class="icon-button" title="Show or hide inspector (B)" aria-label="Toggle inspector" aria-controls="inspector-panel" aria-expanded=${s.mobile ? s.sheet : !s.panelHidden} onClick=${ () => this.togglePanel()}><${Icon} name="menu"/></button><span class="offline-badge"><i/> OFFLINE-READY</span><button class="icon-button" title="Credits and evidence" aria-label="View sources" onClick=${ () => this.patch({ | |
| tab: 'sources', | |
| sheet: s.mobile, | |
| panelHidden: false | |
| })}><${Icon} name="link"/></button><button class="icon-button" title="Keyboard and mouse controls" aria-label="Help" onClick=${ () => this.patch({ | |
| modal: 'help' | |
| })}><${Icon} name="help"/></button></div></header> | |
| <div class="workspace"><nav class="layer-rail" aria-label="Model modules"><button class=${'rail-special' + (s.selected === 'D0' ? ' selected' : '')} title="DSpark draft stages" onClick=${ () => { | |
| this.patch({ | |
| view: 'architecture' | |
| }); | |
| this.select('D0'); | |
| } | |
| }>D</button><div class="rail-caption">OUT</div><div class="rail-layers">${[...LAYERS].reverse().map(m => html`<button key=${m.id} aria-label=${m.label + ' ' + m.mode} class=${'rail-layer ' + (s.selected === m.id ? 'selected' : '')} style=${{ | |
| '--layer-color': COL[m.mode] | |
| }} onClick=${ () => { | |
| if (s.view !== 'architecture') | |
| this.patch({ | |
| view: 'architecture' | |
| }); | |
| this.select(m.id); | |
| } | |
| } title=${m.label + ' / ' + m.mode + ' / ' + m.part}><span>${String(m.index).padStart(2, '0')}</span><i/></button>`)}</div><div class="rail-caption">IN</div><button class=${'rail-special' + (s.selected === 'vision' ? ' selected' : '')} title="Vision encoder" onClick=${ () => { | |
| this.patch({ | |
| view: 'architecture' | |
| }); | |
| this.select('vision'); | |
| } | |
| }>V</button></nav> | |
| <main class="main"> | |
| ${s.view === 'architecture' && html`<div class="architecture-controls" aria-label="Architecture controls"><div class="deck-switch" role="group" aria-label="Backbone layout"><button class=${s.layout === 'tower' ? 'active' : ''} aria-pressed=${s.layout === 'tower'} onClick=${ () => this.setLayout('tower')}>Tower <small>L00-L39</small></button><button class=${s.layout === 'split' ? 'active' : ''} aria-pressed=${s.layout === 'split'} onClick=${ () => this.setLayout('split')}>Split <small>20 + 20</small></button></div><span class="same-weights" title="Same checkpoint in either layout. Both groups run during decoding.">One sequence, not two routes.</span><div class="activity-switch" role="group" aria-label="Computation story"><button class=${s.activity === 'inference' ? 'active' : ''} aria-pressed=${s.activity === 'inference'} onClick=${ () => this.setActivity('inference')}>Inference</button><button class=${s.activity === 'training' ? 'active' : ''} aria-pressed=${s.activity === 'training'} onClick=${ () => this.setActivity('training')}>Training <small>schematic</small></button></div></div>`} | |
| <section class=${'world' + (s.view === 'architecture' && selected ? ' exploded' : '')} aria-label="Interactive 3D visualization"><canvas ref=${this.canvas} tabIndex="0" aria-label="3D model. Drag to orbit, right-drag to pan, scroll to zoom. Use the layer navigation for accessible selection."/><div class="world-labels" ref=${this.labels}/><div class="scene-tip" ref=${this.tip}><b/><span/><p/></div> | |
| <div class="scene-heading"><span class="scene-index">${s.view === 'architecture' ? '01' : s.view === 'storage' ? '02' : '03'} /</span><div><b>${s.view === 'architecture' ? (selected ? selected.label + ' / ' + (s.expertView ? 'individual experts' : 'tensor dissection') : s.layout === 'tower' ? 'The checkpoint in layer order' : 'One forward pass, shown in two groups') : s.view === 'storage' ? 'The weight of the model' : 'The global cache, isolated'}</b><small>${s.view === 'architecture' ? (selected ? 'Each solid is a named stored tensor. Click to explain.' : s.layout === 'tower' ? 'L00-L39 / one inventory / click any layer to unfold.' : 'Decode: L00-L19 then L20-L39 then head. No layers skipped.') : s.view === 'storage' ? 'Same logical weights. Different storage formats.' : 'Runtime cache, independent of the checkpoint switch.'}</small></div></div> | |
| ${s.view === 'architecture' && selected && html`<div class="expansion-tools"><div class="expansion-switch" role="group" aria-label="Tensor arrangement"><button aria-pressed=${s.detailLayout === 'column'} class=${s.detailLayout === 'column' ? 'active' : ''} onClick=${ () => this.patch({ | |
| detailLayout: 'column' | |
| })}>Columns</button><button aria-pressed=${s.detailLayout === 'floor'} class=${s.detailLayout === 'floor' ? 'active' : ''} onClick=${ () => this.patch({ | |
| detailLayout: 'floor' | |
| })}>Floor</button></div>${(selected.mode || selected.id.startsWith('D')) && html`<div class="expansion-switch" role="group" aria-label="Expert representation"><button aria-pressed=${!s.expertView} class=${!s.expertView ? 'active' : ''} onClick=${ () => this.patch({ | |
| expertView: false, | |
| expert: null | |
| })}>Tensor banks</button><button aria-pressed=${s.expertView} class=${s.expertView ? 'active' : ''} onClick=${ () => this.patch({ | |
| expertView: true, | |
| expert: null | |
| })}>${selected.id.startsWith('D') ? '128' : '384'} experts</button></div>`}<button class="tool-btn fit-text" onClick=${ () => this.engine && this.engine.focus(selected.id)}>Fit layer</button><button class="tool-btn" aria-label="Close layer expansion" onClick=${ () => this.patch({ | |
| selected: null, | |
| tensor: null, | |
| hoverTensor: null, | |
| expert: null | |
| })}><${Icon} name="close" size=${11}/> Close</button></div><div class="expansion-caption">${s.detailLayout === 'floor' && !s.expertView ? '' : s.expertView ? 'Click an expert to split its gate, up and down matrices.' : 'Click a label or marker. Full inventory stays in the inspector.'}</div>`} | |
| <div class="scene-tools"><button class=${'icon-button ' + (s.labels ? 'active' : '')} aria-label="Toggle all layer labels" aria-pressed=${s.labels} onClick=${ () => this.patch({ | |
| labels: !s.labels | |
| })} title="Layer labels (L)"><${Icon} name="labels"/></button><button class=${'icon-button ' + (s.links ? 'active' : '')} aria-label="Toggle flow paths" aria-pressed=${s.links} onClick=${ () => this.patch({ | |
| links: !s.links | |
| })} title="Flow paths (K)"><${Icon} name="link"/></button><button class="icon-button" aria-label="Reset camera" onClick=${ () => this.engine && this.engine.reset()} title="Reset view (R)"><${Icon} name="reset"/></button><button class="icon-button" aria-label="Save 3D canvas image" onClick=${ () => this.snapshot()} title="Save canvas image"><${Icon} name="camera"/></button></div> | |
| <div class="scene-scale"><span class="scale-glyph"/><span>1 cubic unit = 1 GB <em>${s.view === 'cache' ? 'global KV payload' : 'modeled weight payload'} / ${this.engine && this.engine.renderer.software ? 'CPU 3D' : 'WebGL2'}</em></span></div><div class="scene-legend">${(s.view === 'cache' ? [['full', 'Encoder KV'], ['dec', 'Decoder KV']] : s.view === 'storage' ? [['expert', 'Experts'], ['engram', 'Engram'], ['attn', 'Attention']] : [['full', 'Full'], ['reindex', 'Reindex'], ['reuse', 'Reuse'], ['swa', 'SWA only']]).map( ([key,l]) => html`<span><i style=${{ | |
| background: COL[key] | |
| }}/>${l}</span>`)}</div> | |
| ${s.view === 'architecture' && s.activity === 'inference' && (phaseI === 3 || s.selected && s.selected[0] === 'D') && html`<div class="draft-diagram"><div><b>DSPARK / FIVE-TOKEN DRAFT</b><small>Synthetic confidence example</small></div><div class="draft-cells">${[.98, .94, .87, .62, .41].map( (confidence, i) => { | |
| let step = (s.time >= 16 ? s.time - 16 : 2) | |
| , stage = step < 1.2 ? 'proposed' : step < 2.8 ? 'scored' : i < 3 ? 'accepted' : i === 3 ? 'corrected' : 'redraft'; | |
| return html`<div class=${'draft-cell ' + stage}><small>t + ${i + 1}</small><b>${step < 1.2 ? '...' : confidence.toFixed(2)}</b><span>${stage}</span></div>`; | |
| } | |
| )}</div><p>Token states, not weight volumes. No measured acceptance rate.</p></div>`} | |
| ${nv && s.view !== 'cache' && html`<div class="format-notice"><b>+${bytes(NV_DELTA, s.binary)}</b> modeled vs native / smaller scale groups</div>`}${s.error && html`<div class="graphics-error"><h2>Graphics unavailable</h2><p>${s.error}</p><p>The inspector, configs, benchmarks and byte audit still work. Enable canvas rendering and reload for the 3D scene.</p></div>`} | |
| </section> | |
| <div class=${'transport-shell' + (s.timelineHidden ? ' collapsed' : '')}><footer id="animation-panel" class="transport" inert=${s.timelineHidden} aria-hidden=${s.timelineHidden} onPointerMove=${e => this.blankMove(e, 'down')} onPointerLeave=${e => e.currentTarget.classList.remove('blank-down')} onPointerDown=${e => this.blankDown(e, 'down')} onPointerUp=${e => this.blankUp(e, 'down')}><button class="timeline-collapse" aria-label="Hide animation bar" title="Hide animation bar (T), or click its blank background" onClick=${ () => this.patch({ | |
| timelineHidden: true | |
| })}><${Icon} name="down" size=${15}/></button><div class="story-text"><div><span class="story-phase" style=${{ | |
| color: phase.color | |
| }}>0${phaseI + 1} / ${phase.name.toUpperCase()}</span><b>${phase.caption}</b></div><span class="story-number" style=${{ | |
| color: phase.color | |
| }}>${phase.active}<small>${s.activity === 'training' ? 'TRAINING SCHEMATIC' : phaseI === 0 || phaseI === 2 ? 'ACTIVE / TOKEN' : 'SCHEMATIC STEP'}</small></span></div><div class="story-description">${phase.desc}</div><div class="transport-row"><button class="play-button" aria-label=${s.playing ? 'Pause animation' : 'Play animation'} onClick=${ () => this.patch({ | |
| playing: !s.playing | |
| })}><${Icon} name=${s.playing ? 'pause' : 'play'} size=${18}/></button><div class="timeline"><div class="phase-buttons">${phases.map( (p, i) => html`<button class=${phaseI === i ? 'active' : ''} style=${{ | |
| '--phase': p.color, | |
| flex: p.to - p.from | |
| }} onClick=${ () => this.seek(p.from + .01)}>${p.label || p.name}</button>`)}</div><input aria-label="Animation timeline" type="range" min="0" max="21.99" step="0.01" value=${s.time} onInput=${e => this.seek(Number(e.target.value))} style=${{ | |
| '--progress': s.time / 22 * 100 + '%' | |
| }}/></div><button class=${'phase-loop ' + (s.loopPhase ? 'active' : '')} aria-label="Loop current animation phase" aria-pressed=${s.loopPhase} title="Stay on this phase, or play the whole request story" onClick=${ () => this.patch({ | |
| loopPhase: !s.loopPhase | |
| })}>${s.loopPhase ? 'Loop phase' : 'All phases'}</button><select class="speed-select" value=${s.speed} aria-label="Animation speed" onChange=${e => this.patch({ | |
| speed: Number(e.target.value) | |
| })}>${[.5, 1, 1.5, 2].map(v => html`<option value=${v}>${v}x</option>`)}</select><button class=${'orbit-button ' + (s.orbit ? 'active' : '')} aria-label="Toggle camera auto orbit" aria-pressed=${s.orbit} onClick=${ () => this.patch({ | |
| orbit: !s.orbit | |
| })}>Orbit</button></div><div class="transport-bottom"><span>${s.activity === 'training' ? 'GENERIC TRAINING SCHEMATIC / NO REAL WEIGHT UPDATES' : phase.name === 'Prefill' ? '* OPTIMIZED PREFILL / NOT THE REFERENCE FORWARD LOOP' : 'SCHEMATIC FLOW / NOT MEASURED TIMING'}</span><span>drag orbit / right-drag pan / wheel zoom</span></div></footer></div> | |
| <div class=${'timeline-dock' + (s.timelineHidden ? ' mini' : '')}><button aria-label=${s.timelineHidden ? 'Show animation bar' : 'Hide animation bar'} aria-expanded=${!s.timelineHidden} aria-controls="animation-panel" onClick=${ () => this.patch({ | |
| timelineHidden: !s.timelineHidden | |
| })}><${Icon} name=${s.timelineHidden ? 'up' : 'down'} size=${14}/><span>${s.timelineHidden ? 'Animation / ' + phase.name : 'Hide animation'}</span></button>${s.timelineHidden && html`<div class="mini-playback"><button aria-label=${s.playing ? 'Pause animation' : 'Play animation'} onClick=${ () => this.patch({ | |
| playing: !s.playing | |
| })}><${Icon} name=${s.playing ? 'pause' : 'play'} size=${14}/></button><select aria-label="Animation phase" value=${phaseI} onChange=${e => this.seek(phases[+e.target.value].from + .01)}>${phases.map( (p, i) => html`<option value=${i}>${p.label || p.name}</option>`)}</select></div>`}<span class="weight-state">${s.activity === 'training' ? 'Training schematic / sizes unchanged' : phase.name === 'Decode' ? 'Decode / all 40 layers' : phase.name === 'Prefill' ? 'Optimized prompt prefill' : 'Inference / weights stay fixed'}</span></div> | |
| </main>${this.renderInspector()}${(s.panelHidden || s.mobile && !s.sheet) && html`<button class="inspector-reopen" aria-label="Show inspector" aria-controls="inspector-panel" onClick=${ () => this.togglePanel(true)} title="Show inspector (B)"><${Icon} name="menu" size=${16}/><span>Inspect</span></button>`}</div>${s.mobile && s.sheet && html`<div class="sheet-scrim" onClick=${ () => this.patch({ | |
| sheet: false | |
| })}/>`}${this.renderModal()}${s.toast && html`<div class="toast" role="status">${s.toast}</div>`}</div>`; | |
| } | |
| } | |
| render(html`<${App}/>`, document.getElementById('root')); | |
| } | |
| )(); | |
| </script> | |
| </body> | |
| </html> | |
Xet Storage Details
- Size:
- 459 kB
- Xet hash:
- 60bfc669cc7f2c244ec3bf5145fb84c809e776eb48d8f86fb3401423591e1a7b
·
Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.