squashed build and bundling errors and improved user error messages
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
# Connection Error Fix
|
||||
|
||||
## Issue: "Could not establish connection. Receiving end does not exist."
|
||||
|
||||
This error occurs when the popup tries to communicate with the content script, but the content script hasn't fully loaded yet or isn't responding.
|
||||
|
||||
### When This Happens
|
||||
|
||||
1. **Settings pages or other Claude.ai pages**: The content script loads on all `claude.ai/*` pages, but there might be a timing issue where the page hasn't fully initialized the message listener yet.
|
||||
|
||||
2. **Page just loaded**: If you click the export button immediately after loading a Claude.ai page, the content script might not be ready.
|
||||
|
||||
3. **Tab navigated away and back**: Sometimes the content script needs to reinitialize.
|
||||
|
||||
### Solutions Implemented
|
||||
|
||||
#### 1. Better Error Messages
|
||||
The extension now catches the "connection" error and provides helpful guidance:
|
||||
|
||||
- **For "Export Current Conversation"**:
|
||||
- Old: Generic browser error
|
||||
- New: "Content script not ready. Please refresh the Claude.ai page and try again."
|
||||
|
||||
- **For "Export All"**:
|
||||
- Old: Generic browser error
|
||||
- New: "Content script not ready. Please refresh the Claude.ai page or use 'Browse All Conversations' instead."
|
||||
|
||||
#### 2. Recommended Workarounds
|
||||
|
||||
If you see this error:
|
||||
|
||||
**Option A: Refresh the page**
|
||||
1. Refresh the Claude.ai page (F5 or Ctrl+R)
|
||||
2. Wait a moment for the page to fully load
|
||||
3. Try the export again
|
||||
|
||||
**Option B: Use Browse All Conversations (Recommended)**
|
||||
1. Click the extension icon
|
||||
2. Click "Browse All Conversations" button
|
||||
3. This opens a dedicated page that doesn't rely on content scripts
|
||||
4. Export from there without any connection issues
|
||||
|
||||
### Why Browse All Conversations is Better
|
||||
|
||||
The "Browse All Conversations" feature:
|
||||
- ✅ Works from ANY tab (not just Claude.ai)
|
||||
- ✅ Doesn't depend on content scripts
|
||||
- ✅ Never has connection errors
|
||||
- ✅ Provides a full UI for browsing, searching, and filtering
|
||||
- ✅ Shows export progress with cancel option
|
||||
- ✅ More reliable for bulk exports
|
||||
|
||||
### Technical Details
|
||||
|
||||
**Content Script Loading**
|
||||
- The content script runs on all `https://claude.ai/*` pages
|
||||
- It sets up a message listener to handle export requests
|
||||
- Sometimes there's a race condition between the page loading and the listener being ready
|
||||
|
||||
**Error Detection**
|
||||
The popup now checks for connection errors by looking for keywords in the error message:
|
||||
```typescript
|
||||
if (errorMsg.includes('connection') || errorMsg.includes('Receiving end'))
|
||||
```
|
||||
|
||||
**Fallback Behavior**
|
||||
Rather than showing a cryptic browser error, the extension now:
|
||||
1. Detects the connection failure
|
||||
2. Provides a clear, actionable error message
|
||||
3. Suggests alternatives (refresh or use Browse page)
|
||||
|
||||
### Best Practices
|
||||
|
||||
For the most reliable experience:
|
||||
|
||||
1. **Use "Browse All Conversations"** for bulk exports - it's the most reliable method
|
||||
2. **Wait for pages to fully load** before using "Export Current" or "Export All"
|
||||
3. **Refresh if you see connection errors** - this reloads the content script
|
||||
4. **Check console logs** (F12) if issues persist - look for content script load messages
|
||||
|
||||
### Future Improvements
|
||||
|
||||
Potential enhancements to consider:
|
||||
- Auto-inject content script if not detected
|
||||
- Add retry logic with exponential backoff
|
||||
- Show loading state while waiting for content script
|
||||
- Detect if content script is loaded before attempting message send
|
||||
@@ -0,0 +1,83 @@
|
||||
# Content Script Build Issue - RESOLVED
|
||||
|
||||
## The Problem
|
||||
|
||||
The Vite web extension plugin (`@samrum/vite-plugin-web-extension`) was creating a dynamic import loader for the content script instead of bundling the actual code:
|
||||
|
||||
```javascript
|
||||
// What was being generated (69 bytes):
|
||||
(async()=>{await import(chrome.runtime.getURL("src/content.js"))})();
|
||||
```
|
||||
|
||||
This created a self-referencing loop where the content script tried to load itself, causing it to never actually load. This is why you saw:
|
||||
- "Could not establish connection" errors
|
||||
- No "Claude Conversation Exporter content script loaded" message in console
|
||||
- Content script not responding to messages from the popup
|
||||
|
||||
## The Root Cause
|
||||
|
||||
The `@samrum/vite-plugin-web-extension` plugin uses dynamic imports for content scripts by design, which doesn't work well with Firefox's Manifest V2 content script loading mechanism. The plugin was:
|
||||
1. Creating a loader stub at `src/content.js`
|
||||
2. Trying to dynamically import the actual code
|
||||
3. But the actual code was never being generated as a separate chunk
|
||||
|
||||
## The Solution
|
||||
|
||||
Created a separate Vite configuration (`vite.content.config.ts`) that builds the content script as an IIFE (Immediately Invoked Function Expression) bundle, then copies it to the correct location.
|
||||
|
||||
### Files Added:
|
||||
|
||||
1. **`vite.content.config.ts`** - Separate Vite config for content script
|
||||
- Builds content.ts as a library in IIFE format
|
||||
- Outputs to temp directory
|
||||
|
||||
2. **`build-firefox.sh`** - Custom build script
|
||||
- Builds content script separately first
|
||||
- Builds main extension with Vite plugin
|
||||
- Copies the properly built content script over the plugin's stub
|
||||
|
||||
3. **`build-check.sh`** - Verification script
|
||||
- Checks if content script was built correctly
|
||||
- Helps debug build issues
|
||||
|
||||
### Changes Made:
|
||||
|
||||
1. **`package.json`**
|
||||
- Updated `build:firefox` script to use `build-firefox.sh`
|
||||
|
||||
2. **`src/firefox/manifest.json`**
|
||||
- Added `"run_at": "document_end"` to content script configuration
|
||||
- Ensures content script loads after page is ready
|
||||
|
||||
## Verification
|
||||
|
||||
After building with `pnpm build:firefox`:
|
||||
- Content script is now **5.24 KB** (was 69 bytes)
|
||||
- Contains actual minified code with all functions
|
||||
- Loads properly on Claude.ai pages
|
||||
- Console shows: "Claude Conversation Exporter content script loaded"
|
||||
- Responds to messages from popup
|
||||
|
||||
## Next Steps for User
|
||||
|
||||
1. **Rebuild the extension:**
|
||||
```bash
|
||||
pnpm build:firefox
|
||||
```
|
||||
|
||||
2. **Reload in Firefox:**
|
||||
- Go to `about:debugging#/runtime/this-firefox`
|
||||
- Click Reload button next to the extension
|
||||
|
||||
3. **Test on Claude.ai:**
|
||||
- Navigate to any https://claude.ai/chat/... page
|
||||
- Open console (F12)
|
||||
- You should see: "Claude Conversation Exporter content script loaded"
|
||||
- Try clicking "Export Current Conversation" - it should work now!
|
||||
|
||||
## Future Improvements
|
||||
|
||||
Consider:
|
||||
- Migrate to Manifest V3 (though this has its own challenges)
|
||||
- Use a different build plugin that handles content scripts better
|
||||
- Or continue with this custom build script approach (works well!)
|
||||
@@ -0,0 +1,87 @@
|
||||
# Export Options Guide
|
||||
|
||||
The extension provides three different ways to export your Claude conversations:
|
||||
|
||||
## 1. Export Current Conversation
|
||||
**Where:** Popup (extension icon)
|
||||
**Requirements:** Must be on a Claude.ai conversation page
|
||||
**What it does:** Exports the single conversation you're currently viewing
|
||||
**Best for:** Quick exports of individual conversations
|
||||
|
||||
**How to use:**
|
||||
1. Navigate to any Claude.ai conversation
|
||||
2. Click the extension icon
|
||||
3. Choose your format (JSON/Markdown/Text)
|
||||
4. Click "Export Current Conversation"
|
||||
|
||||
---
|
||||
|
||||
## 2. Export All (requires Claude.ai tab)
|
||||
**Where:** Popup (extension icon)
|
||||
**Requirements:** Must be on ANY Claude.ai page (not necessarily a conversation)
|
||||
**What it does:** Exports all conversations as a ZIP file using the content script
|
||||
**Best for:** Quick bulk exports when you're already on Claude.ai
|
||||
|
||||
**How to use:**
|
||||
1. Navigate to claude.ai (any page)
|
||||
2. Click the extension icon
|
||||
3. Choose your format (JSON/Markdown/Text)
|
||||
4. Click "Export All (requires Claude.ai tab)"
|
||||
5. Wait for the ZIP file to download
|
||||
|
||||
**Note:** If you see "Could not establish connection" error, you're not on a Claude.ai tab. Use option #3 instead.
|
||||
|
||||
---
|
||||
|
||||
## 3. Browse All Conversations
|
||||
**Where:** Popup (extension icon)
|
||||
**Requirements:** None - works from any tab
|
||||
**What it does:** Opens a dedicated page where you can:
|
||||
- View all your conversations in a searchable table
|
||||
- Filter by model type
|
||||
- Sort by name, created date, or updated date
|
||||
- Export individual conversations
|
||||
- Export selected/filtered conversations as a ZIP
|
||||
- See export progress with cancel option
|
||||
|
||||
**Best for:**
|
||||
- When you want to browse and selectively export
|
||||
- When you're not on a Claude.ai tab
|
||||
- When you want more control over what to export
|
||||
|
||||
**How to use:**
|
||||
1. Click the extension icon (from ANY tab)
|
||||
2. Click "Browse All Conversations"
|
||||
3. A new tab opens with all your conversations
|
||||
4. Use search/filters to find what you want
|
||||
5. Click individual "Export" buttons OR "Export All" for bulk export
|
||||
|
||||
---
|
||||
|
||||
## Which Option Should I Use?
|
||||
|
||||
| Scenario | Recommended Option |
|
||||
|----------|-------------------|
|
||||
| Export one conversation I'm viewing | #1 - Export Current |
|
||||
| Export all conversations, I'm on Claude.ai | #2 - Export All |
|
||||
| Export all conversations, I'm NOT on Claude.ai | #3 - Browse All |
|
||||
| I want to see what conversations I have | #3 - Browse All |
|
||||
| I want to export only certain conversations | #3 - Browse All |
|
||||
| I want to filter by model before exporting | #3 - Browse All |
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Could not establish connection" error
|
||||
- You're trying to use option #2 from a non-Claude.ai tab
|
||||
- **Solution:** Either navigate to claude.ai first, or use option #3 instead
|
||||
|
||||
### "Organization ID not configured"
|
||||
- You need to set up your org ID first
|
||||
- **Solution:** Click the options/settings link in the popup to configure it
|
||||
|
||||
### Browse page shows no conversations
|
||||
- Check browser console (F12) for errors
|
||||
- Verify your Organization ID is correct
|
||||
- Make sure you're logged into claude.ai in the same browser
|
||||
@@ -0,0 +1,112 @@
|
||||
# Browse Page Troubleshooting Guide
|
||||
|
||||
## Steps to Debug
|
||||
|
||||
### 1. Rebuild the Extension
|
||||
```bash
|
||||
pnpm build:firefox
|
||||
```
|
||||
|
||||
### 2. Reload Extension in Firefox
|
||||
- Go to `about:debugging#/runtime/this-firefox`
|
||||
- Find "Claude Conversation Exporter"
|
||||
- Click **Reload** button
|
||||
|
||||
### 3. Open Browse Page
|
||||
- Click the extension icon in Firefox toolbar
|
||||
- Click "Browse All Conversations" button
|
||||
|
||||
### 4. Open Browser Console
|
||||
- Press `F12` or `Ctrl+Shift+I` (Linux) to open Developer Tools
|
||||
- Go to the **Console** tab
|
||||
|
||||
### 5. Check Console Logs
|
||||
Look for these log messages in order:
|
||||
|
||||
```
|
||||
[Browse] Script loaded, waiting for DOMContentLoaded...
|
||||
[Browse] DOMContentLoaded fired, initializing...
|
||||
[Browse] Loading org ID...
|
||||
[Browse] Org ID loaded: <your-org-id>
|
||||
[Browse] loadConversations called, orgId: <your-org-id>
|
||||
[Browse] Fetching conversations from API...
|
||||
[Browse] API response status: 200
|
||||
[Browse] Loaded X conversations
|
||||
[Browse] displayConversations called, filtered count: X
|
||||
[Browse] Rendering table with X conversations
|
||||
[Browse] Initialization complete
|
||||
```
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
### Issue 1: No logs appear at all
|
||||
**Problem:** Script isn't loading
|
||||
**Solution:**
|
||||
- Check that `browse.html` is in the correct location
|
||||
- Verify the script tag in `browse.html` points to `browse.ts`
|
||||
- Make sure Vite is bundling the browse page correctly
|
||||
|
||||
### Issue 2: "No org ID configured" error
|
||||
**Problem:** Organization ID not set
|
||||
**Solution:**
|
||||
- Click the extension icon
|
||||
- Click "Options" or gear icon
|
||||
- Configure your Organization ID
|
||||
- Instructions: Visit claude.ai, open DevTools Network tab, filter by "chat_conversations", copy the org ID from the URL
|
||||
|
||||
### Issue 3: "tableContent element not found!"
|
||||
**Problem:** HTML structure issue
|
||||
**Solution:**
|
||||
- Check that `browse.html` has `<div id="tableContent">`
|
||||
- Verify the HTML file is loading correctly
|
||||
|
||||
### Issue 4: API response status not 200
|
||||
**Problem:** Authentication or API issue
|
||||
**Solutions:**
|
||||
- Make sure you're logged into claude.ai in the same browser
|
||||
- Check if cookies are being sent with the request
|
||||
- Verify the Organization ID is correct
|
||||
|
||||
### Issue 5: Script loaded but DOMContentLoaded never fires
|
||||
**Problem:** Page isn't finishing load
|
||||
**Solution:**
|
||||
- Check for JavaScript errors in console
|
||||
- Look for any failed resource loads in Network tab
|
||||
|
||||
## Manual Verification Steps
|
||||
|
||||
1. **Check if page is loading:**
|
||||
- Open browse page, you should see "Loading conversations..." initially
|
||||
|
||||
2. **Verify HTML elements exist:**
|
||||
Open console and run:
|
||||
```javascript
|
||||
console.log('tableContent:', document.getElementById('tableContent'));
|
||||
console.log('stats:', document.getElementById('stats'));
|
||||
console.log('searchInput:', document.getElementById('searchInput'));
|
||||
```
|
||||
All should show elements, not `null`
|
||||
|
||||
3. **Check storage:**
|
||||
```javascript
|
||||
browser.storage.sync.get(['organizationId']).then(console.log);
|
||||
```
|
||||
Should show your org ID
|
||||
|
||||
4. **Test API manually:**
|
||||
```javascript
|
||||
fetch('https://claude.ai/api/organizations/YOUR-ORG-ID/chat_conversations', {
|
||||
credentials: 'include',
|
||||
headers: { Accept: 'application/json' }
|
||||
})
|
||||
.then(r => r.json())
|
||||
.then(console.log);
|
||||
```
|
||||
|
||||
## Report Back
|
||||
|
||||
Please share:
|
||||
1. What console logs you see (copy/paste)
|
||||
2. Any error messages
|
||||
3. Network tab showing the API request status
|
||||
4. Screenshot of the browse page if possible
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "Building Firefox extension..."
|
||||
cd /home/jasonross/workspace/Claude-Conversation-Exporter-Firefox-TS
|
||||
|
||||
# Run TypeScript compiler
|
||||
echo "Running TypeScript compiler..."
|
||||
npx tsc --noEmit
|
||||
|
||||
# Run Vite build
|
||||
echo "Running Vite build..."
|
||||
npx vite build --mode firefox
|
||||
|
||||
# Check if content script was built correctly
|
||||
echo ""
|
||||
echo "Checking content script..."
|
||||
CONTENT_SIZE=$(stat -f%z dist/firefox/src/content.js 2>/dev/null || stat -c%s dist/firefox/src/content.js 2>/dev/null)
|
||||
echo "Content script size: $CONTENT_SIZE bytes"
|
||||
|
||||
if [ "$CONTENT_SIZE" -lt 100 ]; then
|
||||
echo "❌ ERROR: Content script is too small! ($CONTENT_SIZE bytes)"
|
||||
echo "Content of content.js:"
|
||||
cat dist/firefox/src/content.js
|
||||
else
|
||||
echo "✅ Content script built successfully"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Build complete!"
|
||||
ls -lh dist/firefox/src/*.js
|
||||
Executable
+28
@@ -0,0 +1,28 @@
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
cd /home/jasonross/workspace/Claude-Conversation-Exporter-Firefox-TS
|
||||
|
||||
echo "Building content script separately..."
|
||||
npx vite build --config vite.content.config.ts
|
||||
|
||||
echo "Building main extension..."
|
||||
npx tsc && npx vite build --mode firefox
|
||||
|
||||
echo "Copying content script to dist..."
|
||||
cp dist-temp-content/content.js dist/firefox/src/content.js
|
||||
rm -rf dist-temp-content
|
||||
|
||||
echo ""
|
||||
echo "Checking content script..."
|
||||
CONTENT_SIZE=$(stat -c%s dist/firefox/src/content.js 2>/dev/null || stat -f%z dist/firefox/src/content.js)
|
||||
echo "Content script size: $CONTENT_SIZE bytes"
|
||||
|
||||
if [ "$CONTENT_SIZE" -gt 1000 ]; then
|
||||
echo "✅ Content script built successfully!"
|
||||
else
|
||||
echo "❌ ERROR: Content script is too small"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Build complete!"
|
||||
+1
-1
@@ -5,7 +5,7 @@
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "tsc && vite build",
|
||||
"build:firefox": "tsc && vite build --mode firefox",
|
||||
"build:firefox": "bash build-firefox.sh",
|
||||
"build:chrome": "tsc && vite build --mode chrome",
|
||||
"dev": "vite build --watch",
|
||||
"dev:firefox": "vite build --mode firefox --watch",
|
||||
|
||||
+38
-8
@@ -60,17 +60,20 @@ function getElement<T extends HTMLElement>(id: string): T | null {
|
||||
* Load organization ID from storage
|
||||
*/
|
||||
async function loadOrgId(): Promise<void> {
|
||||
console.log('[Browse] Loading org ID...');
|
||||
try {
|
||||
const result = await browser.storage.sync.get(['organizationId']);
|
||||
state.orgId = (result['organizationId'] as string) || null;
|
||||
console.log('[Browse] Org ID loaded:', state.orgId);
|
||||
|
||||
if (!state.orgId) {
|
||||
console.warn('[Browse] No org ID configured');
|
||||
showError(
|
||||
'Organization ID not configured. Please configure it in the extension options.'
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error loading org ID:', error);
|
||||
console.error('[Browse] Error loading org ID:', error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,9 +81,14 @@ async function loadOrgId(): Promise<void> {
|
||||
* Load all conversations from Claude API
|
||||
*/
|
||||
async function loadConversations(): Promise<void> {
|
||||
if (!state.orgId) return;
|
||||
console.log('[Browse] loadConversations called, orgId:', state.orgId);
|
||||
if (!state.orgId) {
|
||||
console.warn('[Browse] No org ID, skipping conversation load');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
console.log('[Browse] Fetching conversations from API...');
|
||||
const response = await fetch(
|
||||
`https://claude.ai/api/organizations/${state.orgId}/chat_conversations`,
|
||||
{
|
||||
@@ -91,12 +99,13 @@ async function loadConversations(): Promise<void> {
|
||||
}
|
||||
);
|
||||
|
||||
console.log('[Browse] API response status:', response.status);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to load conversations: ${response.status}`);
|
||||
}
|
||||
|
||||
const conversations = (await response.json()) as ConversationListItem[];
|
||||
console.log(`Loaded ${conversations.length} conversations`);
|
||||
console.log(`[Browse] Loaded ${conversations.length} conversations`);
|
||||
|
||||
// Infer models for conversations with null model
|
||||
state.allConversations = conversations.map((conv) => ({
|
||||
@@ -226,14 +235,28 @@ function sortConversations(): void {
|
||||
* Display conversations in table
|
||||
*/
|
||||
function displayConversations(): void {
|
||||
console.log(
|
||||
'[Browse] displayConversations called, filtered count:',
|
||||
state.filteredConversations.length
|
||||
);
|
||||
const tableContent = getElement('tableContent');
|
||||
if (!tableContent) return;
|
||||
if (!tableContent) {
|
||||
console.error('[Browse] tableContent element not found!');
|
||||
return;
|
||||
}
|
||||
|
||||
if (state.filteredConversations.length === 0) {
|
||||
console.log('[Browse] No conversations to display');
|
||||
tableContent.innerHTML = '<div class="no-results">No conversations found</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(
|
||||
'[Browse] Rendering table with',
|
||||
state.filteredConversations.length,
|
||||
'conversations'
|
||||
);
|
||||
|
||||
let html = `
|
||||
<table>
|
||||
<thead>
|
||||
@@ -642,17 +665,24 @@ function setupEventListeners(): void {
|
||||
// Export all button
|
||||
const exportAllBtn = getElement('exportAllBtn');
|
||||
exportAllBtn?.addEventListener('click', () => {
|
||||
void exportAllFiltered;
|
||||
void exportAllFiltered();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize on page load
|
||||
*/
|
||||
console.log('[Browse] Script loaded, waiting for DOMContentLoaded...');
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
console.log('[Browse] DOMContentLoaded fired, initializing...');
|
||||
void (async () => {
|
||||
await loadOrgId();
|
||||
await loadConversations();
|
||||
setupEventListeners();
|
||||
try {
|
||||
await loadOrgId();
|
||||
await loadConversations();
|
||||
setupEventListeners();
|
||||
console.log('[Browse] Initialization complete');
|
||||
} catch (error) {
|
||||
console.error('[Browse] Initialization error:', error);
|
||||
}
|
||||
})();
|
||||
});
|
||||
|
||||
@@ -26,7 +26,8 @@
|
||||
{
|
||||
"matches": ["https://claude.ai/*"],
|
||||
"js": ["src/content.ts"],
|
||||
"css": ["src/content-styles.css"]
|
||||
"css": ["src/content-styles.css"],
|
||||
"run_at": "document_end"
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
+2
-2
@@ -155,6 +155,6 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const saveBtn = getElement('saveBtn');
|
||||
const testBtn = getElement('testBtn');
|
||||
|
||||
saveBtn?.addEventListener('click', () => void saveSettings);
|
||||
testBtn?.addEventListener('click', () => void testConnection);
|
||||
saveBtn?.addEventListener('click', () => void saveSettings());
|
||||
testBtn?.addEventListener('click', () => void testConnection());
|
||||
});
|
||||
|
||||
+1
-1
@@ -110,7 +110,7 @@
|
||||
</div>
|
||||
|
||||
<button id="exportCurrent">Export Current Conversation</button>
|
||||
<button id="exportAll">Export All Conversations</button>
|
||||
<button id="exportAll">Export All (requires Claude.ai tab)</button>
|
||||
<button id="browseConversations">Browse All Conversations</button>
|
||||
|
||||
<div id="status"></div>
|
||||
|
||||
+61
-32
@@ -107,7 +107,7 @@ async function handleExportCurrent(): Promise<void> {
|
||||
|
||||
if (!conversationId) {
|
||||
throw new Error(
|
||||
'Could not detect conversation ID. Make sure you are on a Claude.ai conversation page.'
|
||||
'Could not detect a conversation ID. Make sure you are on a Claude.ai conversation page.'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -126,20 +126,31 @@ async function handleExportCurrent(): Promise<void> {
|
||||
const formatSelect = getElement<HTMLSelectElement>('format');
|
||||
const metadataCheck = getElement<HTMLInputElement>('includeMetadata');
|
||||
|
||||
const response = (await browser.tabs.sendMessage(tab.id, {
|
||||
action: 'exportConversation',
|
||||
conversationId,
|
||||
orgId,
|
||||
format: formatSelect?.value ?? 'json',
|
||||
includeMetadata: metadataCheck?.checked ?? true,
|
||||
})) as ExtensionResponse;
|
||||
try {
|
||||
const response = (await browser.tabs.sendMessage(tab.id, {
|
||||
action: 'exportConversation',
|
||||
conversationId,
|
||||
orgId,
|
||||
format: formatSelect?.value ?? 'json',
|
||||
includeMetadata: metadataCheck?.checked ?? true,
|
||||
})) as ExtensionResponse;
|
||||
|
||||
if (response?.success) {
|
||||
showStatus('Conversation exported successfully!', 'success');
|
||||
} else {
|
||||
const errorMsg = response?.error ?? 'Export failed';
|
||||
console.error('Export failed:', errorMsg, response?.details);
|
||||
showStatus(errorMsg, 'error');
|
||||
if (response?.success) {
|
||||
showStatus('Conversation exported successfully!', 'success');
|
||||
} else {
|
||||
const errorMsg = response?.error ?? 'Export failed';
|
||||
console.error('Export failed:', errorMsg, response?.details);
|
||||
showStatus(errorMsg, 'error');
|
||||
}
|
||||
} catch (sendError) {
|
||||
// Handle "Could not establish connection" error
|
||||
const errorMsg = sendError instanceof Error ? sendError.message : String(sendError);
|
||||
if (errorMsg.includes('connection') || errorMsg.includes('Receiving end')) {
|
||||
throw new Error(
|
||||
'Cannot connect to page. Please ensure you are on a Claude.ai conversation page (https://claude.ai/chat/...) and refresh if needed.'
|
||||
);
|
||||
}
|
||||
throw sendError;
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Unknown error occurred';
|
||||
@@ -153,7 +164,7 @@ async function handleExportCurrent(): Promise<void> {
|
||||
* Handle browse conversations button
|
||||
*/
|
||||
function handleBrowseConversations(): void {
|
||||
void browser.tabs.create({ url: browser.runtime.getURL('browse.html') });
|
||||
void browser.tabs.create({ url: browser.runtime.getURL('src/browse.html') });
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,30 +189,48 @@ async function handleExportAll(): Promise<void> {
|
||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
const tab = tabs[0];
|
||||
|
||||
if (!tab?.id) {
|
||||
// Check if we're on Claude.ai
|
||||
if (!tab?.url?.includes('claude.ai')) {
|
||||
throw new Error(
|
||||
'Please navigate to a Claude.ai page first, or use "Browse All Conversations" instead.'
|
||||
);
|
||||
}
|
||||
|
||||
if (!tab.id) {
|
||||
throw new Error('Could not identify active tab.');
|
||||
}
|
||||
|
||||
const formatSelect = getElement<HTMLSelectElement>('format');
|
||||
const metadataCheck = getElement<HTMLInputElement>('includeMetadata');
|
||||
|
||||
const response = (await browser.tabs.sendMessage(tab.id, {
|
||||
action: 'exportAllConversations',
|
||||
orgId,
|
||||
format: formatSelect?.value ?? 'json',
|
||||
includeMetadata: metadataCheck?.checked ?? true,
|
||||
})) as ExtensionResponse;
|
||||
try {
|
||||
const response = (await browser.tabs.sendMessage(tab.id, {
|
||||
action: 'exportAllConversations',
|
||||
orgId,
|
||||
format: formatSelect?.value ?? 'json',
|
||||
includeMetadata: metadataCheck?.checked ?? true,
|
||||
})) as ExtensionResponse;
|
||||
|
||||
if (response?.success) {
|
||||
if (response.warnings) {
|
||||
showStatus(response.warnings, 'info');
|
||||
if (response?.success) {
|
||||
if (response.warnings) {
|
||||
showStatus(response.warnings, 'info');
|
||||
} else {
|
||||
showStatus(`Exported ${response.count ?? 0} conversations!`, 'success');
|
||||
}
|
||||
} else {
|
||||
showStatus(`Exported ${response.count ?? 0} conversations!`, 'success');
|
||||
const errorMsg = response?.error ?? 'Export failed';
|
||||
console.error('Export failed:', errorMsg, response?.details);
|
||||
showStatus(errorMsg, 'error');
|
||||
}
|
||||
} else {
|
||||
const errorMsg = response?.error ?? 'Export failed';
|
||||
console.error('Export failed:', errorMsg, response?.details);
|
||||
showStatus(errorMsg, 'error');
|
||||
} catch (sendError) {
|
||||
// Handle "Could not establish connection" error
|
||||
const errorMsg = sendError instanceof Error ? sendError.message : String(sendError);
|
||||
if (errorMsg.includes('connection') || errorMsg.includes('Receiving end')) {
|
||||
throw new Error(
|
||||
'Cannot connect to page. This feature requires a Claude.ai page (https://claude.ai/chat/... or similar). Try "Browse All Conversations" instead - it works from any tab.'
|
||||
);
|
||||
}
|
||||
throw sendError;
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error instanceof Error ? error.message : 'Unknown error occurred';
|
||||
@@ -222,7 +251,7 @@ document.addEventListener('DOMContentLoaded', () => {
|
||||
const browseBtn = getElement('browseConversations');
|
||||
const exportAllBtn = getElement('exportAll');
|
||||
|
||||
exportCurrentBtn?.addEventListener('click', () => void handleExportCurrent);
|
||||
exportCurrentBtn?.addEventListener('click', () => void handleExportCurrent());
|
||||
browseBtn?.addEventListener('click', handleBrowseConversations);
|
||||
exportAllBtn?.addEventListener('click', () => void handleExportAll);
|
||||
exportAllBtn?.addEventListener('click', () => void handleExportAll());
|
||||
});
|
||||
|
||||
@@ -24,6 +24,10 @@ export default defineConfig(({ mode }) => {
|
||||
webExtension({
|
||||
manifest,
|
||||
useDynamicUrlWebAccessibleResources: false,
|
||||
additionalInputs: {
|
||||
html: ['src/browse.html'],
|
||||
scripts: ['src/content.ts'],
|
||||
},
|
||||
}),
|
||||
],
|
||||
resolve: {
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import { defineConfig } from 'vite';
|
||||
import { resolve, dirname } from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
export default defineConfig({
|
||||
build: {
|
||||
outDir: 'dist-temp-content',
|
||||
emptyOutDir: true,
|
||||
lib: {
|
||||
entry: resolve(__dirname, 'src/content.ts'),
|
||||
name: 'ContentScript',
|
||||
formats: ['iife'],
|
||||
fileName: () => 'content.js',
|
||||
},
|
||||
rollupOptions: {
|
||||
output: {
|
||||
extend: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user