figma-console-mcp 0.1.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 (118) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +328 -0
  3. package/dist/browser/base.d.ts +50 -0
  4. package/dist/browser/base.d.ts.map +1 -0
  5. package/dist/browser/base.js +6 -0
  6. package/dist/browser/base.js.map +1 -0
  7. package/dist/browser/local.d.ts +66 -0
  8. package/dist/browser/local.d.ts.map +1 -0
  9. package/dist/browser/local.js +223 -0
  10. package/dist/browser/local.js.map +1 -0
  11. package/dist/cloudflare/browser/base.js +5 -0
  12. package/dist/cloudflare/browser/cloudflare.js +156 -0
  13. package/dist/cloudflare/browser-manager.js +157 -0
  14. package/dist/cloudflare/core/config.js +161 -0
  15. package/dist/cloudflare/core/console-monitor.js +382 -0
  16. package/dist/cloudflare/core/enrichment/enrichment-service.js +272 -0
  17. package/dist/cloudflare/core/enrichment/index.js +7 -0
  18. package/dist/cloudflare/core/enrichment/relationship-mapper.js +351 -0
  19. package/dist/cloudflare/core/enrichment/style-resolver.js +326 -0
  20. package/dist/cloudflare/core/figma-api.js +273 -0
  21. package/dist/cloudflare/core/figma-desktop-connector.js +383 -0
  22. package/dist/cloudflare/core/figma-style-extractor.js +311 -0
  23. package/dist/cloudflare/core/figma-tools.js +2299 -0
  24. package/dist/cloudflare/core/logger.js +53 -0
  25. package/dist/cloudflare/core/snippet-injector.js +96 -0
  26. package/dist/cloudflare/core/types/enriched.js +5 -0
  27. package/dist/cloudflare/core/types/index.js +4 -0
  28. package/dist/cloudflare/index.js +1059 -0
  29. package/dist/cloudflare/test-browser.js +88 -0
  30. package/dist/config.d.ts +17 -0
  31. package/dist/config.d.ts.map +1 -0
  32. package/dist/config.js +141 -0
  33. package/dist/config.js.map +1 -0
  34. package/dist/core/config.d.ts +17 -0
  35. package/dist/core/config.d.ts.map +1 -0
  36. package/dist/core/config.js +162 -0
  37. package/dist/core/config.js.map +1 -0
  38. package/dist/core/console-monitor.d.ts +81 -0
  39. package/dist/core/console-monitor.d.ts.map +1 -0
  40. package/dist/core/console-monitor.js +383 -0
  41. package/dist/core/console-monitor.js.map +1 -0
  42. package/dist/core/enrichment/enrichment-service.d.ts +52 -0
  43. package/dist/core/enrichment/enrichment-service.d.ts.map +1 -0
  44. package/dist/core/enrichment/enrichment-service.js +273 -0
  45. package/dist/core/enrichment/enrichment-service.js.map +1 -0
  46. package/dist/core/enrichment/index.d.ts +8 -0
  47. package/dist/core/enrichment/index.d.ts.map +1 -0
  48. package/dist/core/enrichment/index.js +8 -0
  49. package/dist/core/enrichment/index.js.map +1 -0
  50. package/dist/core/enrichment/relationship-mapper.d.ts +106 -0
  51. package/dist/core/enrichment/relationship-mapper.d.ts.map +1 -0
  52. package/dist/core/enrichment/relationship-mapper.js +352 -0
  53. package/dist/core/enrichment/relationship-mapper.js.map +1 -0
  54. package/dist/core/enrichment/style-resolver.d.ts +80 -0
  55. package/dist/core/enrichment/style-resolver.d.ts.map +1 -0
  56. package/dist/core/enrichment/style-resolver.js +327 -0
  57. package/dist/core/enrichment/style-resolver.js.map +1 -0
  58. package/dist/core/figma-api.d.ts +137 -0
  59. package/dist/core/figma-api.d.ts.map +1 -0
  60. package/dist/core/figma-api.js +274 -0
  61. package/dist/core/figma-api.js.map +1 -0
  62. package/dist/core/figma-desktop-connector.d.ts +52 -0
  63. package/dist/core/figma-desktop-connector.d.ts.map +1 -0
  64. package/dist/core/figma-desktop-connector.js +384 -0
  65. package/dist/core/figma-desktop-connector.js.map +1 -0
  66. package/dist/core/figma-style-extractor.d.ts +76 -0
  67. package/dist/core/figma-style-extractor.d.ts.map +1 -0
  68. package/dist/core/figma-style-extractor.js +312 -0
  69. package/dist/core/figma-style-extractor.js.map +1 -0
  70. package/dist/core/figma-tools.d.ts +15 -0
  71. package/dist/core/figma-tools.d.ts.map +1 -0
  72. package/dist/core/figma-tools.js +2300 -0
  73. package/dist/core/figma-tools.js.map +1 -0
  74. package/dist/core/logger.d.ts +22 -0
  75. package/dist/core/logger.d.ts.map +1 -0
  76. package/dist/core/logger.js +54 -0
  77. package/dist/core/logger.js.map +1 -0
  78. package/dist/core/snippet-injector.d.ts +24 -0
  79. package/dist/core/snippet-injector.d.ts.map +1 -0
  80. package/dist/core/snippet-injector.js +97 -0
  81. package/dist/core/snippet-injector.js.map +1 -0
  82. package/dist/core/types/enriched.d.ts +213 -0
  83. package/dist/core/types/enriched.d.ts.map +1 -0
  84. package/dist/core/types/enriched.js +6 -0
  85. package/dist/core/types/enriched.js.map +1 -0
  86. package/dist/core/types/index.d.ts +112 -0
  87. package/dist/core/types/index.d.ts.map +1 -0
  88. package/dist/core/types/index.js +5 -0
  89. package/dist/core/types/index.js.map +1 -0
  90. package/dist/index.d.ts +8 -0
  91. package/dist/index.d.ts.map +1 -0
  92. package/dist/index.js +72 -0
  93. package/dist/index.js.map +1 -0
  94. package/dist/local.d.ts +57 -0
  95. package/dist/local.d.ts.map +1 -0
  96. package/dist/local.js +668 -0
  97. package/dist/local.js.map +1 -0
  98. package/dist/logger.d.ts +22 -0
  99. package/dist/logger.d.ts.map +1 -0
  100. package/dist/logger.js +45 -0
  101. package/dist/logger.js.map +1 -0
  102. package/dist/server.d.ts +40 -0
  103. package/dist/server.d.ts.map +1 -0
  104. package/dist/server.js +99 -0
  105. package/dist/server.js.map +1 -0
  106. package/dist/tools/index.d.ts +15 -0
  107. package/dist/tools/index.d.ts.map +1 -0
  108. package/dist/tools/index.js +184 -0
  109. package/dist/tools/index.js.map +1 -0
  110. package/dist/types/index.d.ts +102 -0
  111. package/dist/types/index.d.ts.map +1 -0
  112. package/dist/types/index.js +6 -0
  113. package/dist/types/index.js.map +1 -0
  114. package/figma-desktop-bridge/README.md +232 -0
  115. package/figma-desktop-bridge/code.js +133 -0
  116. package/figma-desktop-bridge/manifest.json +13 -0
  117. package/figma-desktop-bridge/ui.html +200 -0
  118. package/package.json +77 -0
