commit 715224653ddf6505ebd9eb8cd8fbbd87c427445d Author: Neill Cox Date: Wed Jun 26 14:29:06 2024 +1000 Initial Commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b2d3c5b --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +db.sqlite3 +.idea/ +.venv/ +.hidden_notes.txt diff --git a/django_gurps/__init__.py b/django_gurps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django_gurps/__pycache__/__init__.cpython-312.pyc b/django_gurps/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..9dcecde Binary files /dev/null and b/django_gurps/__pycache__/__init__.cpython-312.pyc differ diff --git a/django_gurps/__pycache__/settings.cpython-312.pyc b/django_gurps/__pycache__/settings.cpython-312.pyc new file mode 100644 index 0000000..5dae058 Binary files /dev/null and b/django_gurps/__pycache__/settings.cpython-312.pyc differ diff --git a/django_gurps/__pycache__/urls.cpython-312.pyc b/django_gurps/__pycache__/urls.cpython-312.pyc new file mode 100644 index 0000000..e73fb39 Binary files /dev/null and b/django_gurps/__pycache__/urls.cpython-312.pyc differ diff --git a/django_gurps/__pycache__/wsgi.cpython-312.pyc b/django_gurps/__pycache__/wsgi.cpython-312.pyc new file mode 100644 index 0000000..31949f8 Binary files /dev/null and b/django_gurps/__pycache__/wsgi.cpython-312.pyc differ diff --git a/django_gurps/asgi.py b/django_gurps/asgi.py new file mode 100644 index 0000000..a38cee8 --- /dev/null +++ b/django_gurps/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for django_gurps project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_gurps.settings") + +application = get_asgi_application() diff --git a/django_gurps/settings.py b/django_gurps/settings.py new file mode 100644 index 0000000..04ce47d --- /dev/null +++ b/django_gurps/settings.py @@ -0,0 +1,157 @@ +""" +Django settings for django_gurps project. + +Generated by 'django-admin startproject' using Django 5.0.1. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.0/ref/settings/ +""" + +import os +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = "django-insecure-x%rs&gy9iphje-l+!^!g@s@w$4@oc0^honvnr-!edwm+uujiu2" + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [ + "gcs.neill.id.au", + "localhost", + "127.0.0.1", + ] + +CSRF_TRUSTED_ORIGINS = [ + "https://gcs.neill.id.au", + ] + + +# Application definition + +INSTALLED_APPS = [ + "gurps_character", + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", + "tailwind", + "theme", + "django_browser_reload", +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "django_browser_reload.middleware.BrowserReloadMiddleware", +] + +ROOT_URLCONF = "django_gurps.urls" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + }, +] + +WSGI_APPLICATION = "django_gurps.wsgi.application" + + +# Database +# https://docs.djangoproject.com/en/5.0/ref/settings/#databases + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.0/topics/i18n/ + +LANGUAGE_CODE = "en-us" + +TIME_ZONE = "UTC" + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.0/howto/static-files/ + +STATIC_URL = "static/" + +# Default primary key field type +# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.postgresql", + "NAME": os.environ["GURPS_DATABASE_NAME"], + "USER": os.environ["GURPS_DATABASE_USER"], + "PASSWORD": os.environ["GURPS_DATABASE_PASSWORD"], + "HOST": os.environ["GURPS_DATABASE_HOST"], + "PORT": os.environ["GURPS_DATABASE_PORT"], + } +} + +TAILWIND_APP_NAME = 'theme' + +INTERNAL_IPS = [ + "127.0.0.1", +] + +LOGIN_REDIRECT_URL = "home" +LOGOUT_REDIRECT_URL = "home" diff --git a/django_gurps/urls.py b/django_gurps/urls.py new file mode 100644 index 0000000..438814d --- /dev/null +++ b/django_gurps/urls.py @@ -0,0 +1,29 @@ +""" +URL configuration for django_gurps project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import include, path +from django.views.generic.base import TemplateView +from django.contrib.auth import views + +urlpatterns = [ + path("gurps/", include("gurps_character.urls")), + path("admin/", admin.site.urls), + path("__reload__/", include("django_browser_reload.urls")), + path("accounts/", include("django.contrib.auth.urls")), + path("", TemplateView.as_view(template_name="home.html"), name="home"), + path('logout/', views.LogoutView.as_view(), name='logout'), +] diff --git a/django_gurps/views.py b/django_gurps/views.py new file mode 100644 index 0000000..8ebfb7f --- /dev/null +++ b/django_gurps/views.py @@ -0,0 +1 @@ +def logout(request): diff --git a/django_gurps/wsgi.py b/django_gurps/wsgi.py new file mode 100644 index 0000000..7ba244a --- /dev/null +++ b/django_gurps/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for django_gurps project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_gurps.settings") + +application = get_wsgi_application() diff --git a/gurps_character/__init__.py b/gurps_character/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gurps_character/__pycache__/__init__.cpython-312.pyc b/gurps_character/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..36d9788 Binary files /dev/null and b/gurps_character/__pycache__/__init__.cpython-312.pyc differ diff --git a/gurps_character/__pycache__/admin.cpython-312.pyc b/gurps_character/__pycache__/admin.cpython-312.pyc new file mode 100644 index 0000000..32c2897 Binary files /dev/null and b/gurps_character/__pycache__/admin.cpython-312.pyc differ diff --git a/gurps_character/__pycache__/apps.cpython-312.pyc b/gurps_character/__pycache__/apps.cpython-312.pyc new file mode 100644 index 0000000..7e1d579 Binary files /dev/null and b/gurps_character/__pycache__/apps.cpython-312.pyc differ diff --git a/gurps_character/__pycache__/forms.cpython-312.pyc b/gurps_character/__pycache__/forms.cpython-312.pyc new file mode 100644 index 0000000..6c4f44f Binary files /dev/null and b/gurps_character/__pycache__/forms.cpython-312.pyc differ diff --git a/gurps_character/__pycache__/models.cpython-312.pyc b/gurps_character/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000..30c54f8 Binary files /dev/null and b/gurps_character/__pycache__/models.cpython-312.pyc differ diff --git a/gurps_character/__pycache__/urls.cpython-312.pyc b/gurps_character/__pycache__/urls.cpython-312.pyc new file mode 100644 index 0000000..39ad717 Binary files /dev/null and b/gurps_character/__pycache__/urls.cpython-312.pyc differ diff --git a/gurps_character/__pycache__/views.cpython-312.pyc b/gurps_character/__pycache__/views.cpython-312.pyc new file mode 100644 index 0000000..1d91d59 Binary files /dev/null and b/gurps_character/__pycache__/views.cpython-312.pyc differ diff --git a/gurps_character/admin.py b/gurps_character/admin.py new file mode 100644 index 0000000..8c7741d --- /dev/null +++ b/gurps_character/admin.py @@ -0,0 +1,7 @@ +from django.contrib import admin + +# Register your models here. +from .models import GURPSCharacter, GameSystem + +admin.site.register(GURPSCharacter) +admin.site.register(GameSystem) diff --git a/gurps_character/apps.py b/gurps_character/apps.py new file mode 100644 index 0000000..726ed8e --- /dev/null +++ b/gurps_character/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class GurpsCharacterConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "gurps_character" diff --git a/gurps_character/forms.py b/gurps_character/forms.py new file mode 100644 index 0000000..772fe64 --- /dev/null +++ b/gurps_character/forms.py @@ -0,0 +1,28 @@ +import json +from django import forms +from django.core.exceptions import ValidationError + + +def validate_file(value): + if value.size > 10**6: + raise ValidationError("File too large") + + try: + data = json.loads(value.read()) + except json.JSONDecodeError: + raise ValidationError("Not a GCS file - json decode") + + try: + version = data['version'] + except KeyError: + import bpdb;bpdb.set_trace() + raise ValidationError("Not a GCS file - key error") + + if version < 4: + raise ValidationError( + f"The file version ({version}) is too old. Please use a newer version (5.20.3) of GCS." + ) + + +class UploadFileForm(forms.Form): + file = forms.FileField(validators=[validate_file]) diff --git a/gurps_character/migrations/0001_initial.py b/gurps_character/migrations/0001_initial.py new file mode 100644 index 0000000..581e4bd --- /dev/null +++ b/gurps_character/migrations/0001_initial.py @@ -0,0 +1,28 @@ +# Generated by Django 5.0.1 on 2024-01-08 10:25 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + initial = True + + dependencies = [] + + operations = [ + migrations.CreateModel( + name="GURPSCharacter", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("uuid", models.CharField(max_length=128, unique=True)), + ("name", models.CharField(max_length=255, unique=True)), + ], + ), + ] diff --git a/gurps_character/migrations/0002_gurpscharacter_details.py b/gurps_character/migrations/0002_gurpscharacter_details.py new file mode 100644 index 0000000..f1f69a9 --- /dev/null +++ b/gurps_character/migrations/0002_gurpscharacter_details.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.1 on 2024-01-10 07:52 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("gurps_character", "0001_initial"), + ] + + operations = [ + migrations.AddField( + model_name="gurpscharacter", + name="details", + field=models.JSONField(default={}), + preserve_default=False, + ), + ] diff --git a/gurps_character/migrations/0003_gamesystem.py b/gurps_character/migrations/0003_gamesystem.py new file mode 100644 index 0000000..b26628c --- /dev/null +++ b/gurps_character/migrations/0003_gamesystem.py @@ -0,0 +1,28 @@ +# Generated by Django 5.0.1 on 2024-01-16 07:12 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("gurps_character", "0002_gurpscharacter_details"), + ] + + operations = [ + migrations.CreateModel( + name="GameSystem", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ("name", models.CharField(max_length=255, unique=True)), + ("description", models.TextField(null=True)), + ], + ), + ] diff --git a/gurps_character/migrations/__init__.py b/gurps_character/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/gurps_character/migrations/__pycache__/0001_initial.cpython-312.pyc b/gurps_character/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 0000000..b48ce8a Binary files /dev/null and b/gurps_character/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/gurps_character/migrations/__pycache__/0002_gurpscharacter_details.cpython-312.pyc b/gurps_character/migrations/__pycache__/0002_gurpscharacter_details.cpython-312.pyc new file mode 100644 index 0000000..79d2c45 Binary files /dev/null and b/gurps_character/migrations/__pycache__/0002_gurpscharacter_details.cpython-312.pyc differ diff --git a/gurps_character/migrations/__pycache__/0003_gamesystem.cpython-312.pyc b/gurps_character/migrations/__pycache__/0003_gamesystem.cpython-312.pyc new file mode 100644 index 0000000..9c7536a Binary files /dev/null and b/gurps_character/migrations/__pycache__/0003_gamesystem.cpython-312.pyc differ diff --git a/gurps_character/migrations/__pycache__/__init__.cpython-312.pyc b/gurps_character/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..24fb52f Binary files /dev/null and b/gurps_character/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/gurps_character/models.py b/gurps_character/models.py new file mode 100644 index 0000000..cb70807 --- /dev/null +++ b/gurps_character/models.py @@ -0,0 +1,391 @@ +from django.db import models + +# Create your models here. + +class GameSystem(models.Model): + name = models.CharField(max_length=255, unique=True) + description = models.TextField(null=True) + + + +class GURPSCharacter(models.Model): + uuid = models.CharField(max_length=128, unique=True) + name = models.CharField(max_length=255, unique=True) + details = models.JSONField() + # owner => user + # campaign => campaign + + def adv_points(self): + return sum([ a['calc']['points'] for a in self.details.get('advantages',[]) if a['calc']['points'] > 0 ]) + + def disadv_points(self): + return sum([ a['calc']['points'] for a in self.details.get('advantages', []) if a['calc']['points'] < -1 ]) + + def quirks_points(self): + return sum([ a['calc']['points'] for a in self.details.get('advantages',[]) if a['calc']['points'] == -1 ]) + + def attr_points(self): + return sum([ a['calc']['points'] for a in self.details['attributes'] ]) + + def skills_points(self): + return sum([ s['points'] for s in self.details.get('skills',[]) ]) + + def spells_points(self): + return 0 + + def race_points(self): + return 0 + + def unspent_points(self): + return self.details['total_points'] - self.adv_points() - \ + self.disadv_points() - self.attr_points() - \ + self.skills_points() - self.spells_points() - \ + self.race_points() - self.quirks_points() + + def get_primary_attr(self, attr_id): + return [ a['calc'] for a in self.details['attributes'] if a['attr_id'] == attr_id ][0] + + def st(self): + return self.get_primary_attr('st') + + def iq(self): + return self.get_primary_attr('iq') + + def dx(self): + return self.get_primary_attr('dx') + + def ht(self): + return self.get_primary_attr('ht') + + def will(self): + return self.get_primary_attr('will') + + def fright_check(self): + return self.get_primary_attr('fright_check') + + def per(self): + return self.get_primary_attr('per') + + def vision(self): + return self.get_primary_attr('vision') + + def hearing(self): + return self.get_primary_attr('hearing') + + def taste_smell(self): + return self.get_primary_attr('taste_smell') + + def touch(self): + return self.get_primary_attr('touch') + + def basic_move(self): + return self.get_primary_attr('basic_move') + + def basic_speed(self): + return self.get_primary_attr('basic_speed') + + def hp(self): + return self.get_primary_attr('hp') + + def fp(self): + return self.get_primary_attr('fp') + + def weight_carried(self): + items = [ i['calc']['extended_weight'] for i in self.details['equipment'] ] + + total_weight = 0 + for i in items: + total_weight += float(i.split()[0]) + + return total_weight + + def enc_level(self): + if self.weight_carried() <= self.basic_lift(): + return 0 + elif self.weight_carried() <= self.basic_lift() * 2: + return 1 + elif self.weight_carried() <= self.basic_lift() * 3: + return 2 + elif self.weight_carried() <= self.basic_lift() * 6: + return 3 + else: + return 4 + + def enc_levels(self): + enc_level = self.enc_level() + dodge = self.details["calc"]["dodge"][0] + basic_move = self.basic_move()["value"] + return [ + { "max_load": round(self.basic_lift()), "move": basic_move, "dodge": dodge, "current": "current" * (enc_level == 0) }, + { "max_load": round(self.basic_lift()) * 2, "move": basic_move -2, "dodge": dodge -1, "current": "current" * (enc_level == 1) }, + { "max_load": round(self.basic_lift()) * 3, "move": basic_move -3, "dodge": dodge -2, "current": "current" * (enc_level == 2) }, + { "max_load": round(self.basic_lift()) * 6, "move": basic_move -4, "dodge": dodge -3, "current": "current" * (enc_level == 3) }, + { "max_load": round(self.basic_lift()) * 10, "move": basic_move -5, "dodge": dodge -4, "current": "current" * (enc_level == 4) }, + ] + + def basic_lift(self): + return float(self.details["calc"]["basic_lift"].split()[0]) + + def weight_unit(self): + return self.details['settings']['default_weight_units'] + + def lift_table(self): + return [ + {"value": round(self.basic_lift()), "label":"Basic Lift"}, + {"value": round(self.basic_lift()) * 2, "label": "One-Handed Lift" }, + {"value": round(self.basic_lift()) * 8, "label":"Two-Handed Lift"}, + {"value": round(self.basic_lift()) * 12, "label":"Shove &ersand; Knock Over"}, + {"value": round(self.basic_lift()) * 24, "label":"Running Shove & Knock Over"}, + {"value": round(self.basic_lift()) * 15, "label":"Carry on Back"}, + {"value": round(self.basic_lift()) * 50, "label":"Shift Slightly"}, + ] + + def reaction_modifiers(self): + modifiers = [] + + for a in self.details.get('advantages',[]): + if "features" in a: + for f in a["features"]: + if f["type"] == "reaction_bonus": + modifiers.append(f) + + return modifiers + def conditional_modifiers(self): + modifiers = [] + + for a in self.details.get('advantages', []): + if "features" in a: + for f in a["features"]: + if f["type"] == "conditional_modifier": + modifiers.append(f) + + return modifiers + + def weapons(self): + return [ w for w in self.details['equipment'] if "weapons" in w] + \ + [ a for a in self.details.get("advantages",[]) if "weapons" in a ] + \ + [ a for a in self.details.get("traits",[]) if "weapons" in a ] + \ + [ s for s in self.details.get("spells", []) if "weapons" in s ] + + def melee_weapons(self): + mw = [] + for item in self.weapons(): + for weapon in item["weapons"]: + if weapon["type"] == "melee_weapon": + if "description" in item: + name = item["description"] + else: + name = item["name"] + mw.append( + { + "name":name, + "usage": weapon["usage"], + "skill_level":weapon["calc"].get("level", 0), + "parry":weapon["calc"].get("parry", "No"), + "block":weapon["calc"].get("block", "No"), + "damage":weapon["calc"]["damage"], + "reach":weapon["calc"].get("reach", ""), + "strength":weapon.get("strength", " ") + } + ) + + return mw + + def ranged_weapons(self): + def muscle_range(wpn_range): + if wpn_range.startswith("x"): + ranges = wpn_range.split("/") + new_ranges = [] + for wpn_range in ranges: + wpn_range = float(wpn_range[1:]) + wpn_range = wpn_range * self.st()["value"] + wpn_range = str(wpn_range) + wpn_range = wpn_range[:wpn_range.index(".")] + new_ranges.append(wpn_range) + + return "/".join(new_ranges) + else: + return wpn_range + + + rw = [] + for item in self.weapons(): + for weapon in item["weapons"]: + if weapon["type"] == "ranged_weapon": + if "description" in item: + name = item["description"] + else: + name = item["name"] + rw.append( + { + "name":name, + "bulk": weapon.get("bulk", " "), + "usage": weapon.get("usage", " "), + "skill_level":weapon["calc"]["level"], + "damage":weapon["calc"]["damage"], + "strength":weapon.get("strength", " "), + "acc":weapon.get("accuracy", 0), + "range": muscle_range(weapon.get("range", " ")), + "rof": weapon.get("rate_of_fire", " "), + "shots": weapon.get("shots", " "), + "recoil": weapon.get("recoil", " ") + } + ) + + return rw + + def traits(self): + traits = [] + for advantage in self.details.get("advantages",[]): + cost = advantage["calc"]["points"] + name = advantage["name"] + + if "categories" in advantage and "Language" in advantage["categories"]: + levels = [ m for m in advantage['modifiers'] if "disabled" not in m ] + + notes = "" + for level in levels: + notes += f"{level['name']}" + if "notes" in level: + notes += f" ({level['notes']}); " + else: + notes += "; " + + notes = notes[:-2] + + elif "notes" in advantage: + notes = advantage["notes"] + elif "modifiers" in advantage: + notes = [ m for m in advantage['modifiers'] if m["cost"] == cost ][0]["name"] + else: + notes = "" + + traits.append({"name":name, "notes":notes, "points":cost, "reference":advantage["reference"]}) + + return traits + + def spells(self): + def get_casting_details(spell): + level = spell['calc']['level'] + + if level < 10: + descr = ( + "Ritual: Need both hands and both feet " + "free, and must speak .Time: 2x." + ) + elif level <15: + descr = ( + "Ritual: Must speak a few quiet words " + "and make a gesture." + ) + elif level < 20: + descr = ( + "Ritual: Must speak a word or two or make " + "a small gesture. May move one yard per second " + "while concentrating. Cost: -1." + ) + elif level < 25: + descr = "Ritual: None. Time: / 2 (round up). Cost: -2." + elif level < 30: + descr = ( + "Ritual: None. Time: / 4 (round up). Cost: -3." + ) + else: + delta = int((level - 25) / 5) + power = 2 + delta + divisor = 2**power + cost = 3 + delta + descr = ( + f"Ritual: None. Time: / {power} round up. Cost: " + f"-{cost}" + ) + + return descr + + + spells = [] + for spell in self.details.get('spells',[]): + notes = ( + f"{get_casting_details(spell)}
Class: {spell['spell_class']}; " + f"Cost: {spell['casting_cost']}; Maintain: {spell['maintenance_cost']}; Time: {spell['casting_time']}; Duration: {spell['duration']}; " + ) + spells.append( + { + 'name': spell["name"], + "college":", ".join(spell["college"]), + "level": spell["calc"]["level"], + "rsl":spell["calc"]["rsl"], + "points":spell["points"], + "reference":spell["reference"], + "notes":notes + } + ) + return spells + + def skills(self): + skills = [] + for skill in self.details.get('skills',[]): + skills.append({'name': skill["name"], "level": skill["calc"]["level"], "rsl":skill["calc"]["rsl"], "points":skill["points"], "reference":skill["reference"] }) + return skills + + def equipment(self): + def get_children(parent, level=1): + children = [] + + for item in parent["children"]: + equipment = { + "name": item["description"], + "uses":"", + "tech_level":item["tech_level"], + "lc":"", + "value":item["value"], + "weight":item["weight"], + "quantity":item.get("quantity",1), + "ref":item["reference"], + "ext_weight": item["calc"]["extended_weight"], + "ext_value": item["calc"]["extended_value"], + "notes": item.get("notes",""), + "equipped": item["equipped"], + "level": level + } + children.append(equipment) + + if "children" in item: + children += get_children(item, level +1) + + return children + equipment = self.details['equipment'] + + equipment_list = [] + for item in equipment: + equipment_list.append( + { + "name": item["description"], + "uses":"", + "tech_level":item["tech_level"], + "lc":"", + "level": 0, + "value":item["value"], + "weight":item["weight"], + "quantity":item.get("quantity",1), + "ref":item.get("reference",""), + "ext_weight": item["calc"]["extended_weight"], + "ext_value": item["calc"]["extended_value"], + "notes": item.get("notes",""), + "equipped": item["equipped"] + } + ) + + if 'children' in item: + equipment_list += get_children(item) + + return equipment_list + + def hit_locations(self): + try: + # v2 + return self.details["settings"]["hit_locations"]["locations"] + except KeyError: + # v4 + return self.details["settings"]["body_type"]["locations"] + diff --git a/gurps_character/templates/characters/details.css b/gurps_character/templates/characters/details.css new file mode 100644 index 0000000..4f981c6 --- /dev/null +++ b/gurps_character/templates/characters/details.css @@ -0,0 +1,1037 @@ + diff --git a/gurps_character/templates/characters/details.html b/gurps_character/templates/characters/details.html new file mode 100644 index 0000000..a8f5b86 --- /dev/null +++ b/gurps_character/templates/characters/details.html @@ -0,0 +1,1626 @@ + + + + + + + {{ name }} + + + + + + + +
+
+
+
+
Portrait
+
+
+
+
Identity
+
+
Name
+
{{ character.details.profile.name }}
+
Title
+
+
Organization
+
+
+
+
+
Miscellaneous
+
+
Created
+
{{ character.details.created_date }}
+
Modified
+
{{ character.details.modified_date }}
+
Player
+
{{ character.details.profile.player_name }}
+
+
+
+
Description
+
+
Gender
+
{{ character.details.profile.gender }}
+
Age
+
{{ character.details.profile.age }}
+
Birthday
+
{{ character.details.profile.birthday }}
+
Religion
+
+
+
+
Height
+
{{ character.details.profile.height }}
+
Weight
+
{{ character.details.profile.weight }}
+
Size
+
??
+
TL
+
{{ character.details.profile.tech_level }}
+
+
+
Hair
+
{{ character.details.profile.hair }}
+
Eyes
+
{{ character.details.profile.eyes }}
+
Skin
+
{{ character.details.profile.skin }}
+
Hand
+
{{ character.details.profile.handedness }}
+
+
+
+
{{ character.details.total_points }} Points
+
+
{{ character.unspent_points }}
+
Unspent
+
{{ character.race_points }}
+
Race
+
{{ character.attr_points }}
+
Attributes
+
{{ character.attr_points }}
+
Advantages
+
{{ character.disadv_points }}
+
Disadvantages
+
{{ character.quirks_points }}
+
Quirks
+
{{ character.skills_points }}
+
Skills
+
{{ character.spells_points }}
+
Spells
+
+
+
+
+
+
+
Primary Attributes
+
+ +
[{{ character.st.points }}]
+
{{ character.st.value }}
+
Strength (ST)
+ +
[{{ character.dx.points }}]
+
{{ character.dx.value }}
+
Dexterity (DX)
+ +
[{{ character.iq.points }}]
+
{{ character.iq.value }}
+
Intelligence (IQ)
+ +
[{{ character.ht.points }}]
+
{{ character.ht.value }}
+
Health (HT)
+ +
+
+
+
Basic Damage
+
+
[20]
+
{{ character.details.calc.thrust }}
+
Basic Thrust
+
[20]
+
{{ character.details.calc.swing }}
+
Basic Swing
+
+
+
+
Secondary Attributes
+
+ +
[{{ character.will.points }}]
+
{{ character.will.value }}
+
Will
+ +
[{{ character.fright_check.points }}]
+
{{ character.fright_check.value }}
+
Fright Check
+ +
[{{ character.per.points }}]
+
{{ character.per.value }}
+
Perception (Per)
+ +
[{{ character.vision.points }}]
+
{{ character.vision.value }}
+
Vision
+ +
[{{ character.hearing.points }}]
+
{{ character.hearing.value }}
+
Hearing
+ +
[{{ character.taste_smell.points }}]
+
{{ character.taste_smell.value }}
+
Taste & Smell
+ +
[{{ character.touch.points }}]
+
{{ character.touch.value }}
+
Touch
+ +
[{{ character.basic_speed.points }}]
+
{{ character.basic_speed.value }}
+
Basic Speed
+ +
[{{ character.basic_move.points }}]
+
{{ character.basic_move.value }}
+
Basic Move
+ +
+
+
+
Point Pools
+
+ +
[{{ character.fp.points }}]
+
{{ character.fp.current }}
+
of
+
{{ character.fp.value }}
+
Fatigue Points (FP)
+ +
[{{ character.hp.points }}]
+
{{ character.hp.current }}
+
of
+
{{ character.hp.value }}
+
Hit Points (HP)
+ +
+
+
+
+
Hit Locations
+
+
Roll
+
Location
+
DR
+ + + {% for location in character.hit_locations %} +
+
{{ location.calc.roll_range }}
+
{{ location.table_name }}
+
{{ location.hit_penalty }}
+
{{ location.calc.dr.all }}
+ {% endfor %} + +
+
+
+
Encumbrance, Move & Dodge
+
Level
+
Max Load
+
Move
+
Dodge
+ + {% for enc_level in character.enc_levels %} +
+ + + + +
+
+
+
{{enc_level.max_load }} {{character.weight_unit }}
+
{{ enc_level.move }}
+
{{ enc_level.dodge }}
+ {% endfor %} + +
+
+
Lifting & Moving Things
+ {% for lift in character.lift_table %} +
{{ lift.value }} {{ character.weight_unit }}
+
{{ lift.label }}
+ {% endfor %} +
+
+
+
+
+
+
±
+
Reaction
+ + {% for r in character.reaction_modifiers %} +
+
{{ r.amount }}
+
{{ r.situation }}
+ {% endfor %} + +
+
+
+
±
+
Condition
+ + {% for r in character.conditional_modifiers %} +
+
{{ r.amount }}
+
{{ r.situation }}
+ {% endfor %} + +
+
+
+
Melee Weapon
+
Usage
+
SL
+
Parry
+
Block
+
Damage
+
Reach
+
ST
+ + {% for w in character.melee_weapons %} +
+
+ {{ w.name }} +
+ +
+
+
{{ w.usage }}
+
{{ w.skill_level }}
+
{{ w.parry }}
+
{{ w.block }}
+
{{ w.damage }} cut
+
{{ w.reach }}*
+
{{ w.strength }}
+ + {% endfor %} +
+
+
+
Ranged Weapon
+
Usage
+
SL
+
Acc
+
Range
+
Damage
+
RoF
+
Shots
+
Bulk
+
Recoil
+
ST
+ + {% for w in character.ranged_weapons %} +
+
+ {{ w.name }} +
+ +
+
+
Thrown
+
{{ w.skill_level }}
+
{{ w.acc }}
+
{{ w.range }}
+
{{ w.damage }}
+
{{ w.rof }}
+
{{ w.shots }}
+
{{ w.bulk }}
+
{{ w.recoil }}
+
{{ w.strength }}
+ {% endfor %} +
+
+
+
Trait
+
Pts
+
+ + + +
+ + {% for trait in character.traits %} +
+
+ {{ trait.name }} +
+ + {{ trait.notes }} +
+
+
{{ trait.points }}
+
{{ trait.reference }}
+ + {% endfor %} +
+
+
+
Skill / Technique
+
SL
+
RSL
+
Pts
+
+ + + +
+ + {% for skill in character.skills %} +
+
+ {{ skill.name }} +
+ + +
+
+
{{ skill.level }}
+
{{ skill.rsl }}
+
{{ skill.points }}
+
{{ skill.reference }}
+ + {% endfor %} + +
+
+
+
Spell
+
College
+
SL
+
RSL
+
Pts
+
+ + + +
+ + {% for spell in character.spells %} +
+
+ {{ spell.name }} +
+ {{ spell.notes | safe }} +
+
+
{{ spell.college }}
+
{{ spell.level }}
+
{{ spell.rsl }}
+
{{ spell.points }}
+
{{ spell.reference }}
+ {% endfor %} + + +
+
+
+
+ + + + +
+
#
+
Carried Equipment (18.75 lb; $485)
+
Uses
+
TL
+
LC
+
+ + + + +
+
+ + + + +
+
+ + + + + + + + +
+
+ + + + + + + + +
+
+ + + +
+ + {% for item in character.equipment %} + +
+
+ + + + +
+
{{ item.quantity }}
+
+ {{ item.name }} +
+ + {{ item.notes }} +
+
+
0
+
{{ item.tech_level }}
+
+
{{ item.value }}
+
{{ item.weight }}
+
{{ item.ext_value }}
+
{{ item.ext_weight}}
+
{{ item.ref }}
+ + {% endfor %} + +
+
+
+
#
+
Other Equipment ($0)
+
Uses
+
TL
+
LC
+
+ + + + +
+
+ + + + +
+
+ + + + + + + + +
+
+ + + + + + + + +
+
+ + + +
+ +
+
+
+
Note
+
+ + + +
+ +
+
+ +
+
+ + + diff --git a/gurps_character/templates/characters/embedded.html b/gurps_character/templates/characters/embedded.html new file mode 100644 index 0000000..bcebc2b --- /dev/null +++ b/gurps_character/templates/characters/embedded.html @@ -0,0 +1,1601 @@ +{% extends "base.html" %} +{% block extra_css %} + +{% endblock %} +{% block content %} +
+
+
+
+
Portrait
+
+
+
+
Identity
+
+
Name
+
{{ character.details.profile.name }}
+
Title
+
+
Organization
+
+
+
+
+
Miscellaneous
+
+
Created
+
{{ character.details.created_date }}
+
Modified
+
{{ character.details.modified_date }}
+
Player
+
{{ character.details.profile.player_name }}
+
+
+
+
Description
+
+
Gender
+
{{ character.details.profile.gender }}
+
Age
+
{{ character.details.profile.age }}
+
Birthday
+
{{ character.details.profile.birthday }}
+
Religion
+
+
+
+
Height
+
{{ character.details.profile.height }}
+
Weight
+
{{ character.details.profile.weight }}
+
Size
+
??
+
TL
+
{{ character.details.profile.tech_level }}
+
+
+
Hair
+
{{ character.details.profile.hair }}
+
Eyes
+
{{ character.details.profile.eyes }}
+
Skin
+
{{ character.details.profile.skin }}
+
Hand
+
{{ character.details.profile.handedness }}
+
+
+
+
{{ character.details.total_points }} Points
+
+
{{ character.unspent_points }}
+
Unspent
+
{{ character.race_points }}
+
Race
+
{{ character.attr_points }}
+
Attributes
+
{{ character.attr_points }}
+
Advantages
+
{{ character.disadv_points }}
+
Disadvantages
+
{{ character.quirks_points }}
+
Quirks
+
{{ character.skills_points }}
+
Skills
+
{{ character.spells_points }}
+
Spells
+
+
+
+
+
+
+
Primary Attributes
+
+ +
[{{ character.st.points }}]
+
{{ character.st.value }}
+
Strength (ST)
+ +
[{{ character.dx.points }}]
+
{{ character.dx.value }}
+
Dexterity (DX)
+ +
[{{ character.iq.points }}]
+
{{ character.iq.value }}
+
Intelligence (IQ)
+ +
[{{ character.ht.points }}]
+
{{ character.ht.value }}
+
Health (HT)
+ +
+
+
+
Basic Damage
+
+
[20]
+
{{ character.details.calc.thrust }}
+
Basic Thrust
+
[20]
+
{{ character.details.calc.swing }}
+
Basic Swing
+
+
+
+
Secondary Attributes
+
+ +
[{{ character.will.points }}]
+
{{ character.will.value }}
+
Will
+ +
[{{ character.fright_check.points }}]
+
{{ character.fright_check.value }}
+
Fright Check
+ +
[{{ character.per.points }}]
+
{{ character.per.value }}
+
Perception (Per)
+ +
[{{ character.vision.points }}]
+
{{ character.vision.value }}
+
Vision
+ +
[{{ character.hearing.points }}]
+
{{ character.hearing.value }}
+
Hearing
+ +
[{{ character.taste_smell.points }}]
+
{{ character.taste_smell.value }}
+
Taste & Smell
+ +
[{{ character.touch.points }}]
+
{{ character.touch.value }}
+
Touch
+ +
[{{ character.basic_speed.points }}]
+
{{ character.basic_speed.value }}
+
Basic Speed
+ +
[{{ character.basic_move.points }}]
+
{{ character.basic_move.value }}
+
Basic Move
+ +
+
+
+
Point Pools
+
+ +
[{{ character.fp.points }}]
+
{{ character.fp.current }}
+
of
+
{{ character.fp.value }}
+
Fatigue Points (FP)
+ +
[{{ character.hp.points }}]
+
{{ character.hp.current }}
+
of
+
{{ character.hp.value }}
+
Hit Points (HP)
+ +
+
+
+
+
Hit Locations
+
+
Roll
+
Location
+
DR
+ + + {% for location in character.hit_locations %} +
+
{{ location.calc.roll_range }}
+
{{ location.table_name }}
+
{{ location.hit_penalty }}
+
{{ location.calc.dr.all }}
+ {% endfor %} + +
+
+
+
Encumbrance, Move & Dodge
+
Level
+
Max Load
+
Move
+
Dodge
+ + {% for enc_level in character.enc_levels %} +
+ + + + +
+
+
+
{{enc_level.max_load }} {{character.weight_unit }}
+
{{ enc_level.move }}
+
{{ enc_level.dodge }}
+ {% endfor %} + +
+
+
Lifting & Moving Things
+ {% for lift in character.lift_table %} +
{{ lift.value }} {{ character.weight_unit }}
+
{{ lift.label }}
+ {% endfor %} +
+
+
+
+
+
+
±
+
Reaction
+ + {% for r in character.reaction_modifiers %} +
+
{{ r.amount }}
+
{{ r.situation }}
+ {% endfor %} + +
+
+
+
±
+
Condition
+ + {% for r in character.conditional_modifiers %} +
+
{{ r.amount }}
+
{{ r.situation }}
+ {% endfor %} + +
+
+
+
Melee Weapon
+
Usage
+
SL
+
Parry
+
Block
+
Damage
+
Reach
+
ST
+ + {% for w in character.melee_weapons %} +
+
+ {{ w.name }} +
+ +
+
+
{{ w.usage }}
+
{{ w.skill_level }}
+
{{ w.parry }}
+
{{ w.block }}
+
{{ w.damage }} cut
+
{{ w.reach }}*
+
{{ w.strength }}
+ + {% endfor %} +
+
+
+
Ranged Weapon
+
Usage
+
SL
+
Acc
+
Range
+
Damage
+
RoF
+
Shots
+
Bulk
+
Recoil
+
ST
+ + {% for w in character.ranged_weapons %} +
+
+ {{ w.name }} +
+ +
+
+
Thrown
+
{{ w.skill_level }}
+
{{ w.acc }}
+
{{ w.range }}
+
{{ w.damage }}
+
{{ w.rof }}
+
{{ w.shots }}
+
{{ w.bulk }}
+
{{ w.recoil }}
+
{{ w.strength }}
+ {% endfor %} +
+
+
+
Trait
+
Pts
+
+ + + +
+ + {% for trait in character.traits %} +
+
+ {{ trait.name }} +
+ + {{ trait.notes }} +
+
+
{{ trait.points }}
+
{{ trait.reference }}
+ + {% endfor %} +
+
+
+
Skill / Technique
+
SL
+
RSL
+
Pts
+
+ + + +
+ + {% for skill in character.skills %} +
+
+ {{ skill.name }} +
+ + +
+
+
{{ skill.level }}
+
{{ skill.rsl }}
+
{{ skill.points }}
+
{{ skill.reference }}
+ + {% endfor %} + +
+
+
+
Spell
+
College
+
SL
+
RSL
+
Pts
+
+ + + +
+ + {% for spell in character.spells %} +
+
+ {{ spell.name }} +
+ {{ spell.notes | safe }} +
+
+
{{ spell.college }}
+
{{ spell.level }}
+
{{ spell.rsl }}
+
{{ spell.points }}
+
{{ spell.reference }}
+ {% endfor %} + + +
+
+
+
+ + + + +
+
#
+
Carried Equipment (18.75 lb; $485)
+
Uses
+
TL
+
LC
+
+ + + + +
+
+ + + + +
+
+ + + + + + + + +
+
+ + + + + + + + +
+
+ + + +
+ + {% for item in character.equipment %} + +
+
+ + + + +
+
{{ item.quantity }}
+
+ {{ item.name }} +
+ + {{ item.notes }} +
+
+
0
+
{{ item.tech_level }}
+
+
{{ item.value }}
+
{{ item.weight }}
+
{{ item.ext_value }}
+
{{ item.ext_weight}}
+
{{ item.ref }}
+ + {% endfor %} + +
+
+
+
#
+
Other Equipment ($0)
+
Uses
+
TL
+
LC
+
+ + + + +
+
+ + + + +
+
+ + + + + + + + +
+
+ + + + + + + + +
+
+ + + +
+ +
+
+
+
Note
+
+ + + +
+ +
+
+ +
+
+{% endblock %} diff --git a/gurps_character/templates/characters/list.html b/gurps_character/templates/characters/list.html new file mode 100644 index 0000000..e0d6520 --- /dev/null +++ b/gurps_character/templates/characters/list.html @@ -0,0 +1,18 @@ +{% extends "base.html" %} +{% block content %} + + +Upload new character +
+ {% csrf_token %} + {{ form }} + +
+{% endblock %} diff --git a/gurps_character/templates/characters/upload.html b/gurps_character/templates/characters/upload.html new file mode 100644 index 0000000..baafd24 --- /dev/null +++ b/gurps_character/templates/characters/upload.html @@ -0,0 +1,7 @@ +{% extends "base.html" %} +{% block content %} +
+ {% csrf_token %} + {{ form }} +
+{% endblock %} diff --git a/gurps_character/templates/home.html b/gurps_character/templates/home.html new file mode 100644 index 0000000..f10f8d5 --- /dev/null +++ b/gurps_character/templates/home.html @@ -0,0 +1,8 @@ +{% extends "base.html" %} +{% block content %} +

