fastled 1.2.68__tar.gz → 1.2.73__tar.gz

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 (129) hide show
  1. {fastled-1.2.68 → fastled-1.2.73}/PKG-INFO +1 -1
  2. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/__init__.py +1 -1
  3. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/app.py +5 -0
  4. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/compile_server_impl.py +26 -16
  5. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/docker_manager.py +112 -15
  6. fastled-1.2.73/src/fastled/keyz.py +84 -0
  7. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/open_browser.py +3 -13
  8. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/parse_args.py +2 -1
  9. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/string_diff.py +22 -0
  10. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/util.py +9 -0
  11. {fastled-1.2.68 → fastled-1.2.73}/src/fastled.egg-info/PKG-INFO +1 -1
  12. fastled-1.2.68/src/fastled/keyz.py +0 -31
  13. {fastled-1.2.68 → fastled-1.2.73}/.aiderignore +0 -0
  14. {fastled-1.2.68 → fastled-1.2.73}/.dockerignore +0 -0
  15. {fastled-1.2.68 → fastled-1.2.73}/.github/workflows/build_multi_docker_image.yml +0 -0
  16. {fastled-1.2.68 → fastled-1.2.73}/.github/workflows/build_webpage.yml +0 -0
  17. {fastled-1.2.68 → fastled-1.2.73}/.github/workflows/lint.yml +0 -0
  18. {fastled-1.2.68 → fastled-1.2.73}/.github/workflows/publish_release.yml +0 -0
  19. {fastled-1.2.68 → fastled-1.2.73}/.github/workflows/template_build_docker_image.yml +0 -0
  20. {fastled-1.2.68 → fastled-1.2.73}/.github/workflows/test_build_exe.yml +0 -0
  21. {fastled-1.2.68 → fastled-1.2.73}/.github/workflows/test_macos.yml +0 -0
  22. {fastled-1.2.68 → fastled-1.2.73}/.github/workflows/test_ubuntu.yml +0 -0
  23. {fastled-1.2.68 → fastled-1.2.73}/.github/workflows/test_win.yml +0 -0
  24. {fastled-1.2.68 → fastled-1.2.73}/.gitignore +0 -0
  25. {fastled-1.2.68 → fastled-1.2.73}/.pylintrc +0 -0
  26. {fastled-1.2.68 → fastled-1.2.73}/.vscode/launch.json +0 -0
  27. {fastled-1.2.68 → fastled-1.2.73}/.vscode/settings.json +0 -0
  28. {fastled-1.2.68 → fastled-1.2.73}/.vscode/tasks.json +0 -0
  29. {fastled-1.2.68 → fastled-1.2.73}/Dockerfile +0 -0
  30. {fastled-1.2.68 → fastled-1.2.73}/LICENSE +0 -0
  31. {fastled-1.2.68 → fastled-1.2.73}/MANIFEST.in +0 -0
  32. {fastled-1.2.68 → fastled-1.2.73}/README.md +0 -0
  33. {fastled-1.2.68 → fastled-1.2.73}/RELEASE.md +0 -0
  34. {fastled-1.2.68 → fastled-1.2.73}/TODO.md +0 -0
  35. {fastled-1.2.68 → fastled-1.2.73}/build_exe.py +0 -0
  36. {fastled-1.2.68 → fastled-1.2.73}/build_site.py +0 -0
  37. {fastled-1.2.68 → fastled-1.2.73}/clean +0 -0
  38. {fastled-1.2.68 → fastled-1.2.73}/compiler/CMakeLists.txt +0 -0
  39. {fastled-1.2.68 → fastled-1.2.73}/compiler/__init__.py +0 -0
  40. {fastled-1.2.68 → fastled-1.2.73}/compiler/arduino-pre-process.sh +0 -0
  41. {fastled-1.2.68 → fastled-1.2.73}/compiler/build.sh +0 -0
  42. {fastled-1.2.68 → fastled-1.2.73}/compiler/build_archive.sh +0 -0
  43. {fastled-1.2.68 → fastled-1.2.73}/compiler/build_fast.sh +0 -0
  44. {fastled-1.2.68 → fastled-1.2.73}/compiler/code_sync.py +0 -0
  45. {fastled-1.2.68 → fastled-1.2.73}/compiler/compile.py +0 -0
  46. {fastled-1.2.68 → fastled-1.2.73}/compiler/compile_lock.py +0 -0
  47. {fastled-1.2.68 → fastled-1.2.73}/compiler/entrypoint.sh +0 -0
  48. {fastled-1.2.68 → fastled-1.2.73}/compiler/extra/100dots.html +0 -0
  49. {fastled-1.2.68 → fastled-1.2.73}/compiler/extra/demo_threejs.html +0 -0
  50. {fastled-1.2.68 → fastled-1.2.73}/compiler/extra/micdemo.html +0 -0
  51. {fastled-1.2.68 → fastled-1.2.73}/compiler/extra/mp3upload.html +0 -0
  52. {fastled-1.2.68 → fastled-1.2.73}/compiler/extra/webgl_postprocessing_unreal_bloom.html +0 -0
  53. {fastled-1.2.68 → fastled-1.2.73}/compiler/final_prewarm.sh +0 -0
  54. {fastled-1.2.68 → fastled-1.2.73}/compiler/init_runtime.py +0 -0
  55. {fastled-1.2.68 → fastled-1.2.73}/compiler/install-arduino-cli.sh +0 -0
  56. {fastled-1.2.68 → fastled-1.2.73}/compiler/libcompile/CMakeLists.txt +0 -0
  57. {fastled-1.2.68 → fastled-1.2.73}/compiler/paths.py +0 -0
  58. {fastled-1.2.68 → fastled-1.2.73}/compiler/pre-process.sh +0 -0
  59. {fastled-1.2.68 → fastled-1.2.73}/compiler/prewarm.sh +0 -0
  60. {fastled-1.2.68 → fastled-1.2.73}/compiler/process-ino.py +0 -0
  61. {fastled-1.2.68 → fastled-1.2.73}/compiler/process_extended.py +0 -0
  62. {fastled-1.2.68 → fastled-1.2.73}/compiler/pyproject.toml +0 -0
  63. {fastled-1.2.68 → fastled-1.2.73}/compiler/run.py +0 -0
  64. {fastled-1.2.68 → fastled-1.2.73}/compiler/server.py +0 -0
  65. {fastled-1.2.68 → fastled-1.2.73}/compiler/sketch_hasher.py +0 -0
  66. {fastled-1.2.68 → fastled-1.2.73}/compiler/wasm_compiler_flags.py +0 -0
  67. {fastled-1.2.68 → fastled-1.2.73}/docker-compose.yml +0 -0
  68. {fastled-1.2.68 → fastled-1.2.73}/install +0 -0
  69. {fastled-1.2.68 → fastled-1.2.73}/install_linux.sh +0 -0
  70. {fastled-1.2.68 → fastled-1.2.73}/lint +0 -0
  71. {fastled-1.2.68 → fastled-1.2.73}/pyproject.toml +0 -0
  72. {fastled-1.2.68 → fastled-1.2.73}/requirements.testing.txt +0 -0
  73. {fastled-1.2.68 → fastled-1.2.73}/setup.cfg +0 -0
  74. {fastled-1.2.68 → fastled-1.2.73}/setup.py +0 -0
  75. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/assets/example.txt +0 -0
  76. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/assets/localhost-key.pem +0 -0
  77. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/assets/localhost.pem +0 -0
  78. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/cli.py +0 -0
  79. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/cli_test.py +0 -0
  80. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/client_server.py +0 -0
  81. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/compile_server.py +0 -0
  82. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/filewatcher.py +0 -0
  83. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/interactive_srcs.py +0 -0
  84. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/keyboard.py +0 -0
  85. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/live_client.py +0 -0
  86. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/paths.py +0 -0
  87. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/project_init.py +0 -0
  88. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/select_sketch_directory.py +0 -0
  89. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/server_fastapi.py +0 -0
  90. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/server_fastapi_cli.py +0 -0
  91. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/server_flask.py +0 -0
  92. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/server_start.py +0 -0
  93. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/settings.py +0 -0
  94. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/site/build.py +0 -0
  95. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/site/examples.py +0 -0
  96. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/sketch.py +0 -0
  97. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/spinner.py +0 -0
  98. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/test/can_run_local_docker_tests.py +0 -0
  99. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/test/examples.py +0 -0
  100. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/types.py +0 -0
  101. {fastled-1.2.68 → fastled-1.2.73}/src/fastled/web_compile.py +0 -0
  102. {fastled-1.2.68 → fastled-1.2.73}/src/fastled.egg-info/SOURCES.txt +0 -0
  103. {fastled-1.2.68 → fastled-1.2.73}/src/fastled.egg-info/dependency_links.txt +0 -0
  104. {fastled-1.2.68 → fastled-1.2.73}/src/fastled.egg-info/entry_points.txt +0 -0
  105. {fastled-1.2.68 → fastled-1.2.73}/src/fastled.egg-info/requires.txt +0 -0
  106. {fastled-1.2.68 → fastled-1.2.73}/src/fastled.egg-info/top_level.txt +0 -0
  107. {fastled-1.2.68 → fastled-1.2.73}/test +0 -0
  108. {fastled-1.2.68 → fastled-1.2.73}/tests/integration/test_build_examples.py +0 -0
  109. {fastled-1.2.68 → fastled-1.2.73}/tests/integration/test_examples.py +0 -0
  110. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/html/index.html +0 -0
  111. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_api.py +0 -0
  112. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_bad_ino.py +0 -0
  113. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_cli.py +0 -0
  114. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_compile_server.py +0 -0
  115. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_docker_linux_on_windows.py +0 -0
  116. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_embedded_data.py +0 -0
  117. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_filechanger.py +0 -0
  118. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_http_server.py +0 -0
  119. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_ino/bad/bad.ino +0 -0
  120. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_ino/bad_platformio/bad_platformio.ino +0 -0
  121. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_ino/bad_platformio/platformio.ini +0 -0
  122. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_ino/embedded/data/bigdata.dat +0 -0
  123. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_ino/embedded/wasm.ino +0 -0
  124. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_ino/wasm/wasm.ino +0 -0
  125. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_project_init.py +0 -0
  126. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_server_and_client_seperatly.py +0 -0
  127. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_string_diff.py +0 -0
  128. {fastled-1.2.68 → fastled-1.2.73}/tests/unit/test_webcompile.py +0 -0
  129. {fastled-1.2.68 → fastled-1.2.73}/upload_package.sh +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastled
