clay-server 2.9.0 → 2.9.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.
@@ -41,6 +41,154 @@ button.top-bar-pill {
41
41
  transition: background 0.15s;
42
42
  }
43
43
  button.top-bar-pill.pill-success:hover { background: color-mix(in srgb, var(--success) 20%, transparent); }
44
+ button.top-bar-pill.pill-accent:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }
45
+
46
+ /* PWA install button — left side of top bar, icon only */
47
+ .top-bar-install-btn {
48
+ position: absolute;
49
+ left: 10px;
50
+ top: 50%;
51
+ transform: translateY(-50%);
52
+ display: flex;
53
+ align-items: center;
54
+ justify-content: center;
55
+ background: none;
56
+ border: none;
57
+ border-radius: 6px;
58
+ color: var(--accent);
59
+ cursor: pointer;
60
+ padding: 3px;
61
+ transition: color 0.15s, background 0.15s;
62
+ }
63
+ .top-bar-install-btn .lucide { width: 14px; height: 14px; }
64
+ .top-bar-install-btn:hover { background: color-mix(in srgb, var(--accent) 12%, transparent); }
65
+ .top-bar-install-btn.hidden { display: none; }
66
+ .pwa-standalone .top-bar-install-btn { display: none !important; }
67
+
68
+ @media (max-width: 768px) {
69
+ .top-bar-install-btn {
70
+ top: auto;
71
+ bottom: 0;
72
+ height: 32px;
73
+ transform: none;
74
+ }
75
+ }
76
+
77
+ /* PWA install modal */
78
+ .pwa-modal {
79
+ position: fixed;
80
+ inset: 0;
81
+ z-index: 700;
82
+ display: flex;
83
+ align-items: center;
84
+ justify-content: center;
85
+ padding: 20px;
86
+ }
87
+
88
+ .pwa-modal.hidden { display: none; }
89
+
90
+ .pwa-modal-backdrop {
91
+ position: absolute;
92
+ inset: 0;
93
+ background: rgba(0,0,0,0.55);
94
+ backdrop-filter: blur(8px);
95
+ -webkit-backdrop-filter: blur(8px);
96
+ }
97
+
98
+ .pwa-modal-card {
99
+ position: relative;
100
+ background: var(--bg);
101
+ border: 1px solid var(--border);
102
+ border-radius: 16px;
103
+ padding: 28px 24px 20px;
104
+ max-width: 340px;
105
+ width: 100%;
106
+ box-shadow: 0 16px 48px rgba(0,0,0,0.35);
107
+ animation: modal-in 0.2s ease-out;
108
+ text-align: center;
109
+ }
110
+
111
+ @keyframes modal-in {
112
+ from { opacity: 0; transform: scale(0.95); }
113
+ to { opacity: 1; transform: scale(1); }
114
+ }
115
+
116
+ .pwa-modal-icon {
117
+ font-size: 36px;
118
+ margin-bottom: 8px;
119
+ }
120
+
121
+ .pwa-modal-title {
122
+ font-family: "Pretendard", system-ui, sans-serif;
123
+ font-size: 18px;
124
+ font-weight: 700;
125
+ color: var(--text);
126
+ margin: 0 0 6px;
127
+ }
128
+
129
+ .pwa-modal-desc {
130
+ font-size: 13px;
131
+ color: var(--text-muted);
132
+ margin: 0 0 16px;
133
+ line-height: 1.5;
134
+ }
135
+
136
+ .pwa-modal-benefits {
137
+ list-style: none;
138
+ padding: 0;
139
+ margin: 0 0 20px;
140
+ text-align: left;
141
+ }
142
+
143
+ .pwa-modal-benefits li {
144
+ display: flex;
145
+ align-items: center;
146
+ gap: 10px;
147
+ font-size: 13px;
148
+ color: var(--text-secondary);
149
+ padding: 7px 0;
150
+ border-bottom: 1px solid var(--border-subtle);
151
+ }
152
+
153
+ .pwa-modal-benefits li:last-child { border-bottom: none; }
154
+
155
+ .pwa-benefit-icon {
156
+ width: 16px;
157
+ height: 16px;
158
+ color: var(--accent);
159
+ flex-shrink: 0;
160
+ }
161
+
162
+ .pwa-modal-buttons {
163
+ display: flex;
164
+ flex-direction: column;
165
+ gap: 8px;
166
+ }
167
+
168
+ .pwa-modal-btn {
169
+ width: 100%;
170
+ padding: 11px;
171
+ border-radius: 10px;
172
+ font-family: "Pretendard", system-ui, sans-serif;
173
+ font-size: 14px;
174
+ font-weight: 600;
175
+ cursor: pointer;
176
+ border: none;
177
+ transition: opacity 0.15s;
178
+ }
179
+
180
+ .pwa-modal-btn:hover { opacity: 0.85; }
181
+
182
+ .pwa-modal-btn.primary {
183
+ background: var(--accent);
184
+ color: #fff;
185
+ }
186
+
187
+ .pwa-modal-btn.secondary {
188
+ background: none;
189
+ color: var(--text-muted);
190
+ font-weight: 500;
191
+ }
44
192
 
