@rashidazarang/airtable-mcp 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/README.md +1 -1
  2. package/airtable_simple_production.js +387 -5
  3. package/examples/claude_simple_config.json +0 -9
  4. package/package.json +10 -1
  5. package/.github/ISSUE_TEMPLATE/bug-report.yml +0 -173
  6. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -38
  7. package/.github/ISSUE_TEMPLATE/custom.md +0 -10
  8. package/.github/ISSUE_TEMPLATE/feature-request.yml +0 -209
  9. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -20
  10. package/.github/ISSUE_TEMPLATE/security-report.yml +0 -216
  11. package/.github/pull_request_template.md +0 -245
  12. package/.github/workflows/ci-cd.yml +0 -408
  13. package/.github/workflows/security-audit.yml +0 -316
  14. package/API_DOCUMENTATION.md +0 -897
  15. package/CAPABILITY_REPORT.md +0 -118
  16. package/CLAUDE_INTEGRATION.md +0 -96
  17. package/CODE_OF_CONDUCT.md +0 -181
  18. package/CONTRIBUTING.md +0 -81
  19. package/DEVELOPMENT.md +0 -190
  20. package/Dockerfile +0 -39
  21. package/Dockerfile.node +0 -20
  22. package/Dockerfile.production +0 -127
  23. package/IMPROVEMENT_PROPOSAL.md +0 -371
  24. package/INSTALLATION.md +0 -183
  25. package/ISSUE_RESPONSES.md +0 -171
  26. package/MCP_REVIEW_SUMMARY.md +0 -142
  27. package/QUICK_START.md +0 -60
  28. package/RELEASE_NOTES_v1.2.0.md +0 -50
  29. package/RELEASE_NOTES_v1.2.1.md +0 -40
  30. package/RELEASE_NOTES_v1.2.2.md +0 -48
  31. package/RELEASE_NOTES_v1.2.3.md +0 -105
  32. package/RELEASE_NOTES_v1.2.4.md +0 -60
  33. package/RELEASE_NOTES_v1.4.0.md +0 -104
  34. package/RELEASE_NOTES_v1.5.0.md +0 -185
  35. package/RELEASE_NOTES_v1.6.0.md +0 -248
  36. package/SECURITY_NOTICE.md +0 -40
  37. package/airtable-clipper/CHANGELOG.md +0 -198
  38. package/airtable-clipper/CHROME_STORE_SUBMISSION.md +0 -343
  39. package/airtable-clipper/LAUNCH_STRATEGY.md +0 -495
  40. package/airtable-clipper/LICENSE +0 -21
  41. package/airtable-clipper/OAUTH_SETUP.md +0 -51
  42. package/airtable-clipper/PRIVACY_POLICY.md +0 -187
  43. package/airtable-clipper/README.md +0 -575
  44. package/airtable-clipper/SUBMIT_TO_CHROME_STORE.md +0 -273
  45. package/airtable-clipper/build.sh +0 -85
  46. package/airtable-clipper/docs/QUICK_START.md +0 -99
  47. package/airtable-clipper/docs/SETUP.md +0 -291
  48. package/airtable-clipper/extension/background.js +0 -337
  49. package/airtable-clipper/extension/base-setup.html +0 -324
  50. package/airtable-clipper/extension/base-setup.js +0 -471
  51. package/airtable-clipper/extension/content.js +0 -771
  52. package/airtable-clipper/extension/icons/README.md +0 -69
  53. package/airtable-clipper/extension/icons/icon-16.png +0 -3
  54. package/airtable-clipper/extension/manifest.json +0 -73
  55. package/airtable-clipper/extension/popup.html +0 -144
  56. package/airtable-clipper/extension/popup.js +0 -475
  57. package/airtable-clipper/extension/styles/content.css +0 -229
  58. package/airtable-clipper/extension/styles/popup.css +0 -477
  59. package/airtable-clipper/privacy-policy.md +0 -63
  60. package/airtable-clipper/releases/v1.0.0/background.js +0 -337
  61. package/airtable-clipper/releases/v1.0.0/base-setup.html +0 -324
  62. package/airtable-clipper/releases/v1.0.0/base-setup.js +0 -471
  63. package/airtable-clipper/releases/v1.0.0/content.js +0 -771
  64. package/airtable-clipper/releases/v1.0.0/icons/README.md +0 -69
  65. package/airtable-clipper/releases/v1.0.0/icons/icon-128.png +0 -2
  66. package/airtable-clipper/releases/v1.0.0/icons/icon-16.png +0 -3
  67. package/airtable-clipper/releases/v1.0.0/icons/icon-32.png +0 -2
  68. package/airtable-clipper/releases/v1.0.0/icons/icon-48.png +0 -2
  69. package/airtable-clipper/releases/v1.0.0/manifest.json +0 -73
  70. package/airtable-clipper/releases/v1.0.0/popup.html +0 -144
  71. package/airtable-clipper/releases/v1.0.0/popup.js +0 -475
  72. package/airtable-clipper/releases/v1.0.0/sidepanel.html +0 -25
  73. package/airtable-clipper/releases/v1.0.0/styles/content.css +0 -229
  74. package/airtable-clipper/releases/v1.0.0/styles/popup.css +0 -477
  75. package/airtable-clipper/releases/v1.0.1/background.js +0 -337
  76. package/airtable-clipper/releases/v1.0.1/base-setup.html +0 -324
  77. package/airtable-clipper/releases/v1.0.1/base-setup.js +0 -471
  78. package/airtable-clipper/releases/v1.0.1/content.js +0 -771
  79. package/airtable-clipper/releases/v1.0.1/icons/README.md +0 -69
  80. package/airtable-clipper/releases/v1.0.1/icons/icon-128.png +0 -2
  81. package/airtable-clipper/releases/v1.0.1/icons/icon-16.png +0 -3
  82. package/airtable-clipper/releases/v1.0.1/icons/icon-32.png +0 -2
  83. package/airtable-clipper/releases/v1.0.1/icons/icon-48.png +0 -2
  84. package/airtable-clipper/releases/v1.0.1/manifest.json +0 -70
  85. package/airtable-clipper/releases/v1.0.1/popup.html +0 -157
  86. package/airtable-clipper/releases/v1.0.1/popup.js +0 -562
  87. package/airtable-clipper/releases/v1.0.1/sidepanel.html +0 -25
  88. package/airtable-clipper/releases/v1.0.1/styles/content.css +0 -229
  89. package/airtable-clipper/releases/v1.0.1/styles/popup.css +0 -647
  90. package/airtable-clipper/releases/v1.0.2/background.js +0 -337
  91. package/airtable-clipper/releases/v1.0.2/base-setup.html +0 -324
  92. package/airtable-clipper/releases/v1.0.2/base-setup.js +0 -471
  93. package/airtable-clipper/releases/v1.0.2/content.js +0 -771
  94. package/airtable-clipper/releases/v1.0.2/icons/README.md +0 -69
  95. package/airtable-clipper/releases/v1.0.2/icons/icon-128.png +0 -2
  96. package/airtable-clipper/releases/v1.0.2/icons/icon-16.png +0 -3
  97. package/airtable-clipper/releases/v1.0.2/icons/icon-32.png +0 -2
  98. package/airtable-clipper/releases/v1.0.2/icons/icon-48.png +0 -2
  99. package/airtable-clipper/releases/v1.0.2/manifest.json +0 -62
  100. package/airtable-clipper/releases/v1.0.2/popup.html +0 -157
  101. package/airtable-clipper/releases/v1.0.2/popup.js +0 -567
  102. package/airtable-clipper/releases/v1.0.2/sidepanel.html +0 -25
  103. package/airtable-clipper/releases/v1.0.2/styles/content.css +0 -229
  104. package/airtable-clipper/releases/v1.0.2/styles/popup.css +0 -647
  105. package/airtable-clipper/terms-of-service.md +0 -124
  106. package/airtable-clipper/test-credentials.md +0 -61
  107. package/airtable-clipper/test-extension/background.js +0 -337
  108. package/airtable-clipper/test-extension/base-setup.html +0 -324
  109. package/airtable-clipper/test-extension/base-setup.js +0 -471
  110. package/airtable-clipper/test-extension/content.js +0 -873
  111. package/airtable-clipper/test-extension/icons/README.md +0 -69
  112. package/airtable-clipper/test-extension/icons/icon-128.png +0 -2
  113. package/airtable-clipper/test-extension/icons/icon-16.png +0 -3
  114. package/airtable-clipper/test-extension/icons/icon-32.png +0 -2
  115. package/airtable-clipper/test-extension/icons/icon-48.png +0 -2
  116. package/airtable-clipper/test-extension/manifest.json +0 -72
  117. package/airtable-clipper/test-extension/popup.html +0 -274
  118. package/airtable-clipper/test-extension/popup.js +0 -729
  119. package/airtable-clipper/test-extension/sidepanel.html +0 -25
  120. package/airtable-clipper/test-extension/styles/content.css +0 -229
  121. package/airtable-clipper/test-extension/styles/popup.css +0 -794
  122. package/airtable_mcp/__init__.py +0 -5
  123. package/airtable_mcp/src/server.py +0 -329
  124. package/airtable_mcp_v2.js +0 -1505
  125. package/airtable_mcp_v2_oauth.js +0 -1048
  126. package/airtable_mcp_v3_advanced.js +0 -1161
  127. package/cleanup.sh +0 -71
  128. package/docker-compose.production.yml +0 -366
  129. package/helm/airtable-mcp/Chart.yaml +0 -122
  130. package/helm/airtable-mcp/values.yaml +0 -538
  131. package/index.js +0 -179
  132. package/inspector.py +0 -148
  133. package/inspector_server.py +0 -337
  134. package/k8s/deployment.yaml +0 -402
  135. package/k8s/namespace.yaml +0 -108
  136. package/k8s/service.yaml +0 -194
  137. package/monitoring/alerts.yml +0 -289
  138. package/monitoring/prometheus.yml +0 -224
  139. package/publish-steps.txt +0 -27
  140. package/quick_test.sh +0 -30
  141. package/requirements.txt +0 -10
  142. package/setup.py +0 -29
  143. package/simple_airtable_server.py +0 -151
  144. package/smithery.yaml +0 -45
  145. package/test_all_features.sh +0 -146
  146. package/test_all_operations.sh +0 -120
  147. package/test_client.py +0 -70
  148. package/test_enhanced_features.js +0 -389
  149. package/test_mcp_comprehensive.js +0 -163
  150. package/test_mock_server.js +0 -180
  151. package/test_v1.4.0_final.sh +0 -131
  152. package/test_v1.5.0_comprehensive.sh +0 -96
  153. package/test_v1.5.0_final.sh +0 -224
  154. package/test_v1.6.0_comprehensive.sh +0 -187
  155. package/test_webhooks.sh +0 -105
