fastled 1.2.33__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.
- fastled/__init__.py +51 -192
- fastled/__main__.py +14 -0
- fastled/__version__.py +6 -0
- fastled/app.py +124 -27
- fastled/args.py +124 -0
- fastled/assets/localhost-key.pem +28 -0
- fastled/assets/localhost.pem +27 -0
- fastled/cli.py +10 -2
- fastled/cli_test.py +21 -0
- fastled/cli_test_interactive.py +21 -0
- fastled/client_server.py +334 -55
- fastled/compile_server.py +12 -1
- fastled/compile_server_impl.py +115 -42
- fastled/docker_manager.py +392 -69
- fastled/emoji_util.py +27 -0
- fastled/filewatcher.py +100 -8
- fastled/find_good_connection.py +105 -0
- fastled/header_dump.py +63 -0
- fastled/install/__init__.py +1 -0
- fastled/install/examples_manager.py +62 -0
- fastled/install/extension_manager.py +113 -0
- fastled/install/main.py +156 -0
- fastled/install/project_detection.py +167 -0
- fastled/install/test_install.py +373 -0
- fastled/install/vscode_config.py +344 -0
- fastled/interruptible_http.py +148 -0
- fastled/keyboard.py +1 -0
- fastled/keyz.py +84 -0
- fastled/live_client.py +26 -1
- fastled/open_browser.py +133 -89
- fastled/parse_args.py +219 -15
- fastled/playwright/chrome_extension_downloader.py +207 -0
- fastled/playwright/playwright_browser.py +773 -0
- fastled/playwright/resize_tracking.py +127 -0
- fastled/print_filter.py +52 -0
- fastled/project_init.py +20 -13
- fastled/select_sketch_directory.py +142 -17
- fastled/server_flask.py +487 -0
- fastled/server_start.py +21 -0
- fastled/settings.py +53 -4
- fastled/site/build.py +2 -10
- fastled/site/examples.py +10 -0
- fastled/sketch.py +129 -7
- fastled/string_diff.py +218 -9
- fastled/test/examples.py +7 -5
- fastled/types.py +22 -2
- fastled/util.py +78 -0
- fastled/version.py +41 -0
- fastled/web_compile.py +401 -218
- fastled/zip_files.py +76 -0
- {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info}/METADATA +533 -382
- fastled-1.4.50.dist-info/RECORD +60 -0
- {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info}/WHEEL +1 -1
- fastled/open_browser2.py +0 -111
- fastled-1.2.33.dist-info/RECORD +0 -33
- {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info}/entry_points.txt +0 -0
- {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info/licenses}/LICENSE +0 -0
- {fastled-1.2.33.dist-info → fastled-1.4.50.dist-info}/top_level.txt +0 -0
fastled/open_browser.py
CHANGED
|
@@ -1,53 +1,55 @@
|
|
|
1
|
-
import
|
|
1
|
+
import atexit
|
|
2
|
+
import random
|
|
2
3
|
import sys
|
|
3
4
|
import time
|
|
4
|
-
import
|
|
5
|
+
import weakref
|
|
5
6
|
from multiprocessing import Process
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
from fastled.playwright.playwright_browser import open_with_playwright
|
|
10
|
+
from fastled.server_flask import run_flask_in_thread
|
|
11
|
+
|
|
12
|
+
# Global reference to keep Playwright browser alive
|
|
13
|
+
_playwright_browser_proxy = None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def cleanup_playwright_browser() -> None:
|
|
17
|
+
"""Clean up the Playwright browser on exit."""
|
|
18
|
+
try:
|
|
19
|
+
global _playwright_browser_proxy
|
|
20
|
+
if _playwright_browser_proxy:
|
|
21
|
+
_playwright_browser_proxy.close()
|
|
22
|
+
_playwright_browser_proxy = None
|
|
23
|
+
except Exception:
|
|
24
|
+
pass
|
|
9
25
|
|
|
26
|
+
|
|
27
|
+
# Register cleanup function
|
|
28
|
+
atexit.register(cleanup_playwright_browser)
|
|
29
|
+
|
|
30
|
+
DEFAULT_PORT = 8089 # different than live version.
|
|
10
31
|
PYTHON_EXE = sys.executable
|
|
11
32
|
|
|
33
|
+
# Use a weak reference set to track processes without preventing garbage collection
|
|
34
|
+
_WEAK_CLEANUP_SET = weakref.WeakSet()
|
|
12
35
|
|
|
13
|
-
def open_http_server_subprocess(
|
|
14
|
-
fastled_js: Path, port: int, open_browser: bool
|
|
15
|
-
) -> None:
|
|
16
|
-
"""Start livereload server in the fastled_js directory and return the process"""
|
|
17
|
-
import shutil
|
|
18
36
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"fastled.open_browser2",
|
|
36
|
-
str(fastled_js),
|
|
37
|
-
"--port",
|
|
38
|
-
str(port),
|
|
39
|
-
]
|
|
40
|
-
# return subprocess.Popen(cmd) # type ignore
|
|
41
|
-
# pipe stderr and stdout to null
|
|
42
|
-
subprocess.run(
|
|
43
|
-
cmd,
|
|
44
|
-
stdout=subprocess.DEVNULL,
|
|
45
|
-
stderr=subprocess.DEVNULL,
|
|
46
|
-
) # type ignore
|
|
47
|
-
except KeyboardInterrupt:
|
|
48
|
-
import _thread
|
|
49
|
-
|
|
50
|
-
_thread.interrupt_main()
|
|
37
|
+
def add_cleanup(proc: Process) -> None:
|
|
38
|
+
"""Add a process to the cleanup list using weak references"""
|
|
39
|
+
_WEAK_CLEANUP_SET.add(proc)
|
|
40
|
+
|
|
41
|
+
# Register a cleanup function that checks if the process is still alive
|
|
42
|
+
def cleanup_if_alive():
|
|
43
|
+
if proc.is_alive():
|
|
44
|
+
try:
|
|
45
|
+
proc.terminate()
|
|
46
|
+
proc.join(timeout=1.0)
|
|
47
|
+
if proc.is_alive():
|
|
48
|
+
proc.kill()
|
|
49
|
+
except Exception:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
atexit.register(cleanup_if_alive)
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
def is_port_free(port: int) -> bool:
|
|
@@ -55,16 +57,22 @@ def is_port_free(port: int) -> bool:
|
|
|
55
57
|
import httpx
|
|
56
58
|
|
|
57
59
|
try:
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
60
|
+
# Try HTTPS first, then fall back to HTTP
|
|
61
|
+
try:
|
|
62
|
+
response = httpx.get(f"https://localhost:{port}", timeout=1, verify=False)
|
|
63
|
+
response.raise_for_status()
|
|
64
|
+
return False
|
|
65
|
+
except (httpx.HTTPError, httpx.ConnectError):
|
|
66
|
+
response = httpx.get(f"http://localhost:{port}", timeout=1)
|
|
67
|
+
response.raise_for_status()
|
|
68
|
+
return False
|
|
61
69
|
except (httpx.HTTPError, httpx.ConnectError):
|
|
62
70
|
return True
|
|
63
71
|
|
|
64
72
|
|
|
65
73
|
def find_free_port(start_port: int) -> int:
|
|
66
74
|
"""Find a free port starting at start_port"""
|
|
67
|
-
for port in range(start_port, start_port + 100):
|
|
75
|
+
for port in range(start_port, start_port + 100, 2):
|
|
68
76
|
if is_port_free(port):
|
|
69
77
|
print(f"Found free port: {port}")
|
|
70
78
|
return port
|
|
@@ -73,14 +81,21 @@ def find_free_port(start_port: int) -> int:
|
|
|
73
81
|
raise ValueError("Could not find a free port")
|
|
74
82
|
|
|
75
83
|
|
|
76
|
-
def wait_for_server(port: int, timeout: int = 10) -> None:
|
|
84
|
+
def wait_for_server(port: int, timeout: int = 10, enable_https: bool = True) -> None:
|
|
77
85
|
"""Wait for the server to start."""
|
|
78
86
|
from httpx import get
|
|
79
87
|
|
|
80
88
|
future_time = time.time() + timeout
|
|
89
|
+
protocol = "https" if enable_https else "http"
|
|
81
90
|
while future_time > time.time():
|
|
82
91
|
try:
|
|
83
|
-
|
|
92
|
+
# Try the specified protocol (HTTPS with SSL verification disabled for self-signed certs)
|
|
93
|
+
url = f"{protocol}://localhost:{port}"
|
|
94
|
+
# print(f"Waiting for server to start at {url}")
|
|
95
|
+
verify = (
|
|
96
|
+
False if enable_https else True
|
|
97
|
+
) # Only disable SSL verification for HTTPS
|
|
98
|
+
response = get(url, timeout=1, verify=verify)
|
|
84
99
|
if response.status_code == 200:
|
|
85
100
|
return
|
|
86
101
|
except Exception:
|
|
@@ -88,54 +103,85 @@ def wait_for_server(port: int, timeout: int = 10) -> None:
|
|
|
88
103
|
raise TimeoutError("Could not connect to server")
|
|
89
104
|
|
|
90
105
|
|
|
91
|
-
def
|
|
92
|
-
|
|
93
|
-
|
|
106
|
+
def spawn_http_server(
|
|
107
|
+
fastled_js: Path,
|
|
108
|
+
compile_server_port: int,
|
|
109
|
+
port: int | None = None,
|
|
110
|
+
open_browser: bool = True,
|
|
111
|
+
app: bool = False,
|
|
112
|
+
enable_https: bool = True,
|
|
113
|
+
) -> Process:
|
|
94
114
|
|
|
95
|
-
if
|
|
96
|
-
|
|
115
|
+
if port is not None and not is_port_free(port):
|
|
116
|
+
raise ValueError(f"Port {port} was specified but in use")
|
|
117
|
+
if port is None:
|
|
118
|
+
offset = random.randint(0, 100)
|
|
119
|
+
port = find_free_port(DEFAULT_PORT + offset)
|
|
97
120
|
|
|
98
|
-
if
|
|
99
|
-
|
|
121
|
+
# Get SSL certificate paths from the fastled assets directory if HTTPS is enabled
|
|
122
|
+
certfile: Path | None = None
|
|
123
|
+
keyfile: Path | None = None
|
|
100
124
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
["npm", "install", "-g", "live-server"],
|
|
104
|
-
stdout=subprocess.DEVNULL,
|
|
105
|
-
stderr=subprocess.DEVNULL,
|
|
106
|
-
)
|
|
125
|
+
if enable_https:
|
|
126
|
+
import fastled
|
|
107
127
|
|
|
128
|
+
assets_dir = Path(fastled.__file__).parent / "assets"
|
|
129
|
+
certfile = assets_dir / "localhost.pem"
|
|
130
|
+
keyfile = assets_dir / "localhost-key.pem"
|
|
108
131
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
else:
|
|
119
|
-
port = find_free_port(DEFAULT_PORT)
|
|
120
|
-
out: Process = Process(
|
|
121
|
-
target=open_http_server_subprocess,
|
|
122
|
-
args=(fastled_js, port, False),
|
|
132
|
+
# port: int,
|
|
133
|
+
# cwd: Path,
|
|
134
|
+
# compile_server_port: int,
|
|
135
|
+
# certfile: Path | None = None,
|
|
136
|
+
# keyfile: Path | None = None,
|
|
137
|
+
|
|
138
|
+
proc = Process(
|
|
139
|
+
target=run_flask_in_thread,
|
|
140
|
+
args=(port, fastled_js, compile_server_port, certfile, keyfile),
|
|
123
141
|
daemon=True,
|
|
124
142
|
)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
143
|
+
add_cleanup(proc)
|
|
144
|
+
proc.start()
|
|
145
|
+
|
|
146
|
+
# Add to cleanup set with weak reference
|
|
147
|
+
add_cleanup(proc)
|
|
130
148
|
|
|
131
|
-
|
|
132
|
-
if
|
|
133
|
-
|
|
149
|
+
wait_for_server(port, enable_https=enable_https)
|
|
150
|
+
if open_browser:
|
|
151
|
+
protocol = "https" if enable_https else "http"
|
|
152
|
+
url = f"{protocol}://localhost:{port}"
|
|
153
|
+
should_use_playwright = app
|
|
154
|
+
|
|
155
|
+
if should_use_playwright:
|
|
156
|
+
if app:
|
|
157
|
+
# For --app mode, try to install browsers if needed
|
|
158
|
+
from fastled.playwright.playwright_browser import (
|
|
159
|
+
install_playwright_browsers,
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
install_playwright_browsers()
|
|
163
|
+
|
|
164
|
+
print(f"Opening FastLED sketch in Playwright browser: {url}")
|
|
165
|
+
print(
|
|
166
|
+
"Auto-resize enabled: Browser window will automatically adjust to content size"
|
|
167
|
+
)
|
|
168
|
+
print(
|
|
169
|
+
"🔧 C++ DevTools Support extension will be loaded for DWARF debugging"
|
|
170
|
+
)
|
|
171
|
+
global _playwright_browser_proxy
|
|
172
|
+
_playwright_browser_proxy = open_with_playwright(
|
|
173
|
+
url, enable_extensions=True
|
|
174
|
+
)
|
|
175
|
+
else:
|
|
176
|
+
print(f"Opening browser to {url}")
|
|
177
|
+
import webbrowser
|
|
134
178
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
179
|
+
webbrowser.open(
|
|
180
|
+
url=url,
|
|
181
|
+
new=1,
|
|
182
|
+
autoraise=True,
|
|
183
|
+
)
|
|
184
|
+
return proc
|
|
139
185
|
|
|
140
186
|
|
|
141
187
|
if __name__ == "__main__":
|
|
@@ -145,9 +191,7 @@ if __name__ == "__main__":
|
|
|
145
191
|
description="Open a browser to the fastled_js directory"
|
|
146
192
|
)
|
|
147
193
|
parser.add_argument(
|
|
148
|
-
"fastled_js",
|
|
149
|
-
type=Path,
|
|
150
|
-
help="Path to the fastled_js directory",
|
|
194
|
+
"fastled_js", type=Path, help="Path to the fastled_js directory"
|
|
151
195
|
)
|
|
152
196
|
parser.add_argument(
|
|
153
197
|
"--port",
|
|
@@ -157,5 +201,5 @@ if __name__ == "__main__":
|
|
|
157
201
|
)
|
|
158
202
|
args = parser.parse_args()
|
|
159
203
|
|
|
160
|
-
proc =
|
|
204
|
+
proc = spawn_http_server(args.fastled_js, args.port, open_browser=True)
|
|
161
205
|
proc.join()
|
fastled/parse_args.py
CHANGED
|
@@ -3,19 +3,66 @@ import os
|
|
|
3
3
|
import sys
|
|
4
4
|
from pathlib import Path
|
|
5
5
|
|
|
6
|
-
from fastled import
|
|
6
|
+
from fastled.args import Args
|
|
7
7
|
from fastled.project_init import project_init
|
|
8
8
|
from fastled.select_sketch_directory import select_sketch_directory
|
|
9
9
|
from fastled.settings import DEFAULT_URL, IMAGE_NAME
|
|
10
10
|
from fastled.sketch import (
|
|
11
|
+
find_sketch_by_partial_name,
|
|
11
12
|
find_sketch_directories,
|
|
12
13
|
looks_like_fastled_repo,
|
|
13
14
|
looks_like_sketch_directory,
|
|
14
15
|
)
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
def
|
|
18
|
+
def _find_fastled_repo(start: Path) -> Path | None:
|
|
19
|
+
"""Find the FastLED repo directory by searching upwards from the current directory."""
|
|
20
|
+
current = start
|
|
21
|
+
while current != current.parent:
|
|
22
|
+
if looks_like_fastled_repo(current):
|
|
23
|
+
return current
|
|
24
|
+
current = current.parent
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
_DEFAULT_HELP_TEXT = """
|
|
29
|
+
FastLED WASM Compiler - Useful options:
|
|
30
|
+
<directory> Directory containing the FastLED sketch to compile
|
|
31
|
+
--init [example] Initialize one of the top tier WASM examples
|
|
32
|
+
--web [url] Use web compiler
|
|
33
|
+
--server Run the compiler server
|
|
34
|
+
--no-platformio Bypass PlatformIO constraints using local Docker compilation
|
|
35
|
+
--quick Build in quick mode (default)
|
|
36
|
+
--profile Enable profiling the C++ build system
|
|
37
|
+
--update Update the docker image for the wasm compiler
|
|
38
|
+
--background-update Update the docker image in the background after compilation
|
|
39
|
+
--purge Remove all FastLED containers and images
|
|
40
|
+
--emsdk-headers <path> Export EMSDK headers ZIP to specified path
|
|
41
|
+
--version Show version information
|
|
42
|
+
--help Show detailed help
|
|
43
|
+
Examples:
|
|
44
|
+
fastled (will auto detect the sketch directory and prompt you)
|
|
45
|
+
fastled my_sketch
|
|
46
|
+
fastled my_sketch --web (compiles using the web compiler only)
|
|
47
|
+
fastled my_sketch --background-update (compiles and updates docker image in background)
|
|
48
|
+
fastled --init Blink (initializes a new sketch directory with the Blink example)
|
|
49
|
+
fastled --server (runs the compiler server in the current directory)
|
|
50
|
+
|
|
51
|
+
For those using Docker:
|
|
52
|
+
--debug Build with debug symbols for dev-tools debugging
|
|
53
|
+
|
|
54
|
+
--release Build in optimized release mode
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def parse_args() -> Args:
|
|
18
59
|
"""Parse command-line arguments."""
|
|
60
|
+
from fastled import __version__
|
|
61
|
+
|
|
62
|
+
# Check if no arguments were provided
|
|
63
|
+
if len(sys.argv) == 1:
|
|
64
|
+
print(_DEFAULT_HELP_TEXT)
|
|
65
|
+
|
|
19
66
|
parser = argparse.ArgumentParser(description=f"FastLED WASM Compiler {__version__}")
|
|
20
67
|
parser.add_argument("--version", action="version", version=f"{__version__}")
|
|
21
68
|
parser.add_argument(
|
|
@@ -36,6 +83,12 @@ def parse_args() -> argparse.Namespace:
|
|
|
36
83
|
action="store_true",
|
|
37
84
|
help="Just compile, skip opening the browser and watching for changes.",
|
|
38
85
|
)
|
|
86
|
+
parser.add_argument(
|
|
87
|
+
"--ram-disk-size",
|
|
88
|
+
type=str,
|
|
89
|
+
default="1gb",
|
|
90
|
+
help="Size of the RAM disk for compilation (e.g., '1gb', '512mb')",
|
|
91
|
+
)
|
|
39
92
|
parser.add_argument(
|
|
40
93
|
"--web",
|
|
41
94
|
"-w",
|
|
@@ -54,7 +107,7 @@ def parse_args() -> argparse.Namespace:
|
|
|
54
107
|
parser.add_argument(
|
|
55
108
|
"--profile",
|
|
56
109
|
action="store_true",
|
|
57
|
-
help="Enable profiling for
|
|
110
|
+
help="Enable profiling of the C++ build system used for wasm compilation.",
|
|
58
111
|
)
|
|
59
112
|
parser.add_argument(
|
|
60
113
|
"--force-compile",
|
|
@@ -64,20 +117,36 @@ def parse_args() -> argparse.Namespace:
|
|
|
64
117
|
parser.add_argument(
|
|
65
118
|
"--no-auto-updates",
|
|
66
119
|
action="store_true",
|
|
67
|
-
help="Disable automatic updates of the wasm compiler image when using docker.",
|
|
120
|
+
help="Disable automatic updates of the wasm compiler image when using docker. (Default: False)",
|
|
121
|
+
)
|
|
122
|
+
parser.add_argument(
|
|
123
|
+
"--no-platformio",
|
|
124
|
+
action="store_true",
|
|
125
|
+
help="Bypass PlatformIO constraints by using local Docker compilation with custom build environment",
|
|
126
|
+
)
|
|
127
|
+
parser.add_argument(
|
|
128
|
+
"--app",
|
|
129
|
+
action="store_true",
|
|
130
|
+
help="Use Playwright app-like browser experience (will download browsers if needed)",
|
|
68
131
|
)
|
|
69
132
|
parser.add_argument(
|
|
133
|
+
"-u",
|
|
70
134
|
"--update",
|
|
71
135
|
"--upgrade",
|
|
72
136
|
action="store_true",
|
|
73
137
|
help="Update the wasm compiler (if necessary) before running",
|
|
74
138
|
)
|
|
139
|
+
parser.add_argument(
|
|
140
|
+
"--background-update",
|
|
141
|
+
action="store_true",
|
|
142
|
+
help="Update the docker image in the background after compilation (user doesn't have to wait)",
|
|
143
|
+
)
|
|
75
144
|
parser.add_argument(
|
|
76
145
|
"--localhost",
|
|
77
146
|
"--local",
|
|
78
147
|
"-l",
|
|
79
148
|
action="store_true",
|
|
80
|
-
help="Use localhost for web compilation from an instance of fastled --server, creating it if necessary",
|
|
149
|
+
help="(Default): Use localhost for web compilation from an instance of fastled --server, creating it if necessary",
|
|
81
150
|
)
|
|
82
151
|
parser.add_argument(
|
|
83
152
|
"--build",
|
|
@@ -97,12 +166,41 @@ def parse_args() -> argparse.Namespace:
|
|
|
97
166
|
help="Remove all FastLED containers and images",
|
|
98
167
|
)
|
|
99
168
|
|
|
169
|
+
parser.add_argument(
|
|
170
|
+
"--clear",
|
|
171
|
+
action="store_true",
|
|
172
|
+
help="Remove all FastLED containers and images",
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
parser.add_argument(
|
|
176
|
+
"--install",
|
|
177
|
+
action="store_true",
|
|
178
|
+
help="Install FastLED development environment with VSCode configuration and Auto Debug extension",
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
parser.add_argument(
|
|
182
|
+
"--dry-run",
|
|
183
|
+
action="store_true",
|
|
184
|
+
help="Run in dry-run mode (simulate actions without making changes)",
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
parser.add_argument(
|
|
188
|
+
"--no-interactive",
|
|
189
|
+
action="store_true",
|
|
190
|
+
help="Run in non-interactive mode (fail instead of prompting for input)",
|
|
191
|
+
)
|
|
192
|
+
parser.add_argument(
|
|
193
|
+
"--emsdk-headers",
|
|
194
|
+
type=str,
|
|
195
|
+
default=None,
|
|
196
|
+
help="Export EMSDK headers ZIP to specified path",
|
|
197
|
+
)
|
|
198
|
+
|
|
100
199
|
build_mode = parser.add_mutually_exclusive_group()
|
|
101
200
|
build_mode.add_argument("--debug", action="store_true", help="Build in debug mode")
|
|
102
201
|
build_mode.add_argument(
|
|
103
202
|
"--quick",
|
|
104
203
|
action="store_true",
|
|
105
|
-
default=True,
|
|
106
204
|
help="Build in quick mode (default)",
|
|
107
205
|
)
|
|
108
206
|
build_mode.add_argument(
|
|
@@ -113,6 +211,62 @@ def parse_args() -> argparse.Namespace:
|
|
|
113
211
|
|
|
114
212
|
args = parser.parse_args()
|
|
115
213
|
|
|
214
|
+
# Handle --emsdk-headers early before other processing
|
|
215
|
+
if args.emsdk_headers:
|
|
216
|
+
from fastled.header_dump import dump_emsdk_headers
|
|
217
|
+
|
|
218
|
+
out_path = args.emsdk_headers
|
|
219
|
+
dump_emsdk_headers(out_path)
|
|
220
|
+
sys.exit(0)
|
|
221
|
+
|
|
222
|
+
# Auto-enable app mode if debug is used and Playwright cache exists
|
|
223
|
+
if args.debug and not args.app:
|
|
224
|
+
playwright_dir = Path.home() / ".fastled" / "playwright"
|
|
225
|
+
if playwright_dir.exists() and any(playwright_dir.iterdir()):
|
|
226
|
+
from fastled.emoji_util import EMO
|
|
227
|
+
|
|
228
|
+
print(
|
|
229
|
+
f"{EMO('⚠️', 'WARNING:')} Debug mode detected with Playwright installed - automatically enabling app mode"
|
|
230
|
+
)
|
|
231
|
+
args.app = True
|
|
232
|
+
elif not args.no_interactive:
|
|
233
|
+
# Prompt user to install Playwright only if not in no-interactive mode
|
|
234
|
+
answer = (
|
|
235
|
+
input("Would you like to install the FastLED debugger? [y/n] ")
|
|
236
|
+
.strip()
|
|
237
|
+
.lower()
|
|
238
|
+
)
|
|
239
|
+
if answer in ["y", "yes"]:
|
|
240
|
+
print(
|
|
241
|
+
"📦 To install Playwright, run: pip install playwright && python -m playwright install"
|
|
242
|
+
)
|
|
243
|
+
print("Then run your command again with --app flag")
|
|
244
|
+
sys.exit(0)
|
|
245
|
+
|
|
246
|
+
# TODO: propagate the library.
|
|
247
|
+
# from fastled.docker_manager import force_remove_previous
|
|
248
|
+
|
|
249
|
+
# if force_remove_previous():
|
|
250
|
+
# print("Removing previous containers...")
|
|
251
|
+
# do itinfront he camer
|
|
252
|
+
# nonw invoke via the
|
|
253
|
+
#
|
|
254
|
+
# Work in progress.
|
|
255
|
+
# set_ramdisk_size("50mb")
|
|
256
|
+
|
|
257
|
+
# if args.ram_disk_size != "0":
|
|
258
|
+
# from fastled.docker_manager import set_ramdisk_size
|
|
259
|
+
# from fastled.util import banner_string
|
|
260
|
+
|
|
261
|
+
# msg = banner_string(f"Setting tmpfs size to {args.ram_disk_size}")
|
|
262
|
+
# print(msg)
|
|
263
|
+
# set_ramdisk_size(args.ram_disk_size)
|
|
264
|
+
|
|
265
|
+
# Handle --install early before other processing
|
|
266
|
+
if args.install:
|
|
267
|
+
# Don't process other arguments when --install is used
|
|
268
|
+
return Args.from_namespace(args)
|
|
269
|
+
|
|
116
270
|
if args.purge:
|
|
117
271
|
from fastled.docker_manager import DockerManager
|
|
118
272
|
|
|
@@ -131,8 +285,38 @@ def parse_args() -> argparse.Namespace:
|
|
|
131
285
|
print(f"Use 'fastled {args.directory}' to compile the project.")
|
|
132
286
|
sys.exit(0)
|
|
133
287
|
|
|
134
|
-
|
|
135
|
-
|
|
288
|
+
cwd: Path = Path(os.getcwd())
|
|
289
|
+
fastled_dir: Path | None = _find_fastled_repo(cwd)
|
|
290
|
+
is_fastled_dir: bool = fastled_dir is not None
|
|
291
|
+
|
|
292
|
+
if not (args.debug or args.quick or args.release):
|
|
293
|
+
if is_fastled_dir:
|
|
294
|
+
# if --quick, --debug, --release are not specified then default to --debug
|
|
295
|
+
args.quick = True
|
|
296
|
+
print("Defaulting to --quick mode in fastled repo")
|
|
297
|
+
else:
|
|
298
|
+
args.quick = True
|
|
299
|
+
print("Defaulting to --quick mode")
|
|
300
|
+
|
|
301
|
+
if args.build or args.interactive:
|
|
302
|
+
if args.directory is not None:
|
|
303
|
+
args.directory = str(Path(args.directory).absolute())
|
|
304
|
+
if not is_fastled_dir:
|
|
305
|
+
print("This command must be run from within the FastLED repo. Exiting...")
|
|
306
|
+
sys.exit(1)
|
|
307
|
+
if cwd != fastled_dir and fastled_dir is not None:
|
|
308
|
+
print(f"Switching to FastLED repo at {fastled_dir}")
|
|
309
|
+
os.chdir(fastled_dir)
|
|
310
|
+
if args.directory is None:
|
|
311
|
+
args.directory = str(Path("examples/wasm").absolute())
|
|
312
|
+
if args.interactive:
|
|
313
|
+
if not args.build:
|
|
314
|
+
print("Adding --build flag when using --interactive")
|
|
315
|
+
args.build = True
|
|
316
|
+
user_wants_update = args.update
|
|
317
|
+
if user_wants_update is not True:
|
|
318
|
+
args.no_auto_updates = True
|
|
319
|
+
return Args.from_namespace(args)
|
|
136
320
|
|
|
137
321
|
if not args.update:
|
|
138
322
|
if args.no_auto_updates:
|
|
@@ -165,6 +349,12 @@ def parse_args() -> argparse.Namespace:
|
|
|
165
349
|
if cwd_is_fastled and not args.web and not args.server:
|
|
166
350
|
print("Forcing --local mode because we are in the FastLED repo")
|
|
167
351
|
args.localhost = True
|
|
352
|
+
if args.no_platformio:
|
|
353
|
+
print(
|
|
354
|
+
"--no-platformio mode enabled: forcing local Docker compilation to bypass PlatformIO constraints"
|
|
355
|
+
)
|
|
356
|
+
args.localhost = True
|
|
357
|
+
args.web = None # Clear web flag to ensure local compilation
|
|
168
358
|
if args.localhost:
|
|
169
359
|
args.web = "localhost"
|
|
170
360
|
if args.interactive and not args.server:
|
|
@@ -179,7 +369,7 @@ def parse_args() -> argparse.Namespace:
|
|
|
179
369
|
print("Searching for sketch directories...")
|
|
180
370
|
sketch_directories = find_sketch_directories(maybe_sketch_dir)
|
|
181
371
|
selected_dir = select_sketch_directory(
|
|
182
|
-
sketch_directories, cwd_is_fastled
|
|
372
|
+
sketch_directories, cwd_is_fastled, is_followup=True
|
|
183
373
|
)
|
|
184
374
|
if selected_dir:
|
|
185
375
|
print(f"Using sketch directory: {selected_dir}")
|
|
@@ -189,10 +379,24 @@ def parse_args() -> argparse.Namespace:
|
|
|
189
379
|
"\nYou either need to specify a sketch directory or run in --server mode."
|
|
190
380
|
)
|
|
191
381
|
sys.exit(1)
|
|
192
|
-
elif args.directory is not None
|
|
193
|
-
|
|
194
|
-
if
|
|
195
|
-
|
|
196
|
-
|
|
382
|
+
elif args.directory is not None:
|
|
383
|
+
# Check if directory is a file path
|
|
384
|
+
if os.path.isfile(args.directory):
|
|
385
|
+
dir_path = Path(args.directory).parent
|
|
386
|
+
if looks_like_sketch_directory(dir_path):
|
|
387
|
+
print(f"Using sketch directory: {dir_path}")
|
|
388
|
+
args.directory = str(dir_path)
|
|
389
|
+
# Check if directory exists as a path
|
|
390
|
+
elif not os.path.exists(args.directory):
|
|
391
|
+
# Directory doesn't exist - try partial name matching
|
|
392
|
+
try:
|
|
393
|
+
matched_dir = find_sketch_by_partial_name(args.directory)
|
|
394
|
+
print(
|
|
395
|
+
f"Matched '{args.directory}' to sketch directory: {matched_dir}"
|
|
396
|
+
)
|
|
397
|
+
args.directory = str(matched_dir)
|
|
398
|
+
except ValueError as e:
|
|
399
|
+
print(f"Error: {e}")
|
|
400
|
+
sys.exit(1)
|
|
197
401
|
|
|
198
|
-
return args
|
|
402
|
+
return Args.from_namespace(args)
|