fastled 1.4.18__tar.gz → 1.4.32__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 (144) hide show
  1. fastled-1.4.32/.vscode/launch.json +14 -0
  2. fastled-1.4.32/.vscode/tasks.json +55 -0
  3. {fastled-1.4.18 → fastled-1.4.32}/PKG-INFO +1 -1
  4. fastled-1.4.32/requirements.docker.txt +1 -0
  5. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/__version__.py +1 -1
  6. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/app.py +10 -0
  7. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/args.py +20 -0
  8. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/client_server.py +51 -15
  9. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/filewatcher.py +3 -0
  10. fastled-1.4.32/src/fastled/install/__init__.py +1 -0
  11. fastled-1.4.32/src/fastled/install/examples_manager.py +62 -0
  12. fastled-1.4.32/src/fastled/install/extension_manager.py +113 -0
  13. fastled-1.4.32/src/fastled/install/main.py +156 -0
  14. fastled-1.4.32/src/fastled/install/project_detection.py +167 -0
  15. fastled-1.4.32/src/fastled/install/test_install.py +373 -0
  16. fastled-1.4.32/src/fastled/install/vscode_config.py +167 -0
  17. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/open_browser.py +7 -4
  18. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/parse_args.py +52 -0
  19. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/playwright/playwright_browser.py +123 -104
  20. fastled-1.4.32/src/fastled/playwright/resize_tracking.py +127 -0
  21. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/settings.py +1 -0
  22. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/web_compile.py +3 -4
  23. {fastled-1.4.18 → fastled-1.4.32}/src/fastled.egg-info/PKG-INFO +1 -1
  24. {fastled-1.4.18 → fastled-1.4.32}/src/fastled.egg-info/SOURCES.txt +9 -0
  25. fastled-1.4.32/task.md +733 -0
  26. {fastled-1.4.18 → fastled-1.4.32}/tests/integration/test_libcompile.py +8 -2
  27. {fastled-1.4.18 → fastled-1.4.32}/tests/integration/test_playwright_integration.py +19 -18
  28. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_manual_api_invocation.py +3 -3
  29. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_session_compile.py +6 -6
  30. fastled-1.4.18/.vscode/launch.json +0 -44
  31. fastled-1.4.18/.vscode/tasks.json +0 -41
  32. fastled-1.4.18/requirements.docker.txt +0 -1
  33. {fastled-1.4.18 → fastled-1.4.32}/.aiderignore +0 -0
  34. {fastled-1.4.18 → fastled-1.4.32}/.cursorrules +0 -0
  35. {fastled-1.4.18 → fastled-1.4.32}/.dockerignore +0 -0
  36. {fastled-1.4.18 → fastled-1.4.32}/.github/workflows/build_multi_docker_image.yml +0 -0
  37. {fastled-1.4.18 → fastled-1.4.32}/.github/workflows/build_webpage.yml +0 -0
  38. {fastled-1.4.18 → fastled-1.4.32}/.github/workflows/lint.yml +0 -0
  39. {fastled-1.4.18 → fastled-1.4.32}/.github/workflows/publish_release.yml +0 -0
  40. {fastled-1.4.18 → fastled-1.4.32}/.github/workflows/template_build_docker_image.yml +0 -0
  41. {fastled-1.4.18 → fastled-1.4.32}/.github/workflows/test_build_exe.yml +0 -0
  42. {fastled-1.4.18 → fastled-1.4.32}/.github/workflows/test_macos.yml +0 -0
  43. {fastled-1.4.18 → fastled-1.4.32}/.github/workflows/test_ubuntu.yml +0 -0
  44. {fastled-1.4.18 → fastled-1.4.32}/.github/workflows/test_win.yml +0 -0
  45. {fastled-1.4.18 → fastled-1.4.32}/.gitignore +0 -0
  46. {fastled-1.4.18 → fastled-1.4.32}/.pylintrc +0 -0
  47. {fastled-1.4.18 → fastled-1.4.32}/.vscode/settings.json +0 -0
  48. {fastled-1.4.18 → fastled-1.4.32}/DEBUGGER.md +0 -0
  49. {fastled-1.4.18 → fastled-1.4.32}/Dockerfile +0 -0
  50. {fastled-1.4.18 → fastled-1.4.32}/FAQ.md +0 -0
  51. {fastled-1.4.18 → fastled-1.4.32}/LICENSE +0 -0
  52. {fastled-1.4.18 → fastled-1.4.32}/MANIFEST.in +0 -0
  53. {fastled-1.4.18 → fastled-1.4.32}/README.md +0 -0
  54. {fastled-1.4.18 → fastled-1.4.32}/RELEASE.md +0 -0
  55. {fastled-1.4.18 → fastled-1.4.32}/TODO.md +0 -0
  56. {fastled-1.4.18 → fastled-1.4.32}/build_exe.py +0 -0
  57. {fastled-1.4.18 → fastled-1.4.32}/build_local_docker.py +0 -0
  58. {fastled-1.4.18 → fastled-1.4.32}/build_site.py +0 -0
  59. {fastled-1.4.18 → fastled-1.4.32}/clean +0 -0
  60. {fastled-1.4.18 → fastled-1.4.32}/compiler/debug.sh +0 -0
  61. {fastled-1.4.18 → fastled-1.4.32}/compiler/run.py +0 -0
  62. {fastled-1.4.18 → fastled-1.4.32}/demo/100dots.html +0 -0
  63. {fastled-1.4.18 → fastled-1.4.32}/demo/demo_threejs.html +0 -0
  64. {fastled-1.4.18 → fastled-1.4.32}/demo/micdemo.html +0 -0
  65. {fastled-1.4.18 → fastled-1.4.32}/demo/mp3upload.html +0 -0
  66. {fastled-1.4.18 → fastled-1.4.32}/demo/webgl_postprocessing_unreal_bloom.html +0 -0
  67. {fastled-1.4.18 → fastled-1.4.32}/docker-compose.yml +0 -0
  68. {fastled-1.4.18 → fastled-1.4.32}/entrypoint.sh +0 -0
  69. {fastled-1.4.18 → fastled-1.4.32}/install +0 -0
  70. {fastled-1.4.18 → fastled-1.4.32}/install_linux.sh +0 -0
  71. {fastled-1.4.18 → fastled-1.4.32}/lint +0 -0
  72. {fastled-1.4.18 → fastled-1.4.32}/pyproject.toml +0 -0
  73. {fastled-1.4.18 → fastled-1.4.32}/requirements.testing.txt +0 -0
  74. {fastled-1.4.18 → fastled-1.4.32}/setup.cfg +0 -0
  75. {fastled-1.4.18 → fastled-1.4.32}/setup.py +0 -0
  76. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/__init__.py +0 -0
  77. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/__main__.py +0 -0
  78. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/assets/example.txt +0 -0
  79. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/assets/localhost-key.pem +0 -0
  80. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/assets/localhost.pem +0 -0
  81. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/cli.py +0 -0
  82. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/cli_test.py +0 -0
  83. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/cli_test_interactive.py +0 -0
  84. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/compile_server.py +0 -0
  85. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/compile_server_impl.py +0 -0
  86. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/docker_manager.py +0 -0
  87. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/find_good_connection.py +0 -0
  88. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/interruptible_http.py +0 -0
  89. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/keyboard.py +0 -0
  90. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/keyz.py +0 -0
  91. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/live_client.py +0 -0
  92. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/paths.py +0 -0
  93. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/playwright/chrome_extension_downloader.py +0 -0
  94. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/print_filter.py +0 -0
  95. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/project_init.py +0 -0
  96. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/select_sketch_directory.py +0 -0
  97. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/server_flask.py +0 -0
  98. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/server_start.py +0 -0
  99. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/site/build.py +0 -0
  100. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/site/examples.py +0 -0
  101. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/sketch.py +0 -0
  102. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/spinner.py +0 -0
  103. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/string_diff.py +0 -0
  104. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/test/can_run_local_docker_tests.py +0 -0
  105. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/test/examples.py +0 -0
  106. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/types.py +0 -0
  107. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/util.py +0 -0
  108. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/version.py +0 -0
  109. {fastled-1.4.18 → fastled-1.4.32}/src/fastled/zip_files.py +0 -0
  110. {fastled-1.4.18 → fastled-1.4.32}/src/fastled.egg-info/dependency_links.txt +0 -0
  111. {fastled-1.4.18 → fastled-1.4.32}/src/fastled.egg-info/entry_points.txt +0 -0
  112. {fastled-1.4.18 → fastled-1.4.32}/src/fastled.egg-info/requires.txt +0 -0
  113. {fastled-1.4.18 → fastled-1.4.32}/src/fastled.egg-info/top_level.txt +0 -0
  114. {fastled-1.4.18 → fastled-1.4.32}/test +0 -0
  115. {fastled-1.4.18 → fastled-1.4.32}/tests/integration/test_build_examples.py +0 -0
  116. {fastled-1.4.18 → fastled-1.4.32}/tests/integration/test_examples.py +0 -0
  117. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/html/index.html +0 -0
  118. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_api.py +0 -0
  119. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_bad_ino.py +0 -0
  120. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_banner_string.py +0 -0
  121. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_cli.py +0 -0
  122. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_cli_no_platformio.py +0 -0
  123. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_compile_server.py +0 -0
  124. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_debug_fetch_source_files.py +0 -0
  125. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_docker_linux_on_windows.py +0 -0
  126. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_embedded_data.py +0 -0
  127. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_filechanger.py +0 -0
  128. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_flask_headers.py +0 -0
  129. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_http_server.py +0 -0
  130. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_ino/bad/bad.ino +0 -0
  131. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_ino/bad_platformio/bad_platformio.ino +0 -0
  132. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_ino/bad_platformio/platformio.ini +0 -0
  133. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_ino/embedded/data/bigdata.dat +0 -0
  134. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_ino/embedded/wasm.ino +0 -0
  135. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_ino/wasm/wasm.ino +0 -0
  136. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_no_platformio_compile.py +0 -0
  137. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_project_init.py +0 -0
  138. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_server_and_client_seperatly.py +0 -0
  139. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_string_diff.py +0 -0
  140. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_string_diff_comprehensive.py +0 -0
  141. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_version.py +0 -0
  142. {fastled-1.4.18 → fastled-1.4.32}/tests/unit/test_webcompile.py +0 -0
  143. {fastled-1.4.18 → fastled-1.4.32}/upload_package.sh +0 -0
  144. {fastled-1.4.18 → fastled-1.4.32}/vscode-plugin/readme +0 -0