@@ -1,471 +0,0 @@
1
- // Airtable Clipper Base Setup Wizard
2
- import { BaseTemplate } from './lib/base-template.js';
3
- import { AirtableClient } from './lib/airtable-client.js';
4
-
5
- class BaseSetupWizard {
6
- constructor() {
7
- this.currentStep = 1;
8
- this.maxStep = 4;
9
- this.setupMethod = 'template';
10
- this.template = null;
11
- this.baseTemplate = new BaseTemplate();
12
- this.init();
13
- }
14
-
15
- init() {
16
- this.bindEvents();
17
- this.updateStepIndicator();
18
- }
19
-
20
- bindEvents() {
21
- // Setup method radio buttons
22
- document.querySelectorAll('input[name="setupMethod"]').forEach(radio => {
23
- radio.addEventListener('change', (e) => {
24
- this.setupMethod = e.target.value;
25
- this.updateStep2Content();
26
- });
27
- });
28
-
29
- // Table checkboxes
30
- document.querySelectorAll('#step2 input[type="checkbox"]').forEach(checkbox => {
31
- checkbox.addEventListener('change', () => {
32
- this.updatePreview();
33
- });
34
- });
35
-
36
- // Base name input
37
- document.getElementById('baseName').addEventListener('input', () => {
38
- this.updatePreview();
39
- });
40
- }
41
-
42
- updateStep2Content() {
43
- const step2 = document.getElementById('step2');
44
- const step3NextBtn = document.getElementById('step3NextBtn');
45
-
46
- if (this.setupMethod === 'existing') {
47
- step2.querySelector('h2').textContent = '🔍 Validate Existing Base';
48
- step2.querySelector('p').textContent = 'We\'ll check your current Airtable base structure and suggest improvements.';
49
- step3NextBtn.textContent = 'Validate Base';
50
- } else {
51
- step2.querySelector('h2').textContent = '📋 Configure Your Database';
52
- step2.querySelector('p').textContent = 'Customize which tables to include based on your use case:';
53
- step3NextBtn.textContent = 'Create Database';
54
- }
55
- }
56
-
57
- async nextStep() {
58
- if (this.currentStep < this.maxStep) {
59
- this.currentStep++;
60
- this.updateStepDisplay();
61
-
62
- if (this.currentStep === 3) {
63
- await this.generatePreview();
64
- } else if (this.currentStep === 4) {
65
- await this.prepareValidation();
66
- }
67
- }
68
- }
69
-
70
- prevStep() {
71
- if (this.currentStep > 1) {
72
- this.currentStep--;
73
- this.updateStepDisplay();
74
- }
75
- }
76
-
77
- updateStepDisplay() {
78
- // Hide all steps
79
- document.querySelectorAll('.wizard-step').forEach(step => {
80
- step.classList.remove('active');
81
- });
82
-
83
- // Show current step
84
- document.getElementById(`step${this.currentStep}`).classList.add('active');
85
-
86
- // Update step indicator
87
- this.updateStepIndicator();
88
- }
89
-
90
- updateStepIndicator() {
91
- document.querySelectorAll('.step-dot').forEach((dot, index) => {
92
- dot.classList.remove('active', 'completed');
93
-
94
- if (index + 1 < this.currentStep) {
95
- dot.classList.add('completed');
96
- } else if (index + 1 === this.currentStep) {
97
- dot.classList.add('active');
98
- }
99
- });
100
- }
101
-
102
- async generatePreview() {
103
- this.showLoading('Generating template...');
104
-
105
- try {
106
- const config = this.getConfiguration();
107
- this.template = await this.baseTemplate.createOptimalBase(config);
108
-
109
- if (this.template.success) {
110
- this.displayTemplatePreview();
111
-
112
- if (this.setupMethod === 'template') {
113
- this.showTemplateLink();
114
- } else {
115
- this.showManualInstructions();
116
- }
117
- } else {
118
- throw new Error(this.template.error || 'Failed to generate template');
119
- }
120
- } catch (error) {
121
- console.error('Template generation failed:', error);
122
- this.showError('Failed to generate template: ' + error.message);
123
- } finally {
124
- this.hideLoading();
125
- }
126
- }
127
-
128
- getConfiguration() {
129
- return {
130
- baseName: document.getElementById('baseName').value || 'Airtable Clipper Database',
131
- includeContacts: document.getElementById('includeContacts').checked,
132
- includeClips: document.getElementById('includeClips').checked,
133
- includeCompanies: document.getElementById('includeCompanies').checked,
134
- includeTasks: document.getElementById('includeTasks').checked
135
- };
136
- }
137
-
138
- displayTemplatePreview() {
139
- const preview = document.getElementById('templatePreview');
140
-
141
- let html = `<h3>📊 ${this.template.template.baseName}</h3>`;
142
- html += `<p style="margin-bottom: 16px; color: #6b7280;">${this.template.template.description}</p>`;
143
-
144
- this.template.template.tables.forEach(table => {
145
- html += `
146
- <div class="table-preview">
147
- <h4>${table.name}</h4>
148
- <p>${table.description}</p>
149
- <div class="field-list">
150
- ${table.fields.slice(0, 8).map(field =>
151
- `<span class="field-tag">${field.name}</span>`
152
- ).join('')}
153
- ${table.fields.length > 8 ?
154
- `<span class="field-tag">+${table.fields.length - 8} more</span>` :
155
- ''
156
- }
157
- </div>
158
- </div>
159
- `;
160
- });
161
-
162
- // Add automation recommendations
163
- if (this.template.template.automations.length > 0) {
164
- html += `
165
- <div style="margin-top: 16px; padding: 12px; background-color: #f0f9ff; border-radius: 6px;">
166
- <h4 style="margin: 0 0 8px 0; color: #0369a1;">🤖 Recommended Automations</h4>
167
- <ul style="margin: 0; padding-left: 16px; font-size: 12px;">
168
- ${this.template.template.automations.map(auto =>
169
- `<li>${auto.name}: ${auto.description}</li>`
170
- ).join('')}
171
- </ul>
172
- </div>
173
- `;
174
- }
175
-
176
- preview.innerHTML = html;
177
- }
178
-
179
- showTemplateLink() {
180
- const templateSection = document.getElementById('templateLinkSection');
181
- const manualSection = document.getElementById('manualSetupSection');
182
-
183
- templateSection.style.display = 'block';
184
- manualSection.style.display = 'none';
185
-
186
- // In a real implementation, this would be a working Airtable template URL
187
- const templateLink = document.getElementById('templateLink');
188
- templateLink.href = this.template.templateUrl || 'https://airtable.com/universe';
189
-
190
- // For now, show manual instructions as fallback
191
- setTimeout(() => {
192
- templateSection.style.display = 'none';
193
- manualSection.style.display = 'block';
194
- this.showManualInstructions();
195
- }, 100);
196
- }
197
-
198
- showManualInstructions() {
199
- const manualSection = document.getElementById('manualSetupSection');
200
- const baseNameDisplay = document.getElementById('baseNameDisplay');
201
- const creationScript = document.getElementById('creationScript');
202
-
203
- manualSection.style.display = 'block';
204
- baseNameDisplay.textContent = this.template.template.baseName;
205
-
206
- // Generate creation script
207
- const script = this.baseTemplate.generateCreationScript(this.template.template);
208
- creationScript.value = script;
209
- }
210
-
211
- async prepareValidation() {
212
- // Pre-populate credentials if available
213
- const existingSettings = await this.getExistingSettings();
214
-
215
- if (existingSettings.airtableToken) {
216
- document.getElementById('validationToken').value = existingSettings.airtableToken;
217
- }
218
-
219
- if (existingSettings.baseId) {
220
- document.getElementById('validationBaseId').value = existingSettings.baseId;
221
- }
222
-
223
- // If both are available, auto-validate
224
- if (existingSettings.airtableToken && existingSettings.baseId) {
225
- setTimeout(() => this.validateBase(), 500);
226
- }
227
- }
228
-
229
- async getExistingSettings() {
230
- try {
231
- const response = await chrome.runtime.sendMessage({
232
- action: 'getSettings'
233
- });
234
-
235
- return response.success ? response.settings : {};
236
- } catch (error) {
237
- console.error('Failed to get existing settings:', error);
238
- return {};
239
- }
240
- }
241
-
242
- async validateBase() {
243
- const token = document.getElementById('validationToken').value.trim();
244
- const baseId = document.getElementById('validationBaseId').value.trim();
245
-
246
- if (!token || !baseId) {
247
- this.showError('Please enter both Personal Access Token and Base ID');
248
- return;
249
- }
250
-
251
- this.showLoading('Validating base structure...');
252
-
253
- try {
254
- const client = new AirtableClient(token, baseId);
255
- const baseTemplate = new BaseTemplate(client);
256
- const validation = await baseTemplate.validateBaseStructure(baseId);
257
-
258
- this.displayValidationResults(validation);
259
-
260
- // Save settings if validation is successful
261
- if (validation.score >= 40) {
262
- await this.saveSettings(token, baseId);
263
- document.getElementById('completeBtn').style.display = 'block';
264
- }
265
-
266
- } catch (error) {
267
- console.error('Validation failed:', error);
268
- this.showError('Validation failed: ' + error.message);
269
- } finally {
270
- this.hideLoading();
271
- }
272
- }
273
-
274
- displayValidationResults(validation) {
275
- const resultsDiv = document.getElementById('validationResults');
276
-
277
- let html = `
278
- <div class="validation-result ${validation.status}">
279
- <h3>
280
- ${this.getStatusIcon(validation.status)}
281
- Base Score: ${validation.score}/${validation.maxScore || 100}
282
- </h3>
283
- <p><strong>Status:</strong> ${this.getStatusText(validation.status)}</p>
284
- `;
285
-
286
- if (validation.issues && validation.issues.length > 0) {
287
- html += `
288
- <div style="margin-top: 12px;">
289
- <strong>Issues Found:</strong>
290
- <ul style="margin: 4px 0 0 20px; font-size: 13px;">
291
- ${validation.issues.map(issue => `<li>${issue}</li>`).join('')}
292
- </ul>
293
- </div>
294
- `;
295
- }
296
-
297
- if (validation.recommendations && validation.recommendations.length > 0) {
298
- html += `
299
- <div style="margin-top: 12px;">
300
- <strong>Recommendations:</strong>
301
- <ul style="margin: 4px 0 0 20px; font-size: 13px;">
302
- ${validation.recommendations.map(rec => `<li>${rec}</li>`).join('')}
303
- </ul>
304
- </div>
305
- `;
306
- }
307
-
308
- html += '</div>';
309
-
310
- if (validation.status === 'excellent' || validation.status === 'good') {
311
- html += `
312
- <div style="margin-top: 16px; padding: 12px; background-color: #d1fae5; border-radius: 6px;">
313
- <strong>🎉 Great news!</strong> Your base is ready for Airtable Clipper.
314
- You can start saving LinkedIn profiles and web content right away.
315
- </div>
316
- `;
317
- } else if (validation.status === 'fair') {
318
- html += `
319
- <div style="margin-top: 16px; padding: 12px; background-color: #fef3c7; border-radius: 6px;">
320
- <strong>⚠️ Base needs some work.</strong> The extension will work, but you'll get
321
- better results by adding the recommended fields and tables.
322
- </div>
323
- `;
324
- } else {
325
- html += `
326
- <div style="margin-top: 16px; padding: 12px; background-color: #fee2e2; border-radius: 6px;">
327
- <strong>❌ Base needs significant improvements.</strong> Please create the missing
328
- tables and fields for the best experience.
329
- </div>
330
- `;
331
- }
332
-
333
- resultsDiv.innerHTML = html;
334
- resultsDiv.style.display = 'block';
335
- }
336
-
337
- getStatusIcon(status) {
338
- const icons = {
339
- 'excellent': '🌟',
340
- 'good': '✅',
341
- 'fair': '⚠️',
342
- 'needs-work': '❌',
343
- 'error': '🚫'
344
- };
345
- return icons[status] || '❓';
346
- }
347
-
348
- getStatusText(status) {
349
- const texts = {
350
- 'excellent': 'Perfect! Your base is optimally configured.',
351
- 'good': 'Great! Your base is well set up for clipping.',
352
- 'fair': 'Good start, but could use some improvements.',
353
- 'needs-work': 'Significant setup needed for best results.',
354
- 'error': 'Unable to validate. Check your credentials.'
355
- };
356
- return texts[status] || 'Unknown status';
357
- }
358
-
359
- async saveSettings(token, baseId) {
360
- try {
361
- await chrome.runtime.sendMessage({
362
- action: 'saveSettings',
363
- settings: {
364
- airtableToken: token,
365
- baseId: baseId
366
- }
367
- });
368
- } catch (error) {
369
- console.error('Failed to save settings:', error);
370
- }
371
- }
372
-
373
- async completeSetup() {
374
- this.showLoading('Completing setup...');
375
-
376
- try {
377
- // Close setup wizard and return to main popup
378
- window.close();
379
-
380
- // Open main popup to show completed setup
381
- chrome.action.openPopup();
382
-
383
- } catch (error) {
384
- console.error('Failed to complete setup:', error);
385
- this.showError('Setup completion failed: ' + error.message);
386
- } finally {
387
- this.hideLoading();
388
- }
389
- }
390
-
391
- showLoading(message = 'Loading...') {
392
- const overlay = document.getElementById('loadingOverlay');
393
- const text = document.getElementById('loadingText');
394
-
395
- text.textContent = message;
396
- overlay.style.display = 'flex';
397
- }
398
-
399
- hideLoading() {
400
- const overlay = document.getElementById('loadingOverlay');
401
- overlay.style.display = 'none';
402
- }
403
-
404
- showError(message) {
405
- // Create a simple error display
406
- const errorDiv = document.createElement('div');
407
- errorDiv.style.cssText = `
408
- position: fixed;
409
- top: 20px;
410
- left: 20px;
411
- right: 20px;
412
- background-color: #fee2e2;
413
- color: #991b1b;
414
- padding: 12px;
415
- border-radius: 6px;
416
- border: 1px solid #fca5a5;
417
- z-index: 10000;
418
- font-size: 14px;
419
- `;
420
- errorDiv.innerHTML = `❌ ${message}`;
421
-
422
- document.body.appendChild(errorDiv);
423
-
424
- // Auto-remove after 5 seconds
425
- setTimeout(() => {
426
- if (errorDiv.parentNode) {
427
- errorDiv.parentNode.removeChild(errorDiv);
428
- }
429
- }, 5000);
430
- }
431
- }
432
-
433
- // Global functions for HTML onclick handlers
434
- window.nextStep = function() {
435
- wizard.nextStep();
436
- };
437
-
438
- window.prevStep = function() {
439
- wizard.prevStep();
440
- };
441
-
442
- window.validateBase = function() {
443
- wizard.validateBase();
444
- };
445
-
446
- window.completeSetup = function() {
447
- wizard.completeSetup();
448
- };
449
-
450
- window.copyScript = function() {
451
- const script = document.getElementById('creationScript');
452
- script.select();
453
- document.execCommand('copy');
454
-
455
- // Show feedback
456
- const button = event.target;
457
- const originalText = button.textContent;
458
- button.textContent = 'Copied!';
459
- button.style.backgroundColor = '#10b981';
460
-
461
- setTimeout(() => {
462
- button.textContent = originalText;
463
- button.style.backgroundColor = '';
464
- }, 2000);
465
- };
466
-
467
- // Initialize wizard when page loads
468
- let wizard;
469
- document.addEventListener('DOMContentLoaded', () => {
470
- wizard = new BaseSetupWizard();
471
- });