fix(export): enable proper file downloads and fix empty PNG export
This commit is contained in:
@@ -223,14 +223,14 @@ import Tooltip from "../components/Tooltip.astro";
|
|||||||
<TuiButton
|
<TuiButton
|
||||||
id="btn-copy-text"
|
id="btn-copy-text"
|
||||||
label="TXT"
|
label="TXT"
|
||||||
title="Copy Text"
|
title="Save as Text"
|
||||||
description="Copy raw ASCII text to clipboard."
|
description="Download raw ASCII text file."
|
||||||
/>
|
/>
|
||||||
<TuiButton
|
<TuiButton
|
||||||
id="btn-copy-html"
|
id="btn-copy-html"
|
||||||
label="HTML"
|
label="HTML"
|
||||||
title="Copy HTML"
|
title="Save as HTML"
|
||||||
description="Copy colored HTML span elements to clipboard."
|
description="Download colored HTML file."
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -417,11 +417,27 @@ export class AsciiController {
|
|||||||
// ============= Export =============
|
// ============= Export =============
|
||||||
|
|
||||||
savePNG(): void {
|
savePNG(): void {
|
||||||
|
const wasShowing = this.zoomState.showMagnifier;
|
||||||
|
|
||||||
|
// 1. Force hide magnifier for clean export
|
||||||
|
if (wasShowing) {
|
||||||
|
this.zoomState.showMagnifier = false;
|
||||||
|
this.requestRender('uniforms');
|
||||||
|
// Force synchronous render
|
||||||
|
this.renderFrame();
|
||||||
|
}
|
||||||
|
|
||||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
||||||
AsciiExporter.downloadPNG(this.canvas, `ascii-art-${timestamp}.png`);
|
AsciiExporter.downloadPNG(this.canvas, `ascii-art-${timestamp}.png`);
|
||||||
|
|
||||||
|
// 2. Restore state
|
||||||
|
if (wasShowing) {
|
||||||
|
this.zoomState.showMagnifier = true;
|
||||||
|
this.requestRender('uniforms');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async copyText(): Promise<void> {
|
saveText(): void {
|
||||||
if (!this.cachedGrid.imgEl) return;
|
if (!this.cachedGrid.imgEl) return;
|
||||||
const text = AsciiExporter.generateText(
|
const text = AsciiExporter.generateText(
|
||||||
this.cachedGrid.imgEl,
|
this.cachedGrid.imgEl,
|
||||||
@@ -429,10 +445,11 @@ export class AsciiController {
|
|||||||
this.cachedGrid.widthCols,
|
this.cachedGrid.widthCols,
|
||||||
Math.floor(this.cachedGrid.heightRows)
|
Math.floor(this.cachedGrid.heightRows)
|
||||||
);
|
);
|
||||||
await AsciiExporter.copyToClipboard(text);
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
||||||
|
AsciiExporter.downloadText(text, `ascii-art-${timestamp}.txt`);
|
||||||
}
|
}
|
||||||
|
|
||||||
async copyHTML(): Promise<void> {
|
saveHTML(): void {
|
||||||
if (!this.cachedGrid.imgEl) return;
|
if (!this.cachedGrid.imgEl) return;
|
||||||
const html = AsciiExporter.generateHTML(
|
const html = AsciiExporter.generateHTML(
|
||||||
this.cachedGrid.imgEl,
|
this.cachedGrid.imgEl,
|
||||||
@@ -440,7 +457,8 @@ export class AsciiController {
|
|||||||
this.cachedGrid.widthCols,
|
this.cachedGrid.widthCols,
|
||||||
Math.floor(this.cachedGrid.heightRows)
|
Math.floor(this.cachedGrid.heightRows)
|
||||||
);
|
);
|
||||||
await AsciiExporter.copyToClipboard(html);
|
const timestamp = new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19);
|
||||||
|
AsciiExporter.downloadText(html, `ascii-art-${timestamp}.html`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============= Utilities =============
|
// ============= Utilities =============
|
||||||
|
|||||||
@@ -8,6 +8,15 @@ export class AsciiExporter {
|
|||||||
link.click();
|
link.click();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static downloadText(content: string, filename: string) {
|
||||||
|
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
|
||||||
|
const link = document.createElement('a');
|
||||||
|
link.href = URL.createObjectURL(blob);
|
||||||
|
link.download = filename;
|
||||||
|
link.click();
|
||||||
|
URL.revokeObjectURL(link.href);
|
||||||
|
}
|
||||||
|
|
||||||
static async copyToClipboard(text: string): Promise<void> {
|
static async copyToClipboard(text: string): Promise<void> {
|
||||||
if (navigator.clipboard && navigator.clipboard.writeText) {
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
||||||
await navigator.clipboard.writeText(text);
|
await navigator.clipboard.writeText(text);
|
||||||
|
|||||||
@@ -308,9 +308,9 @@ export class UIBindings {
|
|||||||
|
|
||||||
const btnCopyText = document.getElementById('btn-copy-text');
|
const btnCopyText = document.getElementById('btn-copy-text');
|
||||||
if (btnCopyText) {
|
if (btnCopyText) {
|
||||||
const handler = async (e: Event) => {
|
const handler = (e: Event) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
await this.controller.copyText();
|
this.controller.saveText();
|
||||||
};
|
};
|
||||||
this.buttonHandlers.set('btn-copy-text', handler);
|
this.buttonHandlers.set('btn-copy-text', handler);
|
||||||
btnCopyText.addEventListener('click', handler);
|
btnCopyText.addEventListener('click', handler);
|
||||||
@@ -318,9 +318,9 @@ export class UIBindings {
|
|||||||
|
|
||||||
const btnCopyHtml = document.getElementById('btn-copy-html');
|
const btnCopyHtml = document.getElementById('btn-copy-html');
|
||||||
if (btnCopyHtml) {
|
if (btnCopyHtml) {
|
||||||
const handler = async (e: Event) => {
|
const handler = (e: Event) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
await this.controller.copyHTML();
|
this.controller.saveHTML();
|
||||||
};
|
};
|
||||||
this.buttonHandlers.set('btn-copy-html', handler);
|
this.buttonHandlers.set('btn-copy-html', handler);
|
||||||
btnCopyHtml.addEventListener('click', handler);
|
btnCopyHtml.addEventListener('click', handler);
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export class WebGLAsciiRenderer {
|
|||||||
private lastImage: HTMLImageElement | null;
|
private lastImage: HTMLImageElement | null;
|
||||||
|
|
||||||
constructor(_canvas: HTMLCanvasElement) {
|
constructor(_canvas: HTMLCanvasElement) {
|
||||||
const gl = _canvas.getContext('webgl', { antialias: false });
|
const gl = _canvas.getContext('webgl', { antialias: false, preserveDrawingBuffer: true });
|
||||||
if (!gl) {
|
if (!gl) {
|
||||||
throw new Error('WebGL not supported');
|
throw new Error('WebGL not supported');
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user