From 7edbc28a9cf678bb12f9d8e33ff473a39ff05747 Mon Sep 17 00:00:00 2001 From: Gamosoft Date: Tue, 21 Apr 2026 17:39:22 +0200 Subject: [PATCH] added more tools, fixed locales --- frontend/app.js | 85 ++++++++++++++++++++++++++++++++++++++++++--- frontend/index.html | 84 +++++++++++++++++++++++++++++++++++--------- locales/de-DE.json | 7 +++- locales/en-GB.json | 7 +++- locales/en-US.json | 7 +++- locales/es-ES.json | 7 +++- locales/fr-FR.json | 7 +++- locales/hu-HU.json | 7 +++- locales/it-IT.json | 7 +++- locales/ja-JP.json | 7 +++- locales/ru-RU.json | 7 +++- locales/sl-SI.json | 7 +++- locales/zh-CN.json | 7 +++- 13 files changed, 214 insertions(+), 32 deletions(-) diff --git a/frontend/app.js b/frontend/app.js index 39bf836..81c7aa1 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -3,6 +3,8 @@ // Configuration constants const CONFIG = { AUTOSAVE_DELAY: 1000, // ms - Debounce before note save (autoSave) and drawing PNG autosave (_drawingScheduleAutosave) + /** Must match drawingRedraw() fill and eraser stroke color (opaque “whiteboard”). */ + DRAWING_BACKGROUND: '#ffffff', SEARCH_DEBOUNCE_DELAY: 500, // ms - Delay before running note search while typing SAVE_INDICATOR_DURATION: 2000, // ms - How long to show "saved" indicator SCROLL_SYNC_DELAY: 50, // ms - Delay to prevent scroll sync interference @@ -504,6 +506,8 @@ function noteApp() { drawingRedoStack: [], drawingDraft: null, drawingIsPointerDown: false, + /** True after the PNG from disk has been decoded into _drawingBaseImage; false after Clear. */ + drawingHasRasterFromFile: false, _drawingAutosaveTimeout: null, // DOM element cache (to avoid repeated querySelector calls) @@ -2904,7 +2908,7 @@ function noteApp() { const dpr = this._drawingDpr; ctx.setTransform(dpr, 0, 0, dpr, 0, 0); ctx.clearRect(0, 0, w, h); - ctx.fillStyle = '#ffffff'; + ctx.fillStyle = CONFIG.DRAWING_BACKGROUND; ctx.fillRect(0, 0, w, h); if (this._drawingBaseImage && this._drawingBaseImage.complete) { ctx.drawImage(this._drawingBaseImage, 0, 0, w, h); @@ -3016,6 +3020,7 @@ function noteApp() { this.drawingDraft = null; this.drawingIsPointerDown = false; this._drawingBaseImage = null; + this.drawingHasRasterFromFile = false; this._drawingLoadToken = Symbol(); const token = this._drawingLoadToken; @@ -3050,28 +3055,98 @@ function noteApp() { }); if (token !== this._drawingLoadToken) return; this._drawingBaseImage = img; + this.drawingHasRasterFromFile = true; this._drawingLayoutCanvas(); } catch (e) { if (token !== this._drawingLoadToken) return; ErrorHandler.handle('load drawing', e); + this.drawingHasRasterFromFile = false; this._drawingLayoutCanvas(); } }, + _drawingRgbToHex(r, g, b) { + const h = (n) => { + const s = n.toString(16); + return s.length === 1 ? `0${s}` : s; + }; + return `#${h(r)}${h(g)}${h(b)}`; + }, + + /** Sample visible canvas color at logical (css) coordinates; sets drawingColor. */ + drawingSampleColor(e) { + const canvas = this._drawingCanvasEl; + const ctx = this._drawingCtx; + if (!canvas || !ctx) return; + const { x, y } = this._drawingCanvasCoords(e); + this.drawingRedraw(); + const dpr = this._drawingDpr || 1; + let ix = Math.floor(x * dpr); + let iy = Math.floor(y * dpr); + ix = Math.max(0, Math.min(ix, canvas.width - 1)); + iy = Math.max(0, Math.min(iy, canvas.height - 1)); + const pix = ctx.getImageData(ix, iy, 1, 1).data; + this.drawingColor = this._drawingRgbToHex(pix[0], pix[1], pix[2]); + }, + + /** True when there is a loaded bitmap and/or session strokes to clear away. */ + drawingClearEnabled() { + if (this.currentMediaType !== 'drawing') return false; + return !!(this.drawingHasRasterFromFile || (this.drawingOps && this.drawingOps.length > 0)); + }, + + /** + * Replace the in-memory drawing with a blank canvas and schedule save so the file on disk + * becomes a fresh white PNG (same dimensions as the viewer). Drops the loaded raster. + */ + async drawingClear() { + if (this.currentMediaType !== 'drawing' || !this.currentMedia) return; + if (!this._drawingBaseImage && this.drawingOps.length === 0) return; + const ok = await this.confirmModalAsk({ + title: this.t('drawing.clear_title'), + message: this.t('drawing.clear_confirm'), + danger: true, + confirmLabel: this.t('drawing.clear'), + }); + if (!ok) return; + this._drawingCancelAutosave(); + if (this._drawingObjectURL) { + try { + URL.revokeObjectURL(this._drawingObjectURL); + } catch (_) { + /* ignore */ + } + this._drawingObjectURL = null; + } + this._drawingBaseImage = null; + this.drawingHasRasterFromFile = false; + this.drawingDraft = null; + this.drawingIsPointerDown = false; + this.drawingOps = []; + this.drawingRedoStack = []; + this.drawingRedraw(); + this._drawingScheduleAutosave(); + }, + drawingPointerDown(e) { if (this.currentMediaType !== 'drawing' || e.button !== 0) return; const canvas = this._drawingCanvasEl; if (!canvas) return; + const tool = this.drawingTool; + if (tool === 'eyedropper') { + e.preventDefault(); + this.drawingSampleColor(e); + return; + } this._drawingCancelAutosave(); canvas.setPointerCapture(e.pointerId); this._drawingPointerId = e.pointerId; const { x, y } = this._drawingCanvasCoords(e); this.drawingIsPointerDown = true; this.drawingRedoStack = []; - const color = this.drawingColor; const lw = this.drawingLineWidth; - const tool = this.drawingTool; - if (tool === 'freehand') { + const color = tool === 'eraser' ? CONFIG.DRAWING_BACKGROUND : this.drawingColor; + if (tool === 'freehand' || tool === 'eraser') { this.drawingDraft = { type: 'stroke', color, lineWidth: lw, points: [[x, y]] }; } else if (tool === 'line') { this.drawingDraft = { type: 'line', color, lineWidth: lw, x1: x, y1: y, x2: x, y2: y }; @@ -3187,7 +3262,7 @@ function noteApp() { }, /** - * Persist the flattened canvas to disk (Ctrl+S, toolbar, autosave). Same feedback as saveNote: + * Persist the flattened canvas to disk (Ctrl+S, autosave). Same feedback as saveNote: * header "Saved" only — never clears stroke undo/redo or reloads the image; stacks reset when * opening another drawing via initDrawingViewer(). */ diff --git a/frontend/index.html b/frontend/index.html index 4cbb8b9..ee2a0eb 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2705,27 +2705,77 @@ -
- - + + + +
+ + + +
@@ -3011,7 +3061,9 @@