Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| f08d5d7563 |
+14
@@ -73,6 +73,9 @@
|
||||
<button class="widget-toolbar-btn" data-action="timer" title="Timer">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="13" r="8"/><path d="M12 9v4l2 2"/><path d="M5 3l2 2"/><path d="M19 3l-2 2"/><line x1="12" y1="1" x2="12" y2="3"/></svg>
|
||||
</button>
|
||||
<button class="widget-toolbar-btn hidden" data-action="image-ref" title="Bild-Referenz">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><circle cx="8.5" cy="8.5" r="1.5"/><path d="M21 15l-5-5L5 21"/></svg>
|
||||
</button>
|
||||
<button class="widget-toolbar-btn" data-action="notebook" title="Alle Notes">
|
||||
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 19.5A2.5 2.5 0 016.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 014 19.5v-15A2.5 2.5 0 016.5 2z"/></svg>
|
||||
</button>
|
||||
@@ -197,6 +200,16 @@
|
||||
<option value="left">Links</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="setting-row">
|
||||
<div class="setting-info">
|
||||
<span class="setting-label">Bild-Referenz Widgets</span>
|
||||
<span class="setting-desc">Bilder als Referenz anzeigen (nur aktuelle Session)</span>
|
||||
</div>
|
||||
<label class="toggle">
|
||||
<input type="checkbox" id="settingImageRef">
|
||||
<span class="slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
@@ -483,6 +496,7 @@
|
||||
<script src="src/js/notes.js"></script>
|
||||
<script src="src/js/calculator.js"></script>
|
||||
<script src="src/js/timer.js"></script>
|
||||
<script src="src/js/image-ref.js"></script>
|
||||
<script src="src/js/data.js"></script>
|
||||
<!-- Onboarding -->
|
||||
<script src="src/js/onboarding.js"></script>
|
||||
|
||||
@@ -1364,6 +1364,91 @@ body.show-desc .bm-desc { display: block; }
|
||||
color: var(--bg-primary);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
IMAGE REFERENCE WIDGET
|
||||
============================================ */
|
||||
.imgref-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
padding: 8px;
|
||||
}
|
||||
.imgref-img-wrapper {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
border-radius: var(--radius-sm);
|
||||
min-height: 80px;
|
||||
}
|
||||
.imgref-img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
object-fit: contain;
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
.imgref-dropzone {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 2px dashed var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-muted);
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s, color 0.2s;
|
||||
min-height: 80px;
|
||||
gap: 8px;
|
||||
}
|
||||
.imgref-dropzone:hover,
|
||||
.imgref-dropzone.dragover {
|
||||
border-color: var(--accent);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
.imgref-dropzone-icon {
|
||||
font-size: 24px;
|
||||
opacity: 0.5;
|
||||
}
|
||||
.imgref-label {
|
||||
width: 100%;
|
||||
margin-top: 8px;
|
||||
font-size: 11px;
|
||||
background: transparent;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-secondary);
|
||||
padding: 4px 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.imgref-label:focus {
|
||||
border-color: var(--accent);
|
||||
outline: none;
|
||||
}
|
||||
.imgref-label::placeholder {
|
||||
color: var(--text-muted);
|
||||
}
|
||||
.imgref-replace-btn {
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
margin-top: 6px;
|
||||
font-size: 10px;
|
||||
background: transparent;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: var(--radius-sm);
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
flex-shrink: 0;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.imgref-replace-btn:hover {
|
||||
border-color: var(--accent);
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
/* ============================================
|
||||
WIDGET TOOLBAR
|
||||
============================================ */
|
||||
|
||||
@@ -20,6 +20,7 @@ async function init() {
|
||||
await Notes.init();
|
||||
await Calculator.init();
|
||||
await Timer.init();
|
||||
await ImageRef.init();
|
||||
initDataButtons();
|
||||
Store.checkQuota();
|
||||
|
||||
|
||||
@@ -0,0 +1,500 @@
|
||||
/* =============================================
|
||||
HELLION NEWTAB — image-ref.js
|
||||
Bild-Referenz Widget: Session-only Bildanzeige
|
||||
mit Canvas API WebP-Konvertierung
|
||||
============================================= */
|
||||
|
||||
const ImageRef = {
|
||||
MAX_IMAGES: 3,
|
||||
STORAGE_KEY: 'widgetStates',
|
||||
SESSION_KEY: 'imageRefData',
|
||||
|
||||
/** @type {Array<{id: string, label: string, x: number, y: number, width: number, height: number, open: boolean}>} */
|
||||
_images: [],
|
||||
_saveTimer: null,
|
||||
|
||||
// ---- STORAGE (persistent: Position/Meta) ----
|
||||
|
||||
/**
|
||||
* Widget-Meta aus persistentem Storage laden
|
||||
*/
|
||||
async load() {
|
||||
const data = await Store.get(this.STORAGE_KEY);
|
||||
if (data && data.imageRef && Array.isArray(data.imageRef.images)) {
|
||||
this._images = data.imageRef.images;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Widget-Meta persistent speichern
|
||||
* Bestehende Notes, Calculator, Timer bleiben erhalten
|
||||
*/
|
||||
async save() {
|
||||
const data = await Store.get(this.STORAGE_KEY) || {};
|
||||
if (data.notes === undefined) data.notes = [];
|
||||
|
||||
// Positionen aus WidgetManager aktualisieren
|
||||
const updated = this._images.map(img => {
|
||||
const ws = WidgetManager.getState(img.id);
|
||||
if (ws) {
|
||||
img.x = ws.x;
|
||||
img.y = ws.y;
|
||||
img.width = ws.width;
|
||||
img.height = ws.height;
|
||||
img.open = ws.open;
|
||||
}
|
||||
return img;
|
||||
});
|
||||
|
||||
data.imageRef = { images: updated };
|
||||
await Store.set(this.STORAGE_KEY, data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Debounced Save
|
||||
*/
|
||||
_debouncedSave() {
|
||||
clearTimeout(this._saveTimer);
|
||||
this._saveTimer = setTimeout(() => this.save(), 500);
|
||||
},
|
||||
|
||||
// ---- SESSION STORAGE (Bilddaten) ----
|
||||
|
||||
/**
|
||||
* Bilddaten in sessionStorage speichern
|
||||
*/
|
||||
_saveSession() {
|
||||
try {
|
||||
const sessionData = {};
|
||||
this._images.forEach(img => {
|
||||
const dataUrl = this._getSessionImage(img.id);
|
||||
if (dataUrl) sessionData[img.id] = dataUrl;
|
||||
});
|
||||
sessionStorage.setItem(this.SESSION_KEY, JSON.stringify(sessionData));
|
||||
} catch (e) {
|
||||
console.warn('ImageRef: sessionStorage Write fehlgeschlagen', e);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Bilddaten aus sessionStorage laden
|
||||
* @returns {Object} - { id: dataUrl, ... }
|
||||
*/
|
||||
_loadSessionAll() {
|
||||
try {
|
||||
const raw = sessionStorage.getItem(this.SESSION_KEY);
|
||||
if (!raw) return {};
|
||||
return JSON.parse(raw);
|
||||
} catch (e) {
|
||||
console.warn('ImageRef: sessionStorage Read fehlgeschlagen', e);
|
||||
return {};
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Einzelnes Bild aus sessionStorage lesen
|
||||
* @param {string} id
|
||||
* @returns {string|null}
|
||||
*/
|
||||
_getSessionImage(id) {
|
||||
const all = this._loadSessionAll();
|
||||
return all[id] || null;
|
||||
},
|
||||
|
||||
/**
|
||||
* Einzelnes Bild in sessionStorage setzen
|
||||
* @param {string} id
|
||||
* @param {string} dataUrl
|
||||
*/
|
||||
_setSessionImage(id, dataUrl) {
|
||||
try {
|
||||
const all = this._loadSessionAll();
|
||||
all[id] = dataUrl;
|
||||
sessionStorage.setItem(this.SESSION_KEY, JSON.stringify(all));
|
||||
} catch (e) {
|
||||
console.warn('ImageRef: sessionStorage Write fehlgeschlagen', e);
|
||||
HellionDialog.alert(
|
||||
'Bild konnte nicht gespeichert werden. Der Browser-Speicher ist voll.',
|
||||
{ type: 'danger', title: 'Speicherfehler' }
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Einzelnes Bild aus sessionStorage entfernen
|
||||
* @param {string} id
|
||||
*/
|
||||
_removeSessionImage(id) {
|
||||
try {
|
||||
const all = this._loadSessionAll();
|
||||
delete all[id];
|
||||
sessionStorage.setItem(this.SESSION_KEY, JSON.stringify(all));
|
||||
} catch (e) {
|
||||
console.warn('ImageRef: sessionStorage Remove fehlgeschlagen', e);
|
||||
}
|
||||
},
|
||||
|
||||
// ---- WIDGET LIFECYCLE ----
|
||||
|
||||
/**
|
||||
* Neues Bild-Widget erstellen (oeffnet File-Dialog)
|
||||
*/
|
||||
async create() {
|
||||
if (!settings.imageRefEnabled) return;
|
||||
|
||||
if (this._images.length >= this.MAX_IMAGES) {
|
||||
await HellionDialog.alert(
|
||||
'Maximal ' + this.MAX_IMAGES + ' Bild-Widgets gleichzeitig. Schliesse eines um ein neues zu oeffnen.',
|
||||
{ type: 'warning', title: 'Limit erreicht' }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Freie ID finden
|
||||
const usedIds = new Set(this._images.map(i => i.id));
|
||||
let slotId = null;
|
||||
for (let i = 0; i < this.MAX_IMAGES; i++) {
|
||||
const candidate = 'image_' + i;
|
||||
if (!usedIds.has(candidate)) {
|
||||
slotId = candidate;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!slotId) return;
|
||||
|
||||
// File-Dialog
|
||||
const file = await this._pickFile();
|
||||
if (!file) return;
|
||||
|
||||
// Bild verarbeiten
|
||||
let dataUrl;
|
||||
try {
|
||||
dataUrl = await this._processFile(file);
|
||||
} catch (err) {
|
||||
await HellionDialog.alert(
|
||||
'Bild konnte nicht geladen werden: ' + err.message,
|
||||
{ type: 'danger', title: 'Bildfehler' }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// In sessionStorage speichern
|
||||
this._setSessionImage(slotId, dataUrl);
|
||||
|
||||
// Meta erstellen
|
||||
const imageData = {
|
||||
id: slotId,
|
||||
label: '',
|
||||
x: 200 + (this._images.length * 40),
|
||||
y: 120 + (this._images.length * 30),
|
||||
width: 320,
|
||||
height: 280,
|
||||
open: true
|
||||
};
|
||||
this._images.push(imageData);
|
||||
|
||||
// Widget erstellen
|
||||
this._createWidget(imageData, dataUrl);
|
||||
await this.save();
|
||||
},
|
||||
|
||||
/**
|
||||
* Widget im DOM erstellen
|
||||
* @param {Object} imageData
|
||||
* @param {string|null} dataUrl
|
||||
*/
|
||||
_createWidget(imageData, dataUrl) {
|
||||
WidgetManager.create('image', {
|
||||
id: imageData.id,
|
||||
title: imageData.label || 'Bild-Referenz',
|
||||
x: imageData.x,
|
||||
y: imageData.y,
|
||||
width: imageData.width,
|
||||
height: imageData.height,
|
||||
open: imageData.open !== false
|
||||
});
|
||||
|
||||
const body = WidgetManager.getBody(imageData.id);
|
||||
if (body) this.renderBody(imageData, body, dataUrl);
|
||||
},
|
||||
|
||||
/**
|
||||
* Widget geschlossen — Daten aufraeumen
|
||||
* @param {string} id
|
||||
*/
|
||||
async onClose(id) {
|
||||
this._removeSessionImage(id);
|
||||
this._images = this._images.filter(img => img.id !== id);
|
||||
await this.save();
|
||||
},
|
||||
|
||||
// ---- UI RENDERING ----
|
||||
|
||||
/**
|
||||
* Widget-Body rendern
|
||||
* @param {Object} imageData
|
||||
* @param {HTMLElement} bodyEl
|
||||
* @param {string|null} dataUrl
|
||||
*/
|
||||
renderBody(imageData, bodyEl, dataUrl) {
|
||||
bodyEl.textContent = '';
|
||||
const container = document.createElement('div');
|
||||
container.className = 'imgref-container';
|
||||
|
||||
if (dataUrl) {
|
||||
// Bild anzeigen
|
||||
const wrapper = document.createElement('div');
|
||||
wrapper.className = 'imgref-img-wrapper';
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.className = 'imgref-img';
|
||||
img.src = dataUrl;
|
||||
img.alt = imageData.label || 'Bild-Referenz';
|
||||
wrapper.appendChild(img);
|
||||
|
||||
// Bild ersetzen Button
|
||||
const replaceBtn = document.createElement('button');
|
||||
replaceBtn.className = 'imgref-replace-btn';
|
||||
replaceBtn.type = 'button';
|
||||
replaceBtn.textContent = 'Bild ersetzen';
|
||||
replaceBtn.addEventListener('click', async () => {
|
||||
const file = await this._pickFile();
|
||||
if (!file) return;
|
||||
try {
|
||||
const newDataUrl = await this._processFile(file);
|
||||
this._setSessionImage(imageData.id, newDataUrl);
|
||||
this.renderBody(imageData, bodyEl, newDataUrl);
|
||||
} catch (err) {
|
||||
await HellionDialog.alert(
|
||||
'Bild konnte nicht geladen werden: ' + err.message,
|
||||
{ type: 'danger', title: 'Bildfehler' }
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
container.append(wrapper, replaceBtn);
|
||||
} else {
|
||||
// Drop-Zone (kein Bild vorhanden)
|
||||
const dropzone = this._createDropzone(imageData, bodyEl);
|
||||
container.appendChild(dropzone);
|
||||
}
|
||||
|
||||
// Label-Input
|
||||
const label = document.createElement('input');
|
||||
label.className = 'imgref-label';
|
||||
label.type = 'text';
|
||||
label.placeholder = 'Beschriftung (optional)';
|
||||
label.maxLength = 100;
|
||||
label.value = imageData.label || '';
|
||||
|
||||
label.addEventListener('input', () => {
|
||||
const text = label.value.trim().slice(0, 100);
|
||||
imageData.label = text;
|
||||
|
||||
// Widget-Titel aktualisieren
|
||||
const entry = WidgetManager._widgets.get(imageData.id);
|
||||
if (entry) {
|
||||
const titleEl = entry.el.querySelector('.widget-title-text');
|
||||
if (titleEl) titleEl.textContent = text || 'Bild-Referenz';
|
||||
entry.state.title = text || 'Bild-Referenz';
|
||||
}
|
||||
|
||||
this._debouncedSave();
|
||||
});
|
||||
|
||||
container.appendChild(label);
|
||||
bodyEl.appendChild(container);
|
||||
},
|
||||
|
||||
/**
|
||||
* Drop-Zone erstellen (fuer leere Widgets / neue Bilder)
|
||||
* @param {Object} imageData
|
||||
* @param {HTMLElement} bodyEl
|
||||
* @returns {HTMLElement}
|
||||
*/
|
||||
_createDropzone(imageData, bodyEl) {
|
||||
const dropzone = document.createElement('div');
|
||||
dropzone.className = 'imgref-dropzone';
|
||||
|
||||
const icon = document.createElement('div');
|
||||
icon.className = 'imgref-dropzone-icon';
|
||||
icon.textContent = '\uD83D\uDDBC\uFE0F';
|
||||
|
||||
const text = document.createElement('span');
|
||||
text.textContent = 'Klicken oder Bild hierher ziehen';
|
||||
|
||||
dropzone.append(icon, text);
|
||||
|
||||
// Klick -> File-Dialog
|
||||
dropzone.addEventListener('click', async () => {
|
||||
const file = await this._pickFile();
|
||||
if (!file) return;
|
||||
try {
|
||||
const dataUrl = await this._processFile(file);
|
||||
this._setSessionImage(imageData.id, dataUrl);
|
||||
this.renderBody(imageData, bodyEl, dataUrl);
|
||||
await this.save();
|
||||
} catch (err) {
|
||||
await HellionDialog.alert(
|
||||
'Bild konnte nicht geladen werden: ' + err.message,
|
||||
{ type: 'danger', title: 'Bildfehler' }
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Drag & Drop
|
||||
dropzone.addEventListener('dragover', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
dropzone.classList.add('dragover');
|
||||
});
|
||||
|
||||
dropzone.addEventListener('dragleave', (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
dropzone.classList.remove('dragover');
|
||||
});
|
||||
|
||||
dropzone.addEventListener('drop', async (e) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
dropzone.classList.remove('dragover');
|
||||
|
||||
const file = e.dataTransfer.files[0];
|
||||
if (!file || !file.type.startsWith('image/')) {
|
||||
await HellionDialog.alert(
|
||||
'Bitte eine Bilddatei verwenden (PNG, JPG, WebP, etc.).',
|
||||
{ type: 'warning', title: 'Kein Bild' }
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const dataUrl = await this._processFile(file);
|
||||
this._setSessionImage(imageData.id, dataUrl);
|
||||
this.renderBody(imageData, bodyEl, dataUrl);
|
||||
await this.save();
|
||||
} catch (err) {
|
||||
await HellionDialog.alert(
|
||||
'Bild konnte nicht geladen werden: ' + err.message,
|
||||
{ type: 'danger', title: 'Bildfehler' }
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return dropzone;
|
||||
},
|
||||
|
||||
// ---- FILE HANDLING ----
|
||||
|
||||
/**
|
||||
* File-Dialog oeffnen
|
||||
* @returns {Promise<File|null>}
|
||||
*/
|
||||
_pickFile() {
|
||||
return new Promise(resolve => {
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = 'image/*';
|
||||
input.addEventListener('change', () => {
|
||||
resolve(input.files[0] || null);
|
||||
});
|
||||
// Cancel erkennen
|
||||
input.addEventListener('cancel', () => resolve(null));
|
||||
input.click();
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Bild per Canvas API zu WebP konvertieren
|
||||
* @param {File} file
|
||||
* @returns {Promise<string>} WebP DataURL
|
||||
*/
|
||||
_processFile(file) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const objectUrl = URL.createObjectURL(file);
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
try {
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0);
|
||||
const webpUrl = canvas.toDataURL('image/webp', 0.85);
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
resolve(webpUrl);
|
||||
} catch (err) {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
|
||||
img.onerror = () => {
|
||||
URL.revokeObjectURL(objectUrl);
|
||||
reject(new Error('Bild konnte nicht geladen werden'));
|
||||
};
|
||||
|
||||
img.src = objectUrl;
|
||||
});
|
||||
},
|
||||
|
||||
// ---- INIT ----
|
||||
|
||||
/**
|
||||
* ImageRef initialisieren (aus app.js aufgerufen)
|
||||
*/
|
||||
async init() {
|
||||
await this.load();
|
||||
|
||||
// Widgets wiederherstellen (nur wenn Feature aktiviert)
|
||||
if (settings.imageRefEnabled && this._images.length > 0) {
|
||||
const sessionData = this._loadSessionAll();
|
||||
|
||||
this._images.forEach(imageData => {
|
||||
if (imageData.open !== false) {
|
||||
const dataUrl = sessionData[imageData.id] || null;
|
||||
this._createWidget(imageData, dataUrl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Close-Event abfangen
|
||||
const self = this;
|
||||
const prevClose = WidgetManager.close;
|
||||
WidgetManager.close = function(id) {
|
||||
prevClose.call(WidgetManager, id);
|
||||
// Pruefen ob es ein Image-Widget ist
|
||||
const isImage = self._images.some(img => img.id === id);
|
||||
if (isImage) {
|
||||
self.onClose(id);
|
||||
}
|
||||
};
|
||||
|
||||
// Minimize-Event abfangen
|
||||
const prevMinimize = WidgetManager.minimize;
|
||||
WidgetManager.minimize = async function(id) {
|
||||
await prevMinimize.call(WidgetManager, id);
|
||||
const isImage = self._images.some(img => img.id === id);
|
||||
if (isImage) {
|
||||
await self.save();
|
||||
}
|
||||
};
|
||||
|
||||
// Open-Event abfangen
|
||||
const prevOpen = WidgetManager.openWidget;
|
||||
WidgetManager.openWidget = async function(id) {
|
||||
await prevOpen.call(WidgetManager, id);
|
||||
const imgData = self._images.find(img => img.id === id);
|
||||
if (imgData) {
|
||||
const body = WidgetManager.getBody(id);
|
||||
if (body && body.children.length === 0) {
|
||||
const dataUrl = self._getSessionImage(id);
|
||||
self.renderBody(imgData, body, dataUrl);
|
||||
}
|
||||
await self.save();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
@@ -53,6 +53,9 @@ const Notes = {
|
||||
if (existing && existing.timer) {
|
||||
saveData.timer = existing.timer;
|
||||
}
|
||||
if (existing && existing.imageRef) {
|
||||
saveData.imageRef = existing.imageRef;
|
||||
}
|
||||
await Store.set(this.STORAGE_KEY, saveData);
|
||||
},
|
||||
|
||||
@@ -527,6 +530,8 @@ const Notes = {
|
||||
Calculator.toggle();
|
||||
} else if (action === 'timer') {
|
||||
Timer.toggle();
|
||||
} else if (action === 'image-ref') {
|
||||
ImageRef.create();
|
||||
} else if (action === 'notebook') {
|
||||
this.openNotebook();
|
||||
}
|
||||
|
||||
+14
-1
@@ -71,6 +71,13 @@ function applySettings() {
|
||||
const showSearchEl = document.getElementById('settingShowSearch');
|
||||
if (showSearchEl) showSearchEl.checked = settings.showSearch;
|
||||
|
||||
// Image-Ref Toggle
|
||||
if (settings.imageRefEnabled === undefined) settings.imageRefEnabled = false;
|
||||
const imgRefCheckbox = document.getElementById('settingImageRef');
|
||||
if (imgRefCheckbox) imgRefCheckbox.checked = settings.imageRefEnabled;
|
||||
const imgRefBtn = document.querySelector('[data-action="image-ref"]');
|
||||
if (imgRefBtn) imgRefBtn.classList.toggle('hidden', !settings.imageRefEnabled);
|
||||
|
||||
// Toolbar-Position
|
||||
document.body.classList.toggle('toolbar-left', settings.toolbarPos === 'left');
|
||||
const toolbarPosEl = document.getElementById('settingToolbarPos');
|
||||
@@ -127,6 +134,11 @@ function bindSettingsEvents() {
|
||||
settingShowSearch: v => {
|
||||
settings.showSearch = v;
|
||||
document.getElementById('searchBarWrapper').classList.toggle('hidden', !v);
|
||||
},
|
||||
settingImageRef: v => {
|
||||
settings.imageRefEnabled = v;
|
||||
const imgBtn = document.querySelector('[data-action="image-ref"]');
|
||||
if (imgBtn) imgBtn.classList.toggle('hidden', !v);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -204,7 +216,8 @@ function bindSettingsEvents() {
|
||||
boards = [];
|
||||
settings = { compact: false, shortenTitles: false, newTab: true, showDesc: false,
|
||||
hideExtra: false, visibleCount: 10, bgUrl: '', theme: 'nebula',
|
||||
showSearch: true, searchEngine: 'google', toolbarPos: 'right' };
|
||||
showSearch: true, searchEngine: 'google', toolbarPos: 'right',
|
||||
imageRefEnabled: false };
|
||||
await saveBoards();
|
||||
await saveSettings();
|
||||
applySettings();
|
||||
|
||||
+2
-1
@@ -16,7 +16,8 @@ let settings = {
|
||||
theme: 'nebula',
|
||||
showSearch: true,
|
||||
searchEngine: 'google',
|
||||
toolbarPos: 'right'
|
||||
toolbarPos: 'right',
|
||||
imageRefEnabled: false
|
||||
};
|
||||
|
||||
function uid() {
|
||||
|
||||
Reference in New Issue
Block a user