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.
- stackthread/__init__.py +6 -0
- stackthread/__main__.py +3 -0
- stackthread/_vendor/THIRD_PARTY_NOTICES.txt +23 -0
- stackthread/_vendor/extract.mjs +15 -0
- stackthread/adapters.py +130 -0
- stackthread/cli.py +342 -0
- stackthread/config.py +409 -0
- stackthread/detect.py +150 -0
- stackthread/lang/__init__.py +1 -0
- stackthread/lang/js/__init__.py +1 -0
- stackthread/lang/js/aliases.py +133 -0
- stackthread/lang/js/build.py +455 -0
- stackthread/lang/js/worker.py +186 -0
- stackthread/lang/python/__init__.py +1 -0
- stackthread/lang/python/build.py +249 -0
- stackthread/lang/python/extract.py +494 -0
- stackthread/lang/python/frameworks.py +700 -0
- stackthread/lang/python/orm.py +300 -0
- stackthread/lang/python/resolve.py +448 -0
- stackthread/lineage.py +965 -0
- stackthread/link.py +130 -0
- stackthread/logs.py +314 -0
- stackthread/model.py +161 -0
- stackthread/paths.py +173 -0
- stackthread/project.py +414 -0
- stackthread/report.py +203 -0
- stackthread/rules.py +48 -0
- stackthread/server/__init__.py +5 -0
- stackthread/server/app.py +696 -0
- stackthread/server/edit.py +108 -0
- stackthread/server/security.py +73 -0
- stackthread/static/THIRD_PARTY_NOTICES.txt +379 -0
- stackthread/static/app.css +1 -0
- stackthread/static/app.js +29 -0
- stackthread/static/index.html +16 -0
- stackthread-0.1.0.dev2.dist-info/METADATA +51 -0
- stackthread-0.1.0.dev2.dist-info/RECORD +40 -0
- stackthread-0.1.0.dev2.dist-info/WHEEL +4 -0
- stackthread-0.1.0.dev2.dist-info/entry_points.txt +2 -0
- 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}}
|