clay-server 5.2.0-beta.2 → 5.2.0-beta.4
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/builtin-mates.js +2 -2
- package/lib/bundled-skills/clay-agent-browser/SKILL.md +6 -2
- package/lib/dm-digest-cursor.js +120 -0
- package/lib/knowledge-db-import-validation.js +46 -0
- package/lib/knowledge-db-import.js +98 -0
- package/lib/knowledge-db-service.js +18 -30
- package/lib/knowledge-db-storage-nedb-maintenance.js +99 -0
- package/lib/knowledge-db-storage-nedb.js +66 -0
- package/lib/knowledge-db-storage.js +72 -0
- package/lib/knowledge-db-validation.js +0 -0
- package/lib/knowledge-file-store.js +7 -1
- package/lib/mate-avatar-catalog.js +12 -0
- package/lib/mate-avatar-migration.js +41 -0
- package/lib/mate-knowledge-mate-bound.js +53 -0
- package/lib/mate-knowledge-mcp-server.js +55 -8
- package/lib/mate-knowledge-service.js +9 -30
- package/lib/mate-knowledge-text-tools.js +106 -0
- package/lib/mates.js +14 -3
- package/lib/mcp-agent-tools.js +117 -0
- package/lib/mcp-connection-store.js +119 -15
- package/lib/mcp-import-credentials.js +31 -0
- package/lib/mcp-local-transport.js +13 -0
- package/lib/mcp-oauth.js +8 -6
- package/lib/mcp-query-bridge.js +30 -0
- package/lib/mcp-remote-connections.js +74 -20
- package/lib/mcp-source-adapters.js +91 -0
- package/lib/mcp-source-context.js +42 -0
- package/lib/mcp-source-discovery.js +169 -0
- package/lib/mcp-source-plugins.js +68 -0
- package/lib/mcp-source-restrictions.js +47 -0
- package/lib/mcp-tool-policy.js +34 -0
- package/lib/project-ask-user.js +6 -4
- package/lib/project-debate.js +1 -0
- package/lib/project-mate-interaction.js +40 -62
- package/lib/project-mcp.js +54 -16
- package/lib/project-session-document.js +8 -3
- package/lib/project-session-github.js +16 -7
- package/lib/project-session-linear.js +14 -6
- package/lib/project-session-notes.js +7 -3
- package/lib/project-sessions.js +6 -2
- package/lib/project-shared-browser.js +56 -12
- package/lib/project.js +49 -34
- package/lib/public/app.js +5 -0
- package/lib/public/appearance-bootstrap.js +14 -0
- package/lib/public/css/clay-workspace.css +29 -0
- package/lib/public/css/home-debate-planning.css +2 -0
- package/lib/public/css/mate-avatar-gallery.css +12 -0
- package/lib/public/css/mate-conversation.css +10 -5
- package/lib/public/css/session-github.css +43 -1
- package/lib/public/css/shared-browser.css +35 -1
- package/lib/public/index.html +10 -4
- package/lib/public/mate-avatars/B01.png +0 -0
- package/lib/public/mate-avatars/B02.png +0 -0
- package/lib/public/mate-avatars/B03.png +0 -0
- package/lib/public/mate-avatars/B04.png +0 -0
- package/lib/public/mate-avatars/B05.png +0 -0
- package/lib/public/mate-avatars/B06.png +0 -0
- package/lib/public/mate-avatars/B07.png +0 -0
- package/lib/public/mate-avatars/B08.png +0 -0
- package/lib/public/mate-avatars/B09.png +0 -0
- package/lib/public/mate-avatars/B10.png +0 -0
- package/lib/public/mate-avatars/B11.png +0 -0
- package/lib/public/mate-avatars/B12.png +0 -0
- package/lib/public/mate-avatars/B13.png +0 -0
- package/lib/public/mate-avatars/B14.png +0 -0
- package/lib/public/mate-avatars/B15.png +0 -0
- package/lib/public/mate-avatars/B16.png +0 -0
- package/lib/public/mate-avatars/B17.png +0 -0
- package/lib/public/mate-avatars/B18.png +0 -0
- package/lib/public/mate-avatars/B19.png +0 -0
- package/lib/public/mate-avatars/B20.png +0 -0
- package/lib/public/mate-avatars/B21.png +0 -0
- package/lib/public/mate-avatars/B22.png +0 -0
- package/lib/public/mate-avatars/B23.png +0 -0
- package/lib/public/mate-avatars/B24.png +0 -0
- package/lib/public/mate-avatars/B25.png +0 -0
- package/lib/public/mate-avatars/B26.png +0 -0
- package/lib/public/mate-avatars/B27.png +0 -0
- package/lib/public/mate-avatars/B28.png +0 -0
- package/lib/public/mate-avatars/B29.png +0 -0
- package/lib/public/mate-avatars/B30.png +0 -0
- package/lib/public/mate-avatars/B31.png +0 -0
- package/lib/public/mate-avatars/B32.png +0 -0
- package/lib/public/mate-avatars/B33.png +0 -0
- package/lib/public/mate-avatars/B34.png +0 -0
- package/lib/public/mate-avatars/B35.png +0 -0
- package/lib/public/mate-avatars/B36.png +0 -0
- package/lib/public/mate-avatars/B37.png +0 -0
- package/lib/public/mate-avatars/B38.png +0 -0
- package/lib/public/mate-avatars/B39.png +0 -0
- package/lib/public/mate-avatars/B40.png +0 -0
- package/lib/public/mate-avatars/B41.png +0 -0
- package/lib/public/mate-avatars/B42.png +0 -0
- package/lib/public/mate-avatars/B43.png +0 -0
- package/lib/public/mate-avatars/B44.png +0 -0
- package/lib/public/mate-avatars/B45.png +0 -0
- package/lib/public/mate-avatars/B46.png +0 -0
- package/lib/public/mate-avatars/B47.png +0 -0
- package/lib/public/mate-avatars/B48.png +0 -0
- package/lib/public/mate-avatars/C01.png +0 -0
- package/lib/public/mate-avatars/C02.png +0 -0
- package/lib/public/mate-avatars/C03.png +0 -0
- package/lib/public/mate-avatars/C04.png +0 -0
- package/lib/public/mate-avatars/C05.png +0 -0
- package/lib/public/mate-avatars/C06.png +0 -0
- package/lib/public/mate-avatars/C07.png +0 -0
- package/lib/public/mate-avatars/C08.png +0 -0
- package/lib/public/mate-avatars/C09.png +0 -0
- package/lib/public/mate-avatars/C10.png +0 -0
- package/lib/public/mate-avatars/C11.png +0 -0
- package/lib/public/mate-avatars/C12.png +0 -0
- package/lib/public/mate-avatars/C13.png +0 -0
- package/lib/public/mate-avatars/C14.png +0 -0
- package/lib/public/mate-avatars/C15.png +0 -0
- package/lib/public/mate-avatars/C16.png +0 -0
- package/lib/public/mate-avatars/C17.png +0 -0
- package/lib/public/mate-avatars/C18.png +0 -0
- package/lib/public/mate-avatars/C19.png +0 -0
- package/lib/public/mate-avatars/C20.png +0 -0
- package/lib/public/mate-avatars/C21.png +0 -0
- package/lib/public/mate-avatars/C22.png +0 -0
- package/lib/public/mate-avatars/C23.png +0 -0
- package/lib/public/mate-avatars/C24.png +0 -0
- package/lib/public/mate-avatars/C25.png +0 -0
- package/lib/public/mate-avatars/C26.png +0 -0
- package/lib/public/mate-avatars/C27.png +0 -0
- package/lib/public/mate-avatars/C28.png +0 -0
- package/lib/public/mate-avatars/C29.png +0 -0
- package/lib/public/mate-avatars/C30.png +0 -0
- package/lib/public/mate-avatars/C31.png +0 -0
- package/lib/public/mate-avatars/C32.png +0 -0
- package/lib/public/mate-avatars/C33.png +0 -0
- package/lib/public/mate-avatars/C34.png +0 -0
- package/lib/public/mate-avatars/C35.png +0 -0
- package/lib/public/mate-avatars/C36.png +0 -0
- package/lib/public/mate-avatars/C37.png +0 -0
- package/lib/public/mate-avatars/C38.png +0 -0
- package/lib/public/mate-avatars/C39.png +0 -0
- package/lib/public/mate-avatars/C40.png +0 -0
- package/lib/public/mate-avatars/C41.png +0 -0
- package/lib/public/mate-avatars/C42.png +0 -0
- package/lib/public/mate-avatars/C43.png +0 -0
- package/lib/public/mate-avatars/C44.png +0 -0
- package/lib/public/mate-avatars/C45.png +0 -0
- package/lib/public/mate-avatars/C46.png +0 -0
- package/lib/public/mate-avatars/C47.png +0 -0
- package/lib/public/mate-avatars/C48.png +0 -0
- package/lib/public/mate-avatars/catalog.json +482 -0
- package/lib/public/mate-avatars/lead-clay.png +0 -0
- package/lib/public/modules/app-message-router.js +4 -0
- package/lib/public/modules/app-messages.js +13 -2
- package/lib/public/modules/app-rendering.js +1 -1
- package/lib/public/modules/avatar.js +15 -41
- package/lib/public/modules/chat-render-runtime.js +10 -1
- package/lib/public/modules/clay-browser-links.js +47 -0
- package/lib/public/modules/clay-log-links.js +1 -1
- package/lib/public/modules/command-palette.js +1 -1
- package/lib/public/modules/debate.js +1 -0
- package/lib/public/modules/history-replay-batch.js +5 -1
- package/lib/public/modules/home-debate-live.js +2 -0
- package/lib/public/modules/home-debate-planning.js +7 -7
- package/lib/public/modules/home-mate-avatar-editor.js +127 -208
- package/lib/public/modules/home-mate-chat.js +6 -6
- package/lib/public/modules/home-mate-settings.js +19 -12
- package/lib/public/modules/markdown.js +4 -0
- package/lib/public/modules/mate-avatar-catalog.js +97 -0
- package/lib/public/modules/mate-management.js +40 -7
- package/lib/public/modules/mcp-connection-view.js +31 -13
- package/lib/public/modules/mcp-discovery-view.js +31 -0
- package/lib/public/modules/mcp-ui.js +23 -4
- package/lib/public/modules/mention.js +3 -0
- package/lib/public/modules/pane-browser-bridge.js +20 -0
- package/lib/public/modules/profile.js +1 -238
- package/lib/public/modules/project-mate-navigation.js +99 -14
- package/lib/public/modules/search-clay-chat.js +16 -2
- package/lib/public/modules/session-github.js +23 -9
- package/lib/public/modules/session-work-primary.js +51 -0
- package/lib/public/modules/shared-browser-settings.js +112 -0
- package/lib/public/modules/shared-browser-tabs.js +13 -3
- package/lib/public/modules/shared-browser.js +27 -6
- package/lib/public/modules/split-view.js +2 -0
- package/lib/public/modules/theme.js +8 -1
- package/lib/public/modules/tools.js +2 -1
- package/lib/public/style.css +1 -0
- package/lib/public/sw.js +3 -1
- package/lib/sdk-bridge.js +9 -3
- package/lib/server-appearance-settings.js +13 -1
- package/lib/server-dm.js +1 -0
- package/lib/server-home-chat-events.js +14 -4
- package/lib/server-home-chat.js +10 -2
- package/lib/server-home-mcp-consent.js +14 -0
- package/lib/server-mates.js +6 -1
- package/lib/server-settings.js +4 -1
- package/lib/server.js +45 -12
- package/lib/session-work-primary.js +50 -0
- package/lib/sessions.js +3 -0
- package/lib/shared-browser-cookie-sync.js +32 -0
- package/lib/shared-browser-cookies.js +79 -0
- package/lib/shared-browser-runtime.js +25 -9
- package/lib/shared-browser-storage-sync.js +43 -0
- package/lib/shared-browser-storage.js +101 -0
- package/lib/shared-browser-tools.js +2 -1
- package/lib/shared-browser-worker.js +46 -10
- package/lib/yoke/acp-agent-profiles.js +2 -2
- package/package.json +5 -1
|
@@ -4,7 +4,7 @@ import { setChatLayout } from './theme.js';
|
|
|
4
4
|
// Generated identities use deterministic local pixel identicons.
|
|
5
5
|
|
|
6
6
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
7
|
-
import { avatarUrl,
|
|
7
|
+
import { avatarUrl, userAvatarSeed } from './avatar.js';
|
|
8
8
|
import { store } from './store.js';
|
|
9
9
|
import { applyTerminalFont } from './terminal-prefs.js';
|
|
10
10
|
|
|
@@ -586,240 +586,3 @@ export function getProfile() {
|
|
|
586
586
|
export function getProfileLang() {
|
|
587
587
|
return profile.lang;
|
|
588
588
|
}
|
|
589
|
-
|
|
590
|
-
// --- Mate profile popover (reuses same UI minus language) ---
|
|
591
|
-
var matePopoverEl = null;
|
|
592
|
-
var mateSaveTimer = null;
|
|
593
|
-
|
|
594
|
-
export function showMateProfilePopover(anchorEl, mateData, onUpdate) {
|
|
595
|
-
if (matePopoverEl) {
|
|
596
|
-
hideMatePopover();
|
|
597
|
-
return;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
var mp = mateData.profile || {};
|
|
601
|
-
var mateName = mp.displayName || mateData.name || '';
|
|
602
|
-
var mateColor = mp.avatarColor || '#5857fc';
|
|
603
|
-
var mateStyle = mp.avatarStyle || 'imprint';
|
|
604
|
-
var mateSeed = mp.avatarSeed || mateData.id || 'mate';
|
|
605
|
-
var mateCustom = mp.avatarCustom || '';
|
|
606
|
-
|
|
607
|
-
matePopoverEl = document.createElement('div');
|
|
608
|
-
matePopoverEl.className = 'profile-popover mate-profile-popover';
|
|
609
|
-
|
|
610
|
-
var html = '';
|
|
611
|
-
|
|
612
|
-
// Banner + close
|
|
613
|
-
html += '<div class="profile-banner">';
|
|
614
|
-
html += '<button class="profile-close-btn">×</button>';
|
|
615
|
-
html += '</div>';
|
|
616
|
-
|
|
617
|
-
// Avatar row
|
|
618
|
-
var mateAvatarSrc = mateCustom ? mateCustom : mateMarkUrl(mateName || 'Mate', 80);
|
|
619
|
-
html += '<div class="profile-avatar-row">';
|
|
620
|
-
html += '<div class="profile-popover-avatar">';
|
|
621
|
-
html += '<img class="profile-popover-avatar-img" src="' + mateAvatarSrc + '" alt="' + escapeAttr(mateName || 'Clay Mate') + '">';
|
|
622
|
-
html += '</div>';
|
|
623
|
-
html += '<div class="profile-name-display">' + escapeAttr(mateName || 'New Mate') + '</div>';
|
|
624
|
-
html += '</div>';
|
|
625
|
-
|
|
626
|
-
// Body
|
|
627
|
-
html += '<div class="profile-popover-body">';
|
|
628
|
-
|
|
629
|
-
// Name
|
|
630
|
-
html += '<div class="profile-field">';
|
|
631
|
-
html += '<label class="profile-field-label">Display Name</label>';
|
|
632
|
-
html += '<input type="text" class="profile-field-input" id="mate-profile-name" value="' + escapeAttr(mateName) + '" placeholder="Name your mate..." maxlength="50" spellcheck="false" autocomplete="off">';
|
|
633
|
-
html += '</div>';
|
|
634
|
-
|
|
635
|
-
// Avatar picker (hidden if avatar is locked)
|
|
636
|
-
var mateAvatarLocked = mp.avatarLocked || false;
|
|
637
|
-
if (!mateAvatarLocked) {
|
|
638
|
-
html += '<div class="profile-field">';
|
|
639
|
-
html += '<label class="profile-field-label">Identity</label>';
|
|
640
|
-
html += '<div class="profile-imprint-panel">';
|
|
641
|
-
html += '<div class="profile-imprint-preview"><img src="' + mateMarkUrl(mateName || 'Mate', 72) + '" alt="Clay Mate initial"><span><strong>Mate initial</strong><small>Based on display name</small></span></div>';
|
|
642
|
-
html += '<div class="profile-imprint-actions profile-mate-mark-actions">';
|
|
643
|
-
html += '<button class="profile-use-imprint' + (!mateCustom ? ' is-active' : '') + '" type="button">Use initial</button>';
|
|
644
|
-
html += '<button class="profile-avatar-upload' + (mateCustom ? ' is-active' : '') + '" type="button">Upload image</button>';
|
|
645
|
-
html += '</div>';
|
|
646
|
-
html += '</div>';
|
|
647
|
-
html += '<input type="file" id="mate-profile-avatar-file" accept="image/*" style="display:none">';
|
|
648
|
-
html += '</div>';
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
html += '</div>'; // close body
|
|
652
|
-
|
|
653
|
-
matePopoverEl.innerHTML = html;
|
|
654
|
-
|
|
655
|
-
// State tracker
|
|
656
|
-
var mateProfile = {
|
|
657
|
-
displayName: mateName,
|
|
658
|
-
avatarStyle: mateStyle,
|
|
659
|
-
avatarSeed: mateSeed,
|
|
660
|
-
avatarColor: mateColor,
|
|
661
|
-
avatarCustom: mateCustom,
|
|
662
|
-
};
|
|
663
|
-
|
|
664
|
-
function refreshChatAvatars() {
|
|
665
|
-
var newUrl = mateAvatarUrl({ profile: mateProfile, id: mateData.id }, 64);
|
|
666
|
-
// Update all mate avatars in the chat area
|
|
667
|
-
var bubbles = document.querySelectorAll('.dm-bubble-avatar-mate');
|
|
668
|
-
for (var i = 0; i < bubbles.length; i++) {
|
|
669
|
-
bubbles[i].src = newUrl;
|
|
670
|
-
}
|
|
671
|
-
// Update body dataset so new messages use the updated URL
|
|
672
|
-
if (document.body.dataset.mateAvatarUrl) {
|
|
673
|
-
document.body.dataset.mateAvatarUrl = newUrl;
|
|
674
|
-
}
|
|
675
|
-
// Update mate sidebar avatar
|
|
676
|
-
var sidebarAvatar = document.querySelector('.mate-sidebar-avatar');
|
|
677
|
-
if (sidebarAvatar) sidebarAvatar.src = newUrl;
|
|
678
|
-
}
|
|
679
|
-
|
|
680
|
-
function debouncedMateUpdate() {
|
|
681
|
-
if (mateSaveTimer) clearTimeout(mateSaveTimer);
|
|
682
|
-
mateSaveTimer = setTimeout(function() {
|
|
683
|
-
if (onUpdate) onUpdate({
|
|
684
|
-
name: mateProfile.displayName,
|
|
685
|
-
profile: {
|
|
686
|
-
displayName: mateProfile.displayName,
|
|
687
|
-
avatarStyle: mateProfile.avatarStyle,
|
|
688
|
-
avatarSeed: mateProfile.avatarSeed,
|
|
689
|
-
avatarColor: mateProfile.avatarColor,
|
|
690
|
-
avatarCustom: mateProfile.avatarCustom,
|
|
691
|
-
},
|
|
692
|
-
});
|
|
693
|
-
refreshChatAvatars();
|
|
694
|
-
mateSaveTimer = null;
|
|
695
|
-
}, 400);
|
|
696
|
-
}
|
|
697
|
-
|
|
698
|
-
function updateMatePopoverHeader() {
|
|
699
|
-
if (!matePopoverEl) return;
|
|
700
|
-
var img = matePopoverEl.querySelector('.profile-popover-avatar-img');
|
|
701
|
-
var nd = matePopoverEl.querySelector('.profile-name-display');
|
|
702
|
-
if (img) img.src = mateProfile.avatarCustom
|
|
703
|
-
? mateProfile.avatarCustom
|
|
704
|
-
: mateMarkUrl(mateProfile.displayName || 'Mate', 80);
|
|
705
|
-
var preview = matePopoverEl.querySelector('.profile-imprint-preview img');
|
|
706
|
-
if (preview) preview.src = mateMarkUrl(mateProfile.displayName || 'Mate', 72);
|
|
707
|
-
if (nd) nd.textContent = mateProfile.displayName || 'New Mate';
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
// Events
|
|
711
|
-
matePopoverEl.querySelector('.profile-close-btn').addEventListener('click', function(e) {
|
|
712
|
-
e.stopPropagation();
|
|
713
|
-
hideMatePopover();
|
|
714
|
-
});
|
|
715
|
-
|
|
716
|
-
var nameInput = matePopoverEl.querySelector('#mate-profile-name');
|
|
717
|
-
nameInput.addEventListener('input', function() {
|
|
718
|
-
mateProfile.displayName = nameInput.value.trim();
|
|
719
|
-
updateMatePopoverHeader();
|
|
720
|
-
debouncedMateUpdate();
|
|
721
|
-
});
|
|
722
|
-
nameInput.addEventListener('keydown', function(e) {
|
|
723
|
-
if (e.key === 'Enter') { e.preventDefault(); hideMatePopover(); }
|
|
724
|
-
e.stopPropagation();
|
|
725
|
-
});
|
|
726
|
-
nameInput.addEventListener('keyup', function(e) { e.stopPropagation(); });
|
|
727
|
-
nameInput.addEventListener('keypress', function(e) { e.stopPropagation(); });
|
|
728
|
-
|
|
729
|
-
// Avatar upload button
|
|
730
|
-
var mateUploadBtn = matePopoverEl.querySelector('.profile-avatar-upload');
|
|
731
|
-
var mateFileInput = matePopoverEl.querySelector('#mate-profile-avatar-file');
|
|
732
|
-
if (mateUploadBtn && mateFileInput) {
|
|
733
|
-
mateUploadBtn.addEventListener('click', function() {
|
|
734
|
-
mateFileInput.click();
|
|
735
|
-
});
|
|
736
|
-
mateFileInput.addEventListener('change', function() {
|
|
737
|
-
var file = mateFileInput.files[0];
|
|
738
|
-
if (!file) return;
|
|
739
|
-
|
|
740
|
-
function uploadMateBlob(blob) {
|
|
741
|
-
blob.arrayBuffer().then(function(ab) {
|
|
742
|
-
var buf = new Uint8Array(ab);
|
|
743
|
-
fetch('/api/mate-avatar/' + encodeURIComponent(mateData.id), {
|
|
744
|
-
method: 'POST',
|
|
745
|
-
headers: { 'Content-Type': 'application/octet-stream' },
|
|
746
|
-
body: buf,
|
|
747
|
-
}).then(function(r) { return r.json(); }).then(function(data) {
|
|
748
|
-
if (data.ok) {
|
|
749
|
-
mateProfile.avatarCustom = data.avatar;
|
|
750
|
-
updateMatePopoverHeader();
|
|
751
|
-
if (mateUploadBtn) {
|
|
752
|
-
mateUploadBtn.classList.add('is-active');
|
|
753
|
-
}
|
|
754
|
-
var imprintButton = matePopoverEl && matePopoverEl.querySelector('.profile-use-imprint');
|
|
755
|
-
if (imprintButton) imprintButton.classList.remove('is-active');
|
|
756
|
-
debouncedMateUpdate();
|
|
757
|
-
}
|
|
758
|
-
});
|
|
759
|
-
});
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
// Load image and open position picker
|
|
763
|
-
var img = new Image();
|
|
764
|
-
var objectUrl = URL.createObjectURL(file);
|
|
765
|
-
img.onload = function() {
|
|
766
|
-
showAvatarPositioner(img, objectUrl, function(croppedBlob) {
|
|
767
|
-
URL.revokeObjectURL(objectUrl);
|
|
768
|
-
uploadMateBlob(croppedBlob);
|
|
769
|
-
});
|
|
770
|
-
};
|
|
771
|
-
img.src = objectUrl;
|
|
772
|
-
});
|
|
773
|
-
}
|
|
774
|
-
|
|
775
|
-
if (!mateAvatarLocked) {
|
|
776
|
-
matePopoverEl.querySelector('.profile-use-imprint').addEventListener('click', function() {
|
|
777
|
-
mateProfile.avatarCustom = '';
|
|
778
|
-
updateMatePopoverHeader();
|
|
779
|
-
matePopoverEl.querySelector('.profile-use-imprint').classList.add('is-active');
|
|
780
|
-
matePopoverEl.querySelector('.profile-avatar-upload').classList.remove('is-active');
|
|
781
|
-
debouncedMateUpdate();
|
|
782
|
-
});
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
matePopoverEl.addEventListener('click', function(e) { e.stopPropagation(); });
|
|
786
|
-
|
|
787
|
-
// Position near anchor
|
|
788
|
-
document.body.appendChild(matePopoverEl);
|
|
789
|
-
refreshIcons();
|
|
790
|
-
|
|
791
|
-
var rect = anchorEl.getBoundingClientRect();
|
|
792
|
-
matePopoverEl.style.position = 'fixed';
|
|
793
|
-
matePopoverEl.style.left = (rect.right + 8) + 'px';
|
|
794
|
-
matePopoverEl.style.zIndex = '9999';
|
|
795
|
-
// Align bottom of popover with bottom of anchor icon
|
|
796
|
-
var popHeight = matePopoverEl.offsetHeight;
|
|
797
|
-
var bottomAligned = rect.bottom - popHeight;
|
|
798
|
-
matePopoverEl.style.top = Math.max(8, bottomAligned) + 'px';
|
|
799
|
-
|
|
800
|
-
setTimeout(function() {
|
|
801
|
-
document.addEventListener('click', closeMateOnOutside);
|
|
802
|
-
document.addEventListener('keydown', closeMateOnEscape);
|
|
803
|
-
}, 0);
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
function closeMateOnOutside(e) {
|
|
807
|
-
if (matePopoverEl && !matePopoverEl.contains(e.target)) {
|
|
808
|
-
hideMatePopover();
|
|
809
|
-
}
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
function closeMateOnEscape(e) {
|
|
813
|
-
if (e.key === 'Escape' && matePopoverEl) {
|
|
814
|
-
hideMatePopover();
|
|
815
|
-
}
|
|
816
|
-
}
|
|
817
|
-
|
|
818
|
-
function hideMatePopover() {
|
|
819
|
-
if (matePopoverEl) {
|
|
820
|
-
matePopoverEl.remove();
|
|
821
|
-
matePopoverEl = null;
|
|
822
|
-
}
|
|
823
|
-
document.removeEventListener('click', closeMateOnOutside);
|
|
824
|
-
document.removeEventListener('keydown', closeMateOnEscape);
|
|
825
|
-
}
|
|
@@ -8,6 +8,9 @@ import { switchProject } from './app-projects.js';
|
|
|
8
8
|
import { closeTerminal } from './terminal.js';
|
|
9
9
|
import { closeFileViewer } from './filebrowser.js';
|
|
10
10
|
import { getActiveMentionMateIds, onMentionActiveChange, bindIconTooltip, hideIconTooltip, showMateCtxMenu } from './sidebar-mates.js';
|
|
11
|
+
import { chooseProjectActivationTarget } from './project-activation.js';
|
|
12
|
+
import { showProjectsHub } from './projects-hub.js';
|
|
13
|
+
import { openProjectSession } from './project-session-navigation.js';
|
|
11
14
|
|
|
12
15
|
var FILTER_MODES = ['all', 'projects', 'mates'];
|
|
13
16
|
var FILTER_LABELS = { all: 'All', projects: 'Projects', mates: 'Mates' };
|
|
@@ -98,9 +101,80 @@ function renderMateHeaderDefault(state) {
|
|
|
98
101
|
renderMateProjectHeader(state, mate);
|
|
99
102
|
}
|
|
100
103
|
|
|
104
|
+
function ordinaryProjects(state) {
|
|
105
|
+
return (state.projectsHubList || []).filter(function (project) {
|
|
106
|
+
return project && project.slug && project.isMate !== true;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function clayIsActive(state, clay) {
|
|
111
|
+
return !!clay && !state.homeShellVisible && !state.dmMode && state.currentSlug === 'mate-' + clay.id;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function clayReturnTargetForState(state) {
|
|
115
|
+
var target = state && state.clayReturnTarget;
|
|
116
|
+
var recordedSlug = target && target.projectSlug;
|
|
117
|
+
var candidates = recordedSlug ? [recordedSlug] : [];
|
|
118
|
+
var slug = chooseProjectActivationTarget(ordinaryProjects(state), candidates);
|
|
119
|
+
if (!slug) return null;
|
|
120
|
+
var sameProject = slug === recordedSlug;
|
|
121
|
+
return {
|
|
122
|
+
projectSlug: slug,
|
|
123
|
+
sessionId: sameProject && target && Number.isSafeInteger(Number(target.sessionId)) && Number(target.sessionId) > 0 ? Number(target.sessionId) : null,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function rememberClayReturnTarget(state) {
|
|
128
|
+
if (!state || state.homeShellVisible || state.dmMode || isMateWorkspace(state) || !state.currentSlug ||
|
|
129
|
+
state.activeProjectSlug !== state.currentSlug || state.sessionActivatedProjectSlug !== state.currentSlug) return;
|
|
130
|
+
var projects = ordinaryProjects(state);
|
|
131
|
+
var found = projects.some(function (project) { return project.slug === state.currentSlug; });
|
|
132
|
+
if (!found) return;
|
|
133
|
+
var target = { projectSlug: state.currentSlug, sessionId: Number.isSafeInteger(Number(state.activeSessionId)) && Number(state.activeSessionId) > 0 ? Number(state.activeSessionId) : null };
|
|
134
|
+
store.set({ clayReturnTarget: target });
|
|
135
|
+
return target;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function returnFromClay() {
|
|
139
|
+
var state = store.snap();
|
|
140
|
+
var target = clayReturnTargetForState(state);
|
|
141
|
+
if (!target) {
|
|
142
|
+
store.set({ pendingProjectSessionNavigation: null, clayReturnPendingProjectSlug: null, clayReturnTarget: null });
|
|
143
|
+
showProjectsHub();
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
store.set({ clayReturnTarget: null, clayReturnPendingProjectSlug: target.sessionId ? target.projectSlug : null });
|
|
147
|
+
if (target.sessionId) openProjectSession(target.projectSlug, target.sessionId);
|
|
148
|
+
else {
|
|
149
|
+
store.set({ pendingProjectSessionNavigation: null });
|
|
150
|
+
switchProject(target.projectSlug);
|
|
151
|
+
}
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function updateClayBrand(state) {
|
|
156
|
+
var entry = document.querySelector('.icon-strip-brand');
|
|
157
|
+
if (!entry) return;
|
|
158
|
+
var clay = navigationMates(state.cachedMatesList).find(function (mate) { return mate.builtinKey === 'clay'; });
|
|
159
|
+
var active = clayIsActive(state, clay);
|
|
160
|
+
var speaking = !!(state.youActivity && state.youActivity.active);
|
|
161
|
+
entry.dataset.clayMode = active ? 'mate' : 'project';
|
|
162
|
+
entry.classList.toggle('clay-speaking', speaking);
|
|
163
|
+
var avatar = entry.querySelector('.clay-brand-avatar');
|
|
164
|
+
var logo = entry.querySelector('.clay-brand-logo');
|
|
165
|
+
if (avatar && clay) avatar.src = mateAvatarUrl(clay, 38);
|
|
166
|
+
if (logo) logo.src = 'clay-studio-symbol.png';
|
|
167
|
+
entry.disabled = !clay;
|
|
168
|
+
entry.classList.toggle('active', active);
|
|
169
|
+
entry.setAttribute('aria-label', clay ? (active ? 'Return to project' : 'Open Clay') : 'Clay is unavailable');
|
|
170
|
+
if (active) entry.setAttribute('aria-current', 'page');
|
|
171
|
+
else entry.removeAttribute('aria-current');
|
|
172
|
+
}
|
|
173
|
+
|
|
101
174
|
export function openMateWorkspace(mateId) {
|
|
102
175
|
var mate = navigationMates(store.get('cachedMatesList')).find(function (item) { return item.id === mateId; });
|
|
103
176
|
if (!mate) return;
|
|
177
|
+
if (mate.builtinKey === 'clay' && store.snap) rememberClayReturnTarget(store.snap());
|
|
104
178
|
if (store.get('currentSlug') !== 'mate-' + mateId) {
|
|
105
179
|
closeTerminal();
|
|
106
180
|
closeFileViewer();
|
|
@@ -126,10 +200,14 @@ function createMateIcon(mateId) {
|
|
|
126
200
|
avatarUrl: mate ? mateAvatarUrl(mate, 24) : '',
|
|
127
201
|
};
|
|
128
202
|
});
|
|
129
|
-
|
|
130
|
-
event.preventDefault();
|
|
203
|
+
function openContextMenu(event) {
|
|
204
|
+
event.preventDefault(); event.stopPropagation(); hideIconTooltip();
|
|
131
205
|
var mate = navigationMates(store.get('cachedMatesList')).find(function (item) { return item.id === mateId; });
|
|
132
206
|
if (mate) showMateCtxMenu(button, mate);
|
|
207
|
+
}
|
|
208
|
+
button.addEventListener('contextmenu', openContextMenu);
|
|
209
|
+
button.addEventListener('keydown', function (event) {
|
|
210
|
+
if (event.key === 'ContextMenu' || (event.shiftKey && event.key === 'F10')) openContextMenu(event);
|
|
133
211
|
});
|
|
134
212
|
return button;
|
|
135
213
|
}
|
|
@@ -142,14 +220,7 @@ export function renderProjectMates() {
|
|
|
142
220
|
var clay = navigationMates(state.cachedMatesList).find(function (mate) { return mate.builtinKey === 'clay'; });
|
|
143
221
|
var clayActive = !!clay && !state.homeShellVisible && !state.dmMode && state.currentSlug === 'mate-' + clay.id;
|
|
144
222
|
document.body.classList.toggle('clay-workspace-active', clayActive);
|
|
145
|
-
|
|
146
|
-
if (clayEntry) {
|
|
147
|
-
clayEntry.disabled = !clay;
|
|
148
|
-
clayEntry.classList.toggle('active', clayActive);
|
|
149
|
-
clayEntry.setAttribute('aria-label', clay ? 'Open Clay' : 'Clay is unavailable');
|
|
150
|
-
if (clayActive) clayEntry.setAttribute('aria-current', 'page');
|
|
151
|
-
else clayEntry.removeAttribute('aria-current');
|
|
152
|
-
}
|
|
223
|
+
updateClayBrand(state);
|
|
153
224
|
document.body.classList.toggle('mate-workspace-active', !state.homeShellVisible && isMateWorkspace(state));
|
|
154
225
|
applyWorkspaceColor(mateForWorkspace(state));
|
|
155
226
|
renderMateHeaderDefault(state);
|
|
@@ -216,19 +287,33 @@ export function initProjectMateNavigation() {
|
|
|
216
287
|
var clayEntry = document.querySelector('.icon-strip-brand');
|
|
217
288
|
if (clayEntry) {
|
|
218
289
|
clayEntry.addEventListener('click', function () {
|
|
219
|
-
var
|
|
290
|
+
var state = store.snap();
|
|
291
|
+
var clay = navigationMates(state.cachedMatesList).find(function (mate) { return mate.builtinKey === 'clay'; });
|
|
220
292
|
hideIconTooltip();
|
|
221
|
-
if (clay)
|
|
293
|
+
if (!clay) return;
|
|
294
|
+
if (!clayIsActive(state, clay)) {
|
|
295
|
+
openMateWorkspace(clay.id);
|
|
296
|
+
} else returnFromClay();
|
|
297
|
+
});
|
|
298
|
+
bindIconTooltip(clayEntry, function () {
|
|
299
|
+
var state = store.snap();
|
|
300
|
+
var clay = navigationMates(state.cachedMatesList).find(function (mate) { return mate.builtinKey === 'clay'; });
|
|
301
|
+
return { text: clayIsActive(state, clay) ? 'Return to project' : 'Open Clay', kind: 'mate', avatarUrl: 'clay-studio-symbol.png' };
|
|
222
302
|
});
|
|
223
|
-
bindIconTooltip(clayEntry, { text: 'Clay', kind: 'mate', avatarUrl: 'clay-studio-symbol.png' });
|
|
224
303
|
}
|
|
225
304
|
var knob = document.getElementById('icon-strip-filter-knob');
|
|
226
305
|
if (knob) knob.addEventListener('click', activateProjectMateFilter);
|
|
227
306
|
store.subscribe(function (state, previous) {
|
|
307
|
+
if (state.clayReturnPendingProjectSlug && state.currentSlug !== previous.currentSlug &&
|
|
308
|
+
state.currentSlug !== state.clayReturnPendingProjectSlug) {
|
|
309
|
+
store.set({ pendingProjectSessionNavigation: null, clayReturnPendingProjectSlug: null });
|
|
310
|
+
} else if (state.clayReturnPendingProjectSlug && !state.pendingProjectSessionNavigation && previous.pendingProjectSessionNavigation) {
|
|
311
|
+
store.set({ clayReturnPendingProjectSlug: null });
|
|
312
|
+
}
|
|
228
313
|
if (state.cachedMatesList !== previous.cachedMatesList || state.homeChatMateId !== previous.homeChatMateId ||
|
|
229
314
|
state.homeShellVisible !== previous.homeShellVisible || state.projectsHubList !== previous.projectsHubList ||
|
|
230
315
|
state.dmUnread !== previous.dmUnread || state.currentSlug !== previous.currentSlug ||
|
|
231
|
-
state.dmMode !== previous.dmMode || state.connected !== previous.connected) renderProjectMates();
|
|
316
|
+
state.dmMode !== previous.dmMode || state.connected !== previous.connected || state.activeSessionId !== previous.activeSessionId || state.youActivity !== previous.youActivity) renderProjectMates();
|
|
232
317
|
if (state.projectMateFilterMode !== previous.projectMateFilterMode ||
|
|
233
318
|
state.projectMateFilterTurns !== previous.projectMateFilterTurns) applyProjectMateFilter();
|
|
234
319
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createHomeDebateQuestionCard, applyHomeDebateQuestion, resolveHomeDebateQuestion, markHomeDebateQuestionSubmitting } from './home-debate-planning.js';
|
|
1
2
|
// Compact Clay conversation hosted inside the global search drawer.
|
|
2
3
|
import { getWs } from './ws-ref.js';
|
|
3
4
|
import { openProjectSession } from './project-session-navigation.js';
|
|
@@ -131,6 +132,7 @@ function appendActivityList(content, items, expanded, onToggle) {
|
|
|
131
132
|
}
|
|
132
133
|
|
|
133
134
|
function renderMessage(message, streaming) {
|
|
135
|
+
if (message.role === "question" && message.flow === "mcp_import") return createHomeDebateQuestionCard(message, respondToImport);
|
|
134
136
|
var row;
|
|
135
137
|
if (message.role === "user") {
|
|
136
138
|
row = createUserBubble({ text: message.text || "" });
|
|
@@ -221,6 +223,13 @@ function stopSearchClayChat() {
|
|
|
221
223
|
return true;
|
|
222
224
|
}
|
|
223
225
|
|
|
226
|
+
function respondToImport(message, action, opener, answers) {
|
|
227
|
+
if (!state || message.status !== 'pending') return;
|
|
228
|
+
if (!send({ type: 'home_mcp_import_response', mateId: state.mateId, sessionId: state.sessionId, requestId: state.requestId, toolId: message.toolId, answers: answers || {} })) return;
|
|
229
|
+
markHomeDebateQuestionSubmitting(message, opener);
|
|
230
|
+
store.set({ searchClayChatState: state }); render();
|
|
231
|
+
}
|
|
232
|
+
|
|
224
233
|
function respondToPermission(permission, decision) {
|
|
225
234
|
if (!state || permission.status !== "pending") return false;
|
|
226
235
|
if (!send({ type: "home_mate_permission_response", mateId: state.mateId, sessionId: state.sessionId, requestId: state.requestId, permissionRequestId: permission.permissionRequestId, decision: decision })) return false;
|
|
@@ -371,7 +380,12 @@ export function handleSearchClayMessage(msg) {
|
|
|
371
380
|
state.updatedAt = Date.now();
|
|
372
381
|
if (msg.mateId) state.mateId = msg.mateId;
|
|
373
382
|
if (msg.type === "home_mate_session_identity" && msg.sessionId) state.sessionId = msg.sessionId;
|
|
374
|
-
if (msg.type ===
|
|
383
|
+
if (msg.type === 'home_mcp_import_question') {
|
|
384
|
+
if (state.stream) { state.messages.push({ role: 'assistant', text: state.stream }); state.stream = ''; }
|
|
385
|
+
state.messages = applyHomeDebateQuestion(state.messages, msg);
|
|
386
|
+
} else if (msg.type === 'home_mcp_import_question_resolved') {
|
|
387
|
+
state.messages = resolveHomeDebateQuestion(state.messages, msg);
|
|
388
|
+
} else if (msg.type === "home_clay_activity") {
|
|
375
389
|
if (!state.awaitingCompletion) return true;
|
|
376
390
|
state.activity = msg.phase || state.activity;
|
|
377
391
|
state.step = typeof msg.step === "number" ? msg.step : state.step;
|
|
@@ -379,7 +393,7 @@ export function handleSearchClayMessage(msg) {
|
|
|
379
393
|
} else if (msg.type === "home_mate_history") {
|
|
380
394
|
state.sessionId = msg.sessionId || state.sessionId;
|
|
381
395
|
state.localId = typeof msg.localId === "number" ? msg.localId : state.localId;
|
|
382
|
-
state.messages = Array.isArray(msg.messages) ? msg.messages.filter(function (item) { return item && (item.role === "user" || item.role === "assistant"); }).map(function (item) { return { role: item.role, text: item.text || "" }; }) : state.messages;
|
|
396
|
+
state.messages = Array.isArray(msg.messages) ? msg.messages.filter(function (item) { return item && (item.role === "user" || item.role === "assistant" || item.role === "question" && item.flow === "mcp_import"); }).map(function (item) { return item.role === "question" ? Object.assign({}, item) : { role: item.role, text: item.text || "" }; }) : state.messages;
|
|
383
397
|
state.processing = msg.isProcessing === true;
|
|
384
398
|
state.awaitingCompletion = state.processing;
|
|
385
399
|
state.pendingTurns = state.processing ? Math.max(1, state.pendingTurns) : 0;
|
|
@@ -3,6 +3,7 @@ import { getWs } from './ws-ref.js';
|
|
|
3
3
|
import { githubLinkTarget } from './github-links.js';
|
|
4
4
|
import { iconHtml, refreshIcons } from './icons.js';
|
|
5
5
|
import { escapeHtml as escapeText } from './utils.js';
|
|
6
|
+
import { primaryWorkPopover, handlePrimaryWorkClick } from './session-work-primary.js';
|
|
6
7
|
|
|
7
8
|
function escapeHtml(value) {
|
|
8
9
|
return escapeText(String(value)).replace(/"/g, '"').replace(/'/g, ''');
|
|
@@ -34,13 +35,16 @@ export function orderedGithubWork(session) {
|
|
|
34
35
|
var links = safeLinks(session).slice().reverse();
|
|
35
36
|
var latestIssue = links.find(function (link) { return link.kind === 'issue' || link.provider === 'linear'; });
|
|
36
37
|
if (latestIssue) links = [latestIssue].concat(links.filter(function (link) { return link !== latestIssue; }));
|
|
37
|
-
|
|
38
|
+
var primary = session && session.workPrimary || {};
|
|
39
|
+
return links.filter(function (link) { return String(primary[link.provider === 'linear' ? 'linear' : link.kind] || '').toLowerCase() === link.url.toLowerCase(); }).concat(
|
|
40
|
+
links.filter(function (link) { return String(primary[link.provider === 'linear' ? 'linear' : link.kind] || '').toLowerCase() !== link.url.toLowerCase(); }));
|
|
38
41
|
}
|
|
39
42
|
function compactGithubWork(links, sessionRow) {
|
|
40
43
|
if (!sessionRow && !links.some(function (link) { return link.provider === 'linear'; })) return links.slice(0, 1);
|
|
41
|
-
var
|
|
44
|
+
var latestLinear = links.find(function (link) { return link.provider === 'linear'; });
|
|
45
|
+
var latestIssue = links.find(function (link) { return link.kind === 'issue' && link.provider !== 'linear'; });
|
|
42
46
|
var latestPr = links.find(function (link) { return link.kind === 'pr'; });
|
|
43
|
-
return [latestIssue, latestPr].filter(Boolean);
|
|
47
|
+
return [latestLinear, latestIssue, latestPr].filter(Boolean);
|
|
44
48
|
}
|
|
45
49
|
export function githubWorkMarkup(session, detailed, expanded, sessionRow) {
|
|
46
50
|
var links = orderedGithubWork(session);
|
|
@@ -57,14 +61,20 @@ export function githubWorkMarkup(session, detailed, expanded, sessionRow) {
|
|
|
57
61
|
var html = anchor(link.url, description, iconHtml('github', 'github-work-brand') + iconHtml(icon, 'github-work-status') + '<span><span class="github-work-kind">' + (link.kind === 'pr' ? 'PR ' : 'Issue ') + '</span>#' + escapeHtml(String(link.number)) + '</span>', 'session-github-link github-kind-' + (link.kind === 'pr' ? 'pr' : 'issue') + ' github-state-' + state);
|
|
58
62
|
if (expanded) html = '<span class="github-work-entry">' + html + '<span class="github-work-entry-title">' + escapeHtml(link.title || '') + '</span><span class="github-work-entry-meta">' + escapeHtml(link.repository + ' · ' + state + (link.stale ? ' · Last known status' : '')) + '</span></span>';
|
|
59
63
|
if (detailed && link.kind === 'pr') {
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
var actions = anchor(link.url + '/files', 'View PR changes on GitHub', 'Changes', 'github-work-action') +
|
|
65
|
+
anchor(link.url + '#pullrequestreview', 'View PR reviews on GitHub', escapeHtml((link.review || 'Reviews').replace(/_/g, ' ').toLowerCase()), 'github-work-action');
|
|
66
|
+
var checks = '';
|
|
62
67
|
(link.checks || []).forEach(function (check) {
|
|
63
|
-
if (/^https:\/\//i.test(check.url))
|
|
68
|
+
if (/^https:\/\//i.test(check.url)) checks += anchor(check.url, check.name + ': ' + check.state, '<span class="github-work-check-name">' + escapeHtml(check.name) + '</span><span class="github-work-check-state">' + escapeHtml(check.state.toLowerCase()) + '</span>', 'github-work-action github-work-check');
|
|
64
69
|
});
|
|
70
|
+
return '<span class="github-work-pr-group" data-github-pr="' + escapeHtml(String(link.number)) + '">' +
|
|
71
|
+
'<span class="github-work-pr-header">' + html + '</span>' +
|
|
72
|
+
'<span class="github-work-pr-actions">' + actions + '</span>' +
|
|
73
|
+
(checks ? '<span class="github-work-pr-checks">' + checks + '</span>' : '') +
|
|
74
|
+
'</span>';
|
|
65
75
|
}
|
|
66
76
|
return html;
|
|
67
|
-
}).join('') + (!detailed && !expanded && links.length > displayed.length ? '<button type="button" class="github-work-more" aria-label="Show all ' + links.length + ' linked ' + (links.some(function (item) { return item.provider === 'linear'; }) ? 'work items' : 'GitHub items') + '" aria-haspopup="dialog" aria-expanded="false" data-github-links="' + escapeHtml(JSON.stringify(safeLinks(session))) + '">+' + (links.length - displayed.length) + '</button>' : '') + '</span>';
|
|
77
|
+
}).join('') + (!detailed && !expanded && links.length > displayed.length ? '<button type="button" class="github-work-more" aria-label="Show all ' + links.length + ' linked ' + (links.some(function (item) { return item.provider === 'linear'; }) ? 'work items' : 'GitHub items') + '" aria-haspopup="dialog" aria-expanded="false" data-work-session="' + escapeHtml(String(session.id || session.localId || '')) + '" data-work-primary="' + escapeHtml(JSON.stringify(session.workPrimary || {})) + '" data-github-links="' + escapeHtml(JSON.stringify(safeLinks(session))) + '">+' + (links.length - displayed.length) + '</button>' : '') + '</span>';
|
|
68
78
|
}
|
|
69
79
|
export function appendGithubWork(row, session, title) {
|
|
70
80
|
var html = githubWorkMarkup(session, false, false, true);
|
|
@@ -130,7 +140,7 @@ export function refreshGithubWork() {
|
|
|
130
140
|
|
|
131
141
|
export function resetGithubWork() {
|
|
132
142
|
closeGithubWorkPopover();
|
|
133
|
-
store.set({ githubWorkSessions: [], githubWorkProject: null, githubWorkRefresh: null });
|
|
143
|
+
store.set({ githubWorkSessions: [], githubWorkProject: null, githubWorkRefresh: null, workPrimaryPending: null });
|
|
134
144
|
var header = document.getElementById('header-github-links');
|
|
135
145
|
if (header) header.remove();
|
|
136
146
|
}
|
|
@@ -184,6 +194,7 @@ function bindGithubWorkPopover() {
|
|
|
184
194
|
if (store.get('githubWorkPopoverBound')) return;
|
|
185
195
|
store.set({ githubWorkPopoverBound: true });
|
|
186
196
|
document.addEventListener('click', function (event) {
|
|
197
|
+
if (handlePrimaryWorkClick(event)) return;
|
|
187
198
|
var linear = event.target.closest('.session-linear-link');
|
|
188
199
|
if (linear && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey) { event.preventDefault(); event.stopPropagation(); closeGithubWorkPopover(); import('./linear-panel.js').then(function (module) { module.openLinear(linear.href); }); return; }
|
|
189
200
|
var button = event.target.closest('.github-work-more');
|
|
@@ -204,7 +215,10 @@ function bindGithubWorkPopover() {
|
|
|
204
215
|
popup.setAttribute('role', 'dialog');
|
|
205
216
|
var workTitle = links.some(function (link) { return link.provider === 'linear'; }) ? 'Linked work' : 'Linked GitHub work';
|
|
206
217
|
popup.setAttribute('aria-label', workTitle);
|
|
207
|
-
popup.
|
|
218
|
+
popup.dataset.sessionId = button.dataset.workSession || '';
|
|
219
|
+
var primary;
|
|
220
|
+
try { primary = JSON.parse(button.dataset.workPrimary || '{}'); } catch (error) { primary = {}; }
|
|
221
|
+
popup.innerHTML = '<strong>' + workTitle + '</strong>' + primaryWorkPopover(links, primary, popup.dataset.sessionId, githubWorkMarkup);
|
|
208
222
|
document.body.appendChild(popup);
|
|
209
223
|
mountGithubWorkPopover(popup, button);
|
|
210
224
|
button.setAttribute('aria-expanded', 'true');
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { store } from './store.js';
|
|
2
|
+
import { getWs } from './ws-ref.js';
|
|
3
|
+
import { escapeHtml } from './utils.js';
|
|
4
|
+
var PRIMARY_PIN = '<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path class="work-primary-pin-head" d="M16 9V4l1-1V2H7v1l1 1v5l-2 3v2h12v-2z"/><path d="M12 14v8"/></svg>';
|
|
5
|
+
function attr(value) { return escapeHtml(String(value)).replace(/"/g, '"').replace(/'/g, '''); }
|
|
6
|
+
export function primaryWorkPopover(links, primary, sessionId, render) {
|
|
7
|
+
var html = ['linear', 'pr', 'issue'].map(function (type) {
|
|
8
|
+
var items = links.filter(function (link) { return (link.provider === 'linear' ? 'linear' : link.kind) === type; }).reverse();
|
|
9
|
+
if (!items.length) return '';
|
|
10
|
+
return '<section class="work-primary-group"><h3>' + ({ linear: 'Linear issues', pr: 'Pull requests', issue: 'GitHub issues' })[type] + '</h3>' + items.map(function (link) {
|
|
11
|
+
var selected = String(primary[type] || '').toLowerCase() === link.url.toLowerCase();
|
|
12
|
+
return '<div class="work-primary-row">' + render({ githubLinks: [link] }, false, true) +
|
|
13
|
+
(sessionId ? '<button type="button" class="work-primary-choice" data-primary-url="' + attr(link.url) + '" aria-label="' + attr((selected ? 'Primary: ' : 'Make primary: ') + (link.identifier || (type === 'pr' ? 'PR #' : 'Issue #') + link.number)) + '" title="' + (selected ? 'Primary' : 'Make primary') + '" aria-pressed="' + selected + '"' + (selected ? ' disabled' : '') + '>' + PRIMARY_PIN + '</button>' : '') + '</div>';
|
|
14
|
+
}).join('') + '</section>';
|
|
15
|
+
}).join('');
|
|
16
|
+
return html + '<p class="work-primary-feedback" role="status" aria-live="polite"></p>';
|
|
17
|
+
}
|
|
18
|
+
export function handlePrimaryWorkClick(event) {
|
|
19
|
+
var button = event.target.closest('.work-primary-choice');
|
|
20
|
+
if (!button) return false;
|
|
21
|
+
event.preventDefault(); event.stopPropagation();
|
|
22
|
+
var popup = button.closest('#github-work-popover'), ws = getWs();
|
|
23
|
+
if (!popup) return true;
|
|
24
|
+
var feedback = popup.querySelector('.work-primary-feedback');
|
|
25
|
+
if (!ws || ws.readyState !== 1) { feedback.textContent = 'Reconnect to save your selection.'; return true; }
|
|
26
|
+
var requestId = 'work-primary-' + Date.now() + '-' + Math.random().toString(36).slice(2);
|
|
27
|
+
store.set({ workPrimaryPending: { requestId: requestId, sessionId: Number(popup.dataset.sessionId), projectSlug: store.get('currentSlug') } });
|
|
28
|
+
popup.querySelectorAll('.work-primary-choice').forEach(function (control) { control.disabled = true; });
|
|
29
|
+
feedback.textContent = 'Saving…';
|
|
30
|
+
var projectSlug = store.get('currentSlug');
|
|
31
|
+
setTimeout(function () {
|
|
32
|
+
handlePrimaryWorkResult({ requestId: requestId, projectSlug: projectSlug, error: 'Save confirmation did not arrive. Reopen the list to check, or retry.' });
|
|
33
|
+
}, 15000);
|
|
34
|
+
try { ws.send(JSON.stringify({ type: 'session_work_primary_set', requestId: requestId, sessionId: Number(popup.dataset.sessionId), url: button.dataset.primaryUrl })); }
|
|
35
|
+
catch (error) { handlePrimaryWorkResult({ requestId: requestId, projectSlug: store.get('currentSlug'), error: 'Could not send your selection. Please retry.' }); }
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
export function handlePrimaryWorkResult(msg) {
|
|
39
|
+
var pending = store.get('workPrimaryPending');
|
|
40
|
+
if (!pending || msg.requestId !== pending.requestId || msg.projectSlug !== pending.projectSlug) return;
|
|
41
|
+
store.set({ workPrimaryPending: null });
|
|
42
|
+
var popup = document.getElementById('github-work-popover');
|
|
43
|
+
if (!popup || Number(popup.dataset.sessionId) !== pending.sessionId || store.get('currentSlug') !== pending.projectSlug) return;
|
|
44
|
+
popup.querySelectorAll('.work-primary-choice').forEach(function (button) {
|
|
45
|
+
var selected = msg.error ? button.getAttribute('aria-pressed') === 'true' : Object.values(msg.workPrimary || {}).some(function (url) { return url.toLowerCase() === button.dataset.primaryUrl.toLowerCase(); });
|
|
46
|
+
button.setAttribute('aria-pressed', String(selected)); button.disabled = selected;
|
|
47
|
+
button.title = selected ? 'Primary' : 'Make primary';
|
|
48
|
+
button.setAttribute('aria-label', (selected ? 'Primary: ' : 'Make primary: ') + button.getAttribute('aria-label').replace(/^(Primary|Make primary): /, ''));
|
|
49
|
+
});
|
|
50
|
+
popup.querySelector('.work-primary-feedback').textContent = msg.error || 'Primary saved.';
|
|
51
|
+
}
|