@chrysb/alphaclaw 0.3.5-beta.1 → 0.4.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 +1 -31
- package/lib/public/assets/icons/google_icon.svg +8 -0
- package/lib/public/css/explorer.css +53 -0
- package/lib/public/js/app.js +126 -105
- package/lib/public/js/components/credentials-modal.js +36 -8
- package/lib/public/js/components/file-tree.js +212 -22
- package/lib/public/js/components/file-viewer/index.js +44 -6
- package/lib/public/js/components/file-viewer/status-banners.js +11 -6
- package/lib/public/js/components/file-viewer/toolbar.js +43 -1
- package/lib/public/js/components/file-viewer/use-editor-selection-restore.js +6 -0
- package/lib/public/js/components/file-viewer/use-file-diff.js +11 -0
- package/lib/public/js/components/file-viewer/use-file-loader.js +12 -2
- package/lib/public/js/components/file-viewer/use-file-viewer.js +94 -2
- package/lib/public/js/components/google/account-row.js +98 -0
- package/lib/public/js/components/google/add-account-modal.js +93 -0
- package/lib/public/js/components/google/index.js +439 -0
- package/lib/public/js/components/google/use-google-accounts.js +41 -0
- package/lib/public/js/components/icons.js +26 -0
- package/lib/public/js/components/sidebar-git-panel.js +43 -14
- package/lib/public/js/components/sidebar.js +91 -75
- package/lib/public/js/lib/api.js +72 -8
- package/lib/public/js/lib/browse-file-policies.js +29 -11
- package/lib/public/js/lib/syntax-highlighters/index.js +6 -5
- package/lib/public/shared/browse-file-policies.json +13 -0
- package/lib/server/constants.js +19 -7
- package/lib/server/google-state.js +187 -0
- package/lib/server/helpers.js +12 -4
- package/lib/server/onboarding/github.js +21 -2
- package/lib/server/onboarding/index.js +1 -3
- package/lib/server/onboarding/openclaw.js +3 -0
- package/lib/server/onboarding/workspace.js +40 -0
- package/lib/server/routes/browse/index.js +90 -2
- package/lib/server/routes/google.js +414 -213
- package/lib/setup/gitignore +3 -0
- package/lib/setup/hourly-git-sync.sh +28 -1
- package/package.json +1 -1
- package/lib/public/js/components/google.js +0 -228
package/bin/alphaclaw.js
CHANGED
|
@@ -626,37 +626,7 @@ try {
|
|
|
626
626
|
} catch {}
|
|
627
627
|
|
|
628
628
|
// ---------------------------------------------------------------------------
|
|
629
|
-
// 10.
|
|
630
|
-
// ---------------------------------------------------------------------------
|
|
631
|
-
|
|
632
|
-
if (process.env.GOG_CLIENT_CREDENTIALS_JSON && process.env.GOG_REFRESH_TOKEN) {
|
|
633
|
-
try {
|
|
634
|
-
const tmpCreds = `/tmp/gog-creds-${process.pid}.json`;
|
|
635
|
-
const tmpToken = `/tmp/gog-token-${process.pid}.json`;
|
|
636
|
-
fs.writeFileSync(tmpCreds, process.env.GOG_CLIENT_CREDENTIALS_JSON);
|
|
637
|
-
execSync(`gog auth credentials set "${tmpCreds}"`, { stdio: "ignore" });
|
|
638
|
-
fs.unlinkSync(tmpCreds);
|
|
639
|
-
fs.writeFileSync(
|
|
640
|
-
tmpToken,
|
|
641
|
-
JSON.stringify({
|
|
642
|
-
email: process.env.GOG_ACCOUNT || "",
|
|
643
|
-
refresh_token: process.env.GOG_REFRESH_TOKEN,
|
|
644
|
-
}),
|
|
645
|
-
);
|
|
646
|
-
execSync(`gog auth tokens import "${tmpToken}"`, { stdio: "ignore" });
|
|
647
|
-
fs.unlinkSync(tmpToken);
|
|
648
|
-
console.log(
|
|
649
|
-
`[alphaclaw] gog CLI configured for ${process.env.GOG_ACCOUNT || "account"}`,
|
|
650
|
-
);
|
|
651
|
-
} catch (e) {
|
|
652
|
-
console.log(`[alphaclaw] gog credentials setup skipped: ${e.message}`);
|
|
653
|
-
}
|
|
654
|
-
} else {
|
|
655
|
-
console.log("[alphaclaw] Google credentials not set -- skipping gog setup");
|
|
656
|
-
}
|
|
657
|
-
|
|
658
|
-
// ---------------------------------------------------------------------------
|
|
659
|
-
// 11. Reconcile channels if already onboarded
|
|
629
|
+
// 10. Reconcile channels if already onboarded
|
|
660
630
|
// ---------------------------------------------------------------------------
|
|
661
631
|
|
|
662
632
|
const configPath = path.join(openclawDir, "openclaw.json");
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img" aria-labelledby="title">
|
|
2
|
+
<title>Google</title>
|
|
3
|
+
<path d="M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z" fill="#4285F4"/>
|
|
4
|
+
<path d="M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z" fill="#34A853"/>
|
|
5
|
+
<path d="M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z" fill="#FBBC05"/>
|
|
6
|
+
<path d="M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z" fill="#EA4335"/>
|
|
7
|
+
<path d="M1 1h22v22H1z" fill="none"/>
|
|
8
|
+
</svg>
|
|
@@ -216,6 +216,28 @@
|
|
|
216
216
|
background: var(--bg-hover);
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
+
.tree-folder.active {
|
|
220
|
+
background: var(--bg-active);
|
|
221
|
+
color: var(--accent);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
.tree-folder.active .arrow {
|
|
225
|
+
color: var(--accent);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
.tree-folder-toggle {
|
|
229
|
+
border: 0;
|
|
230
|
+
margin: 0;
|
|
231
|
+
padding: 0;
|
|
232
|
+
background: transparent;
|
|
233
|
+
color: inherit;
|
|
234
|
+
display: inline-flex;
|
|
235
|
+
align-items: center;
|
|
236
|
+
justify-content: center;
|
|
237
|
+
cursor: pointer;
|
|
238
|
+
flex: 0 0 auto;
|
|
239
|
+
}
|
|
240
|
+
|
|
219
241
|
.arrow {
|
|
220
242
|
font-size: 10px;
|
|
221
243
|
transition: transform 0.15s;
|
|
@@ -565,6 +587,37 @@
|
|
|
565
587
|
flex-shrink: 0;
|
|
566
588
|
}
|
|
567
589
|
|
|
590
|
+
.file-viewer-icon-action {
|
|
591
|
+
display: inline-flex;
|
|
592
|
+
align-items: center;
|
|
593
|
+
justify-content: center;
|
|
594
|
+
width: 28px;
|
|
595
|
+
height: 28px;
|
|
596
|
+
margin-right: 10px;
|
|
597
|
+
border: 1px solid var(--border);
|
|
598
|
+
border-radius: 8px;
|
|
599
|
+
background: rgba(255, 255, 255, 0.02);
|
|
600
|
+
color: var(--text-muted);
|
|
601
|
+
transition: border-color 0.12s ease, color 0.12s ease, background 0.12s ease;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
.file-viewer-icon-action:hover {
|
|
605
|
+
border-color: rgba(148, 163, 184, 0.45);
|
|
606
|
+
color: var(--text);
|
|
607
|
+
background: rgba(148, 163, 184, 0.08);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
.file-viewer-icon-action.is-disabled {
|
|
611
|
+
opacity: 0.45;
|
|
612
|
+
cursor: not-allowed;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
.file-viewer-icon-action-icon {
|
|
616
|
+
width: 14px;
|
|
617
|
+
height: 14px;
|
|
618
|
+
flex-shrink: 0;
|
|
619
|
+
}
|
|
620
|
+
|
|
568
621
|
.file-viewer-view-toggle {
|
|
569
622
|
display: flex;
|
|
570
623
|
align-items: center;
|
package/lib/public/js/app.js
CHANGED
|
@@ -28,7 +28,7 @@ import { Gateway } from "./components/gateway.js";
|
|
|
28
28
|
import { Channels, ALL_CHANNELS } from "./components/channels.js";
|
|
29
29
|
import { Pairings } from "./components/pairings.js";
|
|
30
30
|
import { DevicePairings } from "./components/device-pairings.js";
|
|
31
|
-
import { Google } from "./components/google.js";
|
|
31
|
+
import { Google } from "./components/google/index.js";
|
|
32
32
|
import { Features } from "./components/features.js";
|
|
33
33
|
import { Providers } from "./components/providers.js";
|
|
34
34
|
import { Welcome } from "./components/welcome.js";
|
|
@@ -52,6 +52,7 @@ const kSidebarMinWidthPx = 180;
|
|
|
52
52
|
const kSidebarMaxWidthPx = 460;
|
|
53
53
|
const kBrowseLastPathUiSettingKey = "browseLastPath";
|
|
54
54
|
const kLastMenuRouteUiSettingKey = "lastMenuRoute";
|
|
55
|
+
const normalizeBrowsePath = (value) => String(value || "").replace(/^\/+|\/+$/g, "");
|
|
55
56
|
|
|
56
57
|
const clampSidebarWidth = (value) =>
|
|
57
58
|
Math.max(kSidebarMinWidthPx, Math.min(kSidebarMaxWidthPx, value));
|
|
@@ -106,7 +107,6 @@ const GeneralTab = ({
|
|
|
106
107
|
onOpenclawVersionActionComplete = () => {},
|
|
107
108
|
onOpenclawUpdate,
|
|
108
109
|
}) => {
|
|
109
|
-
const [googleKey, setGoogleKey] = useState(0);
|
|
110
110
|
const [dashboardLoading, setDashboardLoading] = useState(false);
|
|
111
111
|
const [repairingWatchdog, setRepairingWatchdog] = useState(false);
|
|
112
112
|
const status = statusData;
|
|
@@ -179,7 +179,6 @@ const GeneralTab = ({
|
|
|
179
179
|
onRefreshStatuses();
|
|
180
180
|
pairingsPoll.refresh();
|
|
181
181
|
devicePoll.refresh();
|
|
182
|
-
setGoogleKey((k) => k + 1);
|
|
183
182
|
}, [isActive]);
|
|
184
183
|
|
|
185
184
|
useEffect(() => {
|
|
@@ -280,7 +279,7 @@ const GeneralTab = ({
|
|
|
280
279
|
onReject=${handleReject}
|
|
281
280
|
/>
|
|
282
281
|
<${Features} onSwitchTab=${onSwitchTab} />
|
|
283
|
-
<${Google}
|
|
282
|
+
<${Google} gatewayStatus=${gatewayStatus} />
|
|
284
283
|
|
|
285
284
|
${repo &&
|
|
286
285
|
html`
|
|
@@ -646,9 +645,27 @@ const App = () => {
|
|
|
646
645
|
setLocation(`/${screen}`);
|
|
647
646
|
setMobileSidebarOpen(false);
|
|
648
647
|
};
|
|
648
|
+
const handleBrowsePreviewFile = useCallback((nextPreviewPath) => {
|
|
649
|
+
const normalizedPreviewPath = normalizeBrowsePath(nextPreviewPath);
|
|
650
|
+
setBrowsePreviewPath(normalizedPreviewPath);
|
|
651
|
+
}, []);
|
|
649
652
|
const navigateToBrowseFile = (relativePath, options = {}) => {
|
|
650
|
-
|
|
651
|
-
|
|
653
|
+
const normalizedTargetPath = normalizeBrowsePath(relativePath);
|
|
654
|
+
const selectingDirectory =
|
|
655
|
+
!!options.directory || String(relativePath || "").trim().endsWith("/");
|
|
656
|
+
const shouldPreservePreview = selectingDirectory && !!options.preservePreview;
|
|
657
|
+
const activePath = normalizeBrowsePath(
|
|
658
|
+
browsePreviewPath || selectedBrowsePath || "",
|
|
659
|
+
);
|
|
660
|
+
const nextPreviewPath =
|
|
661
|
+
shouldPreservePreview && activePath && activePath !== normalizedTargetPath
|
|
662
|
+
? activePath
|
|
663
|
+
: "";
|
|
664
|
+
setBrowsePreviewPath(nextPreviewPath);
|
|
665
|
+
const routeOptions = selectingDirectory
|
|
666
|
+
? { ...options, view: "edit" }
|
|
667
|
+
: options;
|
|
668
|
+
setLocation(buildBrowseRoute(normalizedTargetPath, routeOptions));
|
|
652
669
|
setMobileSidebarOpen(false);
|
|
653
670
|
};
|
|
654
671
|
const handleSidebarLogout = async () => {
|
|
@@ -879,7 +896,7 @@ const App = () => {
|
|
|
879
896
|
onSelectNavItem=${handleSelectNavItem}
|
|
880
897
|
selectedBrowsePath=${selectedBrowsePath}
|
|
881
898
|
onSelectBrowseFile=${navigateToBrowseFile}
|
|
882
|
-
onPreviewBrowseFile=${
|
|
899
|
+
onPreviewBrowseFile=${handleBrowsePreviewFile}
|
|
883
900
|
acHasUpdate=${acHasUpdate}
|
|
884
901
|
acLatest=${acLatest}
|
|
885
902
|
acDismissed=${acDismissed}
|
|
@@ -921,112 +938,116 @@ const App = () => {
|
|
|
921
938
|
</span>
|
|
922
939
|
</div>
|
|
923
940
|
<div class=${isBrowseRoute ? "w-full" : "max-w-2xl w-full mx-auto"}>
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
941
|
+
<div style=${{ display: isBrowseRoute ? "block" : "none" }}>
|
|
942
|
+
<${FileViewer}
|
|
943
|
+
filePath=${activeBrowsePath}
|
|
944
|
+
isPreviewOnly=${false}
|
|
945
|
+
browseView=${browseViewerMode}
|
|
946
|
+
onRequestEdit=${(targetPath) => {
|
|
947
|
+
const normalizedTargetPath = String(targetPath || "");
|
|
948
|
+
if (
|
|
949
|
+
normalizedTargetPath &&
|
|
950
|
+
normalizedTargetPath !== selectedBrowsePath
|
|
951
|
+
) {
|
|
952
|
+
navigateToBrowseFile(normalizedTargetPath, { view: "edit" });
|
|
953
|
+
return;
|
|
954
|
+
}
|
|
955
|
+
setLocation(buildBrowseRoute(selectedBrowsePath, { view: "edit" }));
|
|
956
|
+
}}
|
|
957
|
+
onRequestClearSelection=${() => {
|
|
958
|
+
setBrowsePreviewPath("");
|
|
959
|
+
setLocation("/browse");
|
|
960
|
+
}}
|
|
961
|
+
/>
|
|
962
|
+
</div>
|
|
963
|
+
<div style=${{ display: isBrowseRoute ? "none" : "block" }}>
|
|
964
|
+
<div style=${{ display: location === "/general" ? "block" : "none" }}>
|
|
965
|
+
<div class="pt-4">
|
|
966
|
+
<${GeneralTab}
|
|
967
|
+
statusData=${sharedStatus}
|
|
968
|
+
watchdogData=${sharedWatchdogStatus}
|
|
969
|
+
onRefreshStatuses=${refreshSharedStatuses}
|
|
970
|
+
onSwitchTab=${(nextTab) => setLocation(`/${nextTab}`)}
|
|
971
|
+
onNavigate=${navigateToSubScreen}
|
|
972
|
+
isActive=${location === "/general"}
|
|
973
|
+
restartingGateway=${restartingGateway}
|
|
974
|
+
onRestartGateway=${handleGatewayRestart}
|
|
975
|
+
restartSignal=${gatewayRestartSignal}
|
|
976
|
+
openclawUpdateInProgress=${openclawUpdateInProgress}
|
|
977
|
+
onOpenclawVersionActionComplete=${handleOpenclawVersionActionComplete}
|
|
978
|
+
onOpenclawUpdate=${handleOpenclawUpdate}
|
|
944
979
|
/>
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
/>
|
|
990
|
-
</div>
|
|
991
|
-
</${Route}>
|
|
992
|
-
<${Route} path="/usage/:sessionId">
|
|
993
|
-
${(params) => html`
|
|
980
|
+
</div>
|
|
981
|
+
</div>
|
|
982
|
+
${!isBrowseRoute && location !== "/general"
|
|
983
|
+
? html`
|
|
984
|
+
<${Switch}>
|
|
985
|
+
<${Route} path="/telegram">
|
|
986
|
+
<div class="pt-4">
|
|
987
|
+
<${TelegramWorkspace} onBack=${exitSubScreen} />
|
|
988
|
+
</div>
|
|
989
|
+
</${Route}>
|
|
990
|
+
<${Route} path="/providers">
|
|
991
|
+
<div class="pt-4">
|
|
992
|
+
<${Providers} onRestartRequired=${setRestartRequired} />
|
|
993
|
+
</div>
|
|
994
|
+
</${Route}>
|
|
995
|
+
<${Route} path="/watchdog">
|
|
996
|
+
<div class="pt-4">
|
|
997
|
+
<${WatchdogTab}
|
|
998
|
+
gatewayStatus=${sharedStatus?.gateway || null}
|
|
999
|
+
openclawVersion=${sharedStatus?.openclawVersion || null}
|
|
1000
|
+
watchdogStatus=${sharedWatchdogStatus}
|
|
1001
|
+
onRefreshStatuses=${refreshSharedStatuses}
|
|
1002
|
+
restartingGateway=${restartingGateway}
|
|
1003
|
+
onRestartGateway=${handleGatewayRestart}
|
|
1004
|
+
restartSignal=${gatewayRestartSignal}
|
|
1005
|
+
openclawUpdateInProgress=${openclawUpdateInProgress}
|
|
1006
|
+
onOpenclawVersionActionComplete=${handleOpenclawVersionActionComplete}
|
|
1007
|
+
onOpenclawUpdate=${handleOpenclawUpdate}
|
|
1008
|
+
/>
|
|
1009
|
+
</div>
|
|
1010
|
+
</${Route}>
|
|
1011
|
+
<${Route} path="/usage/:sessionId">
|
|
1012
|
+
${(params) => html`
|
|
1013
|
+
<div class="pt-4">
|
|
1014
|
+
<${UsageTab}
|
|
1015
|
+
sessionId=${decodeURIComponent(params.sessionId || "")}
|
|
1016
|
+
onSelectSession=${(id) =>
|
|
1017
|
+
setLocation(`/usage/${encodeURIComponent(String(id || ""))}`)}
|
|
1018
|
+
onBackToSessions=${() => setLocation("/usage")}
|
|
1019
|
+
/>
|
|
1020
|
+
</div>
|
|
1021
|
+
`}
|
|
1022
|
+
</${Route}>
|
|
1023
|
+
<${Route} path="/usage">
|
|
994
1024
|
<div class="pt-4">
|
|
995
1025
|
<${UsageTab}
|
|
996
|
-
sessionId=${decodeURIComponent(params.sessionId || "")}
|
|
997
1026
|
onSelectSession=${(id) =>
|
|
998
1027
|
setLocation(`/usage/${encodeURIComponent(String(id || ""))}`)}
|
|
999
1028
|
onBackToSessions=${() => setLocation("/usage")}
|
|
1000
1029
|
/>
|
|
1001
1030
|
</div>
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
<${Route} path="/webhooks">
|
|
1023
|
-
${() => renderWebhooks("")}
|
|
1024
|
-
</${Route}>
|
|
1025
|
-
<${Route}>
|
|
1026
|
-
<${RouteRedirect} to="/general" />
|
|
1027
|
-
</${Route}>
|
|
1028
|
-
</${Switch}>
|
|
1029
|
-
`}
|
|
1031
|
+
</${Route}>
|
|
1032
|
+
<${Route} path="/envars">
|
|
1033
|
+
<div class="pt-4">
|
|
1034
|
+
<${Envars} onRestartRequired=${setRestartRequired} />
|
|
1035
|
+
</div>
|
|
1036
|
+
</${Route}>
|
|
1037
|
+
<${Route} path="/webhooks/:hookName">
|
|
1038
|
+
${(params) =>
|
|
1039
|
+
renderWebhooks(decodeURIComponent(params.hookName || ""))}
|
|
1040
|
+
</${Route}>
|
|
1041
|
+
<${Route} path="/webhooks">
|
|
1042
|
+
${() => renderWebhooks("")}
|
|
1043
|
+
</${Route}>
|
|
1044
|
+
<${Route}>
|
|
1045
|
+
<${RouteRedirect} to="/general" />
|
|
1046
|
+
</${Route}>
|
|
1047
|
+
</${Switch}>
|
|
1048
|
+
`
|
|
1049
|
+
: null}
|
|
1050
|
+
</div>
|
|
1030
1051
|
</div>
|
|
1031
1052
|
<${ToastContainer}
|
|
1032
1053
|
className="fixed bottom-10 right-4 z-50 space-y-2 pointer-events-none"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { h } from "https://esm.sh/preact";
|
|
2
|
-
import {
|
|
2
|
+
import { useEffect, useRef, useState } from "https://esm.sh/preact/hooks";
|
|
3
3
|
import htm from "https://esm.sh/htm";
|
|
4
4
|
import { saveGoogleCredentials } from "../lib/api.js";
|
|
5
5
|
import { SecretInput } from "./secret-input.js";
|
|
@@ -9,16 +9,37 @@ import { ActionButton } from "./action-button.js";
|
|
|
9
9
|
import { CloseIcon } from "./icons.js";
|
|
10
10
|
const html = htm.bind(h);
|
|
11
11
|
|
|
12
|
-
export const CredentialsModal = ({
|
|
12
|
+
export const CredentialsModal = ({
|
|
13
|
+
visible,
|
|
14
|
+
onClose,
|
|
15
|
+
onSaved,
|
|
16
|
+
title = "Connect Google Workspace",
|
|
17
|
+
submitLabel = "Connect Google",
|
|
18
|
+
defaultInstrType = "workspace",
|
|
19
|
+
client = "default",
|
|
20
|
+
personal = false,
|
|
21
|
+
accountId = "",
|
|
22
|
+
initialValues = {},
|
|
23
|
+
}) => {
|
|
13
24
|
const [clientId, setClientId] = useState("");
|
|
14
25
|
const [clientSecret, setClientSecret] = useState("");
|
|
15
26
|
const [email, setEmail] = useState("");
|
|
16
27
|
const [error, setError] = useState("");
|
|
17
28
|
const [saving, setSaving] = useState(false);
|
|
18
|
-
const [instrType, setInstrType] = useState(
|
|
29
|
+
const [instrType, setInstrType] = useState(defaultInstrType);
|
|
19
30
|
const [redirectUriCopied, setRedirectUriCopied] = useState(false);
|
|
20
31
|
const fileRef = useRef(null);
|
|
21
32
|
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (!visible) return;
|
|
35
|
+
setClientId(String(initialValues.clientId || ""));
|
|
36
|
+
setClientSecret(String(initialValues.clientSecret || ""));
|
|
37
|
+
setEmail(String(initialValues.email || ""));
|
|
38
|
+
setInstrType(defaultInstrType);
|
|
39
|
+
setError("");
|
|
40
|
+
setRedirectUriCopied(false);
|
|
41
|
+
}, [visible, initialValues, defaultInstrType]);
|
|
42
|
+
|
|
22
43
|
if (!visible) return null;
|
|
23
44
|
|
|
24
45
|
const redirectUri = `${window.location.origin}/auth/google/callback`;
|
|
@@ -50,15 +71,22 @@ export const CredentialsModal = ({ visible, onClose, onSaved }) => {
|
|
|
50
71
|
const submit = async () => {
|
|
51
72
|
setError("");
|
|
52
73
|
if (!clientId || !clientSecret || !email) {
|
|
53
|
-
setError("
|
|
74
|
+
setError("Client ID, Client Secret, and Email are required");
|
|
54
75
|
return;
|
|
55
76
|
}
|
|
56
77
|
setSaving(true);
|
|
57
78
|
try {
|
|
58
|
-
const data = await saveGoogleCredentials(
|
|
79
|
+
const data = await saveGoogleCredentials({
|
|
80
|
+
clientId,
|
|
81
|
+
clientSecret,
|
|
82
|
+
email,
|
|
83
|
+
client,
|
|
84
|
+
personal,
|
|
85
|
+
accountId,
|
|
86
|
+
});
|
|
59
87
|
if (data.ok) {
|
|
60
88
|
onClose();
|
|
61
|
-
onSaved();
|
|
89
|
+
onSaved?.(data.account);
|
|
62
90
|
} else setError(data.error || "Failed to save credentials");
|
|
63
91
|
} catch {
|
|
64
92
|
setError("Request failed");
|
|
@@ -104,7 +132,7 @@ export const CredentialsModal = ({ visible, onClose, onSaved }) => {
|
|
|
104
132
|
panelClassName="bg-modal border border-border rounded-xl p-6 max-w-lg w-full space-y-4"
|
|
105
133
|
>
|
|
106
134
|
<${PageHeader}
|
|
107
|
-
title
|
|
135
|
+
title=${title}
|
|
108
136
|
actions=${html`
|
|
109
137
|
<button
|
|
110
138
|
type="button"
|
|
@@ -338,7 +366,7 @@ export const CredentialsModal = ({ visible, onClose, onSaved }) => {
|
|
|
338
366
|
loading=${saving}
|
|
339
367
|
tone="primary"
|
|
340
368
|
size="lg"
|
|
341
|
-
idleLabel
|
|
369
|
+
idleLabel=${submitLabel}
|
|
342
370
|
loadingLabel="Saving..."
|
|
343
371
|
className="w-full px-4 py-2 rounded-lg text-sm"
|
|
344
372
|
/>
|