45
193
  /* Pill close button (onboarding) */
46
194
  .pill-close {
@@ -58,22 +206,6 @@ button.top-bar-pill.pill-success:hover { background: color-mix(in srgb, var(--su
58
206
  .pill-close:hover { opacity: 1; }
59
207
  .pill-close .lucide { width: 10px; height: 10px; }
60
208
 
61
- /* Onboarding pill text */
62
- .onboarding-pill-text { display: inline-flex; align-items: center; gap: 4px; }
63
- .onboarding-pill-text .lucide { width: 12px; height: 12px; }
64
- .onboarding-pill-text a,
65
- .onboarding-pill-text button.onboarding-cta {
66
- color: inherit;
67
- font-weight: 600;
68
- text-decoration: underline;
69
- background: none;
70
- border: none;
71
- font-family: inherit;
72
- font-size: inherit;
73
- cursor: pointer;
74
- padding: 0;
75
- }
76
-
77
209
  /* Update pill wrapper */
78
210
  #update-pill-wrap {
79
211
  position: relative;
@@ -183,6 +315,33 @@ button.top-bar-pill.pill-success:hover { background: color-mix(in srgb, var(--su
183
315
 
184
316
  #confirm-modal.hidden { display: none; }
185
317
 
318
+ #skill-install-modal {
319
+ position: fixed;
320
+ inset: 0;
321
+ z-index: 300;
322
+ display: flex;
323
+ align-items: center;
324
+ justify-content: center;
325
+ }
326
+
327
+ #skill-install-modal.hidden { display: none; }
328
+
329
+ .skill-install-dialog { max-width: 460px; padding: 28px 32px; }
330
+ .skill-install-title { font-weight: 600; font-size: 17px; margin-bottom: 8px; color: var(--text); }
331
+ .skill-install-reason { color: var(--text-secondary); font-size: 14px; line-height: 1.5; margin-bottom: 18px; }
332
+ .skill-install-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 18px; }
333
+ .skill-install-item { display: flex; align-items: center; gap: 12px; padding: 12px 14px; border-radius: 10px; background: rgba(var(--overlay-rgb), 0.05); border: 1px solid var(--border); }
334
+ .skill-install-item .skill-icon { font-size: 20px; flex-shrink: 0; }
335
+ .skill-install-item .skill-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
336
+ .skill-install-item .skill-name { font-weight: 500; font-size: 14px; color: var(--text); }
337
+ .skill-install-item .skill-scope { font-size: 12px; color: var(--text-muted); }
338
+ .skill-install-item .skill-status { margin-left: auto; flex-shrink: 0; }
339
+ .skill-status-ok { color: var(--success, #2ecc71); font-size: 16px; font-weight: 600; }
340
+ #skill-install-status { font-size: 13px; margin-bottom: 14px; display: flex; align-items: center; gap: 8px; color: var(--text-secondary); }
341
+ #skill-install-status.hidden { display: none; }
342
+ .confirm-proceed { background: var(--success, #2ecc71); color: #fff; border: none; }
343
+ .confirm-proceed:hover { opacity: 0.9; }
344
+
186
345
  .confirm-backdrop {
187
346
  position: absolute;
188
347
  inset: 0;
@@ -1160,6 +1160,7 @@
1160
1160
  min-width: 220px;
1161
1161
  max-width: 300px;
1162
1162
  }
1163
+ .schedule-popover.hidden { display: none; }
1163
1164
 
1164
1165
  .schedule-popover-name {
1165
1166
  font-weight: 700;
@@ -71,7 +71,7 @@
71
71
  .server-settings-nav-inner {
72
72
  width: 200px;
73
73
  flex-shrink: 0;
74
- padding: 60px 6px 40px 0;
74
+ padding: calc(var(--safe-top) + 60px) 6px 40px 0;
75
75
  }
76
76
 
77
77
  /* --- Nav header --- */
@@ -89,6 +89,11 @@
89
89
  white-space: nowrap;
90
90
  }
91
91
 
92
+ /* --- Nav dropdown (mobile only) --- */
93
+ .settings-nav-dropdown {
94
+ display: none;
95
+ }
96
+
92
97
  /* --- Nav items --- */
93
98
  .server-settings-nav-items {
94
99
  display: flex;
@@ -149,13 +154,13 @@
149
154
  }
150
155
 
151
156
  .server-settings-content-inner {
152
- padding: 60px 40px 60px 40px;
157
+ padding: calc(var(--safe-top) + 60px) 40px calc(var(--safe-bottom) + 60px) 40px;
153
158
  }
154
159
 
155
160
  /* ========== CLOSE COLUMN ========== */
156
161
  .server-settings-close-col {
157
162
  position: fixed;
158
- top: 60px;
163
+ top: calc(var(--safe-top) + 60px);
159
164
  right: 20px;
160
165
  z-index: 5;
161
166
  }
@@ -734,7 +739,7 @@
734
739
 
735
740
  .server-settings-nav-inner {
736
741
  width: 100%;
737
- padding: 0;
742
+ padding: var(--safe-top) 0 0;
738
743
  display: flex;
739
744
  flex-direction: column;
740
745
  gap: 0;
@@ -744,41 +749,39 @@
744
749
  padding: 12px 16px 8px;
745
750
  }
746
751
 
747
- /* Scroll wrapper with fade hints */
752
+ /* Hide desktop nav items on mobile, show dropdown instead */
748
753
  .server-settings-nav-items {
749
- flex-direction: row;
750
- overflow-x: auto;
751
- gap: 2px;
752
- scrollbar-width: none;
753
- padding: 0 16px 10px;
754
- -webkit-mask-image: linear-gradient(to right, transparent 0, black 16px, black calc(100% - 24px), transparent 100%);
755
- mask-image: linear-gradient(to right, transparent 0, black 16px, black calc(100% - 24px), transparent 100%);
756
- }
757
-
758
- .server-settings-nav-items::-webkit-scrollbar { display: none; }
759
-
760
- .settings-nav-item {
761
- flex-shrink: 0;
762
- font-size: 13px;
763
- padding: 6px 12px;
764
- border-radius: 20px;
765
- background: rgba(var(--overlay-rgb), 0.04);
766
- }
767
-
768
- .settings-nav-item.active {
769
- background: rgba(var(--overlay-rgb), 0.12);
754
+ display: none;
770
755
  }
771
756
 
772
- .settings-nav-item.settings-nav-danger {
773
- background: var(--error-12);
757
+ .settings-nav-dropdown {
758
+ display: block;
759
+ width: calc(100% - 32px);
760
+ margin: 0 16px 12px;
761
+ padding: 10px 12px;
762
+ font-family: "Pretendard", system-ui, sans-serif;
763
+ font-size: 14px;
764
+ font-weight: 600;
765
+ color: var(--text);
766
+ background: var(--bg-alt);
767
+ border: 1px solid var(--border);
768
+ border-radius: 10px;
769
+ -webkit-appearance: none;
770
+ appearance: none;
771
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
772
+ background-repeat: no-repeat;
773
+ background-position: right 12px center;
774
+ cursor: pointer;
774
775
  }
775
776
 
776
- .settings-nav-separator {
777
- display: none;
777
+ .settings-nav-dropdown optgroup {
778
+ font-weight: 700;
779
+ color: var(--text-muted);
778
780
  }
779
781
 
780
- .settings-nav-category {
781
- display: none;
782
+ .settings-nav-dropdown option {
783
+ font-weight: 500;
784
+ color: var(--text);
782
785
  }
783
786
 
784
787
  .server-settings-content {
@@ -787,11 +790,11 @@
787
790
  }
788
791
 
789
792
  .server-settings-content-inner {
790
- padding: 24px 16px;
793
+ padding: 24px 16px calc(var(--safe-bottom) + 24px);
791
794
  }
792
795
 
793
796
  .server-settings-close-col {
794
- top: 12px;
797
+ top: calc(var(--safe-top) + 12px);
795
798
  right: 12px;
796
799
  }
797
800
 
@@ -162,13 +162,6 @@
162
162
  color: var(--text-dimmer); /* darker icon on light thumb */
163
163
  }
164
164
 
165
- /* Debug button in top bar */
166
- .top-bar-actions #debug-menu-wrap { position: relative; }
167
- .top-bar-actions #debug-menu-wrap.hidden { display: none; }
168
- .top-bar-actions #debug-btn { color: var(--error); opacity: 0.6; }
169
- .top-bar-actions #debug-btn:hover { opacity: 1; background: var(--error-8); }
170
- .top-bar-actions #debug-btn.active { opacity: 1; background: var(--error-12); }
171
-
172
165
  /* ==========================================================================
173
166
  Title Bar — split into sidebar-column and main-column sections
174
167
  ========================================================================== */
@@ -5,8 +5,13 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, viewport-fit=cover">
6
6
  <meta name="apple-mobile-web-app-capable" content="yes">
7
7
  <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
8
- <link rel="manifest" href="manifest.json">
9
- <meta name="theme-color" content="#2F2E2B">
8
+ <script>
9
+ (function(){
10
+ var isIOS=/iPad|iPhone|iPod/.test(navigator.userAgent)||(navigator.platform==="MacIntel"&&navigator.maxTouchPoints>1);
11
+ if(!isIOS){var l=document.createElement("link");l.rel="manifest";l.href="manifest.json";document.head.appendChild(l)}
12
+ })();
13
+ </script>
14
+ <meta name="theme-color" content="#282a36">
10
15
  <link rel="icon" type="image/png" href="favicon-banded.png">
11
16
  <link rel="apple-touch-icon" href="apple-touch-icon.png">
12
17
  <title>Clay</title>
@@ -17,12 +22,14 @@
17
22
  <script>
18
23
  (function(){try{var k="clay-theme-vars",v=localStorage.getItem(k),r=document.documentElement;if(v){var o=JSON.parse(v),p;for(p in o)r.style.setProperty(p,o[p]);var vt=localStorage.getItem(k.replace("-vars","-variant"));if(vt==="light"){r.classList.add("light-theme");r.classList.remove("dark-theme")}else{r.classList.add("dark-theme");r.classList.remove("light-theme")}var m=document.querySelector('meta[name="theme-color"]');if(m&&o["--bg"])m.setAttribute("content",o["--bg"])}else{var sl=window.matchMedia&&window.matchMedia("(prefers-color-scheme: light)").matches;if(sl){r.classList.add("light-theme");r.classList.remove("dark-theme")}}}catch(e){}})();
19
24
  </script>
25
+ <script>if(window.navigator.standalone||window.matchMedia("(display-mode:standalone)").matches){document.documentElement.classList.add("pwa-standalone")}</script>
20
26
  <link rel="stylesheet" href="style.css">
21
27
  </head>
22
28
  <body>
23
29
  <div id="layout">
24
30
  <!-- === Top Bar (full width, forms reverse-ㄱ with icon strip) === -->
25
31
  <div id="top-bar">
32
+ <button id="pwa-install-pill" class="top-bar-install-btn hidden" title="Install app"><i data-lucide="download"></i></button>
26
33
  <div class="top-bar-center">
27
34
  <a href="https://github.com/chadbyte/clay" target="_blank" rel="noopener" class="top-bar-title"><img class="top-bar-icon" src="favicon-banded.png" width="16" height="16" alt="">Clay <span id="footer-version" class="footer-version"></span></a>
28
35
  <div id="update-pill-wrap" class="hidden">
@@ -32,30 +39,11 @@
32
39
  <div class="popover-row"><div class="popover-label">Or run manually:</div><div class="popover-cmd"><code>npx clay-server@latest</code><button class="popover-copy" title="Copy"><i data-lucide="copy"></i></button></div></div>
33
40
  </div>
34
41
  </div>
35
- <div id="onboarding-pill" class="top-bar-pill pill-accent hidden">
36
- <span class="onboarding-pill-text" id="onboarding-pill-text"></span>
37
- <button id="onboarding-pill-close" class="pill-close" aria-label="Dismiss"><i data-lucide="x"></i></button>
38
- </div>
39
42
  </div>
40
43
  <div class="top-bar-actions">
41
44
  <!-- Pill badges -->
42
45
  <div id="skip-perms-pill" class="top-bar-pill pill-error hidden"><i data-lucide="shield-off"></i> <span>Skip perms</span></div>
43
- <span id="client-count" class="hidden"></span>
44
- <div id="debug-menu-wrap" class="hidden">
45
- <button id="debug-btn" title="Debug"><i data-lucide="bug"></i></button>
46
- <div id="debug-menu" class="hidden">
47
- <label class="notif-option">
48
- <span>Update banner</span>
49
- <input type="checkbox" id="debug-toggle-update">
50
- <span class="toggle-track"><span class="toggle-thumb"></span></span>
51
- </label>
52
- <label class="notif-option">
53
- <span>Onboarding banner</span>
54
- <input type="checkbox" id="debug-toggle-onboarding">
55
- <span class="toggle-track"><span class="toggle-thumb"></span></span>
56
- </label>
57
- </div>
58
- </div>
46
+ <div id="client-count" class="top-bar-pill pill-accent hidden"><i data-lucide="users"></i> <span id="client-count-text"></span></div>
59
47
  <label id="theme-toggle-btn" class="theme-toggle" title="Toggle dark/light mode">
60
48
  <input type="checkbox" id="theme-toggle-check">
61
49
  <span class="theme-toggle-track">
@@ -614,6 +602,28 @@
614
602
  <div class="server-settings-nav">
615
603
  <div class="server-settings-nav-inner">
616
604
  <div class="server-settings-nav-header" id="settings-server-name"></div>
605
+ <select id="settings-nav-dropdown" class="settings-nav-dropdown">
606
+ <optgroup label="General">
607
+ <option value="overview" selected>Status</option>
608
+ <option value="notifications">Notifications</option>
609
+ </optgroup>
610
+ <optgroup label="Session">
611
+ <option value="models">Model &amp; Behavior</option>
612
+ <option value="claudemd">CLAUDE.md</option>
613
+ <option value="environment">Environment Variables</option>
614
+ </optgroup>
615
+ <optgroup label="Admin" class="settings-admin-only">
616
+ <option value="admin-users">Users</option>
617
+ <option value="admin-invites">Invites</option>
618
+ <option value="admin-projects">Projects</option>
619
+ <option value="admin-smtp">Email (SMTP)</option>
620
+ </optgroup>
621
+ <optgroup label="Server">
622
+ <option value="security">Security</option>
623
+ <option value="restart">Restart Server</option>
624
+ <option value="shutdown">Shutdown Server</option>
625
+ </optgroup>
626
+ </select>
617
627
  <div class="server-settings-nav-items">
618
628
  <div class="settings-nav-category">General</div>
619
629
  <button class="settings-nav-item active" data-section="overview"><span>Status</span></button>
@@ -1025,6 +1035,20 @@
1025
1035
  </div>
1026
1036
  </div>
1027
1037
 
1038
+ <div id="skill-install-modal" class="hidden">
1039
+ <div class="confirm-backdrop"></div>
1040
+ <div class="confirm-dialog skill-install-dialog">
1041
+ <div class="skill-install-title" id="skill-install-title"></div>
1042
+ <div class="skill-install-reason" id="skill-install-reason"></div>
1043
+ <div class="skill-install-list" id="skill-install-list"></div>
1044
+ <div id="skill-install-status" class="hidden"></div>
1045
+ <div class="confirm-actions">
1046
+ <button class="confirm-btn confirm-cancel" id="skill-install-cancel">Cancel</button>
1047
+ <button class="confirm-btn confirm-delete" id="skill-install-ok">Install</button>
1048
+ </div>
1049
+ </div>
1050
+ </div>
1051
+
1028
1052
  <div id="add-project-modal" class="hidden">
1029
1053
  <div class="confirm-backdrop"></div>
1030
1054
  <div class="confirm-dialog add-project-dialog">
@@ -1392,5 +1416,22 @@
1392
1416
  <script src="https://cdn.jsdelivr.net/npm/@xterm/xterm@5/lib/xterm.min.js"></script>
1393
1417
  <script src="https://cdn.jsdelivr.net/npm/@xterm/addon-fit@0/lib/addon-fit.min.js"></script>
1394
1418
  <script type="module" src="app.js"></script>
1419
+ <div id="pwa-install-modal" class="pwa-modal hidden">
1420
+ <div class="pwa-modal-backdrop"></div>
1421
+ <div class="pwa-modal-card">
1422
+ <div class="pwa-modal-icon">📲</div>
1423
+ <h3 class="pwa-modal-title">Add to Home Screen</h3>
1424
+ <p class="pwa-modal-desc">Add Clay to your home screen for a better experience.</p>
1425
+ <ul class="pwa-modal-benefits">
1426
+ <li><i data-lucide="maximize" class="pwa-benefit-icon"></i>Full-screen — no browser toolbar</li>
1427
+ <li><i data-lucide="bell" class="pwa-benefit-icon"></i>Push notifications when tasks finish</li>
1428
+ <li><i data-lucide="zap" class="pwa-benefit-icon"></i>Instant launch from home screen</li>
1429
+ </ul>
1430
+ <div class="pwa-modal-buttons">
1431
+ <button id="pwa-modal-confirm" class="pwa-modal-btn primary">Continue</button>
1432
+ <button id="pwa-modal-cancel" class="pwa-modal-btn secondary">Not now</button>
1433
+ </div>
1434
+ </div>
1435
+ </div>
1395
1436
  </body>
1396
1437
  </html>
@@ -5,8 +5,8 @@
5
5
  "start_url": "/",
6
6
  "scope": "/",
7
7
  "display": "standalone",
8
- "background_color": "#2F2E2B",
9
- "theme_color": "#2F2E2B",
8
+ "background_color": "#282a36",
9
+ "theme_color": "#282a36",
10
10
  "icons": [
11
11
  {
12
12
  "src": "icon-192.png",
@@ -187,29 +187,7 @@ export function initNotifications(_ctx) {
187
187
  }
188
188
  })();
189
189
 
190
- // --- Onboarding pill badge (HTTPS / Push) ---
191
- onboardingPill = $("onboarding-pill");
192
- onboardingText = $("onboarding-pill-text");
193
- onboardingClose = $("onboarding-pill-close");
194
- onboardingDismissed = localStorage.getItem("onboarding-dismissed");
195
-
196
- if (onboardingClose) {
197
- onboardingClose.addEventListener("click", function () {
198
- hideOnboarding();
199
- localStorage.setItem("onboarding-dismissed", "1");
200
- onboardingDismissed = "1";
201
- });
202
- }
203
-
204
- // Suggest HTTPS setup for push notification support
205
- if (location.protocol !== "https:" && location.hostname !== "localhost") {
206
- if (!onboardingDismissed) {
207
- showOnboarding(
208
- iconHtml("bell-ring") +
209
- ' <a href="/setup">Set up HTTPS</a>'
210
- );
211
- }
212
- }
190
+ // Onboarding pill removed install flow handled by PWA install button
213
191
 
214
192
  // --- Tooltip ---
215
193
  var tooltipEl = document.createElement("div");
@@ -495,6 +473,20 @@ export function initNotifications(_ctx) {
495
473
  if (!("serviceWorker" in navigator)) return;
496
474
  if (location.protocol !== "https:" && location.hostname !== "localhost") return;
497
475
 
476
+ // iOS Safari (non-standalone): unregister existing SW and skip registration
477
+ // to prevent iOS from treating the app as a modern PWA (which shows a floating toolbar).
478
+ // SW will be registered once the app is launched in standalone mode from the home screen.
479
+ var isIOS = /iPad|iPhone|iPod/.test(navigator.userAgent) ||
480
+ (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
481
+ var isStandalone = window.navigator.standalone ||
482
+ window.matchMedia("(display-mode:standalone)").matches;
483
+ if (isIOS && !isStandalone) {
484
+ navigator.serviceWorker.getRegistrations().then(function (regs) {
485
+ regs.forEach(function (r) { r.unregister(); });
486
+ });
487
+ return;
488
+ }
489
+
498
490
  navigator.serviceWorker.register("/sw.js")
499
491
  .then(function () { return navigator.serviceWorker.ready; })
500
492
  .then(function (reg) {
@@ -547,90 +539,10 @@ export function initNotifications(_ctx) {
547
539
  location.href = "/setup" + (isTailscale ? "" : "?mode=lan");
548
540
  return;
549
541
  }
550
- // Browser: show onboarding banner
551
- if (!onboardingDismissed) {
552
- showOnboarding(
553
- iconHtml("bell-ring") +
554
- ' <button class="onboarding-cta" id="onboarding-enable-push">Enable push</button>'
555
- );
556
- var enableBtn = $("onboarding-enable-push");
557
- if (enableBtn) {
558
- enableBtn.addEventListener("click", function () {
559
- subscribePush();
560
- notifTogglePush.checked = true;
561
- hideOnboarding();
562
- localStorage.setItem("onboarding-dismissed", "1");
563
- });
564
- }
565
- }
566
542
  }
567
543
  });
568
544
  })
569
545
  .catch(function () {});
570
546
  })();
571
547
 
572
- // --- Debug panel ---
573
- (function () {
574
- var debugBtn = $("debug-btn");
575
- var debugMenu = $("debug-menu");
576
- if (!debugBtn || !debugMenu) return;
577
-
578
- var debugToggleUpdate = $("debug-toggle-update");
579
- var debugToggleOnboarding = $("debug-toggle-onboarding");
580
-
581
- debugBtn.addEventListener("click", function (e) {
582
- e.stopPropagation();
583
- var open = debugMenu.classList.toggle("hidden");
584
- debugBtn.classList.toggle("active", !open);
585
-
586
- // Sync toggle states with current pill visibility
587
- var updatePillWrap = $("update-pill-wrap");
588
- if (debugToggleUpdate && updatePillWrap) {
589
- debugToggleUpdate.checked = !updatePillWrap.classList.contains("hidden");
590
- }
591
- if (debugToggleOnboarding && onboardingPill) {
592
- debugToggleOnboarding.checked = !onboardingPill.classList.contains("hidden");
593
- }
594
- });
595
-
596
- document.addEventListener("click", function (e) {
597
- if (!debugMenu.contains(e.target) && e.target !== debugBtn) {
598
- debugMenu.classList.add("hidden");
599
- debugBtn.classList.remove("active");
600
- }
601
- });
602
-
603
- if (debugToggleUpdate) {
604
- debugToggleUpdate.addEventListener("change", function () {
605
- var pillWrap = $("update-pill-wrap");
606
- if (!pillWrap) return;
607
- if (debugToggleUpdate.checked) {
608
- // Trigger real update check from server (debug mode uses v0.0.9)
609
- if (ctx.ws && ctx.connected) {
610
- ctx.ws.send(JSON.stringify({ type: "check_update" }));
611
- }
612
- } else {
613
- pillWrap.classList.add("hidden");
614
- }
615
- refreshIcons();
616
- });
617
- }
618
-
619
- if (debugToggleOnboarding) {
620
- debugToggleOnboarding.addEventListener("change", function () {
621
- if (debugToggleOnboarding.checked) {
622
- if (!onboardingText.innerHTML.trim()) {
623
- showOnboarding(
624
- iconHtml("bell-ring") +
625
- ' <a href="/setup">Set up HTTPS</a>'
626
- );
627
- } else {
628
- onboardingPill.classList.remove("hidden");
629
- }
630
- } else {
631
- hideOnboarding();
632
- }
633
- });
634
- }
635
- })();
636
548
  }