diff --git a/esbuild-helper/copy-file-plugin.js b/esbuild-helper/copy-file-plugin.js
new file mode 100644
index 0000000..64b1b27
--- /dev/null
+++ b/esbuild-helper/copy-file-plugin.js
@@ -0,0 +1,16 @@
+import fs from "fs";
+
+const copyFilePlugin = (fileSrcPath, fileDestPAth) => ({
+ name: "copy-file-plugin",
+ setup(build) {
+ build.onEnd(async () => {
+ try {
+ fs.copyFileSync(fileSrcPath, fileDestPAth);
+ } catch (e) {
+ console.error(`Failed to copy file: ${fileSrcPath}`, e);
+ }
+ });
+ },
+});
+
+export default copyFilePlugin;
diff --git a/esbuild.js b/esbuild.js
index 9a596dd..8a959bc 100644
--- a/esbuild.js
+++ b/esbuild.js
@@ -2,6 +2,7 @@ import * as esbuild from "esbuild";
import copyStaticFiles from "esbuild-copy-static-files";
import * as fsExtra from "fs-extra";
import { sentryEsbuildPlugin } from "@sentry/esbuild-plugin";
+import copyFilePlugin from "./esbuild-helper/copy-file-plugin.js";
fsExtra.emptyDirSync("./dist");
@@ -13,6 +14,8 @@ esbuild.build({
sourcemap: true, // Source map needed for sentry to work
mainFields: ["module", "main"],
plugins: [
+ copyFilePlugin("./manifest.json", "./dist/manifest.json"),
+ copyFilePlugin("./sw.js", "./dist/sw.js"),
copyStaticFiles({
src: "./css",
dest: "dist/css",
diff --git a/html/index.html b/html/index.html
index f8f6de9..b397284 100644
--- a/html/index.html
+++ b/html/index.html
@@ -14,6 +14,7 @@
type="image/png"
href="./assets/images/favicon/favicon-48.png"
/>
+
diff --git a/js/editor.js b/js/editor.js
index 3f3e5d8..98752a9 100644
--- a/js/editor.js
+++ b/js/editor.js
@@ -759,6 +759,28 @@ export async function initSentry() {
});
}
+export function registerSW() {
+ try {
+ if ("serviceWorker" in navigator) {
+ window.addEventListener("load", () => {
+ navigator.serviceWorker
+ .register("../sw.js")
+ .then((registration) => {
+ console.log(
+ "Service Worker registered with scope:",
+ registration.scope
+ );
+ })
+ .catch((error) => {
+ console.error("Service Worker registration failed:", error);
+ });
+ });
+ }
+ } catch (error) {
+ console.error("Service Worker registration failed:", error);
+ }
+}
+
export async function init() {
initSentry();
setupDocument();
@@ -770,4 +792,5 @@ export async function init() {
setupListeners();
evaluate(editor.innerText);
updateOutputDisplay(output);
+ registerSW();
}
diff --git a/manifest.json b/manifest.json
new file mode 100644
index 0000000..2a967c6
--- /dev/null
+++ b/manifest.json
@@ -0,0 +1,31 @@
+{
+ "name": "Type to Calculate",
+ "short_name": "Type to Calculate",
+
+ "icons": [
+ {
+ "src": "/assets/images/favicon/favicon-16.png",
+ "sizes": "16x16",
+ "type": "image/png"
+ },
+ {
+ "src": "/assets/images/favicon/favicon-32.png",
+ "sizes": "32x32",
+ "type": "image/png"
+ },
+ {
+ "src": "/assets/images/favicon/favicon-48.png",
+ "sizes": "48x48",
+ "type": "image/png"
+ },
+ {
+ "src": "/assets/images/favicon/favicon-150.png",
+ "sizes": "150x150",
+ "type": "image/png"
+ }
+ ],
+ "start_url": ".",
+ "display": "standalone",
+ "theme_color": "#191919",
+ "background_color": "#7f7f7f"
+}
diff --git a/sw.js b/sw.js
new file mode 100644
index 0000000..e4a0bd9
--- /dev/null
+++ b/sw.js
@@ -0,0 +1,28 @@
+// sw.js
+const CACHE_NAME = "ttc-cache-v1";
+const urlsToCache = [
+ "/",
+ "/index.html",
+ "css/*.css",
+ "js/editor.js",
+ "/favicon-150.png",
+ "/favicon-48.png",
+];
+
+// Install the service worker
+self.addEventListener("install", (event) => {
+ event.waitUntil(
+ caches.open(CACHE_NAME).then((cache) => {
+ return cache.addAll(urlsToCache);
+ })
+ );
+});
+
+// Fetch the cached assets
+self.addEventListener("fetch", (event) => {
+ event.respondWith(
+ caches.match(event.request).then((response) => {
+ return response || fetch(event.request);
+ })
+ );
+});