Arrange main and subprogram panes side by side
This commit is contained in:
@@ -99,7 +99,7 @@ function ProgramPane({ text, setText, activeGcodes, executionTrace, programStatu
|
|||||||
const node = traceRef.current;
|
const node = traceRef.current;
|
||||||
if (node) node.scrollTop = node.scrollHeight;
|
if (node) node.scrollTop = node.scrollHeight;
|
||||||
}, [executionTrace]);
|
}, [executionTrace]);
|
||||||
return <section className="axis-program-pane" data-axis-feature="AXIS-UI-07" aria-label="G-code program"><div className="program-editor-wrap"><div className="program-gutter" aria-label="G-code line numbers">{lines.map((_, index) => { const line = index + 1; const className = line === mainLine ? "current-line" : executedLines.has(line) ? "completed-line" : ""; return <span key={index} className={className} aria-current={line === mainLine ? "true" : undefined}>{line}</span>; })}</div><div className="program-editor-input"><div ref={highlightsRef} className="program-line-highlights" aria-hidden="true">{lines.map((_, index) => { const line = index + 1; const className = line === mainLine ? "current-line" : executedLines.has(line) ? "completed-line" : ""; return <span key={index} className={className} />; })}</div><textarea ref={editorRef} id="axis-program" data-axis-feature="AXIS-EDIT-01" value={text} onChange={(event) => setText(event.target.value)} onScroll={() => { if (editorRef.current && highlightsRef.current) { highlightsRef.current.scrollTop = editorRef.current.scrollTop; highlightsRef.current.scrollLeft = editorRef.current.scrollLeft; } }} spellCheck={false} aria-invalid={Boolean(diagnostic)} aria-label="G-code program" /></div></div><div className="program-execution-line" role="status" aria-label="Currently executing G-code"><span>{executionLine ? `Executing source line ${executionLine}` : "Execution idle"}</span><code>{executionLineText || "-"}</code></div><div className="program-execution-trace" role="log" aria-label="G-code execution trace" aria-live="polite"><strong>Execution trace</strong><ol ref={traceRef}>{executionTrace.slice(-8).map((entry, index, entries) => { const isCurrent = executing && index === entries.length - 1; return <li key={`${entry.line}-${index}`} className={isCurrent ? "executing-line" : "completed-line"} aria-current={isCurrent ? "true" : undefined}><span>L{entry.line}</span><code>{entry.text || "-"}</code></li>; })}</ol></div><aside className="program-side"><div className="program-actions"><TextButton feature="AXIS-EDIT-01" onClick={onOpenFile}>Open file</TextButton><TextButton feature="AXIS-EDIT-01" onClick={onLoad} disabled={!canLoad}>Load</TextButton><TextButton feature="AXIS-CTRL-02" onClick={onRun} disabled={!canRun}>Run</TextButton><TextButton feature="AXIS-CTRL-02" onClick={onStep} disabled={!canStep}>Step</TextButton></div><label className="active-gcodes-label">Active G-Codes:<textarea data-axis-feature="AXIS-UI-07" readOnly value={activeGcodes} aria-label="Active G-Codes" /></label>{diagnostic && <output className="program-diagnostic" role="alert" data-axis-feature="AXIS-EDIT-01">{diagnostic}</output>}</aside></section>;
|
return <section className="axis-program-pane" data-axis-feature="AXIS-UI-07" aria-label="G-code program"><div className="program-main-layout"><div className="program-editor-wrap"><div className="program-gutter" aria-label="G-code line numbers">{lines.map((_, index) => { const line = index + 1; const className = line === mainLine ? "current-line" : executedLines.has(line) ? "completed-line" : ""; return <span key={index} className={className} aria-current={line === mainLine ? "true" : undefined}>{line}</span>; })}</div><div className="program-editor-input"><div ref={highlightsRef} className="program-line-highlights" aria-hidden="true">{lines.map((_, index) => { const line = index + 1; const className = line === mainLine ? "current-line" : executedLines.has(line) ? "completed-line" : ""; return <span key={index} className={className} />; })}</div><textarea ref={editorRef} id="axis-program" data-axis-feature="AXIS-EDIT-01" value={text} onChange={(event) => setText(event.target.value)} onScroll={() => { if (editorRef.current && highlightsRef.current) { highlightsRef.current.scrollTop = editorRef.current.scrollTop; highlightsRef.current.scrollLeft = editorRef.current.scrollLeft; } }} spellCheck={false} aria-invalid={Boolean(diagnostic)} aria-label="G-code program" /></div></div><div className="program-execution-panel"><div className="program-execution-line" role="status" aria-label="Currently executing G-code"><span>{executionLine ? `Executing source line ${executionLine}` : "Execution idle"}</span><code>{executionLineText || "-"}</code></div><div className="program-execution-trace" role="log" aria-label="G-code execution trace" aria-live="polite"><strong>Subprogram / execution trace</strong><ol ref={traceRef}>{executionTrace.slice(-16).map((entry, index, entries) => { const isCurrent = executing && index === entries.length - 1; return <li key={`${entry.line}-${index}`} className={isCurrent ? "executing-line" : "completed-line"} aria-current={isCurrent ? "true" : undefined}><span>L{entry.line}</span><code>{entry.text || "-"}</code></li>; })}</ol></div></div></div><aside className="program-side"><div className="program-actions"><TextButton feature="AXIS-EDIT-01" onClick={onOpenFile}>Open file</TextButton><TextButton feature="AXIS-EDIT-01" onClick={onLoad} disabled={!canLoad}>Load</TextButton><TextButton feature="AXIS-CTRL-02" onClick={onRun} disabled={!canRun}>Run</TextButton><TextButton feature="AXIS-CTRL-02" onClick={onStep} disabled={!canStep}>Step</TextButton></div><label className="active-gcodes-label">Active G-Codes:<textarea data-axis-feature="AXIS-UI-07" readOnly value={activeGcodes} aria-label="Active G-Codes" /></label>{diagnostic && <output className="program-diagnostic" role="alert" data-axis-feature="AXIS-EDIT-01">{diagnostic}</output>}</aside></section>;
|
||||||
}
|
}
|
||||||
|
|
||||||
function DiagnosticsPanel({ state, gates }: { state: typeof INITIAL_STATE; gates: Record<string, { enabled: boolean; reason: string }> }) {
|
function DiagnosticsPanel({ state, gates }: { state: typeof INITIAL_STATE; gates: Record<string, { enabled: boolean; reason: string }> }) {
|
||||||
|
|||||||
@@ -170,7 +170,7 @@ output { font: 12px ui-monospace, monospace; text-align: right; }
|
|||||||
.axis-slider { display: grid; grid-template-columns: repeat(3, minmax(170px, 1fr)); gap: 12px; }
|
.axis-slider { display: grid; grid-template-columns: repeat(3, minmax(170px, 1fr)); gap: 12px; }
|
||||||
.axis-slider label { grid-template-columns: auto 48px; }
|
.axis-slider label { grid-template-columns: auto 48px; }
|
||||||
|
|
||||||
.axis-program-pane { display: grid; grid-template-columns: minmax(0, 1fr) 230px; grid-template-rows: minmax(170px, 1fr) auto minmax(170px, auto); flex: 0 0 430px; height: 430px; min-height: 430px; margin: 0 5px 5px; border: 2px solid #777; background: #f7f7f7; }
|
.axis-program-pane { display: grid; grid-template-columns: minmax(0, 1fr) 230px; grid-template-rows: minmax(0, 1fr); flex: 0 0 430px; height: 430px; min-height: 430px; margin: 0 5px 5px; border: 2px solid #777; background: #f7f7f7; }
|
||||||
.axis-diagnostics { display: grid; gap: 8px; padding: 10px; min-height: 190px; overflow: auto; background: #f7f7f7; font-size: 12px; }
|
.axis-diagnostics { display: grid; gap: 8px; padding: 10px; min-height: 190px; overflow: auto; background: #f7f7f7; font-size: 12px; }
|
||||||
.diagnostic-summary, .diagnostic-error, .diagnostic-row { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
|
.diagnostic-summary, .diagnostic-error, .diagnostic-row { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
|
||||||
.diagnostic-summary { padding-bottom: 6px; border-bottom: 1px solid #b3b3b3; }
|
.diagnostic-summary { padding-bottom: 6px; border-bottom: 1px solid #b3b3b3; }
|
||||||
@@ -178,6 +178,7 @@ output { font: 12px ui-monospace, monospace; text-align: right; }
|
|||||||
.diagnostic-section { display: grid; gap: 3px; }
|
.diagnostic-section { display: grid; gap: 3px; }
|
||||||
.diagnostic-section h3 { margin: 0; font-size: 12px; }
|
.diagnostic-section h3 { margin: 0; font-size: 12px; }
|
||||||
.diagnostic-row code { min-width: 160px; color: #26384a; }
|
.diagnostic-row code { min-width: 160px; color: #26384a; }
|
||||||
|
.program-main-layout { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); grid-template-rows: minmax(0, 1fr); grid-column: 1; grid-row: 1; min-width: 0; min-height: 0; }
|
||||||
.program-editor-wrap { display: grid; grid-template-columns: 34px minmax(0, 1fr); min-width: 0; min-height: 0; }
|
.program-editor-wrap { display: grid; grid-template-columns: 34px minmax(0, 1fr); min-width: 0; min-height: 0; }
|
||||||
.program-gutter { display: flex; flex-direction: column; align-items: end; padding: 6px 6px 6px 0; overflow: hidden; border-right: 1px solid #c2c2c2; color: #888; background: #e4e4e4; font: 12px/1.45 ui-monospace, monospace; user-select: none; }
|
.program-gutter { display: flex; flex-direction: column; align-items: end; padding: 6px 6px 6px 0; overflow: hidden; border-right: 1px solid #c2c2c2; color: #888; background: #e4e4e4; font: 12px/1.45 ui-monospace, monospace; user-select: none; }
|
||||||
.program-gutter span { height: 17px; }
|
.program-gutter span { height: 17px; }
|
||||||
@@ -189,19 +190,20 @@ output { font: 12px ui-monospace, monospace; text-align: right; }
|
|||||||
.program-line-highlights span.completed-line { background: #eeeeee; }
|
.program-line-highlights span.completed-line { background: #eeeeee; }
|
||||||
.program-line-highlights span.current-line { background: #ffe066; }
|
.program-line-highlights span.current-line { background: #ffe066; }
|
||||||
.program-editor-input textarea { position: relative; z-index: 1; width: 100%; height: 100%; min-height: 180px; padding: 6px 8px; resize: none; border: 0; outline: none; background: transparent; color: #111; font: 12px/1.45 ui-monospace, monospace; tab-size: 4; }
|
.program-editor-input textarea { position: relative; z-index: 1; width: 100%; height: 100%; min-height: 180px; padding: 6px 8px; resize: none; border: 0; outline: none; background: transparent; color: #111; font: 12px/1.45 ui-monospace, monospace; tab-size: 4; }
|
||||||
.program-execution-line { display: grid; grid-column: 1; grid-row: 2; grid-template-columns: max-content minmax(0, 1fr); align-items: baseline; gap: 10px; min-width: 0; padding: 5px 8px; border-top: 1px solid #9aa8b4; border-bottom: 1px solid #9aa8b4; background: #ffe066; color: #3f2d00; font: 12px/1.35 ui-monospace, monospace; }
|
.program-execution-panel { display: grid; grid-template-rows: auto minmax(0, 1fr); grid-column: 2; grid-row: 1; min-width: 0; min-height: 0; border-left: 1px solid #9aa8b4; background: #f4f7fa; }
|
||||||
|
.program-execution-line { display: grid; grid-column: 1; grid-row: 1; grid-template-columns: max-content minmax(0, 1fr); align-items: baseline; gap: 10px; min-width: 0; padding: 5px 8px; border-top: 1px solid #9aa8b4; border-bottom: 1px solid #9aa8b4; background: #ffe066; color: #3f2d00; font: 12px/1.35 ui-monospace, monospace; }
|
||||||
.program-execution-line span { font-weight: 700; white-space: nowrap; }
|
.program-execution-line span { font-weight: 700; white-space: nowrap; }
|
||||||
.program-execution-line code { min-width: 0; overflow: hidden; color: #111; text-overflow: ellipsis; white-space: nowrap; }
|
.program-execution-line code { min-width: 0; overflow: hidden; color: #111; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
.program-execution-trace { grid-column: 1; grid-row: 3; min-width: 0; min-height: 170px; padding: 7px 8px; overflow: auto; border-bottom: 1px solid #9aa8b4; background: #f4f7fa; color: #253746; font: 11px/1.35 ui-monospace, monospace; }
|
.program-execution-trace { display: grid; grid-template-rows: auto minmax(0, 1fr); grid-column: 1; grid-row: 2; min-width: 0; min-height: 0; padding: 7px 8px; overflow: hidden; border-bottom: 1px solid #9aa8b4; background: #f4f7fa; color: #253746; font: 11px/1.35 ui-monospace, monospace; }
|
||||||
.program-execution-trace strong { display: block; margin-bottom: 2px; font-size: 11px; }
|
.program-execution-trace strong { display: block; margin-bottom: 2px; font-size: 11px; }
|
||||||
.program-execution-trace ol { display: grid; gap: 2px; max-height: 270px; margin: 0; padding: 0; overflow: auto; list-style: none; }
|
.program-execution-trace ol { display: grid; align-content: start; gap: 2px; max-height: none; margin: 0; padding: 0; overflow: auto; list-style: none; }
|
||||||
.program-execution-trace li { display: grid; grid-template-columns: 42px minmax(0, 1fr); gap: 8px; min-width: 0; }
|
.program-execution-trace li { display: grid; grid-template-columns: 42px minmax(0, 1fr); gap: 8px; min-width: 0; }
|
||||||
.program-execution-trace li span { color: #174d84; font-weight: 700; }
|
.program-execution-trace li span { color: #174d84; font-weight: 700; }
|
||||||
.program-execution-trace li code { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
.program-execution-trace li code { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||||
.program-execution-trace li.completed-line { padding: 3px 6px; border-left: 3px solid #b8b8b8; background: #e8e8e8; color: #666; }
|
.program-execution-trace li.completed-line { padding: 3px 6px; border-left: 3px solid #b8b8b8; background: #e8e8e8; color: #666; }
|
||||||
.program-execution-trace li.executing-line { padding: 3px 6px; border-left: 3px solid #d09b00; background: #ffe066; color: #111; font-weight: 700; }
|
.program-execution-trace li.executing-line { padding: 3px 6px; border-left: 3px solid #d09b00; background: #ffe066; color: #111; font-weight: 700; }
|
||||||
.program-execution-trace li.executing-line span { color: #6b4b00; }
|
.program-execution-trace li.executing-line span { color: #6b4b00; }
|
||||||
.program-side { display: flex; grid-column: 2; grid-row: 1 / span 3; flex-direction: column; gap: 12px; padding: 8px; border-left: 1px solid #aaa; background: #d9d9d9; }
|
.program-side { display: flex; grid-column: 2; grid-row: 1; flex-direction: column; gap: 12px; min-width: 0; min-height: 0; padding: 8px; border-left: 1px solid #aaa; background: #d9d9d9; }
|
||||||
.program-actions { display: flex; flex-wrap: wrap; gap: 5px; }
|
.program-actions { display: flex; flex-wrap: wrap; gap: 5px; }
|
||||||
.active-gcodes-label { display: grid; gap: 5px; color: #333; }
|
.active-gcodes-label { display: grid; gap: 5px; color: #333; }
|
||||||
.active-gcodes-label textarea { width: 100%; min-height: 84px; resize: vertical; border: 1px solid #999; background: white; color: #222; font: 11px/1.35 ui-monospace, monospace; }
|
.active-gcodes-label textarea { width: 100%; min-height: 84px; resize: vertical; border: 1px solid #999; background: white; color: #222; font: 11px/1.35 ui-monospace, monospace; }
|
||||||
@@ -223,10 +225,11 @@ output { font: 12px ui-monospace, monospace; text-align: right; }
|
|||||||
.axis-top-pane { grid-template-columns: 1fr; height: auto; min-height: 0; flex: 0 0 auto; }
|
.axis-top-pane { grid-template-columns: 1fr; height: auto; min-height: 0; flex: 0 0 auto; }
|
||||||
.axis-notebook { min-height: 330px; }
|
.axis-notebook { min-height: 330px; }
|
||||||
.axis-preview { min-height: 320px; }
|
.axis-preview { min-height: 320px; }
|
||||||
.axis-program-pane { flex: 0 0 auto; height: auto; min-height: 460px; grid-template-columns: 1fr; grid-template-rows: minmax(150px, 1fr) auto minmax(170px, auto) auto; }
|
.axis-program-pane { flex: 0 0 auto; height: auto; min-height: 700px; grid-template-columns: 1fr; grid-template-rows: minmax(520px, 1fr) auto; }
|
||||||
.program-execution-line { grid-column: 1; grid-row: 2; }
|
.program-main-layout { grid-template-columns: 1fr; grid-template-rows: minmax(220px, 1fr) minmax(260px, 1fr); grid-column: 1; grid-row: 1; }
|
||||||
.program-execution-trace { grid-column: 1; grid-row: 3; }
|
.program-editor-wrap { grid-column: 1; grid-row: 1; }
|
||||||
.program-side { grid-column: 1; grid-row: 4; border-top: 1px solid #aaa; border-left: 0; }
|
.program-execution-panel { grid-column: 1; grid-row: 2; border-top: 1px solid #9aa8b4; border-left: 0; }
|
||||||
|
.program-side { grid-column: 1; grid-row: 2; border-top: 1px solid #aaa; border-left: 0; }
|
||||||
.active-gcodes-label textarea { min-height: 58px; }
|
.active-gcodes-label textarea { min-height: 58px; }
|
||||||
}
|
}
|
||||||
@media (max-width: 620px) {
|
@media (max-width: 620px) {
|
||||||
|
|||||||
Reference in New Issue
Block a user