@rashidazarang/airtable-mcp 2.1.0 → 2.1.1
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 +10 -1
- package/.github/ISSUE_TEMPLATE/bug-report.yml +0 -173
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
- package/.github/ISSUE_TEMPLATE/custom.md +0 -10
- package/.github/ISSUE_TEMPLATE/feature-request.yml +0 -209
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
- package/.github/ISSUE_TEMPLATE/security-report.yml +0 -216
- package/.github/pull_request_template.md +0 -245
- package/.github/workflows/ci-cd.yml +0 -408
- package/.github/workflows/security-audit.yml +0 -316
- package/API_DOCUMENTATION.md +0 -897
- package/CAPABILITY_REPORT.md +0 -118
- package/CLAUDE_INTEGRATION.md +0 -96
- package/CODE_OF_CONDUCT.md +0 -181
- package/CONTRIBUTING.md +0 -81
- package/DEVELOPMENT.md +0 -190
- package/Dockerfile +0 -39
- package/Dockerfile.node +0 -20
- package/Dockerfile.production +0 -127
- package/IMPROVEMENT_PROPOSAL.md +0 -371
- package/INSTALLATION.md +0 -183
- package/ISSUE_RESPONSES.md +0 -171
- package/MCP_REVIEW_SUMMARY.md +0 -142
- package/QUICK_START.md +0 -60
- package/RELEASE_NOTES_v1.2.0.md +0 -50
- package/RELEASE_NOTES_v1.2.1.md +0 -40
- package/RELEASE_NOTES_v1.2.2.md +0 -48
- package/RELEASE_NOTES_v1.2.3.md +0 -105
- package/RELEASE_NOTES_v1.2.4.md +0 -60
- package/RELEASE_NOTES_v1.4.0.md +0 -104
- package/RELEASE_NOTES_v1.5.0.md +0 -185
- package/RELEASE_NOTES_v1.6.0.md +0 -248
- package/SECURITY_NOTICE.md +0 -40
- package/airtable-clipper/CHANGELOG.md +0 -198
- package/airtable-clipper/CHROME_STORE_SUBMISSION.md +0 -343
- package/airtable-clipper/LAUNCH_STRATEGY.md +0 -495
- package/airtable-clipper/LICENSE +0 -21
- package/airtable-clipper/OAUTH_SETUP.md +0 -51
- package/airtable-clipper/PRIVACY_POLICY.md +0 -187
- package/airtable-clipper/README.md +0 -575
- package/airtable-clipper/SUBMIT_TO_CHROME_STORE.md +0 -273
- package/airtable-clipper/build.sh +0 -85
- package/airtable-clipper/docs/QUICK_START.md +0 -99
- package/airtable-clipper/docs/SETUP.md +0 -291
- package/airtable-clipper/extension/background.js +0 -337
- package/airtable-clipper/extension/base-setup.html +0 -324
- package/airtable-clipper/extension/base-setup.js +0 -471
- package/airtable-clipper/extension/content.js +0 -771
- package/airtable-clipper/extension/icons/README.md +0 -69
- package/airtable-clipper/extension/icons/icon-16.png +0 -3
- package/airtable-clipper/extension/manifest.json +0 -73
- package/airtable-clipper/extension/popup.html +0 -144
- package/airtable-clipper/extension/popup.js +0 -475
- package/airtable-clipper/extension/styles/content.css +0 -229
- package/airtable-clipper/extension/styles/popup.css +0 -477
- package/airtable-clipper/privacy-policy.md +0 -63
- package/airtable-clipper/releases/v1.0.0/background.js +0 -337
- package/airtable-clipper/releases/v1.0.0/base-setup.html +0 -324
- package/airtable-clipper/releases/v1.0.0/base-setup.js +0 -471
- package/airtable-clipper/releases/v1.0.0/content.js +0 -771
- package/airtable-clipper/releases/v1.0.0/icons/README.md +0 -69
- package/airtable-clipper/releases/v1.0.0/icons/icon-128.png +0 -2
- package/airtable-clipper/releases/v1.0.0/icons/icon-16.png +0 -3
- package/airtable-clipper/releases/v1.0.0/icons/icon-32.png +0 -2
- package/airtable-clipper/releases/v1.0.0/icons/icon-48.png +0 -2
- package/airtable-clipper/releases/v1.0.0/manifest.json +0 -73
- package/airtable-clipper/releases/v1.0.0/popup.html +0 -144
- package/airtable-clipper/releases/v1.0.0/popup.js +0 -475
- package/airtable-clipper/releases/v1.0.0/sidepanel.html +0 -25
- package/airtable-clipper/releases/v1.0.0/styles/content.css +0 -229
- package/airtable-clipper/releases/v1.0.0/styles/popup.css +0 -477
- package/airtable-clipper/releases/v1.0.1/background.js +0 -337
- package/airtable-clipper/releases/v1.0.1/base-setup.html +0 -324
- package/airtable-clipper/releases/v1.0.1/base-setup.js +0 -471
- package/airtable-clipper/releases/v1.0.1/content.js +0 -771
- package/airtable-clipper/releases/v1.0.1/icons/README.md +0 -69
- package/airtable-clipper/releases/v1.0.1/icons/icon-128.png +0 -2
- package/airtable-clipper/releases/v1.0.1/icons/icon-16.png +0 -3
- package/airtable-clipper/releases/v1.0.1/icons/icon-32.png +0 -2
- package/airtable-clipper/releases/v1.0.1/icons/icon-48.png +0 -2
- package/airtable-clipper/releases/v1.0.1/manifest.json +0 -70
- package/airtable-clipper/releases/v1.0.1/popup.html +0 -157
- package/airtable-clipper/releases/v1.0.1/popup.js +0 -562
- package/airtable-clipper/releases/v1.0.1/sidepanel.html +0 -25
- package/airtable-clipper/releases/v1.0.1/styles/content.css +0 -229
- package/airtable-clipper/releases/v1.0.1/styles/popup.css +0 -647
- package/airtable-clipper/releases/v1.0.2/background.js +0 -337
- package/airtable-clipper/releases/v1.0.2/base-setup.html +0 -324
- package/airtable-clipper/releases/v1.0.2/base-setup.js +0 -471
- package/airtable-clipper/releases/v1.0.2/content.js +0 -771
- package/airtable-clipper/releases/v1.0.2/icons/README.md +0 -69
- package/airtable-clipper/releases/v1.0.2/icons/icon-128.png +0 -2
- package/airtable-clipper/releases/v1.0.2/icons/icon-16.png +0 -3
- package/airtable-clipper/releases/v1.0.2/icons/icon-32.png +0 -2
- package/airtable-clipper/releases/v1.0.2/icons/icon-48.png +0 -2
- package/airtable-clipper/releases/v1.0.2/manifest.json +0 -62
- package/airtable-clipper/releases/v1.0.2/popup.html +0 -157
- package/airtable-clipper/releases/v1.0.2/popup.js +0 -567
- package/airtable-clipper/releases/v1.0.2/sidepanel.html +0 -25
- package/airtable-clipper/releases/v1.0.2/styles/content.css +0 -229
- package/airtable-clipper/releases/v1.0.2/styles/popup.css +0 -647
- package/airtable-clipper/terms-of-service.md +0 -124
- package/airtable-clipper/test-credentials.md +0 -61
- package/airtable-clipper/test-extension/background.js +0 -337
- package/airtable-clipper/test-extension/base-setup.html +0 -324
- package/airtable-clipper/test-extension/base-setup.js +0 -471
- package/airtable-clipper/test-extension/content.js +0 -873
- package/airtable-clipper/test-extension/icons/README.md +0 -69
- package/airtable-clipper/test-extension/icons/icon-128.png +0 -2
- package/airtable-clipper/test-extension/icons/icon-16.png +0 -3
- package/airtable-clipper/test-extension/icons/icon-32.png +0 -2
- package/airtable-clipper/test-extension/icons/icon-48.png +0 -2
- package/airtable-clipper/test-extension/manifest.json +0 -72
- package/airtable-clipper/test-extension/popup.html +0 -274
- package/airtable-clipper/test-extension/popup.js +0 -729
- package/airtable-clipper/test-extension/sidepanel.html +0 -25
- package/airtable-clipper/test-extension/styles/content.css +0 -229
- package/airtable-clipper/test-extension/styles/popup.css +0 -794
- package/airtable_mcp/__init__.py +0 -5
- package/airtable_mcp/src/server.py +0 -329
- package/airtable_mcp_v2.js +0 -1505
- package/airtable_mcp_v2_oauth.js +0 -1048
- package/airtable_mcp_v3_advanced.js +0 -1161
- package/cleanup.sh +0 -71
- package/docker-compose.production.yml +0 -366
- package/helm/airtable-mcp/Chart.yaml +0 -122
- package/helm/airtable-mcp/values.yaml +0 -538
- package/index.js +0 -179
- package/inspector.py +0 -148
- package/inspector_server.py +0 -337
- package/k8s/deployment.yaml +0 -402
- package/k8s/namespace.yaml +0 -108
- package/k8s/service.yaml +0 -194
- package/monitoring/alerts.yml +0 -289
- package/monitoring/prometheus.yml +0 -224
- package/publish-steps.txt +0 -27
- package/quick_test.sh +0 -30
- package/requirements.txt +0 -10
- package/setup.py +0 -29
- package/simple_airtable_server.py +0 -151
- package/smithery.yaml +0 -45
- package/test_all_features.sh +0 -146
- package/test_all_operations.sh +0 -120
- package/test_client.py +0 -70
- package/test_enhanced_features.js +0 -389
- package/test_mcp_comprehensive.js +0 -163
- package/test_mock_server.js +0 -180
- package/test_v1.4.0_final.sh +0 -131
- package/test_v1.5.0_comprehensive.sh +0 -96
- package/test_v1.5.0_final.sh +0 -224
- package/test_v1.6.0_comprehensive.sh +0 -187
- package/test_webhooks.sh +0 -105
|
@@ -1,475 +0,0 @@
|
|
|
1
|
-
// Airtable Clipper Popup Script
|
|
2
|
-
import { AirtableClient, AirtableError } from './lib/airtable-client.js';
|
|
3
|
-
|
|
4
|
-
class PopupController {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.client = null;
|
|
7
|
-
this.currentTab = null;
|
|
8
|
-
this.settings = {};
|
|
9
|
-
this.init();
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
async init() {
|
|
13
|
-
// Get current tab
|
|
14
|
-
const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
|
|
15
|
-
this.currentTab = tabs[0];
|
|
16
|
-
|
|
17
|
-
// Load settings and initialize UI
|
|
18
|
-
await this.loadSettings();
|
|
19
|
-
this.initializeUI();
|
|
20
|
-
this.bindEvents();
|
|
21
|
-
|
|
22
|
-
// Check connection status
|
|
23
|
-
await this.checkConnection();
|
|
24
|
-
|
|
25
|
-
// Update UI based on current page
|
|
26
|
-
this.updateUIForCurrentPage();
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
async loadSettings() {
|
|
30
|
-
try {
|
|
31
|
-
const response = await chrome.runtime.sendMessage({
|
|
32
|
-
action: 'getSettings'
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
if (response.success) {
|
|
36
|
-
this.settings = response.settings;
|
|
37
|
-
this.populateSettingsUI();
|
|
38
|
-
|
|
39
|
-
if (this.settings.airtableToken && this.settings.baseId) {
|
|
40
|
-
this.client = new AirtableClient(this.settings.airtableToken, this.settings.baseId);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
} catch (error) {
|
|
44
|
-
console.error('Failed to load settings:', error);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
populateSettingsUI() {
|
|
49
|
-
// Populate configuration form
|
|
50
|
-
const tokenInput = document.getElementById('airtableToken');
|
|
51
|
-
const baseIdInput = document.getElementById('baseId');
|
|
52
|
-
|
|
53
|
-
if (tokenInput) tokenInput.value = this.settings.airtableToken || '';
|
|
54
|
-
if (baseIdInput) baseIdInput.value = this.settings.baseId || '';
|
|
55
|
-
|
|
56
|
-
// Populate settings
|
|
57
|
-
const defaultTableSelect = document.getElementById('defaultTable');
|
|
58
|
-
const linkedinTableSelect = document.getElementById('linkedinTable');
|
|
59
|
-
const autoSaveCheckbox = document.getElementById('autoSave');
|
|
60
|
-
const notificationsCheckbox = document.getElementById('notifications');
|
|
61
|
-
|
|
62
|
-
if (defaultTableSelect) defaultTableSelect.value = this.settings.defaultTable || 'Clips';
|
|
63
|
-
if (linkedinTableSelect) linkedinTableSelect.value = this.settings.linkedinTable || 'Contacts';
|
|
64
|
-
if (autoSaveCheckbox) autoSaveCheckbox.checked = this.settings.autoSave || false;
|
|
65
|
-
if (notificationsCheckbox) notificationsCheckbox.checked = this.settings.notifications !== false;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
initializeUI() {
|
|
69
|
-
const isConnected = this.client !== null;
|
|
70
|
-
|
|
71
|
-
// Show/hide sections based on connection status
|
|
72
|
-
document.getElementById('configuration').style.display = isConnected ? 'none' : 'block';
|
|
73
|
-
document.getElementById('quickActions').style.display = isConnected ? 'block' : 'none';
|
|
74
|
-
document.getElementById('settings').style.display = 'none'; // Hidden by default
|
|
75
|
-
|
|
76
|
-
// Update connection status
|
|
77
|
-
this.updateConnectionStatus(isConnected);
|
|
78
|
-
|
|
79
|
-
// Load usage stats if connected
|
|
80
|
-
if (isConnected) {
|
|
81
|
-
this.loadUsageStats();
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
bindEvents() {
|
|
86
|
-
// Configuration events
|
|
87
|
-
document.getElementById('connectBtn')?.addEventListener('click', () => this.handleConnect());
|
|
88
|
-
|
|
89
|
-
// Quick action events
|
|
90
|
-
document.getElementById('quickSaveBtn')?.addEventListener('click', () => this.handleQuickSave());
|
|
91
|
-
document.getElementById('linkedinSaveBtn')?.addEventListener('click', () => this.handleLinkedInSave());
|
|
92
|
-
document.getElementById('bulkModeBtn')?.addEventListener('click', () => this.handleBulkMode());
|
|
93
|
-
|
|
94
|
-
// Footer events
|
|
95
|
-
document.getElementById('settingsToggle')?.addEventListener('click', () => this.toggleSettings());
|
|
96
|
-
document.getElementById('helpBtn')?.addEventListener('click', () => this.openHelp());
|
|
97
|
-
document.getElementById('sidePanelBtn')?.addEventListener('click', () => this.openSidePanel());
|
|
98
|
-
|
|
99
|
-
// Setup database button
|
|
100
|
-
document.getElementById('setupDatabaseBtn')?.addEventListener('click', () => this.openDatabaseSetup());
|
|
101
|
-
|
|
102
|
-
// Settings events
|
|
103
|
-
document.getElementById('autoSave')?.addEventListener('change', () => this.saveSettings());
|
|
104
|
-
document.getElementById('notifications')?.addEventListener('change', () => this.saveSettings());
|
|
105
|
-
document.getElementById('defaultTable')?.addEventListener('change', () => this.saveSettings());
|
|
106
|
-
document.getElementById('linkedinTable')?.addEventListener('change', () => this.saveSettings());
|
|
107
|
-
|
|
108
|
-
// Status message close
|
|
109
|
-
document.getElementById('statusClose')?.addEventListener('click', () => this.hideStatusMessage());
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async handleConnect() {
|
|
113
|
-
const connectBtn = document.getElementById('connectBtn');
|
|
114
|
-
const connectText = document.getElementById('connectText');
|
|
115
|
-
const connectLoading = document.getElementById('connectLoading');
|
|
116
|
-
|
|
117
|
-
const token = document.getElementById('airtableToken').value.trim();
|
|
118
|
-
const baseId = document.getElementById('baseId').value.trim();
|
|
119
|
-
|
|
120
|
-
if (!token || !baseId) {
|
|
121
|
-
this.showStatusMessage('Please enter both token and base ID', 'error');
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
// Show loading state
|
|
126
|
-
connectBtn.disabled = true;
|
|
127
|
-
connectText.textContent = 'Connecting...';
|
|
128
|
-
connectLoading.style.display = 'inline';
|
|
129
|
-
|
|
130
|
-
try {
|
|
131
|
-
// Test connection
|
|
132
|
-
const testClient = new AirtableClient(token, baseId);
|
|
133
|
-
const result = await testClient.testConnection();
|
|
134
|
-
|
|
135
|
-
if (result.success) {
|
|
136
|
-
// Save settings
|
|
137
|
-
await this.saveSettings({ airtableToken: token, baseId: baseId });
|
|
138
|
-
|
|
139
|
-
// Update client
|
|
140
|
-
this.client = testClient;
|
|
141
|
-
|
|
142
|
-
// Load table options
|
|
143
|
-
await this.loadTableOptions();
|
|
144
|
-
|
|
145
|
-
// Update UI
|
|
146
|
-
this.initializeUI();
|
|
147
|
-
this.showStatusMessage('Connected successfully!', 'success');
|
|
148
|
-
} else {
|
|
149
|
-
throw new Error(result.message);
|
|
150
|
-
}
|
|
151
|
-
} catch (error) {
|
|
152
|
-
console.error('Connection failed:', error);
|
|
153
|
-
this.showStatusMessage(`Connection failed: ${error.message}`, 'error');
|
|
154
|
-
} finally {
|
|
155
|
-
// Reset button state
|
|
156
|
-
connectBtn.disabled = false;
|
|
157
|
-
connectText.textContent = 'Connect to Airtable';
|
|
158
|
-
connectLoading.style.display = 'none';
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
async handleQuickSave() {
|
|
163
|
-
if (!this.client) return;
|
|
164
|
-
|
|
165
|
-
this.showLoading('Saving to Airtable...');
|
|
166
|
-
|
|
167
|
-
try {
|
|
168
|
-
// Send message to content script to extract page data
|
|
169
|
-
const response = await chrome.tabs.sendMessage(this.currentTab.id, {
|
|
170
|
-
action: 'extractPageData',
|
|
171
|
-
type: 'general'
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
if (response && response.success) {
|
|
175
|
-
// Save to Airtable via background script
|
|
176
|
-
const saveResponse = await chrome.runtime.sendMessage({
|
|
177
|
-
action: 'saveToAirtable',
|
|
178
|
-
data: {
|
|
179
|
-
type: 'general',
|
|
180
|
-
title: response.data.title,
|
|
181
|
-
url: response.data.url,
|
|
182
|
-
content: response.data.content,
|
|
183
|
-
timestamp: new Date().toISOString()
|
|
184
|
-
}
|
|
185
|
-
});
|
|
186
|
-
|
|
187
|
-
if (saveResponse.success) {
|
|
188
|
-
this.showStatusMessage('Saved to Airtable!', 'success');
|
|
189
|
-
this.updateUsageStats();
|
|
190
|
-
} else {
|
|
191
|
-
throw new Error(saveResponse.error);
|
|
192
|
-
}
|
|
193
|
-
} else {
|
|
194
|
-
// Fallback: save basic page info
|
|
195
|
-
await this.saveBasicPageInfo();
|
|
196
|
-
}
|
|
197
|
-
} catch (error) {
|
|
198
|
-
console.error('Quick save failed:', error);
|
|
199
|
-
this.showStatusMessage(`Save failed: ${error.message}`, 'error');
|
|
200
|
-
} finally {
|
|
201
|
-
this.hideLoading();
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
async handleLinkedInSave() {
|
|
206
|
-
if (!this.client) return;
|
|
207
|
-
|
|
208
|
-
this.showLoading('Extracting LinkedIn profile...');
|
|
209
|
-
|
|
210
|
-
try {
|
|
211
|
-
const response = await chrome.tabs.sendMessage(this.currentTab.id, {
|
|
212
|
-
action: 'extractLinkedInProfile'
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
if (response && response.success) {
|
|
216
|
-
const saveResponse = await chrome.runtime.sendMessage({
|
|
217
|
-
action: 'saveToAirtable',
|
|
218
|
-
data: {
|
|
219
|
-
type: 'linkedin',
|
|
220
|
-
...response.data,
|
|
221
|
-
timestamp: new Date().toISOString()
|
|
222
|
-
}
|
|
223
|
-
});
|
|
224
|
-
|
|
225
|
-
if (saveResponse.success) {
|
|
226
|
-
this.showStatusMessage('LinkedIn profile saved!', 'success');
|
|
227
|
-
this.updateUsageStats();
|
|
228
|
-
} else {
|
|
229
|
-
throw new Error(saveResponse.error);
|
|
230
|
-
}
|
|
231
|
-
} else {
|
|
232
|
-
throw new Error('Failed to extract LinkedIn profile');
|
|
233
|
-
}
|
|
234
|
-
} catch (error) {
|
|
235
|
-
console.error('LinkedIn save failed:', error);
|
|
236
|
-
this.showStatusMessage(`LinkedIn save failed: ${error.message}`, 'error');
|
|
237
|
-
} finally {
|
|
238
|
-
this.hideLoading();
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
async handleBulkMode() {
|
|
243
|
-
try {
|
|
244
|
-
// Send message to content script to enter bulk mode
|
|
245
|
-
await chrome.tabs.sendMessage(this.currentTab.id, {
|
|
246
|
-
action: 'enterBulkMode'
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
// Close popup
|
|
250
|
-
window.close();
|
|
251
|
-
} catch (error) {
|
|
252
|
-
console.error('Bulk mode failed:', error);
|
|
253
|
-
this.showStatusMessage('Bulk mode not available on this page', 'error');
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
async saveBasicPageInfo() {
|
|
258
|
-
const saveResponse = await chrome.runtime.sendMessage({
|
|
259
|
-
action: 'saveToAirtable',
|
|
260
|
-
data: {
|
|
261
|
-
type: 'general',
|
|
262
|
-
title: this.currentTab.title,
|
|
263
|
-
url: this.currentTab.url,
|
|
264
|
-
timestamp: new Date().toISOString()
|
|
265
|
-
}
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
if (saveResponse.success) {
|
|
269
|
-
this.showStatusMessage('Page saved to Airtable!', 'success');
|
|
270
|
-
this.updateUsageStats();
|
|
271
|
-
} else {
|
|
272
|
-
throw new Error(saveResponse.error);
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
async loadTableOptions() {
|
|
277
|
-
if (!this.client) return;
|
|
278
|
-
|
|
279
|
-
try {
|
|
280
|
-
const tables = await this.client.listTables();
|
|
281
|
-
|
|
282
|
-
const defaultTableSelect = document.getElementById('defaultTable');
|
|
283
|
-
const linkedinTableSelect = document.getElementById('linkedinTable');
|
|
284
|
-
|
|
285
|
-
// Clear existing options
|
|
286
|
-
defaultTableSelect.innerHTML = '';
|
|
287
|
-
linkedinTableSelect.innerHTML = '';
|
|
288
|
-
|
|
289
|
-
// Add table options
|
|
290
|
-
tables.forEach(table => {
|
|
291
|
-
const option1 = new Option(table.name, table.name);
|
|
292
|
-
const option2 = new Option(table.name, table.name);
|
|
293
|
-
|
|
294
|
-
defaultTableSelect.add(option1);
|
|
295
|
-
linkedinTableSelect.add(option2);
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
// Set current values
|
|
299
|
-
defaultTableSelect.value = this.settings.defaultTable || (tables.length > 0 ? tables[0].name : '');
|
|
300
|
-
linkedinTableSelect.value = this.settings.linkedinTable || 'Contacts';
|
|
301
|
-
|
|
302
|
-
} catch (error) {
|
|
303
|
-
console.error('Failed to load tables:', error);
|
|
304
|
-
}
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
async checkConnection() {
|
|
308
|
-
if (!this.client) return false;
|
|
309
|
-
|
|
310
|
-
try {
|
|
311
|
-
const result = await this.client.testConnection();
|
|
312
|
-
this.updateConnectionStatus(result.success);
|
|
313
|
-
return result.success;
|
|
314
|
-
} catch (error) {
|
|
315
|
-
this.updateConnectionStatus(false);
|
|
316
|
-
return false;
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
updateConnectionStatus(isConnected) {
|
|
321
|
-
const statusDot = document.querySelector('.status-dot');
|
|
322
|
-
const statusText = document.querySelector('.status-text');
|
|
323
|
-
|
|
324
|
-
if (isConnected) {
|
|
325
|
-
statusDot.className = 'status-dot connected';
|
|
326
|
-
statusText.textContent = 'Connected';
|
|
327
|
-
} else {
|
|
328
|
-
statusDot.className = 'status-dot disconnected';
|
|
329
|
-
statusText.textContent = 'Not Connected';
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
updateUIForCurrentPage() {
|
|
334
|
-
if (!this.currentTab) return;
|
|
335
|
-
|
|
336
|
-
const url = this.currentTab.url;
|
|
337
|
-
const quickSaveBtn = document.getElementById('quickSaveBtn');
|
|
338
|
-
const quickSaveSubtext = document.getElementById('quickSaveSubtext');
|
|
339
|
-
const linkedinSaveBtn = document.getElementById('linkedinSaveBtn');
|
|
340
|
-
|
|
341
|
-
// Update quick save button text based on current page
|
|
342
|
-
if (url.includes('linkedin.com/in/')) {
|
|
343
|
-
quickSaveSubtext.textContent = 'Save LinkedIn profile';
|
|
344
|
-
linkedinSaveBtn.style.display = 'block';
|
|
345
|
-
} else if (url.includes('github.com')) {
|
|
346
|
-
quickSaveSubtext.textContent = 'Save GitHub repository';
|
|
347
|
-
} else if (url.includes('twitter.com') || url.includes('x.com')) {
|
|
348
|
-
quickSaveSubtext.textContent = 'Save tweet';
|
|
349
|
-
} else {
|
|
350
|
-
quickSaveSubtext.textContent = 'Save this page';
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
// Hide LinkedIn button if not on LinkedIn
|
|
354
|
-
if (!url.includes('linkedin.com')) {
|
|
355
|
-
linkedinSaveBtn.style.display = 'none';
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
|
|
359
|
-
async saveSettings(newSettings = null) {
|
|
360
|
-
try {
|
|
361
|
-
const settingsToSave = newSettings || {
|
|
362
|
-
defaultTable: document.getElementById('defaultTable')?.value || this.settings.defaultTable,
|
|
363
|
-
linkedinTable: document.getElementById('linkedinTable')?.value || this.settings.linkedinTable,
|
|
364
|
-
autoSave: document.getElementById('autoSave')?.checked || this.settings.autoSave,
|
|
365
|
-
notifications: document.getElementById('notifications')?.checked !== false,
|
|
366
|
-
...this.settings
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
if (newSettings) {
|
|
370
|
-
Object.assign(settingsToSave, newSettings);
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
const response = await chrome.runtime.sendMessage({
|
|
374
|
-
action: 'saveSettings',
|
|
375
|
-
settings: settingsToSave
|
|
376
|
-
});
|
|
377
|
-
|
|
378
|
-
if (response.success) {
|
|
379
|
-
this.settings = settingsToSave;
|
|
380
|
-
}
|
|
381
|
-
} catch (error) {
|
|
382
|
-
console.error('Failed to save settings:', error);
|
|
383
|
-
}
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
async loadUsageStats() {
|
|
387
|
-
try {
|
|
388
|
-
const result = await chrome.storage.local.get(['usageStats']);
|
|
389
|
-
const stats = result.usageStats || { weeklyClips: 0, totalClips: 0 };
|
|
390
|
-
|
|
391
|
-
document.getElementById('weeklyClips').textContent = stats.weeklyClips;
|
|
392
|
-
document.getElementById('usageInfo').style.display = 'block';
|
|
393
|
-
} catch (error) {
|
|
394
|
-
console.error('Failed to load usage stats:', error);
|
|
395
|
-
}
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
async updateUsageStats() {
|
|
399
|
-
// Reload usage stats after saving
|
|
400
|
-
setTimeout(() => this.loadUsageStats(), 500);
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
toggleSettings() {
|
|
404
|
-
const settings = document.getElementById('settings');
|
|
405
|
-
const isVisible = settings.style.display !== 'none';
|
|
406
|
-
|
|
407
|
-
settings.style.display = isVisible ? 'none' : 'block';
|
|
408
|
-
|
|
409
|
-
const toggleBtn = document.getElementById('settingsToggle');
|
|
410
|
-
toggleBtn.textContent = isVisible ? 'Settings' : 'Hide Settings';
|
|
411
|
-
}
|
|
412
|
-
|
|
413
|
-
openHelp() {
|
|
414
|
-
chrome.tabs.create({
|
|
415
|
-
url: 'https://github.com/rashidazarang/airtable-clipper#readme'
|
|
416
|
-
});
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
async openSidePanel() {
|
|
420
|
-
try {
|
|
421
|
-
await chrome.runtime.sendMessage({
|
|
422
|
-
action: 'openSidePanel'
|
|
423
|
-
});
|
|
424
|
-
window.close();
|
|
425
|
-
} catch (error) {
|
|
426
|
-
console.error('Failed to open side panel:', error);
|
|
427
|
-
}
|
|
428
|
-
}
|
|
429
|
-
|
|
430
|
-
openDatabaseSetup() {
|
|
431
|
-
// Open the database setup wizard in a new window
|
|
432
|
-
chrome.tabs.create({
|
|
433
|
-
url: chrome.runtime.getURL('base-setup.html'),
|
|
434
|
-
active: true
|
|
435
|
-
});
|
|
436
|
-
window.close();
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
showStatusMessage(message, type = 'success') {
|
|
440
|
-
const statusMessage = document.getElementById('statusMessage');
|
|
441
|
-
const statusIcon = document.getElementById('statusIcon');
|
|
442
|
-
const statusText = document.getElementById('statusText');
|
|
443
|
-
|
|
444
|
-
statusIcon.textContent = type === 'success' ? '✅' : '❌';
|
|
445
|
-
statusText.textContent = message;
|
|
446
|
-
statusMessage.className = `status-message ${type}`;
|
|
447
|
-
statusMessage.style.display = 'block';
|
|
448
|
-
|
|
449
|
-
// Auto-hide after 3 seconds
|
|
450
|
-
setTimeout(() => this.hideStatusMessage(), 3000);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
hideStatusMessage() {
|
|
454
|
-
const statusMessage = document.getElementById('statusMessage');
|
|
455
|
-
statusMessage.style.display = 'none';
|
|
456
|
-
}
|
|
457
|
-
|
|
458
|
-
showLoading(text = 'Processing...') {
|
|
459
|
-
const loadingOverlay = document.getElementById('loadingOverlay');
|
|
460
|
-
const loadingText = document.getElementById('loadingText');
|
|
461
|
-
|
|
462
|
-
loadingText.textContent = text;
|
|
463
|
-
loadingOverlay.style.display = 'flex';
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
hideLoading() {
|
|
467
|
-
const loadingOverlay = document.getElementById('loadingOverlay');
|
|
468
|
-
loadingOverlay.style.display = 'none';
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
// Initialize popup when DOM is loaded
|
|
473
|
-
document.addEventListener('DOMContentLoaded', () => {
|
|
474
|
-
new PopupController();
|
|
475
|
-
});
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8">
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
-
<title>Airtable Clipper Dashboard</title>
|
|
7
|
-
<link rel="stylesheet" href="styles/popup.css">
|
|
8
|
-
</head>
|
|
9
|
-
<body>
|
|
10
|
-
<div class="header">
|
|
11
|
-
<h1>📎 Airtable Clipper</h1>
|
|
12
|
-
<p>Dashboard & Bulk Operations</p>
|
|
13
|
-
</div>
|
|
14
|
-
|
|
15
|
-
<div style="padding: 20px; text-align: center;">
|
|
16
|
-
<h2>🚀 Coming Soon!</h2>
|
|
17
|
-
<p>The dashboard panel is under development.</p>
|
|
18
|
-
<p>For now, use the main extension popup to save content to Airtable.</p>
|
|
19
|
-
|
|
20
|
-
<button onclick="chrome.action.openPopup();" style="margin-top: 20px; padding: 10px 20px; background: #667eea; color: white; border: none; border-radius: 6px; cursor: pointer;">
|
|
21
|
-
Open Main Panel
|
|
22
|
-
</button>
|
|
23
|
-
</div>
|
|
24
|
-
</body>
|
|
25
|
-
</html>
|