@chrysb/alphaclaw 0.1.15 → 0.1.17

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.
@@ -47,4 +47,69 @@ body::before {
47
47
  /* Google scope picker toggle buttons */
48
48
  .scope-btn { background: rgba(255,255,255,0.03); color: var(--text-muted); border: 1px solid var(--border); transition: all 0.15s; }
49
49
  .scope-btn:hover { border-color: var(--text-dim); color: var(--text); }
50
- .scope-btn.active { background: var(--bg-active); color: var(--accent); border-color: var(--accent-dim); }
50
+ .scope-btn-read.active {
51
+ background: rgba(255, 255, 255, 0.12);
52
+ color: #f3f4f6;
53
+ border-color: rgba(255, 255, 255, 0.35);
54
+ }
55
+ .scope-btn-write.active {
56
+ background: rgba(255, 255, 255, 0.12);
57
+ color: #f3f4f6;
58
+ border-color: rgba(255, 255, 255, 0.35);
59
+ }
60
+
61
+ /* Reusable cyan action buttons */
62
+ .ac-btn-cyan {
63
+ border: 1px solid var(--accent-dim);
64
+ background: linear-gradient(
65
+ 180deg,
66
+ rgba(99, 235, 255, 0.14) 0%,
67
+ rgba(99, 235, 255, 0.08) 100%
68
+ );
69
+ color: var(--accent);
70
+ box-shadow: inset 0 0 0 1px rgba(99, 235, 255, 0.12);
71
+ transition:
72
+ border-color 0.15s ease,
73
+ background 0.15s ease,
74
+ color 0.15s ease,
75
+ box-shadow 0.15s ease,
76
+ transform 0.15s ease;
77
+ }
78
+
79
+ .ac-btn-cyan:hover:not(:disabled) {
80
+ border-color: rgba(99, 235, 255, 0.7);
81
+ background: linear-gradient(
82
+ 180deg,
83
+ rgba(99, 235, 255, 0.24) 0%,
84
+ rgba(99, 235, 255, 0.12) 100%
85
+ );
86
+ color: #b9f8ff;
87
+ box-shadow:
88
+ inset 0 0 0 1px rgba(99, 235, 255, 0.2),
89
+ 0 0 12px rgba(99, 235, 255, 0.14);
90
+ }
91
+
92
+ .ac-btn-cyan:active:not(:disabled) {
93
+ transform: translateY(1px);
94
+ }
95
+
96
+ .ac-btn-cyan:disabled {
97
+ opacity: 0.5;
98
+ cursor: not-allowed;
99
+ }
100
+
101
+ .ac-btn-cyan-ghost {
102
+ border: 1px solid var(--accent-dim);
103
+ color: rgba(99, 235, 255, 0.75);
104
+ background: rgba(99, 235, 255, 0.04);
105
+ transition:
106
+ border-color 0.15s ease,
107
+ color 0.15s ease,
108
+ background 0.15s ease;
109
+ }
110
+
111
+ .ac-btn-cyan-ghost:hover {
112
+ border-color: rgba(99, 235, 255, 0.5);
113
+ color: #a4f3ff;
114
+ background: rgba(99, 235, 255, 0.08);
115
+ }
@@ -116,15 +116,23 @@ const GeneralTab = ({ onSwitchTab }) => {
116
116
  if (!syncCron) return;
117
117
  setSyncCronEnabled(syncCron.enabled !== false);
118
118
  setSyncCronSchedule(syncCron.schedule || "0 * * * *");
119
- setSyncCronChoice(syncCron.enabled === false ? "disabled" : syncCron.schedule || "0 * * * *");
119
+ setSyncCronChoice(
120
+ syncCron.enabled === false
121
+ ? "disabled"
122
+ : syncCron.schedule || "0 * * * *",
123
+ );
120
124
  }, [syncCron?.enabled, syncCron?.schedule]);
121
125
 
