added support for local editing with different editor

This commit is contained in:
Parker TenBroeck 2026-03-11 10:06:04 -04:00
parent 3cce2983a5
commit 0289d1171f
11 changed files with 2252 additions and 1096 deletions

File diff suppressed because it is too large Load diff

View file

@ -7,50 +7,307 @@
<link rel="stylesheet" href="styles.css" />
</head>
<body>
<header class="topbar">
<div class="brand">
<div class="dot"></div>
<div>
<div class="title">Circuit Web UI</div>
<div class="subtitle">LEDs • HEX • Switches • Buttons • circuit.vhdl</div>
</div>
</div>
<div class="controls">
<div class="status">
<span class="pill" id="wsPill">DISCONNECTED</span>
<span class="muted" id="wsUrlText"></span>
</div>
<!-- single toggle button now -->
<button id="connectToggleBtn">Connect</button>
<button id="sendInputsBtn" class="secondary" disabled>Send Inputs</button>
</div>
</header>
<main class="grid">
<!-- Left: Editor -->
<section class="card editor">
<section id="editorSection" class="card editor">
<div class="cardHeader">
<div class="cardTitle">circuit.vhdl</div>
<div class="cardTitle">VHDL</div>
<div class="cardActions">
<button id="loadExampleBtn" class="secondary">Load example</button>
</div>
</div>
<!-- NEW: editor with line-number gutter -->
<div class="editorWrap">
<pre id="lineGutter" class="lineGutter" aria-hidden="true"></pre>
<textarea id="vhdlEditor" spellcheck="false"></textarea>
</div>
</section>
<div class="hint">
Saved locally as you type. On <b>Connect</b>, the UI sends <code>{"circuit.vhdl": "..."}</code> as the first WebSocket message.
<section class="card inputs">
<div class="cardHeader cardHeaderWrap">
<div class="cardTitle">Inputs</div>
<div class="panelTopStatus">
<span class="pill state-disabled" id="statusPill">DISABLED</span>
<label class="modeToggle" for="modeToggle">
<span class="modeLabel">Local</span>
<input id="modeToggle" type="checkbox" aria-label="Toggle remote mode" />
<span class="modeSlider" aria-hidden="true"></span>
<span class="modeLabel">Remote</span>
</label>
<button id="connectToggleBtn">Connect</button>
<button id="runToggleBtn" class="secondary" disabled>Start</button>
</div>
</div>
<div class="twoCols">
<div class="block">
<div class="cardActions">
<button id="allSwOffBtn" class="secondary">All switches off</button>
<button id="allSwOnBtn" class="secondary">All switches on</button>
</div>
<div class="blockTitle">Switches (32, latched)</div>
<div id="switchGrid" class="ioGrid switchGrid">
<div class="ioCell">
<div class="toggle" data-bit="0" title="Toggle switch 0"></div>
<label>SW[0]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="1" title="Toggle switch 1"></div>
<label>SW[1]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="2" title="Toggle switch 2"></div>
<label>SW[2]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="3" title="Toggle switch 3"></div>
<label>SW[3]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="4" title="Toggle switch 4"></div>
<label>SW[4]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="5" title="Toggle switch 5"></div>
<label>SW[5]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="6" title="Toggle switch 6"></div>
<label>SW[6]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="7" title="Toggle switch 7"></div>
<label>SW[7]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="8" title="Toggle switch 8"></div>
<label>SW[8]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="9" title="Toggle switch 9"></div>
<label>SW[9]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="10" title="Toggle switch 10"></div>
<label>SW[10]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="11" title="Toggle switch 11"></div>
<label>SW[11]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="12" title="Toggle switch 12"></div>
<label>SW[12]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="13" title="Toggle switch 13"></div>
<label>SW[13]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="14" title="Toggle switch 14"></div>
<label>SW[14]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="15" title="Toggle switch 15"></div>
<label>SW[15]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="16" title="Toggle switch 16"></div>
<label>SW[16]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="17" title="Toggle switch 17"></div>
<label>SW[17]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="18" title="Toggle switch 18"></div>
<label>SW[18]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="19" title="Toggle switch 19"></div>
<label>SW[19]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="20" title="Toggle switch 20"></div>
<label>SW[20]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="21" title="Toggle switch 21"></div>
<label>SW[21]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="22" title="Toggle switch 22"></div>
<label>SW[22]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="23" title="Toggle switch 23"></div>
<label>SW[23]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="24" title="Toggle switch 24"></div>
<label>SW[24]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="25" title="Toggle switch 25"></div>
<label>SW[25]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="26" title="Toggle switch 26"></div>
<label>SW[26]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="27" title="Toggle switch 27"></div>
<label>SW[27]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="28" title="Toggle switch 28"></div>
<label>SW[28]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="29" title="Toggle switch 29"></div>
<label>SW[29]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="30" title="Toggle switch 30"></div>
<label>SW[30]</label>
</div>
<div class="ioCell">
<div class="toggle" data-bit="31" title="Toggle switch 31"></div>
<label>SW[31]</label>
</div>
</div>
</div>
<div class="block">
<div class="blockTitle">Buttons (32, momentary)</div>
<div class="subBlockTitle">Standard BTN (0-7, 16-31)</div>
<div id="buttonGrid" class="ioGrid buttonGrid">
<div class="ioCell">
<button class="momentary" type="button" data-bit="0" title="Momentary BTN 0"></button>
<label>BTN[0]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="1" title="Momentary BTN 1"></button>
<label>BTN[1]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="2" title="Momentary BTN 2"></button>
<label>BTN[2]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="3" title="Momentary BTN 3"></button>
<label>BTN[3]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="4" title="Momentary BTN 4"></button>
<label>BTN[4]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="5" title="Momentary BTN 5"></button>
<label>BTN[5]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="6" title="Momentary BTN 6"></button>
<label>BTN[6]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="7" title="Momentary BTN 7"></button>
<label>BTN[7]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="16" title="Momentary BTN 16"></button>
<label>BTN[16]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="17" title="Momentary BTN 17"></button>
<label>BTN[17]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="18" title="Momentary BTN 18"></button>
<label>BTN[18]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="19" title="Momentary BTN 19"></button>
<label>BTN[19]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="20" title="Momentary BTN 20"></button>
<label>BTN[20]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="21" title="Momentary BTN 21"></button>
<label>BTN[21]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="22" title="Momentary BTN 22"></button>
<label>BTN[22]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="23" title="Momentary BTN 23"></button>
<label>BTN[23]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="24" title="Momentary BTN 24"></button>
<label>BTN[24]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="25" title="Momentary BTN 25"></button>
<label>BTN[25]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="26" title="Momentary BTN 26"></button>
<label>BTN[26]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="27" title="Momentary BTN 27"></button>
<label>BTN[27]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="28" title="Momentary BTN 28"></button>
<label>BTN[28]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="29" title="Momentary BTN 29"></button>
<label>BTN[29]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="30" title="Momentary BTN 30"></button>
<label>BTN[30]</label>
</div>
<div class="ioCell">
<button class="momentary" type="button" data-bit="31" title="Momentary BTN 31"></button>
<label>BTN[31]</label>
</div>
</div>
<div class="subBlockTitle">4x4 Keypad Matrix via BTN (8-15)</div>
<div id="keypadGrid" class="keypadGrid">
<button class="momentary keypadBtn" type="button" data-row-bit="8" data-col-bit="12" data-key="7">7</button>
<button class="momentary keypadBtn" type="button" data-row-bit="8" data-col-bit="13" data-key="8">8</button>
<button class="momentary keypadBtn" type="button" data-row-bit="8" data-col-bit="14" data-key="9">9</button>
<button class="momentary keypadBtn op" type="button" data-row-bit="8" data-col-bit="15" data-key="/">/</button>
<button class="momentary keypadBtn" type="button" data-row-bit="9" data-col-bit="12" data-key="4">4</button>
<button class="momentary keypadBtn" type="button" data-row-bit="9" data-col-bit="13" data-key="5">5</button>
<button class="momentary keypadBtn" type="button" data-row-bit="9" data-col-bit="14" data-key="6">6</button>
<button class="momentary keypadBtn op" type="button" data-row-bit="9" data-col-bit="15" data-key="*">*</button>
<button class="momentary keypadBtn" type="button" data-row-bit="10" data-col-bit="12" data-key="1">1</button>
<button class="momentary keypadBtn" type="button" data-row-bit="10" data-col-bit="13" data-key="2">2</button>
<button class="momentary keypadBtn" type="button" data-row-bit="10" data-col-bit="14" data-key="3">3</button>
<button class="momentary keypadBtn op" type="button" data-row-bit="10" data-col-bit="15" data-key="-">-</button>
<button class="momentary keypadBtn" type="button" data-row-bit="11" data-col-bit="12" data-key="0">0</button>
<button class="momentary keypadBtn" type="button" data-row-bit="11" data-col-bit="13" data-key=".">.</button>
<button class="momentary keypadBtn op" type="button" data-row-bit="11" data-col-bit="14" data-key="=">=</button>
<button class="momentary keypadBtn op" type="button" data-row-bit="11" data-col-bit="15" data-key="+">+</button>
</div>
</div>
</div>
</section>
<!-- Right: IO -->
<section class="card io">
<div class="cardHeader">
<div class="cardTitle">Outputs</div>
@ -58,55 +315,480 @@
<div class="outputs">
<div class="block">
<div class="blockTitle">LEDs (32)</div>
<div id="ledRow" class="ledRow"></div>
<div class="bitLabelRow" id="ledLabels"></div>
<div id="ledRow" class="ledRow">
<div class="ledGroup">
<div class="led" data-bit="0" title="LED[0]"></div>
<div class="led" data-bit="1" title="LED[1]"></div>
<div class="led" data-bit="2" title="LED[2]"></div>
<div class="led" data-bit="3" title="LED[3]"></div>
<div class="led" data-bit="4" title="LED[4]"></div>
<div class="led" data-bit="5" title="LED[5]"></div>
<div class="led" data-bit="6" title="LED[6]"></div>
<div class="led" data-bit="7" title="LED[7]"></div>
</div>
<div class="ledGroup">
<div class="led" data-bit="8" title="LED[8]"></div>
<div class="led" data-bit="9" title="LED[9]"></div>
<div class="led" data-bit="10" title="LED[10]"></div>
<div class="led" data-bit="11" title="LED[11]"></div>
<div class="led" data-bit="12" title="LED[12]"></div>
<div class="led" data-bit="13" title="LED[13]"></div>
<div class="led" data-bit="14" title="LED[14]"></div>
<div class="led" data-bit="15" title="LED[15]"></div>
</div>
<div class="ledGroup">
<div class="led" data-bit="16" title="LED[16]"></div>
<div class="led" data-bit="17" title="LED[17]"></div>
<div class="led" data-bit="18" title="LED[18]"></div>
<div class="led" data-bit="19" title="LED[19]"></div>
<div class="led" data-bit="20" title="LED[20]"></div>
<div class="led" data-bit="21" title="LED[21]"></div>
<div class="led" data-bit="22" title="LED[22]"></div>
<div class="led" data-bit="23" title="LED[23]"></div>
</div>
<div class="ledGroup">
<div class="led" data-bit="24" title="LED[24]"></div>
<div class="led" data-bit="25" title="LED[25]"></div>
<div class="led" data-bit="26" title="LED[26]"></div>
<div class="led" data-bit="27" title="LED[27]"></div>
<div class="led" data-bit="28" title="LED[28]"></div>
<div class="led" data-bit="29" title="LED[29]"></div>
<div class="led" data-bit="30" title="LED[30]"></div>
<div class="led" data-bit="31" title="LED[31]"></div>
</div>
</div>
</div>
<div class="block">
<div class="blockTitle">HEX (4 digits, 7-seg + dp)</div>
<div id="hexRow" class="hexRow"></div>
<div class="hint small">
Assumes each digit is 8 bits: <code>bit0=a</code>, <code>1=b</code>, <code>2=c</code>, <code>3=d</code>, <code>4=e</code>, <code>5=f</code>, <code>6=g</code>, <code>7=dp</code>.
<div id="hexRow" class="hexRow">
<div class="hexGroup">
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="0">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[0]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="1">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[1]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="2">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[2]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="3">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[3]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="4">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[4]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="5">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[5]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="6">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[6]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="7">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[7]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="8">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[8]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="9">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[9]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="10">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[10]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="11">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[11]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="12">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[12]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="13">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[13]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="14">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[14]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="15">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[15]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="16">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[16]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="17">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[17]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="18">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[18]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="19">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[19]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="20">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[20]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="21">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[21]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="22">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[22]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="23">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[23]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="24">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[24]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="25">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[25]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="26">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[26]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="27">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[27]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="28">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[28]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="29">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[29]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="30">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[30]</div>
</div>
<div class="hexDigitWrap">
<div class="sevenSeg" data-digit="31">
<div class="seg a" data-seg="a"></div>
<div class="seg b" data-seg="b"></div>
<div class="seg c" data-seg="c"></div>
<div class="seg d" data-seg="d"></div>
<div class="seg e" data-seg="e"></div>
<div class="seg f" data-seg="f"></div>
<div class="seg g" data-seg="g"></div>
<div class="seg dp" data-seg="dp"></div>
</div>
<div class="digitLabel">SEG[31]</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Bottom-left: Inputs -->
<section class="card inputs">
<div class="cardHeader">
<div class="cardTitle">Inputs</div>
<div class="cardActions">
<button id="allSwOffBtn" class="secondary">All switches off</button>
<button id="allSwOnBtn" class="secondary">All switches on</button>
</div>
</div>
<div class="twoCols">
<div class="block">
<div class="blockTitle">Switches (32, latched)</div>
<div id="switchGrid" class="ioGrid"></div>
</div>
<div class="block">
<div class="blockTitle">Buttons (32, momentary)</div>
<div id="buttonGrid" class="ioGrid"></div>
</div>
</div>
<div class="hint">
Switches toggle a bit. Buttons set the bit while pressed (mouse/touch) and clear on release.
</div>
</section>
<!-- Bottom-right: Logs -->
<section class="card logs">
<div class="cardHeader">
<div class="cardTitle">Logs</div>
<div class="cardActions">
<button id="clearLogBtn" class="secondary">Clear</button>
<button id="autoscrollBtn" class="secondary">Autoscroll: on</button>
</div>
</div>
@ -114,6 +796,13 @@
</section>
</main>
<script>
// Optional runtime config hook:
// window.VHDL_UI_CONFIG = {
// mode: "local" | "remote",
// externalFiles: { "circuit.vhdl": "..." }
// };
</script>
<script src="app.js"></script>
</body>
</html>
</html>