3
- Version: 1.2.68
3
+ Version: 1.2.73
4
4
  Summary: FastLED Wasm Compiler
5
5
  Home-page: https://github.com/zackees/fastled-wasm
6
6
  Maintainer: Zachary Vorhies
@@ -13,7 +13,7 @@ from .types import BuildMode, CompileResult, CompileServerError
13
13
  # IMPORTANT! There's a bug in github which will REJECT any version update
14
14
  # that has any other change in the repo. Please bump the version as the
15
15
  # ONLY change in a commit, or else the pypi update and the release will fail.
16
- __version__ = "1.2.68"
16
+ __version__ = "1.2.73"
17
17
 
18
18
  DOCKER_FILE = (
19
19
  "https://raw.githubusercontent.com/zackees/fastled-wasm/refs/heads/main/Dockerfile"
@@ -43,6 +43,8 @@ def run_server(args: Args) -> int:
43
43
 
44
44
 
45
45
  def main() -> int:
46
+ from fastled import __version__
47
+
46
48
  args = parse_args()
47
49
  interactive: bool = args.interactive
48
50
  has_server = args.server
@@ -52,6 +54,9 @@ def main() -> int:
52
54
  # directory: Path | None = Path(args.directory).absolute() if args.directory else None
53
55
  directory: Path | None = Path(args.directory) if args.directory else None
54
56
 
57
+ # now it is safe to print out the version
58
+ print(f"FastLED version: {__version__}")
59
+
55
60
  if update:
56
61
  # Force auto_update to ensure update check happens
57
62
  compile_server = CompileServer(interactive=False, auto_updates=True)
@@ -13,6 +13,7 @@ from fastled.docker_manager import (
13
13
  Container,
14
14
  DockerManager,
15
15
  RunningContainer,
16
+ Volume,
16
17
  )
17
18
  from fastled.interactive_srcs import INTERACTIVE_SOURCES
18
19
  from fastled.settings import DEFAULT_CONTAINER_NAME, IMAGE_NAME, SERVER_PORT
@@ -209,27 +210,32 @@ class CompileServerImpl:
209
210
  else:
210
211
  server_command = ["python", "/js/run.py", "server"] + SERVER_OPTIONS
211
212
  ports = {80: port}
212
- volumes = None
213
+ volumes = []
213
214
  if self.fastled_src_dir:
214
215
  print(
215
216
  f"Mounting FastLED source directory {self.fastled_src_dir} into container /host/fastled/src"
216
217
  )
217
- volumes = {
218
- str(self.fastled_src_dir): {"bind": "/host/fastled/src", "mode": "ro"}
219
- }
218
+ volumes.append(
219
+ Volume(
220
+ host_path=str(self.fastled_src_dir),
221
+ container_path="/host/fastled/src",
222
+ mode="ro",
223
+ )
224
+ )
220
225
  if self.interactive:
221
226
  # add the mapped directory to the container
222
227
  print(f"Mounting {self.mapped_dir} into container /mapped")
223
- # volumes = {str(self.mapped_dir): {"bind": "/mapped", "mode": "rw"}}
224
- # add it
225
228
  assert self.mapped_dir is not None
226
229
  dir_name = self.mapped_dir.name
227
230
  if not volumes:
228
- volumes = {}
229
- volumes[str(self.mapped_dir)] = {
230
- "bind": f"/mapped/{dir_name}",
231
- "mode": "rw",
232
- }
231
+ volumes = []
232
+ volumes.append(
233
+ Volume(
234
+ host_path=str(self.mapped_dir),
235
+ container_path=f"/mapped/{dir_name}",
236
+ mode="rw",
237
+ )
238
+ )
233
239
  if self.fastled_src_dir is not None:
234
240
  # to allow for interactive compilation
235
241
  interactive_sources = list(INTERACTIVE_SOURCES)
@@ -237,11 +243,13 @@ class CompileServerImpl:
237
243
  src_path = Path(src).absolute()
238
244
  if src_path.exists():
239
245
  print(f"Mounting {src} into container")
240
- src_str = str(src_path)
241
- volumes[src_str] = {
242
- "bind": f"/js/fastled/{src}",
243
- "mode": "rw",
244
- }
246
+ volumes.append(
247
+ Volume(
248
+ host_path=str(src_path),
249
+ container_path=f"/js/fastled/{src}",
250
+ mode="rw",
251
+ )
252
+ )
245
253
  else:
246
254
  print(f"Could not find {src}")
247
255
 
@@ -277,6 +285,8 @@ class CompileServerImpl:
277
285
  return self.docker.is_container_running(self.container_name)
278
286
 
279
287
  def stop(self) -> None:
288
+ if self.docker.is_suspended:
289
+ return
280
290
  if self.running_container:
281
291
  self.running_container.detach()
282
292
  self.running_container = None
@@ -3,6 +3,7 @@ New abstraction for Docker management with improved Ctrl+C handling.
3
3
  """
4
4
 
5
5
  import _thread
6
+ import json
6
7
  import os
7
8
  import platform
8
9
  import subprocess
@@ -11,6 +12,7 @@ import threading
11
12
  import time
12
13
  import traceback
13
14
  import warnings
15
+ from dataclasses import dataclass
14
16
  from datetime import datetime, timezone
15
17
  from pathlib import Path
16
18
 
@@ -63,6 +65,40 @@ def get_lock(image_name: str) -> FileLock:
63
65
  return out
64
66
 
65
67
 
68
+ @dataclass
69
+ class Volume:
70
+ """
71
+ Represents a Docker volume mapping between host and container.
72
+
73
+ Attributes:
74
+ host_path: Path on the host system (e.g., "C:\\Users\\username\\project")
75
+ container_path: Path inside the container (e.g., "/app/data")
76
+ mode: Access mode, "rw" for read-write or "ro" for read-only
77
+ """
78
+
79
+ host_path: str
80
+ container_path: str
81
+ mode: str = "rw"
82
+
83
+ def to_dict(self) -> dict[str, dict[str, str]]:
84
+ """Convert the Volume object to the format expected by Docker API."""
85
+ return {self.host_path: {"bind": self.container_path, "mode": self.mode}}
86
+
87
+ @classmethod
88
+ def from_dict(cls, volume_dict: dict[str, dict[str, str]]) -> list["Volume"]:
89
+ """Create Volume objects from a Docker volume dictionary."""
90
+ volumes = []
91
+ for host_path, config in volume_dict.items():
92
+ volumes.append(
93
+ cls(
94
+ host_path=host_path,
95
+ container_path=config["bind"],
96
+ mode=config.get("mode", "rw"),
97
+ )
98
+ )
99
+ return volumes
100
+
101
+
66
102
  class RunningContainer:
67
103
  def __init__(self, container, first_run=False):
68
104
  self.container = container
@@ -104,10 +140,46 @@ class RunningContainer:
104
140
  self.detach()
105
141
 
106
142
 
143
+ def _hack_to_fix_mac(volumes: list[Volume] | None) -> list[Volume] | None:
144
+ """Fixes the volume mounts on MacOS by removing the mode."""
145
+ if volumes is None:
146
+ return None
147
+ if sys.platform != "darwin":
148
+ # Only macos needs hacking.
149
+ return volumes
150
+
151
+ volumes = volumes.copy()
152
+ # Work around a Docker bug on MacOS where the expected network socket to the
153
+ # the host is not mounted correctly. This was actually fixed in recent versions
154
+ # of docker client but there is a large chunk of Docker clients out there with
155
+ # this bug in it.
156
+ #
157
+ # This hack is done by mounting the socket directly to the container.
158
+ # This socket talks to the docker daemon on the host.
159
+ #
160
+ # Found here.
161
+ # https://github.com/docker/docker-py/issues/3069#issuecomment-1316778735
162
+ # if it exists already then return the input
163
+ for volume in volumes:
164
+ if volume.host_path == "/var/run/docker.sock":
165
+ return volumes
166
+ # ok it doesn't exist, so add it
167
+ volumes.append(
168
+ Volume(
169
+ host_path="/var/run/docker.sock",
170
+ container_path="/var/run/docker.sock",
171
+ mode="rw",
172
+ )
173
+ )
174
+ return volumes
175
+
176
+
107
177
  class DockerManager:
108
178
  def __init__(self) -> None:
109
179
  from docker.errors import DockerException
110
180
 
181
+ self.is_suspended: bool = False
182
+
111
183
  try:
112
184
  self._client: DockerClient | None = None
113
185
  self.first_run = False
@@ -314,8 +386,6 @@ class DockerManager:
314
386
 
315
387
  # Quick check for latest version
316
388
  with Spinner(f"Pulling newer version of {image_name}:{tag}..."):
317
- # This needs to be swapped out using the the command line interface AI!
318
- # _ = self.client.images.pull(image_name, tag=tag)
319
389
  cmd_list = ["docker", "pull", f"{image_name}:{tag}"]
320
390
  cmd_str = subprocess.list2cmdline(cmd_list)
321
391
  print(f"Running command: {cmd_str}")
@@ -355,8 +425,8 @@ class DockerManager:
355
425
  self,
356
426
  container: Container,
357
427
  command: str | None,
358
- volumes: dict | None,
359
- ports: dict | None,
428
+ volumes_dict: dict[str, dict[str, str]] | None,
429
+ ports: dict[int, int] | None,
360
430
  ) -> bool:
361
431
  """Compare if existing container has matching configuration"""
362
432
  try:
@@ -386,7 +456,7 @@ class DockerManager:
386
456
  return False
387
457
 
388
458
  # Check volumes if specified
389
- if volumes:
459
+ if volumes_dict:
390
460
  container_mounts = (
391
461
  {
392
462
  m["Source"]: {"bind": m["Destination"], "mode": m["Mode"]}
@@ -396,7 +466,7 @@ class DockerManager:
396
466
  else {}
397
467
  )
398
468
 
399
- for host_dir, mount in volumes.items():
469
+ for host_dir, mount in volumes_dict.items():
400
470
  if host_dir not in container_mounts:
401
471
  print(f"Volume {host_dir} not found in container mounts.")
402
472
  return False
@@ -446,7 +516,7 @@ class DockerManager:
446
516
  tag: str,
447
517
  container_name: str,
448
518
  command: str | None = None,
449
- volumes: dict[str, dict[str, str]] | None = None,
519
+ volumes: list[Volume] | None = None,
450
520
  ports: dict[int, int] | None = None,
451
521
  remove_previous: bool = False,
452
522
  ) -> Container:
@@ -455,11 +525,23 @@ class DockerManager:
455
525
  If it exists with different config, remove and recreate it.
456
526
 
457
527
  Args:
458
- volumes: Dict mapping host paths to dicts with 'bind' and 'mode' keys
459
- Example: {'/host/path': {'bind': '/container/path', 'mode': 'rw'}}
528
+ volumes: List of Volume objects for container volume mappings
460
529
  ports: Dict mapping host ports to container ports
461
530
  Example: {8080: 80} maps host port 8080 to container port 80
462
531
  """
532
+ volumes = _hack_to_fix_mac(volumes)
533
+ # Convert volumes to the format expected by Docker API
534
+ volumes_dict = None
535
+ if volumes is not None:
536
+ volumes_dict = {}
537
+ for volume in volumes:
538
+ volumes_dict.update(volume.to_dict())
539
+
540
+ # Serialize the volumes to a json string
541
+ if volumes_dict:
542
+ volumes_str = json.dumps(volumes_dict)
543
+ print(f"Volumes: {volumes_str}")
544
+ print("Done")
463
545
  image_name = f"{image_name}:{tag}"
464
546
  try:
465
547
  container: Container = self.client.containers.get(container_name)
@@ -469,7 +551,9 @@ class DockerManager:
469
551
  container.remove(force=True)
470
552
  raise NotFound("Container removed due to remove_previous")
471
553
  # Check if configuration matches
472
- elif not self._container_configs_match(container, command, volumes, ports):
554
+ elif not self._container_configs_match(
555
+ container, command, volumes_dict, ports
556
+ ):
473
557
  print(
474
558
  f"Container {container_name} exists but with different configuration. Removing and recreating..."
475
559
  )
@@ -517,7 +601,7 @@ class DockerManager:
517
601
  name=container_name,
518
602
  detach=True,
519
603
  tty=True,
520
- volumes=volumes,
604
+ volumes=volumes_dict,
521
605
  ports=ports, # type: ignore
522
606
  remove=True,
523
607
  )