@@ -0,0 +1,223 @@
1
+ /**
2
+ * Local Browser Manager
3
+ * Connects to Figma Desktop via Chrome Remote Debugging Protocol
4
+ */
5
+ import puppeteer from 'puppeteer-core';
6
+ import { createChildLogger } from '../core/logger.js';
7
+ const logger = createChildLogger({ component: 'local-browser' });
8
+ /**
9
+ * Local Browser Manager
10
+ * Connects to existing Figma Desktop instance via remote debugging port
11
+ */
12
+ export class LocalBrowserManager {
13
+ constructor(config) {
14
+ this.browser = null;
15
+ this.page = null;
16
+ this.config = config;
17
+ }
18
+ /**
19
+ * Connect to Figma Desktop via remote debugging port
20
+ */
21
+ async launch() {
22
+ if (this.browser) {
23
+ logger.info('Browser already connected, reusing instance');
24
+ return;
25
+ }
26
+ const { debugHost, debugPort } = this.config;
27
+ const browserURL = `http://${debugHost}:${debugPort}`;
28
+ logger.info({ browserURL }, 'Connecting to Figma Desktop');
29
+ try {
30
+ // Connect to existing browser (Figma Desktop)
31
+ this.browser = await puppeteer.connect({
32
+ browserURL,
33
+ defaultViewport: null, // Use Figma's viewport
34
+ });
35
+ logger.info('Connected to Figma Desktop successfully');
36
+ // Handle disconnection
37
+ this.browser.on('disconnected', () => {
38
+ logger.warn('Disconnected from Figma Desktop');
39
+ this.browser = null;
40
+ this.page = null;
41
+ });
42
+ }
43
+ catch (error) {
44
+ logger.error({ error, browserURL }, 'Failed to connect to Figma Desktop');
45
+ throw new Error(`Failed to connect to Figma Desktop at ${browserURL}.\n\n` +
46
+ `Make sure:\n` +
47
+ `1. Figma Desktop is running\n` +
48
+ `2. Figma was launched with: --remote-debugging-port=${debugPort}\n` +
49
+ `3. "Use Developer VM" is enabled in: Plugins → Development → Use Developer VM\n\n` +
50
+ `macOS launch command:\n` +
51
+ ` open -a "Figma" --args --remote-debugging-port=${debugPort}\n\n` +
52
+ `Error: ${error instanceof Error ? error.message : String(error)}`);
53
+ }
54
+ }
55
+ /**
56
+ * Find the best page for plugin debugging
57
+ * Actively searches for pages with workers across ALL tabs
58
+ */
59
+ async findBestPage() {
60
+ if (!this.browser) {
61
+ return null;
62
+ }
63
+ const pages = await this.browser.pages();
64
+ // Find Figma pages with workers
65
+ const figmaPages = pages.filter(p => {
66
+ const url = p.url();
67
+ return url.includes('figma.com') && !url.includes('devtools');
68
+ });
69
+ if (figmaPages.length === 0) {
70
+ return null;
71
+ }
72
+ // Check each page for workers
73
+ const pagesWithWorkers = figmaPages
74
+ .map(p => ({
75
+ page: p,
76
+ workerCount: p.workers().length,
77
+ url: p.url()
78
+ }))
79
+ .filter(p => p.workerCount > 0)
80
+ .sort((a, b) => b.workerCount - a.workerCount); // Most workers first
81
+ if (pagesWithWorkers.length > 0) {
82
+ logger.info({
83
+ url: pagesWithWorkers[0].url,
84
+ workerCount: pagesWithWorkers[0].workerCount,
85
+ totalPagesWithWorkers: pagesWithWorkers.length
86
+ }, 'Found page with active plugin workers');
87
+ return pagesWithWorkers[0].page;
88
+ }
89
+ // No workers found - prefer design/file pages
90
+ const designPage = figmaPages.find(p => p.url().includes('/design/') || p.url().includes('/file/'));
91
+ return designPage || figmaPages[0];
92
+ }
93
+ /**
94
+ * Get active Figma page or create new one
95
+ * Prefers pages with active plugin workers for plugin debugging
96
+ */
97
+ async getPage() {
98
+ if (!this.browser) {
99
+ await this.launch();
100
+ }
101
+ // ALWAYS re-check for best page (don't cache if we might have missed workers)
102
+ const bestPage = await this.findBestPage();
103
+ if (bestPage) {
104
+ const workerCount = bestPage.workers().length;
105
+ logger.info({
106
+ url: bestPage.url(),
107
+ workerCount,
108
+ cached: this.page === bestPage
109
+ }, 'Selected page for monitoring');
110
+ this.page = bestPage;
111
+ return this.page;
112
+ }
113
+ // Fallback: Get any existing page or create new one
114
+ const pages = await this.browser.pages();
115
+ if (pages.length > 0 && pages[0].url() !== 'about:blank') {
116
+ logger.warn({ url: pages[0].url() }, 'No Figma pages found, using first available page');
117
+ this.page = pages[0];
118
+ return this.page;
119
+ }
120
+ // Last resort: Create new page
121
+ logger.warn('No suitable pages found, creating new page in Figma Desktop');
122
+ this.page = await this.browser.newPage();
123
+ return this.page;
124
+ }
125
+ /**
126
+ * Navigate to Figma URL
127
+ */
128
+ async navigateToFigma(figmaUrl) {
129
+ const page = await this.getPage();
130
+ // Default to Figma homepage if no URL provided
131
+ const url = figmaUrl || 'https://www.figma.com';
132
+ logger.info({ url }, 'Navigating to Figma');
133
+ try {
134
+ await page.goto(url, {
135
+ waitUntil: 'networkidle2',
136
+ timeout: 30000,
137
+ });
138
+ logger.info({ url }, 'Navigation successful');
139
+ return page;
140
+ }
141
+ catch (error) {
142
+ logger.error({ error, url }, 'Navigation failed');
143
+ throw new Error(`Failed to navigate to ${url}: ${error}`);
144
+ }
145
+ }
146
+ /**
147
+ * Reload current page
148
+ */
149
+ async reload(hardReload = false) {
150
+ if (!this.page || this.page.isClosed()) {
151
+ throw new Error('No active page to reload');
152
+ }
153
+ logger.info({ hardReload }, 'Reloading page');
154
+ try {
155
+ await this.page.reload({
156
+ waitUntil: 'networkidle2',
157
+ timeout: 30000,
158
+ });
159
+ logger.info('Page reloaded successfully');
160
+ }
161
+ catch (error) {
162
+ logger.error({ error }, 'Page reload failed');
163
+ throw new Error(`Page reload failed: ${error}`);
164
+ }
165
+ }
166
+ /**
167
+ * Execute JavaScript in page context
168
+ */
169
+ async evaluate(fn) {
170
+ const page = await this.getPage();
171
+ return page.evaluate(fn);
172
+ }
173
+ // Screenshot functionality removed - use Figma REST API's getImages() instead
174
+ // See: figma_take_screenshot and figma_get_component_image tools
175
+ /**
176
+ * Check if browser is connected
177
+ */
178
+ isRunning() {
179
+ return this.browser !== null && this.browser.isConnected();
180
+ }
181
+ /**
182
+ * Disconnect from browser (doesn't close Figma Desktop)
183
+ */
184
+ async close() {
185
+ if (!this.browser) {
186
+ return;
187
+ }
188
+ logger.info('Disconnecting from Figma Desktop');
189
+ try {
190
+ // Just disconnect, don't close Figma Desktop
191
+ this.browser.disconnect();
192
+ this.browser = null;
193
+ this.page = null;
194
+ logger.info('Disconnected from Figma Desktop successfully');
195
+ }
196
+ catch (error) {
197
+ logger.error({ error }, 'Failed to disconnect from browser');
198
+ throw error;
199
+ }
200
+ }
201
+ /**
202
+ * Get current page URL
203
+ */
204
+ getCurrentUrl() {
205
+ if (!this.page || this.page.isClosed()) {
206
+ return null;
207
+ }
208
+ return this.page.url();
209
+ }
210
+ /**
211
+ * Wait for navigation
212
+ */
213
+ async waitForNavigation(timeout = 30000) {
214
+ if (!this.page || this.page.isClosed()) {
215
+ throw new Error('No active page');
216
+ }
217
+ await this.page.waitForNavigation({
218
+ waitUntil: 'networkidle2',
219
+ timeout,
220
+ });
221
+ }
222
+ }
223
+ //# sourceMappingURL=local.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local.js","sourceRoot":"","sources":["../../src/browser/local.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Browser Manager Interface
3
+ * Abstract interface for browser automation across different runtimes
4
+ */
5
+ export {};
@@ -0,0 +1,156 @@
1
+ /**
2
+ * Cloudflare Browser Manager
3
+ * Manages Puppeteer browser instance lifecycle for Cloudflare Browser Rendering API
4
+ */
5
+ import puppeteer from '@cloudflare/puppeteer';
6
+ import { createChildLogger } from '../core/logger.js';
7
+ const logger = createChildLogger({ component: 'cloudflare-browser' });
8
+ /**
9
+ * Cloudflare Browser Manager
10
+ * Implements IBrowserManager for Cloudflare Browser Rendering API
11
+ */
12
+ export class CloudflareBrowserManager {
13
+ constructor(env, config) {
14
+ this.browser = null;
15
+ this.page = null;
16
+ this.env = env;
17
+ this.config = config;
18
+ }
19
+ /**
20
+ * Launch browser instance via Cloudflare Browser Rendering API
21
+ */
22
+ async launch() {
23
+ if (this.browser) {
24
+ logger.info('Browser already running, reusing instance');
25
+ return;
26
+ }
27
+ logger.info('Launching browser with Cloudflare Browser Rendering API');
28
+ try {
29
+ this.browser = await puppeteer.launch(this.env.BROWSER, {
30
+ keep_alive: 600000, // Keep alive for 10 minutes
31
+ });
32
+ logger.info('Browser launched successfully');
33
+ }
34
+ catch (error) {
35
+ logger.error({ error }, 'Failed to launch browser');
36
+ throw new Error(`Browser launch failed: ${error}`);
37
+ }
38
+ }
39
+ /**
40
+ * Get or create a page instance
41
+ */
42
+ async getPage() {
43
+ if (!this.browser) {
44
+ await this.launch();
45
+ }
46
+ if (this.page && !this.page.isClosed()) {
47
+ return this.page;
48
+ }
49
+ logger.info('Creating new browser page');
50
+ this.page = await this.browser.newPage();
51
+ // Set viewport size
52
+ await this.page.setViewport({
53
+ width: 1920,
54
+ height: 1080,
55
+ deviceScaleFactor: 1,
56
+ });
57
+ logger.info('Browser page created');
58
+ return this.page;
59
+ }
60
+ /**
61
+ * Navigate to Figma URL
62
+ */
63
+ async navigateToFigma(figmaUrl) {
64
+ const page = await this.getPage();
65
+ // Default to Figma homepage if no URL provided
66
+ const url = figmaUrl || 'https://www.figma.com';
67
+ logger.info({ url }, 'Navigating to Figma');
68
+ try {
69
+ await page.goto(url, {
70
+ waitUntil: 'networkidle2',
71
+ timeout: 30000,
72
+ });
73
+ logger.info({ url }, 'Navigation successful');
74
+ return page;
75
+ }
76
+ catch (error) {
77
+ logger.error({ error, url }, 'Navigation failed');
78
+ throw new Error(`Failed to navigate to ${url}: ${error}`);
79
+ }
80
+ }
81
+ /**
82
+ * Reload current page
83
+ */
84
+ async reload(hardReload = false) {
85
+ if (!this.page || this.page.isClosed()) {
86
+ throw new Error('No active page to reload');
87
+ }
88
+ logger.info({ hardReload }, 'Reloading page');
89
+ try {
90
+ await this.page.reload({
91
+ waitUntil: 'networkidle2',
92
+ timeout: 30000,
93
+ });
94
+ logger.info('Page reloaded successfully');
95
+ }
96
+ catch (error) {
97
+ logger.error({ error }, 'Page reload failed');
98
+ throw new Error(`Page reload failed: ${error}`);
99
+ }
100
+ }
101
+ /**
102
+ * Execute JavaScript in page context
103
+ */
104
+ async evaluate(fn) {
105
+ const page = await this.getPage();
106
+ return page.evaluate(fn);
107
+ }
108
+ // Screenshot functionality removed - use Figma REST API's getImages() instead
109
+ // See: figma_take_screenshot and figma_get_component_image tools
110
+ /**
111
+ * Check if browser is running
112
+ */
113
+ isRunning() {
114
+ return this.browser !== null && this.browser.isConnected();
115
+ }
116
+ /**
117
+ * Close browser instance
118
+ */
119
+ async close() {
120
+ if (!this.browser) {
121
+ return;
122
+ }
123
+ logger.info('Closing browser');
124
+ try {
125
+ await this.browser.close();
126
+ this.browser = null;
127
+ this.page = null;
128
+ logger.info('Browser closed successfully');
129
+ }
130
+ catch (error) {
131
+ logger.error({ error }, 'Failed to close browser');
132
+ throw error;
133
+ }
134
+ }
135
+ /**
136
+ * Get current page URL
137
+ */
138
+ getCurrentUrl() {
139
+ if (!this.page || this.page.isClosed()) {
140
+ return null;
141
+ }
142
+ return this.page.url();
143
+ }
144
+ /**
145
+ * Wait for navigation
146
+ */
147
+ async waitForNavigation(timeout = 30000) {
148
+ if (!this.page || this.page.isClosed()) {
149
+ throw new Error('No active page');
150
+ }
151
+ await this.page.waitForNavigation({
152
+ waitUntil: 'networkidle2',
153
+ timeout,
154
+ });
155
+ }
156
+ }
@@ -0,0 +1,157 @@
1
+ /**
2
+ * Browser Manager
3
+ * Manages Puppeteer browser instance lifecycle for Cloudflare Browser Rendering API
4
+ */
5
+ import puppeteer from '@cloudflare/puppeteer';
6
+ import { createChildLogger } from './core/logger.js';
7
+ const logger = createChildLogger({ component: 'browser-manager' });
8
+ /**
9
+ * Browser Manager
10
+ * Handles browser instance creation, page management, and navigation
11
+ */
12
+ export class BrowserManager {
13
+ constructor(env, config) {
14
+ this.browser = null;
15
+ this.page = null;
16
+ this.env = env;
17
+ this.config = config;
18
+ }
19
+ /**
20
+ * Launch browser instance
21
+ */
22
+ async launch() {
23
+ if (this.browser) {
24
+ logger.info('Browser already running, reusing instance');
25
+ return this.browser;
26
+ }
27
+ logger.info('Launching browser with Cloudflare Browser Rendering API');
28
+ try {
29
+ this.browser = await puppeteer.launch(this.env.BROWSER, {
30
+ keep_alive: 600000, // Keep alive for 10 minutes
31
+ });
32
+ logger.info('Browser launched successfully');
33
+ return this.browser;
34
+ }
35
+ catch (error) {
36
+ logger.error({ error }, 'Failed to launch browser');
37
+ throw new Error(`Browser launch failed: ${error}`);
38
+ }
39
+ }
40
+ /**
41
+ * Get or create a page instance
42
+ */
43
+ async getPage() {
44
+ if (!this.browser) {
45
+ await this.launch();
46
+ }
47
+ if (this.page && !this.page.isClosed()) {
48
+ return this.page;
49
+ }
50
+ logger.info('Creating new browser page');
51
+ this.page = await this.browser.newPage();
52
+ // Set viewport size
53
+ await this.page.setViewport({
54
+ width: 1920,
55
+ height: 1080,
56
+ deviceScaleFactor: 1,
57
+ });
58
+ logger.info('Browser page created');
59
+ return this.page;
60
+ }
61
+ /**
62
+ * Navigate to Figma URL
63
+ */
64
+ async navigateToFigma(figmaUrl) {
65
+ const page = await this.getPage();
66
+ // Default to Figma homepage if no URL provided
67
+ const url = figmaUrl || 'https://www.figma.com';
68
+ logger.info({ url }, 'Navigating to Figma');
69
+ try {
70
+ await page.goto(url, {
71
+ waitUntil: 'networkidle2',
72
+ timeout: 30000,
73
+ });
74
+ logger.info({ url }, 'Navigation successful');
75
+ return page;
76
+ }
77
+ catch (error) {
78
+ logger.error({ error, url }, 'Navigation failed');
79
+ throw new Error(`Failed to navigate to ${url}: ${error}`);
80
+ }
81
+ }
82
+ /**
83
+ * Reload current page
84
+ */
85
+ async reload(hardReload = false) {
86
+ if (!this.page || this.page.isClosed()) {
87
+ throw new Error('No active page to reload');
88
+ }
89
+ logger.info({ hardReload }, 'Reloading page');
90
+ try {
91
+ await this.page.reload({
92
+ waitUntil: 'networkidle2',
93
+ timeout: 30000,
94
+ });
95
+ logger.info('Page reloaded successfully');
96
+ }
97
+ catch (error) {
98
+ logger.error({ error }, 'Page reload failed');
99
+ throw new Error(`Page reload failed: ${error}`);
100
+ }
101
+ }
102
+ /**
103
+ * Execute JavaScript in page context
104
+ */
105
+ async evaluate(fn) {
106
+ const page = await this.getPage();
107
+ return page.evaluate(fn);
108
+ }
109
+ // Screenshot functionality removed - use Figma REST API's getImages() instead
110
+ // See: figma_take_screenshot and figma_get_component_image tools
111
+ /**
112
+ * Check if browser is running
113
+ */
114
+ isRunning() {
115
+ return this.browser !== null && this.browser.isConnected();
116
+ }
117
+ /**
118
+ * Close browser instance
119
+ */
120
+ async close() {
121
+ if (!this.browser) {
122
+ return;
123
+ }
124
+ logger.info('Closing browser');
125
+ try {
126
+ await this.browser.close();
127
+ this.browser = null;
128
+ this.page = null;
129
+ logger.info('Browser closed successfully');
130
+ }
131
+ catch (error) {
132
+ logger.error({ error }, 'Failed to close browser');
133
+ throw error;
134
+ }
135
+ }
136
+ /**
137
+ * Get current page URL
138
+ */
139
+ getCurrentUrl() {
140
+ if (!this.page || this.page.isClosed()) {
141
+ return null;
142
+ }
143
+ return this.page.url();
144
+ }
145
+ /**
146
+ * Wait for navigation
147
+ */
148
+ async waitForNavigation(timeout = 30000) {
149
+ if (!this.page || this.page.isClosed()) {
150
+ throw new Error('No active page');
151
+ }
152
+ await this.page.waitForNavigation({
153
+ waitUntil: 'networkidle2',
154
+ timeout,
155
+ });
156
+ }
157
+ }