fastled 1.3.30__py3-none-any.whl → 1.4.50__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. fastled/__init__.py +30 -2
  2. fastled/__main__.py +14 -0
  3. fastled/__version__.py +1 -1
  4. fastled/app.py +51 -2
  5. fastled/args.py +33 -0
  6. fastled/client_server.py +188 -40
  7. fastled/compile_server.py +10 -0
  8. fastled/compile_server_impl.py +34 -1
  9. fastled/docker_manager.py +56 -14
  10. fastled/emoji_util.py +27 -0
  11. fastled/filewatcher.py +6 -3
  12. fastled/find_good_connection.py +105 -0
  13. fastled/header_dump.py +63 -0
  14. fastled/install/__init__.py +1 -0
  15. fastled/install/examples_manager.py +62 -0
  16. fastled/install/extension_manager.py +113 -0
  17. fastled/install/main.py +156 -0
  18. fastled/install/project_detection.py +167 -0
  19. fastled/install/test_install.py +373 -0
  20. fastled/install/vscode_config.py +344 -0
  21. fastled/interruptible_http.py +148 -0
  22. fastled/live_client.py +21 -1
  23. fastled/open_browser.py +84 -16
  24. fastled/parse_args.py +110 -9
  25. fastled/playwright/chrome_extension_downloader.py +207 -0
  26. fastled/playwright/playwright_browser.py +773 -0
  27. fastled/playwright/resize_tracking.py +127 -0
  28. fastled/print_filter.py +52 -52
  29. fastled/project_init.py +20 -13
  30. fastled/select_sketch_directory.py +142 -19
  31. fastled/server_flask.py +37 -1
  32. fastled/settings.py +47 -3
  33. fastled/sketch.py +121 -4
  34. fastled/string_diff.py +162 -26
  35. fastled/test/examples.py +7 -5
  36. fastled/types.py +4 -0
  37. fastled/util.py +34 -0
  38. fastled/version.py +41 -41
  39. fastled/web_compile.py +379 -236
  40. fastled/zip_files.py +76 -0
  41. {fastled-1.3.30.dist-info → fastled-1.4.50.dist-info}/METADATA +533 -508
  42. fastled-1.4.50.dist-info/RECORD +60 -0
  43. fastled-1.3.30.dist-info/RECORD +0 -44
  44. {fastled-1.3.30.dist-info → fastled-1.4.50.dist-info}/WHEEL +0 -0
  45. {fastled-1.3.30.dist-info → fastled-1.4.50.dist-info}/entry_points.txt +0 -0
  46. {fastled-1.3.30.dist-info → fastled-1.4.50.dist-info}/licenses/LICENSE +0 -0
  47. {fastled-1.3.30.dist-info → fastled-1.4.50.dist-info}/top_level.txt +0 -0
fastled/__init__.py CHANGED
@@ -7,6 +7,7 @@ from typing import Generator
7
7
 
8
8
  from .__version__ import __version__
9
9
  from .compile_server import CompileServer
10
+ from .emoji_util import EMO
10
11
  from .live_client import LiveClient
11
12
  from .settings import DOCKER_FILE, IMAGE_NAME
12
13
  from .site.build import build
@@ -39,15 +40,31 @@ class Api:
39
40
  host: str | CompileServer | None = None,
40
41
  build_mode: BuildMode = BuildMode.QUICK,
41
42
  profile: bool = False, # When true then profile information will be enabled and included in the zip.
43
+ no_platformio: bool = False,
42
44
  ) -> CompileResult:
45
+ from fastled.sketch import looks_like_fastled_repo
43
46
  from fastled.web_compile import web_compile
44
47
 
45
48
  if isinstance(host, CompileServer):
46
49
  host = host.url()
47
50
  if isinstance(directory, str):
48
51
  directory = Path(directory)
