foxygit / nfo Log in
commit 9cb7a3fa8123c6eaa4bd024ab56459864762405b
Author:     MrjensK <jens.se@icloud.com>
AuthorDate: Mon Apr 20 20:40:15 2026 +0200
Commit:     MrjensK <jens.se@icloud.com>
CommitDate: Mon Apr 20 20:40:15 2026 +0200

    Implementerar localStorage-persistens med auto-sav
    e.Implementerar localStorage-persistens med auto-save.
---
 index.html | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 95 insertions(+)

diff --git a/index.html b/index.html
index 7d57242..1f901e3 100644
--- a/index.html
+++ b/index.html
@@ -697,6 +697,7 @@ function setCanvasBg(color) {
   document.getElementById('canvas-bg-swatch').style.background = color;
   hideCanvasBgPicker();
   if (ctx) drawAll();
+  scheduleSessionSave();
 }
 document.addEventListener('click', e => {
   if (!document.getElementById('canvas-bg-picker').contains(e.target) &&
@@ -866,6 +867,7 @@ function pushUndo() {
   undoStack.push(cloneMap(sparseMap));
   if (undoStack.length > 50) undoStack.shift();
   redoStack = [];
+  scheduleSessionSave();
 }
 function undo() {
   if (!undoStack.length) return;
@@ -959,6 +961,7 @@ function renderTabs() {
   add.dataset.tabAdd = '1';
   bar.appendChild(add);
   bar.style.top = document.getElementById('topbar').offsetHeight + 'px';
+  scheduleSessionSave();
 }
 function switchTab(idx) {
   if (idx === activeTab) return;
@@ -1104,6 +1107,7 @@ function toggleCenter() {
   isCentered = !isCentered;
   document.getElementById('output-wrap').classList.toggle('centered', isCentered);
   document.getElementById('center-btn').classList.toggle('active', isCentered);
+  scheduleSessionSave();
 }
 function adjFont(delta) {
   const steps = [0.5,0.75,1,1.25,1.5,2,2.5,3];
@@ -1342,6 +1346,95 @@ function doImgConvert() {
   if (!editMode) toggleEdit();
 }

+// ── Session persistence ───────────────────────────────────────────────────────
+function u8ToB64(arr) {
+  let s = '';
+  for (let i = 0; i < arr.length; i++) s += String.fromCharCode(arr[i]);
+  return btoa(s);
+}
+function b64ToU8(s) {
+  const b = atob(s), a = new Uint8Array(b.length);
+  for (let i = 0; i < b.length; i++) a[i] = b.charCodeAt(i);
+  return a;
+}
+
+let _saveTimer = null;
+function scheduleSessionSave() {
+  clearTimeout(_saveTimer);
+  _saveTimer = setTimeout(saveSession, 1000);
+}
+
+function saveSession() {
+  saveCurrentTab();
+  try {
+    const data = {
+      activeTab,
+      tabs: tabs.map(t => ({
+        name: t.name,
+        sparseMap: [...t.sparseMap.entries()],
+        lastBytes: t.lastBytes ? u8ToB64(t.lastBytes) : null,
+        canvasCols: t.canvasCols,
+        canvasRows: t.canvasRows,
+        canvasBg: t.canvasBg,
+        isCentered: t.isCentered,
+      }))
+    };
+    localStorage.setItem('nfoed_session', JSON.stringify(data));
+  } catch(e) {
+    console.warn('Session save failed:', e);
+  }
+}
+
+function loadSession() {
+  try {
+    const raw = localStorage.getItem('nfoed_session');
+    if (!raw) return false;
+    const data = JSON.parse(raw);
+    if (!data.tabs || !data.tabs.length) return false;
+    tabs = data.tabs.map(t => ({
+      name: t.name,
+      sparseMap: new Map(t.sparseMap),
+      lastFile: null,
+      lastBytes: t.lastBytes ? b64ToU8(t.lastBytes) : null,
+      canvasCols: t.canvasCols || 80,
+      canvasRows: t.canvasRows || 25,
+      canvasBg: t.canvasBg || '#000000',
+      isCentered: t.isCentered || false,
+      undoStack: [],
+      redoStack: [],
+    }));
+    activeTab = Math.min(data.activeTab || 0, tabs.length - 1);
+    const t = tabs[activeTab];
+    sparseMap = t.sparseMap;
+    lastFile = null;
+    lastBytes = t.lastBytes;
+    canvasCols = t.canvasCols;
+    canvasRows = t.canvasRows;
+    canvasBg = t.canvasBg;
+    isCentered = t.isCentered;
+    undoStack = []; redoStack = [];
+    document.getElementById('canvas-bg-swatch').style.background = canvasBg;
+    document.getElementById('output-wrap').classList.toggle('centered', isCentered);
+    document.getElementById('center-btn').classList.toggle('active', isCentered);
+    if (sparseMap.size > 0) {
+      showOutput();
+      document.getElementById('filename').textContent = t.name;
+      document.getElementById('s-name').textContent = t.name;
+      document.getElementById('width-wrap').classList.add('visible');
+      const { maxRow, maxCol } = getBounds(sparseMap);
+      document.getElementById('s-dims').textContent = `${maxCol+1}×${maxRow+1}`;
+      document.getElementById('s-type').textContent = lastBytes ? 'restored' : 'new';
+      document.getElementById('s-size').textContent = lastBytes ? fmt(lastBytes.length) : '—';
+      document.fonts.ready.then(() => drawAll());
+    }
+    renderTabs();
+    return true;
+  } catch(e) {
+    console.warn('Session load failed:', e);
+    return false;
+  }
+}
+
 // ── Tab bar delegation ────────────────────────────────────────────────────────
 document.getElementById('tabbar').addEventListener('click', e => {
   const closeBtn = e.target.closest('[data-close-idx]');
@@ -1359,6 +1452,8 @@ dz.addEventListener('drop', e=>{e.preventDefault();dz.classList.remove('over');c
 document.getElementById('file-input').addEventListener('change', function(){if(this.files[0])processFile(this.files[0])});
 document.addEventListener('dragover', e=>e.preventDefault());
 document.addEventListener('drop', e=>{e.preventDefault();const f=e.dataTransfer.files[0];if(f)processFile(f)});
+
+loadSession();
 </script>
 </body>
 </html>