Skip to content

优化管理前端 || Optimize management front-end#3603

Open
zjhcx wants to merge 12 commits into
walinejs:mainfrom
zjhcx:admin-ui
Open

优化管理前端 || Optimize management front-end#3603
zjhcx wants to merge 12 commits into
walinejs:mainfrom
zjhcx:admin-ui

Conversation

@zjhcx
Copy link
Copy Markdown

@zjhcx zjhcx commented May 20, 2026

概述

美化 Waline 的管理前端

详情

重构了管理后端的顶栏。移除了硬编码的 /ui 前缀,引入 getUiPath 工具函数来动态计算基础路径,使管理前端更稳定。将原先的分散代理合并为统一的 /api 代理,切换样式引入方式(从 .scss 改为原生的 .css)。

其他

  • 修复了 akismetmathjax 服务中的部分插件加载与函数命名规范问题。

截图

截屏2026-05-19 21 56 41 截屏2026-05-19 21 55 48

Overview

Beautify Waline’s admin front-end

Details

Refactored the top bar of the admin backend. The hard-coded /ui prefix is ​​removed, and the getUiPath utility function is introduced to dynamically calculate the base path, making the management front-end more stable. Merge the original distributed agents into a unified /api agent, and switch the style import method (from .scss to native .css).

Others

  • Fixed some plug-in loading and function naming convention issues in akismet and mathjax services.

Screenshot

Screenshot 2026-05-19 21 56 41 Screenshot 2026-05-19 21 55 48

@github-actions github-actions Bot changed the title 优化管理前端 优化管理前端 || Optimize management front-end May 20, 2026
Comment thread error.log Outdated
Comment thread packages/server/src/service/avatar.js Outdated
// oxlint-disable-next-line func-names
module.exports = function () {
return (ctx) => {
const uiBasePath = getUiBasePath(ctx.path);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

想知道这部分的修改是为什么?是想要创建后台的本地开发环境吗?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个是为了开发的时候预览方便

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

开发的时候应该直接 pnpm run admin:dev 启动就可以了,不用这个这么麻烦吧?
这里面比较多的代码写的比较 for devlopment 不太适合放到 production 逻辑里来。

Comment thread packages/admin/src/pages/user/index.jsx Outdated
})}
>
{React.createElement(Icons[social])}
<img
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

需要将 Icon SVG 通过 React Component 的形式引入,因为 hover 的时候有一个颜色的变化,这个通过 img 引入是没办法做到的。

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

变色问题已解决

Comment thread data/waline.sqlite Outdated
Comment thread packages/admin/src/utils/ui.js Outdated
@@ -0,0 +1,7 @@
export const uiBase = /\/ui(?:\/|$)/u.test(location.pathname) ? '/ui' : '';

export const getUiPath = (path = '') => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个解决的是什么问题?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

实现管理前端开发时可以不加/ui前缀

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

get,不过我看你是不是移除了这个逻辑了已经?看最新的 commit 里面没有这个方法了。

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR focuses on improving Waline’s admin UI appearance and robustness by refactoring the admin header styling/layout, making /ui path handling less hard-coded, and adjusting how admin assets (JS/CSS/icons) and avatars are resolved.

Changes:

  • Refactor admin header UI and switch admin styles from SCSS entry to a CSS import bundle.
  • Introduce utilities for /ui path generation and social icon URL resolution; replace SVG-as-React-component icons with <img> assets.
  • Update server dashboard middleware to optionally serve a local admin bundle and adjust default avatar URL generation.

Reviewed changes

