stackthread 0.1.0.dev2__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.

Potentially problematic release.


This version of stackthread might be problematic. Click here for more details.

Files changed (40) hide show
  1. stackthread/__init__.py +6 -0
  2. stackthread/__main__.py +3 -0
  3. stackthread/_vendor/THIRD_PARTY_NOTICES.txt +23 -0
  4. stackthread/_vendor/extract.mjs +15 -0
  5. stackthread/adapters.py +130 -0
  6. stackthread/cli.py +342 -0
  7. stackthread/config.py +409 -0
  8. stackthread/detect.py +150 -0
  9. stackthread/lang/__init__.py +1 -0
  10. stackthread/lang/js/__init__.py +1 -0
  11. stackthread/lang/js/aliases.py +133 -0
  12. stackthread/lang/js/build.py +455 -0
  13. stackthread/lang/js/worker.py +186 -0
  14. stackthread/lang/python/__init__.py +1 -0
  15. stackthread/lang/python/build.py +249 -0
  16. stackthread/lang/python/extract.py +494 -0
  17. stackthread/lang/python/frameworks.py +700 -0
  18. stackthread/lang/python/orm.py +300 -0
  19. stackthread/lang/python/resolve.py +448 -0
  20. stackthread/lineage.py +965 -0
  21. stackthread/link.py +130 -0
  22. stackthread/logs.py +314 -0
  23. stackthread/model.py +161 -0
  24. stackthread/paths.py +173 -0
  25. stackthread/project.py +414 -0
  26. stackthread/report.py +203 -0
  27. stackthread/rules.py +48 -0
  28. stackthread/server/__init__.py +5 -0
  29. stackthread/server/app.py +696 -0
  30. stackthread/server/edit.py +108 -0
  31. stackthread/server/security.py +73 -0
  32. stackthread/static/THIRD_PARTY_NOTICES.txt +379 -0
  33. stackthread/static/app.css +1 -0
  34. stackthread/static/app.js +29 -0
  35. stackthread/static/index.html +16 -0
  36. stackthread-0.1.0.dev2.dist-info/METADATA +51 -0
  37. stackthread-0.1.0.dev2.dist-info/RECORD +40 -0
  38. stackthread-0.1.0.dev2.dist-info/WHEEL +4 -0
  39. stackthread-0.1.0.dev2.dist-info/entry_points.txt +2 -0
  40. stackthread-0.1.0.dev2.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,108 @@