@@ -529,9 +613,16 @@ class DockerManager:
529
613
  tag: str,
530
614
  container_name: str,
531
615
  command: str | None = None,
532
- volumes: dict[str, dict[str, str]] | None = None,
616
+ volumes: list[Volume] | None = None,
533
617
  ports: dict[int, int] | None = None,
534
618
  ) -> None:
619
+ # Convert volumes to the format expected by Docker API
620
+ volumes = _hack_to_fix_mac(volumes)
621
+ volumes_dict = None
622
+ if volumes is not None:
623
+ volumes_dict = {}
624
+ for volume in volumes:
625
+ volumes_dict.update(volume.to_dict())
535
626
  # Remove existing container
536
627
  try:
537
628
  container: Container = self.client.containers.get(container_name)
@@ -548,9 +639,13 @@ class DockerManager:
548
639
  "--name",
549
640
  container_name,
550
641
  ]
551
- if volumes:
552
- for host_dir, mount in volumes.items():
553
- docker_command.extend(["-v", f"{host_dir}:{mount['bind']}"])
642
+ if volumes_dict:
643
+ for host_dir, mount in volumes_dict.items():
644
+ docker_volume_arg = [
645
+ "-v",
646
+ f"{host_dir}:{mount['bind']}:{mount['mode']}",
647
+ ]
648
+ docker_command.extend(docker_volume_arg)
554
649
  if ports:
555
650
  for host_port, container_port in ports.items():
556
651
  docker_command.extend(["-p", f"{host_port}:{container_port}"])
@@ -591,6 +686,8 @@ class DockerManager:
591
686
  """
592
687
  Suspend (pause) the container.
593
688
  """
689
+ if self.is_suspended:
690
+ return
594
691
  if isinstance(container, str):
595
692
  container_name = container
596
693
  # container = self.get_container(container)
@@ -0,0 +1,84 @@
1
+ # This was an experiment for https://localhost
2
+ # Important for audio sampling from sites like Youtube or an app like Audacity.
3
+ # However, pkg_resources does not work in the exe version of this app.
4
+ # For this to work pyinstaller needs special handling (probably a spec-file)
5
+ # in order to properly package up external resources.
6
+
7
+
8
+ from dataclasses import dataclass
9
+
10
+ _ENABLE_SSL_CONFIG = False
11
+
12
+
13
+ @dataclass
14
+ class SslConfig:
15
+ cert: str
16
+ key: str
17
+
18
+
19
+ def get_ssl_config() -> SslConfig | None:
20
+ """Get the keys for the server"""
21
+ if not _ENABLE_SSL_CONFIG:
22
+ return None
23
+ return SslConfig(
24
+ cert=_CERT,
25
+ key=_PRIVATE_KEY,
26
+ )
27
+
28
+
29
+ _PRIVATE_KEY = """-----BEGIN PRIVATE KEY-----
30
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDlxbWcpUXPpjqs
31
+ DJPgFF1FsXPZqq0JPJqssHh4ZLfN0h4yJmj+kRcHS+pkgXnG46g6bUcL/AK5Ba08
32
+ vwnUUGkPH0v4ShKiAGYwvOcbWaqTmvvJuIoaDBXh2jSCeOTagNoaHLYEugARkkEu
33
+ 0/FEW5P/79wU5vJ5G+SyZ8rBCVdxlU57pL1hKWBU7K+BLWsCiZ308NMpzHF5APZ6
34
+ YxVjhFosJPr4TjN6yXr+whrsAjSTHamD5690MbXWyyPG0jwPQyjBot/cNtt8GrsN
35
+ gcjA1E+8VKFvxO8RvZanMZLb0CGEpt7u3oaJ/jprHEsw+/UhnG6Qhksm8C/DN9kP
36
+ hselewffAgMBAAECggEARjQ6YTo+Mkvf8WGGbRjLxteJRiBX7lKOD+V7aY2ce06P
37
+ 21LREbbTCm+vljXZN2OnqvJomsjNLCsH21+jaTOIZg5x79LyDn2Au7N8CWdELwVT
38
+ mTbBO2Ql63P4R0UY54onGYNcOeV6z+OX9u7a8L/qYHCxFdHalBZpsfj0gjaQeStJ
39
+ JSnvGjo6tKkwC/nUmX01qEVQgUO1+39WYqCaIWjijZNXt6XiKclEuu1AkL0u6Mpt
40
+ CzvzEDrEA66D0Lvl3Tek9B4O16Oie5anNnNMHigwU9yVc6dI8vDCRSEiz7laPTFK
41
+ xzOCQmqPGClKXkX3U+OvZp/Ss9U26Wpu0AbRKTvzAQKBgQDsMR9NjMpOmUaWkAwl
42
+ 1wlUxsZ9YkRuTy7R3RfIdYWj6Lcoc4/iN0qILFM7xidkHhYTFqnsnP1SQkV6lEHV
43
+ OalYxZu9F2l1rHPc8G5YWh/KOg1rAEI47MVT4iwhA2fw6JLti/rm25AeSTMjSTqu
44
+ ht3146036opcIF3v86oGUrSXDwKBgQD5CsNcwLeUDGXozjq62T8/mTYwd2Tw3aiY
45
+ KaGp+exAW321vYm5SKsMitBMGU2tGFlv5eptSI48h7SCpgnszaexw7yj30KuvqjG
46
+ bBqq/MsKuXHyn2sG0A7MJ6zfk+4l46B45blDJZ+x7xL0dyS4UCU3zUeesgSGo4zK
47
+ ZOspPIQCMQKBgQCk35VuWP1P6IbxyxPvxi/pUeh01gfWyMdyD9fuQrtLM8PHJQQn
48
+ cVlBvU9MxoHwzV+za3qqhNwAc+p0KtHZuiqQoUCZuqIPVpZ6gAtG+YJ/dA6xxrhz
49
+ bDRC3frYALyp2m/WCoTWaiYsPgTIePHRqqt+XbQo+DwlGyL3wSvKxijx2QKBgCb0
50
+ OwioEE70/X/DukX9szn0chh0pHJUiYl7gZD/yadraCdkRUWZC0BD+j7c+lxn4Z1y
51
+ HhAH+E+Zfm+tHwJOTLuufTQ4uMpygh2/TRCPyAaeaSdlLi17n8TpM84o6mg8yZ3/
52
+ eNH68Za4aYOZm0HFL30h++DjwXd534zM6keh8pgRAoGBAKUrsjDGjuSo8l1fi4Cq
53
+ INu/rQop2h/db02zyJP5q7NKhE1nqogeLwwn+2M/LtHQ1nIzZR+rvrLNgt6oWY31
54
+ sPsv8JUfVT8GmdhU9KKmizK6eUu3rWdj2+rJARmuEaPmHcD5O6oJaGU0qadqQP34
55
+ H+enwWmpyZXAIbEu/q63DFhV
56
+ -----END PRIVATE KEY-----"""
57
+
58
+ _CERT = """-----BEGIN _CERTIFICATE-----
59
+ MIIEfTCCAuWgAwIBAgIRAPb7jkLrCuqToG+s3AQYeuUwDQYJKoZIhvcNAQELBQAw
60
+ gakxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTE/MD0GA1UECww2REVT
61
+ S1RPUC1JMzcxOERPXFphY2ggVm9yaGllc0BERVNLVE9QLUkzNzE4RE8gKG5pdGVy
62
+ aXMpMUYwRAYDVQQDDD1ta2NlcnQgREVTS1RPUC1JMzcxOERPXFphY2ggVm9yaGll
63
+ c0BERVNLVE9QLUkzNzE4RE8gKG5pdGVyaXMpMB4XDTI1MDQyODAwMzk1MFoXDTI3
64
+ MDcyODAwMzk1MFowajEnMCUGA1UEChMebWtjZXJ0IGRldmVsb3BtZW50IGNlcnRp
65
+ ZmljYXRlMT8wPQYDVQQLDDZERVNLVE9QLUkzNzE4RE9cWmFjaCBWb3JoaWVzQERF
66
+ U0tUT1AtSTM3MThETyAobml0ZXJpcykwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
67
+ ggEKAoIBAQDlxbWcpUXPpjqsDJPgFF1FsXPZqq0JPJqssHh4ZLfN0h4yJmj+kRcH
68
+ S+pkgXnG46g6bUcL/AK5Ba08vwnUUGkPH0v4ShKiAGYwvOcbWaqTmvvJuIoaDBXh
69
+ 2jSCeOTagNoaHLYEugARkkEu0/FEW5P/79wU5vJ5G+SyZ8rBCVdxlU57pL1hKWBU
70
+ 7K+BLWsCiZ308NMpzHF5APZ6YxVjhFosJPr4TjN6yXr+whrsAjSTHamD5690MbXW
71
+ yyPG0jwPQyjBot/cNtt8GrsNgcjA1E+8VKFvxO8RvZanMZLb0CGEpt7u3oaJ/jpr
72
+ HEsw+/UhnG6Qhksm8C/DN9kPhselewffAgMBAAGjXjBcMA4GA1UdDwEB/wQEAwIF
73
+ oDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBSPBydvhr9wI+FsoW/H
74
+ WK3DbS8IUDAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggGB
75
+ AJVrF1yczZaxt+A2AhdeFbJQUR6NzGBTc20YeWF1YzLV5sV3QVumwZLP2M9ggRgd
76
+ xWV0xfwUHobFQk6RIPTADcLKctiurql0cgF4DPnpWVvto9RM00U3AkQcMj3xtKBV
77
+ wUqo83TcbqgL+euudFZ09gGTs9u9AENaZPcMh+rW8DDO92t+EwMI/IfopxVOJGUB
78
+ RSM3yTwV93BMYBuddt8mclzLzPK/1WONfsHU2xEascaHR1tYMOmJN9Vq4o0fzWxo
79
+ a2vI6K0aJqZV/ztdXq3akwLc6/e9hyptHWa0i/022xVCyNWIlnuEhT7ENMPxh6rX
80
+ ZCQCZVnhcSWAyFjggLJql3aSID5fPF8rmN7wWsB/I5pl9qwMR1/THMPrm5aWn1Xj
81
+ xW6PxkSGm73kd57DH7tqm5HTd8eYCbnsFofI9rC7xI6HCfwchKp+YHvIEu/LJ56E
82
+ FLnCZW/orYkHCzWntzxv1bddrw1BwaNR8Q+mu3imRP8fuyXb2UkFkINVVyOOWHuW
83
+ Kw==
84
+ -----END _CERTIFICATE-----"""
@@ -8,7 +8,6 @@ from fastled.keyz import get_ssl_config
8
8
 
9
9
  DEFAULT_PORT = 8089 # different than live version.
10
10
  PYTHON_EXE = sys.executable
11
- SSL_CONFIG = get_ssl_config()
12
11
 
13
12
 
14
13
  # print(f"SSL Config: {SSL_CONFIG.certfile}, {SSL_CONFIG.keyfile}")
@@ -21,6 +20,7 @@ def _open_http_server_subprocess(
21
20
  print("\n################################################################")
22
21
  print(f"# Opening browser to {fastled_js} on port {port}")
23
22
  print("################################################################\n")
23
+ ssl = get_ssl_config()
24
24
  try:
25
25
  # Fallback to our Python server
26
26
  cmd = [
@@ -32,18 +32,8 @@ def _open_http_server_subprocess(
32
32
  str(port),
33
33
  ]
34
34
  # Pass SSL flags if available
35
- if SSL_CONFIG and SSL_CONFIG.certfile and SSL_CONFIG.keyfile:
36
- cmd.extend(
37
- [
38
- "--cert",
39
- str(SSL_CONFIG.certfile),
40
- "--key",
41
- str(SSL_CONFIG.keyfile),
42
- ]
43
- )
44
- print(
45
- f"Running server on port {port} with certs: {SSL_CONFIG.certfile}, {SSL_CONFIG.keyfile}"
46
- )
35
+ if ssl:
36
+ raise NotImplementedError("SSL is not implemented yet")
47
37
  print(f"Running server on port {port}.")
48
38
  print(f"Command: {subprocess.list2cmdline(cmd)}")
49
39
  # Suppress output
@@ -3,7 +3,6 @@ import os
3
3
  import sys
4
4
  from pathlib import Path
5
5
 
6
- from fastled import __version__
7
6
  from fastled.project_init import project_init
8
7
  from fastled.select_sketch_directory import select_sketch_directory
9
8
  from fastled.settings import DEFAULT_URL, IMAGE_NAME
@@ -27,6 +26,8 @@ def _find_fastled_repo(start: Path) -> Path | None:
27
26
 
28
27
  def parse_args() -> Args:
29
28
  """Parse command-line arguments."""
29
+ from fastled import __version__
30
+
30
31
  parser = argparse.ArgumentParser(description=f"FastLED WASM Compiler {__version__}")
31
32
  parser.add_argument("--version", action="version", version=f"{__version__}")
32
33
  parser.add_argument(
@@ -3,6 +3,24 @@ from pathlib import Path
3
3
  from rapidfuzz import fuzz
4
4
 
5
5
 
6
+ def _filter_out_obvious_bad_choices(
7
+ input_str: str, string_list: list[str]
8
+ ) -> list[str]:
9
+ """
10
+ Filter out strings that are too different from the input string.
11
+ This is a heuristic and may not be perfect.
12
+ """
13
+ input_chars = set(input_str)
14
+ filtered_list = []
15
+ for s in string_list:
16
+ # Check if at least half of the input characters are in the string
17
+ s_chars = set(s)
18
+ common_chars = input_chars.intersection(s_chars)
19
+ if len(common_chars) >= len(input_chars) / 2:
20
+ filtered_list.append(s)
21
+ return filtered_list
22
+
23
+
6
24
  # Returns the min distance strings. If there is a tie, it returns
7
25
  # all the strings that have the same min distance.
8
26
  # Returns a tuple of index and string.
@@ -24,6 +42,10 @@ def string_diff(
24
42
  string_list = [s.lower() for s in string_list]
25
43
  input_string = input_string.lower()
26
44
 
45
+ # Apply set membership filtering for queries with 3+ characters
46
+ if len(input_string) >= 3:
47
+ string_list = _filter_out_obvious_bad_choices(input_string, string_list)
48
+
27
49
  is_substring = False
28
50
  for s in string_list:
29
51
  if input_string in s:
@@ -8,3 +8,12 @@ def hash_file(file_path: Path) -> str:
8
8
  for chunk in iter(lambda: f.read(4096), b""):
9
9
  hasher.update(chunk)
10
10
  return hasher.hexdigest()
11
+
12
+
13
+ def banner_string(msg: str) -> str:
14
+ """
15
+ Return `msg` surrounded by a border of # characters, including
16
+ leading and trailing newlines.
17
+ """
18
+ border = "#" * (len(msg) + 4)
19
+ return f"\n{border}\n# {msg}\n{border}\n"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastled
3
- Version: 1.2.68
3
+ Version: 1.2.73
4
4
  Summary: FastLED Wasm Compiler
5
5
  Home-page: https://github.com/zackees/fastled-wasm
6
6
  Maintainer: Zachary Vorhies
@@ -1,31 +0,0 @@
1
- import importlib.resources as pkg_resources
2
- from dataclasses import dataclass
3
- from pathlib import Path
4
-
5
-
6
- @dataclass
7
- class SslConfig:
8
- certfile: Path
9
- keyfile: Path
10
-
11
-
12
- def get_asset_path(filename: str) -> Path | None:
13
- """Locate a file from the fastled.assets package resources."""
14
- try:
15
- resource = pkg_resources.files("fastled.assets").joinpath(filename)
16
- # Convert to Path for file-system access
17
- path = Path(str(resource))
18
- return path if path.exists() else None
19
- except (ModuleNotFoundError, AttributeError):
20
- return None
21
-
22
-
23
- def get_ssl_config() -> SslConfig | None:
24
- """Get the keys for the server"""
25
- # certfile = get_asset_path("localhost-key.pem")
26
- # keyfile = get_asset_path("localhost.pem")
27
- # if certfile is None or keyfile is None:
28
- # raise ValueError("Could not find keys for server")
29
- # # return certfile, keyfile
30
- # return SslConfig(certfile=certfile, keyfile=keyfile)
31
- return None
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes