├── .editorconfig ├── .gitignore ├── .gitmodules ├── .prettierignore ├── .prettierrc.json ├── LICENSE ├── README.md ├── netlify.toml ├── package.json ├── protocols └── external │ ├── agl-screenshooter.xml │ ├── agl-shell-desktop.xml │ ├── agl-shell.xml │ ├── aura-output-management.xml │ ├── aura-shell.xml │ ├── chrome-color-management.xml │ ├── gtk-shell.xml │ ├── input-method-unstable-v2.xml │ ├── mir-shell-unstable-v1.xml │ ├── nvidia-eglstream-controller.xml │ ├── nvidia-eglstream.xml │ ├── overlay-prioritizer.xml │ ├── surface-augmenter.xml │ ├── tizen-extension.xml │ ├── virtual-keyboard-unstable-v1.xml │ └── wayland-drm.xml ├── public ├── android-chrome-192x192.png ├── android-chrome-512x512.png ├── apple-touch-icon.png ├── browserconfig.xml ├── favicon-16x16.png ├── favicon-32x32.png ├── favicon.ico ├── index.html ├── logo.svg ├── logos │ ├── Steam_Deck.svg │ ├── cosmic.svg │ ├── deepin.svg │ ├── gnome.svg │ ├── hyprland.svg │ ├── kde.svg │ ├── labwc.svg │ ├── louvre.svg │ ├── mir.svg │ ├── sway.svg │ ├── wayfire.svg │ └── weston.svg ├── manifest.json ├── mstile-144x144.png ├── mstile-150x150.png ├── mstile-310x150.png ├── mstile-310x310.png ├── mstile-70x70.png ├── robots.txt └── safari-pinned-tab.svg ├── scripts ├── bin │ ├── print-protocol-registry-items.ts │ ├── regenerate-protocols-data.ts │ └── render-static-html.tsx └── lib │ └── utils.ts ├── src ├── App.test.tsx ├── App.tsx ├── analytics │ └── plausible.ts ├── components │ ├── WaylandArg.tsx │ ├── WaylandCompositors.tsx │ ├── WaylandCopyright.tsx │ ├── WaylandDataTable.tsx │ ├── WaylandDescription.tsx │ ├── WaylandEntry.tsx │ ├── WaylandEnum.tsx │ ├── WaylandEvent.tsx │ ├── WaylandInterface.tsx │ ├── WaylandProtocol.tsx │ ├── WaylandRequest.tsx │ ├── breadcrumbs │ │ └── Breadcrumbs.tsx │ ├── common │ │ ├── hooks-utils.ts │ │ ├── index.ts │ │ ├── utils.ts │ │ └── wayland-protocol-icons.ts │ ├── content │ │ ├── Badge.tsx │ │ ├── ProtocolBadge.tsx │ │ └── WaylandElementSignature.tsx │ ├── layout │ │ ├── Footer.tsx │ │ ├── Header.tsx │ │ ├── Logo.tsx │ │ ├── MultiColumnLayout.tsx │ │ ├── ScrollToTop.tsx │ │ └── overlays │ │ │ ├── OutlineOverlay.tsx │ │ │ ├── OverlayBackground.tsx │ │ │ └── SidebarOverlay.tsx │ ├── outline │ │ ├── WaylandInterfaceOutline.tsx │ │ └── WaylandProtocolOutline.tsx │ └── sidebar-navigation │ │ ├── SidebarNavLink.tsx │ │ └── WaylandProtocolLinks.tsx ├── config │ └── index.ts ├── data │ ├── compositor-registry.ts │ ├── compositors │ │ ├── README.md │ │ ├── cage.json │ │ ├── cosmic.json │ │ ├── gamescope.json │ │ ├── hyprland.json │ │ ├── jay.json │ │ ├── kwin.json │ │ ├── labwc.json │ │ ├── louvre.json │ │ ├── mir.json │ │ ├── mutter.json │ │ ├── niri.json │ │ ├── sway.json │ │ ├── treeland.json │ │ ├── wayfire.json │ │ └── weston.json │ ├── protocol-registry.ts │ └── protocols │ │ ├── agl-screenshooter.json │ │ ├── agl-shell-desktop.json │ │ ├── agl-shell.json │ │ ├── alpha-modifier-v1.json │ │ ├── aura-output-management.json │ │ ├── aura-shell.json │ │ ├── chrome-color-management.json │ │ ├── color-management-v1.json │ │ ├── commit-timing-v1.json │ │ ├── content-type-v1.json │ │ ├── cosmic-image-source-unstable-v1.json │ │ ├── cosmic-output-management-unstable-v1.json │ │ ├── cosmic-screencopy-unstable-v1.json │ │ ├── cosmic-screencopy-unstable-v2.json │ │ ├── cosmic-toplevel-info-unstable-v1.json │ │ ├── cosmic-toplevel-management-unstable-v1.json │ │ ├── cosmic-workspace-unstable-v1.json │ │ ├── cursor-shape-v1.json │ │ ├── drm-lease-v1.json │ │ ├── ext-data-control-v1.json │ │ ├── ext-foreign-toplevel-list-v1.json │ │ ├── ext-idle-notify-v1.json │ │ ├── ext-image-capture-source-v1.json │ │ ├── ext-image-copy-capture-v1.json │ │ ├── ext-session-lock-v1.json │ │ ├── ext-transient-seat-v1.json │ │ ├── ext-workspace-v1.json │ │ ├── fifo-v1.json │ │ ├── fractional-scale-v1.json │ │ ├── fullscreen-shell-unstable-v1.json │ │ ├── gtk-shell.json │ │ ├── hyprland-ctm-control-v1.json │ │ ├── hyprland-focus-grab-v1.json │ │ ├── hyprland-global-shortcuts-v1.json │ │ ├── hyprland-lock-notify-v1.json │ │ ├── hyprland-surface-v1.json │ │ ├── hyprland-toplevel-export-v1.json │ │ ├── hyprland-toplevel-mapping-v1.json │ │ ├── idle-inhibit-unstable-v1.json │ │ ├── input-method-unstable-v1.json │ │ ├── input-method-unstable-v2.json │ │ ├── input-timestamps-unstable-v1.json │ │ ├── ivi-application.json │ │ ├── ivi-hmi-controller.json │ │ ├── kde-appmenu.json │ │ ├── kde-blur.json │ │ ├── kde-contrast.json │ │ ├── kde-dpms.json │ │ ├── kde-external-brightness-v1.json │ │ ├── kde-fake-input.json │ │ ├── kde-idle.json │ │ ├── kde-keystate.json │ │ ├── kde-lockscreen-overlay-v1.json │ │ ├── kde-output-device-v2.json │ │ ├── kde-output-management-v2.json │ │ ├── kde-output-management.json │ │ ├── kde-output-order-v1.json │ │ ├── kde-outputdevice.json │ │ ├── kde-plasma-shell.json │ │ ├── kde-plasma-virtual-desktop.json │ │ ├── kde-plasma-window-management.json │ │ ├── kde-primary-output-v1.json │ │ ├── kde-screen-edge-v1.json │ │ ├── kde-server-decoration-palette.json │ │ ├── kde-server-decoration.json │ │ ├── kde-shadow.json │ │ ├── kde-slide.json │ │ ├── kde-zkde-screencast-unstable-v1.json │ │ ├── keyboard-shortcuts-inhibit-unstable-v1.json │ │ ├── linux-dmabuf-unstable-v1.json │ │ ├── linux-dmabuf-v1.json │ │ ├── linux-drm-syncobj-v1.json │ │ ├── linux-explicit-synchronization-unstable-v1.json │ │ ├── mir-shell-unstable-v1.json │ │ ├── nvidia-eglstream-controller.json │ │ ├── nvidia-eglstream.json │ │ ├── overlay-prioritizer.json │ │ ├── pointer-constraints-unstable-v1.json │ │ ├── pointer-gestures-unstable-v1.json │ │ ├── presentation-time.json │ │ ├── primary-selection-unstable-v1.json │ │ ├── relative-pointer-unstable-v1.json │ │ ├── security-context-v1.json │ │ ├── single-pixel-buffer-v1.json │ │ ├── surface-augmenter.json │ │ ├── tablet-unstable-v2.json │ │ ├── tablet-v2.json │ │ ├── tearing-control-v1.json │ │ ├── text-cursor-position.json │ │ ├── text-input-unstable-v3.json │ │ ├── tizen-extension.json │ │ ├── treeland-capture-unstable-v1.json │ │ ├── treeland-dde-shell-v1.json │ │ ├── treeland-foreign-toplevel-manager-v1.json │ │ ├── treeland-output-manager-v1.json │ │ ├── treeland-personalization-manager-v1.json │ │ ├── treeland-shortcut-manager-v1.json │ │ ├── treeland-virtual-output-manager-v1.json │ │ ├── treeland-wallpaper-color-v1.json │ │ ├── treeland-window-management-v1.json │ │ ├── viewporter.json │ │ ├── virtual-keyboard-unstable-v1.json │ │ ├── wayland-drm.json │ │ ├── wayland.json │ │ ├── weston-content-protection.json │ │ ├── weston-debug.json │ │ ├── weston-desktop-shell.json │ │ ├── weston-direct-display.json │ │ ├── weston-output-capture.json │ │ ├── weston-test.json │ │ ├── weston-touch-calibration.json │ │ ├── wlr-data-control-unstable-v1.json │ │ ├── wlr-export-dmabuf-unstable-v1.json │ │ ├── wlr-foreign-toplevel-management-unstable-v1.json │ │ ├── wlr-gamma-control-unstable-v1.json │ │ ├── wlr-input-inhibitor-unstable-v1.json │ │ ├── wlr-layer-shell-unstable-v1.json │ │ ├── wlr-output-management-unstable-v1.json │ │ ├── wlr-output-power-management-unstable-v1.json │ │ ├── wlr-screencopy-unstable-v1.json │ │ ├── wlr-virtual-pointer-unstable-v1.json │ │ ├── xdg-activation-v1.json │ │ ├── xdg-decoration-unstable-v1.json │ │ ├── xdg-dialog-v1.json │ │ ├── xdg-foreign-unstable-v2.json │ │ ├── xdg-output-unstable-v1.json │ │ ├── xdg-shell.json │ │ ├── xdg-system-bell-v1.json │ │ ├── xdg-toplevel-drag-v1.json │ │ ├── xdg-toplevel-icon-v1.json │ │ ├── xdg-toplevel-tag-v1.json │ │ ├── xwayland-keyboard-grab-unstable-v1.json │ │ └── xwayland-shell-v1.json ├── gitlab-api │ └── index.ts ├── index.css ├── index.tsx ├── lib │ └── xml-protocol-transformers.ts ├── logo.svg ├── model │ ├── protocol-source-link-builder.ts │ ├── wayland-protocol-metadata.ts │ └── wayland.ts ├── pages │ ├── 404.tsx │ ├── GitLab.tsx │ └── Homepage.tsx ├── react-app-env.d.ts ├── reportWebVitals.ts └── setupTests.ts ├── tailwind.config.js ├── tsconfig.json ├── tsconfig.scripts.json └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | root=true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 4 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "protocols/wayland"] 2 | path = protocols/wayland 3 | url = https://gitlab.freedesktop.org/wayland/wayland-protocols.git 4 | [submodule "protocols/wlr"] 5 | path = protocols/wlr 6 | url = https://gitlab.freedesktop.org/wlroots/wlr-protocols.git 7 | [submodule "protocols/kde"] 8 | path = protocols/kde 9 | url = https://invent.kde.org/libraries/plasma-wayland-protocols.git 10 | [submodule "protocols/weston"] 11 | path = protocols/weston 12 | url = https://gitlab.freedesktop.org/wayland/weston.git 13 | [submodule "protocols/libwayland"] 14 | path = protocols/libwayland 15 | url = https://gitlab.freedesktop.org/wayland/wayland.git 16 | [submodule "protocols/hyprland"] 17 | path = protocols/hyprland 18 | url = https://github.com/hyprwm/hyprland-protocols.git 19 | [submodule "protocols/treeland"] 20 | path = protocols/treeland 21 | url = https://github.com/linuxdeepin/treeland-protocols.git 22 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | /protocols/ 2 | /src/data/protocols/ 3 | /src/data/compositors/ 4 | /build/ 5 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "semi": false, 4 | "singleQuote": true 5 | } 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Valentin Hăloiu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Wayland Explorer 2 | 3 | Easily browse and read Wayland protocols documentation. 4 | 5 | ## Motivation for this project 6 | 7 | Wayland protocols are published as XML files. While the [core Wayland protocol](https://gitlab.freedesktop.org/wayland/wayland/-/blob/main/protocol/wayland.xml) specification is also available in HTML format for reading [online](https://wayland.freedesktop.org/docs/html/apa.html), that's not the case for all the [other](https://gitlab.freedesktop.org/wayland/wayland-protocols/-/tree/main/unstable) [Wayland](https://gitlab.freedesktop.org/wayland/wayland-protocols/-/tree/main/stable) [protocols](https://gitlab.freedesktop.org/wlroots/wlr-protocols/-/tree/master/unstable) which are not part of the core protocol. 8 | 9 | This project attempts to fill this gap by parsing the XML protocol files and converting them to HTML in order to make it easy to browse and read them on the web. 10 | 11 | ## Technologies 12 | 13 | Built with :sparkling_heart: using React, [Tailwind UI](https://tailwindui.com/components) and [vscode-codicons](https://github.com/microsoft/vscode-codicons). 14 | 15 | ## Disclaimer 16 | 17 | This project has no affiliation with the official Wayland project. 18 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build.environment] 2 | NODE_VERSION = "14" 3 | 4 | [[redirects]] 5 | from = "/api/event" 6 | to = "https://plausible.io/api/event" 7 | status = 200 8 | 9 | [[redirects]] 10 | from = "/" 11 | to = "/protocols/" 12 | 13 | [[redirects]] 14 | from = "/protocols/kde-screencast" 15 | to = "/protocols/kde-zkde-screencast-unstable-v1" 16 | 17 | [[redirects]] 18 | from = "/protocols/linux-dmabuf-unstable-v1" 19 | to = "/protocols/linux-dmabuf-v1" 20 | 21 | [[redirects]] 22 | from = "/protocols/tablet-unstable-v2" 23 | to = "/protocols/tablet-v2" 24 | 25 | [[redirects]] 26 | from = "/protocols/wayland-protocols/*" 27 | to = "/protocols/wayland-protocols-gitlab.html" 28 | status = 200 29 | force = true 30 | 31 | [[redirects]] 32 | from = "/protocols/*" 33 | to = "/protocols/404.html" 34 | status = 404 35 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wayland-explorer", 3 | "description": "Wayland protocol explorer", 4 | "version": "0.1.0", 5 | "private": true, 6 | "license": "MIT", 7 | "homepage": "/protocols", 8 | "dependencies": { 9 | "@headlessui/react": "^1.4.1", 10 | "@vscode/codicons": "^0.0.21", 11 | "fast-xml-parser": "^4.5.0", 12 | "plausible-tracker": "^0.3.1", 13 | "react": "^17.0.2", 14 | "react-dom": "^17.0.2", 15 | "react-scripts": "5.0.0", 16 | "web-vitals": "^2.1.0", 17 | "wouter": "^2.7.4" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "env BUILD_PATH=build/protocols react-scripts build", 22 | "postbuild": "ts-node --project=./tsconfig.scripts.json ./scripts/bin/render-static-html.tsx", 23 | "test": "react-scripts test", 24 | "regenerate-protocols": "ts-node --project=./tsconfig.scripts.json ./scripts/bin/regenerate-protocols-data.ts", 25 | "print-registry": "ts-node --project=./tsconfig.scripts.json ./scripts/bin/print-protocol-registry-items.ts", 26 | "eject": "react-scripts eject" 27 | }, 28 | "eslintConfig": { 29 | "extends": [ 30 | "react-app", 31 | "react-app/jest" 32 | ] 33 | }, 34 | "browserslist": { 35 | "production": [ 36 | ">0.2%", 37 | "not dead", 38 | "not op_mini all" 39 | ], 40 | "development": [ 41 | "last 1 chrome version", 42 | "last 1 firefox version", 43 | "last 1 safari version" 44 | ] 45 | }, 46 | "husky": { 47 | "hooks": { 48 | "pre-commit": "lint-staged" 49 | } 50 | }, 51 | "lint-staged": { 52 | "src/**/*.{js,jsx,ts,tsx,json,css,scss,md}": [ 53 | "prettier --write" 54 | ] 55 | }, 56 | "devDependencies": { 57 | "@testing-library/jest-dom": "^5.11.4", 58 | "@testing-library/react": "^12.0.0", 59 | "@testing-library/user-event": "^13.2.1", 60 | "@types/html-minifier": "^4.0.0", 61 | "@types/jest": "^27.0.1", 62 | "@types/node": "^16.7.10", 63 | "@types/react": "^17.0.0", 64 | "@types/react-dom": "^17.0.0", 65 | "autoprefixer": "^10.4.2", 66 | "html-minifier": "^4.0.0", 67 | "husky": "^7.0.2", 68 | "lint-staged": "^12.3.1", 69 | "postcss": "^8.4.5", 70 | "prettier": "^2.3.2", 71 | "tailwind-scrollbar": "^1.3.1", 72 | "tailwindcss": "^3.0.16", 73 | "ts-node": "^10.2.1", 74 | "typescript": "^4.4.2" 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /protocols/external/agl-screenshooter.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Copyright © 2020 Collabora. Ltd, 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a 8 | copy of this software and associated documentation files (the "Software"), 9 | to deal in the Software without restriction, including without limitation 10 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 11 | and/or sell copies of the Software, and to permit persons to whom the 12 | Software is furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice (including the next 15 | paragraph) shall be included in all copies or substantial portions of the 16 | Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 19 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 20 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 21 | THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 22 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 23 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 24 | DEALINGS IN THE SOFTWARE. 25 | 26 | 27 | 28 | 29 | agl compositor extension that performs a screenshot of the output, which 30 | is represented by a 'wl_output' object. 31 | 32 | A client would call 'take_shot' request and wait until the compositor 33 | finishes to write the data to a wayland buffer, moment in which signals 34 | back the client with the help of the 'done' event. Clients should wait 35 | until the 'done' event is received, if they want to take another 36 | screenshot, or take another screnshot of a different output. 37 | 38 | The client must provide a wl_shm-based wl_buffer of the correct size when 39 | taking a shot. The compositor will write the shot into the wl_buffer and then 40 | send the 'done' event that signals completion of writing the data. 41 | 42 | Once the compositor has finished to transfer the data back into the supplied 43 | wayland buffer, the client should be able to transfer it to a popular 44 | file format on the disk. 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | Takes a screenshot of the wayland output represented by a 'wl_output' 56 | object. Clients should first retrieve it using global registry, as well 57 | as the 'wl_shm' object in order to create a wayland buffer type of 58 | object ('wl_buffer'). 59 | 60 | Clients can derive the stride and size from the 'wl_output' object, and 61 | later on use those when creating shm-based 'wl_buffer', as well as supplying 62 | the pixel format. 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | Even sent back to notify client 'take_shot' request has completed. 72 | 73 | 74 | 75 | 76 | 77 | 78 | Destroys the 'weston_screenshooter' interface. 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /protocols/external/gtk-shell.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | gtk_shell is a protocol extension providing additional features for 6 | clients implementing it. 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | -------------------------------------------------------------------------------- /public/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vially/wayland-explorer/40253a7719f14f401329f526fbce8d46f55a482b/public/android-chrome-192x192.png -------------------------------------------------------------------------------- /public/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vially/wayland-explorer/40253a7719f14f401329f526fbce8d46f55a482b/public/android-chrome-512x512.png -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vially/wayland-explorer/40253a7719f14f401329f526fbce8d46f55a482b/public/apple-touch-icon.png -------------------------------------------------------------------------------- /public/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | #da532c 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /public/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vially/wayland-explorer/40253a7719f14f401329f526fbce8d46f55a482b/public/favicon-16x16.png -------------------------------------------------------------------------------- /public/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vially/wayland-explorer/40253a7719f14f401329f526fbce8d46f55a482b/public/favicon-32x32.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vially/wayland-explorer/40253a7719f14f401329f526fbce8d46f55a482b/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 13 | 14 | 18 | 19 | 28 | 29 | Wayland Explorer 30 | 31 | 32 | 44 |
45 | 55 | 56 | -------------------------------------------------------------------------------- /public/logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 17 | 19 | 43 | 45 | 46 | 48 | image/svg+xml 49 | 51 | 52 | 53 | 54 | 55 | 60 | 66 | 70 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /public/logos/Steam_Deck.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /public/logos/cosmic.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 57 | -------------------------------------------------------------------------------- /public/logos/deepin.svg: -------------------------------------------------------------------------------- 1 | 2 | 13 | 15 | 33 | 37 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /public/logos/gnome.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /public/logos/hyprland.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /public/logos/labwc.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 39 | 45 | 50 | 51 | -------------------------------------------------------------------------------- /public/logos/louvre.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 27 | -------------------------------------------------------------------------------- /public/logos/mir.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/logos/sway.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | 10 | 13 | 26 | 28 | 30 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /public/logos/wayfire.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.16, written by Peter Selinger 2001-2019 9 | 10 | 12 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Wayland Explorer", 3 | "name": "Wayland Protocol Explorer", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "android-chrome-192x192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "android-chrome-512x512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/mstile-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vially/wayland-explorer/40253a7719f14f401329f526fbce8d46f55a482b/public/mstile-144x144.png -------------------------------------------------------------------------------- /public/mstile-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vially/wayland-explorer/40253a7719f14f401329f526fbce8d46f55a482b/public/mstile-150x150.png -------------------------------------------------------------------------------- /public/mstile-310x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vially/wayland-explorer/40253a7719f14f401329f526fbce8d46f55a482b/public/mstile-310x150.png -------------------------------------------------------------------------------- /public/mstile-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vially/wayland-explorer/40253a7719f14f401329f526fbce8d46f55a482b/public/mstile-310x310.png -------------------------------------------------------------------------------- /public/mstile-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vially/wayland-explorer/40253a7719f14f401329f526fbce8d46f55a482b/public/mstile-70x70.png -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /public/safari-pinned-tab.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 7 | 8 | Created by potrace 1.11, written by Peter Selinger 2001-2013 9 | 10 | 12 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /scripts/bin/print-protocol-registry-items.ts: -------------------------------------------------------------------------------- 1 | import path from 'path' 2 | import { findXMLFiles } from '../lib/utils' 3 | 4 | const relativeProtocolDirs = [ 5 | path.join('protocols', 'wayland', 'stable'), 6 | path.join('protocols', 'wayland', 'unstable'), 7 | path.join('protocols', 'wlr', 'unstable'), 8 | ] 9 | 10 | const protocolItemFor = 11 | (source: string, stability: string) => 12 | (protocolXmlFile: string): string => { 13 | const protocolId = path.basename(protocolXmlFile, '.xml') 14 | return ` 15 | { 16 | id: '${protocolId}', 17 | name: '', 18 | source: WaylandProtocolSource.${source}, 19 | stability: WaylandProtocolStability.${stability}, 20 | protocol: require('./protocols/${protocolId}.json'), 21 | },` 22 | } 23 | 24 | async function main() { 25 | const protocolDirs = relativeProtocolDirs.map((waylandDir) => 26 | path.resolve(__dirname, '../../', waylandDir) 27 | ) 28 | 29 | const [waylandStableFiles, waylandUnstableFiles, wlrUnstableFiles] = 30 | await Promise.all(protocolDirs.map(findXMLFiles)) 31 | 32 | const protocolItems = [ 33 | protocolItemFor('WaylandCore', 'Stable')('wayland.xml'), 34 | ...waylandStableFiles.map( 35 | protocolItemFor('WaylandProtocols', 'Stable') 36 | ), 37 | ...waylandUnstableFiles.map( 38 | protocolItemFor('WaylandProtocols', 'Unstable') 39 | ), 40 | ...wlrUnstableFiles.map(protocolItemFor('WlrProtocols', 'Unstable')), 41 | ].join('') 42 | 43 | console.log( 44 | `const protocols: WaylandProtocolRegistryItem[] = [${protocolItems}\n]` 45 | ) 46 | } 47 | 48 | main() 49 | -------------------------------------------------------------------------------- /scripts/bin/render-static-html.tsx: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs/promises' 2 | import { minify } from 'html-minifier' 3 | import * as path from 'path' 4 | import { renderToString } from 'react-dom/server' 5 | import { Router } from 'wouter' 6 | import staticLocationHook from 'wouter/static-location' 7 | import App from '../../src/App' 8 | import { waylandProtocolRegistry } from '../../src/data/protocol-registry' 9 | 10 | const buildDir = path.resolve(__dirname, '../../build/protocols') 11 | 12 | interface StaticPageDescriptor { 13 | routerPath: string 14 | fileName: string 15 | pageTitle: string 16 | } 17 | 18 | async function main() { 19 | const indexHTMLPlaceholder = await readIndexHTML() 20 | 21 | const renderPage = async (page: StaticPageDescriptor) => { 22 | await renderAndWriteHTML(indexHTMLPlaceholder, page) 23 | console.log(`✔ ${page.routerPath} rendered`) 24 | } 25 | 26 | await Promise.all(allPageDescriptors().map(renderPage)) 27 | } 28 | 29 | function allPageDescriptors(): StaticPageDescriptor[] { 30 | const staticPages: StaticPageDescriptor[] = [ 31 | { 32 | routerPath: '/', 33 | fileName: 'index.html', 34 | pageTitle: 'Wayland Protocol Documentation | Wayland Explorer', 35 | }, 36 | { 37 | routerPath: '/404', 38 | fileName: '404.html', 39 | pageTitle: '404 - Page not found', 40 | }, 41 | { 42 | routerPath: '/wayland-protocols/0', 43 | fileName: 'wayland-protocols-gitlab.html', 44 | pageTitle: 'Git', 45 | }, 46 | ] 47 | 48 | const protocolPages: StaticPageDescriptor[] = 49 | waylandProtocolRegistry.protocols.map((protocol) => ({ 50 | routerPath: `/${protocol.id}`, 51 | fileName: `${protocol.id}.html`, 52 | pageTitle: `${protocol.name} protocol | Wayland Explorer`, 53 | })) 54 | 55 | return [...staticPages, ...protocolPages] 56 | } 57 | 58 | async function renderAndWriteHTML( 59 | indexHTML: string, 60 | { routerPath, fileName, pageTitle }: StaticPageDescriptor 61 | ): Promise { 62 | const contentHTML = renderToString( 63 | 64 | 65 | 66 | ) 67 | 68 | const html = injectContentIntoHTML(contentHTML, pageTitle, indexHTML) 69 | await fs.writeFile(path.join(buildDir, fileName), html) 70 | } 71 | 72 | async function readIndexHTML(): Promise { 73 | const htmlFilePath = path.join(buildDir, 'index.html') 74 | return fs.readFile(htmlFilePath, { encoding: 'utf-8' }) 75 | } 76 | 77 | function injectContentIntoHTML( 78 | contentHTML: string, 79 | pageTitle: string, 80 | indexHTMLPlaceholder: string 81 | ): string { 82 | const minifiedContentHTML = minify(contentHTML, { 83 | collapseWhitespace: true, 84 | }) 85 | const contentPlaceholder = '
' 86 | const contentReplacement = `
${minifiedContentHTML}
` 87 | 88 | const titlePlaceholder = 'Wayland Explorer' 89 | const titleReplacement = `${pageTitle}` 90 | 91 | return indexHTMLPlaceholder 92 | .replace(titlePlaceholder, titleReplacement) 93 | .replace(contentPlaceholder, contentReplacement) 94 | } 95 | 96 | main() 97 | -------------------------------------------------------------------------------- /scripts/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { existsSync, promises as fs } from 'fs' 2 | import path from 'path' 3 | 4 | export function coerceArray(value: T | T[]): T[] { 5 | if (value === null || value === undefined) return [] 6 | 7 | return Array.isArray(value) ? value : [value] 8 | } 9 | 10 | export async function findXMLFiles(rootDirectory: string): Promise { 11 | if (!existsSync(rootDirectory)) { 12 | return [] 13 | } 14 | 15 | const files = await fs.readdir(rootDirectory) 16 | 17 | const xmlFiles = await Promise.all( 18 | files.map(async (baseName): Promise => { 19 | const fileName = path.join(rootDirectory, baseName) 20 | const stat = await fs.lstat(fileName) 21 | if (stat.isDirectory()) { 22 | return findXMLFiles(fileName) 23 | } else if (fileName.endsWith('.xml')) { 24 | return [fileName] 25 | } 26 | return [] 27 | }) 28 | ) 29 | 30 | return xmlFiles.flat() 31 | } 32 | 33 | export function protocolIdFor(srcFileName: string): string { 34 | let baseName = path.basename(srcFileName, '.xml') 35 | 36 | // Strip `org-` prefix from some KDE protocol file names (e.g.: 37 | // `org-kde-plasma-virtual-desktop`) 38 | if ( 39 | path.dirname(srcFileName).endsWith('/kde/src/protocols') && 40 | baseName.startsWith('org-kde-') 41 | ) { 42 | baseName = baseName.substring('org-'.length) 43 | } 44 | 45 | // Add `kde-` prefix to KDE protocol file names to avoid potential future name clashes 46 | const prefix = 47 | path.dirname(srcFileName).endsWith('/kde/src/protocols') && 48 | !baseName.startsWith('kde-') 49 | ? 'kde-' 50 | : '' 51 | 52 | return `${prefix}${baseName}` 53 | } 54 | -------------------------------------------------------------------------------- /src/App.test.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { render, screen } from '@testing-library/react' 3 | import App from './App' 4 | 5 | test('renders learn react link', () => { 6 | render() 7 | const linkElement = screen.getByText(/learn react/i) 8 | expect(linkElement).toBeInTheDocument() 9 | }) 10 | -------------------------------------------------------------------------------- /src/App.tsx: -------------------------------------------------------------------------------- 1 | import { useRoute } from 'wouter' 2 | import { useAnalytics } from './analytics/plausible' 3 | import { MultiColumnLayout } from './components/layout/MultiColumnLayout' 4 | import { WaylandProtocolOutline } from './components/outline/WaylandProtocolOutline' 5 | import { WaylandProtocol } from './components/WaylandProtocol' 6 | import { waylandProtocolRegistry } from './data/protocol-registry' 7 | import { NotFound } from './pages/404' 8 | import { Homepage } from './pages/Homepage' 9 | import { GitLab, GitLabMrList } from './pages/GitLab' 10 | 11 | function App() { 12 | let contentView = 13 | let outlineView = null 14 | 15 | const [isGitlabMrList] = useRoute<{ iid: string }>('/wayland-protocols') 16 | const [isGitlab, gitlabParams] = useRoute<{ iid: string }>( 17 | '/wayland-protocols/:iid' 18 | ) 19 | 20 | const [match, params] = useRoute<{ protocolId: string }>('/:protocolId') 21 | const isHomepage = !match && !isGitlab 22 | 23 | useAnalytics().trackPageview() 24 | 25 | if (isGitlabMrList) { 26 | return 27 | } else if (isGitlab && gitlabParams?.iid) { 28 | return 29 | } else if (match && params?.protocolId) { 30 | const protocolWithMetadata = match 31 | ? waylandProtocolRegistry.getProtocolWithMetadata(params.protocolId) 32 | : null 33 | 34 | contentView = protocolWithMetadata ? ( 35 | 39 | ) : ( 40 | 41 | ) 42 | 43 | outlineView = protocolWithMetadata ? ( 44 | 45 | ) : null 46 | } 47 | 48 | return ( 49 | 50 | {contentView} 51 | 52 | ) 53 | } 54 | 55 | export default App 56 | -------------------------------------------------------------------------------- /src/analytics/plausible.ts: -------------------------------------------------------------------------------- 1 | import Plausible from 'plausible-tracker' 2 | import { appConfig } from '../config' 3 | 4 | export interface AnalyticsTracker { 5 | trackPageview: () => void 6 | } 7 | 8 | const noopTracker: AnalyticsTracker = { trackPageview: () => {} } 9 | 10 | let currentTracker: AnalyticsTracker = noopTracker 11 | 12 | export function setupAnalytics() { 13 | const enabled = 14 | process.env.NODE_ENV === 'production' && 15 | !localStorage.getItem('plausible_ignore') 16 | 17 | currentTracker = enabled 18 | ? Plausible({ apiHost: appConfig.analyticsDomain }) 19 | : noopTracker 20 | } 21 | 22 | export function useAnalytics(): AnalyticsTracker { 23 | return currentTracker 24 | } 25 | -------------------------------------------------------------------------------- /src/components/WaylandArg.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | WaylandArgModel, 3 | WaylandEnumModel, 4 | WaylandEventModel, 5 | WaylandRequestModel, 6 | } from './common' 7 | import { buildHashPathFor } from './common/utils' 8 | import { WaylandColorTheme as colors } from './common/wayland-protocol-icons' 9 | 10 | export const WaylandArg: React.FC<{ 11 | element: WaylandArgModel 12 | interfaceName: string 13 | parentElement: WaylandRequestModel | WaylandEventModel | WaylandEnumModel 14 | }> = ({ element, interfaceName, parentElement }) => ( 15 | 16 | 23 | {element.name} 24 | 25 | : {element.argType} 26 | {element.interface && ( 27 | 28 | {'<'} 29 | 33 | {element.interface} 34 | 35 | {'>'} 36 | 37 | )} 38 | {element.enum && ( 39 | 40 | {'<'} 41 | 46 | {'>'} 47 | 48 | )} 49 | 50 | ) 51 | 52 | const ArgEnum: React.FC<{ 53 | interfaceName: string 54 | argEnum: string 55 | protocol?: string 56 | }> = ({ argEnum, interfaceName, protocol }) => { 57 | let enumName = argEnum 58 | if (enumName.includes('.')) { 59 | ;[interfaceName, enumName] = enumName.split('.') 60 | } 61 | 62 | return ( 63 | 64 | {interfaceName}. 65 | {enumName} 66 | 67 | ) 68 | } 69 | -------------------------------------------------------------------------------- /src/components/WaylandCopyright.tsx: -------------------------------------------------------------------------------- 1 | import { WaylandCopyrightModel, WaylandElementProps } from './common' 2 | 3 | export const WaylandCopyright: React.FC< 4 | WaylandElementProps 5 | > = ({ element }) => ( 6 |
7 |

8 | 9 | 10 | Copyright 11 | 12 |

13 | {element.text?.split('\n\n').map((text, index) => ( 14 |

15 | {text} 16 |

17 | ))} 18 |
19 | ) 20 | -------------------------------------------------------------------------------- /src/components/WaylandDescription.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { WaylandDescriptionModel, WaylandElementProps } from './common' 3 | 4 | export const WaylandDescription: React.FC< 5 | WaylandElementProps 6 | > = ({ element }) => ( 7 |
8 | {element.summary && ( 9 |
10 | {element.summary} 11 |
12 | )} 13 | 14 | {element.text && ( 15 |
16 | {splitText(element.text)} 17 |
18 | )} 19 |
20 | ) 21 | 22 | function splitText(description: string) { 23 | const out = [] 24 | let listItems = null 25 | let matches = description.split(/((?:^|\n+)(?:[*-] )|\n\n)/); 26 | let paragraphGap = undefined 27 | let isListItem = false 28 | for (let i = 0; i < matches.length; i++) { 29 | let chunk = matches[i] 30 | if (i % 2) { 31 | paragraphGap = chunk.startsWith('\n\n') ? 'mt-3' : undefined 32 | isListItem = chunk.endsWith(' ') 33 | } else { 34 | if (isListItem) { 35 | listItems ??= [] 36 | listItems.push(
  • {chunk}
  • ) 37 | } else { 38 | if (listItems !== null) { 39 | out.push(
      {listItems}
    ) 40 | listItems = null 41 | } 42 | out.push(

    {chunk}

    ) 43 | } 44 | } 45 | } 46 | return out 47 | } 48 | -------------------------------------------------------------------------------- /src/components/WaylandEntry.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | WaylandElementProps, 3 | WaylandEntryModel, 4 | WaylandEnumModel, 5 | } from './common' 6 | 7 | export const WaylandEntry: React.FC< 8 | WaylandElementProps & { 9 | interfaceName: string 10 | enumElement: WaylandEnumModel 11 | } 12 | > = ({ element, enumElement, interfaceName }) => ( 13 | 18 | {element.name} 19 | 20 | ) 21 | -------------------------------------------------------------------------------- /src/components/WaylandEnum.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { WaylandDataTable } from './WaylandDataTable' 3 | import { WaylandDescription } from './WaylandDescription' 4 | import { WaylandElementProps, WaylandEnumModel } from './common' 5 | import { WaylandColorTheme as colors } from './common/wayland-protocol-icons' 6 | import { Badge } from './content/Badge' 7 | import { WaylandElementSignature } from './content/WaylandElementSignature' 8 | 9 | export const WaylandEnum: React.FC< 10 | WaylandElementProps & { interfaceName: string } 11 | > = ({ element, interfaceName }) => ( 12 |
    13 |
    14 | 20 | 21 | 22 | 23 | {interfaceName}:: 24 | 25 | {element.name} 26 | 27 | 28 | {(element.bitfield || element.since) && ( 29 |
    30 | {element.bitfield && ( 31 | 35 | bitfield 36 | 37 | )} 38 | {element.since && since {element.since}} 39 |
    40 | )} 41 |
    42 | 43 | 47 | 48 | {!!element.entries?.length && ( 49 | 54 | )} 55 | 56 | {element.description && ( 57 | 58 | )} 59 |
    60 | ) 61 | -------------------------------------------------------------------------------- /src/components/WaylandEvent.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { WaylandDataTable } from './WaylandDataTable' 3 | import { WaylandDescription } from './WaylandDescription' 4 | import { WaylandElementProps, WaylandEventModel } from './common' 5 | import { WaylandColorTheme as colors } from './common/wayland-protocol-icons' 6 | import { Badge } from './content/Badge' 7 | import { WaylandElementSignature } from './content/WaylandElementSignature' 8 | 9 | export const WaylandEvent: React.FC< 10 | WaylandElementProps & { interfaceName: string } 11 | > = ({ element, interfaceName }) => ( 12 |
    13 |
    14 | 20 | 21 | 22 | 23 | {interfaceName}:: 24 | 25 | {element.name} 26 | 27 | 28 | {(element.eventType || element.since) && ( 29 |
    30 | {element.deprecatedSince && ( 31 | 36 | Deprecated since {element.deprecatedSince} 37 | 38 | )} 39 | {element.eventType && ( 40 | 41 | Type: {element.eventType} 42 | 43 | )} 44 | {element.since && since {element.since}} 45 |
    46 | )} 47 |
    48 | 49 | 53 | 54 | {!!element.args?.length && ( 55 | 60 | )} 61 | 62 | {element.description && ( 63 | 64 | )} 65 |
    66 | ) 67 | -------------------------------------------------------------------------------- /src/components/WaylandInterface.tsx: -------------------------------------------------------------------------------- 1 | import { WaylandDeprecationItem } from '../model/wayland-protocol-metadata' 2 | import { WaylandElementProps, WaylandInterfaceModel } from './common' 3 | import { WaylandColorTheme as colors } from './common/wayland-protocol-icons' 4 | import { Badge } from './content/Badge' 5 | import { WaylandDescription } from './WaylandDescription' 6 | import { WaylandEnum } from './WaylandEnum' 7 | import { WaylandEvent } from './WaylandEvent' 8 | import { WaylandRequest } from './WaylandRequest' 9 | 10 | export const WaylandInterface: React.FC< 11 | WaylandElementProps & { 12 | deprecated?: WaylandDeprecationItem 13 | } 14 | > = ({ element, deprecated }) => ( 15 |
    16 |
    17 |

    21 | 26 | 27 | {element.name} 28 | 29 |

    30 | 31 |
    32 | {deprecated ? ( 33 | 39 | Deprecated 40 | 41 | ) : ( 42 | <> 43 | )} 44 | version {element.version} 45 |
    46 |
    47 | 48 | {element.description ? ( 49 |
    50 | 51 |
    52 | ) : ( 53 |
    54 | )} 55 | 56 |
    57 | {element.requests?.map((request, index) => ( 58 | 63 | ))} 64 | {element.events?.map((event, index) => ( 65 | 70 | ))} 71 | {element.enums?.map((childElement, index) => ( 72 | 77 | ))} 78 |
    79 |
    80 | ) 81 | -------------------------------------------------------------------------------- /src/components/WaylandProtocol.tsx: -------------------------------------------------------------------------------- 1 | import { WaylandProtocolMetadata } from '../model/wayland-protocol-metadata' 2 | import { WaylandBreadcrumbs } from './breadcrumbs/Breadcrumbs' 3 | import { WaylandProtocolModel } from './common' 4 | import { usePageTitle } from './common/hooks-utils' 5 | import { WaylandCopyright } from './WaylandCopyright' 6 | import { WaylandDescription } from './WaylandDescription' 7 | import { WaylandInterface } from './WaylandInterface' 8 | import { WaylandCompositors } from './WaylandCompositors' 9 | 10 | export const WaylandProtocol: React.FC<{ 11 | element: WaylandProtocolModel 12 | metadata: WaylandProtocolMetadata 13 | }> = ({ element, metadata }) => { 14 | usePageTitle(`${metadata.name} protocol`) 15 | 16 | const get_deprecation = (name: string) => { 17 | const deprecated = metadata.deprecated ? metadata.deprecated : [] 18 | return deprecated.find((item) => item.name === name) 19 | } 20 | 21 | return ( 22 |
    23 |
    24 |
    25 |

    26 | {metadata.name} 27 |

    28 |
    29 | 30 | 31 | 32 | {element.description && ( 33 |
    34 | 35 |
    36 | )} 37 |
    38 | {element.interfaces.map((childElement, index) => ( 39 |
    40 | 44 |
    45 |
    46 | ))} 47 | 48 | {element.copyright && ( 49 | 50 | )} 51 |
    52 | ) 53 | } 54 | -------------------------------------------------------------------------------- /src/components/WaylandRequest.tsx: -------------------------------------------------------------------------------- 1 | import { WaylandElementProps, WaylandRequestModel } from './common' 2 | import { WaylandColorTheme as colors } from './common/wayland-protocol-icons' 3 | import { Badge } from './content/Badge' 4 | import { WaylandElementSignature } from './content/WaylandElementSignature' 5 | import { WaylandDataTable } from './WaylandDataTable' 6 | import { WaylandDescription } from './WaylandDescription' 7 | 8 | export const WaylandRequest: React.FC< 9 | WaylandElementProps & { interfaceName: string } 10 | > = ({ element, interfaceName }) => ( 11 |
    12 |
    13 | 19 | 20 | 21 | 22 | {interfaceName}:: 23 | 24 | {element.name} 25 | 26 | 27 | {(element.requestType || element.since) && ( 28 |
    29 | {element.deprecatedSince && ( 30 | 35 | Deprecated since {element.deprecatedSince} 36 | 37 | )} 38 | {element.requestType && ( 39 | 40 | Type: {element.requestType} 41 | 42 | )} 43 | {element.since && since {element.since}} 44 |
    45 | )} 46 |
    47 | 48 | 52 | 53 | {!!element.args?.length && ( 54 | 59 | )} 60 | 61 | {element.description && ( 62 | 63 | )} 64 |
    65 | ) 66 | -------------------------------------------------------------------------------- /src/components/breadcrumbs/Breadcrumbs.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | kdeProtocolsWithHardcodedPrefix, 4 | urlForWaylandProtocol, 5 | urlForWaylandProtocolSource, 6 | urlForWaylandProtocolStability, 7 | } from '../../model/protocol-source-link-builder' 8 | import { 9 | WaylandProtocolMetadata, 10 | WaylandProtocolSource, 11 | } from '../../model/wayland-protocol-metadata' 12 | 13 | const SolidChevronRight: React.FC<{ className: string }> = ({ className }) => { 14 | // Heroicon name: solid/chevron-right 15 | return ( 16 | 29 | ) 30 | } 31 | 32 | export const BreadcrumbSection: React.FC<{ 33 | children?: React.ReactNode 34 | href?: string 35 | }> = ({ children, href }) => 36 | href ? ( 37 | 43 | {children} 44 | 45 | ) : ( 46 | {children} 47 | ) 48 | 49 | export const WaylandBreadcrumbs: React.FC<{ 50 | metadata: WaylandProtocolMetadata 51 | }> = ({ metadata }) => { 52 | const xmlFileBaseName = 53 | metadata.source === WaylandProtocolSource.KDEProtocols && 54 | !kdeProtocolsWithHardcodedPrefix.includes(metadata.id) 55 | ? metadata.id.substring(4) 56 | : metadata.id 57 | 58 | const source = 59 | metadata.source === WaylandProtocolSource.External ? ( 60 | external 61 | ) : ( 62 | 65 | {metadata.source} 66 | 67 | ) 68 | 69 | const stability = metadata.source !== WaylandProtocolSource.WaylandCore && 70 | metadata.source !== WaylandProtocolSource.External && ( 71 | 72 | {metadata.stability} 73 | 74 | ) 75 | 76 | return ( 77 | 78 | {source} 79 | {stability} 80 | 81 | {xmlFileBaseName}.xml 82 | 83 | 84 | ) 85 | } 86 | 87 | export const Breadcrumbs: React.FC<{ children?: React.ReactNode }> = ({ 88 | children, 89 | }) => { 90 | return ( 91 | 109 | ) 110 | } 111 | -------------------------------------------------------------------------------- /src/components/common/hooks-utils.ts: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | 3 | const addBodyClass = (className: string) => 4 | document.body.classList.add(className) 5 | 6 | const removeBodyClass = (className: string) => 7 | document.body.classList.remove(className) 8 | 9 | export function useBodyClass(className: string) { 10 | useEffect(() => { 11 | // Set up 12 | addBodyClass(className) 13 | 14 | // Clean up 15 | return () => removeBodyClass(className) 16 | }, [className]) 17 | } 18 | 19 | export function usePageTitle(title: string) { 20 | useEffect(() => { 21 | document.title = `${title} | Wayland Explorer` 22 | }, [title]) 23 | } 24 | -------------------------------------------------------------------------------- /src/components/common/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WaylandArg, 3 | WaylandCopyright, 4 | WaylandDescription, 5 | WaylandElement, 6 | WaylandEntry, 7 | WaylandEnum, 8 | WaylandEvent, 9 | WaylandInterface, 10 | WaylandProtocol, 11 | WaylandRequest, 12 | } from '../../model/wayland' 13 | 14 | export interface WaylandElementProps { 15 | element: T 16 | } 17 | 18 | // Aliases to avoid collisions with the component names 19 | export type WaylandProtocolModel = WaylandProtocol 20 | export type WaylandCopyrightModel = WaylandCopyright 21 | export type WaylandDescriptionModel = WaylandDescription 22 | export type WaylandInterfaceModel = WaylandInterface 23 | export type WaylandRequestModel = WaylandRequest 24 | export type WaylandEventModel = WaylandEvent 25 | export type WaylandEnumModel = WaylandEnum 26 | export type WaylandEntryModel = WaylandEntry 27 | export type WaylandArgModel = WaylandArg 28 | -------------------------------------------------------------------------------- /src/components/common/utils.ts: -------------------------------------------------------------------------------- 1 | import { 2 | WaylandArg, 3 | WaylandEntry, 4 | WaylandEnum, 5 | WaylandEvent, 6 | WaylandRequest, 7 | } from '../../model/wayland' 8 | 9 | export function buildHashPathFor( 10 | interfaceName: string, 11 | parentElement: WaylandEvent | WaylandRequest | WaylandEnum, 12 | arg: WaylandArg | WaylandEntry, 13 | options?: { includeHashSymbol: boolean } 14 | ): string { 15 | return `${options?.includeHashSymbol ? '#' : ''}${interfaceName}:${ 16 | parentElement.type 17 | }:${parentElement.name}:${arg.type}:${arg.name}` 18 | } 19 | -------------------------------------------------------------------------------- /src/components/common/wayland-protocol-icons.ts: -------------------------------------------------------------------------------- 1 | import { WaylandElementType } from '../../model/wayland' 2 | 3 | export interface WaylandElementUIConfig { 4 | icon: string 5 | color: string 6 | } 7 | 8 | export enum WaylandColorTheme { 9 | Interface = 'text-blue-500', 10 | Request = 'text-pink-500', 11 | Event = 'text-emerald-500', 12 | Enum = 'text-orange-500', 13 | Entry = 'text-orange-50', 14 | } 15 | 16 | const waylandIcons: Record = { 17 | [WaylandElementType.Protocol]: { icon: '', color: '' }, 18 | [WaylandElementType.Interface]: { 19 | icon: 'interface', 20 | color: WaylandColorTheme.Interface, 21 | }, 22 | [WaylandElementType.Request]: { 23 | icon: 'method', 24 | color: WaylandColorTheme.Request, 25 | }, 26 | [WaylandElementType.Event]: { 27 | icon: 'event', 28 | color: WaylandColorTheme.Event, 29 | }, 30 | [WaylandElementType.Enum]: { icon: 'enum', color: WaylandColorTheme.Enum }, 31 | [WaylandElementType.Entry]: { icon: '', color: WaylandColorTheme.Entry }, 32 | [WaylandElementType.Arg]: { icon: '', color: '' }, 33 | [WaylandElementType.Copyright]: { icon: '', color: '' }, 34 | [WaylandElementType.Description]: { icon: '', color: '' }, 35 | } 36 | 37 | export const waylandElementConfigFor = ( 38 | elementType: WaylandElementType 39 | ): WaylandElementUIConfig => waylandIcons[elementType] 40 | -------------------------------------------------------------------------------- /src/components/content/Badge.tsx: -------------------------------------------------------------------------------- 1 | export const Badge: React.FC<{ 2 | bgColor?: string 3 | textColor?: string 4 | fontWeigth?: string 5 | title?: string 6 | children?: React.ReactNode 7 | }> = ({ children, bgColor, textColor, fontWeigth, title }) => ( 8 | 14 | {children} 15 | 16 | ) 17 | -------------------------------------------------------------------------------- /src/components/content/ProtocolBadge.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { 3 | WaylandProtocolMetadata, 4 | WaylandProtocolSource, 5 | WaylandProtocolStability, 6 | } from '../../model/wayland-protocol-metadata' 7 | import { Badge } from './Badge' 8 | 9 | interface BadgeTheme { 10 | textColor: string 11 | backgroundColor: string 12 | } 13 | 14 | const coreBadgeTheme: BadgeTheme = { 15 | textColor: 'text-green-800', 16 | backgroundColor: 'bg-green-100', 17 | } 18 | 19 | const waylandProtocolsStableBadgeTheme: BadgeTheme = { 20 | textColor: 'text-blue-800', 21 | backgroundColor: 'bg-blue-100', 22 | } 23 | 24 | const waylandProtocolsUnstableBadgeTheme: BadgeTheme = { 25 | textColor: 'text-pink-800', 26 | backgroundColor: 'bg-pink-100', 27 | } 28 | 29 | const wlrProtocolsUnstableBadgeTheme: BadgeTheme = { 30 | textColor: 'text-red-800', 31 | backgroundColor: 'bg-red-100', 32 | } 33 | 34 | const kdeProtocolsUnstableBadgeTheme: BadgeTheme = { 35 | textColor: 'text-purple-800', 36 | backgroundColor: 'bg-purple-100', 37 | } 38 | 39 | const hyprlandProtocolsUnstableBadgeTheme: BadgeTheme = { 40 | textColor: 'text-sky-800', 41 | backgroundColor: 'bg-sky-100', 42 | } 43 | 44 | const cosmicProtocolsUnstableBadgeTheme: BadgeTheme = { 45 | textColor: 'text-amber-800', 46 | backgroundColor: 'bg-red-100', 47 | } 48 | 49 | const westonProtocolsUnstableBadgeTheme: BadgeTheme = { 50 | textColor: 'text-yellow-800', 51 | backgroundColor: 'bg-yellow-100', 52 | } 53 | 54 | const treelandProtocolsUnstableBadgeTheme: BadgeTheme = { 55 | textColor: 'text-cyan-800', 56 | backgroundColor: 'bg-cyan-100', 57 | } 58 | 59 | const externalProtocolsBadgeTheme: BadgeTheme = { 60 | textColor: 'text-gray-800', 61 | backgroundColor: 'bg-gray-100', 62 | } 63 | 64 | function badgeThemeFor( 65 | source: WaylandProtocolSource, 66 | stability: WaylandProtocolStability 67 | ): BadgeTheme { 68 | if (source === WaylandProtocolSource.WaylandCore) { 69 | return coreBadgeTheme 70 | } else if (source === WaylandProtocolSource.WaylandProtocols) { 71 | return stability === WaylandProtocolStability.Stable 72 | ? waylandProtocolsStableBadgeTheme 73 | : waylandProtocolsUnstableBadgeTheme 74 | } else if (source === WaylandProtocolSource.WlrProtocols) { 75 | return wlrProtocolsUnstableBadgeTheme 76 | } else if (source === WaylandProtocolSource.KDEProtocols) { 77 | return kdeProtocolsUnstableBadgeTheme 78 | } else if (source === WaylandProtocolSource.HyprlandProtocols) { 79 | return hyprlandProtocolsUnstableBadgeTheme 80 | } else if (source === WaylandProtocolSource.CosmicProtocols) { 81 | return cosmicProtocolsUnstableBadgeTheme 82 | } else if (source === WaylandProtocolSource.WestonProtocols) { 83 | return westonProtocolsUnstableBadgeTheme 84 | } else if (source === WaylandProtocolSource.TreelandProtocols) { 85 | return treelandProtocolsUnstableBadgeTheme 86 | } else { 87 | return externalProtocolsBadgeTheme 88 | } 89 | } 90 | 91 | export const ProtocolBadge: React.FC<{ protocol: WaylandProtocolMetadata }> = ({ 92 | protocol, 93 | }) => { 94 | const theme = badgeThemeFor(protocol.source, protocol.stability) 95 | return ( 96 | 97 | {protocol.source === WaylandProtocolSource.WaylandCore 98 | ? 'core' 99 | : protocol.source === WaylandProtocolSource.WlrProtocols 100 | ? 'wlr' 101 | : protocol.source === WaylandProtocolSource.KDEProtocols 102 | ? 'kde' 103 | : protocol.source === WaylandProtocolSource.HyprlandProtocols 104 | ? 'hyprland' 105 | : protocol.source === WaylandProtocolSource.CosmicProtocols 106 | ? 'cosmic' 107 | : protocol.source === WaylandProtocolSource.WestonProtocols 108 | ? 'weston' 109 | : protocol.source === WaylandProtocolSource.TreelandProtocols 110 | ? 'treeland' 111 | : protocol.source === WaylandProtocolSource.External 112 | ? 'external' 113 | : protocol.stability} 114 | 115 | ) 116 | } 117 | -------------------------------------------------------------------------------- /src/components/content/WaylandElementSignature.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { isWaylandEnumElement, WaylandElementType } from '../../model/wayland' 3 | import { 4 | WaylandEnumModel, 5 | WaylandEventModel, 6 | WaylandRequestModel, 7 | } from '../common' 8 | import { waylandElementConfigFor } from '../common/wayland-protocol-icons' 9 | import { WaylandArg } from '../WaylandArg' 10 | import { WaylandEntry } from '../WaylandEntry' 11 | 12 | export const WaylandElementSignature: React.FC<{ 13 | element: WaylandRequestModel | WaylandEventModel | WaylandEnumModel 14 | interfaceName: string 15 | }> = ({ element, interfaceName }) => ( 16 |
    17 |
    18 |             {element.name}
    19 |             {isWaylandEnumElement(element) ? (
    20 |                 
    21 |                     {' { '}
    22 |                     {element.entries?.map((entry, index) => (
    23 |                         
    24 |                             
    29 |                             {index !== element.entries!.length - 1 && ', '}
    30 |                         
    31 |                     ))}
    32 |                     {' } '}
    33 |                 
    34 |             ) : (
    35 |                 
    36 |                     (
    37 |                     {element.args.map((childElement, index) => (
    38 |                         
    39 |                             
    44 |                             {index !== element.args!.length - 1 && ', '}
    45 |                         
    46 |                     ))}
    47 |                     )
    48 |                 
    49 |             )}
    50 |         
    51 |
    52 | ) 53 | 54 | const colorFor = (elementType: WaylandElementType): string => 55 | elementType === WaylandElementType.Event 56 | ? 'text-emerald-300' 57 | : waylandElementConfigFor(elementType).color 58 | -------------------------------------------------------------------------------- /src/components/layout/Footer.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Link } from 'wouter' 3 | 4 | export const Footer: React.FC = () => ( 5 |
    6 |

    7 | Footer 8 |

    9 |
    10 |
    11 | 33 |
    34 |

    35 | © 2025 Wayland Explorer 36 |

    37 |

    38 | This website is not affiliated in any way with the 39 | official Wayland project. All content on this website is 40 | automatically generated from the Wayland protocol XML 41 | files. 42 |

    43 |

    44 | The{' '} 45 | 50 | Visual Studio Code - Codicons 51 | {' '} 52 | used on this website are licensed under{' '} 53 | 58 | CC BY 4.0 59 | 60 | . 61 |

    62 |
    63 |
    64 |
    65 |
    66 | ) 67 | -------------------------------------------------------------------------------- /src/components/layout/Header.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import { Logo } from './Logo' 3 | 4 | export const Header: React.FC<{ 5 | showOutlineButton: boolean 6 | setIsSidebarOpen: (open: boolean) => void 7 | setIsOutlineOpen: (open: boolean) => void 8 | }> = ({ setIsSidebarOpen, setIsOutlineOpen, showOutlineButton }) => ( 9 |
    10 |
    11 | 34 |
    35 | 36 |
    37 | 38 | {showOutlineButton && ( 39 | 62 | )} 63 |
    64 |
    65 | ) 66 | -------------------------------------------------------------------------------- /src/components/layout/Logo.tsx: -------------------------------------------------------------------------------- 1 | import { Link } from 'wouter' 2 | 3 | export const Logo: React.FC = () => ( 4 | 5 | 6 | Logo 7 | 11 | Wayland Explorer 12 | 13 | 14 | 15 | ) 16 | -------------------------------------------------------------------------------- /src/components/layout/MultiColumnLayout.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react' 2 | import { WaylandProtocolLinks } from '../sidebar-navigation/WaylandProtocolLinks' 3 | import { Footer } from './Footer' 4 | import { Header } from './Header' 5 | import { Logo } from './Logo' 6 | import { OutlineOverlay } from './overlays/OutlineOverlay' 7 | import { SidebarOverlay } from './overlays/SidebarOverlay' 8 | import { ScrollToTop } from './ScrollToTop' 9 | 10 | export const MultiColumnLayout: React.FC<{ 11 | hideSidebar?: boolean 12 | outlineView?: React.ReactNode 13 | children?: React.ReactNode 14 | }> = ({ outlineView, hideSidebar, children }) => { 15 | const [isSidebarOpen, setIsSidebarOpen] = useState(false) 16 | const [isOutlineOpen, setIsOutlineOpen] = useState(false) 17 | 18 | return ( 19 |
    20 | 21 | 22 | 23 | 24 | {outlineView} 25 | 26 | 27 |
    32 | 33 |
    38 |
    43 | 54 |
    55 |
    64 | {children} 65 |
    66 |
    67 | {outlineView && ( 68 | 76 | )} 77 |
    78 | 79 | 80 |
    81 | ) 82 | } 83 | -------------------------------------------------------------------------------- /src/components/layout/ScrollToTop.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react' 2 | import { useLocation } from 'wouter' 3 | 4 | export const ScrollToTop = () => { 5 | const [pathname] = useLocation() 6 | 7 | useEffect(() => { 8 | if (window.location.hash === '') { 9 | window.scrollTo(0, 0) 10 | } 11 | }, [pathname]) 12 | 13 | return null 14 | } 15 | -------------------------------------------------------------------------------- /src/components/layout/overlays/OutlineOverlay.tsx: -------------------------------------------------------------------------------- 1 | import { Transition } from '@headlessui/react' 2 | import React from 'react' 3 | import { OverlayBackground } from './OverlayBackground' 4 | 5 | export const OutlineOverlay: React.FC<{ 6 | open: boolean 7 | setIsOpen: (open: boolean) => void 8 | children?: React.ReactNode 9 | }> = ({ open, setIsOpen, children }) => ( 10 | 11 |
    17 |
    18 | 19 | 20 |
    21 | 30 | 39 | 61 | 62 |
    63 |
    64 |

    68 | Outline 69 |

    70 |
    71 |
    setIsOpen(false)} 74 | > 75 | {children} 76 |
    77 |
    78 |
    79 |
    80 |
    81 |
    82 |
    83 | ) 84 | -------------------------------------------------------------------------------- /src/components/layout/overlays/OverlayBackground.tsx: -------------------------------------------------------------------------------- 1 | import { Transition } from '@headlessui/react' 2 | import { useBodyClass } from '../../common/hooks-utils' 3 | 4 | export function OverlayBackground() { 5 | useBodyClass('overflow-hidden') 6 | 7 | return ( 8 |