Welcome to the character store

+ + + +

Currently it works with GURPS, specifically characters created by GCS

+{% endblock %} diff --git a/gurps_character/templates/navbar.html b/gurps_character/templates/navbar.html new file mode 100644 index 0000000..3e018b3 --- /dev/null +++ b/gurps_character/templates/navbar.html @@ -0,0 +1,48 @@ + diff --git a/gurps_character/templates/registration/login.html b/gurps_character/templates/registration/login.html new file mode 100644 index 0000000..f542d7d --- /dev/null +++ b/gurps_character/templates/registration/login.html @@ -0,0 +1,38 @@ +{% extends "base.html" %} + +{% block content %} + +{% if form.errors %} +

Your username and password didn't match. Please try again.

+{% endif %} + +{% if next %} + {% if user.is_authenticated %} +

Your account doesn't have access to this page. To proceed, + please login with an account that has access.

+ {% else %} +

Please login to see this page.

+ {% endif %} +{% endif %} + +
+{% csrf_token %} + + + + + + + + + +
{{ form.username.label_tag }}{{ form.username }}
{{ form.password.label_tag }}{{ form.password }}
+ + + +
+ +{# Assumes you set up the password_reset view in your URLconf #} +

Lost password?

+ +{% endblock %} diff --git a/gurps_character/tests.py b/gurps_character/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/gurps_character/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/gurps_character/urls.py b/gurps_character/urls.py new file mode 100644 index 0000000..13c2d8f --- /dev/null +++ b/gurps_character/urls.py @@ -0,0 +1,10 @@ +from django.urls import path + +from . import views + +urlpatterns = [ + path("", views.index, name="index"), + path("details//", views.details, name="details"), + path("upload", views.upload_file, name="upload"), + path("download//", views.download, name="download"), +] diff --git a/gurps_character/views.py b/gurps_character/views.py new file mode 100644 index 0000000..6b08b8e --- /dev/null +++ b/gurps_character/views.py @@ -0,0 +1,67 @@ +import json + +from django.shortcuts import render +from django.http import HttpResponse, HttpResponseRedirect +from django.urls import reverse + +from .models import GURPSCharacter +from .forms import UploadFileForm + +def index(request): + characters = GURPSCharacter.objects.all() + context = {"characters":characters} + + if request.method == "POST": + form = UploadFileForm(request.POST, request.FILES) + if form.is_valid(): + handle_uploaded_file(request.FILES["file"]) + return HttpResponseRedirect(reverse("index")) + else: + form = UploadFileForm() + + context['form'] = form + return render(request, "characters/list.html", context) + +def details(request, uuid): + character = GURPSCharacter.objects.get(uuid=uuid) + + context = {"character": character} + #import bpdb;bpdb.set_trace() + + return render(request, "characters/embedded.html", context) + + +def upload_file(request): + if request.method == "POST": + form = UploadFileForm(request.POST, request.FILES) + if form.is_valid(): + handle_uploaded_file(request.FILES["file"]) + return HttpResponseRedirect("/success/url/") + else: + form = UploadFileForm() + return render(request, "characters/upload.html", {"form": form}) + +def handle_uploaded_file(f): + import bpdb;bpdb.set_trace() + f.seek(0) # We read the file in the validator + data = json.loads(f.read()) + + uuid = data['id'] + name = data["profile"]["name"] + + + try: + character = GURPSCharacter.objects.get(uuid=uuid) + character.details = data + character.name = name + character.save() + except GURPSCharacter.DoesNotExist: + character = GURPSCharacter(uuid=uuid, name=name, details = data) + character.save() + +def download(irequest, uuid): + mime_type = "application/x-gcs-gcs" + character = GURPSCharacter.objects.get(uuid=uuid) + response = HttpResponse(json.dumps(character.details), content_type=mime_type) + response['Content-Disposition'] = "attachment; filename=%s.gcs" % character.name + return response diff --git a/gurps_vars.sh b/gurps_vars.sh new file mode 100644 index 0000000..1035cf1 --- /dev/null +++ b/gurps_vars.sh @@ -0,0 +1,5 @@ +export GURPS_DATABASE_NAME=neill +export GURPS_DATABASE_USER=neill +export GURPS_DATABASE_PASSWORD=ht6a!nce3216 +export GURPS_DATABASE_HOST=172.23.0.36 +export GURPS_DATABASE_PORT=5432 diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..fed0310 --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "django_gurps.settings") + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/notes.txt b/notes.txt new file mode 100644 index 0000000..ae35a60 --- /dev/null +++ b/notes.txt @@ -0,0 +1,41 @@ +Users + - id + - name + + (part of django auth) + +GMs + - user + - campaign + + # A GM is a user linked to a campaign. They get extra priovs on that campaign + +Characters + - id + - owner (user-id) + - campaing (campaign id) + - game system? + +Campaign + - id + - name + - game system + +Campaign-Players + - campaign id + - user id + +Game System + - id + - name + + +A character is owned by a user and is part of a campaign + +A campaign has one or more gms and zero or more players + +A user may view characters they own, or if they are a gm then characters that are part of one of their campaigns + + +Permissions + diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..855636a --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +django +tailwind +django_browser_reload +psycopg diff --git a/theme/__init__.py b/theme/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/theme/__pycache__/__init__.cpython-312.pyc b/theme/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..aec6c5f Binary files /dev/null and b/theme/__pycache__/__init__.cpython-312.pyc differ diff --git a/theme/__pycache__/apps.cpython-312.pyc b/theme/__pycache__/apps.cpython-312.pyc new file mode 100644 index 0000000..917ce77 Binary files /dev/null and b/theme/__pycache__/apps.cpython-312.pyc differ diff --git a/theme/apps.py b/theme/apps.py new file mode 100644 index 0000000..bec7464 --- /dev/null +++ b/theme/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class ThemeConfig(AppConfig): + name = 'theme' diff --git a/theme/static/css/dist/styles.css b/theme/static/css/dist/styles.css new file mode 100644 index 0000000..7e32723 --- /dev/null +++ b/theme/static/css/dist/styles.css @@ -0,0 +1,841 @@ +/* +! tailwindcss v3.4.1 | MIT License | https://tailwindcss.com +*/ + +/* +1. Prevent padding and border from affecting element width. (https://github.com/mozdevs/cssremedy/issues/4) +2. Allow adding a border to an element by just adding a border-width. (https://github.com/tailwindcss/tailwindcss/pull/116) +*/ + +*, +::before, +::after { + box-sizing: border-box; + /* 1 */ + border-width: 0; + /* 2 */ + border-style: solid; + /* 2 */ + border-color: #e5e7eb; + /* 2 */ +} + +::before, +::after { + --tw-content: ''; +} + +/* +1. Use a consistent sensible line-height in all browsers. +2. Prevent adjustments of font size after orientation changes in iOS. +3. Use a more readable tab size. +4. Use the user's configured `sans` font-family by default. +5. Use the user's configured `sans` font-feature-settings by default. +6. Use the user's configured `sans` font-variation-settings by default. +7. Disable tap highlights on iOS +*/ + +html, +:host { + line-height: 1.5; + /* 1 */ + -webkit-text-size-adjust: 100%; + /* 2 */ + -moz-tab-size: 4; + /* 3 */ + -o-tab-size: 4; + tab-size: 4; + /* 3 */ + font-family: ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; + /* 4 */ + font-feature-settings: normal; + /* 5 */ + font-variation-settings: normal; + /* 6 */ + -webkit-tap-highlight-color: transparent; + /* 7 */ +} + +/* +1. Remove the margin in all browsers. +2. Inherit line-height from `html` so users can set them as a class directly on the `html` element. +*/ + +body { + margin: 0; + /* 1 */ + line-height: inherit; + /* 2 */ +} + +/* +1. Add the correct height in Firefox. +2. Correct the inheritance of border color in Firefox. (https://bugzilla.mozilla.org/show_bug.cgi?id=190655) +3. Ensure horizontal rules are visible by default. +*/ + +hr { + height: 0; + /* 1 */ + color: inherit; + /* 2 */ + border-top-width: 1px; + /* 3 */ +} + +/* +Add the correct text decoration in Chrome, Edge, and Safari. +*/ + +abbr:where([title]) { + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +/* +Remove the default font size and weight for headings. +*/ + +h1, +h2, +h3, +h4, +h5, +h6 { + font-size: inherit; + font-weight: inherit; +} + +/* +Reset links to optimize for opt-in styling instead of opt-out. +*/ + +a { + color: inherit; + text-decoration: inherit; +} + +/* +Add the correct font weight in Edge and Safari. +*/ + +b, +strong { + font-weight: bolder; +} + +/* +1. Use the user's configured `mono` font-family by default. +2. Use the user's configured `mono` font-feature-settings by default. +3. Use the user's configured `mono` font-variation-settings by default. +4. Correct the odd `em` font sizing in all browsers. +*/ + +code, +kbd, +samp, +pre { + font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + /* 1 */ + font-feature-settings: normal; + /* 2 */ + font-variation-settings: normal; + /* 3 */ + font-size: 1em; + /* 4 */ +} + +/* +Add the correct font size in all browsers. +*/ + +small { + font-size: 80%; +} + +/* +Prevent `sub` and `sup` elements from affecting the line height in all browsers. +*/ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* +1. Remove text indentation from table contents in Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=999088, https://bugs.webkit.org/show_bug.cgi?id=201297) +2. Correct table border color inheritance in all Chrome and Safari. (https://bugs.chromium.org/p/chromium/issues/detail?id=935729, https://bugs.webkit.org/show_bug.cgi?id=195016) +3. Remove gaps between table borders by default. +*/ + +table { + text-indent: 0; + /* 1 */ + border-color: inherit; + /* 2 */ + border-collapse: collapse; + /* 3 */ +} + +/* +1. Change the font styles in all browsers. +2. Remove the margin in Firefox and Safari. +3. Remove default padding in all browsers. +*/ + +button, +input, +optgroup, +select, +textarea { + font-family: inherit; + /* 1 */ + font-feature-settings: inherit; + /* 1 */ + font-variation-settings: inherit; + /* 1 */ + font-size: 100%; + /* 1 */ + font-weight: inherit; + /* 1 */ + line-height: inherit; + /* 1 */ + color: inherit; + /* 1 */ + margin: 0; + /* 2 */ + padding: 0; + /* 3 */ +} + +/* +Remove the inheritance of text transform in Edge and Firefox. +*/ + +button, +select { + text-transform: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Remove default button styles. +*/ + +button, +[type='button'], +[type='reset'], +[type='submit'] { + -webkit-appearance: button; + /* 1 */ + background-color: transparent; + /* 2 */ + background-image: none; + /* 2 */ +} + +/* +Use the modern Firefox focus style for all focusable elements. +*/ + +:-moz-focusring { + outline: auto; +} + +/* +Remove the additional `:invalid` styles in Firefox. (https://github.com/mozilla/gecko-dev/blob/2f9eacd9d3d995c937b4251a5557d95d494c9be1/layout/style/res/forms.css#L728-L737) +*/ + +:-moz-ui-invalid { + box-shadow: none; +} + +/* +Add the correct vertical alignment in Chrome and Firefox. +*/ + +progress { + vertical-align: baseline; +} + +/* +Correct the cursor style of increment and decrement buttons in Safari. +*/ + +::-webkit-inner-spin-button, +::-webkit-outer-spin-button { + height: auto; +} + +/* +1. Correct the odd appearance in Chrome and Safari. +2. Correct the outline style in Safari. +*/ + +[type='search'] { + -webkit-appearance: textfield; + /* 1 */ + outline-offset: -2px; + /* 2 */ +} + +/* +Remove the inner padding in Chrome and Safari on macOS. +*/ + +::-webkit-search-decoration { + -webkit-appearance: none; +} + +/* +1. Correct the inability to style clickable types in iOS and Safari. +2. Change font properties to `inherit` in Safari. +*/ + +::-webkit-file-upload-button { + -webkit-appearance: button; + /* 1 */ + font: inherit; + /* 2 */ +} + +/* +Add the correct display in Chrome and Safari. +*/ + +summary { + display: list-item; +} + +/* +Removes the default spacing and border for appropriate elements. +*/ + +blockquote, +dl, +dd, +h1, +h2, +h3, +h4, +h5, +h6, +hr, +figure, +p, +pre { + margin: 0; +} + +fieldset { + margin: 0; + padding: 0; +} + +legend { + padding: 0; +} + +ol, +ul, +menu { + list-style: none; + margin: 0; + padding: 0; +} + +/* +Reset default styling for dialogs. +*/ + +dialog { + padding: 0; +} + +/* +Prevent resizing textareas horizontally by default. +*/ + +textarea { + resize: vertical; +} + +/* +1. Reset the default placeholder opacity in Firefox. (https://github.com/tailwindlabs/tailwindcss/issues/3300) +2. Set the default placeholder color to the user's configured gray 400 color. +*/ + +input::-moz-placeholder, textarea::-moz-placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +input::placeholder, +textarea::placeholder { + opacity: 1; + /* 1 */ + color: #9ca3af; + /* 2 */ +} + +/* +Set the default cursor for buttons. +*/ + +button, +[role="button"] { + cursor: pointer; +} + +/* +Make sure disabled buttons don't get the pointer cursor. +*/ + +:disabled { + cursor: default; +} + +/* +1. Make replaced elements `display: block` by default. (https://github.com/mozdevs/cssremedy/issues/14) +2. Add `vertical-align: middle` to align replaced elements more sensibly by default. (https://github.com/jensimmons/cssremedy/issues/14#issuecomment-634934210) + This can trigger a poorly considered lint error in some tools but is included by design. +*/ + +img, +svg, +video, +canvas, +audio, +iframe, +embed, +object { + display: block; + /* 1 */ + vertical-align: middle; + /* 2 */ +} + +/* +Constrain images and videos to the parent width and preserve their intrinsic aspect ratio. (https://github.com/mozdevs/cssremedy/issues/14) +*/ + +img, +video { + max-width: 100%; + height: auto; +} + +/* Make elements with the HTML hidden attribute stay hidden by default */ + +[hidden] { + display: none; +} + +[type='text'],input:where(:not([type])),[type='email'],[type='url'],[type='password'],[type='number'],[type='date'],[type='datetime-local'],[type='month'],[type='search'],[type='tel'],[type='time'],[type='week'],[multiple],textarea,select { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + border-radius: 0px; + padding-top: 0.5rem; + padding-right: 0.75rem; + padding-bottom: 0.5rem; + padding-left: 0.75rem; + font-size: 1rem; + line-height: 1.5rem; + --tw-shadow: 0 0 #0000; +} + +[type='text']:focus, input:where(:not([type])):focus, [type='email']:focus, [type='url']:focus, [type='password']:focus, [type='number']:focus, [type='date']:focus, [type='datetime-local']:focus, [type='month']:focus, [type='search']:focus, [type='tel']:focus, [type='time']:focus, [type='week']:focus, [multiple]:focus, textarea:focus, select:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); + border-color: #2563eb; +} + +input::-moz-placeholder, textarea::-moz-placeholder { + color: #6b7280; + opacity: 1; +} + +input::placeholder,textarea::placeholder { + color: #6b7280; + opacity: 1; +} + +::-webkit-datetime-edit-fields-wrapper { + padding: 0; +} + +::-webkit-date-and-time-value { + min-height: 1.5em; + text-align: inherit; +} + +::-webkit-datetime-edit { + display: inline-flex; +} + +::-webkit-datetime-edit,::-webkit-datetime-edit-year-field,::-webkit-datetime-edit-month-field,::-webkit-datetime-edit-day-field,::-webkit-datetime-edit-hour-field,::-webkit-datetime-edit-minute-field,::-webkit-datetime-edit-second-field,::-webkit-datetime-edit-millisecond-field,::-webkit-datetime-edit-meridiem-field { + padding-top: 0; + padding-bottom: 0; +} + +select { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 0.5rem center; + background-repeat: no-repeat; + background-size: 1.5em 1.5em; + padding-right: 2.5rem; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; +} + +[multiple],[size]:where(select:not([size="1"])) { + background-image: initial; + background-position: initial; + background-repeat: unset; + background-size: initial; + padding-right: 0.75rem; + -webkit-print-color-adjust: unset; + print-color-adjust: unset; +} + +[type='checkbox'],[type='radio'] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + padding: 0; + -webkit-print-color-adjust: exact; + print-color-adjust: exact; + display: inline-block; + vertical-align: middle; + background-origin: border-box; + -webkit-user-select: none; + -moz-user-select: none; + user-select: none; + flex-shrink: 0; + height: 1rem; + width: 1rem; + color: #2563eb; + background-color: #fff; + border-color: #6b7280; + border-width: 1px; + --tw-shadow: 0 0 #0000; +} + +[type='checkbox'] { + border-radius: 0px; +} + +[type='radio'] { + border-radius: 100%; +} + +[type='checkbox']:focus,[type='radio']:focus { + outline: 2px solid transparent; + outline-offset: 2px; + --tw-ring-inset: var(--tw-empty,/*!*/ /*!*/); + --tw-ring-offset-width: 2px; + --tw-ring-offset-color: #fff; + --tw-ring-color: #2563eb; + --tw-ring-offset-shadow: var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color); + --tw-ring-shadow: var(--tw-ring-inset) 0 0 0 calc(2px + var(--tw-ring-offset-width)) var(--tw-ring-color); + box-shadow: var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow); +} + +[type='checkbox']:checked,[type='radio']:checked { + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +[type='checkbox']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M12.207 4.793a1 1 0 010 1.414l-5 5a1 1 0 01-1.414 0l-2-2a1 1 0 011.414-1.414L6.5 9.086l4.293-4.293a1 1 0 011.414 0z'/%3e%3c/svg%3e"); +} + +@media (forced-colors: active) { + [type='checkbox']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='radio']:checked { + background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3ccircle cx='8' cy='8' r='3'/%3e%3c/svg%3e"); +} + +@media (forced-colors: active) { + [type='radio']:checked { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:checked:hover,[type='checkbox']:checked:focus,[type='radio']:checked:hover,[type='radio']:checked:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='checkbox']:indeterminate { + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 16 16'%3e%3cpath stroke='white' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M4 8h8'/%3e%3c/svg%3e"); + border-color: transparent; + background-color: currentColor; + background-size: 100% 100%; + background-position: center; + background-repeat: no-repeat; +} + +@media (forced-colors: active) { + [type='checkbox']:indeterminate { + -webkit-appearance: auto; + -moz-appearance: auto; + appearance: auto; + } +} + +[type='checkbox']:indeterminate:hover,[type='checkbox']:indeterminate:focus { + border-color: transparent; + background-color: currentColor; +} + +[type='file'] { + background: unset; + border-color: inherit; + border-width: 0; + border-radius: 0; + padding: 0; + font-size: unset; + line-height: inherit; +} + +[type='file']:focus { + outline: 1px solid ButtonText; + outline: 1px auto -webkit-focus-ring-color; +} + +*, ::before, ::after { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +::backdrop { + --tw-border-spacing-x: 0; + --tw-border-spacing-y: 0; + --tw-translate-x: 0; + --tw-translate-y: 0; + --tw-rotate: 0; + --tw-skew-x: 0; + --tw-skew-y: 0; + --tw-scale-x: 1; + --tw-scale-y: 1; + --tw-pan-x: ; + --tw-pan-y: ; + --tw-pinch-zoom: ; + --tw-scroll-snap-strictness: proximity; + --tw-gradient-from-position: ; + --tw-gradient-via-position: ; + --tw-gradient-to-position: ; + --tw-ordinal: ; + --tw-slashed-zero: ; + --tw-numeric-figure: ; + --tw-numeric-spacing: ; + --tw-numeric-fraction: ; + --tw-ring-inset: ; + --tw-ring-offset-width: 0px; + --tw-ring-offset-color: #fff; + --tw-ring-color: rgb(59 130 246 / 0.5); + --tw-ring-offset-shadow: 0 0 #0000; + --tw-ring-shadow: 0 0 #0000; + --tw-shadow: 0 0 #0000; + --tw-shadow-colored: 0 0 #0000; + --tw-blur: ; + --tw-brightness: ; + --tw-contrast: ; + --tw-grayscale: ; + --tw-hue-rotate: ; + --tw-invert: ; + --tw-saturate: ; + --tw-sepia: ; + --tw-drop-shadow: ; + --tw-backdrop-blur: ; + --tw-backdrop-brightness: ; + --tw-backdrop-contrast: ; + --tw-backdrop-grayscale: ; + --tw-backdrop-hue-rotate: ; + --tw-backdrop-invert: ; + --tw-backdrop-opacity: ; + --tw-backdrop-saturate: ; + --tw-backdrop-sepia: ; +} + +.container { + width: 100%; +} + +@media (min-width: 640px) { + .container { + max-width: 640px; + } +} + +@media (min-width: 768px) { + .container { + max-width: 768px; + } +} + +@media (min-width: 1024px) { + .container { + max-width: 1024px; + } +} + +@media (min-width: 1280px) { + .container { + max-width: 1280px; + } +} + +@media (min-width: 1536px) { + .container { + max-width: 1536px; + } +} + +.static { + position: static; +} + +.mx-auto { + margin-left: auto; + margin-right: auto; +} + +.block { + display: block; +} + +.inline { + display: inline; +} + +.flex { + display: flex; +} + +.grid { + display: grid; +} + +.hidden { + display: none; +} + +.h-screen { + height: 100vh; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.border { + border-width: 1px; +} + +.bg-gray-50 { + --tw-bg-opacity: 1; + background-color: rgb(249 250 251 / var(--tw-bg-opacity)); +} + +.font-serif { + font-family: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; +} + +.text-5xl { + font-size: 3rem; + line-height: 1; +} + +.leading-normal { + line-height: 1.5; +} + +.tracking-normal { + letter-spacing: 0em; +} diff --git a/theme/static_src/.gitignore b/theme/static_src/.gitignore new file mode 100644 index 0000000..3c3629e --- /dev/null +++ b/theme/static_src/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/theme/static_src/package-lock.json b/theme/static_src/package-lock.json new file mode 100644 index 0000000..12c2bcd --- /dev/null +++ b/theme/static_src/package-lock.json @@ -0,0 +1,1527 @@ +{ + "name": "theme", + "version": "3.8.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "theme", + "version": "3.8.0", + "license": "MIT", + "devDependencies": { + "@tailwindcss/aspect-ratio": "^0.4.2", + "@tailwindcss/forms": "^0.5.7", + "@tailwindcss/typography": "^0.5.10", + "cross-env": "^7.0.3", + "postcss": "^8.4.32", + "postcss-import": "^15.1.0", + "postcss-nested": "^6.0.1", + "postcss-simple-vars": "^7.0.1", + "rimraf": "^5.0.5", + "tailwindcss": "^3.4.0" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", + "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "dev": true, + "dependencies": { + "@jridgewell/set-array": "^1.0.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.9" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", + "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.21", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.21.tgz", + "integrity": "sha512-SRfKmRe1KvYnxjEMtxEr+J4HIeMX5YBg/qhRHpxEIGjhX1rshcHlnFUE9K0GazhVKWM7B+nARSkV8LuvJdJ5/g==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@tailwindcss/aspect-ratio": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/aspect-ratio/-/aspect-ratio-0.4.2.tgz", + "integrity": "sha512-8QPrypskfBa7QIMuKHg2TA7BqES6vhBrDLOv8Unb6FcFyd3TjKbc6lcmb9UPQHxfl24sXoJ41ux/H7qQQvfaSQ==", + "dev": true, + "peerDependencies": { + "tailwindcss": ">=2.0.0 || >=3.0.0 || >=3.0.0-alpha.1" + } + }, + "node_modules/@tailwindcss/forms": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.7.tgz", + "integrity": "sha512-QE7X69iQI+ZXwldE+rzasvbJiyV/ju1FGHH0Qn2W3FKbuYtqp8LKcy6iSw79fVUT5/Vvf+0XgLCeYVG+UV6hOw==", + "dev": true, + "dependencies": { + "mini-svg-data-uri": "^1.2.3" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1" + } + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz", + "integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==", + "dev": true, + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/ansi-regex": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", + "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/cross-env": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz", + "integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "bin": { + "cross-env": "src/bin/cross-env.js", + "cross-env-shell": "src/bin/cross-env-shell.js" + }, + "engines": { + "node": ">=10.14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", + "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "10.3.10", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.10.tgz", + "integrity": "sha512-fa46+tv1Ak0UPK1TOy/pZrIybNNt4HCv7SDzwyfiOZkvZLEbjsZkJBPtDHVshZjbecAoAGSC20MjLDG/qr679g==", + "dev": true, + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^2.3.5", + "minimatch": "^9.0.1", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0", + "path-scurry": "^1.10.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.1.tgz", + "integrity": "sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jackspeak": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", + "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.0.tgz", + "integrity": "sha512-gFqAIbuKyyso/3G2qhiO2OM6shY6EPP/R0+mkDbyspxKazh8BXDC5FiFsUjlczgdNz/vfra0da2y+aHrusLG/Q==", + "dev": true, + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/lilconfig": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", + "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true + }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.1.0.tgz", + "integrity": "sha512-/1clY/ui8CzjKFyjdvwPWJUYKiFVXG2I2cY0ssG7h4+hwk+XOIX7ZSG9Q7TW8TW3Kp3BUSqgFWBLgL4PJ+Blag==", + "dev": true, + "engines": { + "node": "14 || >=16.14" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mini-svg-data-uri": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", + "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", + "dev": true, + "bin": { + "mini-svg-data-uri": "cli.js" + } + }, + "node_modules/minimatch": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", + "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz", + "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", + "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-scurry": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.1.tgz", + "integrity": "sha512-MkhCqzzBEpPvxxQ71Md0b1Kk51W01lrYvlMzSUaIzNsODdd7mqhiimSZlr+VegAz5Z6Vzt9Xg2ttE//XBhH3EQ==", + "dev": true, + "dependencies": { + "lru-cache": "^9.1.1 || ^10.0.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picocolors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.33", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.33.tgz", + "integrity": "sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-load-config/node_modules/lilconfig": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.0.0.tgz", + "integrity": "sha512-K2U4W2Ff5ibV7j7ydLr+zLAkIg5JJ4lPn1Ltsdt+Tz/IjQ8buJ55pZAxoP34lqIiwtF9iAvtLv3JGv7CAyAg+g==", + "dev": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/postcss-nested": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.0.1.tgz", + "integrity": "sha512-mEp4xPMi5bSWiMbsgoPfcP74lsWLHkQbZc3sY+jWYd65CUwXrUaTp0fmNpa01ZcETKlIgUdFN/MpS2xZtqL9dQ==", + "dev": true, + "dependencies": { + "postcss-selector-parser": "^6.0.11" + }, + "engines": { + "node": ">=12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nested/node_modules/postcss-selector-parser": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-simple-vars": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/postcss-simple-vars/-/postcss-simple-vars-7.0.1.tgz", + "integrity": "sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A==", + "dev": true, + "engines": { + "node": ">=14.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.2.1" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.8", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", + "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.5.tgz", + "integrity": "sha512-CqDakW+hMe/Bz202FPEymy68P+G50RfMQK+Qo5YUqc9SPipvbGjCGKd0RSKEelbsfQuw3g5NZDSrlZZAJurH1A==", + "dev": true, + "dependencies": { + "glob": "^10.3.7" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map-js": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", + "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.1.tgz", + "integrity": "sha512-qAYmXRfk3ENzuPBakNK0SRrUDipP8NQnEY6772uDhflcQz5EhRdD7JNZxyrFHVQNCwULPBn6FNPp9brpO7ctcA==", + "dev": true, + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.5.3", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.19.1", + "lilconfig": "^2.1.0", + "micromatch": "^4.0.5", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.0.0", + "postcss": "^8.4.23", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.1", + "postcss-nested": "^6.0.1", + "postcss-selector-parser": "^6.0.11", + "resolve": "^1.22.2", + "sucrase": "^3.32.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.0.15", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.15.tgz", + "integrity": "sha512-rEYkQOMUCEMhsKbK66tbEU9QVIxbhN18YiniAwA7XQYTVBqrBy+P2p5JcdqsHgKM2zWylp8d7J6eszocfds5Sw==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "dev": true, + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/theme/static_src/package.json b/theme/static_src/package.json new file mode 100644 index 0000000..a391915 --- /dev/null +++ b/theme/static_src/package.json @@ -0,0 +1,28 @@ +{ + "name": "theme", + "version": "3.8.0", + "description": "", + "scripts": { + "start": "npm run dev", + "build": "npm run build:clean && npm run build:tailwind", + "build:clean": "rimraf ../static/css/dist", + "build:tailwind": "cross-env NODE_ENV=production tailwindcss --postcss -i ./src/styles.css -o ../static/css/dist/styles.css --minify", + "dev": "cross-env NODE_ENV=development tailwindcss --postcss -i ./src/styles.css -o ../static/css/dist/styles.css -w", + "tailwindcss": "node ./node_modules/tailwindcss/lib/cli.js" + }, + "keywords": [], + "author": "", + "license": "MIT", + "devDependencies": { + "@tailwindcss/aspect-ratio": "^0.4.2", + "@tailwindcss/forms": "^0.5.7", + "@tailwindcss/typography": "^0.5.10", + "cross-env": "^7.0.3", + "postcss": "^8.4.32", + "postcss-import": "^15.1.0", + "postcss-nested": "^6.0.1", + "postcss-simple-vars": "^7.0.1", + "rimraf": "^5.0.5", + "tailwindcss": "^3.4.0" + } +} diff --git a/theme/static_src/postcss.config.js b/theme/static_src/postcss.config.js new file mode 100644 index 0000000..0b09b34 --- /dev/null +++ b/theme/static_src/postcss.config.js @@ -0,0 +1,7 @@ +module.exports = { + plugins: { + "postcss-import": {}, + "postcss-simple-vars": {}, + "postcss-nested": {} + }, +} diff --git a/theme/static_src/src/styles.css b/theme/static_src/src/styles.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/theme/static_src/src/styles.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/theme/static_src/tailwind.config.js b/theme/static_src/tailwind.config.js new file mode 100644 index 0000000..9485bc6 --- /dev/null +++ b/theme/static_src/tailwind.config.js @@ -0,0 +1,57 @@ +/** + * This is a minimal config. + * + * If you need the full config, get it from here: + * https://unpkg.com/browse/tailwindcss@latest/stubs/defaultConfig.stub.js + */ + +module.exports = { + content: [ + /** + * HTML. Paths to Django template files that will contain Tailwind CSS classes. + */ + + /* Templates within theme app (/templates), e.g. base.html. */ + '../templates/**/*.html', + + /* + * Main templates directory of the project (BASE_DIR/templates). + * Adjust the following line to match your project structure. + */ + '../../templates/**/*.html', + + /* + * Templates in other django apps (BASE_DIR//templates). + * Adjust the following line to match your project structure. + */ + '../../**/templates/**/*.html', + + /** + * JS: If you use Tailwind CSS in JavaScript, uncomment the following lines and make sure + * patterns match your project structure. + */ + /* JS 1: Ignore any JavaScript in node_modules folder. */ + // '!../../**/node_modules', + /* JS 2: Process all JavaScript files in the project. */ + // '../../**/*.js', + + /** + * Python: If you use Tailwind CSS classes in Python, uncomment the following line + * and make sure the pattern below matches your project structure. + */ + // '../../**/*.py' + ], + theme: { + extend: {}, + }, + plugins: [ + /** + * '@tailwindcss/forms' is the forms plugin that provides a minimal styling + * for forms. If you don't like it or have own styling for forms, + * comment the line below to disable '@tailwindcss/forms'. + */ + require('@tailwindcss/forms'), + require('@tailwindcss/typography'), + require('@tailwindcss/aspect-ratio'), + ], +} diff --git a/theme/templates/base.html b/theme/templates/base.html new file mode 100644 index 0000000..5dea45e --- /dev/null +++ b/theme/templates/base.html @@ -0,0 +1,27 @@ + + + + Django GURPS + + + + + + +{% block extra_css %}{% endblock %} + + + + + + {% include "navbar.html" %} + +
+
+ {% block content %}{% endblock %} +
+
+ +