claude-code-templates 1.24.17 → 1.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/create-claude-config.js +1 -0
- package/package.json +2 -1
- package/src/analytics-web/chats_mobile.html +631 -67
- package/src/chats-mobile.js +38 -1
- package/src/index.js +44 -1
- package/src/session-sharing.js +396 -0
|
@@ -704,31 +704,82 @@
|
|
|
704
704
|
box-shadow: 0 0 8px rgba(255, 170, 0, 0.6);
|
|
705
705
|
}
|
|
706
706
|
|
|
707
|
-
/*
|
|
708
|
-
.
|
|
709
|
-
|
|
710
|
-
border:
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
707
|
+
/* Action buttons group - unified design with orange accent */
|
|
708
|
+
.action-buttons-group {
|
|
709
|
+
display: inline-flex;
|
|
710
|
+
border-radius: 8px;
|
|
711
|
+
overflow: hidden;
|
|
712
|
+
border: 2px solid rgba(255, 170, 0, 0.4);
|
|
713
|
+
background: rgba(255, 170, 0, 0.08);
|
|
714
|
+
box-shadow: 0 2px 8px rgba(255, 170, 0, 0.15);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
.action-btn {
|
|
718
|
+
background: transparent;
|
|
719
|
+
border: none;
|
|
720
|
+
border-right: 1px solid rgba(255, 170, 0, 0.2);
|
|
721
|
+
color: var(--text-primary);
|
|
722
|
+
padding: 10px 18px;
|
|
714
723
|
cursor: pointer;
|
|
715
724
|
font-size: 0.875rem;
|
|
725
|
+
font-weight: 600;
|
|
716
726
|
transition: all 0.2s ease;
|
|
717
727
|
display: flex;
|
|
718
728
|
align-items: center;
|
|
719
|
-
gap:
|
|
729
|
+
gap: 8px;
|
|
730
|
+
white-space: nowrap;
|
|
720
731
|
}
|
|
721
732
|
|
|
722
|
-
.
|
|
723
|
-
|
|
724
|
-
color: white;
|
|
725
|
-
border-color: var(--terminal-orange);
|
|
733
|
+
.action-btn:last-child {
|
|
734
|
+
border-right: none;
|
|
726
735
|
}
|
|
727
736
|
|
|
728
|
-
.
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
737
|
+
.action-btn svg {
|
|
738
|
+
width: 15px;
|
|
739
|
+
height: 15px;
|
|
740
|
+
transition: all 0.2s ease;
|
|
741
|
+
stroke: var(--text-primary);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
.action-btn:hover {
|
|
745
|
+
background: rgba(255, 170, 0, 0.12);
|
|
746
|
+
transform: translateY(-1px);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
.action-btn.resume-btn:hover {
|
|
750
|
+
background: rgba(63, 185, 80, 0.15);
|
|
751
|
+
color: rgba(63, 185, 80, 1);
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
.action-btn.resume-btn:hover svg {
|
|
755
|
+
stroke: rgba(63, 185, 80, 1);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
.action-btn.share-btn:hover {
|
|
759
|
+
background: rgba(59, 130, 246, 0.15);
|
|
760
|
+
color: rgba(59, 130, 246, 1);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
.action-btn.share-btn:hover svg {
|
|
764
|
+
stroke: rgba(59, 130, 246, 1);
|
|
765
|
+
}
|
|
766
|
+
|
|
767
|
+
.action-btn.search-btn:hover {
|
|
768
|
+
background: rgba(251, 146, 60, 0.15);
|
|
769
|
+
color: rgba(251, 146, 60, 1);
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
.action-btn.search-btn:hover svg {
|
|
773
|
+
stroke: rgba(251, 146, 60, 1);
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
.action-btn.search-btn.active {
|
|
777
|
+
background: rgba(251, 146, 60, 0.2);
|
|
778
|
+
color: rgba(251, 146, 60, 1);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
.action-btn.search-btn.active svg {
|
|
782
|
+
stroke: rgba(251, 146, 60, 1);
|
|
732
783
|
}
|
|
733
784
|
|
|
734
785
|
/* Conversations list */
|
|
@@ -816,6 +867,88 @@
|
|
|
816
867
|
margin-top: 4px;
|
|
817
868
|
}
|
|
818
869
|
|
|
870
|
+
/* Project grouping styles */
|
|
871
|
+
.project-group {
|
|
872
|
+
border-bottom: 1px solid var(--border-secondary);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
.project-header {
|
|
876
|
+
display: flex;
|
|
877
|
+
align-items: center;
|
|
878
|
+
padding: 16px 20px;
|
|
879
|
+
background: var(--bg-secondary);
|
|
880
|
+
border-bottom: 1px solid var(--border-primary);
|
|
881
|
+
cursor: pointer;
|
|
882
|
+
transition: background-color 0.2s ease;
|
|
883
|
+
position: sticky;
|
|
884
|
+
top: 0;
|
|
885
|
+
z-index: 10;
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
.project-header:hover {
|
|
889
|
+
background: var(--bg-tertiary);
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
.project-avatar {
|
|
893
|
+
width: 44px;
|
|
894
|
+
height: 44px;
|
|
895
|
+
border-radius: 8px;
|
|
896
|
+
background: linear-gradient(135deg, var(--terminal-orange) 0%, #e67e22 100%);
|
|
897
|
+
display: flex;
|
|
898
|
+
align-items: center;
|
|
899
|
+
justify-content: center;
|
|
900
|
+
font-size: 1.2rem;
|
|
901
|
+
margin-right: 12px;
|
|
902
|
+
flex-shrink: 0;
|
|
903
|
+
color: var(--bg-primary);
|
|
904
|
+
font-weight: bold;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
.project-info {
|
|
908
|
+
flex: 1;
|
|
909
|
+
min-width: 0;
|
|
910
|
+
}
|
|
911
|
+
|
|
912
|
+
.project-name {
|
|
913
|
+
font-weight: 600;
|
|
914
|
+
color: var(--text-primary);
|
|
915
|
+
font-size: 1rem;
|
|
916
|
+
margin-bottom: 2px;
|
|
917
|
+
}
|
|
918
|
+
|
|
919
|
+
.project-count {
|
|
920
|
+
color: var(--text-secondary);
|
|
921
|
+
font-size: 0.85rem;
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
.project-toggle {
|
|
925
|
+
margin-left: 8px;
|
|
926
|
+
color: var(--text-secondary);
|
|
927
|
+
transition: transform 0.2s ease;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
.toggle-icon {
|
|
931
|
+
transition: transform 0.2s ease;
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
.toggle-icon.expanded {
|
|
935
|
+
transform: rotate(180deg);
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
/* Smaller avatar for conversations within groups */
|
|
939
|
+
.conversation-avatar-small {
|
|
940
|
+
width: 36px;
|
|
941
|
+
height: 36px;
|
|
942
|
+
font-size: 0.85rem;
|
|
943
|
+
background: var(--bg-tertiary);
|
|
944
|
+
color: var(--text-primary);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
/* Indent conversations within project groups */
|
|
948
|
+
.project-group .conversation-item {
|
|
949
|
+
padding-left: 36px;
|
|
950
|
+
}
|
|
951
|
+
|
|
819
952
|
.conversation-state {
|
|
820
953
|
font-size: 0.7rem;
|
|
821
954
|
padding: 3px 8px;
|
|
@@ -1353,13 +1486,16 @@
|
|
|
1353
1486
|
align-items: center;
|
|
1354
1487
|
justify-content: center;
|
|
1355
1488
|
flex: 0 0 auto;
|
|
1489
|
+
gap: 8px;
|
|
1356
1490
|
}
|
|
1357
1491
|
|
|
1492
|
+
|
|
1358
1493
|
.header-right {
|
|
1359
1494
|
display: flex;
|
|
1360
1495
|
align-items: center;
|
|
1361
1496
|
justify-content: flex-end;
|
|
1362
1497
|
flex: 1;
|
|
1498
|
+
gap: 12px;
|
|
1363
1499
|
}
|
|
1364
1500
|
|
|
1365
1501
|
.chat-view-back {
|
|
@@ -1904,20 +2040,36 @@
|
|
|
1904
2040
|
←
|
|
1905
2041
|
</button>
|
|
1906
2042
|
<div class="chat-view-info">
|
|
1907
|
-
<h2 class="chat-view-title" id="chatViewTitle">Select a
|
|
2043
|
+
<h2 class="chat-view-title" id="chatViewTitle">Select a session</h2>
|
|
1908
2044
|
<p class="chat-view-subtitle" id="chatViewSubtitle"></p>
|
|
1909
2045
|
</div>
|
|
1910
2046
|
</div>
|
|
1911
2047
|
<div class="header-center">
|
|
1912
|
-
<
|
|
1913
|
-
|
|
1914
|
-
|
|
2048
|
+
<div class="action-buttons-group" style="display: none;" id="actionButtonsGroup">
|
|
2049
|
+
<button class="action-btn resume-btn" id="resumeConversation" onclick="resumeConversationWithClaude()">
|
|
2050
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2051
|
+
<polygon points="5 3 19 12 5 21 5 3"></polygon>
|
|
2052
|
+
</svg>
|
|
2053
|
+
<span>Resume</span>
|
|
2054
|
+
</button>
|
|
2055
|
+
<button class="action-btn share-btn" id="shareConversation" onclick="shareConversation()">
|
|
2056
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2057
|
+
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>
|
|
2058
|
+
<polyline points="16 6 12 2 8 6"></polyline>
|
|
2059
|
+
<line x1="12" y1="2" x2="12" y2="15"></line>
|
|
2060
|
+
</svg>
|
|
2061
|
+
<span>Share</span>
|
|
2062
|
+
</button>
|
|
2063
|
+
<button class="action-btn search-btn" id="chatSearchToggle">
|
|
2064
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
2065
|
+
<circle cx="11" cy="11" r="8"></circle>
|
|
2066
|
+
<path d="m21 21-4.35-4.35"></path>
|
|
2067
|
+
</svg>
|
|
2068
|
+
<span>Search</span>
|
|
2069
|
+
</button>
|
|
2070
|
+
</div>
|
|
1915
2071
|
</div>
|
|
1916
2072
|
<div class="header-right">
|
|
1917
|
-
<button class="search-toggle-btn" id="chatSearchToggle">
|
|
1918
|
-
<span>🔍</span>
|
|
1919
|
-
<span>Search</span>
|
|
1920
|
-
</button>
|
|
1921
2073
|
<div class="tools-toggle" id="toolsToggle">
|
|
1922
2074
|
<span class="tools-toggle-label" onclick="document.getElementById('showToolsSwitch').click()">Show Tools</span>
|
|
1923
2075
|
<label class="toggle-switch">
|
|
@@ -1991,6 +2143,106 @@
|
|
|
1991
2143
|
</div>
|
|
1992
2144
|
</div>
|
|
1993
2145
|
|
|
2146
|
+
<!-- Confirm Share Modal (Security Warning) -->
|
|
2147
|
+
<div class="modal-overlay" id="confirmShareModal">
|
|
2148
|
+
<div class="modal">
|
|
2149
|
+
<div class="modal-header" style="background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);">
|
|
2150
|
+
<span class="modal-icon">📤</span>
|
|
2151
|
+
<h3 class="modal-title">Share Your Session</h3>
|
|
2152
|
+
</div>
|
|
2153
|
+
<div style="padding: 20px;">
|
|
2154
|
+
<p class="modal-description" style="margin-bottom: 16px; line-height: 1.5;">
|
|
2155
|
+
Your conversation will be uploaded to <strong>x0.at</strong>, a temporary file hosting service.
|
|
2156
|
+
The generated link can be shared with others to clone your session.
|
|
2157
|
+
</p>
|
|
2158
|
+
|
|
2159
|
+
<div style="background: rgba(59, 130, 246, 0.08); border-left: 3px solid #3b82f6; padding: 12px; border-radius: 6px; margin-bottom: 16px;">
|
|
2160
|
+
<h4 style="color: #3b82f6; margin: 0 0 8px 0; font-size: 14px;">📋 What you're sharing</h4>
|
|
2161
|
+
<p style="margin: 0; font-size: 13px; color: var(--text-secondary); line-height: 1.4;">
|
|
2162
|
+
Last <strong>100 messages</strong> from this conversation (or all messages if less than 100)
|
|
2163
|
+
</p>
|
|
2164
|
+
</div>
|
|
2165
|
+
|
|
2166
|
+
<div style="background: rgba(234, 179, 8, 0.08); border-left: 3px solid #eab308; padding: 12px; border-radius: 6px; margin-bottom: 16px;">
|
|
2167
|
+
<h4 style="color: #eab308; margin: 0 0 8px 0; font-size: 14px;">ℹ️ About x0.at</h4>
|
|
2168
|
+
<ul style="margin: 0; padding-left: 20px; font-size: 13px; color: var(--text-secondary); line-height: 1.5;">
|
|
2169
|
+
<li style="margin-bottom: 4px;">Simple temporary file hosting (open source)</li>
|
|
2170
|
+
<li style="margin-bottom: 4px;">Files available for 3-100 days depending on size</li>
|
|
2171
|
+
<li style="margin-bottom: 4px;">Anyone with the link can download your session</li>
|
|
2172
|
+
<li style="margin-bottom: 4px;">No encryption - avoid sharing sensitive data</li>
|
|
2173
|
+
</ul>
|
|
2174
|
+
</div>
|
|
2175
|
+
|
|
2176
|
+
<div style="background: rgba(100, 116, 139, 0.08); border-left: 3px solid #64748b; padding: 12px; border-radius: 6px;">
|
|
2177
|
+
<p style="margin: 0; font-size: 12px; color: var(--text-secondary); line-height: 1.4;">
|
|
2178
|
+
💡 <strong>Tip:</strong> Only share links with people you trust. The session includes your conversation history and may contain project-specific information.
|
|
2179
|
+
</p>
|
|
2180
|
+
</div>
|
|
2181
|
+
</div>
|
|
2182
|
+
|
|
2183
|
+
<div class="modal-actions">
|
|
2184
|
+
<button class="modal-btn secondary" onclick="closeConfirmShareModal()">Cancel</button>
|
|
2185
|
+
<button class="modal-btn primary" onclick="proceedWithShare()" style="background: #3b82f6;">
|
|
2186
|
+
Continue & Upload
|
|
2187
|
+
</button>
|
|
2188
|
+
</div>
|
|
2189
|
+
</div>
|
|
2190
|
+
</div>
|
|
2191
|
+
|
|
2192
|
+
<!-- Share Modal -->
|
|
2193
|
+
<div class="modal-overlay" id="shareModal">
|
|
2194
|
+
<div class="modal">
|
|
2195
|
+
<div class="modal-header">
|
|
2196
|
+
<span class="modal-icon">📤</span>
|
|
2197
|
+
<h3 class="modal-title">Share Conversation</h3>
|
|
2198
|
+
</div>
|
|
2199
|
+
<div id="shareModalLoading" style="text-align: center; padding: 40px; display: none;">
|
|
2200
|
+
<div class="loading-spinner"></div>
|
|
2201
|
+
<p style="margin-top: 16px; color: var(--text-secondary);">Uploading session...</p>
|
|
2202
|
+
</div>
|
|
2203
|
+
<div id="shareModalContent" style="display: none;">
|
|
2204
|
+
<p class="modal-description">
|
|
2205
|
+
Your conversation has been uploaded to x0.at. Share the command or QR code below with others.
|
|
2206
|
+
</p>
|
|
2207
|
+
<div id="shareMessageInfo" style="display: none; padding: 12px; background: var(--bg-tertiary); border-radius: 6px; margin-bottom: 16px;">
|
|
2208
|
+
<p style="font-size: 13px; color: var(--text-secondary); margin: 0;">
|
|
2209
|
+
<span id="shareMessageCount"></span>
|
|
2210
|
+
</p>
|
|
2211
|
+
</div>
|
|
2212
|
+
<div style="text-align: center; margin: 24px 0;">
|
|
2213
|
+
<div style="margin-bottom: 16px; display: flex; flex-direction: column; align-items: center;">
|
|
2214
|
+
<h4 style="margin-bottom: 12px; color: var(--text-primary);">📱 Scan QR Code</h4>
|
|
2215
|
+
<img id="shareQRCode" src="" alt="QR Code" style="max-width: 300px; border-radius: 8px; border: 2px solid var(--border-color); display: block; margin: 0 auto;">
|
|
2216
|
+
<p style="font-size: 12px; color: var(--text-secondary); margin-top: 12px; margin-bottom: 0;">
|
|
2217
|
+
Scan this QR code to get the share command
|
|
2218
|
+
</p>
|
|
2219
|
+
</div>
|
|
2220
|
+
</div>
|
|
2221
|
+
<div>
|
|
2222
|
+
<h4 style="margin-bottom: 8px; color: var(--text-primary);">📋 Share Command</h4>
|
|
2223
|
+
<div class="modal-command" id="shareModalCommand">
|
|
2224
|
+
<!-- Command will be inserted here -->
|
|
2225
|
+
</div>
|
|
2226
|
+
</div>
|
|
2227
|
+
<div style="margin-top: 16px; padding: 12px; background: var(--bg-tertiary); border-radius: 6px;">
|
|
2228
|
+
<p style="font-size: 12px; color: var(--text-secondary); margin: 0;">
|
|
2229
|
+
🔗 Direct URL: <a id="shareDirectUrl" href="#" target="_blank" style="color: var(--text-accent); word-break: break-all;"></a>
|
|
2230
|
+
</p>
|
|
2231
|
+
<p style="font-size: 12px; color: var(--text-warning); margin: 8px 0 0 0;">
|
|
2232
|
+
⚠️ Files kept for 3-100 days (based on size)
|
|
2233
|
+
</p>
|
|
2234
|
+
<p style="font-size: 12px; color: var(--text-secondary); margin: 4px 0 0 0;">
|
|
2235
|
+
🔓 Files are not encrypted by default
|
|
2236
|
+
</p>
|
|
2237
|
+
</div>
|
|
2238
|
+
</div>
|
|
2239
|
+
<div class="modal-actions">
|
|
2240
|
+
<button class="modal-btn secondary" onclick="closeShareModal()">Close</button>
|
|
2241
|
+
<button class="modal-btn primary" id="copyShareCommandBtn" onclick="copyShareCommand()" style="display: none;">Copy Command</button>
|
|
2242
|
+
</div>
|
|
2243
|
+
</div>
|
|
2244
|
+
</div>
|
|
2245
|
+
|
|
1994
2246
|
<!-- Import WebSocket and Data Services -->
|
|
1995
2247
|
<script src="services/WebSocketService.js"></script>
|
|
1996
2248
|
<script src="services/DataService.js"></script>
|
|
@@ -2239,56 +2491,101 @@
|
|
|
2239
2491
|
|
|
2240
2492
|
renderConversations(conversations, states = {}) {
|
|
2241
2493
|
const conversationsList = document.getElementById('conversationsList');
|
|
2242
|
-
|
|
2494
|
+
|
|
2243
2495
|
if (conversations.length === 0) {
|
|
2244
2496
|
conversationsList.innerHTML = `
|
|
2245
2497
|
<div class="no-conversations">
|
|
2246
2498
|
<div class="no-conversations-icon">💬</div>
|
|
2247
|
-
<h3>No
|
|
2248
|
-
<p>Start a
|
|
2499
|
+
<h3>No sessions found</h3>
|
|
2500
|
+
<p>Start a session with Claude Code to see it here</p>
|
|
2249
2501
|
</div>
|
|
2250
2502
|
`;
|
|
2251
2503
|
return;
|
|
2252
2504
|
}
|
|
2253
2505
|
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
const stateClass = this.getStateClass(state);
|
|
2257
|
-
const stateLabel = this.getStateLabel(state);
|
|
2258
|
-
|
|
2259
|
-
// Debug logging for first few conversations
|
|
2260
|
-
console.log(`🔍 Conversation ${conv.id.slice(-8)}: State="${state}" -> Label="${stateLabel}" Class="${stateClass}"`);
|
|
2261
|
-
|
|
2262
|
-
const lastActivity = this.formatRelativeTime(new Date(conv.lastModified));
|
|
2263
|
-
const messageCount = conv.messageCount || 0;
|
|
2506
|
+
// Group conversations by project
|
|
2507
|
+
const groupedByProject = conversations.reduce((groups, conv) => {
|
|
2264
2508
|
const projectName = conv.project || 'Unknown Project';
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2509
|
+
if (!groups[projectName]) {
|
|
2510
|
+
groups[projectName] = [];
|
|
2511
|
+
}
|
|
2512
|
+
groups[projectName].push(conv);
|
|
2513
|
+
return groups;
|
|
2514
|
+
}, {});
|
|
2515
|
+
|
|
2516
|
+
// Sort projects alphabetically
|
|
2517
|
+
const sortedProjects = Object.keys(groupedByProject).sort();
|
|
2518
|
+
|
|
2519
|
+
// Initialize expanded state if not exists
|
|
2520
|
+
if (!this.expandedProjects) {
|
|
2521
|
+
this.expandedProjects = new Set(); // All collapsed by default
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2524
|
+
// Render grouped conversations
|
|
2525
|
+
conversationsList.innerHTML = sortedProjects.map(projectName => {
|
|
2526
|
+
const projectConversations = groupedByProject[projectName];
|
|
2527
|
+
const isExpanded = this.expandedProjects.has(projectName);
|
|
2268
2528
|
const firstLetter = projectName.charAt(0).toUpperCase();
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2529
|
+
const conversationCount = projectConversations.length;
|
|
2530
|
+
|
|
2531
|
+
// Render conversations for this project
|
|
2532
|
+
const conversationsHTML = projectConversations.map(conv => {
|
|
2533
|
+
const state = states[conv.id] || 'inactive';
|
|
2534
|
+
const stateClass = this.getStateClass(state);
|
|
2535
|
+
const stateLabel = this.getStateLabel(state);
|
|
2536
|
+
|
|
2537
|
+
const lastActivity = this.formatRelativeTime(new Date(conv.lastModified));
|
|
2538
|
+
const messageCount = conv.messageCount || 0;
|
|
2539
|
+
const conversationId = conv.id.slice(-8);
|
|
2540
|
+
|
|
2541
|
+
return `
|
|
2542
|
+
<div class="conversation-item" data-conversation-id="${conv.id}" style="display: ${isExpanded ? 'flex' : 'none'}">
|
|
2543
|
+
<div class="conversation-avatar conversation-avatar-small">
|
|
2544
|
+
${conversationId.substring(0, 2).toUpperCase()}
|
|
2545
|
+
</div>
|
|
2546
|
+
<div class="conversation-content">
|
|
2547
|
+
<div class="conversation-header">
|
|
2548
|
+
<div class="conversation-name">Session ${conversationId}</div>
|
|
2549
|
+
<div class="conversation-time">${lastActivity}</div>
|
|
2550
|
+
</div>
|
|
2551
|
+
<div class="conversation-meta">
|
|
2552
|
+
<span class="conversation-state ${stateClass}">${stateLabel}</span>
|
|
2553
|
+
${messageCount > 0 ? `<span class="message-count">${messageCount}</span>` : ''}
|
|
2554
|
+
</div>
|
|
2555
|
+
</div>
|
|
2274
2556
|
</div>
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2557
|
+
`;
|
|
2558
|
+
}).join('');
|
|
2559
|
+
|
|
2560
|
+
return `
|
|
2561
|
+
<div class="project-group" data-project="${projectName}">
|
|
2562
|
+
<div class="project-header" data-project="${projectName}">
|
|
2563
|
+
<div class="project-avatar">
|
|
2564
|
+
${firstLetter}
|
|
2279
2565
|
</div>
|
|
2280
|
-
<div class="
|
|
2281
|
-
|
|
2566
|
+
<div class="project-info">
|
|
2567
|
+
<div class="project-name">${projectName}</div>
|
|
2568
|
+
<div class="project-count">${conversationCount} session${conversationCount !== 1 ? 's' : ''}</div>
|
|
2282
2569
|
</div>
|
|
2283
|
-
<div class="
|
|
2284
|
-
<
|
|
2285
|
-
|
|
2570
|
+
<div class="project-toggle">
|
|
2571
|
+
<svg class="toggle-icon ${isExpanded ? 'expanded' : ''}" width="20" height="20" viewBox="0 0 20 20" fill="currentColor">
|
|
2572
|
+
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd"/>
|
|
2573
|
+
</svg>
|
|
2286
2574
|
</div>
|
|
2287
2575
|
</div>
|
|
2576
|
+
${conversationsHTML}
|
|
2288
2577
|
</div>
|
|
2289
2578
|
`;
|
|
2290
2579
|
}).join('');
|
|
2291
2580
|
|
|
2581
|
+
// Bind project toggle events
|
|
2582
|
+
conversationsList.querySelectorAll('.project-header').forEach(header => {
|
|
2583
|
+
header.addEventListener('click', (e) => {
|
|
2584
|
+
const projectName = header.dataset.project;
|
|
2585
|
+
this.toggleProject(projectName);
|
|
2586
|
+
});
|
|
2587
|
+
});
|
|
2588
|
+
|
|
2292
2589
|
// Bind conversation click events
|
|
2293
2590
|
conversationsList.querySelectorAll('.conversation-item').forEach(item => {
|
|
2294
2591
|
item.addEventListener('click', () => {
|
|
@@ -2298,6 +2595,17 @@
|
|
|
2298
2595
|
});
|
|
2299
2596
|
}
|
|
2300
2597
|
|
|
2598
|
+
toggleProject(projectName) {
|
|
2599
|
+
if (this.expandedProjects.has(projectName)) {
|
|
2600
|
+
this.expandedProjects.delete(projectName);
|
|
2601
|
+
} else {
|
|
2602
|
+
this.expandedProjects.add(projectName);
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
// Re-render conversations with current states
|
|
2606
|
+
this.renderConversations(this.conversations, this.conversationStates);
|
|
2607
|
+
}
|
|
2608
|
+
|
|
2301
2609
|
selectConversation(conversationId) {
|
|
2302
2610
|
this.selectedConversationId = conversationId;
|
|
2303
2611
|
|
|
@@ -2322,7 +2630,7 @@
|
|
|
2322
2630
|
const projectName = conversation.project || 'Unknown Project';
|
|
2323
2631
|
const convId = conversation.id.slice(-8);
|
|
2324
2632
|
chatViewTitle.textContent = projectName;
|
|
2325
|
-
chatViewSubtitle.textContent = `
|
|
2633
|
+
chatViewSubtitle.textContent = `Session ${convId}`;
|
|
2326
2634
|
|
|
2327
2635
|
// Show chat view with animation
|
|
2328
2636
|
chatView.classList.add('active');
|
|
@@ -2334,10 +2642,14 @@
|
|
|
2334
2642
|
chatView.classList.remove('show-tools');
|
|
2335
2643
|
}
|
|
2336
2644
|
|
|
2337
|
-
// Show
|
|
2645
|
+
// Show action buttons group
|
|
2646
|
+
const actionButtonsGroup = document.getElementById('actionButtonsGroup');
|
|
2647
|
+
actionButtonsGroup.style.display = 'inline-flex';
|
|
2648
|
+
|
|
2338
2649
|
const resumeBtn = document.getElementById('resumeConversation');
|
|
2339
|
-
|
|
2650
|
+
const shareBtn = document.getElementById('shareConversation');
|
|
2340
2651
|
resumeBtn.setAttribute('data-conversation-id', conversationId);
|
|
2652
|
+
shareBtn.setAttribute('data-conversation-id', conversationId);
|
|
2341
2653
|
|
|
2342
2654
|
// Load messages (placeholder for now)
|
|
2343
2655
|
this.loadChatMessages(conversationId);
|
|
@@ -2352,9 +2664,9 @@
|
|
|
2352
2664
|
// Clean up scroll tracking when leaving conversation
|
|
2353
2665
|
this.removeScrollTracking();
|
|
2354
2666
|
|
|
2355
|
-
// Hide
|
|
2356
|
-
const
|
|
2357
|
-
|
|
2667
|
+
// Hide action buttons group
|
|
2668
|
+
const actionButtonsGroup = document.getElementById('actionButtonsGroup');
|
|
2669
|
+
actionButtonsGroup.style.display = 'none';
|
|
2358
2670
|
|
|
2359
2671
|
// Remove active state from conversations
|
|
2360
2672
|
document.querySelectorAll('.conversation-item').forEach(item => {
|
|
@@ -3024,14 +3336,68 @@
|
|
|
3024
3336
|
|
|
3025
3337
|
|
|
3026
3338
|
filterConversations(searchTerm) {
|
|
3027
|
-
const
|
|
3028
|
-
const
|
|
3339
|
+
const term = searchTerm.toLowerCase().trim();
|
|
3340
|
+
const projectGroups = document.querySelectorAll('.project-group');
|
|
3341
|
+
|
|
3342
|
+
if (!term) {
|
|
3343
|
+
// If search is empty, collapse all projects and reset
|
|
3344
|
+
this.expandedProjects.clear(); // Close all projects
|
|
3029
3345
|
|
|
3030
|
-
|
|
3031
|
-
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
|
|
3346
|
+
projectGroups.forEach(group => {
|
|
3347
|
+
group.style.display = 'block';
|
|
3348
|
+
|
|
3349
|
+
// Hide all conversations
|
|
3350
|
+
const conversations = group.querySelectorAll('.conversation-item');
|
|
3351
|
+
conversations.forEach(conv => {
|
|
3352
|
+
conv.style.display = 'none';
|
|
3353
|
+
});
|
|
3354
|
+
|
|
3355
|
+
// Update toggle icon to collapsed state
|
|
3356
|
+
const toggleIcon = group.querySelector('.toggle-icon');
|
|
3357
|
+
if (toggleIcon) {
|
|
3358
|
+
toggleIcon.classList.remove('expanded');
|
|
3359
|
+
}
|
|
3360
|
+
});
|
|
3361
|
+
return;
|
|
3362
|
+
}
|
|
3363
|
+
|
|
3364
|
+
// Filter projects and conversations
|
|
3365
|
+
projectGroups.forEach(group => {
|
|
3366
|
+
const projectName = group.dataset.project;
|
|
3367
|
+
const projectNameLower = projectName.toLowerCase();
|
|
3368
|
+
const conversations = group.querySelectorAll('.conversation-item');
|
|
3369
|
+
|
|
3370
|
+
let hasMatchingConversation = false;
|
|
3371
|
+
|
|
3372
|
+
// Check each conversation in this project
|
|
3373
|
+
conversations.forEach(item => {
|
|
3374
|
+
const nameElement = item.querySelector('.conversation-name');
|
|
3375
|
+
const name = nameElement ? nameElement.textContent.toLowerCase() : '';
|
|
3376
|
+
const matches = name.includes(term) || projectNameLower.includes(term);
|
|
3377
|
+
|
|
3378
|
+
if (matches) {
|
|
3379
|
+
item.style.display = 'flex';
|
|
3380
|
+
hasMatchingConversation = true;
|
|
3381
|
+
} else {
|
|
3382
|
+
item.style.display = 'none';
|
|
3383
|
+
}
|
|
3384
|
+
});
|
|
3385
|
+
|
|
3386
|
+
// Show project group if it has matching conversations or if project name matches
|
|
3387
|
+
if (hasMatchingConversation || projectNameLower.includes(term)) {
|
|
3388
|
+
group.style.display = 'block';
|
|
3389
|
+
// Auto-expand project when searching
|
|
3390
|
+
if (!this.expandedProjects.has(projectName)) {
|
|
3391
|
+
this.expandedProjects.add(projectName);
|
|
3392
|
+
// Update toggle icon
|
|
3393
|
+
const toggleIcon = group.querySelector('.toggle-icon');
|
|
3394
|
+
if (toggleIcon) {
|
|
3395
|
+
toggleIcon.classList.add('expanded');
|
|
3396
|
+
}
|
|
3397
|
+
}
|
|
3398
|
+
} else {
|
|
3399
|
+
group.style.display = 'none';
|
|
3400
|
+
}
|
|
3035
3401
|
});
|
|
3036
3402
|
}
|
|
3037
3403
|
|
|
@@ -3647,6 +4013,11 @@
|
|
|
3647
4013
|
// Clear advanced search filters
|
|
3648
4014
|
this.resetAdvancedSearch();
|
|
3649
4015
|
|
|
4016
|
+
// Reset expanded projects state (close all projects)
|
|
4017
|
+
if (this.expandedProjects) {
|
|
4018
|
+
this.expandedProjects.clear();
|
|
4019
|
+
}
|
|
4020
|
+
|
|
3650
4021
|
// Hide search results info
|
|
3651
4022
|
searchResultsInfo.classList.remove('active');
|
|
3652
4023
|
|
|
@@ -4351,6 +4722,199 @@
|
|
|
4351
4722
|
}
|
|
4352
4723
|
}
|
|
4353
4724
|
|
|
4725
|
+
// Share conversation functions
|
|
4726
|
+
function shareConversation() {
|
|
4727
|
+
const shareBtn = document.getElementById('shareConversation');
|
|
4728
|
+
const conversationId = shareBtn.getAttribute('data-conversation-id');
|
|
4729
|
+
|
|
4730
|
+
if (!conversationId) {
|
|
4731
|
+
console.error('No conversation ID found');
|
|
4732
|
+
return;
|
|
4733
|
+
}
|
|
4734
|
+
|
|
4735
|
+
console.log('📤 Opening share confirmation for conversation:', conversationId);
|
|
4736
|
+
|
|
4737
|
+
// Show confirmation modal first
|
|
4738
|
+
const confirmModal = document.getElementById('confirmShareModal');
|
|
4739
|
+
confirmModal.classList.add('show');
|
|
4740
|
+
|
|
4741
|
+
// Close modal when clicking outside
|
|
4742
|
+
confirmModal.addEventListener('click', (e) => {
|
|
4743
|
+
if (e.target === confirmModal) {
|
|
4744
|
+
closeConfirmShareModal();
|
|
4745
|
+
}
|
|
4746
|
+
});
|
|
4747
|
+
}
|
|
4748
|
+
|
|
4749
|
+
function closeConfirmShareModal() {
|
|
4750
|
+
const confirmModal = document.getElementById('confirmShareModal');
|
|
4751
|
+
confirmModal.classList.remove('show');
|
|
4752
|
+
}
|
|
4753
|
+
|
|
4754
|
+
async function proceedWithShare() {
|
|
4755
|
+
// Close confirmation modal
|
|
4756
|
+
closeConfirmShareModal();
|
|
4757
|
+
|
|
4758
|
+
const shareBtn = document.getElementById('shareConversation');
|
|
4759
|
+
const conversationId = shareBtn.getAttribute('data-conversation-id');
|
|
4760
|
+
|
|
4761
|
+
if (!conversationId) {
|
|
4762
|
+
console.error('No conversation ID found');
|
|
4763
|
+
return;
|
|
4764
|
+
}
|
|
4765
|
+
|
|
4766
|
+
console.log('📤 User confirmed - Proceeding with share for conversation:', conversationId);
|
|
4767
|
+
|
|
4768
|
+
// Show share modal with loading state
|
|
4769
|
+
const modal = document.getElementById('shareModal');
|
|
4770
|
+
const loadingDiv = document.getElementById('shareModalLoading');
|
|
4771
|
+
const contentDiv = document.getElementById('shareModalContent');
|
|
4772
|
+
const copyBtn = document.getElementById('copyShareCommandBtn');
|
|
4773
|
+
|
|
4774
|
+
modal.classList.add('show');
|
|
4775
|
+
loadingDiv.style.display = 'block';
|
|
4776
|
+
contentDiv.style.display = 'none';
|
|
4777
|
+
copyBtn.style.display = 'none';
|
|
4778
|
+
|
|
4779
|
+
// Close modal when clicking outside
|
|
4780
|
+
modal.addEventListener('click', (e) => {
|
|
4781
|
+
if (e.target === modal) {
|
|
4782
|
+
closeShareModal();
|
|
4783
|
+
}
|
|
4784
|
+
});
|
|
4785
|
+
|
|
4786
|
+
try {
|
|
4787
|
+
// Call API to share the conversation
|
|
4788
|
+
const response = await fetch(`/api/conversations/${conversationId}/share`, {
|
|
4789
|
+
method: 'POST',
|
|
4790
|
+
headers: {
|
|
4791
|
+
'Content-Type': 'application/json'
|
|
4792
|
+
}
|
|
4793
|
+
});
|
|
4794
|
+
|
|
4795
|
+
if (!response.ok) {
|
|
4796
|
+
throw new Error(`Failed to share session: ${response.statusText}`);
|
|
4797
|
+
}
|
|
4798
|
+
|
|
4799
|
+
const data = await response.json();
|
|
4800
|
+
|
|
4801
|
+
console.log('✅ Session shared successfully:', data);
|
|
4802
|
+
|
|
4803
|
+
// Update modal with share data
|
|
4804
|
+
const qrCodeImg = document.getElementById('shareQRCode');
|
|
4805
|
+
const commandDiv = document.getElementById('shareModalCommand');
|
|
4806
|
+
const directUrlLink = document.getElementById('shareDirectUrl');
|
|
4807
|
+
|
|
4808
|
+
// Set QR code
|
|
4809
|
+
if (data.qrCode && data.qrCode.dataUrl) {
|
|
4810
|
+
qrCodeImg.src = data.qrCode.dataUrl;
|
|
4811
|
+
qrCodeImg.style.display = 'block';
|
|
4812
|
+
} else {
|
|
4813
|
+
qrCodeImg.style.display = 'none';
|
|
4814
|
+
}
|
|
4815
|
+
|
|
4816
|
+
// Set command
|
|
4817
|
+
commandDiv.textContent = data.shareCommand;
|
|
4818
|
+
commandDiv.setAttribute('data-command', data.shareCommand);
|
|
4819
|
+
|
|
4820
|
+
// Set direct URL
|
|
4821
|
+
directUrlLink.textContent = data.uploadUrl;
|
|
4822
|
+
directUrlLink.href = data.uploadUrl;
|
|
4823
|
+
|
|
4824
|
+
// Show message count information
|
|
4825
|
+
const messageInfoDiv = document.getElementById('shareMessageInfo');
|
|
4826
|
+
const messageCountSpan = document.getElementById('shareMessageCount');
|
|
4827
|
+
|
|
4828
|
+
if (data.wasLimited) {
|
|
4829
|
+
messageCountSpan.innerHTML = `⚠️ This session has <strong>${data.totalMessageCount}</strong> messages. Sharing last <strong>${data.messageCount}</strong> messages to keep file size manageable.`;
|
|
4830
|
+
messageInfoDiv.style.display = 'block';
|
|
4831
|
+
} else {
|
|
4832
|
+
messageCountSpan.innerHTML = `✅ Sharing <strong>${data.messageCount}</strong> messages from this conversation.`;
|
|
4833
|
+
messageInfoDiv.style.display = 'block';
|
|
4834
|
+
}
|
|
4835
|
+
|
|
4836
|
+
// Show content and hide loading
|
|
4837
|
+
loadingDiv.style.display = 'none';
|
|
4838
|
+
contentDiv.style.display = 'block';
|
|
4839
|
+
copyBtn.style.display = 'block';
|
|
4840
|
+
|
|
4841
|
+
} catch (error) {
|
|
4842
|
+
console.error('❌ Failed to share session:', error);
|
|
4843
|
+
alert(`Failed to share session: ${error.message}`);
|
|
4844
|
+
closeShareModal();
|
|
4845
|
+
}
|
|
4846
|
+
}
|
|
4847
|
+
|
|
4848
|
+
function closeShareModal() {
|
|
4849
|
+
const modal = document.getElementById('shareModal');
|
|
4850
|
+
modal.classList.remove('show');
|
|
4851
|
+
}
|
|
4852
|
+
|
|
4853
|
+
function copyShareCommand() {
|
|
4854
|
+
const commandDiv = document.getElementById('shareModalCommand');
|
|
4855
|
+
const copyBtn = document.getElementById('copyShareCommandBtn');
|
|
4856
|
+
const command = commandDiv.getAttribute('data-command');
|
|
4857
|
+
|
|
4858
|
+
if (!command) {
|
|
4859
|
+
console.error('No command found to copy');
|
|
4860
|
+
return;
|
|
4861
|
+
}
|
|
4862
|
+
|
|
4863
|
+
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
4864
|
+
navigator.clipboard.writeText(command).then(() => {
|
|
4865
|
+
// Show success feedback
|
|
4866
|
+
const originalText = copyBtn.textContent;
|
|
4867
|
+
copyBtn.textContent = '✅ Copied!';
|
|
4868
|
+
copyBtn.style.backgroundColor = 'rgba(63, 185, 80, 0.8)';
|
|
4869
|
+
copyBtn.style.borderColor = 'rgba(63, 185, 80, 1)';
|
|
4870
|
+
|
|
4871
|
+
setTimeout(() => {
|
|
4872
|
+
copyBtn.textContent = originalText;
|
|
4873
|
+
copyBtn.style.backgroundColor = '';
|
|
4874
|
+
copyBtn.style.borderColor = '';
|
|
4875
|
+
}, 1500);
|
|
4876
|
+
|
|
4877
|
+
console.log('📋 Share command copied to clipboard:', command);
|
|
4878
|
+
}).catch(err => {
|
|
4879
|
+
console.error('Failed to copy to clipboard:', err);
|
|
4880
|
+
fallbackCopyShare(command);
|
|
4881
|
+
});
|
|
4882
|
+
} else {
|
|
4883
|
+
// Fallback for browsers without clipboard API
|
|
4884
|
+
fallbackCopyShare(command);
|
|
4885
|
+
}
|
|
4886
|
+
}
|
|
4887
|
+
|
|
4888
|
+
function fallbackCopyShare(command) {
|
|
4889
|
+
// Create a temporary text area to select and copy
|
|
4890
|
+
const tempTextArea = document.createElement('textarea');
|
|
4891
|
+
tempTextArea.value = command;
|
|
4892
|
+
tempTextArea.style.position = 'fixed';
|
|
4893
|
+
tempTextArea.style.left = '-9999px';
|
|
4894
|
+
document.body.appendChild(tempTextArea);
|
|
4895
|
+
tempTextArea.select();
|
|
4896
|
+
|
|
4897
|
+
try {
|
|
4898
|
+
document.execCommand('copy');
|
|
4899
|
+
const copyBtn = document.getElementById('copyShareCommandBtn');
|
|
4900
|
+
const originalText = copyBtn.textContent;
|
|
4901
|
+
copyBtn.textContent = '✅ Copied!';
|
|
4902
|
+
copyBtn.style.backgroundColor = 'rgba(63, 185, 80, 0.8)';
|
|
4903
|
+
|
|
4904
|
+
setTimeout(() => {
|
|
4905
|
+
copyBtn.textContent = originalText;
|
|
4906
|
+
copyBtn.style.backgroundColor = '';
|
|
4907
|
+
}, 1500);
|
|
4908
|
+
|
|
4909
|
+
console.log('📋 Share command copied using fallback method:', command);
|
|
4910
|
+
} catch (err) {
|
|
4911
|
+
console.error('Fallback copy failed:', err);
|
|
4912
|
+
alert(`Please copy this command manually:\n\n${command}`);
|
|
4913
|
+
} finally {
|
|
4914
|
+
document.body.removeChild(tempTextArea);
|
|
4915
|
+
}
|
|
4916
|
+
}
|
|
4917
|
+
|
|
4354
4918
|
// Initialize the app
|
|
4355
4919
|
document.addEventListener('DOMContentLoaded', () => {
|
|
4356
4920
|
new ChatsMobileApp();
|