chore: update author name in package.json and refine file URL handling in renderer.js
This commit is contained in:
+79
-40
@@ -343,7 +343,7 @@ function handleImageSelection(filePath) {
|
||||
resetBgSettings();
|
||||
|
||||
// Show image in preview element
|
||||
elements.imageElement.src = `file://${filePath}`;
|
||||
elements.imageElement.src = toFileUrl(filePath);
|
||||
|
||||
// Set file metadata in control tower
|
||||
const stats = getFileStats(filePath);
|
||||
@@ -367,7 +367,7 @@ function handleImageSelection(filePath) {
|
||||
// Initialize CropperJS on image
|
||||
initCropper();
|
||||
};
|
||||
img.src = `file://${filePath}`;
|
||||
img.src = toFileUrl(filePath);
|
||||
}
|
||||
|
||||
function getFileStats(path) {
|
||||
@@ -388,6 +388,19 @@ function formatBytes(bytes, decimals = 2) {
|
||||
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
|
||||
}
|
||||
|
||||
function toFileUrl(filePath) {
|
||||
const normalized = filePath.replace(/\\/g, '/');
|
||||
return normalized.startsWith('/')
|
||||
? `file://${normalized}`
|
||||
: `file:///${normalized}`;
|
||||
}
|
||||
|
||||
function unlockPostProcessingControls() {
|
||||
elements.secCanvasBg.classList.remove('disabled-group');
|
||||
elements.secExport.classList.remove('disabled-group');
|
||||
elements.ctrlAutoTrim.disabled = false;
|
||||
}
|
||||
|
||||
// --- CROPPERJS CONTROLLER HUB ---
|
||||
function initCropper() {
|
||||
if (cropper) {
|
||||
@@ -412,7 +425,9 @@ function initCropper() {
|
||||
// Wire toolbar click listeners
|
||||
elements.ctrlCropDrag.classList.add('active');
|
||||
elements.ctrlCropBox.classList.remove('active');
|
||||
elements.ctrlAutoTrim.disabled = true; // Disabled until background is removed
|
||||
elements.ctrlAutoTrim.disabled = !processedTempPath;
|
||||
|
||||
applyWorkspaceBackground();
|
||||
}
|
||||
|
||||
function setupToolbarListeners() {
|
||||
@@ -1133,23 +1148,18 @@ async function executeBackgroundRemoval() {
|
||||
if (result.success) {
|
||||
processedTempPath = result.outputPath;
|
||||
showToast(getTrans('toastTitleSuccess'), getTrans('toastTrimCompleteMsg'), "success");
|
||||
|
||||
// Update workspace preview image source to output transparent PNG
|
||||
// Add timestamp to bypass browser cache refreshing
|
||||
const freshSrc = `file://${processedTempPath}?t=${Date.now()}`;
|
||||
|
||||
// Re-initialize CropperJS on the new image src
|
||||
unlockPostProcessingControls();
|
||||
|
||||
const freshSrc = `${toFileUrl(processedTempPath)}?t=${Date.now()}`;
|
||||
elements.imageElement.onload = () => {
|
||||
elements.imageElement.onload = null;
|
||||
try {
|
||||
initCropper();
|
||||
} catch (err) {
|
||||
console.error('Cropper init failed:', err);
|
||||
}
|
||||
};
|
||||
elements.imageElement.src = freshSrc;
|
||||
|
||||
// Wait for image source update to build the cropper safely
|
||||
setTimeout(() => {
|
||||
initCropper();
|
||||
|
||||
// Unlock background customization and export
|
||||
elements.secCanvasBg.classList.remove('disabled-group');
|
||||
elements.secExport.classList.remove('disabled-group');
|
||||
elements.ctrlAutoTrim.disabled = false; // Unlock trim feature!
|
||||
}, 300);
|
||||
|
||||
} else {
|
||||
elements.errorOverlayLog.innerText = result.error || getTrans('toastSetupErrorMsg');
|
||||
@@ -1184,44 +1194,81 @@ function switchBgMode(mode) {
|
||||
|
||||
if (mode === 'transparent') {
|
||||
elements.tabBgTrans.classList.add('active');
|
||||
elements.canvasBgOverlay.style.opacity = '0';
|
||||
} else if (mode === 'solid') {
|
||||
elements.tabBgSolid.classList.add('active');
|
||||
elements.subpanelSolid.classList.remove('hidden');
|
||||
elements.canvasBgOverlay.style.background = activeSolidColor;
|
||||
elements.canvasBgOverlay.style.opacity = '1';
|
||||
} else if (mode === 'gradient') {
|
||||
elements.tabBgGrad.classList.add('active');
|
||||
elements.subpanelGradient.classList.remove('hidden');
|
||||
|
||||
// Draw current active gradient stops onto the workspace preview
|
||||
const cssGrad = buildCssGradientString(activeGradientStops);
|
||||
elements.canvasBgOverlay.style.background = cssGrad;
|
||||
elements.canvasBgOverlay.style.opacity = '1';
|
||||
}
|
||||
|
||||
applyWorkspaceBackground();
|
||||
}
|
||||
|
||||
function buildCssGradientString(stops) {
|
||||
return `linear-gradient(135deg, ${stops[0].color}, ${stops[1].color})`;
|
||||
}
|
||||
|
||||
function applyWorkspaceBackground() {
|
||||
const wrapper = elements.canvasWrapper;
|
||||
wrapper.style.background = '';
|
||||
wrapper.style.backgroundImage = '';
|
||||
wrapper.style.backgroundColor = '';
|
||||
|
||||
if (activeBgMode === 'transparent') {
|
||||
wrapper.classList.add('checkerboard');
|
||||
return;
|
||||
}
|
||||
|
||||
wrapper.classList.remove('checkerboard');
|
||||
|
||||
const bgValue = activeBgMode === 'solid'
|
||||
? activeSolidColor
|
||||
: buildCssGradientString(activeGradientStops);
|
||||
|
||||
wrapper.style.background = bgValue;
|
||||
|
||||
// Keep cropper inner layers transparent so the wrapper background shows through
|
||||
wrapper.querySelectorAll('.cropper-wrap, .cropper-bg, .cropper-canvas').forEach((el) => {
|
||||
el.style.background = 'transparent';
|
||||
el.style.backgroundImage = 'none';
|
||||
});
|
||||
}
|
||||
|
||||
function selectGradientSwatch(btn) {
|
||||
const gradKey = [...btn.classList].find((cls) => cls.startsWith('grad-'));
|
||||
if (!gradKey || !GRADIENTS[gradKey]) return;
|
||||
|
||||
document.querySelectorAll('.grad-swatch').forEach((s) => s.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
activeGradientStops = GRADIENTS[gradKey].map((stop) => ({ ...stop }));
|
||||
|
||||
if (activeBgMode === 'gradient') {
|
||||
applyWorkspaceBackground();
|
||||
}
|
||||
}
|
||||
|
||||
function setupBgSwatches() {
|
||||
// Solid Swatches
|
||||
const solidSwatches = document.querySelectorAll('.swatch:not(.custom)');
|
||||
solidSwatches.forEach(swatch => {
|
||||
swatch.addEventListener('click', (e) => {
|
||||
solidSwatches.forEach(s => s.classList.remove('active'));
|
||||
e.target.classList.add('active');
|
||||
e.currentTarget.classList.add('active');
|
||||
|
||||
activeSolidColor = e.target.getAttribute('data-color');
|
||||
elements.canvasBgOverlay.style.background = activeSolidColor;
|
||||
activeSolidColor = e.currentTarget.getAttribute('data-color');
|
||||
if (activeBgMode === 'solid') {
|
||||
applyWorkspaceBackground();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Custom Color Picker
|
||||
elements.customColorPicker.addEventListener('input', (e) => {
|
||||
activeSolidColor = e.target.value;
|
||||
elements.canvasBgOverlay.style.background = activeSolidColor;
|
||||
if (activeBgMode === 'solid') {
|
||||
applyWorkspaceBackground();
|
||||
}
|
||||
|
||||
// Highlight custom swatch borders
|
||||
elements.btnCustomColorPicker.classList.add('active');
|
||||
@@ -1232,14 +1279,7 @@ function setupBgSwatches() {
|
||||
const gradSwatches = document.querySelectorAll('.grad-swatch');
|
||||
gradSwatches.forEach(swatch => {
|
||||
swatch.addEventListener('click', (e) => {
|
||||
gradSwatches.forEach(s => s.classList.remove('active'));
|
||||
e.target.classList.add('active');
|
||||
|
||||
const gradKey = e.target.className.split(' ').find(cls => cls.startsWith('grad-'));
|
||||
if (GRADIENTS[gradKey]) {
|
||||
activeGradientStops = GRADIENTS[gradKey];
|
||||
elements.canvasBgOverlay.style.background = e.target.getAttribute('data-gradient');
|
||||
}
|
||||
selectGradientSwatch(e.currentTarget);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -1276,7 +1316,6 @@ async function executeExport() {
|
||||
ctx.fillStyle = activeSolidColor;
|
||||
ctx.fillRect(0, 0, finalCanvas.width, finalCanvas.height);
|
||||
} else if (activeBgMode === 'gradient') {
|
||||
// Render the 135deg gradient vector on final output canvas
|
||||
const grad = ctx.createLinearGradient(0, 0, finalCanvas.width, finalCanvas.height);
|
||||
activeGradientStops.forEach(stop => {
|
||||
grad.addColorStop(stop.offset, stop.color);
|
||||
|
||||
Reference in New Issue
Block a user