roxxie-proxy-u-prod 0.1.0 → 0.2.2

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.
Files changed (47) hide show
  1. package/README.md +62 -74
  2. package/dist/assets/{index-DcvPYWS3.js → index-CttvagWK.js} +164 -172
  3. package/dist/assets/index-EeRhLAof.css +1 -0
  4. package/dist/assets/{inject-BnVzMVpl.js → inject-xtH4w5z9.js} +8 -5
  5. package/dist/favicon.png +0 -0
  6. package/embed.js +2 -1
  7. package/examples/single-page.html +3 -1
  8. package/package.json +43 -41
  9. package/runtime/index.html +5 -4
  10. package/scripts/build.mjs +11 -5
  11. package/scripts/smoke.mjs +264 -73
  12. package/scripts/verify.mjs +100 -1
  13. package/source/packages/chrome/index.html +3 -2
  14. package/source/packages/chrome/public/favicon.png +0 -0
  15. package/source/packages/chrome/src/Tab/Tab.tsx +1 -1
  16. package/source/packages/chrome/src/assets/favicon.ts +1 -1
  17. package/source/packages/chrome/src/components/Button.tsx +11 -22
  18. package/source/packages/chrome/src/components/Checkbox.tsx +8 -12
  19. package/source/packages/chrome/src/components/Input.tsx +3 -4
  20. package/source/packages/chrome/src/components/StartScreen.tsx +34 -49
  21. package/source/packages/chrome/src/components/TopSiteButton.tsx +2 -2
  22. package/source/packages/chrome/src/pages/NewTabPage.tsx +42 -10
  23. package/source/packages/chrome/src/pages/SettingsPage.tsx +10 -11
  24. package/source/packages/chrome/src/runtime-base.ts +1 -1
  25. package/source/packages/chrome/src/services/SettingsService.ts +1 -1
  26. package/source/packages/chrome/src/style.css +7 -7
  27. package/source/packages/chrome/src/themes.ts +21 -21
  28. package/source/packages/inject/src/errorpage/errorpage.css +22 -35
  29. package/source/packages/inject/src/errorpage/errorpage.ts +6 -3
  30. package/source/packages/roxxie-transport/package.json +1 -1
  31. package/source/packages/roxxie-transport/src/connection.ts +1 -1
  32. package/source/packages/roxxie-transport/src/transport.ts +2 -2
  33. package/source/packages/scramjet/packages/core/dist/scramjet.js +1 -1
  34. package/source/packages/scramjet/packages/core/dist/scramjet.mjs +1 -1
  35. package/source/packages/scramjet/packages/core/dist/scramjet_bundled.js +1 -1
  36. package/source/packages/scramjet/packages/core/dist/scramjet_bundled.mjs +1 -1
  37. package/source/packages/tracker-protocol/package.json +1 -1
  38. package/source/packages/tracker-protocol/src/index.ts +34 -10
  39. package/source/pnpm-lock.yaml +4 -1
  40. package/standalone.html +27 -0
  41. package/dist/assets/index-BdRlQ7d_.css +0 -1
  42. package/dist/icon.png +0 -0
  43. package/dist/roxxie-icon.svg +0 -12
  44. package/source/packages/chrome/public/icon.png +0 -0
  45. package/source/packages/chrome/public/roxxie-icon.svg +0 -12
  46. package/source/packages/scramjet/packages/core/dist/temp-types-build/index.js +0 -23
  47. package/source/packages/scramjet/packages/core/dist/temp-types-build/index.js.map +0 -1
@@ -1,6 +1,7 @@
1
1
  /* SPDX-License-Identifier: AGPL-3.0-only */
2
2
 
3
3
  import { access, readFile, readdir, stat } from "node:fs/promises";
4
+ import { createHash } from "node:crypto";
4
5
  import { dirname, join, resolve } from "node:path";
5
6
  import { fileURLToPath } from "node:url";
6
7
 
@@ -8,14 +9,22 @@ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
8
9
  const manifest = JSON.parse(
9
10
  await readFile(join(packageRoot, "package.json"), "utf8")
10
11
  );
