feat: copy output easily. also build fixes.

This commit is contained in:
2026-02-23 12:07:19 +01:00
parent caa3f3c061
commit bf945716f9
12 changed files with 135 additions and 7 deletions

View File

@@ -92,6 +92,31 @@
gap: 6px;
}
.output-content {
display: flex;
flex-direction: column;
gap: 8px;
}
.output-toolbar {
display: flex;
justify-content: flex-end;
}
.output-copy-button {
font-size: 11px;
padding: 3px 10px;
background-color: var(--vscode-button-secondaryBackground);
color: var(--vscode-button-secondaryForeground);
border: 1px solid var(--vscode-panel-border);
border-radius: 4px;
cursor: pointer;
}
.output-copy-button:hover {
background-color: var(--vscode-list-hoverBackground);
}
.output-item {
background-color: var(--vscode-sideBar-background);
border-radius: 4px;

View File

@@ -250,6 +250,35 @@ export const Panel: React.FC = () => {
});
};
const handleCopyOutput = async () => {
if (panelOutputEntries.length === 0) {
return;
}
const outputText = panelOutputEntries.map((entry) => entry.message).join('\n\n');
if (typeof navigator !== 'undefined' && navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
await navigator.clipboard.writeText(outputText);
return;
}
if (typeof document === 'undefined' || typeof document.createElement !== 'function') {
return;
}
const textArea = document.createElement('textarea');
textArea.value = outputText;
textArea.setAttribute('readonly', '');
textArea.style.position = 'absolute';
textArea.style.left = '-9999px';
document.body.appendChild(textArea);
textArea.select();
if (typeof document.execCommand === 'function') {
document.execCommand('copy');
}
document.body.removeChild(textArea);
};
const renderTaskRow = (task: TaskProgress, isChild = false) => (
<div key={task.taskId} className={`task-item status-${task.status} ${isChild ? 'task-child-row' : ''}`.trim()}>
<div className="task-status">
@@ -387,12 +416,25 @@ export const Panel: React.FC = () => {
panelOutputEntries.length === 0 ? (
<div className="panel-empty">{t('panel.noOutput')}</div>
) : (
<div className="output-list">
{panelOutputEntries.map((entry) => (
<div key={entry.id} className={`output-item output-${entry.kind}`}>
{entry.message}
</div>
))}
<div className="output-content">
<div className="output-toolbar">
<button
type="button"
className="output-copy-button"
onClick={() => {
void handleCopyOutput();
}}
>
{t('panel.copyOutput')}
</button>
</div>
<div className="output-list">
{panelOutputEntries.map((entry) => (
<div key={entry.id} className={`output-item output-${entry.kind}`}>
{entry.message}
</div>
))}
</div>
</div>
)
)}

View File

@@ -629,6 +629,7 @@
"panel.closeTitle": "Panel schließen",
"panel.noRecentTasks": "Keine aktuellen Aufgaben",
"panel.noOutput": "Keine Ausgabe",
"panel.copyOutput": "Ausgabe kopieren",
"panel.openPostEditor": "Öffne einen Beitragseditor, um Beitragslinks zu sehen",
"panel.loadingPostLinks": "Beitragslinks werden geladen...",
"panel.noPostLinks": "Keine Beitragslinks für diesen Beitrag",

View File

@@ -629,6 +629,7 @@
"panel.closeTitle": "Close panel",
"panel.noRecentTasks": "No recent tasks",
"panel.noOutput": "No output",
"panel.copyOutput": "Copy Output",
"panel.openPostEditor": "Open a post editor to view post links",
"panel.loadingPostLinks": "Loading post links...",
"panel.noPostLinks": "No post links for this post",

View File

@@ -629,6 +629,7 @@
"panel.closeTitle": "Cerrar panel",
"panel.noRecentTasks": "No hay tareas recientes",
"panel.noOutput": "Sin salida",
"panel.copyOutput": "Copiar salida",
"panel.openPostEditor": "Abre un editor de entradas para ver los enlaces",
"panel.loadingPostLinks": "Cargando enlaces de entradas...",
"panel.noPostLinks": "No hay enlaces para esta entrada",

View File

@@ -629,6 +629,7 @@
"panel.closeTitle": "Fermer le panneau",
"panel.noRecentTasks": "Aucune tâche récente",
"panel.noOutput": "Aucune sortie",
"panel.copyOutput": "Copier la sortie",
"panel.openPostEditor": "Ouvrez un éditeur d'article pour voir les liens",
"panel.loadingPostLinks": "Chargement des liens d'articles...",
"panel.noPostLinks": "Aucun lien pour cet article",

View File

@@ -629,6 +629,7 @@
"panel.closeTitle": "Chiudi pannello",
"panel.noRecentTasks": "Nessuna attività recente",
"panel.noOutput": "Nessun output",
"panel.copyOutput": "Copia output",
"panel.openPostEditor": "Apri un editor post per visualizzare i collegamenti",
"panel.loadingPostLinks": "Caricamento collegamenti post...",
"panel.noPostLinks": "Nessun collegamento per questo post",

View File

@@ -0,0 +1,3 @@
export function resolvePyodideIndexURL(workerModuleUrl: string): string {
return new URL('../../../node_modules/pyodide/', workerModuleUrl).toString();
}

View File

@@ -1,6 +1,7 @@
import { loadPyodide, type PyodideInterface } from 'pyodide';
import type { PythonWorkerMessage, PythonWorkerRequest } from './runtimeProtocol';
import { parseMacroContextV1, parseMacroResultV1 } from './abiV1';
import { resolvePyodideIndexURL } from './pyodideAssetUrl';
let runtime: PyodideInterface | null = null;
let activeRequestId: string | null = null;
@@ -183,6 +184,7 @@ json.dumps(__bds_entrypoints)
async function bootstrapRuntime(): Promise<void> {
try {
runtime = await loadPyodide({
indexURL: resolvePyodideIndexURL(import.meta.url),
stdout: (chunk) => {
if (!activeRequestId) {
return;