@@ -0,0 +1,14 @@
1
+ {
2
+ "version": "0.2.0",
3
+ "configurations": [
4
+ {
5
+ "name": "\ud83c\udfaf Auto Debug (Smart File Detection)",
6
+ "type": "auto-debug",
7
+ "request": "launch",
8
+ "map": {
9
+ "*.ino": "Arduino: Run .ino with FastLED",
10
+ "*.py": "Python: Current File (UV)"
11
+ }
12
+ }
13
+ ]
14
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "version": "2.0.0",
3
+ "tasks": [
4
+ {
5
+ "type": "shell",
6
+ "label": "Run FastLED (Debug)",
7
+ "command": "fastled",
8
+ "args": [
9
+ "${file}",
10
+ "--debug",
11
+ "--app"
12
+ ],
13
+ "options": {
14
+ "cwd": "${workspaceFolder}"
15
+ },
16
+ "group": {
17
+ "kind": "build",
18
+ "isDefault": true
19
+ },
20
+ "presentation": {
21
+ "echo": true,
22
+ "reveal": "always",
23
+ "focus": true,
24
+ "panel": "new",
25
+ "showReuseMessage": false,
26
+ "clear": true
27
+ },
28
+ "detail": "Run FastLED with debug mode and app visualization",
29
+ "problemMatcher": []
30
+ },
31
+ {
32
+ "type": "shell",
33
+ "label": "Run FastLED (Quick)",
34
+ "command": "fastled",
35
+ "args": [
36
+ "${file}",
37
+ "--background-update"
38
+ ],
39
+ "options": {
40
+ "cwd": "${workspaceFolder}"
41
+ },
42
+ "group": "build",
43
+ "presentation": {
44
+ "echo": true,
45
+ "reveal": "always",
46
+ "focus": true,
47
+ "panel": "new",
48
+ "showReuseMessage": false,
49
+ "clear": true
50
+ },
51
+ "detail": "Run FastLED with quick background update mode",
52
+ "problemMatcher": []
53
+ }
54
+ ]
55
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fastled
3
- Version: 1.4.18
3
+ Version: 1.4.32
4
4
  Summary: FastLED Wasm Compiler
5
5
  Home-page: https://github.com/zackees/fastled-wasm
6
6
  Maintainer: Zachary Vorhies
@@ -0,0 +1 @@
1
+ fastled-wasm-server>=1.1.37
@@ -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.4.18"
4
+ __version__ = "1.4.32"
5
5
 
6
6
  __version_url_latest__ = "https://raw.githubusercontent.com/zackees/fastled-wasm/refs/heads/main/src/fastled/__version__.py"
@@ -51,6 +51,16 @@ def main() -> int:
51
51
  from fastled.select_sketch_directory import select_sketch_directory
52
52
 
53
53
  args = parse_args()
54
+
55
+ # Handle --install command early
56
+ if args.install:
57
+ from fastled.install.main import fastled_install
58
+
59
+ result = fastled_install(
60
+ dry_run=args.dry_run, no_interactive=args.no_interactive
61
+ )
62
+ return 0 if result else 1
63
+
54
64
  interactive: bool = args.interactive
55
65
  has_server = args.server
56
66
  update: bool = args.update
@@ -16,6 +16,7 @@ class Args:
16
16
  app: bool # New flag to trigger Playwright browser with browser download if needed
17
17
  auto_update: bool | None
18
18
  update: bool
19
+ background_update: bool
19
20
  localhost: bool
20
21
  build: bool
21
22
  server: bool
@@ -25,6 +26,9 @@ class Args:
25
26
  release: bool
26
27
  ram_disk_size: str # suffixed liked "25mb" or "1gb"
27
28
  clear = False # Force the last running container to be removed. Useful for benchmarking.
29
+ install: bool = False # Install FastLED development environment
30
+ dry_run: bool = False # Dry run mode for testing
31
+ no_interactive: bool = False # Non-interactive mode
28
32
 
29
33
  @staticmethod
30
34
  def from_namespace(args: argparse.Namespace) -> "Args":
@@ -57,6 +61,9 @@ class Args:
57
61
  args.no_auto_updates, bool | None
58
62
  ), f"expected bool | None, got {type(args.no_auto_updates)}"
