about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorelioat <elioat@tilde.institute>2025-03-30 10:13:06 -0400
committerelioat <elioat@tilde.institute>2025-03-30 10:13:06 -0400
commit1c8d453f75411144c724f39c73a75f7bcdd2efe7 (patch)
treec71933385408ad190d89b8f361f0bd38c789a624
parent47a0aa9cc904fd5aca73ad589df1acd56d406682 (diff)
downloadtour-1c8d453f75411144c724f39c73a75f7bcdd2efe7.tar.gz
*
-rw-r--r--js/leibovitz/index.html29
-rw-r--r--js/leibovitz/leibovitz.js84
-rw-r--r--js/leibovitz/manifest.json39
-rw-r--r--js/leibovitz/service-worker.js81
4 files changed, 215 insertions, 18 deletions
diff --git a/js/leibovitz/index.html b/js/leibovitz/index.html
index 986485c..7a5ca55 100644
--- a/js/leibovitz/index.html
+++ b/js/leibovitz/index.html
@@ -218,6 +218,19 @@
             padding: 10px;
             box-shadow: 0 -2px 4px rgba(0, 0, 0, 0.1);
             flex-shrink: 0;
+            position: relative;
+        }
+        #offline-status {
+            position: absolute;
+            top: 10px;
+            right: 10px;
+            font-size: 12px;
+            color: #666;
+            display: none;
+            font-family: 'ChicagoFLF', sans-serif;
+        }
+        #offline-status.visible {
+            display: block;
         }
         .top-controls {
             display: flex;
@@ -423,6 +436,7 @@
 </div>
 
 <div id="settings-container">
+    <div id="offline-status">Offline Mode</div>
     <div class="top-controls">
         <select id="dither-select">
             <option value="none">No Dithering</option>
@@ -451,5 +465,20 @@
 <script src="blur.js"></script>
 <script src="balance.js"></script>
 <script src="leibovitz.js"></script>
+<script>
+// Add offline status handling
+window.addEventListener('online', () => {
+    document.getElementById('offline-status').classList.remove('visible');
+});
+
+window.addEventListener('offline', () => {
+    document.getElementById('offline-status').classList.add('visible');
+});
+
+// Check initial online status
+if (!navigator.onLine) {
+    document.getElementById('offline-status').classList.add('visible');
+}
+</script>
 </body>
 </html>
diff --git a/js/leibovitz/leibovitz.js b/js/leibovitz/leibovitz.js
index cdf9ca4..45821ba 100644
--- a/js/leibovitz/leibovitz.js
+++ b/js/leibovitz/leibovitz.js
@@ -9,6 +9,7 @@ const focusControl = document.getElementById('focus-control');
 const focusSlider = document.getElementById('focus-slider');
 const focusValue = document.getElementById('focus-value');
 const slideControls = document.querySelector('.slide-controls');
+const SETTINGS_KEY = 'leibovitz-settings';
 
 let cameraOn = false;
 let stream = null;
@@ -418,4 +419,85 @@ DitherManager.reset = function() {
     this._modeSelect.value = 'none';
     this._notifyObservers();
     resetEffects();
-};
\ No newline at end of file
+};
+
+function saveSettings() {
+    const settings = {
+        blur: BlurManager._currentBlur,
+        contrast: ContrastManager._currentContrast,
+        color: ColorManager._currentColor,
+        balance: BalanceManager._currentBalance,
+        dither: DitherManager._currentMode,
+        blockSize: DitherManager._currentBlockSize
+    };
+    localStorage.setItem(SETTINGS_KEY, JSON.stringify(settings));
+}
+
+function loadSettings() {
+    const savedSettings = localStorage.getItem(SETTINGS_KEY);
+    if (savedSettings) {
+        const settings = JSON.parse(savedSettings);
+        
+        // Apply saved settings
+        if (settings.blur !== undefined) {
+            BlurManager._currentBlur = settings.blur;
+            BlurManager._slider.value = settings.blur;
+            BlurManager._value.textContent = `${settings.blur}%`;
+        }
+        
+        if (settings.contrast !== undefined) {
+            ContrastManager._currentContrast = settings.contrast;
+            ContrastManager._slider.value = settings.contrast;
+            document.getElementById('contrast-value').textContent = settings.contrast;
+        }
+        
+        if (settings.color !== undefined) {
+            ColorManager._currentColor = settings.color;
+            ColorManager._colorInput.value = settings.color;
+        }
+        
+        if (settings.balance !== undefined) {
+            BalanceManager._currentBalance = settings.balance;
+            BalanceManager.balanceSlider.value = settings.balance;
+            BalanceManager.balanceValue.textContent = `${settings.balance}K`;
+        }
+        
+        if (settings.dither !== undefined) {
+            DitherManager._currentMode = settings.dither;
+            DitherManager._modeSelect.value = settings.dither;
+        }
+        
+        if (settings.blockSize !== undefined) {
+            DitherManager._currentBlockSize = settings.blockSize;
+            DitherManager._blockSizeSlider.value = settings.blockSize;
+            document.getElementById('block-size-value').textContent = `${settings.blockSize}px`;
+        }
+    }
+}
+
+// Add event listeners for settings changes
+function setupSettingsListeners() {
+    // Blur
+    BlurManager._slider.addEventListener('change', saveSettings);
+    
+    // Contrast
+    ContrastManager._slider.addEventListener('change', saveSettings);
+    
+    // Color
+    ColorManager._colorInput.addEventListener('change', saveSettings);
+    
+    // Balance
+    BalanceManager.balanceSlider.addEventListener('change', saveSettings);
+    
+    // Dither
+    DitherManager._modeSelect.addEventListener('change', saveSettings);
+    
+    // Block Size
+    if (DitherManager._blockSizeSlider) {
+        DitherManager._blockSizeSlider.addEventListener('change', saveSettings);
+    }
+}
+
+// Call this after all managers are initialized
+setupSettingsListeners();
+loadSettings();
\ No newline at end of file
diff --git a/js/leibovitz/manifest.json b/js/leibovitz/manifest.json
index 4b4a2c4..2232716 100644
--- a/js/leibovitz/manifest.json
+++ b/js/leibovitz/manifest.json
@@ -1,5 +1,12 @@
 {
  "name": "Leibovitz",
+ "short_name": "Leibovitz",
+ "description": "A web-based camera that lets you make fun photos",
+ "start_url": "/",
+ "display": "standalone",
+ "background_color": "#f5f5dc",
+ "theme_color": "#f5f5dc",
+ "orientation": "portrait",
  "icons": [
   {
    "src": "\/android-icon-36x36.png",
@@ -36,6 +43,38 @@
    "sizes": "192x192",
    "type": "image\/png",
    "density": "4.0"
+  },
+  {
+   "src": "\/apple-icon-180x180.png",
+   "sizes": "180x180",
+   "type": "image\/png"
+  }
+ ],
+ "screenshots": [
+  {
+   "src": "\/screenshot1.png",
+   "sizes": "1080x1920",
+   "type": "image\/png",
+   "platform": "narrow",
+   "label": "Leibovitz Camera App"
+  }
+ ],
+ "categories": ["photo", "camera", "art"],
+ "prefer_related_applications": false,
+ "shortcuts": [
+  {
+   "name": "Take Photo",
+   "short_name": "Camera",
+   "description": "Open camera to take a photo",
+   "url": "\/?action=camera",
+   "icons": [{ "src": "\/android-icon-96x96.png", "sizes": "96x96" }]
+  },
+  {
+   "name": "Edit Photo",
+   "short_name": "Edit",
+   "description": "Open photo editor",
+   "url": "\/?action=edit",
+   "icons": [{ "src": "\/android-icon-96x96.png", "sizes": "96x96" }]
   }
  ]
 }
