viveworker 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +178 -0
- package/launchd/io.viveworker.app.plist.example +39 -0
- package/ntfy/docker-compose.yml.example +10 -0
- package/ntfy/server.yml.example +28 -0
- package/package.json +24 -0
- package/scripts/lib/markdown-render.mjs +274 -0
- package/scripts/lib/pairing.mjs +83 -0
- package/scripts/viveworker-bridge.mjs +8892 -0
- package/scripts/viveworker.mjs +1353 -0
- package/viveworker.env.example +99 -0
- package/web/app.css +2303 -0
- package/web/app.js +3867 -0
- package/web/i18n.js +937 -0
- package/web/icons/apple-touch-icon.png +0 -0
- package/web/icons/viveworker-beacon-v.svg +19 -0
- package/web/icons/viveworker-icon-1024.png +0 -0
- package/web/icons/viveworker-icon-192.png +0 -0
- package/web/icons/viveworker-icon-512.png +0 -0
- package/web/icons/viveworker-v-check.svg +19 -0
- package/web/icons/viveworker-v-pulse.svg +24 -0
- package/web/index.html +17 -0
- package/web/manifest.webmanifest +22 -0
- package/web/sw.js +153 -0
|
Binary file
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<radialGradient id="bg" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(524 120) rotate(90) scale(920 834)">
|
|
4
|
+
<stop stop-color="#132432"/>
|
|
5
|
+
<stop offset="0.45" stop-color="#0C151C"/>
|
|
6
|
+
<stop offset="1" stop-color="#091016"/>
|
|
7
|
+
</radialGradient>
|
|
8
|
+
<radialGradient id="signal" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(512 244) rotate(90) scale(182 244)">
|
|
9
|
+
<stop stop-color="#84CBFF" stop-opacity="0.3"/>
|
|
10
|
+
<stop offset="1" stop-color="#84CBFF" stop-opacity="0"/>
|
|
11
|
+
</radialGradient>
|
|
12
|
+
</defs>
|
|
13
|
+
<rect width="1024" height="1024" rx="224" fill="url(#bg)"/>
|
|
14
|
+
<ellipse cx="512" cy="246" rx="184" ry="132" fill="url(#signal)"/>
|
|
15
|
+
<path d="M322 370L477 718C493 754 531 754 547 718L702 370" stroke="#F4FAFF" stroke-width="96" stroke-linecap="round" stroke-linejoin="round"/>
|
|
16
|
+
<circle cx="512" cy="228" r="28" fill="#8CCBFF"/>
|
|
17
|
+
<path d="M438 216C438 175 471 142 512 142C553 142 586 175 586 216" stroke="#8CCBFF" stroke-width="30" stroke-linecap="round"/>
|
|
18
|
+
<path d="M384 220C384 149 441 92 512 92C583 92 640 149 640 220" stroke="#8CCBFF" stroke-width="20" stroke-linecap="round" opacity="0.95"/>
|
|
19
|
+
</svg>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<linearGradient id="bg" x1="188" y1="122" x2="841" y2="931" gradientUnits="userSpaceOnUse">
|
|
4
|
+
<stop stop-color="#16222B"/>
|
|
5
|
+
<stop offset="0.48" stop-color="#0D141A"/>
|
|
6
|
+
<stop offset="1" stop-color="#0A1116"/>
|
|
7
|
+
</linearGradient>
|
|
8
|
+
<radialGradient id="accent" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(780 252) rotate(128.783) scale(251.649)">
|
|
9
|
+
<stop stop-color="#8BE9C1" stop-opacity="0.34"/>
|
|
10
|
+
<stop offset="1" stop-color="#8BE9C1" stop-opacity="0"/>
|
|
11
|
+
</radialGradient>
|
|
12
|
+
</defs>
|
|
13
|
+
<rect width="1024" height="1024" rx="224" fill="url(#bg)"/>
|
|
14
|
+
<circle cx="781" cy="254" r="120" fill="url(#accent)"/>
|
|
15
|
+
<path d="M286 300L468 730C486 772 538 772 556 730L738 300" stroke="#F8FBFE" stroke-width="108" stroke-linecap="round" stroke-linejoin="round"/>
|
|
16
|
+
<circle cx="760" cy="276" r="116" fill="#8BE9C1"/>
|
|
17
|
+
<circle cx="760" cy="276" r="94" fill="#132028" fill-opacity="0.2"/>
|
|
18
|
+
<path d="M707 277L741 312L815 238" stroke="#0C161C" stroke-width="42" stroke-linecap="round" stroke-linejoin="round"/>
|
|
19
|
+
</svg>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<svg width="1024" height="1024" viewBox="0 0 1024 1024" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
2
|
+
<defs>
|
|
3
|
+
<radialGradient id="bg" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(238 164) rotate(43.8309) scale(925.664)">
|
|
4
|
+
<stop stop-color="#173448"/>
|
|
5
|
+
<stop offset="0.38" stop-color="#0F1F2B"/>
|
|
6
|
+
<stop offset="1" stop-color="#091117"/>
|
|
7
|
+
</radialGradient>
|
|
8
|
+
<radialGradient id="glow" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(760 248) rotate(130.1) scale(256.971)">
|
|
9
|
+
<stop stop-color="#5ED6FF" stop-opacity="0.34"/>
|
|
10
|
+
<stop offset="1" stop-color="#5ED6FF" stop-opacity="0"/>
|
|
11
|
+
</radialGradient>
|
|
12
|
+
<filter id="soft" x="557" y="90" width="320" height="320" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
|
13
|
+
<feGaussianBlur stdDeviation="24"/>
|
|
14
|
+
</filter>
|
|
15
|
+
</defs>
|
|
16
|
+
<rect width="1024" height="1024" rx="224" fill="url(#bg)"/>
|
|
17
|
+
<circle cx="717" cy="250" r="112" fill="url(#glow)"/>
|
|
18
|
+
<g filter="url(#soft)">
|
|
19
|
+
<circle cx="717" cy="250" r="58" fill="#7DD3FC" fill-opacity="0.36"/>
|
|
20
|
+
</g>
|
|
21
|
+
<path d="M290 302L469 731C487 773 537 773 555 731L734 302" stroke="#F6FBFF" stroke-width="104" stroke-linecap="round" stroke-linejoin="round"/>
|
|
22
|
+
<path d="M697 180C748 180 789 221 789 272" stroke="#7DD3FC" stroke-width="40" stroke-linecap="round"/>
|
|
23
|
+
<path d="M667 120C752 120 821 189 821 274" stroke="#7DD3FC" stroke-width="28" stroke-linecap="round" opacity="0.92"/>
|
|
24
|
+
</svg>
|
package/web/index.html
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="ja">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
6
|
+
<meta name="theme-color" content="#101418">
|
|
7
|
+
<link rel="manifest" href="/manifest.webmanifest">
|
|
8
|
+
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png">
|
|
9
|
+
<link rel="icon" type="image/png" sizes="192x192" href="/icons/viveworker-icon-192.png">
|
|
10
|
+
<link rel="stylesheet" href="/app.css">
|
|
11
|
+
<title>viveworker</title>
|
|
12
|
+
</head>
|
|
13
|
+
<body>
|
|
14
|
+
<div id="app"></div>
|
|
15
|
+
<script type="module" src="/app.js"></script>
|
|
16
|
+
</body>
|
|
17
|
+
</html>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "/app",
|
|
3
|
+
"name": "viveworker",
|
|
4
|
+
"short_name": "viveworker",
|
|
5
|
+
"start_url": "/app",
|
|
6
|
+
"scope": "/",
|
|
7
|
+
"display": "standalone",
|
|
8
|
+
"background_color": "#101418",
|
|
9
|
+
"theme_color": "#101418",
|
|
10
|
+
"icons": [
|
|
11
|
+
{
|
|
12
|
+
"src": "/icons/viveworker-icon-192.png",
|
|
13
|
+
"sizes": "192x192",
|
|
14
|
+
"type": "image/png"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"src": "/icons/viveworker-icon-512.png",
|
|
18
|
+
"sizes": "512x512",
|
|
19
|
+
"type": "image/png"
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
package/web/sw.js
ADDED
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
const CACHE_NAME = "viveworker-v2";
|
|
2
|
+
const APP_ASSETS = ["/app.css", "/app.js", "/i18n.js"];
|
|
3
|
+
const APP_ROUTES = new Set(["/", "/app", "/app/"]);
|
|
4
|
+
const CACHED_PATHS = new Set(APP_ASSETS);
|
|
5
|
+
|
|
6
|
+
self.addEventListener("install", (event) => {
|
|
7
|
+
event.waitUntil(
|
|
8
|
+
(async () => {
|
|
9
|
+
const cache = await caches.open(CACHE_NAME);
|
|
10
|
+
await cache.addAll(APP_ASSETS);
|
|
11
|
+
await self.skipWaiting();
|
|
12
|
+
})()
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
self.addEventListener("activate", (event) => {
|
|
17
|
+
event.waitUntil(
|
|
18
|
+
(async () => {
|
|
19
|
+
const keys = await caches.keys();
|
|
20
|
+
await Promise.all(
|
|
21
|
+
keys
|
|
22
|
+
.filter((key) => key !== CACHE_NAME)
|
|
23
|
+
.map((key) => caches.delete(key))
|
|
24
|
+
);
|
|
25
|
+
await self.clients.claim();
|
|
26
|
+
})()
|
|
27
|
+
);
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
self.addEventListener("fetch", (event) => {
|
|
31
|
+
if (event.request.method !== "GET") {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const url = new URL(event.request.url);
|
|
36
|
+
if (url.origin !== self.location.origin) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (url.pathname.startsWith("/api/") || url.pathname === "/sw.js") {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (APP_ROUTES.has(url.pathname)) {
|
|
45
|
+
event.respondWith(networkFirst(event.request, "/app"));
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (CACHED_PATHS.has(url.pathname)) {
|
|
50
|
+
event.respondWith(networkFirst(event.request, url.pathname));
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
self.addEventListener("push", (event) => {
|
|
55
|
+
let payload = {};
|
|
56
|
+
try {
|
|
57
|
+
payload = event.data ? event.data.json() : {};
|
|
58
|
+
} catch {
|
|
59
|
+
payload = {
|
|
60
|
+
title: "viveworker",
|
|
61
|
+
body: event.data ? event.data.text() : "A new Codex item is available.",
|
|
62
|
+
data: { url: "/app" },
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const title = payload.title || "viveworker";
|
|
67
|
+
const options = {
|
|
68
|
+
body: payload.body || "A new Codex item is available.",
|
|
69
|
+
tag: payload.tag || "",
|
|
70
|
+
data: payload.data || { url: "/app" },
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
event.waitUntil(self.registration.showNotification(title, options));
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
self.addEventListener("notificationclick", (event) => {
|
|
77
|
+
event.notification.close();
|
|
78
|
+
const targetUrl = event.notification?.data?.url || "/app";
|
|
79
|
+
event.waitUntil(openTargetWindow(targetUrl));
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
self.addEventListener("pushsubscriptionchange", (event) => {
|
|
83
|
+
event.waitUntil(notifyClients("pushsubscriptionchange"));
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
async function openTargetWindow(targetUrl) {
|
|
87
|
+
const target = new URL(targetUrl, self.location.origin);
|
|
88
|
+
const clients = await self.clients.matchAll({
|
|
89
|
+
type: "window",
|
|
90
|
+
includeUncontrolled: true,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const preferredClients = clients
|
|
94
|
+
.slice()
|
|
95
|
+
.sort((left, right) => scoreClient(right.url) - scoreClient(left.url));
|
|
96
|
+
|
|
97
|
+
for (const client of preferredClients) {
|
|
98
|
+
if (typeof client.focus === "function") {
|
|
99
|
+
if (typeof client.navigate === "function") {
|
|
100
|
+
await client.navigate(target.toString());
|
|
101
|
+
}
|
|
102
|
+
await client.focus();
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (self.clients.openWindow) {
|
|
108
|
+
await self.clients.openWindow(target.toString());
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function scoreClient(urlString) {
|
|
113
|
+
try {
|
|
114
|
+
const url = new URL(urlString);
|
|
115
|
+
let score = 0;
|
|
116
|
+
if (APP_ROUTES.has(url.pathname)) {
|
|
117
|
+
score += 20;
|
|
118
|
+
}
|
|
119
|
+
if (url.pathname === "/app" || url.pathname === "/app/") {
|
|
120
|
+
score += 5;
|
|
121
|
+
}
|
|
122
|
+
return score;
|
|
123
|
+
} catch {
|
|
124
|
+
return 0;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
async function notifyClients(type) {
|
|
129
|
+
const clients = await self.clients.matchAll({
|
|
130
|
+
type: "window",
|
|
131
|
+
includeUncontrolled: true,
|
|
132
|
+
});
|
|
133
|
+
for (const client of clients) {
|
|
134
|
+
client.postMessage({ type });
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function networkFirst(request, cacheKey) {
|
|
139
|
+
const cache = await caches.open(CACHE_NAME);
|
|
140
|
+
try {
|
|
141
|
+
const response = await fetch(request, { cache: "no-store" });
|
|
142
|
+
if (response && response.ok) {
|
|
143
|
+
await cache.put(cacheKey, response.clone());
|
|
144
|
+
}
|
|
145
|
+
return response;
|
|
146
|
+
} catch {
|
|
147
|
+
const cached = await cache.match(cacheKey);
|
|
148
|
+
if (cached) {
|
|
149
|
+
return cached;
|
|
150
|
+
}
|
|
151
|
+
return Response.error();
|
|
152
|
+
}
|
|
153
|
+
}
|