Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions composeJournalsApp/src/webMain/resources/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
<title>spectacled Journals</title>
<link type="text/css" rel="stylesheet" href="styles.css">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="manifest" href="manifest.webmanifest">
<link rel="apple-touch-icon" href="icon-512.png">
<meta name="theme-color" content="#006896">
</head>
<body style="text-align: center; align-content: center">
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 50 50" role="presentation">
Expand All @@ -17,5 +20,16 @@
</circle>
</svg>
<script type="application/javascript" src="composeJournalsApp.js"></script>
<script>
// Register the offline service worker (see service-worker.js). Guarded so it is a no-op in
// browsers without service-worker support.
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('service-worker.js').catch(function (error) {
console.warn('Service worker registration failed:', error);
});
});
}
</script>
</body>
</html>
20 changes: 20 additions & 0 deletions composeJournalsApp/src/webMain/resources/manifest.webmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "spectacled Journals",
"short_name": "Journals",
"description": "Keep private, standards-based journal entries synced over CalDAV.",
"lang": "en",
"start_url": ".",
"scope": ".",
"display": "standalone",
"orientation": "any",
"background_color": "#ffffff",
"theme_color": "#006896",
"icons": [
{
"src": "icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
}
]
}
63 changes: 63 additions & 0 deletions composeJournalsApp/src/webMain/resources/service-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Spectacled offline service worker.
//
// Strategy: network-first for same-origin GET requests to static app-shell assets, falling back
// to the cache only when the network fails (i.e. offline). Network-first is deliberate:
// - It never serves a stale bundle or a stale sql.js worker while online, so app updates and
// the DAT-6 IndexedDB persistence mechanism always load the freshest files. The cache is
// purely an offline fallback.
// - Dynamic requests are never cached: non-GET methods (CalDAV PROPFIND/REPORT/PUT/POST), any
// cross-origin request (the CalDAV server / proxy), and same-origin requests that aren't a
// navigation or a known static asset extension all pass straight through, untouched.
//
// Bump CACHE_VERSION whenever this file changes to drop the previous cache on activation.

const CACHE_VERSION = 'spectacled-shell-v1';

// Only these same-origin responses are treated as cacheable app-shell assets.
const SHELL_ASSET = /\.(?:js|mjs|css|wasm|html|ico|png|svg|webmanifest|json|woff2?)$/i;

self.addEventListener('install', () => {
// Take over as soon as installed instead of waiting for existing tabs to close.
self.skipWaiting();
});

self.addEventListener('activate', (event) => {
event.waitUntil((async () => {
const keys = await caches.keys();
await Promise.all(keys.filter((k) => k !== CACHE_VERSION).map((k) => caches.delete(k)));
await self.clients.claim();
})());
});

self.addEventListener('fetch', (event) => {
const request = event.request;
const url = new URL(request.url);

// Leave everything that isn't a same-origin GET for a static shell asset (or a navigation) to
// the browser's default handling — crucially, all CalDAV/proxy traffic.
if (request.method !== 'GET' || url.origin !== self.location.origin) return;
const isNavigation = request.mode === 'navigate';
if (!isNavigation && !SHELL_ASSET.test(url.pathname)) return;

event.respondWith((async () => {
const cache = await caches.open(CACHE_VERSION);
try {
const response = await fetch(request);
// Cache a copy of successful same-origin responses for offline use.
if (response && response.ok && response.type === 'basic') {
cache.put(request, response.clone());
}
return response;
} catch (error) {
// Offline: serve the cached copy if we have one.
const cached = await cache.match(request);
if (cached) return cached;
// For a page load with no cached match, fall back to the cached app shell.
if (isNavigation) {
const shell = (await cache.match('index.html')) || (await cache.match('./'));
if (shell) return shell;
}
throw error;
}
})());
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions composeNotesApp/src/webMain/resources/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
<title>spectacled Notes</title>
<link type="text/css" rel="stylesheet" href="styles.css">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="manifest" href="manifest.webmanifest">
<link rel="apple-touch-icon" href="icon-512.png">
<meta name="theme-color" content="#994c2c">
</head>
<body style="text-align: center; align-content: center">
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 50 50" role="presentation">
Expand All @@ -17,5 +20,16 @@
</circle>
</svg>
<script type="application/javascript" src="composeNotesApp.js"></script>
<script>
// Register the offline service worker (see service-worker.js). Guarded so it is a no-op in
// browsers without service-worker support.
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('service-worker.js').catch(function (error) {
console.warn('Service worker registration failed:', error);
});
});
}
</script>
</body>
</html>
20 changes: 20 additions & 0 deletions composeNotesApp/src/webMain/resources/manifest.webmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "spectacled Notes",
"short_name": "Notes",
"description": "Keep private, standards-based notes synced over CalDAV.",
"lang": "en",
"start_url": ".",
"scope": ".",
"display": "standalone",
"orientation": "any",
"background_color": "#ffffff",
"theme_color": "#994c2c",
"icons": [
{
"src": "icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
}
]
}
63 changes: 63 additions & 0 deletions composeNotesApp/src/webMain/resources/service-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Spectacled offline service worker.
//
// Strategy: network-first for same-origin GET requests to static app-shell assets, falling back
// to the cache only when the network fails (i.e. offline). Network-first is deliberate:
// - It never serves a stale bundle or a stale sql.js worker while online, so app updates and
// the DAT-6 IndexedDB persistence mechanism always load the freshest files. The cache is
// purely an offline fallback.
// - Dynamic requests are never cached: non-GET methods (CalDAV PROPFIND/REPORT/PUT/POST), any
// cross-origin request (the CalDAV server / proxy), and same-origin requests that aren't a
// navigation or a known static asset extension all pass straight through, untouched.
//
// Bump CACHE_VERSION whenever this file changes to drop the previous cache on activation.

