ROCmPort-AI / index.html
tazwarrrr's picture
initial update
1985a9f verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ROCmPort AI</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link
href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500&family=Space+Grotesk:wght@500;600;700&display=swap"
rel="stylesheet">
<style>
:root {
--bg: #030303;
--s1: #0a0a0b;
--s2: #121214;
--s3: #1a1a1e;
--b1: rgba(255, 255, 255, 0.08);
--b2: rgba(255, 255, 255, 0.15);
--red: #ff3344;
--red-glow: rgba(255, 51, 68, 0.4);
--green: #00ff88;
--green-glow: rgba(0, 255, 136, 0.4);
--yellow: #ffcc00;
--cyan: #00d9ff;
--muted: #88888e;
--t1: #a1a1aa;
--t2: #d4d4d8;
--t3: #ffffff;
--mono: 'JetBrains Mono', monospace;
--sans: 'Space Grotesk', sans-serif;
--spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
cursor: none !important;
}
.hide {
display: none !important;
}
body {
background: var(--bg);
color: var(--t1);
font-family: var(--sans);
font-size: 14px;
line-height: 1.6;
overflow-x: hidden;
min-height: 100vh;
}
/* Animated Gradient Background */
body::before {
content: '';
position: fixed;
inset: 0;
background:
radial-gradient(circle at 20% 30%, rgba(0, 217, 255, 0.05), transparent 40%),
radial-gradient(circle at 80% 70%, rgba(255, 51, 68, 0.05), transparent 40%),
radial-gradient(circle at 50% 50%, rgba(0, 255, 136, 0.03), transparent 60%);
z-index: -1;
animation: bgMove 20s ease-in-out infinite alternate;
}
@keyframes bgMove {
0% {
transform: scale(1) translate(0, 0);
}
50% {
transform: scale(1.1) translate(20px, -20px);
}
100% {
transform: scale(1) translate(-20px, 20px);
}
}
.w {
max-width: 1200px;
margin: 0 auto;
padding: 32px 24px;
position: relative;
}
/* Container Glow */
.w::after {
content: '';
position: absolute;
inset: 0;
background: radial-gradient(circle at 50% 0%, rgba(255, 51, 68, 0.08), transparent 70%);
pointer-events: none;
z-index: -1;
}
header {
padding-bottom: 24px;
border-bottom: 1px solid var(--b1);
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 24px;
}
.logo {
font-weight: 700;
font-size: 18px;
color: var(--t3);
letter-spacing: -0.02em;
}
.logo em {
font-style: normal;
color: var(--red);
text-shadow: 0 0 15px var(--red-glow);
}
.hr {
font-size: 12px;
color: var(--muted);
display: flex;
align-items: center;
gap: 10px;
background: var(--s1);
padding: 6px 12px;
border-radius: 20px;
border: 1px solid var(--b1);
}
.hd {
width: 6px;
height: 6px;
border-radius: 50%;
background: var(--green);
box-shadow: 0 0 10px var(--green-glow);
}
.hd.on {
animation: pulse 2s ease-in-out infinite;
}
@keyframes pulse {
0%,
100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.4;
transform: scale(0.8);
}
}
.g {
display: grid;
grid-template-columns: 1.2fr 0.8fr;
gap: 24px;
padding: 0;
}
.fs {
grid-column: 1 / -1;
}
@media (max-width: 900px) {
.g {
grid-template-columns: 1fr;
}
}
/* Card Styling */
.p {
background: var(--s1);
border: 1px solid var(--b1);
border-radius: 12px;
overflow: hidden;
display: flex;
flex-direction: column;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
backdrop-filter: blur(10px);
transition: transform 0.3s var(--spring), border-color 0.3s ease;
}
.p:hover {
border-color: var(--b2);
}
.ph {
padding: 12px 16px;
border-bottom: 1px solid var(--b1);
display: flex;
align-items: center;
justify-content: space-between;
font-size: 12px;
color: var(--muted);
background: rgba(255, 255, 255, 0.02);
}
.ph b {
color: var(--red);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
}
textarea.code {
width: 100%;
flex: 1;
min-height: 300px;
background: var(--bg);
border: none;
color: var(--t2);
font-family: var(--mono);
font-size: 13px;
line-height: 1.7;
padding: 20px;
resize: vertical;
outline: none;
caret-color: var(--red);
will-change: transform;
}
.db {
padding: 12px 16px;
border-top: 1px solid var(--b1);
display: flex;
align-items: center;
gap: 8px;
background: var(--s1);
}
.db .l {
font-size: 11px;
color: var(--muted);
font-weight: 500;
}
.ch {
font-family: var(--sans);
font-size: 11px;
padding: 4px 12px;
background: var(--s2);
border: 1px solid var(--b1);
border-radius: 6px;
color: var(--t1);
cursor: pointer;
transition: all 0.2s var(--spring);
}
.ch:hover {
background: var(--s3);
color: var(--t3);
transform: translateY(-1px);
border-color: var(--b2);
}
.ch.on {
background: var(--red);
border-color: var(--red);
color: #fff;
box-shadow: 0 0 15px var(--red-glow);
}
.bg {
margin: 16px;
padding: 14px;
background: var(--red);
border: none;
border-radius: 8px;
color: #fff;
font-family: var(--sans);
font-size: 14px;
font-weight: 700;
cursor: pointer;
transition: all 0.3s var(--spring);
text-transform: uppercase;
letter-spacing: 0.05em;
box-shadow: 0 4px 15px var(--red-glow);
}
.bg:hover {
background: #ff4d5a;
transform: translateY(-2px);
box-shadow: 0 6px 20px var(--red-glow);
}
.bg:active {
transform: translateY(0);
}
.bg:disabled {
opacity: 0.4;
cursor: not-allowed;
transform: none;
box-shadow: none;
}
/* Agent log */
.al {
padding: 12px;
display: flex;
flex-direction: column;
gap: 8px;
}
.ar {
padding: 12px 16px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.03);
border: 1px solid transparent;
transition: all 0.4s var(--spring);
animation: slideIn 0.5s var(--spring) forwards;
opacity: 0;
transform: translateX(20px);
}
@keyframes slideIn {
to {
opacity: 1;
transform: translateX(0);
}
}
.ar.run {
border-color: var(--cyan);
background: rgba(0, 217, 255, 0.05);
}
.ar.done {
border-color: var(--green);
background: rgba(0, 255, 136, 0.05);
}
.ar.fail {
border-color: var(--red);
background: rgba(255, 51, 68, 0.05);
}
.ar.retry {
border-color: var(--yellow);
background: rgba(255, 204, 0, 0.05);
animation: pulse-border 1.5s ease-in-out infinite;
}
@keyframes pulse-border {
50% {
border-color: rgba(255, 204, 0, 0.2);
}
}
.at {
display: flex;
align-items: center;
gap: 12px;
}
.an {
font-size: 10px;
font-weight: 700;
color: var(--muted);
min-width: 90px;
text-transform: uppercase;
letter-spacing: 0.1em;
}
.am {
font-size: 13px;
color: var(--t2);
font-weight: 500;
}
.ad {
font-size: 11px;
color: var(--muted);
margin-top: 4px;
padding-left: 102px;
white-space: pre-wrap;
line-height: 1.6;
max-height: 100px;
overflow-y: auto;
}
.ad .w {
color: var(--yellow);
font-weight: 600;
}
.ad .g {
color: var(--green);
font-weight: 600;
}
/* Horizontal Timeline */
.timeline {
display: flex;
justify-content: space-between;
padding: 16px 20px;
background: rgba(255, 255, 255, 0.02);
border-bottom: 1px solid var(--b1);
margin-bottom: 8px;
}
.node {
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
position: relative;
flex: 1;
}
.node::after {
content: '';
position: absolute;
top: 12px;
left: 50%;
width: 100%;
height: 2px;
background: var(--b1);
z-index: 0;
}
.node:last-child::after {
display: none;
}
.ni {
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--s3);
border: 2px solid var(--b1);
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
z-index: 1;
transition: all 0.4s var(--spring);
}
.node.on .ni {
background: var(--cyan);
border-color: var(--cyan);
color: #000;
box-shadow: 0 0 15px var(--cyan);
}
.node.done .ni {
background: var(--green);
border-color: var(--green);
color: #000;
box-shadow: 0 0 15px var(--green);
}
.node.fail .ni {
background: var(--red);
border-color: var(--red);
color: #fff;
}
.node.retry .ni {
animation: pulse-node 1s var(--spring) infinite;
background: var(--yellow);
border-color: var(--yellow);
}
@keyframes pulse-node {
0%,
100% {
transform: scale(1);
}
50% {
transform: scale(1.2);
}
}
.nl {
font-size: 9px;
font-weight: 700;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.node.on .nl,
.node.done .nl {
color: var(--t3);
}
/* Tabs */
.tabs {
display: flex;
gap: 8px;
}
.tab {
background: var(--s2);
border: 1px solid var(--b1);
padding: 6px 16px;
border-radius: 8px;
font-family: var(--sans);
font-size: 12px;
font-weight: 600;
color: var(--muted);
cursor: pointer;
transition: all 0.2s var(--spring);
}
.tab:hover {
color: var(--t2);
background: var(--s3);
}
.tab.on {
color: var(--t3);
background: var(--red);
border-color: var(--red);
box-shadow: 0 0 10px var(--red-glow);
}
.tc {
display: none;
padding: 0;
animation: fadeIn 0.4s ease;
}
.tc.on {
display: block;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Summary row */
.sum-row {
padding: 24px;
display: flex;
align-items: center;
gap: 32px;
flex-wrap: wrap;
border-bottom: 1px solid var(--b1);
background: rgba(0, 255, 136, 0.02);
}
.sum-big {
font-size: 32px;
font-weight: 800;
color: var(--green);
line-height: 1;
letter-spacing: -0.02em;
text-shadow: 0 0 20px var(--green-glow);
}
.sum-big .u {
font-size: 13px;
font-weight: 500;
color: var(--muted);
margin-left: 4px;
display: block;
margin-top: 4px;
letter-spacing: 0;
}
.sum-big .vic {
font-size: 11px;
color: var(--cyan);
font-weight: 600;
display: block;
margin-top: 8px;
text-shadow: none;
opacity: 0.8;
}
.sum-sep {
width: 1px;
height: 40px;
background: var(--b1);
}
.sum-chk {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
color: var(--t2);
font-weight: 500;
}
.sum-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.sum-dot.ok {
background: var(--green);
box-shadow: 0 0 8px var(--green-glow);
}
.sum-dot.no {
background: var(--red);
box-shadow: 0 0 8px var(--red-glow);
}
.sum-dot.na {
background: var(--muted);
box-shadow: none;
}
.sum-type {
font-size: 11px;
color: var(--cyan);
text-transform: uppercase;
letter-spacing: 0.1em;
font-weight: 700;
padding: 4px 10px;
background: rgba(0, 217, 255, 0.1);
border-radius: 4px;
}
.sum-bar {
padding: 16px 24px;
display: flex;
align-items: center;
gap: 12px;
flex-wrap: wrap;
border-bottom: 1px solid var(--b1);
}
.bs {
font-family: var(--sans);
font-size: 11px;
font-weight: 700;
padding: 8px 16px;
border-radius: 8px;
border: 1px solid var(--b1);
background: var(--s2);
color: var(--t2);
cursor: pointer;
transition: all 0.2s var(--spring);
text-transform: uppercase;
letter-spacing: 0.05em;
}
.bs:hover {
border-color: var(--b2);
transform: translateY(-1px);
background: var(--s3);
}
.bs.r {
background: var(--bg);
border-color: var(--red);
color: var(--red);
}
.bs.r:hover {
background: var(--red);
color: #fff;
box-shadow: 0 4px 15px var(--red-glow);
}
.bs.gr {
background: var(--green);
border-color: var(--green);
color: #000;
}
.bs.gr:hover {
box-shadow: 0 4px 15px var(--green-glow);
transform: translateY(-2px);
}
.sp {
flex: 1;
}
/* Details tab */
.dm {
display: grid;
grid-template-columns: repeat(5, 1fr);
border-bottom: 1px solid var(--b1);
}
@media (max-width: 800px) {
.dm {
grid-template-columns: repeat(2, 1fr);
}
}
.di {
padding: 20px;
border-right: 1px solid var(--b1);
background: rgba(255, 255, 255, 0.01);
}
.di:last-child {
border-right: none;
}
.dl {
font-size: 10px;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.1em;
margin-bottom: 8px;
font-weight: 700;
}
.dv {
font-size: 20px;
font-weight: 800;
line-height: 1;
margin-bottom: 4px;
color: var(--t3);
}
.dv.g {
color: var(--green);
}
.dv.c {
color: var(--cyan);
}
.dv.y {
color: var(--yellow);
}
.dv.t {
color: var(--t2);
font-size: 13px;
}
.ds {
font-size: 10px;
color: var(--muted);
line-height: 1.4;
}
/* Benchmark bars */
.bk {
padding: 24px;
border-bottom: 1px solid var(--b1);
}
.bk-t {
font-size: 11px;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.1em;
margin-bottom: 16px;
font-weight: 700;
}
.br {
display: flex;
align-items: center;
gap: 16px;
margin-bottom: 12px;
}
.br:last-child {
margin-bottom: 0;
}
.bl {
font-size: 12px;
color: var(--t2);
width: 140px;
flex-shrink: 0;
font-weight: 500;
}
.bt {
flex: 1;
height: 8px;
background: var(--bg);
border-radius: 4px;
overflow: hidden;
border: 1px solid var(--b1);
}
.bf {
height: 100%;
border-radius: 4px;
transition: width 1s var(--spring);
width: 0;
}
.bf.bad {
background: linear-gradient(90deg, #ff334466, #ff3344);
box-shadow: 0 0 10px rgba(255, 51, 68, 0.3);
}
.bf.good {
background: linear-gradient(90deg, #00ff8866, #00ff88);
box-shadow: 0 0 10px rgba(0, 255, 136, 0.3);
}
.bv {
font-size: 12px;
font-weight: 700;
width: 40px;
text-align: right;
flex-shrink: 0;
}
.bv.bad {
color: var(--red);
}
.bv.good {
color: var(--green);
}
/* Simple mode note */
.sn {
padding: 20px;
border: 1px solid var(--cyan);
border-radius: 12px;
background: rgba(0, 217, 255, 0.05);
margin: 24px;
font-size: 13px;
color: var(--t2);
line-height: 1.6;
border-left-width: 4px;
}
/* Diff */
.dg {
display: grid;
grid-template-columns: 1fr 1fr;
background: var(--bg);
}
.dfs {
min-width: 0;
}
@media (max-width: 780px) {
.dg {
grid-template-columns: 1fr;
}
.dfs:first-child {
border-right: none !important;
border-bottom: 1px solid var(--b1);
}
}
.dfs:first-child {
border-right: 1px solid var(--b1);
}
.dfh {
padding: 10px 16px;
border-bottom: 1px solid var(--b1);
font-size: 11px;
color: var(--muted);
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
background: var(--s2);
}
.dft {
font-size: 9px;
font-weight: 800;
padding: 2px 6px;
border-radius: 4px;
text-transform: uppercase;
}
.dft.cu {
background: rgba(255, 51, 68, 0.2);
color: var(--red);
}
.dft.ro {
background: rgba(0, 255, 136, 0.2);
color: var(--green);
}
.dfp {
padding: 20px;
font-family: var(--mono);
font-size: 12px;
line-height: 1.7;
overflow: auto;
max-height: min(70vh, 760px);
white-space: pre-wrap;
overflow-wrap: anywhere;
word-break: break-word;
tab-size: 2;
color: var(--t2);
}
.dlo {
background: rgba(255, 51, 68, 0.08);
color: var(--red);
display: block;
border-left: 2px solid rgba(255, 51, 68, 0.45);
padding-left: 8px;
}
.dln {
background: rgba(0, 255, 136, 0.08);
color: var(--green);
display: block;
border-left: 2px solid rgba(0, 255, 136, 0.45);
padding-left: 8px;
}
/* Loading Skeleton */
.skeleton {
position: relative;
overflow: hidden;
background: var(--s2);
border-radius: 12px;
height: 200px;
margin-top: 24px;
}
.skeleton::after {
content: '';
position: absolute;
inset: 0;
transform: translateX(-100%);
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.05), transparent);
animation: shimmer 1.5s infinite;
}
@keyframes shimmer {
100% {
transform: translateX(100%);
}
}
/* Custom Cursor */
#cursor {
position: fixed;
width: 20px;
height: 20px;
background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.4);
border-radius: 50%;
pointer-events: none;
z-index: 9999;
transition: transform 0.1s ease, width 0.3s var(--spring), height 0.3s var(--spring), background 0.3s ease;
mix-blend-mode: difference;
}
#cursor.active {
transform: scale(3);
background: rgba(255, 51, 68, 0.3);
border-color: var(--red);
}
/* Modal */
.mo {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.85);
z-index: 1000;
place-items: center;
backdrop-filter: blur(8px);
}
.mo.open {
display: grid;
}
.mb {
background: var(--s1);
border: 1px solid var(--b1);
border-radius: 16px;
width: 90%;
max-width: 800px;
max-height: 90vh;
overflow: hidden;
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.6);
}
.mt {
padding: 16px 24px;
border-bottom: 1px solid var(--b1);
display: flex;
justify-content: space-between;
align-items: center;
background: var(--s2);
}
.mt h3 {
font-size: 16px;
color: var(--t3);
font-weight: 700;
}
.mx {
background: none;
border: none;
color: var(--muted);
font-size: 24px;
cursor: pointer !important;
line-height: 1;
transition: color 0.2s;
}
.mx:hover {
color: var(--t3);
}
.mc {
padding: 24px;
}
.mc textarea {
width: 100%;
height: 400px;
background: var(--bg);
border: 1px solid var(--b1);
border-radius: 8px;
padding: 16px;
color: var(--cyan);
font-family: var(--mono);
font-size: 12px;
line-height: 1.6;
resize: vertical;
outline: none;
}
.mc textarea:focus {
border-color: var(--cyan);
box-shadow: 0 0 10px rgba(0, 217, 255, 0.2);
}
.mf {
padding: 16px 24px;
border-top: 1px solid var(--b1);
display: flex;
justify-content: flex-end;
gap: 12px;
background: var(--s2);
}
::-webkit-scrollbar {
width: 6px;
height: 6px;
}
::-webkit-scrollbar-track {
background: transparent;
}
::-webkit-scrollbar-thumb {
background: var(--b1);
border-radius: 10px;
}
::-webkit-scrollbar-thumb:hover {
background: var(--b2);
}
footer {
padding: 32px 0;
border-top: 1px solid var(--b1);
display: flex;
justify-content: space-between;
font-size: 11px;
color: var(--muted);
font-weight: 500;
}
footer a {
color: var(--muted);
text-decoration: none;
transition: color 0.2s;
border-bottom: 1px solid transparent;
}
footer a:hover {
color: var(--t2);
border-bottom-color: var(--muted);
}
.idle {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
color: var(--b2);
font-size: 13px;
font-weight: 500;
min-height: 100px;
}
/* Data source badge */
.ds-badge {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 10px;
font-weight: 800;
letter-spacing: 0.08em;
text-transform: uppercase;
padding: 4px 10px;
border-radius: 4px;
margin-left: 12px;
vertical-align: middle;
}
.ds-badge.real {
background: rgba(0,255,136,0.15);
color: var(--green);
border: 1px solid rgba(0,255,136,0.3);
}
.ds-badge.demo {
background: rgba(255,204,0,0.12);
color: var(--yellow);
border: 1px solid rgba(255,204,0,0.3);
}
.ds-badge.sim {
background: rgba(255,255,255,0.06);
color: var(--muted);
border: 1px solid var(--b1);
}
/* Risk matrix panel */
.risk-panel {
margin: 0 24px 24px;
border-radius: 10px;
overflow: hidden;
border: 1px solid var(--b1);
}
.risk-header {
background: rgba(255,255,255,0.03);
padding: 10px 16px;
font-size: 11px;
font-weight: 700;
color: var(--muted);
text-transform: uppercase;
letter-spacing: 0.08em;
border-bottom: 1px solid var(--b1);
display: flex;
align-items: center;
gap: 10px;
}
.risk-badge {
font-size: 9px;
font-weight: 800;
padding: 2px 6px;
border-radius: 3px;
text-transform: uppercase;
letter-spacing: 0.05em;
}
.risk-badge.crit { background: rgba(255,51,68,0.2); color: var(--red); }
.risk-badge.high { background: rgba(255,153,0,0.2); color: #ff9900; }
.risk-badge.med { background: rgba(255,204,0,0.2); color: var(--yellow); }
.risk-row {
padding: 12px 16px;
border-bottom: 1px solid rgba(255,255,255,0.04);
display: grid;
grid-template-columns: 70px 1fr auto;
gap: 12px;
align-items: start;
font-size: 12px;
transition: background 0.2s;
}
.risk-row:last-child { border-bottom: none; }
.risk-row:hover { background: rgba(255,255,255,0.02); }
.risk-loc {
font-family: var(--mono);
font-size: 11px;
color: var(--muted);
padding-top: 1px;
}
.risk-desc { color: var(--t2); line-height: 1.5; }
.risk-hint {
font-size: 10px;
color: var(--cyan);
margin-top: 4px;
line-height: 1.4;
}
</style>
</head>
<div id="cursor"></div>
<div class="w">
<header>
<div class="logo">ROCmPort <em>AI</em></div>
<div class="hr">
<div class="hd on" id="hdot"></div>
<span id="hstat">Ready</span>
</div>
</header>
<div class="g">
<div class="p">
<div class="ph">
<div><b>//</b> CUDA source</div>
<div id="lc">0 lines</div>
</div>
<textarea class="code" id="inp" spellcheck="false" placeholder="// Paste CUDA code here
// or pick a demo below
__global__ void kernel(float* A, float* B, int N) {
int idx = blockIdx.x * blockDim.x + threadIdx.x;
...
}"></textarea>
<div class="db">
<span class="l">Select a template:</span>
<button class="ch" onclick="lk('vector_add', this)">Vector addition</button>
<button class="ch" onclick="lk('matrix_multiply', this)">Matrix multiplication</button>
<button class="ch" onclick="lk('convolution_2d', this)">2D convolution</button>
<button class="ch" onclick="lk('reduction', this)">Parallel reduction</button>
</div>
<button class="bg" id="go" onclick="go()">Port to ROCm</button>
</div>
<div class="p">
<div class="ph">
<div><b>//</b> Pipeline</div>
<div id="pt">0.0s</div>
</div>
<div class="timeline" id="tl">
<!-- Nodes injected by JS -->
</div>
<div class="al" id="al">
<div class="idle">Paste CUDA code to begin migration</div>
</div>
</div>
<div class="p fs hide" id="rp">
<div class="ph">
<div style="display:flex;align-items:center;gap:12px"><b>//</b> Results</div>
<div class="tabs" id="tabs">
<button class="tab on" onclick="stab('sum',this)">Summary</button>
<button class="tab" onclick="stab('diff',this)">Visual Diff</button>
<button class="tab" onclick="stab('det',this)">Performance</button>
</div>
</div>
<div id="t-loader" class="hide">
<div class="skeleton"></div>
</div>
<div id="t-sum" class="tc on"></div>
<div id="t-diff" class="tc"></div>
<div id="t-det" class="tc">
</div>
</div>
</div>
<footer>
<div>ROCmPort AI</div>
<div><a href="https://x.com/TazwarEnan" target="_blank">Tazwar Ahnaf Enan</a> · <a
href="https://github.com/tazwaryayyyy" target="_blank">GitHub</a></div>
</footer>
</div>
<div class="mo" id="modal">
<div class="mb">
<div class="mt">
<h3>Edit ROCm code</h3><button class="mx" onclick="cm()">&times;</button>
</div>
<div class="mc"><textarea id="edt"></textarea></div>
<div class="mf"><button class="bs" onclick="cm()">Cancel</button><button class="bs r"
onclick="rec()">Re-test</button></div>
</div>
</div>
<script>
const API = window.location.protocol === 'file:'
? 'http://localhost:8000'
: window.location.origin;
const S = { code: '', kn: 'custom', run: false, t0: null, iv: null, rep: null, tl: [], kernels: {} };
const AG = {
analyzer: { n: 'ANALYZER', i: '🔍' },
translator: { n: 'TRANSLATOR', i: '🔄' },
optimizer: { n: 'OPTIMIZER', i: '⚡' },
tester: { n: 'TESTER', i: '🧪' },
coordinator: { n: 'COORDINATOR', i: '📋' }
};
// Custom Cursor Logic
const cur = document.getElementById('cursor');
document.addEventListener('mousemove', (e) => {
cur.style.left = e.clientX + 'px';
cur.style.top = e.clientY + 'px';
const target = e.target;
const isClickable = target.onclick ||
target.tagName === 'BUTTON' ||
target.tagName === 'A' ||
target.tagName === 'TEXTAREA' ||
target.classList.contains('ch') ||
target.classList.contains('tab');
if (isClickable) {
cur.classList.add('active');
if (target.id === 'go') cur.style.background = 'rgba(255, 51, 68, 0.5)';
else cur.style.background = 'rgba(255, 255, 255, 0.3)';
} else {
cur.classList.remove('active');
cur.style.background = 'rgba(255, 255, 255, 0.2)';
}
});
async function init() {
const ta = document.getElementById('inp');
ta.oninput = () => {
document.getElementById('lc').textContent = ta.value.split('\n').length + ' lines';
S.code = ta.value;
};
try {
const r = await fetch(API + '/demo-kernels');
S.kernels = await r.json();
} catch (e) { S.kernels = FB; }
}
function lk(n, btn) {
document.querySelectorAll('.ch').forEach(c => c.classList.remove('on'));
btn.classList.add('on');
const code = S.kernels[n] || FB[n] || '', ta = document.getElementById('inp');
ta.value = code; S.code = code; S.kn = n;
document.getElementById('lc').textContent = code.split('\n').length + ' lines';
}
function stab(id, btn) {
document.querySelectorAll('.tab').forEach(t => t.classList.remove('on'));
document.querySelectorAll('.tc').forEach(t => t.classList.remove('on'));
btn.classList.add('on');
document.getElementById('t-' + id).classList.add('on');
if (id === 'diff' && S.rep) rDiff(S.code, S.rep.optimized_code);
}
async function go() {
if (S.run) return;
const code = document.getElementById('inp').value.trim();
if (!code) return;
S.code = code; S.run = true; S.t0 = Date.now(); S.tl = [];
const btn = document.getElementById('go');
btn.disabled = true;
btn.textContent = 'Running pipeline...';
document.getElementById('hstat').textContent = 'Pipeline running...';
document.getElementById('rp').classList.add('hide');
bLog();
sTimer();
try {
const simpleModeCheckbox = document.getElementById('sm');
const res = await fetch(API + '/port', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
cuda_code: code,
kernel_name: S.kn,
simple_mode: simpleModeCheckbox ? simpleModeCheckbox.checked : false
})
});
// Show results panel with loader immediately
document.getElementById('rp').classList.remove('hide');
document.getElementById('t-loader').classList.remove('hide');
document.getElementById('t-sum').classList.remove('on');
document.getElementById('t-diff').classList.remove('on');
document.getElementById('t-det').classList.remove('on');
const rd = res.body.getReader(), dc = new TextDecoder();
let buf = '';
while (true) {
const { done, value } = await rd.read();
if (done) break;
buf += dc.decode(value, { stream: true });
const lines = buf.split('\n');
buf = lines.pop();
for (const ln of lines) {
if (!ln.startsWith('data: ')) continue;
const raw = ln.slice(6).trim();
if (raw === '[DONE]') { done_(); break; }
try { hEvt(JSON.parse(raw)); } catch (e) { console.error('Parse error:', e); }
}
}
} catch (e) {
document.getElementById('hstat').textContent = 'Pipeline error';
document.getElementById('t-loader').classList.add('hide'); // Hide loader on error
console.error(e);
} finally {
xTimer();
S.run = false;
btn.disabled = false;
btn.textContent = 'Port to ROCm';
document.getElementById('t-loader').classList.add('hide');
}
}
function hEvt(ev) {
uLog(ev.agent, ev.status, ev.message, ev.detail);
if (ev.agent === 'tester' && (ev.status === 'done' || ev.status === 'failed')) {
const m = ev.message.match(/([\d.]+)x/);
if (m) {
const sp = parseFloat(m[1]), ok = sp >= 1, im = ev.message.match(/Iteration (\d+)/i);
S.tl.push({
label: 'Iteration ' + (im ? im[1] : S.tl.length + 1) + (ok ? ' (optimized)' : ' (baseline)'),
speedup: sp,
good: ok
});
}
}
if (ev.agent === 'coordinator' && ev.status === 'done' && ev.detail) {
try {
const r = JSON.parse(ev.detail);
S.rep = r;
rRes(r, S.tl);
} catch (e) { console.error('Coordinator detail parse error:', e); }
}
}
function done_() {
document.getElementById('hstat').textContent = 'Pipeline complete';
document.getElementById('t-loader').classList.add('hide');
if (!S.rep) {
document.getElementById('t-sum').innerHTML = '<div class="idle">Migration finished but no report was generated. Check agent logs for details.</div>';
document.getElementById('t-sum').classList.add('on');
}
}
function bLog() {
const el = document.getElementById('al');
const tl = document.getElementById('tl');
el.innerHTML = '';
tl.innerHTML = '';
let i = 0;
for (const [k, obj] of Object.entries(AG)) {
// Log row
const d = document.createElement('div');
d.className = 'ar';
d.id = 'ar-' + k;
d.style.animationDelay = (i * 0.1) + 's';
d.innerHTML = `
<div class="at">
<span class="an">${obj.n}</span>
<span class="am" id="am-${k}">Waiting</span>
</div>
<div class="ad" id="ad-${k}"></div>`;
el.appendChild(d);
// Timeline node
const n = document.createElement('div');
n.className = 'node';
n.id = 'nd-' + k;
n.title = obj.n;
n.innerHTML = `<div class="ni">${obj.i}</div><div class="nl">${obj.n.slice(0, 3)}</div>`;
tl.appendChild(n);
i++;
}
}
function uLog(a, s, m, d) {
const row = document.getElementById('ar-' + a);
const node = document.getElementById('nd-' + a);
if (!row || !node) return;
const statusClass = { running: 'run', done: 'done', failed: 'fail', retrying: 'retry' }[s] || '';
row.className = 'ar ' + statusClass;
node.className = 'node ' + (s === 'running' ? 'on' : s === 'retrying' ? 'retry' : s === 'done' ? 'done' : s === 'failed' ? 'fail' : '');
const me = document.getElementById('am-' + a);
if (me) me.textContent = m;
// Node tooltip message update
node.title = m;
const de = document.getElementById('ad-' + a);
if (de && d) {
de.innerHTML = esc(d)
.replace(/\u26a0\ufe0f([^\n]*)/g, '<span class="w">⚠️ $1</span>')
.replace(/\u2705([^\n]*)/g, '<span class="g">✅ $1</span>');
de.scrollTop = de.scrollHeight;
}
}
function rRes(r, tl) {
// Hide loader, show summary
document.getElementById('t-loader').classList.add('hide');
document.getElementById('t-sum').classList.add('on');
const v = r.verification || {}, bw = r.bandwidth_utilized;
const dot = ok => `<div class="sum-dot ${ok === true ? 'ok' : ok === false ? 'no' : 'na'}"></div>`;
// Data source badge
const ds = r.data_source || 'simulated';
const dsBadge = ds === 'real_rocm'
? `<span class="ds-badge real">🟢 LIVE MI300X</span>`
: ds === 'demo_artifact'
? `<span class="ds-badge demo">🟡 DEMO DATA</span>`
: `<span class="ds-badge sim">⚪ SIMULATED</span>`;
document.getElementById('t-sum').innerHTML = `
<div class="sum-row">
<div class="sum-big">
${r.speedup}x
${dsBadge}
<span class="u">vs baseline hipify</span>
<span class="vic">Measured against declared baseline. ${ds === 'demo_artifact' ? 'Representative MI300X values — set ROCM_AVAILABLE=true for real numbers.' : ds === 'real_rocm' ? 'Real rocprof measurement on AMD MI300X.' : 'Set ROCM_AVAILABLE=true on AMD Cloud for real numbers.'}</span>
</div>
<div class="sum-sep"></div>
<div>
<div class="sum-chk">${dot(v.compiled_successfully)} Compiled${v.mock_mode ? ' (simulated)' : ''}</div>
<div class="sum-chk" style="margin-top:8px">${dot(v.executed_without_error)} Executed without error</div>
<div class="sum-chk" style="margin-top:8px">${dot(v.output_matches_expected)} Output matches expected</div>
</div>
<div class="sum-sep"></div>
<div class="sum-type">${(r.bottleneck || 'optimized').toLowerCase()}</div>
</div>
<div class="sum-bar">
<button class="bs r" onclick="om()">Edit code</button>
<button class="bs gr" onclick="exM()">Export PR</button>
<button class="bs" onclick="dlR()">Download report</button>
<div class="sp"></div>
</div>
<div class="sn" id="sn" style="margin: 24px; border-left-width: 4px;">
<div style="font-weight: bold; margin-bottom: 8px; color: var(--cyan);">🧠 Simple explanation</div>
${r.simplified_explanation ? esc(r.simplified_explanation) : '<em>Simplified explanation will appear here</em>'}
</div>
${riskMatrix(r.static_risk_report)}`;
// Details tab
let dh = `<div class="dm">
<div class="di"><div class="dl">Speedup</div><div class="dv g">${r.speedup}x</div><div class="ds">optimized ROCm vs straight hipify output</div></div>
<div class="di"><div class="dl">Bandwidth</div><div class="dv c">${bw != null ? bw.toFixed(1) : '—'}%</div><div class="ds">of MI300X 5.3 TB/s HBM3</div></div>
<div class="di"><div class="dl">Changes</div><div class="dv y">${r.total_changes}</div><div class="ds">hipify + LLM + optimizer changes</div></div>
<div class="di"><div class="dl">Iterations</div><div class="dv c">${r.iterations || 1}</div><div class="ds">optimizer retry loop count</div></div>
<div class="di"><div class="dl">Type</div><div class="dv t">${(r.bottleneck || '—').toUpperCase()}</div><div class="ds">workload classification</div></div>
</div>`;
if (tl.length) {
dh += '<div class="bk"><div class="bk-t">Benchmark iterations (optimized vs baseline hipify)</div>';
tl.forEach(d => {
const pct = Math.min(Math.max((d.speedup / 2) * 100, 3), 95);
dh += `<div class="br">
<div class="bl">${esc(d.label)}</div>
<div class="bt"><div class="bf ${d.good ? 'good' : 'bad'}" style="width: 0" data-w="${pct}%"></div></div>
<div class="bv ${d.good ? 'good' : 'bad'}">${d.speedup}x</div>
</div>`;
});
dh += '</div>';
}
document.getElementById('t-det').innerHTML = dh;
tsm(); // Ensure simple note visibility matches current toggle state
// Progress bar animation
setTimeout(() => {
document.querySelectorAll('.bf[data-w]').forEach(b => {
b.style.width = b.dataset.w;
});
}, 100);
}
function riskMatrix(srr) {
if (!srr || !srr.items || srr.items.length === 0) return '';
const levelClass = { CRITICAL: 'crit', HIGH: 'high', MEDIUM: 'med' };
const critical = srr.critical_count || 0;
const high = srr.high_count || 0;
const medium = srr.medium_count || 0;
let rows = srr.items.map(item => {
const cls = levelClass[item.risk_level] || 'med';
const loc = item.line ? `line ${item.line}` : '—';
return `<div class="risk-row">
<div class="risk-loc">${esc(loc)}</div>
<div>
<div class="risk-desc">${esc(item.description)}</div>
<div class="risk-hint">Fix: ${esc(item.amd_fix_hint)}</div>
</div>
<div><span class="risk-badge ${cls}">${esc(item.risk_level)}</span></div>
</div>`;
}).join('');
const scanMs = srr.scan_duration_ms != null ? `${srr.scan_duration_ms.toFixed(1)}ms` : '';
return `<div class="risk-panel">
<div class="risk-header">
⚠️ Static Risk Scan
${critical > 0 ? `<span class="risk-badge crit">${critical} CRITICAL</span>` : ''}
${high > 0 ? `<span class="risk-badge high">${high} HIGH</span>` : ''}
${medium > 0 ? `<span class="risk-badge med">${medium} MEDIUM</span>` : ''}
<span style="margin-left:auto;font-size:9px;opacity:0.5">Pure-Python pre-scan · ${scanMs}</span>
</div>
${rows}
</div>`;
}
function rDiff(o, n) {
if (!o || !n) return;
document.getElementById('t-diff').innerHTML = `<div class="dg">
<div class="dfs"><div class="dfh"><span class="dft cu">CUDA</span> Original Source</div><pre class="dfp" id="d-o"></pre></div>
<div class="dfs"><div class="dfh"><span class="dft ro">ROCm</span> Optimized HIP</div><pre class="dfp" id="d-n"></pre></div>
</div>`;
const oL = o.split('\n'), nL = n.split('\n'), mx = Math.max(oL.length, nL.length);
let oH = '', nH = '';
for (let i = 0; i < mx; i++) {
const a = oL[i] ?? '', b = nL[i] ?? '', c = a !== b;
oH += `<span class="${c ? 'dlo' : ''}">${esc(a)}\n</span>`;
nH += `<span class="${c ? 'dln' : ''}">${esc(b)}\n</span>`;
}
const left = document.getElementById('d-o');
const right = document.getElementById('d-n');
left.innerHTML = oH;
right.innerHTML = nH;
// Keep both panes aligned while scrolling for easier comparison.
let syncing = false;
left.addEventListener('scroll', () => {
if (syncing) return;
syncing = true;
right.scrollTop = left.scrollTop;
syncing = false;
}, { passive: true });
right.addEventListener('scroll', () => {
if (syncing) return;
syncing = true;
left.scrollTop = right.scrollTop;
syncing = false;
}, { passive: true });
}
function sTimer() { S.iv = setInterval(() => { document.getElementById('pt').textContent = ((Date.now() - S.t0) / 1000).toFixed(1) + 's' }, 100) }
function xTimer() { clearInterval(S.iv) }
function dlR() {
const r = S.rep; if (!r) return;
const md = `# ROCmPort AI — Migration Report\n\n## Results\n- **Speedup**: ${r.speedup}x\n- **Bandwidth**: ${r.bandwidth_utilized ? r.bandwidth_utilized.toFixed(1) : '—'}%\n- **Changes**: ${r.total_changes}\n- **Iterations**: ${r.iterations}\n- **Type**: ${r.bottleneck}\n\n${r.amd_advantage_explanation ? '> ' + r.amd_advantage_explanation + '\n\n' : ''}${r.cost_estimate ? '## Cost Impact\n- Manual: ' + r.cost_estimate.manual_porting_weeks + '\n- ROCmPort: ' + r.cost_estimate.rocmport_minutes + '\n- Savings: ' + r.cost_estimate.estimated_savings + '\n\n' : ''}## ROCm/HIP Code\n\`\`\`cpp\n${r.optimized_code || ''}\n\`\`\`\n\n---\n*Generated by ROCmPort AI*\n`;
const a = document.createElement('a'); a.href = URL.createObjectURL(new Blob([md], { type: 'text/markdown' })); a.download = 'rocmport-migration-report.md'; a.click();
}
function om() { if (!S.rep) return alert('No results yet!'); document.getElementById('edt').value = S.rep?.optimized_code || ''; document.getElementById('modal').classList.add('open') }
function cm() { document.getElementById('modal').classList.remove('open') }
async function rec() {
const code = document.getElementById('edt').value.trim(); if (!code) return;
try {
const res = await fetch(API + '/recompile', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ edited_code: code, kernel_name: S.kn }) });
const r = await res.json();
if (r.success) { cm(); if (r.result) rRes(r.result, S.tl); }
else alert('Failed: ' + (r.detail || 'Unknown'))
} catch (e) { alert('Error: ' + e.message) }
}
async function exM() {
if (!S.rep) return;
try {
const currentInput = document.getElementById('inp')?.value || '';
const payload = {
original_cuda: S.code || currentInput,
final_rocm: S.rep.optimized_code || '',
migration_report: S.rep
};
const res = await fetch(API + '/export', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload)
});
if (!res.ok) {
let msg = `Export failed (${res.status})`;
try {
const err = await res.json();
if (err && err.detail) msg = err.detail;
} catch (_) { }
throw new Error(msg);
}
const a = document.createElement('a');
a.href = URL.createObjectURL(await res.blob());
a.download = 'rocmport-migration.zip';
a.click();
} catch (e) {
alert('Export error: ' + (e.message || 'Unknown error'));
}
}
function tsm() {
const sn = document.getElementById('sn');
if (sn) sn.classList.remove('hide');
}
function esc(s) { return String(s ?? '').replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;') }
const FB = {
vector_add: `#include <cuda_runtime.h>\n\n__global__ void vector_add_kernel(float* A, float* B, float* C, int N) {\n int idx = blockIdx.x * blockDim.x + threadIdx.x;\n if (idx < N) {\n C[idx] = A[idx] + B[idx];\n }\n}\n\nint main() {\n int N = 1 << 24;\n size_t size = N * sizeof(float);\n float *d_A, *d_B, *d_C;\n cudaMalloc(&d_A, size);\n cudaMalloc(&d_B, size);\n cudaMalloc(&d_C, size);\n int threads = 128;\n int blocks = (N + threads - 1) / threads;\n vector_add_kernel<<<blocks, threads>>>(d_A, d_B, d_C, N);\n cudaDeviceSynchronize();\n cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);\n return 0;\n}`,
matrix_multiply: `#include <cuda_runtime.h>\n#define WARP_SIZE 32\n\n__global__ void matmul_kernel(float* A, float* B, float* C, int N) {\n int row = blockIdx.y * blockDim.y + threadIdx.y;\n int col = blockIdx.x * blockDim.x + threadIdx.x;\n float sum = 0.0f;\n if (row < N && col < N) {\n for (int k = 0; k < N; k++)\n sum += A[row * N + k] * B[k * N + col];\n C[row * N + col] = sum;\n }\n}\n\n__global__ void warp_reduce(float* data, float* result, int N) {\n int tid = threadIdx.x;\n extern __shared__ float sdata[];\n sdata[tid] = (tid < N) ? data[tid] : 0;\n __syncthreads();\n for (int s = WARP_SIZE/2; s > 0; s >>= 1) {\n if (tid < s) sdata[tid] += sdata[tid + s];\n __syncthreads();\n }\n if (tid == 0) result[blockIdx.x] = sdata[0];\n}\n\nint main() {\n int N = 1024;\n size_t size = N * N * sizeof(float);\n float *d_A, *d_B, *d_C;\n cudaMalloc(&d_A, size);\n cudaMalloc(&d_B, size);\n cudaMalloc(&d_C, size);\n dim3 block(16, 16);\n dim3 grid((N+15)/16, (N+15)/16);\n matmul_kernel<<<grid, block>>>(d_A, d_B, d_C, N);\n cudaDeviceSynchronize();\n cudaFree(d_A); cudaFree(d_B); cudaFree(d_C);\n return 0;\n}`,
convolution_2d: `#include <cuda_runtime.h>\n#define BLOCK_SIZE 16\n\n__global__ void conv2d_kernel(\n float* input, float* kernel, float* output,\n int width, int height\n) {\n int x = blockIdx.x * blockDim.x + threadIdx.x;\n int y = blockIdx.y * blockDim.y + threadIdx.y;\n if (x >= width || y >= height) return;\n float sum = 0.0f;\n for (int ky = -1; ky <= 1; ky++) {\n for (int kx = -1; kx <= 1; kx++) {\n int ix = x + kx, iy = y + ky;\n if (ix >= 0 && ix < width && iy >= 0 && iy < height)\n sum += input[iy * width + ix] * kernel[(ky+1)*3 + (kx+1)];\n }\n }\n output[y * width + x] = sum;\n}\n\nint main() {\n int W = 2048, H = 2048;\n float *d_in, *d_ker, *d_out;\n cudaMalloc(&d_in, W*H*sizeof(float));\n cudaMalloc(&d_ker, 9*sizeof(float));\n cudaMalloc(&d_out, W*H*sizeof(float));\n dim3 block(BLOCK_SIZE, BLOCK_SIZE);\n dim3 grid((W+BLOCK_SIZE-1)/BLOCK_SIZE, (H+BLOCK_SIZE-1)/BLOCK_SIZE);\n conv2d_kernel<<<grid, block>>>(d_in, d_ker, d_out, W, H);\n cudaDeviceSynchronize();\n cudaFree(d_in); cudaFree(d_ker); cudaFree(d_out);\n return 0;\n}`,
reduction: `#include <cuda_runtime.h>\n#include <stdio.h>\n#include <iostream>\n#include <vector>\n#include <numeric>\n\n// Tree-based reduction kernel\n__global__ void reduction_kernel(float* g_idata, float* g_odata, unsigned int n) {\n extern __shared__ float sdata[];\n unsigned int tid = threadIdx.x;\n unsigned int i = blockIdx.x * (blockDim.x * 2) + threadIdx.x;\n\n float mySum = (i < n) ? g_idata[i] : 0;\n if (i + blockDim.x < n) mySum += g_idata[i + blockDim.x];\n sdata[tid] = mySum;\n __syncthreads();\n\n for (unsigned int s = blockDim.x / 2; s > 32; s >>= 1) {\n if (tid < s) sdata[tid] = mySum = mySum + sdata[tid + s];\n __syncthreads();\n }\n\n // DELIBERATE WARP-SIZE BUG: Unroll to 32 instead of 64\n if (tid < 32) {\n volatile float* vsmem = sdata;\n vsmem[tid] = mySum = mySum + vsmem[tid + 32];\n vsmem[tid] = mySum = mySum + vsmem[tid + 16];\n vsmem[tid] = mySum = mySum + vsmem[tid + 8];\n vsmem[tid] = mySum = mySum + vsmem[tid + 4];\n vsmem[tid] = mySum = mySum + vsmem[tid + 2];\n vsmem[tid] = mySum = mySum + vsmem[tid + 1];\n }\n\n if (tid == 0) g_odata[blockIdx.x] = sdata[0];\n}\n\nint main() {\n const int N = 1048576;\n // ... Host code for Parallel Reduction demo\n printf("Parallel Reduction demo loaded.\\n");\n return 0;\n}`
};
init();
</script>
</body>
</html>