@chrysb/alphaclaw 0.5.6 → 0.5.7-beta.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/bin/alphaclaw.js +6 -1
- package/lib/public/css/agents.css +92 -0
- package/lib/public/css/explorer.css +101 -0
- package/lib/public/css/shell.css +15 -4
- package/lib/public/js/app.js +69 -3
- package/lib/public/js/components/action-button.js +5 -0
- package/lib/public/js/components/agents-tab/agent-bindings-section/helpers.js +76 -0
- package/lib/public/js/components/agents-tab/agent-bindings-section/index.js +490 -0
- package/lib/public/js/components/agents-tab/agent-bindings-section/use-agent-bindings.js +256 -0
- package/lib/public/js/components/agents-tab/agent-detail-panel.js +74 -0
- package/lib/public/js/components/agents-tab/agent-identity-section.js +175 -0
- package/lib/public/js/components/agents-tab/agent-overview/index.js +53 -0
- package/lib/public/js/components/agents-tab/agent-overview/manage-card.js +44 -0
- package/lib/public/js/components/agents-tab/agent-overview/model-card.js +158 -0
- package/lib/public/js/components/agents-tab/agent-overview/use-model-card.js +169 -0
- package/lib/public/js/components/agents-tab/agent-overview/use-workspace-card.js +45 -0
- package/lib/public/js/components/agents-tab/agent-overview/workspace-card.js +47 -0
- package/lib/public/js/components/agents-tab/agent-pairing-section.js +265 -0
- package/lib/public/js/components/agents-tab/create-agent-modal.js +189 -0
- package/lib/public/js/components/agents-tab/create-channel-modal.js +323 -0
- package/lib/public/js/components/agents-tab/delete-agent-dialog.js +50 -0
- package/lib/public/js/components/agents-tab/edit-agent-modal.js +109 -0
- package/lib/public/js/components/agents-tab/index.js +148 -0
- package/lib/public/js/components/agents-tab/use-agents.js +89 -0
- package/lib/public/js/components/channel-account-status-badge.js +35 -0
- package/lib/public/js/components/channel-operations-panel.js +33 -0
- package/lib/public/js/components/channels.js +545 -60
- package/lib/public/js/components/envars.js +25 -4
- package/lib/public/js/components/general/index.js +21 -11
- package/lib/public/js/components/general/use-general-tab.js +78 -16
- package/lib/public/js/components/google/gmail-setup-wizard.js +1 -3
- package/lib/public/js/components/google/index.js +28 -30
- package/lib/public/js/components/icons.js +37 -0
- package/lib/public/js/components/models-tab/index.js +58 -224
- package/lib/public/js/components/models-tab/model-picker.js +212 -0
- package/lib/public/js/components/models-tab/use-models.js +17 -14
- package/lib/public/js/components/onboarding/use-welcome-pairing.js +4 -4
- package/lib/public/js/components/onboarding/welcome-pairing-step.js +2 -2
- package/lib/public/js/components/overflow-menu.js +122 -0
- package/lib/public/js/components/pairings.js +36 -8
- package/lib/public/js/components/routes/agents-route.js +27 -0
- package/lib/public/js/components/routes/general-route.js +2 -0
- package/lib/public/js/components/routes/index.js +1 -0
- package/lib/public/js/components/routes/telegram-route.js +2 -2
- package/lib/public/js/components/secret-input.js +8 -1
- package/lib/public/js/components/sidebar.js +64 -26
- package/lib/public/js/components/telegram-workspace/index.js +175 -74
- package/lib/public/js/components/telegram-workspace/manage.js +83 -10
- package/lib/public/js/components/telegram-workspace/onboarding.js +9 -8
- package/lib/public/js/components/webhooks.js +43 -18
- package/lib/public/js/hooks/use-app-shell-controller.js +7 -0
- package/lib/public/js/hooks/use-browse-navigation.js +8 -5
- package/lib/public/js/hooks/use-destination-session-selection.js +8 -1
- package/lib/public/js/lib/api.js +163 -9
- package/lib/public/js/lib/app-navigation.js +2 -1
- package/lib/public/js/lib/channel-create-operation.js +102 -0
- package/lib/public/js/lib/format.js +14 -0
- package/lib/public/js/lib/sse.js +51 -0
- package/lib/public/js/lib/telegram-api.js +38 -18
- package/lib/public/setup.html +1 -0
- package/lib/public/shared/browse-file-policies.json +0 -1
- package/lib/server/agents/service.js +1478 -0
- package/lib/server/constants.js +2 -2
- package/lib/server/env.js +3 -1
- package/lib/server/gateway.js +104 -20
- package/lib/server/gmail-watch.js +29 -2
- package/lib/server/onboarding/import/import-applier.js +0 -1
- package/lib/server/onboarding/index.js +0 -6
- package/lib/server/onboarding/workspace.js +73 -38
- package/lib/server/openclaw-config.js +23 -0
- package/lib/server/operation-events.js +141 -0
- package/lib/server/routes/agents.js +266 -0
- package/lib/server/routes/pairings.js +135 -25
- package/lib/server/routes/system.js +90 -10
- package/lib/server/routes/telegram.js +247 -51
- package/lib/server/telegram-workspace.js +61 -10
- package/lib/server/topic-registry.js +66 -7
- package/lib/server/watchdog.js +39 -1
- package/lib/server/webhooks.js +60 -12
- package/lib/server.js +21 -7
- package/lib/setup/core-prompts/AGENTS.md +6 -5
- package/lib/setup/core-prompts/TOOLS.md +1 -8
- package/package.json +1 -1
- package/lib/setup/skills/control-ui/SKILL.md +0 -62
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import { useEffect, useRef } from "https://esm.sh/preact/hooks";
|
|
3
|
+
import htm from "https://esm.sh/htm";
|
|
4
|
+
|
|
5
|
+
const html = htm.bind(h);
|
|
6
|
+
|
|
7
|
+
const VerticalDotsIcon = ({ className = "" }) => html`
|
|
8
|
+
<svg class=${className} width="16" height="16" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true">
|
|
9
|
+
<circle cx="8" cy="3" r="1.5" />
|
|
10
|
+
<circle cx="8" cy="8" r="1.5" />
|
|
11
|
+
<circle cx="8" cy="13" r="1.5" />
|
|
12
|
+
</svg>
|
|
13
|
+
`;
|
|
14
|
+
|
|
15
|
+
export const OverflowMenu = ({
|
|
16
|
+
open = false,
|
|
17
|
+
onToggle = () => {},
|
|
18
|
+
onClose = () => {},
|
|
19
|
+
ariaLabel = "Open menu",
|
|
20
|
+
title = "",
|
|
21
|
+
menuRef = null,
|
|
22
|
+
renderTrigger = null,
|
|
23
|
+
triggerDisabled = false,
|
|
24
|
+
children = null,
|
|
25
|
+
}) => {
|
|
26
|
+
const internalMenuRef = useRef(null);
|
|
27
|
+
const setMenuNodeRef = (node) => {
|
|
28
|
+
internalMenuRef.current = node;
|
|
29
|
+
if (typeof menuRef === "function") {
|
|
30
|
+
menuRef(node);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
if (menuRef && typeof menuRef === "object") {
|
|
34
|
+
menuRef.current = node;
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (!open) return undefined;
|
|
40
|
+
const handleWindowClick = (event) => {
|
|
41
|
+
const root = internalMenuRef.current;
|
|
42
|
+
if (!root) return;
|
|
43
|
+
if (root.contains(event.target)) return;
|
|
44
|
+
onClose(event);
|
|
45
|
+
};
|
|
46
|
+
window.addEventListener("click", handleWindowClick);
|
|
47
|
+
return () => window.removeEventListener("click", handleWindowClick);
|
|
48
|
+
}, [open, onClose]);
|
|
49
|
+
|
|
50
|
+
return html`
|
|
51
|
+
<div class="brand-menu" ref=${setMenuNodeRef}>
|
|
52
|
+
${typeof renderTrigger === "function"
|
|
53
|
+
? renderTrigger({
|
|
54
|
+
open,
|
|
55
|
+
onToggle: (event) => {
|
|
56
|
+
event.stopPropagation();
|
|
57
|
+
onToggle(event);
|
|
58
|
+
},
|
|
59
|
+
ariaLabel,
|
|
60
|
+
title: title || ariaLabel,
|
|
61
|
+
})
|
|
62
|
+
: html`
|
|
63
|
+
<button
|
|
64
|
+
type="button"
|
|
65
|
+
class="brand-menu-trigger"
|
|
66
|
+
aria-label=${ariaLabel}
|
|
67
|
+
aria-expanded=${open ? "true" : "false"}
|
|
68
|
+
title=${title || ariaLabel}
|
|
69
|
+
disabled=${triggerDisabled}
|
|
70
|
+
onclick=${(event) => {
|
|
71
|
+
event.stopPropagation();
|
|
72
|
+
onToggle(event);
|
|
73
|
+
}}
|
|
74
|
+
>
|
|
75
|
+
<${VerticalDotsIcon} />
|
|
76
|
+
</button>
|
|
77
|
+
`}
|
|
78
|
+
${open
|
|
79
|
+
? html`
|
|
80
|
+
<div class="brand-dropdown" onclick=${(event) => event.stopPropagation()}>
|
|
81
|
+
${children}
|
|
82
|
+
</div>
|
|
83
|
+
`
|
|
84
|
+
: null}
|
|
85
|
+
</div>
|
|
86
|
+
`;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
export const OverflowMenuItem = ({
|
|
90
|
+
children = null,
|
|
91
|
+
onClick = () => {},
|
|
92
|
+
className = "",
|
|
93
|
+
iconSrc = "",
|
|
94
|
+
disabled = false,
|
|
95
|
+
}) => html`
|
|
96
|
+
<button
|
|
97
|
+
type="button"
|
|
98
|
+
class=${`brand-dropdown-item ${className} ${disabled
|
|
99
|
+
? "opacity-50 cursor-not-allowed"
|
|
100
|
+
: ""}`.trim()}
|
|
101
|
+
disabled=${disabled}
|
|
102
|
+
onclick=${(event) => {
|
|
103
|
+
event.stopPropagation();
|
|
104
|
+
if (disabled) return;
|
|
105
|
+
onClick(event);
|
|
106
|
+
}}
|
|
107
|
+
>
|
|
108
|
+
${iconSrc
|
|
109
|
+
? html`
|
|
110
|
+
<span class="flex w-full items-center gap-2 leading-none">
|
|
111
|
+
<img
|
|
112
|
+
src=${iconSrc}
|
|
113
|
+
alt=""
|
|
114
|
+
class="block w-4 h-4 rounded-sm"
|
|
115
|
+
aria-hidden="true"
|
|
116
|
+
/>
|
|
117
|
+
<span>${children}</span>
|
|
118
|
+
</span>
|
|
119
|
+
`
|
|
120
|
+
: children}
|
|
121
|
+
</button>
|
|
122
|
+
`;
|
|
@@ -4,39 +4,45 @@ import htm from 'https://esm.sh/htm';
|
|
|
4
4
|
import { ActionButton } from './action-button.js';
|
|
5
5
|
const html = htm.bind(h);
|
|
6
6
|
|
|
7
|
-
const PairingRow = ({ p, onApprove, onReject }) => {
|
|
7
|
+
export const PairingRow = ({ p, onApprove, onReject }) => {
|
|
8
8
|
const [busy, setBusy] = useState(null);
|
|
9
9
|
|
|
10
10
|
const handle = async (action) => {
|
|
11
11
|
setBusy(action);
|
|
12
12
|
try {
|
|
13
|
-
if (action === "approve") await onApprove(p.id, p.channel);
|
|
14
|
-
else await onReject(p.id, p.channel);
|
|
13
|
+
if (action === "approve") await onApprove(p.id, p.channel, p.accountId);
|
|
14
|
+
else await onReject(p.id, p.channel, p.accountId);
|
|
15
15
|
} catch {
|
|
16
16
|
setBusy(null);
|
|
17
17
|
}
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
const label = (p.channel || 'unknown').charAt(0).toUpperCase() + (p.channel || '').slice(1);
|
|
21
|
+
const accountId = String(p.accountId || "").trim();
|
|
22
|
+
const accountName = String(p.accountName || "").trim();
|
|
23
|
+
const accountSuffix =
|
|
24
|
+
accountId && accountId !== "default"
|
|
25
|
+
? ` · ${accountName || accountId}`
|
|
26
|
+
: "";
|
|
21
27
|
|
|
22
28
|
if (busy === "approve") {
|
|
23
29
|
return html`
|
|
24
30
|
<div class="bg-black/30 rounded-lg p-3 mb-2 flex items-center gap-2">
|
|
25
31
|
<span class="text-green-400 text-sm">Approved</span>
|
|
26
|
-
<span class="text-gray-500 text-xs">${label} · ${p.code || p.id || '?'}</span>
|
|
32
|
+
<span class="text-gray-500 text-xs">${label}${accountSuffix} · ${p.code || p.id || '?'}</span>
|
|
27
33
|
</div>`;
|
|
28
34
|
}
|
|
29
35
|
if (busy === "reject") {
|
|
30
36
|
return html`
|
|
31
37
|
<div class="bg-black/30 rounded-lg p-3 mb-2 flex items-center gap-2">
|
|
32
38
|
<span class="text-gray-400 text-sm">Rejected</span>
|
|
33
|
-
<span class="text-gray-500 text-xs">${label} · ${p.code || p.id || '?'}</span>
|
|
39
|
+
<span class="text-gray-500 text-xs">${label}${accountSuffix} · ${p.code || p.id || '?'}</span>
|
|
34
40
|
</div>`;
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
return html`
|
|
38
44
|
<div class="bg-black/30 rounded-lg p-3 mb-2">
|
|
39
|
-
<div class="font-medium text-sm mb-2">${label} · <code class="text-gray-400">${p.code || p.id || '?'}</code></div>
|
|
45
|
+
<div class="font-medium text-sm mb-2">${label}${accountSuffix} · <code class="text-gray-400">${p.code || p.id || '?'}</code></div>
|
|
40
46
|
<div class="flex gap-2">
|
|
41
47
|
<${ActionButton}
|
|
42
48
|
onClick=${() => handle("approve")}
|
|
@@ -60,11 +66,29 @@ const ALL_CHANNELS = ['telegram', 'discord'];
|
|
|
60
66
|
|
|
61
67
|
const capitalize = (s) => s.charAt(0).toUpperCase() + s.slice(1);
|
|
62
68
|
|
|
63
|
-
export function Pairings({
|
|
69
|
+
export function Pairings({
|
|
70
|
+
pending,
|
|
71
|
+
channels,
|
|
72
|
+
visible,
|
|
73
|
+
onApprove,
|
|
74
|
+
onReject,
|
|
75
|
+
statusRefreshing = false,
|
|
76
|
+
}) {
|
|
64
77
|
if (!visible) return null;
|
|
65
78
|
|
|
66
79
|
const unpaired = ALL_CHANNELS
|
|
67
|
-
.filter((ch) =>
|
|
80
|
+
.filter((ch) => {
|
|
81
|
+
const info = channels?.[ch];
|
|
82
|
+
if (!info) return false;
|
|
83
|
+
const accounts =
|
|
84
|
+
info.accounts && typeof info.accounts === "object" ? info.accounts : {};
|
|
85
|
+
if (Object.keys(accounts).length > 0) {
|
|
86
|
+
return Object.values(accounts).some(
|
|
87
|
+
(acc) => acc && acc.status !== "paired",
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
return info.status !== "paired";
|
|
91
|
+
})
|
|
68
92
|
.map(capitalize);
|
|
69
93
|
|
|
70
94
|
const channelList = unpaired.length <= 2
|
|
@@ -78,6 +102,10 @@ export function Pairings({ pending, channels, visible, onApprove, onReject }) {
|
|
|
78
102
|
? html`<div>
|
|
79
103
|
${pending.map(p => html`<${PairingRow} key=${p.id} p=${p} onApprove=${onApprove} onReject=${onReject} />`)}
|
|
80
104
|
</div>`
|
|
105
|
+
: statusRefreshing
|
|
106
|
+
? html`<div class="text-center py-4 space-y-2">
|
|
107
|
+
<p class="text-gray-300 text-sm">Updating pairing status...</p>
|
|
108
|
+
</div>`
|
|
81
109
|
: html`<div class="text-center py-4 space-y-2">
|
|
82
110
|
<div class="text-3xl">💬</div>
|
|
83
111
|
<p class="text-gray-300 text-sm">Send a message to your bot on ${channelList}</p>
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import htm from "https://esm.sh/htm";
|
|
3
|
+
import { AgentsTab } from "../agents-tab/index.js";
|
|
4
|
+
|
|
5
|
+
const html = htm.bind(h);
|
|
6
|
+
|
|
7
|
+
export const AgentsRoute = ({
|
|
8
|
+
agents = [],
|
|
9
|
+
loading = false,
|
|
10
|
+
saving = false,
|
|
11
|
+
agentsActions = {},
|
|
12
|
+
selectedAgentId = "",
|
|
13
|
+
onSelectAgent = () => {},
|
|
14
|
+
onNavigateToBrowseFile = () => {},
|
|
15
|
+
onSetLocation = () => {},
|
|
16
|
+
}) => html`
|
|
17
|
+
<${AgentsTab}
|
|
18
|
+
agents=${agents}
|
|
19
|
+
loading=${loading}
|
|
20
|
+
saving=${saving}
|
|
21
|
+
agentsActions=${agentsActions}
|
|
22
|
+
selectedAgentId=${selectedAgentId}
|
|
23
|
+
onSelectAgent=${onSelectAgent}
|
|
24
|
+
onNavigateToBrowseFile=${onNavigateToBrowseFile}
|
|
25
|
+
onSetLocation=${onSetLocation}
|
|
26
|
+
/>
|
|
27
|
+
`;
|
|
@@ -8,6 +8,7 @@ export const GeneralRoute = ({
|
|
|
8
8
|
statusData = null,
|
|
9
9
|
watchdogData = null,
|
|
10
10
|
doctorStatusData = null,
|
|
11
|
+
agents = [],
|
|
11
12
|
doctorWarningDismissedUntilMs = 0,
|
|
12
13
|
onRefreshStatuses = () => {},
|
|
13
14
|
onSetLocation = () => {},
|
|
@@ -26,6 +27,7 @@ export const GeneralRoute = ({
|
|
|
26
27
|
statusData=${statusData}
|
|
27
28
|
watchdogData=${watchdogData}
|
|
28
29
|
doctorStatusData=${doctorStatusData}
|
|
30
|
+
agents=${agents}
|
|
29
31
|
doctorWarningDismissedUntilMs=${doctorWarningDismissedUntilMs}
|
|
30
32
|
onRefreshStatuses=${onRefreshStatuses}
|
|
31
33
|
onSwitchTab=${(nextTab) => onSetLocation(`/${nextTab}`)}
|
|
@@ -4,8 +4,8 @@ import { TelegramWorkspace } from "../telegram-workspace/index.js";
|
|
|
4
4
|
|
|
5
5
|
const html = htm.bind(h);
|
|
6
6
|
|
|
7
|
-
export const TelegramRoute = ({ onBack = () => {} }) => html`
|
|
7
|
+
export const TelegramRoute = ({ accountId = "default", onBack = () => {} }) => html`
|
|
8
8
|
<div class="pt-4">
|
|
9
|
-
<${TelegramWorkspace} onBack=${onBack} />
|
|
9
|
+
<${TelegramWorkspace} key=${accountId} accountId=${accountId} onBack=${onBack} />
|
|
10
10
|
</div>
|
|
11
11
|
`;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { h } from "https://esm.sh/preact";
|
|
2
2
|
import { useState } from "https://esm.sh/preact/hooks";
|
|
3
3
|
import htm from "https://esm.sh/htm";
|
|
4
|
+
import { LoadingSpinner } from "./loading-spinner.js";
|
|
4
5
|
const html = htm.bind(h);
|
|
5
6
|
|
|
6
7
|
/**
|
|
@@ -17,10 +18,12 @@ export const SecretInput = ({
|
|
|
17
18
|
placeholder = "",
|
|
18
19
|
inputClass = "",
|
|
19
20
|
disabled = false,
|
|
21
|
+
loading = false,
|
|
20
22
|
isSecret = true,
|
|
21
23
|
}) => {
|
|
22
24
|
const [visible, setVisible] = useState(false);
|
|
23
25
|
const showToggle = isSecret;
|
|
26
|
+
const isDisabled = disabled || loading;
|
|
24
27
|
|
|
25
28
|
return html`
|
|
26
29
|
<div class="flex-1 min-w-0 flex items-center gap-1">
|
|
@@ -30,14 +33,18 @@ export const SecretInput = ({
|
|
|
30
33
|
placeholder=${placeholder}
|
|
31
34
|
onInput=${onInput}
|
|
32
35
|
onBlur=${onBlur}
|
|
33
|
-
disabled=${
|
|
36
|
+
disabled=${isDisabled}
|
|
34
37
|
class=${inputClass}
|
|
35
38
|
autocomplete="off"
|
|
36
39
|
/>
|
|
40
|
+
${loading
|
|
41
|
+
? html`<${LoadingSpinner} className="h-3 w-3 text-gray-500 shrink-0" />`
|
|
42
|
+
: null}
|
|
37
43
|
${showToggle
|
|
38
44
|
? html`<button
|
|
39
45
|
type="button"
|
|
40
46
|
onclick=${() => setVisible((v) => !v)}
|
|
47
|
+
disabled=${isDisabled}
|
|
41
48
|
class="text-gray-500 hover:text-gray-300 px-1 text-xs shrink-0"
|
|
42
49
|
>
|
|
43
50
|
${visible ? "Hide" : "Show"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { h } from "https://esm.sh/preact";
|
|
2
2
|
import { useEffect, useRef, useState } from "https://esm.sh/preact/hooks";
|
|
3
3
|
import htm from "https://esm.sh/htm";
|
|
4
|
-
import { HomeLineIcon, FolderLineIcon } from "./icons.js";
|
|
4
|
+
import { AddLineIcon, HomeLineIcon, FolderLineIcon } from "./icons.js";
|
|
5
5
|
import { FileTree } from "./file-tree.js";
|
|
6
|
+
import { OverflowMenu, OverflowMenuItem } from "./overflow-menu.js";
|
|
6
7
|
import { UpdateActionButton } from "./update-action-button.js";
|
|
7
8
|
import { SidebarGitPanel } from "./sidebar-git-panel.js";
|
|
8
9
|
import { readUiSettings, writeUiSettings } from "../lib/ui-settings.js";
|
|
@@ -49,6 +50,10 @@ export const AppSidebar = ({
|
|
|
49
50
|
acLatest = "",
|
|
50
51
|
acUpdating = false,
|
|
51
52
|
onAcUpdate = () => {},
|
|
53
|
+
agents = [],
|
|
54
|
+
selectedAgentId = "",
|
|
55
|
+
onSelectAgent = () => {},
|
|
56
|
+
onAddAgent = () => {},
|
|
52
57
|
}) => {
|
|
53
58
|
const browseLayoutRef = useRef(null);
|
|
54
59
|
const browseBottomPanelRef = useRef(null);
|
|
@@ -123,36 +128,27 @@ export const AppSidebar = ({
|
|
|
123
128
|
setIsResizingBrowsePanels(true);
|
|
124
129
|
};
|
|
125
130
|
|
|
131
|
+
const setupSection = navSections.find((section) => section.label === "Setup") || null;
|
|
132
|
+
const remainingSections = navSections.filter((section) => section.label !== "Setup");
|
|
133
|
+
|
|
126
134
|
return html`
|
|
127
135
|
<div class=${`app-sidebar ${mobileSidebarOpen ? "mobile-open" : ""}`}>
|
|
128
136
|
<div class="sidebar-brand">
|
|
129
137
|
<img src="./img/logo.svg" alt="" width="20" height="20" />
|
|
130
138
|
<span><span style="color: var(--accent)">alpha</span>claw</span>
|
|
131
139
|
${authEnabled && html`
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
${menuOpen && html`
|
|
145
|
-
<div class="brand-dropdown">
|
|
146
|
-
<a
|
|
147
|
-
href="#"
|
|
148
|
-
onclick=${(event) => {
|
|
149
|
-
event.preventDefault();
|
|
150
|
-
onLogout();
|
|
151
|
-
}}
|
|
152
|
-
>Log out</a>
|
|
153
|
-
</div>
|
|
154
|
-
`}
|
|
155
|
-
</div>
|
|
140
|
+
<${OverflowMenu}
|
|
141
|
+
open=${menuOpen}
|
|
142
|
+
onToggle=${onToggleMenu}
|
|
143
|
+
onClose=${onToggleMenu}
|
|
144
|
+
ariaLabel="Menu"
|
|
145
|
+
title="Menu"
|
|
146
|
+
menuRef=${menuRef}
|
|
147
|
+
>
|
|
148
|
+
<${OverflowMenuItem} onClick=${() => onLogout()}>
|
|
149
|
+
Log out
|
|
150
|
+
</${OverflowMenuItem}>
|
|
151
|
+
</${OverflowMenu}>
|
|
156
152
|
`}
|
|
157
153
|
</div>
|
|
158
154
|
<div class="sidebar-tabs">
|
|
@@ -181,7 +177,49 @@ export const AppSidebar = ({
|
|
|
181
177
|
minHeight: 0,
|
|
182
178
|
}}
|
|
183
179
|
>
|
|
184
|
-
${
|
|
180
|
+
${setupSection
|
|
181
|
+
? html`
|
|
182
|
+
<div class="sidebar-label">Menu</div>
|
|
183
|
+
<nav class="sidebar-nav">
|
|
184
|
+
${setupSection.items.map(
|
|
185
|
+
(item) => html`
|
|
186
|
+
<a
|
|
187
|
+
class=${selectedNavId === item.id ? "active" : ""}
|
|
188
|
+
onclick=${() => onSelectNavItem(item.id)}
|
|
189
|
+
>
|
|
190
|
+
${item.label}
|
|
191
|
+
</a>
|
|
192
|
+
`,
|
|
193
|
+
)}
|
|
194
|
+
</nav>
|
|
195
|
+
`
|
|
196
|
+
: null}
|
|
197
|
+
<div class="sidebar-agents-header">
|
|
198
|
+
<div class="sidebar-label sidebar-agents-label">Agents</div>
|
|
199
|
+
<button
|
|
200
|
+
type="button"
|
|
201
|
+
class="sidebar-agents-add-button"
|
|
202
|
+
onclick=${onAddAgent}
|
|
203
|
+
title="Add agent"
|
|
204
|
+
aria-label="Add agent"
|
|
205
|
+
>
|
|
206
|
+
<${AddLineIcon} className="sidebar-agents-add-icon" />
|
|
207
|
+
</button>
|
|
208
|
+
</div>
|
|
209
|
+
<div class="sidebar-agents-list">
|
|
210
|
+
${agents.map(
|
|
211
|
+
(agent) => html`
|
|
212
|
+
<button
|
|
213
|
+
key=${agent.id}
|
|
214
|
+
class=${`sidebar-agent-item ${selectedAgentId === agent.id ? "active" : ""}`}
|
|
215
|
+
onclick=${() => onSelectAgent(agent.id)}
|
|
216
|
+
>
|
|
217
|
+
<span class="sidebar-agent-name">${agent.name || agent.id}</span>
|
|
218
|
+
</button>
|
|
219
|
+
`,
|
|
220
|
+
)}
|
|
221
|
+
</div>
|
|
222
|
+
${remainingSections.map(
|
|
185
223
|
(section) => html`
|
|
186
224
|
<div class="sidebar-label">${section.label}</div>
|
|
187
225
|
<nav class="sidebar-nav">
|