59
63
  assert isinstance(args.update, bool), f"expected bool, got {type(args.update)}"
64
+ assert isinstance(
65
+ args.background_update, bool
66
+ ), f"expected bool, got {type(args.background_update)}"
60
67
  assert isinstance(
61
68
  args.localhost, bool
62
69
  ), f"expected bool, got {type(args.localhost)}"
@@ -68,6 +75,15 @@ class Args:
68
75
  assert isinstance(
69
76
  args.release, bool
70
77
  ), f"expected bool, got {type(args.release)}"
78
+ assert isinstance(
79
+ args.install, bool
80
+ ), f"expected bool, got {type(args.install)}"
81
+ assert isinstance(
82
+ args.dry_run, bool
83
+ ), f"expected bool, got {type(args.dry_run)}"
84
+ assert isinstance(
85
+ args.no_interactive, bool
86
+ ), f"expected bool, got {type(args.no_interactive)}"
71
87
 
72
88
  init: bool | str = False
73
89
  if args.init is None:
@@ -88,6 +104,7 @@ class Args:
88
104
  app=args.app,
89
105
  auto_update=not args.no_auto_updates,
90
106
  update=args.update,
107
+ background_update=args.background_update,
91
108
  localhost=args.localhost,
92
109
  build=args.build,
93
110
  server=args.server,
