Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
724dc1c
Fix 404 on spectacledSqlWorker.js by using an absolute copy path
claude Jul 10, 2026
c9b837a
Serve spectacledSqlWorker.js via Kotlin's own JS/Wasm resources, not …
claude Jul 10, 2026
6c7b05c
Revert webpack.config.d cleanup and update comment for the previous c…
claude Jul 10, 2026
c46fd43
Move spectacledSqlWorker.js into each app's webMain resources, not sh…
claude Jul 10, 2026
fb01729
Update comment for sqljs.worker.js in DatabaseDriverFactory
patrickunterwegs Jul 10, 2026
35b27a6
Fix ICS TEXT unescaping corrupting backslash sequences
claude Jul 11, 2026
e5e9686
Add a real commonTest suite for ICS handling and domain logic (QUA-1)
claude Jul 11, 2026
1e4c3b8
Merge branch 'claude/spectacled-code-review-followup' of https://gith…
patrickunterwegs Jul 12, 2026
ab93d9f
Test review and additional tests
patrickunterwegs Jul 12, 2026
23e9867
Make categories with commas survive both the ICS and DB round trips (…
claude Jul 12, 2026
e9fc0c5
Merge branch 'claude/spectacled-code-review-followup' of https://gith…
patrickunterwegs Jul 12, 2026
f7010cb
Merge branch 'main' into claude/spectacled-code-review-followup
patrickunterwegs Jul 12, 2026
851a19a
Make the SyncCoordinator conflict/sync state machine testable and tes…
claude Jul 12, 2026
bfd0baf
Merge branch 'claude/spectacled-code-review-followup' of https://gith…
patrickunterwegs Jul 15, 2026
2e29d8e
Merge branch 'main' into claude/spectacled-code-review-followup
patrickunterwegs Jul 16, 2026
fcc6790
Route all CalDAV calls through injectable data-source interfaces
claude Jul 16, 2026
fd8644e
Harmonize DAV data-source Koin bindings to singleOf, matching the rep…
claude Jul 16, 2026
7414654
Minor code formatting update, removed comment
patrickunterwegs Jul 16, 2026
2b757d2
Resolved warnings
patrickunterwegs Jul 16, 2026
e40bc9c
variable namings update
patrickunterwegs Jul 16, 2026
8f20664
Add wire-format tests for the low-level DAV parsing functions
claude Jul 16, 2026
3c88b05
Merge branch 'main' into claude/spectacled-code-review-followup
patrickunterwegs Jul 16, 2026
0768569
Load the IANA tz database on web so kotlinx-datetime resolves named z…
claude Jul 16, 2026
549e7a6
Updated yarn.lock files
patrickunterwegs Jul 16, 2026
5a0202b
Actually import @js-joda/timezone in the js/wasmJs test bundles
claude Jul 16, 2026
4c13d61
Load the tz database via a shared expect/actual and guard the serializer
claude Jul 16, 2026
24e1df1
Load the web tz database from web startup instead of a common expect/…
claude Jul 16, 2026
9021925
Silence webpack 'os'/'path' not-found noise in the shared browser tes…
claude Jul 16, 2026
74c9c8d
QUA-12: push IO dispatch into the network + credential data sources
claude Jul 17, 2026
2591cf1
Merge branch 'main' into claude/spectacled-code-review-followup
patrickunterwegs Jul 17, 2026
959d3d7
Merge branch 'claude/spectacled-code-review-followup' of https://gith…
patrickunterwegs Jul 17, 2026
a20a22f
Revert QUA-12 (#52): dispatcher refactor broke credentials on iOS/Des…
claude Jul 17, 2026
622ae30
Merge branch 'claude/spectacled-code-review-followup' of https://gith…
patrickunterwegs Jul 17, 2026
79fa299
Merge branch 'main' into claude/spectacled-code-review-followup
patrickunterwegs Jul 17, 2026
d9c91fd
BLD-6: add web app manifest + offline service worker to the three web…
claude Jul 17, 2026
6296ad2
Merge branch 'claude/spectacled-code-review-followup' of https://gith…
patrickunterwegs Jul 17, 2026
c77e096
Merge branch 'main' into claude/spectacled-code-review-followup
patrickunterwegs Aug 25, 2026
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;
}
})());
});
Loading