122
- const saveSyncCronSettings = async ({ enabled = syncCronEnabled, schedule = syncCronSchedule }) => {
126
+ const saveSyncCronSettings = async ({
127
+ enabled = syncCronEnabled,
128
+ schedule = syncCronSchedule,
129
+ }) => {
123
130
  if (savingSyncCron) return;
124
131
  setSavingSyncCron(true);
125
132
  try {
126
133
  const data = await updateSyncCron({ enabled, schedule });
127
- if (!data.ok) throw new Error(data.error || "Could not save sync settings");
134
+ if (!data.ok)
135
+ throw new Error(data.error || "Could not save sync settings");
128
136
  showToast("Sync schedule updated", "success");
129
137
  statusPoll.refresh();
130
138
  } catch (err) {
@@ -148,12 +156,26 @@ const GeneralTab = ({ onSwitchTab }) => {
148
156
  />
149
157
  <${Google} key=${googleKey} gatewayStatus=${gatewayStatus} />
150
158
 
151
- ${repo && html`
159
+ ${repo &&
160
+ html`
152
161
  <div class="bg-surface border border-border rounded-xl p-4">
153
162
  <div class="flex items-center justify-between gap-3">
154
163
  <div class="flex items-center gap-2 min-w-0">
155
- <svg class="w-4 h-4 text-gray-400" viewBox="0 0 16 16" fill="currentColor"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"/></svg>
156
- <a href="https://github.com/${repo}" target="_blank" class="text-sm text-gray-400 hover:text-gray-200 transition-colors truncate">${repo}</a>
164
+ <svg
165
+ class="w-4 h-4 text-gray-400"
166
+ viewBox="0 0 16 16"
167
+ fill="currentColor"
168
+ >
169
+ <path
170
+ d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27.68 0 1.36.09 2 .27 1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.013 8.013 0 0016 8c0-4.42-3.58-8-8-8z"
171
+ />
172
+ </svg>
173
+ <a
174
+ href="https://github.com/${repo}"
175
+ target="_blank"
176
+ class="text-sm text-gray-400 hover:text-gray-200 transition-colors truncate"
177
+ >${repo}</a
178
+ >
157
179
  </div>
158
180
  <div class="flex items-center gap-2 shrink-0">
159
181
  <span class="text-xs text-gray-400">Auto-sync</span>
@@ -164,7 +186,9 @@ const GeneralTab = ({ onSwitchTab }) => {
164
186
  const nextChoice = e.target.value;
165
187
  setSyncCronChoice(nextChoice);
166
188
  const nextEnabled = nextChoice !== "disabled";
167
- const nextSchedule = nextEnabled ? nextChoice : syncCronSchedule;
189
+ const nextSchedule = nextEnabled
190
+ ? nextChoice
191
+ : syncCronSchedule;
168
192
  setSyncCronEnabled(nextEnabled);
169
193
  setSyncCronSchedule(nextSchedule);
170
194
  saveSyncCronSettings({
@@ -176,7 +200,9 @@ const GeneralTab = ({ onSwitchTab }) => {
176
200
  class="appearance-none bg-black/30 border border-border rounded-lg pl-2.5 pr-9 py-1.5 text-xs text-gray-300 ${savingSyncCron
177
201
  ? "opacity-50 cursor-not-allowed"
178
202
  : ""}"
179
- title=${syncCron?.installed === false ? "Not Installed Yet" : syncCronStatusText}
203
+ title=${syncCron?.installed === false
204
+ ? "Not Installed Yet"
205
+ : syncCronStatusText}
180
206
  >
181
207
  <option value="disabled">Disabled</option>
182
208
  <option value="*/30 * * * *">Every 30 min</option>
@@ -195,7 +221,7 @@ const GeneralTab = ({ onSwitchTab }) => {
195
221
  <div class="bg-surface border border-border rounded-xl p-4">
196
222
  <div class="flex items-center justify-between">
197
223
  <div>
198
- <h2 class="font-semibold text-sm">Gateway Dashboard</h2>
224
+ <h2 class="font-semibold text-sm">OpenClaw Gateway Dashboard</h2>
199
225
  </div>
200
226
  <button
201
227
  onclick=${async () => {
@@ -203,18 +229,20 @@ const GeneralTab = ({ onSwitchTab }) => {
203
229
  setDashboardLoading(true);
204
230
  try {
205
231
  const data = await fetchDashboardUrl();
206
- console.log('[dashboard] response:', JSON.stringify(data));
207
- window.open(data.url || '/openclaw', '_blank');
232
+ console.log("[dashboard] response:", JSON.stringify(data));
233
+ window.open(data.url || "/openclaw", "_blank");
208
234
  } catch (err) {
209
- console.error('[dashboard] error:', err);
210
- window.open('/openclaw', '_blank');
235
+ console.error("[dashboard] error:", err);
236
+ window.open("/openclaw", "_blank");
211
237
  }
212
238
  setDashboardLoading(false);
213
239
  }}
214
240
  disabled=${dashboardLoading}
215
- class="text-xs px-2.5 py-1 rounded-lg border border-border text-gray-500 hover:text-gray-300 hover:border-gray-500 transition-colors ${dashboardLoading ? 'opacity-50 cursor-not-allowed' : ''}"
241
+ class="text-xs px-2.5 py-1 rounded-lg border border-border text-gray-500 hover:text-gray-300 hover:border-gray-500 transition-colors ${dashboardLoading
242
+ ? "opacity-50 cursor-not-allowed"
243
+ : ""}"
216
244
  >
217
- ${dashboardLoading ? 'Opening...' : 'Open'}
245
+ ${dashboardLoading ? "Opening..." : "Open"}
218
246
  </button>
219
247
  </div>
220
248
  <${DevicePairings}
@@ -257,18 +285,21 @@ function App() {
257
285
  useEffect(() => {
258
286
  if (!onboarded) return;
259
287
  let active = true;
260
- const check = async () => {
288
+ const check = async (refresh = false) => {
261
289
  try {
262
- const data = await fetchAlphaclawVersion(false);
290
+ const data = await fetchAlphaclawVersion(refresh);
263
291
  if (!active) return;
264
292
  setAcVersion(data.currentVersion || null);
265
293
  setAcLatest(data.latestVersion || null);
266
294
  setAcHasUpdate(!!data.hasUpdate);
267
295
  } catch {}
268
296
  };
269
- check();
270
- const id = setInterval(check, 5 * 60 * 1000);
271
- return () => { active = false; clearInterval(id); };
297
+ check(true);
298
+ const id = setInterval(() => check(false), 5 * 60 * 1000);
299
+ return () => {
300
+ active = false;
301
+ clearInterval(id);
302
+ };
272
303
  }, [onboarded]);
273
304
 
274
305
  const handleAcUpdate = async () => {
@@ -292,7 +323,10 @@ function App() {
292
323
  // Still loading onboard status
293
324
  if (onboarded === null) {
294
325
  return html`
295
- <div class="min-h-screen flex items-center justify-center" style="position: relative; z-index: 1">
326
+ <div
327
+ class="min-h-screen flex items-center justify-center"
328
+ style="position: relative; z-index: 1"
329
+ >
296
330
  <svg
297
331
  class="animate-spin h-6 w-6"
298
332
  style="color: var(--text-muted)"
@@ -320,7 +354,10 @@ function App() {
320
354
 
321
355
  if (!onboarded) {
322
356
  return html`
323
- <div class="min-h-screen flex justify-center pt-12 pb-8 px-4" style="position: relative; z-index: 1">
357
+ <div
358
+ class="min-h-screen flex justify-center pt-12 pb-8 px-4"
359
+ style="position: relative; z-index: 1"
360
+ >
324
361
  <${Welcome} onComplete=${() => setOnboarded(true)} />
325
362
  </div>
326
363
  <${ToastContainer} />
@@ -354,13 +391,17 @@ function App() {
354
391
  )}
355
392
  </nav>
356
393
  <div class="sidebar-footer">
357
- ${acHasUpdate && acLatest && !acDismissed ? html`
358
- <button
359
- onclick=${handleAcUpdate}
360
- disabled=${acUpdating}
361
- class="sidebar-update-btn"
362
- >${acUpdating ? "Updating..." : `Update to v${acLatest}`}</button>
363
- ` : null}
394
+ ${acHasUpdate && acLatest && !acDismissed
395
+ ? html`
396
+ <button
397
+ onclick=${handleAcUpdate}
398
+ disabled=${acUpdating}
399
+ class="sidebar-update-btn"
400
+ >
401
+ ${acUpdating ? "Updating..." : `Update to v${acLatest}`}
402
+ </button>
403
+ `
404
+ : null}
364
405
  </div>
365
406
  </div>
366
407
 
@@ -380,12 +421,26 @@ function App() {
380
421
 
381
422
  <div class="app-statusbar">
382
423
  <div class="statusbar-left">
383
- ${acVersion ? html`<span style="color: var(--text-muted)">v${acVersion}</span>` : null}
424
+ ${acVersion
425
+ ? html`<span style="color: var(--text-muted)">v${acVersion}</span>`
426
+ : null}
384
427
  </div>
385
428
  <div class="statusbar-right">
386
- <a href="https://docs.openclaw.ai" target="_blank" rel="noreferrer">docs</a>
387
- <a href="https://discord.com/invite/clawd" target="_blank" rel="noreferrer">discord</a>
388
- <a href="https://github.com/openclaw/openclaw" target="_blank" rel="noreferrer">github</a>
429
+ <a href="https://docs.openclaw.ai" target="_blank" rel="noreferrer"
430
+ >docs</a
431
+ >
432
+ <a
433
+ href="https://discord.com/invite/clawd"
434
+ target="_blank"
435
+ rel="noreferrer"
436
+ >discord</a
437
+ >
438
+ <a
439
+ href="https://github.com/openclaw/openclaw"
440
+ target="_blank"
441
+ rel="noreferrer"
442
+ >github</a
443
+ >
389
444
  </div>
390
445
  </div>
391
446
  </div>
@@ -322,13 +322,13 @@ export const CredentialsModal = ({ visible, onClose, onSaved }) => {
322
322
  <button
323
323
  onclick=${submit}
324
324
  disabled=${saving}
325
- class="flex-1 bg-green-500 text-black font-medium py-2 rounded-lg hover:opacity-85 transition-opacity text-sm"
325
+ class="flex-1 font-medium py-2 rounded-lg text-sm ac-btn-cyan"
326
326
  >
327
327
  ${saving ? "Saving..." : "Connect Google"}
328
328
  </button>
329
329
  <button
330
330
  onclick=${onClose}
331
- class="px-4 bg-gray-800 text-gray-300 py-2 rounded-lg hover:bg-gray-700 transition-colors text-sm"
331
+ class="px-4 py-2 rounded-lg text-sm ac-btn-cyan-ghost"
332
332
  >
333
333
  Cancel
334
334
  </button>
@@ -175,10 +175,7 @@ export function Google({ gatewayStatus }) {
175
175
  <button
176
176
  onclick=${() => startAuth(email)}
177
177
  disabled=${isAuthed && !scopesChanged}
178
- class="text-xs font-medium px-3 py-1.5 rounded-lg ${isAuthed &&
179
- !scopesChanged
180
- ? "bg-gray-600 text-gray-400 cursor-not-allowed"
181
- : "bg-white text-black hover:opacity-85"}"
178
+ class="text-xs font-medium px-3 py-1.5 rounded-lg ac-btn-cyan"
182
179
  >
183
180
  ${isAuthed ? "Update Permissions" : "Sign in with Google"}
184
181
  </button>
@@ -207,7 +204,7 @@ export function Google({ gatewayStatus }) {
207
204
  </p>
208
205
  <button
209
206
  onclick=${() => setModalOpen(true)}
210
- class="bg-white text-black text-sm font-medium px-4 py-2 rounded-lg hover:opacity-85"
207
+ class="text-sm font-medium px-4 py-2 rounded-lg ac-btn-cyan"
211
208
  >
212
209
  Set up Google
213
210
  </button>
@@ -59,8 +59,8 @@ export function ScopePicker({ scopes, onToggle, apiStatus, loading }) {
59
59
  <span class="text-sm">${s.icon} ${s.label}</span>
60
60
  <div class="flex items-center gap-2">
61
61
  ${apiIndicator}
62
- <button onclick=${() => onToggle(`${s.key}:read`)} class="scope-btn ${readOn ? 'active' : ''} text-xs px-2 py-0.5 rounded">Read</button>
63
- <button onclick=${() => onToggle(`${s.key}:write`)} class="scope-btn ${writeOn ? 'active' : ''} text-xs px-2 py-0.5 rounded">Write</button>
62
+ <button onclick=${() => onToggle(`${s.key}:read`)} class="scope-btn scope-btn-read ${readOn ? 'active' : ''} text-xs px-2 py-0.5 rounded">Read</button>
63
+ <button onclick=${() => onToggle(`${s.key}:write`)} class="scope-btn scope-btn-write ${writeOn ? 'active' : ''} text-xs px-2 py-0.5 rounded">Write</button>
64
64
  </div>
65
65
  </div>`;
66
66
  })}
@@ -91,7 +91,6 @@ const registerGoogleRoutes = ({
91
91
 
92
92
  const services = req.body.services || [
93
93
  "gmail:read",
94
- "gmail:write",
95
94
  "calendar:read",
96
95
  "calendar:write",
97
96
  "drive:read",
@@ -208,7 +207,7 @@ const registerGoogleRoutes = ({
208
207
  const email = req.query.email || "";
209
208
  const services = (
210
209
  req.query.services ||
211
- "gmail:read,gmail:write,calendar:read,calendar:write,drive:read,sheets:read,docs:read"
210
+ "gmail:read,calendar:read,calendar:write,drive:read,sheets:read,docs:read"
212
211
  )
213
212
  .split(",")
214
213
  .filter(Boolean);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chrysb/alphaclaw",
3
- "version": "0.1.15",
3
+ "version": "0.1.17",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },