fastled 1.2.50__tar.gz → 1.2.60__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.50 → fastled-1.2.60}/.github/workflows/test_macos.yml +1 -1
  2. {fastled-1.2.50 → fastled-1.2.60}/.github/workflows/test_ubuntu.yml +1 -1
  3. {fastled-1.2.50 → fastled-1.2.60}/.github/workflows/test_win.yml +1 -1
  4. {fastled-1.2.50 → fastled-1.2.60}/.gitignore +1 -1
  5. {fastled-1.2.50 → fastled-1.2.60}/Dockerfile +2 -2
  6. {fastled-1.2.50 → fastled-1.2.60}/PKG-INFO +18 -4
  7. {fastled-1.2.50 → fastled-1.2.60}/README.md +13 -1
  8. {fastled-1.2.50 → fastled-1.2.60}/compiler/compile.py +3 -0
  9. fastled-1.2.60/compiler/entrypoint.sh +16 -0
  10. fastled-1.2.60/compiler/extra/micdemo.html +136 -0
  11. fastled-1.2.60/compiler/process_extended.py +109 -0
  12. fastled-1.2.60/compiler/pyproject.toml +22 -0
  13. {fastled-1.2.50 → fastled-1.2.60}/compiler/server.py +3 -0
  14. {fastled-1.2.50 → fastled-1.2.60}/lint +1 -3
  15. {fastled-1.2.50 → fastled-1.2.60}/pyproject.toml +4 -2
  16. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/__init__.py +1 -1
  17. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/app.py +1 -2
  18. fastled-1.2.60/src/fastled/assets/localhost-key.pem +28 -0
  19. fastled-1.2.60/src/fastled/assets/localhost.pem +27 -0
  20. fastled-1.2.60/src/fastled/cli_test.py +20 -0
  21. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/client_server.py +8 -7
  22. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/docker_manager.py +2 -1
  23. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/filewatcher.py +78 -1
  24. fastled-1.2.60/src/fastled/keyz.py +30 -0
  25. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/open_browser.py +55 -65
  26. fastled-1.2.60/src/fastled/open_browser2.py +108 -0
  27. fastled-1.2.60/src/fastled/server_fastapi.py +60 -0
  28. fastled-1.2.60/src/fastled/server_fastapi_cli.py +61 -0
  29. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/settings.py +1 -0
  30. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/site/build.py +2 -10
  31. fastled-1.2.60/src/fastled/site/examples.py +10 -0
  32. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/string_diff.py +23 -5
  33. {fastled-1.2.50 → fastled-1.2.60}/src/fastled.egg-info/PKG-INFO +18 -4
  34. {fastled-1.2.50 → fastled-1.2.60}/src/fastled.egg-info/SOURCES.txt +31 -21
  35. {fastled-1.2.50 → fastled-1.2.60}/src/fastled.egg-info/requires.txt +4 -2
  36. {fastled-1.2.50 → fastled-1.2.60}/test +2 -2
  37. {fastled-1.2.50/tests → fastled-1.2.60/tests/integration}/test_build_examples.py +12 -1
  38. {fastled-1.2.50/tests → fastled-1.2.60/tests/integration}/test_examples.py +1 -1
  39. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_embedded_data.py +8 -2
  40. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_http_server.py +2 -2
  41. fastled-1.2.60/tests/unit/test_string_diff.py +80 -0
  42. fastled-1.2.50/compiler/entrypoint.sh +0 -7
  43. fastled-1.2.50/compiler/requirements.txt +0 -12
  44. fastled-1.2.50/src/fastled/open_browser2.py +0 -111
  45. {fastled-1.2.50 → fastled-1.2.60}/.aiderignore +0 -0
  46. {fastled-1.2.50 → fastled-1.2.60}/.dockerignore +0 -0
  47. {fastled-1.2.50 → fastled-1.2.60}/.github/workflows/build_multi_docker_image.yml +0 -0
  48. {fastled-1.2.50 → fastled-1.2.60}/.github/workflows/build_webpage.yml +0 -0
  49. {fastled-1.2.50 → fastled-1.2.60}/.github/workflows/lint.yml +0 -0
  50. {fastled-1.2.50 → fastled-1.2.60}/.github/workflows/publish_release.yml +0 -0
  51. {fastled-1.2.50 → fastled-1.2.60}/.github/workflows/template_build_docker_image.yml +0 -0
  52. {fastled-1.2.50 → fastled-1.2.60}/.github/workflows/test_build_exe.yml +0 -0
  53. {fastled-1.2.50 → fastled-1.2.60}/.pylintrc +0 -0
  54. {fastled-1.2.50 → fastled-1.2.60}/.vscode/launch.json +0 -0
  55. {fastled-1.2.50 → fastled-1.2.60}/.vscode/settings.json +0 -0
  56. {fastled-1.2.50 → fastled-1.2.60}/.vscode/tasks.json +0 -0
  57. {fastled-1.2.50 → fastled-1.2.60}/LICENSE +0 -0
  58. {fastled-1.2.50 → fastled-1.2.60}/MANIFEST.in +0 -0
  59. {fastled-1.2.50 → fastled-1.2.60}/RELEASE.md +0 -0
  60. {fastled-1.2.50 → fastled-1.2.60}/TODO.md +0 -0
  61. {fastled-1.2.50 → fastled-1.2.60}/build_exe.py +0 -0
  62. {fastled-1.2.50 → fastled-1.2.60}/build_site.py +0 -0
  63. {fastled-1.2.50 → fastled-1.2.60}/clean +0 -0
  64. {fastled-1.2.50 → fastled-1.2.60}/compiler/CMakeLists.txt +0 -0
  65. {fastled-1.2.50 → fastled-1.2.60}/compiler/__init__.py +0 -0
  66. {fastled-1.2.50 → fastled-1.2.60}/compiler/arduino-pre-process.sh +0 -0
  67. {fastled-1.2.50 → fastled-1.2.60}/compiler/build.sh +0 -0
  68. {fastled-1.2.50 → fastled-1.2.60}/compiler/build_archive.sh +0 -0
  69. {fastled-1.2.50 → fastled-1.2.60}/compiler/build_fast.sh +0 -0
  70. {fastled-1.2.50 → fastled-1.2.60}/compiler/code_sync.py +0 -0
  71. {fastled-1.2.50 → fastled-1.2.60}/compiler/compile_lock.py +0 -0
  72. {fastled-1.2.50 → fastled-1.2.60}/compiler/extra/100dots.html +0 -0
  73. {fastled-1.2.50 → fastled-1.2.60}/compiler/extra/demo_threejs.html +0 -0
  74. {fastled-1.2.50 → fastled-1.2.60}/compiler/extra/webgl_postprocessing_unreal_bloom.html +0 -0
  75. {fastled-1.2.50 → fastled-1.2.60}/compiler/final_prewarm.sh +0 -0
  76. {fastled-1.2.50 → fastled-1.2.60}/compiler/init_runtime.py +0 -0
  77. {fastled-1.2.50 → fastled-1.2.60}/compiler/install-arduino-cli.sh +0 -0
  78. {fastled-1.2.50 → fastled-1.2.60}/compiler/libcompile/CMakeLists.txt +0 -0
  79. {fastled-1.2.50 → fastled-1.2.60}/compiler/paths.py +0 -0
  80. {fastled-1.2.50 → fastled-1.2.60}/compiler/pre-process.sh +0 -0
  81. {fastled-1.2.50 → fastled-1.2.60}/compiler/prewarm.sh +0 -0
  82. {fastled-1.2.50 → fastled-1.2.60}/compiler/process-ino.py +0 -0
  83. {fastled-1.2.50 → fastled-1.2.60}/compiler/run.py +0 -0
  84. {fastled-1.2.50 → fastled-1.2.60}/compiler/sketch_hasher.py +0 -0
  85. {fastled-1.2.50 → fastled-1.2.60}/compiler/wasm_compiler_flags.py +0 -0
  86. {fastled-1.2.50 → fastled-1.2.60}/docker-compose.yml +0 -0
  87. {fastled-1.2.50 → fastled-1.2.60}/install +0 -0
  88. {fastled-1.2.50 → fastled-1.2.60}/install_linux.sh +0 -0
  89. {fastled-1.2.50 → fastled-1.2.60}/requirements.testing.txt +0 -0
  90. {fastled-1.2.50 → fastled-1.2.60}/setup.cfg +0 -0
  91. {fastled-1.2.50 → fastled-1.2.60}/setup.py +0 -0
  92. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/assets/example.txt +0 -0
  93. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/cli.py +0 -0
  94. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/compile_server.py +0 -0
  95. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/compile_server_impl.py +0 -0
  96. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/interactive_srcs.py +0 -0
  97. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/keyboard.py +0 -0
  98. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/live_client.py +0 -0
  99. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/parse_args.py +0 -0
  100. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/paths.py +0 -0
  101. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/project_init.py +0 -0
  102. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/select_sketch_directory.py +0 -0
  103. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/sketch.py +0 -0
  104. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/spinner.py +0 -0
  105. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/test/can_run_local_docker_tests.py +0 -0
  106. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/test/examples.py +0 -0
  107. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/types.py +0 -0
  108. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/util.py +0 -0
  109. {fastled-1.2.50 → fastled-1.2.60}/src/fastled/web_compile.py +0 -0
  110. {fastled-1.2.50 → fastled-1.2.60}/src/fastled.egg-info/dependency_links.txt +0 -0
  111. {fastled-1.2.50 → fastled-1.2.60}/src/fastled.egg-info/entry_points.txt +0 -0
  112. {fastled-1.2.50 → fastled-1.2.60}/src/fastled.egg-info/top_level.txt +0 -0
  113. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/html/index.html +0 -0
  114. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_api.py +0 -0
  115. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_bad_ino.py +0 -0
  116. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_cli.py +0 -0
  117. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_compile_server.py +0 -0
  118. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_docker_linux_on_windows.py +0 -0
  119. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_filechanger.py +0 -0
  120. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_ino/bad/bad.ino +0 -0
  121. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_ino/bad_platformio/bad_platformio.ino +0 -0
  122. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_ino/bad_platformio/platformio.ini +0 -0
  123. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_ino/embedded/data/bigdata.dat +0 -0
  124. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_ino/embedded/wasm.ino +0 -0
  125. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_ino/wasm/wasm.ino +0 -0
  126. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_project_init.py +0 -0
  127. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_server_and_client_seperatly.py +0 -0
  128. {fastled-1.2.50/tests → fastled-1.2.60/tests/unit}/test_webcompile.py +0 -0
  129. {fastled-1.2.50 → fastled-1.2.60}/upload_package.sh +0 -0
@@ -26,4 +26,4 @@ jobs:
26
26
  run: ./test
27
27
 
28
28
  - name: live run
29
- run: pip install . && cd tests/test_ino/wasm && sudo rm -rf fastled_js && time fastled-wasm --just-compile && find fastled_js | sort
29
+ run: pip install . && cd tests/unit/test_ino/wasm && sudo rm -rf fastled_js && time fastled-wasm --just-compile && find fastled_js | sort
@@ -26,4 +26,4 @@ jobs:
26
26
  run: ./test
27
27
 
28
28
  - name: live run
29
- run: pip install . && cd tests/test_ino/wasm && sudo rm -rf fastled_js && time fastled-wasm --just-compile && find fastled_js | sort
29
+ run: pip install . && cd tests/unit/test_ino/wasm && sudo rm -rf fastled_js && time fastled-wasm --just-compile && find fastled_js | sort
@@ -28,5 +28,5 @@ jobs:
28
28
  shell: bash -l {0}
29
29
 
30
30
  - name: live run
31
- run: pip install . && cd tests/test_ino/wasm && rm -rf fastled_js && time fastled-wasm --web --just-compile && find fastled_js | sort
31
+ run: pip install . && cd tests/unit/test_ino/wasm && rm -rf fastled_js && time fastled-wasm --web --just-compile && find fastled_js | sort
32
32
  shell: bash -l {0}
