fastled 1.2.6__py3-none-any.whl → 1.2.8__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
@@ -1,6 +1,7 @@
1
1
  """FastLED Wasm Compiler package."""
2
2
 
3
3
  # context
4
+ import subprocess
4
5
  from contextlib import contextmanager
5
6
  from pathlib import Path
6
7
  from typing import Generator
@@ -13,7 +14,7 @@ from .types import BuildMode, CompileResult, CompileServerError
13
14
  # IMPORTANT! There's a bug in github which will REJECT any version update
14
15
  # that has any other change in the repo. Please bump the version as the
15
16
  # ONLY change in a commit, or else the pypi update and the release will fail.
16
- __version__ = "1.2.6"
17
+ __version__ = "1.2.8"
17
18
 
18
19
 
19
20
  class Api:
@@ -138,10 +139,10 @@ class Docker:
138
139
  @staticmethod
139
140
  def purge() -> None:
140
141
  from fastled.docker_manager import DockerManager
141
- from fastled.settings import CONTAINER_NAME
142
+ from fastled.settings import IMAGE_NAME
142
143
 
143
144
  docker_mgr = DockerManager()
144
- docker_mgr.purge(CONTAINER_NAME)
145
+ docker_mgr.purge(image_name=IMAGE_NAME)
145
146
 
146
147
  @staticmethod
