新增真实G代码编辑区
结论:AXIS 风格浏览器仿真页面新增可编辑 G-code pane、Run Editor Text、get/setProgramText 和 runEditorProgramText,编辑后的真实 G-code 继续通过 LinuxCNC WASM 执行并纳入 browser gate。
This commit is contained in:
@@ -441,6 +441,45 @@
|
||||
font-size: 12px;
|
||||
font-weight: 650;
|
||||
}
|
||||
.program-editor-wrap {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(240px, 0.85fr) minmax(280px, 1fr);
|
||||
min-height: 0;
|
||||
}
|
||||
.program-editor-panel {
|
||||
min-height: 0;
|
||||
border-right: 1px solid var(--line);
|
||||
display: grid;
|
||||
grid-template-rows: auto minmax(0, 1fr);
|
||||
background: #ededed;
|
||||
}
|
||||
.program-editor-toolbar {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid var(--line);
|
||||
padding: 4px 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.program-editor-toolbar div {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.program-editor {
|
||||
width: 100%;
|
||||
min-height: 0;
|
||||
border: 0;
|
||||
resize: none;
|
||||
padding: 8px 10px;
|
||||
background: #fbfbfb;
|
||||
color: var(--text);
|
||||
font: 13px/1.45 "SFMono-Regular", "Consolas", monospace;
|
||||
tab-size: 2;
|
||||
outline: none;
|
||||
}
|
||||
.axis-statusbar {
|
||||
display: grid;
|
||||
grid-template-columns: 120px minmax(120px, 1fr) minmax(180px, auto);
|
||||
@@ -485,6 +524,8 @@
|
||||
@media (max-width: 1040px) {
|
||||
main { grid-template-rows: auto auto auto minmax(0, 1fr) 220px auto; }
|
||||
.axis-workspace { grid-template-columns: 1fr; }
|
||||
.program-editor-wrap { grid-template-columns: 1fr; }
|
||||
.program-editor-panel { border-right: 0; border-bottom: 1px solid var(--line); min-height: 180px; }
|
||||
svg { min-height: 280px; }
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
@@ -532,6 +573,7 @@
|
||||
<div class="program-controls">
|
||||
<select data-program-selector aria-label="Simulation test program"></select>
|
||||
<button type="button" data-run-program>Run</button>
|
||||
<button type="button" data-run-editor-program>Run Editor Text</button>
|
||||
</div>
|
||||
<div class="playback-controls" aria-label="Simulation playback controls">
|
||||
<button type="button" class="toolbar-button" data-playback-reset>Reset</button>
|
||||
@@ -655,7 +697,18 @@
|
||||
<span>Program: <span data-selected-program>Executed through the LinuxCNC-backed WASM interpreter.</span> | Source: <span data-program-source>loading</span></span>
|
||||
<span>Active line <span data-active-line>-</span></span>
|
||||
</div>
|
||||
<div class="program-lines" data-program-lines></div>
|
||||
<div class="program-editor-wrap">
|
||||
<div class="program-editor-panel" data-axis-shell="program-editor">
|
||||
<div class="program-editor-toolbar">
|
||||
<span>Editable G-code</span>
|
||||
<div>
|
||||
<span data-program-editor-status>loaded</span>
|
||||
</div>
|
||||
</div>
|
||||
<textarea class="program-editor" spellcheck="false" data-program-editor aria-label="Editable G-code program"></textarea>
|
||||
</div>
|
||||
<div class="program-lines" data-program-lines></div>
|
||||
</div>
|
||||
</section>
|
||||
<section class="axis-gcode-pane" data-axis-shell="machine-state" aria-label="Machine state">
|
||||
<div class="axis-gcode-head">
|
||||
@@ -700,9 +753,11 @@
|
||||
} from "./simulation-app.js";
|
||||
|
||||
const runButton = document.querySelector("[data-run-program]");
|
||||
const runEditorButton = document.querySelector("[data-run-editor-program]");
|
||||
const programSelector = document.querySelector("[data-program-selector]");
|
||||
const openProgramButton = document.querySelector("[data-open-program]");
|
||||
const openProgramFile = document.querySelector("[data-open-program-file]");
|
||||
const programEditor = document.querySelector("[data-program-editor]");
|
||||
const resetButton = document.querySelector("[data-playback-reset]");
|
||||
const prevButton = document.querySelector("[data-playback-prev]");
|
||||
const playButton = document.querySelector("[data-playback-play]");
|
||||
@@ -711,6 +766,14 @@
|
||||
const speedSelector = document.querySelector("[data-playback-speed]");
|
||||
let playbackTimer = null;
|
||||
let playbackIndex = 0;
|
||||
let programEditorState = {
|
||||
text: "",
|
||||
metadata: {
|
||||
label: "Custom G-code program",
|
||||
source: "editor",
|
||||
sourceLabel: "Editor text",
|
||||
},
|
||||
};
|
||||
|
||||
function setAllText(selector, value) {
|
||||
document.querySelectorAll(selector).forEach((node) => {
|
||||
@@ -738,6 +801,28 @@
|
||||
return window.linuxCncRealSimulationState;
|
||||
}
|
||||
|
||||
function setProgramEditorText(text, metadata = {}) {
|
||||
programEditorState = {
|
||||
text,
|
||||
metadata: {
|
||||
...programEditorState.metadata,
|
||||
...metadata,
|
||||
},
|
||||
};
|
||||
if (programEditor && programEditor.value !== text) {
|
||||
programEditor.value = text;
|
||||
}
|
||||
setAllText("[data-program-editor-status]", programEditorState.metadata.sourceLabel ?? "Editor text");
|
||||
return { ...programEditorState, metadata: { ...programEditorState.metadata } };
|
||||
}
|
||||
|
||||
function getProgramEditorText() {
|
||||
return {
|
||||
text: programEditor?.value ?? programEditorState.text,
|
||||
metadata: { ...programEditorState.metadata },
|
||||
};
|
||||
}
|
||||
|
||||
function stopPlayback() {
|
||||
if (playbackTimer) {
|
||||
clearInterval(playbackTimer);
|
||||
@@ -809,6 +894,11 @@
|
||||
try {
|
||||
const state = await runRealBrowserSimulation({ documentRef: document, programId });
|
||||
window.linuxCncRealSimulationState = state;
|
||||
setProgramEditorText(state.programText, {
|
||||
label: state.program?.label ?? "Built-in test program",
|
||||
source: "builtin",
|
||||
sourceLabel: state.program?.sourceLabel ?? "Built-in test program",
|
||||
});
|
||||
renderPlayback(0);
|
||||
return state;
|
||||
} finally {
|
||||
@@ -838,6 +928,7 @@
|
||||
filename: metadata.filename ?? null,
|
||||
};
|
||||
window.linuxCncRealSimulationState = state;
|
||||
setProgramEditorText(programText, state.program);
|
||||
document.body.dataset.simulationProgramId = "custom";
|
||||
document.body.dataset.simulationProgramSource = state.program.source;
|
||||
setAllText("[data-selected-program]", state.program.label);
|
||||
@@ -860,6 +951,15 @@
|
||||
});
|
||||
}
|
||||
|
||||
async function runEditorProgramText() {
|
||||
const { text, metadata } = getProgramEditorText();
|
||||
return runProgramText(text, {
|
||||
...metadata,
|
||||
source: "editor",
|
||||
sourceLabel: "Editor text",
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
const state = await runSelectedProgram(DEFAULT_SIMULATION_PROGRAM_ID);
|
||||
window.linuxCncRealSimulationState = state;
|
||||
@@ -872,6 +972,9 @@
|
||||
play: startPlayback,
|
||||
pause: stopPlayback,
|
||||
finishPlayback: () => renderPlayback(currentState()?.motion.length - 1 ?? 0),
|
||||
getProgramText: getProgramEditorText,
|
||||
setProgramText: setProgramEditorText,
|
||||
runEditorProgramText,
|
||||
runProgramText,
|
||||
loadProgramFile,
|
||||
runProgramById: async (programId) => {
|
||||
@@ -887,6 +990,24 @@
|
||||
document.querySelector("[data-canonical-output]").textContent = error?.stack ?? error?.message ?? String(error);
|
||||
});
|
||||
});
|
||||
runEditorButton?.addEventListener("click", () => {
|
||||
window.linuxCncRealSimulationApi.runEditorProgramText().catch((error) => {
|
||||
document.body.dataset.simulationReady = "false";
|
||||
setAllText("[data-simulation-status]", "failed");
|
||||
document.querySelector("[data-canonical-output]").textContent = error?.stack ?? error?.message ?? String(error);
|
||||
});
|
||||
});
|
||||
programEditor?.addEventListener("input", () => {
|
||||
programEditorState = {
|
||||
text: programEditor.value,
|
||||
metadata: {
|
||||
...programEditorState.metadata,
|
||||
source: "editor",
|
||||
sourceLabel: "Editor text",
|
||||
},
|
||||
};
|
||||
setAllText("[data-program-editor-status]", "Editor text");
|
||||
});
|
||||
openProgramButton?.addEventListener("click", () => openProgramFile?.click());
|
||||
openProgramFile?.addEventListener("change", () => {
|
||||
const file = openProgramFile.files?.[0];
|
||||
|
||||
Reference in New Issue
Block a user