clay-server 5.5.0-beta.4 → 5.5.0-beta.5
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/github-connection-store.js +39 -0
- package/lib/github-runtime.js +43 -0
- package/lib/project-github-connection.js +164 -0
- package/lib/project.js +8 -0
- package/lib/public/appearance-bootstrap.js +6 -0
- package/lib/public/css/chrome-material.css +133 -0
- package/lib/public/css/menus.css +1 -17
- package/lib/public/css/navigation-island.css +261 -0
- package/lib/public/css/notifications-center.css +1 -17
- package/lib/public/css/settings-ui.css +8 -2
- package/lib/public/css/shared-browser.css +1 -18
- package/lib/public/css/split-view-layout.css +9 -5
- package/lib/public/css/sticky-notes.css +1 -18
- package/lib/public/css/title-bar.css +1 -1
- package/lib/public/css/workspace-background-gallery.css +43 -0
- package/lib/public/css/you.css +13 -10
- package/lib/public/index.html +53 -46
- package/lib/public/modules/app-messages.js +5 -0
- package/lib/public/modules/appearance-preferences.js +81 -0
- package/lib/public/modules/context-sources.js +2 -282
- package/lib/public/modules/github-mark.js +2 -0
- package/lib/public/modules/pane-appearance-bridge.js +9 -1
- package/lib/public/modules/pane-header-tooltips.js +36 -9
- package/lib/public/modules/personal-email-editor.js +305 -0
- package/lib/public/modules/personal-integration-editor.js +70 -0
- package/lib/public/modules/personal-settings-dialog.js +15 -1
- package/lib/public/modules/session-list-material.js +25 -0
- package/lib/public/modules/sidebar-collapse-focus.js +22 -0
- package/lib/public/modules/sidebar-sessions.js +2 -0
- package/lib/public/modules/sidebar.js +9 -10
- package/lib/public/modules/split-pane-renderer.js +5 -5
- package/lib/public/modules/split-view-shell-controls.js +9 -5
- package/lib/public/modules/split-view.js +2 -2
- package/lib/public/modules/theme.js +10 -28
- package/lib/public/modules/tool-pane-chrome.js +4 -1
- package/lib/public/modules/tool-pane-material.js +23 -0
- package/lib/public/modules/tooltip.js +7 -3
- package/lib/public/modules/tui-attention.js +9 -4
- package/lib/public/modules/user-settings-account.js +10 -1
- package/lib/public/modules/user-settings-github.js +176 -0
- package/lib/public/modules/user-settings-integrations.js +23 -12
- package/lib/public/modules/user-settings-layout.js +5 -3
- package/lib/public/modules/user-settings-themes.js +6 -2
- package/lib/public/modules/user-settings.js +2 -1
- package/lib/public/modules/workspace-background-gallery.js +92 -0
- package/lib/public/modules/workspace-custom-colors.js +132 -0
- package/lib/public/modules/you-activity.js +4 -2
- package/lib/public/pane-assets/app-C27ONMHZ.js +1 -0
- package/lib/public/pane-assets/chunk-4REWYEXI.js +1 -0
- package/lib/public/pane-assets/chunk-6EDR4PMU.js +1 -0
- package/lib/public/pane-assets/{chunk-X45WI2U7.js → chunk-76JE6SO6.js} +1 -1
- package/lib/public/pane-assets/chunk-AFATDM32.js +1 -0
- package/lib/public/pane-assets/{chunk-UVTS6GUM.js → chunk-DTNAWMGQ.js} +1 -1
- package/lib/public/pane-assets/chunk-EGCR2MH4.js +1 -0
- package/lib/public/pane-assets/chunk-ET4NUBYW.js +1 -0
- package/lib/public/pane-assets/chunk-EYJZ3R7Y.js +1 -0
- package/lib/public/pane-assets/{chunk-FB5B2WYF.js → chunk-I5Q4CZDJ.js} +1 -1
- package/lib/public/pane-assets/{chunk-53KRXJDL.js → chunk-KJN6KGEN.js} +1 -1
- package/lib/public/pane-assets/chunk-KZ3CYS5U.js +1 -0
- package/lib/public/pane-assets/chunk-MR4FHQFA.js +1 -0
- package/lib/public/pane-assets/chunk-NRC5RDIL.js +1 -0
- package/lib/public/pane-assets/chunk-NRP4NXVZ.js +1 -0
- package/lib/public/pane-assets/chunk-OERDYIHU.js +1 -0
- package/lib/public/pane-assets/{chunk-TK446XFF.js → chunk-OGIHAXLD.js} +1 -1
- package/lib/public/pane-assets/chunk-OQBKX36J.js +2 -0
- package/lib/public/pane-assets/chunk-Q2HJ5CWT.js +1 -0
- package/lib/public/pane-assets/{chunk-4WYURTPH.js → chunk-QOL6PHMZ.js} +1 -1
- package/lib/public/pane-assets/{chunk-AMZ7P526.js → chunk-TVKTFEST.js} +1 -1
- package/lib/public/pane-assets/{chunk-TU3AHPSN.js → chunk-UMXZTYOY.js} +1 -1
- package/lib/public/pane-assets/chunk-W5MFUKXJ.js +1 -0
- package/lib/public/pane-assets/chunk-W7XXER5Z.js +174 -0
- package/lib/public/pane-assets/chunk-Y77DR2RU.js +1 -0
- package/lib/public/pane-assets/{chunk-SGMEO3P7.js → chunk-YBYNU4VI.js} +1 -1
- package/lib/public/pane-assets/{chunk-JIV7O5GN.js → chunk-YHBTK56T.js} +1 -1
- package/lib/public/pane-assets/manifest.json +451 -437
- package/lib/public/pane-assets/style-F4MQRRX2.css +1 -0
- package/lib/public/style.css +4 -0
- package/lib/public/workspace-art/workshop-dark.webp +0 -0
- package/lib/public/workspace-art/workshop-light.webp +0 -0
- package/lib/public/workspace-background-catalog.js +40 -0
- package/lib/server-appearance-settings.js +16 -5
- package/lib/server.js +6 -0
- package/lib/session-github.js +1 -7
- package/lib/terminal-manager.js +5 -1
- package/lib/terminal.js +10 -1
- package/package.json +1 -1
- package/lib/public/pane-assets/app-PV3SEOTX.js +0 -1
- package/lib/public/pane-assets/chunk-2G6DB3ES.js +0 -1
- package/lib/public/pane-assets/chunk-6QFLYOVY.js +0 -1
- package/lib/public/pane-assets/chunk-BK2NRZFM.js +0 -1
- package/lib/public/pane-assets/chunk-D3WR2TYZ.js +0 -2
- package/lib/public/pane-assets/chunk-E7D5A27I.js +0 -1
- package/lib/public/pane-assets/chunk-FDBSE7BE.js +0 -174
- package/lib/public/pane-assets/chunk-FHB76KH2.js +0 -1
- package/lib/public/pane-assets/chunk-FNGO6432.js +0 -1
- package/lib/public/pane-assets/chunk-HF7AK56Q.js +0 -1
- package/lib/public/pane-assets/chunk-ILVRGTDF.js +0 -1
- package/lib/public/pane-assets/chunk-L3SFMSLP.js +0 -1
- package/lib/public/pane-assets/chunk-PAY3A4JS.js +0 -1
- package/lib/public/pane-assets/chunk-QVHC324H.js +0 -1
- package/lib/public/pane-assets/chunk-SFANEWZA.js +0 -1
- package/lib/public/pane-assets/chunk-US5VELGB.js +0 -1
- package/lib/public/pane-assets/chunk-VZ2OKDAO.js +0 -1
- package/lib/public/pane-assets/style-563D4UN6.css +0 -1
|
@@ -225,7 +225,6 @@ export function handlePaneSearchMessage(event, host) {
|
|
|
225
225
|
}
|
|
226
226
|
return false;
|
|
227
227
|
}
|
|
228
|
-
|
|
229
228
|
function startPaneRename(header, titleEl, pane) {
|
|
230
229
|
if (header.querySelector(".split-pane-rename-input")) return;
|
|
231
230
|
var section = header.parentNode, frame = section.querySelector('.split-pane-frame');
|
|
@@ -234,7 +233,7 @@ function startPaneRename(header, titleEl, pane) {
|
|
|
234
233
|
initial.binding.sessionOriginId !== frame.dataset.sessionOriginId) return;
|
|
235
234
|
var input = document.createElement("input");
|
|
236
235
|
input.type = "text"; input.className = "split-pane-rename-input"; input.value = pane.title;
|
|
237
|
-
titleEl.style.display = "none";
|
|
236
|
+
titleEl.style.display = "none"; titleEl.parentNode.insertBefore(input, titleEl); input.focus(); input.select();
|
|
238
237
|
var done = false;
|
|
239
238
|
var origin = frame.dataset.sessionOriginId, binding = initial.binding, socket = getWs();
|
|
240
239
|
function current() {
|
|
@@ -293,8 +292,9 @@ export function createSplitPane(pane, closePane) {
|
|
|
293
292
|
var session = sessionById(pane.sessionId); var vendor = session && session.vendor || "unknown";
|
|
294
293
|
var vendorIcon = document.createElement("img"); vendorIcon.className = "split-pane-vendor"; vendorIcon.hidden = !VENDOR_AVATARS[vendor];
|
|
295
294
|
if (!vendorIcon.hidden) vendorIcon.src = VENDOR_AVATARS[vendor]; vendorIcon.alt = ""; vendorIcon.title = VENDOR_NAMES[vendor] || vendor; header.appendChild(vendorIcon);
|
|
295
|
+
var identity = document.createElement("span"); identity.className = "split-pane-identity"; header.appendChild(identity);
|
|
296
296
|
var title = document.createElement("span"); title.className = "split-pane-title"; title.textContent = pane.title; title.title = "Rename session";
|
|
297
|
-
title.addEventListener("click", function () { if (title.getAttribute('aria-disabled') === 'true' || !syncPaneHeaderState(paneEl)) return; var current = sessionById(Number(paneEl.dataset.sessionId)); if (current) startPaneRename(header, title, { sessionId: current.id, title: current.title }); });
|
|
297
|
+
title.addEventListener("click", function () { if (title.getAttribute('aria-disabled') === 'true' || !syncPaneHeaderState(paneEl)) return; var current = sessionById(Number(paneEl.dataset.sessionId)); if (current) startPaneRename(header, title, { sessionId: current.id, title: current.title }); }); identity.appendChild(title);
|
|
298
298
|
var dragSpace = document.createElement("span"); dragSpace.className = "split-pane-drag-space"; dragSpace.title = "Drag to rearrange panes"; dragSpace.setAttribute("aria-hidden", "true"); header.appendChild(dragSpace);
|
|
299
299
|
var frame = document.createElement("iframe"); frame.className = "split-pane-frame"; frame.dataset.projectSlug = pane.slug;
|
|
300
300
|
frame.dataset.sessionId = String(pane.sessionId); frame.src = paneUrl(pane); frame.title = pane.title;
|
|
@@ -323,7 +323,7 @@ export function createSplitPane(pane, closePane) {
|
|
|
323
323
|
}
|
|
324
324
|
close.dataset.sessionId = String(pane.sessionId); close.addEventListener("click", function () { if (syncPaneHeaderState(paneEl)) closePane(Number(this.dataset.sessionId)); }); header.appendChild(close);
|
|
325
325
|
var info = document.createElement("button"); info.type = "button"; info.className = "split-pane-info"; info.setAttribute("aria-label", "Session information"); info.title = "Session information"; info.innerHTML = iconHtml("info");
|
|
326
|
-
info.addEventListener("click", function (event) { event.stopPropagation(); if (syncPaneHeaderState(paneEl)) togglePaneInfo(info, frame); });
|
|
326
|
+
info.addEventListener("click", function (event) { event.stopPropagation(); if (syncPaneHeaderState(paneEl)) togglePaneInfo(info, frame); }); identity.insertBefore(info, title.nextSibling);
|
|
327
327
|
var sidebarButton = document.createElement("button"); sidebarButton.type = "button"; sidebarButton.className = "split-pane-sidebar";
|
|
328
328
|
var sidebarExpanded = !document.getElementById("layout").classList.contains("sidebar-collapsed");
|
|
329
329
|
sidebarButton.setAttribute("aria-label", sidebarExpanded ? "Collapse sidebar" : "Open sidebar");
|
|
@@ -378,7 +378,7 @@ export function createSplitPane(pane, closePane) {
|
|
|
378
378
|
var runtime = document.createElement("div"); runtime.className = "split-view-runtime";
|
|
379
379
|
vendorIcon.classList.add("split-view-runtime-icon"); runtime.appendChild(vendorIcon);
|
|
380
380
|
var runtimeModel = document.createElement("span"); runtimeModel.className = "split-view-model"; runtime.appendChild(runtimeModel);
|
|
381
|
-
header.insertBefore(runtime,
|
|
381
|
+
header.insertBefore(runtime, identity); header.appendChild(close); paneEl.appendChild(frame);
|
|
382
382
|
syncPaneHeaderState(paneEl);
|
|
383
383
|
return paneEl;
|
|
384
384
|
}
|
|
@@ -1,21 +1,23 @@
|
|
|
1
1
|
import { toggleSidebarCollapse } from './sidebar.js';
|
|
2
2
|
|
|
3
|
-
export function syncSidebarReopenButton(button
|
|
3
|
+
export function syncSidebarReopenButton(button) {
|
|
4
4
|
if (!button) return;
|
|
5
5
|
var layout = document.getElementById("layout");
|
|
6
6
|
var desktop = !window.matchMedia || window.matchMedia("(min-width: 769px)").matches;
|
|
7
|
-
button.hidden = !layout || !desktop || !
|
|
7
|
+
button.hidden = !layout || !desktop || !layout.classList.contains("sidebar-collapsed");
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
export function createSidebarReopenButton() {
|
|
11
|
-
var target = document.
|
|
11
|
+
var target = document.getElementById("icon-strip");
|
|
12
12
|
if (!target) return null;
|
|
13
|
+
var existing = document.getElementById("sidebar-shell-expand-btn");
|
|
14
|
+
if (existing) return existing;
|
|
13
15
|
var button = document.createElement("button");
|
|
14
16
|
button.id = "sidebar-shell-expand-btn";
|
|
15
17
|
button.type = "button";
|
|
16
18
|
button.title = "Open sidebar";
|
|
17
19
|
button.setAttribute("aria-label", "Open sidebar");
|
|
18
|
-
button.setAttribute("aria-controls", "sidebar-column");
|
|
20
|
+
button.setAttribute("aria-controls", "sidebar-column mate-sidebar-column");
|
|
19
21
|
button.setAttribute("aria-expanded", "false");
|
|
20
22
|
button.innerHTML = '<i data-lucide="panel-left-open"></i>';
|
|
21
23
|
button.hidden = true;
|
|
@@ -23,6 +25,8 @@ export function createSidebarReopenButton() {
|
|
|
23
25
|
if (button.hidden) return;
|
|
24
26
|
toggleSidebarCollapse();
|
|
25
27
|
});
|
|
26
|
-
|
|
28
|
+
var hint = document.getElementById("icon-strip-hint-project");
|
|
29
|
+
hint.parentNode.insertBefore(button, hint);
|
|
30
|
+
syncSidebarReopenButton(button);
|
|
27
31
|
return button;
|
|
28
32
|
}
|
|
@@ -200,7 +200,7 @@ function renderSplit(split) {
|
|
|
200
200
|
else clearRetainedPanes(host);
|
|
201
201
|
var panes = split && split.panes;
|
|
202
202
|
if (!panes || panes.length < 2) {
|
|
203
|
-
|
|
203
|
+
syncSidebarReopenButton(shellSidebarExpand);
|
|
204
204
|
placeStickyNotesOverlay(false);
|
|
205
205
|
var frames = host.querySelectorAll('.split-pane-frame');
|
|
206
206
|
var retain = frames.length > 0 && frames.length <= 3 && Array.from(frames).every(function (frame) { return frame.dataset.projectSlug === store.get('currentSlug') && frame.clayPane; });
|
|
@@ -212,7 +212,7 @@ function renderSplit(split) {
|
|
|
212
212
|
nativeApp.classList.remove("split-native-hidden");
|
|
213
213
|
return;
|
|
214
214
|
}
|
|
215
|
-
|
|
215
|
+
syncSidebarReopenButton(shellSidebarExpand);
|
|
216
216
|
placeStickyNotesOverlay(true);
|
|
217
217
|
nativeApp.classList.add("split-native-hidden");
|
|
218
218
|
host.classList.add("visible");
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { store } from './store.js';
|
|
2
|
+
import { appearanceStatus, queueAppearanceSave, initWorkspaceBackground, effectiveAppearance, workspaceBackgrounds } from './appearance-preferences.js';
|
|
2
3
|
import { refreshIcons } from './icons.js';
|
|
3
4
|
import { setTerminalTheme } from './terminal.js';
|
|
4
5
|
import { setTuiSessionTheme } from './session-tui-view.js';
|
|
@@ -120,10 +121,10 @@ export function onThemeChange(fn) {
|
|
|
120
121
|
// The appearance this frame currently shows, for the parent shell to hand to
|
|
121
122
|
// its pane iframes. Never persisted from here.
|
|
122
123
|
export function getAppearanceSnapshot() {
|
|
123
|
-
var pref =
|
|
124
|
+
var pref = effectiveAppearance();
|
|
124
125
|
return { themeId: currentThemeId, userId: store.get('myUserId') || null,
|
|
125
|
-
preference: { configured: !!pref.configured || !!pref.mode || !!pref.theme, theme: pref.theme || null, mode: pref.mode || null,
|
|
126
|
-
lightTheme: pref.lightTheme || null, darkTheme: pref.darkTheme || null } };
|
|
126
|
+
preference: { configured: !!pref.configured || !!pref.mode || !!pref.theme || !!pref.background, theme: pref.theme || null, mode: pref.mode || null,
|
|
127
|
+
lightTheme: pref.lightTheme || null, darkTheme: pref.darkTheme || null, background: pref.background === 'workshop' ? 'workshop' : 'plain', backgroundColor: workspaceBackgrounds.getColor(pref.backgroundColor, pref.customBackgroundColors).id, customBackgroundColors: pref.customBackgroundColors || [] } };
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
// A pane iframe mirrors the parent's live appearance. The server remains the
|
|
@@ -464,6 +465,7 @@ function rebuildPicker() {
|
|
|
464
465
|
|
|
465
466
|
// --- Init ---
|
|
466
467
|
export function initTheme() {
|
|
468
|
+
initWorkspaceBackground();
|
|
467
469
|
var seed = window.__clayAppearanceSeed;
|
|
468
470
|
if (seed && seed.configured) store.set({ appearancePreference: seed });
|
|
469
471
|
var initialMode = seed && seed.configured && (seed.mode === 'light' || seed.mode === 'dark')
|
|
@@ -507,11 +509,6 @@ export function initTheme() {
|
|
|
507
509
|
|
|
508
510
|
}
|
|
509
511
|
|
|
510
|
-
function appearanceStatus(text, error) {
|
|
511
|
-
store.set({ appearanceSaveStatus: text, appearanceSaveError: !!error });
|
|
512
|
-
var status = document.getElementById('us-appearance-status');
|
|
513
|
-
if (status) { status.textContent = text; status.dataset.state = error ? 'error' : ''; }
|
|
514
|
-
}
|
|
515
512
|
export function getAppearanceChoices() {
|
|
516
513
|
return { mode: (store.get('appearanceDraft') || store.get('appearancePreference') || {}).mode || 'system', lightTheme: themeIdForMode('light'), darkTheme: themeIdForMode('dark') };
|
|
517
514
|
}
|
|
@@ -520,12 +517,14 @@ export function retryAppearanceSave() {
|
|
|
520
517
|
if (draft) saveAppearance(draft.theme, draft.mode, draft);
|
|
521
518
|
}
|
|
522
519
|
export function selectAppearanceMode(mode) {
|
|
520
|
+
if ((store.get('appearanceColorEditor') || {}).saving) return;
|
|
523
521
|
var effective = mode === 'system' ? getSystemMode() : mode;
|
|
524
522
|
var id = themeIdForMode(effective);
|
|
525
523
|
saveAppearance(id, mode === 'system' ? null : mode);
|
|
526
524
|
applyTheme(id);
|
|
527
525
|
}
|
|
528
526
|
export function selectAppearanceTheme(mode, id) {
|
|
527
|
+
if ((store.get('appearanceColorEditor') || {}).saving) return;
|
|
529
528
|
if (!getTheme(id) || getTheme(id).variant !== mode) return;
|
|
530
529
|
var effective = getEffectiveMode();
|
|
531
530
|
var current = store.get('appearanceDraft') || store.get('appearancePreference') || {};
|
|
@@ -536,34 +535,17 @@ export function selectAppearanceTheme(mode, id) {
|
|
|
536
535
|
function saveAppearance(theme, mode, overrides) {
|
|
537
536
|
var values = Object.assign({}, store.get('appearanceDraft') || store.get('appearancePreference') || {}, {theme:theme, mode:mode}, overrides || {});
|
|
538
537
|
delete values.configured;
|
|
538
|
+
['lightTheme', 'darkTheme'].forEach(function (key) { if (!values[key]) delete values[key]; });
|
|
539
539
|
var selected = getTheme(theme);
|
|
540
540
|
if (selected) values[selected.variant + 'Theme'] = theme;
|
|
541
|
-
|
|
542
|
-
var queue = store.get('appearanceSaveQueue') || Promise.resolve();
|
|
543
|
-
var next = queue.catch(function () {}).then(function () {return persistAppearance(values);});
|
|
544
|
-
store.set({appearanceSaveQueue:next});
|
|
545
|
-
return next;
|
|
546
|
-
}
|
|
547
|
-
async function persistAppearance(values) {
|
|
548
|
-
var request = (store.get('appearanceSaveRequest') || 0) + 1;
|
|
549
|
-
store.set({ appearanceSaveRequest: request });
|
|
550
|
-
appearanceStatus('Saving appearance…');
|
|
551
|
-
try {
|
|
552
|
-
var response = await fetch('/api/user/appearance', {method:'PUT',headers:{'Content-Type':'application/json','X-Clay-Settings':'1'},body:JSON.stringify(values)});
|
|
553
|
-
if (!response.ok) throw new Error('Could not save appearance. Your preview remains. Retry to save it.');
|
|
554
|
-
if (store.get('appearanceSaveRequest') !== request) return;
|
|
555
|
-
var saved = await response.json();
|
|
556
|
-
store.set({ appearancePreference: saved, appearanceDraft: store.get('appearanceDraft') === values ? null : store.get('appearanceDraft') });
|
|
557
|
-
[STORAGE_KEY,SKIN_KEY,MODE_KEY,STORAGE_KEY+'-vars',STORAGE_KEY+'-variant'].forEach(function (key) {try {localStorage.removeItem(key);}catch(error){}});
|
|
558
|
-
appearanceStatus('Saved. Applied to this view; other devices use it when they reload.');
|
|
559
|
-
} catch(error) {if(store.get('appearanceSaveRequest') === request) appearanceStatus(error.message,true);}
|
|
541
|
+
return queueAppearanceSave(values);
|
|
560
542
|
}
|
|
561
543
|
async function loadAppearance() {
|
|
562
544
|
try {
|
|
563
545
|
var response = await fetch('/api/user/appearance');
|
|
564
546
|
if (!response.ok) throw new Error('Appearance preferences unavailable.');
|
|
565
547
|
var data = await response.json();
|
|
566
|
-
if(store.get('appearanceSaveRequest'))return;
|
|
548
|
+
if(store.get('appearanceDraft') || store.get('appearanceSaveRequest'))return;
|
|
567
549
|
// A pane that already mirrors its parent's live view is newer than this GET, which may predate a pending save.
|
|
568
550
|
if(externalThemeId)return;
|
|
569
551
|
if (data.configured) {store.set({appearancePreference:data});applyTheme(themeIdForMode(data.mode || getSystemMode()));}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bindToolPaneMaterial } from './tool-pane-material.js';
|
|
1
2
|
import { ensureWorkbenchWindowControls, syncWorkbenchWindowControls } from './workbench-window-controls.js';
|
|
2
3
|
import { WORKBENCH_PANELS } from './workbench-panels.js';
|
|
3
4
|
import { recordToolExpansion } from './view-presentation-state.js';
|
|
@@ -16,7 +17,7 @@ function titleFor(panel, name) {
|
|
|
16
17
|
try { return browser && browser.url ? new URL(browser.url).hostname : 'Browser'; } catch (error) { return 'Browser'; }
|
|
17
18
|
}
|
|
18
19
|
export function renderToolPaneChrome(panel, name) {
|
|
19
|
-
if (panel.dataset.fileInstance) return;
|
|
20
|
+
if (panel.dataset.fileInstance) { bindToolPaneMaterial(panel, panel.querySelector(':scope > .tool-pane-header')); return; }
|
|
20
21
|
if (WORKBENCH_PANELS[name]) { renderWorkbenchChrome(panel, name); return; }
|
|
21
22
|
var definition = TOOL_PANES[name];
|
|
22
23
|
if (!definition) return;
|
|
@@ -50,6 +51,7 @@ export function renderToolPaneChrome(panel, name) {
|
|
|
50
51
|
if (title.textContent !== label) { title.textContent = label; title.title = label; }
|
|
51
52
|
}
|
|
52
53
|
update();
|
|
54
|
+
bindToolPaneMaterial(panel, header);
|
|
53
55
|
}
|
|
54
56
|
|
|
55
57
|
store.subscribe(function (state, previous) {
|
|
@@ -61,6 +63,7 @@ store.subscribe(function (state, previous) {
|
|
|
61
63
|
function renderWorkbenchChrome(panel, name) {
|
|
62
64
|
panel.classList.add('workbench-pane-shell');
|
|
63
65
|
var header = panel.querySelector(':scope > header');
|
|
66
|
+
bindToolPaneMaterial(panel, header);
|
|
64
67
|
if (!header || header.dataset.toolPane === name) return;
|
|
65
68
|
header.dataset.toolPane = name;
|
|
66
69
|
header.classList.add('tool-pane-header', 'split-pane-header');
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// Keep the content backing below the real header without moving content nodes
|
|
2
|
+
// or giving filters to containers that own viewport-fixed controls.
|
|
3
|
+
export function bindToolPaneMaterial(panel, header) {
|
|
4
|
+
if (!header || panel.classList.contains('tool-pane-material')) return;
|
|
5
|
+
function measure() {
|
|
6
|
+
if (!panel.isConnected || !header.getClientRects().length) return;
|
|
7
|
+
// Layout coordinates stay stable during the existing pane scale animation.
|
|
8
|
+
var bottom = header.offsetTop + header.offsetHeight;
|
|
9
|
+
var value = Math.max(0, bottom) + 'px';
|
|
10
|
+
if (panel.style.getPropertyValue('--tool-pane-body-top') !== value) panel.style.setProperty('--tool-pane-body-top', value);
|
|
11
|
+
}
|
|
12
|
+
measure();
|
|
13
|
+
panel.classList.add('tool-pane-material');
|
|
14
|
+
var observer = typeof ResizeObserver === 'function' ? new ResizeObserver(measure) : null;
|
|
15
|
+
if (observer) { observer.observe(header); observer.observe(panel); }
|
|
16
|
+
else window.addEventListener('resize', measure);
|
|
17
|
+
panel.addEventListener('clay-tool-pane-dispose', function () {
|
|
18
|
+
if (observer) observer.disconnect();
|
|
19
|
+
else window.removeEventListener('resize', measure);
|
|
20
|
+
panel.classList.remove('tool-pane-material');
|
|
21
|
+
panel.style.removeProperty('--tool-pane-body-top');
|
|
22
|
+
}, { once: true });
|
|
23
|
+
}
|
|
@@ -18,8 +18,8 @@ function initTooltips() {
|
|
|
18
18
|
document.body.appendChild(tooltipEl);
|
|
19
19
|
|
|
20
20
|
// Convert existing title attributes to data-tip in target areas
|
|
21
|
-
convertTitles();
|
|
22
21
|
initPaneHeaderTooltips();
|
|
22
|
+
convertTitles();
|
|
23
23
|
|
|
24
24
|
// Delegate hover events on document for [data-tip]
|
|
25
25
|
document.addEventListener("mouseover", function (e) {
|
|
@@ -96,7 +96,7 @@ function cancelShow() {
|
|
|
96
96
|
function showTooltipAt(target) {
|
|
97
97
|
if (!tooltipEl || !target.isConnected || !target.getClientRects().length) return;
|
|
98
98
|
// A message preview already describes this Worker/task tab.
|
|
99
|
-
if (target.getAttribute('aria-describedby')
|
|
99
|
+
if ((target.getAttribute('aria-describedby') || '').split(/\s+/).indexOf('agent-message-bubble') >= 0 && document.getElementById('agent-message-bubble')) return;
|
|
100
100
|
// Suppress tooltip when an associated popover/picker is open
|
|
101
101
|
var suppressSel = target.getAttribute("data-tip-suppress-when-open");
|
|
102
102
|
if (suppressSel) {
|
|
@@ -171,7 +171,11 @@ function hideTooltip() {
|
|
|
171
171
|
currentTarget = null;
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
function pruneTooltip() {
|
|
174
|
+
function pruneTooltip() {
|
|
175
|
+
if (!currentTarget) return;
|
|
176
|
+
if (!currentTarget.isConnected || !currentTarget.getClientRects().length) { cancelShow(); hideTooltip(); }
|
|
177
|
+
else if (!currentTarget.hasAttribute('data-tip-html') && tooltipEl.textContent !== currentTarget.getAttribute('data-tip')) showTooltipAt(currentTarget);
|
|
178
|
+
}
|
|
175
179
|
|
|
176
180
|
function dismissTooltip() { cancelShow(); hideTooltip(); }
|
|
177
181
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
1
2
|
// tui-attention.js
|
|
2
3
|
//
|
|
3
4
|
// Modal that mounts a transient xterm attached to a Claude TUI session's
|
|
@@ -64,7 +65,7 @@ function teardownModalXterm() {
|
|
|
64
65
|
|
|
65
66
|
function modalSend(msg) {
|
|
66
67
|
if (!modalWs || modalWs.readyState !== 1) return;
|
|
67
|
-
try { modalWs.send(JSON.stringify(msg)); } catch (e) {}
|
|
68
|
+
try { if (store.get('tuiModalGithubAttachKey')) msg.githubAttachKey = store.get('tuiModalGithubAttachKey'); modalWs.send(JSON.stringify(msg)); } catch (e) {}
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
function fitModalXterm() {
|
|
@@ -143,6 +144,7 @@ export function openTuiModal(terminalId, sourceSlug, info) {
|
|
|
143
144
|
modalTerminalId = terminalId;
|
|
144
145
|
modalSourceSlug = sourceSlug;
|
|
145
146
|
var infoObj = info || {};
|
|
147
|
+
store.set({tuiModalGithubAttachKey:infoObj.githubAttachKey || null,tuiModalProcessExited:false});
|
|
146
148
|
modalCloseHook = typeof infoObj.onClose === "function" ? infoObj.onClose : null;
|
|
147
149
|
setModalBreadcrumb({
|
|
148
150
|
projectIcon: infoObj.projectIcon || null,
|
|
@@ -206,7 +208,8 @@ export function openTuiModal(terminalId, sourceSlug, info) {
|
|
|
206
208
|
} else if (msg.type === "term_resized" && modalXterm && msg.cols > 0 && msg.rows > 0) {
|
|
207
209
|
try { modalXterm.resize(msg.cols, msg.rows); } catch (err) {}
|
|
208
210
|
} else if (msg.type === "term_exited" && modalXterm) {
|
|
209
|
-
|
|
211
|
+
store.set({tuiModalProcessExited:true});
|
|
212
|
+
try { modalXterm.write("\r\n\x1b[90m[process exited]\x1b[0m\r\n"); } catch (err) {}
|
|
210
213
|
} else if (msg.type === "term_closed") {
|
|
211
214
|
closeTuiModal();
|
|
212
215
|
}
|
|
@@ -237,16 +240,18 @@ export function openTuiModal(terminalId, sourceSlug, info) {
|
|
|
237
240
|
|
|
238
241
|
export function closeTuiModal() {
|
|
239
242
|
if (!modalEl) return;
|
|
243
|
+
var processExited = store.get('tuiModalProcessExited');
|
|
240
244
|
var closeHook = modalCloseHook;
|
|
241
245
|
modalCloseHook = null;
|
|
242
246
|
if (modalTerminalId != null && modalWs && modalWs.readyState === 1) {
|
|
243
|
-
try { modalWs.send(JSON.stringify({ type: "term_detach", id: modalTerminalId })); } catch (e) {}
|
|
247
|
+
try { modalWs.send(JSON.stringify({ type: "term_detach", id: modalTerminalId, githubAttachKey: store.get('tuiModalGithubAttachKey') })); } catch (e) {}
|
|
244
248
|
}
|
|
245
249
|
if (modalWs) {
|
|
246
250
|
try { modalWs.close(); } catch (e) {}
|
|
247
251
|
modalWs = null;
|
|
248
252
|
}
|
|
249
253
|
modalTerminalId = null;
|
|
254
|
+
store.set({tuiModalGithubAttachKey:null,tuiModalProcessExited:false});
|
|
250
255
|
modalSourceSlug = null;
|
|
251
256
|
teardownModalXterm();
|
|
252
257
|
modalEl.classList.add("hidden");
|
|
@@ -255,7 +260,7 @@ export function closeTuiModal() {
|
|
|
255
260
|
modalKeyHandler = null;
|
|
256
261
|
}
|
|
257
262
|
if (closeHook) {
|
|
258
|
-
try { closeHook(); } catch (e) {}
|
|
263
|
+
try { closeHook({ exited: processExited }); } catch (e) {}
|
|
259
264
|
}
|
|
260
265
|
}
|
|
261
266
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { selectIntegrationEditor } from './personal-integration-editor.js';
|
|
1
2
|
import { refreshIcons } from './icons.js';
|
|
2
3
|
import { resetAccountSessions } from './user-settings-sessions.js';
|
|
3
4
|
import { confirmSettingsAction as showConfirmDialog } from './server-settings-state.js';
|
|
@@ -283,6 +284,7 @@ export function renderEmailSettings() {
|
|
|
283
284
|
var acc = accounts[i];
|
|
284
285
|
var row = document.createElement('div');
|
|
285
286
|
row.className = 'us-integration-email-row';
|
|
287
|
+
row.dataset.emailAccountId = acc.id;
|
|
286
288
|
row.innerHTML =
|
|
287
289
|
'<div class="us-voice-row-controls"><div class="us-integration-brand">' + providerIcon(acc.provider) + '</div>' +
|
|
288
290
|
'<div class="us-voice-provider-copy"><strong>' + escHtml(acc.email) + '</strong><small>Email · ' + escHtml(({gmail:'Gmail',outlook:'Outlook Mail',yahoo:'Yahoo Mail',custom:'Custom email'})[acc.provider] || 'Custom email') + (acc.label ? ' · ' + escHtml(acc.label) : '') + '</small></div>' +
|
|
@@ -292,7 +294,10 @@ export function renderEmailSettings() {
|
|
|
292
294
|
manageBtn.setAttribute('aria-label', 'Manage ' + acc.email);
|
|
293
295
|
manageBtn.addEventListener('click', function () {
|
|
294
296
|
var panel = this.closest('.us-integration-email-row').querySelector('.us-email-manage');
|
|
295
|
-
|
|
297
|
+
var row = this.closest('.us-integration-email-row');
|
|
298
|
+
selectIntegrationEditor('email-manage:' + row.dataset.emailAccountId, '[data-email-account-id="' + CSS.escape(row.dataset.emailAccountId) + '"] .us-email-manage', function () {
|
|
299
|
+
panel.hidden = false; row.querySelector('[aria-expanded]').setAttribute('aria-expanded', 'true'); panel.querySelector('button').focus();
|
|
300
|
+
});
|
|
296
301
|
});
|
|
297
302
|
var removeBtn = row.querySelector('[data-account-id]');
|
|
298
303
|
removeBtn.addEventListener('click', function () {
|
|
@@ -305,6 +310,10 @@ export function renderEmailSettings() {
|
|
|
305
310
|
});
|
|
306
311
|
|
|
307
312
|
listEl.appendChild(row);
|
|
313
|
+
var active = store.get('personalIntegrationEditor');
|
|
314
|
+
if (active && active.key === 'email-manage:' + acc.id) {
|
|
315
|
+
row.querySelector('.us-email-manage').hidden = false; manageBtn.setAttribute('aria-expanded','true');
|
|
316
|
+
}
|
|
308
317
|
}
|
|
309
318
|
refreshIcons();
|
|
310
319
|
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { selectIntegrationEditor, clearIntegrationEditor } from './personal-integration-editor.js';
|
|
2
|
+
import { showToast } from './utils.js';
|
|
3
|
+
import { store } from './store.js';
|
|
4
|
+
import { getWs } from './ws-ref.js';
|
|
5
|
+
import { githubMark } from './github-mark.js';
|
|
6
|
+
import { confirmSettingsAction } from './server-settings-state.js';
|
|
7
|
+
import { openTuiModal, closeTuiModal, getTuiModalTerminalId } from './tui-attention.js';
|
|
8
|
+
function context() { return JSON.stringify([store.get('myUserId'),store.get('currentSlug'),store.get('projectSplitViewSocketGeneration')]); }
|
|
9
|
+
function send(action) {
|
|
10
|
+
var ws = getWs();
|
|
11
|
+
if (!ws || ws.readyState !== 1 || !store.get('githubSettingsContextId')) return false;
|
|
12
|
+
var requestId = crypto.randomUUID(), data = store.get('githubSettingsData') || {};
|
|
13
|
+
ws.send(JSON.stringify({type:'personal_github_'+action,requestId:requestId,contextId:store.get('githubSettingsContextId'),reviewId:data.reviewId}));
|
|
14
|
+
if (action !== 'release' && action !== 'stop') {
|
|
15
|
+
store.set({githubSettingsRequest:requestId,githubSettingsPending:true,githubSettingsAction:action});
|
|
16
|
+
if (action === 'get') store.set({githubSettingsData:Object.assign({},data,{state:'checking',observed:null,reviewId:null})});
|
|
17
|
+
setTimeout(function () {
|
|
18
|
+
if (store.get('githubSettingsRequest') !== requestId || !store.get('githubSettingsPending')) return;
|
|
19
|
+
store.set({githubSettingsPending:false,githubSettingsData:Object.assign({},store.get('githubSettingsData'),{state:'unknown',reviewId:null,message:'The response is unknown. Stop any active login, then refresh. No action is retried automatically.'})}); render();
|
|
20
|
+
},20000);
|
|
21
|
+
}
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
function text(tag,value,cls) { var el=document.createElement(tag); el.textContent=value; if(cls) el.className=cls; return el; }
|
|
25
|
+
function button(parent,label,action,disabled) {
|
|
26
|
+
var el=text('button',label,'settings-btn-sm');el.type='button';el.disabled=!!disabled;el.onclick=action;parent.appendChild(el);return el;
|
|
27
|
+
}
|
|
28
|
+
function open() { selectIntegrationEditor('github', '#us-github-editor', function () { loadGithubIntegration(); if (!store.get('githubSettingsPending')) send('get'); store.set({githubSettingsEditing:true});render();document.getElementById('us-github-editor').focus(); }); }
|
|
29
|
+
function closeTerminal() {
|
|
30
|
+
var id=store.get('githubSettingsTerminal');store.set({githubSettingsTerminal:null});
|
|
31
|
+
if(id && getTuiModalTerminalId() === id) closeTuiModal();
|
|
32
|
+
}
|
|
33
|
+
export function closeGithubEditor() { store.set({githubSettingsEditing:false});clearIntegrationEditor('github');render(); }
|
|
34
|
+
function close() { closeGithubEditor();var el=document.querySelector('#us-github-connections button') || document.getElementById('us-github-add');if(el)el.focus(); }
|
|
35
|
+
function statusLabel(data) {
|
|
36
|
+
var saved=data.saved && data.saved.connection, observed=data.observed;
|
|
37
|
+
if (!saved) return '';
|
|
38
|
+
if (!observed || data.state === 'checking') return 'Saved · Verification pending';
|
|
39
|
+
if (observed.status !== 'authenticated') return 'Saved · '+(observed.status === 'unauthenticated' ? 'Sign-in required' : 'Verification unknown');
|
|
40
|
+
var sameRuntime=['user','uid','gid','home'].every(function(key){return saved.runtime[key]===data.runtime[key];});
|
|
41
|
+
if(saved.account.id !== observed.account.id || saved.account.login !== observed.account.login || !sameRuntime)return 'Saved · Account or runtime changed';
|
|
42
|
+
return 'Verified '+new Date(observed.checkedAt).toLocaleString();
|
|
43
|
+
}
|
|
44
|
+
function render() {
|
|
45
|
+
var editor = document.getElementById('us-github-editor');
|
|
46
|
+
var focused = editor && editor.contains(document.activeElement);
|
|
47
|
+
renderEditor();
|
|
48
|
+
if (focused && editor && !editor.hidden && !editor.contains(document.activeElement)) editor.focus();
|
|
49
|
+
}
|
|
50
|
+
function renderEditor() {
|
|
51
|
+
var root=document.getElementById('us-github-connections'), add=document.getElementById('us-github-add'), editor=document.getElementById('us-github-editor');
|
|
52
|
+
if(!root || !add || !editor)return;
|
|
53
|
+
var data=store.get('githubSettingsData') || {}, saved=data.saved && data.saved.connection, observed=data.observed, pending=store.get('githubSettingsPending');
|
|
54
|
+
add.hidden=!!saved;
|
|
55
|
+
if(saved){
|
|
56
|
+
var row=root.firstElementChild;
|
|
57
|
+
if(!row){
|
|
58
|
+
row=text('div','','us-voice-row');row.dataset.connectionId='github';var controls=text('div','','us-voice-row-controls');
|
|
59
|
+
var brand=text('span','','us-integration-brand');brand.innerHTML=githubMark();var copy=text('div','','us-voice-provider-copy');
|
|
60
|
+
copy.append(text('strong',''),text('small',''));controls.append(brand,copy,text('span','','us-integration-state'));
|
|
61
|
+
var manage=button(controls,'Manage',open);manage.setAttribute('aria-controls','us-github-editor');
|
|
62
|
+
row.appendChild(controls);root.appendChild(row);
|
|
63
|
+
}
|
|
64
|
+
row.querySelector('strong').textContent=saved.account.login;
|
|
65
|
+
row.querySelector('small').textContent='GitHub · '+saved.account.host+' · ID '+saved.account.id;
|
|
66
|
+
row.querySelector('.us-integration-state').textContent=statusLabel(data);
|
|
67
|
+
row.querySelector('button').setAttribute('aria-expanded',String(!!store.get('githubSettingsEditing')));
|
|
68
|
+
if(store.get('githubSettingsEditing') && editor.parentElement!==row)row.appendChild(editor);
|
|
69
|
+
} else {
|
|
70
|
+
var setup=document.getElementById('us-integration-setup');if(editor.parentElement!==setup)setup.appendChild(editor);
|
|
71
|
+
root.replaceChildren();
|
|
72
|
+
}
|
|
73
|
+
editor.hidden=!store.get('githubSettingsEditing'); editor.replaceChildren();
|
|
74
|
+
if(editor.hidden)return;
|
|
75
|
+
editor.appendChild(text('h3',saved?'Manage GitHub':'Connect GitHub'));
|
|
76
|
+
if(data.runtime){
|
|
77
|
+
editor.appendChild(text('p',data.runtime.shared?'This server OS account shares GitHub CLI credentials across Clay Studio accounts and other processes.':'GitHub CLI runs as your mapped OS account.','settings-hint'));
|
|
78
|
+
var details=text('details'), summary=text('summary','Runtime account');details.append(summary,text('p','OS account '+data.runtime.user+' · UID '+data.runtime.uid+' / GID '+data.runtime.gid+' · HOME '+data.runtime.home,'settings-hint'));editor.appendChild(details);
|
|
79
|
+
}
|
|
80
|
+
if(observed){
|
|
81
|
+
var labels={missing:'GitHub CLI is not installed for this runtime. Ask the server operator to install gh, then refresh.',unknown:'Authentication could not be verified. Check the network and server setup, then refresh.',unauthenticated:'GitHub CLI is not authenticated for github.com.'};
|
|
82
|
+
editor.appendChild(text('p',observed.account?'Current CLI account: '+observed.account.login+' · ID '+observed.account.id+' · '+observed.account.host:labels[observed.status] || 'Verification pending.','settings-hint'));
|
|
83
|
+
if(observed.overridden)editor.appendChild(text('p','GH_TOKEN or GITHUB_TOKEN controls the effective CLI account. Browser sign-in cannot change this override. Ask the server operator to update the environment.','settings-hint'));
|
|
84
|
+
}
|
|
85
|
+
if(saved)editor.appendChild(text('p','Saved account: '+saved.account.login+' · ID '+saved.account.id+' · '+saved.account.host+'. Connected '+new Date(saved.connectedAt).toLocaleString()+'.','settings-hint'));
|
|
86
|
+
var help=text('a','Official GitHub CLI setup and authentication','settings-link');help.href='https://cli.github.com/manual/gh_auth_login';help.target='_blank';help.rel='noopener noreferrer';editor.appendChild(help);
|
|
87
|
+
if(data.state === 'started' || data.state === 'stopping' || store.get('githubSettingsTerminal')){
|
|
88
|
+
editor.appendChild(text('p',data.message || 'Waiting for GitHub approval…','settings-hint'));
|
|
89
|
+
button(editor,'Stop login',function(){send('stop');},data.state==='stopping');return;
|
|
90
|
+
}
|
|
91
|
+
var actions=text('div','','settings-form-actions');editor.appendChild(actions);
|
|
92
|
+
button(actions,'Refresh',function(){send('get');},pending);
|
|
93
|
+
if(observed && observed.account && data.reviewId){
|
|
94
|
+
button(actions,(saved?'Confirm account: ':'Connect ')+observed.account.login,function(){send('connect');},pending);
|
|
95
|
+
}
|
|
96
|
+
if(data.canLogin && observed && ['authenticated','unauthenticated'].indexOf(observed.status)>=0 && data.reviewId){
|
|
97
|
+
editor.appendChild(text('p','A new login may replace the GitHub CLI account for this runtime and change its Git authentication settings. GitHub CLI owns the credentials. After approval, verify and explicitly confirm the resulting account.','settings-hint'));
|
|
98
|
+
button(actions,saved?'Reconnect with browser':'Sign in with browser',function(){
|
|
99
|
+
confirmSettingsAction('Start gh auth login for github.com in OS account '+data.runtime.user+' (HOME '+data.runtime.home+')? This may replace credentials used by other applications. Approve only at https://github.com/login/device.','Start GitHub login',function(){send('login');});
|
|
100
|
+
},pending);
|
|
101
|
+
} else if(data.runtime && data.runtime.shared && !data.canLogin && !(observed && observed.overridden))editor.appendChild(text('p','Only a server administrator can start a new login in the shared OS account. You can connect its verified existing account.','settings-hint'));
|
|
102
|
+
if(saved && data.reviewId)button(actions,'Disconnect',function(){confirmSettingsAction('Remove only this Clay Studio association? GitHub CLI credentials remain available to other applications and existing CLI-backed GitHub tools.','Disconnect',function(){send('disconnect');});},pending);
|
|
103
|
+
button(actions,'Cancel',close,pending);
|
|
104
|
+
}
|
|
105
|
+
export function initGithubIntegration(root){
|
|
106
|
+
var list=text('div');list.id='us-github-connections';root.querySelector('#us-linear-connections').after(list);
|
|
107
|
+
var editor=text('section','','settings-form');editor.id='us-github-editor';editor.hidden=true;editor.tabIndex=-1;root.querySelector('#us-integration-setup').appendChild(editor);
|
|
108
|
+
var add=text('button','','us-integration-tile');add.type='button';add.id='us-github-add';add.innerHTML=githubMark()+'<span>GitHub</span>';add.onclick=open;root.querySelector('.us-integration-tiles').appendChild(add);
|
|
109
|
+
var status=text('p','','settings-status');status.id='us-github-status';status.setAttribute('role','status');status.setAttribute('aria-live','polite');list.after(status);
|
|
110
|
+
store.subscribe(function(state,previous){
|
|
111
|
+
if(state.myUserId!==previous.myUserId || state.currentSlug!==previous.currentSlug || state.projectSplitViewSocketGeneration!==previous.projectSplitViewSocketGeneration || state.connected===false && previous.connected!==false){leaveGithubIntegration();}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
export function loadGithubIntegration(){
|
|
115
|
+
if(store.get('githubSettingsContextId'))return;
|
|
116
|
+
store.set({githubSettingsContextId:crypto.randomUUID(),githubSettingsContext:context(),githubSettingsData:null});send('get');
|
|
117
|
+
}
|
|
118
|
+
export function leaveGithubIntegration(){
|
|
119
|
+
if(!store.get('githubSettingsContextId'))return;
|
|
120
|
+
if(store.get('githubSettingsTerminal') || store.get('githubSettingsPending') && store.get('githubSettingsAction')==='login') {
|
|
121
|
+
store.set({githubSettingsClosing:{contextId:store.get('githubSettingsContextId'),requestId:store.get('githubSettingsRequest'),owner:store.get('myUserId')}});
|
|
122
|
+
showToast(store.get('connected')===false?'GitHub login closure is unconfirmed after disconnection. Refresh before another attempt.':'Stopping the GitHub login terminal. Waiting for exit acknowledgement.','info');
|
|
123
|
+
}
|
|
124
|
+
send('release');closeTerminal();
|
|
125
|
+
store.set({githubSettingsContextId:null,githubSettingsRequest:null,githubSettingsPending:false,githubSettingsEditing:false,githubSettingsData:null});
|
|
126
|
+
var root=document.querySelector('.us-section[data-section=us-integrations]');if(root)root.dataset.githubLoaded='false';
|
|
127
|
+
clearIntegrationEditor('github');render();
|
|
128
|
+
}
|
|
129
|
+
export function handleGithubIntegration(message){
|
|
130
|
+
var closing=store.get('githubSettingsClosing');
|
|
131
|
+
if(closing && closing.contextId===message.contextId && closing.requestId===message.requestId && closing.owner===store.get('myUserId')) {
|
|
132
|
+
if(message.state==='stopped' || message.state==='unknown') {
|
|
133
|
+
showToast(message.message,message.state==='unknown'?'error':'info');
|
|
134
|
+
if(message.state==='stopped')store.set({githubSettingsClosing:null});
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if(!store.get('githubSettingsContextId') || store.get('githubSettingsContext')!==context() || message.contextId!==store.get('githubSettingsContextId') || message.requestId!==store.get('githubSettingsRequest'))return;
|
|
138
|
+
var previous=store.get('githubSettingsData') || {};
|
|
139
|
+
var data=Object.assign({},previous,message);
|
|
140
|
+
if(message.state==='error' || message.state==='unknown')data.observed=null;
|
|
141
|
+
store.set({githubSettingsData:data,githubSettingsPending:['checking','verifying','stopping'].indexOf(message.state)>=0});
|
|
142
|
+
var status=document.getElementById('us-github-status');if(status){status.textContent=message.message || (message.state==='checking'?'Checking GitHub…':'');status.dataset.state=['error','unknown'].indexOf(message.state)>=0?'error':'';}
|
|
143
|
+
if(message.state==='started'){
|
|
144
|
+
store.set({githubSettingsTerminal:message.terminalId});
|
|
145
|
+
openTuiModal(message.terminalId,message.slug,{sessionTitle:'GitHub sign in',compact:true,githubAttachKey:message.attachKey,onClose:function(result){if(!result.exited && store.get('githubSettingsTerminal')===message.terminalId)send('stop');}});
|
|
146
|
+
} else if(['ready','saved','stopped','unknown'].indexOf(message.state)>=0)closeTerminal();
|
|
147
|
+
var root=document.querySelector('.us-section[data-section=us-integrations]');if(root && message.saved)root.dataset.githubLoaded='true';
|
|
148
|
+
if(message.state==='saved'){store.set({githubSettingsEditing:false});}
|
|
149
|
+
render();
|
|
150
|
+
if(message.state==='saved')close();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Retain the editor until the exact login reports terminal exit; unknown never navigates.
|
|
154
|
+
export function stopGithubEditorLogin() {
|
|
155
|
+
var data = store.get('githubSettingsData') || {};
|
|
156
|
+
var login = store.get('githubSettingsTerminal') || ['started','stopping'].indexOf(data.state) >= 0 ||
|
|
157
|
+
store.get('githubSettingsAction') === 'login' && (store.get('githubSettingsPending') || data.state === 'unknown');
|
|
158
|
+
if (!login) return Promise.resolve(true);
|
|
159
|
+
return new Promise(function (resolve) {
|
|
160
|
+
var contextId = store.get('githubSettingsContextId'), requestId = store.get('githubSettingsRequest');
|
|
161
|
+
var timer;
|
|
162
|
+
function finish(ok) { clearTimeout(timer); unsubscribe(); resolve(ok); }
|
|
163
|
+
var unsubscribe = store.subscribe(function (state) {
|
|
164
|
+
if (state.githubSettingsContextId !== contextId || state.githubSettingsRequest !== requestId) { finish(false); return; }
|
|
165
|
+
var current = state.githubSettingsData || {};
|
|
166
|
+
if (current.state === 'stopped') finish(true);
|
|
167
|
+
else if (current.state === 'unknown' || state.connected === false) finish(false);
|
|
168
|
+
});
|
|
169
|
+
timer = setTimeout(function () {
|
|
170
|
+
var message = 'GitHub login closure is unconfirmed. Keep this editor open and ask the server operator to inspect it. No login is retried automatically.';
|
|
171
|
+
store.set({githubSettingsPending:false,githubSettingsData:Object.assign({},store.get('githubSettingsData'),{state:'unknown',message:message,reviewId:null})});
|
|
172
|
+
showToast(message,'error'); render(); finish(false);
|
|
173
|
+
},20000);
|
|
174
|
+
if (!send('stop')) finish(false);
|
|
175
|
+
});
|
|
176
|
+
}
|