@chrysb/alphaclaw 0.1.16 → 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.
- package/lib/public/css/theme.css +66 -1
- package/lib/public/js/app.js +84 -29
- package/lib/public/js/components/credentials-modal.js +2 -2
- package/lib/public/js/components/google.js +2 -5
- package/lib/public/js/components/scope-picker.js +2 -2
- package/lib/server/routes/google.js +1 -2
- package/package.json +1 -1
package/lib/public/css/theme.css
CHANGED
|
@@ -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 {
|
|
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
|
+
}
|
package/lib/public/js/app.js
CHANGED
|
@@ -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(
|
|
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 ({
|
|
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)
|
|
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 &&
|
|
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
|
|
156
|
-
|
|
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
|
|
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
|
|
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(
|
|
207
|
-
window.open(data.url ||
|
|
232
|
+
console.log("[dashboard] response:", JSON.stringify(data));
|
|
233
|
+
window.open(data.url || "/openclaw", "_blank");
|
|
208
234
|
} catch (err) {
|
|
209
|
-
console.error(
|
|
210
|
-
window.open(
|
|
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
|
|
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 ?
|
|
245
|
+
${dashboardLoading ? "Opening..." : "Open"}
|
|
218
246
|
</button>
|
|
219
247
|
</div>
|
|
220
248
|
<${DevicePairings}
|
|
@@ -268,7 +296,10 @@ function App() {
|
|
|
268
296
|
};
|
|
269
297
|
check(true);
|
|
270
298
|
const id = setInterval(() => check(false), 5 * 60 * 1000);
|
|
271
|
-
return () => {
|
|
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
|
|
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
|
|
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
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
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
|
|
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"
|
|
387
|
-
|
|
388
|
-
|
|
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
|
|
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
|
|
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
|
|
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="
|
|
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,
|
|
210
|
+
"gmail:read,calendar:read,calendar:write,drive:read,sheets:read,docs:read"
|
|
212
211
|
)
|
|
213
212
|
.split(",")
|
|
214
213
|
.filter(Boolean);
|