147
148
  def build_from_github(
@@ -157,7 +158,6 @@ class Docker:
157
158
  Returns:
158
159
  Container name.
159
160
  """
160
- import subprocess
161
161
 
162
162
  from fastled.docker_manager import DockerManager
163
163
  from fastled.settings import CONTAINER_NAME, IMAGE_NAME
@@ -197,6 +197,14 @@ class Docker:
197
197
 
198
198
  docker_mgr = DockerManager()
199
199
 
200
+ platform_tag = ""
201
+ # if "arm" in docker_mgr.architecture():
202
+ if (
203
+ "arm"
204
+ in subprocess.run(["uname", "-m"], capture_output=True).stdout.decode()
205
+ ):
206
+ platform_tag = "-arm64"
207
+
200
208
  # Build the image
201
209
  docker_mgr.build_image(
202
210
  image_name=IMAGE_NAME,
@@ -204,6 +212,7 @@ class Docker:
204
212
  dockerfile_path=dockerfile_path,
205
213
  build_context=output_dir,
206
214
  build_args={"NO_PREWARM": "1"},
215
+ platform_tag=platform_tag,
207
216
  )
208
217
 
209
218
  # Run the container and return it
@@ -223,7 +232,7 @@ class Docker:
223
232
  def build_from_fastled_repo(
224
233
  project_root: Path | str = Path("."), platform_tag: str = ""
225
234
  ) -> str:
226
- """Build the FastLED WASM compiler Docker image, which will be tagged as "latest".
235
+ """Build the FastLED WASM compiler Docker image, which will be tagged as "main".
227
236
 
228
237
  Args:
229
238
  project_root: Path to the FastLED project root directory
@@ -244,6 +253,17 @@ class Docker:
244
253
 
245
254
  docker_mgr = DockerManager()
246
255
 
256
+ platform_tag = ""
257
+ # if "arm" in docker_mgr.architecture():
258
+ if (
259
+ "arm"
260
+ in subprocess.run(["uname", "-m"], capture_output=True).stdout.decode()
261
+ ):
262
+ platform_tag = "-arm64"
263
+
264
+ # if image exists, remove it
265
+ docker_mgr.purge(image_name=IMAGE_NAME)
266
+
247
267
  # Build the image
248
268
  docker_mgr.build_image(
249
269
  image_name=IMAGE_NAME,
fastled/app.py CHANGED
@@ -65,8 +65,12 @@ def main() -> int:
65
65
  with Api.server(
66
66
  auto_updates=False, container_name=docker_image_name
67
67
  ) as server:
68
- print(f"Server started at {server.url()}")
69
68
  sketch_dir = Path("examples/wasm")
69
+ if args.just_compile:
70
+ result = server.web_compile(sketch_dir)
71
+ print(result)
72
+ return 0
73
+ print(f"Server started at {server.url()}")
70
74
  with Api.live_client(
71
75
  sketch_directory=sketch_dir, host=server
72
76
  ) as client:
fastled/docker_manager.py CHANGED
@@ -4,6 +4,7 @@ New abstraction for Docker management with improved Ctrl+C handling.
4
4
 
5
5
  import _thread
6
6
  import os
7
+ import platform
7
8
  import subprocess
8
9
  import sys
9
10
  import threading
@@ -477,7 +478,7 @@ class DockerManager:
477
478
  print(
478
479
  f"Container {container_name} did not restart within {timeout} seconds."
479
480
  )
480
- container.stop()
481
+ container.stop(timeout=0)
481
482
  print(f"Container {container_name} has been stopped.")
482
483
  container.start()
483
484
  elif container.status == "paused":
@@ -503,6 +504,7 @@ class DockerManager:
503
504
  tty=True,
504
505
  volumes=volumes,
505
506
  ports=ports,
507
+ remove=True,
506
508
  )
507
509
  return container
508
510
 
@@ -554,6 +556,8 @@ class DockerManager:
554
556
  if isinstance(container, str):
555
557
  container = self.get_container(container)
556
558
 
559
+ assert container is not None, "Container not found."
560
+
557
561
  print(f"Attaching to container {container.name}...")
558
562
 
559
563
  first_run = self.first_run
@@ -572,7 +576,11 @@ class DockerManager:
572
576
  print(f"Could not put container {container_name} to sleep.")
573
577
  return
574
578
  try:
575
- container.pause()
579
+ if platform.system() == "Windows":
580
+ container.pause()
581
+ else:
582
+ container.stop()
583
+ container.remove()
576
584
  print(f"Container {container.name} has been suspended.")
577
585
  except KeyboardInterrupt:
578
586
  print(f"Container {container.name} interrupted by keyboard interrupt.")
@@ -585,21 +593,23 @@ class DockerManager:
585
593
  """
586
594
  if isinstance(container, str):
587
595
  container = self.get_container(container)
596
+ if not container:
597
+ print(f"Could not resume container {container}.")
598
+ return
588
599
  try:
589
600
  container.unpause()
590
601
  print(f"Container {container.name} has been resumed.")
591
602
  except Exception as e:
592
603
  print(f"Failed to resume container {container.name}: {e}")
593
604
 
594
- def get_container(self, container_name: str) -> Container:
605
+ def get_container(self, container_name: str) -> Container | None:
595
606
  """
596
607
  Get a container by name.
597
608
  """
598
609
  try:
599
610
  return self.client.containers.get(container_name)
600
611
  except docker.errors.NotFound:
601
- print(f"Container {container_name} not found.")
602
- raise
612
+ return None
603
613
 
604
614
  def is_container_running(self, container_name: str) -> bool:
605
615
  """
@@ -689,11 +699,13 @@ class DockerManager:
689
699
  if any(image_name in tag for tag in container.image.tags):
690
700
  print(f"Removing container {container.name}")
691
701
  container.remove(force=True)
702
+
692
703
  except Exception as e:
693
704
  print(f"Error removing containers: {e}")
694
705
 
695
706
  # Remove all images with this name
696
707
  try:
708
+ self.client.images.prune(filters={"dangling": False})
697
709
  images = self.client.images.list()
698
710
  for image in images:
699
711
  if any(image_name in tag for tag in image.tags):
fastled/keyboard.py CHANGED
@@ -6,12 +6,10 @@ import time
6
6
  from queue import Empty, Queue
7
7
  from threading import Thread
8
8
 
9
- _WHITE_SPACE = [" ", "\r", "\n"]
9
+ _WHITE_SPACE = {" ", "\n", "\r"} # Including Enter key as whitespace
10
10
 
11
11
 
12
- # Original space bar, but now also enter key.
13
12
  class SpaceBarWatcher:
14
-
15
13
  @classmethod
16
14
  def watch_space_bar_pressed(cls, timeout: float = 0) -> bool:
17
15
  watcher = cls()
@@ -32,9 +30,7 @@ class SpaceBarWatcher:
32
30
  self.process.start()
33
31
 
34
32
  def _watch_for_space(self) -> None:
35
- # Set stdin to non-blocking mode
36
33
  fd = sys.stdin.fileno()
37
-
38
34
  if os.name == "nt": # Windows
39
35
  import msvcrt
40
36
 
@@ -51,14 +47,13 @@ class SpaceBarWatcher:
51
47
  char = msvcrt.getch().decode() # type: ignore
52
48
  if char in _WHITE_SPACE:
53
49
  self.queue.put(ord(" "))
54
-
55
50
  else: # Unix-like systems
56
51
  import termios
57
52
  import tty
58
53
 
59
54
  old_settings = termios.tcgetattr(fd) # type: ignore
60
55
  try:
61
- tty.setraw(fd) # type: ignore
56
+ tty.setcbreak(fd) # Use cbreak mode to avoid console issues
62
57
  while True:
63
58
  # Check for cancel signal
64
59
  try:
@@ -70,21 +65,22 @@ class SpaceBarWatcher:
70
65
  # Check if there's input ready
71
66
  if select.select([sys.stdin], [], [], 0.1)[0]:
72
67
  char = sys.stdin.read(1)
73
- if ord(char) == 3: # ctrl+c on mac, maybe also linux?
68
+ if ord(char) == 3: # Ctrl+C
74
69
  _thread.interrupt_main()
75
70
  break
76
-
77
71
  if char in _WHITE_SPACE:
78
72
  self.queue.put(ord(" "))
79
73
  finally:
80
- termios.tcsetattr(fd, termios.TCSADRAIN, old_settings) # type: ignore
74
+ termios.tcsetattr(
75
+ fd, termios.TCSADRAIN, old_settings
76
+ ) # Restore terminal settings
81
77
 
82
78
  def space_bar_pressed(self) -> bool:
83
79
  found = False
84
80
  while not self.queue.empty():
85
81
  try:
86
- key = self.queue.get(block=False, timeout=0.1)
87
- if key == ord(" "):
82
+ key = self.queue.get(block=False)
83
+ if key == ord(" "): # Spacebar
88
84
  found = True
89
85
  self.queue.task_done()
90
86
  except Empty:
fastled/open_browser.py CHANGED
@@ -1,4 +1,3 @@
1
- import os
2
1
  import shutil
3
2
  import socket
4
3
  import subprocess
@@ -17,7 +16,8 @@ def _open_browser_python(fastled_js: Path) -> None:
17
16
  subprocess.run(
18
17
  [sys.executable, "-m", "nodejs.npm", "install", "-g", "live-server"]
19
18
  )
20
- os.system(f"cd {fastled_js} && live-server")
19
+ proc = subprocess.Popen(["cd", fastled_js, "&&", "live-server"])
20
+ proc.wait()
21
21
 
22
22
 
23
23
  def _find_open_port(start_port: int) -> int:
@@ -32,12 +32,10 @@ def _find_open_port(start_port: int) -> int:
32
32
 
33
33
  def _run_server(fastled_js: Path) -> None:
34
34
  """Function to run in separate process that starts the livereload server"""
35
- sys.stderr = open(os.devnull, "w") # Suppress stderr output
36
- _open_browser_python(fastled_js)
35
+
37
36
  try:
38
37
  # Keep the process running
39
- while True:
40
- pass
38
+ _open_browser_python(fastled_js)
41
39
  except KeyboardInterrupt:
42
40
  print("\nShutting down livereload server...")
43
41
 
fastled/site/build.py CHANGED
@@ -189,21 +189,53 @@ INDEX_TEMPLATE = """<!DOCTYPE html>
189
189
  const splashScreen = document.querySelector('.splash-screen');
190
190
  const splashText = document.querySelector('.splash-text');
191
191
 
192
- // Wait for font to load
193
- document.fonts.ready.then(() => {{
194
- // Fade in the text
195
- splashText.style.opacity = '1';
196
-
197
- // Wait for page load plus fade-in time before starting fade-out sequence
198
- window.addEventListener('load', () => {{
199
- setTimeout(() => {{
200
- splashScreen.style.opacity = '0';
201
- setTimeout(() => {{
202
- splashScreen.style.display = 'none';
203
- }}, 500); // Remove from DOM after fade completes
204
- }}, 1500); // Wait for load + 1.5s (giving time for fade-in)
192
+ // Ensure splash screen always gets removed
193
+ const removeSplashScreen = () => {{
194
+ splashScreen.style.opacity = '0';
195
+ setTimeout(() => {{
196
+ splashScreen.style.display = 'none';
197
+ }}, 500);
198
+ }};
199
+
200
+ // Set a maximum time the splash screen can stay
201
+ const maxSplashTime = setTimeout(removeSplashScreen, 2000); // Reduced from 5000ms to 2000ms
202
+
203
+ // Try to do nice fade-in/fade-out when possible
204
+ try {{
205
+ // Add a fallback timer in case font loading fails silently
206
+ const fontTimeout = setTimeout(() => {{
207
+ splashText.style.opacity = '1';
208
+ setTimeout(removeSplashScreen, 1000);
209
+ }}, 1000);
210
+
211
+ Promise.all([
212
+ // Wrap font loading in a timeout promise
213
+ Promise.race([
214
+ document.fonts.ready,
215
+ new Promise((_, reject) => setTimeout(reject, 1500))
216
+ ]),
217
+ new Promise(resolve => {{
218
+ if (document.readyState === 'complete') {{
219
+ resolve();
220
+ }} else {{
221
+ window.addEventListener('load', resolve);
222
+ }}
223
+ }})
224
+ ]).then(() => {{
225
+ clearTimeout(maxSplashTime);
226
+ clearTimeout(fontTimeout);
227
+ splashText.style.opacity = '1';
228
+ setTimeout(removeSplashScreen, 1500);
229
+ }}).catch(() => {{
230
+ // If either promise fails, ensure splash screen is removed
231
+ clearTimeout(maxSplashTime);
232
+ removeSplashScreen();
205
233
  }});
206
- }});
234
+ }} catch (e) {{
235
+ // Final fallback if anything goes wrong
236
+ console.warn('Splash screen error:', e);
237
+ removeSplashScreen();
238
+ }}
207
239
  const links = document.querySelectorAll('.example-link');
208
240
  const iframe = document.getElementById('example-frame');
209
241
  const navPane = document.querySelector('.nav-pane');
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: fastled
3
- Version: 1.2.6
3
+ Version: 1.2.8
4
4
  Summary: FastLED Wasm Compiler
5
5
  Home-page: https://github.com/zackees/fastled-wasm
6
6
  Maintainer: Zachary Vorhies
@@ -186,6 +186,14 @@ with Api.server() as server:
186
186
  client.stop()
187
187
  ```
188
188
 
189
+ **Build Docker Image from a local copy of the FastLED repo**
190
+ ```python
191
+ from fastapi import Docker, Api
192
+ container_name: str = Docker.build_from_fastled_repo()
193
+ with Api.server(container_name=container_name) as server:
194
+ ...
195
+ ```
196
+
189
197
  # Features
190
198
 
191
199
  ## Hot reload by default
@@ -266,6 +274,7 @@ A: A big chunk of space is being used by unnecessary javascript `emscripten` bun
266
274
 
267
275
  # Revisions
268
276
 
277
+ * 1.2.7 - A bunch of fixes for MacOS and probably linux.
269
278
  * 1.2.6 - Now builds image from the project root of FastLED.
270
279
  * 1.1.25 - Fix up paths for `--init`
271
280
  * 1.1.24 - Mac/Linux now properly responds to ctrl-c when waiting for a key event.
@@ -1,14 +1,14 @@
1
- fastled/__init__.py,sha256=MJ3_SJdZ4H4gB-qUEp2pRvzUGqHZae2Wf0BH62eQ-k4,9666
2
- fastled/app.py,sha256=H_uUI5YKF6_cjXN3P5PZwLrkQTlSwKVXvveLEK9Kt4c,2973
1
+ fastled/__init__.py,sha256=uwodiBeAIY3UwJMasclrEVIobhZa40r2tJLZ9LrgGhA,10260
2
+ fastled/app.py,sha256=CgM-pvuiA_IJCesfJkndGtDcXzp0ddGV7YXPvNkngfk,3134
3
3
  fastled/cli.py,sha256=FjVr31ht0UPlAcmX-84NwfAGMQHTkrCe4o744jCAxiw,375
4
4
  fastled/client_server.py,sha256=8L62zNtkGtErDtWrr4XVNsv7ji2zoS5rlqfCnwI3VKU,13177
5
5
  fastled/compile_server.py,sha256=Z7rHFs3M6QPbSCsbgHAQDk6GTVAJMMPCXtD4Y0mu8RM,2659
6
6
  fastled/compile_server_impl.py,sha256=B_7zjdKHxX2JbNcx26hntwtk8-MyQTs6LMZlpOEuloM,8746
7
- fastled/docker_manager.py,sha256=S27-s672d9s4SiurNgcUGC5wtZF_YF8fKlw-Izyew5w,28575
7
+ fastled/docker_manager.py,sha256=Nf0x2BpwT1BvSsi63ZkDhQDMhjiWrjFiXxgowtQMrhM,28955
8
8
  fastled/filewatcher.py,sha256=LwEQJkqADsArZyY499RLAer6JjJyDwaQBcAvT7xmp3c,6708
9
- fastled/keyboard.py,sha256=Uwzfgd85BiZTi5D8f3HRQex9cvOuveagUqjULeTEHw4,3423
9
+ fastled/keyboard.py,sha256=Tf_wJDAhaEWGguoBhWUu_9QRvMCPQEqNZckCsBERcGs,3423
10
10
  fastled/live_client.py,sha256=b9mVJ-8w_zoEDwKIlAEUC5Q1La5W5rR6ct-exOcgJec,2561
11
- fastled/open_browser.py,sha256=uhx_UkZ4URUcFn--ZOGMacjL6gFZHwnMu2qYyok2YzE,1620
11
+ fastled/open_browser.py,sha256=pmywq-OsaCx-5-WPZCHqR2hV9bQU8Skbj4Ub-dnil6Y,1549
12
12
  fastled/parse_args.py,sha256=LD2PpSEhyq3ZqPWa_TJ90Fb-O-yldi7rnen2fWLOPjo,6868
13
13
  fastled/paths.py,sha256=VsPmgu0lNSCFOoEC0BsTYzDygXqy15AHUfN-tTuzDZA,99
14
14
  fastled/project_init.py,sha256=bBt4DwmW5hZkm9ICt9Qk-0Nr_0JQM7icCgH5Iv-bCQs,3984
@@ -21,12 +21,12 @@ fastled/types.py,sha256=PpSEtzFCkWtSIEMC0QXGl966R97vLoryVl3yFW0YhTs,1475
21
21
  fastled/util.py,sha256=t4M3NFMhnCzfYbLvIyJi0RdFssZqbTN_vVIaej1WV-U,265
22
22
  fastled/web_compile.py,sha256=05PeLJ77QQC6PUKjDhsntBmyBola6QQIfF2k-zjYNE4,10261
23
23
  fastled/assets/example.txt,sha256=lTBovRjiz0_TgtAtbA1C5hNi2ffbqnNPqkKg6UiKCT8,54
24
- fastled/site/build.py,sha256=vb1np-3rX5lmPX1Be4OazGtlrsZQYnVDmR2sE4Km7Kw,12536
24
+ fastled/site/build.py,sha256=Eq_u3oRxP88twv5IskKfH3tMr6IuT3I5C5zl94TUECw,13854
25
25
  fastled/test/can_run_local_docker_tests.py,sha256=LEuUbHctRhNNFWcvnz2kEGmjDJeXO4c3kNpizm3yVJs,400
26
26
  fastled/test/examples.py,sha256=6xPwx_k9_XwYTpI1nk4SrYbsJKHJACd30GzD1epGqhY,1597
27
- fastled-1.2.6.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
28
- fastled-1.2.6.dist-info/METADATA,sha256=HZGK3cNdNevOiGDk1_P8bWiuaSrPDj-wJ90Nf23-UYA,19796
29
- fastled-1.2.6.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
30
- fastled-1.2.6.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
31
- fastled-1.2.6.dist-info/top_level.txt,sha256=Bbv5kpJpZhWNCvDF4K0VcvtBSDMa8B7PTOrZa9CezHY,8
32
- fastled-1.2.6.dist-info/RECORD,,
27
+ fastled-1.2.8.dist-info/LICENSE,sha256=b6pOoifSXiUaz_lDS84vWlG3fr4yUKwB8fzkrH9R8bQ,1064
28
+ fastled-1.2.8.dist-info/METADATA,sha256=caqi5xUdIJLL8E1e3fbA7vJoBPhkO00Dyzn5sAwtzYU,20082
29
+ fastled-1.2.8.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
30
+ fastled-1.2.8.dist-info/entry_points.txt,sha256=RCwmzCSOS4-C2i9EziANq7Z2Zb4KFnEMR1FQC0bBwAw,101
31
+ fastled-1.2.8.dist-info/top_level.txt,sha256=Bbv5kpJpZhWNCvDF4K0VcvtBSDMa8B7PTOrZa9CezHY,8
32
+ fastled-1.2.8.dist-info/RECORD,,