fastled 1.3.38__py3-none-any.whl → 1.4.0__py3-none-any.whl

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.
fastled/__init__.py CHANGED
@@ -95,7 +95,6 @@ class Api:
95
95
  no_platformio: bool = False,
96
96
  ) -> CompileServer:
97
97
  """Uses docker to spawn a compile server from the given name."""
98
- from fastled.compile_server import CompileServer
99
98
 
100
99
  out = CompileServer(
101
100
  container_name=container_name,
fastled/__version__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # IMPORTANT! There's a bug in github which will REJECT any version update
2
2
  # that has any other change in the repo. Please bump the version as the
3
3
  # ONLY change in a commit, or else the pypi update and the release will fail.
4
- __version__ = "1.3.38"
4
+ __version__ = "1.4.0"
5
5
 
6
6
  __version_url_latest__ = "https://raw.githubusercontent.com/zackees/fastled-wasm/refs/heads/main/src/fastled/__version__.py"
fastled/client_server.py CHANGED
@@ -25,6 +25,16 @@ from fastled.web_compile import (
25
25
  )
26
26
 
27
27
 
28
+ def _always_false() -> bool:
29
+ return False
30
+
31
+
32
+ try:
33
+ from fastled.playwright_browser import is_playwright_available
34
+ except ImportError:
35
+ is_playwright_available = _always_false
36
+
37
+
28
38
  def _create_error_html(error_message: str) -> str:
29
39
  return f"""<!DOCTYPE html>
30
40
  <html>
fastled/open_browser.py CHANGED
@@ -8,6 +8,29 @@ from pathlib import Path
8
8
 
9
9
  from fastled.server_flask import run_flask_in_thread
10
10
 
11
+ try:
12
+ from fastled.playwright_browser import is_playwright_available, open_with_playwright
13
+
14
+ PLAYWRIGHT_AVAILABLE = is_playwright_available()
15
+ except ImportError:
16
+ PLAYWRIGHT_AVAILABLE = False
17
+ open_with_playwright = None
18
+
19
+ # Global reference to keep Playwright browser alive
20
+ _playwright_browser_proxy = None
21
+
22
+
23
+ def cleanup_playwright_browser() -> None:
24
+ """Clean up the Playwright browser on exit."""
25
+ global _playwright_browser_proxy
26
+ if _playwright_browser_proxy:
27
+ _playwright_browser_proxy.close()
28
+ _playwright_browser_proxy = None
29
+
30
+
31
+ # Register cleanup function
32
+ atexit.register(cleanup_playwright_browser)
33
+
11
34
  DEFAULT_PORT = 8089 # different than live version.
12
35
  PYTHON_EXE = sys.executable
13
36
 
@@ -105,14 +128,23 @@ def spawn_http_server(
105
128
 
106
129
  wait_for_server(port)
107
130
  if open_browser:
108
- print(f"Opening browser to http://localhost:{port}")
109
- import webbrowser
110
-
111
- webbrowser.open(
112
- url=f"http://localhost:{port}",
113
- new=1,
114
- autoraise=True,
115
- )
131
+ url = f"http://localhost:{port}"
132
+ if PLAYWRIGHT_AVAILABLE and open_with_playwright is not None:
133
+ print(f"Opening FastLED sketch in Playwright browser: {url}")
134
+ print(
135
+ "Auto-resize enabled: Browser window will automatically adjust to content size"
136
+ )
137
+ global _playwright_browser_proxy
138
+ _playwright_browser_proxy = open_with_playwright(url)
139
+ else:
140
+ print(f"Opening browser to {url}")
141
+ import webbrowser
142
+
143
+ webbrowser.open(
144
+ url=url,
145
+ new=1,
146
+ autoraise=True,
147
+ )
116
148
  return proc
117
149
 
118
150
 
fastled/parse_args.py CHANGED
@@ -82,8 +82,8 @@ def parse_args() -> Args:
82
82
  parser.add_argument(
83
83
  "--ram-disk-size",
84
84
  type=str,
85
- default="0",
86
- help="Set the size of the ramdisk for the docker container. Use suffixes like '25mb' or '1gb'.",
85
+ default="1gb",
86
+ help="Size of the RAM disk for compilation (e.g., '1gb', '512mb')",
87
87
  )
88
88
  parser.add_argument(
89
89
  "--web",
@@ -113,7 +113,7 @@ def parse_args() -> Args:
113
113
  parser.add_argument(
114
114
  "--no-auto-updates",
115
115
  action="store_true",
116
- help="Disable automatic updates of the wasm compiler image when using docker.",
116
+ help="Disable automatic updates of the wasm compiler image when using docker. (Default: False)",
117
117
  )
118
118
  parser.add_argument(
119
119
  "--no-platformio",
@@ -0,0 +1,495 @@
1
+ """
2
+ Playwright browser integration for FastLED WASM compiler.
3
+
4
+ This module provides functionality to open the compiled FastLED sketch
5
+ in a Playwright browser instead of the default system browser when
6
+ the 'full' optional dependency is installed.
7
+ """
8
+
9
+ import asyncio
10
+ import sys
11
+ import warnings
12
+ from typing import TYPE_CHECKING, Any
13
+
14
+ if TYPE_CHECKING:
15
+ from playwright.async_api import Browser, Page
16
+
17
+ try:
18
+ from playwright.async_api import Browser, Page, async_playwright
19
+
20
+ PLAYWRIGHT_AVAILABLE = True
21
+ except ImportError:
22
+ PLAYWRIGHT_AVAILABLE = False
23
+ async_playwright = None
24
+ Browser = Any # type: ignore
25
+ Page = Any # type: ignore
26
+
27
+
28
+ def is_playwright_available() -> bool:
29
+ """Check if Playwright is available."""
30
+ return PLAYWRIGHT_AVAILABLE
31
+
32
+
33
+ class PlaywrightBrowser:
34
+ """Playwright browser manager for FastLED sketches."""
35
+
36
+ def __init__(self, headless: bool = False):
37
+ """Initialize the Playwright browser manager.
38
+
39
+ Args:
40
+ headless: Whether to run the browser in headless mode
41
+ """
42
+ if not PLAYWRIGHT_AVAILABLE:
43
+ raise ImportError(
44
+ "Playwright is not installed. Install with: pip install fastled[full]"
45
+ )
46
+
47
+ self.headless = headless
48
+ self.auto_resize = True # Always enable auto-resize
49
+ self.browser: Any = None
50
+ self.page: Any = None
51
+ self.playwright: Any = None
52
+ self._should_exit = asyncio.Event()
53
+
54
+ async def start(self) -> None:
55
+ """Start the Playwright browser."""
56
+ if self.playwright is None and async_playwright is not None:
57
+ self.playwright = await async_playwright().start()
58
+
59
+ if self.browser is None and self.playwright is not None:
60
+ # Try Chrome first, then Firefox, then WebKit
61
+ try:
62
+ self.browser = await self.playwright.chromium.launch(
63
+ headless=self.headless,
64
+ args=["--disable-web-security", "--allow-running-insecure-content"],
65
+ )
66
+ except Exception:
67
+ try:
68
+ self.browser = await self.playwright.firefox.launch(
69
+ headless=self.headless
70
+ )
71
+ except Exception:
72
+ self.browser = await self.playwright.webkit.launch(
73
+ headless=self.headless
74
+ )
75
+
76
+ if self.page is None and self.browser is not None:
77
+ # Create a new browser context and page
78
+ context = await self.browser.new_context()
79
+ self.page = await context.new_page()
80
+
81
+ async def open_url(self, url: str) -> None:
82
+ """Open a URL in the Playwright browser.
83
+
84
+ Args:
85
+ url: The URL to open
86
+ """
87
+ if self.page is None:
88
+ await self.start()
89
+
90
+ print(f"Opening FastLED sketch in Playwright browser: {url}")
91
+ if self.page is not None:
92
+ await self.page.goto(url)
93
+
94
+ # Wait for the page to load
95
+ await self.page.wait_for_load_state("networkidle")
96
+
97
+ # Set up auto-resizing functionality if enabled
98
+ if self.auto_resize:
99
+ await self._setup_auto_resize()
100
+
101
+ async def _setup_auto_resize(self) -> None:
102
+ """Set up automatic window resizing based on content size."""
103
+ if self.page is None:
104
+ print("[PYTHON] Cannot setup auto-resize: page is None")
105
+ return
106
+
107
+ print(
108
+ "[PYTHON] Setting up browser window tracking with viewport-only adjustment"
109
+ )
110
+
111
+ # Start polling loop that tracks browser window changes and adjusts viewport only
112
+ asyncio.create_task(self._track_browser_adjust_viewport())
113
+
114
+ async def _get_window_info(self) -> dict[str, int] | None:
115
+ """Get browser window dimensions information.
116
+
117
+ Returns:
118
+ Dictionary containing window dimensions or None if unable to retrieve
119
+ """
120
+ if self.page is None:
121
+ return None
122
+
123
+ try:
124
+ return await self.page.evaluate(
125
+ """
126
+ () => {
127
+ return {
128
+ outerWidth: window.outerWidth,
129
+ outerHeight: window.outerHeight,
130
+ innerWidth: window.innerWidth,
131
+ innerHeight: window.innerHeight,
132
+ contentWidth: document.documentElement.clientWidth,
133
+ contentHeight: document.documentElement.clientHeight
134
+ };
135
+ }
136
+ """
137
+ )
138
+ except Exception:
139
+ return None
140
+
141
+ async def _track_browser_adjust_viewport(self) -> None:
142
+ """Track browser window outer size changes and adjust viewport accordingly."""
143
+ if self.page is None:
144
+ return
145
+
146
+ print(
147
+ "[PYTHON] Starting browser window tracking (outer size → viewport adjustment)"
148
+ )
149
+ last_outer_size = None
150
+
151
+ while True:
152
+ try:
153
+ # Wait 1 second between polls
154
+ await asyncio.sleep(1)
155
+
156
+ # Check if page is still alive
157
+ if self.page is None or self.page.is_closed():
158
+ print("[PYTHON] Page closed, signaling exit")
159
+ self._should_exit.set()
160
+ return
161
+
162
+ # Get browser window dimensions
163
+ window_info = await self._get_window_info()
164
+
165
+ if window_info:
166
+ current_outer = (
167
+ window_info["outerWidth"],
168
+ window_info["outerHeight"],
169
+ )
170
+
171
+ # Print current state occasionally
172
+ if last_outer_size is None or current_outer != last_outer_size:
173
+ print(
174
+ f"[PYTHON] Browser: outer={window_info['outerWidth']}x{window_info['outerHeight']}, content={window_info['contentWidth']}x{window_info['contentHeight']}"
175
+ )
176
+
177
+ # Track changes in OUTER window size (user resizes browser)
178
+ if last_outer_size is None or current_outer != last_outer_size:
179
+
180
+ if last_outer_size is not None:
181
+ print("[PYTHON] *** BROWSER WINDOW RESIZED ***")
182
+ print(
183
+ f"[PYTHON] Outer window changed from {last_outer_size[0]}x{last_outer_size[1]} to {current_outer[0]}x{current_outer[1]}"
184
+ )
185
+
186
+ last_outer_size = current_outer
187
+
188
+ # Set viewport to match the outer window size
189
+ if not self.headless:
190
+ try:
191
+ outer_width = int(window_info["outerWidth"])
192
+ outer_height = int(window_info["outerHeight"])
193
+
194
+ print(
195
+ f"[PYTHON] Setting viewport to match outer window size: {outer_width}x{outer_height}"
196
+ )
197
+
198
+ await self.page.set_viewport_size(
199
+ {"width": outer_width, "height": outer_height}
200
+ )
201
+ print("[PYTHON] Viewport set successfully")
202
+
203
+ # Wait briefly for browser to settle after viewport change
204
+ # await asyncio.sleep(0.5)
205
+
206
+ # Query the actual window dimensions after the viewport change
207
+ updated_window_info = await self._get_window_info()
208
+
209
+ if updated_window_info:
210
+
211
+ # Update our tracking with the actual final outer size
212
+ last_outer_size = (
213
+ updated_window_info["outerWidth"],
214
+ updated_window_info["outerHeight"],
215
+ )
216
+ print(
217
+ f"[PYTHON] Updated last_outer_size to actual final size: {last_outer_size}"
218
+ )
219
+ else:
220
+ print("[PYTHON] Could not get updated window info")
221
+
222
+ except Exception as e:
223
+ print(f"[PYTHON] Failed to set viewport: {e}")
224
+
225
+ else:
226
+ print("[PYTHON] Could not get browser window info")
227
+
228
+ except Exception as e:
229
+ print(f"[PYTHON] Error in browser tracking: {e}")
230
+ continue
231
+
232
+ async def wait_for_close(self) -> None:
233
+ """Wait for the browser to be closed."""
234
+ if self.browser is None:
235
+ return
236
+
237
+ try:
238
+ # Wait for the browser to be closed
239
+ while not self.browser.is_closed():
240
+ await asyncio.sleep(1)
241
+ except Exception:
242
+ pass
243
+
244
+ async def close(self) -> None:
245
+ """Close the Playwright browser."""
246
+ if self.page:
247
+ await self.page.close()
248
+ self.page = None
249
+
250
+ if self.browser:
251
+ await self.browser.close()
252
+ self.browser = None
253
+
254
+ if self.playwright:
255
+ await self.playwright.stop()
256
+ self.playwright = None
257
+
258
+
259
+ def run_playwright_browser(url: str, headless: bool = False) -> None:
260
+ """Run Playwright browser in a separate process.
261
+
262
+ Args:
263
+ url: The URL to open
264
+ headless: Whether to run in headless mode
265
+ """
266
+ if not PLAYWRIGHT_AVAILABLE:
267
+ warnings.warn(
268
+ "Playwright is not installed. Install with: pip install fastled[full]. "
269
+ "Falling back to default browser."
270
+ )
271
+ return
272
+
273
+ async def main():
274
+ browser = PlaywrightBrowser(headless=headless)
275
+ try:
276
+ await browser.start()
277
+ await browser.open_url(url)
278
+
279
+ if not headless:
280
+ print("Playwright browser opened. Press Ctrl+C to close.")
281
+ await browser.wait_for_close()
282
+ else:
283
+ # In headless mode, just wait a bit for the page to load
284
+ await asyncio.sleep(2)
285
+
286
+ except KeyboardInterrupt:
287
+ print("\nClosing Playwright browser...")
288
+ finally:
289
+ await browser.close()
290
+
291
+ try:
292
+ asyncio.run(main())
293
+ except KeyboardInterrupt:
294
+ print("\nPlaywright browser closed.")
295
+ except Exception as e:
296
+ warnings.warn(
297
+ f"Playwright browser failed: {e}. Falling back to default browser."
298
+ )
299
+
300
+
301
+ class PlaywrightBrowserProxy:
302
+ """Proxy object to manage Playwright browser lifecycle."""
303
+
304
+ def __init__(self):
305
+ self.process = None
306
+ self.browser_manager = None
307
+ self.monitor_thread = None
308
+ self._closing_intentionally = False
309
+
310
+ def open(self, url: str, headless: bool = False) -> None:
311
+ """Open URL with Playwright browser and keep it alive.
312
+
313
+ Args:
314
+ url: The URL to open
315
+ headless: Whether to run in headless mode
316
+ """
317
+ if not PLAYWRIGHT_AVAILABLE:
318
+ warnings.warn(
319
+ "Playwright is not installed. Install with: pip install fastled[full]. "
320
+ "Falling back to default browser."
321
+ )
322
+ # Fall back to default browser
323
+ import webbrowser
324
+
325
+ webbrowser.open(url)
326
+ return
327
+
328
+ try:
329
+ # Run Playwright in a separate process to avoid blocking
330
+ import multiprocessing
331
+
332
+ self.process = multiprocessing.Process(
333
+ target=run_playwright_browser_persistent,
334
+ args=(url, headless),
335
+ )
336
+ self.process.start()
337
+
338
+ # Start monitoring thread to exit main process when browser subprocess exits
339
+ self._start_monitor_thread()
340
+
341
+ # Register cleanup
342
+ import atexit
343
+
344
+ atexit.register(self.close)
345
+
346
+ except Exception as e:
347
+ warnings.warn(
348
+ f"Failed to start Playwright browser: {e}. Falling back to default browser."
349
+ )
350
+ import webbrowser
351
+
352
+ webbrowser.open(url)
353
+
354
+ def _start_monitor_thread(self) -> None:
355
+ """Start a thread to monitor the browser process and exit main process when it terminates."""
356
+ if self.monitor_thread is not None:
357
+ return
358
+
359
+ import os
360
+ import threading
361
+
362
+ def monitor_process():
363
+ """Monitor the browser process and exit when it terminates."""
364
+ if self.process is None:
365
+ return
366
+
367
+ try:
368
+ # Wait for the process to terminate
369
+ self.process.join()
370
+
371
+ # Check if the process terminated (and we didn't kill it ourselves)
372
+ if (
373
+ self.process.exitcode is not None
374
+ and not self._closing_intentionally
375
+ ):
376
+ print("[MAIN] Browser closed, exiting main program")
377
+ # Force exit the entire program
378
+ os._exit(0)
379
+
380
+ except Exception as e:
381
+ print(f"[MAIN] Error monitoring browser process: {e}")
382
+
383
+ self.monitor_thread = threading.Thread(target=monitor_process, daemon=True)
384
+ self.monitor_thread.start()
385
+
386
+ def close(self) -> None:
387
+ """Close the Playwright browser."""
388
+ if self.process and self.process.is_alive():
389
+ print("Closing Playwright browser...")
390
+ # Mark that we're intentionally closing to prevent monitor from triggering exit
391
+ self._closing_intentionally = True
392
+ self.process.terminate()
393
+ self.process.join(timeout=5)
394
+ if self.process.is_alive():
395
+ self.process.kill()
396
+ self.process = None
397
+
398
+
399
+ def run_playwright_browser_persistent(url: str, headless: bool = False) -> None:
400
+ """Run Playwright browser in a persistent mode that stays alive until terminated.
401
+
402
+ Args:
403
+ url: The URL to open
404
+ headless: Whether to run in headless mode
405
+ """
406
+ if not PLAYWRIGHT_AVAILABLE:
407
+ return
408
+
409
+ async def main():
410
+ browser = PlaywrightBrowser(headless=headless)
411
+ try:
412
+ await browser.start()
413
+ await browser.open_url(url)
414
+
415
+ print(
416
+ "Playwright browser opened. Browser will remain open until the FastLED process exits."
417
+ )
418
+
419
+ # Keep the browser alive until exit is signaled
420
+ while not browser._should_exit.is_set():
421
+ await asyncio.sleep(0.1)
422
+
423
+ except KeyboardInterrupt:
424
+ print("\nClosing Playwright browser...")
425
+ except Exception as e:
426
+ print(f"Playwright browser error: {e}")
427
+ finally:
428
+ await browser.close()
429
+
430
+ try:
431
+ asyncio.run(main())
432
+ except KeyboardInterrupt:
433
+ print("\nPlaywright browser closed.")
434
+ except Exception as e:
435
+ print(f"Playwright browser failed: {e}")
436
+
437
+
438
+ def open_with_playwright(url: str, headless: bool = False) -> PlaywrightBrowserProxy:
439
+ """Open URL with Playwright browser and return a proxy object for lifecycle management.
440
+
441
+ This function can be used as a drop-in replacement for webbrowser.open().
442
+
443
+ Args:
444
+ url: The URL to open
445
+ headless: Whether to run in headless mode
446
+
447
+ Returns:
448
+ PlaywrightBrowserProxy object for managing the browser lifecycle
449
+ """
450
+ proxy = PlaywrightBrowserProxy()
451
+ proxy.open(url, headless)
452
+ return proxy
453
+
454
+
455
+ def install_playwright_browsers() -> bool:
456
+ """Install Playwright browsers if not already installed.
457
+
458
+ Returns:
459
+ True if installation was successful or browsers were already installed
460
+ """
461
+ if not PLAYWRIGHT_AVAILABLE:
462
+ return False
463
+
464
+ try:
465
+ from playwright.sync_api import sync_playwright
466
+
467
+ with sync_playwright() as p:
468
+ # Try to launch a browser to see if it's installed
469
+ try:
470
+ browser = p.chromium.launch(headless=True)
471
+ browser.close()
472
+ return True
473
+ except Exception:
474
+ pass
475
+
476
+ # If we get here, browsers need to be installed
477
+ print("Installing Playwright browsers...")
478
+ import subprocess
479
+
480
+ result = subprocess.run(
481
+ [sys.executable, "-m", "playwright", "install", "chromium"],
482
+ capture_output=True,
483
+ text=True,
484
+ )
485
+
486
+ if result.returncode == 0:
487
+ print("Playwright browsers installed successfully.")
488
+ return True
489
+ else:
490
+ print(f"Failed to install Playwright browsers: {result.stderr}")
491
+ return False
492
+
493
+ except Exception as e:
494
+ print(f"Error installing Playwright browsers: {e}")
495
+ return False
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastled
3
- Version: 1.3.38
3
+ Version: 1.4.0
4
4
  Summary: FastLED Wasm Compiler
5
5
  Home-page: https://github.com/zackees/fastled-wasm
6
6
  Maintainer: Zachary Vorhies
@@ -22,6 +22,8 @@ Requires-Dist: Flask>=3.0.0
22
22
  Requires-Dist: flask-cors>=4.0.0
23
23
  Requires-Dist: livereload
24
24
  Requires-Dist: disklru>=2.0.4
25
+ Provides-Extra: full
26
+ Requires-Dist: playwright>=1.40.0; extra == "full"
25
27
  Dynamic: home-page
26
28
  Dynamic: license-file
27
29
  Dynamic: maintainer
@@ -71,7 +73,34 @@ $ cd mysketchdirectory
71
73
  $ fastled
72
74
  ```
73
75
 
76
+ ## Playwright Browser Support
74
77
 
78
+ For enhanced browser control and automation capabilities, you can install the full version with Playwright support:
79
+
80
+ ```bash
81
+ $ pip install fastled[full]
82
+ ```
83
+
84
+ When installed, FastLED will automatically use Playwright to open your compiled sketch in a controlled browser environment instead of your system's default browser:
85
+
86
+ ```bash
87
+ $ fastled my_sketch
88
+ ```
89
+
90
+ The Playwright browser provides better automation capabilities and is especially useful for:
91
+ - Automated testing of your LED sketches
92
+ - Consistent cross-platform browser behavior
93
+ - Advanced debugging and development workflows
94
+ - Persistent browser sessions that stay open until the FastLED process exits
95
+
96
+ **Key Benefits:**
97
+ - Automatically enabled when `fastled[full]` is installed - no additional flags needed
98
+ - The Playwright browser remains open throughout your development session
99
+ - Automatic cleanup when the FastLED process exits
100
+ - Better control over browser behavior and automation capabilities
101
+ - Consistent behavior across different platforms
102
+
103
+ If Playwright is not installed, the system will gracefully fall back to your default browser.
75
104
 
76
105
  # Install
77
106
 
@@ -81,14 +110,26 @@ This is a python app, so any python package manager will work. We also provide p
81
110
 
82
111
  `pip install fastled`
83
112
 
113
+ ### Pip with Playwright Support
114
+
115
+ `pip install fastled[full]`
116
+
84
117
  ### UV
85
118
 
86
119
  `uv pip install fastled --system`
87
120
 
121
+ ### UV with Playwright Support
122
+
123
+ `uv pip install "fastled[full]" --system`
124
+
88
125
  ### Pipx
89
126
 
90
127
  `pipx install fastled`
91
128
 
129
+ ### Pipx with Playwright Support
130
+
131
+ `pipx install "fastled[full]"`
132
+
92
133
  ### Executables
93
134
 
94
135
  * Windows: https://github.com/zackees/fastled-wasm/releases/latest/download/fastled-windows-x64.zip
@@ -1,12 +1,12 @@
1
- fastled/__init__.py,sha256=NBk5Ef65nIe0F_rvBrSGeoyouFOKFeQXqHrTCJQheeI,7201
1
+ fastled/__init__.py,sha256=ha3O0CDjzbyeMMHkptpxLFt7jWlupMuPav2N3v46JJw,7144
2
2
  fastled/__main__.py,sha256=OcKv2ER1_iQAsZzLIUb3C8hRC9L2clNOhCrjpshrlf4,336
3
- fastled/__version__.py,sha256=9FmVYi6n9qXRl6U6l1Xr9-rsxJ3ncEYDzHGxGM-sA_E,373
3
+ fastled/__version__.py,sha256=8-8HxUEfNauwJ4tp9B9B_cOmZd65cNGlcqReTxEQzUo,372
4
4
  fastled/app.py,sha256=TFVn4qIRdt7dYbpDWudEHrhvD9pwyj9sIGXs4F26nhk,5880
5
5
  fastled/args.py,sha256=kucRGYpff_YKfmMpwWsJh6WIrvW_UPcNlZNFdw15z-Y,3475
6
6
  fastled/cli.py,sha256=drgR2AOxVrj3QEz58iiKscYAumbbin2vIV-k91VCOAA,561
7
7
  fastled/cli_test.py,sha256=W-1nODZrip_JU6BEbYhxOa4ckxduOsiX8zIoRkTyxv4,550
8
8
  fastled/cli_test_interactive.py,sha256=BjNhveZOk5aCffHbcrxPQQjWmAuj4ClVKKcKX5eY6yM,542
9
- fastled/client_server.py,sha256=2H3HBstR0kSKfLrz0LFbBIHy2WYBKc5VzhPxavhdS6o,20393
9
+ fastled/client_server.py,sha256=Gvrc_dvQmEPFOmQvPi7gIj3PLj4yVUVJRO1Z9VqKj9I,20579
10
10
  fastled/compile_server.py,sha256=yQtwLOSKINO1CKD0NWxf-7YQKSatf9sF9RuqaWGOkCs,3038
11
11
  fastled/compile_server_impl.py,sha256=9vTGaDQ0W_g9Xsfy0gC3nJEc2g_pnXcF4VO2U3GLOVg,11982
12
12
  fastled/docker_manager.py,sha256=rkq39ZKrU6NHIyDa3mzs0Unb6o9oMeAwxhqiuHJU_RY,40291
@@ -14,9 +14,10 @@ fastled/filewatcher.py,sha256=gEcJJHTDJ1X3gKJzltmEBhixWGbZj2eJD7a4vwSvITQ,10036
14
14
  fastled/keyboard.py,sha256=UTAsqCn1UMYnB8YDzENiLTj4GeL45tYfEcO7_5fLFEg,3556
15
15
  fastled/keyz.py,sha256=LO-8m_7CpNDiZLM-FXhQ30f9gN1bUYz5lOsUPTIbI-c,4020
16
16
  fastled/live_client.py,sha256=yp_ujG92EHYpSedGOUteuG2nQvMKbp1GbUpgQ6nU4Dc,3083
17
- fastled/open_browser.py,sha256=DFyMrc1qic4Go7eLNPqMaLuMvTaE73NixdfSKV0yyp8,3709
18
- fastled/parse_args.py,sha256=Uu7aNaXtV9afE-z_zpINzykPnFewdN6Goiqgs6Rtt-c,11359
17
+ fastled/open_browser.py,sha256=xYENAjcGqJzDlqga0y4unOSx3GWONX8QV-HoP0StGvo,4778
18
+ fastled/parse_args.py,sha256=MAkoaa0zIQ-LfqAqu_uDBJnKe8HJbvuznnL_ONBX64c,11349
19
19
  fastled/paths.py,sha256=VsPmgu0lNSCFOoEC0BsTYzDygXqy15AHUfN-tTuzDZA,99
20
+ fastled/playwright_browser.py,sha256=SoUZUVfpcGRt6dXoQUASHNgMXIQzFYqN6bBvqsVZDsU,17033
20
21
  fastled/print_filter.py,sha256=nc_rqYYdCUPinFycaK7fiQF5PG1up51pmJptR__QyAs,1499
21
22
  fastled/project_init.py,sha256=bBt4DwmW5hZkm9ICt9Qk-0Nr_0JQM7icCgH5Iv-bCQs,3984
22
23
  fastled/select_sketch_directory.py,sha256=-eudwCns3AKj4HuHtSkZAFwbnf005SNL07pOzs9VxnE,1383
@@ -37,9 +38,9 @@ fastled/site/build.py,sha256=2YKU_UWKlJdGnjdbAbaL0co6kceFMSTVYwH1KCmgPZA,13987
37
38
  fastled/site/examples.py,sha256=s6vj2zJc6BfKlnbwXr1QWY1mzuDBMt6j5MEBOWjO_U8,155
38
39
  fastled/test/can_run_local_docker_tests.py,sha256=LEuUbHctRhNNFWcvnz2kEGmjDJeXO4c3kNpizm3yVJs,400
39
40
  fastled/test/examples.py,sha256=GfaHeY1E8izBl6ZqDVjz--RHLyVR4NRnQ5pBesCFJFY,1673
40
- fastled-1.3.38.dist-info/licenses/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
41
- fastled-1.3.38.dist-info/METADATA,sha256=j0Pj8VUdX6Tvsf6r5lbEfFF9dLO0FCJLHcm3Rw7feuo,30847
42
- fastled-1.3.38.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
43
- fastled-1.3.38.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
44
- fastled-1.3.38.dist-info/top_level.txt,sha256=Bbv5kpJpZhWNCvDF4K0VcvtBSDMa8B7PTOrZa9CezHY,8
45
- fastled-1.3.38.dist-info/RECORD,,
41
+ fastled-1.4.0.dist-info/licenses/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
42
+ fastled-1.4.0.dist-info/METADATA,sha256=ZZz8meXb5dwXovyqYmW5YgL_-2ltHRvkgyfTbxiC5uY,32253
43
+ fastled-1.4.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
44
+ fastled-1.4.0.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
45
+ fastled-1.4.0.dist-info/top_level.txt,sha256=Bbv5kpJpZhWNCvDF4K0VcvtBSDMa8B7PTOrZa9CezHY,8
46
+ fastled-1.4.0.dist-info/RECORD,,