put the code copy button into the markdown processor

This commit is contained in:
Oleksandr 2025-11-22 09:00:00 +02:00
parent 7f75bba052
commit b9accda035
Signed by: Xanazf
GPG key ID: 821EEC32761AC17C
20 changed files with 176 additions and 211 deletions

2
.pnp.cjs generated
View file

@ -50,6 +50,7 @@ const RAW_RUNTIME_STATE =
["astro-breadcrumbs", "virtual:a9b1222052dffa20c83605ac26b64fd717aa2982dc89da74b78301a8333c50a120c12db3f68c240302341b52215e986347cefceb71633b5918936083bd9430ce#npm:3.3.1"],\
["astro-icon", "npm:1.1.5"],\
["hast-util-from-html", "npm:2.0.3"],\
["hastscript", "npm:9.0.1"],\
["jsonc-parser", "npm:3.3.1"],\
["pagefind", "npm:1.4.0"],\
["quickshell-docs", "workspace:."],\
@ -6380,6 +6381,7 @@ const RAW_RUNTIME_STATE =
["astro-breadcrumbs", "virtual:a9b1222052dffa20c83605ac26b64fd717aa2982dc89da74b78301a8333c50a120c12db3f68c240302341b52215e986347cefceb71633b5918936083bd9430ce#npm:3.3.1"],\
["astro-icon", "npm:1.1.5"],\
["hast-util-from-html", "npm:2.0.3"],\
["hastscript", "npm:9.0.1"],\
["jsonc-parser", "npm:3.3.1"],\
["pagefind", "npm:1.4.0"],\
["quickshell-docs", "workspace:."],\

View file

@ -23,6 +23,7 @@
"astro-breadcrumbs": "^3.3.1",
"astro-icon": "^1.1.5",
"hast-util-from-html": "^2.0.3",
"hastscript": "^9.0.1",
"rehype": "^13.0.2",
"remark-github-blockquote-alert": "^2.0.0",
"solid-js": "^1.9.10",

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -3,51 +3,34 @@
---
<script>
setTimeout(() => {
// code copy
let blocks = document.querySelectorAll("pre");
if (blocks.length > 0) {
blocks.forEach((block) => {
let button = document.createElement("button");
button.className = "copy-button";
button.innerHTML = `<svg
xmlns="http://www.w3.org/2000/svg"
width="1em"
height="1em"
viewBox="0 0 256 256"
>
<path
fill="currentColor"
d="M200 32h-36.26a47.92 47.92 0 0 0-71.48 0H56a16 16 0 0 0-16 16v168a16 16 0 0 0 16 16h144a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16m-72 0a32 32 0 0 1 32 32H96a32 32 0 0 1 32-32m72 184H56V48h26.75A47.9 47.9 0 0 0 80 64v8a8 8 0 0 0 8 8h80a8 8 0 0 0 8-8v-8a47.9 47.9 0 0 0-2.75-16H200Z"
/>
</svg>
`;
button.onclick = () => {
let snippet = block.innerText ?? "";
navigator.clipboard.writeText(snippet);
button.classList.toggle("copied");
setTimeout(() => button.classList.remove("copied"), 1000);
};
block.appendChild(button);
});
}
}, 3001)
// setTimeout(() => {
// // code copy
// let blocks = document.querySelectorAll("pre");
// if (blocks.length > 0) {
// blocks.forEach((block) => {
// let button = document.createElement("button");
// button.className = "copy-button";
// button.innerHTML = `<svg
// xmlns="http://www.w3.org/2000/svg"
// width="1em"
// height="1em"
// viewBox="0 0 256 256"
// >
// <path
// fill="currentColor"
// d="M200 32h-36.26a47.92 47.92 0 0 0-71.48 0H56a16 16 0 0 0-16 16v168a16 16 0 0 0 16 16h144a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16m-72 0a32 32 0 0 1 32 32H96a32 32 0 0 1 32-32m72 184H56V48h26.75A47.9 47.9 0 0 0 80 64v8a8 8 0 0 0 8 8h80a8 8 0 0 0 8-8v-8a47.9 47.9 0 0 0-2.75-16H200Z"
// />
// </svg>
// `;
// button.onclick = () => {
// let snippet = block.innerText ?? "";
// navigator.clipboard.writeText(snippet);
// button.classList.toggle("copied");
// setTimeout(() => button.classList.remove("copied"), 1000);
// };
// block.appendChild(button);
// });
// }
// }, 3001)
// heading copy
let headings = document.getElementsByClassName("heading")
if (headings.length > 0) {
for (const heading of headings) {
let button = heading.querySelector("span")
if (button) {
button.onclick = () => {
let link = window.location.href.split("#")[0];
link += `#-${heading.textContent?.slice(10).trimEnd().replaceAll(" ", "-").toLowerCase()}`;
window.location.href = link
navigator.clipboard.writeText(link);
heading.classList.toggle("copied")
setTimeout(() => heading.classList.remove("copied"), 1000);
}
}
}
}
</script>

