@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,337 +0,0 @@
|
|
|
1
|
-
// Airtable Clipper - Background Service Worker
|
|
2
|
-
// Handles context menus, notifications, and cross-tab communication
|
|
3
|
-
|
|
4
|
-
class BackgroundService {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.init();
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
init() {
|
|
10
|
-
// Create context menu on installation
|
|
11
|
-
chrome.runtime.onInstalled.addListener(() => {
|
|
12
|
-
this.createContextMenus();
|
|
13
|
-
this.setupNotifications();
|
|
14
|
-
});
|
|
15
|
-
|
|
16
|
-
// Handle context menu clicks
|
|
17
|
-
chrome.contextMenus.onClicked.addListener((info, tab) => {
|
|
18
|
-
this.handleContextMenuClick(info, tab);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
// Handle messages from content scripts and popup
|
|
22
|
-
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|
23
|
-
this.handleMessage(message, sender, sendResponse);
|
|
24
|
-
return true; // Keep message channel open for async responses
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
// Handle extension icon clicks
|
|
28
|
-
chrome.action.onClicked.addListener((tab) => {
|
|
29
|
-
this.handleActionClick(tab);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// Periodic cleanup
|
|
33
|
-
chrome.alarms.onAlarm.addListener((alarm) => {
|
|
34
|
-
if (alarm.name === 'cleanup') {
|
|
35
|
-
this.cleanupStorage();
|
|
36
|
-
}
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
// Set up cleanup alarm
|
|
40
|
-
chrome.alarms.create('cleanup', { delayInMinutes: 60, periodInMinutes: 60 });
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
createContextMenus() {
|
|
44
|
-
// Remove existing menus
|
|
45
|
-
chrome.contextMenus.removeAll();
|
|
46
|
-
|
|
47
|
-
// Main menu
|
|
48
|
-
chrome.contextMenus.create({
|
|
49
|
-
id: 'save-to-airtable',
|
|
50
|
-
title: 'Save to Airtable',
|
|
51
|
-
contexts: ['page', 'selection', 'link', 'image']
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// LinkedIn specific
|
|
55
|
-
chrome.contextMenus.create({
|
|
56
|
-
id: 'save-linkedin-profile',
|
|
57
|
-
title: 'Save LinkedIn Profile',
|
|
58
|
-
contexts: ['page'],
|
|
59
|
-
documentUrlPatterns: ['*://www.linkedin.com/in/*', '*://linkedin.com/in/*']
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
// Quick save selection
|
|
63
|
-
chrome.contextMenus.create({
|
|
64
|
-
id: 'quick-save-selection',
|
|
65
|
-
title: 'Quick Save Selected Text',
|
|
66
|
-
contexts: ['selection']
|
|
67
|
-
});
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
async handleContextMenuClick(info, tab) {
|
|
71
|
-
try {
|
|
72
|
-
switch (info.menuItemId) {
|
|
73
|
-
case 'save-to-airtable':
|
|
74
|
-
await this.triggerSave(tab, { type: 'general', info });
|
|
75
|
-
break;
|
|
76
|
-
case 'save-linkedin-profile':
|
|
77
|
-
await this.triggerSave(tab, { type: 'linkedin', info });
|
|
78
|
-
break;
|
|
79
|
-
case 'quick-save-selection':
|
|
80
|
-
await this.triggerSave(tab, { type: 'selection', text: info.selectionText });
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
83
|
-
} catch (error) {
|
|
84
|
-
console.error('Context menu error:', error);
|
|
85
|
-
this.showErrorNotification(error.message);
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
async triggerSave(tab, data) {
|
|
90
|
-
// Send message to content script to initiate save
|
|
91
|
-
try {
|
|
92
|
-
const response = await chrome.tabs.sendMessage(tab.id, {
|
|
93
|
-
action: 'triggerSave',
|
|
94
|
-
data: data
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
if (response && response.success) {
|
|
98
|
-
this.showSuccessNotification('Content saved to Airtable!');
|
|
99
|
-
} else {
|
|
100
|
-
throw new Error(response?.error || 'Failed to save content');
|
|
101
|
-
}
|
|
102
|
-
} catch (error) {
|
|
103
|
-
console.error('Save trigger error:', error);
|
|
104
|
-
// Fallback: open popup if content script isn't available
|
|
105
|
-
chrome.action.openPopup();
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
async handleMessage(message, sender, sendResponse) {
|
|
110
|
-
try {
|
|
111
|
-
switch (message.action) {
|
|
112
|
-
case 'saveToAirtable':
|
|
113
|
-
const result = await this.saveToAirtable(message.data);
|
|
114
|
-
sendResponse({ success: true, result });
|
|
115
|
-
break;
|
|
116
|
-
|
|
117
|
-
case 'getSettings':
|
|
118
|
-
const settings = await this.getSettings();
|
|
119
|
-
sendResponse({ success: true, settings });
|
|
120
|
-
break;
|
|
121
|
-
|
|
122
|
-
case 'saveSettings':
|
|
123
|
-
await this.saveSettings(message.settings);
|
|
124
|
-
sendResponse({ success: true });
|
|
125
|
-
break;
|
|
126
|
-
|
|
127
|
-
case 'showNotification':
|
|
128
|
-
this.showNotification(message.title, message.message, message.type);
|
|
129
|
-
sendResponse({ success: true });
|
|
130
|
-
break;
|
|
131
|
-
|
|
132
|
-
case 'openSidePanel':
|
|
133
|
-
if (sender.tab) {
|
|
134
|
-
chrome.sidePanel.open({ tabId: sender.tab.id });
|
|
135
|
-
}
|
|
136
|
-
sendResponse({ success: true });
|
|
137
|
-
break;
|
|
138
|
-
|
|
139
|
-
default:
|
|
140
|
-
sendResponse({ success: false, error: 'Unknown action' });
|
|
141
|
-
}
|
|
142
|
-
} catch (error) {
|
|
143
|
-
console.error('Message handler error:', error);
|
|
144
|
-
sendResponse({ success: false, error: error.message });
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
async saveToAirtable(data) {
|
|
149
|
-
// Get user settings
|
|
150
|
-
const settings = await this.getSettings();
|
|
151
|
-
|
|
152
|
-
if (!settings.airtableToken || !settings.baseId) {
|
|
153
|
-
throw new Error('Airtable credentials not configured');
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
// Import Airtable client
|
|
157
|
-
const { AirtableClient } = await import('./lib/airtable-client.js');
|
|
158
|
-
const client = new AirtableClient(settings.airtableToken, settings.baseId);
|
|
159
|
-
|
|
160
|
-
// Process and save data
|
|
161
|
-
const processedData = await this.processData(data, settings);
|
|
162
|
-
const result = await client.createRecord(processedData.table, processedData.fields);
|
|
163
|
-
|
|
164
|
-
// Update usage stats
|
|
165
|
-
await this.updateUsageStats();
|
|
166
|
-
|
|
167
|
-
return result;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
async processData(data, settings) {
|
|
171
|
-
// Smart data processing based on content type
|
|
172
|
-
if (data.type === 'linkedin') {
|
|
173
|
-
return {
|
|
174
|
-
table: settings.linkedinTable || 'Contacts',
|
|
175
|
-
fields: this.processLinkedInData(data)
|
|
176
|
-
};
|
|
177
|
-
} else if (data.type === 'selection') {
|
|
178
|
-
return {
|
|
179
|
-
table: settings.defaultTable || 'Clips',
|
|
180
|
-
fields: {
|
|
181
|
-
'Content': data.text,
|
|
182
|
-
'Source URL': data.url,
|
|
183
|
-
'Date Saved': new Date().toISOString(),
|
|
184
|
-
'Type': 'Text Selection'
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
} else {
|
|
188
|
-
return {
|
|
189
|
-
table: settings.defaultTable || 'Clips',
|
|
190
|
-
fields: {
|
|
191
|
-
'Title': data.title || 'Untitled',
|
|
192
|
-
'URL': data.url,
|
|
193
|
-
'Content': data.content,
|
|
194
|
-
'Date Saved': new Date().toISOString(),
|
|
195
|
-
'Type': 'Web Page'
|
|
196
|
-
}
|
|
197
|
-
};
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
processLinkedInData(data) {
|
|
202
|
-
return {
|
|
203
|
-
'Name': data.name || '',
|
|
204
|
-
'Title': data.title || '',
|
|
205
|
-
'Company': data.company || '',
|
|
206
|
-
'Location': data.location || '',
|
|
207
|
-
'LinkedIn URL': data.url || '',
|
|
208
|
-
'Date Added': new Date().toISOString(),
|
|
209
|
-
'Source': 'LinkedIn',
|
|
210
|
-
'Notes': data.about ? data.about.substring(0, 500) + '...' : ''
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
async getSettings() {
|
|
215
|
-
const result = await chrome.storage.sync.get([
|
|
216
|
-
'airtableToken',
|
|
217
|
-
'baseId',
|
|
218
|
-
'linkedinTable',
|
|
219
|
-
'defaultTable',
|
|
220
|
-
'autoSave',
|
|
221
|
-
'notifications'
|
|
222
|
-
]);
|
|
223
|
-
|
|
224
|
-
return {
|
|
225
|
-
airtableToken: result.airtableToken || '',
|
|
226
|
-
baseId: result.baseId || '',
|
|
227
|
-
linkedinTable: result.linkedinTable || 'Contacts',
|
|
228
|
-
defaultTable: result.defaultTable || 'Clips',
|
|
229
|
-
autoSave: result.autoSave || false,
|
|
230
|
-
notifications: result.notifications !== false // Default to true
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
async saveSettings(settings) {
|
|
235
|
-
await chrome.storage.sync.set(settings);
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
async updateUsageStats() {
|
|
239
|
-
const stats = await chrome.storage.local.get(['usageStats']);
|
|
240
|
-
const currentStats = stats.usageStats || {
|
|
241
|
-
totalClips: 0,
|
|
242
|
-
weeklyClips: 0,
|
|
243
|
-
lastResetDate: new Date().toDateString()
|
|
244
|
-
};
|
|
245
|
-
|
|
246
|
-
// Reset weekly counter if it's a new week
|
|
247
|
-
const today = new Date().toDateString();
|
|
248
|
-
if (currentStats.lastResetDate !== today) {
|
|
249
|
-
const lastReset = new Date(currentStats.lastResetDate);
|
|
250
|
-
const daysSinceReset = Math.floor((Date.now() - lastReset.getTime()) / (1000 * 60 * 60 * 24));
|
|
251
|
-
|
|
252
|
-
if (daysSinceReset >= 7) {
|
|
253
|
-
currentStats.weeklyClips = 0;
|
|
254
|
-
currentStats.lastResetDate = today;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
currentStats.totalClips++;
|
|
259
|
-
currentStats.weeklyClips++;
|
|
260
|
-
|
|
261
|
-
await chrome.storage.local.set({ usageStats: currentStats });
|
|
262
|
-
}
|
|
263
|
-
|
|
264
|
-
setupNotifications() {
|
|
265
|
-
// Check if notifications are supported
|
|
266
|
-
if ('notifications' in chrome) {
|
|
267
|
-
// Request notification permission if needed
|
|
268
|
-
chrome.notifications.getPermissionLevel((level) => {
|
|
269
|
-
if (level !== 'granted') {
|
|
270
|
-
console.log('Notification permission not granted');
|
|
271
|
-
}
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
showSuccessNotification(message) {
|
|
277
|
-
this.showNotification('Success!', message, 'success');
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
showErrorNotification(message) {
|
|
281
|
-
this.showNotification('Error', message, 'error');
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
async showNotification(title, message, type = 'basic') {
|
|
285
|
-
const settings = await this.getSettings();
|
|
286
|
-
if (!settings.notifications) return;
|
|
287
|
-
|
|
288
|
-
const iconUrl = type === 'error' ? 'icons/icon-error-48.png' : 'icons/icon-48.png';
|
|
289
|
-
|
|
290
|
-
chrome.notifications.create({
|
|
291
|
-
type: 'basic',
|
|
292
|
-
iconUrl: iconUrl,
|
|
293
|
-
title: title,
|
|
294
|
-
message: message
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
async handleActionClick(tab) {
|
|
299
|
-
// Check if this is a LinkedIn profile page
|
|
300
|
-
if (tab.url.includes('linkedin.com/in/')) {
|
|
301
|
-
// Try to extract LinkedIn data automatically
|
|
302
|
-
try {
|
|
303
|
-
await this.triggerSave(tab, { type: 'linkedin' });
|
|
304
|
-
} catch (error) {
|
|
305
|
-
// Fallback to opening popup
|
|
306
|
-
chrome.action.openPopup();
|
|
307
|
-
}
|
|
308
|
-
} else {
|
|
309
|
-
// Open popup for general pages
|
|
310
|
-
chrome.action.openPopup();
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
async cleanupStorage() {
|
|
315
|
-
// Clean up old temporary data
|
|
316
|
-
const result = await chrome.storage.local.get(null);
|
|
317
|
-
const keysToRemove = [];
|
|
318
|
-
|
|
319
|
-
for (const [key, value] of Object.entries(result)) {
|
|
320
|
-
// Remove temporary data older than 24 hours
|
|
321
|
-
if (key.startsWith('temp_') && value.timestamp) {
|
|
322
|
-
const age = Date.now() - value.timestamp;
|
|
323
|
-
if (age > 24 * 60 * 60 * 1000) { // 24 hours
|
|
324
|
-
keysToRemove.push(key);
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
if (keysToRemove.length > 0) {
|
|
330
|
-
await chrome.storage.local.remove(keysToRemove);
|
|
331
|
-
console.log(`Cleaned up ${keysToRemove.length} temporary items`);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// Initialize the background service
|
|
337
|
-
new BackgroundService();
|
|
@@ -1,324 +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 - Base Setup</title>
|
|
7
|
-
<link rel="stylesheet" href="styles/popup.css">
|
|
8
|
-
<style>
|
|
9
|
-
.setup-wizard {
|
|
10
|
-
width: 420px;
|
|
11
|
-
max-height: 600px;
|
|
12
|
-
overflow-y: auto;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
.wizard-step {
|
|
16
|
-
display: none;
|
|
17
|
-
padding: 20px;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
.wizard-step.active {
|
|
21
|
-
display: block;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
.step-indicator {
|
|
25
|
-
display: flex;
|
|
26
|
-
justify-content: center;
|
|
27
|
-
margin-bottom: 20px;
|
|
28
|
-
gap: 10px;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
.step-dot {
|
|
32
|
-
width: 12px;
|
|
33
|
-
height: 12px;
|
|
34
|
-
border-radius: 50%;
|
|
35
|
-
background-color: #e5e7eb;
|
|
36
|
-
transition: background-color 0.3s ease;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
.step-dot.active {
|
|
40
|
-
background-color: #667eea;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
.step-dot.completed {
|
|
44
|
-
background-color: #10b981;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
.template-preview {
|
|
48
|
-
border: 1px solid #e5e7eb;
|
|
49
|
-
border-radius: 8px;
|
|
50
|
-
padding: 16px;
|
|
51
|
-
margin: 16px 0;
|
|
52
|
-
background-color: #f9fafb;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
.table-preview {
|
|
56
|
-
margin-bottom: 16px;
|
|
57
|
-
padding: 12px;
|
|
58
|
-
border: 1px solid #d1d5db;
|
|
59
|
-
border-radius: 6px;
|
|
60
|
-
background-color: white;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
.table-preview h4 {
|
|
64
|
-
margin: 0 0 8px 0;
|
|
65
|
-
color: #374151;
|
|
66
|
-
font-size: 14px;
|
|
67
|
-
font-weight: 600;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
.table-preview p {
|
|
71
|
-
margin: 0 0 8px 0;
|
|
72
|
-
font-size: 12px;
|
|
73
|
-
color: #6b7280;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
.field-list {
|
|
77
|
-
display: flex;
|
|
78
|
-
flex-wrap: wrap;
|
|
79
|
-
gap: 4px;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
.field-tag {
|
|
83
|
-
background-color: #e0e7ff;
|
|
84
|
-
color: #3730a3;
|
|
85
|
-
padding: 2px 6px;
|
|
86
|
-
border-radius: 3px;
|
|
87
|
-
font-size: 10px;
|
|
88
|
-
font-weight: 500;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
.validation-result {
|
|
92
|
-
margin: 16px 0;
|
|
93
|
-
padding: 12px;
|
|
94
|
-
border-radius: 6px;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
.validation-result.excellent {
|
|
98
|
-
background-color: #d1fae5;
|
|
99
|
-
border: 1px solid #a7f3d0;
|
|
100
|
-
color: #065f46;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
.validation-result.good {
|
|
104
|
-
background-color: #dbeafe;
|
|
105
|
-
border: 1px solid #93c5fd;
|
|
106
|
-
color: #1e40af;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
.validation-result.fair {
|
|
110
|
-
background-color: #fef3c7;
|
|
111
|
-
border: 1px solid #fcd34d;
|
|
112
|
-
color: #92400e;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
.validation-result.needs-work {
|
|
116
|
-
background-color: #fee2e2;
|
|
117
|
-
border: 1px solid #fca5a5;
|
|
118
|
-
color: #991b1b;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
.wizard-actions {
|
|
122
|
-
display: flex;
|
|
123
|
-
justify-content: space-between;
|
|
124
|
-
margin-top: 20px;
|
|
125
|
-
padding-top: 16px;
|
|
126
|
-
border-top: 1px solid #e5e7eb;
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
.copy-script-area {
|
|
130
|
-
width: 100%;
|
|
131
|
-
height: 200px;
|
|
132
|
-
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', monospace;
|
|
133
|
-
font-size: 11px;
|
|
134
|
-
resize: vertical;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
.template-url {
|
|
138
|
-
display: block;
|
|
139
|
-
margin: 16px 0;
|
|
140
|
-
padding: 12px;
|
|
141
|
-
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
142
|
-
color: white;
|
|
143
|
-
text-decoration: none;
|
|
144
|
-
border-radius: 6px;
|
|
145
|
-
text-align: center;
|
|
146
|
-
font-weight: 500;
|
|
147
|
-
transition: transform 0.2s ease;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
.template-url:hover {
|
|
151
|
-
transform: translateY(-1px);
|
|
152
|
-
}
|
|
153
|
-
</style>
|
|
154
|
-
</head>
|
|
155
|
-
<body class="setup-wizard">
|
|
156
|
-
<!-- Step Indicator -->
|
|
157
|
-
<div class="step-indicator">
|
|
158
|
-
<div class="step-dot active" data-step="1"></div>
|
|
159
|
-
<div class="step-dot" data-step="2"></div>
|
|
160
|
-
<div class="step-dot" data-step="3"></div>
|
|
161
|
-
<div class="step-dot" data-step="4"></div>
|
|
162
|
-
</div>
|
|
163
|
-
|
|
164
|
-
<!-- Step 1: Welcome & Template Choice -->
|
|
165
|
-
<div class="wizard-step active" id="step1">
|
|
166
|
-
<h2>🚀 Create Your Airtable Clipper Database</h2>
|
|
167
|
-
<p>Let's set up the perfect Airtable base for your web clipping needs. This wizard will create optimized tables with all the right fields.</p>
|
|
168
|
-
|
|
169
|
-
<div class="form-group">
|
|
170
|
-
<label>Choose your setup method:</label>
|
|
171
|
-
<div style="margin-top: 12px;">
|
|
172
|
-
<label class="checkbox-label">
|
|
173
|
-
<input type="radio" name="setupMethod" value="template" checked>
|
|
174
|
-
<span class="checkmark"></span>
|
|
175
|
-
<strong>One-Click Template</strong> (Recommended)
|
|
176
|
-
<br><small style="margin-left: 24px; color: #6b7280;">Instantly copy our proven database structure</small>
|
|
177
|
-
</label>
|
|
178
|
-
|
|
179
|
-
<label class="checkbox-label" style="margin-top: 12px;">
|
|
180
|
-
<input type="radio" name="setupMethod" value="manual">
|
|
181
|
-
<span class="checkmark"></span>
|
|
182
|
-
<strong>Manual Setup Guide</strong>
|
|
183
|
-
<br><small style="margin-left: 24px; color: #6b7280;">Step-by-step instructions to create tables yourself</small>
|
|
184
|
-
</label>
|
|
185
|
-
|
|
186
|
-
<label class="checkbox-label" style="margin-top: 12px;">
|
|
187
|
-
<input type="radio" name="setupMethod" value="existing">
|
|
188
|
-
<span class="checkmark"></span>
|
|
189
|
-
<strong>Validate Existing Base</strong>
|
|
190
|
-
<br><small style="margin-left: 24px; color: #6b7280;">Check if your current base is clipper-ready</small>
|
|
191
|
-
</label>
|
|
192
|
-
</div>
|
|
193
|
-
</div>
|
|
194
|
-
|
|
195
|
-
<div class="wizard-actions">
|
|
196
|
-
<button class="btn secondary" onclick="window.close()">Cancel</button>
|
|
197
|
-
<button class="btn primary" onclick="nextStep()">Next</button>
|
|
198
|
-
</div>
|
|
199
|
-
</div>
|
|
200
|
-
|
|
201
|
-
<!-- Step 2: Template Configuration -->
|
|
202
|
-
<div class="wizard-step" id="step2">
|
|
203
|
-
<h2>📋 Configure Your Database</h2>
|
|
204
|
-
<p>Customize which tables to include based on your use case:</p>
|
|
205
|
-
|
|
206
|
-
<div class="form-group">
|
|
207
|
-
<label class="checkbox-label">
|
|
208
|
-
<input type="checkbox" id="includeContacts" checked>
|
|
209
|
-
<span class="checkmark"></span>
|
|
210
|
-
<strong>Contacts Table</strong> - LinkedIn profiles & leads
|
|
211
|
-
</label>
|
|
212
|
-
|
|
213
|
-
<label class="checkbox-label">
|
|
214
|
-
<input type="checkbox" id="includeClips" checked>
|
|
215
|
-
<span class="checkmark"></span>
|
|
216
|
-
<strong>Web Clips Table</strong> - Articles & web content
|
|
217
|
-
</label>
|
|
218
|
-
|
|
219
|
-
<label class="checkbox-label">
|
|
220
|
-
<input type="checkbox" id="includeCompanies" checked>
|
|
221
|
-
<span class="checkmark"></span>
|
|
222
|
-
<strong>Companies Table</strong> - Organization profiles
|
|
223
|
-
</label>
|
|
224
|
-
|
|
225
|
-
<label class="checkbox-label">
|
|
226
|
-
<input type="checkbox" id="includeTasks">
|
|
227
|
-
<span class="checkmark"></span>
|
|
228
|
-
<strong>Tasks Table</strong> - Follow-up actions
|
|
229
|
-
</label>
|
|
230
|
-
</div>
|
|
231
|
-
|
|
232
|
-
<div class="form-group">
|
|
233
|
-
<label for="baseName">Base Name:</label>
|
|
234
|
-
<input type="text" id="baseName" value="Airtable Clipper Database" placeholder="Enter base name">
|
|
235
|
-
</div>
|
|
236
|
-
|
|
237
|
-
<div class="wizard-actions">
|
|
238
|
-
<button class="btn secondary" onclick="prevStep()">Back</button>
|
|
239
|
-
<button class="btn primary" onclick="nextStep()">Preview</button>
|
|
240
|
-
</div>
|
|
241
|
-
</div>
|
|
242
|
-
|
|
243
|
-
<!-- Step 3: Template Preview -->
|
|
244
|
-
<div class="wizard-step" id="step3">
|
|
245
|
-
<h2>👀 Preview Your Database</h2>
|
|
246
|
-
<p>Here's what will be created in your Airtable base:</p>
|
|
247
|
-
|
|
248
|
-
<div class="template-preview" id="templatePreview">
|
|
249
|
-
<!-- Dynamically populated -->
|
|
250
|
-
</div>
|
|
251
|
-
|
|
252
|
-
<!-- Template Link -->
|
|
253
|
-
<div id="templateLinkSection" style="display: none;">
|
|
254
|
-
<h3>🎉 One-Click Template Ready!</h3>
|
|
255
|
-
<a href="#" class="template-url" id="templateLink" target="_blank">
|
|
256
|
-
📋 Copy Template to Your Airtable →
|
|
257
|
-
</a>
|
|
258
|
-
<p style="font-size: 12px; color: #6b7280; text-align: center;">
|
|
259
|
-
This will open Airtable in a new tab and copy the complete database structure
|
|
260
|
-
</p>
|
|
261
|
-
</div>
|
|
262
|
-
|
|
263
|
-
<!-- Manual Setup -->
|
|
264
|
-
<div id="manualSetupSection" style="display: none;">
|
|
265
|
-
<h3>📝 Manual Setup Instructions</h3>
|
|
266
|
-
<ol style="font-size: 13px; line-height: 1.5; padding-left: 20px;">
|
|
267
|
-
<li>Go to <a href="https://airtable.com" target="_blank">airtable.com</a> and create a new base</li>
|
|
268
|
-
<li>Name it "<span id="baseNameDisplay"></span>"</li>
|
|
269
|
-
<li>Create the tables shown above with their respective fields</li>
|
|
270
|
-
<li>Copy your Base ID and return to configure the extension</li>
|
|
271
|
-
</ol>
|
|
272
|
-
|
|
273
|
-
<details style="margin-top: 16px;">
|
|
274
|
-
<summary style="cursor: pointer; font-weight: 500;">📋 Copy Creation Script</summary>
|
|
275
|
-
<p style="font-size: 12px; margin: 8px 0;">Copy this script and paste it into Airtable's Scripting app:</p>
|
|
276
|
-
<textarea class="copy-script-area" id="creationScript" readonly></textarea>
|
|
277
|
-
<button class="btn secondary" onclick="copyScript()" style="margin-top: 8px;">Copy Script</button>
|
|
278
|
-
</details>
|
|
279
|
-
</div>
|
|
280
|
-
|
|
281
|
-
<div class="wizard-actions">
|
|
282
|
-
<button class="btn secondary" onclick="prevStep()">Back</button>
|
|
283
|
-
<button class="btn primary" onclick="nextStep()" id="step3NextBtn">Create Database</button>
|
|
284
|
-
</div>
|
|
285
|
-
</div>
|
|
286
|
-
|
|
287
|
-
<!-- Step 4: Validation & Completion -->
|
|
288
|
-
<div class="wizard-step" id="step4">
|
|
289
|
-
<h2>✅ Database Validation</h2>
|
|
290
|
-
<p>Let's check if your Airtable base is set up correctly:</p>
|
|
291
|
-
|
|
292
|
-
<div class="form-group" id="credentialsSection">
|
|
293
|
-
<label for="validationToken">Personal Access Token:</label>
|
|
294
|
-
<input type="password" id="validationToken" placeholder="pat...">
|
|
295
|
-
|
|
296
|
-
<label for="validationBaseId">Base ID:</label>
|
|
297
|
-
<input type="text" id="validationBaseId" placeholder="app...">
|
|
298
|
-
|
|
299
|
-
<button class="btn primary full-width" onclick="validateBase()" id="validateBtn">
|
|
300
|
-
Validate Base Setup
|
|
301
|
-
</button>
|
|
302
|
-
</div>
|
|
303
|
-
|
|
304
|
-
<div id="validationResults" style="display: none;">
|
|
305
|
-
<!-- Validation results will be shown here -->
|
|
306
|
-
</div>
|
|
307
|
-
|
|
308
|
-
<div class="wizard-actions">
|
|
309
|
-
<button class="btn secondary" onclick="prevStep()">Back</button>
|
|
310
|
-
<button class="btn primary" onclick="completeSetup()" id="completeBtn" style="display: none;">
|
|
311
|
-
Complete Setup
|
|
312
|
-
</button>
|
|
313
|
-
</div>
|
|
314
|
-
</div>
|
|
315
|
-
|
|
316
|
-
<!-- Loading Overlay -->
|
|
317
|
-
<div class="loading-overlay" id="loadingOverlay" style="display: none;">
|
|
318
|
-
<div class="loading-spinner"></div>
|
|
319
|
-
<div class="loading-text" id="loadingText">Processing...</div>
|
|
320
|
-
</div>
|
|
321
|
-
|
|
322
|
-
<script type="module" src="base-setup.js"></script>
|
|
323
|
-
</body>
|
|
324
|
-
</html>
|