Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix editor
  • Loading branch information
yakhyo committed Feb 27, 2026
commit 0465f8848bf80bded2a247a6fe5974072d590428
4 changes: 2 additions & 2 deletions _includes/pyodide-runtime.njk
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@
return;
}

const PYODIDE_CDN = "https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.js";
const PYODIDE_CDN = "https://cdn.jsdelivr.net/pyodide/v0.27.5/full/pyodide.js";
const WORKER_URL = "{{ '/js/pyodide-worker.js' | url }}";
const DEFAULT_TIMEOUT_MS = 12000;
const DEFAULT_TIMEOUT_MS = 30000;

let worker = null;
let runCounter = 0;
Expand Down
41 changes: 32 additions & 9 deletions _includes/python-runner.njk
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@
const resetBtn = header.querySelector(`#reset-btn-${index}`);
const copyBtn = header.querySelector(`#copy-btn-${index}`);
const statusBadge = header.querySelector(`#status-${index}`);

copyBtn.addEventListener('click', async () => {
try {
await navigator.clipboard.writeText(block.innerText);
Expand All @@ -281,7 +282,8 @@
}
setTimeout(() => (statusBadge.textContent = ''), 2000);
});
runBtn.addEventListener('click', async () => {

async function runCode() {
output.classList.add('active');
output.classList.remove('error');
output.textContent = '';
Expand All @@ -290,15 +292,26 @@
runBtn.disabled = true;
try {
const result = await window.PyRuntime.run(block.textContent || '', {
timeoutMs: 12000,
timeoutMs: 30000,
useWorker: true
});
const combinedOutput = [result.stdout, result.stderr].filter(Boolean).join('\n');
statusBadge.textContent = 'Done';
if (!combinedOutput) {
if (!result.stdout && !result.stderr) {
output.textContent = '(Executed successfully, no output)';
} else {
output.textContent = combinedOutput;
output.innerHTML = '';
if (result.stdout) {
const el = document.createElement('span');
el.textContent = result.stdout;
output.appendChild(el);
}
if (result.stderr) {
if (result.stdout) output.appendChild(document.createTextNode('\n'));
const el = document.createElement('span');
el.style.color = '#fbbf24';
el.textContent = result.stderr;
output.appendChild(el);
}
}
} catch (err) {
output.classList.add('error');
Expand All @@ -307,18 +320,28 @@
} finally {
loadingOverlay.style.display = 'none';
runBtn.disabled = false;
output.scrollTop = output.scrollHeight;
setTimeout(() => {
if (statusBadge.textContent !== 'Error')
statusBadge.textContent = '';

if (statusBadge.textContent !== 'Error') statusBadge.textContent = '';
}, 3000);
}
}

// Ctrl+Enter / Cmd+Enter to run
block.addEventListener('keydown', (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
e.preventDefault();
runCode();
}
});

runBtn.addEventListener('click', runCode);

resetBtn.addEventListener('click', () => {
block.textContent = originalCode;
renderHighlightedEditable(block, editorLanguage, baseClassName);

output.classList.remove('active');
output.innerHTML = '';
statusBadge.textContent = 'Reset';
setTimeout(() => statusBadge.textContent = '', 2000);
});
Expand Down
4 changes: 2 additions & 2 deletions _includes/python-split-editor-tool.njk
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
</header>

<div class="flex-1 flex min-h-0">
<div class="w-1/2 flex flex-col border-r border-slate-700">
<div class="w-[55%] flex flex-col border-r border-slate-700">
{% if toolEditorTopBar %}
<div class="bg-slate-800 px-4 py-2 text-xs text-slate-400 font-medium flex justify-between items-center border-b border-slate-700">
<span>{{ toolEditorTopBar }}</span>
Expand All @@ -48,7 +48,7 @@
</div>
</div>

<div class="w-1/2 flex flex-col {{ toolOutputPaneClass }}">
<div class="w-[45%] flex flex-col {{ toolOutputPaneClass }}">
{% if toolOutputHeader %}
<div class="bg-slate-800 px-4 py-2 text-xs text-slate-400 font-medium border-b border-slate-700">
{{ toolOutputHeader }}
Expand Down
119 changes: 74 additions & 45 deletions _includes/python-tool-runner.njk
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@
border-left: 1px solid var(--tool-border);
}

/* Loading overlay */
.tool-loading {
position: absolute;
inset: 0;
Expand All @@ -142,6 +141,16 @@
margin-bottom: 0.5rem;
}

.tool-output-stdout {
color: var(--tool-output-text);
}
.tool-output-stderr {
color: #fbbf24;
}
.tool-output-error {
color: var(--tool-error);
}

</style>

{% include "editor-helpers.njk" %}{% include "pyodide-runtime.njk" %}
Expand All @@ -155,14 +164,14 @@
makeEditable
} = window.EditorHelpers;

