Changes
10 changed files (+198/-24)
-
-
@@ -91,6 +91,7 @@ pub fn build(b: *std.Build) !void {_ = dir.addCopyFile(b.path("src/index.html"), "index.html"); _ = dir.addCopyFile(elements_js, "elements.js"); _ = dir.addCopyFile(b.path("src/logo.svg"), "logo.svg"); _ = dir.addCopyDirectory(b.path("src/icons"), ".icons", .{}); _ = dir.addCopyDirectory(b.path("vendor/barlow"), "vendor/barlow", .{}); _ = dir.addCopyDirectory(b.path("vendor/Inter"), "vendor/Inter", .{});
-
-
-
@@ -21,3 +21,13 @@ slot name =role : String -> Attribute msg role name = attribute "role" name commandFor : String -> Attribute msg commandFor = attribute "commandfor" command : String -> Attribute msg command = attribute "command"
-
-
-
@@ -11,8 +11,8 @@ module Models.PWS01.Main exposing (main)import Browser import Browser.Navigation exposing (Key) import Html exposing (a, hr, node, p, span, text) import Html.Attributes exposing (attribute, href) import Html exposing (a, button, hr, img, node, p, span, text) import Html.Attributes exposing (attribute, href, src) import Html.LivingStandard exposing (..) import Json.Decode import Models.PWS01.Parameters as Parameters
-
@@ -158,7 +158,7 @@ update msg model =|> Tuple.mapSecond (Cmd.map PreferencesMsg) UrlRequested (Browser.Internal url) -> if String.endsWith ".txt" url.path then if String.endsWith ".txt" url.path || not (url.path == model.url.path) then ( model, Browser.Navigation.load (Url.toString url) ) else
-
@@ -203,6 +203,7 @@ view model =[] [ node "x-preview" [ slot "preview" , Html.Attributes.id "preview" , attribute "print-theme" (case parameters.parameters.rendering.colorSchema of Parameters.BlackOnWhite ->
-
@@ -226,6 +227,29 @@ view model =highlighting [] ] , node "x-toolbar" [ slot "toolbar" ] [ a [ href "/" ] [ img [ src "/logo.svg" , Html.Attributes.style "width" "100%" , Html.Attributes.style "height" "100%" ] [] , span [] [ text "Top page" ] ] , button [ commandFor "preview", command "--reset-viewport" ] [ img [ src "/.icons/zoom-reset.svg" , Html.Attributes.style "width" "100%" , Html.Attributes.style "height" "100%" ] [] , span [] [ text "Reset viewport" ] ] ] , node "x-panel" [ slot "parameters" ] [ node "x-parameters"
-
-
-
@@ -17,6 +17,7 @@ import { XPreview } from "./x-preview.js";import { XQueryHealer } from "./x-query-healer.js"; import { XQueryHealerItems } from "./x-query-healer-items.js"; import { XRadioBox } from "./x-radio-box.js"; import { XToolbar } from "./x-toolbar.js"; customElements.define("x-app-layout", XAppLayout); customElements.define("x-error-page", XErrorPage);
-
@@ -29,3 +30,4 @@ customElements.define("x-preview", XPreview);customElements.define("x-query-healer", XQueryHealer); customElements.define("x-query-healer-items", XQueryHealerItems); customElements.define("x-radio-box", XRadioBox); customElements.define("x-toolbar", XToolbar);
-
-
-
@@ -13,8 +13,8 @@inset: 0; display: grid; grid-template-rows: minmax(0, 1fr) 50dvh; grid-template-columns: minmax(0, 1fr); grid-template-areas: "preview" "parameters"; grid-template-columns: 2rem minmax(0, 1fr); grid-template-areas: "toolbar preview" "parameters parameters"; font-family: "Inter", sans-serif; }
-
@@ -22,21 +22,32 @@grid-area: preview; } ::slotted([slot="parameters"]), ::slotted([slot="toolbar"]) { position: relative; box-shadow: 0 0 3px oklch(0% 0 0deg / 0.2); z-index: 100; } ::slotted([slot="parameters"]) { grid-area: parameters; position: relative; border: none; border-top: 1px solid ButtonFace; } box-shadow: 0 0 3px oklch(0% 0 0deg / 0.2); z-index: 100; ::slotted([slot="toolbar"]) { grid-area: toolbar; align-self: start; border: 1px solid ButtonFace; border-top: none; border-left: none; } @media screen and (min-width: 980px) { :host { grid-template-columns: minmax(0, 1fr) min(20rem, 30vw); grid-template-columns: 2rem minmax(0, 1fr) min(20rem, 30vw); grid-template-rows: minmax(0, 1fr); grid-template-areas: "preview parameters"; grid-template-areas: "toolbar preview parameters"; } ::slotted([slot="parameters"]) {
-
@@ -54,7 +65,7 @@display: contents; } ::slotted([slot="parameters"]) { ::slotted([slot="parameters"]), ::slotted([slot="toolbar"]) { display: none; } }
-
-
-
@@ -27,5 +27,9 @@ export class XAppLayout extends HTMLElement {const parameters = document.createElement("slot"); parameters.name = "parameters"; shadow.appendChild(parameters); const toolbar = document.createElement("slot"); toolbar.name = "toolbar"; shadow.appendChild(toolbar); } }
-
-
-
@@ -83,22 +83,11 @@ export class XPreview extends HTMLElement {this.#gesturePlane.classList.add("gesture-plane"); shadow.appendChild(this.#gesturePlane); const reset = document.createElement("button"); reset.textContent = "Reset Viewport"; reset.classList.add("reset"); reset.classList.add("hidden"); reset.addEventListener("click", (event) => { this.#dx = 0; this.#dy = 0; this.#scale = 1.0; this.#render(); }); shadow.appendChild(reset); } connectedCallback() { this.addEventListener("command", this.#onCommand); this.#gesturePlane.addEventListener("wheel", this.#onWheel); this.#gesturePlane.addEventListener("touchstart", this.#onTouchChange); this.#gesturePlane.addEventListener("touchend", this.#onTouchChange);
-
@@ -111,6 +100,8 @@ export class XPreview extends HTMLElement {} disconnectedCallback() { this.removeEventListener("command", this.#onCommand); this.#gesturePlane.removeEventListener("wheel", this.#onWheel); this.#gesturePlane.removeEventListener("touchstart", this.#onTouchChange); this.#gesturePlane.removeEventListener("touchend", this.#onTouchChange);
-
@@ -119,6 +110,18 @@ export class XPreview extends HTMLElement {this.#resizeObserver.disconnect(); } #onCommand = (event) => { switch (event.command) { case "--reset-viewport": this.#dx = 0; this.#dy = 0; this.#scale = 1.0; this.#render(); return; } }; #onTouchChange = (event) => { switch (event.touches.length) { case 0:
-
-
-
@@ -0,0 +1,57 @@/* * Copyright 2026 Shota FUJI * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. * * SPDX-License-Identifier: MPL-2.0 */ :host { display: flex; flex-direction: column; justify-self: start; overflow: hidden; transform: translateX(-100%) translateX(2rem); transition: transform 100ms ease-in-out; } .container { display: flex; flex-direction: column; transform: translateX(100%) translateX(-1.97rem); transition: transform 100ms ease-in-out; } ::slotted(*) { display: grid; grid-template-columns: 1rem minmax(max-content, 1fr); gap: 0.5rem; padding: 0.5rem; border: none; white-space: nowrap; font: inherit; font-size: 0.9rem; background-color: Canvas; color: CanvasText; text-decoration: none; } ::slotted(:hover), ::slotted(:focus-visible) { background-color: SelectedItem; color: SelectedItemText; outline: none; } :host(:focus-within), :host(:hover) { transform: translateX(0); & > .container { transform: translateX(0); } }
-
-
-
@@ -0,0 +1,29 @@// Copyright 2026 Shota FUJI // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // // SPDX-License-Identifier: MPL-2.0 import css from "./x-toolbar.css"; export class XToolbar extends HTMLElement { constructor() { super(); const shadow = this.attachShadow({ mode: "open", }); const style = document.createElement("style"); style.textContent = css; shadow.appendChild(style); const container = document.createElement("div"); container.classList.add("container"); shadow.appendChild(container); container.appendChild(document.createElement("slot")); } }
-
-
src/icons/zoom-reset.svg (new)
-
@@ -0,0 +1,33 @@<!-- Copyright 2026 Shota FUJI This source code and the design it expresses are licensed under the Creative Commons Attribution 4.0 International License. SPDX-License-Identifier: CC-BY-4.0 --> <svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" viewBox="0 0 100 100"> <style> #graphics { fill: black; stroke: black; @media (prefers-color-scheme: dark) { fill: white; stroke: white; --color: white; } } </style> <mask id="mask" mask-type="luminance"> <rect fill="white" x="0" y="0" width="100" height="100" /> <path fill="none" stroke="black" stroke-width="7" d="M70,45 l10,-5 l10,5" /> <path fill="none" stroke="black" stroke-width="7" d="M0,45 l10,5 l10,-5" /> </mask> <g id="graphics" stroke-width="10"> <circle mask="url(#mask)" fill="none" stroke-width="10" cx="47" cy="45" r="35" /> <line fill="none" x1="95" y1="93" x2="70" y2="68" /> <path fill="none" stroke-width="7" d="M67,53 l15,-8 l10,14" /> <path fill="none" stroke-width="7" d="M1,32 l10,14 l15,-8" /> </g> </svg>
-