claude-code-templates 1.25.0 ā 1.26.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/package.json +1 -1
- package/src/analytics-web/chats_mobile.html +76 -230
- package/src/chats-mobile.js +12 -11
- package/src/session-sharing.js +97 -36
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-templates",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.26.0",
|
|
4
4
|
"description": "CLI tool to setup Claude Code configurations with framework-specific commands, automation hooks and MCP Servers for your projects",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -755,12 +755,12 @@
|
|
|
755
755
|
stroke: rgba(63, 185, 80, 1);
|
|
756
756
|
}
|
|
757
757
|
|
|
758
|
-
.action-btn.
|
|
758
|
+
.action-btn.download-btn:hover {
|
|
759
759
|
background: rgba(59, 130, 246, 0.15);
|
|
760
760
|
color: rgba(59, 130, 246, 1);
|
|
761
761
|
}
|
|
762
762
|
|
|
763
|
-
.action-btn.
|
|
763
|
+
.action-btn.download-btn:hover svg {
|
|
764
764
|
stroke: rgba(59, 130, 246, 1);
|
|
765
765
|
}
|
|
766
766
|
|
|
@@ -2052,13 +2052,13 @@
|
|
|
2052
2052
|
</svg>
|
|
2053
2053
|
<span>Resume</span>
|
|
2054
2054
|
</button>
|
|
2055
|
-
<button class="action-btn
|
|
2055
|
+
<button class="action-btn download-btn" id="downloadConversation" onclick="downloadConversation()">
|
|
2056
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="
|
|
2058
|
-
<polyline points="
|
|
2059
|
-
<line x1="12" y1="
|
|
2057
|
+
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path>
|
|
2058
|
+
<polyline points="7 10 12 15 17 10"></polyline>
|
|
2059
|
+
<line x1="12" y1="15" x2="12" y2="3"></line>
|
|
2060
2060
|
</svg>
|
|
2061
|
-
<span>
|
|
2061
|
+
<span>Download</span>
|
|
2062
2062
|
</button>
|
|
2063
2063
|
<button class="action-btn search-btn" id="chatSearchToggle">
|
|
2064
2064
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
@@ -2143,106 +2143,58 @@
|
|
|
2143
2143
|
</div>
|
|
2144
2144
|
</div>
|
|
2145
2145
|
|
|
2146
|
-
<!--
|
|
2147
|
-
<div class="modal-overlay" id="
|
|
2146
|
+
<!-- Download Context Modal -->
|
|
2147
|
+
<div class="modal-overlay" id="downloadModal">
|
|
2148
2148
|
<div class="modal">
|
|
2149
2149
|
<div class="modal-header" style="background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%);">
|
|
2150
|
-
<span class="modal-icon"
|
|
2151
|
-
<h3 class="modal-title">
|
|
2150
|
+
<span class="modal-icon">š„</span>
|
|
2151
|
+
<h3 class="modal-title">Download Conversation Context</h3>
|
|
2152
2152
|
</div>
|
|
2153
2153
|
<div style="padding: 20px;">
|
|
2154
2154
|
<p class="modal-description" style="margin-bottom: 16px; line-height: 1.5;">
|
|
2155
|
-
|
|
2156
|
-
The generated link can be shared with others to clone your session.
|
|
2155
|
+
Download this conversation as a <strong>Markdown context file</strong> that Claude Code can read to continue your work.
|
|
2157
2156
|
</p>
|
|
2158
2157
|
|
|
2159
2158
|
<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
|
|
2159
|
+
<h4 style="color: #3b82f6; margin: 0 0 8px 0; font-size: 14px;">š What's included</h4>
|
|
2161
2160
|
<p style="margin: 0; font-size: 13px; color: var(--text-secondary); line-height: 1.4;">
|
|
2162
|
-
|
|
2161
|
+
Full conversation history (last <strong>100 messages</strong>) formatted for Claude Code to understand
|
|
2163
2162
|
</p>
|
|
2164
2163
|
</div>
|
|
2165
2164
|
|
|
2166
|
-
<div style="background: rgba(
|
|
2167
|
-
<h4 style="color: #
|
|
2168
|
-
<
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2165
|
+
<div style="background: rgba(16, 185, 129, 0.08); border-left: 3px solid #10b981; padding: 12px; border-radius: 6px; margin-bottom: 16px;">
|
|
2166
|
+
<h4 style="color: #10b981; margin: 0 0 8px 0; font-size: 14px;">š” How to continue the conversation</h4>
|
|
2167
|
+
<p style="margin: 0 0 12px 0; font-size: 13px; color: var(--text-secondary); line-height: 1.4;">
|
|
2168
|
+
After downloading, use one of these commands to load the context:
|
|
2169
|
+
</p>
|
|
2170
|
+
|
|
2171
|
+
<div style="margin-bottom: 10px;">
|
|
2172
|
+
<p style="margin: 0 0 4px 0; font-size: 12px; font-weight: 600; color: #10b981;">Option 1: Direct reference</p>
|
|
2173
|
+
<code style="display: block; background: rgba(0,0,0,0.5); padding: 8px; border-radius: 4px; font-size: 12px; color: #e5e7eb; overflow-x: auto; white-space: nowrap;">claude "read @context-file.md and continue"</code>
|
|
2174
|
+
</div>
|
|
2175
|
+
|
|
2176
|
+
<div>
|
|
2177
|
+
<p style="margin: 0 0 4px 0; font-size: 12px; font-weight: 600; color: #10b981;">Option 2: Using cat (pipe)</p>
|
|
2178
|
+
<code style="display: block; background: rgba(0,0,0,0.5); padding: 8px; border-radius: 4px; font-size: 12px; color: #e5e7eb; overflow-x: auto; white-space: nowrap;">cat context-file.md | claude</code>
|
|
2179
|
+
</div>
|
|
2174
2180
|
</div>
|
|
2175
2181
|
|
|
2176
2182
|
<div style="background: rgba(100, 116, 139, 0.08); border-left: 3px solid #64748b; padding: 12px; border-radius: 6px;">
|
|
2177
2183
|
<p style="margin: 0; font-size: 12px; color: var(--text-secondary); line-height: 1.4;">
|
|
2178
|
-
|
|
2184
|
+
š¾ <strong>Tip:</strong> Save the downloaded file in your project directory for easy access
|
|
2179
2185
|
</p>
|
|
2180
2186
|
</div>
|
|
2181
2187
|
</div>
|
|
2182
2188
|
|
|
2183
2189
|
<div class="modal-actions">
|
|
2184
|
-
<button class="modal-btn secondary" onclick="
|
|
2185
|
-
<button class="modal-btn primary" onclick="
|
|
2186
|
-
|
|
2190
|
+
<button class="modal-btn secondary" onclick="closeDownloadModal()">Cancel</button>
|
|
2191
|
+
<button class="modal-btn primary" onclick="proceedWithDownload()" style="background: #3b82f6;">
|
|
2192
|
+
Download Context File
|
|
2187
2193
|
</button>
|
|
2188
2194
|
</div>
|
|
2189
2195
|
</div>
|
|
2190
2196
|
</div>
|
|
2191
2197
|
|
|
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
|
-
|
|
2246
2198
|
<!-- Import WebSocket and Data Services -->
|
|
2247
2199
|
<script src="services/WebSocketService.js"></script>
|
|
2248
2200
|
<script src="services/DataService.js"></script>
|
|
@@ -2647,9 +2599,9 @@
|
|
|
2647
2599
|
actionButtonsGroup.style.display = 'inline-flex';
|
|
2648
2600
|
|
|
2649
2601
|
const resumeBtn = document.getElementById('resumeConversation');
|
|
2650
|
-
const
|
|
2602
|
+
const downloadBtn = document.getElementById('downloadConversation');
|
|
2651
2603
|
resumeBtn.setAttribute('data-conversation-id', conversationId);
|
|
2652
|
-
|
|
2604
|
+
downloadBtn.setAttribute('data-conversation-id', conversationId);
|
|
2653
2605
|
|
|
2654
2606
|
// Load messages (placeholder for now)
|
|
2655
2607
|
this.loadChatMessages(conversationId);
|
|
@@ -4722,70 +4674,52 @@
|
|
|
4722
4674
|
}
|
|
4723
4675
|
}
|
|
4724
4676
|
|
|
4725
|
-
//
|
|
4726
|
-
function
|
|
4727
|
-
const
|
|
4728
|
-
const conversationId =
|
|
4677
|
+
// Download conversation context functions
|
|
4678
|
+
function downloadConversation() {
|
|
4679
|
+
const downloadBtn = document.getElementById('downloadConversation');
|
|
4680
|
+
const conversationId = downloadBtn.getAttribute('data-conversation-id');
|
|
4729
4681
|
|
|
4730
4682
|
if (!conversationId) {
|
|
4731
4683
|
console.error('No conversation ID found');
|
|
4732
4684
|
return;
|
|
4733
4685
|
}
|
|
4734
4686
|
|
|
4735
|
-
console.log('
|
|
4687
|
+
console.log('š„ Opening download modal for conversation:', conversationId);
|
|
4736
4688
|
|
|
4737
|
-
// Show
|
|
4738
|
-
const
|
|
4739
|
-
|
|
4689
|
+
// Show download modal
|
|
4690
|
+
const downloadModal = document.getElementById('downloadModal');
|
|
4691
|
+
downloadModal.classList.add('show');
|
|
4740
4692
|
|
|
4741
4693
|
// Close modal when clicking outside
|
|
4742
|
-
|
|
4743
|
-
if (e.target ===
|
|
4744
|
-
|
|
4694
|
+
downloadModal.addEventListener('click', (e) => {
|
|
4695
|
+
if (e.target === downloadModal) {
|
|
4696
|
+
closeDownloadModal();
|
|
4745
4697
|
}
|
|
4746
4698
|
});
|
|
4747
4699
|
}
|
|
4748
4700
|
|
|
4749
|
-
function
|
|
4750
|
-
const
|
|
4751
|
-
|
|
4701
|
+
function closeDownloadModal() {
|
|
4702
|
+
const downloadModal = document.getElementById('downloadModal');
|
|
4703
|
+
downloadModal.classList.remove('show');
|
|
4752
4704
|
}
|
|
4753
4705
|
|
|
4754
|
-
async function
|
|
4755
|
-
// Close
|
|
4756
|
-
|
|
4706
|
+
async function proceedWithDownload() {
|
|
4707
|
+
// Close modal
|
|
4708
|
+
closeDownloadModal();
|
|
4757
4709
|
|
|
4758
|
-
const
|
|
4759
|
-
const conversationId =
|
|
4710
|
+
const downloadBtn = document.getElementById('downloadConversation');
|
|
4711
|
+
const conversationId = downloadBtn.getAttribute('data-conversation-id');
|
|
4760
4712
|
|
|
4761
4713
|
if (!conversationId) {
|
|
4762
4714
|
console.error('No conversation ID found');
|
|
4763
4715
|
return;
|
|
4764
4716
|
}
|
|
4765
4717
|
|
|
4766
|
-
console.log('
|
|
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
|
-
});
|
|
4718
|
+
console.log('š„ Downloading context for conversation:', conversationId);
|
|
4785
4719
|
|
|
4786
4720
|
try {
|
|
4787
|
-
// Call API to
|
|
4788
|
-
const response = await fetch(`/api/conversations/${conversationId}/
|
|
4721
|
+
// Call API to export the conversation as markdown
|
|
4722
|
+
const response = await fetch(`/api/conversations/${conversationId}/download`, {
|
|
4789
4723
|
method: 'POST',
|
|
4790
4724
|
headers: {
|
|
4791
4725
|
'Content-Type': 'application/json'
|
|
@@ -4793,125 +4727,37 @@
|
|
|
4793
4727
|
});
|
|
4794
4728
|
|
|
4795
4729
|
if (!response.ok) {
|
|
4796
|
-
throw new Error(`Failed to
|
|
4730
|
+
throw new Error(`Failed to export session: ${response.statusText}`);
|
|
4797
4731
|
}
|
|
4798
4732
|
|
|
4799
4733
|
const data = await response.json();
|
|
4800
4734
|
|
|
4801
|
-
console.log('ā
Session
|
|
4735
|
+
console.log('ā
Session exported successfully:', data);
|
|
4802
4736
|
|
|
4803
|
-
//
|
|
4804
|
-
const
|
|
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
|
-
}
|
|
4737
|
+
// Create a blob from the markdown content
|
|
4738
|
+
const blob = new Blob([data.markdown], { type: 'text/markdown;charset=utf-8' });
|
|
4815
4739
|
|
|
4816
|
-
//
|
|
4817
|
-
|
|
4818
|
-
|
|
4740
|
+
// Create a temporary download link
|
|
4741
|
+
const url = window.URL.createObjectURL(blob);
|
|
4742
|
+
const a = document.createElement('a');
|
|
4743
|
+
a.href = url;
|
|
4744
|
+
a.download = data.filename;
|
|
4745
|
+
document.body.appendChild(a);
|
|
4746
|
+
a.click();
|
|
4819
4747
|
|
|
4820
|
-
//
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
// Show message count information
|
|
4825
|
-
const messageInfoDiv = document.getElementById('shareMessageInfo');
|
|
4826
|
-
const messageCountSpan = document.getElementById('shareMessageCount');
|
|
4748
|
+
// Cleanup
|
|
4749
|
+
window.URL.revokeObjectURL(url);
|
|
4750
|
+
document.body.removeChild(a);
|
|
4827
4751
|
|
|
4752
|
+
// Show success notification
|
|
4753
|
+
console.log(`š„ Downloaded: ${data.filename}`);
|
|
4828
4754
|
if (data.wasLimited) {
|
|
4829
|
-
|
|
4830
|
-
messageInfoDiv.style.display = 'block';
|
|
4831
|
-
} else {
|
|
4832
|
-
messageCountSpan.innerHTML = `ā
Sharing <strong>${data.messageCount}</strong> messages from this conversation.`;
|
|
4833
|
-
messageInfoDiv.style.display = 'block';
|
|
4755
|
+
console.log(`ā ļø Exported ${data.messageCount} of ${data.totalMessageCount} messages`);
|
|
4834
4756
|
}
|
|
4835
4757
|
|
|
4836
|
-
// Show content and hide loading
|
|
4837
|
-
loadingDiv.style.display = 'none';
|
|
4838
|
-
contentDiv.style.display = 'block';
|
|
4839
|
-
copyBtn.style.display = 'block';
|
|
4840
|
-
|
|
4841
4758
|
} catch (error) {
|
|
4842
|
-
console.error('ā Failed to
|
|
4843
|
-
alert(`Failed to
|
|
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);
|
|
4759
|
+
console.error('ā Failed to download context:', error);
|
|
4760
|
+
alert(`Failed to download context: ${error.message}`);
|
|
4915
4761
|
}
|
|
4916
4762
|
}
|
|
4917
4763
|
|
package/src/chats-mobile.js
CHANGED
|
@@ -478,8 +478,8 @@ class ChatsMobile {
|
|
|
478
478
|
}
|
|
479
479
|
});
|
|
480
480
|
|
|
481
|
-
// API to
|
|
482
|
-
this.app.post('/api/conversations/:id/
|
|
481
|
+
// API to download a conversation session as markdown
|
|
482
|
+
this.app.post('/api/conversations/:id/download', async (req, res) => {
|
|
483
483
|
try {
|
|
484
484
|
const conversationId = req.params.id;
|
|
485
485
|
const conversation = this.data.conversations.find(conv => conv.id === conversationId);
|
|
@@ -488,24 +488,25 @@ class ChatsMobile {
|
|
|
488
488
|
return res.status(404).json({ error: 'Conversation not found' });
|
|
489
489
|
}
|
|
490
490
|
|
|
491
|
-
console.log(chalk.cyan(
|
|
491
|
+
console.log(chalk.cyan(`š„ Exporting conversation ${conversationId} as markdown...`));
|
|
492
492
|
|
|
493
|
-
//
|
|
494
|
-
const
|
|
493
|
+
// Export the session as markdown using SessionSharing module
|
|
494
|
+
const exportResult = await this.sessionSharing.exportSessionAsMarkdown(conversationId, conversation);
|
|
495
495
|
|
|
496
496
|
res.json({
|
|
497
497
|
success: true,
|
|
498
498
|
conversationId: conversationId,
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
499
|
+
markdown: exportResult.markdown,
|
|
500
|
+
filename: exportResult.filename,
|
|
501
|
+
messageCount: exportResult.messageCount,
|
|
502
|
+
totalMessageCount: exportResult.totalMessageCount,
|
|
503
|
+
wasLimited: exportResult.wasLimited,
|
|
503
504
|
timestamp: new Date().toISOString()
|
|
504
505
|
});
|
|
505
506
|
} catch (error) {
|
|
506
|
-
console.error('Error
|
|
507
|
+
console.error('Error exporting conversation:', error);
|
|
507
508
|
res.status(500).json({
|
|
508
|
-
error: 'Failed to
|
|
509
|
+
error: 'Failed to export session',
|
|
509
510
|
message: error.message
|
|
510
511
|
});
|
|
511
512
|
}
|
package/src/session-sharing.js
CHANGED
|
@@ -8,62 +8,123 @@ const execAsync = promisify(exec);
|
|
|
8
8
|
const QRCode = require('qrcode');
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* SessionSharing - Handles exporting
|
|
12
|
-
* Uses x0.at - a simple, reliable file hosting service
|
|
11
|
+
* SessionSharing - Handles exporting Claude Code sessions as downloadable context
|
|
13
12
|
*/
|
|
14
13
|
class SessionSharing {
|
|
15
14
|
constructor(conversationAnalyzer) {
|
|
16
15
|
this.conversationAnalyzer = conversationAnalyzer;
|
|
17
|
-
this.uploadService = 'x0.at';
|
|
18
|
-
this.uploadUrl = 'https://x0.at';
|
|
19
16
|
}
|
|
20
17
|
|
|
21
18
|
/**
|
|
22
|
-
* Export
|
|
23
|
-
* @param {string} conversationId - Conversation ID to
|
|
19
|
+
* Export conversation session as downloadable markdown file
|
|
20
|
+
* @param {string} conversationId - Conversation ID to export
|
|
24
21
|
* @param {Object} conversationData - Full conversation data object
|
|
25
|
-
* @param {Object} options -
|
|
26
|
-
* @returns {Promise<Object>}
|
|
22
|
+
* @param {Object} options - Export options (messageLimit, etc.)
|
|
23
|
+
* @returns {Promise<Object>} Export result with markdown content and filename
|
|
27
24
|
*/
|
|
28
|
-
async
|
|
29
|
-
console.log(chalk.blue(
|
|
25
|
+
async exportSessionAsMarkdown(conversationId, conversationData, options = {}) {
|
|
26
|
+
console.log(chalk.blue(`š„ Preparing session ${conversationId} for download...`));
|
|
30
27
|
|
|
31
28
|
try {
|
|
32
|
-
// 1.
|
|
33
|
-
const
|
|
29
|
+
// 1. Get conversation messages
|
|
30
|
+
const allMessages = await this.conversationAnalyzer.getParsedConversation(conversationData.filePath);
|
|
34
31
|
|
|
35
|
-
//
|
|
36
|
-
const
|
|
32
|
+
// Limit messages to avoid large file sizes (default: last 100 messages)
|
|
33
|
+
const messageLimit = options.messageLimit || 100;
|
|
34
|
+
const messages = allMessages.slice(-messageLimit);
|
|
37
35
|
|
|
38
|
-
//
|
|
39
|
-
const
|
|
36
|
+
// 2. Convert to markdown format
|
|
37
|
+
const markdown = this.convertToMarkdown(messages, conversationData, {
|
|
38
|
+
messageCount: messages.length,
|
|
39
|
+
totalMessageCount: allMessages.length,
|
|
40
|
+
wasLimited: allMessages.length > messageLimit
|
|
41
|
+
});
|
|
40
42
|
|
|
41
|
-
//
|
|
42
|
-
const
|
|
43
|
+
// 3. Generate filename
|
|
44
|
+
const projectName = (conversationData.project || 'session').replace(/[^a-zA-Z0-9-_]/g, '-');
|
|
45
|
+
const date = new Date().toISOString().split('T')[0];
|
|
46
|
+
const filename = `claude-context-${projectName}-${date}.md`;
|
|
43
47
|
|
|
44
|
-
console.log(chalk.green(`ā
Session
|
|
45
|
-
console.log(chalk.
|
|
46
|
-
console.log(chalk.gray(`š Direct URL: ${uploadUrl}`));
|
|
47
|
-
console.log(chalk.yellow(`ā ļø Files kept for 3-100 days (based on size)`));
|
|
48
|
-
console.log(chalk.gray(`š Note: Files are not encrypted by default`));
|
|
48
|
+
console.log(chalk.green(`ā
Session exported successfully!`));
|
|
49
|
+
console.log(chalk.gray(`š Exported ${messages.length} messages`));
|
|
49
50
|
|
|
50
51
|
return {
|
|
51
52
|
success: true,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
messageCount: sessionExport.conversation.messageCount,
|
|
58
|
-
totalMessageCount: sessionExport.conversation.totalMessageCount,
|
|
59
|
-
wasLimited: sessionExport.conversation.wasLimited
|
|
53
|
+
markdown,
|
|
54
|
+
filename,
|
|
55
|
+
messageCount: messages.length,
|
|
56
|
+
totalMessageCount: allMessages.length,
|
|
57
|
+
wasLimited: allMessages.length > messageLimit
|
|
60
58
|
};
|
|
61
59
|
} catch (error) {
|
|
62
|
-
console.error(chalk.red('ā Failed to
|
|
60
|
+
console.error(chalk.red('ā Failed to export session:'), error.message);
|
|
63
61
|
throw error;
|
|
64
62
|
}
|
|
65
63
|
}
|
|
66
64
|
|
|
65
|
+
/**
|
|
66
|
+
* Convert conversation messages to markdown format optimized for Claude Code
|
|
67
|
+
* @param {Array} messages - Parsed conversation messages
|
|
68
|
+
* @param {Object} conversationData - Conversation metadata
|
|
69
|
+
* @param {Object} stats - Export statistics
|
|
70
|
+
* @returns {string} Markdown formatted content
|
|
71
|
+
*/
|
|
72
|
+
convertToMarkdown(messages, conversationData, stats) {
|
|
73
|
+
const lines = [];
|
|
74
|
+
|
|
75
|
+
// Header for Claude Code
|
|
76
|
+
lines.push('# Previous Conversation Context\n');
|
|
77
|
+
lines.push('> **Note to Claude Code**: This file contains the complete conversation history from a previous session. Read and understand this context to continue helping the user with their task.\n');
|
|
78
|
+
lines.push(`**Project:** ${conversationData.project || 'Unknown'}`);
|
|
79
|
+
lines.push(`**Date:** ${new Date().toISOString().split('T')[0]}`);
|
|
80
|
+
lines.push(`**Messages in this export:** ${stats.messageCount}${stats.wasLimited ? ` (most recent from a total of ${stats.totalMessageCount})` : ''}`);
|
|
81
|
+
lines.push('');
|
|
82
|
+
lines.push('---');
|
|
83
|
+
lines.push('');
|
|
84
|
+
|
|
85
|
+
// Conversation
|
|
86
|
+
lines.push('## š¬ Conversation History\n');
|
|
87
|
+
|
|
88
|
+
messages.forEach((msg, index) => {
|
|
89
|
+
const role = msg.role === 'user' ? 'š¤ User' : 'š¤ Assistant';
|
|
90
|
+
const timestamp = new Date(msg.timestamp).toLocaleString();
|
|
91
|
+
|
|
92
|
+
lines.push(`### Message ${index + 1}: ${role}`);
|
|
93
|
+
lines.push(`*${timestamp}*\n`);
|
|
94
|
+
|
|
95
|
+
// Extract text content from message
|
|
96
|
+
if (Array.isArray(msg.content)) {
|
|
97
|
+
msg.content.forEach(block => {
|
|
98
|
+
if (block.type === 'text') {
|
|
99
|
+
lines.push(block.text);
|
|
100
|
+
} else if (block.type === 'tool_use') {
|
|
101
|
+
lines.push(`\`\`\`${block.name || 'tool'}`);
|
|
102
|
+
lines.push(JSON.stringify(block.input || {}, null, 2));
|
|
103
|
+
lines.push('```');
|
|
104
|
+
} else if (block.type === 'tool_result') {
|
|
105
|
+
lines.push('**Tool Result:**');
|
|
106
|
+
lines.push('```');
|
|
107
|
+
lines.push(typeof block.content === 'string' ? block.content : JSON.stringify(block.content, null, 2));
|
|
108
|
+
lines.push('```');
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
} else if (typeof msg.content === 'string') {
|
|
112
|
+
lines.push(msg.content);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
lines.push('');
|
|
116
|
+
lines.push('---');
|
|
117
|
+
lines.push('');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Footer
|
|
121
|
+
lines.push('\n---');
|
|
122
|
+
lines.push('');
|
|
123
|
+
lines.push('*Generated by Claude Code Templates - [aitmpl.com](https://aitmpl.com)*');
|
|
124
|
+
|
|
125
|
+
return lines.join('\n');
|
|
126
|
+
}
|
|
127
|
+
|
|
67
128
|
/**
|
|
68
129
|
* Export session data to standardized format
|
|
69
130
|
* @param {string} conversationId - Conversation ID
|
|
@@ -117,14 +178,14 @@ class SessionSharing {
|
|
|
117
178
|
exported_at: new Date().toISOString(),
|
|
118
179
|
conversation: {
|
|
119
180
|
id: conversationId,
|
|
120
|
-
project: conversationData.project || '
|
|
181
|
+
project: conversationData.project || 'shared-session',
|
|
121
182
|
created: conversationData.created,
|
|
122
183
|
lastModified: conversationData.lastModified,
|
|
123
184
|
messageCount: messages.length,
|
|
124
185
|
totalMessageCount: allMessages.length,
|
|
125
186
|
wasLimited: allMessages.length > messageLimit,
|
|
126
187
|
tokens: conversationData.tokens,
|
|
127
|
-
model: conversationData.modelInfo?.primaryModel || '
|
|
188
|
+
model: conversationData.modelInfo?.primaryModel || 'claude-sonnet-4-5-20250929'
|
|
128
189
|
},
|
|
129
190
|
messages: jsonlMessages,
|
|
130
191
|
metadata: {
|
|
@@ -223,8 +284,8 @@ class SessionSharing {
|
|
|
223
284
|
console.log(chalk.green(`\nā
Session installed successfully!`));
|
|
224
285
|
console.log(chalk.cyan(`š Location: ${installResult.sessionPath}`));
|
|
225
286
|
|
|
226
|
-
// Show resume command
|
|
227
|
-
const resumeCommand = `claude --resume ${installResult.
|
|
287
|
+
// Show resume command (only conversation ID needed)
|
|
288
|
+
const resumeCommand = `claude --resume ${installResult.conversationId}`;
|
|
228
289
|
console.log(chalk.yellow(`\nš” To continue this conversation, run:`));
|
|
229
290
|
console.log(chalk.white(`\n ${resumeCommand}\n`));
|
|
230
291
|
console.log(chalk.gray(` Or open Claude Code to see it in your sessions list`));
|