@@ -14,7 +14,7 @@ __pycache__/
14
14
  .Python
15
15
  prod_env/
16
16
  data/
17
- !tests/test_ino/embedded/data
17
+ !tests/**/embedded/data
18
18
  build/
19
19
  develop-eggs/
20
20
  dist/
@@ -124,8 +124,8 @@ RUN echo 'export LANG=en_US.UTF-8' >> /etc/profile && \
124
124
  RUN pip install uv==0.6.5
125
125
 
126
126
  # Get the compiler requirements and install them.
127
- COPY compiler/requirements.txt /install/requirements.txt
128
- RUN uv pip install --system -r /install/requirements.txt
127
+ COPY compiler/pyproject.toml /install/pyproject.toml
128
+ RUN uv pip install --system -r /install/pyproject.toml
129
129
 
130
130
  RUN pio settings set check_platformio_interval 9999
131
131
  RUN pio settings set enable_telemetry 0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastled
3
- Version: 1.2.50
3
+ Version: 1.2.60
4
4
  Summary: FastLED Wasm Compiler
5
5
  Home-page: https://github.com/zackees/fastled-wasm
6
6
  Maintainer: Zachary Vorhies
@@ -19,8 +19,10 @@ Requires-Dist: disklru>=2.0.1
19
19
  Requires-Dist: appdirs>=1.4.4
20
20
  Requires-Dist: rapidfuzz>=3.10.1
21
21
  Requires-Dist: progress>=1.6
22
- Requires-Dist: Flask>=3.0.0
23
- Requires-Dist: livereload
22
+ Requires-Dist: fastapi>=0.115.12
23
+ Requires-Dist: uvicorn>=0.34.2
24
+ Requires-Dist: pywebview>=5.4
25
+ Requires-Dist: watchfiles>=1.0.5
24
26
  Dynamic: home-page
25
27
  Dynamic: license-file
26
28
  Dynamic: maintainer
@@ -295,8 +297,8 @@ A: `delay()` will block `loop()` which blocks the main thread of the browser. Th
295
297
  Q: How can I get the compiled size of my FastLED sketch smaller?
296
298
  A: A big chunk of space is being used by unnecessary javascript `emscripten` bundling. The wasm_compiler_settings.py file in the FastLED repo can tweak this.
297
299
 
298
- # Revisions
299
300
 
301
+ # Revisions
300
302
  * 1.2.31 - Bunch of fixes and ease of use while compiling code in the repo.
301
303
  * 1.2.22 - Prefer to use `live-server` from npm. If npm exists on the system then do a background install of `live-server` for next run.
302
304
  * 1.2.20 - Fixed up path issue for web browser launch for hot reload.
@@ -382,3 +384,15 @@ A: A big chunk of space is being used by unnecessary javascript `emscripten` bun
382
384
  * 1.0.2 - Small bug with new installs.
383
385
  * 1.0.1 - Re-use is no longer the default, due to problems.
384
386
  * 1.0.0 - Initial release.
387
+
388
+
389
+ # TODO
390
+ * `live-server --https --cert=localhost.pem --key=localhost-key.pem --port=5500`
391
+ * `live-server --port=8416 --host=localhost . --https --cert=C:/Users/niteris/dev/fastled-wasm/src/fastled/assets/localhost-key.pem --key=C:/Users/niteris/dev/fastled-wasm/src/fastled/assets/localhost.pem --no-browser`
392
+
393
+
394
+
395
+ live-server --https --port=8416 --host=localhost . --cert=C:/Users/niteris/dev/fastled-wasm/src/fastled/assets/localhost-key.pem --key=C:/Users/niteris/dev/fastled-wasm/src/fastled/assets/localhost.pem --no-browser
396
+
397
+
398
+ live-server --https --cert=src/fastled/assets/localhost.pem --key=src/fastled/assets/localhost-key.pem --port=5500
@@ -268,8 +268,8 @@ A: `delay()` will block `loop()` which blocks the main thread of the browser. Th
268
268
  Q: How can I get the compiled size of my FastLED sketch smaller?
