@chrysb/alphaclaw 0.6.2-beta.5 → 0.6.2-beta.6
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/cron.css +91 -39
- package/lib/public/js/components/cron-tab/cron-calendar.js +287 -164
- package/lib/public/js/components/cron-tab/cron-insights-panel.js +325 -0
- package/lib/public/js/components/cron-tab/cron-job-detail.js +38 -363
- package/lib/public/js/components/cron-tab/cron-job-settings-card.js +233 -0
- package/lib/public/js/components/cron-tab/cron-overview.js +40 -19
- package/lib/public/js/components/cron-tab/cron-prompt-editor.js +173 -0
- package/lib/public/js/components/cron-tab/cron-run-history-panel.js +69 -56
- package/lib/public/js/components/cron-tab/cron-runs-trend-card.js +20 -2
- package/lib/public/js/components/cron-tab/index.js +170 -78
- package/lib/public/js/components/file-viewer/editor-surface.js +5 -1
- package/lib/public/js/components/file-viewer/use-editor-line-number-sync.js +36 -0
- package/lib/public/js/components/file-viewer/use-file-viewer.js +7 -23
- package/lib/public/js/components/file-viewer/utils.js +1 -5
- package/lib/public/js/components/onboarding/welcome-pairing-step.js +88 -59
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { h } from "https://esm.sh/preact";
|
|
2
|
+
import { useEffect, useMemo, useRef, useState } from "https://esm.sh/preact/hooks";
|
|
2
3
|
import htm from "https://esm.sh/htm";
|
|
3
4
|
import { ActionButton } from "../action-button.js";
|
|
4
5
|
import { PageHeader } from "../page-header.js";
|
|
@@ -11,94 +12,185 @@ import { useCronTab } from "./use-cron-tab.js";
|
|
|
11
12
|
const html = htm.bind(h);
|
|
12
13
|
|
|
13
14
|
export const CronTab = ({ jobId = "", onSetLocation = () => {} }) => {
|
|
14
|
-
const {
|
|
15
|
+
const { state, actions } = useCronTab({ jobId, onSetLocation });
|
|
16
|
+
const [showJobSelector, setShowJobSelector] = useState(false);
|
|
17
|
+
const selectorShellRef = useRef(null);
|
|
15
18
|
const isAllJobsSelected = state.selectedRouteKey === kAllCronJobsRouteKey;
|
|
16
19
|
const noJobs = state.jobs.length === 0;
|
|
20
|
+
const selectedJob = state.selectedJob;
|
|
21
|
+
const selectedJobLabel = useMemo(() => {
|
|
22
|
+
if (isAllJobsSelected) return "All jobs";
|
|
23
|
+
const selectedJob = state.jobs.find(
|
|
24
|
+
(job) => String(job?.id || "") === String(state.selectedRouteKey || ""),
|
|
25
|
+
);
|
|
26
|
+
return String(selectedJob?.name || selectedJob?.id || "All jobs");
|
|
27
|
+
}, [isAllJobsSelected, state.jobs, state.selectedRouteKey]);
|
|
28
|
+
const hasUnsavedDetailChanges = useMemo(() => {
|
|
29
|
+
if (isAllJobsSelected || !selectedJob) return false;
|
|
30
|
+
const sessionTarget = String(
|
|
31
|
+
state.routingDraft?.sessionTarget || selectedJob?.sessionTarget || "main",
|
|
32
|
+
);
|
|
33
|
+
const wakeMode = String(
|
|
34
|
+
state.routingDraft?.wakeMode || selectedJob?.wakeMode || "now",
|
|
35
|
+
);
|
|
36
|
+
const deliveryMode = String(
|
|
37
|
+
state.routingDraft?.deliveryMode || selectedJob?.delivery?.mode || "none",
|
|
38
|
+
);
|
|
39
|
+
const currentSessionTarget = String(selectedJob?.sessionTarget || "main");
|
|
40
|
+
const currentWakeMode = String(selectedJob?.wakeMode || "now");
|
|
41
|
+
const currentDeliveryMode = String(selectedJob?.delivery?.mode || "none");
|
|
42
|
+
const isRoutingDirty =
|
|
43
|
+
sessionTarget !== currentSessionTarget ||
|
|
44
|
+
wakeMode !== currentWakeMode ||
|
|
45
|
+
deliveryMode !== currentDeliveryMode;
|
|
46
|
+
const isPromptDirty = state.promptValue !== state.savedPromptValue;
|
|
47
|
+
return isRoutingDirty || isPromptDirty;
|
|
48
|
+
}, [
|
|
49
|
+
isAllJobsSelected,
|
|
50
|
+
selectedJob,
|
|
51
|
+
state.promptValue,
|
|
52
|
+
state.routingDraft?.deliveryMode,
|
|
53
|
+
state.routingDraft?.sessionTarget,
|
|
54
|
+
state.routingDraft?.wakeMode,
|
|
55
|
+
state.savedPromptValue,
|
|
56
|
+
]);
|
|
57
|
+
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!showJobSelector) return () => {};
|
|
60
|
+
const handlePointerDown = (event) => {
|
|
61
|
+
if (selectorShellRef.current?.contains(event.target)) return;
|
|
62
|
+
setShowJobSelector(false);
|
|
63
|
+
};
|
|
64
|
+
const handleKeyDown = (event) => {
|
|
65
|
+
if (event.key !== "Escape") return;
|
|
66
|
+
setShowJobSelector(false);
|
|
67
|
+
};
|
|
68
|
+
window.addEventListener("pointerdown", handlePointerDown);
|
|
69
|
+
window.addEventListener("keydown", handleKeyDown);
|
|
70
|
+
return () => {
|
|
71
|
+
window.removeEventListener("pointerdown", handlePointerDown);
|
|
72
|
+
window.removeEventListener("keydown", handleKeyDown);
|
|
73
|
+
};
|
|
74
|
+
}, [showJobSelector]);
|
|
75
|
+
|
|
76
|
+
const handleSelectAllJobs = () => {
|
|
77
|
+
actions.selectAllJobs();
|
|
78
|
+
setShowJobSelector(false);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const handleSelectJob = (nextJobId) => {
|
|
82
|
+
actions.selectJob(nextJobId);
|
|
83
|
+
setShowJobSelector(false);
|
|
84
|
+
};
|
|
17
85
|
|
|
18
86
|
return html`
|
|
19
87
|
<div class="cron-tab-shell">
|
|
20
88
|
<div class="cron-tab-header">
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
89
|
+
<div class="cron-tab-header-content">
|
|
90
|
+
<${PageHeader}
|
|
91
|
+
leading=${html`
|
|
92
|
+
<div class="cron-tab-selector-shell" ref=${selectorShellRef}>
|
|
93
|
+
<button
|
|
94
|
+
type="button"
|
|
95
|
+
class=${`cron-tab-selector-toggle ${showJobSelector ? "is-open" : ""}`}
|
|
96
|
+
onClick=${() => setShowJobSelector((value) => !value)}
|
|
97
|
+
aria-expanded=${showJobSelector}
|
|
98
|
+
aria-haspopup="listbox"
|
|
99
|
+
>
|
|
100
|
+
<span class="cron-tab-selector-title">${selectedJobLabel}</span>
|
|
101
|
+
<span class="cron-tab-selector-caret">▾</span>
|
|
102
|
+
</button>
|
|
103
|
+
${showJobSelector
|
|
104
|
+
? html`
|
|
105
|
+
<div class="cron-tab-selector-dropdown">
|
|
106
|
+
<${CronJobList}
|
|
107
|
+
jobs=${state.jobs}
|
|
108
|
+
selectedRouteKey=${state.selectedRouteKey}
|
|
109
|
+
onSelectAllJobs=${handleSelectAllJobs}
|
|
110
|
+
onSelectJob=${handleSelectJob}
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
`
|
|
114
|
+
: null}
|
|
115
|
+
</div>
|
|
116
|
+
`}
|
|
117
|
+
actions=${html`
|
|
118
|
+
${isAllJobsSelected || noJobs
|
|
119
|
+
? html`
|
|
120
|
+
<${ActionButton}
|
|
121
|
+
onClick=${actions.refreshAll}
|
|
122
|
+
tone="secondary"
|
|
123
|
+
size="sm"
|
|
124
|
+
idleLabel="Refresh"
|
|
125
|
+
/>
|
|
126
|
+
`
|
|
127
|
+
: html`
|
|
128
|
+
<${ActionButton}
|
|
129
|
+
onClick=${actions.saveChanges}
|
|
130
|
+
loading=${state.savingChanges}
|
|
131
|
+
disabled=${!hasUnsavedDetailChanges}
|
|
132
|
+
tone="primary"
|
|
133
|
+
size="sm"
|
|
134
|
+
idleLabel="Save changes"
|
|
135
|
+
loadingLabel="Saving..."
|
|
136
|
+
/>
|
|
137
|
+
`}
|
|
138
|
+
`}
|
|
139
|
+
/>
|
|
140
|
+
</div>
|
|
32
141
|
</div>
|
|
33
142
|
<div class="cron-tab-main">
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
style=${{ width: `${state.listPanelWidthPx}px` }}
|
|
38
|
-
>
|
|
39
|
-
<${CronJobList}
|
|
40
|
-
jobs=${state.jobs}
|
|
41
|
-
selectedRouteKey=${state.selectedRouteKey}
|
|
42
|
-
onSelectAllJobs=${actions.selectAllJobs}
|
|
43
|
-
onSelectJob=${actions.selectJob}
|
|
44
|
-
/>
|
|
45
|
-
</aside>
|
|
46
|
-
<div
|
|
47
|
-
class=${`cron-list-resizer ${state.isResizingListPanel ? "is-resizing" : ""}`}
|
|
48
|
-
onpointerdown=${actions.onListResizerPointerDown}
|
|
49
|
-
role="separator"
|
|
50
|
-
aria-orientation="vertical"
|
|
51
|
-
aria-label="Resize cron jobs list"
|
|
52
|
-
></div>
|
|
53
|
-
<main class="cron-detail-panel">
|
|
54
|
-
${noJobs
|
|
55
|
-
? html`
|
|
56
|
-
<div class="h-full flex items-center justify-center text-sm text-gray-500">
|
|
57
|
-
No cron jobs configured. Cron jobs are managed via the OpenClaw CLI.
|
|
58
|
-
</div>
|
|
59
|
-
`
|
|
60
|
-
: isAllJobsSelected
|
|
143
|
+
<div class="cron-tab-main-content">
|
|
144
|
+
<main class="cron-detail-panel">
|
|
145
|
+
${noJobs
|
|
61
146
|
? html`
|
|
62
|
-
|
|
63
|
-
jobs
|
|
64
|
-
|
|
65
|
-
bulkUsageByJobId=${state.bulkUsageByJobId}
|
|
66
|
-
bulkRunsByJobId=${state.bulkRunsByJobId}
|
|
67
|
-
onSelectJob=${actions.selectJob}
|
|
68
|
-
/>
|
|
147
|
+
<div class="h-full flex items-center justify-center text-sm text-gray-500">
|
|
148
|
+
No cron jobs configured. Cron jobs are managed via the OpenClaw CLI.
|
|
149
|
+
</div>
|
|
69
150
|
`
|
|
70
|
-
:
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
151
|
+
: isAllJobsSelected
|
|
152
|
+
? html`
|
|
153
|
+
<${CronOverview}
|
|
154
|
+
jobs=${state.jobs}
|
|
155
|
+
status=${state.status}
|
|
156
|
+
bulkUsageByJobId=${state.bulkUsageByJobId}
|
|
157
|
+
bulkRunsByJobId=${state.bulkRunsByJobId}
|
|
158
|
+
onSelectJob=${handleSelectJob}
|
|
159
|
+
/>
|
|
160
|
+
`
|
|
161
|
+
: html`
|
|
162
|
+
<${CronJobDetail}
|
|
163
|
+
job=${state.selectedJob}
|
|
164
|
+
runEntries=${state.runEntries}
|
|
165
|
+
runTotal=${state.runTotal}
|
|
166
|
+
runHasMore=${state.runHasMore}
|
|
167
|
+
loadingMoreRuns=${state.loadingMoreRuns}
|
|
168
|
+
runStatusFilter=${state.runStatusFilter}
|
|
169
|
+
onSetRunStatusFilter=${actions.setRunStatusFilter}
|
|
170
|
+
onLoadMoreRuns=${actions.loadMoreRuns}
|
|
171
|
+
onRunNow=${actions.runSelectedJobNow}
|
|
172
|
+
runningJob=${state.runningJob}
|
|
173
|
+
onToggleEnabled=${actions.setSelectedJobEnabled}
|
|
174
|
+
togglingJobEnabled=${state.togglingJobEnabled}
|
|
175
|
+
usage=${state.usage}
|
|
176
|
+
usageDays=${state.usageDays}
|
|
177
|
+
onSetUsageDays=${actions.setUsageDays}
|
|
178
|
+
promptValue=${state.promptValue}
|
|
179
|
+
savedPromptValue=${state.savedPromptValue}
|
|
180
|
+
onChangePrompt=${actions.setPromptValue}
|
|
181
|
+
onSaveChanges=${actions.saveChanges}
|
|
182
|
+
savingChanges=${state.savingChanges}
|
|
183
|
+
routingDraft=${state.routingDraft}
|
|
184
|
+
onChangeRoutingDraft=${actions.setRoutingDraft}
|
|
185
|
+
deliverySessions=${state.deliverySessions}
|
|
186
|
+
loadingDeliverySessions=${state.loadingDeliverySessions}
|
|
187
|
+
deliverySessionsError=${state.deliverySessionsError}
|
|
188
|
+
destinationSessionKey=${state.destinationSessionKey}
|
|
189
|
+
onChangeDestinationSessionKey=${actions.setDestinationSessionKey}
|
|
190
|
+
/>
|
|
191
|
+
`}
|
|
192
|
+
</main>
|
|
193
|
+
</div>
|
|
102
194
|
</div>
|
|
103
195
|
</div>
|
|
104
196
|
`;
|
|
@@ -13,6 +13,7 @@ const EditorTextarea = ({
|
|
|
13
13
|
handleEditorSelectionChange,
|
|
14
14
|
isEditBlocked,
|
|
15
15
|
isPreviewOnly,
|
|
16
|
+
textareaWrap = "soft",
|
|
16
17
|
}) => html`
|
|
17
18
|
<textarea
|
|
18
19
|
class=${overlay ? "file-viewer-editor file-viewer-editor-overlay" : "file-viewer-editor"}
|
|
@@ -33,7 +34,7 @@ const EditorTextarea = ({
|
|
|
33
34
|
data-gramm="false"
|
|
34
35
|
data-gramm_editor="false"
|
|
35
36
|
data-enable-grammarly="false"
|
|
36
|
-
wrap
|
|
37
|
+
wrap=${textareaWrap}
|
|
37
38
|
></textarea>
|
|
38
39
|
`;
|
|
39
40
|
|
|
@@ -55,6 +56,7 @@ export const EditorSurface = ({
|
|
|
55
56
|
handleEditorSelectionChange,
|
|
56
57
|
isEditBlocked,
|
|
57
58
|
isPreviewOnly,
|
|
59
|
+
textareaWrap = "soft",
|
|
58
60
|
}) => html`
|
|
59
61
|
<div class=${editorShellClassName} aria-hidden=${editorShellAriaHidden}>
|
|
60
62
|
<div class="file-viewer-editor-line-num-col" ref=${editorLineNumbersRef}>
|
|
@@ -106,6 +108,7 @@ export const EditorSurface = ({
|
|
|
106
108
|
handleEditorSelectionChange=${handleEditorSelectionChange}
|
|
107
109
|
isEditBlocked=${isEditBlocked}
|
|
108
110
|
isPreviewOnly=${isPreviewOnly}
|
|
111
|
+
textareaWrap=${textareaWrap}
|
|
109
112
|
/>
|
|
110
113
|
</div>
|
|
111
114
|
`
|
|
@@ -120,6 +123,7 @@ export const EditorSurface = ({
|
|
|
120
123
|
handleEditorSelectionChange=${handleEditorSelectionChange}
|
|
121
124
|
isEditBlocked=${isEditBlocked}
|
|
122
125
|
isPreviewOnly=${isPreviewOnly}
|
|
126
|
+
textareaWrap=${textareaWrap}
|
|
123
127
|
/>
|
|
124
128
|
`}
|
|
125
129
|
</div>
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { useCallback, useEffect } from "https://esm.sh/preact/hooks";
|
|
2
|
+
|
|
3
|
+
export const useEditorLineNumberSync = ({
|
|
4
|
+
enabled = false,
|
|
5
|
+
syncKey = "",
|
|
6
|
+
editorLineNumberRowRefs,
|
|
7
|
+
editorHighlightLineRefs,
|
|
8
|
+
}) => {
|
|
9
|
+
const syncEditorLineNumberHeights = useCallback(() => {
|
|
10
|
+
if (!enabled) return;
|
|
11
|
+
const numberRows = editorLineNumberRowRefs?.current || [];
|
|
12
|
+
const highlightRows = editorHighlightLineRefs?.current || [];
|
|
13
|
+
const rowCount = Math.min(numberRows.length, highlightRows.length);
|
|
14
|
+
for (let index = 0; index < rowCount; index += 1) {
|
|
15
|
+
const numberRow = numberRows[index];
|
|
16
|
+
const highlightRow = highlightRows[index];
|
|
17
|
+
if (!numberRow || !highlightRow) continue;
|
|
18
|
+
numberRow.style.height = `${highlightRow.offsetHeight}px`;
|
|
19
|
+
}
|
|
20
|
+
}, [editorHighlightLineRefs, editorLineNumberRowRefs, enabled]);
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
syncEditorLineNumberHeights();
|
|
24
|
+
}, [syncEditorLineNumberHeights, syncKey]);
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
if (!enabled) return () => {};
|
|
28
|
+
const onResize = () => syncEditorLineNumberHeights();
|
|
29
|
+
window.addEventListener("resize", onResize);
|
|
30
|
+
return () => window.removeEventListener("resize", onResize);
|
|
31
|
+
}, [enabled, syncEditorLineNumberHeights]);
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
syncEditorLineNumberHeights,
|
|
35
|
+
};
|
|
36
|
+
};
|
|
@@ -32,6 +32,7 @@ import { useFileDiff } from "./use-file-diff.js";
|
|
|
32
32
|
import { useFileViewerDraftSync } from "./use-file-viewer-draft-sync.js";
|
|
33
33
|
import { useFileViewerHotkeys } from "./use-file-viewer-hotkeys.js";
|
|
34
34
|
import { useEditorSelectionRestore } from "./use-editor-selection-restore.js";
|
|
35
|
+
import { useEditorLineNumberSync } from "./use-editor-line-number-sync.js";
|
|
35
36
|
|
|
36
37
|
export const useFileViewer = ({
|
|
37
38
|
filePath = "",
|
|
@@ -190,29 +191,12 @@ export const useFileViewer = ({
|
|
|
190
191
|
[parsedFrontmatter.body, isMarkdownFile],
|
|
191
192
|
);
|
|
192
193
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const numberRow = numberRows[index];
|
|
200
|
-
const highlightRow = highlightRows[index];
|
|
201
|
-
if (!numberRow || !highlightRow) continue;
|
|
202
|
-
numberRow.style.height = `${highlightRow.offsetHeight}px`;
|
|
203
|
-
}
|
|
204
|
-
}, [shouldUseHighlightedEditor, viewMode]);
|
|
205
|
-
|
|
206
|
-
useEffect(() => {
|
|
207
|
-
syncEditorLineNumberHeights();
|
|
208
|
-
}, [content, syncEditorLineNumberHeights]);
|
|
209
|
-
|
|
210
|
-
useEffect(() => {
|
|
211
|
-
if (!shouldUseHighlightedEditor || viewMode !== "edit") return () => {};
|
|
212
|
-
const onResize = () => syncEditorLineNumberHeights();
|
|
213
|
-
window.addEventListener("resize", onResize);
|
|
214
|
-
return () => window.removeEventListener("resize", onResize);
|
|
215
|
-
}, [shouldUseHighlightedEditor, viewMode, syncEditorLineNumberHeights]);
|
|
194
|
+
useEditorLineNumberSync({
|
|
195
|
+
enabled: shouldUseHighlightedEditor && viewMode === "edit",
|
|
196
|
+
syncKey: `${normalizedPath}:${renderContent.length}:${highlightedEditorLines.length}`,
|
|
197
|
+
editorLineNumberRowRefs,
|
|
198
|
+
editorHighlightLineRefs,
|
|
199
|
+
});
|
|
216
200
|
|
|
217
201
|
useEffect(() => {
|
|
218
202
|
if (!isMarkdownFile && viewMode !== "edit") {
|
|
@@ -13,11 +13,7 @@ export const clampSelectionIndex = (value, maxValue) => {
|
|
|
13
13
|
export const countTextLines = (content) => {
|
|
14
14
|
const text = String(content || "");
|
|
15
15
|
if (!text) return 1;
|
|
16
|
-
|
|
17
|
-
for (let index = 0; index < text.length; index += 1) {
|
|
18
|
-
if (text.charCodeAt(index) === 10) lineCount += 1;
|
|
19
|
-
}
|
|
20
|
-
return lineCount;
|
|
16
|
+
return text.split(/\r\n|\r|\n/).length;
|
|
21
17
|
};
|
|
22
18
|
|
|
23
19
|
export const shouldUseSimpleEditorMode = ({
|
|
@@ -43,7 +43,9 @@ const PairingRow = ({ pairing, onApprove, onReject }) => {
|
|
|
43
43
|
<div class="font-medium text-sm">
|
|
44
44
|
${pairing.code || pairing.id || "Pending request"}
|
|
45
45
|
</div>
|
|
46
|
-
<span
|
|
46
|
+
<span
|
|
47
|
+
class="text-[11px] px-2 py-0.5 rounded-full border border-border text-gray-400"
|
|
48
|
+
>
|
|
47
49
|
Request
|
|
48
50
|
</span>
|
|
49
51
|
</div>
|
|
@@ -54,14 +56,18 @@ const PairingRow = ({ pairing, onApprove, onReject }) => {
|
|
|
54
56
|
<button
|
|
55
57
|
onclick=${handleApprove}
|
|
56
58
|
disabled=${!!busyAction}
|
|
57
|
-
class="ac-btn-green text-xs font-medium px-3 py-1.5 rounded-lg ${busyAction
|
|
59
|
+
class="ac-btn-green text-xs font-medium px-3 py-1.5 rounded-lg ${busyAction
|
|
60
|
+
? "opacity-50 cursor-not-allowed"
|
|
61
|
+
: ""}"
|
|
58
62
|
>
|
|
59
63
|
${busyAction === "approve" ? "Approving..." : "Approve"}
|
|
60
64
|
</button>
|
|
61
65
|
<button
|
|
62
66
|
onclick=${handleReject}
|
|
63
67
|
disabled=${!!busyAction}
|
|
64
|
-
class="ac-btn-secondary text-xs font-medium px-3 py-1.5 rounded-lg ${busyAction
|
|
68
|
+
class="ac-btn-secondary text-xs font-medium px-3 py-1.5 rounded-lg ${busyAction
|
|
69
|
+
? "opacity-50 cursor-not-allowed"
|
|
70
|
+
: ""}"
|
|
65
71
|
>
|
|
66
72
|
${busyAction === "reject" ? "Rejecting..." : "Reject"}
|
|
67
73
|
</button>
|
|
@@ -83,15 +89,20 @@ export const WelcomePairingStep = ({
|
|
|
83
89
|
onSkip,
|
|
84
90
|
}) => {
|
|
85
91
|
const channelMeta = kChannelMeta[channel] || {
|
|
86
|
-
label: channel
|
|
92
|
+
label: channel
|
|
93
|
+
? channel.charAt(0).toUpperCase() + channel.slice(1)
|
|
94
|
+
: "Channel",
|
|
87
95
|
iconSrc: "",
|
|
88
96
|
};
|
|
89
97
|
const channelInfo = channels?.[channel];
|
|
90
98
|
|
|
91
99
|
if (!channel) {
|
|
92
100
|
return html`
|
|
93
|
-
<div
|
|
94
|
-
|
|
101
|
+
<div
|
|
102
|
+
class="bg-red-900/30 border border-red-800 rounded-xl p-3 text-red-300 text-sm"
|
|
103
|
+
>
|
|
104
|
+
Missing channel configuration. Go back and add a Telegram or Discord bot
|
|
105
|
+
token.
|
|
95
106
|
</div>
|
|
96
107
|
`;
|
|
97
108
|
}
|
|
@@ -101,12 +112,16 @@ export const WelcomePairingStep = ({
|
|
|
101
112
|
<div class="min-h-[300px] pb-6 px-6 flex flex-col">
|
|
102
113
|
<div class="flex-1 flex items-center justify-center text-center">
|
|
103
114
|
<div class="space-y-3 max-w-xl mx-auto">
|
|
104
|
-
<p class="text-sm font-medium text-green-300 mb-12"
|
|
115
|
+
<p class="text-sm font-medium text-green-300 mb-12">
|
|
116
|
+
🎉 Setup complete
|
|
117
|
+
</p>
|
|
105
118
|
<p class="text-xs text-gray-300">
|
|
106
|
-
Your ${channelMeta.label} channel is connected. You can switch to
|
|
119
|
+
Your ${channelMeta.label} channel is connected. You can switch to
|
|
120
|
+
${channelMeta.label} and start using your agent now.
|
|
107
121
|
</p>
|
|
108
122
|
<p class="text-xs text-gray-500 font-normal opacity-85">
|
|
109
|
-
Continue to the dashboard to explore extras like Google Workspace
|
|
123
|
+
Continue to the dashboard to explore extras like Google Workspace
|
|
124
|
+
and additional integrations.
|
|
110
125
|
</p>
|
|
111
126
|
</div>
|
|
112
127
|
</div>
|
|
@@ -124,60 +139,74 @@ export const WelcomePairingStep = ({
|
|
|
124
139
|
<div class="min-h-[300px] pb-6 flex flex-col gap-3">
|
|
125
140
|
<div class="flex items-center justify-end gap-2">
|
|
126
141
|
<${Badge} tone="warning"
|
|
127
|
-
>${
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
142
|
+
>${
|
|
143
|
+
loading
|
|
144
|
+
? "Checking..."
|
|
145
|
+
: pairings.length > 0
|
|
146
|
+
? "Pairing request detected"
|
|
147
|
+
: "Awaiting pairing"
|
|
148
|
+
}</${Badge}
|
|
132
149
|
>
|
|
133
150
|
</div>
|
|
134
151
|
|
|
135
|
-
${
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
(
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
152
|
+
${
|
|
153
|
+
pairings.length > 0
|
|
154
|
+
? html`<div class="flex-1 flex items-center">
|
|
155
|
+
<div class="w-full">
|
|
156
|
+
${pairings.map(
|
|
157
|
+
(pairing) =>
|
|
158
|
+
html`<${PairingRow}
|
|
159
|
+
key=${pairing.id}
|
|
160
|
+
pairing=${pairing}
|
|
161
|
+
onApprove=${onApprove}
|
|
162
|
+
onReject=${onReject}
|
|
163
|
+
/>`,
|
|
164
|
+
)}
|
|
165
|
+
</div>
|
|
166
|
+
</div>`
|
|
167
|
+
: html`<div
|
|
168
|
+
class="flex-1 flex items-center justify-center text-center py-4"
|
|
169
|
+
>
|
|
170
|
+
<div class="space-y-4">
|
|
171
|
+
${channelMeta.iconSrc
|
|
172
|
+
? html`<img
|
|
173
|
+
src=${channelMeta.iconSrc}
|
|
174
|
+
alt=${channelMeta.label}
|
|
175
|
+
class="w-8 h-8 mx-auto rounded-md"
|
|
176
|
+
/>`
|
|
177
|
+
: null}
|
|
178
|
+
<p class="text-gray-300 text-sm">
|
|
179
|
+
Send a message to your ${channelMeta.label} bot
|
|
180
|
+
</p>
|
|
181
|
+
<p class="text-gray-600 text-xs">
|
|
182
|
+
The pairing request will appear here in 5-10 seconds
|
|
183
|
+
</p>
|
|
184
|
+
</div>
|
|
185
|
+
</div>`
|
|
186
|
+
}
|
|
166
187
|
|
|
167
|
-
${
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
188
|
+
${
|
|
189
|
+
error
|
|
190
|
+
? html`<div
|
|
191
|
+
class="bg-red-900/30 border border-red-800 rounded-xl p-3 text-red-300 text-sm"
|
|
192
|
+
>
|
|
193
|
+
${error}
|
|
194
|
+
</div>`
|
|
195
|
+
: null
|
|
196
|
+
}
|
|
197
|
+
${
|
|
198
|
+
pairings.length === 0
|
|
199
|
+
? html`<div class="pt-3 text-center">
|
|
200
|
+
<button
|
|
201
|
+
type="button"
|
|
202
|
+
onclick=${onSkip}
|
|
203
|
+
class="ac-tip-link text-xs font-medium"
|
|
204
|
+
>
|
|
205
|
+
Skip pairing for now
|
|
206
|
+
</button>
|
|
207
|
+
</div>`
|
|
208
|
+
: null
|
|
209
|
+
}
|
|
181
210
|
</div>
|
|
182
211
|
`;
|
|
183
212
|
};
|