const CACHE_VERSION = 'spectacled-shell-v1';

// Only these same-origin responses are treated as cacheable app-shell assets.
const SHELL_ASSET = /\.(?:js|mjs|css|wasm|html|ico|png|svg|webmanifest|json|woff2?)$/i;

self.addEventListener('install', () => {
// Take over as soon as installed instead of waiting for existing tabs to close.
self.skipWaiting();
});

self.addEventListener('activate', (event) => {
event.waitUntil((async () => {
const keys = await caches.keys();
await Promise.all(keys.filter((k) => k !== CACHE_VERSION).map((k) => caches.delete(k)));
await self.clients.claim();
})());
});

self.addEventListener('fetch', (event) => {
const request = event.request;
const url = new URL(request.url);

// Leave everything that isn't a same-origin GET for a static shell asset (or a navigation) to
// the browser's default handling — crucially, all CalDAV/proxy traffic.
if (request.method !== 'GET' || url.origin !== self.location.origin) return;
const isNavigation = request.mode === 'navigate';
if (!isNavigation && !SHELL_ASSET.test(url.pathname)) return;

event.respondWith((async () => {
const cache = await caches.open(CACHE_VERSION);
try {
const response = await fetch(request);
// Cache a copy of successful same-origin responses for offline use.
if (response && response.ok && response.type === 'basic') {
cache.put(request, response.clone());
}
return response;
} catch (error) {
// Offline: serve the cached copy if we have one.
const cached = await cache.match(request);
if (cached) return cached;
// For a page load with no cached match, fall back to the cached app shell.
if (isNavigation) {
const shell = (await cache.match('index.html')) || (await cache.match('./'));
if (shell) return shell;
}
throw error;
}
})());
});
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions composeTasksApp/src/webMain/resources/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
<title>spectacled Tasks</title>
<link type="text/css" rel="stylesheet" href="styles.css">
<link rel="icon" type="image/x-icon" href="favicon.ico">
<link rel="manifest" href="manifest.webmanifest">
<link rel="apple-touch-icon" href="icon-512.png">
<meta name="theme-color" content="#296f23">
</head>
<body style="text-align: center; align-content: center">
<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 50 50" role="presentation">
Expand All @@ -17,5 +20,16 @@
</circle>
</svg>
<script type="application/javascript" src="composeTasksApp.js"></script>
<script>
// Register the offline service worker (see service-worker.js). Guarded so it is a no-op in
// browsers without service-worker support.
if ('serviceWorker' in navigator) {
window.addEventListener('load', function () {
navigator.serviceWorker.register('service-worker.js').catch(function (error) {
console.warn('Service worker registration failed:', error);
});
});
}
</script>
</body>
</html>
20 changes: 20 additions & 0 deletions composeTasksApp/src/webMain/resources/manifest.webmanifest
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "spectacled Tasks",
"short_name": "Tasks",
"description": "Keep private, standards-based tasks synced over CalDAV.",
"lang": "en",
"start_url": ".",
"scope": ".",
"display": "standalone",
"orientation": "any",
"background_color": "#ffffff",
"theme_color": "#296f23",
"icons": [
{
"src": "icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
}
]
}
63 changes: 63 additions & 0 deletions composeTasksApp/src/webMain/resources/service-worker.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Spectacled offline service worker.
//
// Strategy: network-first for same-origin GET requests to static app-shell assets, falling back
// to the cache only when the network fails (i.e. offline). Network-first is deliberate:
// - It never serves a stale bundle or a stale sql.js worker while online, so app updates and
// the DAT-6 IndexedDB persistence mechanism always load the freshest files. The cache is
// purely an offline fallback.
// - Dynamic requests are never cached: non-GET methods (CalDAV PROPFIND/REPORT/PUT/POST), any
// cross-origin request (the CalDAV server / proxy), and same-origin requests that aren't a
// navigation or a known static asset extension all pass straight through, untouched.
//
// Bump CACHE_VERSION whenever this file changes to drop the previous cache on activation.

const CACHE_VERSION = 'spectacled-shell-v1';

// Only these same-origin responses are treated as cacheable app-shell assets.
const SHELL_ASSET = /\.(?:js|mjs|css|wasm|html|ico|png|svg|webmanifest|json|woff2?)$/i;

self.addEventListener('install', () => {
// Take over as soon as installed instead of waiting for existing tabs to close.
self.skipWaiting();
});

self.addEventListener('activate', (event) => {
event.waitUntil((async () => {
const keys = await caches.keys();
await Promise.all(keys.filter((k) => k !== CACHE_VERSION).map((k) => caches.delete(k)));
await self.clients.claim();
})());
});

self.addEventListener('fetch', (event) => {
const request = event.request;
const url = new URL(request.url);

// Leave everything that isn't a same-origin GET for a static shell asset (or a navigation) to
// the browser's default handling — crucially, all CalDAV/proxy traffic.
if (request.method !== 'GET' || url.origin !== self.location.origin) return;
const isNavigation = request.mode === 'navigate';
if (!isNavigation && !SHELL_ASSET.test(url.pathname)) return;

event.respondWith((async () => {
const cache = await caches.open(CACHE_VERSION);
try {
const response = await fetch(request);
// Cache a copy of successful same-origin responses for offline use.
if (response && response.ok && response.type === 'basic') {
cache.put(request, response.clone());
}
return response;
} catch (error) {
// Offline: serve the cached copy if we have one.
const cached = await cache.match(request);
if (cached) return cached;
// For a page load with no cached match, fall back to the cached app shell.
if (isNavigation) {
const shell = (await cache.match('index.html')) || (await cache.match('./'));
if (shell) return shell;
}
throw error;
}
})());
});