From ab5b5e967f3b1044115c92ceb39fb9a04d4b5fa7 Mon Sep 17 00:00:00 2001
From: martyy-code
Date: Wed, 29 Jul 2026 14:09:32 +0200
Subject: [PATCH] feat(web): add code-block styling and copy button to install
commands
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Each bash install command (visible + inside ) is now
rendered through a new client component that:
- Wraps the command in a rounded, bordered container with a muted
bg-muted/30 header strip and a real (functional) copy button.
The header label defaults to "bash" but is overridable.
- Renders the command as a plain
without going
through Shiki. Shiki would have required the bash grammar to
be preloaded in apps/web/lib/shiki.ts, which the project does
not currently do — going through Shiki would have thrown
ShikiError on every detail page render. Plain
avoids
that surface entirely and keeps the visual idiom intact.
- copy state is local: navigator.clipboard.writeText with a
silent catch (clipboard API can fail in non-secure contexts,
e.g. http on a non-localhost origin; the user can still
select-and-copy manually).
- copyLabel prop lets future i18n or test code inject custom
"Copy"/"Copied" strings; default is English.
InstallCommand swaps its 4 calls
for . Otherwise the JSX structure is
unchanged.
Verification:
- typecheck exits 0
- visual shape matches the existing Code tab in the detail page
(same border + rounded + label + copy header), reinforcing the
visual idiom without depending on Shiki for shell languages.
Co-Authored-By: Claude
---
.../command-block/command-block.tsx | 60 +++++++++++++++++++
apps/web/components/command-block/index.ts | 1 +
.../install-command/install-command.tsx | 10 ++--
3 files changed, 66 insertions(+), 5 deletions(-)
create mode 100644 apps/web/components/command-block/command-block.tsx
create mode 100644 apps/web/components/command-block/index.ts
diff --git a/apps/web/components/command-block/command-block.tsx b/apps/web/components/command-block/command-block.tsx
new file mode 100644
index 0000000..69796d5
--- /dev/null
+++ b/apps/web/components/command-block/command-block.tsx
@@ -0,0 +1,60 @@
+"use client"
+
+import { useState } from "react"
+
+interface CommandBlockProps {
+ code: string
+ label?: string
+ showCopy?: boolean
+ /**
+ * Override the default "Copy" / "Copied" label pair (e.g. for localized
+ * UI). The function receives the current state and returns the label.
+ */
+ copyLabel?: (state: "idle" | "copied") => string
+}
+
+export function CommandBlock({
+ code,
+ label = "bash",
+ showCopy = true,
+ copyLabel,
+}: CommandBlockProps) {
+ const [copied, setCopied] = useState(false)
+
+ const onCopy = async () => {
+ try {
+ await navigator.clipboard.writeText(code)
+ setCopied(true)
+ window.setTimeout(() => setCopied(false), 1500)
+ } catch {
+ // Clipboard API can fail in non-secure contexts; silently ignore —
+ // the user can still select-and-copy manually.
+ }
+ }
+
+ return (
+
+
+
+ {label}
+
+ {showCopy && (
+
+ )}
+
+
+ {code}
+
+
+ )
+}
diff --git a/apps/web/components/command-block/index.ts b/apps/web/components/command-block/index.ts
new file mode 100644
index 0000000..7bcfe17
--- /dev/null
+++ b/apps/web/components/command-block/index.ts
@@ -0,0 +1 @@
+export { CommandBlock } from "./command-block"
diff --git a/apps/web/components/install-command/install-command.tsx b/apps/web/components/install-command/install-command.tsx
index 3d288c0..784829d 100644
--- a/apps/web/components/install-command/install-command.tsx
+++ b/apps/web/components/install-command/install-command.tsx
@@ -1,4 +1,4 @@
-import { CodeBlock } from "@/components/code-block"
+import { CommandBlock } from "@/components/command-block"
interface InstallCommandProps {
itemId: string
@@ -20,7 +20,7 @@ export function InstallCommand({ itemId }: InstallCommandProps) {
Add this component to your project with the shadcn CLI. Pick the
mode that matches how you installed the registry.
-
+
Other install modes
@@ -30,14 +30,14 @@ export function InstallCommand({ itemId }: InstallCommandProps) {
URL mode — works against the deployed showcase site:
-
+
Namespace mode — register once, then any item by short name: