mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-04 12:32:12 +00:00
feat: make UI installable as a PWA (#1417)
* feat: make UI installable as a PWA Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com> * fix(pwa): address review feedback - Regenerate bun.lock for new PWA deps (Vercel builds with bun). - Switch SW to prompt registration so deploys don't reload tabs mid-run. - Remove orphaned public/manifest.json; app links the generated /manifest.webmanifest. - Drop json from workbox globPatterns to avoid precaching stray JSON. Verified bun build emits sw.js, manifest.webmanifest, and precaches _shell.html. * fix(pwa): bun.lock, prompt SW registration, tighten globPatterns - Regenerate bun.lock for new PWA deps (Vercel builds with bun). - Switch SW to prompt registration so deploys don't reload tabs mid-run. - Drop json from workbox globPatterns to avoid precaching stray JSON. Verified bun build emits sw.js, manifest.webmanifest, and precaches _shell.html. --------- Co-authored-by: open-swe[bot] <215916821+open-swe[bot]@users.noreply.github.com>
This commit is contained in:
parent
9754c2d791
commit
02cfdbda5b
9 changed files with 3821 additions and 117 deletions
1429
ui/bun.lock
1429
ui/bun.lock
File diff suppressed because it is too large
Load diff
|
|
@ -57,7 +57,10 @@
|
||||||
"prettier-plugin-tailwindcss": "^0.7.2",
|
"prettier-plugin-tailwindcss": "^0.7.2",
|
||||||
"typescript": "^5.9.3",
|
"typescript": "^5.9.3",
|
||||||
"vite": "^7.3.1",
|
"vite": "^7.3.1",
|
||||||
|
"vite-plugin-pwa": "^1.3.0",
|
||||||
"vitest": "^4.1.0",
|
"vitest": "^4.1.0",
|
||||||
"web-vitals": "^5.1.0"
|
"web-vitals": "^5.1.0",
|
||||||
|
"workbox-build": "^7.4.1",
|
||||||
|
"workbox-window": "^7.4.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
ui/public/logo512.png
Normal file
BIN
ui/public/logo512.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 72 KiB |
|
|
@ -1,25 +0,0 @@
|
||||||
{
|
|
||||||
"short_name": "TanStack App",
|
|
||||||
"name": "Create TanStack App Sample",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "favicon.ico",
|
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
|
||||||
"type": "image/x-icon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "logo192.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "192x192"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "logo512.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "512x512"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#ffffff"
|
|
||||||
}
|
|
||||||
15
ui/src/client.tsx
Normal file
15
ui/src/client.tsx
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { StartClient } from "@tanstack/react-start/client"
|
||||||
|
import { StrictMode } from "react"
|
||||||
|
import { hydrateRoot } from "react-dom/client"
|
||||||
|
import { registerSW } from "virtual:pwa-register"
|
||||||
|
|
||||||
|
// prompt mode: a new SW installs in the background and takes over on the next
|
||||||
|
// load, so deploys never reload a tab mid-agent-run.
|
||||||
|
registerSW()
|
||||||
|
|
||||||
|
hydrateRoot(
|
||||||
|
document,
|
||||||
|
<StrictMode>
|
||||||
|
<StartClient />
|
||||||
|
</StrictMode>
|
||||||
|
)
|
||||||
|
|
@ -1,22 +1,29 @@
|
||||||
import { HeadContent, Outlet, Scripts, createRootRoute } from "@tanstack/react-router";
|
import {
|
||||||
import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools";
|
HeadContent,
|
||||||
import { TanStackDevtools } from "@tanstack/react-devtools";
|
Outlet,
|
||||||
import { QueryClientProvider } from "@tanstack/react-query";
|
Scripts,
|
||||||
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
|
createRootRoute,
|
||||||
import { useState } from "react";
|
} from "@tanstack/react-router"
|
||||||
|
import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools"
|
||||||
|
import { TanStackDevtools } from "@tanstack/react-devtools"
|
||||||
|
import { QueryClientProvider } from "@tanstack/react-query"
|
||||||
|
import { ReactQueryDevtools } from "@tanstack/react-query-devtools"
|
||||||
|
import { useState } from "react"
|
||||||
|
|
||||||
import appCss from "../styles.css?url";
|
import appCss from "../styles.css?url"
|
||||||
import { makeQueryClient } from "@/lib/query";
|
import { makeQueryClient } from "@/lib/query"
|
||||||
|
|
||||||
export const Route = createRootRoute({
|
export const Route = createRootRoute({
|
||||||
head: () => ({
|
head: () => ({
|
||||||
meta: [
|
meta: [
|
||||||
{ charSet: "utf-8" },
|
{ charSet: "utf-8" },
|
||||||
{ name: "viewport", content: "width=device-width, initial-scale=1" },
|
{ name: "viewport", content: "width=device-width, initial-scale=1" },
|
||||||
|
{ name: "theme-color", content: "#000000" },
|
||||||
{ title: "open-swe" },
|
{ title: "open-swe" },
|
||||||
],
|
],
|
||||||
links: [
|
links: [
|
||||||
{ rel: "stylesheet", href: appCss },
|
{ rel: "stylesheet", href: appCss },
|
||||||
|
{ rel: "manifest", href: "/manifest.webmanifest" },
|
||||||
{ rel: "icon", type: "image/png", href: "/favicon.png" },
|
{ rel: "icon", type: "image/png", href: "/favicon.png" },
|
||||||
{ rel: "apple-touch-icon", href: "/apple-touch-icon.png" },
|
{ rel: "apple-touch-icon", href: "/apple-touch-icon.png" },
|
||||||
],
|
],
|
||||||
|
|
@ -24,14 +31,16 @@ export const Route = createRootRoute({
|
||||||
notFoundComponent: () => (
|
notFoundComponent: () => (
|
||||||
<main className="container mx-auto p-4 pt-16">
|
<main className="container mx-auto p-4 pt-16">
|
||||||
<h1 className="text-2xl font-medium">404</h1>
|
<h1 className="text-2xl font-medium">404</h1>
|
||||||
<p className="text-muted-foreground">The requested page could not be found.</p>
|
<p className="text-muted-foreground">
|
||||||
|
The requested page could not be found.
|
||||||
|
</p>
|
||||||
</main>
|
</main>
|
||||||
),
|
),
|
||||||
shellComponent: RootDocument,
|
shellComponent: RootDocument,
|
||||||
});
|
})
|
||||||
|
|
||||||
function RootDocument({ children }: { children: React.ReactNode }) {
|
function RootDocument({ children }: { children: React.ReactNode }) {
|
||||||
const [queryClient] = useState(() => makeQueryClient());
|
const [queryClient] = useState(() => makeQueryClient())
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
|
|
@ -43,7 +52,10 @@ function RootDocument({ children }: { children: React.ReactNode }) {
|
||||||
<TanStackDevtools
|
<TanStackDevtools
|
||||||
config={{ position: "bottom-right" }}
|
config={{ position: "bottom-right" }}
|
||||||
plugins={[
|
plugins={[
|
||||||
{ name: "Tanstack Router", render: <TanStackRouterDevtoolsPanel /> },
|
{
|
||||||
|
name: "Tanstack Router",
|
||||||
|
render: <TanStackRouterDevtoolsPanel />,
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
<ReactQueryDevtools initialIsOpen={false} />
|
<ReactQueryDevtools initialIsOpen={false} />
|
||||||
|
|
@ -51,5 +63,5 @@ function RootDocument({ children }: { children: React.ReactNode }) {
|
||||||
<Scripts />
|
<Scripts />
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@
|
||||||
"jsx": "react-jsx",
|
"jsx": "react-jsx",
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
"lib": ["ES2022", "DOM", "DOM.Iterable"],
|
||||||
"types": ["vite/client"],
|
"types": ["vite/client", "vite-plugin-pwa/client"],
|
||||||
|
|
||||||
/* Bundler mode */
|
/* Bundler mode */
|
||||||
"moduleResolution": "bundler",
|
"moduleResolution": "bundler",
|
||||||
|
|
|
||||||
|
|
@ -5,17 +5,57 @@ import viteReact from "@vitejs/plugin-react"
|
||||||
import viteTsConfigPaths from "vite-tsconfig-paths"
|
import viteTsConfigPaths from "vite-tsconfig-paths"
|
||||||
import tailwindcss from "@tailwindcss/vite"
|
import tailwindcss from "@tailwindcss/vite"
|
||||||
import { nitro } from "nitro/vite"
|
import { nitro } from "nitro/vite"
|
||||||
|
import { VitePWA } from "vite-plugin-pwa"
|
||||||
|
|
||||||
const config = defineConfig({
|
const config = defineConfig({
|
||||||
plugins: [
|
plugins: [
|
||||||
devtools(),
|
devtools(),
|
||||||
nitro(),
|
nitro(),
|
||||||
// this is the plugin that enables path aliases
|
|
||||||
viteTsConfigPaths({
|
viteTsConfigPaths({
|
||||||
projects: ["./tsconfig.json"],
|
projects: ["./tsconfig.json"],
|
||||||
}),
|
}),
|
||||||
tailwindcss(),
|
tailwindcss(),
|
||||||
tanstackStart({ spa: { enabled: true } }),
|
tanstackStart({ spa: { enabled: true } }),
|
||||||
|
VitePWA({
|
||||||
|
injectRegister: false,
|
||||||
|
registerType: "prompt",
|
||||||
|
outDir: ".output/public",
|
||||||
|
integration: {
|
||||||
|
closeBundleOrder: "pre",
|
||||||
|
},
|
||||||
|
manifest: {
|
||||||
|
id: "/",
|
||||||
|
name: "Open SWE",
|
||||||
|
short_name: "Open SWE",
|
||||||
|
description: "Open-source coding agents for Slack, Linear, and GitHub.",
|
||||||
|
start_url: "/",
|
||||||
|
scope: "/",
|
||||||
|
display: "standalone",
|
||||||
|
theme_color: "#000000",
|
||||||
|
background_color: "#ffffff",
|
||||||
|
icons: [
|
||||||
|
{
|
||||||
|
src: "/favicon.png",
|
||||||
|
sizes: "192x192",
|
||||||
|
type: "image/png",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
src: "/logo512.png",
|
||||||
|
sizes: "512x512",
|
||||||
|
type: "image/png",
|
||||||
|
purpose: "any maskable",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
workbox: {
|
||||||
|
navigateFallback: "/_shell.html",
|
||||||
|
navigateFallbackDenylist: [/^\/dashboard\/api\//, /^\/_serverFn\//],
|
||||||
|
globPatterns: ["**/*.{js,css,png,svg,ico,webmanifest}"],
|
||||||
|
additionalManifestEntries: [
|
||||||
|
{ url: "/_shell.html", revision: new Date().toISOString() },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
}),
|
||||||
viteReact(),
|
viteReact(),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
|
|
||||||
2382
ui/yarn.lock
2382
ui/yarn.lock
File diff suppressed because it is too large
Load diff
Loading…
Add table
Reference in a new issue