1
+ """Reading and writing source files on behalf of the browser, with every guard from design §14."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import os
7
+ import subprocess
8
+ import tempfile
9
+ from dataclasses import dataclass
10
+ from pathlib import Path, PurePosixPath
11
+
12
+ from stackthread.config import Config
13
+ from stackthread.paths import matches
14
+
15
+ MAX_BYTES = 2 * 1024 * 1024
16
+
17
+
18
+ class EditError(Exception):
19
+ def __init__(self, status: int, message: str) -> None:
20
+ super().__init__(message)
21
+ self.status = status
22
+ self.message = message
23
+
24
+
25
+ @dataclass
26
+ class Written:
27
+ hash: str
28
+ formatted: bool
29
+ output: str
30
+
31
+
32
+ def digest(data: bytes) -> str:
33
+ return hashlib.sha256(data).hexdigest()[:20]
34
+
35
+
36
+ def _safe_path(config: Config, file: str) -> Path:
37
+ """The absolute path of a project file, only if it is inside a declared source after resolving symlinks."""
38
+ if not file or "\x00" in file:
39
+ raise EditError(400, "missing file")
40
+ rel = PurePosixPath(file.replace("\\", "/"))
41
+ if rel.is_absolute() or ".." in rel.parts or (rel.parts and ":" in rel.parts[0]):
42
+ raise EditError(400, "file must be a path relative to the project root")
43
+ root = config.root.resolve()
44
+ path = (root / rel).resolve()
45
+ if not path.is_relative_to(root):
46
+ raise EditError(403, "file is outside the project")
47
+ if not any(path.is_relative_to(config.source_root(s)) for s in config.sources):
48
+ raise EditError(403, "file is outside every configured source")
49
+ return path
50
+
51
+
52
+ def read(config: Config, file: str) -> tuple[str, str]:
53
+ path = _safe_path(config, file)
54
+ if not path.is_file():
55
+ raise EditError(404, "no such file")
56
+ data = path.read_bytes()
57
+ if len(data) > MAX_BYTES:
58
+ raise EditError(413, "file is larger than 2 MB")
59
+ try:
60
+ text = data.decode("utf-8")
61
+ except UnicodeDecodeError:
62
+ raise EditError(415, "file is not UTF-8 text") from None
63
+ return text.replace("\r\n", "\n"), digest(data)
64
+
65
+
66
+ def write(config: Config, file: str, expected_hash: str, text: str) -> Written:
67
+ if not config.server.edit:
68
+ raise EditError(403, "editing is turned off (server.edit = false or --read-only)")
69
+ path = _safe_path(config, file)
70
+ rel = path.relative_to(config.root.resolve()).as_posix()
71
+ if matches(rel, config.server.edit_deny) or matches(path.name, config.server.edit_deny):
72
+ raise EditError(403, f"{rel} matches server.edit_deny and cannot be edited here")
73
+ if not path.is_file():
74
+ raise EditError(404, "no such file; stackthread only edits existing files")
75
+ current = path.read_bytes()
76
+ if digest(current) != expected_hash:
77
+ raise EditError(409, "the file changed on disk since it was opened; reload it, then edit again")
78
+ if "\r\n" in current.decode("utf-8", errors="replace"):
79
+ text = text.replace("\r\n", "\n").replace("\n", "\r\n")
80
+ data = text.encode("utf-8")
81
+ if len(data) > MAX_BYTES:
82
+ raise EditError(413, "the new text is larger than 2 MB")
83
+ mode = path.stat().st_mode
84
+ fd, tmp = tempfile.mkstemp(prefix=".stackthread-", dir=path.parent)
85
+ try:
86
+ with os.fdopen(fd, "wb") as f:
87
+ f.write(data)
88
+ Path(tmp).chmod(mode & 0o7777)
89
+ Path(tmp).replace(path)
90
+ except OSError as e:
91
+ Path(tmp).unlink(missing_ok=True)
92
+ raise EditError(500, f"could not write the file: {e}") from None
93
+ formatted, output = _format(config, path)
94
+ return Written(digest(path.read_bytes()), formatted, output)
95
+
96
+
97
+ def _format(config: Config, path: Path) -> tuple[bool, str]:
98
+ lang = "python" if path.suffix == ".py" else "typescript"
99
+ command = config.server.format.get(lang)
100
+ if not command:
101
+ return False, ""
102
+ args = [part.replace("{file}", str(path)) for part in command]
103
+ try:
104
+ done = subprocess.run(args, cwd=config.root, capture_output=True, text=True, timeout=30, check=False)
105
+ except (OSError, subprocess.TimeoutExpired) as e:
106
+ return False, f"formatter did not run: {e}"
107
+ output = (done.stdout + done.stderr).strip()
108
+ return done.returncode == 0, output[-2000:]
@@ -0,0 +1,73 @@
1
+ """Who may talk to the server: loopback Host and Origin, and the session token (design §14)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hmac
6
+ import json
7
+ import os
8
+ import secrets
9
+ import time
10
+ from pathlib import Path
11
+ from urllib.parse import urlsplit
12
+
13
+ from stackthread import __version__
14
+
15
+ LOOPBACK = ("127.0.0.1", "localhost", "[::1]", "::1")
16
+ TOKEN_HEADER = "X-Stackthread-Token" # noqa: S105 - a header name, not a secret
17
+
18
+
19
+ def new_token() -> str:
20
+ return secrets.token_urlsafe(32)
21
+
22
+
23
+ def host_allowed(host_header: str | None, port: int) -> bool:
24
+ """Only `127.0.0.1:<port>`, `localhost:<port>` or `[::1]:<port>`. Stops DNS rebinding."""
25
+ if not host_header:
26
+ return False
27
+ host = host_header.strip().lower()
28
+ return host in {f"{name}:{port}" for name in ("127.0.0.1", "localhost", "[::1]")}
29
+
30
+
31
+ def origin_allowed(origin: str | None) -> bool:
32
+ """Requests from a page carry an Origin; it must be a loopback origin (the UI itself, or the Vite dev server
33
+ that proxies to us). Requests without an Origin (curl, the CLI, same-origin GETs) are allowed."""
34
+ if not origin or origin == "null":
35
+ return origin is None
36
+ parts = urlsplit(origin)
37
+ return parts.scheme in ("http", "https") and (parts.hostname or "") in ("127.0.0.1", "localhost", "::1")
38
+
39
+
40
+ def token_ok(expected: str, given: str | None) -> bool:
41
+ return bool(given) and hmac.compare_digest(expected.encode(), (given or "").encode())
42
+
43
+
44
+ def write_session(state_dir: Path, port: int, token: str, root: Path) -> Path:
45
+ """`.stackthread/session.json`: lets the Vite plugin find the server. Readable by the user only."""
46
+ state_dir.mkdir(parents=True, exist_ok=True)
47
+ ignore = state_dir / ".gitignore"
48
+ if not ignore.exists():
49
+ ignore.write_text("*\n", encoding="utf-8")
50
+ path = state_dir / "session.json"
51
+ tmp = path.with_suffix(".tmp")
52
+ data = {
53
+ "port": port,
54
+ "token": token,
55
+ "pid": os.getpid(),
56
+ "root": str(root),
57
+ "version": __version__,
58
+ "started": int(time.time()),
59
+ }
60
+ fd = os.open(tmp, os.O_WRONLY | os.O_CREAT | os.O_TRUNC, 0o600)
61
+ with os.fdopen(fd, "w", encoding="utf-8") as f:
62
+ json.dump(data, f)
63
+ tmp.replace(path)
64
+ return path
65
+
66
+
67
+ def clear_session(state_dir: Path, token: str) -> None:
68
+ path = state_dir / "session.json"
69
+ try:
70
+ if json.loads(path.read_text(encoding="utf-8")).get("token") == token:
71
+ path.unlink()
72
+ except (OSError, ValueError):
73
+ pass
@@ -0,0 +1,379 @@
1
+ Third-party software bundled in this file. Each package keeps its own licence.
2
+
3
+ ---- preact@11.2.0 (MIT)
4
+
5
+ The MIT License (MIT)
6
+
7
+ Copyright (c) 2015-present Jason Miller
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+
27
+ ---- @codemirror/view@6.43.15 (MIT)
28
+
29
+ MIT License
30
+
31
+ Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
32
+
33
+ Permission is hereby granted, free of charge, to any person obtaining a copy
34
+ of this software and associated documentation files (the "Software"), to deal
35
+ in the Software without restriction, including without limitation the rights
36
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
37
+ copies of the Software, and to permit persons to whom the Software is
38
+ furnished to do so, subject to the following conditions:
39
+
40
+ The above copyright notice and this permission notice shall be included in
41
+ all copies or substantial portions of the Software.
42
+
43
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
44
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
45
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
46
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
47
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
48
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
49
+ THE SOFTWARE.
50
+
51
+ ---- @codemirror/state@6.7.6 (MIT)
52
+
53
+ MIT License
54
+
55
+ Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
56
+
57
+ Permission is hereby granted, free of charge, to any person obtaining a copy
58
+ of this software and associated documentation files (the "Software"), to deal
59
+ in the Software without restriction, including without limitation the rights
60
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
61
+ copies of the Software, and to permit persons to whom the Software is
62
+ furnished to do so, subject to the following conditions:
63
+
64
+ The above copyright notice and this permission notice shall be included in
65
+ all copies or substantial portions of the Software.
66
+
67
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
68
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
69
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
70
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
71
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
72
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
73
+ THE SOFTWARE.
74
+
75
+ ---- @codemirror/language@6.13.1 (MIT)
76
+
77
+ MIT License
78
+
79
+ Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
80
+
81
+ Permission is hereby granted, free of charge, to any person obtaining a copy
82
+ of this software and associated documentation files (the "Software"), to deal
83
+ in the Software without restriction, including without limitation the rights
84
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
85
+ copies of the Software, and to permit persons to whom the Software is
86
+ furnished to do so, subject to the following conditions:
87
+
88
+ The above copyright notice and this permission notice shall be included in
89
+ all copies or substantial portions of the Software.
90
+
91
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
92
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
93
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
94
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
95
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
96
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
97
+ THE SOFTWARE.
98
+
99
+ ---- @codemirror/commands@6.11.1 (MIT)
100
+
101
+ MIT License
102
+
103
+ Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
104
+
105
+ Permission is hereby granted, free of charge, to any person obtaining a copy
106
+ of this software and associated documentation files (the "Software"), to deal
107
+ in the Software without restriction, including without limitation the rights
108
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
109
+ copies of the Software, and to permit persons to whom the Software is
110
+ furnished to do so, subject to the following conditions:
111
+
112
+ The above copyright notice and this permission notice shall be included in
113
+ all copies or substantial portions of the Software.
114
+
115
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
116
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
117
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
118
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
119
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
120
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
121
+ THE SOFTWARE.
122
+
123
+ ---- @codemirror/lang-javascript@6.2.5 (MIT)
124
+
125
+ MIT License
126
+
127
+ Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
128
+
129
+ Permission is hereby granted, free of charge, to any person obtaining a copy
130
+ of this software and associated documentation files (the "Software"), to deal
131
+ in the Software without restriction, including without limitation the rights
132
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
133
+ copies of the Software, and to permit persons to whom the Software is
134
+ furnished to do so, subject to the following conditions:
135
+
136
+ The above copyright notice and this permission notice shall be included in
137
+ all copies or substantial portions of the Software.
138
+
139
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
140
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
141
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
142
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
143
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
144
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
145
+ THE SOFTWARE.
146
+
147
+ ---- @codemirror/lang-python@6.2.1 (MIT)
148
+
149
+ MIT License
150
+
151
+ Copyright (C) 2018-2021 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
152
+
153
+ Permission is hereby granted, free of charge, to any person obtaining a copy
154
+ of this software and associated documentation files (the "Software"), to deal
155
+ in the Software without restriction, including without limitation the rights
156
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
157
+ copies of the Software, and to permit persons to whom the Software is
158
+ furnished to do so, subject to the following conditions:
159
+
160
+ The above copyright notice and this permission notice shall be included in
161
+ all copies or substantial portions of the Software.
162
+
163
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
164
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
165
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
166
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
167
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
168
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
169
+ THE SOFTWARE.
170
+
171
+ ---- @lezer/highlight@1.2.5 (MIT)
172
+
173
+ MIT License
174
+
175
+ Copyright (C) 2018 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
176
+
177
+ Permission is hereby granted, free of charge, to any person obtaining a copy
178
+ of this software and associated documentation files (the "Software"), to deal
179
+ in the Software without restriction, including without limitation the rights
180
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
181
+ copies of the Software, and to permit persons to whom the Software is
182
+ furnished to do so, subject to the following conditions:
183
+
184
+ The above copyright notice and this permission notice shall be included in
185
+ all copies or substantial portions of the Software.
186
+
187
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
188
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
189
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
190
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
191
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
192
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
193
+ THE SOFTWARE.
194
+
195
+ ---- @lezer/common@1.5.5 (MIT)
196
+
197
+ MIT License
198
+
199
+ Copyright (C) 2018 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
200
+
201
+ Permission is hereby granted, free of charge, to any person obtaining a copy
202
+ of this software and associated documentation files (the "Software"), to deal
203
+ in the Software without restriction, including without limitation the rights
204
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
205
+ copies of the Software, and to permit persons to whom the Software is
206
+ furnished to do so, subject to the following conditions:
207
+
208
+ The above copyright notice and this permission notice shall be included in
209
+ all copies or substantial portions of the Software.
210
+
211
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
212
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
213
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
214
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
215
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
216
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
217
+ THE SOFTWARE.
218
+
219
+ ---- @lezer/lr@1.4.12 (MIT)
220
+
221
+ MIT License
222
+
223
+ Copyright (C) 2018 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
224
+
225
+ Permission is hereby granted, free of charge, to any person obtaining a copy
226
+ of this software and associated documentation files (the "Software"), to deal
227
+ in the Software without restriction, including without limitation the rights
228
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
229
+ copies of the Software, and to permit persons to whom the Software is
230
+ furnished to do so, subject to the following conditions:
231
+
232
+ The above copyright notice and this permission notice shall be included in
233
+ all copies or substantial portions of the Software.
234
+
235
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
236
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
237
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
238
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
239
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
240
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
241
+ THE SOFTWARE.
242
+
243
+ ---- @lezer/javascript@1.5.6 (MIT)
244
+
245
+ MIT License
246
+
247
+ Copyright (C) 2018 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
248
+
249
+ Permission is hereby granted, free of charge, to any person obtaining a copy
250
+ of this software and associated documentation files (the "Software"), to deal
251
+ in the Software without restriction, including without limitation the rights
252
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
253
+ copies of the Software, and to permit persons to whom the Software is
254
+ furnished to do so, subject to the following conditions:
255
+
256
+ The above copyright notice and this permission notice shall be included in
257
+ all copies or substantial portions of the Software.
258
+
259
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
260
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
261
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
262
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
263
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
264
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
265
+ THE SOFTWARE.
266
+
267
+ ---- @lezer/python@1.1.19 (MIT)
268
+
269
+ MIT License
270
+
271
+ Copyright (C) 2020 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
272
+
273
+ Permission is hereby granted, free of charge, to any person obtaining a copy
274
+ of this software and associated documentation files (the "Software"), to deal
275
+ in the Software without restriction, including without limitation the rights
276
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
277
+ copies of the Software, and to permit persons to whom the Software is
278
+ furnished to do so, subject to the following conditions:
279
+
280
+ The above copyright notice and this permission notice shall be included in
281
+ all copies or substantial portions of the Software.
282
+
283
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
284
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
285
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
286
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
287
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
288
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
289
+ THE SOFTWARE.
290
+
291
+ ---- style-mod@4.1.4 (MIT)
292
+
293
+ Copyright (C) 2018 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
294
+
295
+ Permission is hereby granted, free of charge, to any person obtaining a copy
296
+ of this software and associated documentation files (the "Software"), to deal
297
+ in the Software without restriction, including without limitation the rights
298
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
299
+ copies of the Software, and to permit persons to whom the Software is
300
+ furnished to do so, subject to the following conditions:
301
+
302
+ The above copyright notice and this permission notice shall be included in
303
+ all copies or substantial portions of the Software.
304
+
305
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
306
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
307
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
308
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
309
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
310
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
311
+ THE SOFTWARE.
312
+
313
+ ---- w3c-keyname@2.2.8 (MIT)
314
+
315
+ Copyright (C) 2016 by Marijn Haverbeke <marijn@haverbeke.berlin> and others
316
+
317
+ Permission is hereby granted, free of charge, to any person obtaining a copy
318
+ of this software and associated documentation files (the "Software"), to deal
319
+ in the Software without restriction, including without limitation the rights
320
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
321
+ copies of the Software, and to permit persons to whom the Software is
322
+ furnished to do so, subject to the following conditions:
323
+
324
+ The above copyright notice and this permission notice shall be included in
325
+ all copies or substantial portions of the Software.
326
+
327
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
328
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
329
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
330
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
331
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
332
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
333
+ THE SOFTWARE.
334
+
335
+ ---- crelt@1.0.7 (MIT)
336
+
337
+ Copyright (C) 2020 by Marijn Haverbeke <marijn@haverbeke.berlin>
338
+
339
+ Permission is hereby granted, free of charge, to any person obtaining a copy
340
+ of this software and associated documentation files (the "Software"), to deal
341
+ in the Software without restriction, including without limitation the rights
342
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
343
+ copies of the Software, and to permit persons to whom the Software is
344
+ furnished to do so, subject to the following conditions:
345
+
346
+ The above copyright notice and this permission notice shall be included in
347
+ all copies or substantial portions of the Software.
348
+
349
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
350
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
351
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
352
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
353
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
354
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
355
+ THE SOFTWARE.
356
+
357
+ ---- @marijn/find-cluster-break@1.0.4 (MIT)
358
+
359
+ MIT License
360
+
361
+ Copyright (C) 2024 by Marijn Haverbeke <marijn@haverbeke.berlin>
362
+
363
+ Permission is hereby granted, free of charge, to any person obtaining a copy
364
+ of this software and associated documentation files (the "Software"), to deal
365
+ in the Software without restriction, including without limitation the rights
366
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
367
+ copies of the Software, and to permit persons to whom the Software is
368
+ furnished to do so, subject to the following conditions:
369
+
370
+ The above copyright notice and this permission notice shall be included in
371
+ all copies or substantial portions of the Software.
372
+
373
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
374
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
375
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
376
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
377
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
378
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
379
+ THE SOFTWARE.
@@ -0,0 +1 @@
1
+ :root,:root[data-theme=dark]{--bg: #0d1322;--bg-2: #111a2c;--panel: #131c30;--panel-2: #172238;--raise: #1c2944;--line: #22304b;--line-2: #2c3d5d;--ink: #dde5f3;--ink-2: #b4c1d8;--mute: #8494b1;--faint: #5b6a86;--accent: #6aa7ff;--accent-ink: #0b1220;--action: #fbbf24;--good: #34d399;--warn: #f59e0b;--bad: #f87171;--screen: #7dd3fc;--client: #a78bfa;--network: #fbbf24;--server: #34d399;--data: #f472b6;--concept: #fda4af;--shadow: 0 18px 40px -18px rgba(0, 0, 0, .65);--scrim: rgba(5, 9, 18, .55);--frame-bg: #ffffff;--syn-keyword: #c792ea;--syn-string: #a5e3a1;--syn-number: #f9a66c;--syn-comment: #637494;--syn-function: #82c4ff;--syn-type: #ffcb6b;--syn-property: #b9c8e6;--syn-tag: #ff8a9a;--syn-attr: #f2c97d;--syn-operator: #89ddff;--gutter: #4a5a78;--selection: rgba(106, 167, 255, .25);--radius: 10px;--mono: ui-monospace, "Cascadia Code", "JetBrains Mono", "SF Mono", Menlo, Consolas, monospace;--sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;color-scheme:dark}:root[data-theme=light]{--bg: #eef1f7;--bg-2: #f6f8fc;--panel: #ffffff;--panel-2: #f7f9fd;--raise: #e9eef7;--line: #e1e6ef;--line-2: #ccd5e3;--ink: #172033;--ink-2: #364259;--mute: #5f6c84;--faint: #8c97ac;--accent: #2563eb;--accent-ink: #ffffff;--action: #b45309;--good: #059669;--warn: #b45309;--bad: #dc2626;--screen: #0284c7;--client: #7c3aed;--network: #b45309;--server: #059669;--data: #db2777;--concept: #e11d48;--shadow: 0 18px 40px -22px rgba(23, 32, 51, .35);--scrim: rgba(23, 32, 51, .28);--frame-bg: #ffffff;--syn-keyword: #7c3aed;--syn-string: #047857;--syn-number: #c2410c;--syn-comment: #7b879c;--syn-function: #1d4ed8;--syn-type: #a16207;--syn-property: #334155;--syn-tag: #be123c;--syn-attr: #a16207;--syn-operator: #0e7490;--gutter: #9aa5b8;--selection: rgba(37, 99, 235, .18);color-scheme:light}*{box-sizing:border-box}html,body{margin:0;height:100%}body{background:var(--bg);color:var(--ink);font:13px/1.45 var(--sans);-webkit-font-smoothing:antialiased;overflow:hidden}button,input,select{font:inherit;color:inherit}a{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline}:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}.mono{font-family:var(--mono)}.muted{color:var(--mute)}.faint{color:var(--faint)}.grow{flex:1;min-width:0}.row{display:flex;align-items:center;gap:8px}[hidden]{display:none!important}.sr{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0)}.shell{display:grid;grid-template-rows:48px 1fr 26px;height:100vh}.shell.panel{grid-template-rows:1fr}.top{display:flex;align-items:center;gap:14px;padding:0 14px;background:linear-gradient(180deg,var(--bg-2),var(--bg));border-bottom:1px solid var(--line)}.brand{display:flex;align-items:center;gap:9px;font-weight:700;letter-spacing:.01em;white-space:nowrap}.brand svg{width:20px;height:20px}.brand small{color:var(--mute);font-weight:500}.tabs{display:flex;gap:2px;background:var(--bg-2);border:1px solid var(--line);border-radius:9px;padding:2px}.tabs button{border:0;background:none;color:var(--mute);padding:5px 12px;border-radius:7px;cursor:pointer;font-weight:600}.tabs button[aria-pressed=true]{background:var(--raise);color:var(--ink)}.tabs button:hover{color:var(--ink)}.search-trigger{display:flex;align-items:center;gap:8px;min-width:240px;max-width:420px;flex:1;background:var(--bg-2);border:1px solid var(--line);border-radius:9px;padding:6px 10px;color:var(--mute);cursor:text}.search-trigger kbd,kbd{font:11px var(--mono);color:var(--mute);border:1px solid var(--line-2);border-bottom-width:2px;border-radius:5px;padding:0 5px;margin-left:auto;background:var(--panel)}.live{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--mute);white-space:nowrap}.live i{width:8px;height:8px;border-radius:50%;background:var(--faint)}.live.on i{background:var(--good);box-shadow:0 0 #34d39999;animation:beat 2.4s infinite}.live.off i{background:var(--bad)}@keyframes beat{0%{box-shadow:0 0 #34d3998c}70%{box-shadow:0 0 0 7px #34d39900}to{box-shadow:0 0 #34d39900}}.status{display:flex;align-items:center;gap:16px;padding:0 14px;border-top:1px solid var(--line);background:var(--bg-2);color:var(--mute);font-size:11.5px;white-space:nowrap;overflow:hidden}.status .push{margin-left:auto}.btn{display:inline-flex;align-items:center;gap:6px;border:1px solid var(--line-2);background:var(--panel-2);color:var(--ink);border-radius:8px;padding:5px 10px;cursor:pointer;font-weight:600;font-size:12.5px;white-space:nowrap}.btn:hover{background:var(--raise)}.btn:disabled{opacity:.5;cursor:default}.btn.primary,.btn.on{background:var(--accent);border-color:var(--accent);color:var(--accent-ink)}.btn.ghost{background:none;border-color:transparent;color:var(--mute)}.btn.ghost:hover{color:var(--ink);background:var(--raise)}.btn svg,.icon{width:15px;height:15px;flex:none}.chip{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:600;padding:1px 7px;border-radius:999px;border:1px solid var(--line-2);color:var(--ink-2);background:var(--panel-2);white-space:nowrap}.chip.inferred{color:var(--warn);border-color:color-mix(in srgb,var(--warn) 45%,transparent)}.chip.heuristic{color:var(--bad);border-color:color-mix(in srgb,var(--bad) 45%,transparent)}.chip.action{color:var(--action);border-color:color-mix(in srgb,var(--action) 45%,transparent)}.bench{display:grid;grid-template-columns:236px minmax(320px,1fr) var(--side, 520px);min-height:0}.bench.no-side{grid-template-columns:236px 1fr}.side-left{border-right:1px solid var(--line);background:var(--bg-2);overflow:auto;padding:10px 8px 24px}.side-left h3{margin:14px 8px 6px;font-size:11px;font-weight:700;color:var(--mute);letter-spacing:.06em;text-transform:uppercase;display:flex;align-items:center;gap:6px}.side-left h3 .count{margin-left:auto;font-weight:600;color:var(--faint);letter-spacing:0}.nav-item{display:flex;align-items:center;gap:8px;width:100%;text-align:left;border:0;background:none;color:var(--ink-2);padding:6px 8px;border-radius:7px;cursor:pointer;min-width:0}.nav-item:hover{background:var(--panel-2);color:var(--ink)}.nav-item[aria-current=true]{background:var(--raise);color:var(--ink);box-shadow:inset 2px 0 0 var(--screen)}.nav-item .path{font:12px var(--mono);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.nav-item .sub{font-size:11px;color:var(--faint);margin-left:auto;white-space:nowrap}.nav-item .dot{width:6px;height:6px;border-radius:50%;background:var(--screen);flex:none;opacity:.7}.empty-note{color:var(--faint);font-size:12px;padding:4px 10px 8px;line-height:1.5}.stage{display:grid;grid-template-rows:44px 1fr;min-width:0;min-height:0;background:var(--bg)}.urlbar{display:flex;align-items:center;gap:6px;padding:6px 10px;border-bottom:1px solid var(--line);background:var(--bg-2)}.urlbar input{flex:1;min-width:0;background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:5px 10px;font:12.5px var(--mono);color:var(--ink)}.frame-wrap{position:relative;min-height:0}.frame-wrap iframe{position:absolute;inset:0;width:100%;height:100%;border:0;background:var(--frame-bg)}.frame-note{position:absolute;inset:0;display:grid;place-items:center;padding:32px;text-align:center;color:var(--mute)}.frame-note>div{max-width:460px;line-height:1.6}.frame-note h2{color:var(--ink);font-size:18px;margin:0 0 8px}.frame-note pre{background:var(--panel);border:1px solid var(--line);border-radius:8px;padding:10px 12px;text-align:left;font:12px var(--mono);color:var(--ink)}.side-right{display:grid;grid-template-rows:minmax(140px,1fr) 6px var(--code, 46%);border-left:1px solid var(--line);min-height:0;min-width:0;background:var(--panel);position:relative}.side-right.no-code{grid-template-rows:1fr}.side-right>*{min-width:0;min-height:0}.splitter-v{position:absolute;left:-4px;top:0;bottom:0;width:8px;cursor:ew-resize;z-index:5}.splitter-h{cursor:ns-resize;position:relative;z-index:5;background:var(--line)}.splitter-h:hover{background:var(--accent)}.lineage{display:grid;grid-template-rows:auto 1fr;min-height:0}.sel-head{padding:14px 16px 12px;border-bottom:1px solid var(--line);background:linear-gradient(180deg,var(--panel-2),var(--panel))}.sel-head .kicker{font-size:11px;color:var(--mute);display:flex;align-items:center;gap:8px}.sel-head h2{margin:4px 0 2px;font:700 16px/1.3 var(--mono);color:var(--ink);word-break:break-word}.sel-head h2 .text{font-family:var(--sans);font-weight:600;color:var(--ink-2)}.sel-head .where{font:12px var(--mono);color:var(--mute)}.sel-head .tools{display:flex;gap:6px;margin-top:10px;flex-wrap:wrap}.layers{overflow:auto;padding:6px 0 24px}.layer{position:relative;padding:4px 12px 2px 16px}.layer-head{display:flex;align-items:center;gap:8px;margin:12px 0 4px;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;color:var(--c)}.layer-head .note{text-transform:none;letter-spacing:0;font-weight:500;color:var(--faint)}.layer-head .count{margin-left:auto;color:var(--faint);font-weight:600;letter-spacing:0}.layer-head .swatch{width:9px;height:9px;border-radius:3px;background:var(--c)}.thread{position:relative;margin-left:4px;padding-left:18px}.thread:before{content:"";position:absolute;left:4px;top:-6px;bottom:6px;width:2px;border-radius:2px;background:linear-gradient(180deg,color-mix(in srgb,var(--c) 85%,transparent),color-mix(in srgb,var(--c) 35%,transparent))}.knot{position:relative;display:grid;grid-template-columns:1fr auto;column-gap:10px;align-items:baseline;padding:5px 8px;margin-left:calc(var(--d, 0) * 14px);border-radius:8px;cursor:pointer;border:1px solid transparent}.knot:before{content:"";position:absolute;left:-18px;top:10px;width:10px;height:10px;border-radius:50%;background:var(--panel);border:2px solid var(--c);z-index:1}.knot[data-depth]:not([data-depth="0"]):after{content:"";position:absolute;left:calc(-13px - var(--d) * 14px);top:15px;width:calc(var(--d) * 14px - 4px);height:1px;background:color-mix(in srgb,var(--c) 45%,transparent)}.knot:hover{background:var(--panel-2);border-color:var(--line)}.knot[aria-current=true]{background:var(--raise);border-color:color-mix(in srgb,var(--c) 50%,var(--line))}.knot .label{font:600 12.5px var(--mono);color:var(--ink);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.knot .loc{font:11px var(--mono);color:var(--faint);white-space:nowrap}.knot .reason{grid-column:1 / -1;font-size:11.5px;color:var(--mute);display:flex;gap:6px;align-items:center;flex-wrap:wrap}.knot.reads .label{color:var(--ink-2);font-weight:500}.knot.reads:before{border-style:dashed}.knot.external{cursor:default}.knot.external .label{color:var(--faint);font-family:var(--sans);font-weight:500}.knot.action:before{background:var(--c)}.more{margin:2px 0 6px 18px;font-size:12px}.empty{display:grid;place-items:center;text-align:center;color:var(--mute);padding:36px 24px;line-height:1.6}.empty .art{width:120px;height:120px;margin-bottom:10px}.empty h2{margin:0 0 6px;font-size:16px;color:var(--ink)}.code{display:grid;grid-template-rows:auto auto 1fr;min-height:0;min-width:0;background:var(--bg-2)}.code>*{min-width:0}.code-bar{flex-wrap:wrap;row-gap:4px}.code-tabs{display:flex;gap:2px;padding:6px 8px 0;overflow-x:auto;border-bottom:1px solid var(--line)}.code-tab{display:flex;align-items:center;gap:6px;border:1px solid transparent;border-bottom:0;background:none;color:var(--mute);padding:6px 10px;border-radius:8px 8px 0 0;cursor:pointer;font:12px var(--mono);white-space:nowrap}.code-tab[aria-selected=true]{background:var(--panel);color:var(--ink);border-color:var(--line);margin-bottom:-1px}.code-tab .x{opacity:.5;border-radius:4px;padding:0 3px}.code-tab .x:hover{opacity:1;background:var(--raise)}.code-tab .dirty{width:7px;height:7px;border-radius:50%;background:var(--action)}.code-bar{display:flex;align-items:center;gap:6px;padding:6px 10px;background:var(--panel);border-bottom:1px solid var(--line)}.code-bar .path{font:12px var(--mono);color:var(--mute);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.code-body{min-height:0;overflow:hidden;position:relative;background:var(--panel)}.code-body .cm-editor{height:100%;font-size:12.5px}.code-body .cm-scroller{font-family:var(--mono);line-height:1.6}.cm-target-line{background:color-mix(in srgb,var(--action) 14%,transparent)!important;box-shadow:inset 3px 0 0 var(--action)}.banner{display:flex;align-items:center;gap:8px;padding:7px 10px;font-size:12px;border-bottom:1px solid var(--line)}.banner.warn{background:color-mix(in srgb,var(--warn) 14%,var(--panel));color:var(--ink)}.banner.bad{background:color-mix(in srgb,var(--bad) 16%,var(--panel));color:var(--ink)}.banner.good{background:color-mix(in srgb,var(--good) 14%,var(--panel));color:var(--ink)}.map{display:grid;grid-template-columns:1fr var(--side, 520px);min-height:0}.map.no-side{grid-template-columns:1fr}.map-main{display:grid;grid-template-rows:auto 1fr;min-height:0;min-width:0}.map-tabs{display:flex;gap:6px;padding:10px 14px;border-bottom:1px solid var(--line);background:var(--bg-2);align-items:center}.map-body{overflow:auto;padding:16px 18px 40px;min-height:0}.cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(260px,1fr));gap:12px}.card{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:12px 14px}.card h4{margin:0 0 8px;font-size:12.5px;display:flex;align-items:center;gap:8px}.card h4 .count{margin-left:auto;color:var(--faint);font-weight:600}.pills{display:flex;flex-wrap:wrap;gap:5px}.pill{border:1px solid color-mix(in srgb,var(--c) 45%,var(--line));background:color-mix(in srgb,var(--c) 9%,transparent);color:var(--ink-2);border-radius:999px;padding:2px 9px;font:11.5px var(--mono);cursor:pointer;white-space:nowrap;max-width:100%;overflow:hidden;text-overflow:ellipsis}.pill:hover{color:var(--ink);border-color:var(--c)}.stats{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:16px}.stat{background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);padding:10px 14px;min-width:120px}.stat b{display:block;font-size:20px;font-weight:700;color:var(--ink)}.stat span{color:var(--mute);font-size:12px}table.grid{width:100%;border-collapse:collapse;background:var(--panel);border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}table.grid th,table.grid td{text-align:left;padding:7px 10px;border-top:1px solid var(--line);vertical-align:top}table.grid td.mono{overflow-wrap:anywhere}table.grid th{font-size:11px;color:var(--mute);font-weight:700;text-transform:uppercase;letter-spacing:.05em;border-top:0;background:var(--panel-2)}table.grid tr.click{cursor:pointer}table.grid tr.click:hover td{background:var(--panel-2)}.method{font:700 11px var(--mono);padding:1px 6px;border-radius:5px;background:var(--raise);color:var(--network)}.sev{font-weight:700;font-size:11px;text-transform:uppercase}.sev.error{color:var(--bad)}.sev.warning{color:var(--warn)}.sev.info{color:var(--accent)}.focus-svg{display:block;width:100%}.focus-svg .n rect{fill:var(--panel);stroke:var(--line-2)}.focus-svg .n:hover rect{stroke:var(--accent)}.focus-svg .n text{fill:var(--ink);font:600 12px var(--mono)}.focus-svg .n text.s{fill:var(--mute);font:11px var(--sans)}.focus-svg .center rect{stroke:var(--accent);stroke-width:1.5;fill:var(--panel-2)}.focus-svg path{fill:none;stroke-width:1.4;opacity:.8}.focus-svg .lbl{fill:var(--faint);font:10.5px var(--sans)}.overlay{position:fixed;inset:0;background:var(--scrim);display:grid;place-items:start center;padding-top:12vh;z-index:50}.palette{width:min(640px,92vw);background:var(--panel);border:1px solid var(--line-2);border-radius:14px;box-shadow:var(--shadow);overflow:hidden}.palette input{width:100%;border:0;border-bottom:1px solid var(--line);background:none;padding:14px 16px;font-size:15px;outline:none;color:var(--ink)}.palette ul{list-style:none;margin:0;padding:6px;max-height:50vh;overflow:auto}.palette li{display:flex;align-items:center;gap:10px;padding:7px 10px;border-radius:8px;cursor:pointer}.palette li[aria-selected=true]{background:var(--raise)}.palette li .name{font:600 12.5px var(--mono)}.palette li .file{margin-left:auto;font:11px var(--mono);color:var(--faint);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:50%}.kind{font:700 10px var(--sans);text-transform:uppercase;letter-spacing:.06em;padding:1px 6px;border-radius:5px;color:var(--accent-ink);background:var(--c, var(--mute))}.toast{position:fixed;right:16px;bottom:40px;background:var(--raise);border:1px solid var(--line-2);color:var(--ink);padding:9px 14px;border-radius:10px;box-shadow:var(--shadow);z-index:60;max-width:520px}.toast.bad{border-color:var(--bad)}.toast.good{border-color:var(--good)}.top{position:relative}.activity{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--line);background:var(--bg-2);color:var(--mute);border-radius:999px;padding:4px 11px;font:600 12px var(--sans);cursor:pointer;white-space:nowrap;max-width:280px}.activity .act-text{overflow:hidden;text-overflow:ellipsis}.activity:hover{color:var(--ink);border-color:var(--line-2)}.activity .act-ico{width:9px;height:9px;border-radius:50%;background:var(--faint);flex:none}.activity.on .act-ico{background:var(--good);animation:beat 2.4s infinite}.activity.busy{color:var(--ink);border-color:color-mix(in srgb,var(--accent) 50%,var(--line));background:color-mix(in srgb,var(--accent) 10%,var(--bg-2))}.activity.busy .act-ico{width:12px;height:12px;background:none;border:2px solid color-mix(in srgb,var(--accent) 25%,transparent);border-top-color:var(--accent);animation:actspin .8s linear infinite}.activity.off .act-ico{background:var(--bad)}.act-bar{position:absolute;left:0;right:0;bottom:-1px;height:3px;overflow:hidden;pointer-events:none;background:color-mix(in srgb,var(--accent) 12%,transparent);z-index:6}.act-bar i{display:block;height:100%;background:var(--accent);transition:width .15s linear}.act-bar.indeterminate i{width:35%;animation:actslide .9s ease-in-out infinite}@keyframes actspin{to{transform:rotate(360deg)}}@keyframes actslide{0%{transform:translate(-110%)}to{transform:translate(300%)}}.act-pop{position:fixed;top:54px;right:56px;width:min(460px,92vw);z-index:45;background:var(--panel);border:1px solid var(--line-2);border-radius:12px;box-shadow:var(--shadow);padding:12px 14px;font-size:12.5px;color:var(--ink-2)}.act-pop h4{margin:0;font-size:13px;color:var(--ink)}.act-pop .act-now{margin:4px 0 10px;font-size:15px;font-weight:700;color:var(--ink)}.log-list{list-style:none;margin:0;padding:0;max-height:300px;overflow:auto;display:grid;gap:3px}.log-list li.log{display:grid;grid-template-columns:62px 64px 1fr;gap:8px;align-items:baseline}.log-area{color:var(--faint);font:11px var(--mono);overflow:hidden;text-overflow:ellipsis}.log-msg{color:var(--ink-2);overflow-wrap:anywhere}.log.warning .log-msg{color:var(--warn)}.log.error .log-msg,.log.critical .log-msg{color:var(--bad)}.status-link{all:unset;cursor:pointer;color:var(--mute)}.status-link:hover{color:var(--ink);text-decoration:underline}.palette.report{padding:18px 20px}.palette.report h3{margin:0 0 6px;font-size:16px;color:var(--ink)}.palette.report p{margin:0 0 12px;line-height:1.5}.palette.report label{display:block;font-weight:600;color:var(--ink);margin-bottom:6px}.palette.report textarea{width:100%;border:1px solid var(--line-2);border-radius:9px;background:var(--bg-2);color:var(--ink);padding:8px 10px;font:13px/1.5 var(--sans);resize:vertical}.not-ready{display:grid;place-items:center;text-align:center;padding:40px}.not-ready>div{max-width:520px}.not-ready h2{margin:0 0 14px;color:var(--ink);font-size:18px}.nr-bar{height:6px;border-radius:4px;background:var(--raise);overflow:hidden;margin:0 auto 10px;max-width:420px}.nr-bar i{display:block;height:100%;background:var(--accent);transition:width .2s linear}@media(max-width:1100px){.bench{grid-template-columns:0 1fr var(--side, 440px)}.side-left{display:none}}@media(prefers-reduced-motion:reduce){*{animation:none!important;transition:none!important}}