Copilot reviewed 14 out of 24 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
packages/server/src/service/avatar.js Switch default avatar URL template to a different Gravatar mirror with query params.
packages/server/src/middleware/dashboard.js Add local admin bundle detection/serving and dynamic /ui base path extraction.
packages/admin/src/utils/ui.js Add getUiPath helper to avoid hard-coded /ui in navigation.
packages/admin/src/utils/socialIcon.js Add helper to map OAuth provider names to local SVG asset URLs.
packages/admin/src/style/style.css Adjust nav hover/focus selectors and remove a border rule.
packages/admin/src/style/index.css New CSS entry file to import normalize/base/grid/custom styles.
packages/admin/src/style/custom.css New large custom stylesheet for updated admin UI theme and layout.
packages/admin/src/pages/user/index.jsx Replace icon components with social icon <img> rendering.
packages/admin/src/pages/profile/index.jsx Use buildAvatar for profile avatar and replace icon components with <img>.
packages/admin/src/pages/manage-comments/utils.js Update buildAvatar behavior to avoid using SVG data URLs and align default mirror URL.
packages/admin/src/pages/login/index.jsx Use captcha hook import and replace icon components with social icon <img> rendering.
packages/admin/src/index.jsx Switch style entry to CSS and add body/container classes for new styling.
packages/admin/src/img/weibo.svg Add SVG asset for Weibo icon.
packages/admin/src/img/twitter.svg Add SVG asset for Twitter icon.
packages/admin/src/img/qq.svg Add SVG asset for QQ icon.
packages/admin/src/img/oidc.svg Add SVG asset for OIDC icon.
packages/admin/src/img/huawei.svg Add SVG asset for Huawei icon.
packages/admin/src/img/google.svg Add SVG asset for Google icon.
packages/admin/src/img/github.svg Add SVG asset for GitHub icon.
packages/admin/src/img/facebook.svg Add SVG asset for Facebook icon.
packages/admin/src/components/icon/index.js Remove old SVGR-based icon exports.
packages/admin/src/components/Header.jsx Refactor header markup, use getUiPath, and render user avatar with buildAvatar.
error.log Adds a local runtime log file (should not be committed).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/server/src/middleware/dashboard.js Outdated
Comment on lines +839 to +848
.upgrade-tips {
padding: 0 10px;
border-bottom: 1px solid rgb(212 167 44 / 40%);

background: #fff8c5;
color: #24292f;

line-height: 36px;
}

Comment thread packages/admin/src/utils/socialIcon.js Outdated
case 'huawei':
return new URL('../img/huawei.svg', import.meta.url).href;
default:
return '';
Comment thread packages/admin/src/style/custom.css Outdated
background: var(--wa-surface-soft);
}

@media (width <= 960px) {
@zjhcx zjhcx requested a review from lizheming May 21, 2026 09:34
Comment thread packages/admin/src/utils/socialIcon.jsx Outdated
/>
));