View file

@ -2,38 +2,38 @@
import { Icon } from "astro-icon/components";
const videos = [
{
author: '<a href="https://github.com/soramanew">soramane</a>',
source: "https://github.com/caelestia-dots/shell",
path: "/assets/showcase/soramane.mp4",
installable: true,
},
{
author: '<a href="https://github.com/end-4">end_4</a>',
source: "https://github.com/end-4/dots-hyprland",
path: "/assets/showcase/end4.mp4",
installable: true,
},
{
author: '<a href="https://outfoxxed.me">outfoxxed</a>',
source:
"https://git.outfoxxed.me/outfoxxed/nixnew/src/branch/master/modules/user/modules/quickshell",
path: "/assets/showcase/outfoxxed.mp4",
},
{
author:
'<a href="https://github.com/pfaj/">pfaj</a> and <a href="https://github.com/bdebiase">bdebiase</a>',
path: "/assets/showcase/pfaj-bdeblase.mp4",
},
{
author: '<a href="https://github.com/flickowoa">flicko</a>',
source: "https://github.com/flickowoa/zephyr",
path: "/assets/showcase/flicko.mp4",
},
{
author: '<a href="https://vaxry.net">vaxry</a>',
path: "/assets/showcase/vaxry.mp4",
},
{
author: '<a href="https://github.com/soramanew">soramane</a>',
source: "https://github.com/caelestia-dots/shell",
path: "/assets/showcase/soramane.mp4",
installable: true,
},
{
author: '<a href="https://github.com/end-4">end_4</a>',
source: "https://github.com/end-4/dots-hyprland",
path: "/assets/showcase/end4.mp4",
installable: true,
},
{
author: '<a href="https://outfoxxed.me">outfoxxed</a>',
source:
"https://git.outfoxxed.me/outfoxxed/nixnew/src/branch/master/modules/user/modules/quickshell",
path: "/assets/showcase/outfoxxed.mp4",
},
{
author:
'<a href="https://github.com/pfaj/">pfaj</a> and <a href="https://github.com/bdebiase">bdebiase</a>',
path: "/assets/showcase/pfaj-bdeblase.mp4",
},
{
author: '<a href="https://github.com/flickowoa">flicko</a>',
source: "https://github.com/flickowoa/zephyr",
path: "/assets/showcase/flicko.mp4",
},
{
author: '<a href="https://vaxry.net">vaxry</a>',
path: "/assets/showcase/vaxry.mp4",
},
];
---
<div class="marquee">

View file

@ -0,0 +1,9 @@
interface CopyButtonOptions {
duration?: number;
copyIcon?: string;
successIcon?: string;
display?: "hover" | "ready";
cssVariables?: string;
}
export type { CopyButtonOptions };

View file