View file

@ -1,396 +1,583 @@
:root{
--bg: #0b0f17;
--card: #121a2a;
--card2:#0f1726;
--text: #e6eefc;
--muted:#9fb0d0;
--accent:#58a6ff;
--ok:#22c55e;
--bad:#ef4444;
--warn:#f59e0b;
--line:#1f2a44;
--shadow: 0 12px 30px rgba(0,0,0,.35);
:root {
--bg: #1e1e1e;
--surface: #252526;
--surface-muted: #2a2a2b;
--surface-header: #2d2d30;
--surface-soft: #1f1f20;
--surface-soft-2: #2a2a2b;
--text: #d4d4d4;
--text-muted: #a9adb3;
--accent: #0e639c;
--accent-soft: #1177bb;
--accent-muted: #264f78;
--ok: #22c55e;
--bad: #ef4444;
--warn: #f59e0b;
--border-strong: #4d4d4d;
--border: #3c3c3c;
--border-soft: #343436;
--button-primary-bg: #0e639c;
--button-primary-bg-hover: #1177bb;
--button-secondary-bg: #33363a;
--button-secondary-bg-hover: #3f4349;
--button-border: #2f5f8f;
--pill-bg: #323234;
--pill-border: #5e6166;
--pill-connected-bg: #1f2c23;
--pill-connected-border: #407a4d;
--pill-disconnected-bg: #342325;
--pill-disconnected-border: #8d4f56;
--led-off-bg: #303033;
--led-off-border: #5b5d62;
--led-on-bg: #2a9d58;
--led-on-border: #5ccd88;
--led-group-bg: #242427;
--led-group-border: #4f5258;
--seg-off-bg: #3a3b3f;
--seg-on-bg: #f59e0b;
--toggle-off-bg: #3a3d41;
--toggle-off-knob: #c4c7cc;
--toggle-on-bg: #2f7d4d;
--toggle-on-knob: #7be8a5;
--momentary-bg: #373a3e;
--momentary-border: #60646b;
--momentary-down-bg: #365a7f;
--momentary-down-border: #5f8fc0;
--keypad-op-bg: #44484f;
--transparent: transparent;
--card-shadow: 0 10px 18px rgba(0, 0, 0, 0.35);
--radius: 14px;
--radius-sm: 10px;
--mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
--sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji","Segoe UI Emoji";
--sans: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, "Apple Color Emoji", "Segoe UI Emoji";
}
*{ box-sizing:border-box; }
html,body{ height:100%; }
body{
margin:0;
background: radial-gradient(1000px 600px at 20% -10%, rgba(88,166,255,.20), transparent 60%),
radial-gradient(900px 500px at 90% 0%, rgba(34,197,94,.12), transparent 60%),
var(--bg);
color:var(--text);
* {
box-sizing: border-box;
}
html,
body {
height: 100%;
}
body {
margin: 0;
background: var(--bg);
color: var(--text);
font-family: var(--sans);
}
.topbar{
position: sticky;
top:0;
z-index: 10;
display:flex;
justify-content:space-between;
align-items:center;
padding:14px 16px;
border-bottom:1px solid rgba(255,255,255,.06);
background: rgba(11,15,23,.8);
backdrop-filter: blur(10px);
button {
padding: 8px 12px;
border: 1px solid var(--button-border);
border-radius: var(--radius-sm);
background: var(--button-primary-bg);
color: var(--text);
cursor: pointer;
transition: background 0.15s ease, transform 0.05s ease;
}
.brand{
display:flex;
gap:12px;
align-items:center;
}
.brand .dot{
width:12px;height:12px;border-radius:50%;
background: var(--accent);
box-shadow: 0 0 18px rgba(88,166,255,.7);
}
.title{ font-weight:700; letter-spacing:.2px; }
.subtitle{ font-size:12px; color:var(--muted); margin-top:2px; }
.controls{
display:flex;
gap:10px;
align-items:center;
}
.status{
display:flex;
gap:8px;
align-items:center;
margin-right:6px;
}
.pill{
font-size:12px;
padding:6px 10px;
border-radius:999px;
border:1px solid rgba(255,255,255,.12);
background: rgba(255,255,255,.04);
}
.muted{ color:var(--muted); font-size:12px; }
button{
border:1px solid rgba(255,255,255,.14);
background: rgba(88,166,255,.14);
color:var(--text);
padding:8px 12px;
border-radius:10px;
cursor:pointer;
transition: transform .05s ease, background .2s ease;
}
button:hover{ background: rgba(88,166,255,.20); }
button:active{ transform: translateY(1px); }
button.secondary{
background: rgba(255,255,255,.06);
}
button.secondary:hover{ background: rgba(255,255,255,.10); }
button:disabled{
opacity:.55;
cursor:not-allowed;
button:hover {
background: var(--button-primary-bg-hover);
}
.grid{
display:grid;
grid-template-columns: 1.2fr 1fr;
grid-template-rows: 420px 1fr;
gap:14px;
padding:14px;
button:active {
transform: translateY(1px);
}
.card{
background: linear-gradient(180deg, rgba(255,255,255,.05), rgba(255,255,255,.03));
border:1px solid rgba(255,255,255,.08);
border-radius: var(--radius);
box-shadow: var(--shadow);
overflow:hidden;
min-height: 0;
button.secondary {
background: var(--button-secondary-bg);
}
.cardHeader{
display:flex;
justify-content:space-between;
align-items:center;
padding:12px 12px;
border-bottom:1px solid rgba(255,255,255,.06);
background: rgba(0,0,0,.15);
button.secondary:hover {
background: var(--button-secondary-bg-hover);
}
.cardTitle{ font-weight:700; }
.cardActions{ display:flex; gap:8px; }
.editor{
display:flex;
flex-direction:column;
button:focus-visible {
outline: 1px solid var(--accent-soft);
outline-offset: 1px;
}
textarea{
width:100%;
height:100%;
padding:12px;
background: rgba(0,0,0,.22);
color:var(--text);
border:0;
outline:none;
resize:none;
button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
textarea {
width: 100%;
height: 100%;
padding: 12px;
border: 0;
outline: none;
resize: none;
color: var(--text);
background: var(--surface-soft);
font-family: var(--mono);
font-size: 13px;
line-height: 1.35;
}
.hint{
padding:10px 12px;
border-top:1px solid rgba(255,255,255,.06);
color:var(--muted);
font-size:12px;
background: rgba(0,0,0,.12);
}
.hint.small{ font-size:11px; }
.io .outputs{
padding:12px;
display:flex;
flex-direction:column;
gap:14px;
}
.blockTitle{
font-size:12px;
color:var(--muted);
margin-bottom:8px;
.muted {
font-size: 12px;
color: var(--text-muted);
}
.ledRow{
display:grid;
grid-template-columns: repeat(16, 1fr);
gap:6px;
.is-hidden {
display: none !important;
}
.led{
.grid {
display: flex;
flex-wrap: wrap;
align-items: stretch;
gap: 14px;
padding: 14px;
}
.card {
flex: 1 1 520px;
min-height: 0;
overflow: hidden;
border: 1px solid var(--border);
border-radius: var(--radius);
background: var(--surface);
box-shadow: var(--card-shadow);
}
.cardHeader {
display: flex;
justify-content: space-between;
align-items: center;
gap: 10px;
padding: 12px;
border-bottom: 1px solid var(--border-soft);
background: var(--surface-header);
}
.cardHeaderWrap {
flex-wrap: wrap;
}
.cardTitle {
font-weight: 700;
}
.cardActions {
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
}
.panelTopStatus {
display: flex;
align-items: center;
gap: 8px;
margin-left: auto;
flex-wrap: nowrap;
white-space: nowrap;
min-width: 0;
flex: 0 0 auto;
}
.pill {
display: inline-flex;
justify-content: center;
align-items: center;
padding: 6px 10px;
font-size: 12px;
border: 1px solid var(--pill-border);
border-radius: 999px;
background: var(--pill-bg);
min-width: 104px;
}
.pill.state-disabled {
border-color: var(--pill-disconnected-border);
background: var(--pill-disconnected-bg);
}
.pill.state-connected {
border-color: color-mix(in srgb, var(--accent-soft) 75%, white 25%);
background: color-mix(in srgb, var(--accent-muted) 72%, black 28%);
}
.pill.state-running {
border-color: var(--pill-connected-border);
background: var(--pill-connected-bg);
box-shadow: 0 0 0 1px color-mix(in srgb, var(--ok) 40%, transparent);
}
.modeToggle {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: 11px;
color: var(--text-muted);
user-select: none;
flex: 0 0 auto;
}
.modeToggle input {
position: absolute;
opacity: 0;
pointer-events: none;
}
.modeSlider {
width: 36px;
height: 18px;
border: 1px solid var(--border-strong);
border-radius: 999px;
background: var(--toggle-off-bg);
position: relative;
}
.modeSlider::after {
content: "";
position: absolute;
top: 1px;
left: 1px;
width: 14px;
height: 14px;
border-radius: 999px;
background: var(--toggle-off-knob);
transition: transform 0.12s ease, background 0.12s ease;
}
.modeToggle input:checked + .modeSlider {
background: var(--accent-muted);
}
.modeToggle input:checked + .modeSlider::after {
transform: translateX(17px);
background: var(--accent-soft);
}
.blockTitle {
margin-bottom: 8px;
font-size: 12px;
color: var(--text-muted);
}
.subBlockTitle {
margin: 10px 0 8px;
font-size: 11px;
color: var(--text-muted);
}
.hint {
padding: 10px 12px;
border-top: 1px solid var(--border-soft);
color: var(--text-muted);
font-size: 12px;
background: var(--surface-soft-2);
}
.editor {
display: flex;
flex-direction: column;
min-height: 420px;
flex: 1.4 1 620px;
}
.editorWrap {
display: flex;
width: 100%;
aspect-ratio: 1/1;
border-radius: 999px;
border:1px solid rgba(255,255,255,.14);
background: rgba(255,255,255,.08);
box-shadow: inset 0 0 0 2px rgba(0,0,0,.18);
height: 100%;
min-height: 0;
background: var(--surface-soft);
}
.led.on{
background: rgba(34,197,94,.70);
border-color: rgba(34,197,94,.9);
box-shadow: 0 0 14px rgba(34,197,94,.45);
}
.bitLabelRow{
display:grid;
grid-template-columns: repeat(16, 1fr);
gap:6px;
margin-top:6px;
color: var(--muted);
font-size: 10px;
.lineGutter {
width: 54px;
margin: 0;
padding: 12px 10px 12px 12px;
overflow: hidden;
user-select: none;
text-align: right;
border-right: 1px solid var(--border-soft);
color: var(--text-muted);
background: var(--surface-soft-2);
font-family: var(--mono);
opacity: .85;
}
.bitLabelRow span{
text-align:center;
font-size: 13px;
line-height: 1.35;
}
.hexRow{
display:flex;
gap:14px;
flex-wrap:wrap;
align-items:center;
textarea#vhdlEditor {
flex: 1;
overflow: auto;
overscroll-behavior-y: none;
background: var(--transparent);
}
/* 7-seg display */
.sevenSeg{
width: 90px;
height: 140px;
position: relative;
background: rgba(0,0,0,.18);
border:1px solid rgba(255,255,255,.10);
border-radius: 14px;
padding:10px;
.io .outputs {
display: flex;
flex-direction: column;
gap: 14px;
padding: 12px;
}
.seg{
position:absolute;
background: rgba(255,255,255,.10);
.ledRow {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
gap: 10px;
}
.ledGroup {
display: grid;
grid-template-columns: repeat(8, minmax(0, 1fr));
gap: 6px;
padding: 8px;
border: 1px solid var(--led-group-border);
border-radius: 8px;
filter: drop-shadow(0 0 0 rgba(0,0,0,0));
}
.seg.on{
background: rgba(88,166,255,.75);
filter: drop-shadow(0 0 8px rgba(88,166,255,.35));
background: var(--led-group-bg);
}
/* segment positions */
.seg.a{ top:10px; left:18px; width:54px; height:12px; }
.seg.d{ bottom:10px; left:18px; width:54px; height:12px; }
.seg.g{ top:64px; left:18px; width:54px; height:12px; }
.seg.f{ top:18px; left:10px; width:12px; height:54px; }
.seg.b{ top:18px; right:10px; width:12px; height:54px; }
.seg.e{ bottom:18px; left:10px; width:12px; height:54px; }
.seg.c{ bottom:18px; right:10px; width:12px; height:54px; }
.seg.dp{
width:12px; height:12px;
bottom:10px; right:10px;
border-radius: 999px;
}
.digitLabel{
margin-top:8px;
font-family: var(--mono);
font-size: 11px;
color: var(--muted);
text-align:center;
}
.inputs{
grid-column: 1 / 2;
display:flex;
flex-direction:column;
min-height: 0;
}
.twoCols{
padding:12px;
display:grid;
grid-template-columns: 1fr 1fr;
gap:12px;
min-height: 0;
}
.ioGrid{
display:grid;
grid-template-columns: repeat(8, 1fr);
gap:8px;
}
.ioCell{
display:flex;
flex-direction:column;
gap:6px;
align-items:center;
padding:8px 6px;
background: rgba(0,0,0,.16);
border:1px solid rgba(255,255,255,.08);
border-radius:12px;
}
.ioCell label{
font-family: var(--mono);
font-size: 11px;
color: var(--muted);
}
.toggle{
width: 38px;
height: 22px;
border-radius: 999px;
.led {
position: relative;
background: rgba(255,255,255,.10);
border:1px solid rgba(255,255,255,.14);
cursor:pointer;
}
.toggle::after{
content:"";
width: 16px;
height: 16px;
position:absolute;
top:2px;
left:2px;
display: grid;
place-items: center;
width: 100%;
border: 1px solid var(--led-off-border);
border-radius: 999px;
background: rgba(255,255,255,.45);
transition: left .12s ease, background .12s ease;
}
.toggle.on{
background: rgba(34,197,94,.24);
border-color: rgba(34,197,94,.55);
}
.toggle.on::after{
left: 20px;
background: rgba(34,197,94,.75);
background: var(--led-off-bg);
aspect-ratio: 1 / 1;
}
.momentary{
width: 40px;
height: 28px;
.led::after {
content: attr(data-bit);
color: var(--text-muted);
font-family: var(--mono);
font-size: 8px;
line-height: 1;
}
.led.on {
border-color: var(--led-on-border);
background: var(--led-on-bg);
box-shadow: 0 0 6px color-mix(in srgb, var(--led-on-bg) 50%, transparent);
}
.hexRow {
display: grid;
grid-template-columns: 1fr;
gap: 6px;
overflow-x: hidden;
}
.hexGroup {
display: grid;
grid-template-columns: repeat(8, minmax(0, 1fr));
gap: 4px;
width: 100%;
padding: 6px;
border: 1px solid var(--border-soft);
border-radius: 10px;
border:1px solid rgba(255,255,255,.14);
background: rgba(255,255,255,.08);
cursor:pointer;
}
.momentary.down{
background: rgba(245,158,11,.25);
border-color: rgba(245,158,11,.55);
box-shadow: 0 0 12px rgba(245,158,11,.18);
background: var(--surface-muted);
}
.logs{
grid-column: 2 / 3;
display:flex;
flex-direction:column;
min-height: 0;
.hexDigitWrap {
display: flex;
flex-direction: column;
align-items: center;
min-width: 0;
}
.logView{
margin:0;
padding:12px;
height:100%;
overflow:auto;
background: rgba(0,0,0,.22);
.sevenSeg {
position: relative;
width: 100%;
max-width: 88px;
aspect-ratio: 88 / 136;
padding: 8%;
border: 1px solid var(--border-strong);
border-radius: 12px;
background: var(--surface-soft);
}
.seg {
position: absolute;
border-radius: 6px;
background: var(--seg-off-bg);
}
.seg.on {
background: var(--seg-on-bg);
box-shadow: 0 0 4px color-mix(in srgb, var(--seg-on-bg) 45%, transparent);
}
.seg.a { top: 8%; left: 21%; width: 58%; height: 7%; }
.seg.b { top: 14%; right: 11%; width: 10%; height: 34%; }
.seg.c { bottom: 14%; right: 11%; width: 10%; height: 34%; }
.seg.d { bottom: 8%; left: 21%; width: 58%; height: 7%; }
.seg.e { bottom: 14%; left: 11%; width: 10%; height: 34%; }
.seg.f { top: 14%; left: 11%; width: 10%; height: 34%; }
.seg.g { top: 46.5%; left: 21%; width: 58%; height: 7%; }
.seg.dp {
right: 5%;
bottom: 5%;
width: 9%;
height: 9%;
border-radius: 999px;
}
.digitLabel {
margin-top: 4px;
color: var(--text-muted);
font-family: var(--mono);
font-size: 10px;
}
.inputs {
display: flex;
flex-direction: column;
min-height: 0;
flex: 1.2 1 560px;
}
.twoCols {
display: grid;
gap: 12px;
min-height: 0;
padding: 12px;
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
.ioGrid {
display: grid;
gap: 6px;
grid-template-columns: repeat(auto-fit, minmax(64px, 1fr));
}
.ioCell {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
min-width: 64px;
min-height: 52px;
padding: 6px 4px;
border: 1px solid var(--border-soft);
border-radius: 12px;
background: var(--surface-soft);
}
.ioCell label {
color: var(--text-muted);
font-family: var(--mono);
font-size: 11px;
}
.toggle {
position: relative;
width: 34px;
height: 20px;
border: 1px solid var(--border-strong);
border-radius: 999px;
background: var(--toggle-off-bg);
cursor: pointer;
}
.toggle::after {
content: "";
position: absolute;
top: 2px;
left: 2px;
width: 14px;
height: 14px;
border-radius: 999px;
background: var(--toggle-off-knob);
transition: left 0.12s ease, background 0.12s ease;
}
.toggle.on {
background: var(--toggle-on-bg);
box-shadow: 0 0 0 1px color-mix(in srgb, var(--toggle-on-knob) 35%, transparent);
}
.toggle.on::after {
left: 16px;
background: var(--toggle-on-knob);
}
.momentary {
width: 34px;
height: 24px;
border: 1px solid var(--momentary-border);
border-radius: var(--radius-sm);
background: var(--momentary-bg);
}
.momentary.down {
border-color: var(--momentary-down-border);
background: var(--momentary-down-bg);
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-soft) 35%, transparent);
}
.keypadGrid {
display: grid;
gap: 6px;
grid-template-columns: repeat(4, minmax(50px, 1fr));
}
.keypadBtn {
width: 100%;
min-height: 32px;
font-family: var(--mono);
font-size: 12px;
}
.keypadBtn.op {
background: var(--keypad-op-bg);
}
.keypadBtn.op:hover {
background: var(--accent-muted);
}
.logs {
display: flex;
flex-direction: column;
min-height: 0;
flex: 1 1 560px;
}
.logView {
height: 100%;
margin: 0;
padding: 12px;
overflow: auto;
white-space: pre-wrap;
word-break: break-word;
background: var(--surface-soft);
font-family: var(--mono);
font-size: 12px;
line-height: 1.35;
white-space: pre-wrap;
word-break: break-word;
}
/* Responsive */
@media (max-width: 1100px){
.grid{
grid-template-columns: 1fr;
grid-template-rows: auto;
@media (max-width: 1100px) {
.card {
flex-basis: 100%;
}
.inputs, .logs{
grid-column: auto;
}
.twoCols{
.twoCols {
grid-template-columns: 1fr;
}
}
/* Editor w/ line numbers */
.editorWrap{
display:flex;
width:100%;
height:100%;
min-height: 0;
background: rgba(0,0,0,.22);
}
.lineGutter{
margin:0;
padding:12px 10px 12px 12px;
width: 54px; /* gutter width */
overflow:hidden;
user-select:none;
text-align:right;
color: rgba(159,176,208,.85);
font-family: var(--mono);
font-size: 13px;
line-height: 1.35;
border-right: 1px solid rgba(255,255,255,.06);
background: rgba(0,0,0,.10);
}
#lineGutter .ln { display:block; }
textarea#vhdlEditor{
overscroll-behavior-y: none;
flex:1;
height:100%;
padding:12px;
background: transparent; /* uses editorWrap background */
color:var(--text);
border:0;
outline:none;
resize:none;
font-family: var(--mono);
font-size: 13px;
line-height: 1.35;
overflow:auto;
.panelTopStatus {
margin-left: auto;
width: auto;
justify-content: flex-end;
}
}