window.initPythonTool = function(editorId, outputId, runBtnId, resetBtnId, statusId) {
window.initPythonTool = function(editorId, outputId, runBtnId, resetBtnId, statusId, options) {
const editor = document.getElementById(editorId);
const output = document.getElementById(outputId);
const runBtn = document.getElementById(runBtnId);
const resetBtn = document.getElementById(resetBtnId);
const status = document.getElementById(statusId);
const { baseClassName, language: editorLanguage } = parseEditorClass(editor);

const timeoutMs = (options && options.timeoutMs) || 30000;
const initialCode = editor.textContent || "";
editor.textContent = initialCode;
const originalCode = editor.textContent || "";
Expand All @@ -180,12 +189,68 @@
setCaretOffset(editor, Math.min(nextCaretOffset, nextSource.length));
};

async function runCode() {
output.textContent = '';
output.innerHTML = '';
loadingOverlay.classList.add('active');
status.textContent = 'Running...';
runBtn.disabled = true;

try {
const code = editor.textContent || "";
const result = await window.PyRuntime.run(code, {
timeoutMs: timeoutMs,
useWorker: true
});

status.textContent = 'Done';

if (!result.stdout && !result.stderr) {
output.textContent = '(Executed successfully, no output)';
} else {
output.innerHTML = '';
if (result.stdout) {
const stdoutEl = document.createElement('span');
stdoutEl.className = 'tool-output-stdout';
stdoutEl.textContent = result.stdout;
output.appendChild(stdoutEl);
}
if (result.stderr) {
if (result.stdout) output.appendChild(document.createTextNode('\n'));
const stderrEl = document.createElement('span');
stderrEl.className = 'tool-output-stderr';
stderrEl.textContent = result.stderr;
output.appendChild(stderrEl);
}
}
} catch (err) {
const errorEl = document.createElement('span');
errorEl.className = 'tool-output-error';
errorEl.textContent = err.message;
output.innerHTML = '';
output.appendChild(errorEl);
status.textContent = 'Error';
} finally {
loadingOverlay.classList.remove('active');
runBtn.disabled = false;
output.scrollTop = output.scrollHeight;
setTimeout(() => {
if (status.textContent !== 'Error') status.textContent = 'Ready';
}, 2000);
}
}

editor.addEventListener('keydown', (event) => {
const offsets = getSelectionOffsets(editor);
if (!offsets) {
// Ctrl+Enter / Cmd+Enter to run
if ((event.ctrlKey || event.metaKey) && event.key === 'Enter') {
event.preventDefault();
runCode();
return;
}

const offsets = getSelectionOffsets(editor);
if (!offsets) return;

const tabText = ' ';
const source = editor.textContent || '';
const { start, end } = offsets;
Expand All @@ -203,10 +268,7 @@
return;
}

if (event.key !== 'Tab') {
return;
}

if (event.key !== 'Tab') return;
event.preventDefault();

if (start !== end) {
Expand All @@ -228,10 +290,7 @@
const lineStart = source.lastIndexOf('\n', Math.max(0, start - 1)) + 1;
const prefix = source.slice(lineStart, start);
const removable = prefix.match(/ {1,4}$/);
if (!removable) {
return;
}

if (!removable) return;
const removeCount = removable[0].length;
const removeStart = start - removeCount;
const nextSource = source.slice(0, removeStart) + source.slice(start);
Expand All @@ -243,44 +302,14 @@
applyEditorText(nextSource, start + tabText.length);
});

runBtn.addEventListener('click', async () => {
output.textContent = '';
output.classList.remove('text-red-400');
loadingOverlay.classList.add('active');
status.textContent = 'Running...';
runBtn.disabled = true;

try {
const code = editor.textContent || "";
const result = await window.PyRuntime.run(code, {
timeoutMs: 12000,
useWorker: true
});
const combinedOutput = [result.stdout, result.stderr].filter(Boolean).join('\n');
status.textContent = 'Done';
if (!combinedOutput) {
output.textContent = '(Executed successfully, no output)';
} else {
output.textContent = combinedOutput;
}
} catch (err) {
output.classList.add('text-red-400');
output.textContent = err.message;
status.textContent = 'Error';
} finally {
loadingOverlay.classList.remove('active');
runBtn.disabled = false;
setTimeout(() => {
if (status.textContent !== 'Error') status.textContent = 'Ready';
}, 2000);
}
});
runBtn.addEventListener('click', runCode);

if (resetBtn) {
resetBtn.addEventListener('click', () => {
editor.textContent = originalCode;
renderHighlightedEditable(editor, editorLanguage, baseClassName);
output.textContent = '';
output.innerHTML = '';
status.textContent = 'Reset';
});
}
Expand Down
Loading