export const getSocialIcon = (social) => {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感觉你这么改之后,和我原来 components/icon/index.js 里的逻辑就是一致的了,没什么改造的必要了?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

改之前的代码在我的电脑上开发时浏览器会把图标当作JS加载,好像打包后不会

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

感觉你这么改之后,和我原来 components/icon/index.js 里的逻辑就是一致的了,没什么改造的必要了?

我现在改成了components/icon/index.jsx,和以前的逻辑差不多,但开发时SVG不会被作为JS加载

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嗯嗯,好的,之前引入的方式会有什么问题么,我比较倾向于使用 import 的形式直接将 svg 文件加载成 React 组件。

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嗯嗯,好的,之前引入的方式会有什么问题么,我比较倾向于使用 import 的形式直接将 svg 文件加载成 React 组件。

我改成直接在components/icon/index.jsx中定义常量了

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

嗯嗯,好的,之前引入的方式会有什么问题么,我比较倾向于使用 import 的形式直接将 svg 文件加载成 React 组件。

import的话开发时会被当作JS加载,改成定义常量可以避免这个问题

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

常量不也是 JS 吗?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

常量不也是 JS 吗?

现在在开发时浏览器请求的components/icon/index.jsx如下

const React = __vite__cjsImport0_react;
const _jsxDEV = __vite__cjsImport1_react_jsxDevRuntime["jsxDEV"];
const _Fragment = __vite__cjsImport1_react_jsxDevRuntime["Fragment"];
import __vite__cjsImport0_react from "/node_modules/.vite/deps/react.js?v=34ec03ef";
var _jsxFileName = "/Users/zhangjiahui/waline/packages/admin/src/components/icon/index.jsx";
import __vite__cjsImport1_react_jsxDevRuntime from "/node_modules/.vite/deps/react_jsx-dev-runtime.js?v=34ec03ef";
const createSvg = (viewBox, children) => function SocialIcon(props) {
    return /* @__PURE__ */
    _jsxDEV("svg", {
        viewBox,
        xmlns: "http://www.w3.org/2000/svg",
        ...props,
        children
    }, void 0, false, {
        fileName: _jsxFileName,
        lineNumber: 6,
        columnNumber: 7
    }, this);
}
;
export const github = createSvg("0 0 1024 1024", /* @__PURE__ */
_jsxDEV("path", {
    fill: "currentColor",
    d: "M512 0C229.12 0 0 229.12 0 512c0 226.56 146.56 417.92 350.08 485.76 25.6 4.48 35.2-10.88 35.2-24.32 0-12.16-.64-52.48-.64-95.36-128.64 23.68-161.92-31.36-172.16-60.16-5.76-14.72-30.72-60.16-52.48-72.32-17.92-9.6-43.52-33.28-.64-33.92 40.32-.64 69.12 37.12 78.72 52.48 46.08 77.44 119.68 55.68 149.12 42.24 4.48-33.28 17.92-55.68 32.64-68.48-113.92-12.8-232.96-56.96-232.96-252.8 0-55.68 19.84-101.76 52.48-137.6-5.12-12.8-23.04-65.28 5.12-135.68 0 0 42.88-13.44 140.8 52.48 40.96-11.52 84.48-17.28 128-17.28 43.52 0 87.04 5.76 128 17.28 97.92-66.56 140.8-52.48 140.8-52.48 28.16 70.4 10.24 122.88 5.12 135.68 32.64 35.84 52.48 81.28 52.48 137.6 0 196.48-119.68 240-233.6 252.8 18.56 16 34.56 46.72 34.56 94.72 0 68.48-.64 123.52-.64 140.8 0 13.44 9.6 29.44 35.2 24.32C877.44 929.92 1024 737.92 1024 512 1024 229.12 794.88 0 512 0z"
}, void 0, false, {
    fileName: _jsxFileName,
    lineNumber: 13,
    columnNumber: 3
}, this));
export const twitter = createSvg("126.444 2.281 589 589", /* @__PURE__ */
_jsxDEV(_Fragment, {
    children: [/* @__PURE__ */
    _jsxDEV("circle", {
        cx: "420.944",
        cy: "296.781",
        r: "294.5",
        fill: "currentColor"
    }, void 0, false, {
        fileName: _jsxFileName,
        lineNumber: 21,
        columnNumber: 5
    }, this), /* @__PURE__ */
    _jsxDEV("path", {
        fill: "#fff",
        d: "M609.773 179.634c-13.891 6.164-28.811 10.331-44.498 12.204 16.01-9.587 28.275-24.779 34.066-42.86a154.78 154.78 0 0 1-49.209 18.801c-14.125-15.056-34.267-24.456-56.551-24.456-42.773 0-77.462 34.675-77.462 77.473 0 6.064.683 11.98 1.996 17.66-64.389-3.236-121.474-34.079-159.684-80.945-6.672 11.446-10.491 24.754-10.491 38.953 0 26.875 13.679 50.587 34.464 64.477a77.122 77.122 0 0 1-35.097-9.686v.979c0 37.54 26.701 68.842 62.145 75.961-6.511 1.784-13.344 2.716-20.413 2.716-4.998 0-9.847-.473-14.584-1.364 9.859 30.769 38.471 53.166 72.363 53.799-26.515 20.785-59.925 33.175-96.212 33.175-6.25 0-12.427-.373-18.491-1.104 34.291 21.988 75.006 34.824 118.759 34.824 142.496 0 220.428-118.052 220.428-220.428 0-3.361-.074-6.697-.236-10.021a157.855 157.855 0 0 0 38.707-40.158z"
    }, void 0, false, {
        fileName: _jsxFileName,
        lineNumber: 22,
        columnNumber: 5
    }, this)]
}, void 0, true, {
    fileName: _jsxFileName,
    lineNumber: 20,
    columnNumber: 3
}, this));
export const facebook = createSvg("0 0 32 32", /* @__PURE__ */
_jsxDEV("path", {
    fill: "currentColor",
    d: "M32 16C32 7.16344 24.8366 0 16 0C7.16344 0 0 7.16344 0 16C0 23.9861 5.85096 30.6053 13.5 31.8056V20.625H9.43751V16H13.5V12.475C13.5 8.465 15.8887 6.25001 19.5434 6.25001C21.294 6.25001 23.125 6.5625 23.125 6.5625V10.5H21.1074C19.1198 10.5 18.5 11.7334 18.5 12.9987V16H22.9375L22.2281 20.625H18.5V31.8056C26.149 30.6053 32 23.9861 32 16"
}, void 0, false, {
    fileName: _jsxFileName,
    lineNumber: 30,
    columnNumber: 3
}, this));
export const weibo = createSvg("0 0 24 24", /* @__PURE__ */
_jsxDEV(_Fragment, {
    children: [/* @__PURE__ */
    _jsxDEV("circle", {
        cx: "12",
        cy: "12",
        r: "12",
        fill: "currentColor"
    }, void 0, false, {
        fileName: _jsxFileName,
        lineNumber: 38,
        columnNumber: 5
    }, this), /* @__PURE__ */
    _jsxDEV("path", {
        fill: "#fff",
        d: "M19.3945 9.4904c.0639.8284-.0762 1.821-.6232 1.8696-.8928.0798-.4271-.9037-.4154-1.4957.0327-1.7159-1.4321-2.9081-2.8669-2.9081-.4065 0-1.3506.2755-1.2052-.5817.0654-.378.3856-.3727.7062-.4154 2.4131-.3218 4.235 1.3382 4.4045 3.5313Zm-3.947 1.9944c1.0374.5676 2.2838.8502 2.0771 2.5346-.0495.4036-.2938.9429-.5399 1.2873-1.7536 2.4548-7.0427 3.4866-10.3456 1.6208-1.1077-.6261-2.254-1.5417-2.0771-3.3659.1522-1.5699 1.2078-2.786 2.2437-3.8219.9882-.9888 2.0303-1.7624 3.4483-2.1189 1.5384-.3868 1.9932.8958 1.5787 2.1604.8911-.0598 2.7795-1.0545 3.6147-.083.3683.4288.2273 1.1954 0 1.7873Zm-1.1219 3.9883c.3324-.3768.6665-.9511.6644-1.62-.0044-2.0648-2.6055-2.8275-4.6529-2.6591-1.1198.0918-1.8731.3282-2.6591.7895-.6414.3765-1.3921.9891-1.5787 1.9108-.4201 2.0727 1.8343 3.0468 3.4898 3.1575 1.9123.1285 3.8269-.5468 4.7359-1.5787Zm3.0327-5.9827c.0871.6326-.0939 1.1807-.4569 1.2049-.6052.0403-.3789-.4145-.4157-.9555-.023-.3332-.2991-.718-.5402-.831-.4728-.2214-1.2049.1613-1.2049-.4984 0-.4898.4248-.4389.665-.4572 1.0518-.0798 1.8296.6453 1.9527 1.5372ZM12.4976 13.6453c.8852 2.6753-3.7268 3.9193-4.5702 1.6202-.5643-1.5375.7907-2.7604 2.2019-2.9081 1.2049-.1261 2.1023.4883 2.3683 1.2879Zm-2.119 1.87c.1878.325.6197.0916.5817-.166-.0443-.2987-.5848-.2545-.5817.166Zm-.748 1.4128c.7754-.1846.8796-1.645-.2908-1.3712-1.084.2296-.8939 1.6287.2908 1.3712Z"
    }, void 0, false, {
        fileName: _jsxFileName,
        lineNumber: 39,
        columnNumber: 5
    }, this)]
}, void 0, true, {
    fileName: _jsxFileName,
    lineNumber: 37,
    columnNumber: 3
}, this));
export const qq = createSvg("0 0 1024 1024", /* @__PURE__ */
_jsxDEV("path", {
    fill: "currentColor",
    d: "M512 1024C794.77 1024 1024 794.77 1024 512C1024 229.23 794.77 0 512 0C229.23 0 0 229.23 0 512C0 794.77 229.23 1024 512 1024ZM718.383 488.193C734.622 528.473 746.254 557.323 756.601 590.562C783.163 676.031 774.57 711.422 767.929 712.281C753.867 714 713.163 647.906 713.163 647.906C713.163 686.109 693.476 736.031 650.82 772.125C671.367 778.453 717.773 795.562 706.757 814.156C697.851 829.234 553.476 823.766 511.835 819.078C470.195 823.766 325.82 829.234 316.913 814.156C305.898 795.484 352.226 778.453 372.851 772.125C330.195 736.109 310.507 686.188 310.507 647.906C310.507 647.906 269.804 714 255.742 712.281C249.179 711.5 240.585 676.109 267.148 590.562C277.556 556.997 289.209 528.143 305.627 487.496C308.926 479.326 312.419 470.679 316.132 461.422C312.382 318.688 371.367 199 511.835 199C650.742 199 711.054 316.344 707.617 461.422C711.425 470.936 715.003 479.812 718.383 488.193Z"
}, void 0, false, {
    fileName: _jsxFileName,
    lineNumber: 47,
    columnNumber: 3
}, this));
export const oidc = createSvg("0 0 48 48", /* @__PURE__ */
_jsxDEV("path", {
    fill: "currentColor",
    d: "M24 0C10.75 0 0 10.75 0 24s10.75 24 24 24 24-10.75 24-24S37.25 0 24 0Zm6.05 24.76 2.87-1.74c-1.21-.76-2.64-1.28-4.23-1.66-.3-.09-.6-.16-.91-.22v14.96l-4.53 2.27v-2.95h0c-8.69-.76-14.36-4.99-14.36-10.13s5.52-9.14 12.85-10.05c0 0 .62-.08 1.51-.14v-6.13l4.53-2.27v8.47c.56.07.91.14.91.14 2.87.45 5.44 1.36 7.48 2.64l2.19-1.44.76 6.8-9.07-1.51Z"
}, void 0, false, {
    fileName: _jsxFileName,
    lineNumber: 54,
    columnNumber: 3
}, this));
export const google = createSvg("0 0 48 48", /* @__PURE__ */
_jsxDEV("path", {
    fill: "currentColor",
    d: "M24 9.5c3.54 0 6.71 1.22 9.21 3.6l6.85-6.85C35.9 2.38 30.47 0 24 0 14.62 0 6.51 5.38 2.56 13.22l7.98 6.19C12.43 13.72 17.74 9.5 24 9.5Zm22.98 15.05c0-1.57-.15-3.09-.38-4.55H24v9.02h12.94c-.58 2.96-2.26 5.48-4.78 7.18l7.73 6c4.51-4.18 7.09-10.36 7.09-17.65ZM10.53 28.59c-.48-1.45-.76-2.99-.76-4.59s.27-3.14.76-4.59l-7.98-6.19C.92 16.46 0 20.12 0 24c0 3.88.92 7.54 2.56 10.78l7.97-6.19ZM24 48c6.48 0 11.93-2.13 15.89-5.81l-7.73-6c-2.15 1.45-4.92 2.3-8.16 2.3-6.26 0-11.57-4.22-13.47-9.91l-7.98 6.19C6.51 42.62 14.62 48 24 48Z"
}, void 0, false, {
    fileName: _jsxFileName,
    lineNumber: 61,
    columnNumber: 3
}, this));
export const huawei = createSvg("0 0 1024 1024", /* @__PURE__ */
_jsxDEV("path", {
    fill: "currentColor",
    d: "M844.8 608c-25.6 51.2-70.4 64-70.4 64-19.2 6.4-38.4 6.4-38.4 6.4H576c89.6-64 268.8-160 268.8-160 19.2 51.2 0 89.6 0 89.6M691.2 768c-32-6.4-89.6-51.2-108.8-64l211.2 6.4C742.4 787.2 691.2 768 691.2 768m-128-108.8q-9.6-9.6 0 0c76.8-179.2 192-313.6 192-313.6s57.6 57.6 57.6 115.2c0 44.8-32 70.4-32 70.4-64 44.8-185.6 108.8-217.6 128m-32-19.2c-32-307.2 32-384 32-384 19.2 0 32 6.4 32 6.4 57.6 12.8 70.4 64 70.4 64 12.8 25.6 0 64 0 64C646.4 473.6 556.8 608 531.2 640c6.4 0 0 0 0 0m-38.4 0c-25.6-32-115.2-166.4-134.4-249.6 0 0-12.8-32 0-64 0 0 12.8-51.2 70.4-64 0 0 25.6-6.4 32-6.4 0 0 64 76.8 32 384m-25.6 12.8s0 6.4 0 0q-9.6 9.6 0 0c-32-12.8-160-76.8-211.2-128 0 0-32-25.6-32-70.4-12.8-51.2 51.2-108.8 51.2-108.8S384 480 467.2 652.8M448 678.4s0 6.4 0 0H288s-19.2 0-38.4-6.4c0 0-44.8-12.8-64-64 0 0-19.2-38.4-6.4-89.6-6.4 0 172.8 96 268.8 160m-6.4 25.6c-19.2 12.8-83.2 57.6-108.8 64-38.4 12.8-76.8-19.2-102.4-64z"
}, void 0, false, {
    fileName: _jsxFileName,
    lineNumber: 68,
    columnNumber: 3
}, this));

sourceMappingURL略,现在会变成直接通过函数计算得出常量值,之前会import SVG文件

Comment thread .gitignore Outdated
.npmrc

data/*
*.sqlite No newline at end of file
Copy link
Copy Markdown
Collaborator

@lizheming lizheming May 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

咱应该不太能直接 ignore *.sqlite,因为 waline 在 assets/ 下提供了个默认的 sqlite 文件需要被 git 管理起来,建议就先忽略 data/* ?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

好,我改一下

@zjhcx zjhcx requested a review from lizheming May 21, 2026 12:21
@lizheming
Copy link
Copy Markdown
Collaborator

lizheming commented May 23, 2026

不介意的话我今天再迭代下这个 PR 帮助它进行合入。


If you don’t mind, I will iterate on this PR today to help it get merged.

@zjhcx
Copy link
Copy Markdown
Author

zjhcx commented May 23, 2026

不介意的话我今天再迭代下这个 PR 帮助它进行合入。

If you don’t mind, I will iterate on this PR today to help it get merged.

好的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants