mirror of
https://github.com/xAL6/zero-loader
synced 2026-06-06 17:03:01 +00:00
e4ce930447
Single-origin localhost-bound Flask app that wraps the three CLI
entry points (Encrypt.py, SideloadGen.py, build.bat) so the operator
drives the whole pipeline from a browser instead of a shell.
Design:
- Bind 127.0.0.1 only; banner printed at startup; no auth.
- Three tabs (Encrypt, Sideload, Build) + an artifacts sidebar.
- Build output streams live via chunked text/plain response.
- Every compile-time flag (uac, RWX_SHELLCODE, DEBUG,
ENABLE_SYNTHETIC_STACK) is a checkbox; forwarded to build.bat
via positional args or the new CFLAGS_EXTRA env var.
- File uploads land in web/workspace/ (gitignored); downloads
served from project root with safe-name regex + no traversal.
Supporting changes:
- build.bat honors CFLAGS_EXTRA so the web UI can inject /DDEBUG
etc. without editing the batch script.
- .gitignore excludes web/.venv/ and web/workspace/.
- README.md gains a collapsible "Web Console" section under
Quick Start, pointing at web/run.bat.
Stack: Flask only (no build step on frontend); single HTML file +
vanilla JS fetch. Dark-theme CSS matching the project's README
badges.
Not a remote service. Operator is responsible for not exposing the
port — the banner says so at startup and a pill in the UI header
reads "127.0.0.1 only".
293 lines
7.0 KiB
CSS
293 lines
7.0 KiB
CSS
/* zero-loader console — minimal dark theme */
|
|
|
|
:root {
|
|
--bg: #0a0d12;
|
|
--bg-panel: #10151c;
|
|
--bg-elev: #161c25;
|
|
--bg-input: #0c1118;
|
|
--line: #1f2731;
|
|
--line-hot: #2a3341;
|
|
--text: #e4e9f0;
|
|
--text-dim: #8a95a7;
|
|
--text-mute: #5a6479;
|
|
--accent: #6ae0d7;
|
|
--accent-dim:#3e9891;
|
|
--warn: #f2b85a;
|
|
--err: #f26a6a;
|
|
--ok: #6adc84;
|
|
}
|
|
|
|
* { box-sizing: border-box; }
|
|
html, body { margin: 0; padding: 0; height: 100%; }
|
|
|
|
body {
|
|
background: var(--bg);
|
|
color: var(--text);
|
|
font: 14px/1.55 "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
|
|
display: grid;
|
|
grid-template-columns: 1fr 320px;
|
|
grid-template-rows: auto auto 1fr;
|
|
grid-template-areas:
|
|
"top top"
|
|
"tabs sidebar"
|
|
"stage sidebar";
|
|
min-height: 100vh;
|
|
}
|
|
|
|
/* ---------- top bar ---------- */
|
|
.top {
|
|
grid-area: top;
|
|
padding: 14px 22px;
|
|
border-bottom: 1px solid var(--line);
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
gap: 16px;
|
|
background: var(--bg-panel);
|
|
}
|
|
.brand { display: flex; align-items: baseline; gap: 10px; }
|
|
.brand .glyph { color: var(--accent); font-size: 14px; }
|
|
.brand .title { font-weight: 600; letter-spacing: .4px; font-size: 15px; }
|
|
.brand .subtitle { color: var(--text-mute); font-size: 12px; letter-spacing: .4px; }
|
|
|
|
.status-bar { display: flex; gap: 8px; }
|
|
.pill {
|
|
font-size: 11px;
|
|
padding: 4px 10px;
|
|
border: 1px solid var(--line-hot);
|
|
border-radius: 100px;
|
|
background: var(--bg-input);
|
|
color: var(--text-dim);
|
|
letter-spacing: .4px;
|
|
}
|
|
.pill.ok { color: var(--ok); border-color: #264a32; }
|
|
.pill.bad { color: var(--err); border-color: #4a2626; }
|
|
.pill.warn { color: var(--warn); border-color: #4a3a1e; }
|
|
|
|
/* ---------- tabs ---------- */
|
|
.tabs {
|
|
grid-area: tabs;
|
|
display: flex;
|
|
gap: 2px;
|
|
padding: 0 22px;
|
|
background: var(--bg-panel);
|
|
border-bottom: 1px solid var(--line);
|
|
}
|
|
.tab {
|
|
background: transparent;
|
|
color: var(--text-mute);
|
|
border: 0;
|
|
padding: 12px 18px;
|
|
font: inherit;
|
|
letter-spacing: .5px;
|
|
cursor: pointer;
|
|
border-bottom: 2px solid transparent;
|
|
transition: color .15s, border-color .15s;
|
|
}
|
|
.tab:hover { color: var(--text-dim); }
|
|
.tab.active { color: var(--accent); border-color: var(--accent); }
|
|
|
|
/* ---------- stage ---------- */
|
|
.stage { grid-area: stage; padding: 26px 22px; overflow: auto; }
|
|
.panel { display: none; max-width: 900px; }
|
|
.panel.active { display: block; }
|
|
|
|
.panel-head { margin-bottom: 18px; }
|
|
.panel-head h2 { margin: 0 0 6px; font-size: 18px; font-weight: 600; letter-spacing: .3px; }
|
|
.panel-head p { margin: 0; color: var(--text-dim); font-size: 13px; }
|
|
|
|
/* ---------- forms ---------- */
|
|
.form { display: flex; flex-direction: column; gap: 16px; }
|
|
|
|
.field {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 6px;
|
|
font-size: 12px;
|
|
color: var(--text-dim);
|
|
letter-spacing: .3px;
|
|
}
|
|
.field > span, .field > legend {
|
|
text-transform: uppercase;
|
|
font-size: 11px;
|
|
letter-spacing: .6px;
|
|
color: var(--text-mute);
|
|
}
|
|
.field input[type="file"],
|
|
.field input[type="text"],
|
|
.field input[type="url"] {
|
|
background: var(--bg-input);
|
|
border: 1px solid var(--line);
|
|
color: var(--text);
|
|
padding: 9px 12px;
|
|
font: inherit;
|
|
font-size: 13px;
|
|
border-radius: 4px;
|
|
outline: none;
|
|
transition: border-color .15s;
|
|
}
|
|
.field input:focus { border-color: var(--accent-dim); }
|
|
|
|
.row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
|
|
|
|
.field.radio {
|
|
flex-direction: row;
|
|
align-items: center;
|
|
gap: 8px;
|
|
padding: 10px 12px;
|
|
border: 1px solid var(--line);
|
|
border-radius: 4px;
|
|
background: var(--bg-input);
|
|
cursor: pointer;
|
|
font-size: 13px;
|
|
color: var(--text);
|
|
}
|
|
.field.radio:has(input:checked) { border-color: var(--accent-dim); color: var(--accent); }
|
|
|
|
fieldset.field {
|
|
border: 1px solid var(--line);
|
|
border-radius: 4px;
|
|
padding: 14px 16px;
|
|
margin: 0;
|
|
}
|
|
fieldset.field legend { padding: 0 6px; }
|
|
|
|
.check {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
padding: 6px 0;
|
|
font-size: 13px;
|
|
color: var(--text);
|
|
cursor: pointer;
|
|
}
|
|
.check input { accent-color: var(--accent); }
|
|
|
|
/* ---------- actions ---------- */
|
|
.actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 14px;
|
|
margin-top: 4px;
|
|
flex-wrap: wrap;
|
|
}
|
|
|
|
button {
|
|
background: var(--bg-elev);
|
|
color: var(--text);
|
|
border: 1px solid var(--line-hot);
|
|
padding: 10px 18px;
|
|
font: inherit;
|
|
font-size: 13px;
|
|
letter-spacing: .4px;
|
|
border-radius: 4px;
|
|
cursor: pointer;
|
|
transition: background .15s, border-color .15s, color .15s;
|
|
}
|
|
button:hover { border-color: var(--accent-dim); color: var(--accent); }
|
|
button.primary {
|
|
background: var(--accent-dim);
|
|
color: #08100e;
|
|
border-color: var(--accent-dim);
|
|
font-weight: 600;
|
|
}
|
|
button.primary:hover { background: var(--accent); color: #08100e; }
|
|
button:disabled { opacity: .4; cursor: not-allowed; }
|
|
|
|
.hint { color: var(--text-mute); font-size: 12px; }
|
|
|
|
/* ---------- console ---------- */
|
|
.console {
|
|
margin: 18px 0 0;
|
|
background: #06090d;
|
|
border: 1px solid var(--line);
|
|
border-radius: 4px;
|
|
padding: 14px 16px;
|
|
color: var(--text-dim);
|
|
font-size: 12.5px;
|
|
min-height: 80px;
|
|
max-height: 380px;
|
|
overflow: auto;
|
|
white-space: pre-wrap;
|
|
word-break: break-word;
|
|
}
|
|
.console.ok { color: var(--ok); }
|
|
.console.bad { color: var(--err); border-color: #4a2626; }
|
|
|
|
.preview-wrap { margin-top: 14px; }
|
|
.preview-wrap summary {
|
|
cursor: pointer;
|
|
color: var(--text-mute);
|
|
font-size: 12px;
|
|
letter-spacing: .4px;
|
|
padding: 6px 0;
|
|
}
|
|
.preview-wrap summary:hover { color: var(--text-dim); }
|
|
.preview {
|
|
margin: 8px 0 0;
|
|
background: #06090d;
|
|
border: 1px solid var(--line);
|
|
border-radius: 4px;
|
|
padding: 14px 16px;
|
|
color: var(--text-dim);
|
|
font-size: 12px;
|
|
max-height: 280px;
|
|
overflow: auto;
|
|
white-space: pre;
|
|
}
|
|
|
|
/* ---------- sidebar ---------- */
|
|
.sidebar {
|
|
grid-area: sidebar;
|
|
background: var(--bg-panel);
|
|
border-left: 1px solid var(--line);
|
|
padding: 22px;
|
|
overflow: auto;
|
|
}
|
|
.sidebar h3 {
|
|
margin: 0 0 14px;
|
|
font-size: 12px;
|
|
font-weight: 600;
|
|
letter-spacing: .8px;
|
|
text-transform: uppercase;
|
|
color: var(--text-mute);
|
|
}
|
|
.artifacts { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 8px; }
|
|
.artifact {
|
|
padding: 12px 14px;
|
|
border: 1px solid var(--line);
|
|
border-radius: 4px;
|
|
background: var(--bg-input);
|
|
}
|
|
.artifact .name {
|
|
font-size: 13px;
|
|
color: var(--text);
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
gap: 10px;
|
|
}
|
|
.artifact .meta { font-size: 11px; color: var(--text-mute); margin-top: 4px; }
|
|
.artifact a.dl {
|
|
color: var(--accent);
|
|
text-decoration: none;
|
|
font-size: 11px;
|
|
letter-spacing: .4px;
|
|
padding: 3px 8px;
|
|
border: 1px solid var(--line-hot);
|
|
border-radius: 3px;
|
|
transition: background .1s;
|
|
}
|
|
.artifact a.dl:hover { background: var(--accent-dim); color: #08100e; }
|
|
|
|
.footnote { color: var(--text-mute); font-size: 11px; margin-top: 14px; }
|
|
.empty-note { color: var(--text-mute); font-size: 12px; padding: 10px 0; }
|
|
|
|
code {
|
|
background: var(--bg-input);
|
|
padding: 1px 5px;
|
|
border-radius: 3px;
|
|
font-size: 11.5px;
|
|
color: var(--accent);
|
|
}
|