@@ -96,4 +113,7 @@ class Args:
96
113
  quick=args.quick,
97
114
  release=args.release,
98
115
  ram_disk_size=args.ram_disk_size,
116
+ install=args.install,
117
+ dry_run=args.dry_run,
118
+ no_interactive=args.no_interactive,
99
119
  )
@@ -100,7 +100,7 @@ def _run_web_compiler(
100
100
 
101
101
  # Guard: libfastled compilation requires volume source mapping
102
102
  if not allow_libcompile:
103
- print("⚠️ libfastled compilation disabled: not running in FastLED repository")
103
+ print("⚠️ libfastled compilation disabled.")
104
104
 
105
105
  start = time.time()
106
106
  web_result = web_compile(
@@ -243,14 +243,35 @@ def _try_make_compile_server(
243
243
  return None
244
244
 
245
245
 
246
- def _is_local_host(host: str) -> bool:
246
+ def _background_update_docker_image() -> None:
247
+ """Perform docker image update in the background."""
248
+ try:
249
+ print("\n🔄 Starting background update of docker image...")
250
+ docker_manager = DockerManager()
251
+ updated = docker_manager.validate_or_download_image(
252
+ image_name=IMAGE_NAME, tag="latest", upgrade=True
253
+ )
254
+ if updated:
255
+ print("✅ Background docker image update completed successfully.")
256
+ else:
257
+ print("ℹ️ Docker image was already up to date.")
258
+ except KeyboardInterrupt:
259
+ print("⚠️ Background docker image update interrupted by user.")
260
+ import _thread
261
+
262
+ _thread.interrupt_main()
263
+ except Exception as e:
264
+ print(f"⚠️ Background docker image update failed: {e}")
265
+
266
+
267
+ def _is_local_host(url: str) -> bool:
247
268
  return (
248
- host.startswith("http://localhost")
249
- or host.startswith("http://127.0.0.1")
250
- or host.startswith("http://0.0.0.0")
251
- or host.startswith("http://[::]")
252
- or host.startswith("http://[::1]")
253
- or host.startswith("http://[::ffff:127.0.0.1]")
269
+ url.startswith("http://localhost")
270
+ or url.startswith("http://127.0.0.1")
271
+ or url.startswith("http://0.0.0.0")
272
+ or url.startswith("http://[::]")
273
+ or url.startswith("http://[::1]")
274
+ or url.startswith("http://[::ffff:127.0.0.1]")
254
275
  )
255
276
 
256
277
 
@@ -268,6 +289,7 @@ def run_client(
268
289
  clear: bool = False,
269
290
  no_platformio: bool = False,
270
291
  app: bool = False, # Use app-like browser experience
292
+ background_update: bool = False,
271
293
  ) -> int:
272
294
  has_checked_newer_version_yet = False
273
295
  compile_server: CompileServer | None = None
@@ -426,9 +448,20 @@ def run_client(
426
448
  )
427
449
  if has_update:
428
450
  print(f"\n🔄 {message}")
429
- print(
430
- "Run with `fastled -u` to update the docker image to the latest version."
431
- )
451
+ if background_update:
452
+ # Start background update in a separate thread
453
+ update_thread = threading.Thread(
454
+ target=_background_update_docker_image, daemon=True
455
+ )
456
+ update_thread.start()
457
+ background_update = False
458
+ else:
459
+ print(
460
+ "Run with `fastled -u` to update the docker image to the latest version."
461
+ )
462
+ print(
463
+ "Or use `--background-update` to update automatically in the background after compilation."
464
+ )
432
465
  except Exception as e:
433
466
  # Don't let Docker check failures interrupt the main flow
434
467
  warnings.warn(f"Failed to check for Docker image updates: {e}")
@@ -461,9 +494,6 @@ def run_client(
461
494
  print(f"\nChanges detected in FastLED source code: {changed_files}")
462
495
  print("Press space bar to trigger compile.")
463
496
 
464
- # Re-evaluate libcompile capability when source code changes
465
- allow_libcompile = True
466
-
467
497
  while True:
468
498
  space_bar_pressed = SpaceBarWatcher.watch_space_bar_pressed(
469
499
  timeout=1.0
@@ -477,6 +507,8 @@ def run_client(
477
507
  print(
478
508
  f"Changes detected in {file_changes}\nHit the space bar to trigger compile."
479
509
  )
510
+ # Re-evaluate libcompile capability when source code changes
511
+ allow_libcompile = True
480
512
 
481
513
  if space_bar_pressed or sketch_files_changed:
482
514
  if space_bar_pressed:
@@ -488,7 +520,9 @@ def run_client(
488
520
  last_compiled_result = compile_function(
489
521
  last_hash_value=None, allow_libcompile=allow_libcompile
490
522
  )
491
- allow_libcompile = False
523
+ allow_libcompile = (
524
+ allow_libcompile and not last_compiled_result.success
525
+ )
492
526
  print("Finished recompile.")
493
527
  # Drain the space bar queue
494
528
  SpaceBarWatcher.watch_space_bar_pressed()
@@ -513,6 +547,7 @@ def run_client_server(args: Args) -> int:
513
547
  profile = bool(args.profile)
514
548
  web: str | bool = args.web if isinstance(args.web, str) else bool(args.web)
515
549
  auto_update = bool(args.auto_update)
550
+ background_update = bool(args.background_update)
516
551
  localhost = bool(args.localhost)
517
552
  directory = args.directory if args.directory else Path(".")
518
553
  just_compile = bool(args.just_compile)
@@ -585,6 +620,7 @@ def run_client_server(args: Args) -> int:
585
620
  clear=args.clear,
586
621
  no_platformio=no_platformio,
587
622
  app=app,
623
+ background_update=background_update,
588
624
  )
589
625
  except KeyboardInterrupt:
590
626
  return 1
@@ -122,6 +122,9 @@ class FileChangedNotifier(threading.Thread):
122
122
  time.sleep(0.1)
123
123
  except KeyboardInterrupt:
124
124
  print("File watcher stopped by user.")
125
+ import _thread
126
+
127
+ _thread.interrupt_main()
125
128
  finally:
126
129
  self.stop()
127
130
 
@@ -0,0 +1 @@
1
+ """FastLED install module."""
@@ -0,0 +1,62 @@
1
+ """Examples installation manager using FastLED's built-in --project-init command."""
2
+
3
+ import subprocess
4
+
5
+
6
+ def install_fastled_examples_via_project_init(
7
+ force: bool = False, no_interactive: bool = False
8
+ ) -> bool:
9
+ """
10
+ Install FastLED examples using built-in --project-init command.
11
+
12
+ Args:
13
+ force: If True, install without prompting
14
+ no_interactive: If True, skip prompting and return False
15
+
16
+ Returns:
17
+ True if installation successful, False otherwise
18
+ """
19
+ if not force:
20
+ if no_interactive:
21
+ print("⚠️ No existing Arduino content found.")
22
+ print(" In non-interactive mode, skipping examples installation.")
23
+ print(" Run 'fastled --project-init' manually to install examples.")
24
+ return False
25
+
26
+ print("No existing Arduino content found.")
27
+ answer = (
28
+ input("Would you like to install FastLED examples? [y/n] ").strip().lower()
29
+ )
30
+ if answer not in ["y", "yes"]:
31
+ print("Skipping FastLED examples installation.")
32
+ return False
33
+
34
+ print("📦 Installing FastLED examples using project initialization...")
35
+
36
+ try:
37
+ # Use FastLED's built-in project initialization
38
+ subprocess.run(
39
+ ["fastled", "--project-init"],
40
+ check=True,
41
+ capture_output=True,
42
+ text=True,
43
+ cwd=".",
44
+ )
45
+
46
+ print("✅ FastLED project initialized successfully!")
47
+ print("📁 Examples and project structure created")
48
+ print("🚀 Quick start: Check for generated .ino files and press F5 to debug")
49
+
50
+ return True
51
+
52
+ except subprocess.CalledProcessError as e:
53
+ print(f"⚠️ Warning: Failed to initialize FastLED project: {e}")
54
+ if e.stderr:
55
+ print(f"Error details: {e.stderr}")
56
+ print("You can manually run: fastled --project-init")
57
+ return False
58
+ except FileNotFoundError:
59
+ print("⚠️ Warning: FastLED package not found. Please install it first:")
60
+ print(" pip install fastled")
61
+ print("Then run: fastled --project-init")
62
+ return False
@@ -0,0 +1,113 @@
1
+ """Auto Debug extension installation manager."""
2
+
3
+ import shutil
4
+ import subprocess
5
+ import tempfile
6
+ from pathlib import Path
7
+ from urllib.request import urlretrieve
8
+
9
+
10
+ def download_auto_debug_extension() -> Path | None:
11
+ """
12
+ Download the Auto Debug extension .vsix file from GitHub.
13
+
14
+ Returns:
15
+ Path to downloaded .vsix file, or None if download fails
16
+ """
17
+ # URL for the Auto Debug extension
18
+ extension_url = "https://github.com/zackees/vscode-auto-debug/releases/latest/download/auto-debug.vsix"
19
+
20
+ try:
21
+ # Create temporary directory
22
+ temp_dir = Path(tempfile.mkdtemp())
23
+ vsix_path = temp_dir / "auto-debug.vsix"
24
+
25
+ print("📥 Downloading Auto Debug extension...")
26
+
27
+ # Download the file
28
+ urlretrieve(extension_url, vsix_path)
29
+
30
+ if vsix_path.exists() and vsix_path.stat().st_size > 0:
31
+ print("✅ Extension downloaded successfully")
32
+ return vsix_path
33
+ else:
34
+ print("❌ Failed to download extension")
35
+ return None
36
+
37
+ except Exception as e:
38
+ print(f"❌ Error downloading extension: {e}")
39
+ return None
40
+
41
+
42
+ def install_vscode_extensions(extension_path: Path) -> bool:
43
+ """
44
+ Install extension in VSCode or Cursor.
45
+
46
+ Args:
47
+ extension_path: Path to .vsix file
48
+
49
+ Returns:
50
+ True if installation successful, False otherwise
51
+ """
52
+ # Try VSCode first
53
+ if shutil.which("code"):
54
+ ide_command = "code"
55
+ ide_name = "VSCode"
56
+ elif shutil.which("cursor"):
57
+ ide_command = "cursor"
58
+ ide_name = "Cursor"
59
+ else:
60
+ print("❌ No supported IDE found (VSCode or Cursor)")
61
+ return False
62
+
63
+ try:
64
+ print(f"📦 Installing extension in {ide_name}...")
65
+
66
+ # Install the extension
67
+ subprocess.run(
68
+ [ide_command, "--install-extension", str(extension_path)],
69
+ capture_output=True,
70
+ text=True,
71
+ check=True,
72
+ )
73
+
74
+ print(f"✅ Auto Debug extension installed in {ide_name}")
75
+ return True
76
+
77
+ except subprocess.CalledProcessError as e:
78
+ print(f"❌ Failed to install extension: {e}")
79
+ if e.stderr:
80
+ print(f"Error details: {e.stderr}")
81
+ return False
82
+ finally:
83
+ # Clean up temporary file
84
+ if extension_path.exists():
85
+ try:
86
+ extension_path.unlink()
87
+ extension_path.parent.rmdir()
88
+ except Exception:
89
+ pass
90
+
91
+
92
+ def install_auto_debug_extension(dry_run: bool = False) -> bool:
93
+ """
94
+ Main function to download and install Auto Debug extension.
95
+
96
+ Args:
97
+ dry_run: If True, simulate installation without actually installing
98
+
99
+ Returns:
100
+ True if installation successful, False otherwise
101
+ """
102
+ if dry_run:
103
+ print("[DRY-RUN]: Would download and install Auto Debug extension")
104
+ print("[DRY-RUN]: NO PLUGIN INSTALLED")
105
+ return True
106
+
107
+ # Download extension
108
+ vsix_path = download_auto_debug_extension()
109
+ if not vsix_path:
110
+ return False
111
+
112
+ # Install extension
113
+ return install_vscode_extensions(vsix_path)
@@ -0,0 +1,156 @@
1
+ """Main installation orchestrator for FastLED --install feature."""
2
+
3
+ import sys
4
+
5
+ from .examples_manager import install_fastled_examples_via_project_init
6
+ from .extension_manager import install_auto_debug_extension
7
+ from .project_detection import (
8
+ check_existing_arduino_content,
9
+ detect_fastled_project,
10
+ is_fastled_repository,
11
+ validate_vscode_project,
12
+ )
13
+ from .vscode_config import (
14
+ generate_fastled_tasks,
15
+ update_launch_json_for_arduino,
16
+ update_vscode_settings_for_fastled,
17
+ )
18
+
19
+
20
+ def fastled_install(dry_run: bool = False, no_interactive: bool = False) -> bool:
21
+ """
22
+ Main installation function with dry-run support.
23
+
24
+ Args:
25
+ dry_run: If True, simulate installation without making changes
26
+ no_interactive: If True, fail instead of prompting for input
27
+
28
+ Returns:
29
+ True if installation successful, False otherwise
30
+ """
31
+ try:
32
+ print("🚀 Starting FastLED installation...")
33
+
34
+ # 1. Validate VSCode project or offer alternatives
35
+ if not validate_vscode_project(no_interactive):
36
+ return False
37
+
38
+ # 2. Detect project type
39
+ is_fastled_project = detect_fastled_project()
40
+ is_repository = is_fastled_repository()
41
+
42
+ if is_fastled_project:
43
+ if is_repository:
44
+ print(
45
+ "✅ Detected FastLED repository - will configure full development environment"
46
+ )
47
+ else:
48
+ print(
49
+ "✅ Detected external FastLED project - will configure Arduino environment"
50
+ )
51
+ else:
52
+ print(
53
+ "✅ Detected standard project - will configure basic Arduino environment"
54
+ )
55
+
56
+ # 3. Auto Debug extension (with prompt)
57
+ if not dry_run and not no_interactive:
58
+ answer = (
59
+ input(
60
+ "\nWould you like to install the plugin for FastLED (auto-debug)? [y/n] "
61
+ )
62
+ .strip()
63
+ .lower()
64
+ )
65
+ elif no_interactive:
66
+ print(
67
+ "\n⚠️ Skipping Auto Debug extension installation in non-interactive mode"
68
+ )
69
+ answer = "no"
70
+ else:
71
+ answer = "yes"
72
+ print("\n[DRY-RUN]: Simulating Auto Debug extension installation...")
73
+
74
+ if answer in ["y", "yes"]:
75
+ if not install_auto_debug_extension(dry_run):
76
+ print(
77
+ "⚠️ Warning: Auto Debug extension installation failed, continuing..."
78
+ )
79
+
80
+ # 4. Configure VSCode files
81
+ print("\n📝 Configuring VSCode files...")
82
+ update_launch_json_for_arduino()
83
+ generate_fastled_tasks()
84
+
85
+ # 5. Examples installation (conditional)
86
+ if not check_existing_arduino_content():
87
+ if no_interactive:
88
+ print(
89
+ "⚠️ No Arduino content found. In non-interactive mode, skipping examples installation."
90
+ )
91
+ print(" Run 'fastled --project-init' manually to install examples.")
92
+ else:
93
+ install_fastled_examples_via_project_init(no_interactive=no_interactive)
94
+ else:
95
+ print(
96
+ "✅ Existing Arduino content detected, skipping examples installation"
97
+ )
98
+
99
+ # 6. Full development setup (repository only)
100
+ if is_fastled_project:
101
+ if is_repository:
102
+ print("\n🔧 Setting up FastLED development environment...")
103
+ update_vscode_settings_for_fastled()
104
+ else:
105
+ print(
106
+ "\n⚠️ Skipping clangd settings - not in FastLED repository (protects your environment)"
107
+ )
108
+
109
+ # 7. Post-installation auto-execution
110
+ if not dry_run:
111
+ auto_execute_fastled()
112
+ else:
113
+ print("\n[DRY-RUN]: Skipping auto-execution")
114
+
115
+ print("\n✅ FastLED installation completed successfully!")
116
+ return True
117
+
118
+ except Exception as e:
119
+ print(f"\n❌ Installation failed: {e}")
120
+ import traceback
121
+
122
+ traceback.print_exc()
123
+ return False
124
+
125
+
126
+ def auto_execute_fastled() -> None:
127
+ """Auto-launch fastled after successful installation."""
128
+ if check_existing_arduino_content():
129
+ print("\n🚀 Auto-launching FastLED...")
130
+
131
+ # Import the main function to avoid circular imports
132
+ from fastled.app import main
133
+
134
+ # Filter out --install and --dry-run from sys.argv
135
+ original_argv = sys.argv.copy()
136
+ filtered_argv = [
137
+ arg for arg in sys.argv if arg not in ["--install", "--dry-run"]
138
+ ]
139
+
140
+ # If no directory specified, add current directory
141
+ if len(filtered_argv) == 1: # Only the command name
142
+ filtered_argv.append(".")
143
+
144
+ # Replace sys.argv temporarily
145
+ sys.argv = filtered_argv
146
+
147
+ try:
148
+ # Call main directly
149
+ main()
150
+ finally:
151
+ # Restore original argv
152
+ sys.argv = original_argv
153
+ else:
154
+ print(
155
+ "\n💡 No Arduino content found. Create some .ino files and run 'fastled' to compile!"
156
+ )