52
+
53
+ # Guard: libfastled compilation requires volume source mapping
54
+ # Only allow libcompile if we're in a FastLED repository
55
+ allow_libcompile = looks_like_fastled_repo(Path(".").resolve())
56
+ if not allow_libcompile:
57
+ print(
58
+ f"{EMO('⚠️', 'WARNING:')} libfastled compilation disabled: not running in FastLED repository"
59
+ )
60
+
49
61
  out: CompileResult = web_compile(
50
- directory, host, build_mode=build_mode, profile=profile
62
+ directory,
63
+ host,
64
+ build_mode=build_mode,
65
+ profile=profile,
66
+ no_platformio=no_platformio,
67
+ allow_libcompile=allow_libcompile,
51
68
  )
52
69
  return out
53
70
 
@@ -64,6 +81,8 @@ class Api:
64
81
  http_port: (
65
82
  int | None
66
83
  ) = None, # None means auto select a free port. -1 means no server.
84
+ no_platformio: bool = False,
85
+ enable_https: bool = True, # Enable HTTPS for the local server
67
86
  ) -> LiveClient:
68
87
  return LiveClient(
69
88
  sketch_directory=sketch_directory,
@@ -75,6 +94,8 @@ class Api:
75
94
  build_mode=build_mode,
76
95
  profile=profile,
77
96
  http_port=http_port,
97
+ no_platformio=no_platformio,
98
+ enable_https=enable_https,
78
99
  )
79
100
 
80
101
  @staticmethod
@@ -85,9 +106,9 @@ class Api:
85
106
  mapped_dir: Path | None = None, # Sketch directory.
86
107
  container_name: str | None = None, # Specific docker container name.
87
108
  remove_previous: bool = False,
109
+ no_platformio: bool = False,
88
110
  ) -> CompileServer:
89
111
  """Uses docker to spawn a compile server from the given name."""
90
- from fastled.compile_server import CompileServer
91
112
 
92
113
  out = CompileServer(
93
114
  container_name=container_name,
@@ -96,6 +117,7 @@ class Api:
96
117
  mapped_dir=mapped_dir,
97
118
  auto_start=auto_start,
98
119
  remove_previous=remove_previous,
120
+ no_platformio=no_platformio,
99
121
  )
100
122
  return out
101
123
 
@@ -108,6 +130,7 @@ class Api:
108
130
  mapped_dir: Path | None = None, # Sketch directory.
109
131
  container_name: str | None = None, # Specific docker container name.
110
132
  remove_previous=False,
133
+ no_platformio: bool = False,
111
134
  ) -> Generator[CompileServer, None, None]:
112
135
  server = Api.spawn_server(
113
136
  interactive=interactive,
@@ -116,6 +139,7 @@ class Api:
116
139
  mapped_dir=mapped_dir,
117
140
  container_name=container_name,
118
141
  remove_previous=remove_previous,
142
+ no_platformio=no_platformio,
119
143
  )
120
144
  try:
121
145
  yield server
@@ -196,6 +220,8 @@ class Test:
196
220
  port: int | None = None,
197
221
  compile_server_port: int | None = None,
198
222
  open_browser: bool = True,
223
+ app: bool = False,
224
+ enable_https: bool = False, # Default to HTTP for tests (tests use http:// URLs)
199
225
  ) -> Process:
200
226
  from fastled.open_browser import spawn_http_server
201
227
 
@@ -207,6 +233,8 @@ class Test:
207
233
  port=port,
208
234
  compile_server_port=compile_server_port,
209
235
  open_browser=open_browser,
236
+ app=app,
237
+ enable_https=enable_https,
210
238
  )
211
239
  return proc
212
240
 
