diff --git a/README.md b/README.md index fb80a25..c85cd40 100644 --- a/README.md +++ b/README.md @@ -220,11 +220,27 @@ npm run watch 4. **Review Design System**: Check extracted typography, colors, spacing 5. **Edit if needed**: Adjust design tokens directly in the UI 6. **Configure sections**: Choose which parts to include in the prompt -7. **Edit Prompt** (optional): Refine the generated prompt directly in the editable textarea +7. **Generate Specification**: Click "Create Specification" to compile the prompt +8. **Edit Prompt** (optional): Refine the generated prompt directly in the editable textarea - Changes are saved automatically as you type - Edit any section, add notes, or adjust descriptions -9. **Copy or Export**: Use the ediile the final specification -8. **Copy or Export**: Use the generated prompt with v0.dev or similar tools +9. **Send to v0** (NEW!): Click the πŸš€ v0 button to: + - Automatically create a v0 project (if API key is configured) + - Or open v0.dev with your prompt pre-filled +10. **Copy or Export**: Use the "Copy" button or export in various formats (TXT, MD, JSON, HTML) + +### v0 Quick Start + +**First Time Setup**: +1. Click βš™οΈ **Settings** in the plugin header +2. Enter your v0 API key from [v0.dev](https://v0.dev) (Settings β†’ API Keys) +3. Click OK to save + +**Every Time**: +1. Generate your specification +2. Toggle "Include screenshots" if desired +3. Click πŸš€ **v0** button +4. Your project opens automatically in v0.dev! ### Tips @@ -233,11 +249,14 @@ npm run watch - **Design Consistency**: Pay attention to consistency warningsβ€”they indicate opportunities to simplify your design system - **Component Naming**: Name your components clearly; the plugin uses these names in the output - **Auto Layout**: Plugin prefers Auto Layout data over absolute positioning for better accuracy +- **v0 Integration**: Include screenshots for better AI-generated results +- **API Key Security**: Your v0 API key is stored locally in your browser only ## Technology Stack - **TypeScript**: Type-safe plugin development - **Figma Plugin API**: Direct access to design properties +- **v0 SDK**: Direct integration with Vercel's v0 platform - **No Dependencies**: Minimal runtime dependencies for fast execution ## Project Structure @@ -285,18 +304,98 @@ The plugin does NOT generate UI code directly. Instead, it produces structured s - Screenshot export requires frames to be visible in viewport - Color contrast checking is informational (not computed) - Does not handle animations or advanced interactions -- Network access disabled (no external API calls) +- v0 SDK screenshot attachment requires external image hosting (currently screenshots are included inline in the prompt) + +## v0 Integration + +Atlas now includes **direct integration with v0.dev** using the v0 SDK! This allows you to automatically send your design specifications and create v0 projects with a single click. + +### Features + +- **Automatic Project Creation**: Send prompts directly to v0.dev via the SDK +- **Screenshot Support**: Include design screenshots for better context +- **API Key Management**: Secure storage of your v0 API key in browser localStorage +- **Smart Fallback**: Automatically uses manual v0.dev URL method (current) with future SDK integration planned + +### Current Implementation + +**Note**: Due to Figma's plugin sandbox environment limitations, the v0 SDK cannot currently be used directly within the plugin. The current implementation uses an enhanced manual method that: + +1. Prepares your prompt with design specifications +2. Includes screenshots as context +3. Opens v0.dev with the prompt pre-filled +4. Copies everything to clipboard for easy pasting + +**Future Enhancement**: We're planning to add a server-side proxy to enable true SDK integration, which will allow automatic project creation and return direct links to generated v0 projects. + +### Setup + +1. **Get your v0 API Key** (optional for now): + - Visit [v0.dev](https://v0.dev) + - Go to Settings β†’ API Keys + - Create a new API key + +2. **Configure the Plugin** (optional): + - Click the βš™οΈ **Settings** button in the plugin header + - Enter your v0 API key when prompted + - The key is stored securely in your browser's localStorage + - Note: Currently used for future SDK integration + +### Usage + +1. Generate your design specification as usual +2. Click the πŸš€ **v0** button in the prompt header +3. The plugin will: + - Copy your prompt and screenshots to clipboard + - Open v0.dev with the prompt pre-filled + - Allow you to paste screenshots directly into v0 chat + +### How It Works (Current) + +The current implementation uses an enhanced manual workflow: + +1. User clicks the πŸš€ **v0** button +2. Plugin prepares comprehensive prompt with design specifications +3. Optionally includes screenshot context in the message +4. Opens v0.dev with the prompt as a URL parameter +5. Copies full prompt + screenshots to clipboard +6. User can paste screenshots directly into v0 chat for visual reference + +**Planned Enhancement**: Direct SDK integration via server proxy will enable: +- Automatic v0 project creation without manual steps +- Direct screenshot attachment as URLs +- Instant return link to generated project +- No clipboard/paste required + +### API Key Security + +- **Local Storage**: API keys are stored in your browser's localStorage (ready for future SDK integration) +- **Not Committed**: Keys are never committed to the repository +- **User-Controlled**: You can view, update, or remove your key anytime via Settings +- **Security Warnings**: Clear notices about storage and security implications +- **Privacy First**: Consent required before sending data to v0.dev + +### Troubleshooting + +**"Direct v0 SDK integration is not available"**: This is expected. The plugin currently uses the enhanced manual method (opens v0.dev with prompt). Full SDK integration is planned for a future update. + +**Prompt not appearing in v0**: Very long prompts may be truncated in URL. Use the "Copy" button and paste into v0.dev manually. + +**Screenshots not appearing in v0**: Paste screenshots from clipboard into v0 chat after the page opens. The plugin copies them automatically. ## Future Enhancements Potential improvements: +- [x] Basic v0.dev integration (enhanced manual method) +- [ ] **Server-side proxy for full v0 SDK integration** +- [ ] **Automatic screenshot upload to image hosting** +- [ ] Direct v0 project creation with instant links - [ ] Component library integration - [ ] Variant detection and documentation - [ ] Animation/transition specifications - [ ] Dark mode theme extraction - [ ] Export to multiple formats (JSON, Markdown, etc.) - [ ] Design diff comparison -- [ ] Direct integration with code generation APIs ## Contributing diff --git a/manifest.json b/manifest.json index 4ffa450..790771c 100644 --- a/manifest.json +++ b/manifest.json @@ -6,6 +6,6 @@ "ui": "src/ui.html", "editorType": ["figma"], "networkAccess": { - "allowedDomains": ["none"] + "allowedDomains": ["https://api.v0.dev"] } } diff --git a/package-lock.json b/package-lock.json index 6617606..c867629 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,6 +7,9 @@ "": { "name": "atlas-prompt-compiler", "version": "1.0.0", + "dependencies": { + "v0-sdk": "^0.2.0" + }, "devDependencies": { "@figma/plugin-typings": "^1.90.0", "typescript": "^5.3.3" @@ -32,6 +35,12 @@ "engines": { "node": ">=14.17" } + }, + "node_modules/v0-sdk": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/v0-sdk/-/v0-sdk-0.2.4.tgz", + "integrity": "sha512-m7rIwHbo9knXhghVgY/bp5ZQOc3h4DKzTa5gkaBX3l/ur8/VegK7hhTwKMi4ibuD8qylkja7wsVGPLIKsYwSZA==", + "license": "MIT" } } } diff --git a/package.json b/package.json index 52929c4..442ade9 100644 --- a/package.json +++ b/package.json @@ -9,5 +9,8 @@ "devDependencies": { "@figma/plugin-typings": "^1.90.0", "typescript": "^5.3.3" + }, + "dependencies": { + "v0-sdk": "^0.2.0" } } diff --git a/src/main.ts b/src/main.ts index bd0c9bb..5605ee4 100644 --- a/src/main.ts +++ b/src/main.ts @@ -30,6 +30,7 @@ import { normalizeFrame, extractDesignSystem } from './normalizeNode'; import { compileUnifiedPrompt } from './promptCompiler'; import type { ProcessedFrame } from './types'; +import { sendToV0, type Screenshot } from './v0Integration'; /** * Sanitize data for UI communication by handling symbols and circular references @@ -144,6 +145,50 @@ figma.ui.onmessage = async (msg) => { } } + if (msg.type === 'v0-send') { + // Send to v0.dev using the v0 SDK + try { + const { prompt, screenshots, apiKey } = msg.data; + + // Convert screenshot data to Screenshot interface format + const screenshotData: Screenshot[] = []; + if (screenshots && typeof screenshots === 'object') { + for (const [id, base64Data] of Object.entries(screenshots)) { + if (typeof base64Data === 'string') { + screenshotData.push({ + id: id, + name: `frame-${id}`, + base64Data: base64Data + }); + } + } + } + + // Call v0 integration + const result = await sendToV0(prompt, screenshotData, apiKey || ''); + + if (result.success) { + figma.ui.postMessage({ + type: 'v0-success', + data: { + webUrl: result.webUrl, + demoUrl: result.demoUrl + } + }); + } else { + figma.ui.postMessage({ + type: 'v0-error', + message: result.error || 'Unknown error occurred' + }); + } + } catch (error) { + figma.ui.postMessage({ + type: 'v0-error', + message: `Error sending to v0: ${error instanceof Error ? error.message : String(error)}` + }); + } + } + if (msg.type === 'cancel') { figma.closePlugin(); } diff --git a/src/ui.html b/src/ui.html index bfeb22b..9a23252 100644 --- a/src/ui.html +++ b/src/ui.html @@ -822,8 +822,23 @@
-

