summaryrefslogtreecommitdiff
path: root/apps/web/src/app/changelog/components/copy-markdown-button.tsx
diff options
context:
space:
mode:
authorMaze Winther <mazewinther@gmail.com>2026-04-14 23:55:20 +0200
committerMaze Winther <mazewinther@gmail.com>2026-04-14 23:55:20 +0200
commit8fe66090e7d4a3a0dcde02cfa828a5c3bee63122 (patch)
treed7dfd95a38d8323f1e3771646c2d14dbfd50f18d /apps/web/src/app/changelog/components/copy-markdown-button.tsx
parent91e1232112421438ce73b9361896f9b58c84fb3e (diff)
downloadopencut-8fe66090e7d4a3a0dcde02cfa828a5c3bee63122.tar.gz
feat: add support for technical details in changelog entries
Diffstat (limited to 'apps/web/src/app/changelog/components/copy-markdown-button.tsx')
-rw-r--r--apps/web/src/app/changelog/components/copy-markdown-button.tsx156
1 files changed, 88 insertions, 68 deletions
diff --git a/apps/web/src/app/changelog/components/copy-markdown-button.tsx b/apps/web/src/app/changelog/components/copy-markdown-button.tsx
index 7ea18b2a..ddf7bbb1 100644
--- a/apps/web/src/app/changelog/components/copy-markdown-button.tsx
+++ b/apps/web/src/app/changelog/components/copy-markdown-button.tsx
@@ -1,68 +1,88 @@
-"use client";
-
-import { useState } from "react";
-import { CheckIcon, ClipboardIcon } from "lucide-react";
-import { getSectionTitle, groupAndOrderChanges } from "../utils";
-import type { Change } from "../utils";
-import { cn } from "@/utils/ui";
-import { Button } from "@/components/ui/button";
-
-function buildMarkdown({
- description,
- changes,
-}: {
- description?: string;
- changes: Change[];
-}): string {
- const lines: string[] = [];
-
- if (description) {
- lines.push(description, "");
- }
-
- const { grouped, orderedTypes } = groupAndOrderChanges({ changes });
-
- for (const type of orderedTypes) {
- lines.push(`## ${getSectionTitle({ type })}`);
- for (const change of grouped[type]) {
- lines.push(`- ${change.text}`);
- }
- lines.push("");
- }
-
- return lines.join("\n").trimEnd();
-}
-
-export function CopyMarkdownButton({
- description,
- changes,
-}: {
- description?: string;
- changes: Change[];
-}) {
- const [copied, setCopied] = useState(false);
-
- const handleCopy = async () => {
- const markdown = buildMarkdown({ description, changes });
- await navigator.clipboard.writeText(markdown);
- setCopied(true);
- setTimeout(() => setCopied(false), 2000);
- };
-
- return (
- <Button
- size="sm"
- variant="text"
- onClick={handleCopy}
- className={cn("flex items-center gap-1.5", copied && "pointer-events-none")}
- title="Copy as markdown"
- >
- {copied ? (
- <CheckIcon className="size-4" />
- ) : (
- <ClipboardIcon className="size-4" />
- )}
- {copied ? "Copied!" : "Copy markdown"}
- </Button>
- );
-}
+"use client";
+
+import { useState } from "react";
+import { CheckIcon, ClipboardIcon } from "lucide-react";
+import {
+ getSectionTitle,
+ groupAndOrderChanges,
+ isSectionCollapsible,
+} from "../utils";
+import type { Change } from "../utils";
+import { cn } from "@/utils/ui";
+import { Button } from "@/components/ui/button";
+
+function buildMarkdown({
+ description,
+ changes,
+}: {
+ description?: string;
+ changes: Change[];
+}): string {
+ const lines: string[] = [];
+
+ if (description) {
+ lines.push(description, "");
+ }
+
+ const { grouped, orderedTypes } = groupAndOrderChanges({ changes });
+
+ for (const type of orderedTypes) {
+ if (isSectionCollapsible({ type })) {
+ lines.push(
+ "<details>",
+ `<summary>${getSectionTitle({ type })}</summary>`,
+ "",
+ );
+ for (const change of grouped[type]) {
+ lines.push(`- ${change.text}`);
+ }
+ lines.push("", "</details>", "");
+ continue;
+ }
+
+ lines.push(`## ${getSectionTitle({ type })}`);
+ for (const change of grouped[type]) {
+ lines.push(`- ${change.text}`);
+ }
+ lines.push("");
+ }
+
+ return lines.join("\n").trimEnd();
+}
+
+export function CopyMarkdownButton({
+ description,
+ changes,
+}: {
+ description?: string;
+ changes: Change[];
+}) {
+ const [copied, setCopied] = useState(false);
+
+ const handleCopy = async () => {
+ const markdown = buildMarkdown({ description, changes });
+ await navigator.clipboard.writeText(markdown);
+ setCopied(true);
+ setTimeout(() => setCopied(false), 2000);
+ };
+
+ return (
+ <Button
+ size="sm"
+ variant="text"
+ onClick={handleCopy}
+ className={cn(
+ "flex items-center gap-1.5",
+ copied && "pointer-events-none",
+ )}
+ title="Copy as markdown"
+ >
+ {copied ? (
+ <CheckIcon className="size-4" />
+ ) : (
+ <ClipboardIcon className="size-4" />
+ )}
+ {copied ? "Copied!" : "Copy markdown"}
+ </Button>
+ );
+}