11
- const expectedCdn = `https://cdn.jsdelivr.net/npm/${manifest.name}@${manifest.version}/dist/`;
12
+ const packageSpec = `${manifest.name}@${manifest.version}`;
13
+ const expectedCdnRoot = `https://cdn.jsdelivr.net/npm/${packageSpec}/`;
14
+ const expectedCdn = `${expectedCdnRoot}dist/`;
15
+ const expectedRuntimeBase = `/${packageSpec}/runtime/`;
16
+ const expectedShell = `https://unpkg.com${expectedRuntimeBase}index.html`;
12
17
  const required = [
13
18
  "embed.js",
19
+ "standalone.html",
20
+ "examples/single-page.html",
14
21
  "LICENSE",
15
22
  "SOURCE.md",
16
23
  "THIRD_PARTY_NOTICES.md",
17
24
  "dist",
25
+ "dist/favicon.png",
18
26
  "source/packages/chrome/src",
27
+ "source/packages/chrome/public/favicon.png",
19
28
  "source/packages/chrome/src/adblock/assets/filters.bin",
20
29
  "source/packages/cdp/dist/chobitsu.js",
21
30
  "source/packages/cdp/src",
@@ -67,10 +76,93 @@ async function* walkFiles(root, relativeRoot = "") {
67
76
 
68
77
  for (const path of required) await access(join(packageRoot, path));
69
78
 
79
+ const expectedLogoSha256 =
80
+ "fda3eb53c94eb3ef61e24e2724b4a87e863e0ce4135767f0041beb86e7107472";
81
+ for (const path of [
82
+ "dist/favicon.png",
83
+ "source/packages/chrome/public/favicon.png",
84
+ ]) {
85
+ const digest = createHash("sha256")
86
+ .update(await readFile(join(packageRoot, path)))
87
+ .digest("hex");
88
+ if (digest !== expectedLogoSha256) {
89
+ throw new Error(`${path} does not contain the approved Roxxie logo`);
90
+ }
91
+ }
92
+
93
+ const themeSource = await readFile(
94
+ join(packageRoot, "source/packages/chrome/src/themes.ts"),
95
+ "utf8"
96
+ );
97
+ for (const requiredThemeValue of [
98
+ ' name: "EDUrocks Dark"',
99
+ ' toolbar: "#222230"',
100
+ ' field: "#11111b"',
101
+ ' accent: "#eb458b"',
102
+ ]) {
103
+ if (!themeSource.includes(requiredThemeValue.trimStart())) {
104
+ throw new Error(`Branded theme is missing ${requiredThemeValue.trim()}`);
105
+ }
106
+ }
107
+
108
+ const startScreenSource = await readFile(
109
+ join(packageRoot, "source/packages/chrome/src/components/StartScreen.tsx"),
110
+ "utf8"
111
+ );
112
+ if (!startScreenSource.includes("--start-action: #eb458b")) {
113
+ throw new Error("Start screen does not use the EDUrocks accent color");
114
+ }
115
+ if (
116
+ /\b(?:linear|radial)-gradient\s*\(|text-shadow\s*:/iu.test(startScreenSource)
117
+ ) {
118
+ throw new Error("Start screen must remain flat and glow-free");
119
+ }
120
+
121
+ if (manifest.version !== "0.2.2") {
122
+ throw new Error("This production release must be version 0.2.2");
123
+ }
124
+
125
+ const embedBytes = await readFile(join(packageRoot, "embed.js"));
126
+ const embed = embedBytes.toString("utf8");
127
+ const expectedEmbedIntegrity = `sha384-${createHash("sha384")
128
+ .update(embedBytes)
129
+ .digest("base64")}`;
130
+ if (!embed.includes(expectedShell)) {
131
+ throw new Error(`embed.js must default to ${expectedShell}`);
132
+ }
133
+ if (/100\.58\.76\.81\/browser/iu.test(embed)) {
134
+ throw new Error("embed.js must not depend on tracker-hosted browser files");
135
+ }
136
+
137
+ for (const path of ["standalone.html", "examples/single-page.html"]) {
138
+ const document = await readFile(join(packageRoot, path), "utf8");
139
+ if (!document.includes(`${expectedCdnRoot}embed.js`)) {
140
+ throw new Error(`${path} must load the exact pinned jsDelivr embed`);
141
+ }
142
+ if (!document.includes(`integrity="${expectedEmbedIntegrity}"`)) {
143
+ throw new Error(`${path} must pin embed.js with ${expectedEmbedIntegrity}`);
144
+ }
145
+ if (!document.includes('crossorigin="anonymous"')) {
146
+ throw new Error(`${path} must request the SRI-protected embed anonymously`);
147
+ }
148
+ if (/data-roxxie-src=/iu.test(document)) {
149
+ throw new Error(
150
+ `${path} must exercise embed.js's production default shell`
151
+ );
152
+ }
153
+ }
154
+
70
155
  const shell = await readFile(join(packageRoot, "runtime/index.html"), "utf8");
71
156
  if (!shell.includes(expectedCdn)) {
72
157
  throw new Error(`runtime/index.html must reference ${expectedCdn}`);
73
158
  }
159
+ if (
160
+ !shell.includes('rel="icon"') ||
161
+ !shell.includes('type="image/png"') ||
162
+ !shell.includes(`${expectedCdn}favicon.png`)
163
+ ) {
164
+ throw new Error("Runtime shell does not reference the pinned branded PNG");
165
+ }
74
166
  if (/127\.0\.0\.1|localhost/.test(shell)) {
75
167
  throw new Error("Production shell contains a loopback address");
76
168
  }
@@ -95,9 +187,16 @@ if (outputFiles.some((path) => path.endsWith(".map"))) {
95
187
  if (outputFiles.includes("index.html")) {
96
188
  throw new Error("CDN dist must not claim that jsDelivr can execute HTML");
97
189
  }
190
+ let hasVersionedRuntimeBase = false;
98
191
  for (const path of JavaScriptFiles) {
99
192
  const source = await readFile(join(packageRoot, "dist", path), "utf8");
100
193
  assertNoLocalPath(`dist/${path}`, source);
194
+ if (source.includes(expectedRuntimeBase)) hasVersionedRuntimeBase = true;
195
+ }
196
+ if (!hasVersionedRuntimeBase) {
197
+ throw new Error(
198
+ `Production JavaScript must use the unpkg runtime base ${expectedRuntimeBase}`
199
+ );
101
200
  }
102
201
  for await (const path of walkFiles(packageRoot)) {
103
202
  const contents = await readFile(join(packageRoot, path));
@@ -2,14 +2,15 @@
2
2
  <html lang="en">
3
3
  <head>
4
4
  <meta charset="UTF-8" />
5
- <link rel="icon" type="image/svg+xml" href="/roxxie-icon.svg" />
5
+ <link rel="icon" type="image/png" sizes="any" href="/favicon.png" />
6
+ <meta name="theme-color" content="#222230" />
6
7
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
8
  <title>Roxxie Proxy U</title>
8
9
 
9
10
  <!--ssr-head-->
10
11
  </head>
11
12
 
12
- <body class="light-mode">
13
+ <body>
13
14
  <!--ssr-body-->
14
15
  <div id="app"></div>
15
16
  <script type="module" src="/src/index.ts"></script>
@@ -138,7 +138,7 @@ export class Tab extends StatefulClass {
138
138
  this.url = url;
139
139
  this.icon = defaultFaviconUrl;
140
140
  if (url.protocol == INTERNAL_URL_PROTOCOL) {
141
- this.icon = null;
141
+ this.icon = roxxieIconUrl;
142
142
  this.history.current().favicon = roxxieIconUrl;
143
143
  switch (url.host) {
144
144
  case "newtab":
@@ -1,4 +1,4 @@
1
1
  import defaultFavicon from "/defaultfavicon.png";
2
2
 
3
3
  export const defaultFaviconUrl = defaultFavicon;
4
- export const roxxieIconUrl = `${import.meta.env.BASE_URL}roxxie-icon.svg`;
4
+ export const roxxieIconUrl = `${import.meta.env.BASE_URL}favicon.png`;
@@ -34,34 +34,23 @@ Button.style = css`
34
34
  }
35
35
  :scope:not(.icon):hover {
36
36
  background: var(--text-10);
37
- background-image: radial-gradient(
38
- 70% 80% at 50% 100%,
39
- var(--text-8),
40
- var(--text-5)
41
- );
37
+ border-color: var(--text-30);
42
38
  }
43
39
  :scope.primary {
44
- background-color: var(--tab_line);
45
- background-image: radial-gradient(
46
- 70% 80% at 50% 100%,
47
- var(--tab_line),
48
- var(--accent-shade-20)
49
- );
50
- box-shadow: inset 0px 0.05em 0.025em rgb(255 255 255 / 30%);
51
- text-shadow: 0 0.055em 0.05em var(--accent-shade-20);
52
- color: white;
53
- font-weight: 500;
40
+ background: var(--tab_line);
41
+ color: #11111b;
42
+ font-weight: 650;
54
43
  border-color: var(--tab_line);
55
44
  }
56
45
 
57
46
  :scope.primary:hover {
58
- background-color: var(--accent-shade-10);
59
- background-image: radial-gradient(
60
- 70% 80% at 50% 100%,
61
- var(--accent-shade-5),
62
- var(--accent-shade-25)
63
- );
64
- filter: saturate(1.1);
47
+ background: var(--accent-shade-10);
48
+ border-color: var(--accent-shade-10);
49
+ }
50
+
51
+ :scope:not(.icon):focus-visible {
52
+ outline: 2px solid var(--tab_line);
53
+ outline-offset: 2px;
65
54
  }
66
55
 
67
56
  :scope:disabled,
@@ -47,8 +47,7 @@ Checkbox.style = css`
47
47
  display: flex;
48
48
  align-items: center;
49
49
  justify-content: center;
50
- color: var(--toolbar_field);
51
- text-shadow: 0 0.055em 0.05em var(--accent-shade-20);
50
+ color: #11111b;
52
51
  font-size: 0.8em;
53
52
  transform: scale(0);
54
53
  transition: transform 120ms cubic-bezier(0.43, 0.91, 0.34, 1.3);
@@ -56,16 +55,13 @@ Checkbox.style = css`
56
55
  }
57
56
 
58
57
  :scope:has(input:checked) {
59
- background-color: var(--tab_line);
60
- background-image: radial-gradient(
61
- 70% 50% at 50% 100%,
62
- var(--accent-tint-20),
63
- var(--accent-shade-10)
64
- );
65
- box-shadow:
66
- inset 0px 0.05em 0.025em rgb(255 255 255 / 40%),
67
- inset 0px -0.05em 0.04em rgb(0 0 0 / 20%);
68
- border: none;
58
+ background: var(--tab_line);
59
+ border-color: var(--tab_line);
60
+ }
61
+
62
+ :scope:has(input:focus-visible) {
63
+ outline: 2px solid var(--tab_line);
64
+ outline-offset: 2px;
69
65
  }
70
66
 
71
67
  :scope:has(input:checked)::after {
@@ -61,14 +61,13 @@ Input.style = css`
61
61
  font-size: 0.9em;
62
62
  color: var(--toolbar_field_text);
63
63
  outline: none;
64
- transition:
65
- border-color 0.2s ease,
66
- box-shadow 0.2s ease;
64
+ transition: border-color 0.2s ease;
67
65
  }
68
66
 
69
67
  input:focus {
70
68
  border-color: var(--tab_line);
71
- box-shadow: 0 0 0 2px var(--accent-20);
69
+ outline: 2px solid var(--tab_line);
70
+ outline-offset: 2px;
72
71
  }
73
72
 
74
73
  input:disabled {
@@ -61,9 +61,14 @@ export function StartScreen(this: FC<StartScreenProps, StartScreenState>) {
61
61
  <div class="start-screen">
62
62
  <main class="start-panel" aria-labelledby="start-title">
63
63
  <header class="product-name">
64
- <span class="product-mark" aria-hidden="true">
65
- R
66
- </span>
64
+ <img
65
+ class="product-mark"
66
+ src={`${import.meta.env.BASE_URL}favicon.png`}
67
+ alt=""
68
+ aria-hidden="true"
69
+ width="56"
70
+ height="56"
71
+ />
67
72
  <span>Roxxie Proxy U</span>
68
73
  </header>
69
74
 
@@ -164,17 +169,18 @@ export function mountStartScreen(props: StartScreenProps): HTMLElement {
164
169
 
165
170
  StartScreen.style = css`
166
171
  :scope {
167
- --start-background: #f2f1ed;
168
- --start-panel: #ffffff;
169
- --start-text: #1c1d1f;
170
- --start-muted: #64676c;
171
- --start-border: #d5d4cf;
172
- --start-subtle: #f7f7f5;
173
- --start-action: #245e4a;
174
- --start-action-hover: #194936;
175
- --start-error-background: #fff7f5;
176
- --start-error-border: #c98d83;
177
- --start-error-text: #732d24;
172
+ --start-background: #11111b;
173
+ --start-panel: #222230;
174
+ --start-text: #ebeff5;
175
+ --start-muted: #b2bdcb;
176
+ --start-border: #4f526b;
177
+ --start-subtle: #191925;
178
+ --start-action: #eb458b;
179
+ --start-action-hover: #d6387a;
180
+ --start-action-text: #11111b;
181
+ --start-error-background: #2d1d26;
182
+ --start-error-border: #a85173;
183
+ --start-error-text: #f3afcb;
178
184
 
179
185
  width: 100%;
180
186
  height: 100%;
@@ -201,29 +207,23 @@ StartScreen.style = css`
201
207
  padding: 2rem;
202
208
  background: var(--start-panel);
203
209
  border: 1px solid var(--start-border);
204
- border-radius: 6px;
210
+ border-radius: 16px;
205
211
  }
206
212
 
207
213
  .product-name {
208
214
  display: flex;
209
215
  align-items: center;
210
- gap: 0.65rem;
216
+ gap: 0.8rem;
211
217
  font-size: 0.9rem;
212
218
  font-weight: 650;
213
219
  letter-spacing: 0.01em;
214
220
  }
215
221
 
216
222
  .product-mark {
217
- width: 1.75rem;
218
- height: 1.75rem;
219
- display: inline-flex;
220
- align-items: center;
221
- justify-content: center;
222
- border-radius: 4px;
223
- background: var(--start-action);
224
- color: #ffffff;
225
- font-size: 0.95rem;
226
- font-weight: 750;
223
+ width: 3.5rem;
224
+ height: 3.5rem;
225
+ object-fit: contain;
226
+ flex: 0 0 auto;
227
227
  }
228
228
 
229
229
  .introduction {
@@ -266,7 +266,7 @@ StartScreen.style = css`
266
266
  padding: 0.9rem 1rem;
267
267
  background: var(--start-subtle);
268
268
  border: 1px solid var(--start-border);
269
- border-radius: 4px;
269
+ border-radius: 10px;
270
270
  }
271
271
 
272
272
  .status-label {
@@ -288,7 +288,7 @@ StartScreen.style = css`
288
288
  padding: 1rem;
289
289
  background: var(--start-error-background);
290
290
  border: 1px solid var(--start-error-border);
291
- border-radius: 4px;
291
+ border-radius: 10px;
292
292
  color: var(--start-error-text);
293
293
  font-size: 0.85rem;
294
294
  }
@@ -323,8 +323,8 @@ StartScreen.style = css`
323
323
  padding: 0.7rem 1rem;
324
324
  background: var(--start-action);
325
325
  border: 1px solid var(--start-action);
326
- border-radius: 4px;
327
- color: #ffffff;
326
+ border-radius: 10px;
327
+ color: var(--start-action-text);
328
328
  font: inherit;
329
329
  font-size: 0.95rem;
330
330
  font-weight: 650;
@@ -337,13 +337,14 @@ StartScreen.style = css`
337
337
  }
338
338
 
339
339
  .start-button:focus-visible {
340
- outline: 3px solid #8ba99e;
340
+ outline: 3px solid #f3afcb;
341
341
  outline-offset: 2px;
342
342
  }
343
343
 
344
344
  .start-button:disabled {
345
- background: #7c9189;
346
- border-color: #7c9189;
345
+ background: #6b4056;
346
+ border-color: #6b4056;
347
+ color: var(--start-muted);
347
348
  cursor: wait;
348
349
  }
349
350
 
@@ -352,22 +353,6 @@ StartScreen.style = css`
352
353
  font-size: 0.78rem;
353
354
  }
354
355
 
355
- @media (prefers-color-scheme: dark) {
356
- :scope {
357
- --start-background: #181a1b;
358
- --start-panel: #222526;
359
- --start-text: #f1f2ef;
360
- --start-muted: #b4b8b5;
361
- --start-border: #444846;
362
- --start-subtle: #292c2d;
363
- --start-action: #3d8068;
364
- --start-action-hover: #4a9278;
365
- --start-error-background: #322321;
366
- --start-error-border: #875c55;
367
- --start-error-text: #f0c1b9;
368
- }
369
- }
370
-
371
356
  @media (max-width: 34rem) {
372
357
  :scope {
373
358
  align-items: flex-start;
@@ -132,8 +132,8 @@ TopSiteButton.style = css`
132
132
 
133
133
  .top-site-button:focus-visible .tile {
134
134
  border-color: var(--tab_line);
135
- box-shadow: 0 0 0 2px var(--accent-20);
136
- outline: none;
135
+ outline: 2px solid var(--tab_line);
136
+ outline-offset: 2px;
137
137
  }
138
138
 
139
139
  .icon-wrapper {
@@ -74,12 +74,15 @@ export function NewTabPage(this: FC<{ tab: Tab }>) {
74
74
  <div>
75
75
  <div class="logo">
76
76
  <img
77
- src={`${import.meta.env.BASE_URL}roxxie-icon.svg`}
77
+ src={`${import.meta.env.BASE_URL}favicon.png`}
78
78
  alt="Roxxie Proxy U logo"
79
- width="56"
80
- height="56"
79
+ width="80"
80
+ height="80"
81
81
  />
82
- <h1>Roxxie Proxy U</h1>
82
+ <div class="logo-copy">
83
+ <span>EDUrocks network</span>
84
+ <h1>Roxxie Proxy U</h1>
85
+ </div>
83
86
  </div>
84
87
  <div class="topbar">
85
88
  <div class="inputcontainercontainer">
@@ -153,16 +156,32 @@ NewTabPage.style = css`
153
156
  .logo {
154
157
  display: flex;
155
158
  align-items: center;
156
- gap: 1rem;
159
+ gap: 1.1rem;
157
160
  margin-bottom: 2rem;
158
161
  }
162
+ .logo-copy {
163
+ display: flex;
164
+ flex-direction: column;
165
+ gap: 0.2rem;
166
+ }
167
+ .logo-copy span {
168
+ color: var(--ntp-text-60);
169
+ font-size: 0.74rem;
170
+ font-weight: 700;
171
+ letter-spacing: 0.08em;
172
+ text-transform: uppercase;
173
+ }
159
174
  .logo h1 {
160
- font-size: 2.25rem;
161
- font-weight: 600;
175
+ font-size: 2.1rem;
176
+ font-weight: 650;
177
+ letter-spacing: -0.025em;
162
178
  user-select: none;
163
179
  }
164
180
  .logo img {
165
181
  display: inline-block;
182
+ width: 5rem;
183
+ height: 5rem;
184
+ object-fit: contain;
166
185
  user-select: none;
167
186
  }
168
187
  .clock {
@@ -187,7 +206,6 @@ NewTabPage.style = css`
187
206
  align-items: center;
188
207
  transition:
189
208
  border-color 0.15s ease-out,
190
- box-shadow 0.15s ease-out,
191
209
  background-color 0.15s ease-out;
192
210
  }
193
211
 
@@ -199,8 +217,8 @@ NewTabPage.style = css`
199
217
 
200
218
  .inputcontainer:focus-within {
201
219
  border-color: var(--tab_line);
202
- box-shadow: 0 0 0 2px var(--accent-20);
203
- outline: none;
220
+ outline: 2px solid var(--tab_line);
221
+ outline-offset: 2px;
204
222
  }
205
223
  input {
206
224
  font-size: 1.05rem;
@@ -260,5 +278,19 @@ NewTabPage.style = css`
260
278
  .top-sites-list {
261
279
  grid-template-columns: repeat(auto-fit, minmax(6.25rem, 6.25rem));
262
280
  }
281
+
282
+ .logo {
283
+ align-items: flex-start;
284
+ gap: 0.8rem;
285
+ }
286
+
287
+ .logo img {
288
+ width: 4rem;
289
+ height: 4rem;
290
+ }
291
+
292
+ .logo h1 {
293
+ font-size: 1.65rem;
294
+ }
263
295
  }
264
296
  `;
@@ -1313,7 +1313,7 @@ export function SettingsPage(
1313
1313
  <div class="about-info">
1314
1314
  <img
1315
1315
  class="browser-logo"
1316
- src={`${import.meta.env.BASE_URL}roxxie-icon.svg`}
1316
+ src={`${import.meta.env.BASE_URL}favicon.png`}
1317
1317
  alt="Roxxie Proxy U logo"
1318
1318
  />
1319
1319
  <div class="browser-info">
@@ -1505,7 +1505,8 @@ SettingsPage.style = css`
1505
1505
 
1506
1506
  .search-input input:focus {
1507
1507
  border-color: var(--tab_line);
1508
- box-shadow: 0 0 0 2px var(--accent-20);
1508
+ outline: 2px solid var(--tab_line);
1509
+ outline-offset: 2px;
1509
1510
  }
1510
1511
 
1511
1512
  .search-input .icon {
@@ -1653,16 +1654,11 @@ SettingsPage.style = css`
1653
1654
  cursor: pointer;
1654
1655
  display: flex;
1655
1656
  flex-direction: column;
1656
- transition:
1657
- border-color 0.15s ease,
1658
- box-shadow 0.15s ease,
1659
- transform 0.15s ease;
1657
+ transition: border-color 0.15s ease;
1660
1658
  }
1661
1659
 
1662
1660
  .layout-card:hover {
1663
1661
  border-color: var(--tab_line);
1664
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
1665
- transform: translateY(-1px);
1666
1662
  }
1667
1663
 
1668
1664
  .layout-card:focus-within {
@@ -1672,7 +1668,8 @@ SettingsPage.style = css`
1672
1668
 
1673
1669
  .layout-card.selected {
1674
1670
  border-color: var(--tab_line);
1675
- box-shadow: 0 0 0 3px var(--accent-20);
1671
+ outline: 2px solid var(--tab_line);
1672
+ outline-offset: 2px;
1676
1673
  }
1677
1674
 
1678
1675
  .layout-radio {
@@ -1833,6 +1830,7 @@ SettingsPage.style = css`
1833
1830
  .browser-logo {
1834
1831
  width: 5rem;
1835
1832
  height: 5rem;
1833
+ object-fit: contain;
1836
1834
  }
1837
1835
 
1838
1836
  .browser-info h3 {
@@ -1894,6 +1892,7 @@ SettingsPage.style = css`
1894
1892
  }
1895
1893
 
1896
1894
  .theme-card {
1895
+ border: 1px solid var(--ntp-text-15);
1897
1896
  border-radius: var(--radius-md);
1898
1897
  overflow: hidden;
1899
1898
  cursor: pointer;
@@ -1903,12 +1902,12 @@ SettingsPage.style = css`
1903
1902
 
1904
1903
  .theme-card:hover {
1905
1904
  border-color: var(--tab_line);
1906
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
1907
1905
  }
1908
1906
 
1909
1907
  .theme-card.selected {
1910
1908
  border-color: var(--tab_line);
1911
- box-shadow: 0 0 0 3px var(--accent-20);
1909
+ outline: 2px solid var(--tab_line);
1910
+ outline-offset: 2px;
1912
1911
  }
1913
1912
 
1914
1913
  .theme-info {
@@ -4,7 +4,7 @@
4
4
  * Static assets may be delivered by a CDN while configuration and service
5
5
  * workers must remain on the application's own origin. Development and the
6
6
  * standalone build use Vite's ordinary base URL; the production npm build
7
- * supplies `/browser/` explicitly.
7
+ * supplies its versioned, same-origin unpkg runtime directory explicitly.
8
8
  */
9
9
  export function runtimeBaseUrl(): URL {
10
10
  const configuredBase = import.meta.env.VITE_RUNTIME_BASE?.trim();
@@ -29,7 +29,7 @@ export type Settings = {
29
29
  export type TabLayoutMode = Settings["tabLayout"];
30
30
 
31
31
  const DEFAULT_SETTINGS: Settings = {
32
- appearance: "system",
32
+ appearance: "dark",
33
33
  tabLayout: "horizontal",
34
34
  verticalTabJustify: "left",
35
35
  sidebarWidth: null,
@@ -33,8 +33,8 @@ iframe {
33
33
  html,
34
34
  body {
35
35
  font-family: var(--font);
36
- color: var(--toolbar_text);
37
- background: var(--toolbar);
36
+ color: var(--toolbar_text, #ebeff5);
37
+ background: var(--toolbar, #222230);
38
38
  accent-color: var(--tab_line);
39
39
  color-scheme: dark;
40
40
 
@@ -93,11 +93,11 @@ body {
93
93
  --accent-shade-40: color-mix(in oklab, black 40%, var(--tab_line));
94
94
  --accent-shade-50: color-mix(in oklab, black 50%, var(--tab_line));
95
95
 
96
- --radius-xs: 3px;
97
- --radius-sm: 4px;
98
- --radius-md: 5px;
99
- --radius-lg: 6px;
100
- --radius-xl: 8px;
96
+ --radius-xs: 4px;
97
+ --radius-sm: 6px;
98
+ --radius-md: 8px;
99
+ --radius-lg: 12px;
100
+ --radius-xl: 16px;
101
101
 
102
102
  --space-xxs: 0.15rem;
103
103
  --space-xs: 0.225rem;