@haruhimemoe/ui 0.11.0 → 0.11.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.11.2] - 2026-10-04
|
|
10
|
+
|
|
11
|
+
### Fixed
|
|
12
|
+
|
|
13
|
+
- `CopyMarkdownButton` copies on Safari and iOS: where `ClipboardItem` exists, the clipboard write starts inside the click with the fetched Markdown as a promised Blob, instead of after an awaited fetch that loses the user activation. Other browsers keep the fetch-then-`writeText` path.
|
|
14
|
+
|
|
15
|
+
## [0.11.1] - 2026-10-04
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- BrandPage asset previews use an empty alt, so axe no longer reports image-redundant-alt next to the download link.
|
|
20
|
+
|
|
9
21
|
## [0.11.0] - 2026-10-04
|
|
10
22
|
|
|
11
23
|
### Added
|
|
@@ -153,7 +165,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
|
153
165
|
- `className` on every component, and the extras passed to `buttonClasses` and `fieldClasses`, merge with tailwind-merge: a caller's class replaces a built-in one that sets the same property (`fieldClasses("w-auto")` drops `w-full`).
|
|
154
166
|
- Shell: `SiteHeader` (brand slot, nav links as data with `aria-current`, actions slot), `NavLinks`, `SiteFooter` (link columns as data, fine print, the haruhime.moe wordmark and a GitHub link) and `PageShell` (skip link, header, main, footer).
|
|
155
167
|
|
|
156
|
-
[unreleased]: https://github.com/haruhimemoe/ui/compare/v0.11.
|
|
168
|
+
[unreleased]: https://github.com/haruhimemoe/ui/compare/v0.11.2...HEAD
|
|
169
|
+
[0.11.2]: https://github.com/haruhimemoe/ui/compare/v0.11.1...v0.11.2
|
|
170
|
+
[0.11.1]: https://github.com/haruhimemoe/ui/compare/v0.11.0...v0.11.1
|
|
157
171
|
[0.11.0]: https://github.com/haruhimemoe/ui/compare/v0.10.0...v0.11.0
|
|
158
172
|
[0.10.0]: https://github.com/haruhimemoe/ui/compare/v0.9.0...v0.10.0
|
|
159
173
|
[0.9.0]: https://github.com/haruhimemoe/ui/compare/v0.8.0...v0.9.0
|
|
@@ -13,5 +13,5 @@ const IMAGE = /\.(svg|png|jpe?g|webp)$/i;
|
|
|
13
13
|
* @returns {JSX.Element} the brand page's sections, in order
|
|
14
14
|
*/
|
|
15
15
|
export function BrandPage({ name, tagline, url, writing, dos, donts, palette, assets, contact, familyHref, fonts = DEFAULT_FONTS, slots, }) {
|
|
16
|
-
return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs(Card, { title: "Name", children: [_jsx("p", { className: "font-bold text-c1 text-xl", children: name }), _jsx("p", { children: tagline }), _jsx("p", { children: writing }), _jsx("p", { children: _jsx("a", { className: LINK, href: url, children: url }) })] }), _jsx(Card, { title: "Logo", children: _jsx("ul", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: assets.map((asset) => (_jsxs("li", { className: "flex flex-col gap-2", children: [IMAGE.test(asset.href) ? (_jsx("div", { className: `flex h-32 items-center justify-center rounded-md p-4 ${asset.dark ? "bg-b6" : "bg-c1"}`, children: _jsx("img", { src: asset.href, alt:
|
|
16
|
+
return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs(Card, { title: "Name", children: [_jsx("p", { className: "font-bold text-c1 text-xl", children: name }), _jsx("p", { children: tagline }), _jsx("p", { children: writing }), _jsx("p", { children: _jsx("a", { className: LINK, href: url, children: url }) })] }), _jsx(Card, { title: "Logo", children: _jsx("ul", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: assets.map((asset) => (_jsxs("li", { className: "flex flex-col gap-2", children: [IMAGE.test(asset.href) ? (_jsx("div", { className: `flex h-32 items-center justify-center rounded-md p-4 ${asset.dark ? "bg-b6" : "bg-c1"}`, children: _jsx("img", { src: asset.href, alt: "", className: "max-h-full max-w-full" }) })) : null, _jsx("a", { className: LINK, href: asset.href, download: true, children: asset.label })] }, asset.href))) }) }), slots?.afterLogo, _jsx(Card, { title: "Colors", children: _jsx("ul", { className: "grid grid-cols-2 gap-3 sm:grid-cols-3", children: Object.entries(palette).map(([token, hex]) => (_jsx("li", { children: _jsx(BrandSwatch, { token: token, hex: hex }) }, token))) }) }), slots?.afterColors, _jsx(Card, { title: "Type", children: _jsx("ul", { className: "flex flex-col gap-1", children: fonts.map((font) => (_jsxs("li", { children: [_jsx("span", { className: "font-bold text-c1", children: font.name }), ": ", font.usage] }, font.name))) }) }), _jsx(Card, { title: "Do's and don'ts", children: _jsxs("div", { className: "grid grid-cols-1 gap-4 sm:grid-cols-2", children: [_jsxs("div", { children: [_jsx("h3", { className: "mb-1 font-bold text-c1", children: "Do" }), _jsx("ul", { className: "list-disc pl-5", children: dos.map((item) => (_jsx("li", { children: item }, item))) })] }), _jsxs("div", { children: [_jsx("h3", { className: "mb-1 font-bold text-c1", children: "Don't" }), _jsx("ul", { className: "list-disc pl-5", children: donts.map((item) => (_jsx("li", { children: item }, item))) })] })] }) }), _jsx(Card, { title: "osu!", children: _jsx("p", { children: "Not affiliated with osu! or ppy. osu! is a trademark of ppy Pty Ltd." }) }), familyHref ? (_jsx(Card, { title: "Family", children: _jsx("a", { className: LINK, href: familyHref, children: "Part of the haruhime.moe family." }) })) : null, _jsx(Card, { title: "Contact", children: _jsx("a", { className: LINK, href: `mailto:${contact}`, children: contact }) }), slots?.end] }));
|
|
17
17
|
}
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* @file src/components/content/CopyMarkdownButton.tsx
|
|
3
3
|
* @desc A button that fetches a page's raw Markdown and copies it to the clipboard, reporting the
|
|
4
4
|
* result in an `<output>` beside it (a polite live region), for a doc page's "Copy as
|
|
5
|
-
* Markdown" action.
|
|
5
|
+
* Markdown" action. Where `ClipboardItem` exists, the clipboard write starts inside the click
|
|
6
|
+
* with the fetch as a promised Blob, since Safari rejects a write that starts after an
|
|
7
|
+
* awaited fetch; elsewhere it fetches, then calls `writeText`. Any failure (the fetch rejects, the response isn't ok, or the clipboard
|
|
6
8
|
* refuses) announces the same failure message; the button never throws. Its own client file
|
|
7
9
|
* with finished class strings (no `cx`), so tailwind-merge stays out of the browser bundle
|
|
8
10
|
* for `ContentPage`, the Server Component that renders it. Not a wrapper around
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
* @file src/components/content/CopyMarkdownButton.tsx
|
|
3
3
|
* @desc A button that fetches a page's raw Markdown and copies it to the clipboard, reporting the
|
|
4
4
|
* result in an `<output>` beside it (a polite live region), for a doc page's "Copy as
|
|
5
|
-
* Markdown" action.
|
|
5
|
+
* Markdown" action. Where `ClipboardItem` exists, the clipboard write starts inside the click
|
|
6
|
+
* with the fetch as a promised Blob, since Safari rejects a write that starts after an
|
|
7
|
+
* awaited fetch; elsewhere it fetches, then calls `writeText`. Any failure (the fetch rejects, the response isn't ok, or the clipboard
|
|
6
8
|
* refuses) announces the same failure message; the button never throws. Its own client file
|
|
7
9
|
* with finished class strings (no `cx`), so tailwind-merge stays out of the browser bundle
|
|
8
10
|
* for `ContentPage`, the Server Component that renders it. Not a wrapper around
|
|
@@ -28,11 +30,23 @@ export function CopyMarkdownButton({ href, label = "Copy as Markdown", copiedLab
|
|
|
28
30
|
const copy = async () => {
|
|
29
31
|
const run = start();
|
|
30
32
|
try {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
33
|
+
if (typeof ClipboardItem !== "undefined" && navigator.clipboard?.write) {
|
|
34
|
+
// Started synchronously in the click so Safari keeps the user activation.
|
|
35
|
+
const blob = fetch(href).then(async (response) => {
|
|
36
|
+
if (!response.ok)
|
|
37
|
+
throw new Error(String(response.status));
|
|
38
|
+
return new Blob([await response.text()], { type: "text/plain" });
|
|
39
|
+
});
|
|
40
|
+
// If the write is refused before reading the blob, its own rejection stays handled.
|
|
41
|
+
blob.catch(() => undefined);
|
|
42
|
+
await navigator.clipboard.write([new ClipboardItem({ "text/plain": blob })]);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
const response = await fetch(href);
|
|
46
|
+
if (!response.ok)
|
|
47
|
+
throw new Error(String(response.status));
|
|
48
|
+
await navigator.clipboard.writeText(await response.text());
|
|
49
|
+
}
|
|
36
50
|
settle(run, "copied");
|
|
37
51
|
}
|
|
38
52
|
catch {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haruhimemoe/ui",
|
|
3
|
-
"version": "0.11.
|
|
3
|
+
"version": "0.11.2",
|
|
4
4
|
"description": "React components for the haruhime.moe osu! tools on Next.js: the osu!-web-style palette as a Tailwind theme, buttons, cards, form fields and confirms, filter controls, tables, osu! beatmap display pieces and player cards, a command palette, content section tooling, brand pages and the site header and footer.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"osu",
|