@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.
Files changed (152) hide show
  1. package/package.json +10 -1
  2. package/.github/ISSUE_TEMPLATE/bug-report.yml +0 -173
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  4. package/.github/ISSUE_TEMPLATE/custom.md +0 -10
  5. package/.github/ISSUE_TEMPLATE/feature-request.yml +0 -209
  6. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  7. package/.github/ISSUE_TEMPLATE/security-report.yml +0 -216
  8. package/.github/pull_request_template.md +0 -245
  9. package/.github/workflows/ci-cd.yml +0 -408
  10. package/.github/workflows/security-audit.yml +0 -316
  11. package/API_DOCUMENTATION.md +0 -897
  12. package/CAPABILITY_REPORT.md +0 -118
  13. package/CLAUDE_INTEGRATION.md +0 -96
  14. package/CODE_OF_CONDUCT.md +0 -181
  15. package/CONTRIBUTING.md +0 -81
  16. package/DEVELOPMENT.md +0 -190
  17. package/Dockerfile +0 -39
  18. package/Dockerfile.node +0 -20
  19. package/Dockerfile.production +0 -127
  20. package/IMPROVEMENT_PROPOSAL.md +0 -371
  21. package/INSTALLATION.md +0 -183
  22. package/ISSUE_RESPONSES.md +0 -171
  23. package/MCP_REVIEW_SUMMARY.md +0 -142
  24. package/QUICK_START.md +0 -60
  25. package/RELEASE_NOTES_v1.2.0.md +0 -50
  26. package/RELEASE_NOTES_v1.2.1.md +0 -40
  27. package/RELEASE_NOTES_v1.2.2.md +0 -48
  28. package/RELEASE_NOTES_v1.2.3.md +0 -105
  29. package/RELEASE_NOTES_v1.2.4.md +0 -60
  30. package/RELEASE_NOTES_v1.4.0.md +0 -104
  31. package/RELEASE_NOTES_v1.5.0.md +0 -185
  32. package/RELEASE_NOTES_v1.6.0.md +0 -248
  33. package/SECURITY_NOTICE.md +0 -40
  34. package/airtable-clipper/CHANGELOG.md +0 -198
  35. package/airtable-clipper/CHROME_STORE_SUBMISSION.md +0 -343
  36. package/airtable-clipper/LAUNCH_STRATEGY.md +0 -495
  37. package/airtable-clipper/LICENSE +0 -21
  38. package/airtable-clipper/OAUTH_SETUP.md +0 -51
  39. package/airtable-clipper/PRIVACY_POLICY.md +0 -187
  40. package/airtable-clipper/README.md +0 -575
  41. package/airtable-clipper/SUBMIT_TO_CHROME_STORE.md +0 -273
  42. package/airtable-clipper/build.sh +0 -85
  43. package/airtable-clipper/docs/QUICK_START.md +0 -99
  44. package/airtable-clipper/docs/SETUP.md +0 -291
  45. package/airtable-clipper/extension/background.js +0 -337
  46. package/airtable-clipper/extension/base-setup.html +0 -324
  47. package/airtable-clipper/extension/base-setup.js +0 -471
  48. package/airtable-clipper/extension/content.js +0 -771
  49. package/airtable-clipper/extension/icons/README.md +0 -69
  50. package/airtable-clipper/extension/icons/icon-16.png +0 -3
  51. package/airtable-clipper/extension/manifest.json +0 -73
  52. package/airtable-clipper/extension/popup.html +0 -144
  53. package/airtable-clipper/extension/popup.js +0 -475
  54. package/airtable-clipper/extension/styles/content.css +0 -229
  55. package/airtable-clipper/extension/styles/popup.css +0 -477
  56. package/airtable-clipper/privacy-policy.md +0 -63
  57. package/airtable-clipper/releases/v1.0.0/background.js +0 -337
  58. package/airtable-clipper/releases/v1.0.0/base-setup.html +0 -324
  59. package/airtable-clipper/releases/v1.0.0/base-setup.js +0 -471
  60. package/airtable-clipper/releases/v1.0.0/content.js +0 -771
  61. package/airtable-clipper/releases/v1.0.0/icons/README.md +0 -69
  62. package/airtable-clipper/releases/v1.0.0/icons/icon-128.png +0 -2
  63. package/airtable-clipper/releases/v1.0.0/icons/icon-16.png +0 -3
  64. package/airtable-clipper/releases/v1.0.0/icons/icon-32.png +0 -2
  65. package/airtable-clipper/releases/v1.0.0/icons/icon-48.png +0 -2
  66. package/airtable-clipper/releases/v1.0.0/manifest.json +0 -73
  67. package/airtable-clipper/releases/v1.0.0/popup.html +0 -144
  68. package/airtable-clipper/releases/v1.0.0/popup.js +0 -475
  69. package/airtable-clipper/releases/v1.0.0/sidepanel.html +0 -25
  70. package/airtable-clipper/releases/v1.0.0/styles/content.css +0 -229
  71. package/airtable-clipper/releases/v1.0.0/styles/popup.css +0 -477
  72. package/airtable-clipper/releases/v1.0.1/background.js +0 -337
  73. package/airtable-clipper/releases/v1.0.1/base-setup.html +0 -324
  74. package/airtable-clipper/releases/v1.0.1/base-setup.js +0 -471
  75. package/airtable-clipper/releases/v1.0.1/content.js +0 -771
  76. package/airtable-clipper/releases/v1.0.1/icons/README.md +0 -69
  77. package/airtable-clipper/releases/v1.0.1/icons/icon-128.png +0 -2
  78. package/airtable-clipper/releases/v1.0.1/icons/icon-16.png +0 -3
  79. package/airtable-clipper/releases/v1.0.1/icons/icon-32.png +0 -2
  80. package/airtable-clipper/releases/v1.0.1/icons/icon-48.png +0 -2
  81. package/airtable-clipper/releases/v1.0.1/manifest.json +0 -70
  82. package/airtable-clipper/releases/v1.0.1/popup.html +0 -157
  83. package/airtable-clipper/releases/v1.0.1/popup.js +0 -562
  84. package/airtable-clipper/releases/v1.0.1/sidepanel.html +0 -25
  85. package/airtable-clipper/releases/v1.0.1/styles/content.css +0 -229
  86. package/airtable-clipper/releases/v1.0.1/styles/popup.css +0 -647
  87. package/airtable-clipper/releases/v1.0.2/background.js +0 -337
  88. package/airtable-clipper/releases/v1.0.2/base-setup.html +0 -324
  89. package/airtable-clipper/releases/v1.0.2/base-setup.js +0 -471
  90. package/airtable-clipper/releases/v1.0.2/content.js +0 -771
  91. package/airtable-clipper/releases/v1.0.2/icons/README.md +0 -69
  92. package/airtable-clipper/releases/v1.0.2/icons/icon-128.png +0 -2
  93. package/airtable-clipper/releases/v1.0.2/icons/icon-16.png +0 -3
  94. package/airtable-clipper/releases/v1.0.2/icons/icon-32.png +0 -2
  95. package/airtable-clipper/releases/v1.0.2/icons/icon-48.png +0 -2
  96. package/airtable-clipper/releases/v1.0.2/manifest.json +0 -62
  97. package/airtable-clipper/releases/v1.0.2/popup.html +0 -157
  98. package/airtable-clipper/releases/v1.0.2/popup.js +0 -567
  99. package/airtable-clipper/releases/v1.0.2/sidepanel.html +0 -25
  100. package/airtable-clipper/releases/v1.0.2/styles/content.css +0 -229
  101. package/airtable-clipper/releases/v1.0.2/styles/popup.css +0 -647
  102. package/airtable-clipper/terms-of-service.md +0 -124
  103. package/airtable-clipper/test-credentials.md +0 -61
  104. package/airtable-clipper/test-extension/background.js +0 -337
  105. package/airtable-clipper/test-extension/base-setup.html +0 -324
  106. package/airtable-clipper/test-extension/base-setup.js +0 -471
  107. package/airtable-clipper/test-extension/content.js +0 -873
  108. package/airtable-clipper/test-extension/icons/README.md +0 -69
  109. package/airtable-clipper/test-extension/icons/icon-128.png +0 -2
  110. package/airtable-clipper/test-extension/icons/icon-16.png +0 -3
  111. package/airtable-clipper/test-extension/icons/icon-32.png +0 -2
  112. package/airtable-clipper/test-extension/icons/icon-48.png +0 -2
  113. package/airtable-clipper/test-extension/manifest.json +0 -72
  114. package/airtable-clipper/test-extension/popup.html +0 -274
  115. package/airtable-clipper/test-extension/popup.js +0 -729
  116. package/airtable-clipper/test-extension/sidepanel.html +0 -25
  117. package/airtable-clipper/test-extension/styles/content.css +0 -229
  118. package/airtable-clipper/test-extension/styles/popup.css +0 -794
  119. package/airtable_mcp/__init__.py +0 -5
  120. package/airtable_mcp/src/server.py +0 -329
  121. package/airtable_mcp_v2.js +0 -1505
  122. package/airtable_mcp_v2_oauth.js +0 -1048
  123. package/airtable_mcp_v3_advanced.js +0 -1161
  124. package/cleanup.sh +0 -71
  125. package/docker-compose.production.yml +0 -366
  126. package/helm/airtable-mcp/Chart.yaml +0 -122
  127. package/helm/airtable-mcp/values.yaml +0 -538
  128. package/index.js +0 -179
  129. package/inspector.py +0 -148
  130. package/inspector_server.py +0 -337
  131. package/k8s/deployment.yaml +0 -402
  132. package/k8s/namespace.yaml +0 -108
  133. package/k8s/service.yaml +0 -194
  134. package/monitoring/alerts.yml +0 -289
  135. package/monitoring/prometheus.yml +0 -224
  136. package/publish-steps.txt +0 -27
  137. package/quick_test.sh +0 -30
  138. package/requirements.txt +0 -10
  139. package/setup.py +0 -29
  140. package/simple_airtable_server.py +0 -151
  141. package/smithery.yaml +0 -45
  142. package/test_all_features.sh +0 -146
  143. package/test_all_operations.sh +0 -120
  144. package/test_client.py +0 -70
  145. package/test_enhanced_features.js +0 -389
  146. package/test_mcp_comprehensive.js +0 -163
  147. package/test_mock_server.js +0 -180
  148. package/test_v1.4.0_final.sh +0 -131
  149. package/test_v1.5.0_comprehensive.sh +0 -96
  150. package/test_v1.5.0_final.sh +0 -224
  151. package/test_v1.6.0_comprehensive.sh +0 -187
  152. 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,229 +0,0 @@