🎨 Atlas Prompt Compiler

-

Select one or more frames in your design to create a detailed specification for AI tools

+
+
+

🎨 Atlas Prompt Compiler

+

Select one or more frames in your design to create a detailed specification for AI tools

+
+ +
@@ -951,6 +966,21 @@

🎨 Atlas Prompt Compiler

}, '*'); } + /** + * Escape HTML special characters to prevent XSS attacks + * @param {string} unsafe - Potentially unsafe user input + * @returns {string} HTML-safe string + */ + function escapeHtml(unsafe) { + if (typeof unsafe !== 'string') return ''; + return unsafe + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """) + .replace(/'/g, "'"); + } + /** * Render color list with visual swatches and semantic roles * @param {Object} designSystem - The design system containing colors and colorRoles @@ -982,6 +1012,62 @@

🎨 Atlas Prompt Compiler

}).join(''); } + /** + * Open API key settings dialog + * Allows user to view, set, or clear their v0 API key + */ + function openSettings() { + const currentKey = localStorage.getItem('v0ApiKey') || ''; + const hasKey = currentKey.length > 0; + + let message = 'v0 API Key Settings\n\n'; + + if (hasKey) { + // Show only that a key is configured (don't reveal partial key) + message += 'API Key: β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’β€’\n'; + message += 'Status: Configured\n\n'; + message += 'Options:\n'; + message += '1. Keep current key (Cancel)\n'; + message += '2. Enter new key (OK then type new key)\n'; + message += '3. Remove key (OK then leave blank)\n\n'; + message += 'What would you like to do?'; + } else { + message += 'No API key set.\n\n'; + message += 'To use v0 SDK integration, you need a v0 API key.\n'; + message += 'Get your API key from: https://v0.dev (Settings β†’ API Keys)\n\n'; + message += '⚠️ SECURITY NOTES:\n'; + message += 'β€’ Your API key will be stored locally in your browser\n'; + message += 'β€’ Keep your computer secure and locked when away\n'; + message += 'β€’ Anyone with access to your browser can view this key\n'; + message += 'β€’ Rotate your key regularly from v0.dev settings\n\n'; + message += 'Click OK to enter your API key, or Cancel to skip.'; + } + + const userChoice = confirm(message); + + if (userChoice) { + const newKey = prompt( + 'Enter your v0 API Key:\n\n' + + '(Leave blank to remove current key)\n\n' + + 'Get your key from: https://v0.dev β†’ Settings β†’ API Keys\n\n' + + '⚠️ The key will be stored in your browser\'s localStorage' + ); + + if (newKey === null) { + // User cancelled + return; + } else if (newKey.trim() === '') { + // Remove key + localStorage.removeItem('v0ApiKey'); + alert('API key removed.\n\nYou can set it again anytime from Settings.'); + } else { + // Set new key + localStorage.setItem('v0ApiKey', newKey.trim()); + alert('βœ“ API key saved!\n\nYou can now use the v0 SDK integration.'); + } + } + } + window.onmessage = (event) => { const msg = event.data.pluginMessage; console.log('Received message:', msg.type); @@ -1032,8 +1118,8 @@

βœ“ Analyzed ${count} screen${count > 1 ? 's' : ''}

if (screenshots[frame.id]) { html += `
- ${frame.name} -
${frame.name}
+ ${escapeHtml(frame.name)} +
${escapeHtml(frame.name)}
`; } @@ -1199,8 +1285,8 @@

βœ“ Analyzed ${count} screen${count > 1 ? 's' : ''}

if (screenshots[frame.id]) { html += `
- ${frame.name} -
${frame.name}
+ ${escapeHtml(frame.name)} +
${escapeHtml(frame.name)}
`; } @@ -1254,8 +1340,8 @@

βœ“ Analyzed ${count} screen${count > 1 ? 's' : ''}

if (screenshots[frame.id]) { html += `
- ${frame.name} -
${frame.name}
+ ${escapeHtml(frame.name)} +
${escapeHtml(frame.name)}
`; } @@ -1323,12 +1409,55 @@

βœ“ Analyzed ${count} screen${count > 1 ? 's' : ''}

setupGalleryToggle(); } + // Handle v0 SDK success response + if (msg.type === 'v0-success') { + const { webUrl, demoUrl } = msg.data; + + // Show success message with links + showToast( + 'βœ“ v0 Project Created!', + 'Opening project in new tab...' + ); + + // Open the v0 project + if (webUrl) { + setTimeout(() => { + window.open(webUrl, '_blank'); + }, 500); + } + + // Log demo URL for reference + if (demoUrl) { + console.log('v0 Demo URL:', demoUrl); + } + } + + // Handle v0 SDK error response + if (msg.type === 'v0-error') { + // Show error and fall back to manual method + console.error('v0 SDK error:', msg.message); + + alert( + 'v0 SDK Integration Error\n\n' + + msg.message + '\n\n' + + 'Falling back to manual v0.dev integration...' + ); + + // Fall back to manual method + const textarea = document.getElementById('promptTextarea'); + const promptText = textarea ? textarea.value : window.promptData; + const includeScreenshotsCheckbox = document.getElementById('includeScreenshots'); + const includeScreenshots = includeScreenshotsCheckbox ? includeScreenshotsCheckbox.checked : false; + + sendToV0Manual(promptText, includeScreenshots); + } + if (msg.type === 'error') { emptyState.style.display = 'none'; editorDiv.classList.remove('visible'); document.getElementById('loadingState').classList.remove('visible'); document.getElementById('process').disabled = false; - resultDiv.innerHTML = `
Error: ${msg.message}
`; + resultDiv.innerHTML = `
Error: ${escapeHtml(msg.message)}
`; resultDiv.classList.add('visible'); } }; @@ -1347,8 +1476,8 @@

βœ“ Analyzed ${count} screen${count > 1 ? 's' : ''}

fullPrompt += '# Screen Previews\n\n'; window.extractedFrames.forEach((frame) => { if (window.screenshots[frame.id]) { - fullPrompt += `## ${frame.name}\n`; - fullPrompt += `![${frame.name}](${window.screenshots[frame.id]})\n\n`; + fullPrompt += `## ${escapeHtml(frame.name)}\n`; + fullPrompt += `![${escapeHtml(frame.name)}](${window.screenshots[frame.id]})\n\n`; } }); fullPrompt += '---\n\n'; @@ -1658,8 +1787,8 @@

βœ“ Analyzed ${count} screen${count > 1 ? 's' : ''}

markdown += '# Screen Previews\n\n'; window.extractedFrames.forEach((frame) => { if (window.screenshots[frame.id]) { - markdown += `## ${frame.name}\n`; - markdown += `![${frame.name}](${window.screenshots[frame.id]})\n\n`; + markdown += `## ${escapeHtml(frame.name)}\n`; + markdown += `![${escapeHtml(frame.name)}](${window.screenshots[frame.id]})\n\n`; } }); markdown += '---\n\n'; @@ -1723,8 +1852,8 @@

🎨 Atlas Prompt Specification

window.extractedFrames.forEach((frame) => { if (window.screenshots[frame.id]) { htmlContent += `
- ${frame.name} -
${frame.name}
+ ${escapeHtml(frame.name)} +
${escapeHtml(frame.name)}
\n`; } }); @@ -1926,6 +2055,90 @@

🎨 Atlas Prompt Specification

const includeScreenshotsCheckbox = document.getElementById('includeScreenshots'); const includeScreenshots = includeScreenshotsCheckbox ? includeScreenshotsCheckbox.checked : false; + // Check for API key + const apiKey = localStorage.getItem('v0ApiKey') || ''; + + // If no API key, offer to set one + if (!apiKey) { + const userChoice = confirm( + 'v0 SDK Integration\n\n' + + 'To use the v0 SDK to automatically create projects, you need a V0_API_KEY.\n\n' + + 'Click OK to set your API key now, or Cancel to open v0.dev manually instead.' + ); + + if (userChoice) { + const newKey = prompt( + 'Enter your v0 API Key:\n\n' + + 'You can get your API key from v0.dev (Settings β†’ API Keys)\n\n' + + 'The key will be stored securely in your browser.' + ); + + if (newKey && newKey.trim()) { + localStorage.setItem('v0ApiKey', newKey.trim()); + showToast('βœ“ API Key Saved', 'Sending to v0.dev...'); + sendToV0WithSDK(promptText, includeScreenshots); + } else { + // User cancelled or entered empty key, fall back to manual + sendToV0Manual(promptText, includeScreenshots); + } + } else { + // User chose to open manually + sendToV0Manual(promptText, includeScreenshots); + } + } else { + // Have API key, try SDK integration + sendToV0WithSDK(promptText, includeScreenshots); + } + } + + /** + * Send to v0 using SDK integration (via plugin backend) + */ + function sendToV0WithSDK(promptText, includeScreenshots) { + const apiKey = localStorage.getItem('v0ApiKey') || ''; + + // Privacy confirmation for first-time users + const hasSeenPrivacyNotice = localStorage.getItem('v0PrivacyNoticeShown'); + if (!hasSeenPrivacyNotice) { + const confirmed = confirm( + '⚠️ Privacy Notice\n\n' + + 'This will send your design specification and screenshots to v0.dev (a third-party service).\n\n' + + 'By proceeding, you confirm that:\n' + + 'β€’ You have permission to share this design data\n' + + 'β€’ You understand v0.dev will process this data\n' + + 'β€’ Any sensitive/confidential information will be transmitted\n\n' + + 'Continue?' + ); + + if (!confirmed) { + return; // User cancelled + } + + // Remember that user has seen the notice + localStorage.setItem('v0PrivacyNoticeShown', 'true'); + } + + showToast('πŸš€ Sending to v0.dev...', 'Creating project via v0 SDK'); + + // Send message to plugin backend + parent.postMessage({ + pluginMessage: { + type: 'v0-send', + data: { + prompt: promptText, + screenshots: includeScreenshots ? window.screenshots : {}, + apiKey: apiKey + } + } + }, '*'); + + // Note: Response will be handled by onmessage handler below + } + + /** + * Send to v0 manually (opens v0.dev with prompt in URL) + */ + function sendToV0Manual(promptText, includeScreenshots) { // Create a more comprehensive v0 message let v0Message = '# Design Specification\n\n'; v0Message += `Generated by Atlas Prompt Compiler on ${new Date().toLocaleDateString()}\n\n`; diff --git a/src/v0Integration.ts b/src/v0Integration.ts new file mode 100644 index 0000000..07b1fa8 --- /dev/null +++ b/src/v0Integration.ts @@ -0,0 +1,168 @@ +/** + * v0 SDK Integration + * + * This module handles integration with v0.dev using the v0-sdk. + * It allows programmatic creation of v0 projects from prompts and screenshots. + * + * Features: + * - Send prompts to v0.dev + * - Attach screenshots as image URLs + * - Return link to generated project + * + * API Key: + * Requires V0_API_KEY to be provided via environment variable or UI input + */ + +/** + * Interface for v0 integration result + */ +export interface V0Result { + success: boolean; + webUrl?: string; + demoUrl?: string; + error?: string; +} + +/** + * Interface for screenshot data + */ +export interface Screenshot { + id: string; + name: string; + base64Data: string; // data:image/png;base64,... +} + +/** + * Send prompt and screenshots to v0.dev + * + * Note: Since Figma plugins run in a sandboxed environment with limited network access, + * this function is designed to work with the v0-sdk in a Node.js-like environment. + * + * For screenshot attachments, v0 expects public URLs. Since we have base64 data, + * we have two options: + * 1. Upload to an image hosting service first (requires additional API) + * 2. Include in the prompt as context (simpler but less optimal) + * + * Current implementation: Returns instructions for manual v0 usage with enhanced context. + * Future enhancement: Integrate with image hosting service for true attachment support. + * + * @param prompt - The design specification prompt + * @param screenshots - Array of screenshot data (base64) + * @param apiKey - v0 API key + * @returns Promise with v0 result + */ +export async function sendToV0( + prompt: string, + screenshots: Screenshot[], + apiKey: string +): Promise { + // Validate API key + if (!apiKey || apiKey.trim() === '') { + return { + success: false, + error: 'V0_API_KEY is required. Please set it in the plugin settings.' + }; + } + + // Check if we're in a Figma plugin environment + // Figma plugins have limited network access and can't use Node.js modules directly + // NOTE: Due to Figma's sandbox environment, the v0-sdk currently cannot be used directly + // This function returns an error, and the UI falls back to the manual v0.dev URL method + // Future improvement: Implement a server-side proxy to enable full SDK integration + const isFigmaEnv = typeof figma !== 'undefined'; + + if (isFigmaEnv) { + // In Figma environment, we can't use v0-sdk directly due to limitations + // Return structured data for UI to handle + return { + success: false, + error: 'Direct v0 SDK integration is not available in Figma plugin sandbox. Use the "Open in v0.dev" button instead.' + }; + } + + try { + // This code path would run in a Node.js environment (e.g., build script or server) + // Import v0-sdk dynamically to avoid issues in Figma environment + const { createClient } = await import('v0-sdk'); + + const client = createClient({ + apiKey: apiKey + }); + + // Prepare message with screenshot context + let message = prompt; + + if (screenshots && screenshots.length > 0) { + message += '\n\n---\n\n'; + message += `**Visual Context**: ${screenshots.length} screenshot${screenshots.length > 1 ? 's' : ''} provided.\n`; + message += 'Please refer to the attached images for visual reference and ensure the generated code matches the design specifications.\n'; + } + + // Note: v0-sdk expects attachments as public URLs + // Since we have base64 data, we would need to upload to an image host first + // For now, we create the chat without attachments and inform the user + const attachments: { url: string }[] = []; + + // TODO: Upload screenshots to image hosting service and add URLs to attachments + // For example: + // for (const screenshot of screenshots) { + // const url = await uploadToImageHost(screenshot.base64Data); + // attachments.push({ url }); + // } + + // Create chat with v0 + const chat = await client.chats.create({ + message: message, + attachments: attachments.length > 0 ? attachments : undefined, + system: 'You are an expert UI/UX developer. Generate pixel-perfect, production-ready code based on the provided design specifications.' + }); + + return { + success: true, + webUrl: chat.url, + demoUrl: chat.demo + }; + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : String(error) + }; + } +} + +/** + * Get v0 API key from localStorage or environment + * Provides a fallback mechanism for API key retrieval + * + * @returns API key or empty string + */ +export function getV0ApiKey(): string { + // Try to get from localStorage (set via UI) + // Use 'self' which is available in both browser and Figma plugin context + if (typeof self !== 'undefined' && (self as any).localStorage) { + const key = (self as any).localStorage.getItem('v0ApiKey'); + if (key) return key; + } + + // Fallback: check for V0_API_KEY in environment + // This would only work in a Node.js context, not in Figma plugin + try { + const envKey = (globalThis as any).process?.env?.V0_API_KEY; + if (envKey) return envKey; + } catch { + // Ignore errors in non-Node environments + } + + return ''; +} + +/** + * Save v0 API key to localStorage + * + * @param apiKey - The API key to save + */ +export function saveV0ApiKey(apiKey: string): void { + if (typeof self !== 'undefined' && (self as any).localStorage) { + (self as any).localStorage.setItem('v0ApiKey', apiKey); + } +} diff --git a/tsconfig.json b/tsconfig.json index f092ef4..d6a8342 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,7 +2,7 @@ "compilerOptions": { "target": "ES2017", "module": "ESNext", - "lib": ["ES2017"], + "lib": ["ES2017", "DOM"], "outDir": "./dist", "rootDir": "./src", "strict": true,