Users & Groups fixes/changes
Signed-off-by: apeters <apeters@korves.net>
This commit is contained in:
parent
ee1f3e59b5
commit
36e606693b
@ -27,23 +27,26 @@ async def cli_processor(streams: tuple[asyncio.StreamReader, asyncio.StreamWrite
|
|||||||
await writer.drain()
|
await writer.drain()
|
||||||
elif cmd == b"\x98":
|
elif cmd == b"\x98":
|
||||||
awaiting = dict()
|
awaiting = dict()
|
||||||
idx = 1
|
tokens = (
|
||||||
for k, v in IN_MEMORY_DB.items():
|
IN_MEMORY_DB["TOKENS"]["LOGIN"] | IN_MEMORY_DB["TOKENS"]["REGISTER"]
|
||||||
if (
|
)
|
||||||
isinstance(v, dict)
|
for idx, (k, v) in enumerate(tokens.items(), start=1):
|
||||||
and v.get("token_type") == "cli_confirmation"
|
awaiting[idx] = (k, v["intention"])
|
||||||
):
|
|
||||||
awaiting[idx] = (k, v["intention"])
|
|
||||||
idx += 1
|
|
||||||
writer.write(f"{json.dumps(awaiting)}\n".encode("ascii"))
|
writer.write(f"{json.dumps(awaiting)}\n".encode("ascii"))
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
elif cmd == b"\x99":
|
elif cmd == b"\x99":
|
||||||
data = await reader.readexactly(14)
|
data = await reader.readexactly(14)
|
||||||
confirmed = data.strip().decode("ascii")
|
confirmed = data.strip().decode("ascii")
|
||||||
code = "%06d" % random.randint(0, 999999)
|
code = "%06d" % random.randint(0, 999999)
|
||||||
IN_MEMORY_DB.get(confirmed, {}).update(
|
if confirmed in IN_MEMORY_DB["TOKENS"]["LOGIN"]:
|
||||||
{"status": "confirmed", "code": code}
|
IN_MEMORY_DB["TOKENS"]["LOGIN"].get(confirmed, {}).update(
|
||||||
)
|
{"status": "confirmed", "code": code}
|
||||||
|
)
|
||||||
|
elif confirmed in IN_MEMORY_DB["TOKENS"]["REGISTER"]:
|
||||||
|
IN_MEMORY_DB["TOKENS"]["REGISTER"].get(confirmed, {}).update(
|
||||||
|
{"status": "confirmed", "code": code}
|
||||||
|
)
|
||||||
writer.write(f"{code}\n".encode("ascii"))
|
writer.write(f"{code}\n".encode("ascii"))
|
||||||
await writer.drain()
|
await writer.drain()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
@ -23,6 +23,7 @@ app.register_blueprint(objects.blueprint)
|
|||||||
app.register_blueprint(profile.blueprint)
|
app.register_blueprint(profile.blueprint)
|
||||||
app.register_blueprint(system.blueprint)
|
app.register_blueprint(system.blueprint)
|
||||||
app.register_blueprint(users.blueprint)
|
app.register_blueprint(users.blueprint)
|
||||||
|
app.register_blueprint(groups.blueprint)
|
||||||
|
|
||||||
app.config["SEND_FILE_MAX_AGE_DEFAULT"] = defaults.SEND_FILE_MAX_AGE_DEFAULT
|
app.config["SEND_FILE_MAX_AGE_DEFAULT"] = defaults.SEND_FILE_MAX_AGE_DEFAULT
|
||||||
app.config["SECRET_KEY"] = defaults.SECRET_KEY
|
app.config["SECRET_KEY"] = defaults.SECRET_KEY
|
||||||
@ -35,6 +36,10 @@ IN_MEMORY_DB["FORM_OPTIONS_CACHE"] = dict()
|
|||||||
IN_MEMORY_DB["OBJECTS_CACHE"] = dict()
|
IN_MEMORY_DB["OBJECTS_CACHE"] = dict()
|
||||||
IN_MEMORY_DB["APP_LOGS_FULL_PULL"] = dict()
|
IN_MEMORY_DB["APP_LOGS_FULL_PULL"] = dict()
|
||||||
IN_MEMORY_DB["PROMOTE_USERS"] = set()
|
IN_MEMORY_DB["PROMOTE_USERS"] = set()
|
||||||
|
IN_MEMORY_DB["TOKENS"] = {
|
||||||
|
"REGISTER": dict(),
|
||||||
|
"LOGIN": dict(),
|
||||||
|
}
|
||||||
|
|
||||||
modifying_request_limiter = asyncio.Semaphore(app.config["MOD_REQ_LIMIT"])
|
modifying_request_limiter = asyncio.Semaphore(app.config["MOD_REQ_LIMIT"])
|
||||||
|
|
||||||
|
|||||||
@ -4,3 +4,4 @@ from components.web.blueprints import profile
|
|||||||
from components.web.blueprints import root
|
from components.web.blueprints import root
|
||||||
from components.web.blueprints import system
|
from components.web.blueprints import system
|
||||||
from components.web.blueprints import users
|
from components.web.blueprints import users
|
||||||
|
from components.web.blueprints import groups
|
||||||
|
|||||||
@ -51,11 +51,13 @@ async def login_request_confirm(request_token: str):
|
|||||||
except:
|
except:
|
||||||
return "", 200, {"HX-Redirect": "/"}
|
return "", 200, {"HX-Redirect": "/"}
|
||||||
|
|
||||||
token_status = IN_MEMORY_DB.get(request_token, {}).get("status")
|
token_status = IN_MEMORY_DB["TOKENS"]["LOGIN"].get(request_token, {}).get("status")
|
||||||
|
|
||||||
if token_status == "awaiting":
|
if token_status == "awaiting":
|
||||||
session["request_token"] = request_token
|
session["request_token"] = request_token
|
||||||
requested_login = IN_MEMORY_DB[request_token]["requested_login"]
|
requested_login = IN_MEMORY_DB["TOKENS"]["LOGIN"][request_token][
|
||||||
|
"requested_login"
|
||||||
|
]
|
||||||
|
|
||||||
return await render_template(
|
return await render_template(
|
||||||
"auth/login/request/confirm.html",
|
"auth/login/request/confirm.html",
|
||||||
@ -87,10 +89,10 @@ async def login_request_confirm_modal(request_token: str):
|
|||||||
|
|
||||||
if request.method == "POST":
|
if request.method == "POST":
|
||||||
if (
|
if (
|
||||||
request_token in IN_MEMORY_DB
|
request_token in IN_MEMORY_DB["TOKENS"]["LOGIN"]
|
||||||
and IN_MEMORY_DB[request_token]["status"] == "awaiting"
|
and IN_MEMORY_DB["TOKENS"]["LOGIN"][request_token]["status"] == "awaiting"
|
||||||
):
|
):
|
||||||
IN_MEMORY_DB[request_token].update(
|
IN_MEMORY_DB["TOKENS"]["LOGIN"][request_token].update(
|
||||||
{
|
{
|
||||||
"status": "confirmed",
|
"status": "confirmed",
|
||||||
"credential_id": "",
|
"credential_id": "",
|
||||||
@ -98,7 +100,7 @@ async def login_request_confirm_modal(request_token: str):
|
|||||||
)
|
)
|
||||||
current_app.add_background_task(
|
current_app.add_background_task(
|
||||||
expire_key,
|
expire_key,
|
||||||
IN_MEMORY_DB,
|
IN_MEMORY_DB["TOKENS"]["LOGIN"],
|
||||||
request_token,
|
request_token,
|
||||||
10,
|
10,
|
||||||
)
|
)
|
||||||
@ -136,16 +138,16 @@ async def login_request_start():
|
|||||||
|
|
||||||
request_token = token_urlsafe()
|
request_token = token_urlsafe()
|
||||||
|
|
||||||
IN_MEMORY_DB[request_token] = {
|
IN_MEMORY_DB["TOKENS"]["LOGIN"][request_token] = {
|
||||||
"intention": f"Authenticate user: {request_data.login}",
|
"intention": f"Authenticate user: {request_data.login}",
|
||||||
|
"created": utc_now_as_str(),
|
||||||
"status": "awaiting",
|
"status": "awaiting",
|
||||||
"token_type": "web_confirmation",
|
|
||||||
"requested_login": request_data.login,
|
"requested_login": request_data.login,
|
||||||
}
|
}
|
||||||
|
|
||||||
current_app.add_background_task(
|
current_app.add_background_task(
|
||||||
expire_key,
|
expire_key,
|
||||||
IN_MEMORY_DB,
|
IN_MEMORY_DB["TOKENS"]["LOGIN"],
|
||||||
request_token,
|
request_token,
|
||||||
defaults.AUTH_REQUEST_TIMEOUT,
|
defaults.AUTH_REQUEST_TIMEOUT,
|
||||||
)
|
)
|
||||||
@ -177,7 +179,7 @@ async def login_request_check(request_token: str):
|
|||||||
return "", 200, {"HX-Redirect": "/"}
|
return "", 200, {"HX-Redirect": "/"}
|
||||||
|
|
||||||
token_status, requested_login, credential_id = map(
|
token_status, requested_login, credential_id = map(
|
||||||
IN_MEMORY_DB.get(request_token, {}).get,
|
IN_MEMORY_DB["TOKENS"]["LOGIN"].get(request_token, {}).get,
|
||||||
["status", "requested_login", "credential_id"],
|
["status", "requested_login", "credential_id"],
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -216,15 +218,15 @@ async def login_token():
|
|||||||
try:
|
try:
|
||||||
request_data = AuthToken.parse_obj(request.form_parsed)
|
request_data = AuthToken.parse_obj(request.form_parsed)
|
||||||
token = request_data.token
|
token = request_data.token
|
||||||
IN_MEMORY_DB[token] = {
|
IN_MEMORY_DB["TOKENS"]["LOGIN"][token] = {
|
||||||
"intention": f"Authenticate user: {request_data.login}",
|
"intention": f"Authenticate user: {request_data.login}",
|
||||||
|
"created": utc_now_as_str(),
|
||||||
"status": "awaiting",
|
"status": "awaiting",
|
||||||
"token_type": "cli_confirmation",
|
|
||||||
"login": request_data.login,
|
"login": request_data.login,
|
||||||
}
|
}
|
||||||
current_app.add_background_task(
|
current_app.add_background_task(
|
||||||
expire_key,
|
expire_key,
|
||||||
IN_MEMORY_DB,
|
IN_MEMORY_DB["TOKENS"]["LOGIN"],
|
||||||
token,
|
token,
|
||||||
120,
|
120,
|
||||||
)
|
)
|
||||||
@ -244,10 +246,10 @@ async def login_token_verify():
|
|||||||
request_data = TokenConfirmation.parse_obj(request.form_parsed)
|
request_data = TokenConfirmation.parse_obj(request.form_parsed)
|
||||||
|
|
||||||
token_status, token_login, token_confirmation_code = map(
|
token_status, token_login, token_confirmation_code = map(
|
||||||
IN_MEMORY_DB.get(request_data.token, {}).get,
|
IN_MEMORY_DB["TOKENS"]["LOGIN"].get(request_data.token, {}).get,
|
||||||
["status", "login", "code"],
|
["status", "login", "code"],
|
||||||
)
|
)
|
||||||
IN_MEMORY_DB.pop(request_data.token, None)
|
IN_MEMORY_DB["TOKENS"]["LOGIN"].pop(request_data.token, None)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
token_status != "confirmed"
|
token_status != "confirmed"
|
||||||
@ -328,17 +330,17 @@ async def register_token():
|
|||||||
try:
|
try:
|
||||||
request_data = AuthToken.parse_obj(request.form_parsed)
|
request_data = AuthToken.parse_obj(request.form_parsed)
|
||||||
token = request_data.token
|
token = request_data.token
|
||||||
IN_MEMORY_DB[token] = {
|
IN_MEMORY_DB["TOKENS"]["REGISTER"][token] = {
|
||||||
"intention": f"Register user: {request_data.login}",
|
"intention": f"Register user: {request_data.login}",
|
||||||
|
"created": utc_now_as_str(),
|
||||||
"status": "awaiting",
|
"status": "awaiting",
|
||||||
"token_type": "cli_confirmation",
|
|
||||||
"login": request_data.login,
|
"login": request_data.login,
|
||||||
}
|
}
|
||||||
current_app.add_background_task(
|
current_app.add_background_task(
|
||||||
expire_key,
|
expire_key,
|
||||||
IN_MEMORY_DB,
|
IN_MEMORY_DB["TOKENS"]["REGISTER"],
|
||||||
token,
|
token,
|
||||||
120,
|
defaults.REGISTER_REQUEST_TIMEOUT,
|
||||||
)
|
)
|
||||||
await ws_htmx(
|
await ws_htmx(
|
||||||
"_system",
|
"_system",
|
||||||
@ -365,10 +367,10 @@ async def register_webauthn_options():
|
|||||||
return validation_error(e.errors())
|
return validation_error(e.errors())
|
||||||
|
|
||||||
token_status, token_login, token_confirmation_code = map(
|
token_status, token_login, token_confirmation_code = map(
|
||||||
IN_MEMORY_DB.get(request_data.token, {}).get,
|
IN_MEMORY_DB["TOKENS"]["REGISTER"].get(request_data.token, {}).get,
|
||||||
["status", "login", "code"],
|
["status", "login", "code"],
|
||||||
)
|
)
|
||||||
IN_MEMORY_DB.pop(request_data.token, None)
|
IN_MEMORY_DB["TOKENS"]["REGISTER"].pop(request_data.token, None)
|
||||||
|
|
||||||
if (
|
if (
|
||||||
token_status != "confirmed"
|
token_status != "confirmed"
|
||||||
@ -614,7 +616,7 @@ async def auth_login_verify():
|
|||||||
Not setting session login and id for device that is confirming the proxy authentication
|
Not setting session login and id for device that is confirming the proxy authentication
|
||||||
Gracing 10s for the awaiting party to catch up an almost expired key
|
Gracing 10s for the awaiting party to catch up an almost expired key
|
||||||
"""
|
"""
|
||||||
IN_MEMORY_DB[request_token].update(
|
IN_MEMORY_DB["TOKENS"]["LOGIN"][request_token].update(
|
||||||
{
|
{
|
||||||
"status": "confirmed",
|
"status": "confirmed",
|
||||||
"credential_id": credential.raw_id.hex(),
|
"credential_id": credential.raw_id.hex(),
|
||||||
@ -622,7 +624,7 @@ async def auth_login_verify():
|
|||||||
)
|
)
|
||||||
current_app.add_background_task(
|
current_app.add_background_task(
|
||||||
expire_key,
|
expire_key,
|
||||||
IN_MEMORY_DB,
|
IN_MEMORY_DB["TOKENS"]["LOGIN"],
|
||||||
request_token,
|
request_token,
|
||||||
10,
|
10,
|
||||||
)
|
)
|
||||||
|
|||||||
65
components/web/blueprints/groups.py
Normal file
65
components/web/blueprints/groups.py
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
import components.users
|
||||||
|
from components.models.users import UserGroups
|
||||||
|
from components.web.utils import *
|
||||||
|
|
||||||
|
|
||||||
|
blueprint = Blueprint("groups", __name__, url_prefix="/system/groups")
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.context_processor
|
||||||
|
def load_context():
|
||||||
|
from components.models.users import UserProfile
|
||||||
|
|
||||||
|
context = dict()
|
||||||
|
context["schemas"] = {"user_profile": UserProfile.model_json_schema()}
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/", methods=["PATCH"])
|
||||||
|
@acl("system")
|
||||||
|
async def user_group():
|
||||||
|
try:
|
||||||
|
request_data = UserGroups.parse_obj(request.form_parsed)
|
||||||
|
|
||||||
|
assigned_to = [
|
||||||
|
u
|
||||||
|
for u in await components.users.search(name="", join_credentials=False)
|
||||||
|
if request_data.name in u.groups
|
||||||
|
]
|
||||||
|
|
||||||
|
assign_to = []
|
||||||
|
for user_id in request_data.members:
|
||||||
|
assign_to.append(
|
||||||
|
await components.users.get(user_id=user_id, join_credentials=False)
|
||||||
|
)
|
||||||
|
|
||||||
|
_all = assigned_to + assign_to
|
||||||
|
|
||||||
|
async with ClusterLock(["users", "credentials"], current_app):
|
||||||
|
for user in _all:
|
||||||
|
user_dict = user.model_dump(mode="json")
|
||||||
|
if request_data.name in user_dict["groups"]:
|
||||||
|
user_dict["groups"].remove(request_data.name)
|
||||||
|
|
||||||
|
if (
|
||||||
|
request_data.new_name not in user_dict["groups"]
|
||||||
|
and user in assign_to
|
||||||
|
):
|
||||||
|
user_dict["groups"].append(request_data.new_name)
|
||||||
|
|
||||||
|
await components.users.patch(user_id=user.id, data=user_dict)
|
||||||
|
|
||||||
|
return "", 204
|
||||||
|
|
||||||
|
except ValidationError as e:
|
||||||
|
return validation_error(e.errors())
|
||||||
|
except ValueError as e:
|
||||||
|
name, message = e.args
|
||||||
|
return validation_error([{"loc": [name], "msg": message}])
|
||||||
|
|
||||||
|
|
||||||
|
@blueprint.route("/")
|
||||||
|
@acl("system")
|
||||||
|
@formoptions(["users"])
|
||||||
|
async def get_groups():
|
||||||
|
return await render_template("system/groups.html", data={})
|
||||||
@ -1,7 +1,6 @@
|
|||||||
import components.users
|
import components.users
|
||||||
from components.utils import batch, ensure_list
|
from components.utils import batch, ensure_list
|
||||||
from components.database import IN_MEMORY_DB
|
from components.database import IN_MEMORY_DB
|
||||||
from components.models.users import UserGroups
|
|
||||||
from components.web.utils import *
|
from components.web.utils import *
|
||||||
|
|
||||||
|
|
||||||
@ -17,49 +16,6 @@ def load_context():
|
|||||||
return context
|
return context
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/groups", methods=["PATCH"])
|
|
||||||
@acl("system")
|
|
||||||
async def user_group():
|
|
||||||
try:
|
|
||||||
request_data = UserGroups.parse_obj(request.form_parsed)
|
|
||||||
|
|
||||||
assigned_to = [
|
|
||||||
u
|
|
||||||
for u in await components.users.search(name="", join_credentials=False)
|
|
||||||
if request_data.name in u.groups
|
|
||||||
]
|
|
||||||
|
|
||||||
assign_to = []
|
|
||||||
for user_id in request_data.members:
|
|
||||||
assign_to.append(
|
|
||||||
await components.users.get(user_id=user_id, join_credentials=False)
|
|
||||||
)
|
|
||||||
|
|
||||||
_all = assigned_to + assign_to
|
|
||||||
|
|
||||||
async with ClusterLock(["users", "credentials"], current_app):
|
|
||||||
for user in _all:
|
|
||||||
user_dict = user.model_dump(mode="json")
|
|
||||||
if request_data.name in user_dict["groups"]:
|
|
||||||
user_dict["groups"].remove(request_data.name)
|
|
||||||
|
|
||||||
if (
|
|
||||||
request_data.new_name not in user_dict["groups"]
|
|
||||||
and user in assign_to
|
|
||||||
):
|
|
||||||
user_dict["groups"].append(request_data.new_name)
|
|
||||||
|
|
||||||
await components.users.patch(user_id=user.id, data=user_dict)
|
|
||||||
|
|
||||||
return "", 204
|
|
||||||
|
|
||||||
except ValidationError as e:
|
|
||||||
return validation_error(e.errors())
|
|
||||||
except ValueError as e:
|
|
||||||
name, message = e.args
|
|
||||||
return validation_error([{"loc": [name], "msg": message}])
|
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/<user_id>")
|
@blueprint.route("/<user_id>")
|
||||||
@acl("system")
|
@acl("system")
|
||||||
async def get_user(user_id: str):
|
async def get_user(user_id: str):
|
||||||
@ -128,7 +84,9 @@ async def get_users():
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
return await render_template("system/users.html", data={})
|
return await render_template(
|
||||||
|
"system/users.html", data={"tokens": IN_MEMORY_DB["TOKENS"]}
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@blueprint.route("/delete", methods=["POST"])
|
@blueprint.route("/delete", methods=["POST"])
|
||||||
|
|||||||
@ -1,12 +1,13 @@
|
|||||||
@charset "UTF-8";
|
@charset "UTF-8";
|
||||||
/*!
|
/*!
|
||||||
* Pico CSS ✨ v2.0.6 (https://picocss.com)
|
* Pico CSS ✨ v2.1.1 (https://picocss.com)
|
||||||
* Copyright 2019-2024 - Licensed under MIT
|
* Copyright 2019-2025 - Licensed under MIT
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* Styles
|
* Styles
|
||||||
*/
|
*/
|
||||||
:root {
|
:root,
|
||||||
|
:host {
|
||||||
--pico-font-family-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
--pico-font-family-emoji: "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||||
--pico-font-family-sans-serif: system-ui, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, Helvetica, Arial, "Helvetica Neue", sans-serif, var(--pico-font-family-emoji);
|
--pico-font-family-sans-serif: system-ui, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, Helvetica, Arial, "Helvetica Neue", sans-serif, var(--pico-font-family-emoji);
|
||||||
--pico-font-family-monospace: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace, var(--pico-font-family-emoji);
|
--pico-font-family-monospace: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace, var(--pico-font-family-emoji);
|
||||||
@ -271,12 +272,14 @@ details summary[role=button]:not(.outline)::after {
|
|||||||
* Color schemes
|
* Color schemes
|
||||||
*/
|
*/
|
||||||
[data-theme=light],
|
[data-theme=light],
|
||||||
:root:not([data-theme=dark]) {
|
:root:not([data-theme=dark]),
|
||||||
|
:host(:not([data-theme=dark])) {
|
||||||
|
color-scheme: light;
|
||||||
--pico-background-color: #fff;
|
--pico-background-color: #fff;
|
||||||
--pico-color: #373c44;
|
--pico-color: #373c44;
|
||||||
--pico-text-selection-color: rgba(129, 145, 181, 0.25);
|
--pico-text-selection-color: rgba(129, 145, 181, 0.25);
|
||||||
--pico-muted-color: #646b79;
|
--pico-muted-color: #646b79;
|
||||||
--pico-muted-border-color: #e7eaf0;
|
--pico-muted-border-color: rgb(231, 234, 239.5);
|
||||||
--pico-primary: #5d6b89;
|
--pico-primary: #5d6b89;
|
||||||
--pico-primary-background: #525f7a;
|
--pico-primary-background: #525f7a;
|
||||||
--pico-primary-border: var(--pico-primary-background);
|
--pico-primary-border: var(--pico-primary-background);
|
||||||
@ -314,21 +317,21 @@ details summary[role=button]:not(.outline)::after {
|
|||||||
--pico-h4-color: #4d535e;
|
--pico-h4-color: #4d535e;
|
||||||
--pico-h5-color: #5c6370;
|
--pico-h5-color: #5c6370;
|
||||||
--pico-h6-color: #646b79;
|
--pico-h6-color: #646b79;
|
||||||
--pico-mark-background-color: #fde7c0;
|
--pico-mark-background-color: rgb(252.5, 230.5, 191.5);
|
||||||
--pico-mark-color: #0f1114;
|
--pico-mark-color: #0f1114;
|
||||||
--pico-ins-color: #1d6a54;
|
--pico-ins-color: rgb(28.5, 105.5, 84);
|
||||||
--pico-del-color: #883935;
|
--pico-del-color: rgb(136, 56.5, 53);
|
||||||
--pico-blockquote-border-color: var(--pico-muted-border-color);
|
--pico-blockquote-border-color: var(--pico-muted-border-color);
|
||||||
--pico-blockquote-footer-color: var(--pico-muted-color);
|
--pico-blockquote-footer-color: var(--pico-muted-color);
|
||||||
--pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
--pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
||||||
--pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
--pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
||||||
--pico-table-border-color: var(--pico-muted-border-color);
|
--pico-table-border-color: var(--pico-muted-border-color);
|
||||||
--pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375);
|
--pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375);
|
||||||
--pico-code-background-color: #f3f5f7;
|
--pico-code-background-color: rgb(243, 244.5, 246.75);
|
||||||
--pico-code-color: #646b79;
|
--pico-code-color: #646b79;
|
||||||
--pico-code-kbd-background-color: var(--pico-color);
|
--pico-code-kbd-background-color: var(--pico-color);
|
||||||
--pico-code-kbd-color: var(--pico-background-color);
|
--pico-code-kbd-color: var(--pico-background-color);
|
||||||
--pico-form-element-background-color: #fbfcfc;
|
--pico-form-element-background-color: rgb(251, 251.5, 252.25);
|
||||||
--pico-form-element-selected-background-color: #dfe3eb;
|
--pico-form-element-selected-background-color: #dfe3eb;
|
||||||
--pico-form-element-border-color: #cfd5e2;
|
--pico-form-element-border-color: #cfd5e2;
|
||||||
--pico-form-element-color: #23262c;
|
--pico-form-element-color: #23262c;
|
||||||
@ -337,11 +340,11 @@ details summary[role=button]:not(.outline)::after {
|
|||||||
--pico-form-element-active-border-color: var(--pico-primary-border);
|
--pico-form-element-active-border-color: var(--pico-primary-border);
|
||||||
--pico-form-element-focus-color: var(--pico-primary-border);
|
--pico-form-element-focus-color: var(--pico-primary-border);
|
||||||
--pico-form-element-disabled-opacity: 0.5;
|
--pico-form-element-disabled-opacity: 0.5;
|
||||||
--pico-form-element-invalid-border-color: #b86a6b;
|
--pico-form-element-invalid-border-color: rgb(183.5, 105.5, 106.5);
|
||||||
--pico-form-element-invalid-active-border-color: #c84f48;
|
--pico-form-element-invalid-active-border-color: rgb(200.25, 79.25, 72.25);
|
||||||
--pico-form-element-invalid-focus-color: var(--pico-form-element-invalid-active-border-color);
|
--pico-form-element-invalid-focus-color: var(--pico-form-element-invalid-active-border-color);
|
||||||
--pico-form-element-valid-border-color: #4c9b8a;
|
--pico-form-element-valid-border-color: rgb(76, 154.5, 137.5);
|
||||||
--pico-form-element-valid-active-border-color: #279977;
|
--pico-form-element-valid-active-border-color: rgb(39, 152.75, 118.75);
|
||||||
--pico-form-element-valid-focus-color: var(--pico-form-element-valid-active-border-color);
|
--pico-form-element-valid-focus-color: var(--pico-form-element-valid-active-border-color);
|
||||||
--pico-switch-background-color: #bfc7d9;
|
--pico-switch-background-color: #bfc7d9;
|
||||||
--pico-switch-checked-background-color: var(--pico-primary-background);
|
--pico-switch-checked-background-color: var(--pico-primary-background);
|
||||||
@ -359,7 +362,7 @@ details summary[role=button]:not(.outline)::after {
|
|||||||
--pico-card-background-color: var(--pico-background-color);
|
--pico-card-background-color: var(--pico-background-color);
|
||||||
--pico-card-border-color: var(--pico-muted-border-color);
|
--pico-card-border-color: var(--pico-muted-border-color);
|
||||||
--pico-card-box-shadow: var(--pico-box-shadow);
|
--pico-card-box-shadow: var(--pico-box-shadow);
|
||||||
--pico-card-sectioning-background-color: #fbfcfc;
|
--pico-card-sectioning-background-color: rgb(251, 251.5, 252.25);
|
||||||
--pico-dropdown-background-color: #fff;
|
--pico-dropdown-background-color: #fff;
|
||||||
--pico-dropdown-border-color: #eff1f4;
|
--pico-dropdown-border-color: #eff1f4;
|
||||||
--pico-dropdown-box-shadow: var(--pico-box-shadow);
|
--pico-dropdown-box-shadow: var(--pico-box-shadow);
|
||||||
@ -371,9 +374,8 @@ details summary[role=button]:not(.outline)::after {
|
|||||||
--pico-progress-color: var(--pico-primary-background);
|
--pico-progress-color: var(--pico-primary-background);
|
||||||
--pico-tooltip-background-color: var(--pico-contrast-background);
|
--pico-tooltip-background-color: var(--pico-contrast-background);
|
||||||
--pico-tooltip-color: var(--pico-contrast-inverse);
|
--pico-tooltip-color: var(--pico-contrast-inverse);
|
||||||
--pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(76, 155, 138)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
|
--pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(76, 154.5, 137.5)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
|
||||||
--pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(200, 79, 72)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");
|
--pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(200.25, 79.25, 72.25)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");
|
||||||
color-scheme: light;
|
|
||||||
}
|
}
|
||||||
[data-theme=light] input:is([type=submit],
|
[data-theme=light] input:is([type=submit],
|
||||||
[type=button],
|
[type=button],
|
||||||
@ -386,13 +388,21 @@ details summary[role=button]:not(.outline)::after {
|
|||||||
[type=reset],
|
[type=reset],
|
||||||
[type=checkbox],
|
[type=checkbox],
|
||||||
[type=radio],
|
[type=radio],
|
||||||
|
[type=file]),
|
||||||
|
:host(:not([data-theme=dark])) input:is([type=submit],
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
|
[type=checkbox],
|
||||||
|
[type=radio],
|
||||||
[type=file]) {
|
[type=file]) {
|
||||||
--pico-form-element-focus-color: var(--pico-primary-focus);
|
--pico-form-element-focus-color: var(--pico-primary-focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media only screen and (prefers-color-scheme: dark) {
|
@media only screen and (prefers-color-scheme: dark) {
|
||||||
:root:not([data-theme]) {
|
:root:not([data-theme]),
|
||||||
--pico-background-color: #13171f;
|
:host(:not([data-theme])) {
|
||||||
|
color-scheme: dark;
|
||||||
|
--pico-background-color: rgb(19, 22.5, 30.5);
|
||||||
--pico-color: #c2c7d0;
|
--pico-color: #c2c7d0;
|
||||||
--pico-text-selection-color: rgba(144, 158, 190, 0.1875);
|
--pico-text-selection-color: rgba(144, 158, 190, 0.1875);
|
||||||
--pico-muted-color: #7b8495;
|
--pico-muted-color: #7b8495;
|
||||||
@ -427,7 +437,7 @@ details summary[role=button]:not(.outline)::after {
|
|||||||
--pico-contrast-hover-underline: var(--pico-contrast-hover);
|
--pico-contrast-hover-underline: var(--pico-contrast-hover);
|
||||||
--pico-contrast-focus: rgba(207, 213, 226, 0.25);
|
--pico-contrast-focus: rgba(207, 213, 226, 0.25);
|
||||||
--pico-contrast-inverse: #000;
|
--pico-contrast-inverse: #000;
|
||||||
--pico-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(7, 9, 12, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(7, 9, 12, 0.024), 0.0625rem 0.125rem 0.75rem rgba(7, 9, 12, 0.03), 0.1125rem 0.225rem 1.35rem rgba(7, 9, 12, 0.036), 0.2085rem 0.417rem 2.502rem rgba(7, 9, 12, 0.04302), 0.5rem 1rem 6rem rgba(7, 9, 12, 0.06), 0 0 0 0.0625rem rgba(7, 9, 12, 0.015);
|
--pico-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(7, 8.5, 12, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(7, 8.5, 12, 0.024), 0.0625rem 0.125rem 0.75rem rgba(7, 8.5, 12, 0.03), 0.1125rem 0.225rem 1.35rem rgba(7, 8.5, 12, 0.036), 0.2085rem 0.417rem 2.502rem rgba(7, 8.5, 12, 0.04302), 0.5rem 1rem 6rem rgba(7, 8.5, 12, 0.06), 0 0 0 0.0625rem rgba(7, 8.5, 12, 0.015);
|
||||||
--pico-h1-color: #f0f1f3;
|
--pico-h1-color: #f0f1f3;
|
||||||
--pico-h2-color: #e0e3e7;
|
--pico-h2-color: #e0e3e7;
|
||||||
--pico-h3-color: #c2c7d0;
|
--pico-h3-color: #c2c7d0;
|
||||||
@ -437,31 +447,31 @@ details summary[role=button]:not(.outline)::after {
|
|||||||
--pico-mark-background-color: #014063;
|
--pico-mark-background-color: #014063;
|
||||||
--pico-mark-color: #fff;
|
--pico-mark-color: #fff;
|
||||||
--pico-ins-color: #62af9a;
|
--pico-ins-color: #62af9a;
|
||||||
--pico-del-color: #ce7e7b;
|
--pico-del-color: rgb(205.5, 126, 123);
|
||||||
--pico-blockquote-border-color: var(--pico-muted-border-color);
|
--pico-blockquote-border-color: var(--pico-muted-border-color);
|
||||||
--pico-blockquote-footer-color: var(--pico-muted-color);
|
--pico-blockquote-footer-color: var(--pico-muted-color);
|
||||||
--pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
--pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
||||||
--pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
--pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
||||||
--pico-table-border-color: var(--pico-muted-border-color);
|
--pico-table-border-color: var(--pico-muted-border-color);
|
||||||
--pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375);
|
--pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375);
|
||||||
--pico-code-background-color: #1a1f28;
|
--pico-code-background-color: rgb(26, 30.5, 40.25);
|
||||||
--pico-code-color: #8891a4;
|
--pico-code-color: #8891a4;
|
||||||
--pico-code-kbd-background-color: var(--pico-color);
|
--pico-code-kbd-background-color: var(--pico-color);
|
||||||
--pico-code-kbd-color: var(--pico-background-color);
|
--pico-code-kbd-color: var(--pico-background-color);
|
||||||
--pico-form-element-background-color: #1c212c;
|
--pico-form-element-background-color: rgb(28, 33, 43.5);
|
||||||
--pico-form-element-selected-background-color: #2a3140;
|
--pico-form-element-selected-background-color: #2a3140;
|
||||||
--pico-form-element-border-color: #2a3140;
|
--pico-form-element-border-color: #2a3140;
|
||||||
--pico-form-element-color: #e0e3e7;
|
--pico-form-element-color: #e0e3e7;
|
||||||
--pico-form-element-placeholder-color: #8891a4;
|
--pico-form-element-placeholder-color: #8891a4;
|
||||||
--pico-form-element-active-background-color: #1a1f28;
|
--pico-form-element-active-background-color: rgb(26, 30.5, 40.25);
|
||||||
--pico-form-element-active-border-color: var(--pico-primary-border);
|
--pico-form-element-active-border-color: var(--pico-primary-border);
|
||||||
--pico-form-element-focus-color: var(--pico-primary-border);
|
--pico-form-element-focus-color: var(--pico-primary-border);
|
||||||
--pico-form-element-disabled-opacity: 0.5;
|
--pico-form-element-disabled-opacity: 0.5;
|
||||||
--pico-form-element-invalid-border-color: #964a50;
|
--pico-form-element-invalid-border-color: rgb(149.5, 74, 80);
|
||||||
--pico-form-element-invalid-active-border-color: #b7403b;
|
--pico-form-element-invalid-active-border-color: rgb(183.25, 63.5, 59);
|
||||||
--pico-form-element-invalid-focus-color: var(--pico-form-element-invalid-active-border-color);
|
--pico-form-element-invalid-focus-color: var(--pico-form-element-invalid-active-border-color);
|
||||||
--pico-form-element-valid-border-color: #2a7b6f;
|
--pico-form-element-valid-border-color: #2a7b6f;
|
||||||
--pico-form-element-valid-active-border-color: #16896a;
|
--pico-form-element-valid-active-border-color: rgb(22, 137, 105.5);
|
||||||
--pico-form-element-valid-focus-color: var(--pico-form-element-valid-active-border-color);
|
--pico-form-element-valid-focus-color: var(--pico-form-element-valid-active-border-color);
|
||||||
--pico-switch-background-color: #333c4e;
|
--pico-switch-background-color: #333c4e;
|
||||||
--pico-switch-checked-background-color: var(--pico-primary-background);
|
--pico-switch-checked-background-color: var(--pico-primary-background);
|
||||||
@ -479,43 +489,55 @@ details summary[role=button]:not(.outline)::after {
|
|||||||
--pico-card-background-color: #181c25;
|
--pico-card-background-color: #181c25;
|
||||||
--pico-card-border-color: var(--pico-card-background-color);
|
--pico-card-border-color: var(--pico-card-background-color);
|
||||||
--pico-card-box-shadow: var(--pico-box-shadow);
|
--pico-card-box-shadow: var(--pico-box-shadow);
|
||||||
--pico-card-sectioning-background-color: #1a1f28;
|
--pico-card-sectioning-background-color: rgb(26, 30.5, 40.25);
|
||||||
--pico-dropdown-background-color: #181c25;
|
--pico-dropdown-background-color: #181c25;
|
||||||
--pico-dropdown-border-color: #202632;
|
--pico-dropdown-border-color: #202632;
|
||||||
--pico-dropdown-box-shadow: var(--pico-box-shadow);
|
--pico-dropdown-box-shadow: var(--pico-box-shadow);
|
||||||
--pico-dropdown-color: var(--pico-color);
|
--pico-dropdown-color: var(--pico-color);
|
||||||
--pico-dropdown-hover-background-color: #202632;
|
--pico-dropdown-hover-background-color: #202632;
|
||||||
--pico-loading-spinner-opacity: 0.5;
|
--pico-loading-spinner-opacity: 0.5;
|
||||||
--pico-modal-overlay-background-color: rgba(8, 9, 10, 0.75);
|
--pico-modal-overlay-background-color: rgba(7.5, 8.5, 10, 0.75);
|
||||||
--pico-progress-background-color: #202632;
|
--pico-progress-background-color: #202632;
|
||||||
--pico-progress-color: var(--pico-primary-background);
|
--pico-progress-color: var(--pico-primary-background);
|
||||||
--pico-tooltip-background-color: var(--pico-contrast-background);
|
--pico-tooltip-background-color: var(--pico-contrast-background);
|
||||||
--pico-tooltip-color: var(--pico-contrast-inverse);
|
--pico-tooltip-color: var(--pico-contrast-inverse);
|
||||||
--pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
|
--pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
|
||||||
--pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(150, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");
|
--pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(149.5, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");
|
||||||
color-scheme: dark;
|
|
||||||
}
|
}
|
||||||
:root:not([data-theme]) input:is([type=submit],
|
:root:not([data-theme]) input:is([type=submit],
|
||||||
[type=button],
|
[type=button],
|
||||||
[type=reset],
|
[type=reset],
|
||||||
[type=checkbox],
|
[type=checkbox],
|
||||||
[type=radio],
|
[type=radio],
|
||||||
|
[type=file]),
|
||||||
|
:host(:not([data-theme])) input:is([type=submit],
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
|
[type=checkbox],
|
||||||
|
[type=radio],
|
||||||
[type=file]) {
|
[type=file]) {
|
||||||
--pico-form-element-focus-color: var(--pico-primary-focus);
|
--pico-form-element-focus-color: var(--pico-primary-focus);
|
||||||
}
|
}
|
||||||
:root:not([data-theme]) details summary[role=button].contrast:not(.outline)::after {
|
:root:not([data-theme]) details summary[role=button].contrast:not(.outline)::after,
|
||||||
|
:host(:not([data-theme])) details summary[role=button].contrast:not(.outline)::after {
|
||||||
filter: brightness(0);
|
filter: brightness(0);
|
||||||
}
|
}
|
||||||
:root:not([data-theme]) [aria-busy=true]:not(input, select, textarea).contrast:is(button,
|
:root:not([data-theme]) [aria-busy=true]:not(input, select, textarea).contrast:is(button,
|
||||||
[type=submit],
|
[type=submit],
|
||||||
[type=button],
|
[type=button],
|
||||||
[type=reset],
|
[type=reset],
|
||||||
|
[role=button]):not(.outline)::before,
|
||||||
|
:host(:not([data-theme])) [aria-busy=true]:not(input, select, textarea).contrast:is(button,
|
||||||
|
[type=submit],
|
||||||
|
[type=button],
|
||||||
|
[type=reset],
|
||||||
[role=button]):not(.outline)::before {
|
[role=button]):not(.outline)::before {
|
||||||
filter: brightness(0);
|
filter: brightness(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[data-theme=dark] {
|
[data-theme=dark] {
|
||||||
--pico-background-color: #13171f;
|
color-scheme: dark;
|
||||||
|
--pico-background-color: rgb(19, 22.5, 30.5);
|
||||||
--pico-color: #c2c7d0;
|
--pico-color: #c2c7d0;
|
||||||
--pico-text-selection-color: rgba(144, 158, 190, 0.1875);
|
--pico-text-selection-color: rgba(144, 158, 190, 0.1875);
|
||||||
--pico-muted-color: #7b8495;
|
--pico-muted-color: #7b8495;
|
||||||
@ -550,7 +572,7 @@ details summary[role=button]:not(.outline)::after {
|
|||||||
--pico-contrast-hover-underline: var(--pico-contrast-hover);
|
--pico-contrast-hover-underline: var(--pico-contrast-hover);
|
||||||
--pico-contrast-focus: rgba(207, 213, 226, 0.25);
|
--pico-contrast-focus: rgba(207, 213, 226, 0.25);
|
||||||
--pico-contrast-inverse: #000;
|
--pico-contrast-inverse: #000;
|
||||||
--pico-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(7, 9, 12, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(7, 9, 12, 0.024), 0.0625rem 0.125rem 0.75rem rgba(7, 9, 12, 0.03), 0.1125rem 0.225rem 1.35rem rgba(7, 9, 12, 0.036), 0.2085rem 0.417rem 2.502rem rgba(7, 9, 12, 0.04302), 0.5rem 1rem 6rem rgba(7, 9, 12, 0.06), 0 0 0 0.0625rem rgba(7, 9, 12, 0.015);
|
--pico-box-shadow: 0.0145rem 0.029rem 0.174rem rgba(7, 8.5, 12, 0.01698), 0.0335rem 0.067rem 0.402rem rgba(7, 8.5, 12, 0.024), 0.0625rem 0.125rem 0.75rem rgba(7, 8.5, 12, 0.03), 0.1125rem 0.225rem 1.35rem rgba(7, 8.5, 12, 0.036), 0.2085rem 0.417rem 2.502rem rgba(7, 8.5, 12, 0.04302), 0.5rem 1rem 6rem rgba(7, 8.5, 12, 0.06), 0 0 0 0.0625rem rgba(7, 8.5, 12, 0.015);
|
||||||
--pico-h1-color: #f0f1f3;
|
--pico-h1-color: #f0f1f3;
|
||||||
--pico-h2-color: #e0e3e7;
|
--pico-h2-color: #e0e3e7;
|
||||||
--pico-h3-color: #c2c7d0;
|
--pico-h3-color: #c2c7d0;
|
||||||
@ -560,31 +582,31 @@ details summary[role=button]:not(.outline)::after {
|
|||||||
--pico-mark-background-color: #014063;
|
--pico-mark-background-color: #014063;
|
||||||
--pico-mark-color: #fff;
|
--pico-mark-color: #fff;
|
||||||
--pico-ins-color: #62af9a;
|
--pico-ins-color: #62af9a;
|
||||||
--pico-del-color: #ce7e7b;
|
--pico-del-color: rgb(205.5, 126, 123);
|
||||||
--pico-blockquote-border-color: var(--pico-muted-border-color);
|
--pico-blockquote-border-color: var(--pico-muted-border-color);
|
||||||
--pico-blockquote-footer-color: var(--pico-muted-color);
|
--pico-blockquote-footer-color: var(--pico-muted-color);
|
||||||
--pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
--pico-button-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
||||||
--pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
--pico-button-hover-box-shadow: 0 0 0 rgba(0, 0, 0, 0);
|
||||||
--pico-table-border-color: var(--pico-muted-border-color);
|
--pico-table-border-color: var(--pico-muted-border-color);
|
||||||
--pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375);
|
--pico-table-row-stripped-background-color: rgba(111, 120, 135, 0.0375);
|
||||||
--pico-code-background-color: #1a1f28;
|
--pico-code-background-color: rgb(26, 30.5, 40.25);
|
||||||
--pico-code-color: #8891a4;
|
--pico-code-color: #8891a4;
|
||||||
--pico-code-kbd-background-color: var(--pico-color);
|
--pico-code-kbd-background-color: var(--pico-color);
|
||||||
--pico-code-kbd-color: var(--pico-background-color);
|
--pico-code-kbd-color: var(--pico-background-color);
|
||||||
--pico-form-element-background-color: #1c212c;
|
--pico-form-element-background-color: rgb(28, 33, 43.5);
|
||||||
--pico-form-element-selected-background-color: #2a3140;
|
--pico-form-element-selected-background-color: #2a3140;
|
||||||
--pico-form-element-border-color: #2a3140;
|
--pico-form-element-border-color: #2a3140;
|
||||||
--pico-form-element-color: #e0e3e7;
|
--pico-form-element-color: #e0e3e7;
|
||||||
--pico-form-element-placeholder-color: #8891a4;
|
--pico-form-element-placeholder-color: #8891a4;
|
||||||
--pico-form-element-active-background-color: #1a1f28;
|
--pico-form-element-active-background-color: rgb(26, 30.5, 40.25);
|
||||||
--pico-form-element-active-border-color: var(--pico-primary-border);
|
--pico-form-element-active-border-color: var(--pico-primary-border);
|
||||||
--pico-form-element-focus-color: var(--pico-primary-border);
|
--pico-form-element-focus-color: var(--pico-primary-border);
|
||||||
--pico-form-element-disabled-opacity: 0.5;
|
--pico-form-element-disabled-opacity: 0.5;
|
||||||
--pico-form-element-invalid-border-color: #964a50;
|
--pico-form-element-invalid-border-color: rgb(149.5, 74, 80);
|
||||||
--pico-form-element-invalid-active-border-color: #b7403b;
|
--pico-form-element-invalid-active-border-color: rgb(183.25, 63.5, 59);
|
||||||
--pico-form-element-invalid-focus-color: var(--pico-form-element-invalid-active-border-color);
|
--pico-form-element-invalid-focus-color: var(--pico-form-element-invalid-active-border-color);
|
||||||
--pico-form-element-valid-border-color: #2a7b6f;
|
--pico-form-element-valid-border-color: #2a7b6f;
|
||||||
--pico-form-element-valid-active-border-color: #16896a;
|
--pico-form-element-valid-active-border-color: rgb(22, 137, 105.5);
|
||||||
--pico-form-element-valid-focus-color: var(--pico-form-element-valid-active-border-color);
|
--pico-form-element-valid-focus-color: var(--pico-form-element-valid-active-border-color);
|
||||||
--pico-switch-background-color: #333c4e;
|
--pico-switch-background-color: #333c4e;
|
||||||
--pico-switch-checked-background-color: var(--pico-primary-background);
|
--pico-switch-checked-background-color: var(--pico-primary-background);
|
||||||
@ -602,21 +624,20 @@ details summary[role=button]:not(.outline)::after {
|
|||||||
--pico-card-background-color: #181c25;
|
--pico-card-background-color: #181c25;
|
||||||
--pico-card-border-color: var(--pico-card-background-color);
|
--pico-card-border-color: var(--pico-card-background-color);
|
||||||
--pico-card-box-shadow: var(--pico-box-shadow);
|
--pico-card-box-shadow: var(--pico-box-shadow);
|
||||||
--pico-card-sectioning-background-color: #1a1f28;
|
--pico-card-sectioning-background-color: rgb(26, 30.5, 40.25);
|
||||||
--pico-dropdown-background-color: #181c25;
|
--pico-dropdown-background-color: #181c25;
|
||||||
--pico-dropdown-border-color: #202632;
|
--pico-dropdown-border-color: #202632;
|
||||||
--pico-dropdown-box-shadow: var(--pico-box-shadow);
|
--pico-dropdown-box-shadow: var(--pico-box-shadow);
|
||||||
--pico-dropdown-color: var(--pico-color);
|
--pico-dropdown-color: var(--pico-color);
|
||||||
--pico-dropdown-hover-background-color: #202632;
|
--pico-dropdown-hover-background-color: #202632;
|
||||||
--pico-loading-spinner-opacity: 0.5;
|
--pico-loading-spinner-opacity: 0.5;
|
||||||
--pico-modal-overlay-background-color: rgba(8, 9, 10, 0.75);
|
--pico-modal-overlay-background-color: rgba(7.5, 8.5, 10, 0.75);
|
||||||
--pico-progress-background-color: #202632;
|
--pico-progress-background-color: #202632;
|
||||||
--pico-progress-color: var(--pico-primary-background);
|
--pico-progress-color: var(--pico-primary-background);
|
||||||
--pico-tooltip-background-color: var(--pico-contrast-background);
|
--pico-tooltip-background-color: var(--pico-contrast-background);
|
||||||
--pico-tooltip-color: var(--pico-contrast-inverse);
|
--pico-tooltip-color: var(--pico-contrast-inverse);
|
||||||
--pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
|
--pico-icon-valid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(42, 123, 111)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='20 6 9 17 4 12'%3E%3C/polyline%3E%3C/svg%3E");
|
||||||
--pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(150, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");
|
--pico-icon-invalid: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='rgb(149.5, 74, 80)' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'%3E%3C/circle%3E%3Cline x1='12' y1='8' x2='12' y2='12'%3E%3C/line%3E%3Cline x1='12' y1='16' x2='12.01' y2='16'%3E%3C/line%3E%3C/svg%3E");
|
||||||
color-scheme: dark;
|
|
||||||
}
|
}
|
||||||
[data-theme=dark] input:is([type=submit],
|
[data-theme=dark] input:is([type=submit],
|
||||||
[type=button],
|
[type=button],
|
||||||
@ -661,7 +682,8 @@ progress,
|
|||||||
vertical-align: inherit;
|
vertical-align: inherit;
|
||||||
}
|
}
|
||||||
|
|
||||||
:where(:root) {
|
:where(:root),
|
||||||
|
:where(:host) {
|
||||||
-webkit-tap-highlight-color: transparent;
|
-webkit-tap-highlight-color: transparent;
|
||||||
-webkit-text-size-adjust: 100%;
|
-webkit-text-size-adjust: 100%;
|
||||||
text-size-adjust: 100%;
|
text-size-adjust: 100%;
|
||||||
@ -1229,7 +1251,8 @@ img {
|
|||||||
fill: currentColor;
|
fill: currentColor;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg:not(:root) {
|
svg:not(:root),
|
||||||
|
svg:not(:host) {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1244,7 +1267,8 @@ samp {
|
|||||||
font-family: var(--pico-font-family);
|
font-family: var(--pico-font-family);
|
||||||
}
|
}
|
||||||
|
|
||||||
pre code {
|
pre code,
|
||||||
|
pre samp {
|
||||||
font-size: inherit;
|
font-size: inherit;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
}
|
}
|
||||||
@ -1256,7 +1280,8 @@ pre {
|
|||||||
|
|
||||||
pre,
|
pre,
|
||||||
code,
|
code,
|
||||||
kbd {
|
kbd,
|
||||||
|
samp {
|
||||||
border-radius: var(--pico-border-radius);
|
border-radius: var(--pico-border-radius);
|
||||||
background: var(--pico-code-background-color);
|
background: var(--pico-code-background-color);
|
||||||
color: var(--pico-code-color);
|
color: var(--pico-code-color);
|
||||||
@ -1265,7 +1290,8 @@ kbd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
code,
|
code,
|
||||||
kbd {
|
kbd,
|
||||||
|
samp {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding: 0.375rem;
|
padding: 0.375rem;
|
||||||
}
|
}
|
||||||
@ -1275,7 +1301,8 @@ pre {
|
|||||||
margin-bottom: var(--pico-spacing);
|
margin-bottom: var(--pico-spacing);
|
||||||
overflow-x: auto;
|
overflow-x: auto;
|
||||||
}
|
}
|
||||||
pre > code {
|
pre > code,
|
||||||
|
pre > samp {
|
||||||
display: block;
|
display: block;
|
||||||
padding: var(--pico-spacing);
|
padding: var(--pico-spacing);
|
||||||
background: none;
|
background: none;
|
||||||
@ -1302,7 +1329,7 @@ figure figcaption {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Miscs
|
* Misc
|
||||||
*/
|
*/
|
||||||
hr {
|
hr {
|
||||||
height: 0;
|
height: 0;
|
||||||
@ -2059,7 +2086,7 @@ details.dropdown {
|
|||||||
position: relative;
|
position: relative;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
}
|
}
|
||||||
details.dropdown summary::after,
|
details.dropdown > summary::after,
|
||||||
details.dropdown > button::after,
|
details.dropdown > button::after,
|
||||||
details.dropdown > a::after {
|
details.dropdown > a::after {
|
||||||
display: block;
|
display: block;
|
||||||
@ -2079,7 +2106,7 @@ nav details.dropdown {
|
|||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
details.dropdown summary:not([role]) {
|
details.dropdown > summary:not([role]) {
|
||||||
height: calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2);
|
height: calc(1rem * var(--pico-line-height) + var(--pico-form-element-spacing-vertical) * 2 + var(--pico-border-width) * 2);
|
||||||
padding: var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);
|
padding: var(--pico-form-element-spacing-vertical) var(--pico-form-element-spacing-horizontal);
|
||||||
border: var(--pico-border-width) solid var(--pico-form-element-border-color);
|
border: var(--pico-border-width) solid var(--pico-form-element-border-color);
|
||||||
@ -2091,22 +2118,22 @@ details.dropdown summary:not([role]) {
|
|||||||
user-select: none;
|
user-select: none;
|
||||||
transition: background-color var(--pico-transition), border-color var(--pico-transition), color var(--pico-transition), box-shadow var(--pico-transition);
|
transition: background-color var(--pico-transition), border-color var(--pico-transition), color var(--pico-transition), box-shadow var(--pico-transition);
|
||||||
}
|
}
|
||||||
details.dropdown summary:not([role]):active, details.dropdown summary:not([role]):focus {
|
details.dropdown > summary:not([role]):active, details.dropdown > summary:not([role]):focus {
|
||||||
border-color: var(--pico-form-element-active-border-color);
|
border-color: var(--pico-form-element-active-border-color);
|
||||||
background-color: var(--pico-form-element-active-background-color);
|
background-color: var(--pico-form-element-active-background-color);
|
||||||
}
|
}
|
||||||
details.dropdown summary:not([role]):focus {
|
details.dropdown > summary:not([role]):focus {
|
||||||
box-shadow: 0 0 0 var(--pico-outline-width) var(--pico-form-element-focus-color);
|
box-shadow: 0 0 0 var(--pico-outline-width) var(--pico-form-element-focus-color);
|
||||||
}
|
}
|
||||||
details.dropdown summary:not([role]):focus-visible {
|
details.dropdown > summary:not([role]):focus-visible {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
details.dropdown summary:not([role])[aria-invalid=false] {
|
details.dropdown > summary:not([role])[aria-invalid=false] {
|
||||||
--pico-form-element-border-color: var(--pico-form-element-valid-border-color);
|
--pico-form-element-border-color: var(--pico-form-element-valid-border-color);
|
||||||
--pico-form-element-active-border-color: var(--pico-form-element-valid-focus-color);
|
--pico-form-element-active-border-color: var(--pico-form-element-valid-focus-color);
|
||||||
--pico-form-element-focus-color: var(--pico-form-element-valid-focus-color);
|
--pico-form-element-focus-color: var(--pico-form-element-valid-focus-color);
|
||||||
}
|
}
|
||||||
details.dropdown summary:not([role])[aria-invalid=true] {
|
details.dropdown > summary:not([role])[aria-invalid=true] {
|
||||||
--pico-form-element-border-color: var(--pico-form-element-invalid-border-color);
|
--pico-form-element-border-color: var(--pico-form-element-invalid-border-color);
|
||||||
--pico-form-element-active-border-color: var(--pico-form-element-invalid-focus-color);
|
--pico-form-element-active-border-color: var(--pico-form-element-invalid-focus-color);
|
||||||
--pico-form-element-focus-color: var(--pico-form-element-invalid-focus-color);
|
--pico-form-element-focus-color: var(--pico-form-element-invalid-focus-color);
|
||||||
@ -2116,18 +2143,18 @@ nav details.dropdown {
|
|||||||
display: inline;
|
display: inline;
|
||||||
margin: calc(var(--pico-nav-element-spacing-vertical) * -1) 0;
|
margin: calc(var(--pico-nav-element-spacing-vertical) * -1) 0;
|
||||||
}
|
}
|
||||||
nav details.dropdown summary::after {
|
nav details.dropdown > summary::after {
|
||||||
transform: rotate(0deg) translateX(0rem);
|
transform: rotate(0deg) translateX(0rem);
|
||||||
}
|
}
|
||||||
nav details.dropdown summary:not([role]) {
|
nav details.dropdown > summary:not([role]) {
|
||||||
height: calc(1rem * var(--pico-line-height) + var(--pico-nav-link-spacing-vertical) * 2);
|
height: calc(1rem * var(--pico-line-height) + var(--pico-nav-link-spacing-vertical) * 2);
|
||||||
padding: calc(var(--pico-nav-link-spacing-vertical) - var(--pico-border-width) * 2) var(--pico-nav-link-spacing-horizontal);
|
padding: calc(var(--pico-nav-link-spacing-vertical) - var(--pico-border-width) * 2) var(--pico-nav-link-spacing-horizontal);
|
||||||
}
|
}
|
||||||
nav details.dropdown summary:not([role]):focus-visible {
|
nav details.dropdown > summary:not([role]):focus-visible {
|
||||||
box-shadow: 0 0 0 var(--pico-outline-width) var(--pico-primary-focus);
|
box-shadow: 0 0 0 var(--pico-outline-width) var(--pico-primary-focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
details.dropdown summary + ul {
|
details.dropdown > summary + ul {
|
||||||
display: flex;
|
display: flex;
|
||||||
z-index: 99;
|
z-index: 99;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
@ -2147,23 +2174,23 @@ details.dropdown summary + ul {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
transition: opacity var(--pico-transition), transform 0s ease-in-out 1s;
|
transition: opacity var(--pico-transition), transform 0s ease-in-out 1s;
|
||||||
}
|
}
|
||||||
details.dropdown summary + ul[dir=rtl] {
|
details.dropdown > summary + ul[dir=rtl] {
|
||||||
right: 0;
|
right: 0;
|
||||||
left: auto;
|
left: auto;
|
||||||
}
|
}
|
||||||
details.dropdown summary + ul li {
|
details.dropdown > summary + ul li {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
padding: calc(var(--pico-form-element-spacing-vertical) * 0.5) var(--pico-form-element-spacing-horizontal);
|
padding: calc(var(--pico-form-element-spacing-vertical) * 0.5) var(--pico-form-element-spacing-horizontal);
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
details.dropdown summary + ul li:first-of-type {
|
details.dropdown > summary + ul li:first-of-type {
|
||||||
margin-top: calc(var(--pico-form-element-spacing-vertical) * 0.5);
|
margin-top: calc(var(--pico-form-element-spacing-vertical) * 0.5);
|
||||||
}
|
}
|
||||||
details.dropdown summary + ul li:last-of-type {
|
details.dropdown > summary + ul li:last-of-type {
|
||||||
margin-bottom: calc(var(--pico-form-element-spacing-vertical) * 0.5);
|
margin-bottom: calc(var(--pico-form-element-spacing-vertical) * 0.5);
|
||||||
}
|
}
|
||||||
details.dropdown summary + ul li a {
|
details.dropdown > summary + ul li a {
|
||||||
display: block;
|
display: block;
|
||||||
margin: calc(var(--pico-form-element-spacing-vertical) * -0.5) calc(var(--pico-form-element-spacing-horizontal) * -1);
|
margin: calc(var(--pico-form-element-spacing-vertical) * -0.5) calc(var(--pico-form-element-spacing-horizontal) * -1);
|
||||||
padding: calc(var(--pico-form-element-spacing-vertical) * 0.5) var(--pico-form-element-spacing-horizontal);
|
padding: calc(var(--pico-form-element-spacing-vertical) * 0.5) var(--pico-form-element-spacing-horizontal);
|
||||||
@ -2173,27 +2200,27 @@ details.dropdown summary + ul li a {
|
|||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
details.dropdown summary + ul li a:hover, details.dropdown summary + ul li a:focus, details.dropdown summary + ul li a:active, details.dropdown summary + ul li a:focus-visible, details.dropdown summary + ul li a[aria-current]:not([aria-current=false]) {
|
details.dropdown > summary + ul li a:hover, details.dropdown > summary + ul li a:focus, details.dropdown > summary + ul li a:active, details.dropdown > summary + ul li a:focus-visible, details.dropdown > summary + ul li a[aria-current]:not([aria-current=false]) {
|
||||||
background-color: var(--pico-dropdown-hover-background-color);
|
background-color: var(--pico-dropdown-hover-background-color);
|
||||||
}
|
}
|
||||||
details.dropdown summary + ul li label {
|
details.dropdown > summary + ul li label {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
details.dropdown summary + ul li:has(label):hover {
|
details.dropdown > summary + ul li:has(label):hover {
|
||||||
background-color: var(--pico-dropdown-hover-background-color);
|
background-color: var(--pico-dropdown-hover-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
details.dropdown[open] summary {
|
details.dropdown[open] > summary {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
details.dropdown[open] summary + ul {
|
details.dropdown[open] > summary + ul {
|
||||||
transform: scaleY(1);
|
transform: scaleY(1);
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
transition: opacity var(--pico-transition), transform 0s ease-in-out 0s;
|
transition: opacity var(--pico-transition), transform 0s ease-in-out 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
details.dropdown[open] summary::before {
|
details.dropdown[open] > summary::before {
|
||||||
display: block;
|
display: block;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
position: fixed;
|
position: fixed;
|
||||||
@ -2340,10 +2367,10 @@ label > details.dropdown {
|
|||||||
/**
|
/**
|
||||||
* Loading ([aria-busy=true])
|
* Loading ([aria-busy=true])
|
||||||
*/
|
*/
|
||||||
[aria-busy=true]:not(input, select, textarea, html) {
|
[aria-busy=true]:not(input, select, textarea, html, form) {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
[aria-busy=true]:not(input, select, textarea, html)::before {
|
[aria-busy=true]:not(input, select, textarea, html, form)::before {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
width: 1em;
|
width: 1em;
|
||||||
height: 1em;
|
height: 1em;
|
||||||
@ -2353,10 +2380,10 @@ label > details.dropdown {
|
|||||||
content: "";
|
content: "";
|
||||||
vertical-align: -0.125em;
|
vertical-align: -0.125em;
|
||||||
}
|
}
|
||||||
[aria-busy=true]:not(input, select, textarea, html):not(:empty)::before {
|
[aria-busy=true]:not(input, select, textarea, html, form):not(:empty)::before {
|
||||||
margin-inline-end: calc(var(--pico-spacing) * 0.5);
|
margin-inline-end: calc(var(--pico-spacing) * 0.5);
|
||||||
}
|
}
|
||||||
[aria-busy=true]:not(input, select, textarea, html):empty {
|
[aria-busy=true]:not(input, select, textarea, html, form):empty {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2372,7 +2399,8 @@ a[aria-busy=true] {
|
|||||||
/**
|
/**
|
||||||
* Modal (<dialog>)
|
* Modal (<dialog>)
|
||||||
*/
|
*/
|
||||||
:root {
|
:root,
|
||||||
|
:host {
|
||||||
--pico-scrollbar-width: 0px;
|
--pico-scrollbar-width: 0px;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2396,43 +2424,43 @@ dialog {
|
|||||||
background-color: var(--pico-modal-overlay-background-color);
|
background-color: var(--pico-modal-overlay-background-color);
|
||||||
color: var(--pico-color);
|
color: var(--pico-color);
|
||||||
}
|
}
|
||||||
dialog article {
|
dialog > article {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
max-height: calc(100vh - var(--pico-spacing) * 2);
|
max-height: calc(100vh - var(--pico-spacing) * 2);
|
||||||
margin: var(--pico-spacing);
|
margin: var(--pico-spacing);
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
@media (min-width: 576px) {
|
@media (min-width: 576px) {
|
||||||
dialog article {
|
dialog > article {
|
||||||
max-width: 95%;
|
max-width: 95%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
dialog article {
|
dialog > article {
|
||||||
max-width: 95%;
|
max-width: 95%;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dialog article > header > * {
|
dialog > article > header > * {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
dialog article > header .close, dialog article > header :is(a, button)[rel=prev] {
|
dialog > article > header .close, dialog > article > header :is(a, button)[rel=prev] {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
margin-left: var(--pico-spacing);
|
margin-left: var(--pico-spacing);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
float: right;
|
float: right;
|
||||||
}
|
}
|
||||||
dialog article > footer {
|
dialog > article > footer {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
dialog article > footer button,
|
dialog > article > footer button,
|
||||||
dialog article > footer [role=button] {
|
dialog > article > footer [role=button] {
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
}
|
}
|
||||||
dialog article > footer button:not(:first-of-type),
|
dialog > article > footer button:not(:first-of-type),
|
||||||
dialog article > footer [role=button]:not(:first-of-type) {
|
dialog > article > footer [role=button]:not(:first-of-type) {
|
||||||
margin-left: calc(var(--pico-spacing) * 0.5);
|
margin-left: calc(var(--pico-spacing) * 0.5);
|
||||||
}
|
}
|
||||||
dialog article .close, dialog article :is(a, button)[rel=prev] {
|
dialog > article .close, dialog > article :is(a, button)[rel=prev] {
|
||||||
display: block;
|
display: block;
|
||||||
width: 1rem;
|
width: 1rem;
|
||||||
height: 1rem;
|
height: 1rem;
|
||||||
@ -2448,7 +2476,7 @@ dialog article .close, dialog article :is(a, button)[rel=prev] {
|
|||||||
opacity: 0.5;
|
opacity: 0.5;
|
||||||
transition: opacity var(--pico-transition);
|
transition: opacity var(--pico-transition);
|
||||||
}
|
}
|
||||||
dialog article .close:is([aria-current]:not([aria-current=false]), :hover, :active, :focus), dialog article :is(a, button)[rel=prev]:is([aria-current]:not([aria-current=false]), :hover, :active, :focus) {
|
dialog > article .close:is([aria-current]:not([aria-current=false]), :hover, :active, :focus), dialog > article :is(a, button)[rel=prev]:is([aria-current]:not([aria-current=false]), :hover, :active, :focus) {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
dialog:not([open]), dialog[open=false] {
|
dialog:not([open]), dialog[open=false] {
|
||||||
@ -2671,7 +2699,7 @@ progress::-moz-progress-bar {
|
|||||||
[data-tooltip] {
|
[data-tooltip] {
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
[data-tooltip]:not(a, button, input) {
|
[data-tooltip]:not(a, button, input, [role=button]) {
|
||||||
border-bottom: 1px dotted;
|
border-bottom: 1px dotted;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
cursor: help;
|
cursor: help;
|
||||||
@ -3220,6 +3248,28 @@ fieldset.vault-unlock {
|
|||||||
padding: var(--pico-spacing) 0;
|
padding: var(--pico-spacing) 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
article.user-group {
|
||||||
|
background-color: var(--pico-form-element-background-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
hgroup ol, hgroup ul {
|
||||||
|
margin-block: calc(var(--pico-spacing) / 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1536px) {
|
||||||
|
.grid-auto-cols {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: calc(var(--pico-spacing) / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: 1280px) {
|
||||||
|
.grid-auto-cols {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: calc(var(--pico-spacing) / 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@media (max-width: 1024px) {
|
@media (max-width: 1024px) {
|
||||||
.split-grid.grid {
|
.split-grid.grid {
|
||||||
gap: calc(var(--pico-spacing) / 2);
|
gap: calc(var(--pico-spacing) / 2);
|
||||||
@ -3231,17 +3281,9 @@ fieldset.vault-unlock {
|
|||||||
}
|
}
|
||||||
.grid-auto-cols {
|
.grid-auto-cols {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
gap: calc(var(--pico-spacing) / 2);
|
gap: calc(var(--pico-spacing) / 2);
|
||||||
}
|
}
|
||||||
.grid-auto-cols .grid-span-all {
|
|
||||||
grid-column: 1/-1;
|
|
||||||
}
|
|
||||||
.grid-auto-cols > article {
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding-bottom: var(--pico-form-element-spacing-vertical);
|
|
||||||
--pico-border-radius: .5rem;
|
|
||||||
}
|
|
||||||
th, td {
|
th, td {
|
||||||
--pico-spacing: 0.75rem;
|
--pico-spacing: 0.75rem;
|
||||||
}
|
}
|
||||||
@ -3260,13 +3302,6 @@ fieldset.vault-unlock {
|
|||||||
grid-template-columns: repeat(1, 1fr);
|
grid-template-columns: repeat(1, 1fr);
|
||||||
gap: calc(var(--pico-spacing) / 2);
|
gap: calc(var(--pico-spacing) / 2);
|
||||||
}
|
}
|
||||||
.grid-auto-cols .grid-span-all {
|
|
||||||
grid-column: 1/-1;
|
|
||||||
}
|
|
||||||
.grid-auto-cols > article {
|
|
||||||
margin-bottom: 0;
|
|
||||||
--pico-border-radius: .5rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.user-group {
|
.user-group {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
|
|||||||
@ -101,6 +101,8 @@
|
|||||||
$sm-breakpoint: map-get(map-get($breakpoints, sm), breakpoint);
|
$sm-breakpoint: map-get(map-get($breakpoints, sm), breakpoint);
|
||||||
$md-breakpoint: map-get(map-get($breakpoints, md), breakpoint);
|
$md-breakpoint: map-get(map-get($breakpoints, md), breakpoint);
|
||||||
$lg-breakpoint: map-get(map-get($breakpoints, lg), breakpoint);
|
$lg-breakpoint: map-get(map-get($breakpoints, lg), breakpoint);
|
||||||
|
$xl-breakpoint: map-get(map-get($breakpoints, xl), breakpoint);
|
||||||
|
$xxl-breakpoint: map-get(map-get($breakpoints, xxl), breakpoint);
|
||||||
|
|
||||||
small {
|
small {
|
||||||
font-size: .875rem;
|
font-size: .875rem;
|
||||||
@ -353,9 +355,27 @@ fieldset.vault-unlock {
|
|||||||
}
|
}
|
||||||
|
|
||||||
article.user-group {
|
article.user-group {
|
||||||
|
background-color: var(--pico-form-element-background-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hgroup ol, hgroup ul {
|
||||||
|
margin-block: calc(var(--pico-spacing)/2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: $xxl-breakpoint) {
|
||||||
|
.grid-auto-cols {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
gap: calc(var(--pico-spacing)/2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media (max-width: $xl-breakpoint) {
|
||||||
|
.grid-auto-cols {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: calc(var(--pico-spacing)/2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@media (max-width: $lg-breakpoint) {
|
@media (max-width: $lg-breakpoint) {
|
||||||
.split-grid.grid {
|
.split-grid.grid {
|
||||||
gap: calc(var(--pico-spacing)/2);
|
gap: calc(var(--pico-spacing)/2);
|
||||||
@ -367,17 +387,9 @@ article.user-group {
|
|||||||
}
|
}
|
||||||
.grid-auto-cols {
|
.grid-auto-cols {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(3, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
gap: calc(var(--pico-spacing)/2);
|
gap: calc(var(--pico-spacing)/2);
|
||||||
}
|
}
|
||||||
.grid-auto-cols .grid-span-all {
|
|
||||||
grid-column: 1 / -1;
|
|
||||||
}
|
|
||||||
.grid-auto-cols > article {
|
|
||||||
margin-bottom: 0;
|
|
||||||
padding-bottom: var(--pico-form-element-spacing-vertical);
|
|
||||||
--pico-border-radius: .5rem;
|
|
||||||
}
|
|
||||||
th, td {
|
th, td {
|
||||||
--pico-spacing: 0.75rem;
|
--pico-spacing: 0.75rem;
|
||||||
}
|
}
|
||||||
@ -396,13 +408,6 @@ article.user-group {
|
|||||||
grid-template-columns: repeat(1, 1fr);
|
grid-template-columns: repeat(1, 1fr);
|
||||||
gap: calc(var(--pico-spacing)/2);
|
gap: calc(var(--pico-spacing)/2);
|
||||||
}
|
}
|
||||||
.grid-auto-cols .grid-span-all {
|
|
||||||
grid-column: 1 / -1;
|
|
||||||
}
|
|
||||||
.grid-auto-cols > article {
|
|
||||||
margin-bottom: 0;
|
|
||||||
--pico-border-radius: .5rem;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.user-group {
|
.user-group {
|
||||||
@ -504,3 +509,4 @@ fieldset.keypair, fieldset.tresor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -51,6 +51,9 @@
|
|||||||
<li>
|
<li>
|
||||||
<a href="#" hx-get="/system/users">Users</a>
|
<a href="#" hx-get="/system/users">Users</a>
|
||||||
</li>
|
</li>
|
||||||
|
<li>
|
||||||
|
<a href="#" hx-get="/system/groups">Groups</a>
|
||||||
|
</li>
|
||||||
<li>
|
<li>
|
||||||
<a href="#" hx-get="/system/logs">Logs</a>
|
<a href="#" hx-get="/system/logs">Logs</a>
|
||||||
</li>
|
</li>
|
||||||
|
|||||||
49
components/web/templates/system/groups.html
Normal file
49
components/web/templates/system/groups.html
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{% if not request.headers.get("Hx-Request") %}
|
||||||
|
{% extends "base.html" %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% block breadcrumb %}
|
||||||
|
<nav aria-label="breadcrumb" id="nav-breadcrumb" hx-swap-oob="true">
|
||||||
|
<ul>
|
||||||
|
<li>System</li>
|
||||||
|
<li><a href="#" hx-target="#body-main" hx-get="{{ request.path }}">Users</a></li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
{% endblock breadcrumb %}
|
||||||
|
|
||||||
|
{% block body %}
|
||||||
|
|
||||||
|
<h4>Groups</h4>
|
||||||
|
|
||||||
|
<div class="grid split-grid">
|
||||||
|
<article class="hide-below-lg">
|
||||||
|
<hgroup>
|
||||||
|
<h5>Groups object</h5>
|
||||||
|
<p>Create an object by defining a unique name.</p>
|
||||||
|
</hgroup>
|
||||||
|
<form hx-disable _="on submit
|
||||||
|
halt the event
|
||||||
|
add @disabled to <fieldset/> in me
|
||||||
|
if $names does not contain #group.value and #group.value is not empty
|
||||||
|
append #group.value to $names
|
||||||
|
render #group-template with (name: #group.value, members: [], newGroup: true)
|
||||||
|
put the result at the end of #user-groups
|
||||||
|
call htmx.process(#user-groups)
|
||||||
|
add @hidden to .no-user-groups
|
||||||
|
transition .user-group's opacity to 1 over 175ms
|
||||||
|
end
|
||||||
|
remove @disabled from <fieldset/> in me
|
||||||
|
end">
|
||||||
|
<fieldset>
|
||||||
|
<input autocomplete="off" id="group" name="group" type="text" placeholder="New group name" />
|
||||||
|
<button type="submit">Create</button>
|
||||||
|
</fieldset>
|
||||||
|
</form>
|
||||||
|
</article>
|
||||||
|
<article>
|
||||||
|
{% include "system/includes/users/groups.html" %}
|
||||||
|
</article>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock body %}
|
||||||
@ -31,12 +31,14 @@
|
|||||||
set group to closest .user-group to event.target
|
set group to closest .user-group to event.target
|
||||||
set value of <[name=name]/> in group to value of <[name=new_name]/> in group
|
set value of <[name=name]/> in group to value of <[name=new_name]/> in group
|
||||||
if <[name=members] option:checked/> in group is empty
|
if <[name=members] option:checked/> in group is empty
|
||||||
|
log event.deta
|
||||||
add @hidden to group
|
add @hidden to group
|
||||||
settle
|
settle
|
||||||
remove group
|
remove group
|
||||||
trigger deleteName
|
trigger deleteName
|
||||||
else
|
else
|
||||||
add @hidden to .user-group-unsaved in group
|
add @hidden to .user-group-unsaved in group
|
||||||
|
remove .user-group-new
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
on change
|
on change
|
||||||
@ -53,31 +55,12 @@
|
|||||||
</mark>
|
</mark>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<section>
|
|
||||||
<form hx-disable _="on submit
|
|
||||||
halt the event
|
|
||||||
add @disabled to <fieldset/> in me
|
|
||||||
if $names does not contain #group.value and #group.value is not empty
|
|
||||||
append #group.value to $names
|
|
||||||
render #group-template with (name: #group.value, members: [])
|
|
||||||
put the result at the end of #user-groups
|
|
||||||
call htmx.process(#user-groups)
|
|
||||||
add @hidden to .no-user-groups
|
|
||||||
transition .user-group's opacity to 1 over 175ms
|
|
||||||
end
|
|
||||||
remove @disabled from <fieldset/> in me
|
|
||||||
end">
|
|
||||||
<fieldset>
|
|
||||||
<input autocomplete="off" id="group" name="group" type="text" placeholder="New group name" />
|
|
||||||
<button type="submit">Create</button>
|
|
||||||
</fieldset>
|
|
||||||
</form>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<template id="group-template">
|
<template id="group-template">
|
||||||
@set hidden to "hidden" unless members is empty
|
@if members is not empty or newGroup
|
||||||
|
@set hidden to `hidden`
|
||||||
|
@end
|
||||||
<article class="user-group">
|
<article class="user-group">
|
||||||
<form hx-patch="/system/users/groups">
|
<form hx-patch="/system/groups">
|
||||||
<input type="hidden" name="name" value="${name}"/>
|
<input type="hidden" name="name" value="${name}"/>
|
||||||
<fieldset data-loading-disable>
|
<fieldset data-loading-disable>
|
||||||
<label>Group name</label>
|
<label>Group name</label>
|
||||||
@ -98,14 +81,26 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
@if newGroup
|
||||||
|
<small class="color-orange-400 user-group-new"
|
||||||
|
_="on change from closest <form/>
|
||||||
|
if <[name=members] option:checked/> in event.target is not empty
|
||||||
|
remove me
|
||||||
|
end">❗ New group, select members and save group to apply</small>
|
||||||
|
@end
|
||||||
<small class="color-yellow-200 user-group-unsaved" ${hidden}>⚠️ unsaved</small>
|
<small class="color-yellow-200 user-group-unsaved" ${hidden}>⚠️ unsaved</small>
|
||||||
<div class="grid-space-between">
|
<div class="grid-space-between">
|
||||||
<a href="#" _="install confirmButton
|
<a href="#" _="install confirmButton
|
||||||
on confirmedButton
|
on confirmedButton
|
||||||
set selectedIndex of <select/> in closest .user-group to -1
|
set selectedIndex of <select/> in closest <form/> to -1
|
||||||
trigger submit on closest <form/>
|
trigger submit on closest <form/>
|
||||||
end">Remove</a>
|
end">Remove</a>
|
||||||
<button data-loading-disable
|
<button data-loading-disable _="on click
|
||||||
|
if (selectedIndex of <select/> in closest <form/>) < 0
|
||||||
|
trigger notification(level: 'validationError', message: 'Missing members', duration: 3000, locations: ['members'])
|
||||||
|
halt the event
|
||||||
|
exit
|
||||||
|
end"
|
||||||
class="button-green user-group-unsaved"
|
class="button-green user-group-unsaved"
|
||||||
${hidden}
|
${hidden}
|
||||||
type="submit">
|
type="submit">
|
||||||
|
|||||||
@ -0,0 +1,31 @@
|
|||||||
|
<hgroup>
|
||||||
|
<h6>Pending logins</h6>
|
||||||
|
{% if not data.tokens["LOGIN"] %}
|
||||||
|
<p>No pending requests</p>
|
||||||
|
{% else %}
|
||||||
|
<p>
|
||||||
|
<ol>
|
||||||
|
{% for token, token_data in data.tokens["LOGIN"].items() %}
|
||||||
|
<li><b>{{ token_data.intention }}</b> on <small _="init js return new Date('{{ token_data.created }}').toLocaleString() end then put result into me">{{ token_data.created }}</small></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</hgroup>
|
||||||
|
|
||||||
|
<hr>
|
||||||
|
|
||||||
|
<hgroup>
|
||||||
|
<h6>Pending registrations</h6>
|
||||||
|
{% if not data.tokens["REGISTER"] %}
|
||||||
|
<p>No pending requests</p>
|
||||||
|
{% else %}
|
||||||
|
<p>
|
||||||
|
<ol>
|
||||||
|
{% for token, token_data in data.tokens["REGISTER"].items() %}
|
||||||
|
<li><b>{{ token_data.intention }}</b> on <small _="init js return new Date('{{ token_data.created }}').toLocaleString() end then put result into me">{{ token_data.created }}</small></li>
|
||||||
|
{% endfor %}
|
||||||
|
</ol>
|
||||||
|
</p>
|
||||||
|
{% endif %}
|
||||||
|
</hgroup>
|
||||||
@ -1,5 +1,5 @@
|
|||||||
<tr
|
<tr
|
||||||
hx-on:click="event.target.nodeName!=='INPUT'?(this.nextElementSibling.toggleAttribute('hidden'),this.nextElementSibling.hidden?null:this.scrollIntoView({behavior: 'smooth'})):null"
|
hx-on:click="event.target.nodeName!=='INPUT'?this.nextElementSibling.toggleAttribute('hidden'):null"
|
||||||
hx-disinherit="*"
|
hx-disinherit="*"
|
||||||
hx-get="/system/users/{{ user.id }}"
|
hx-get="/system/users/{{ user.id }}"
|
||||||
hx-trigger="htmx:afterRequest[event.detail.successful==true] from:next .user-details"
|
hx-trigger="htmx:afterRequest[event.detail.successful==true] from:next .user-details"
|
||||||
|
|||||||
@ -13,20 +13,23 @@
|
|||||||
|
|
||||||
{% block body %}
|
{% block body %}
|
||||||
|
|
||||||
<h4>Users and groups</h4>
|
<h4>Users</h4>
|
||||||
|
|
||||||
<section>
|
<details class="show-below-lg">
|
||||||
<h5>Groups</h5>
|
<summary role="button" class="button-slate-800">Pending logins and registrations</summary>
|
||||||
{% include "system/includes/users/groups.html" %}
|
<article>
|
||||||
</section>
|
{% include "system/includes/users/pending_tokens.html" %}
|
||||||
|
</article>
|
||||||
<hr>
|
</details>
|
||||||
|
|
||||||
<div class="grid split-grid">
|
<div class="grid split-grid">
|
||||||
|
<article class="hide-below-lg">
|
||||||
|
{% include "system/includes/users/pending_tokens.html" %}
|
||||||
|
</article>
|
||||||
<article id="system-users-full-table">
|
<article id="system-users-full-table">
|
||||||
<h5>Users</h5>
|
|
||||||
{% include "system/includes/users/table.html" %}
|
{% include "system/includes/users/table.html" %}
|
||||||
</article>
|
</article>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{% endblock body %}
|
{% endblock body %}
|
||||||
|
|||||||
@ -4,6 +4,7 @@ from pydantic import constr
|
|||||||
ACCEPT_LANGUAGES = ["en", "de"]
|
ACCEPT_LANGUAGES = ["en", "de"]
|
||||||
WEBAUTHN_CHALLENGE_TIMEOUT = 30 # seconds
|
WEBAUTHN_CHALLENGE_TIMEOUT = 30 # seconds
|
||||||
AUTH_REQUEST_TIMEOUT = 300 # seconds
|
AUTH_REQUEST_TIMEOUT = 300 # seconds
|
||||||
|
REGISTER_REQUEST_TIMEOUT = 300 # seconds
|
||||||
TABLE_PAGE_SIZE = 20
|
TABLE_PAGE_SIZE = 20
|
||||||
TRUSTED_PROXIES = ["127.0.0.1", "::1"]
|
TRUSTED_PROXIES = ["127.0.0.1", "::1"]
|
||||||
TEMPLATES_AUTO_RELOAD = True
|
TEMPLATES_AUTO_RELOAD = True
|
||||||
|
|||||||
2
ctrl
2
ctrl
@ -23,7 +23,7 @@ parser.add_argument(
|
|||||||
)
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"-t",
|
"-t",
|
||||||
"--confirm_token",
|
"--confirm-token",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help="Generate a token when prompted by the application",
|
help="Generate a token when prompted by the application",
|
||||||
)
|
)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user