From 90ff10b0a1b87122da982e877b725cdb9fedca8f Mon Sep 17 00:00:00 2001 From: codex-check Date: Wed, 23 Sep 2026 13:48:27 +0800 Subject: [PATCH 1/7] feat: link Registry releases to Web installation --- docs/30-unit-tdd/registry-web.md | 13 ++++- scripts/check_registry.py | 15 +++++- src/inkcre_extension_registry/service/app.py | 50 ++++++++++++++++++- .../service/static/registry.css | 2 +- .../service/templates/base.html | 4 +- .../service/templates/catalog.html | 5 +- .../service/templates/detail.html | 15 +++--- src/inkcre_extension_registry/service/ui.py | 10 ++++ tasks/registry-install-handoff/packet.md | 7 +++ web/registry.scss | 7 +++ 10 files changed, 113 insertions(+), 15 deletions(-) create mode 100644 tasks/registry-install-handoff/packet.md diff --git a/docs/30-unit-tdd/registry-web.md b/docs/30-unit-tdd/registry-web.md index eb48cc7..83ed44d 100644 --- a/docs/30-unit-tdd/registry-web.md +++ b/docs/30-unit-tdd/registry-web.md @@ -21,9 +21,20 @@ version. Missing or withdrawn versions return a human-readable 404. Existing `/v1`, `/simple`, `/packages`, and Module Federation asset paths retain their machine-facing semantics. +The detail page links to client-web's `/extensions?install=&version=`. +The Registry neither reads deployment state nor installs the Release. When a +client-web page opens the Registry, it passes only its Web origin as +`client_origin`; the catalog preserves it through search, filters, and version +links. The Registry accepts HTTPS origins (and local HTTP for development), +then constructs a fixed client-web path and displays the destination host. A +direct Registry visit defaults to `https://app.inkcre.dev`. The return query +contains only the Extension name and exact version; client-web reads the +Release anew from its own configured Registry and requires confirmation. + The UI does not invent descriptions, popularity, trust badges, compatibility verdicts, installation state, or download counts that Registry does not own. -Package inspection and copying an ID do not install or activate an Extension. +Package inspection, copying an ID, and opening the install link do not install +or activate an Extension. ## Publisher workspace diff --git a/scripts/check_registry.py b/scripts/check_registry.py index 9c863ab..4f9a9c6 100644 --- a/scripts/check_registry.py +++ b/scripts/check_registry.py @@ -76,6 +76,8 @@ async def request(method: str, path: str, expected: int = 200, *, private=False, } assert (await request("GET", "/v1/extensions")).json() == [] await request("GET", "/") + await request("GET", "/?client_origin=https://example.invalid/path", 400) + await request("GET", "/?client_origin=https://user@example.invalid", 400) await request("GET", "/v1/publisher", 401) await request( "POST", "/v1/extensions/other/extension/releases", 403, private=True, json=association @@ -156,7 +158,18 @@ async def request(method: str, path: str, expected: int = 200, *, private=False, await request("POST", version_path + "/publish", private=True) await request("POST", version_path + "/publish", private=True) assert (await request("GET", "/v1/extensions")).json()[0]["name"] == "check/extension" - await request("GET", "/explore/check/extension?version=1.0.0") + catalog = await request("GET", "/?client_origin=https://preview.example") + assert 'name="client_origin" value="https://preview.example"' in catalog.text + assert "/explore/check/extension?client_origin=https%3A" in catalog.text + detail = await request( + "GET", "/explore/check/extension?version=1.0.0&client_origin=https://preview.example" + ) + assert ( + 'href="https://preview.example/extensions?install=check%2Fextension&version=1.0.0"' + in detail.text + ) + assert "version=1.0.0&client_origin=https%3A" in detail.text + assert "Install in preview.example" in detail.text await request("GET", "/v1/extensions/check/extension") assert (await request("GET", version_path)).json()["state"] == "published" await request("GET", "/simple/") diff --git a/src/inkcre_extension_registry/service/app.py b/src/inkcre_extension_registry/service/app.py index 0fe5140..30cdc21 100644 --- a/src/inkcre_extension_registry/service/app.py +++ b/src/inkcre_extension_registry/service/app.py @@ -76,6 +76,7 @@ ) from .storage import ArtifactStore from .ui import ( + DEFAULT_CLIENT_WEB_ORIGIN, SCRIPT, extension_catalog_html, extension_detail_html, @@ -92,6 +93,38 @@ r"^/legacy/$|^/v1/extensions/[^/]+/[^/]+/releases/[^/]+/(module-federation|documentation/[^/]+)$" ) + +def _client_web_origin(value: str | None) -> str: + if value is None: + return DEFAULT_CLIENT_WEB_ORIGIN + if ( + not value + or any(character.isspace() for character in value) + or any(character in value for character in ("\\", "%")) + ): + raise HTTPException(400, "client_origin must be a Web origin") + try: + parsed = urlparse(value) + hostname = parsed.hostname + if parsed.port == 0: + raise ValueError("invalid port") + except ValueError as error: + raise HTTPException(400, "client_origin must be a Web origin") from error + local_http = parsed.scheme == "http" and hostname in {"localhost", "127.0.0.1", "::1"} + if ( + not (parsed.scheme == "https" or local_http) + or not hostname + or parsed.username is not None + or parsed.password is not None + or parsed.path not in {"", "/"} + or parsed.params + or parsed.query + or parsed.fragment + ): + raise HTTPException(400, "client_origin must be a Web origin") + return f"{parsed.scheme}://{parsed.netloc}" + + DOCUMENTATION_ERRORS = { code: {"model": RegistryError, "description": description} for code, description in { @@ -569,10 +602,17 @@ async def extension_catalog( request: Request, q: Annotated[str, Query(max_length=200)] = "", publisher: Annotated[str, Query(max_length=64)] = "", + client_origin: Annotated[str | None, Query(max_length=512)] = None, ) -> HTMLResponse: + web_origin = _client_web_origin(client_origin) extensions = await _repository(request).list_extensions() return html_response( - extension_catalog_html(extensions, query=q.strip(), namespace=publisher) + extension_catalog_html( + extensions, + query=q.strip(), + namespace=publisher, + client_origin=web_origin, + ) ) @app.get("/assets/registry.js", include_in_schema=False) @@ -592,7 +632,9 @@ async def extension_detail( name: RegistrySegment, request: Request, version: Annotated[str | None, Query(max_length=128)] = None, + client_origin: Annotated[str | None, Query(max_length=512)] = None, ) -> HTMLResponse: + web_origin = _client_web_origin(client_origin) extension_name = _validate_identity(namespace, name) extension = await _repository(request).get_extension(extension_name) selected = select_release(extension, version) if extension else None @@ -602,7 +644,11 @@ async def extension_detail( hosted = await _documentation(request).discover( extension_name, selected.version, request.app.state.settings ) - document = extension_detail_html(extension, version, hosted) if extension else None + document = ( + extension_detail_html(extension, version, hosted, client_origin=web_origin) + if extension + else None + ) if document is None: return html_response( page( diff --git a/src/inkcre_extension_registry/service/static/registry.css b/src/inkcre_extension_registry/service/static/registry.css index 42c3c19..c87a523 100644 --- a/src/inkcre_extension_registry/service/static/registry.css +++ b/src/inkcre_extension_registry/service/static/registry.css @@ -1 +1 @@ -:root{--ref-elevation-raised-low-shadow-type:dropShadow;--ref-elevation-raised-low-radius:2;--ref-elevation-raised-low-color:rgba(12, 21, 48, 0.0784313725);--ref-elevation-raised-low-offset-x:0;--ref-elevation-raised-low-offset-y:1;--ref-elevation-raised-low-spread:0;--ref-elevation-raised-md-shadow-type:dropShadow;--ref-elevation-raised-md-radius:20;--ref-elevation-raised-md-color:rgba(12, 21, 48, 0.1215686275);--ref-elevation-raised-md-offset-x:0;--ref-elevation-raised-md-offset-y:6;--ref-elevation-raised-md-spread:0;--ref-elevation-raised-high-shadow-type:dropShadow;--ref-elevation-raised-high-radius:32;--ref-elevation-raised-high-color:rgba(12, 21, 84, 0.1607843137);--ref-elevation-raised-high-offset-x:0;--ref-elevation-raised-high-offset-y:12;--ref-elevation-raised-high-spread:0;--ref-color-neutral-2:#070707;--ref-color-neutral-6:#131313;--ref-color-neutral-12:#1f1f1f;--ref-color-neutral-20:#303030;--ref-color-neutral-30:#474747;--ref-color-neutral-40:#5e5e5e;--ref-color-neutral-50:#777777;--ref-color-neutral-60:#919191;--ref-color-neutral-70:#ababab;--ref-color-neutral-80:#c6c6c6;--ref-color-neutral-90:#e2e2e2;--ref-color-neutral-94:#eeeeee;--ref-color-neutral-98:#f9f9f9;--ref-color-neutral-100:#ffffff;--ref-color-tertiary-40:#1f9aa5;--ref-color-tertiary-90:#d0f4f4;--ref-color-danger-10:#410008;--ref-color-danger-30:#92001e;--ref-color-danger-35:#a80324;--ref-color-danger-40:#b9192f;--ref-color-danger-60:#ff525c;--ref-color-danger-65:#ff7073;--ref-color-danger-70:#ff8889;--ref-color-danger-80:#ffb3b1;--ref-color-danger-90:#ffdad8;--ref-color-danger-98:#fff8f7;--ref-color-brand-2:#0b1220;--ref-color-brand-6:#121928;--ref-color-brand-12:#1f2635;--ref-color-brand-20:#303747;--ref-color-brand-30:#474e5f;--ref-color-brand-40:#5f6677;--ref-color-brand-50:#787e91;--ref-color-brand-60:#9198ac;--ref-color-brand-70:#acb3c7;--ref-color-brand-80:#b2bccf;--ref-color-brand-90:#c8cee3;--ref-color-brand-94:#f7f7fb;--ref-color-brand-98:#fbfcff;--ref-radius-xs:4px;--ref-radius-sm:8px;--ref-radius-md:16px;--ref-radius-lg:32px;--ref-radius-none:0px;--ref-radius-full:999px;--ref-space-md:16px;--ref-space-lg:32px;--ref-space-xl:56px;--ref-space-xs:4px;--ref-space-sm:8px;--ref-typo-family-sans:system-ui, -apple-system, sans-serif;--ref-typo-family-mono: SF Mono , Monaco, Roboto Mono , monospace;--ref-breakpoint-mobile:768px;--ref-breakpoint-tablet:1024px;--ref-breakpoint-desktop:1280px;--ref-opacity-muted:0.72;--ref-opacity-medium:0.84;--ref-opacity-strong:0.92;--ref-size-lg:48px;--ref-size-md:36px;--ref-size-sm:24px;--ref-size-icon-sm:20px;--ref-size-icon-md:24px;--ref-size-icon-lg:36px;--ref-size-iconfont-sm:8px;--ref-size-iconfont-md:12px;--ref-size-iconfont-lg:18px;--ref-size-xl:58px;--ref-size-xs:18px;--ref-font-label-lg-font-size:14px;--ref-font-label-lg-text-decoration:none;--ref-font-label-lg-font-family:Inter;--ref-font-label-lg-font-weight:400;--ref-font-label-lg-font-style:normal;--ref-font-label-lg-font-stretch:normal;--ref-font-label-lg-letter-spacing:0px;--ref-font-label-lg-line-height:16px;--ref-font-label-lg-paragraph-indent:0px;--ref-font-label-lg-paragraph-spacing:0px;--ref-font-label-lg-text-case:none;--ref-font-label-md-font-size:12px;--ref-font-label-md-text-decoration:none;--ref-font-label-md-font-family:Inter;--ref-font-label-md-font-weight:400;--ref-font-label-md-font-style:normal;--ref-font-label-md-font-stretch:normal;--ref-font-label-md-letter-spacing:0px;--ref-font-label-md-line-height:16px;--ref-font-label-md-paragraph-indent:0px;--ref-font-label-md-paragraph-spacing:0px;--ref-font-label-md-text-case:none;--ref-font-label-sm-font-size:10px;--ref-font-label-sm-text-decoration:none;--ref-font-label-sm-font-family:Roboto;--ref-font-label-sm-font-weight:700;--ref-font-label-sm-font-style:normal;--ref-font-label-sm-font-stretch:normal;--ref-font-label-sm-letter-spacing:0.7px;--ref-font-label-sm-line-height:14px;--ref-font-label-sm-paragraph-indent:0px;--ref-font-label-sm-paragraph-spacing:0px;--ref-font-label-sm-text-case:none;--ref-font-label-lg-underlined-font-size:14px;--ref-font-label-lg-underlined-text-decoration:underline;--ref-font-label-lg-underlined-font-family:Inter;--ref-font-label-lg-underlined-font-weight:400;--ref-font-label-lg-underlined-font-style:normal;--ref-font-label-lg-underlined-font-stretch:normal;--ref-font-label-lg-underlined-letter-spacing:0px;--ref-font-label-lg-underlined-line-height:16px;--ref-font-label-lg-underlined-paragraph-indent:0px;--ref-font-label-lg-underlined-paragraph-spacing:0px;--ref-font-label-lg-underlined-text-case:none;--ref-font-label-lg-mono-font-size:14px;--ref-font-label-lg-mono-text-decoration:none;--ref-font-label-lg-mono-font-family:JetBrains Mono;--ref-font-label-lg-mono-font-weight:400;--ref-font-label-lg-mono-font-style:normal;--ref-font-label-lg-mono-font-stretch:normal;--ref-font-label-lg-mono-letter-spacing:0px;--ref-font-label-lg-mono-line-height:16px;--ref-font-label-lg-mono-paragraph-indent:0px;--ref-font-label-lg-mono-paragraph-spacing:0px;--ref-font-label-lg-mono-text-case:none;--ref-font-label-sm-mono-font-size:10px;--ref-font-label-sm-mono-text-decoration:none;--ref-font-label-sm-mono-font-family:JetBrains Mono;--ref-font-label-sm-mono-font-weight:700;--ref-font-label-sm-mono-font-style:normal;--ref-font-label-sm-mono-font-stretch:normal;--ref-font-label-sm-mono-letter-spacing:0.4px;--ref-font-label-sm-mono-line-height:14px;--ref-font-label-sm-mono-paragraph-indent:0px;--ref-font-label-sm-mono-paragraph-spacing:0px;--ref-font-label-sm-mono-text-case:none;--ref-font-title-sm-font-size:22px;--ref-font-title-sm-text-decoration:none;--ref-font-title-sm-font-family:Inter;--ref-font-title-sm-font-weight:400;--ref-font-title-sm-font-style:normal;--ref-font-title-sm-font-stretch:normal;--ref-font-title-sm-letter-spacing:0px;--ref-font-title-sm-line-height:28px;--ref-font-title-sm-paragraph-indent:0px;--ref-font-title-sm-paragraph-spacing:0px;--ref-font-title-sm-text-case:none;--ref-font-title-sm-mono-font-size:22px;--ref-font-title-sm-mono-text-decoration:none;--ref-font-title-sm-mono-font-family:IBM Plex Mono;--ref-font-title-sm-mono-font-weight:400;--ref-font-title-sm-mono-font-style:normal;--ref-font-title-sm-mono-font-stretch:normal;--ref-font-title-sm-mono-letter-spacing:0.66px;--ref-font-title-sm-mono-line-height:28px;--ref-font-title-sm-mono-paragraph-indent:0px;--ref-font-title-sm-mono-paragraph-spacing:0px;--ref-font-title-sm-mono-text-case:none;--ref-font-body-lg-font-size:18px;--ref-font-body-lg-text-decoration:none;--ref-font-body-lg-font-family:Inter;--ref-font-body-lg-font-weight:400;--ref-font-body-lg-font-style:normal;--ref-font-body-lg-font-stretch:normal;--ref-font-body-lg-letter-spacing:0px;--ref-font-body-lg-line-height:24px;--ref-font-body-lg-paragraph-indent:0px;--ref-font-body-lg-paragraph-spacing:0px;--ref-font-body-lg-text-case:none;--ref-font-body-lg-mono-font-size:18px;--ref-font-body-lg-mono-text-decoration:none;--ref-font-body-lg-mono-font-family:JetBrains Mono;--ref-font-body-lg-mono-font-weight:400;--ref-font-body-lg-mono-font-style:normal;--ref-font-body-lg-mono-font-stretch:normal;--ref-font-body-lg-mono-letter-spacing:0px;--ref-font-body-lg-mono-line-height:24px;--ref-font-body-lg-mono-paragraph-indent:0px;--ref-font-body-lg-mono-paragraph-spacing:0px;--ref-font-body-lg-mono-text-case:none;--ref-font-headline-lg-font-size:36px;--ref-font-headline-lg-text-decoration:none;--ref-font-headline-lg-font-family:Inter;--ref-font-headline-lg-font-weight:400;--ref-font-headline-lg-font-style:normal;--ref-font-headline-lg-font-stretch:normal;--ref-font-headline-lg-letter-spacing:0px;--ref-font-headline-lg-line-height:48px;--ref-font-headline-lg-paragraph-indent:0px;--ref-font-headline-lg-paragraph-spacing:0px;--ref-font-headline-lg-text-case:none;--sys-elevation-raised-low-shadow-type:dropShadow;--sys-elevation-raised-low-radius:2;--sys-elevation-raised-low-color:rgba(12, 21, 48, 0.0784313725);--sys-elevation-raised-low-offset-x:0;--sys-elevation-raised-low-offset-y:1;--sys-elevation-raised-low-spread:0;--sys-elevation-raised-md-shadow-type:dropShadow;--sys-elevation-raised-md-radius:20;--sys-elevation-raised-md-color:rgba(12, 21, 48, 0.1215686275);--sys-elevation-raised-md-offset-x:0;--sys-elevation-raised-md-offset-y:6;--sys-elevation-raised-md-spread:0;--sys-elevation-raised-high-shadow-type:dropShadow;--sys-elevation-raised-high-radius:32;--sys-elevation-raised-high-color:rgba(12, 21, 84, 0.1607843137);--sys-elevation-raised-high-offset-x:0;--sys-elevation-raised-high-offset-y:12;--sys-elevation-raised-high-spread:0;--sys-radius-xs:4px;--sys-radius-sm:8px;--sys-radius-md:16px;--sys-radius-lg:32px;--sys-radius-none:0px;--sys-radius-full:999px;--sys-space-md:16px;--sys-space-lg:32px;--sys-space-xl:56px;--sys-space-xs:4px;--sys-space-sm:8px;--sys-typo-family-sans:system-ui, -apple-system, sans-serif;--sys-typo-family-mono: SF Mono , Monaco, Roboto Mono , monospace;--sys-breakpoint-mobile:768px;--sys-breakpoint-tablet:1024px;--sys-breakpoint-desktop:1280px;--sys-opacity-muted:0.72;--sys-opacity-medium:0.84;--sys-opacity-strong:0.92;--sys-size-lg:48px;--sys-size-md:36px;--sys-size-sm:24px;--sys-size-icon-sm:20px;--sys-size-icon-md:24px;--sys-size-icon-lg:36px;--sys-size-iconfont-sm:8px;--sys-size-iconfont-md:12px;--sys-size-iconfont-lg:18px;--sys-size-xl:58px;--sys-size-xs:18px;--sys-font-label-lg-font-size:14px;--sys-font-label-lg-text-decoration:none;--sys-font-label-lg-font-family:Inter;--sys-font-label-lg-font-weight:400;--sys-font-label-lg-font-style:normal;--sys-font-label-lg-font-stretch:normal;--sys-font-label-lg-letter-spacing:0px;--sys-font-label-lg-line-height:16px;--sys-font-label-lg-paragraph-indent:0px;--sys-font-label-lg-paragraph-spacing:0px;--sys-font-label-lg-text-case:none;--sys-font-label-md-font-size:12px;--sys-font-label-md-text-decoration:none;--sys-font-label-md-font-family:Inter;--sys-font-label-md-font-weight:400;--sys-font-label-md-font-style:normal;--sys-font-label-md-font-stretch:normal;--sys-font-label-md-letter-spacing:0px;--sys-font-label-md-line-height:16px;--sys-font-label-md-paragraph-indent:0px;--sys-font-label-md-paragraph-spacing:0px;--sys-font-label-md-text-case:none;--sys-font-label-sm-font-size:10px;--sys-font-label-sm-text-decoration:none;--sys-font-label-sm-font-family:Roboto;--sys-font-label-sm-font-weight:700;--sys-font-label-sm-font-style:normal;--sys-font-label-sm-font-stretch:normal;--sys-font-label-sm-letter-spacing:0.7px;--sys-font-label-sm-line-height:14px;--sys-font-label-sm-paragraph-indent:0px;--sys-font-label-sm-paragraph-spacing:0px;--sys-font-label-sm-text-case:none;--sys-font-label-lg-underlined-font-size:14px;--sys-font-label-lg-underlined-text-decoration:underline;--sys-font-label-lg-underlined-font-family:Inter;--sys-font-label-lg-underlined-font-weight:400;--sys-font-label-lg-underlined-font-style:normal;--sys-font-label-lg-underlined-font-stretch:normal;--sys-font-label-lg-underlined-letter-spacing:0px;--sys-font-label-lg-underlined-line-height:16px;--sys-font-label-lg-underlined-paragraph-indent:0px;--sys-font-label-lg-underlined-paragraph-spacing:0px;--sys-font-label-lg-underlined-text-case:none;--sys-font-label-lg-mono-font-size:14px;--sys-font-label-lg-mono-text-decoration:none;--sys-font-label-lg-mono-font-family:JetBrains Mono;--sys-font-label-lg-mono-font-weight:400;--sys-font-label-lg-mono-font-style:normal;--sys-font-label-lg-mono-font-stretch:normal;--sys-font-label-lg-mono-letter-spacing:0px;--sys-font-label-lg-mono-line-height:16px;--sys-font-label-lg-mono-paragraph-indent:0px;--sys-font-label-lg-mono-paragraph-spacing:0px;--sys-font-label-lg-mono-text-case:none;--sys-font-label-sm-mono-font-size:10px;--sys-font-label-sm-mono-text-decoration:none;--sys-font-label-sm-mono-font-family:JetBrains Mono;--sys-font-label-sm-mono-font-weight:700;--sys-font-label-sm-mono-font-style:normal;--sys-font-label-sm-mono-font-stretch:normal;--sys-font-label-sm-mono-letter-spacing:0.4px;--sys-font-label-sm-mono-line-height:14px;--sys-font-label-sm-mono-paragraph-indent:0px;--sys-font-label-sm-mono-paragraph-spacing:0px;--sys-font-label-sm-mono-text-case:none;--sys-font-title-sm-font-size:22px;--sys-font-title-sm-text-decoration:none;--sys-font-title-sm-font-family:Inter;--sys-font-title-sm-font-weight:400;--sys-font-title-sm-font-style:normal;--sys-font-title-sm-font-stretch:normal;--sys-font-title-sm-letter-spacing:0px;--sys-font-title-sm-line-height:28px;--sys-font-title-sm-paragraph-indent:0px;--sys-font-title-sm-paragraph-spacing:0px;--sys-font-title-sm-text-case:none;--sys-font-title-sm-mono-font-size:22px;--sys-font-title-sm-mono-text-decoration:none;--sys-font-title-sm-mono-font-family:IBM Plex Mono;--sys-font-title-sm-mono-font-weight:400;--sys-font-title-sm-mono-font-style:normal;--sys-font-title-sm-mono-font-stretch:normal;--sys-font-title-sm-mono-letter-spacing:0.66px;--sys-font-title-sm-mono-line-height:28px;--sys-font-title-sm-mono-paragraph-indent:0px;--sys-font-title-sm-mono-paragraph-spacing:0px;--sys-font-title-sm-mono-text-case:none;--sys-font-body-lg-font-size:18px;--sys-font-body-lg-text-decoration:none;--sys-font-body-lg-font-family:Inter;--sys-font-body-lg-font-weight:400;--sys-font-body-lg-font-style:normal;--sys-font-body-lg-font-stretch:normal;--sys-font-body-lg-letter-spacing:0px;--sys-font-body-lg-line-height:24px;--sys-font-body-lg-paragraph-indent:0px;--sys-font-body-lg-paragraph-spacing:0px;--sys-font-body-lg-text-case:none;--sys-font-body-lg-mono-font-size:18px;--sys-font-body-lg-mono-text-decoration:none;--sys-font-body-lg-mono-font-family:JetBrains Mono;--sys-font-body-lg-mono-font-weight:400;--sys-font-body-lg-mono-font-style:normal;--sys-font-body-lg-mono-font-stretch:normal;--sys-font-body-lg-mono-letter-spacing:0px;--sys-font-body-lg-mono-line-height:24px;--sys-font-body-lg-mono-paragraph-indent:0px;--sys-font-body-lg-mono-paragraph-spacing:0px;--sys-font-body-lg-mono-text-case:none;--sys-font-headline-lg-font-size:36px;--sys-font-headline-lg-text-decoration:none;--sys-font-headline-lg-font-family:Inter;--sys-font-headline-lg-font-weight:400;--sys-font-headline-lg-font-style:normal;--sys-font-headline-lg-font-stretch:normal;--sys-font-headline-lg-letter-spacing:0px;--sys-font-headline-lg-line-height:48px;--sys-font-headline-lg-paragraph-indent:0px;--sys-font-headline-lg-paragraph-spacing:0px;--sys-font-headline-lg-text-case:none}:root,html[data-theme=light]{color-scheme:light;--sys-color-text-base:#070707;--sys-color-text-muted:#919191;--sys-color-text-danger-on:#fff8f7;--sys-color-text-primary:#fbfcff;--sys-color-text-subtle:#5e5e5e;--sys-color-surface-base:#ffffff;--sys-color-surface-muted:#e2e2e2;--sys-color-surface-subtle:#f9f9f9;--sys-color-surface-primary:#121928;--sys-color-surface-primary-hover:#474e5f;--sys-color-surface-subtle-hover:#eeeeee;--sys-color-surface-danger:#a80324;--sys-color-surface-danger-hover:#ff7073;--sys-color-surface-base-hover:#eeeeee;--sys-color-border-base:#303030;--sys-color-border-strong:#070707;--sys-color-border-subtle:#c6c6c6;--sys-color-border-muted:#919191;--sys-color-feedback-error:#b9192f;--comp-switch-track-bg:#070707;--comp-switch-handle-bg:#f9f9f9;--comp-switch-label-color:#070707}@media(prefers-color-scheme: dark){:root:not([data-theme]),html[data-theme=dark]{color-scheme:dark;--sys-color-text-base:#f9f9f9;--sys-color-text-muted:#f9f9f9;--sys-color-text-danger-on:#fff8f7;--sys-color-text-primary:#0b1220;--sys-color-text-subtle:#c6c6c6;--sys-color-surface-base:#474747;--sys-color-surface-muted:#131313;--sys-color-surface-subtle:#1f1f1f;--sys-color-surface-primary:#c8cee3;--sys-color-surface-primary-hover:#9198ac;--sys-color-surface-subtle-hover:#5e5e5e;--sys-color-surface-danger:#b9192f;--sys-color-surface-danger-hover:#b9192f;--sys-color-surface-base-hover:#474747;--sys-color-border-base:#5e5e5e;--sys-color-border-strong:#c6c6c6;--sys-color-border-subtle:#5e5e5e;--sys-color-border-muted:#5e5e5e;--sys-color-feedback-error:#ff525c;--comp-switch-track-bg:#f9f9f9;--comp-switch-handle-bg:#070707;--comp-switch-label-color:#f9f9f9}}html[data-theme=dark]{color-scheme:dark;--sys-color-text-base:#f9f9f9;--sys-color-text-muted:#f9f9f9;--sys-color-text-danger-on:#fff8f7;--sys-color-text-primary:#0b1220;--sys-color-text-subtle:#c6c6c6;--sys-color-surface-base:#474747;--sys-color-surface-muted:#131313;--sys-color-surface-subtle:#1f1f1f;--sys-color-surface-primary:#c8cee3;--sys-color-surface-primary-hover:#9198ac;--sys-color-surface-subtle-hover:#5e5e5e;--sys-color-surface-danger:#b9192f;--sys-color-surface-danger-hover:#b9192f;--sys-color-surface-base-hover:#474747;--sys-color-border-base:#5e5e5e;--sys-color-border-strong:#c6c6c6;--sys-color-border-subtle:#5e5e5e;--sys-color-border-muted:#5e5e5e;--sys-color-feedback-error:#ff525c}*{box-sizing:border-box}body{margin:0;font-family:var(--ref-typo-family-sans);font-size:14px;line-height:1.5;color:var(--sys-color-text-base);background:var(--sys-color-surface-base)}[hidden]{display:none !important}a{color:inherit;text-underline-offset:3px}button,input,select,textarea{font:inherit}button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--sys-color-border-strong);outline-offset:3px}h1,h2,h3,p{margin:0}h1{font-size:var(--sys-font-title-sm-font-size);font-weight:var(--sys-font-title-sm-font-weight);line-height:var(--sys-font-title-sm-line-height);letter-spacing:var(--sys-font-title-sm-letter-spacing)}h2,h3{font-size:var(--sys-font-body-lg-font-size);font-weight:var(--sys-font-body-lg-font-weight);line-height:var(--sys-font-body-lg-line-height);letter-spacing:var(--sys-font-body-lg-letter-spacing)}code,.extension-name{font-family:var(--ref-typo-family-mono);overflow-wrap:anywhere}.sr-only,.skip-link:not(:focus){position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.skip-link:focus{display:block;padding:8px}.site-header{display:flex;align-items:center;justify-content:space-between;gap:var(--sys-space-md);padding:var(--sys-space-md) var(--sys-space-lg);background:var(--sys-color-surface-subtle);border-bottom:1px solid var(--sys-color-border-base)}.site-header nav{display:flex;align-items:center;gap:var(--sys-space-md)}.site-header nav a{text-decoration:none}.site-header [aria-current=page]{text-decoration:underline}.brand{display:flex;align-items:center;gap:var(--sys-space-sm);text-decoration:none;font-size:var(--sys-font-title-sm-font-size);font-weight:var(--sys-font-title-sm-font-weight);line-height:var(--sys-font-title-sm-line-height);letter-spacing:var(--sys-font-title-sm-letter-spacing);font-family:var(--ref-typo-family-mono)}.brand svg,.brand>span:first-child{display:block;width:32px;height:32px}.count,.extension-name,.field-help{color:var(--sys-color-text-subtle);font-size:12px}main{padding:var(--sys-space-lg) var(--sys-space-xl)}.section-heading,.detail-heading,.workspace-heading,.workspace-toolbar,.dialog-heading{display:flex;align-items:center;justify-content:space-between;gap:var(--sys-space-md);margin-bottom:var(--sys-space-md)}.count{margin-left:var(--sys-space-sm);font-weight:400}.button,.icon-button{display:inline-flex;align-items:center;justify-content:center;gap:var(--sys-space-sm);padding:var(--sys-space-sm) var(--sys-space-md);border:1px solid var(--sys-color-border-base);border-radius:0;background:var(--sys-color-surface-subtle);color:var(--sys-color-text-base);text-decoration:none;cursor:pointer;font-size:var(--sys-font-label-lg-font-size);font-weight:var(--sys-font-label-lg-font-weight);line-height:var(--sys-font-label-lg-line-height);letter-spacing:var(--sys-font-label-lg-letter-spacing)}.button:hover,.icon-button:hover{background:var(--sys-color-surface-subtle-hover)}.button:disabled,.icon-button:disabled{opacity:.5;cursor:not-allowed}.icon-button{width:32px;height:32px;padding:0;border-color:rgba(0,0,0,0)}.button-primary{background:var(--sys-color-surface-primary);color:var(--sys-color-text-primary)}.button-primary:hover{background:var(--sys-color-surface-primary-hover)}input,select,textarea{width:100%;min-width:0;padding:var(--sys-space-sm);border:1px solid var(--sys-color-border-subtle);border-radius:0;background:var(--sys-color-surface-base);color:var(--sys-color-text-base)}input,select{min-height:36px}.search-form{display:flex;gap:var(--sys-space-sm);margin-bottom:var(--sys-space-md)}.search-form input{flex:1}.search-form select{width:auto;max-width:240px}.search-summary{margin-bottom:var(--sys-space-md)}.catalog-list,.version-list{list-style:none;margin:0;padding:0}.catalog-list{display:grid;gap:var(--sys-space-md)}.extension-card{display:flex;align-items:center;justify-content:space-between;gap:var(--sys-space-md);border:1px solid var(--sys-color-border-base);padding:var(--sys-space-md);text-decoration:none}.extension-card h2{font-size:var(--sys-font-label-lg-font-size);font-weight:var(--sys-font-label-lg-font-weight);line-height:var(--sys-font-label-lg-line-height);letter-spacing:var(--sys-font-label-lg-letter-spacing);margin-bottom:var(--sys-space-sm)}.extension-card:hover{background:var(--sys-color-surface-subtle)}.card-action{flex-shrink:0;font-size:12px}.back-link{display:inline-block;margin-bottom:var(--sys-space-lg)}.detail-heading{margin-bottom:var(--sys-space-lg)}.detail-heading h1{margin-bottom:var(--sys-space-sm)}.detail-layout{display:grid;grid-template-columns:minmax(0, 1fr) 280px;gap:var(--sys-space-lg)}.distribution-panel,.sidebar-panel,.release-row,.connect-card{border:1px solid var(--sys-color-border-base);padding:var(--sys-space-md);margin-bottom:var(--sys-space-md)}.metadata{margin:var(--sys-space-md) 0}.metadata>div{display:grid;grid-template-columns:120px minmax(0, 1fr);gap:var(--sys-space-md);margin-bottom:var(--sys-space-sm)}.metadata dt{color:var(--sys-color-text-subtle)}.metadata dd{margin:0;overflow-wrap:anywhere}.version-list{margin-top:var(--sys-space-md)}.version-list a{display:flex;justify-content:space-between;gap:var(--sys-space-sm);padding:var(--sys-space-sm);text-decoration:none}.version-list [aria-current],.version-list a:hover{background:var(--sys-color-surface-subtle-hover)}.version-list span{font-size:12px}.publisher-meta>div{grid-template-columns:auto 1fr}.connect-layout{max-width:480px}label{display:block;margin-bottom:var(--sys-space-sm)}form>.button{margin-top:var(--sys-space-md)}.search-form>.button{margin-top:0}.workspace-toolbar>div,.release-actions,.release-pagination{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sys-space-sm)}.release-row{display:flex;align-items:center;justify-content:space-between;gap:var(--sys-space-md)}.release-row h3,.release-row p{margin-bottom:var(--sys-space-sm);overflow-wrap:anywhere}.release-row p{color:var(--sys-color-text-subtle);font-size:12px}.badge{display:inline-block;margin-bottom:var(--sys-space-sm);padding:0 var(--sys-space-xs);border:1px solid var(--sys-color-border-subtle);font-size:12px}.release-pagination{margin-bottom:var(--sys-space-md)}.form-status:not(:empty),.empty-state,noscript p{margin:var(--sys-space-md) 0}.error{color:var(--sys-color-feedback-error)}dialog{width:min(560px,100% - 32px);max-height:calc(100% - 32px);padding:var(--sys-space-lg);border:1px solid var(--sys-color-border-base);background:var(--sys-color-surface-base);color:var(--sys-color-text-base)}dialog::backdrop{background:rgba(0,0,0,.3)}.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--sys-space-md);margin-top:var(--sys-space-md)}.full-width{grid-column:1/-1}.field-help{margin-top:var(--sys-space-xs)}.toast:not(:empty){position:fixed;bottom:var(--sys-space-md);right:var(--sys-space-md);max-width:calc(100% - 32px);padding:var(--sys-space-sm) var(--sys-space-md);border:1px solid var(--sys-color-border-base);background:var(--sys-color-surface-subtle)}@media(max-width: 768px){.site-header,main{padding:var(--sys-space-md)}.site-header nav{gap:var(--sys-space-sm)}.brand{font-size:18px}.search-form{flex-wrap:wrap}.search-form input{flex-basis:100%}.search-form select{flex:1;max-width:none}.detail-layout,.form-grid{grid-template-columns:minmax(0, 1fr)}.detail-heading,.workspace-toolbar,.release-row{align-items:flex-start;flex-wrap:wrap}dialog{padding:var(--sys-space-md)}.metadata>div{grid-template-columns:100px minmax(0, 1fr);gap:var(--sys-space-sm)}} +:root{--ref-elevation-raised-low-shadow-type:dropShadow;--ref-elevation-raised-low-radius:2;--ref-elevation-raised-low-color:rgba(12, 21, 48, 0.0784313725);--ref-elevation-raised-low-offset-x:0;--ref-elevation-raised-low-offset-y:1;--ref-elevation-raised-low-spread:0;--ref-elevation-raised-md-shadow-type:dropShadow;--ref-elevation-raised-md-radius:20;--ref-elevation-raised-md-color:rgba(12, 21, 48, 0.1215686275);--ref-elevation-raised-md-offset-x:0;--ref-elevation-raised-md-offset-y:6;--ref-elevation-raised-md-spread:0;--ref-elevation-raised-high-shadow-type:dropShadow;--ref-elevation-raised-high-radius:32;--ref-elevation-raised-high-color:rgba(12, 21, 84, 0.1607843137);--ref-elevation-raised-high-offset-x:0;--ref-elevation-raised-high-offset-y:12;--ref-elevation-raised-high-spread:0;--ref-color-neutral-2:#070707;--ref-color-neutral-6:#131313;--ref-color-neutral-12:#1f1f1f;--ref-color-neutral-20:#303030;--ref-color-neutral-30:#474747;--ref-color-neutral-40:#5e5e5e;--ref-color-neutral-50:#777777;--ref-color-neutral-60:#919191;--ref-color-neutral-70:#ababab;--ref-color-neutral-80:#c6c6c6;--ref-color-neutral-90:#e2e2e2;--ref-color-neutral-94:#eeeeee;--ref-color-neutral-98:#f9f9f9;--ref-color-neutral-100:#ffffff;--ref-color-tertiary-40:#1f9aa5;--ref-color-tertiary-90:#d0f4f4;--ref-color-danger-10:#410008;--ref-color-danger-30:#92001e;--ref-color-danger-35:#a80324;--ref-color-danger-40:#b9192f;--ref-color-danger-60:#ff525c;--ref-color-danger-65:#ff7073;--ref-color-danger-70:#ff8889;--ref-color-danger-80:#ffb3b1;--ref-color-danger-90:#ffdad8;--ref-color-danger-98:#fff8f7;--ref-color-brand-2:#0b1220;--ref-color-brand-6:#121928;--ref-color-brand-12:#1f2635;--ref-color-brand-20:#303747;--ref-color-brand-30:#474e5f;--ref-color-brand-40:#5f6677;--ref-color-brand-50:#787e91;--ref-color-brand-60:#9198ac;--ref-color-brand-70:#acb3c7;--ref-color-brand-80:#b2bccf;--ref-color-brand-90:#c8cee3;--ref-color-brand-94:#f7f7fb;--ref-color-brand-98:#fbfcff;--ref-radius-xs:4px;--ref-radius-sm:8px;--ref-radius-md:16px;--ref-radius-lg:32px;--ref-radius-none:0px;--ref-radius-full:999px;--ref-space-md:16px;--ref-space-lg:32px;--ref-space-xl:56px;--ref-space-xs:4px;--ref-space-sm:8px;--ref-typo-family-sans:system-ui, -apple-system, sans-serif;--ref-typo-family-mono: SF Mono , Monaco, Roboto Mono , monospace;--ref-breakpoint-mobile:768px;--ref-breakpoint-tablet:1024px;--ref-breakpoint-desktop:1280px;--ref-opacity-muted:0.72;--ref-opacity-medium:0.84;--ref-opacity-strong:0.92;--ref-size-lg:48px;--ref-size-md:36px;--ref-size-sm:24px;--ref-size-icon-sm:20px;--ref-size-icon-md:24px;--ref-size-icon-lg:36px;--ref-size-iconfont-sm:8px;--ref-size-iconfont-md:12px;--ref-size-iconfont-lg:18px;--ref-size-xl:58px;--ref-size-xs:18px;--ref-font-label-lg-font-size:14px;--ref-font-label-lg-text-decoration:none;--ref-font-label-lg-font-family:Inter;--ref-font-label-lg-font-weight:400;--ref-font-label-lg-font-style:normal;--ref-font-label-lg-font-stretch:normal;--ref-font-label-lg-letter-spacing:0px;--ref-font-label-lg-line-height:16px;--ref-font-label-lg-paragraph-indent:0px;--ref-font-label-lg-paragraph-spacing:0px;--ref-font-label-lg-text-case:none;--ref-font-label-md-font-size:12px;--ref-font-label-md-text-decoration:none;--ref-font-label-md-font-family:Inter;--ref-font-label-md-font-weight:400;--ref-font-label-md-font-style:normal;--ref-font-label-md-font-stretch:normal;--ref-font-label-md-letter-spacing:0px;--ref-font-label-md-line-height:16px;--ref-font-label-md-paragraph-indent:0px;--ref-font-label-md-paragraph-spacing:0px;--ref-font-label-md-text-case:none;--ref-font-label-sm-font-size:10px;--ref-font-label-sm-text-decoration:none;--ref-font-label-sm-font-family:Roboto;--ref-font-label-sm-font-weight:700;--ref-font-label-sm-font-style:normal;--ref-font-label-sm-font-stretch:normal;--ref-font-label-sm-letter-spacing:0.7px;--ref-font-label-sm-line-height:14px;--ref-font-label-sm-paragraph-indent:0px;--ref-font-label-sm-paragraph-spacing:0px;--ref-font-label-sm-text-case:none;--ref-font-label-lg-underlined-font-size:14px;--ref-font-label-lg-underlined-text-decoration:underline;--ref-font-label-lg-underlined-font-family:Inter;--ref-font-label-lg-underlined-font-weight:400;--ref-font-label-lg-underlined-font-style:normal;--ref-font-label-lg-underlined-font-stretch:normal;--ref-font-label-lg-underlined-letter-spacing:0px;--ref-font-label-lg-underlined-line-height:16px;--ref-font-label-lg-underlined-paragraph-indent:0px;--ref-font-label-lg-underlined-paragraph-spacing:0px;--ref-font-label-lg-underlined-text-case:none;--ref-font-label-lg-mono-font-size:14px;--ref-font-label-lg-mono-text-decoration:none;--ref-font-label-lg-mono-font-family:JetBrains Mono;--ref-font-label-lg-mono-font-weight:400;--ref-font-label-lg-mono-font-style:normal;--ref-font-label-lg-mono-font-stretch:normal;--ref-font-label-lg-mono-letter-spacing:0px;--ref-font-label-lg-mono-line-height:16px;--ref-font-label-lg-mono-paragraph-indent:0px;--ref-font-label-lg-mono-paragraph-spacing:0px;--ref-font-label-lg-mono-text-case:none;--ref-font-label-sm-mono-font-size:10px;--ref-font-label-sm-mono-text-decoration:none;--ref-font-label-sm-mono-font-family:JetBrains Mono;--ref-font-label-sm-mono-font-weight:700;--ref-font-label-sm-mono-font-style:normal;--ref-font-label-sm-mono-font-stretch:normal;--ref-font-label-sm-mono-letter-spacing:0.4px;--ref-font-label-sm-mono-line-height:14px;--ref-font-label-sm-mono-paragraph-indent:0px;--ref-font-label-sm-mono-paragraph-spacing:0px;--ref-font-label-sm-mono-text-case:none;--ref-font-title-sm-font-size:22px;--ref-font-title-sm-text-decoration:none;--ref-font-title-sm-font-family:Inter;--ref-font-title-sm-font-weight:400;--ref-font-title-sm-font-style:normal;--ref-font-title-sm-font-stretch:normal;--ref-font-title-sm-letter-spacing:0px;--ref-font-title-sm-line-height:28px;--ref-font-title-sm-paragraph-indent:0px;--ref-font-title-sm-paragraph-spacing:0px;--ref-font-title-sm-text-case:none;--ref-font-title-sm-mono-font-size:22px;--ref-font-title-sm-mono-text-decoration:none;--ref-font-title-sm-mono-font-family:IBM Plex Mono;--ref-font-title-sm-mono-font-weight:400;--ref-font-title-sm-mono-font-style:normal;--ref-font-title-sm-mono-font-stretch:normal;--ref-font-title-sm-mono-letter-spacing:0.66px;--ref-font-title-sm-mono-line-height:28px;--ref-font-title-sm-mono-paragraph-indent:0px;--ref-font-title-sm-mono-paragraph-spacing:0px;--ref-font-title-sm-mono-text-case:none;--ref-font-body-lg-font-size:18px;--ref-font-body-lg-text-decoration:none;--ref-font-body-lg-font-family:Inter;--ref-font-body-lg-font-weight:400;--ref-font-body-lg-font-style:normal;--ref-font-body-lg-font-stretch:normal;--ref-font-body-lg-letter-spacing:0px;--ref-font-body-lg-line-height:24px;--ref-font-body-lg-paragraph-indent:0px;--ref-font-body-lg-paragraph-spacing:0px;--ref-font-body-lg-text-case:none;--ref-font-body-lg-mono-font-size:18px;--ref-font-body-lg-mono-text-decoration:none;--ref-font-body-lg-mono-font-family:JetBrains Mono;--ref-font-body-lg-mono-font-weight:400;--ref-font-body-lg-mono-font-style:normal;--ref-font-body-lg-mono-font-stretch:normal;--ref-font-body-lg-mono-letter-spacing:0px;--ref-font-body-lg-mono-line-height:24px;--ref-font-body-lg-mono-paragraph-indent:0px;--ref-font-body-lg-mono-paragraph-spacing:0px;--ref-font-body-lg-mono-text-case:none;--ref-font-headline-lg-font-size:36px;--ref-font-headline-lg-text-decoration:none;--ref-font-headline-lg-font-family:Inter;--ref-font-headline-lg-font-weight:400;--ref-font-headline-lg-font-style:normal;--ref-font-headline-lg-font-stretch:normal;--ref-font-headline-lg-letter-spacing:0px;--ref-font-headline-lg-line-height:48px;--ref-font-headline-lg-paragraph-indent:0px;--ref-font-headline-lg-paragraph-spacing:0px;--ref-font-headline-lg-text-case:none;--sys-elevation-raised-low-shadow-type:dropShadow;--sys-elevation-raised-low-radius:2;--sys-elevation-raised-low-color:rgba(12, 21, 48, 0.0784313725);--sys-elevation-raised-low-offset-x:0;--sys-elevation-raised-low-offset-y:1;--sys-elevation-raised-low-spread:0;--sys-elevation-raised-md-shadow-type:dropShadow;--sys-elevation-raised-md-radius:20;--sys-elevation-raised-md-color:rgba(12, 21, 48, 0.1215686275);--sys-elevation-raised-md-offset-x:0;--sys-elevation-raised-md-offset-y:6;--sys-elevation-raised-md-spread:0;--sys-elevation-raised-high-shadow-type:dropShadow;--sys-elevation-raised-high-radius:32;--sys-elevation-raised-high-color:rgba(12, 21, 84, 0.1607843137);--sys-elevation-raised-high-offset-x:0;--sys-elevation-raised-high-offset-y:12;--sys-elevation-raised-high-spread:0;--sys-radius-xs:4px;--sys-radius-sm:8px;--sys-radius-md:16px;--sys-radius-lg:32px;--sys-radius-none:0px;--sys-radius-full:999px;--sys-space-md:16px;--sys-space-lg:32px;--sys-space-xl:56px;--sys-space-xs:4px;--sys-space-sm:8px;--sys-typo-family-sans:system-ui, -apple-system, sans-serif;--sys-typo-family-mono: SF Mono , Monaco, Roboto Mono , monospace;--sys-breakpoint-mobile:768px;--sys-breakpoint-tablet:1024px;--sys-breakpoint-desktop:1280px;--sys-opacity-muted:0.72;--sys-opacity-medium:0.84;--sys-opacity-strong:0.92;--sys-size-lg:48px;--sys-size-md:36px;--sys-size-sm:24px;--sys-size-icon-sm:20px;--sys-size-icon-md:24px;--sys-size-icon-lg:36px;--sys-size-iconfont-sm:8px;--sys-size-iconfont-md:12px;--sys-size-iconfont-lg:18px;--sys-size-xl:58px;--sys-size-xs:18px;--sys-font-label-lg-font-size:14px;--sys-font-label-lg-text-decoration:none;--sys-font-label-lg-font-family:Inter;--sys-font-label-lg-font-weight:400;--sys-font-label-lg-font-style:normal;--sys-font-label-lg-font-stretch:normal;--sys-font-label-lg-letter-spacing:0px;--sys-font-label-lg-line-height:16px;--sys-font-label-lg-paragraph-indent:0px;--sys-font-label-lg-paragraph-spacing:0px;--sys-font-label-lg-text-case:none;--sys-font-label-md-font-size:12px;--sys-font-label-md-text-decoration:none;--sys-font-label-md-font-family:Inter;--sys-font-label-md-font-weight:400;--sys-font-label-md-font-style:normal;--sys-font-label-md-font-stretch:normal;--sys-font-label-md-letter-spacing:0px;--sys-font-label-md-line-height:16px;--sys-font-label-md-paragraph-indent:0px;--sys-font-label-md-paragraph-spacing:0px;--sys-font-label-md-text-case:none;--sys-font-label-sm-font-size:10px;--sys-font-label-sm-text-decoration:none;--sys-font-label-sm-font-family:Roboto;--sys-font-label-sm-font-weight:700;--sys-font-label-sm-font-style:normal;--sys-font-label-sm-font-stretch:normal;--sys-font-label-sm-letter-spacing:0.7px;--sys-font-label-sm-line-height:14px;--sys-font-label-sm-paragraph-indent:0px;--sys-font-label-sm-paragraph-spacing:0px;--sys-font-label-sm-text-case:none;--sys-font-label-lg-underlined-font-size:14px;--sys-font-label-lg-underlined-text-decoration:underline;--sys-font-label-lg-underlined-font-family:Inter;--sys-font-label-lg-underlined-font-weight:400;--sys-font-label-lg-underlined-font-style:normal;--sys-font-label-lg-underlined-font-stretch:normal;--sys-font-label-lg-underlined-letter-spacing:0px;--sys-font-label-lg-underlined-line-height:16px;--sys-font-label-lg-underlined-paragraph-indent:0px;--sys-font-label-lg-underlined-paragraph-spacing:0px;--sys-font-label-lg-underlined-text-case:none;--sys-font-label-lg-mono-font-size:14px;--sys-font-label-lg-mono-text-decoration:none;--sys-font-label-lg-mono-font-family:JetBrains Mono;--sys-font-label-lg-mono-font-weight:400;--sys-font-label-lg-mono-font-style:normal;--sys-font-label-lg-mono-font-stretch:normal;--sys-font-label-lg-mono-letter-spacing:0px;--sys-font-label-lg-mono-line-height:16px;--sys-font-label-lg-mono-paragraph-indent:0px;--sys-font-label-lg-mono-paragraph-spacing:0px;--sys-font-label-lg-mono-text-case:none;--sys-font-label-sm-mono-font-size:10px;--sys-font-label-sm-mono-text-decoration:none;--sys-font-label-sm-mono-font-family:JetBrains Mono;--sys-font-label-sm-mono-font-weight:700;--sys-font-label-sm-mono-font-style:normal;--sys-font-label-sm-mono-font-stretch:normal;--sys-font-label-sm-mono-letter-spacing:0.4px;--sys-font-label-sm-mono-line-height:14px;--sys-font-label-sm-mono-paragraph-indent:0px;--sys-font-label-sm-mono-paragraph-spacing:0px;--sys-font-label-sm-mono-text-case:none;--sys-font-title-sm-font-size:22px;--sys-font-title-sm-text-decoration:none;--sys-font-title-sm-font-family:Inter;--sys-font-title-sm-font-weight:400;--sys-font-title-sm-font-style:normal;--sys-font-title-sm-font-stretch:normal;--sys-font-title-sm-letter-spacing:0px;--sys-font-title-sm-line-height:28px;--sys-font-title-sm-paragraph-indent:0px;--sys-font-title-sm-paragraph-spacing:0px;--sys-font-title-sm-text-case:none;--sys-font-title-sm-mono-font-size:22px;--sys-font-title-sm-mono-text-decoration:none;--sys-font-title-sm-mono-font-family:IBM Plex Mono;--sys-font-title-sm-mono-font-weight:400;--sys-font-title-sm-mono-font-style:normal;--sys-font-title-sm-mono-font-stretch:normal;--sys-font-title-sm-mono-letter-spacing:0.66px;--sys-font-title-sm-mono-line-height:28px;--sys-font-title-sm-mono-paragraph-indent:0px;--sys-font-title-sm-mono-paragraph-spacing:0px;--sys-font-title-sm-mono-text-case:none;--sys-font-body-lg-font-size:18px;--sys-font-body-lg-text-decoration:none;--sys-font-body-lg-font-family:Inter;--sys-font-body-lg-font-weight:400;--sys-font-body-lg-font-style:normal;--sys-font-body-lg-font-stretch:normal;--sys-font-body-lg-letter-spacing:0px;--sys-font-body-lg-line-height:24px;--sys-font-body-lg-paragraph-indent:0px;--sys-font-body-lg-paragraph-spacing:0px;--sys-font-body-lg-text-case:none;--sys-font-body-lg-mono-font-size:18px;--sys-font-body-lg-mono-text-decoration:none;--sys-font-body-lg-mono-font-family:JetBrains Mono;--sys-font-body-lg-mono-font-weight:400;--sys-font-body-lg-mono-font-style:normal;--sys-font-body-lg-mono-font-stretch:normal;--sys-font-body-lg-mono-letter-spacing:0px;--sys-font-body-lg-mono-line-height:24px;--sys-font-body-lg-mono-paragraph-indent:0px;--sys-font-body-lg-mono-paragraph-spacing:0px;--sys-font-body-lg-mono-text-case:none;--sys-font-headline-lg-font-size:36px;--sys-font-headline-lg-text-decoration:none;--sys-font-headline-lg-font-family:Inter;--sys-font-headline-lg-font-weight:400;--sys-font-headline-lg-font-style:normal;--sys-font-headline-lg-font-stretch:normal;--sys-font-headline-lg-letter-spacing:0px;--sys-font-headline-lg-line-height:48px;--sys-font-headline-lg-paragraph-indent:0px;--sys-font-headline-lg-paragraph-spacing:0px;--sys-font-headline-lg-text-case:none}:root,html[data-theme=light]{color-scheme:light;--sys-color-text-base:#070707;--sys-color-text-muted:#919191;--sys-color-text-danger-on:#fff8f7;--sys-color-text-primary:#fbfcff;--sys-color-text-subtle:#5e5e5e;--sys-color-surface-base:#ffffff;--sys-color-surface-muted:#e2e2e2;--sys-color-surface-subtle:#f9f9f9;--sys-color-surface-primary:#121928;--sys-color-surface-primary-hover:#474e5f;--sys-color-surface-subtle-hover:#eeeeee;--sys-color-surface-danger:#a80324;--sys-color-surface-danger-hover:#ff7073;--sys-color-surface-base-hover:#eeeeee;--sys-color-border-base:#303030;--sys-color-border-strong:#070707;--sys-color-border-subtle:#c6c6c6;--sys-color-border-muted:#919191;--sys-color-feedback-error:#b9192f;--comp-switch-track-bg:#070707;--comp-switch-handle-bg:#f9f9f9;--comp-switch-label-color:#070707}@media(prefers-color-scheme: dark){:root:not([data-theme]),html[data-theme=dark]{color-scheme:dark;--sys-color-text-base:#f9f9f9;--sys-color-text-muted:#f9f9f9;--sys-color-text-danger-on:#fff8f7;--sys-color-text-primary:#0b1220;--sys-color-text-subtle:#c6c6c6;--sys-color-surface-base:#474747;--sys-color-surface-muted:#131313;--sys-color-surface-subtle:#1f1f1f;--sys-color-surface-primary:#c8cee3;--sys-color-surface-primary-hover:#9198ac;--sys-color-surface-subtle-hover:#5e5e5e;--sys-color-surface-danger:#b9192f;--sys-color-surface-danger-hover:#b9192f;--sys-color-surface-base-hover:#474747;--sys-color-border-base:#5e5e5e;--sys-color-border-strong:#c6c6c6;--sys-color-border-subtle:#5e5e5e;--sys-color-border-muted:#5e5e5e;--sys-color-feedback-error:#ff525c;--comp-switch-track-bg:#f9f9f9;--comp-switch-handle-bg:#070707;--comp-switch-label-color:#f9f9f9}}html[data-theme=dark]{color-scheme:dark;--sys-color-text-base:#f9f9f9;--sys-color-text-muted:#f9f9f9;--sys-color-text-danger-on:#fff8f7;--sys-color-text-primary:#0b1220;--sys-color-text-subtle:#c6c6c6;--sys-color-surface-base:#474747;--sys-color-surface-muted:#131313;--sys-color-surface-subtle:#1f1f1f;--sys-color-surface-primary:#c8cee3;--sys-color-surface-primary-hover:#9198ac;--sys-color-surface-subtle-hover:#5e5e5e;--sys-color-surface-danger:#b9192f;--sys-color-surface-danger-hover:#b9192f;--sys-color-surface-base-hover:#474747;--sys-color-border-base:#5e5e5e;--sys-color-border-strong:#c6c6c6;--sys-color-border-subtle:#5e5e5e;--sys-color-border-muted:#5e5e5e;--sys-color-feedback-error:#ff525c}*{box-sizing:border-box}body{margin:0;font-family:var(--ref-typo-family-sans);font-size:14px;line-height:1.5;color:var(--sys-color-text-base);background:var(--sys-color-surface-base)}[hidden]{display:none !important}a{color:inherit;text-underline-offset:3px}button,input,select,textarea{font:inherit}button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:2px solid var(--sys-color-border-strong);outline-offset:3px}h1,h2,h3,p{margin:0}h1{font-size:var(--sys-font-title-sm-font-size);font-weight:var(--sys-font-title-sm-font-weight);line-height:var(--sys-font-title-sm-line-height);letter-spacing:var(--sys-font-title-sm-letter-spacing)}h2,h3{font-size:var(--sys-font-body-lg-font-size);font-weight:var(--sys-font-body-lg-font-weight);line-height:var(--sys-font-body-lg-line-height);letter-spacing:var(--sys-font-body-lg-letter-spacing)}code,.extension-name{font-family:var(--ref-typo-family-mono);overflow-wrap:anywhere}.sr-only,.skip-link:not(:focus){position:absolute;width:1px;height:1px;padding:0;overflow:hidden;clip-path:inset(50%);white-space:nowrap}.skip-link:focus{display:block;padding:8px}.site-header{display:flex;align-items:center;justify-content:space-between;gap:var(--sys-space-md);padding:var(--sys-space-md) var(--sys-space-lg);background:var(--sys-color-surface-subtle);border-bottom:1px solid var(--sys-color-border-base)}.site-header nav{display:flex;align-items:center;gap:var(--sys-space-md)}.site-header nav a{text-decoration:none}.site-header [aria-current=page]{text-decoration:underline}.brand{display:flex;align-items:center;gap:var(--sys-space-sm);text-decoration:none;font-size:var(--sys-font-title-sm-font-size);font-weight:var(--sys-font-title-sm-font-weight);line-height:var(--sys-font-title-sm-line-height);letter-spacing:var(--sys-font-title-sm-letter-spacing);font-family:var(--ref-typo-family-mono)}.brand svg,.brand>span:first-child{display:block;width:32px;height:32px}.count,.extension-name,.field-help{color:var(--sys-color-text-subtle);font-size:12px}main{padding:var(--sys-space-lg) var(--sys-space-xl)}.section-heading,.detail-heading,.workspace-heading,.workspace-toolbar,.dialog-heading{display:flex;align-items:center;justify-content:space-between;gap:var(--sys-space-md);margin-bottom:var(--sys-space-md)}.count{margin-left:var(--sys-space-sm);font-weight:400}.button,.icon-button{display:inline-flex;align-items:center;justify-content:center;gap:var(--sys-space-sm);padding:var(--sys-space-sm) var(--sys-space-md);border:1px solid var(--sys-color-border-base);border-radius:0;background:var(--sys-color-surface-subtle);color:var(--sys-color-text-base);text-decoration:none;cursor:pointer;font-size:var(--sys-font-label-lg-font-size);font-weight:var(--sys-font-label-lg-font-weight);line-height:var(--sys-font-label-lg-line-height);letter-spacing:var(--sys-font-label-lg-letter-spacing)}.button:hover,.icon-button:hover{background:var(--sys-color-surface-subtle-hover)}.button:disabled,.icon-button:disabled{opacity:.5;cursor:not-allowed}.icon-button{width:32px;height:32px;padding:0;border-color:rgba(0,0,0,0)}.button-primary{background:var(--sys-color-surface-primary);color:var(--sys-color-text-primary)}.button-primary:hover{background:var(--sys-color-surface-primary-hover)}input,select,textarea{width:100%;min-width:0;padding:var(--sys-space-sm);border:1px solid var(--sys-color-border-subtle);border-radius:0;background:var(--sys-color-surface-base);color:var(--sys-color-text-base)}input,select{min-height:36px}.search-form{display:flex;gap:var(--sys-space-sm);margin-bottom:var(--sys-space-md)}.search-form input{flex:1}.search-form select{width:auto;max-width:240px}.search-summary{margin-bottom:var(--sys-space-md)}.catalog-list,.version-list{list-style:none;margin:0;padding:0}.catalog-list{display:grid;gap:var(--sys-space-md)}.extension-card{display:flex;align-items:center;justify-content:space-between;gap:var(--sys-space-md);border:1px solid var(--sys-color-border-base);padding:var(--sys-space-md);text-decoration:none}.extension-card h2{font-size:var(--sys-font-label-lg-font-size);font-weight:var(--sys-font-label-lg-font-weight);line-height:var(--sys-font-label-lg-line-height);letter-spacing:var(--sys-font-label-lg-letter-spacing);margin-bottom:var(--sys-space-sm)}.extension-card:hover{background:var(--sys-color-surface-subtle)}.card-action{flex-shrink:0;font-size:12px}.back-link{display:inline-block;margin-bottom:var(--sys-space-lg)}.detail-heading{margin-bottom:var(--sys-space-lg)}.detail-heading h1{margin-bottom:var(--sys-space-sm)}.detail-actions{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sys-space-sm)}.detail-layout{display:grid;grid-template-columns:minmax(0, 1fr) 280px;gap:var(--sys-space-lg)}.distribution-panel,.sidebar-panel,.release-row,.connect-card{border:1px solid var(--sys-color-border-base);padding:var(--sys-space-md);margin-bottom:var(--sys-space-md)}.metadata{margin:var(--sys-space-md) 0}.metadata>div{display:grid;grid-template-columns:120px minmax(0, 1fr);gap:var(--sys-space-md);margin-bottom:var(--sys-space-sm)}.metadata dt{color:var(--sys-color-text-subtle)}.metadata dd{margin:0;overflow-wrap:anywhere}.version-list{margin-top:var(--sys-space-md)}.version-list a{display:flex;justify-content:space-between;gap:var(--sys-space-sm);padding:var(--sys-space-sm);text-decoration:none}.version-list [aria-current],.version-list a:hover{background:var(--sys-color-surface-subtle-hover)}.version-list span{font-size:12px}.publisher-meta>div{grid-template-columns:auto 1fr}.connect-layout{max-width:480px}label{display:block;margin-bottom:var(--sys-space-sm)}form>.button{margin-top:var(--sys-space-md)}.search-form>.button{margin-top:0}.workspace-toolbar>div,.release-actions,.release-pagination{display:flex;flex-wrap:wrap;align-items:center;gap:var(--sys-space-sm)}.release-row{display:flex;align-items:center;justify-content:space-between;gap:var(--sys-space-md)}.release-row h3,.release-row p{margin-bottom:var(--sys-space-sm);overflow-wrap:anywhere}.release-row p{color:var(--sys-color-text-subtle);font-size:12px}.badge{display:inline-block;margin-bottom:var(--sys-space-sm);padding:0 var(--sys-space-xs);border:1px solid var(--sys-color-border-subtle);font-size:12px}.release-pagination{margin-bottom:var(--sys-space-md)}.form-status:not(:empty),.empty-state,noscript p{margin:var(--sys-space-md) 0}.error{color:var(--sys-color-feedback-error)}dialog{width:min(560px,100% - 32px);max-height:calc(100% - 32px);padding:var(--sys-space-lg);border:1px solid var(--sys-color-border-base);background:var(--sys-color-surface-base);color:var(--sys-color-text-base)}dialog::backdrop{background:rgba(0,0,0,.3)}.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--sys-space-md);margin-top:var(--sys-space-md)}.full-width{grid-column:1/-1}.field-help{margin-top:var(--sys-space-xs)}.toast:not(:empty){position:fixed;bottom:var(--sys-space-md);right:var(--sys-space-md);max-width:calc(100% - 32px);padding:var(--sys-space-sm) var(--sys-space-md);border:1px solid var(--sys-color-border-base);background:var(--sys-color-surface-subtle)}@media(max-width: 768px){.site-header,main{padding:var(--sys-space-md)}.site-header nav{gap:var(--sys-space-sm)}.brand{font-size:18px}.search-form{flex-wrap:wrap}.search-form input{flex-basis:100%}.search-form select{flex:1;max-width:none}.detail-layout,.form-grid{grid-template-columns:minmax(0, 1fr)}.detail-heading,.workspace-toolbar,.release-row{align-items:flex-start;flex-wrap:wrap}dialog{padding:var(--sys-space-md)}.metadata>div{grid-template-columns:100px minmax(0, 1fr);gap:var(--sys-space-sm)}} diff --git a/src/inkcre_extension_registry/service/templates/base.html b/src/inkcre_extension_registry/service/templates/base.html index 95d2f09..9444f9c 100644 --- a/src/inkcre_extension_registry/service/templates/base.html +++ b/src/inkcre_extension_registry/service/templates/base.html @@ -14,12 +14,12 @@