subpath feature

This commit is contained in:
Lucas Winther committed 2026-07-10 10:13:37 +02:00
1 parent 2d668b3fb8
commit 076efc2101
11 files changed
+32 -31

No files matched your search

+1 -1
View File
@@ -120,7 +120,7 @@ export function App() {
// main thread (Architecture.md §5). The worker script is bundled by the
// server at startup and served at a stable URL (see src/index.ts) because
// Bun's bundler does not follow `new Worker(new URL(...))` graphs yet.
const worker = new Worker("/inference.worker.js", { type: "module" });
const worker = new Worker("inference.worker.js", { type: "module" });
worker.addEventListener("message", (event: MessageEvent<unknown>) => {
const message = event.data;
if (!isWorkerToUiMessage(message)) return;
+4 -4
View File
@@ -122,7 +122,7 @@ export function login(
password: string,
fetchFn: FetchLike = defaultFetch,
): Promise<AuthUser> {
return requestUser(fetchFn, "/api/auth/login", { username, password });
return requestUser(fetchFn, "api/auth/login", { username, password });
}
/** POST /api/auth/register → user (auto signed in), or throws (400/409). */
@@ -131,7 +131,7 @@ export function register(
password: string,
fetchFn: FetchLike = defaultFetch,
): Promise<AuthUser> {
return requestUser(fetchFn, "/api/auth/register", { username, password });
return requestUser(fetchFn, "api/auth/register", { username, password });
}
/** Result of the startup /me probe (fetchAuthStatus). */
@@ -158,7 +158,7 @@ export async function fetchAuthStatus(
fetchFn: FetchLike = defaultFetch,
): Promise<AuthStatus> {
try {
const response = await fetchFn("/api/auth/me", {
const response = await fetchFn("api/auth/me", {
method: "GET",
credentials: "same-origin",
});
@@ -179,7 +179,7 @@ export async function fetchAuthStatus(
*/
export async function logout(fetchFn: FetchLike = defaultFetch): Promise<void> {
try {
await fetchFn("/api/auth/logout", {
await fetchFn("api/auth/logout", {
method: "POST",
credentials: "same-origin",
});
+2 -2
View File
@@ -263,7 +263,7 @@ export class ServerEngine implements Engine {
let response: Response;
try {
response = await this.fetchFn(`/api/transcribe?${params}`, {
response = await this.fetchFn(`api/transcribe?${params}`, {
method: "POST",
credentials: "same-origin",
headers: { "Content-Type": "application/octet-stream" },
@@ -327,7 +327,7 @@ export async function fetchServerEngineStatus(
fetchFn: FetchLike = defaultFetch,
): Promise<ServerEngineStatus> {
try {
const response = await fetchFn("/api/transcribe/status", {
const response = await fetchFn("api/transcribe/status", {
method: "GET",
credentials: "same-origin",
});
+5 -5
View File
@@ -2,21 +2,21 @@
"name": "Whisper Web — on-device speech-to-text",
"short_name": "Whisper Web",
"description": "Speech-to-text that runs entirely in your browser via OpenAI Whisper. Works offline once installed with a downloaded model.",
"id": "/",
"start_url": "/",
"scope": "/",
"id": "./",
"start_url": "./",
"scope": "./",
"display": "standalone",
"theme_color": "#16181d",
"background_color": "#16181d",
"icons": [
{
"src": "/icons/icon-192.png",
"src": "icons/icon-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "/icons/icon-512.png",
"src": "icons/icon-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
+1 -1
View File
@@ -27,7 +27,7 @@ export function precachePathsFromOutputs(
for (const outputPath of outputPaths) {
const normalized = outputPath.replaceAll("\\", "/").replace(/^\.?\//, "");
if (normalized.endsWith(".map")) continue;
paths.add(normalized === "index.html" ? "/" : `/${normalized}`);
paths.add(normalized === "index.html" ? "./" : normalized);
}
for (const extra of extraPaths) paths.add(extra);
return [...paths].sort();
+1 -1
View File
@@ -31,7 +31,7 @@ export function registerServiceWorker(options: RegisterOptions): void {
if (container.controller) options.onControlled?.();
container
.register("/sw.js")
.register("sw.js")
.then((registration) => {
const notify = (worker: ServiceWorker) => {
options.onUpdateReady(() => {
+5 -4
View File
@@ -18,6 +18,7 @@
// In dev src/index.ts injects an empty list + "dev"; in production
// scripts/build.ts injects the hashed dist asset list + a content hash.
const PRECACHE: string[] = JSON.parse("__PRECACHE_MANIFEST__") as string[];
const PRECACHE_URLS = PRECACHE.map(path => new URL(path, sw.location.href).pathname);
const VERSION = "__CACHE_VERSION__";
const SHELL_CACHE = `whisper-shell-${VERSION}`;
@@ -118,19 +119,19 @@ sw.addEventListener("fetch", (event) => {
if (url.origin !== sw.location.origin) return;
// API is network-only (D-007) — auth and the server engine must never be
// served stale from cache.
if (url.pathname.startsWith("/api/")) return;
if (url.pathname.includes("/api/")) return;
if (url.pathname.startsWith("/ort/")) {
if (url.pathname.includes("/ort/")) {
event.respondWith(cacheFirst(RUNTIME_CACHE, request));
return;
}
if (PRECACHE.includes(url.pathname)) {
if (PRECACHE_URLS.includes(url.pathname)) {
event.respondWith(cacheFirst(SHELL_CACHE, request));
return;
}
// Anything else (dev's unhashed shell, navigations to deep links):
// network-first with cache fallback; navigations fall back to the shell.
event.respondWith(networkFirst(request, request.mode === "navigate" ? "/" : null));
event.respondWith(networkFirst(request, request.mode === "navigate" ? "./" : null));
});
export {};
+1 -1
View File
@@ -31,7 +31,7 @@ import {
// them at /ort/* (src/index.ts; copied into dist/ort by scripts/build.ts)
// and the service worker runtime-caches them cache-first (src/pwa/sw.ts).
if (env.backends.onnx.wasm) {
env.backends.onnx.wasm.wasmPaths = "/ort/";
env.backends.onnx.wasm.wasmPaths = "ort/";
}
// `lib: ["DOM"]` types `self` as Window; give it the worker-scope shape we use.