fastled/__main__.py ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ FastLED Package Main Entry Point
4
+ Enables running the package as: python -m fastled
5
+ """
6
+
7
+ import sys
8
+
9
+ from fastled.cli import main
10
+
11
+ if __name__ == "__main__":
12
+ # Pass execution to the main function from cli.py
13
+ # This enables 'python -m fastled' to work the same as 'fastled' command
14
+ sys.exit(main())
fastled/__version__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # IMPORTANT! There's a bug in github which will REJECT any version update
2
2
  # that has any other change in the repo. Please bump the version as the
3
3
  # ONLY change in a commit, or else the pypi update and the release will fail.
4
- __version__ = "1.3.30"
4
+ __version__ = "1.4.50"
5
5
 
6
6
  __version_url_latest__ = "https://raw.githubusercontent.com/zackees/fastled-wasm/refs/heads/main/src/fastled/__version__.py"
fastled/app.py CHANGED
@@ -9,8 +9,10 @@ from pathlib import Path
9
9
 
10
10
  from fastled.client_server import run_client_server
11
11
  from fastled.compile_server import CompileServer
12
+ from fastled.emoji_util import EMO
12
13
  from fastled.filewatcher import file_watcher_set
13
14
  from fastled.parse_args import Args, parse_args
15
+ from fastled.settings import DEFAULT_URL
14
16
  from fastled.sketch import find_sketch_directories, looks_like_fastled_repo
15
17
 
16
18
 
@@ -27,6 +29,7 @@ def run_server(args: Args) -> int:
27
29
  mapped_dir=mapped_dir,
28
30
  auto_start=True,
29
31
  remove_previous=args.clear,
32
+ no_platformio=args.no_platformio,
30
33
  )
31
34
 
32
35
  if not interactive:
@@ -50,6 +53,41 @@ def main() -> int:
50
53
  from fastled.select_sketch_directory import select_sketch_directory
51
54
 
52
55
  args = parse_args()
56
+
57
+ if args.emsdk_headers:
58
+ import httpx
59
+
60
+ out_path = args.emsdk_headers
61
+ base_url = args.web if isinstance(args.web, str) else DEFAULT_URL
62
+ try:
63
+ response = httpx.get(f"{base_url}/headers/emsdk")
64
+ if response.status_code == 200:
65
+ Path(out_path).parent.mkdir(parents=True, exist_ok=True)
66
+ with open(out_path, "wb") as f:
67
+ f.write(response.content)
68
+ print(f"{EMO('✅','SUCCESS:')} EMSDK headers exported to {out_path}")
69
+ return 0
70
+ else:
71
+ print(
72
+ f"{EMO('❌','ERROR:')} Failed to export EMSDK headers: HTTP {response.status_code}"
73
+ )
74
+ return 1
75
+ except KeyboardInterrupt:
76
+ print("\nExiting from main...")
77
+ return 1
78
+ except Exception as e:
79
+ print(f"{EMO('❌','ERROR:')} Exception: {e}")
80
+ return 1
81
+
82
+ # Handle --install command early
83
+ if args.install:
84
+ from fastled.install.main import fastled_install
85
+
86
+ result = fastled_install(
87
+ dry_run=args.dry_run, no_interactive=args.no_interactive
88
+ )
89
+ return 0 if result else 1
90
+
53
91
  interactive: bool = args.interactive
54
92
  has_server = args.server
55
93
  update: bool = args.update
@@ -62,6 +100,14 @@ def main() -> int:
62
100
  # now it is safe to print out the version
63
101
  print(f"FastLED version: {__version__}")
64
102
 
103
+ # Print current working directory
104
+ print(f"Current working directory: {os.getcwd()}")
105
+
106
+ # Check if Playwright browsers are installed
107
+ playwright_dir = Path.home() / ".fastled" / "playwright"
108
+ if playwright_dir.exists() and any(playwright_dir.iterdir()):
109
+ print(f"{EMO('🎭', '*')} Playwright browsers available at: {playwright_dir}")
110
+
65
111
  # Resolve some of the last interactive arguments
66
112
  # 1. If interactive is set and the sketch directory is not given,
67
113
  # then prompt the user for a sketch directory.
@@ -75,7 +121,7 @@ def main() -> int:
75
121
  sketch_list: list[Path] = find_sketch_directories()
76
122
  if sketch_list:
77
123
  maybe_dir: str | None = select_sketch_directory(
78
- sketch_list, cwd_looks_like_fastled_repo
124
+ sketch_list, cwd_looks_like_fastled_repo, is_followup=True
79
125
  )
80
126
  if maybe_dir is not None:
81
127
  directory = Path(maybe_dir)
@@ -87,7 +133,9 @@ def main() -> int:
87
133
 
88
134
  if update:
89
135
  # Force auto_update to ensure update check happens
90
- compile_server = CompileServer(interactive=False, auto_updates=True)
136
+ compile_server = CompileServer(
137
+ interactive=False, auto_updates=True, no_platformio=args.no_platformio
138
+ )
91
139
  compile_server.stop()
92
140
  print("Finished updating.")
93
141
  return 0
@@ -109,6 +157,7 @@ def main() -> int:
109
157
  mapped_dir=directory,
110
158
  auto_start=False,
111
159
  remove_previous=args.clear,
160
+ no_platformio=args.no_platformio,
112
161
  )
113
162
 
114
163
  server.start(wait_for_startup=False)
fastled/args.py CHANGED
@@ -12,8 +12,11 @@ class Args:
12
12
  interactive: bool
13
13
  profile: bool
14
14
  force_compile: bool
15
+ no_platformio: bool
16
+ app: bool # New flag to trigger Playwright browser with browser download if needed
15
17
  auto_update: bool | None
16
18
  update: bool
19
+ background_update: bool
17
20
  localhost: bool
18
21
  build: bool
19
22
  server: bool
@@ -23,6 +26,10 @@ class Args:
23
26
  release: bool
24
27
  ram_disk_size: str # suffixed liked "25mb" or "1gb"
25
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
32
+ emsdk_headers: str | None = None # Path to export EMSDK headers ZIP
26
33
 
27
34
  @staticmethod
28
35
  def from_namespace(args: argparse.Namespace) -> "Args":
@@ -47,10 +54,17 @@ class Args:
47
54
  assert isinstance(
48
55
  args.force_compile, bool
49
56
  ), f"expected bool, got {type(args.force_compile)}"
57
+ assert isinstance(
58
+ args.no_platformio, bool
59
+ ), f"expected bool, got {type(args.no_platformio)}"
60
+ assert isinstance(args.app, bool), f"expected bool, got {type(args.app)}"
50
61
  assert isinstance(
51
62
  args.no_auto_updates, bool | None
52
63
  ), f"expected bool | None, got {type(args.no_auto_updates)}"
53
64
  assert isinstance(args.update, bool), f"expected bool, got {type(args.update)}"
65
+ assert isinstance(
66
+ args.background_update, bool
67
+ ), f"expected bool, got {type(args.background_update)}"
54
68
  assert isinstance(
55
69
  args.localhost, bool
56
70
  ), f"expected bool, got {type(args.localhost)}"
@@ -62,6 +76,18 @@ class Args:
62
76
  assert isinstance(
63
77
  args.release, bool
64
78
  ), f"expected bool, got {type(args.release)}"
79
+ assert isinstance(
80
+ args.install, bool
81
+ ), f"expected bool, got {type(args.install)}"
82
+ assert isinstance(
83
+ args.dry_run, bool
84
+ ), f"expected bool, got {type(args.dry_run)}"
85
+ assert isinstance(
86
+ args.no_interactive, bool
87
+ ), f"expected bool, got {type(args.no_interactive)}"
88
+ assert isinstance(
89
+ args.emsdk_headers, str | None
90
+ ), f"expected str | None, got {type(args.emsdk_headers)}"
65
91
 
66
92
  init: bool | str = False
67
93
  if args.init is None:
@@ -78,8 +104,11 @@ class Args:
78
104
  interactive=args.interactive,
79
105
  profile=args.profile,
80
106
  force_compile=args.force_compile,
107
+ no_platformio=args.no_platformio,
108
+ app=args.app,
81
109
  auto_update=not args.no_auto_updates,
82
110
  update=args.update,
111
+ background_update=args.background_update,
83
112
  localhost=args.localhost,
84
113
  build=args.build,
85
114
  server=args.server,
@@ -88,4 +117,8 @@ class Args:
88
117
  quick=args.quick,
89
118
  release=args.release,
90
119
  ram_disk_size=args.ram_disk_size,
120
+ install=args.install,
121
+ dry_run=args.dry_run,
122
+ emsdk_headers=args.emsdk_headers,
123
+ no_interactive=args.no_interactive,
91
124
  )