269
269
  A: A big chunk of space is being used by unnecessary javascript `emscripten` bundling. The wasm_compiler_settings.py file in the FastLED repo can tweak this.
270
270
 
271
- # Revisions
272
271
 
272
+ # Revisions
273
273
  * 1.2.31 - Bunch of fixes and ease of use while compiling code in the repo.
274
274
  * 1.2.22 - Prefer to use `live-server` from npm. If npm exists on the system then do a background install of `live-server` for next run.
275
275
  * 1.2.20 - Fixed up path issue for web browser launch for hot reload.
@@ -355,3 +355,15 @@ A: A big chunk of space is being used by unnecessary javascript `emscripten` bun
355
355
  * 1.0.2 - Small bug with new installs.
356
356
  * 1.0.1 - Re-use is no longer the default, due to problems.
357
357
  * 1.0.0 - Initial release.
358
+
359
+
360
+ # TODO
361
+ * `live-server --https --cert=localhost.pem --key=localhost-key.pem --port=5500`
362
+ * `live-server --port=8416 --host=localhost . --https --cert=C:/Users/niteris/dev/fastled-wasm/src/fastled/assets/localhost-key.pem --key=C:/Users/niteris/dev/fastled-wasm/src/fastled/assets/localhost.pem --no-browser`
363
+
364
+
365
+
366
+ live-server --https --port=8416 --host=localhost . --cert=C:/Users/niteris/dev/fastled-wasm/src/fastled/assets/localhost-key.pem --key=C:/Users/niteris/dev/fastled-wasm/src/fastled/assets/localhost.pem --no-browser
367
+
368
+
369
+ live-server --https --cert=src/fastled/assets/localhost.pem --key=src/fastled/assets/localhost-key.pem --port=5500
@@ -40,6 +40,7 @@ _FASTLED_MODULES_DIR = FASTLED_COMPILER_DIR / "modules"
40
40
  _INDEX_HTML_SRC = FASTLED_COMPILER_DIR / "index.html"
41
41
  _INDEX_CSS_SRC = FASTLED_COMPILER_DIR / "index.css"
42
42
  _INDEX_JS_SRC = FASTLED_COMPILER_DIR / "index.js"
43
+ _PIO_VERBOSE = True
43
44
 
44
45
 
45
46
  _WASM_COMPILER_SETTTINGS = FASTLED_COMPILER_DIR / "wasm_compiler_flags.py"
