102 lines · 5.7 KB
import clientSource from "./frame-client.js?raw";
import { toBase64 } from "../crypto";

async function sha256Base64(text: string): Promise<string> {
  return toBase64(new Uint8Array(await crypto.subtle.digest("SHA-256", new TextEncoder().encode(text))));
}

const STYLE = `
*{box-sizing:border-box}html,body{margin:0;height:100%;background:#0b0d10;color:#d7dce2;font:13px/1.4 "IBM Plex Sans",system-ui,sans-serif}
#app{display:grid;grid-template-columns:280px 1fr;grid-template-rows:36px 1fr 24px;height:100%}
header{grid-column:1/3;display:flex;align-items:center;gap:12px;padding:0 12px;border-bottom:1px solid #1f242b;background:#111419}
header b{font-weight:600}#ref{color:#8b949e;font-family:"IBM Plex Mono",monospace;font-size:12px}
aside{border-right:1px solid #1f242b;overflow:auto;display:flex;flex-direction:column;background:#0f1216}
.section{padding:6px 8px;font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:#8b949e;display:flex;justify-content:space-between;align-items:center}
.row{padding:2px 8px;cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.row:hover{background:#1a1f26}
.row.active{background:#1f2a37}.row.dirty{color:#e3b341}.dir{color:#aeb6bf}
.change{display:flex;gap:6px;align-items:center}.change span:nth-child(2){flex:1;overflow:hidden;text-overflow:ellipsis}
.badge{font-family:monospace;font-size:11px;width:14px}.sA{color:#3fb950}.sM{color:#e3b341}.sD{color:#f85149}
button{background:#21262d;color:#d7dce2;border:1px solid #30363d;border-radius:4px;padding:3px 8px;cursor:pointer;font:inherit}
button:disabled{opacity:.5;cursor:default}button.primary{background:#238636;border-color:#2ea043;color:#fff}
button.link{background:none;border:none;padding:0 4px;color:#8b949e}
#commit-box{padding:8px;display:flex;flex-direction:column;gap:6px;border-top:1px solid #1f242b}
textarea,input[type=text]{background:#0b0d10;color:#d7dce2;border:1px solid #30363d;border-radius:4px;padding:4px 6px;font:inherit}
.push{margin-top:auto}#message{min-height:56px;resize:vertical}#branch-hint{color:#8b949e;font-size:12px}
main{display:flex;flex-direction:column;min-width:0;min-height:0}
#tabs{display:flex;border-bottom:1px solid #1f242b;background:#111419;overflow-x:auto;min-height:32px}
.tab{display:flex;align-items:center;gap:4px;padding:0 10px;border-right:1px solid #1f242b;cursor:pointer;color:#8b949e}
.tab.active{background:#0b0d10;color:#fff}
#crumb{padding:4px 12px;color:#8b949e;font-size:12px;border-bottom:1px solid #1f242b;min-height:24px}
#body{flex:1;display:flex;min-height:0;font:13px/20px "IBM Plex Mono",ui-monospace,monospace}
#gutter{margin:0;padding:8px 8px 8px 12px;color:#484f58;text-align:right;overflow:hidden;user-select:none;white-space:pre;font:inherit}
#editor{flex:1;margin:0;border:0;border-radius:0;padding:8px;resize:none;outline:none;white-space:pre;overflow:auto;tab-size:2;font:inherit}
#notice{padding:24px;color:#8b949e}
footer{grid-column:1/3;display:flex;align-items:center;padding:0 12px;border-top:1px solid #1f242b;background:#111419;font-size:12px;color:#8b949e}
#status-msg.error{color:#f85149}
`;

const BODY = `
<div id="app">
  <header><b id="title">Loading…</b><span id="ref"></span></header>