mirror of
https://github.com/decompme/decomp.me.git
synced 2026-02-14 10:34:12 -06:00
CodeMirror & PresetSelect fix (#413)
* yarn upgrade * update eslint no-multiple-empty-lines rule * update Next.js & React Resolves #358. * use CodeMirror in scratch editor * use codemirror on new page * move extensions out of CodeMirror component * grow scratch codemirror * fix state bug * initial theming * fix scratch page SSR * fix credits page rendering * max width for AsyncButton error popup * word wrap * bye monaco * cleanup * style scrollbars everywhere * tab stuff * updates * more * update codemirror theme * new react, backend deps, preset on scratch, bugfixes * improve monospace areas * store preset name on scratch * stylelint * mypy * mypy 2 * monospace * fix monospace * add padding to editor * fix saving * fix selection colours * Highlight assembly line on source selected line change (#416) Co-authored-by: Alex Bates <hi@imalex.xyz> * mypy & black * font stuff * remove courier new * make diff style consistent with editor * fix codemirror exception when editing eof * attempt to reduce layout thrashing * /new: store presetName in localStorage * use react-window for diff * link diff column scroll position * hide double scrollbar * increase overscan * pointer-events:none on diff header * fix diff header thing * make diff columns non-draggable * dont shrink tabs * add draggable bar between diff columns * remove log * fix 2-col mode * fix diff align * DiffCell support cell=undefined * drag better * style .cm-search * eth style changes * reduce rerenders when dragging diff bar Co-authored-by: Alex Bates <hi@imalex.xyz> Co-authored-by: hatal175 <hatal175@users.noreply.github.com> Co-authored-by: Alex Bates <alex@nanaian.town>
This commit is contained in:
@@ -1,5 +0,0 @@
|
||||
# 8/8/2021
|
||||
|
||||
* Improved performance by only sending context to the backend if it was changed from the current version
|
||||
|
||||
* Added scratch forking
|
||||
220
README.md
220
README.md
@@ -7,230 +7,12 @@
|
||||
|
||||
A collaborative decompilation and reverse engineering website, built with Next.js and Django.
|
||||
|
||||
## Directory structure
|
||||
```
|
||||
frontend/
|
||||
public/ ; Static files
|
||||
src/ ; React/Typescript sourcecode
|
||||
|
||||
backend/
|
||||
compilers/ ; Compiler binaries and configuration
|
||||
coreapp/ ; API Django app
|
||||
migrations/ ; Database migrations (generated by Django)
|
||||
decompme/ ; Main Django app
|
||||
|
||||
.env ; Default configuration
|
||||
.env.local ; Local configuration overrides (not checked-in)
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
See [DOCKER.md](DOCKER.md) for instructions on how to run the project in a Docker container.
|
||||
|
||||
Dependencies:
|
||||
- Python >=3.9
|
||||
- Node.js
|
||||
- [Yarn](https://yarnpkg.com/getting-started/install)
|
||||
|
||||
---
|
||||
|
||||
- Create a file to hold environment variables:
|
||||
```shell
|
||||
touch .env.local
|
||||
```
|
||||
|
||||
### Frontend
|
||||
```shell
|
||||
cd frontend
|
||||
```
|
||||
|
||||
- Install dependencies
|
||||
```shell
|
||||
yarn
|
||||
```
|
||||
|
||||
- Start the development webserver
|
||||
```shell
|
||||
yarn dev
|
||||
```
|
||||
|
||||
- Access the site via [http://localhost:8080](http://localhost:8080)
|
||||
|
||||
### Backend
|
||||
```shell
|
||||
cd backend
|
||||
```
|
||||
|
||||
* [Install poetry](https://python-poetry.org/docs/master/#installing-with-the-official-installer) and python dependencies
|
||||
```shell
|
||||
poetry install
|
||||
```
|
||||
|
||||
- Install compilers
|
||||
```shell
|
||||
poetry run python compilers/download.py
|
||||
```
|
||||
|
||||
- Set up the database
|
||||
```shell
|
||||
poetry run python manage.py migrate
|
||||
```
|
||||
|
||||
- Start the API server
|
||||
```shell
|
||||
poetry run python manage.py runserver
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
The following setup sections are optional.
|
||||
|
||||
### Wine setup (for local development, running Windows compilers)
|
||||
- Create a wineprefix dir
|
||||
```shell
|
||||
WINEPREFIX=$HOME/.wine WINEARCH=win32 wineboot --init
|
||||
```
|
||||
|
||||
- Add the WINEPREFIX setting to your .local.env file in the root of the repo
|
||||
```shell
|
||||
echo "WINEPREFIX=$HOME/.wine" >> .local.env
|
||||
```
|
||||
|
||||
### GitHub authentication
|
||||
|
||||
- [Register a new OAuth application](https://github.com/settings/applications/new)
|
||||
- "Homepage URL" should be the URL you access the frontend on (e.g. `http://localhost:8080`)
|
||||
- "Authorization callback URL" should be the same as the homepage URL, but with `/login` appended
|
||||
|
||||
- Edit `.env.local`:
|
||||
- Set `GITHUB_CLIENT_ID` to the application client ID
|
||||
- Set `GITHUB_CLIENT_SECRET` to the application client secret (do **not** share this)
|
||||
|
||||
### Running inside an nginx proxy
|
||||
|
||||
Running decomp.me using nginx as a proxy better emulates the production environment and can avoid cookie-related issues.
|
||||
|
||||
- Install nginx
|
||||
|
||||
- Create an nginx site configuration (typically `/etc/nginx/sites-available/decomp.local`)
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
client_max_body_size 5M;
|
||||
|
||||
server_name decomp.local www.decomp.local;
|
||||
|
||||
location / {
|
||||
try_files $uri @proxy_frontend;
|
||||
}
|
||||
|
||||
location /api {
|
||||
try_files $uri @proxy_api;
|
||||
}
|
||||
location /admin {
|
||||
try_files $uri @proxy_api;
|
||||
}
|
||||
location /static {
|
||||
try_files $uri @proxy_api;
|
||||
}
|
||||
|
||||
location @proxy_api {
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header X-Url-Scheme $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
}
|
||||
|
||||
location @proxy_frontend {
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header X-Url-Scheme $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Enable the site
|
||||
```shell
|
||||
ln -s /etc/nginx/sites-available/decomp.local /etc/nginx/sites-enabled/decomp.local
|
||||
```
|
||||
|
||||
- Add the following lines to `/etc/hosts`:
|
||||
```
|
||||
127.0.0.1 decomp.local
|
||||
127.0.0.1 www.decomp.local
|
||||
```
|
||||
|
||||
- Edit `.env.local`:
|
||||
- Set `API_BASE=/api`
|
||||
- Set `ALLOWED_HOSTS=decomp.local`
|
||||
|
||||
- If you set up GitHub authentication, change the application URLs to `http://decomp.local` and `http://decomp.local/login`
|
||||
|
||||
- Restart nginx, the frontend, and the backend
|
||||
|
||||
- Access the site via [http://decomp.local](http://decomp.local)
|
||||
|
||||
### Sandbox jail
|
||||
|
||||
There is support for running subprocesses within [`nsjail`](https://github.com/google/nsjail).
|
||||
|
||||
This is controlled by the `SANDBOX` settings, and is disabled by default in the development `.env` but is enabled inside the `backend` Docker container.
|
||||
|
||||
To enable it locally outside of the Docker container:
|
||||
|
||||
- Build or install `nsjail` locally. Example instructions for Ubuntu:
|
||||
- `apt-get install autoconf bison flex gcc g++ git libprotobuf-dev libnl-route-3-dev libtool make pkg-config protobuf-compiler`
|
||||
- `git clone --recursive --branch=3.0 https://github.com/google/nsjail`
|
||||
- `cd nsjail && make`
|
||||
- Enable `unprivileged_userns_clone`
|
||||
- Temporary: `sudo sysctl -w kernel.unprivileged_userns_clone=1`
|
||||
- Permanent: `echo 'kernel.unprivileged_userns_clone=1' | sudo tee -a /etc/sysctl.d/00-local-userns.conf && sudo service procps restart`
|
||||
|
||||
- Edit `.env.local`:
|
||||
- Set `USE_SANDBOX_JAIL=on`
|
||||
- Set `SANDBOX_NSJAIL_BIN_PATH` to the absolute path of the `nsjail` binary built above
|
||||
|
||||
## Contributing
|
||||
|
||||
Contributions are very much welcome! You may want to [join our Discord server](https://discord.gg/sutqNShRRs).
|
||||
|
||||
### Storybook
|
||||
To learn more, see [CONTRIBUTING.md](docs/CONTRIBUTING.md)
|
||||
|
||||
Use `yarn storybook` to run a Storybook instance on [http://localhost:6006](http://localhost:6006). This is useful for testing UI components in isolation.
|
||||
|
||||
### Linting
|
||||
|
||||
- Check frontend
|
||||
```shell
|
||||
cd frontend
|
||||
yarn lint
|
||||
```
|
||||
|
||||
- Autofix frontend
|
||||
```shell
|
||||
cd frontend
|
||||
yarn lint --fix
|
||||
```
|
||||
|
||||
- Check backend
|
||||
```shell
|
||||
cd backend
|
||||
mypy
|
||||
```
|
||||
|
||||
### Updating the database
|
||||
|
||||
If you modify any database models (`models.py`), you'll need to run the following to update the database:
|
||||
```shell
|
||||
poetry run python manage.py makemigrations
|
||||
poetry run python manage.py migrate
|
||||
```
|
||||
|
||||
## License
|
||||
decomp.me uses the MIT license. All dependencies may contain their own licenses, which decomp.me respects.
|
||||
|
||||
@@ -102,6 +102,7 @@ def from_id(compiler_id: str) -> Compiler:
|
||||
return _compilers[compiler_id]
|
||||
|
||||
|
||||
@cache
|
||||
def available_compilers() -> List[Compiler]:
|
||||
return sorted(
|
||||
_compilers.values(),
|
||||
@@ -109,6 +110,7 @@ def available_compilers() -> List[Compiler]:
|
||||
)
|
||||
|
||||
|
||||
@cache
|
||||
def available_platforms() -> List[Platform]:
|
||||
return sorted(
|
||||
set(compiler.platform for compiler in available_compilers()),
|
||||
@@ -121,6 +123,13 @@ def available_presets(platform: Platform) -> List[Preset]:
|
||||
return [p for p in _presets if p.compiler.platform == platform]
|
||||
|
||||
|
||||
def preset_from_name(name: str) -> Optional[Preset]:
|
||||
for p in _presets:
|
||||
if p.name == name:
|
||||
return p
|
||||
return None
|
||||
|
||||
|
||||
DUMMY = DummyCompiler(id="dummy", platform=platforms.DUMMY, cc="")
|
||||
|
||||
# GBA
|
||||
|
||||
18
backend/coreapp/migrations/0019_scratch_preset.py
Normal file
18
backend/coreapp/migrations/0019_scratch_preset.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.0.3 on 2022-03-30 11:28
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("coreapp", "0018_rename_compilers"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="scratch",
|
||||
name="preset",
|
||||
field=models.CharField(blank=True, max_length=100, null=True),
|
||||
),
|
||||
]
|
||||
@@ -53,6 +53,7 @@ class Scratch(models.Model):
|
||||
compiler_flags = models.TextField(
|
||||
max_length=1000, default="", blank=True
|
||||
) # TODO: reference a CompilerConfig
|
||||
preset = models.CharField(max_length=100, blank=True, null=True)
|
||||
target_assembly = models.ForeignKey(Assembly, on_delete=models.CASCADE)
|
||||
source_code = models.TextField(blank=True)
|
||||
context = models.TextField(blank=True)
|
||||
|
||||
@@ -110,6 +110,7 @@ class ScratchCreateSerializer(serializers.Serializer[None]):
|
||||
compiler = serializers.CharField(allow_blank=True, required=True)
|
||||
platform = serializers.CharField(allow_blank=True, required=False)
|
||||
compiler_flags = serializers.CharField(allow_blank=True, required=False)
|
||||
preset = serializers.CharField(allow_blank=True, required=False)
|
||||
source_code = serializers.CharField(allow_blank=True, required=False)
|
||||
target_asm = serializers.CharField(allow_blank=True)
|
||||
context = serializers.CharField(allow_blank=True) # type: ignore
|
||||
|
||||
@@ -34,6 +34,8 @@ from ..serializers import (
|
||||
ScratchSerializer,
|
||||
TerseScratchSerializer,
|
||||
)
|
||||
from ..platforms import Platform
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -168,11 +170,10 @@ def create_scratch(data: Dict[str, Any], allow_project=False) -> Scratch:
|
||||
create_ser.is_valid(raise_exception=True)
|
||||
data = create_ser.validated_data
|
||||
|
||||
platform: Optional[Platform] = None
|
||||
given_platform = data.get("platform")
|
||||
if given_platform:
|
||||
platform = platforms.from_id(given_platform)
|
||||
else:
|
||||
platform = None
|
||||
|
||||
compiler = compilers.from_id(data["compiler"])
|
||||
project = data.get("project")
|
||||
@@ -212,6 +213,10 @@ def create_scratch(data: Dict[str, Any], allow_project=False) -> Scratch:
|
||||
compiler_flags = data.get("compiler_flags", "")
|
||||
compiler_flags = CompilerWrapper.filter_compiler_flags(compiler_flags)
|
||||
|
||||
preset = data.get("preset", "")
|
||||
if preset and not compilers.preset_from_name(preset):
|
||||
raise serializers.ValidationError("Unknown preset:" + preset)
|
||||
|
||||
name = data.get("name", diff_label) or "Untitled"
|
||||
|
||||
if allow_project and (project or rom_address):
|
||||
@@ -241,6 +246,7 @@ def create_scratch(data: Dict[str, Any], allow_project=False) -> Scratch:
|
||||
"name": name,
|
||||
"compiler": compiler.id,
|
||||
"compiler_flags": compiler_flags,
|
||||
"preset": preset,
|
||||
"context": context,
|
||||
"diff_label": diff_label,
|
||||
"source_code": source_code,
|
||||
|
||||
331
backend/poetry.lock
generated
331
backend/poetry.lock
generated
@@ -36,7 +36,7 @@ tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>
|
||||
|
||||
[[package]]
|
||||
name = "black"
|
||||
version = "22.1.0"
|
||||
version = "22.3.0"
|
||||
description = "The uncompromising code formatter."
|
||||
category = "dev"
|
||||
optional = false
|
||||
@@ -47,7 +47,7 @@ click = ">=8.0.0"
|
||||
mypy-extensions = ">=0.4.3"
|
||||
pathspec = ">=0.9.0"
|
||||
platformdirs = ">=2"
|
||||
tomli = ">=1.1.0"
|
||||
tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""}
|
||||
typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""}
|
||||
|
||||
[package.extras]
|
||||
@@ -88,11 +88,11 @@ unicode_backport = ["unicodedata2"]
|
||||
|
||||
[[package]]
|
||||
name = "click"
|
||||
version = "8.0.4"
|
||||
version = "8.1.0"
|
||||
description = "Composable command line interface toolkit"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
colorama = {version = "*", markers = "platform_system == \"Windows\""}
|
||||
@@ -109,7 +109,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
|
||||
name = "coreapi"
|
||||
version = "2.3.3"
|
||||
description = "Python client library for Core API."
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
@@ -123,7 +123,7 @@ uritemplate = "*"
|
||||
name = "coreschema"
|
||||
version = "0.0.4"
|
||||
description = "Core Schema."
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
@@ -132,7 +132,7 @@ jinja2 = "*"
|
||||
|
||||
[[package]]
|
||||
name = "cryptography"
|
||||
version = "36.0.1"
|
||||
version = "36.0.2"
|
||||
description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers."
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -230,7 +230,7 @@ Django = ">=2.2"
|
||||
name = "django-stubs"
|
||||
version = "1.9.0"
|
||||
description = "Mypy stubs for Django"
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
@@ -245,9 +245,9 @@ typing-extensions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "django-stubs-ext"
|
||||
version = "0.3.1"
|
||||
version = "0.4.0"
|
||||
description = "Monkey-patching and extensions for django-stubs"
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
@@ -271,7 +271,7 @@ pytz = "*"
|
||||
name = "djangorestframework-stubs"
|
||||
version = "1.4.0"
|
||||
description = "PEP-484 stubs for django-rest-framework"
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
@@ -305,17 +305,17 @@ python-versions = ">=3.5"
|
||||
name = "itypes"
|
||||
version = "1.2.0"
|
||||
description = "Simple immutable types for python."
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "jinja2"
|
||||
version = "3.0.3"
|
||||
version = "3.1.1"
|
||||
description = "A very fast and expressive template engine."
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
python-versions = ">=3.7"
|
||||
|
||||
[package.dependencies]
|
||||
MarkupSafe = ">=2.0"
|
||||
@@ -352,9 +352,9 @@ dev = ["black (==21.10b0)", "coverage (>=4.5.4)", "fixit (==0.1.1)", "flake8 (>=
|
||||
|
||||
[[package]]
|
||||
name = "markupsafe"
|
||||
version = "2.1.0"
|
||||
version = "2.1.1"
|
||||
description = "Safely add untrusted strings to HTML/XML markup."
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.7"
|
||||
|
||||
@@ -486,7 +486,7 @@ python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "pytz"
|
||||
version = "2021.3"
|
||||
version = "2022.1"
|
||||
description = "World timezone definitions, modern and historical"
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -543,7 +543,7 @@ python-versions = ">=3.5"
|
||||
|
||||
[[package]]
|
||||
name = "stdlibs"
|
||||
version = "2022.2.2"
|
||||
version = "2022.3.16"
|
||||
description = "List of packages in the stdlib"
|
||||
category = "dev"
|
||||
optional = false
|
||||
@@ -561,7 +561,7 @@ python-versions = "*"
|
||||
name = "toml"
|
||||
version = "0.10.2"
|
||||
description = "Python Library for Tom's Obvious, Minimal Language"
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
|
||||
|
||||
@@ -575,7 +575,7 @@ python-versions = ">=3.7"
|
||||
|
||||
[[package]]
|
||||
name = "tqdm"
|
||||
version = "4.63.0"
|
||||
version = "4.63.1"
|
||||
description = "Fast, Extensible Progress Meter"
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -602,25 +602,25 @@ pathspec = ">=0.8.1"
|
||||
|
||||
[[package]]
|
||||
name = "types-pytz"
|
||||
version = "2021.3.5"
|
||||
version = "2021.3.6"
|
||||
description = "Typing stubs for pytz"
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "types-pyyaml"
|
||||
version = "6.0.4"
|
||||
version = "6.0.5"
|
||||
description = "Typing stubs for PyYAML"
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
[[package]]
|
||||
name = "types-requests"
|
||||
version = "2.27.11"
|
||||
version = "2.27.15"
|
||||
description = "Typing stubs for requests"
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
@@ -629,9 +629,9 @@ types-urllib3 = "<1.27"
|
||||
|
||||
[[package]]
|
||||
name = "types-urllib3"
|
||||
version = "1.26.10"
|
||||
version = "1.26.11"
|
||||
description = "Typing stubs for urllib3"
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = "*"
|
||||
|
||||
@@ -657,7 +657,7 @@ typing-extensions = ">=3.7.4"
|
||||
|
||||
[[package]]
|
||||
name = "tzdata"
|
||||
version = "2021.5"
|
||||
version = "2022.1"
|
||||
description = "Provider of IANA time zone data"
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -667,20 +667,20 @@ python-versions = ">=2"
|
||||
name = "uritemplate"
|
||||
version = "4.1.1"
|
||||
description = "Implementation of RFC 6570 URI Templates"
|
||||
category = "main"
|
||||
category = "dev"
|
||||
optional = false
|
||||
python-versions = ">=3.6"
|
||||
|
||||
[[package]]
|
||||
name = "urllib3"
|
||||
version = "1.26.8"
|
||||
version = "1.26.9"
|
||||
description = "HTTP library with thread-safe connection pooling, file post, and more."
|
||||
category = "main"
|
||||
optional = false
|
||||
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
|
||||
|
||||
[package.extras]
|
||||
brotli = ["brotlipy (>=0.6.0)"]
|
||||
brotli = ["brotlicffi (>=0.8.0)", "brotli (>=1.0.9)", "brotlipy (>=0.6.0)"]
|
||||
secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
|
||||
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
|
||||
|
||||
@@ -703,7 +703,7 @@ trailrunner = ">=1.0"
|
||||
|
||||
[[package]]
|
||||
name = "watchdog"
|
||||
version = "2.1.6"
|
||||
version = "2.1.7"
|
||||
description = "Filesystem events monitoring"
|
||||
category = "main"
|
||||
optional = false
|
||||
@@ -723,7 +723,7 @@ python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7"
|
||||
[metadata]
|
||||
lock-version = "1.1"
|
||||
python-versions = "^3.9"
|
||||
content-hash = "11914ccd6e68c044ea3ca98b2b83eb1278f67e5162f7913f8a0affc8d86a5b7a"
|
||||
content-hash = "abb7ad3c74a9c8d3add60d978689e979d215a5129ed30e7afc59224250cfcf1d"
|
||||
|
||||
[metadata.files]
|
||||
ansiwrap = [
|
||||
@@ -739,29 +739,29 @@ attrs = [
|
||||
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
|
||||
]
|
||||
black = [
|
||||
{file = "black-22.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6"},
|
||||
{file = "black-22.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866"},
|
||||
{file = "black-22.1.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e21e1f1efa65a50e3960edd068b6ae6d64ad6235bd8bfea116a03b21836af71"},
|
||||
{file = "black-22.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f69158a7d120fd641d1fa9a921d898e20d52e44a74a6fbbcc570a62a6bc8ab"},
|
||||
{file = "black-22.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:228b5ae2c8e3d6227e4bde5920d2fc66cc3400fde7bcc74f480cb07ef0b570d5"},
|
||||
{file = "black-22.1.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b1a5ed73ab4c482208d20434f700d514f66ffe2840f63a6252ecc43a9bc77e8a"},
|
||||
{file = "black-22.1.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:35944b7100af4a985abfcaa860b06af15590deb1f392f06c8683b4381e8eeaf0"},
|
||||
{file = "black-22.1.0-cp36-cp36m-win_amd64.whl", hash = "sha256:7835fee5238fc0a0baf6c9268fb816b5f5cd9b8793423a75e8cd663c48d073ba"},
|
||||
{file = "black-22.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:dae63f2dbf82882fa3b2a3c49c32bffe144970a573cd68d247af6560fc493ae1"},
|
||||
{file = "black-22.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fa1db02410b1924b6749c245ab38d30621564e658297484952f3d8a39fce7e8"},
|
||||
{file = "black-22.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:c8226f50b8c34a14608b848dc23a46e5d08397d009446353dad45e04af0c8e28"},
|
||||
{file = "black-22.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2d6f331c02f0f40aa51a22e479c8209d37fcd520c77721c034517d44eecf5912"},
|
||||
{file = "black-22.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:742ce9af3086e5bd07e58c8feb09dbb2b047b7f566eb5f5bc63fd455814979f3"},
|
||||
{file = "black-22.1.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:fdb8754b453fb15fad3f72cd9cad3e16776f0964d67cf30ebcbf10327a3777a3"},
|
||||
{file = "black-22.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f5660feab44c2e3cb24b2419b998846cbb01c23c7fe645fee45087efa3da2d61"},
|
||||
{file = "black-22.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:6f2f01381f91c1efb1451998bd65a129b3ed6f64f79663a55fe0e9b74a5f81fd"},
|
||||
{file = "black-22.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:efbadd9b52c060a8fc3b9658744091cb33c31f830b3f074422ed27bad2b18e8f"},
|
||||
{file = "black-22.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8871fcb4b447206904932b54b567923e5be802b9b19b744fdff092bd2f3118d0"},
|
||||
{file = "black-22.1.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ccad888050f5393f0d6029deea2a33e5ae371fd182a697313bdbd835d3edaf9c"},
|
||||
{file = "black-22.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:07e5c049442d7ca1a2fc273c79d1aecbbf1bc858f62e8184abe1ad175c4f7cc2"},
|
||||
{file = "black-22.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:373922fc66676133ddc3e754e4509196a8c392fec3f5ca4486673e685a421321"},
|
||||
{file = "black-22.1.0-py3-none-any.whl", hash = "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d"},
|
||||
{file = "black-22.1.0.tar.gz", hash = "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5"},
|
||||
{file = "black-22.3.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:2497f9c2386572e28921fa8bec7be3e51de6801f7459dffd6e62492531c47e09"},
|
||||
{file = "black-22.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5795a0375eb87bfe902e80e0c8cfaedf8af4d49694d69161e5bd3206c18618bb"},
|
||||
{file = "black-22.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e3556168e2e5c49629f7b0f377070240bd5511e45e25a4497bb0073d9dda776a"},
|
||||
{file = "black-22.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:67c8301ec94e3bcc8906740fe071391bce40a862b7be0b86fb5382beefecd968"},
|
||||
{file = "black-22.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:fd57160949179ec517d32ac2ac898b5f20d68ed1a9c977346efbac9c2f1e779d"},
|
||||
{file = "black-22.3.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:cc1e1de68c8e5444e8f94c3670bb48a2beef0e91dddfd4fcc29595ebd90bb9ce"},
|
||||
{file = "black-22.3.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6d2fc92002d44746d3e7db7cf9313cf4452f43e9ea77a2c939defce3b10b5c82"},
|
||||
{file = "black-22.3.0-cp36-cp36m-win_amd64.whl", hash = "sha256:a6342964b43a99dbc72f72812bf88cad8f0217ae9acb47c0d4f141a6416d2d7b"},
|
||||
{file = "black-22.3.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:328efc0cc70ccb23429d6be184a15ce613f676bdfc85e5fe8ea2a9354b4e9015"},
|
||||
{file = "black-22.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06f9d8846f2340dfac80ceb20200ea5d1b3f181dd0556b47af4e8e0b24fa0a6b"},
|
||||
{file = "black-22.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:ad4efa5fad66b903b4a5f96d91461d90b9507a812b3c5de657d544215bb7877a"},
|
||||
{file = "black-22.3.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8477ec6bbfe0312c128e74644ac8a02ca06bcdb8982d4ee06f209be28cdf163"},
|
||||
{file = "black-22.3.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:637a4014c63fbf42a692d22b55d8ad6968a946b4a6ebc385c5505d9625b6a464"},
|
||||
{file = "black-22.3.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:863714200ada56cbc366dc9ae5291ceb936573155f8bf8e9de92aef51f3ad0f0"},
|
||||
{file = "black-22.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10dbe6e6d2988049b4655b2b739f98785a884d4d6b85bc35133a8fb9a2233176"},
|
||||
{file = "black-22.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:cee3e11161dde1b2a33a904b850b0899e0424cc331b7295f2a9698e79f9a69a0"},
|
||||
{file = "black-22.3.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:5891ef8abc06576985de8fa88e95ab70641de6c1fca97e2a15820a9b69e51b20"},
|
||||
{file = "black-22.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:30d78ba6bf080eeaf0b7b875d924b15cd46fec5fd044ddfbad38c8ea9171043a"},
|
||||
{file = "black-22.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:ee8f1f7228cce7dffc2b464f07ce769f478968bfb3dd1254a4c2eeed84928aad"},
|
||||
{file = "black-22.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ee227b696ca60dd1c507be80a6bc849a5a6ab57ac7352aad1ffec9e8b805f21"},
|
||||
{file = "black-22.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:9b542ced1ec0ceeff5b37d69838106a6348e60db7b8fdd245294dc1d26136265"},
|
||||
{file = "black-22.3.0-py3-none-any.whl", hash = "sha256:bc58025940a896d7e5356952228b68f793cf5fcb342be703c3a2669a1488cb72"},
|
||||
{file = "black-22.3.0.tar.gz", hash = "sha256:35020b8886c022ced9282b51b5a875b6d1ab0c387b31a065b84db7c33085ca79"},
|
||||
]
|
||||
certifi = [
|
||||
{file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
|
||||
@@ -824,8 +824,8 @@ charset-normalizer = [
|
||||
{file = "charset_normalizer-2.0.12-py3-none-any.whl", hash = "sha256:6881edbebdb17b39b4eaaa821b438bf6eddffb4468cf344f09f89def34a8b1df"},
|
||||
]
|
||||
click = [
|
||||
{file = "click-8.0.4-py3-none-any.whl", hash = "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1"},
|
||||
{file = "click-8.0.4.tar.gz", hash = "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb"},
|
||||
{file = "click-8.1.0-py3-none-any.whl", hash = "sha256:19a4baa64da924c5e0cd889aba8e947f280309f1a2ce0947a3e3a7bcb7cc72d6"},
|
||||
{file = "click-8.1.0.tar.gz", hash = "sha256:977c213473c7665d3aa092b41ff12063227751c41d7b17165013e10069cc5cd2"},
|
||||
]
|
||||
colorama = [
|
||||
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
|
||||
@@ -840,26 +840,26 @@ coreschema = [
|
||||
{file = "coreschema-0.0.4.tar.gz", hash = "sha256:9503506007d482ab0867ba14724b93c18a33b22b6d19fb419ef2d239dd4a1607"},
|
||||
]
|
||||
cryptography = [
|
||||
{file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:73bc2d3f2444bcfeac67dd130ff2ea598ea5f20b40e36d19821b4df8c9c5037b"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:2d87cdcb378d3cfed944dac30596da1968f88fb96d7fc34fdae30a99054b2e31"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:74d6c7e80609c0f4c2434b97b80c7f8fdfaa072ca4baab7e239a15d6d70ed73a"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:6c0c021f35b421ebf5976abf2daacc47e235f8b6082d3396a2fe3ccd537ab173"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d59a9d55027a8b88fd9fd2826c4392bd487d74bf628bb9d39beecc62a644c12"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a817b961b46894c5ca8a66b599c745b9a3d9f822725221f0e0fe49dc043a3a3"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:94ae132f0e40fe48f310bba63f477f14a43116f05ddb69d6fa31e93f05848ae2"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:7be0eec337359c155df191d6ae00a5e8bbb63933883f4f5dffc439dac5348c3f"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:e0344c14c9cb89e76eb6a060e67980c9e35b3f36691e15e1b7a9e58a0a6c6dc3"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-win32.whl", hash = "sha256:4caa4b893d8fad33cf1964d3e51842cd78ba87401ab1d2e44556826df849a8ca"},
|
||||
{file = "cryptography-36.0.1-cp36-abi3-win_amd64.whl", hash = "sha256:391432971a66cfaf94b21c24ab465a4cc3e8bf4a939c1ca5c3e3a6e0abebdbcf"},
|
||||
{file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:bb5829d027ff82aa872d76158919045a7c1e91fbf241aec32cb07956e9ebd3c9"},
|
||||
{file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ebc15b1c22e55c4d5566e3ca4db8689470a0ca2babef8e3a9ee057a8b82ce4b1"},
|
||||
{file = "cryptography-36.0.1-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:596f3cd67e1b950bc372c33f1a28a0692080625592ea6392987dba7f09f17a94"},
|
||||
{file = "cryptography-36.0.1-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:30ee1eb3ebe1644d1c3f183d115a8c04e4e603ed6ce8e394ed39eea4a98469ac"},
|
||||
{file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ec63da4e7e4a5f924b90af42eddf20b698a70e58d86a72d943857c4c6045b3ee"},
|
||||
{file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca238ceb7ba0bdf6ce88c1b74a87bffcee5afbfa1e41e173b1ceb095b39add46"},
|
||||
{file = "cryptography-36.0.1-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:ca28641954f767f9822c24e927ad894d45d5a1e501767599647259cbf030b903"},
|
||||
{file = "cryptography-36.0.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:39bdf8e70eee6b1c7b289ec6e5d84d49a6bfa11f8b8646b5b3dfe41219153316"},
|
||||
{file = "cryptography-36.0.1.tar.gz", hash = "sha256:53e5c1dc3d7a953de055d77bef2ff607ceef7a2aac0353b5d630ab67f7423638"},
|
||||
{file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_universal2.whl", hash = "sha256:4e2dddd38a5ba733be6a025a1475a9f45e4e41139d1321f412c6b360b19070b6"},
|
||||
{file = "cryptography-36.0.2-cp36-abi3-macosx_10_10_x86_64.whl", hash = "sha256:4881d09298cd0b669bb15b9cfe6166f16fc1277b4ed0d04a22f3d6430cb30f1d"},
|
||||
{file = "cryptography-36.0.2-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ea634401ca02367c1567f012317502ef3437522e2fc44a3ea1844de028fa4b84"},
|
||||
{file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:7be666cc4599b415f320839e36367b273db8501127b38316f3b9f22f17a0b815"},
|
||||
{file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8241cac0aae90b82d6b5c443b853723bcc66963970c67e56e71a2609dc4b5eaf"},
|
||||
{file = "cryptography-36.0.2-cp36-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7b2d54e787a884ffc6e187262823b6feb06c338084bbe80d45166a1cb1c6c5bf"},
|
||||
{file = "cryptography-36.0.2-cp36-abi3-manylinux_2_24_x86_64.whl", hash = "sha256:c2c5250ff0d36fd58550252f54915776940e4e866f38f3a7866d92b32a654b86"},
|
||||
{file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_aarch64.whl", hash = "sha256:ec6597aa85ce03f3e507566b8bcdf9da2227ec86c4266bd5e6ab4d9e0cc8dab2"},
|
||||
{file = "cryptography-36.0.2-cp36-abi3-musllinux_1_1_x86_64.whl", hash = "sha256:ca9f686517ec2c4a4ce930207f75c00bf03d94e5063cbc00a1dc42531511b7eb"},
|
||||
{file = "cryptography-36.0.2-cp36-abi3-win32.whl", hash = "sha256:f64b232348ee82f13aac22856515ce0195837f6968aeaa94a3d0353ea2ec06a6"},
|
||||
{file = "cryptography-36.0.2-cp36-abi3-win_amd64.whl", hash = "sha256:53e0285b49fd0ab6e604f4c5d9c5ddd98de77018542e88366923f152dbeb3c29"},
|
||||
{file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:32db5cc49c73f39aac27574522cecd0a4bb7384e71198bc65a0d23f901e89bb7"},
|
||||
{file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d2b3d199647468d410994dbeb8cec5816fb74feb9368aedf300af709ef507e3e"},
|
||||
{file = "cryptography-36.0.2-pp37-pypy37_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:da73d095f8590ad437cd5e9faf6628a218aa7c387e1fdf67b888b47ba56a17f0"},
|
||||
{file = "cryptography-36.0.2-pp38-pypy38_pp73-macosx_10_10_x86_64.whl", hash = "sha256:0a3bf09bb0b7a2c93ce7b98cb107e9170a90c51a0162a20af1c61c765b90e60b"},
|
||||
{file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8897b7b7ec077c819187a123174b645eb680c13df68354ed99f9b40a50898f77"},
|
||||
{file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82740818f2f240a5da8dfb8943b360e4f24022b093207160c77cadade47d7c85"},
|
||||
{file = "cryptography-36.0.2-pp38-pypy38_pp73-manylinux_2_24_x86_64.whl", hash = "sha256:1f64a62b3b75e4005df19d3b5235abd43fa6358d5516cfc43d87aeba8d08dd51"},
|
||||
{file = "cryptography-36.0.2-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:e167b6b710c7f7bc54e67ef593f8731e1f45aa35f8a8a7b72d6e42ec76afd4b3"},
|
||||
{file = "cryptography-36.0.2.tar.gz", hash = "sha256:70f8f4f7bb2ac9f340655cbac89d68c527af5bb4387522a8413e841e3e6628c9"},
|
||||
]
|
||||
cxxfilt = [
|
||||
{file = "cxxfilt-0.3.0-py2.py3-none-any.whl", hash = "sha256:774e85a8d0157775ed43276d89397d924b104135762d86b3a95f81f203094e07"},
|
||||
@@ -890,8 +890,8 @@ django-stubs = [
|
||||
{file = "django_stubs-1.9.0-py3-none-any.whl", hash = "sha256:59c9f81af64d214b1954eaf90f037778c8d2b9c2de946a3cda177fefcf588fbd"},
|
||||
]
|
||||
django-stubs-ext = [
|
||||
{file = "django-stubs-ext-0.3.1.tar.gz", hash = "sha256:783c198d7e39a41be0b90fd843fa2770243a642922af679be4b19e03b82c8c28"},
|
||||
{file = "django_stubs_ext-0.3.1-py3-none-any.whl", hash = "sha256:a51a3e9e844d4e1cacaaedbb33bf3def78a3956eed5d9575a640bd97ccd99cec"},
|
||||
{file = "django-stubs-ext-0.4.0.tar.gz", hash = "sha256:3104c4748c34bd741c310a3e6af90dffba46e41bccbe243896e38a708262876b"},
|
||||
{file = "django_stubs_ext-0.4.0-py3-none-any.whl", hash = "sha256:901fc77b6338ea29fa381300ff598dd57d461a4882b756404e2aa7724f76fd7d"},
|
||||
]
|
||||
djangorestframework = [
|
||||
{file = "djangorestframework-3.13.1-py3-none-any.whl", hash = "sha256:24c4bf58ed7e85d1fe4ba250ab2da926d263cd57d64b03e8dcef0ac683f8b1aa"},
|
||||
@@ -914,8 +914,8 @@ itypes = [
|
||||
{file = "itypes-1.2.0.tar.gz", hash = "sha256:af886f129dea4a2a1e3d36595a2d139589e4dd287f5cab0b40e799ee81570ff1"},
|
||||
]
|
||||
jinja2 = [
|
||||
{file = "Jinja2-3.0.3-py3-none-any.whl", hash = "sha256:077ce6014f7b40d03b47d1f1ca4b0fc8328a692bd284016f806ed0eaca390ad8"},
|
||||
{file = "Jinja2-3.0.3.tar.gz", hash = "sha256:611bb273cd68f3b993fabdc4064fc858c5b47a973cb5aa7999ec1ba405c87cd7"},
|
||||
{file = "Jinja2-3.1.1-py3-none-any.whl", hash = "sha256:539835f51a74a69f41b848a9645dbdc35b4f20a3b601e2d9a7e22947b15ff119"},
|
||||
{file = "Jinja2-3.1.1.tar.gz", hash = "sha256:640bed4bb501cbd17194b3cace1dc2126f5b619cf068a726b98192a0fde74ae9"},
|
||||
]
|
||||
jwt = [
|
||||
{file = "jwt-1.3.1-py3-none-any.whl", hash = "sha256:61c9170f92e736b530655e75374681d4fcca9cfa8763ab42be57353b2b203494"},
|
||||
@@ -952,46 +952,46 @@ libcst = [
|
||||
{file = "libcst-0.4.1.tar.gz", hash = "sha256:961ab38c0ef318c384a287f1e4f877bb61ce93945f352b14b5dbbe7a317882b1"},
|
||||
]
|
||||
markupsafe = [
|
||||
{file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:3028252424c72b2602a323f70fbf50aa80a5d3aa616ea6add4ba21ae9cc9da4c"},
|
||||
{file = "MarkupSafe-2.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:290b02bab3c9e216da57c1d11d2ba73a9f73a614bbdcc027d299a60cdfabb11a"},
|
||||
{file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e104c0c2b4cd765b4e83909cde7ec61a1e313f8a75775897db321450e928cce"},
|
||||
{file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:24c3be29abb6b34052fd26fc7a8e0a49b1ee9d282e3665e8ad09a0a68faee5b3"},
|
||||
{file = "MarkupSafe-2.1.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:204730fd5fe2fe3b1e9ccadb2bd18ba8712b111dcabce185af0b3b5285a7c989"},
|
||||
{file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d3b64c65328cb4cd252c94f83e66e3d7acf8891e60ebf588d7b493a55a1dbf26"},
|
||||
{file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:96de1932237abe0a13ba68b63e94113678c379dca45afa040a17b6e1ad7ed076"},
|
||||
{file = "MarkupSafe-2.1.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:75bb36f134883fdbe13d8e63b8675f5f12b80bb6627f7714c7d6c5becf22719f"},
|
||||
{file = "MarkupSafe-2.1.0-cp310-cp310-win32.whl", hash = "sha256:4056f752015dfa9828dce3140dbadd543b555afb3252507348c493def166d454"},
|
||||
{file = "MarkupSafe-2.1.0-cp310-cp310-win_amd64.whl", hash = "sha256:d4e702eea4a2903441f2735799d217f4ac1b55f7d8ad96ab7d4e25417cb0827c"},
|
||||
{file = "MarkupSafe-2.1.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f0eddfcabd6936558ec020130f932d479930581171368fd728efcfb6ef0dd357"},
|
||||
{file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5ddea4c352a488b5e1069069f2f501006b1a4362cb906bee9a193ef1245a7a61"},
|
||||
{file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:09c86c9643cceb1d87ca08cdc30160d1b7ab49a8a21564868921959bd16441b8"},
|
||||
{file = "MarkupSafe-2.1.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a0a0abef2ca47b33fb615b491ce31b055ef2430de52c5b3fb19a4042dbc5cadb"},
|
||||
{file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:736895a020e31b428b3382a7887bfea96102c529530299f426bf2e636aacec9e"},
|
||||
{file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:679cbb78914ab212c49c67ba2c7396dc599a8479de51b9a87b174700abd9ea49"},
|
||||
{file = "MarkupSafe-2.1.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:84ad5e29bf8bab3ad70fd707d3c05524862bddc54dc040982b0dbcff36481de7"},
|
||||
{file = "MarkupSafe-2.1.0-cp37-cp37m-win32.whl", hash = "sha256:8da5924cb1f9064589767b0f3fc39d03e3d0fb5aa29e0cb21d43106519bd624a"},
|
||||
{file = "MarkupSafe-2.1.0-cp37-cp37m-win_amd64.whl", hash = "sha256:454ffc1cbb75227d15667c09f164a0099159da0c1f3d2636aa648f12675491ad"},
|
||||
{file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:142119fb14a1ef6d758912b25c4e803c3ff66920635c44078666fe7cc3f8f759"},
|
||||
{file = "MarkupSafe-2.1.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:b2a5a856019d2833c56a3dcac1b80fe795c95f401818ea963594b345929dffa7"},
|
||||
{file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1d1fb9b2eec3c9714dd936860850300b51dbaa37404209c8d4cb66547884b7ed"},
|
||||
{file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:62c0285e91414f5c8f621a17b69fc0088394ccdaa961ef469e833dbff64bd5ea"},
|
||||
{file = "MarkupSafe-2.1.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fc3150f85e2dbcf99e65238c842d1cfe69d3e7649b19864c1cc043213d9cd730"},
|
||||
{file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f02cf7221d5cd915d7fa58ab64f7ee6dd0f6cddbb48683debf5d04ae9b1c2cc1"},
|
||||
{file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:d5653619b3eb5cbd35bfba3c12d575db2a74d15e0e1c08bf1db788069d410ce8"},
|
||||
{file = "MarkupSafe-2.1.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7d2f5d97fcbd004c03df8d8fe2b973fe2b14e7bfeb2cfa012eaa8759ce9a762f"},
|
||||
{file = "MarkupSafe-2.1.0-cp38-cp38-win32.whl", hash = "sha256:3cace1837bc84e63b3fd2dfce37f08f8c18aeb81ef5cf6bb9b51f625cb4e6cd8"},
|
||||
{file = "MarkupSafe-2.1.0-cp38-cp38-win_amd64.whl", hash = "sha256:fabbe18087c3d33c5824cb145ffca52eccd053061df1d79d4b66dafa5ad2a5ea"},
|
||||
{file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:023af8c54fe63530545f70dd2a2a7eed18d07a9a77b94e8bf1e2ff7f252db9a3"},
|
||||
{file = "MarkupSafe-2.1.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:d66624f04de4af8bbf1c7f21cc06649c1c69a7f84109179add573ce35e46d448"},
|
||||
{file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c532d5ab79be0199fa2658e24a02fce8542df196e60665dd322409a03db6a52c"},
|
||||
{file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e67ec74fada3841b8c5f4c4f197bea916025cb9aa3fe5abf7d52b655d042f956"},
|
||||
{file = "MarkupSafe-2.1.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:30c653fde75a6e5eb814d2a0a89378f83d1d3f502ab710904ee585c38888816c"},
|
||||
{file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:961eb86e5be7d0973789f30ebcf6caab60b844203f4396ece27310295a6082c7"},
|
||||
{file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:598b65d74615c021423bd45c2bc5e9b59539c875a9bdb7e5f2a6b92dfcfc268d"},
|
||||
{file = "MarkupSafe-2.1.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:599941da468f2cf22bf90a84f6e2a65524e87be2fce844f96f2dd9a6c9d1e635"},
|
||||
{file = "MarkupSafe-2.1.0-cp39-cp39-win32.whl", hash = "sha256:e6f7f3f41faffaea6596da86ecc2389672fa949bd035251eab26dc6697451d05"},
|
||||
{file = "MarkupSafe-2.1.0-cp39-cp39-win_amd64.whl", hash = "sha256:b8811d48078d1cf2a6863dafb896e68406c5f513048451cd2ded0473133473c7"},
|
||||
{file = "MarkupSafe-2.1.0.tar.gz", hash = "sha256:80beaf63ddfbc64a0452b841d8036ca0611e049650e20afcb882f5d3c266d65f"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-win32.whl", hash = "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6"},
|
||||
{file = "MarkupSafe-2.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-win32.whl", hash = "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff"},
|
||||
{file = "MarkupSafe-2.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-win32.whl", hash = "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1"},
|
||||
{file = "MarkupSafe-2.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-win32.whl", hash = "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c"},
|
||||
{file = "MarkupSafe-2.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247"},
|
||||
{file = "MarkupSafe-2.1.1.tar.gz", hash = "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b"},
|
||||
]
|
||||
moreorless = [
|
||||
{file = "moreorless-0.4.0-py2.py3-none-any.whl", hash = "sha256:17f1fbef60fd21c84ee085a929fe3acefcaddca30df5dd09c024e9939a9e6a00"},
|
||||
@@ -1117,8 +1117,8 @@ python-levenshtein = [
|
||||
{file = "python-Levenshtein-0.12.2.tar.gz", hash = "sha256:dc2395fbd148a1ab31090dd113c366695934b9e85fe5a4b2a032745efd0346f6"},
|
||||
]
|
||||
pytz = [
|
||||
{file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"},
|
||||
{file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"},
|
||||
{file = "pytz-2022.1-py2.py3-none-any.whl", hash = "sha256:e68985985296d9a66a881eb3193b0906246245294a881e7c8afe623866ac6a5c"},
|
||||
{file = "pytz-2022.1.tar.gz", hash = "sha256:1e760e2fe6a8163bc0b3d9a19c4f84342afa0a2affebfaa84b01b978a02ecaa7"},
|
||||
]
|
||||
pyyaml = [
|
||||
{file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
|
||||
@@ -1168,8 +1168,8 @@ sqlparse = [
|
||||
{file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"},
|
||||
]
|
||||
stdlibs = [
|
||||
{file = "stdlibs-2022.2.2-py3-none-any.whl", hash = "sha256:ab08f11e6e523a8b7b0bcba490bf2de2452e1b2f5c1299f070727ebf5f1f7914"},
|
||||
{file = "stdlibs-2022.2.2.tar.gz", hash = "sha256:dc03d51e0a29fb727ce89a22b71424fe8ab7856f186c9d1281457443ed8facc0"},
|
||||
{file = "stdlibs-2022.3.16-py3-none-any.whl", hash = "sha256:7e8c69fe8a6cb53be3d9b3987bd1db3bbc417e00bc294c2ec95cfbf7759ab5c2"},
|
||||
{file = "stdlibs-2022.3.16.tar.gz", hash = "sha256:975709d4bff578a0668921f301741bdf147e8376e0aee2e46e8b2c99a26ba949"},
|
||||
]
|
||||
textwrap3 = [
|
||||
{file = "textwrap3-0.9.2-py2.py3-none-any.whl", hash = "sha256:bf5f4c40faf2a9ff00a9e0791fed5da7415481054cef45bb4a3cfb1f69044ae0"},
|
||||
@@ -1184,28 +1184,28 @@ tomli = [
|
||||
{file = "tomli-2.0.1.tar.gz", hash = "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f"},
|
||||
]
|
||||
tqdm = [
|
||||
{file = "tqdm-4.63.0-py2.py3-none-any.whl", hash = "sha256:e643e071046f17139dea55b880dc9b33822ce21613b4a4f5ea57f202833dbc29"},
|
||||
{file = "tqdm-4.63.0.tar.gz", hash = "sha256:1d9835ede8e394bb8c9dcbffbca02d717217113adc679236873eeaac5bc0b3cd"},
|
||||
{file = "tqdm-4.63.1-py2.py3-none-any.whl", hash = "sha256:6461b009d6792008d0000e1b0c7ca50195ec78c0e808a3a6b668a56a3236c3a5"},
|
||||
{file = "tqdm-4.63.1.tar.gz", hash = "sha256:4230a49119a416c88cc47d0d2d32d5d90f1a282d5e497d49801950704e49863d"},
|
||||
]
|
||||
trailrunner = [
|
||||
{file = "trailrunner-1.1.3-py3-none-any.whl", hash = "sha256:7eea60167384329012a5b5464b4e97da68e8984fa52286094c08dbe9fce8901d"},
|
||||
{file = "trailrunner-1.1.3.tar.gz", hash = "sha256:ceb22d0c6e6439a72d4d94585aae2599107f6f9cf28c102cb7643a958f8b9bb7"},
|
||||
]
|
||||
types-pytz = [
|
||||
{file = "types-pytz-2021.3.5.tar.gz", hash = "sha256:fef8de238ee95135952229a2a23bfb87bd63d5a6c8598106a46cfcf48f069ea8"},
|
||||
{file = "types_pytz-2021.3.5-py3-none-any.whl", hash = "sha256:8831f689379ac9e2a62668157381379ed74b3702980e08e71f8673c179c4e3c7"},
|
||||
{file = "types-pytz-2021.3.6.tar.gz", hash = "sha256:74547fd90d8d8ab4f1eedf3a344a7d186d97486973895f81221a712e1e2cd993"},
|
||||
{file = "types_pytz-2021.3.6-py3-none-any.whl", hash = "sha256:6805c72d51118923c5bf98633c39593d5b464d2ab49a803440e2d7ab6b8920df"},
|
||||
]
|
||||
types-pyyaml = [
|
||||
{file = "types-PyYAML-6.0.4.tar.gz", hash = "sha256:6252f62d785e730e454dfa0c9f0fb99d8dae254c5c3c686903cf878ea27c04b7"},
|
||||
{file = "types_PyYAML-6.0.4-py3-none-any.whl", hash = "sha256:693b01c713464a6851f36ff41077f8adbc6e355eda929addfb4a97208aea9b4b"},
|
||||
{file = "types-PyYAML-6.0.5.tar.gz", hash = "sha256:464e050914f3d1d83a8c038e1cf46da5cb96b7cd02eaa096bcaa03675edd8a2e"},
|
||||
{file = "types_PyYAML-6.0.5-py3-none-any.whl", hash = "sha256:2fd21310870addfd51db621ad9f3b373f33ee3cbb81681d70ef578760bd22d35"},
|
||||
]
|
||||
types-requests = [
|
||||
{file = "types-requests-2.27.11.tar.gz", hash = "sha256:6a7ed24b21780af4a5b5e24c310b2cd885fb612df5fd95584d03d87e5f2a195a"},
|
||||
{file = "types_requests-2.27.11-py3-none-any.whl", hash = "sha256:506279bad570c7b4b19ac1f22e50146538befbe0c133b2cea66a9b04a533a859"},
|
||||
{file = "types-requests-2.27.15.tar.gz", hash = "sha256:2d371183c535208d2cc8fe7473d9b49c344c7077eb70302eb708638fb86086a8"},
|
||||
{file = "types_requests-2.27.15-py3-none-any.whl", hash = "sha256:77d09182a68e447e9e8b0ffc21abf54618b96f07689dffbb6a41cf0356542969"},
|
||||
]
|
||||
types-urllib3 = [
|
||||
{file = "types-urllib3-1.26.10.tar.gz", hash = "sha256:a26898f530e6c3f43f25b907f2b884486868ffd56a9faa94cbf9b3eb6e165d6a"},
|
||||
{file = "types_urllib3-1.26.10-py3-none-any.whl", hash = "sha256:d755278d5ecd7a7a6479a190e54230f241f1a99c19b81518b756b19dc69e518c"},
|
||||
{file = "types-urllib3-1.26.11.tar.gz", hash = "sha256:24d64e441168851eb05f1d022de18ae31558f5649c8f1117e384c2e85e31315b"},
|
||||
{file = "types_urllib3-1.26.11-py3-none-any.whl", hash = "sha256:bd0abc01e9fb963e4fddd561a56d21cc371b988d1245662195c90379077139cd"},
|
||||
]
|
||||
typing-extensions = [
|
||||
{file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"},
|
||||
@@ -1217,45 +1217,46 @@ typing-inspect = [
|
||||
{file = "typing_inspect-0.7.1.tar.gz", hash = "sha256:047d4097d9b17f46531bf6f014356111a1b6fb821a24fe7ac909853ca2a782aa"},
|
||||
]
|
||||
tzdata = [
|
||||
{file = "tzdata-2021.5-py2.py3-none-any.whl", hash = "sha256:3eee491e22ebfe1e5cfcc97a4137cd70f092ce59144d81f8924a844de05ba8f5"},
|
||||
{file = "tzdata-2021.5.tar.gz", hash = "sha256:68dbe41afd01b867894bbdfd54fa03f468cfa4f0086bfb4adcd8de8f24f3ee21"},
|
||||
{file = "tzdata-2022.1-py2.py3-none-any.whl", hash = "sha256:238e70234214138ed7b4e8a0fab0e5e13872edab3be586ab8198c407620e2ab9"},
|
||||
{file = "tzdata-2022.1.tar.gz", hash = "sha256:8b536a8ec63dc0751342b3984193a3118f8fca2afe25752bb9b7fffd398552d3"},
|
||||
]
|
||||
uritemplate = [
|
||||
{file = "uritemplate-4.1.1-py2.py3-none-any.whl", hash = "sha256:830c08b8d99bdd312ea4ead05994a38e8936266f84b9a7878232db50b044e02e"},
|
||||
{file = "uritemplate-4.1.1.tar.gz", hash = "sha256:4346edfc5c3b79f694bccd6d6099a322bbeb628dbf2cd86eea55a456ce5124f0"},
|
||||
]
|
||||
urllib3 = [
|
||||
{file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"},
|
||||
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
|
||||
{file = "urllib3-1.26.9-py2.py3-none-any.whl", hash = "sha256:44ece4d53fb1706f667c9bd1c648f5469a2ec925fcf3a776667042d645472c14"},
|
||||
{file = "urllib3-1.26.9.tar.gz", hash = "sha256:aabaf16477806a5e1dd19aa41f8c2b7950dd3c746362d7e3223dbe6de6ac448e"},
|
||||
]
|
||||
usort = [
|
||||
{file = "usort-1.0.2-py3-none-any.whl", hash = "sha256:0e7ee0702902d4d54fdd35cbc81f5590df2573db29e72aeb6eddaa9e9d01cef9"},
|
||||
{file = "usort-1.0.2.tar.gz", hash = "sha256:f0dbdfcf18b117323dff3a03df804957ba3b755c1069d2cf98bee133592bd369"},
|
||||
]
|
||||
watchdog = [
|
||||
{file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9693f35162dc6208d10b10ddf0458cc09ad70c30ba689d9206e02cd836ce28a3"},
|
||||
{file = "watchdog-2.1.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:aba5c812f8ee8a3ff3be51887ca2d55fb8e268439ed44110d3846e4229eb0e8b"},
|
||||
{file = "watchdog-2.1.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:4ae38bf8ba6f39d5b83f78661273216e7db5b00f08be7592062cb1fc8b8ba542"},
|
||||
{file = "watchdog-2.1.6-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:ad6f1796e37db2223d2a3f302f586f74c72c630b48a9872c1e7ae8e92e0ab669"},
|
||||
{file = "watchdog-2.1.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:922a69fa533cb0c793b483becaaa0845f655151e7256ec73630a1b2e9ebcb660"},
|
||||
{file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:b2fcf9402fde2672545b139694284dc3b665fd1be660d73eca6805197ef776a3"},
|
||||
{file = "watchdog-2.1.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:3386b367e950a11b0568062b70cc026c6f645428a698d33d39e013aaeda4cc04"},
|
||||
{file = "watchdog-2.1.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8f1c00aa35f504197561060ca4c21d3cc079ba29cf6dd2fe61024c70160c990b"},
|
||||
{file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b52b88021b9541a60531142b0a451baca08d28b74a723d0c99b13c8c8d48d604"},
|
||||
{file = "watchdog-2.1.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8047da932432aa32c515ec1447ea79ce578d0559362ca3605f8e9568f844e3c6"},
|
||||
{file = "watchdog-2.1.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e92c2d33858c8f560671b448205a268096e17870dcf60a9bb3ac7bfbafb7f5f9"},
|
||||
{file = "watchdog-2.1.6-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b7d336912853d7b77f9b2c24eeed6a5065d0a0cc0d3b6a5a45ad6d1d05fb8cd8"},
|
||||
{file = "watchdog-2.1.6-py3-none-manylinux2014_aarch64.whl", hash = "sha256:cca7741c0fcc765568350cb139e92b7f9f3c9a08c4f32591d18ab0a6ac9e71b6"},
|
||||
{file = "watchdog-2.1.6-py3-none-manylinux2014_armv7l.whl", hash = "sha256:25fb5240b195d17de949588628fdf93032ebf163524ef08933db0ea1f99bd685"},
|
||||
{file = "watchdog-2.1.6-py3-none-manylinux2014_i686.whl", hash = "sha256:be9be735f827820a06340dff2ddea1fb7234561fa5e6300a62fe7f54d40546a0"},
|
||||
{file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64.whl", hash = "sha256:d0d19fb2441947b58fbf91336638c2b9f4cc98e05e1045404d7a4cb7cddc7a65"},
|
||||
{file = "watchdog-2.1.6-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:3becdb380d8916c873ad512f1701f8a92ce79ec6978ffde92919fd18d41da7fb"},
|
||||
{file = "watchdog-2.1.6-py3-none-manylinux2014_s390x.whl", hash = "sha256:ae67501c95606072aafa865b6ed47343ac6484472a2f95490ba151f6347acfc2"},
|
||||
{file = "watchdog-2.1.6-py3-none-manylinux2014_x86_64.whl", hash = "sha256:e0f30db709c939cabf64a6dc5babb276e6d823fd84464ab916f9b9ba5623ca15"},
|
||||
{file = "watchdog-2.1.6-py3-none-win32.whl", hash = "sha256:e02794ac791662a5eafc6ffeaf9bcc149035a0e48eb0a9d40a8feb4622605a3d"},
|
||||
{file = "watchdog-2.1.6-py3-none-win_amd64.whl", hash = "sha256:bd9ba4f332cf57b2c1f698be0728c020399ef3040577cde2939f2e045b39c1e5"},
|
||||
{file = "watchdog-2.1.6-py3-none-win_ia64.whl", hash = "sha256:a0f1c7edf116a12f7245be06120b1852275f9506a7d90227648b250755a03923"},
|
||||
{file = "watchdog-2.1.6.tar.gz", hash = "sha256:a36e75df6c767cbf46f61a91c70b3ba71811dfa0aca4a324d9407a06a8b7a2e7"},
|
||||
{file = "watchdog-2.1.7-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:177bae28ca723bc00846466016d34f8c1d6a621383b6caca86745918d55c7383"},
|
||||
{file = "watchdog-2.1.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1d1cf7dfd747dec519486a98ef16097e6c480934ef115b16f18adb341df747a4"},
|
||||
{file = "watchdog-2.1.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:7f14ce6adea2af1bba495acdde0e510aecaeb13b33f7bd2f6324e551b26688ca"},
|
||||
{file = "watchdog-2.1.7-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:4d0e98ac2e8dd803a56f4e10438b33a2d40390a72750cff4939b4b274e7906fa"},
|
||||
{file = "watchdog-2.1.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:81982c7884aac75017a6ecc72f1a4fedbae04181a8665a34afce9539fc1b3fab"},
|
||||
{file = "watchdog-2.1.7-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:0b4a1fe6201c6e5a1926f5767b8664b45f0fcb429b62564a41f490ff1ce1dc7a"},
|
||||
{file = "watchdog-2.1.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:6e6ae29b72977f2e1ee3d0b760d7ee47896cb53e831cbeede3e64485e5633cc8"},
|
||||
{file = "watchdog-2.1.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:b9777664848160449e5b4260e0b7bc1ae0f6f4992a8b285db4ec1ef119ffa0e2"},
|
||||
{file = "watchdog-2.1.7-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:19b36d436578eb437e029c6b838e732ed08054956366f6dd11875434a62d2b99"},
|
||||
{file = "watchdog-2.1.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:b61acffaf5cd5d664af555c0850f9747cc5f2baf71e54bbac164c58398d6ca7b"},
|
||||
{file = "watchdog-2.1.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1e877c70245424b06c41ac258023ea4bd0c8e4ff15d7c1368f17cd0ae6e351dd"},
|
||||
{file = "watchdog-2.1.7-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d802d65262a560278cf1a65ef7cae4e2bc7ecfe19e5451349e4c67e23c9dc420"},
|
||||
{file = "watchdog-2.1.7-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:b3750ee5399e6e9c69eae8b125092b871ee9e2fcbd657a92747aea28f9056a5c"},
|
||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_aarch64.whl", hash = "sha256:ed6d9aad09a2a948572224663ab00f8975fae242aa540509737bb4507133fa2d"},
|
||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_armv7l.whl", hash = "sha256:b26e13e8008dcaea6a909e91d39b629a39635d1a8a7239dd35327c74f4388601"},
|
||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_i686.whl", hash = "sha256:0908bb50f6f7de54d5d31ec3da1654cb7287c6b87bce371954561e6de379d690"},
|
||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_ppc64.whl", hash = "sha256:bdcbf75580bf4b960fb659bbccd00123d83119619195f42d721e002c1621602f"},
|
||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:81a5861d0158a7e55fe149335fb2bbfa6f48cbcbd149b52dbe2cd9a544034bbd"},
|
||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_s390x.whl", hash = "sha256:03b43d583df0f18782a0431b6e9e9965c5b3f7cf8ec36a00b930def67942c385"},
|
||||
{file = "watchdog-2.1.7-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ae934e34c11aa8296c18f70bf66ed60e9870fcdb4cc19129a04ca83ab23e7055"},
|
||||
{file = "watchdog-2.1.7-py3-none-win32.whl", hash = "sha256:49639865e3db4be032a96695c98ac09eed39bbb43fe876bb217da8f8101689a6"},
|
||||
{file = "watchdog-2.1.7-py3-none-win_amd64.whl", hash = "sha256:340b875aecf4b0e6672076a6f05cfce6686935559bb6d34cebedee04126a9566"},
|
||||
{file = "watchdog-2.1.7-py3-none-win_ia64.whl", hash = "sha256:351e09b6d9374d5bcb947e6ac47a608ec25b9d70583e9db00b2fcdb97b00b572"},
|
||||
{file = "watchdog-2.1.7.tar.gz", hash = "sha256:3fd47815353be9c44eebc94cc28fe26b2b0c5bd889dafc4a5a7cbdf924143480"},
|
||||
]
|
||||
wrapt = [
|
||||
{file = "wrapt-1.14.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:5a9a1889cc01ed2ed5f34574c90745fab1dd06ec2eee663e8ebeefe363e8efd7"},
|
||||
|
||||
@@ -14,16 +14,12 @@ cxxfilt = "^0.3.0"
|
||||
django-cors-headers = "^3.11.0"
|
||||
django-environ = "^0.8.1"
|
||||
django-filter = "^21.1"
|
||||
django-stubs = "^1.9.0"
|
||||
django-stubs-ext = "^0.3.1"
|
||||
djangorestframework = "^3.13.1"
|
||||
djangorestframework-stubs = "^1.4.0"
|
||||
mypy = "^0.931"
|
||||
psycopg2-binary = "^2.9.3"
|
||||
pycparser = "^2.21"
|
||||
python-Levenshtein = "^0.12.2"
|
||||
responses = "^0.18.0"
|
||||
types-requests = "^2.27.10"
|
||||
watchdog = "^2.1.6"
|
||||
jwt = "^1.3.1"
|
||||
PyGithub = "^1.55"
|
||||
@@ -33,6 +29,10 @@ tqdm = "^4.62.3"
|
||||
[tool.poetry.dev-dependencies]
|
||||
black = "^22.1.0"
|
||||
usort = "^1.0.1"
|
||||
django-stubs-ext = "^0.4.0"
|
||||
django-stubs = "1.9.0"
|
||||
djangorestframework-stubs = "^1.4.0"
|
||||
types-requests = "^2.27.15"
|
||||
|
||||
[build-system]
|
||||
requires = ["poetry-core>=1.0.0"]
|
||||
|
||||
119
docs/CONTRIBUTING.md
Normal file
119
docs/CONTRIBUTING.md
Normal file
@@ -0,0 +1,119 @@
|
||||
# Contributing
|
||||
|
||||
## Directory structure
|
||||
```
|
||||
frontend/
|
||||
public/ ; Static files
|
||||
src/ ; React/Typescript sourcecode
|
||||
|
||||
backend/
|
||||
compilers/ ; Compiler binaries and configuration
|
||||
coreapp/ ; API Django app
|
||||
migrations/ ; Database migrations (generated by Django)
|
||||
decompme/ ; Main Django app
|
||||
|
||||
.env ; Default configuration
|
||||
.env.local ; Local configuration overrides (not checked-in)
|
||||
```
|
||||
|
||||
## Setup
|
||||
|
||||
See [DOCKER.md](DOCKER.md) for instructions on how to run the project in a Docker container. Otherwise, continue reading this guide.
|
||||
|
||||
Dependencies:
|
||||
- Python >=3.9
|
||||
- Node.js
|
||||
- [Yarn](https://yarnpkg.com/getting-started/install)
|
||||
- [Poetry](https://python-poetry.org/docs/master/#installing-with-the-official-installer)
|
||||
|
||||
---
|
||||
Create a file to hold environment variables:
|
||||
```shell
|
||||
touch .env.local
|
||||
```
|
||||
|
||||
### Backend
|
||||
```shell
|
||||
cd backend
|
||||
```
|
||||
|
||||
* Install Python dependencies with [poetry](https://python-poetry.org/docs/master/#installing-with-the-official-installer)
|
||||
```shell
|
||||
poetry install
|
||||
```
|
||||
|
||||
- Install compilers
|
||||
```shell
|
||||
poetry run python compilers/download.py
|
||||
```
|
||||
|
||||
- Set up the database
|
||||
```shell
|
||||
poetry run python manage.py migrate
|
||||
```
|
||||
|
||||
- Start the API server
|
||||
```shell
|
||||
poetry run python manage.py runserver
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Frontend
|
||||
```shell
|
||||
cd frontend
|
||||
```
|
||||
|
||||
- Install dependencies
|
||||
```shell
|
||||
yarn
|
||||
```
|
||||
|
||||
- Start the development webserver
|
||||
```shell
|
||||
yarn dev
|
||||
```
|
||||
|
||||
- Access the site via [http://localhost:8080](http://localhost:8080)
|
||||
|
||||
|
||||
### Optional steps
|
||||
- [Configure wine for Windows compiler on Linux](WINE.md)
|
||||
- [Set up GitHub authentication](GITHUB.md)
|
||||
- [Install nsjail to run the compiler sandbox](SANDBOX.md)
|
||||
- [Configure an nginx reverse proxy](NGINX.md)
|
||||
|
||||
|
||||
## Notes
|
||||
|
||||
### Updating the database
|
||||
|
||||
If you modify any database models (`models.py`), you'll need to run the following to update the database:
|
||||
```shell
|
||||
poetry run python manage.py makemigrations
|
||||
poetry run python manage.py migrate
|
||||
```
|
||||
|
||||
## Linting
|
||||
|
||||
- Check frontend
|
||||
```shell
|
||||
cd frontend
|
||||
yarn lint
|
||||
```
|
||||
|
||||
- Autofix frontend
|
||||
```shell
|
||||
cd frontend
|
||||
yarn lint --fix
|
||||
```
|
||||
|
||||
- Check backend
|
||||
```shell
|
||||
cd backend
|
||||
mypy
|
||||
```
|
||||
|
||||
### Storybook
|
||||
|
||||
Use `yarn storybook` to run a Storybook instance on [http://localhost:6006](http://localhost:6006). This is useful for testing UI components in isolation.
|
||||
9
docs/GITHUB.md
Normal file
9
docs/GITHUB.md
Normal file
@@ -0,0 +1,9 @@
|
||||
### GitHub authentication
|
||||
|
||||
- [Register a new OAuth application](https://github.com/settings/applications/new)
|
||||
- "Homepage URL" should be the URL you access the frontend on (e.g. `http://localhost:8080`)
|
||||
- "Authorization callback URL" should be the same as the homepage URL, but with `/login` appended
|
||||
|
||||
- Edit `.env.local`:
|
||||
- Set `GITHUB_CLIENT_ID` to the application client ID
|
||||
- Set `GITHUB_CLIENT_SECRET` to the application client secret (do **not** share this)
|
||||
76
docs/NGINX.md
Normal file
76
docs/NGINX.md
Normal file
@@ -0,0 +1,76 @@
|
||||
### Running inside an nginx proxy
|
||||
|
||||
Running decomp.me using nginx as a proxy better emulates the production environment and can avoid cookie-related issues.
|
||||
|
||||
- Install nginx
|
||||
|
||||
- Create an nginx site configuration (typically `/etc/nginx/sites-available/decomp.local`)
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
client_max_body_size 5M;
|
||||
|
||||
server_name decomp.local www.decomp.local;
|
||||
|
||||
location / {
|
||||
try_files $uri @proxy_frontend;
|
||||
}
|
||||
|
||||
location /api {
|
||||
try_files $uri @proxy_api;
|
||||
}
|
||||
location /admin {
|
||||
try_files $uri @proxy_api;
|
||||
}
|
||||
location /static {
|
||||
try_files $uri @proxy_api;
|
||||
}
|
||||
|
||||
location @proxy_api {
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header X-Url-Scheme $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://127.0.0.1:8000;
|
||||
}
|
||||
|
||||
location @proxy_frontend {
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header X-Url-Scheme $scheme;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_redirect off;
|
||||
proxy_pass http://127.0.0.1:8080;
|
||||
}
|
||||
|
||||
location /_next/webpack-hmr {
|
||||
proxy_pass http://127.0.0.1:8080/_next/webpack-hmr;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "upgrade";
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
- Enable the site
|
||||
```shell
|
||||
ln -s /etc/nginx/sites-available/decomp.local /etc/nginx/sites-enabled/decomp.local
|
||||
```
|
||||
|
||||
- Add the following lines to `/etc/hosts`:
|
||||
```
|
||||
127.0.0.1 decomp.local
|
||||
127.0.0.1 www.decomp.local
|
||||
```
|
||||
|
||||
- Edit `.env.local`:
|
||||
- Set `API_BASE=/api`
|
||||
- Set `ALLOWED_HOSTS=decomp.local`
|
||||
|
||||
- If you set up GitHub authentication, change the application URLs to `http://decomp.local` and `http://decomp.local/login`
|
||||
|
||||
- Restart nginx, the frontend, and the backend
|
||||
|
||||
- Access the site via [http://decomp.local](http://decomp.local)
|
||||
19
docs/SANDBOX.md
Normal file
19
docs/SANDBOX.md
Normal file
@@ -0,0 +1,19 @@
|
||||
### Sandbox jail
|
||||
|
||||
There is support for running subprocesses within [`nsjail`](https://github.com/google/nsjail).
|
||||
|
||||
This is controlled by the `SANDBOX` settings, and is disabled by default in the development `.env` but is enabled inside the `backend` Docker container.
|
||||
|
||||
To enable it locally outside of the Docker container:
|
||||
|
||||
- Build or install `nsjail` locally. Example instructions for Ubuntu:
|
||||
- `apt-get install autoconf bison flex gcc g++ git libprotobuf-dev libnl-route-3-dev libtool make pkg-config protobuf-compiler`
|
||||
- `git clone --recursive --branch=3.0 https://github.com/google/nsjail`
|
||||
- `cd nsjail && make`
|
||||
- Enable `unprivileged_userns_clone`
|
||||
- Temporary: `sudo sysctl -w kernel.unprivileged_userns_clone=1`
|
||||
- Permanent: `echo 'kernel.unprivileged_userns_clone=1' | sudo tee -a /etc/sysctl.d/00-local-userns.conf && sudo service procps restart`
|
||||
|
||||
- Edit `.env.local`:
|
||||
- Set `USE_SANDBOX_JAIL=on`
|
||||
- Set `SANDBOX_NSJAIL_BIN_PATH` to the absolute path of the `nsjail` binary built above
|
||||
10
docs/WINE.md
Normal file
10
docs/WINE.md
Normal file
@@ -0,0 +1,10 @@
|
||||
### Wine setup (for local development, running Windows compilers)
|
||||
- Create a wineprefix dir
|
||||
```shell
|
||||
WINEPREFIX=$HOME/.wine WINEARCH=win32 wineboot --init
|
||||
```
|
||||
|
||||
- Add the WINEPREFIX setting to your .local.env file in the root of the repo
|
||||
```shell
|
||||
echo "WINEPREFIX=$HOME/.wine" >> .local.env
|
||||
```
|
||||
@@ -22,7 +22,7 @@
|
||||
"no-else-return": "off",
|
||||
"no-trailing-spaces": "error",
|
||||
"no-multi-spaces": "error",
|
||||
"no-multiple-empty-lines": "error",
|
||||
"no-multiple-empty-lines": ["error", { "max": 1, "maxBOF": 0, "maxEOF": 0 }],
|
||||
"comma-dangle": "off",
|
||||
"@typescript-eslint/comma-dangle": ["error", "always-multiline"],
|
||||
"comma-spacing": "off",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const path = require("path")
|
||||
const { config } = require("dotenv")
|
||||
const { execSync } = require("child_process")
|
||||
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin")
|
||||
|
||||
for (const envFile of [".env.local", ".env"]) {
|
||||
config({ path: `../${envFile}` })
|
||||
@@ -54,11 +53,6 @@ module.exports = {
|
||||
include: path.resolve(__dirname, '../src'),
|
||||
})
|
||||
|
||||
config.plugins.push(new MonacoWebpackPlugin({
|
||||
languages: [],
|
||||
filename: "[name].worker.[contenthash].js",
|
||||
}))
|
||||
|
||||
return config
|
||||
},
|
||||
}
|
||||
|
||||
1
frontend/next-env.d.ts
vendored
1
frontend/next-env.d.ts
vendored
@@ -1,5 +1,4 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/types/global" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
const { execSync } = require("child_process")
|
||||
|
||||
const { config } = require("dotenv")
|
||||
const MonacoWebpackPlugin = require("monaco-editor-webpack-plugin")
|
||||
|
||||
for (const envFile of [".env.local", ".env"]) {
|
||||
config({ path: `../${envFile}` })
|
||||
@@ -72,11 +71,6 @@ module.exports = withPlausibleProxy({
|
||||
use: ["@svgr/webpack"],
|
||||
})
|
||||
|
||||
config.plugins.push(new MonacoWebpackPlugin({
|
||||
languages: [],
|
||||
filename: "[name].worker.[contenthash].js",
|
||||
}))
|
||||
|
||||
return config
|
||||
},
|
||||
images: {
|
||||
@@ -87,4 +81,6 @@ module.exports = withPlausibleProxy({
|
||||
runtimeCaching,
|
||||
disable: process.env.NODE_ENV === "development",
|
||||
},
|
||||
swcMinify: true,
|
||||
experimental: {},
|
||||
}))))
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@badrap/bar-of-progress": "^0.1.2",
|
||||
"@codemirror/basic-setup": "^0.19.1",
|
||||
"@codemirror/lang-cpp": "^0.19.1",
|
||||
"@primer/octicons-react": "^16.3.1",
|
||||
"@react-hook/resize-observer": "^1.2.2",
|
||||
"ansi-to-react": "^6.1.6",
|
||||
@@ -21,19 +23,20 @@
|
||||
"downshift": "^6.1.7",
|
||||
"framer-motion": "^4.1.17",
|
||||
"is-mobile": "^3.0.0",
|
||||
"monaco-editor": "^0.29.1",
|
||||
"next": "^11.1.2",
|
||||
"next": "12",
|
||||
"next-plausible": "^3.1.4",
|
||||
"next-pwa": "^5.3.1",
|
||||
"next-translate": "^1.3.5",
|
||||
"react": "^18.0.0-alpha-fd5e01c2e-20210913",
|
||||
"react": "^18.0.0",
|
||||
"react-contenteditable": "^3.3.6",
|
||||
"react-dom": "^18.0.0-alpha-fd5e01c2e-20210913",
|
||||
"react-dom": "^18.0.0",
|
||||
"react-hot-toast": "^2.1.0",
|
||||
"react-laag": "^2.0.3",
|
||||
"react-modal": "^3.14.4",
|
||||
"react-simple-resizer": "^2.1.0",
|
||||
"react-timeago": "^6.2.1",
|
||||
"react-virtualized-auto-sizer": "^1.0.6",
|
||||
"react-window": "^1.8.6",
|
||||
"sass": "^1.42.1",
|
||||
"swr": "^1.2.1",
|
||||
"use-debounce": "^7.0.0",
|
||||
@@ -41,7 +44,7 @@
|
||||
"use-persisted-state": "^0.3.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.15.8",
|
||||
"@babel/core": "^7.17.8",
|
||||
"@next/eslint-plugin-next": "^11.1.2",
|
||||
"@storybook/addon-actions": "^6.3.10",
|
||||
"@storybook/addon-essentials": "^6.3.10",
|
||||
@@ -53,15 +56,17 @@
|
||||
"@storybook/preset-scss": "^1.0.3",
|
||||
"@storybook/react": "^6.3.10",
|
||||
"@svgr/webpack": "^5.5.0",
|
||||
"@testing-library/jest-dom": "^5.14.1",
|
||||
"@testing-library/react": "^13.0.0-alpha.1",
|
||||
"@testing-library/user-event": "^13.4.1",
|
||||
"@testing-library/jest-dom": "^5.16.3",
|
||||
"@testing-library/react": "^13.0.0-alpha.6",
|
||||
"@testing-library/user-event": "^13.5.0",
|
||||
"@types/react-modal": "^3.13.1",
|
||||
"@types/react-virtualized-auto-sizer": "^1.0.1",
|
||||
"@types/react-window": "^1.8.5",
|
||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||
"@typescript-eslint/parser": "^5.0.0",
|
||||
"autoprefixer": "^10.3.1",
|
||||
"babel-jest": "^27.2.5",
|
||||
"babel-loader": "^8.2.2",
|
||||
"babel-jest": "^27.5.1",
|
||||
"babel-loader": "^8.2.4",
|
||||
"css-loader": "^6.4.0",
|
||||
"cssnano": "^5.0.7",
|
||||
"dotenv": "^10.0.0",
|
||||
@@ -71,11 +76,10 @@
|
||||
"eslint-plugin-css-modules": "^2.11.0",
|
||||
"eslint-plugin-react": "^7.26.1",
|
||||
"eslint-plugin-react-hooks": "^4.2.0",
|
||||
"eslint-plugin-testing-library": "^4.12.4",
|
||||
"eslint-plugin-testing-library": "^5.1.0",
|
||||
"identity-obj-proxy": "^3.0.0",
|
||||
"jest": "^27.2.5",
|
||||
"jest": "^27.5.1",
|
||||
"jest-transform-stub": "^2.0.0",
|
||||
"monaco-editor-webpack-plugin": "^5.0.0",
|
||||
"next-remove-imports": "^1.0.6",
|
||||
"postcss": "^8.3.6",
|
||||
"postcss-loader": "^6.1.1",
|
||||
|
||||
@@ -4,13 +4,20 @@
|
||||
color: var(--a800);
|
||||
padding: 1rem;
|
||||
border-radius: 0.4rem;
|
||||
|
||||
min-width: 6rem;
|
||||
max-width: 40rem;
|
||||
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
pre {
|
||||
max-width: 100%;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
}
|
||||
|
||||
.asyncBtn {
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
scrollbar-width: thin;
|
||||
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
font-family: var(--monospace);
|
||||
font-size: 0.8rem;
|
||||
white-space: pre;
|
||||
}
|
||||
|
||||
@@ -30,9 +30,10 @@ export type Props = {
|
||||
compilation: api.Compilation
|
||||
isCompiling?: boolean
|
||||
isCompilationOld?: boolean
|
||||
selectedSourceLine: number | null
|
||||
}
|
||||
|
||||
export default function CompilationPanel({ compilation, isCompiling, isCompilationOld }: Props) {
|
||||
export default function CompilationPanel({ compilation, isCompiling, isCompilationOld, selectedSourceLine }: Props) {
|
||||
const [diff, setDiff] = useState<api.DiffOutput | null>(null)
|
||||
const problemState = getProblemState(compilation)
|
||||
|
||||
@@ -47,6 +48,7 @@ export default function CompilationPanel({ compilation, isCompiling, isCompilati
|
||||
diff={diff}
|
||||
isCompiling={isCompiling}
|
||||
isCurrentOutdated={isCompilationOld || problemState == ProblemState.ERRORS}
|
||||
selectedSourceLine={selectedSourceLine}
|
||||
/>
|
||||
</resizer.Section>
|
||||
<resizer.Bar
|
||||
|
||||
@@ -1,54 +1,44 @@
|
||||
.diff {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
overflow: auto;
|
||||
|
||||
scrollbar-color: #fff3 transparent;
|
||||
scrollbar-width: thin;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.column {
|
||||
height: max-content;
|
||||
overflow: initial !important; /* overrides resizer.Section inline style */
|
||||
}
|
||||
|
||||
.outdated {
|
||||
filter: grayscale(25%) brightness(70%);
|
||||
.bodyContainer {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.bar {
|
||||
cursor: col-resize;
|
||||
background: var(--a50);
|
||||
|
||||
position: sticky !important; /* overrides resizer.Bar inline style */
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.row {
|
||||
padding: 0 4px;
|
||||
height: 1.25em;
|
||||
z-index: 99;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
|
||||
height: 3em;
|
||||
padding: 0 1em;
|
||||
|
||||
position: sticky;
|
||||
top: 0;
|
||||
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
color: var(--g1200);
|
||||
border-bottom: 1px dashed var(--a50);
|
||||
|
||||
backdrop-filter: blur(3px);
|
||||
|
||||
user-select: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
// Columns
|
||||
.headers,
|
||||
.row {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-template-columns: var(--diff-left-width) auto var(--diff-right-width);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
@supports not (backdrop-filter: blur(3px)) {
|
||||
@@ -59,22 +49,52 @@
|
||||
|
||||
.body {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: 8px 0;
|
||||
overflow: overlay !important; // note: webkit only, see #275
|
||||
|
||||
user-select: text;
|
||||
font-family: monospace;
|
||||
font-size: 12px;
|
||||
font-family: var(--monospace);
|
||||
font-size: 0.75rem;
|
||||
white-space: pre;
|
||||
|
||||
list-style: none;
|
||||
|
||||
scrollbar-color: #fff3 transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
.lineNumber {
|
||||
.row {
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
|
||||
align-items: stretch;
|
||||
|
||||
> .cell {
|
||||
overflow: hidden;
|
||||
|
||||
color: #b3c1d3;
|
||||
|
||||
padding: 0 1em;
|
||||
|
||||
&.highlight {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
|
||||
.lineNumber {
|
||||
color: #c6c6c6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cell span.lineNumber {
|
||||
display: inline-block;
|
||||
|
||||
color: var(--a300);
|
||||
color: #676e95;
|
||||
|
||||
min-width: 3ch;
|
||||
min-width: 24px;
|
||||
padding: 0 3px 0 5px;
|
||||
text-align: right;
|
||||
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.immediate { color: #6d6dff; }
|
||||
|
||||
@@ -1,14 +1,22 @@
|
||||
/* eslint css-modules/no-unused-class: off */
|
||||
|
||||
import { ReactNode } from "react"
|
||||
import { createContext, CSSProperties, forwardRef, HTMLAttributes, useContext, useEffect, useState } from "react"
|
||||
|
||||
import classNames from "classnames"
|
||||
import * as resizer from "react-simple-resizer"
|
||||
import AutoSizer from "react-virtualized-auto-sizer"
|
||||
import { FixedSizeList } from "react-window"
|
||||
|
||||
import * as api from "../../lib/api"
|
||||
import { useSize } from "../../lib/hooks"
|
||||
import Loading from "../loading.svg"
|
||||
|
||||
import styles from "./Diff.module.scss"
|
||||
import DragBar from "./DragBar"
|
||||
|
||||
const PADDING_TOP = 0
|
||||
const PADDING_BOTTOM = 0
|
||||
|
||||
const SelectedSourceLineContext = createContext<number | null>(null)
|
||||
|
||||
function FormatDiffText({ texts }: { texts: api.DiffText[] }) {
|
||||
return <> {
|
||||
@@ -24,57 +32,135 @@ function FormatDiffText({ texts }: { texts: api.DiffText[] }) {
|
||||
} </>
|
||||
}
|
||||
|
||||
function DiffColumn({ diff, prop, header, className }: {
|
||||
diff: api.DiffOutput | null
|
||||
prop: keyof api.DiffRow & keyof api.DiffHeader
|
||||
header: ReactNode
|
||||
function DiffCell({ cell, className }: {
|
||||
cell: api.DiffCell | undefined
|
||||
className?: string
|
||||
}) {
|
||||
return <resizer.Section className={classNames(styles.column, className)} minSize={100}>
|
||||
<div className={classNames(styles.row, styles.header)}>
|
||||
{header}
|
||||
</div>
|
||||
<div className={styles.body}>
|
||||
{diff?.rows?.map?.((row, i) => (
|
||||
<div key={i} className={styles.row}>
|
||||
{typeof row[prop]?.src_line != "undefined" && <span className={styles.lineNumber}>{row[prop].src_line}</span>}
|
||||
{row[prop] && <FormatDiffText texts={row[prop].text} />}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</resizer.Section>
|
||||
const selectedSourceLine = useContext(SelectedSourceLineContext)
|
||||
const hasLineNo = typeof cell?.src_line != "undefined"
|
||||
|
||||
if (!cell)
|
||||
return <div className={classNames(styles.cell, className)} />
|
||||
|
||||
return <div
|
||||
className={classNames(className, {
|
||||
[styles.cell]: true,
|
||||
[styles.highlight]: hasLineNo && cell.src_line == selectedSourceLine,
|
||||
})}
|
||||
>
|
||||
{hasLineNo && <span className={styles.lineNumber}>{cell.src_line}</span>}
|
||||
<FormatDiffText texts={cell.text} />
|
||||
</div>
|
||||
}
|
||||
|
||||
function DiffRow({ data, index, style }: {
|
||||
data: api.DiffRow[]
|
||||
index: number
|
||||
style: CSSProperties
|
||||
}) {
|
||||
const row = data[index]
|
||||
|
||||
return <li
|
||||
className={styles.row}
|
||||
style={{
|
||||
...style,
|
||||
top: `${parseFloat(style.top.toString()) + PADDING_TOP}px`,
|
||||
}}
|
||||
>
|
||||
<DiffCell cell={row.base} />
|
||||
<DiffCell cell={row.current} />
|
||||
<DiffCell cell={row.previous} />
|
||||
</li>
|
||||
}
|
||||
|
||||
// https://github.com/bvaughn/react-window#can-i-add-padding-to-the-top-and-bottom-of-a-list
|
||||
const innerElementType = forwardRef<HTMLUListElement, HTMLAttributes<HTMLUListElement>>(({ style, ...rest }, ref) => {
|
||||
return <ul
|
||||
ref={ref}
|
||||
style={{
|
||||
...style,
|
||||
height: `${parseFloat(style.height.toString()) + PADDING_TOP + PADDING_BOTTOM}px`,
|
||||
}}
|
||||
{...rest}
|
||||
/>
|
||||
})
|
||||
innerElementType.displayName = "innerElementType"
|
||||
|
||||
function DiffBody({ diff }: { diff: api.DiffOutput }) {
|
||||
return <div className={styles.bodyContainer}>
|
||||
{diff?.rows && <AutoSizer>
|
||||
{({ height, width }) => (
|
||||
<FixedSizeList
|
||||
className={styles.body}
|
||||
itemCount={diff.rows.length}
|
||||
itemData={diff.rows}
|
||||
itemSize={16}
|
||||
overscanCount={40}
|
||||
width={width}
|
||||
height={height}
|
||||
innerElementType={innerElementType}
|
||||
>
|
||||
{DiffRow}
|
||||
</FixedSizeList>
|
||||
)}
|
||||
</AutoSizer>}
|
||||
</div>
|
||||
}
|
||||
|
||||
export type Props = {
|
||||
diff: api.DiffOutput
|
||||
isCompiling: boolean
|
||||
isCurrentOutdated: boolean
|
||||
selectedSourceLine: number | null
|
||||
}
|
||||
|
||||
export default function Diff({ diff, isCompiling, isCurrentOutdated }: Props) {
|
||||
return <resizer.Container className={styles.diff}>
|
||||
<DiffColumn diff={diff} prop="base" header="Target" />
|
||||
<resizer.Bar
|
||||
size={1}
|
||||
className={styles.bar}
|
||||
expandInteractiveArea={{ left: 2, right: 2 }}
|
||||
/>
|
||||
<DiffColumn
|
||||
diff={diff}
|
||||
prop="current"
|
||||
header={<>
|
||||
export default function Diff({ diff, isCompiling, isCurrentOutdated, selectedSourceLine }: Props) {
|
||||
const container = useSize<HTMLDivElement>()
|
||||
|
||||
const [barPos, setBarPos] = useState(NaN)
|
||||
const [prevBarPos, setPrevBarPos] = useState(NaN)
|
||||
|
||||
const hasPreviousColumn = !!diff?.rows?.[0]?.previous
|
||||
|
||||
const columnMinWidth = 100
|
||||
const clampedBarPos = Math.max(columnMinWidth, Math.min(container.width - columnMinWidth - (hasPreviousColumn ? columnMinWidth : 0), barPos))
|
||||
const clampedPrevBarPos = hasPreviousColumn ? Math.max(clampedBarPos + columnMinWidth, Math.min(container.width - columnMinWidth, prevBarPos)) : container.width
|
||||
|
||||
useEffect(() => {
|
||||
// Initially distribute the bar positions across the container
|
||||
if (isNaN(barPos) && container.width) {
|
||||
const numSections = hasPreviousColumn ? 3 : 2
|
||||
|
||||
setBarPos(container.width / numSections)
|
||||
setPrevBarPos(container.width / numSections * 2)
|
||||
}
|
||||
}, [barPos, container.width, hasPreviousColumn])
|
||||
|
||||
return <div
|
||||
ref={container.ref}
|
||||
className={styles.diff}
|
||||
style={{
|
||||
"--diff-left-width": `${clampedBarPos}px`,
|
||||
"--diff-right-width": `${container.width - clampedPrevBarPos}px`,
|
||||
"--diff-current-filter": isCurrentOutdated ? "grayscale(25%) brightness(70%)" : "",
|
||||
} as CSSProperties}
|
||||
>
|
||||
<DragBar pos={clampedBarPos} onChange={setBarPos} />
|
||||
{hasPreviousColumn && <DragBar pos={clampedPrevBarPos} onChange={setPrevBarPos} />}
|
||||
<div className={styles.headers}>
|
||||
<div className={styles.header}>
|
||||
Target
|
||||
</div>
|
||||
<div className={styles.header}>
|
||||
Current
|
||||
{isCompiling && <Loading width={20} height={20} />}
|
||||
</>}
|
||||
className={classNames({ [styles.outdated]: isCurrentOutdated })}
|
||||
/>
|
||||
{diff?.header?.previous && <>
|
||||
<resizer.Bar
|
||||
size={1}
|
||||
className={styles.bar}
|
||||
expandInteractiveArea={{ left: 2, right: 2 }}
|
||||
/>
|
||||
<DiffColumn diff={diff} prop="previous" header="Saved" />
|
||||
</>}
|
||||
</resizer.Container>
|
||||
</div>
|
||||
{hasPreviousColumn && <div className={styles.header}>
|
||||
Previous
|
||||
</div>}
|
||||
</div>
|
||||
<SelectedSourceLineContext.Provider value={selectedSourceLine}>
|
||||
<DiffBody diff={diff} />
|
||||
</SelectedSourceLineContext.Provider>
|
||||
</div>
|
||||
}
|
||||
|
||||
24
frontend/src/components/Diff/DragBar.module.scss
Normal file
24
frontend/src/components/Diff/DragBar.module.scss
Normal file
@@ -0,0 +1,24 @@
|
||||
.vertical {
|
||||
width: 4px;
|
||||
height: 100%;
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
z-index: 99;
|
||||
|
||||
cursor: col-resize;
|
||||
|
||||
&::after {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 1px;
|
||||
height: 100%;
|
||||
background: var(--a50);
|
||||
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
}
|
||||
41
frontend/src/components/Diff/DragBar.tsx
Normal file
41
frontend/src/components/Diff/DragBar.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
|
||||
import styles from "./DragBar.module.scss"
|
||||
|
||||
export interface Props {
|
||||
pos: number
|
||||
onChange: (pos: number) => void
|
||||
}
|
||||
|
||||
export default function DragBar({ pos, onChange }: Props) {
|
||||
const [isActive, setIsActive] = useState(false)
|
||||
const ref = useRef<HTMLDivElement>()
|
||||
|
||||
useEffect(() => {
|
||||
const onMouseMove = (evt: MouseEvent) => {
|
||||
if (isActive) {
|
||||
const parent = ref.current.parentElement
|
||||
onChange(evt.clientX - parent.getBoundingClientRect().x)
|
||||
}
|
||||
}
|
||||
|
||||
const onMouseUp = () => {
|
||||
setIsActive(false)
|
||||
}
|
||||
|
||||
document.addEventListener("mousemove", onMouseMove)
|
||||
document.addEventListener("mouseup", onMouseUp)
|
||||
|
||||
return () => {
|
||||
document.removeEventListener("mousemove", onMouseMove)
|
||||
document.removeEventListener("mouseup", onMouseUp)
|
||||
}
|
||||
})
|
||||
|
||||
return <div
|
||||
ref={ref}
|
||||
className={styles.vertical}
|
||||
style={{ left: `${pos}px` }}
|
||||
onMouseDown={() => setIsActive(true)}
|
||||
/>
|
||||
}
|
||||
140
frontend/src/components/Editor/CodeMirror.tsx
Normal file
140
frontend/src/components/Editor/CodeMirror.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
import { MutableRefObject, useEffect, useRef } from "react"
|
||||
|
||||
import { Extension, EditorState } from "@codemirror/state"
|
||||
import { EditorView } from "@codemirror/view"
|
||||
import { useDebouncedCallback } from "use-debounce"
|
||||
|
||||
import { materialPalenight } from "../../lib/themes/dark"
|
||||
|
||||
export interface Props {
|
||||
value: string
|
||||
onChange?: (value: string) => void
|
||||
onHoveredLineChange?: (value: number | null) => void
|
||||
onSelectedLineChange?: (value: number) => void
|
||||
className?: string
|
||||
viewRef?: MutableRefObject<EditorView | null>
|
||||
extensions: Extension // const
|
||||
}
|
||||
|
||||
export default function CodeMirror({
|
||||
value,
|
||||
onChange,
|
||||
onHoveredLineChange,
|
||||
onSelectedLineChange,
|
||||
className,
|
||||
viewRef: viewRefProp,
|
||||
extensions,
|
||||
}: Props) {
|
||||
const el = useRef<HTMLDivElement>()
|
||||
|
||||
const valueRef = useRef(value)
|
||||
valueRef.current = value
|
||||
|
||||
const onChangeRef = useRef(onChange)
|
||||
onChangeRef.current = onChange
|
||||
|
||||
const viewRef = useRef<EditorView>()
|
||||
|
||||
const extensionsRef = useRef(extensions)
|
||||
extensionsRef.current = extensions
|
||||
|
||||
const selectedLineRef = useRef<number>()
|
||||
const hoveredLineRef = useRef<number>()
|
||||
|
||||
const onHoveredLineChangeRef = useRef(onHoveredLineChange)
|
||||
onHoveredLineChangeRef.current = onHoveredLineChange
|
||||
|
||||
const onSelectedLineChangeRef = useRef(onSelectedLineChange)
|
||||
onSelectedLineChangeRef.current = onSelectedLineChange
|
||||
|
||||
// Initial view creation
|
||||
useEffect(() => {
|
||||
viewRef.current = new EditorView({
|
||||
state: EditorState.create({
|
||||
doc: valueRef.current,
|
||||
extensions: [
|
||||
EditorState.transactionExtender.of(({ newDoc, newSelection }) => {
|
||||
// value / onChange
|
||||
const newValue = newDoc.toString()
|
||||
if (newValue !== valueRef.current) {
|
||||
onChangeRef.current?.(newValue)
|
||||
}
|
||||
|
||||
// selectedSourceLine
|
||||
const line = newDoc.lineAt(newSelection.main.from).number
|
||||
if (hoveredLineRef.current !== line) {
|
||||
hoveredLineRef.current = line
|
||||
requestAnimationFrame(() => {
|
||||
onSelectedLineChangeRef.current?.(line)
|
||||
})
|
||||
}
|
||||
|
||||
return null
|
||||
}),
|
||||
extensionsRef.current,
|
||||
materialPalenight,
|
||||
],
|
||||
}),
|
||||
parent: el.current,
|
||||
})
|
||||
|
||||
if (viewRefProp)
|
||||
viewRefProp.current = viewRef.current
|
||||
|
||||
return () => {
|
||||
viewRef.current.destroy()
|
||||
viewRef.current = null
|
||||
if (viewRefProp)
|
||||
viewRefProp.current = null
|
||||
}
|
||||
}, [viewRefProp])
|
||||
|
||||
// Replace doc when `value` prop changes
|
||||
useEffect(() => {
|
||||
const view = viewRef.current
|
||||
if (view) {
|
||||
const prevValue = view.state.doc.toString()
|
||||
|
||||
if (prevValue != value) {
|
||||
view.dispatch(
|
||||
view.state.update({
|
||||
changes: {
|
||||
from: 0,
|
||||
to: prevValue.length,
|
||||
insert: value,
|
||||
},
|
||||
})
|
||||
)
|
||||
}
|
||||
}
|
||||
}, [value])
|
||||
|
||||
const debouncedOnMouseMove = useDebouncedCallback(
|
||||
event => {
|
||||
if (!onHoveredLineChangeRef.current)
|
||||
return
|
||||
|
||||
const view = viewRef.current
|
||||
let newLine: number | null = null
|
||||
if (view) {
|
||||
const line = view.state.doc.lineAt(view.posAtCoords({ x: event.clientX, y: event.clientY })).number
|
||||
if (line) {
|
||||
newLine = line
|
||||
}
|
||||
}
|
||||
|
||||
if (selectedLineRef.current != newLine) {
|
||||
selectedLineRef.current = newLine
|
||||
onHoveredLineChangeRef.current?.(newLine)
|
||||
}
|
||||
},
|
||||
100,
|
||||
{ leading: true, trailing: true },
|
||||
)
|
||||
|
||||
return <div
|
||||
ref={el}
|
||||
onMouseMove={debouncedOnMouseMove}
|
||||
className={className}
|
||||
style={{ fontSize: "0.8em" }} />
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
.editor {
|
||||
flex: 1;
|
||||
|
||||
resize: none;
|
||||
border: 0;
|
||||
outline: none !important;
|
||||
|
||||
font-size: 12px;
|
||||
font-family: monospace;
|
||||
line-height: 1.5;
|
||||
|
||||
user-select: initial;
|
||||
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.loadingContainer {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
flex-grow: 1;
|
||||
|
||||
svg {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import { Suspense } from "react"
|
||||
|
||||
import dynamic from "next/dynamic"
|
||||
|
||||
import classNames from "classnames"
|
||||
import mobile from "is-mobile"
|
||||
|
||||
import LoadingSpinner from "../loading.svg"
|
||||
|
||||
import styles from "./Editor.module.scss"
|
||||
import type { Props as MonacoEditorProps } from "./MonacoEditor"
|
||||
import getTheme from "./monacoTheme"
|
||||
|
||||
const isMobile = mobile()
|
||||
const isSSR = typeof window === "undefined"
|
||||
|
||||
interface Props extends MonacoEditorProps {
|
||||
bubbleSuspense?: boolean
|
||||
useLoadingSpinner?: boolean
|
||||
}
|
||||
|
||||
const MonacoEditor = (isSSR || isMobile) ? null : dynamic(() => import("./MonacoEditor"))
|
||||
|
||||
// Wrapper component that asyncronously loads MonacoEditor on desktop,
|
||||
// falling back to a simple textarea on mobile
|
||||
export default function Editor(props: Props) {
|
||||
const monacoTheme = getTheme()
|
||||
const style = {
|
||||
color: monacoTheme.colors["editor.foreground"],
|
||||
backgroundColor: monacoTheme.colors["editor.background"],
|
||||
padding: (props.padding ?? (props.showMargin ? 20 : 0)) + "px",
|
||||
}
|
||||
|
||||
const textarea = <textarea
|
||||
className={classNames(styles.editor, props.className)}
|
||||
spellCheck={false}
|
||||
value={props.value}
|
||||
readOnly={!props.onChange}
|
||||
onChange={event => {
|
||||
const value = event.target.value
|
||||
if (props.onChange)
|
||||
props.onChange(value)
|
||||
}}
|
||||
style={style}
|
||||
/>
|
||||
|
||||
if (MonacoEditor) {
|
||||
const loading = props.useLoadingSpinner ? <div
|
||||
className={classNames(styles.loadingContainer, props.className)}
|
||||
style={style}
|
||||
>
|
||||
<LoadingSpinner />
|
||||
</div> : textarea
|
||||
|
||||
if (props.bubbleSuspense) {
|
||||
return <MonacoEditor {...props} />
|
||||
} else {
|
||||
return <Suspense fallback={loading}>
|
||||
<MonacoEditor {...props} />
|
||||
</Suspense>
|
||||
}
|
||||
} else {
|
||||
return textarea
|
||||
}
|
||||
}
|
||||
@@ -1,34 +0,0 @@
|
||||
.container {
|
||||
flex-grow: 1;
|
||||
|
||||
/* fix border-radius overflow */
|
||||
overflow: hidden;
|
||||
|
||||
& :global(.monaco-editor),
|
||||
& :global(.monaco-editor-background),
|
||||
& :global(.margin),
|
||||
& :global(.inputarea.ime-input) {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
/* weird box at top-left */
|
||||
& :global(.monaco-hover) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
& :global(.mac .monaco-mouse-cursor-text) {
|
||||
cursor: text !important;
|
||||
}
|
||||
|
||||
& :global(.view-lines) {
|
||||
user-select: initial;
|
||||
}
|
||||
}
|
||||
|
||||
.readonly {
|
||||
cursor: text;
|
||||
|
||||
& :global(.cursor) {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
import { useState } from "react"
|
||||
|
||||
import { ComponentStory, ComponentMeta } from "@storybook/react"
|
||||
|
||||
import MonacoEditor from "./MonacoEditor"
|
||||
|
||||
export default {
|
||||
title: "MonacoEditor",
|
||||
component: MonacoEditor,
|
||||
} as ComponentMeta<typeof MonacoEditor>
|
||||
|
||||
const Template: ComponentStory<typeof MonacoEditor> = args => {
|
||||
const [value, setValue] = useState(args.value)
|
||||
|
||||
return <div style={{ display: "flex", width: "95vw", height: "95vh" }}>
|
||||
<MonacoEditor {...args} value={value} onChange={setValue} />
|
||||
</div>
|
||||
}
|
||||
|
||||
export const C: ComponentStory<typeof MonacoEditor> = Template.bind({})
|
||||
C.args = {
|
||||
language: "c",
|
||||
value:
|
||||
`s32 collision_heap_free(void* data) {
|
||||
if (gGameStatusPtr->isBattle) {
|
||||
return _heap_free(&D_803DA800, data);
|
||||
} else {
|
||||
return _heap_free(&D_80268000, data);
|
||||
}
|
||||
}
|
||||
`,
|
||||
lineNumbers: true,
|
||||
showMargin: true,
|
||||
}
|
||||
|
||||
export const Mips: ComponentStory<typeof MonacoEditor> = Template.bind({})
|
||||
Mips.args = {
|
||||
language: "mips",
|
||||
value:
|
||||
`.set noat # allow manual use of $at
|
||||
.set noreorder # don't insert nops after branches
|
||||
|
||||
glabel sins
|
||||
/* 3F9F0 800645F0 3084FFFF */ andi $a0, $a0, 0xffff
|
||||
/* 3F9F4 800645F4 00042102 */ srl $a0, $a0, 4
|
||||
/* 3F9F8 800645F8 30820400 */ andi $v0, $a0, 0x400
|
||||
/* 3F9FC 800645FC 10400004 */ beqz $v0, .L80064610
|
||||
/* 3FA00 80064600 00802821 */ addu $a1, $a0, $zero
|
||||
/* 3FA04 80064604 00041027 */ nor $v0, $zero, $a0
|
||||
/* 3FA08 80064608 08019185 */ j .L80064614
|
||||
/* 3FA0C 8006460C 304203FF */ andi $v0, $v0, 0x3ff
|
||||
.L80064610:
|
||||
/* 3FA10 80064610 308203FF */ andi $v0, $a0, 0x3ff
|
||||
.L80064614:
|
||||
/* 3FA14 80064614 00021040 */ sll $v0, $v0, 1
|
||||
/* 3FA18 80064618 3C038009 */ lui $v1, %hi(sintable)
|
||||
/* 3FA1C 8006461C 00621821 */ addu $v1, $v1, $v0
|
||||
/* 3FA20 80064620 94633DE0 */ lhu $v1, %lo(sintable)($v1)
|
||||
/* 3FA24 80064624 30A20800 */ andi $v0, $a1, 0x800
|
||||
/* 3FA28 80064628 14400003 */ bnez $v0, .L80064638
|
||||
/* 3FA2C 8006462C 00031023 */ negu $v0, $v1
|
||||
/* 3FA30 80064630 0801918F */ j .L8006463C
|
||||
/* 3FA34 80064634 00031400 */ sll $v0, $v1, 0x10
|
||||
.L80064638:
|
||||
/* 3FA38 80064638 00021400 */ sll $v0, $v0, 0x10
|
||||
.L8006463C:
|
||||
/* 3FA3C 8006463C 03E00008 */ jr $ra
|
||||
/* 3FA40 80064640 00021403 */ sra $v0, $v0, 0x10
|
||||
/* 3FA44 80064644 00000000 */ nop
|
||||
/* 3FA48 80064648 00000000 */ nop
|
||||
/* 3FA4C 8006464C 00000000 */ nop
|
||||
`,
|
||||
}
|
||||
|
||||
export const OverScrollTest: ComponentStory<typeof MonacoEditor> = () => {
|
||||
const args = Mips.args
|
||||
const [value, setValue] = useState(args.value)
|
||||
|
||||
return <div style={{ height: "150vh" }}>
|
||||
Page should begin scrolling after the editor hits the bottom
|
||||
|
||||
<div style={{ display: "flex", width: "95vw", height: "400px" }}>
|
||||
<MonacoEditor {...args} value={value} onChange={setValue} language="mips" />
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
|
||||
export const Readonly: ComponentStory<typeof MonacoEditor> = () => {
|
||||
const args = Mips.args
|
||||
|
||||
return <div style={{ display: "flex", width: "95vw", height: "95vh" }}>
|
||||
<MonacoEditor {...args} value={args.value} language="mips" />
|
||||
</div>
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
import { useEffect, useState, useRef, MutableRefObject } from "react"
|
||||
|
||||
import classNames from "classnames"
|
||||
import { editor, languages } from "monaco-editor"
|
||||
|
||||
import * as c from "./language/c"
|
||||
import * as mips from "./language/mips"
|
||||
import styles from "./MonacoEditor.module.scss"
|
||||
import monacoTheme from "./monacoTheme"
|
||||
|
||||
import "monaco-editor/min/vs/editor/editor.main.css"
|
||||
|
||||
if (typeof window === "undefined") {
|
||||
throw new Error("Editor component does not work with SSR, use next/dynamic with { ssr: false }")
|
||||
}
|
||||
|
||||
languages.register({ id: "decompme_c" })
|
||||
languages.setLanguageConfiguration("decompme_c", c.conf)
|
||||
languages.setMonarchTokensProvider("decompme_c", c.language)
|
||||
|
||||
languages.register({ id: "decompme_mips" })
|
||||
languages.setLanguageConfiguration("decompme_mips", mips.conf)
|
||||
languages.setMonarchTokensProvider("decompme_mips", mips.language)
|
||||
|
||||
function convertLanguage(language: string) {
|
||||
if (language === "c")
|
||||
return "decompme_c"
|
||||
else if (language === "mips")
|
||||
return "decompme_mips"
|
||||
else
|
||||
return "plaintext"
|
||||
}
|
||||
|
||||
export type EditorInstance = editor.IStandaloneCodeEditor;
|
||||
|
||||
export type Props = {
|
||||
className?: string
|
||||
|
||||
// This is a controlled component
|
||||
value: string
|
||||
onChange?: (value: string) => void
|
||||
|
||||
instanceRef?: MutableRefObject<editor.IStandaloneCodeEditor>
|
||||
|
||||
// Options
|
||||
language: "c" | "mips"
|
||||
lineNumbers?: boolean
|
||||
showMargin?: boolean
|
||||
padding?: number // css
|
||||
}
|
||||
|
||||
export default function Editor({ value, onChange, className, showMargin, padding, language, lineNumbers, instanceRef }: Props) {
|
||||
const isReadOnly = typeof onChange === "undefined"
|
||||
const containerRef = useRef<HTMLDivElement>(null)
|
||||
const [editorInstance, setEditorInstance] = useState<editor.IStandaloneCodeEditor | null>(null)
|
||||
|
||||
// Effect to set up the editor. This is run once when the component is mounted.
|
||||
useEffect(() => {
|
||||
editor.defineTheme("custom", monacoTheme())
|
||||
|
||||
const editorInstance = editor.create(containerRef.current, {
|
||||
language: convertLanguage(language),
|
||||
value,
|
||||
theme: "custom",
|
||||
autoDetectHighContrast: false,
|
||||
minimap: {
|
||||
enabled: false,
|
||||
},
|
||||
lineNumbers: lineNumbers ? "on" : "off",
|
||||
renderLineHighlightOnlyWhenFocus: true,
|
||||
scrollBeyondLastLine: false,
|
||||
scrollbar: {
|
||||
alwaysConsumeMouseWheel: false,
|
||||
},
|
||||
contextmenu: true,
|
||||
//fontLigatures: true,
|
||||
//fontFamily: "Jetbrains Mono",
|
||||
readOnly: isReadOnly,
|
||||
domReadOnly: isReadOnly,
|
||||
occurrencesHighlight: !isReadOnly,
|
||||
renderLineHighlight: isReadOnly ? "none" : "all",
|
||||
padding: {
|
||||
top: padding ?? (showMargin ? 20 : 0), // to match gutter
|
||||
bottom: padding ?? 0,
|
||||
},
|
||||
glyphMargin: !!showMargin,
|
||||
folding: !!showMargin,
|
||||
lineDecorationsWidth: padding ?? (showMargin ? 10 : 0),
|
||||
lineNumbersMinChars: showMargin ? 2 : 0,
|
||||
automaticLayout: true,
|
||||
})
|
||||
setEditorInstance(editorInstance)
|
||||
if (instanceRef)
|
||||
instanceRef.current = editorInstance
|
||||
|
||||
const model = editorInstance.getModel()
|
||||
if (model) {
|
||||
model.onDidChangeContent(() => {
|
||||
if (onChange)
|
||||
onChange(model.getValue())
|
||||
})
|
||||
} else {
|
||||
console.error("monaco editor has no model")
|
||||
}
|
||||
|
||||
return () => editorInstance.dispose()
|
||||
}, []) // eslint-disable-line react-hooks/exhaustive-deps
|
||||
|
||||
// Update value.
|
||||
useEffect(() => {
|
||||
const model = editorInstance?.getModel()
|
||||
|
||||
// Only update the model value if it is different; otherwise, the
|
||||
// model state will be reset every time the user types!
|
||||
if (model && model.getValue() !== value) {
|
||||
console.warn("editor value reset")
|
||||
model.setValue(value)
|
||||
}
|
||||
}, [editorInstance, value])
|
||||
|
||||
// Update language.
|
||||
useEffect(() => {
|
||||
const model = editorInstance?.getModel()
|
||||
|
||||
if (model) {
|
||||
editor.setModelLanguage(model, convertLanguage(language))
|
||||
}
|
||||
}, [editorInstance, language])
|
||||
|
||||
useEffect(() => {
|
||||
editorInstance?.updateOptions({ lineNumbers: lineNumbers ? "on" : "off" })
|
||||
}, [editorInstance, lineNumbers])
|
||||
|
||||
useEffect(() => {
|
||||
editorInstance?.updateOptions({
|
||||
glyphMargin: !!showMargin,
|
||||
folding: !!showMargin,
|
||||
lineDecorationsWidth: padding ?? (showMargin ? 10 : 0),
|
||||
lineNumbersMinChars: showMargin ? 2 : 0,
|
||||
})
|
||||
}, [editorInstance, padding, showMargin])
|
||||
|
||||
return <div
|
||||
ref={containerRef}
|
||||
className={classNames(
|
||||
styles.container,
|
||||
className,
|
||||
{
|
||||
[styles.readonly]: isReadOnly,
|
||||
},
|
||||
)}
|
||||
onKeyDownCapture={e => {
|
||||
if (isReadOnly) {
|
||||
// disable changing lines with arrow keys
|
||||
if (e.key === "ArrowDown" || e.key === "ArrowUp" || e.key === "ArrowLeft" || e.key === "ArrowRight") {
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
// disable command palette
|
||||
if (e.key === "F1") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
}
|
||||
} else {
|
||||
// Command Palette
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === "p") {
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
if (e.shiftKey)
|
||||
editorInstance?.trigger("", "editor.action.quickCommand", "")
|
||||
//console.log(editor.getSupportedActions())
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
import Editor from "./Editor"
|
||||
|
||||
export default Editor
|
||||
@@ -1,449 +0,0 @@
|
||||
import type { languages } from "monaco-editor"
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
comments: {
|
||||
lineComment: "//",
|
||||
blockComment: ["/*", "*/"],
|
||||
},
|
||||
brackets: [
|
||||
["{", "}"],
|
||||
["[", "]"],
|
||||
["(", ")"],
|
||||
],
|
||||
autoClosingPairs: [
|
||||
{ open: "[", close: "]" },
|
||||
{ open: "{", close: "}" },
|
||||
{ open: "(", close: ")" },
|
||||
{ open: "'", close: "'", notIn: ["string", "comment"] },
|
||||
{ open: "\"", close: "\"", notIn: ["string"] },
|
||||
],
|
||||
surroundingPairs: [
|
||||
{ open: "{", close: "}" },
|
||||
{ open: "[", close: "]" },
|
||||
{ open: "(", close: ")" },
|
||||
{ open: "\"", close: "\"" },
|
||||
{ open: "'", close: "'" },
|
||||
],
|
||||
folding: {
|
||||
markers: {
|
||||
start: new RegExp("^\\s*#pragma\\s+region\\b"),
|
||||
end: new RegExp("^\\s*#pragma\\s+endregion\\b"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
export const language: languages.IMonarchLanguage = {
|
||||
defaultToken: "",
|
||||
tokenPostfix: ".c",
|
||||
|
||||
brackets: [
|
||||
{ token: "delimiter.curly", open: "{", close: "}" },
|
||||
{ token: "delimiter.parenthesis", open: "(", close: ")" },
|
||||
{ token: "delimiter.square", open: "[", close: "]" },
|
||||
{ token: "delimiter.angle", open: "<", close: ">" },
|
||||
],
|
||||
|
||||
keywords: [
|
||||
"abstract",
|
||||
"amp",
|
||||
"array",
|
||||
"auto",
|
||||
"break",
|
||||
"case",
|
||||
"catch",
|
||||
"class",
|
||||
"const",
|
||||
"constexpr",
|
||||
"const_cast",
|
||||
"continue",
|
||||
"cpu",
|
||||
"decltype",
|
||||
"default",
|
||||
"delegate",
|
||||
"delete",
|
||||
"do",
|
||||
"double",
|
||||
"dynamic_cast",
|
||||
"each",
|
||||
"else",
|
||||
"enum",
|
||||
"event",
|
||||
"explicit",
|
||||
"export",
|
||||
"extern",
|
||||
"false",
|
||||
"final",
|
||||
"finally",
|
||||
"for",
|
||||
"friend",
|
||||
"gcnew",
|
||||
"generic",
|
||||
"goto",
|
||||
"if",
|
||||
"in",
|
||||
"initonly",
|
||||
"inline",
|
||||
"interface",
|
||||
"interior_ptr",
|
||||
"internal",
|
||||
"literal",
|
||||
"long",
|
||||
"mutable",
|
||||
"namespace",
|
||||
"new",
|
||||
"noexcept",
|
||||
"nullptr",
|
||||
"__nullptr",
|
||||
"operator",
|
||||
"override",
|
||||
"partial",
|
||||
"pascal",
|
||||
"pin_ptr",
|
||||
"private",
|
||||
"property",
|
||||
"protected",
|
||||
"public",
|
||||
"ref",
|
||||
"register",
|
||||
"reinterpret_cast",
|
||||
"restrict",
|
||||
"return",
|
||||
"safe_cast",
|
||||
"sealed",
|
||||
"short",
|
||||
"signed",
|
||||
"sizeof",
|
||||
"static",
|
||||
"static_assert",
|
||||
"static_cast",
|
||||
"struct",
|
||||
"switch",
|
||||
"template",
|
||||
"this",
|
||||
"thread_local",
|
||||
"throw",
|
||||
"tile_static",
|
||||
"true",
|
||||
"try",
|
||||
"typedef",
|
||||
"typeid",
|
||||
"typename",
|
||||
"union",
|
||||
"using",
|
||||
"virtual",
|
||||
"volatile",
|
||||
"wchar_t",
|
||||
"where",
|
||||
"while",
|
||||
|
||||
"_asm", // reserved word with one underscores
|
||||
"_based",
|
||||
"_cdecl",
|
||||
"_declspec",
|
||||
"_fastcall",
|
||||
"_if_exists",
|
||||
"_if_not_exists",
|
||||
"_inline",
|
||||
"_multiple_inheritance",
|
||||
"_pascal",
|
||||
"_single_inheritance",
|
||||
"_stdcall",
|
||||
"_virtual_inheritance",
|
||||
"_w64",
|
||||
|
||||
"__abstract", // reserved word with two underscores
|
||||
"__alignof",
|
||||
"__asm",
|
||||
"__assume",
|
||||
"__based",
|
||||
"__box",
|
||||
"__builtin_alignof",
|
||||
"__cdecl",
|
||||
"__clrcall",
|
||||
"__declspec",
|
||||
"__delegate",
|
||||
"__event",
|
||||
"__except",
|
||||
"__fastcall",
|
||||
"__finally",
|
||||
"__forceinline",
|
||||
"__gc",
|
||||
"__hook",
|
||||
"__identifier",
|
||||
"__if_exists",
|
||||
"__if_not_exists",
|
||||
"__inline",
|
||||
"__int128",
|
||||
"__int16",
|
||||
"__int32",
|
||||
"__int64",
|
||||
"__int8",
|
||||
"__interface",
|
||||
"__leave",
|
||||
"__m128",
|
||||
"__m128d",
|
||||
"__m128i",
|
||||
"__m256",
|
||||
"__m256d",
|
||||
"__m256i",
|
||||
"__m64",
|
||||
"__multiple_inheritance",
|
||||
"__newslot",
|
||||
"__nogc",
|
||||
"__noop",
|
||||
"__nounwind",
|
||||
"__novtordisp",
|
||||
"__pascal",
|
||||
"__pin",
|
||||
"__pragma",
|
||||
"__property",
|
||||
"__ptr32",
|
||||
"__ptr64",
|
||||
"__raise",
|
||||
"__restrict",
|
||||
"__resume",
|
||||
"__sealed",
|
||||
"__single_inheritance",
|
||||
"__stdcall",
|
||||
"__super",
|
||||
"__thiscall",
|
||||
"__try",
|
||||
"__try_cast",
|
||||
"__typeof",
|
||||
"__unaligned",
|
||||
"__unhook",
|
||||
"__uuidof",
|
||||
"__value",
|
||||
"__virtual_inheritance",
|
||||
"__w64",
|
||||
"__wchar_t",
|
||||
],
|
||||
|
||||
types: [
|
||||
"void",
|
||||
"s64",
|
||||
"s32",
|
||||
"s16",
|
||||
"s8",
|
||||
"u64",
|
||||
"u32",
|
||||
"u16",
|
||||
"u8",
|
||||
"f64",
|
||||
"f32",
|
||||
"bool",
|
||||
"char",
|
||||
"int",
|
||||
"float",
|
||||
"long",
|
||||
"unsigned",
|
||||
],
|
||||
|
||||
operators: [
|
||||
"=",
|
||||
"!",
|
||||
"~",
|
||||
"?",
|
||||
":",
|
||||
"&&",
|
||||
"||",
|
||||
"++",
|
||||
"--",
|
||||
"+",
|
||||
"-",
|
||||
"*",
|
||||
"/",
|
||||
"&",
|
||||
"|",
|
||||
"^",
|
||||
"%",
|
||||
"<<",
|
||||
">>",
|
||||
">>>",
|
||||
"+=",
|
||||
"-=",
|
||||
"*=",
|
||||
"/=",
|
||||
"&=",
|
||||
"|=",
|
||||
"^=",
|
||||
"%=",
|
||||
"<<=",
|
||||
">>=",
|
||||
">>>=",
|
||||
"...",
|
||||
],
|
||||
|
||||
comparisonOperators: [
|
||||
">",
|
||||
"<",
|
||||
"==",
|
||||
"<=",
|
||||
">=",
|
||||
"!=",
|
||||
],
|
||||
|
||||
// we include these common regular expressions
|
||||
symbols: /[=><!~?:&|+\-*/^%]+/,
|
||||
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
|
||||
integersuffix: /(ll|LL|u|U|l|L)?(ll|LL|u|U|l|L)?/,
|
||||
floatsuffix: /[fFlL]?/,
|
||||
encoding: /u|u8|U|L/,
|
||||
|
||||
// The main tokenizer for our languages
|
||||
tokenizer: {
|
||||
root: [
|
||||
// Macro
|
||||
[/[A-Z][A-Z0-9_]*(?=\b)/, { token: "macro" }],
|
||||
|
||||
// PascalCase for types
|
||||
[/[A-Z]\w*/, { token: "storage.type" }],
|
||||
|
||||
// function call
|
||||
[/[a-zA-Z_]\w*(?=\()/, { token: "function" }],
|
||||
|
||||
// identifiers and keywords
|
||||
[
|
||||
/[a-zA-Z_]\w*/,
|
||||
{
|
||||
cases: {
|
||||
"@keywords": { token: "keyword.$0" },
|
||||
"@types": { token: "storage.type.$0" },
|
||||
"@default": "identifier",
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
// The preprocessor checks must be before whitespace as they check /^\s*#/ which
|
||||
// otherwise fails to match later after other whitespace has been removed.
|
||||
|
||||
// Inclusion
|
||||
[/^\s*#\s*include/, { token: "keyword.directive.include", next: "@include" }],
|
||||
|
||||
// Preprocessor directive
|
||||
[/^\s*#\s*\w+/, "keyword.directive"],
|
||||
|
||||
// whitespace
|
||||
{ include: "@whitespace" },
|
||||
|
||||
// [[ attributes ]].
|
||||
[/\[\s*\[/, { token: "annotation", next: "@annotation" }],
|
||||
// delimiters and operators
|
||||
[/[{}()[\]]/, "@brackets"],
|
||||
[/[<>](?!@symbols)/, "@brackets"],
|
||||
[
|
||||
/@symbols/,
|
||||
{
|
||||
cases: {
|
||||
"@comparisonOperators": { token: "operator.comparison" },
|
||||
"@operators": { token: "operator" },
|
||||
"@default": { token: "delimiter" },
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
// numbers
|
||||
[/\d*\d+[eE]([-+]?\d+)?(@floatsuffix)/, "number.float"],
|
||||
[/\d*\.\d+([eE][-+]?\d+)?(@floatsuffix)/, "number.float"],
|
||||
[/0[xX][0-9a-fA-F']*[0-9a-fA-F](@integersuffix)/, "number.hex"],
|
||||
[/0[0-7']*[0-7](@integersuffix)/, "number.octal"],
|
||||
[/0[bB][0-1']*[0-1](@integersuffix)/, "number.binary"],
|
||||
[/\d[\d']*\d(@integersuffix)/, "number"],
|
||||
[/\d(@integersuffix)/, "number"],
|
||||
|
||||
// delimiter: after number because of .\d floats
|
||||
[/[;,.]/, "delimiter"],
|
||||
|
||||
// strings
|
||||
[/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string
|
||||
[/"/, "string", "@string"],
|
||||
|
||||
// characters
|
||||
[/'[^\\']'/, "string"],
|
||||
[/(')(@escapes)(')/, ["string", "string.escape", "string"]],
|
||||
[/'/, "string.invalid"],
|
||||
],
|
||||
|
||||
whitespace: [
|
||||
[/[ \t\r\n]+/, ""],
|
||||
[/\/\*\*(?!\/)/, "comment.doc", "@doccomment"],
|
||||
[/\/\*/, "comment", "@comment"],
|
||||
[/\/\/.*\\$/, "comment", "@linecomment"],
|
||||
[/\/\/.*$/, "comment"],
|
||||
],
|
||||
|
||||
comment: [
|
||||
[/[^/*]+/, "comment"],
|
||||
[/\*\//, "comment", "@pop"],
|
||||
[/[/*]/, "comment"],
|
||||
],
|
||||
|
||||
//For use with continuous line comments
|
||||
linecomment: [
|
||||
[/.*[^\\]$/, "comment", "@pop"],
|
||||
[/[^]+/, "comment"],
|
||||
],
|
||||
|
||||
//Identical copy of comment above, except for the addition of .doc
|
||||
doccomment: [
|
||||
[/[^/*]+/, "comment.doc"],
|
||||
[/\*\//, "comment.doc", "@pop"],
|
||||
[/[/*]/, "comment.doc"],
|
||||
],
|
||||
|
||||
string: [
|
||||
[/[^\\"]+/, "string"],
|
||||
[/@escapes/, "string.escape"],
|
||||
[/\\./, "string.escape.invalid"],
|
||||
[/"/, "string", "@pop"],
|
||||
],
|
||||
|
||||
raw: [
|
||||
[
|
||||
/(.*)(\))(?:([^ ()\\\t"]*))(")/,
|
||||
{
|
||||
cases: {
|
||||
"$3==$S2": [
|
||||
"string.raw",
|
||||
"string.raw.end",
|
||||
"string.raw.end",
|
||||
{ token: "string.raw.end", next: "@pop" },
|
||||
],
|
||||
"@default": ["string.raw", "string.raw", "string.raw", "string.raw"],
|
||||
},
|
||||
},
|
||||
],
|
||||
[/.*/, "string.raw"],
|
||||
],
|
||||
|
||||
annotation: [
|
||||
{ include: "@whitespace" },
|
||||
[/using|alignas/, "keyword"],
|
||||
[/[a-zA-Z0-9_]+/, "annotation"],
|
||||
[/[,:]/, "delimiter"],
|
||||
[/[()]/, "@brackets"],
|
||||
[/\]\s*\]/, { token: "annotation", next: "@pop" }],
|
||||
],
|
||||
|
||||
include: [
|
||||
[
|
||||
/(\s*)(<)([^<>]*)(>)/,
|
||||
[
|
||||
"",
|
||||
"keyword.directive.include.begin",
|
||||
"string.include.identifier",
|
||||
{ token: "keyword.directive.include.end", next: "@pop" },
|
||||
] as languages.IMonarchLanguageAction,
|
||||
],
|
||||
[
|
||||
/(\s*)(")([^"]*)(")/,
|
||||
[
|
||||
"",
|
||||
"keyword.directive.include.begin",
|
||||
"string.include.identifier",
|
||||
{ token: "keyword.directive.include.end", next: "@pop" },
|
||||
] as languages.IMonarchLanguageAction,
|
||||
],
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -1,167 +0,0 @@
|
||||
import type { languages } from "monaco-editor"
|
||||
|
||||
export const conf: languages.LanguageConfiguration = {
|
||||
comments: {
|
||||
lineComment: "#",
|
||||
blockComment: ["/*", "*/"],
|
||||
},
|
||||
brackets: [
|
||||
["(", ")"],
|
||||
],
|
||||
autoClosingPairs: [
|
||||
{ open: "(", close: ")" },
|
||||
{ open: "\"", close: "\"", notIn: ["string"] },
|
||||
],
|
||||
surroundingPairs: [
|
||||
{ open: "(", close: ")" },
|
||||
{ open: "\"", close: "\"" },
|
||||
],
|
||||
}
|
||||
|
||||
export const language: languages.IMonarchLanguage = {
|
||||
defaultToken: "",
|
||||
tokenPostfix: ".mips",
|
||||
|
||||
brackets: [
|
||||
{ token: "delimiter.parenthesis", open: "(", close: ")" },
|
||||
],
|
||||
|
||||
keywords: [
|
||||
"glabel",
|
||||
],
|
||||
|
||||
instructions: [
|
||||
"lb", "lbu", "ld", "ldl", "ldr", "lh", "lhu", "ll", "lld", "lw", "lwl", "lwr", "lwu", "sb", "sc", "scd", "sd", "sdl", "sdr", "sh", "sw", "swl", "swr", "sync", "add", "addi", "addiu", "addu", "and", "andi", "dadd", "daddi", "daddiu", "daddu", "ddiv", "ddivu", "div", "divu", "dmult", "dmultu", "dsll", "dsll32", "dsllv", "dsra", "dsra32", "dsrav", "dsrl", "dsrl32", "dsrlv", "dsub", "dsubu", "lui", "mfhi", "mflo", "mthi", "mtlo", "mult", "multu", "nor", "or", "ori", "sll", "sllv", "slt", "slti", "sltiu", "sltu", "sra", "srav", "srl", "srlv", "sub", "subu", "xor", "xori", "beq", "beql", "bgez", "bgezal", "bgezall", "bgezl", "bgtz", "bgtzl", "blez", "blezl", "bltz", "bltzal", "bltzall", "bltzl", "bne", "bnel", "j", "jal", "jalr", "jr", "break", "syscall", "teq", "teqi", "tge", "tgei", "tgeiu", "tgeu", "tlt", "tlti", "tltiu", "tltu", "tne", "tnei", "cache", "dmfc0", "dmtc0", "eret", "mfc0", "mtc0", "tlbp", "tlbr", "tlbwi", "tlbwr", "bc1f", "bc1fl", "bc1t", "bc1tl", "cfc1", "ctc1", "dmfc1", "dmtc1", "ldc1", "lwc1", "mfc1", "mtc1", "sdc1", "swc1",
|
||||
"beqz", "bnez", "negu", "nop",
|
||||
],
|
||||
|
||||
registers: [
|
||||
"$zero", "$t0", "$s0", "$t8", "$at", "$t1", "$s1", "$t9", "$v0", "$t2", "$s2", "$k0", "$v1", "$t3", "$s3", "$k1", "$a0", "$t4", "$s4", "$gp", "$a1", "$t5", "$s5", "$sp", "$a2", "$t6", "$s6", "$s8", "$a3", "$t7", "$s7", "$ra",
|
||||
],
|
||||
|
||||
// we include these common regular expressions
|
||||
escapes: /\\(?:[abfnrtv\\"']|x[0-9A-Fa-f]{1,4}|u[0-9A-Fa-f]{4}|U[0-9A-Fa-f]{8})/,
|
||||
integersuffix: /(ll|LL|u|U|l|L)?(ll|LL|u|U|l|L)?/,
|
||||
encoding: /u|u8|U|L/,
|
||||
|
||||
// The main tokenizer for our languages
|
||||
tokenizer: {
|
||||
root: [
|
||||
// whitespace
|
||||
{ include: "@whitespace" },
|
||||
|
||||
// identifiers and keywords
|
||||
[
|
||||
/[a-zA-Z_]\w*/,
|
||||
{
|
||||
cases: {
|
||||
"jal": { token: "function" },
|
||||
"@instructions": { token: "support.function.$0" },
|
||||
"@keywords": { token: "keyword.$0" },
|
||||
"@default": "identifier",
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
/\$\w+/,
|
||||
{
|
||||
cases: {
|
||||
"@registers": { token: "entity.name.register.$0" },
|
||||
"@default": "identifier",
|
||||
},
|
||||
},
|
||||
],
|
||||
[/%(hi|lo)/, "macro"],
|
||||
[/\.\w+/, { token: "keyword.directive" }],
|
||||
|
||||
// delimiters and operators
|
||||
[/[()]/, "@brackets"],
|
||||
|
||||
// numbers
|
||||
[/\d*\d+[eE]([-+]?\d+)?/, "number.float"],
|
||||
[/\d*\.\d+([eE][-+]?\d+)?/, "number.float"],
|
||||
[/0[xX][0-9a-fA-F']*[0-9a-fA-F](@integersuffix)/, "number.hex"],
|
||||
[/0[0-7']*[0-7](@integersuffix)/, "number.octal"],
|
||||
[/0[bB][0-1']*[0-1](@integersuffix)/, "number.binary"],
|
||||
[/\d[\d']*\d(@integersuffix)/, "number"],
|
||||
[/\d(@integersuffix)/, "number"],
|
||||
|
||||
// delimiter: after number because of .\d floats
|
||||
[/[;,.]/, "delimiter"],
|
||||
|
||||
// strings
|
||||
[/"([^"\\]|\\.)*$/, "string.invalid"], // non-teminated string
|
||||
[/"/, "string", "@string"],
|
||||
|
||||
// characters
|
||||
[/'[^\\']'/, "string"],
|
||||
[/(')(@escapes)(')/, ["string", "string.escape", "string"]],
|
||||
[/'/, "string.invalid"],
|
||||
],
|
||||
|
||||
whitespace: [
|
||||
[/[ \t\r\n]+/, ""],
|
||||
[/\/\*/, "comment", "@comment"],
|
||||
[/#.*\\$/, "comment", "@linecomment"],
|
||||
[/#.*$/, "comment"],
|
||||
],
|
||||
|
||||
comment: [
|
||||
[/[^/*]+/, "comment"],
|
||||
[/\*\//, "comment", "@pop"],
|
||||
[/[/*]/, "comment"],
|
||||
],
|
||||
|
||||
//For use with continuous line comments
|
||||
linecomment: [
|
||||
[/.*[^#]$/, "comment", "@pop"],
|
||||
[/[^]+/, "comment"],
|
||||
],
|
||||
|
||||
string: [
|
||||
[/[^\\"]+/, "string"],
|
||||
[/@escapes/, "string.escape"],
|
||||
[/\\./, "string.escape.invalid"],
|
||||
[/"/, "string", "@pop"],
|
||||
],
|
||||
|
||||
raw: [
|
||||
[
|
||||
/(.*)(\))(?:([^ ()\\\t"]*))(")/,
|
||||
{
|
||||
cases: {
|
||||
"$3==$S2": [
|
||||
"string.raw",
|
||||
"string.raw.end",
|
||||
"string.raw.end",
|
||||
{ token: "string.raw.end", next: "@pop" },
|
||||
],
|
||||
"@default": ["string.raw", "string.raw", "string.raw", "string.raw"],
|
||||
},
|
||||
},
|
||||
],
|
||||
[/.*/, "string.raw"],
|
||||
],
|
||||
|
||||
include: [
|
||||
[
|
||||
/(\s*)(<)([^<>]*)(>)/,
|
||||
[
|
||||
"",
|
||||
"keyword.directive.include.begin",
|
||||
"string.include.identifier",
|
||||
{ token: "keyword.directive.include.end", next: "@pop" },
|
||||
] as languages.IMonarchLanguageAction,
|
||||
],
|
||||
[
|
||||
/(\s*)(")([^"]*)(")/,
|
||||
[
|
||||
"",
|
||||
"keyword.directive.include.begin",
|
||||
"string.include.identifier",
|
||||
{ token: "keyword.directive.include.end", next: "@pop" },
|
||||
] as languages.IMonarchLanguageAction,
|
||||
],
|
||||
],
|
||||
},
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
import type { editor } from "monaco-editor"
|
||||
|
||||
export default function getTheme(): editor.IStandaloneThemeData {
|
||||
const style = typeof window !== "undefined" ? window.getComputedStyle(document.body) : null
|
||||
|
||||
return {
|
||||
"base": "vs-dark",
|
||||
"inherit": false,
|
||||
"rules": [
|
||||
{ "token": "", "foreground": "8599b3" },
|
||||
{ "token": "identifier", "foreground": "8599B3" },
|
||||
{ "token": "delimiter", "foreground": "656f7e" },
|
||||
{ "token": "operator", "foreground": "7F83FF" },
|
||||
{ "token": "operator.comparison", "foreground": "FF4ABA" },
|
||||
{ "token": "comment", "foreground": "465173" },
|
||||
{ "token": "string", "foreground": "0AFAFA" },
|
||||
{ "token": "number", "foreground": "0AFAFA" },
|
||||
{ "token": "function", "foreground": "FF4A98" },
|
||||
{ "token": "constant.language", "foreground": "0AFAFA" },
|
||||
{ "token": "constant.character, constant.other", "foreground": "0AFAFA" },
|
||||
{ "token": "keyword", "foreground": "7F83FF" },
|
||||
{ "token": "entity.name", "foreground": "FF4A98" },
|
||||
{ "token": "entity.other.attribute-name", "foreground": "FF4A98" },
|
||||
{ "token": "support.function", "foreground": "45B8FF" },
|
||||
{ "token": "storage", "foreground": "45B8FF" },
|
||||
{ "token": "macro", "foreground": "3bff6c" },
|
||||
],
|
||||
"colors": {
|
||||
"editor.foreground": "#8599b3",
|
||||
"editor.background": style?.getPropertyValue?.("--g200") || "#111415",
|
||||
"editor.selectionBackground": "#ffffff22",
|
||||
"editor.lineHighlightBackground": "#ccccff07",
|
||||
"editorCursor.foreground": "#c9cbfc",
|
||||
"editorWhitespace.foreground": "#c9cbfc11",
|
||||
"editorLineNumber.foreground":"#ccccff60",
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,6 @@ export default class ErrorBoundary extends React.Component<Props, State> {
|
||||
return <div className={classNames(styles.error, this.props.className)} />
|
||||
}
|
||||
|
||||
return this.props.children
|
||||
return this.props.children || null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,7 @@ import ReactModal from "react-modal"
|
||||
|
||||
import styles from "./Modal.module.scss"
|
||||
|
||||
export type Props = {
|
||||
}
|
||||
|
||||
export default function Modal(props: ReactModal.Props & Props) {
|
||||
export default function Modal(props: ReactModal.Props) {
|
||||
|
||||
return <ReactModal
|
||||
className={styles.dialog}
|
||||
|
||||
@@ -3,7 +3,6 @@ import classNames from "classnames"
|
||||
import PlatformIcon from "./PlatformIcon"
|
||||
import styles from "./PlatformSelect.module.scss"
|
||||
|
||||
|
||||
export type Props = {
|
||||
platforms: {
|
||||
[key: string]: {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react"
|
||||
|
||||
import { act, render } from "@testing-library/react"
|
||||
import { render } from "@testing-library/react"
|
||||
|
||||
import * as api from "../../lib/api"
|
||||
|
||||
@@ -26,17 +26,16 @@ const scratchJson: api.Scratch = {
|
||||
"parent": null,
|
||||
"project": null,
|
||||
"project_function": null,
|
||||
"preset": "",
|
||||
}
|
||||
|
||||
test("renders without causing a state change", async () => {
|
||||
const onChange = jest.fn()
|
||||
|
||||
act(() => {
|
||||
render(<Scratch
|
||||
scratch={scratchJson}
|
||||
onChange={onChange}
|
||||
/>)
|
||||
})
|
||||
render(<Scratch
|
||||
scratch={scratchJson}
|
||||
onChange={onChange}
|
||||
/>)
|
||||
|
||||
expect(onChange).toHaveBeenCalledTimes(0)
|
||||
})
|
||||
|
||||
@@ -31,18 +31,21 @@ export default function Scratch({
|
||||
|
||||
const [leftTab, setLeftTab] = useState("source")
|
||||
const [rightTab, setRightTab] = useState("diff")
|
||||
const [selectedSourceLine, setSelectedSourceLine] = useState<number | null>()
|
||||
|
||||
const setCompilerOpts = setCompilerOptsFunction({ scratch, setScratch })
|
||||
|
||||
const leftTabs = useLeftTabs({
|
||||
scratch,
|
||||
setScratch,
|
||||
setSelectedSourceLine,
|
||||
})
|
||||
|
||||
const rightTabs = useRightTabs({
|
||||
compilation,
|
||||
isCompiling,
|
||||
isCompilationOld,
|
||||
selectedSourceLine,
|
||||
})
|
||||
|
||||
return <div ref={container.ref} className={styles.container}>
|
||||
|
||||
@@ -36,6 +36,12 @@
|
||||
|
||||
.editor {
|
||||
flex-grow: 1;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
:global(.cm-editor) {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.loading {
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { useEffect, useState } from "react"
|
||||
|
||||
import { basicSetup } from "@codemirror/basic-setup"
|
||||
import { cpp } from "@codemirror/lang-cpp"
|
||||
|
||||
import * as api from "../../lib/api"
|
||||
import Button from "../Button"
|
||||
import Editor from "../Editor"
|
||||
import CodeMirror from "../Editor/CodeMirror"
|
||||
import Loading from "../loading.svg"
|
||||
import Modal from "../Modal"
|
||||
|
||||
@@ -16,7 +19,7 @@ export type Props = {
|
||||
}
|
||||
|
||||
export default function ScratchDecompileModal({ open, onClose, scratch, setSourceCode }: Props) {
|
||||
const [decompiledCode, setDecompiledCode] = useState(null)
|
||||
const [decompiledCode, setDecompiledCode] = useState<string | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
@@ -43,14 +46,12 @@ export default function ScratchDecompileModal({ open, onClose, scratch, setSourc
|
||||
<p>This is generally useful when you've edited the function signature or symbols pertaining to the function.
|
||||
This new decompilation should reflect your changes. </p>
|
||||
|
||||
{decompiledCode ? <>
|
||||
<Editor
|
||||
{(typeof decompiledCode == "string") ? <>
|
||||
<CodeMirror
|
||||
className={styles.editor}
|
||||
language="c"
|
||||
value={decompiledCode}
|
||||
onChange={c => setDecompiledCode(c)}
|
||||
lineNumbers
|
||||
showMargin
|
||||
extensions={[basicSetup, cpp()]}
|
||||
/>
|
||||
<p>Would you like to reset this scratch's source code to this newly decompiled iteration?</p>
|
||||
</> : <Loading className={styles.loading} />}
|
||||
|
||||
@@ -5,11 +5,30 @@
|
||||
|
||||
.editor {
|
||||
height: 100%;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
:global(.cm-editor) {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
:global(.cm-gutters),
|
||||
:global(.cm-content) {
|
||||
margin: 1em;
|
||||
}
|
||||
|
||||
:global(.cm-gutters) {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
:global(.cm-content) {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.diffTab {
|
||||
display: flex;
|
||||
overflow: auto;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.about {
|
||||
|
||||
@@ -1,16 +1,39 @@
|
||||
import { useRef } from "react"
|
||||
|
||||
import { basicSetup, EditorView } from "@codemirror/basic-setup"
|
||||
import { indentMore, indentLess } from "@codemirror/commands"
|
||||
import { cpp } from "@codemirror/lang-cpp"
|
||||
import { indentUnit } from "@codemirror/language"
|
||||
import { StateCommand } from "@codemirror/state"
|
||||
import { keymap } from "@codemirror/view"
|
||||
|
||||
import { Compilation, Scratch, useUserIsYou } from "../../lib/api"
|
||||
import CompilerOpts from "../compiler/CompilerOpts"
|
||||
import CompilationPanel from "../Diff/CompilationPanel"
|
||||
import Editor from "../Editor"
|
||||
import { EditorInstance } from "../Editor/MonacoEditor"
|
||||
import CodeMirror from "../Editor/CodeMirror"
|
||||
import ScoreBadge from "../ScoreBadge"
|
||||
import { Tab } from "../Tabs"
|
||||
|
||||
import AboutScratch from "./AboutScratch"
|
||||
import styles from "./renderTabs.module.scss"
|
||||
|
||||
const indent: StateCommand = ({ state, dispatch }) => {
|
||||
if (state.selection.ranges.some(r => !r.empty)) return indentMore({ state, dispatch })
|
||||
dispatch(state.update(state.replaceSelection(" "), { scrollIntoView: true, userEvent: "input" }))
|
||||
return true
|
||||
}
|
||||
|
||||
const CODEMIRROR_EXTENSIONS = [
|
||||
basicSetup,
|
||||
cpp(),
|
||||
keymap.of([{
|
||||
key: "Tab",
|
||||
run: indent,
|
||||
shift: indentLess,
|
||||
}]),
|
||||
indentUnit.of(" "),
|
||||
]
|
||||
|
||||
type ScratchTab = LeftScratchTab | RightScratchTab
|
||||
|
||||
function renderTabs(tabs: {[i: number]: JSX.Element}, filter?: Array<ScratchTab>) {
|
||||
@@ -36,12 +59,13 @@ export enum RightScratchTab {
|
||||
* @param {Array<LeftScratchTab>} [filter=undefined] The tabs that you want to filter out
|
||||
* @returns Left tabs of scratch
|
||||
*/
|
||||
export function useLeftTabs({ scratch, setScratch }: {
|
||||
export function useLeftTabs({ scratch, setScratch, setSelectedSourceLine }: {
|
||||
scratch: Scratch
|
||||
setScratch: (s: Partial<Scratch>) => void
|
||||
setSelectedSourceLine: (s: number | null) => void
|
||||
}, filter?: Array<LeftScratchTab>): React.ReactElement<typeof Tab>[] {
|
||||
const sourceEditor = useRef<EditorInstance>()
|
||||
const contextEditor = useRef<EditorInstance>()
|
||||
const sourceEditor = useRef<EditorView>()
|
||||
const contextEditor = useRef<EditorView>()
|
||||
const userIsYou = useUserIsYou()
|
||||
|
||||
return renderTabs({
|
||||
@@ -50,19 +74,17 @@ export function useLeftTabs({ scratch, setScratch }: {
|
||||
key="source"
|
||||
tabKey="source"
|
||||
label="Source code"
|
||||
onSelect={() => sourceEditor.current && sourceEditor.current.focus()}
|
||||
onSelect={() => sourceEditor.current?.focus?.()}
|
||||
>
|
||||
<Editor
|
||||
instanceRef={sourceEditor}
|
||||
<CodeMirror
|
||||
viewRef={sourceEditor}
|
||||
className={styles.editor}
|
||||
language="c"
|
||||
value={scratch.source_code}
|
||||
onChange={value => {
|
||||
setScratch({ source_code: value })
|
||||
}}
|
||||
lineNumbers
|
||||
showMargin
|
||||
bubbleSuspense
|
||||
onSelectedLineChange={setSelectedSourceLine}
|
||||
extensions={CODEMIRROR_EXTENSIONS}
|
||||
/>
|
||||
</Tab>
|
||||
),
|
||||
@@ -80,19 +102,16 @@ export function useLeftTabs({ scratch, setScratch }: {
|
||||
tabKey="context"
|
||||
label="Context"
|
||||
className={styles.context}
|
||||
onSelect={() => contextEditor.current && contextEditor.current.focus()}
|
||||
onSelect={() => contextEditor.current?.focus?.()}
|
||||
>
|
||||
<Editor
|
||||
instanceRef={contextEditor}
|
||||
<CodeMirror
|
||||
viewRef={contextEditor}
|
||||
className={styles.editor}
|
||||
language="c"
|
||||
value={scratch.context}
|
||||
onChange={value => {
|
||||
setScratch({ context: value })
|
||||
}}
|
||||
lineNumbers
|
||||
showMargin
|
||||
bubbleSuspense
|
||||
extensions={CODEMIRROR_EXTENSIONS}
|
||||
/>
|
||||
</Tab>
|
||||
),
|
||||
@@ -115,10 +134,11 @@ export function useLeftTabs({ scratch, setScratch }: {
|
||||
* @param {Array<RightScratchTab>} [filter=undefined] The tabs that you want to filter out
|
||||
* @returns Right tabs of scratch
|
||||
*/
|
||||
export function useRightTabs({ compilation, isCompiling, isCompilationOld }: {
|
||||
export function useRightTabs({ compilation, isCompiling, isCompilationOld, selectedSourceLine }: {
|
||||
compilation?: Compilation
|
||||
isCompiling: boolean
|
||||
isCompilationOld: boolean
|
||||
selectedSourceLine: number | null
|
||||
}, filter?: Array<RightScratchTab>): React.ReactElement<typeof Tab>[] {
|
||||
return renderTabs({
|
||||
[RightScratchTab.DIFF]: (
|
||||
@@ -137,6 +157,7 @@ export function useRightTabs({ compilation, isCompiling, isCompilationOld }: {
|
||||
compilation={compilation}
|
||||
isCompiling={isCompiling}
|
||||
isCompilationOld={isCompilationOld}
|
||||
selectedSourceLine={selectedSourceLine}
|
||||
/>}
|
||||
</Tab>
|
||||
),
|
||||
|
||||
@@ -16,7 +16,6 @@ import ScratchIcon from "./ScratchIcon"
|
||||
import styles from "./ScratchList.module.scss"
|
||||
import UserLink from "./user/UserLink"
|
||||
|
||||
|
||||
export interface Props {
|
||||
url?: string
|
||||
className?: string
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
|
||||
.tabButton {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
|
||||
display: inline-flex;
|
||||
gap: 0.5em;
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
|
||||
font-family: monospace;
|
||||
font-family: var(--monospace);
|
||||
font-size: 0.8rem;
|
||||
|
||||
padding: 0.6em 1em;
|
||||
|
||||
@@ -63,6 +63,7 @@ export function FlagOption({ flag, description }: { flag: string, description?:
|
||||
export type CompilerOptsT = {
|
||||
compiler: string
|
||||
compiler_flags: string
|
||||
preset: string
|
||||
}
|
||||
|
||||
export type Props = {
|
||||
@@ -80,6 +81,7 @@ export default function CompilerOpts({ platform, value, onChange, isPopup }: Pro
|
||||
onChange({
|
||||
compiler,
|
||||
compiler_flags: opts,
|
||||
preset: "",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -87,6 +89,7 @@ export default function CompilerOpts({ platform, value, onChange, isPopup }: Pro
|
||||
onChange({
|
||||
compiler,
|
||||
compiler_flags: opts,
|
||||
preset: "",
|
||||
})
|
||||
}
|
||||
|
||||
@@ -94,6 +97,7 @@ export default function CompilerOpts({ platform, value, onChange, isPopup }: Pro
|
||||
onChange({
|
||||
compiler: preset.compiler,
|
||||
compiler_flags: preset.flags,
|
||||
preset: preset.name,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -116,7 +120,7 @@ export default function CompilerOpts({ platform, value, onChange, isPopup }: Pro
|
||||
<PlatformIcon platform={platform} size={32} />
|
||||
<div className={styles.preset}>
|
||||
Preset
|
||||
<PresetSelect platform={platform} flags={opts} setPreset={setPreset} />
|
||||
<PresetSelect platform={platform} presetName={value.preset} setPreset={setPreset} />
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.container} data-is-popup={isPopup}>
|
||||
|
||||
@@ -1,33 +1,43 @@
|
||||
|
||||
import * as api from "../../lib/api"
|
||||
import Select from "../Select"
|
||||
import Select from "../Select2"
|
||||
|
||||
export default function PresetSelect({ className, platform, flags, setPreset, serverPresets }: {
|
||||
function presetsToOptions(presets: api.CompilerPreset[], addCustom: boolean): { [key: string]: string } {
|
||||
const options = {}
|
||||
|
||||
if (addCustom) {
|
||||
options["Custom"] = "Custom"
|
||||
}
|
||||
|
||||
for (const preset of presets) {
|
||||
options[preset.name] = preset.name
|
||||
}
|
||||
|
||||
return options
|
||||
}
|
||||
|
||||
export default function PresetSelect({ className, platform, presetName, setPreset, serverPresets }: {
|
||||
className?: string
|
||||
platform: string
|
||||
flags: string
|
||||
presetName: string // "" for custom
|
||||
setPreset: (preset: api.CompilerPreset) => void
|
||||
serverPresets?: api.CompilerPreset[]
|
||||
}) {
|
||||
if (!serverPresets)
|
||||
serverPresets = api.usePlatforms()[platform].presets
|
||||
|
||||
const selectedPreset = serverPresets.find(p => p.flags === flags)
|
||||
const selectedPreset = serverPresets.find(p => p.name === presetName)
|
||||
|
||||
return <Select className={className} onChange={e => {
|
||||
if ((e.target as HTMLSelectElement).value === "custom") {
|
||||
return
|
||||
}
|
||||
if (!selectedPreset && presetName !== "")
|
||||
console.warn(`Scratch.preset == '${presetName}' but no preset with that name was found.`)
|
||||
|
||||
const preset = serverPresets.find(p => p.name === (e.target as HTMLSelectElement).value)
|
||||
|
||||
setPreset(preset)
|
||||
}}>
|
||||
{!selectedPreset && <option value="custom" selected>Custom</option>}
|
||||
{serverPresets.map(preset =>
|
||||
<option key={preset.name} value={preset.name} selected={preset === selectedPreset}>
|
||||
{preset.name}
|
||||
</option>
|
||||
)}
|
||||
</Select>
|
||||
return <Select
|
||||
className={className}
|
||||
options={presetsToOptions(serverPresets, !selectedPreset)}
|
||||
value={selectedPreset?.name || "Custom"}
|
||||
onChange={name => {
|
||||
const preset = serverPresets.find(p => p.name === name)
|
||||
if (preset)
|
||||
setPreset(preset)
|
||||
}}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
position: relative;
|
||||
|
||||
display: inline-block;
|
||||
|
||||
img {
|
||||
border-radius: 999px;
|
||||
overflow: hidden;
|
||||
|
||||
@@ -12,8 +12,8 @@ export type Props = {
|
||||
}
|
||||
|
||||
export default function UserAvatar({ user, className }: Props) {
|
||||
return <div className={classNames(styles.avatar, className)}>
|
||||
return <span className={classNames(styles.avatar, className)}>
|
||||
{!api.isAnonUser(user) && user.avatar_url && <Image src={user.avatar_url} alt="" layout="fill" />}
|
||||
{user.is_online && <div className={styles.online} title="Online" />}
|
||||
</div>
|
||||
</span>
|
||||
}
|
||||
|
||||
@@ -188,6 +188,7 @@ export interface Scratch extends TerseScratch {
|
||||
slug: string // avoid using, use `url` instead
|
||||
description: string
|
||||
compiler_flags: string
|
||||
preset: string
|
||||
source_code: string
|
||||
context: string
|
||||
diff_label: string
|
||||
@@ -352,6 +353,7 @@ export function useSaveScratch(localScratch: Scratch): () => Promise<Scratch> {
|
||||
context: undefinedIfUnchanged(savedScratch, localScratch, "context"),
|
||||
compiler: undefinedIfUnchanged(savedScratch, localScratch, "compiler"),
|
||||
compiler_flags: undefinedIfUnchanged(savedScratch, localScratch, "compiler_flags"),
|
||||
preset: undefinedIfUnchanged(savedScratch, localScratch, "preset"),
|
||||
name: undefinedIfUnchanged(savedScratch, localScratch, "name"),
|
||||
description: undefinedIfUnchanged(savedScratch, localScratch, "description"),
|
||||
})
|
||||
|
||||
130
frontend/src/lib/themes/dark.ts
Normal file
130
frontend/src/lib/themes/dark.ts
Normal file
@@ -0,0 +1,130 @@
|
||||
import { HighlightStyle, tags } from "@codemirror/highlight"
|
||||
import { Extension } from "@codemirror/state"
|
||||
import { EditorView } from "@codemirror/view"
|
||||
|
||||
const ivory = "#abb2bf",
|
||||
darkBackground = "#21252b",
|
||||
highlightBackground = "rgba(255, 255, 255, 0.05)",
|
||||
background = "#121415",
|
||||
tooltipBackground = "#353a42",
|
||||
selection = "rgba(255, 255, 255, 0.1)",
|
||||
cursor = "#ffffff"
|
||||
|
||||
// TODO move to css
|
||||
export const materialPalenightTheme = EditorView.theme(
|
||||
{
|
||||
"&": {
|
||||
color: "#ffffff",
|
||||
backgroundColor: background,
|
||||
},
|
||||
|
||||
".cm-content": {
|
||||
caretColor: cursor,
|
||||
},
|
||||
|
||||
"&.cm-focused .cm-cursor": {
|
||||
borderLeftColor: cursor,
|
||||
},
|
||||
|
||||
"&.cm-focused .cm-selectionBackground, .cm-selectionBackground, .cm-content ::selection":
|
||||
{ backgroundColor: selection },
|
||||
|
||||
".cm-panels": { backgroundColor: darkBackground, color: "#ffffff" },
|
||||
".cm-panels.cm-panels-top": { borderBottom: "2px solid black" },
|
||||
".cm-panels.cm-panels-bottom": { borderTop: "2px solid black" },
|
||||
|
||||
".cm-searchMatch": {
|
||||
backgroundColor: "#72a1ff59",
|
||||
outline: "1px solid #457dff",
|
||||
},
|
||||
".cm-searchMatch.cm-searchMatch-selected": {
|
||||
backgroundColor: "#6199ff2f",
|
||||
},
|
||||
|
||||
".cm-activeLine": { backgroundColor: highlightBackground },
|
||||
".cm-selectionMatch": { backgroundColor: "#aafe661a" },
|
||||
|
||||
"&.cm-focused .cm-matchingBracket, &.cm-focused .cm-nonmatchingBracket": {
|
||||
backgroundColor: "#bad0f847",
|
||||
outline: "1px solid #515a6b",
|
||||
},
|
||||
|
||||
".cm-gutters": {
|
||||
background: background,
|
||||
color: "#676e95",
|
||||
border: "none",
|
||||
},
|
||||
|
||||
".cm-activeLineGutter": {
|
||||
color: "#c6c6c6",
|
||||
backgroundColor: highlightBackground,
|
||||
},
|
||||
|
||||
".cm-foldPlaceholder": {
|
||||
backgroundColor: "transparent",
|
||||
border: "none",
|
||||
color: "#ddd",
|
||||
},
|
||||
|
||||
".cm-tooltip": {
|
||||
border: "none",
|
||||
backgroundColor: tooltipBackground,
|
||||
},
|
||||
".cm-tooltip .cm-tooltip-arrow:before": {
|
||||
borderTopColor: "transparent",
|
||||
borderBottomColor: "transparent",
|
||||
},
|
||||
".cm-tooltip .cm-tooltip-arrow:after": {
|
||||
borderTopColor: tooltipBackground,
|
||||
borderBottomColor: tooltipBackground,
|
||||
},
|
||||
".cm-tooltip-autocomplete": {
|
||||
"& > ul > li[aria-selected]": {
|
||||
backgroundColor: highlightBackground,
|
||||
color: ivory,
|
||||
},
|
||||
},
|
||||
},
|
||||
{ dark: true }
|
||||
)
|
||||
|
||||
// https://github.com/codemirror/highlight/blob/main/src/highlight.ts#L549
|
||||
export const highlightStyle = HighlightStyle.define([
|
||||
{ tag: tags.link, class: "cmt-link" },
|
||||
{ tag: tags.heading, class: "cmt-heading" },
|
||||
{ tag: tags.emphasis, class: "cmt-emphasis" },
|
||||
{ tag: tags.strong, class: "cmt-strong" },
|
||||
{ tag: tags.keyword, class: "cmt-keyword" },
|
||||
{ tag: tags.atom, class: "cmt-atom" },
|
||||
{ tag: tags.bool, class: "cmt-bool" },
|
||||
{ tag: tags.url, class: "cmt-url" },
|
||||
{ tag: tags.labelName, class: "cmt-labelName" },
|
||||
{ tag: tags.inserted, class: "cmt-inserted" },
|
||||
{ tag: tags.deleted, class: "cmt-deleted" },
|
||||
{ tag: tags.literal, class: "cmt-literal" },
|
||||
{ tag: tags.string, class: "cmt-string" },
|
||||
{ tag: tags.number, class: "cmt-number" },
|
||||
{ tag: [tags.regexp, tags.escape, tags.special(tags.string)], class: "cmt-string2" },
|
||||
{ tag: tags.variableName, class: "cmt-variableName" },
|
||||
{ tag: tags.local(tags.variableName), class: "cmt-variableName cmt-local" },
|
||||
{ tag: tags.definition(tags.variableName), class: "cmt-variableName cmt-definition" },
|
||||
{ tag: tags.special(tags.variableName), class: "cmt-variableName2" },
|
||||
{ tag: tags.definition(tags.propertyName), class: "cmt-propertyName cmt-definition" },
|
||||
{ tag: tags.typeName, class: "cmt-typeName" },
|
||||
{ tag: tags.namespace, class: "cmt-namespace" },
|
||||
{ tag: tags.className, class: "cmt-className" },
|
||||
{ tag: tags.macroName, class: "cmt-macroName" },
|
||||
{ tag: tags.propertyName, class: "cmt-propertyName" },
|
||||
{ tag: tags.operator, class: "cmt-operator" },
|
||||
{ tag: tags.comment, class: "cmt-comment" },
|
||||
{ tag: tags.meta, class: "cmt-meta" },
|
||||
{ tag: tags.invalid, class: "cmt-invalid" },
|
||||
{ tag: tags.punctuation, class: "cmt-punctuation" },
|
||||
])
|
||||
|
||||
/// Extension to enable the Material Palenight theme (both the editor theme and
|
||||
/// the highlight style).
|
||||
export const materialPalenight: Extension = [
|
||||
materialPalenightTheme,
|
||||
highlightStyle,
|
||||
]
|
||||
@@ -2,10 +2,111 @@
|
||||
|
||||
:root {
|
||||
--link: #3db8e9;
|
||||
--monospace: "Menlo", "Monaco", monospace;
|
||||
}
|
||||
|
||||
.themePlum {
|
||||
@include theme.theme(#951fd9, #292f33);
|
||||
|
||||
.cm-content {
|
||||
color: #8599b3;
|
||||
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.cm-gutters,
|
||||
.cm-content {
|
||||
font-size: 0.8rem;
|
||||
font-family: var(--monospace);
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.cmt-variableName {
|
||||
color: #ff4a98;
|
||||
}
|
||||
|
||||
.cmt-punctuation {
|
||||
color: #656f7e;
|
||||
}
|
||||
|
||||
.cmt-operator {
|
||||
color: #7f83ff;
|
||||
}
|
||||
|
||||
.cmt-comment {
|
||||
color: #465173;
|
||||
}
|
||||
|
||||
.cmt-string,
|
||||
.cmt-number,
|
||||
.cmt-bool {
|
||||
color: #0afafa;
|
||||
}
|
||||
|
||||
.cmt-typeName {
|
||||
color: #45b8ff;
|
||||
}
|
||||
|
||||
.cmt-keyword {
|
||||
color: #7f83ff;
|
||||
}
|
||||
|
||||
.cmt-meta {
|
||||
color: #3bff6c;
|
||||
}
|
||||
}
|
||||
|
||||
.cm-panel {
|
||||
font-size: 1rem;
|
||||
|
||||
// from Button.module.scss
|
||||
.cm-button {
|
||||
border-radius: 4px;
|
||||
padding: 0.4em 0.8em;
|
||||
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
|
||||
user-select: none;
|
||||
appearance: none;
|
||||
|
||||
display: inline-flex;
|
||||
gap: 0.5em;
|
||||
align-items: center;
|
||||
|
||||
background: transparent;
|
||||
color: var(--a800);
|
||||
border: 1px solid var(--g500);
|
||||
|
||||
&:not(:disabled) {
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
color: var(--a900);
|
||||
border-color: var(--g800);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.cm-textfield {
|
||||
padding: 0.4em 0.6em;
|
||||
|
||||
color: var(--g1200);
|
||||
background: var(--g200);
|
||||
font: 0.8rem var(--monospace);
|
||||
border: 1px solid var(--g500);
|
||||
border-radius: 4px;
|
||||
|
||||
outline: none !important;
|
||||
|
||||
&::-webkit-input-placeholder {
|
||||
color: var(--g700);
|
||||
}
|
||||
}
|
||||
|
||||
input[type=checkbox] {
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
|
||||
html {
|
||||
@@ -71,3 +172,8 @@ main {
|
||||
.routerProgressBar {
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
div {
|
||||
scrollbar-color: #fff3 transparent;
|
||||
scrollbar-width: thin;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@
|
||||
|
||||
color: var(--g1200);
|
||||
background: var(--g200);
|
||||
font: 0.8em monospace;
|
||||
font: 0.8rem var(--monospace);
|
||||
border: 1px solid var(--g500);
|
||||
border-radius: 4px;
|
||||
|
||||
@@ -88,6 +88,12 @@
|
||||
|
||||
border: 1px solid var(--g500);
|
||||
border-radius: 4px;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
:global(.cm-editor) {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.compilerContainer {
|
||||
|
||||
@@ -5,13 +5,15 @@ import { GetStaticProps } from "next"
|
||||
import Link from "next/link"
|
||||
import { useRouter } from "next/router"
|
||||
|
||||
import { basicSetup } from "@codemirror/basic-setup"
|
||||
import { cpp } from "@codemirror/lang-cpp"
|
||||
import { usePlausible } from "next-plausible"
|
||||
import useTranslation from "next-translate/useTranslation"
|
||||
|
||||
import AsyncButton from "../components/AsyncButton"
|
||||
import { useCompilersForPlatform } from "../components/compiler/compilers"
|
||||
import PresetSelect from "../components/compiler/PresetSelect"
|
||||
import Editor from "../components/Editor"
|
||||
import CodeMirror from "../components/Editor/CodeMirror"
|
||||
import Footer from "../components/Footer"
|
||||
import Nav from "../components/Nav"
|
||||
import PageTitle from "../components/PageTitle"
|
||||
@@ -74,6 +76,7 @@ export default function NewScratch({ serverCompilers }: {
|
||||
const [platform, setPlatform] = useState("")
|
||||
const [compilerId, setCompiler] = useState<string>()
|
||||
const [compilerFlags, setCompilerFlags] = useState<string>("")
|
||||
const [presetName, setPresetName] = useState<string>("")
|
||||
|
||||
const router = useRouter()
|
||||
const plausible = usePlausible()
|
||||
@@ -84,11 +87,10 @@ export default function NewScratch({ serverCompilers }: {
|
||||
}, [asm])
|
||||
const [label, setLabel] = useState<string>("")
|
||||
|
||||
const [lineNumbers, setLineNumbers] = useState(false)
|
||||
|
||||
const setPreset = (preset: api.CompilerPreset) => {
|
||||
setCompiler(preset.compiler)
|
||||
setCompilerFlags(preset.flags)
|
||||
setPresetName(preset.name)
|
||||
}
|
||||
|
||||
// Load fields from localStorage
|
||||
@@ -100,6 +102,7 @@ export default function NewScratch({ serverCompilers }: {
|
||||
setPlatform(localStorage["new_scratch_platform"] ?? "")
|
||||
setCompiler(localStorage["new_scratch_compiler"] ?? undefined)
|
||||
setCompilerFlags(localStorage["new_scratch_compilerFlags"] ?? "")
|
||||
setPresetName(localStorage["new_scratch_presetName"] ?? "")
|
||||
} catch (error) {
|
||||
console.warn("bad localStorage", error)
|
||||
}
|
||||
@@ -113,7 +116,8 @@ export default function NewScratch({ serverCompilers }: {
|
||||
localStorage["new_scratch_platform"] = platform
|
||||
localStorage["new_scratch_compiler"] = compilerId
|
||||
localStorage["new_scratch_compilerFlags"] = compilerFlags
|
||||
}, [label, asm, context, platform, compilerId, compilerFlags])
|
||||
localStorage["new_scratch_presetName"] = presetName
|
||||
}, [label, asm, context, platform, compilerId, compilerFlags, presetName])
|
||||
|
||||
const platformCompilers = useCompilersForPlatform(platform, serverCompilers.compilers)
|
||||
const compiler = platformCompilers[compilerId]
|
||||
@@ -150,6 +154,7 @@ export default function NewScratch({ serverCompilers }: {
|
||||
platform,
|
||||
compiler: compilerId,
|
||||
compiler_flags: compilerFlags,
|
||||
preset: presetName,
|
||||
diff_label: label || defaultLabel || "",
|
||||
})
|
||||
|
||||
@@ -162,7 +167,6 @@ export default function NewScratch({ serverCompilers }: {
|
||||
|
||||
await router.push(scratch.html_url)
|
||||
} catch (error) {
|
||||
setLineNumbers(true) // line numbers are likely relevant to the error
|
||||
console.error(error)
|
||||
throw error
|
||||
}
|
||||
@@ -222,7 +226,7 @@ export default function NewScratch({ serverCompilers }: {
|
||||
<PresetSelect
|
||||
className={styles.compilerChoiceSelect}
|
||||
platform={platform}
|
||||
flags={compilerFlags}
|
||||
presetName={presetName}
|
||||
setPreset={setPreset}
|
||||
serverPresets={platform && serverCompilers.platforms[platform].presets}
|
||||
/>
|
||||
@@ -243,32 +247,29 @@ export default function NewScratch({ serverCompilers }: {
|
||||
placeholder={defaultLabel}
|
||||
onChange={e => setLabel((e.target as HTMLInputElement).value)}
|
||||
className={styles.textInput}
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.editorContainer}>
|
||||
<p className={styles.label}>Target assembly <small>(required)</small></p>
|
||||
<Editor
|
||||
<CodeMirror
|
||||
className={styles.editor}
|
||||
language="mips"
|
||||
value={asm}
|
||||
onChange={setAsm}
|
||||
padding={10}
|
||||
showMargin={lineNumbers}
|
||||
lineNumbers={lineNumbers}
|
||||
extensions={basicSetup}
|
||||
/>
|
||||
</div>
|
||||
<div className={styles.editorContainer}>
|
||||
<p className={styles.label}>
|
||||
Context <small>(any typedefs, structs, and declarations you would like to include go here; typically generated with m2ctx.py)</small>
|
||||
</p>
|
||||
<Editor
|
||||
<CodeMirror
|
||||
className={styles.editor}
|
||||
language="c"
|
||||
value={context}
|
||||
onChange={setContext}
|
||||
padding={10}
|
||||
showMargin={lineNumbers}
|
||||
lineNumbers={lineNumbers}
|
||||
extensions={[basicSetup, cpp()]}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Suspense, useState } from "react"
|
||||
import { Suspense, useState, useEffect } from "react"
|
||||
|
||||
import { GetServerSideProps } from "next"
|
||||
|
||||
import useSWR from "swr"
|
||||
import { useDebouncedCallback } from "use-debounce"
|
||||
|
||||
import LoadingSpinner from "../../components/loading.svg"
|
||||
import PageTitle from "../../components/PageTitle"
|
||||
@@ -60,7 +61,8 @@ export const getServerSideProps: GetServerSideProps = async context => {
|
||||
}
|
||||
|
||||
export default function ScratchPage({ initialScratch, initialCompilation }: { initialScratch: api.Scratch, initialCompilation?: api.Compilation }) {
|
||||
const [scratch, setScratch] = useState(initialScratch)
|
||||
const [scratch, setScratchImmediate] = useState(initialScratch)
|
||||
const setScratch = useDebouncedCallback(setScratchImmediate, 100, { leading: true, trailing: true }) // reduce layout thrashing
|
||||
|
||||
useWarnBeforeScratchUnload(scratch)
|
||||
|
||||
@@ -81,6 +83,21 @@ export default function ScratchPage({ initialScratch, initialCompilation }: { in
|
||||
setScratch(scratch => ({ ...scratch, owner: cached.owner }))
|
||||
}
|
||||
|
||||
// Scratch uses suspense but SSR does not support it so we just render a loading state
|
||||
// in server-side rendering mode.
|
||||
const [isMounted, setIsMounted] = useState(false)
|
||||
useEffect(() => {
|
||||
setIsMounted(true)
|
||||
}, [])
|
||||
if (!isMounted) {
|
||||
return <>
|
||||
<ScratchPageTitle scratch={scratch} compilation={initialCompilation} />
|
||||
<main className={styles.container}>
|
||||
<LoadingSpinner className={styles.loading} />
|
||||
</main>
|
||||
</>
|
||||
}
|
||||
|
||||
return <>
|
||||
<ScratchPageTitle scratch={scratch} compilation={initialCompilation} />
|
||||
<main className={styles.container}>
|
||||
|
||||
@@ -15,7 +15,8 @@
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"esModuleInterop": true,
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true
|
||||
"isolatedModules": true,
|
||||
"incremental": true
|
||||
},
|
||||
"include": [
|
||||
"next-env.d.ts",
|
||||
|
||||
7496
frontend/yarn.lock
7496
frontend/yarn.lock
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user