\ No newline at end of file
diff --git a/js/leibovitz/service-worker.js b/js/leibovitz/service-worker.js
index 3ed58c1..5f8ab69 100644
--- a/js/leibovitz/service-worker.js
+++ b/js/leibovitz/service-worker.js
@@ -1,10 +1,18 @@
-const CACHE_NAME = 'lut-cam-cache-v1';
+const CACHE_NAME = 'leibovitz-cache-v1';
 const urlsToCache = [
     '/',
     './',
     'index.html',
-    'lut.js',
+    'leibovitz.js',
+    'blur.js',
+    'contrast.js',
+    'color.js',
+    'balance.js',
+    'dither.js',
     'service-worker.js',
+    'ChicagoFLF.ttf',
+    'manifest.json',
+    // Icons
     'android-icon-192x192.png',
     'android-icon-512x512.png',
     'favicon.ico',
@@ -21,23 +29,62 @@ const urlsToCache = [
     'apple-icon-152x152.png'
 ];
 
+// Install event - cache all necessary files
 self.addEventListener('install', event => {
-  event.waitUntil(
-    caches.open(CACHE_NAME)
-      .then(cache => {
-        return cache.addAll(urlsToCache);
-      })
-  );
+    event.waitUntil(
+        caches.open(CACHE_NAME)
+            .then(cache => {
+                return cache.addAll(urlsToCache);
+            })
+    );
 });
 
+// Activate event - clean up old caches
+self.addEventListener('activate', event => {
+    event.waitUntil(
+        caches.keys().then(cacheNames => {
+            return Promise.all(
+                cacheNames.map(cacheName => {
+                    if (cacheName !== CACHE_NAME) {
+                        return caches.delete(cacheName);
+                    }
+                })
+            );
+        })
+    );
+});
+
+// Fetch event - serve from cache, fallback to network
 self.addEventListener('fetch', event => {
-  event.respondWith(
-    caches.match(event.request)
-      .then(response => {
-        if (response) {
-          return response;
-        }
-        return fetch(event.request);
-      })
-  );
+    event.respondWith(
+        caches.match(event.request)
+            .then(response => {
+                // Return cached response if found
+                if (response) {
+                    return response;
+                }
+                
+                // Clone the request because it can only be used once
+                const fetchRequest = event.request.clone();
+                
+                // Try to fetch from network
+                return fetch(fetchRequest).then(response => {
+                    // Check if we received a valid response
+                    if (!response || response.status !== 200 || response.type !== 'basic') {
+                        return response;
+                    }
+                    
+                    // Clone the response because it can only be used once
+                    const responseToCache = response.clone();
+                    
+                    // Cache the fetched response
+                    caches.open(CACHE_NAME)
+                        .then(cache => {
+                            cache.put(event.request, responseToCache);
+                        });
+                    
+                    return response;
+                });
+            })
+    );
 });
\ No newline at end of file