@@ -108,6 +109,8 @@ def compile(
108
109
  cmd_list.extend(["pio", "run"])
109
110
  if not auto_clean:
110
111
  cmd_list.append("--disable-auto-clean")
112
+ if _PIO_VERBOSE:
113
+ cmd_list.append("-v")
111
114
 
112
115
  def _open_process(cmd_list: list[str] = cmd_list) -> subprocess.Popen:
113
116
  out = subprocess.Popen(
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # load emsdk environment
4
+ source /emsdk/emsdk_env.sh
5
+ export PATH="$PATH:/emsdk/upstream/bin"
6
+
7
+ # initialize runtime
8
+ python init_runtime.py
9
+
10
+ # only do the final prewarm if RUNTIME_PREWARM is set to "1"
11
+ if [[ "${RUNTIME_PREWARM:-0}" == "1" ]]; then
12
+ ./final_prewarm.sh
13
+ fi
14
+
15
+ # hand off to the main command
16
+ exec "$@"
@@ -0,0 +1,136 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Mic & System Audio Demo</title>
7
+ <style>
8
+ body { font-family: sans-serif; text-align: center; padding: 2rem; }
9
+ #controls { margin-bottom: 1rem; }
10
+ #volume { font-size: 2rem; margin-bottom: 1rem; }
11
+ #bar {
12
+ width: 300px;
13
+ height: 30px;
14
+ background: #eee;
15
+ margin: 0 auto;
16
+ border: 1px solid #ccc;
17
+ border-radius: 4px;
18
+ overflow: hidden;
19
+ }
20
+ #fill {
21
+ height: 100%;
22
+ width: 0%;
23
+ background: #76ce60;
24
+ transition: width 0.1s ease;
25
+ }
26
+ #mic-select { margin-left: 0.5rem; }
27
+ </style>
28
+ </head>
29
+ <body>
30
+ <h1>Audio Volume Demo</h1>
31
+ <div id="controls">
32
+ <label><input type="radio" name="source" value="mic" checked> Microphone</label>
33
+ <label><input type="radio" name="source" value="tab"> Tab/Screen Audio</label>
34
+ <select id="mic-select"></select>
35
+ </div>
36
+ <div id="volume">-∞ dBFS</div>
37
+ <div id="bar"><div id="fill"></div></div>
38
+
39
+ <script>
40
+ let audioCtx, analyser, dataArray;
41
+ let currentStream;
42
+
43
+ async function listAudioInputs() {
44
+ const devices = await navigator.mediaDevices.enumerateDevices();
45
+ const mics = devices.filter(d => d.kind === 'audioinput');
46
+ const select = document.getElementById('mic-select');
47
+ select.innerHTML = '';
48
+ mics.forEach((mic, i) => {
49
+ const option = document.createElement('option');
50
+ option.value = mic.deviceId;
51
+ option.text = mic.label || `Microphone ${i+1}`;
52
+ select.appendChild(option);
53
+ });
54
+ select.onchange = () => startMic(select.value);
55
+ if (mics.length) startMic(mics[0].deviceId);
56
+ }
57
+
58
+ async function startMic(deviceId) {
59
+ stopStream();
60
+ try {
61
+ currentStream = await navigator.mediaDevices.getUserMedia({ audio: { deviceId: { exact: deviceId } } });
62
+ setupAudioGraph(currentStream);
63
+ } catch (err) {
64
+ alert('Error accessing microphone: ' + err.message);
65
+ }
66
+ }
67
+
68
+ async function startTabAudio() {
69
+ stopStream();
70
+ try {
71
+ currentStream = await navigator.mediaDevices.getDisplayMedia({ audio: true, video: false });
72
+ setupAudioGraph(currentStream);
73
+ } catch (err) {
74
+ alert('Error capturing tab/screen audio: ' + err.message);
75
+ }
76
+ }
77
+
78
+ function stopStream() {
79
+ if (currentStream) {
80
+ currentStream.getTracks().forEach(track => track.stop());
81
+ }
82
+ }
83
+
84
+ function setupAudioGraph(stream) {
85
+ if (!audioCtx) {
86
+ audioCtx = new (window.AudioContext || window.webkitAudioContext)();
87
+ }
88
+ const source = audioCtx.createMediaStreamSource(stream);
89
+ analyser = audioCtx.createAnalyser();
90
+ analyser.fftSize = 2048;
91
+ source.connect(analyser);
92
+ dataArray = new Float32Array(analyser.fftSize);
93
+ updateVolume();
94
+ }
95
+
96
+ function updateVolume() {
97
+ analyser.getFloatTimeDomainData(dataArray);
98
+ let max = 0;
99
+ for (let i = 0; i < dataArray.length; i++) {
100
+ const v = Math.abs(dataArray[i]);
101
+ if (v > max) max = v;
102
+ }
103
+
104
+ const db = max > 0 ? 20 * Math.log10(max) : -Infinity;
105
+ const dbDisplay = db === -Infinity ? '-∞' : db.toFixed(1);
106
+ document.getElementById('volume').textContent = `${dbDisplay} dBFS`;
107
+
108
+ const percent = Math.min(max * 100, 100);
109
+ document.getElementById('fill').style.width = percent + '%';
110
+
111
+ requestAnimationFrame(updateVolume);
112
+ }
113
+
114
+ // Switcher
115
+ document.querySelectorAll('input[name="source"]').forEach(radio => {
116
+ radio.addEventListener('change', e => {
117
+ const mode = e.target.value;
118
+ if (mode === 'mic') {
119
+ document.getElementById('mic-select').style.display = '';
120
+ listAudioInputs();
121
+ } else {
122
+ document.getElementById('mic-select').style.display = 'none';
123
+ startTabAudio();
124
+ }
125
+ });
126
+ });
127
+
128
+ // Initialize
129
+ if (navigator.mediaDevices && navigator.mediaDevices.enumerateDevices) {
130
+ listAudioInputs();
131
+ } else {
132
+ alert('enumerateDevices() not supported.');
133
+ }
134
+ </script>
135
+ </body>
136
+ </html>
@@ -0,0 +1,109 @@
1
+ """
2
+ Experimental module that extends the multiprocessing.Process class to redirect
3
+ stdout and stderr to pipes that can be read line-by-line in real time.
4
+
5
+ """
6
+
7
+ import multiprocessing
8
+ import os
9
+ import sys
10
+ import time
11
+
12
+
13
+ class ProcessExtended(multiprocessing.Process):
14
+ def __init__(self, target, args=(), kwargs=None):
15
+ if kwargs is None:
16
+ kwargs = {}
17
+ # Store the target function and its arguments.
18
+ self._target_func = target
19
+ self._target_args = args
20
+ self._target_kwargs = kwargs
21
+ # Create OS pipes for stdout and stderr.
22
+ self._stdout_pipe_read, self._stdout_pipe_write = os.pipe()
23
+ self._stderr_pipe_read, self._stderr_pipe_write = os.pipe()
24
+ super().__init__()
25
+
26
+ def run(self):
27
+ """
28
+ In the child process, close the parent's copy of the read ends,
29
+ duplicate the write ends to stdout and stderr, and execute the target function.
30
+ """
31
+ # In child: close the parent's read ends.
32
+ os.close(self._stdout_pipe_read)
33
+ os.close(self._stderr_pipe_read)
34
+
35
+ # Redirect stdout and stderr to the write ends of the pipes.
36
+ os.dup2(self._stdout_pipe_write, sys.stdout.fileno())
37
+ os.dup2(self._stderr_pipe_write, sys.stderr.fileno())
38
+
39
+ # Reassign the Python-level streams with line buffering.
40
+ sys.stdout = os.fdopen(sys.stdout.fileno(), "w", buffering=1)
41
+ sys.stderr = os.fdopen(sys.stderr.fileno(), "w", buffering=1)
42
+
43
+ try:
44
+ self._target_func(*self._target_args, **self._target_kwargs)
45
+ finally:
46
+ # Flush and close the write ends so the parent can detect EOF.
47
+ sys.stdout.flush()
48
+ sys.stderr.flush()
49
+ os.close(self._stdout_pipe_write)
50
+ os.close(self._stderr_pipe_write)
51
+
52
+ def start(self):
53
+ """
54
+ Start the child process and, in the parent process, close the write ends
55
+ of the pipes so that reading from the read ends will end once the child closes them.
56
+ """
57
+ super().start()
58
+ # In the parent process, close the write ends.
59
+ os.close(self._stdout_pipe_write)
60
+ os.close(self._stderr_pipe_write)
61
+
62
+ @property
63
+ def stdout(self):
64
+ """
65
+ Returns a file object that can be iterated line-by-line to read the child's stdout.
66
+ """
67
+ if not hasattr(self, "_stdout_file"):
68
+ self._stdout_file = os.fdopen(self._stdout_pipe_read, "r", buffering=1)
69
+ return self._stdout_file
70
+
71
+ @property
72
+ def stderr(self):
73
+ """
74
+ Returns a file object that can be iterated line-by-line to read the child's stderr.
75
+ """
76
+ if not hasattr(self, "_stderr_file"):
77
+ self._stderr_file = os.fdopen(self._stderr_pipe_read, "r", buffering=1)
78
+ return self._stderr_file
79
+
80
+
81
+ # -------------------------------
82
+ # Example usage:
83
+ # -------------------------------
84
+ def worker():
85
+ for i in range(5):
86
+ print(f"stdout line {i}")
87
+ print(f"stderr line {i}", file=sys.stderr)
88
+ time.sleep(1)
89
+
90
+
91
+ if __name__ == "__main__":
92
+ proc = ProcessExtended(target=worker)
93
+ proc.start()
94
+
95
+ # Iterate over stdout in real time.
96
+ # Note: In a real-world scenario you might want to use threads or select/poll to
97
+ # concurrently process stdout and stderr if both are needed in real time.
98
+ try:
99
+ for line in proc.stdout:
100
+ print("Streamed stdout:", line, end="")
101
+ except KeyboardInterrupt:
102
+ pass
103
+
104
+ # Wait for the process to finish.
105
+ proc.join()
106
+
107
+ # Optionally, process any remaining stderr output.
108
+ for line in proc.stderr:
109
+ print("Streamed stderr:", line, end="")
@@ -0,0 +1,22 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "fastled_server"
7
+ version = "0.1.0"
8
+ description = "FastLED Server"
9
+ requires-python = ">=3.10"
10
+
11
+ dependencies = [
12
+ "platformio==6.1.17",
13
+ "fastapi==0.115.11",
14
+ "uvicorn[standard]==0.34.0",
15
+ "python-multipart==0.0.20",
16
+ "disklru==2.0.0",
17
+ "psutil==6.1.1",
18
+ "wormhole-tx",
19
+ ]
20
+
21
+ [tool.setuptools]
22
+ package-dir = {"" = "."}
@@ -55,6 +55,9 @@ _EXAMPLES: list[str] = [
55
55
  "FxNoiseRing",
56
56
  "FxSdCard",
57
57
  "FxWater",
58
+ "Wave2d",
59
+ "FxWave2d",
60
+ "FireCylinder",
58
61
  ]