1
- /* Airtable Clipper Content Script Styles */
2
-
3
- /* Floating Action Button */
4
- #airtable-clipper-fab {
5
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
6
- }
7
-
8
- #airtable-clipper-fab .fab-main {
9
- transition: all 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
10
- }
11
-
12
- #airtable-clipper-fab .fab-main:hover {
13
- transform: scale(1.1) rotate(5deg);
14
- }
15
-
16
- #airtable-clipper-fab .fab-main:active {
17
- transform: scale(0.95);
18
- }
19
-
20
- /* Selection Toolbar */
21
- #airtable-clipper-selection-toolbar {
22
- animation: fadeInUp 0.2s ease;
23
- }
24
-
25
- #airtable-clipper-selection-toolbar .selection-btn:hover {
26
- background: rgba(255, 255, 255, 0.1);
27
- }
28
-
29
- /* Bulk Mode Overlay */
30
- #airtable-clipper-bulk-overlay {
31
- animation: slideDown 0.3s ease;
32
- }
33
-
34
- #airtable-clipper-bulk-overlay .bulk-btn:hover {
35
- background: rgba(255, 255, 255, 0.3);
36
- transform: translateY(-1px);
37
- }
38
-
39
- /* Bulk Selection Highlights */
40
- .airtable-clipper-selected {
41
- background: rgba(102, 126, 234, 0.1) !important;
42
- border: 2px solid #667eea !important;
43
- border-radius: 4px;
44
- transition: all 0.2s ease;
45
- }
46
-
47
- .airtable-clipper-selectable:hover {
48
- background: rgba(102, 126, 234, 0.05) !important;
49
- cursor: pointer;
50
- }
51
-
52
- /* Notifications */
53
- #airtable-clipper-notification {
54
- animation: slideInRight 0.3s ease;
55
- backdrop-filter: blur(10px);
56
- }
57
-
58
- /* Animations */
59
- @keyframes fadeInUp {
60
- from {
61
- opacity: 0;
62
- transform: translateY(10px);
63
- }
64
- to {
65
- opacity: 1;
66
- transform: translateY(0);
67
- }
68
- }
69
-
70
- @keyframes slideDown {
71
- from {
72
- transform: translateY(-100%);
73
- }
74
- to {
75
- transform: translateY(0);
76
- }
77
- }
78
-
79
- @keyframes slideInRight {
80
- from {
81
- transform: translateX(100%);
82
- opacity: 0;
83
- }
84
- to {
85
- transform: translateX(0);
86
- opacity: 1;
87
- }
88
- }
89
-
90
- @keyframes pulse {
91
- 0%, 100% {
92
- opacity: 1;
93
- }
94
- 50% {
95
- opacity: 0.7;
96
- }
97
- }
98
-
99
- /* Loading States */
100
- .airtable-clipper-loading {
101
- position: relative;
102
- opacity: 0.7;
103
- }
104
-
105
- .airtable-clipper-loading::after {
106
- content: '';
107
- position: absolute;
108
- top: 50%;
109
- left: 50%;
110
- width: 20px;
111
- height: 20px;
112
- margin: -10px 0 0 -10px;
113
- border: 2px solid #667eea;
114
- border-top: 2px solid transparent;
115
- border-radius: 50%;
116
- animation: spin 1s linear infinite;
117
- }
118
-
119
- @keyframes spin {
120
- 0% { transform: rotate(0deg); }
121
- 100% { transform: rotate(360deg); }
122
- }
123
-
124
- /* Site-specific enhancements */
125
-
126
- /* LinkedIn */
127
- body[class*="linkedin"] .airtable-clipper-selected,
128
- [data-test-id*="search-result"] .airtable-clipper-selected {
129
- box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3);
130
- }
131
-
132
- /* GitHub */
133
- .repository-content .airtable-clipper-selected,
134
- .Box .airtable-clipper-selected {
135
- border-radius: 6px;
136
- }
137
-
138
- /* Stack Overflow */
139
- .question-summary .airtable-clipper-selected,
140
- .answer .airtable-clipper-selected {
141
- border-left: 4px solid #667eea;
142
- padding-left: 8px;
143
- }
144
-
145
- /* Medium */
146
- article .airtable-clipper-selected {
147
- border-radius: 8px;
148
- padding: 8px;
149
- margin: 4px 0;
150
- }
151
-
152
- /* Responsive Design */
153
- @media (max-width: 768px) {
154
- #airtable-clipper-fab {
155
- bottom: 80px; /* Above mobile browser UI */
156
- right: 16px;
157
- z-index: 10000;
158
- }
159
-
160
- #airtable-clipper-fab .fab-main {
161
- width: 48px;
162
- height: 48px;
163
- font-size: 18px;
164
- }
165
-
166
- #airtable-clipper-bulk-overlay {
167
- font-size: 12px;
168
- padding: 8px 12px;
169
- }
170
-
171
- #airtable-clipper-bulk-overlay .bulk-header {
172
- flex-direction: column;
173
- gap: 8px;
174
- align-items: flex-start;
175
- }
176
-
177
- #airtable-clipper-bulk-overlay .bulk-actions {
178
- display: flex;
179
- gap: 8px;
180
- }
181
-
182
- #airtable-clipper-notification {
183
- top: 10px;
184
- right: 10px;
185
- left: 10px;
186
- max-width: none;
187
- font-size: 13px;
188
- }
189
- }
190
-
191
- /* Dark Mode Support */
192
- @media (prefers-color-scheme: dark) {
193
- .airtable-clipper-selected {
194
- background: rgba(102, 126, 234, 0.2) !important;
195
- border-color: #818cf8 !important;
196
- }
197
-
198
- .airtable-clipper-selectable:hover {
199
- background: rgba(102, 126, 234, 0.1) !important;
200
- }
201
- }
202
-
203
- /* High Contrast Mode */
204
- @media (prefers-contrast: high) {
205
- #airtable-clipper-fab .fab-main {
206
- border: 2px solid #000;
207
- }
208
-
209
- .airtable-clipper-selected {
210
- border-width: 3px !important;
211
- background: rgba(102, 126, 234, 0.3) !important;
212
- }
213
- }
214
-
215
- /* Reduced Motion */
216
- @media (prefers-reduced-motion: reduce) {
217
- #airtable-clipper-fab .fab-main,
218
- #airtable-clipper-notification,
219
- #airtable-clipper-selection-toolbar,
220
- #airtable-clipper-bulk-overlay,
221
- .airtable-clipper-selected {
222
- animation: none !important;
223
- transition: none !important;
224
- }
225
-
226
- #airtable-clipper-fab .fab-main:hover {
227
- transform: none !important;
228
- }
229
- }