Add option to view preview image in separate tab

This commit is contained in:
javl
2026-08-18 15:22:31 +02:00
parent fe7017e14c
commit 5e1d70b54e
3 changed files with 51 additions and 25 deletions
+8 -3
View File
@@ -796,13 +796,18 @@ input[type="file"]::file-selector-button {
/* background: var(--primary-container); */ /* background: var(--primary-container); */
margin: var(--space-2) var(--space-4) var(--space-2) 0; margin: var(--space-2) var(--space-4) var(--space-2) 0;
max-width: 100%; max-width: 100%;
/* Display width/height/aspect-ratio are set in JS (setCanvasDisplaySize) cursor: pointer;
so small glyphs get enlarged without distorting their aspect ratio; /* Display width/height are set in JS (setCanvasDisplaySize), capped to
independent min-width/min-height here would stretch non-square canvases. */ 128px on the largest side while preserving aspect ratio, and never
scaled up beyond the canvas' actual pixel size. */
image-rendering: crisp-edges; image-rendering: crisp-edges;
image-rendering: pixelated; image-rendering: pixelated;
} }
#preview-note {
display: none;
}
.inlineImg { .inlineImg {
border-radius: var(--radius-lg); border-radius: var(--radius-lg);
margin-top: var(--space-2); margin-top: var(--space-2);
+2 -1
View File
@@ -59,6 +59,7 @@
<h2>1. Select image</h2> <h2>1. Select image</h2>
<div class="note upload-note">You can import multiple images at once.<br>All processing is done locally in your browser: your images are not uploaded or stored anywhere online.</div> <div class="note upload-note">You can import multiple images at once.<br>All processing is done locally in your browser: your images are not uploaded or stored anywhere online.</div>
<input type="file" id="file-input" name="file-input" multiple/><br /> <input type="file" id="file-input" name="file-input" multiple/><br />
<div id="only-images-file-error" class="msg error-msg">Only images file type are allowed</div>
<div id="file-input-column-items"></div> <div id="file-input-column-items"></div>
<template id="file-input-entry-template"> <template id="file-input-entry-template">
<div class="file-input-entry"> <div class="file-input-entry">
@@ -122,7 +123,6 @@
</div> </div>
</li> </li>
</template> </template>
<div id="only-images-file-error" class="msg error-msg">Only images file type are allowed</div>
<div class="no-file-selected msg">Select one or more images at <a href="#step-1">step 1</a> first.</div> <div class="no-file-selected msg">Select one or more images at <a href="#step-1">step 1</a> first.</div>
<button id="all-same-size" onclick="allSameSize()">Apply first image size to all images</button> <button id="all-same-size" onclick="allSameSize()">Apply first image size to all images</button>
</div> </div>
@@ -230,6 +230,7 @@
<section class="section step bottom-divider" id="step-3"> <section class="section step bottom-divider" id="step-3">
<!-- <div class="step-num">3</div> --> <!-- <div class="step-num">3</div> -->
<h2>Preview</h2> <h2>Preview</h2>
<div id="preview-note" class="note">Click image to view at actual size</div>
<section class="sub-section"> <section class="sub-section">
<div class="no-file-selected msg">No files selected</div> <div class="no-file-selected msg">No files selected</div>
<div id="images-canvas-container"></div> <div id="images-canvas-container"></div>
+41 -21
View File
@@ -260,17 +260,32 @@ function invert(canvas, ctx) {
ctx.putImageData(imageData, 0, 0); ctx.putImageData(imageData, 0, 0);
} }
// Size the on-screen canvas so tiny glyphs stay legible without distorting // Open the canvas' current contents as a full-size image in a new tab,
// their aspect ratio (independent min-width/min-height would stretch them). // like a right-click "open image in new tab" on a regular <img>.
function openCanvasInNewTab(canvas) {
canvas.toBlob((blob) => {
const url = URL.createObjectURL(blob);
window.open(url, '_blank');
// Give the new tab time to load the image before freeing the blob.
setTimeout(() => URL.revokeObjectURL(url), 30000);
});
}
// Size the on-screen canvas, preserving aspect ratio: shown at its actual
// pixel size up to 128px on its largest side, scaled down to fit within
// 128px otherwise. Never scaled up. Wires up click-to-open-in-new-tab
// (the "click to open at actual size" hint lives once in the Preview
// header, not per canvas).
function setCanvasDisplaySize(canvas) { function setCanvasDisplaySize(canvas) {
const minVisibleSize = 96; const maxPreviewSize = 128;
const scale = Math.max(1, minVisibleSize / Math.min(canvas.width, canvas.height)); const largestSide = Math.max(canvas.width, canvas.height);
// eslint-disable-next-line no-param-reassign const scale = largestSide > maxPreviewSize ? maxPreviewSize / largestSide : 1;
canvas.style.aspectRatio = `${canvas.width} / ${canvas.height}`;
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
canvas.style.width = `${Math.round(canvas.width * scale)}px`; canvas.style.width = `${Math.round(canvas.width * scale)}px`;
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
canvas.style.height = 'auto'; canvas.style.height = `${Math.round(canvas.height * scale)}px`;
// eslint-disable-next-line no-param-reassign
canvas.onclick = () => openCanvasInNewTab(canvas);
} }
// Draw the image onto the canvas, taking into account color and scaling // Draw the image onto the canvas, taking into account color and scaling
@@ -655,9 +670,11 @@ function setTextInputSize(width, height) {
function checkImagesAvailable() { function checkImagesAvailable() {
const error = document.getElementById('no-images-error'); const error = document.getElementById('no-images-error');
const glyphNameNote = document.getElementById('glyph-name-note'); const glyphNameNote = document.getElementById('glyph-name-note');
const previewNote = document.getElementById('preview-note');
const hasImages = images.length() > 0; const hasImages = images.length() > 0;
error.style.display = hasImages ? 'none' : 'block'; error.style.display = hasImages ? 'none' : 'block';
glyphNameNote.style.display = hasImages ? 'block' : 'none'; glyphNameNote.style.display = hasImages ? 'block' : 'none';
previewNote.style.display = hasImages ? 'block' : 'none';
return hasImages; return hasImages;
} }
@@ -712,6 +729,7 @@ function handleTextInput(drawMode) {
: listToImageVertical(list, canvas); : listToImageVertical(list, canvas);
if (success) { if (success) {
setCanvasDisplaySize(canvas);
document.getElementById('text-input-error').style.display = 'none'; document.getElementById('text-input-error').style.display = 'none';
document.querySelectorAll('.no-file-selected').forEach((el) => { document.querySelectorAll('.no-file-selected').forEach((el) => {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
@@ -927,32 +945,34 @@ function wrapFullExample(arrayCode, varName, width, height) {
#include <Adafruit_GFX.h> #include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h> #include <Adafruit_SSD1306.h>
#define OLED_RESET 4 #define SCREEN_WIDTH 128
Adafruit_SSD1306 display(OLED_RESET); #define SCREEN_HEIGHT 64
#if (SSD1306_LCDHEIGHT != ${settings.screenHeight}) // Initialize display with standard I2C reset pin config (-1)
#error("Height incorrect, please fix Adafruit_SSD1306.h!"); Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);
#endif
${arrayCode} ${arrayCode}
void setup() { void setup() {
// initialize with the I2C addr 0x3D for the 128x64 Serial.begin(9600);
// replace with 0x3C for the 128x32
display.begin(SSD1306_SWITCHCAPVCC, 0x3D); // Initialize with I2C address 0x3C. You might have to change this to 0x3D for your display.
if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println(F("SSD1306 allocation failed. Check your wiring or I2C address!"));
for(;;);
}
display.clearDisplay(); // Make sure the display is cleared display.clearDisplay(); // Make sure the display is cleared
// Draw the bitmap:
// drawBitmap(x position, y position, bitmap data, bitmap width, bitmap height, color)
display.drawBitmap(0, 0, ${varName}, ${width}, ${height}, WHITE);
// Update the display // drawBitmap(x_coordinate, y_coordinate, array_name, width, height, color)
display.drawBitmap(0, 0, ${varName}, ${width}, ${height}, SSD1306_WHITE);
// Push the memory buffer out to the physical display
display.display(); display.display();
} }
void loop() { void loop() {
} }`;
`;
} }
// Removes the trailing separator comma left after the last byte value. // Removes the trailing separator comma left after the last byte value.