59
62
 
60
63
 
@@ -11,9 +11,7 @@ echo Running black src tests compiler
11
11
  black src tests compiler
12
12
  echo Running isort src tests compiler
13
13
  isort --profile black src tests compiler
14
- echo Running mypy src compiler
15
- mypy src tests compiler
16
14
  echo Running pyright src tests compiler
17
- pyright src compiler tests
15
+ uv run pyright src compiler tests
18
16
  echo Linting complete!
19
17
  exit 0
@@ -20,8 +20,10 @@ dependencies = [
20
20
  "appdirs>=1.4.4",
21
21
  "rapidfuzz>=3.10.1",
22
22
  "progress>=1.6",
23
- "Flask>=3.0.0",
24
- "livereload",
23
+ "fastapi>=0.115.12",
24
+ "uvicorn>=0.34.2",
25
+ "pywebview>=5.4",
26
+ "watchfiles>=1.0.5",
25
27
  ]
26
28
 
27
29
  dynamic = ["version"]
@@ -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.50"
16
+ __version__ = "1.2.60"
17
17
 
18
18
  DOCKER_FILE = (
19
19
  "https://raw.githubusercontent.com/zackees/fastled-wasm/refs/heads/main/Dockerfile"
@@ -122,8 +122,7 @@ if __name__ == "__main__":
122
122
  import os
123
123
 
124
124
  os.chdir("../fastled")
125
- sys.argv.append("examples/wasm")
126
- sys.argv.append("--just-compile")
125
+ sys.argv.append("examples/FxWave2d")
127
126
  sys.exit(main())
128
127
  except KeyboardInterrupt:
129
128
  print("\nExiting from main...")
@@ -0,0 +1,28 @@
1
+ -----BEGIN PRIVATE KEY-----
2
+ MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDlxbWcpUXPpjqs
3
+ DJPgFF1FsXPZqq0JPJqssHh4ZLfN0h4yJmj+kRcHS+pkgXnG46g6bUcL/AK5Ba08
4
+ vwnUUGkPH0v4ShKiAGYwvOcbWaqTmvvJuIoaDBXh2jSCeOTagNoaHLYEugARkkEu
5
+ 0/FEW5P/79wU5vJ5G+SyZ8rBCVdxlU57pL1hKWBU7K+BLWsCiZ308NMpzHF5APZ6
6
+ YxVjhFosJPr4TjN6yXr+whrsAjSTHamD5690MbXWyyPG0jwPQyjBot/cNtt8GrsN
7
+ gcjA1E+8VKFvxO8RvZanMZLb0CGEpt7u3oaJ/jprHEsw+/UhnG6Qhksm8C/DN9kP
8
+ hselewffAgMBAAECggEARjQ6YTo+Mkvf8WGGbRjLxteJRiBX7lKOD+V7aY2ce06P
9
+ 21LREbbTCm+vljXZN2OnqvJomsjNLCsH21+jaTOIZg5x79LyDn2Au7N8CWdELwVT
10
+ mTbBO2Ql63P4R0UY54onGYNcOeV6z+OX9u7a8L/qYHCxFdHalBZpsfj0gjaQeStJ
11
+ JSnvGjo6tKkwC/nUmX01qEVQgUO1+39WYqCaIWjijZNXt6XiKclEuu1AkL0u6Mpt
12
+ CzvzEDrEA66D0Lvl3Tek9B4O16Oie5anNnNMHigwU9yVc6dI8vDCRSEiz7laPTFK
13
+ xzOCQmqPGClKXkX3U+OvZp/Ss9U26Wpu0AbRKTvzAQKBgQDsMR9NjMpOmUaWkAwl
14
+ 1wlUxsZ9YkRuTy7R3RfIdYWj6Lcoc4/iN0qILFM7xidkHhYTFqnsnP1SQkV6lEHV
15
+ OalYxZu9F2l1rHPc8G5YWh/KOg1rAEI47MVT4iwhA2fw6JLti/rm25AeSTMjSTqu
16
+ ht3146036opcIF3v86oGUrSXDwKBgQD5CsNcwLeUDGXozjq62T8/mTYwd2Tw3aiY
17
+ KaGp+exAW321vYm5SKsMitBMGU2tGFlv5eptSI48h7SCpgnszaexw7yj30KuvqjG
18
+ bBqq/MsKuXHyn2sG0A7MJ6zfk+4l46B45blDJZ+x7xL0dyS4UCU3zUeesgSGo4zK
19
+ ZOspPIQCMQKBgQCk35VuWP1P6IbxyxPvxi/pUeh01gfWyMdyD9fuQrtLM8PHJQQn
20
+ cVlBvU9MxoHwzV+za3qqhNwAc+p0KtHZuiqQoUCZuqIPVpZ6gAtG+YJ/dA6xxrhz
21
+ bDRC3frYALyp2m/WCoTWaiYsPgTIePHRqqt+XbQo+DwlGyL3wSvKxijx2QKBgCb0
22
+ OwioEE70/X/DukX9szn0chh0pHJUiYl7gZD/yadraCdkRUWZC0BD+j7c+lxn4Z1y
23
+ HhAH+E+Zfm+tHwJOTLuufTQ4uMpygh2/TRCPyAaeaSdlLi17n8TpM84o6mg8yZ3/
24
+ eNH68Za4aYOZm0HFL30h++DjwXd534zM6keh8pgRAoGBAKUrsjDGjuSo8l1fi4Cq
25
+ INu/rQop2h/db02zyJP5q7NKhE1nqogeLwwn+2M/LtHQ1nIzZR+rvrLNgt6oWY31
26
+ sPsv8JUfVT8GmdhU9KKmizK6eUu3rWdj2+rJARmuEaPmHcD5O6oJaGU0qadqQP34
27
+ H+enwWmpyZXAIbEu/q63DFhV
28
+ -----END PRIVATE KEY-----
@@ -0,0 +1,27 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIIEfTCCAuWgAwIBAgIRAPb7jkLrCuqToG+s3AQYeuUwDQYJKoZIhvcNAQELBQAw
3
+ gakxHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBDQTE/MD0GA1UECww2REVT
4
+ S1RPUC1JMzcxOERPXFphY2ggVm9yaGllc0BERVNLVE9QLUkzNzE4RE8gKG5pdGVy
5
+ aXMpMUYwRAYDVQQDDD1ta2NlcnQgREVTS1RPUC1JMzcxOERPXFphY2ggVm9yaGll
6
+ c0BERVNLVE9QLUkzNzE4RE8gKG5pdGVyaXMpMB4XDTI1MDQyODAwMzk1MFoXDTI3
7
+ MDcyODAwMzk1MFowajEnMCUGA1UEChMebWtjZXJ0IGRldmVsb3BtZW50IGNlcnRp
8
+ ZmljYXRlMT8wPQYDVQQLDDZERVNLVE9QLUkzNzE4RE9cWmFjaCBWb3JoaWVzQERF
9
+ U0tUT1AtSTM3MThETyAobml0ZXJpcykwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
10
+ ggEKAoIBAQDlxbWcpUXPpjqsDJPgFF1FsXPZqq0JPJqssHh4ZLfN0h4yJmj+kRcH
11
+ S+pkgXnG46g6bUcL/AK5Ba08vwnUUGkPH0v4ShKiAGYwvOcbWaqTmvvJuIoaDBXh
12
+ 2jSCeOTagNoaHLYEugARkkEu0/FEW5P/79wU5vJ5G+SyZ8rBCVdxlU57pL1hKWBU
13
+ 7K+BLWsCiZ308NMpzHF5APZ6YxVjhFosJPr4TjN6yXr+whrsAjSTHamD5690MbXW
14
+ yyPG0jwPQyjBot/cNtt8GrsNgcjA1E+8VKFvxO8RvZanMZLb0CGEpt7u3oaJ/jpr
15
+ HEsw+/UhnG6Qhksm8C/DN9kPhselewffAgMBAAGjXjBcMA4GA1UdDwEB/wQEAwIF
16
+ oDATBgNVHSUEDDAKBggrBgEFBQcDATAfBgNVHSMEGDAWgBSPBydvhr9wI+FsoW/H
17
+ WK3DbS8IUDAUBgNVHREEDTALgglsb2NhbGhvc3QwDQYJKoZIhvcNAQELBQADggGB
18
+ AJVrF1yczZaxt+A2AhdeFbJQUR6NzGBTc20YeWF1YzLV5sV3QVumwZLP2M9ggRgd
19
+ xWV0xfwUHobFQk6RIPTADcLKctiurql0cgF4DPnpWVvto9RM00U3AkQcMj3xtKBV
20
+ wUqo83TcbqgL+euudFZ09gGTs9u9AENaZPcMh+rW8DDO92t+EwMI/IfopxVOJGUB
21
+ RSM3yTwV93BMYBuddt8mclzLzPK/1WONfsHU2xEascaHR1tYMOmJN9Vq4o0fzWxo
22
+ a2vI6K0aJqZV/ztdXq3akwLc6/e9hyptHWa0i/022xVCyNWIlnuEhT7ENMPxh6rX
23
+ ZCQCZVnhcSWAyFjggLJql3aSID5fPF8rmN7wWsB/I5pl9qwMR1/THMPrm5aWn1Xj
24
+ xW6PxkSGm73kd57DH7tqm5HTd8eYCbnsFofI9rC7xI6HCfwchKp+YHvIEu/LJ56E
25
+ FLnCZW/orYkHCzWntzxv1bddrw1BwaNR8Q+mu3imRP8fuyXb2UkFkINVVyOOWHuW
26
+ Kw==
27
+ -----END CERTIFICATE-----
@@ -0,0 +1,20 @@
1
+ import sys
2
+
3
+ from fastled.app import main as app_main
4
+
5
+ if __name__ == "__main__":
6
+ # Note that the entry point for the exe is in cli.py
7
+ try:
8
+ import os
9
+
10
+ os.chdir("../fastled")
11
+ # sys.argv.append("--server")
12
+ # sys.argv.append("--local")
13
+ sys.argv.append("examples/FxWave2d")
14
+ sys.exit(app_main())
15
+ except KeyboardInterrupt:
16
+ print("\nExiting from main...")
17
+ sys.exit(1)
18
+ except Exception as e:
19
+ print(f"Error: {e}")
20
+ sys.exit(1)
@@ -7,7 +7,7 @@ from pathlib import Path
7
7
 
8
8
  from fastled.compile_server import CompileServer
9
9
  from fastled.docker_manager import DockerManager
10
- from fastled.filewatcher import FileWatcherProcess
10
+ from fastled.filewatcher import DebouncedFileWatcherProcess, FileWatcherProcess
11
11
  from fastled.keyboard import SpaceBarWatcher
12
12
  from fastled.open_browser import open_browser_process
13
13
  from fastled.parse_args import Args
@@ -231,9 +231,8 @@ def run_client(
231
231
  return 1
232
232
 
233
233
  excluded_patterns = ["fastled_js"]
234
-
235
- sketch_filewatcher = FileWatcherProcess(
236
- directory, excluded_patterns=excluded_patterns
234
+ debounced_sketch_watcher = DebouncedFileWatcherProcess(
235
+ FileWatcherProcess(directory, excluded_patterns=excluded_patterns),
237
236
  )
238
237
 
239
238
  source_code_watcher: FileWatcherProcess | None = None
@@ -246,7 +245,7 @@ def run_client(
246
245
  def trigger_rebuild_if_sketch_changed(
247
246
  last_compiled_result: CompileResult,
248
247
  ) -> tuple[bool, CompileResult]:
249
- changed_files = sketch_filewatcher.get_all_changes()
248
+ changed_files = debounced_sketch_watcher.get_all_changes()
250
249
  if changed_files:
251
250
  print(f"\nChanges detected in {changed_files}")
252
251
  last_hash_value = last_compiled_result.hash_value
@@ -301,7 +300,9 @@ def run_client(
301
300
  timeout=1.0
302
301
  )
303
302
  file_changes = source_code_watcher.get_all_changes()
304
- sketch_files_changed = sketch_filewatcher.get_all_changes()
303
+ sketch_files_changed = (
304
+ debounced_sketch_watcher.get_all_changes()
305
+ )
305
306
 
306
307
  if file_changes:
307
308
  print(
@@ -331,7 +332,7 @@ def run_client(
331
332
  print(f"Error: {e}")
332
333
  return 1
333
334
  finally:
334
- sketch_filewatcher.stop()
335
+ debounced_sketch_watcher.stop()
335
336
  if compile_server:
336
337
  compile_server.stop()
337
338
  if browser_proc:
@@ -205,6 +205,7 @@ class DockerManager:
205
205
  """Check if Docker is running by pinging the Docker daemon."""
206
206
 
207
207
  if not DockerManager.is_docker_installed():
208
+ print("Docker is not installed.")
208
209
  return False
209
210
  try:
210
211
  # self.client.ping()
@@ -767,7 +768,7 @@ class DockerManager:
767
768
  print(f"Error removing images: {e}")
768
769
 
769
770
 
770
- def main():
771
+ def main() -> None:
771
772
  # Register SIGINT handler
772
773
  # signal.signal(signal.SIGINT, handle_sigint)
773
774