@ -13,6 +13,7 @@ import type {
VersionsData,
} from "./module";
import type { SearchLists } from "./search";
import type { CopyButtonOptions } from "./codeblock";
export type {
QMLTypeLinkObject,
@ -28,4 +29,5 @@ export type {
VersionData,
VersionsData,
SearchLists,
CopyButtonOptions,
};

View file

@ -14,12 +14,14 @@ import { remarkAlert } from "remark-github-blockquote-alert";
import rehypeShiki from "@shikijs/rehype";
import sectionize from "@hbsnow/rehype-sectionize";
import type { ShikiTransformer } from "shiki";
import { h } from "hastscript";
import {
getQMLTypeLinkObject,
getQMLTypeLink,
getIconForLink,
} from "./helpers.ts";
import type { CopyButtonOptions } from "@_types";
let currentVersion = "NOVERSION";
@ -155,7 +157,54 @@ const shikiRewriteTypelinks: ShikiTransformer = {
},
};
export const markdownConfig: AstroMarkdownOptions = {
const shikiCopyButton: ShikiTransformer = {
name: "copy-button",
pre(node) {
const options: CopyButtonOptions = {
duration: 3000,
};
const button = h(
"button",
{
class: "copy-button",
role: "button",
"aria-label": "Copy to clipboard",
"alia-live": "polite",
// "data-code": removeCodeAnnotations(this.source),
onclick: `
navigator.clipboard.writeText(this.dataset.code);
this.classList.add('copied');
this.setAttribute('aria-pressed', 'true');
setTimeout(() => { this.classList.remove('copied'); this.setAttribute('aria-pressed', 'false');}, ${options.duration})
`,
},
[
h("span", { class: "ready" }),
h("span", { class: "success" }),
h(
"svg",
{
class: "copy-icon",
role: "icon",
xmlns: "http://www.w3.org/2000/svg",
width: "1em",
height: "1em",
viewBox: "0 0 256 256",
},
[
h("path", {
fill: "currentColor",
d: "M200 32h-36.26a47.92 47.92 0 0 0-71.48 0H56a16 16 0 0 0-16 16v168a16 16 0 0 0 16 16h144a16 16 0 0 0 16-16V48a16 16 0 0 0-16-16m-72 0a32 32 0 0 1 32 32H96a32 32 0 0 1 32-32m72 184H56V48h26.75A47.9 47.9 0 0 0 80 64v8a8 8 0 0 0 8 8h80a8 8 0 0 0 8-8v-8a47.9 47.9 0 0 0-2.75-16H200Z",
}),
]
),
]
);
node.children.splice(0, 0, button);
},
};
const markdownConfig: AstroMarkdownOptions = {
syntaxHighlight: false,
remarkPlugins: [
remarkParseAtTypes,
@ -182,7 +231,7 @@ export const markdownConfig: AstroMarkdownOptions = {
},
defaultColor: false,
wrap: true,
transformers: [shikiRewriteTypelinks],
transformers: [shikiRewriteTypelinks, shikiCopyButton],
},
],
// FIXME: incompatible types between unified/Plugin and Astro/RehypePlugin
@ -206,7 +255,7 @@ async function getMarkdownProcessor(): Promise<MarkdownProcessor> {
return globalMarkdownProcessor;
}
export async function processMarkdown(
async function processMarkdown(
version: string,
markdown: string
): Promise<string> {
@ -217,3 +266,5 @@ export async function processMarkdown(
currentVersion = "NOVERSION";
return r;
}
export { markdownConfig, processMarkdown };

View file

@ -1,120 +0,0 @@
//# FIXME: fuseConfig.ts
// --
// generateSearchLists.ts
interface SearchLists {
slug: string;
link: string;
summary: string;
}
// --
// generateTypeData.ts
interface QuickshellBase {
type: string;
module: string;
name: string;
}
interface QuickshellInstance {
name?: string;
type: {
gadget?: QuickshellGadget;
type: string;
module: string;
name: string;
of?: QuickshellBase;
};
details?: string;
flags?: string[];
}
interface QuickshellGadget {
[key: string]: QuickshellInstance;
}
interface QuickshellProps {
[key: string]: QuickshellInstance;
}
interface QuickshellFunction {
ret: QuickshellInstance;
name: string;
id: string;
details: string;
params: QuickshellInstance[];
}
interface QuickshellSignal {
[key: string]: {
name: string;
details: string;
params: QuickshellInstance[];
};
}
interface QuickshellVariant {
[key: string]: {
name?: string;
details: string;
params?: QuickshellInstance[];
};
}
interface TypeData {
name: string;
description: string;
details: string;
flags?: string[];
contains?: string[];
super?: QuickshellBase;
properties?: QuickshellProps;
functions?: QuickshellFunction[];
signals?: QuickshellSignal;
variants?: QuickshellVariant;
subtypes?: QuickshellData[];
}
interface ModuleData {
name: string;
description: string;
details: string;
types: TypeData[];
}
interface VersionData {
name: string;
changelog?: string;
modules: ModuleData[];
}
interface VersionsData {
default: string;
versions: VersionData[];
}
// --
// helpers.ts
interface QMLTypeLinkObject {
type: string;
module?: string;
name?: string;
mtype?: string;
mname?: string;
}
// --
export type {
QuickshellBase,
QuickshellInstance,
QuickshellGadget,
QuickshellProps,
QuickshellFunction,
QuickshellSignal,
QuickshellVariant,
TypeData,
ModuleData,
VersionData,
VersionsData,
QMLTypeLinkObject,
};

View file

@ -21,3 +21,4 @@ const { title, description } = Astro.props;
<slot />
</body>
</html>

View file

@ -15,16 +15,18 @@ interface Props {
title: string;
description: string;
headings?: ConfigHeading[];
type?: TypeData
type?: TypeData;
}
const { title, description, headings, type } = Astro.props;
let url = Astro.url.pathname.split("/").filter(s => s !== "");
const breadcrumbs = [{
text: "custom",
href: "/",
}];
const breadcrumbs = [
{
text: "custom",
href: "/",
},
];
let linkPath = "";
if (url[0] === "docs") {
@ -90,4 +92,33 @@ for (const segment of url) {
<Footer/>
</body>
</html>
<script>
// FIXME: need to make this work properly, or fold into the markdown processor
let headings = document.getElementsByClassName("heading")
if (headings.length > 0) {
for (const heading of headings) {
let button = heading.querySelector("h2")
if (button) {
button.onclick = () => {
let link = window.location.href.split("#")[0];
link += `#${button.textContent?.trimEnd().replaceAll(" ", "-").toLowerCase()}`;
window.location.href = link
navigator.clipboard.writeText(link);
heading.classList.toggle("copied")
setTimeout(() => heading.classList.remove("copied"), 1000);
}
}
let spanButton = heading.querySelector("span")
if (spanButton) {
spanButton.onclick = () => {
let link = window.location.href.split("#")[0];
link += `#${spanButton.textContent?.trim().replaceAll(" ", "-").toLowerCase()}`;
window.location.href = link
navigator.clipboard.writeText(link);
spanButton.classList.toggle("copied")
setTimeout(() => heading.classList.remove("copied"), 1000);
}
}
}
}
</script>

View file

@ -7,10 +7,13 @@ export interface Props {
frontmatter: {
title: string;
description?: string;
}
};
}
const { headings, frontmatter: { title, description } } = Astro.props;
const {
headings,
frontmatter: { title, description },
} = Astro.props;
---
<GuideLayout title={title} description={description ?? ""} headings={headings}>
<slot/>

View file

@ -27,7 +27,7 @@ pre {
overflow: hidden;
text-wrap: wrap;
& > button {
& .copy-button {
all: unset;
width: 2rem;
height: 2rem;
@ -45,6 +45,7 @@ pre {
background-color: hsl(var(--blue) 85% 35% / 0.1);
cursor: pointer;
transition: color 0.25s;
z-index: 10;
&:hover {
color: hsl(var(--blue) 100% 75%);

View file

@ -3736,7 +3736,7 @@ __metadata:
languageName: node
linkType: hard
"hastscript@npm:^9.0.0":
"hastscript@npm:^9.0.0, hastscript@npm:^9.0.1":
version: 9.0.1
resolution: "hastscript@npm:9.0.1"
dependencies:
@ -5557,6 +5557,7 @@ __metadata:
astro-breadcrumbs: "npm:^3.3.1"
astro-icon: "npm:^1.1.5"
hast-util-from-html: "npm:^2.0.3"
hastscript: "npm:^9.0.1"
jsonc-parser: "npm:^3.3.1"
pagefind: "npm:^1.4.0"
rehype: "npm:^13.0.2"